brintos

brintos / linux-shallow public Read only

0
0
Text · 8.0 KiB · 9f8c53b Raw
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