summaryrefslogtreecommitdiff
path: root/sound/firewire/fireworks/fireworks_transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/fireworks/fireworks_transaction.c')
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c176
1 files changed, 156 insertions, 20 deletions
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
index aac91d8485d5..81a65ebb5f71 100644
--- a/sound/firewire/fireworks/fireworks_transaction.c
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -38,6 +38,9 @@
#define ERROR_DELAY_MS 5
#define EFC_TIMEOUT_MS 125
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
static DEFINE_SPINLOCK(transaction_queues_lock);
static LIST_HEAD(transaction_queues);
@@ -57,6 +60,14 @@ struct transaction_queue {
wait_queue_head_t wait;
};
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+ const void *cmd, unsigned int size)
+{
+ return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+ MEMORY_SPACE_EFW_COMMAND,
+ (void *)cmd, size, 0);
+}
+
int snd_efw_transaction_run(struct fw_unit *unit,
const void *cmd, unsigned int cmd_size,
void *resp, unsigned int resp_size)
@@ -78,9 +89,7 @@ int snd_efw_transaction_run(struct fw_unit *unit,
tries = 0;
do {
- ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
- MEMORY_SPACE_EFW_COMMAND,
- (void *)cmd, cmd_size, 0);
+ ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
if (ret < 0)
break;
@@ -107,27 +116,92 @@ int snd_efw_transaction_run(struct fw_unit *unit,
}
static void
-efw_response(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, unsigned long long offset,
- void *data, size_t length, void *callback_data)
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
{
- struct fw_device *device;
- struct transaction_queue *t;
- unsigned long flags;
- int rcode;
- u32 seqnum;
+ size_t capacity, till_end;
+ struct snd_efw_transaction *t;
- rcode = RCODE_TYPE_ERROR;
- if (length < sizeof(struct snd_efw_transaction)) {
- rcode = RCODE_DATA_ERROR;
- goto end;
- } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
- rcode = RCODE_ADDRESS_ERROR;
+ spin_lock_irq(&efw->lock);
+
+ t = (struct snd_efw_transaction *)data;
+ length = min_t(size_t, t->length * sizeof(t->length), length);
+
+ if (efw->push_ptr < efw->pull_ptr)
+ capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+ else
+ capacity = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+ /* confirm enough space for this response */
+ if (capacity < length) {
+ *rcode = RCODE_CONFLICT_ERROR;
goto end;
}
- seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+ /* copy to ring buffer */
+ while (length > 0) {
+ till_end = snd_efw_resp_buf_size -
+ (unsigned int)(efw->push_ptr - efw->resp_buf);
+ till_end = min_t(unsigned int, length, till_end);
+
+ memcpy(efw->push_ptr, data, till_end);
+
+ efw->push_ptr += till_end;
+ if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ efw->push_ptr = efw->resp_buf;
+
+ length -= till_end;
+ data += till_end;
+ }
+
+ /* for hwdep */
+ efw->resp_queues++;
+ wake_up(&efw->hwdep_wait);
+
+ *rcode = RCODE_COMPLETE;
+end:
+ spin_unlock_irq(&efw->lock);
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode)
+{
+ struct fw_device *device;
+ struct snd_efw *efw;
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ efw = instances[i];
+ if (efw == NULL)
+ continue;
+ device = fw_parent_device(efw->unit);
+ if ((device->card != card) ||
+ (device->generation != generation))
+ continue;
+ smp_rmb(); /* node id vs. generation */
+ if (device->node_id != source)
+ continue;
+
+ break;
+ }
+ if (i == SNDRV_CARDS)
+ goto end;
+
+ copy_resp_to_buf(efw, data, length, rcode);
+end:
+ spin_unlock_irq(&instances_lock);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+ void *data, size_t length, int *rcode, u32 seqnum)
+{
+ struct fw_device *device;
+ struct transaction_queue *t;
+ unsigned long flags;
spin_lock_irqsave(&transaction_queues_lock, flags);
list_for_each_entry(t, &transaction_queues, list) {
@@ -144,14 +218,76 @@ efw_response(struct fw_card *card, struct fw_request *request,
t->size = min_t(unsigned int, length, t->size);
memcpy(t->buf, data, t->size);
wake_up(&t->wait);
- rcode = RCODE_COMPLETE;
+ *rcode = RCODE_COMPLETE;
}
}
spin_unlock_irqrestore(&transaction_queues_lock, flags);
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ int rcode, dummy;
+ u32 seqnum;
+
+ rcode = RCODE_TYPE_ERROR;
+ if (length < sizeof(struct snd_efw_transaction)) {
+ rcode = RCODE_DATA_ERROR;
+ goto end;
+ } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+ rcode = RCODE_ADDRESS_ERROR;
+ goto end;
+ }
+
+ seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+ if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
+ handle_resp_for_kernel(card, generation, source,
+ data, length, &rcode, seqnum);
+ if (snd_efw_resp_buf_debug)
+ handle_resp_for_user(card, generation, source,
+ data, length, &dummy);
+ } else {
+ handle_resp_for_user(card, generation, source,
+ data, length, &rcode);
+ }
end:
fw_send_response(card, request, rcode);
}
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != NULL)
+ continue;
+ instances[i] = efw;
+ break;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+ unsigned int i;
+
+ spin_lock_irq(&instances_lock);
+
+ for (i = 0; i < SNDRV_CARDS; i++) {
+ if (instances[i] != efw)
+ continue;
+ instances[i] = NULL;
+ }
+
+ spin_unlock_irq(&instances_lock);
+}
+
void snd_efw_transaction_bus_reset(struct fw_unit *unit)
{
struct transaction_queue *t;