325 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * fireworks_transaction.c - a part of driver for Fireworks based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7 8/*9 * Fireworks have its own transaction. The transaction can be delivered by AV/C10 * Vendor Specific command frame or usual asynchronous transaction. At least,11 * Windows driver and firmware version 5.5 or later don't use AV/C command.12 *13 * Transaction substance:14 * At first, 6 data exist. Following to the data, parameters for each command15 * exist. All of the parameters are 32 bit aligned to big endian.16 * data[0]: Length of transaction substance17 * data[1]: Transaction version18 * data[2]: Sequence number. This is incremented by the device19 * data[3]: Transaction category20 * data[4]: Transaction command21 * data[5]: Return value in response.22 * data[6-]: Parameters23 *24 * Transaction address:25 * command: 0xecc00000000026 * response: 0xecc080000000 (default)27 *28 * I note that the address for response can be changed by command. But this29 * module uses the default address.30 */31#include "./fireworks.h"32 33#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL34#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL35 36#define ERROR_RETRIES 337#define ERROR_DELAY_MS 538#define EFC_TIMEOUT_MS 12539 40static DEFINE_SPINLOCK(instances_lock);41static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;42 43static DEFINE_SPINLOCK(transaction_queues_lock);44static LIST_HEAD(transaction_queues);45 46enum transaction_queue_state {47 STATE_PENDING,48 STATE_BUS_RESET,49 STATE_COMPLETE50};51 52struct transaction_queue {53 struct list_head list;54 struct fw_unit *unit;55 void *buf;56 unsigned int size;57 u32 seqnum;58 enum transaction_queue_state state;59 wait_queue_head_t wait;60};61 62int snd_efw_transaction_cmd(struct fw_unit *unit,63 const void *cmd, unsigned int size)64{65 return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,66 MEMORY_SPACE_EFW_COMMAND,67 (void *)cmd, size, 0);68}69 70int snd_efw_transaction_run(struct fw_unit *unit,71 const void *cmd, unsigned int cmd_size,72 void *resp, unsigned int resp_size)73{74 struct transaction_queue t;75 unsigned int tries;76 int ret;77 78 t.unit = unit;79 t.buf = resp;80 t.size = resp_size;81 t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;82 t.state = STATE_PENDING;83 init_waitqueue_head(&t.wait);84 85 spin_lock_irq(&transaction_queues_lock);86 list_add_tail(&t.list, &transaction_queues);87 spin_unlock_irq(&transaction_queues_lock);88 89 tries = 0;90 do {91 ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);92 if (ret < 0)93 break;94 95 wait_event_timeout(t.wait, t.state != STATE_PENDING,96 msecs_to_jiffies(EFC_TIMEOUT_MS));97 98 if (t.state == STATE_COMPLETE) {99 ret = t.size;100 break;101 } else if (t.state == STATE_BUS_RESET) {102 msleep(ERROR_DELAY_MS);103 } else if (++tries >= ERROR_RETRIES) {104 dev_err(&t.unit->device, "EFW transaction timed out\n");105 ret = -EIO;106 break;107 }108 } while (1);109 110 spin_lock_irq(&transaction_queues_lock);111 list_del(&t.list);112 spin_unlock_irq(&transaction_queues_lock);113 114 return ret;115}116 117static void118copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)119{120 size_t capacity, till_end;121 struct snd_efw_transaction *t;122 123 t = (struct snd_efw_transaction *)data;124 length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);125 126 spin_lock(&efw->lock);127 128 if (efw->push_ptr < efw->pull_ptr)129 capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);130 else131 capacity = snd_efw_resp_buf_size -132 (unsigned int)(efw->push_ptr - efw->pull_ptr);133 134 /* confirm enough space for this response */135 if (capacity < length) {136 *rcode = RCODE_CONFLICT_ERROR;137 goto end;138 }139 140 /* copy to ring buffer */141 while (length > 0) {142 till_end = snd_efw_resp_buf_size -143 (unsigned int)(efw->push_ptr - efw->resp_buf);144 till_end = min_t(unsigned int, length, till_end);145 146 memcpy(efw->push_ptr, data, till_end);147 148 efw->push_ptr += till_end;149 if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)150 efw->push_ptr -= snd_efw_resp_buf_size;151 152 length -= till_end;153 data += till_end;154 }155 156 /* for hwdep */157 wake_up(&efw->hwdep_wait);158 159 *rcode = RCODE_COMPLETE;160end:161 spin_unlock_irq(&efw->lock);162}163 164static void165handle_resp_for_user(struct fw_card *card, int generation, int source,166 void *data, size_t length, int *rcode)167{168 struct fw_device *device;169 struct snd_efw *efw;170 unsigned int i;171 172 spin_lock_irq(&instances_lock);173 174 for (i = 0; i < SNDRV_CARDS; i++) {175 efw = instances[i];176 if (efw == NULL)177 continue;178 device = fw_parent_device(efw->unit);179 if ((device->card != card) ||180 (device->generation != generation))181 continue;182 smp_rmb(); /* node id vs. generation */183 if (device->node_id != source)184 continue;185 186 break;187 }188 if (i == SNDRV_CARDS)189 goto end;190 191 copy_resp_to_buf(efw, data, length, rcode);192end:193 spin_unlock(&instances_lock);194}195 196static void197handle_resp_for_kernel(struct fw_card *card, int generation, int source,198 void *data, size_t length, int *rcode, u32 seqnum)199{200 struct fw_device *device;201 struct transaction_queue *t;202 unsigned long flags;203 204 spin_lock_irqsave(&transaction_queues_lock, flags);205 list_for_each_entry(t, &transaction_queues, list) {206 device = fw_parent_device(t->unit);207 if ((device->card != card) ||208 (device->generation != generation))209 continue;210 smp_rmb(); /* node_id vs. generation */211 if (device->node_id != source)212 continue;213 214 if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {215 t->state = STATE_COMPLETE;216 t->size = min_t(unsigned int, length, t->size);217 memcpy(t->buf, data, t->size);218 wake_up(&t->wait);219 *rcode = RCODE_COMPLETE;220 }221 }222 spin_unlock_irqrestore(&transaction_queues_lock, flags);223}224 225static void226efw_response(struct fw_card *card, struct fw_request *request,227 int tcode, int destination, int source,228 int generation, unsigned long long offset,229 void *data, size_t length, void *callback_data)230{231 int rcode, dummy;232 u32 seqnum;233 234 rcode = RCODE_TYPE_ERROR;235 if (length < sizeof(struct snd_efw_transaction)) {236 rcode = RCODE_DATA_ERROR;237 goto end;238 } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {239 rcode = RCODE_ADDRESS_ERROR;240 goto end;241 }242 243 seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);244 if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {245 handle_resp_for_kernel(card, generation, source,246 data, length, &rcode, seqnum);247 if (snd_efw_resp_buf_debug)248 handle_resp_for_user(card, generation, source,249 data, length, &dummy);250 } else {251 handle_resp_for_user(card, generation, source,252 data, length, &rcode);253 }254end:255 fw_send_response(card, request, rcode);256}257 258void snd_efw_transaction_add_instance(struct snd_efw *efw)259{260 unsigned int i;261 262 spin_lock_irq(&instances_lock);263 264 for (i = 0; i < SNDRV_CARDS; i++) {265 if (instances[i] != NULL)266 continue;267 instances[i] = efw;268 break;269 }270 271 spin_unlock_irq(&instances_lock);272}273 274void snd_efw_transaction_remove_instance(struct snd_efw *efw)275{276 unsigned int i;277 278 spin_lock_irq(&instances_lock);279 280 for (i = 0; i < SNDRV_CARDS; i++) {281 if (instances[i] != efw)282 continue;283 instances[i] = NULL;284 }285 286 spin_unlock_irq(&instances_lock);287}288 289void snd_efw_transaction_bus_reset(struct fw_unit *unit)290{291 struct transaction_queue *t;292 293 spin_lock_irq(&transaction_queues_lock);294 list_for_each_entry(t, &transaction_queues, list) {295 if ((t->unit == unit) &&296 (t->state == STATE_PENDING)) {297 t->state = STATE_BUS_RESET;298 wake_up(&t->wait);299 }300 }301 spin_unlock_irq(&transaction_queues_lock);302}303 304static struct fw_address_handler resp_register_handler = {305 .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,306 .address_callback = efw_response307};308 309int snd_efw_transaction_register(void)310{311 static const struct fw_address_region resp_register_region = {312 .start = MEMORY_SPACE_EFW_RESPONSE,313 .end = MEMORY_SPACE_EFW_RESPONSE +314 SND_EFW_RESPONSE_MAXIMUM_BYTES315 };316 return fw_core_add_address_handler(&resp_register_handler,317 &resp_register_region);318}319 320void snd_efw_transaction_unregister(void)321{322 WARN_ON(!list_empty(&transaction_queues));323 fw_core_remove_address_handler(&resp_register_handler);324}325