Path: blob/master/sound/firewire/fireworks/fireworks_transaction.c
29269 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);8384scoped_guard(spinlock_irq, &transaction_queues_lock) {85list_add_tail(&t.list, &transaction_queues);86}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);108109scoped_guard(spinlock_irq, &transaction_queues_lock) {110list_del(&t.list);111}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);124125guard(spinlock)(&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;136return;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;159}160161static void162handle_resp_for_user(struct fw_card *card, int generation, int source,163void *data, size_t length, int *rcode)164{165struct fw_device *device;166struct snd_efw *efw;167unsigned int i;168169guard(spinlock_irq)(&instances_lock);170171for (i = 0; i < SNDRV_CARDS; i++) {172efw = instances[i];173if (efw == NULL)174continue;175device = fw_parent_device(efw->unit);176if ((device->card != card) ||177(device->generation != generation))178continue;179smp_rmb(); /* node id vs. generation */180if (device->node_id != source)181continue;182183break;184}185if (i == SNDRV_CARDS)186return;187188copy_resp_to_buf(efw, data, length, rcode);189}190191static void192handle_resp_for_kernel(struct fw_card *card, int generation, int source,193void *data, size_t length, int *rcode, u32 seqnum)194{195struct fw_device *device;196struct transaction_queue *t;197198guard(spinlock_irqsave)(&transaction_queues_lock);199list_for_each_entry(t, &transaction_queues, list) {200device = fw_parent_device(t->unit);201if ((device->card != card) ||202(device->generation != generation))203continue;204smp_rmb(); /* node_id vs. generation */205if (device->node_id != source)206continue;207208if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {209t->state = STATE_COMPLETE;210t->size = min_t(unsigned int, length, t->size);211memcpy(t->buf, data, t->size);212wake_up(&t->wait);213*rcode = RCODE_COMPLETE;214}215}216}217218static void219efw_response(struct fw_card *card, struct fw_request *request,220int tcode, int destination, int source,221int generation, unsigned long long offset,222void *data, size_t length, void *callback_data)223{224int rcode, dummy;225u32 seqnum;226227rcode = RCODE_TYPE_ERROR;228if (length < sizeof(struct snd_efw_transaction)) {229rcode = RCODE_DATA_ERROR;230goto end;231} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {232rcode = RCODE_ADDRESS_ERROR;233goto end;234}235236seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);237if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {238handle_resp_for_kernel(card, generation, source,239data, length, &rcode, seqnum);240if (snd_efw_resp_buf_debug)241handle_resp_for_user(card, generation, source,242data, length, &dummy);243} else {244handle_resp_for_user(card, generation, source,245data, length, &rcode);246}247end:248fw_send_response(card, request, rcode);249}250251void snd_efw_transaction_add_instance(struct snd_efw *efw)252{253unsigned int i;254255guard(spinlock_irq)(&instances_lock);256257for (i = 0; i < SNDRV_CARDS; i++) {258if (instances[i] != NULL)259continue;260instances[i] = efw;261break;262}263}264265void snd_efw_transaction_remove_instance(struct snd_efw *efw)266{267unsigned int i;268269guard(spinlock_irq)(&instances_lock);270271for (i = 0; i < SNDRV_CARDS; i++) {272if (instances[i] != efw)273continue;274instances[i] = NULL;275}276}277278void snd_efw_transaction_bus_reset(struct fw_unit *unit)279{280struct transaction_queue *t;281282guard(spinlock_irq)(&transaction_queues_lock);283list_for_each_entry(t, &transaction_queues, list) {284if ((t->unit == unit) &&285(t->state == STATE_PENDING)) {286t->state = STATE_BUS_RESET;287wake_up(&t->wait);288}289}290}291292static struct fw_address_handler resp_register_handler = {293.length = SND_EFW_RESPONSE_MAXIMUM_BYTES,294.address_callback = efw_response295};296297int snd_efw_transaction_register(void)298{299static const struct fw_address_region resp_register_region = {300.start = MEMORY_SPACE_EFW_RESPONSE,301.end = MEMORY_SPACE_EFW_RESPONSE +302SND_EFW_RESPONSE_MAXIMUM_BYTES303};304return fw_core_add_address_handler(&resp_register_handler,305&resp_register_region);306}307308void snd_efw_transaction_unregister(void)309{310WARN_ON(!list_empty(&transaction_queues));311fw_core_remove_address_handler(&resp_register_handler);312}313314315