Path: blob/master/sound/firewire/fireworks/fireworks_transaction.c
26439 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* fireworks_transaction.c - a part of driver for Fireworks based devices3*4* Copyright (c) 2013-2014 Takashi Sakamoto5*/67/*8* Fireworks have its own transaction. The transaction can be delivered by AV/C9* Vendor Specific command frame or usual asynchronous transaction. At least,10* Windows driver and firmware version 5.5 or later don't use AV/C command.11*12* Transaction substance:13* At first, 6 data exist. Following to the data, parameters for each command14* exist. All of the parameters are 32 bit aligned to big endian.15* data[0]: Length of transaction substance16* data[1]: Transaction version17* data[2]: Sequence number. This is incremented by the device18* data[3]: Transaction category19* data[4]: Transaction command20* data[5]: Return value in response.21* data[6-]: Parameters22*23* Transaction address:24* command: 0xecc00000000025* response: 0xecc080000000 (default)26*27* I note that the address for response can be changed by command. But this28* module uses the default address.29*/30#include "./fireworks.h"3132#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL33#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL3435#define ERROR_RETRIES 336#define ERROR_DELAY_MS 537#define EFC_TIMEOUT_MS 1253839static DEFINE_SPINLOCK(instances_lock);40static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;4142static DEFINE_SPINLOCK(transaction_queues_lock);43static LIST_HEAD(transaction_queues);4445enum transaction_queue_state {46STATE_PENDING,47STATE_BUS_RESET,48STATE_COMPLETE49};5051struct transaction_queue {52struct list_head list;53struct fw_unit *unit;54void *buf;55unsigned int size;56u32 seqnum;57enum transaction_queue_state state;58wait_queue_head_t wait;59};6061int snd_efw_transaction_cmd(struct fw_unit *unit,62const void *cmd, unsigned int size)63{64return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,65MEMORY_SPACE_EFW_COMMAND,66(void *)cmd, size, 0);67}6869int snd_efw_transaction_run(struct fw_unit *unit,70const void *cmd, unsigned int cmd_size,71void *resp, unsigned int resp_size)72{73struct transaction_queue t;74unsigned int tries;75int ret;7677t.unit = unit;78t.buf = resp;79t.size = resp_size;80t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;81t.state = STATE_PENDING;82init_waitqueue_head(&t.wait);8384spin_lock_irq(&transaction_queues_lock);85list_add_tail(&t.list, &transaction_queues);86spin_unlock_irq(&transaction_queues_lock);8788tries = 0;89do {90ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);91if (ret < 0)92break;9394wait_event_timeout(t.wait, t.state != STATE_PENDING,95msecs_to_jiffies(EFC_TIMEOUT_MS));9697if (t.state == STATE_COMPLETE) {98ret = t.size;99break;100} else if (t.state == STATE_BUS_RESET) {101msleep(ERROR_DELAY_MS);102} else if (++tries >= ERROR_RETRIES) {103dev_err(&t.unit->device, "EFW transaction timed out\n");104ret = -EIO;105break;106}107} while (1);108109spin_lock_irq(&transaction_queues_lock);110list_del(&t.list);111spin_unlock_irq(&transaction_queues_lock);112113return ret;114}115116static void117copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)118{119size_t capacity, till_end;120struct snd_efw_transaction *t;121122t = (struct snd_efw_transaction *)data;123length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);124125spin_lock(&efw->lock);126127if (efw->push_ptr < efw->pull_ptr)128capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);129else130capacity = snd_efw_resp_buf_size -131(unsigned int)(efw->push_ptr - efw->pull_ptr);132133/* confirm enough space for this response */134if (capacity < length) {135*rcode = RCODE_CONFLICT_ERROR;136goto end;137}138139/* copy to ring buffer */140while (length > 0) {141till_end = snd_efw_resp_buf_size -142(unsigned int)(efw->push_ptr - efw->resp_buf);143till_end = min_t(unsigned int, length, till_end);144145memcpy(efw->push_ptr, data, till_end);146147efw->push_ptr += till_end;148if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)149efw->push_ptr -= snd_efw_resp_buf_size;150151length -= till_end;152data += till_end;153}154155/* for hwdep */156wake_up(&efw->hwdep_wait);157158*rcode = RCODE_COMPLETE;159end:160spin_unlock_irq(&efw->lock);161}162163static void164handle_resp_for_user(struct fw_card *card, int generation, int source,165void *data, size_t length, int *rcode)166{167struct fw_device *device;168struct snd_efw *efw;169unsigned int i;170171spin_lock_irq(&instances_lock);172173for (i = 0; i < SNDRV_CARDS; i++) {174efw = instances[i];175if (efw == NULL)176continue;177device = fw_parent_device(efw->unit);178if ((device->card != card) ||179(device->generation != generation))180continue;181smp_rmb(); /* node id vs. generation */182if (device->node_id != source)183continue;184185break;186}187if (i == SNDRV_CARDS)188goto end;189190copy_resp_to_buf(efw, data, length, rcode);191end:192spin_unlock(&instances_lock);193}194195static void196handle_resp_for_kernel(struct fw_card *card, int generation, int source,197void *data, size_t length, int *rcode, u32 seqnum)198{199struct fw_device *device;200struct transaction_queue *t;201unsigned long flags;202203spin_lock_irqsave(&transaction_queues_lock, flags);204list_for_each_entry(t, &transaction_queues, list) {205device = fw_parent_device(t->unit);206if ((device->card != card) ||207(device->generation != generation))208continue;209smp_rmb(); /* node_id vs. generation */210if (device->node_id != source)211continue;212213if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {214t->state = STATE_COMPLETE;215t->size = min_t(unsigned int, length, t->size);216memcpy(t->buf, data, t->size);217wake_up(&t->wait);218*rcode = RCODE_COMPLETE;219}220}221spin_unlock_irqrestore(&transaction_queues_lock, flags);222}223224static void225efw_response(struct fw_card *card, struct fw_request *request,226int tcode, int destination, int source,227int generation, unsigned long long offset,228void *data, size_t length, void *callback_data)229{230int rcode, dummy;231u32 seqnum;232233rcode = RCODE_TYPE_ERROR;234if (length < sizeof(struct snd_efw_transaction)) {235rcode = RCODE_DATA_ERROR;236goto end;237} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {238rcode = RCODE_ADDRESS_ERROR;239goto end;240}241242seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);243if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {244handle_resp_for_kernel(card, generation, source,245data, length, &rcode, seqnum);246if (snd_efw_resp_buf_debug)247handle_resp_for_user(card, generation, source,248data, length, &dummy);249} else {250handle_resp_for_user(card, generation, source,251data, length, &rcode);252}253end:254fw_send_response(card, request, rcode);255}256257void snd_efw_transaction_add_instance(struct snd_efw *efw)258{259unsigned int i;260261spin_lock_irq(&instances_lock);262263for (i = 0; i < SNDRV_CARDS; i++) {264if (instances[i] != NULL)265continue;266instances[i] = efw;267break;268}269270spin_unlock_irq(&instances_lock);271}272273void snd_efw_transaction_remove_instance(struct snd_efw *efw)274{275unsigned int i;276277spin_lock_irq(&instances_lock);278279for (i = 0; i < SNDRV_CARDS; i++) {280if (instances[i] != efw)281continue;282instances[i] = NULL;283}284285spin_unlock_irq(&instances_lock);286}287288void snd_efw_transaction_bus_reset(struct fw_unit *unit)289{290struct transaction_queue *t;291292spin_lock_irq(&transaction_queues_lock);293list_for_each_entry(t, &transaction_queues, list) {294if ((t->unit == unit) &&295(t->state == STATE_PENDING)) {296t->state = STATE_BUS_RESET;297wake_up(&t->wait);298}299}300spin_unlock_irq(&transaction_queues_lock);301}302303static struct fw_address_handler resp_register_handler = {304.length = SND_EFW_RESPONSE_MAXIMUM_BYTES,305.address_callback = efw_response306};307308int snd_efw_transaction_register(void)309{310static const struct fw_address_region resp_register_region = {311.start = MEMORY_SPACE_EFW_RESPONSE,312.end = MEMORY_SPACE_EFW_RESPONSE +313SND_EFW_RESPONSE_MAXIMUM_BYTES314};315return fw_core_add_address_handler(&resp_register_handler,316&resp_register_region);317}318319void snd_efw_transaction_unregister(void)320{321WARN_ON(!list_empty(&transaction_queues));322fw_core_remove_address_handler(&resp_register_handler);323}324325326