|
@@ -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;
|