aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>2014-04-25 09:45:13 -0400
committerTakashi Iwai <tiwai@suse.de>2014-05-26 08:28:58 -0400
commit555e8a8f7f149544eb7d4aa3a6420bc4c3055638 (patch)
tree9d2e78ab890ccb42bb21f9ec4cc3e21183dd81f3 /sound
parent594ddced821dee39a548efe46d7f834bae013505 (diff)
ALSA: fireworks: Add command/response functionality into hwdep interface
This commit adds two functionality for hwdep interface, adds two parameters for this driver, add a node for proc interface. To receive responses from devices, this driver already allocate own callback into initial memory space in host controller. This means no one can allocate its own callback to the address. So this driver must give a way for user applications to receive responses. This commit adds a functionality to receive responses via hwdep interface. The application can receive responses to read from this interface. To achieve this, this commit adds a buffer to queue responses. The default size of this buffer is 1024 bytes. This size can be changed to give preferrable size to 'resp_buf_size' parameter for this driver. The application should notice rest of space in this buffer because this driver don't push responses when this buffer has no space. Additionaly, this commit adds a functionality to transmit commands via hwdep interface. The application can transmit commands to write into this interface. I note that the application can transmit one command at once, but can receive as many responses as possible untill the user-buffer is full. When using these interfaces, the application must keep maximum number of sequence number in command within the number in firewire.h because this driver uses this number to distinguish the response is against the command by the application or this driver. Usually responses against commands which the application transmits are pushed into this buffer. But to enable 'resp_buf_debug' parameter for this driver, all responses are pushed into the buffer. When using this mode, I reccomend to expand the size of buffer. Finally this commit adds a new node into proc interface to output status of the buffer. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/firewire/fireworks/fireworks.c21
-rw-r--r--sound/firewire/fireworks/fireworks.h22
-rw-r--r--sound/firewire/fireworks/fireworks_command.c8
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c131
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c18
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c176
6 files changed, 328 insertions, 48 deletions
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index f8d06f56618f..a354d26afe9e 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -24,6 +24,8 @@ MODULE_LICENSE("GPL v2");
24static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 24static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
25static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 25static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
26static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 26static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
27unsigned int snd_efw_resp_buf_size = 1024;
28bool snd_efw_resp_buf_debug = false;
27 29
28module_param_array(index, int, NULL, 0444); 30module_param_array(index, int, NULL, 0444);
29MODULE_PARM_DESC(index, "card index"); 31MODULE_PARM_DESC(index, "card index");
@@ -31,6 +33,11 @@ module_param_array(id, charp, NULL, 0444);
31MODULE_PARM_DESC(id, "ID string"); 33MODULE_PARM_DESC(id, "ID string");
32module_param_array(enable, bool, NULL, 0444); 34module_param_array(enable, bool, NULL, 0444);
33MODULE_PARM_DESC(enable, "enable Fireworks sound card"); 35MODULE_PARM_DESC(enable, "enable Fireworks sound card");
36module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444);
37MODULE_PARM_DESC(resp_buf_size,
38 "response buffer size (max 4096, default 1024)");
39module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444);
40MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
34 41
35static DEFINE_MUTEX(devices_mutex); 42static DEFINE_MUTEX(devices_mutex);
36static DECLARE_BITMAP(devices_used, SNDRV_CARDS); 43static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
@@ -182,6 +189,7 @@ efw_card_free(struct snd_card *card)
182 } 189 }
183 190
184 mutex_destroy(&efw->mutex); 191 mutex_destroy(&efw->mutex);
192 kfree(efw->resp_buf);
185} 193}
186 194
187static int 195static int
@@ -219,6 +227,17 @@ efw_probe(struct fw_unit *unit,
219 spin_lock_init(&efw->lock); 227 spin_lock_init(&efw->lock);
220 init_waitqueue_head(&efw->hwdep_wait); 228 init_waitqueue_head(&efw->hwdep_wait);
221 229
230 /* prepare response buffer */
231 snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
232 SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U);
233 efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL);
234 if (efw->resp_buf == NULL) {
235 err = -ENOMEM;
236 goto error;
237 }
238 efw->pull_ptr = efw->push_ptr = efw->resp_buf;
239 snd_efw_transaction_add_instance(efw);
240
222 err = get_hardware_info(efw); 241 err = get_hardware_info(efw);
223 if (err < 0) 242 if (err < 0)
224 goto error; 243 goto error;
@@ -256,6 +275,7 @@ end:
256 mutex_unlock(&devices_mutex); 275 mutex_unlock(&devices_mutex);
257 return err; 276 return err;
258error: 277error:
278 snd_efw_transaction_remove_instance(efw);
259 mutex_unlock(&devices_mutex); 279 mutex_unlock(&devices_mutex);
260 snd_card_free(card); 280 snd_card_free(card);
261 return err; 281 return err;
@@ -274,6 +294,7 @@ static void efw_remove(struct fw_unit *unit)
274 struct snd_efw *efw = dev_get_drvdata(&unit->device); 294 struct snd_efw *efw = dev_get_drvdata(&unit->device);
275 295
276 snd_efw_stream_destroy_duplex(efw); 296 snd_efw_stream_destroy_duplex(efw);
297 snd_efw_transaction_remove_instance(efw);
277 298
278 snd_card_disconnect(efw->card); 299 snd_card_disconnect(efw->card);
279 snd_card_free_when_closed(efw->card); 300 snd_card_free_when_closed(efw->card);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 4aaf2dce5ec8..494195bd3296 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -49,6 +49,9 @@
49 */ 49 */
50#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U 50#define SND_EFW_RESPONSE_MAXIMUM_BYTES 0x200U
51 51
52extern unsigned int snd_efw_resp_buf_size;
53extern bool snd_efw_resp_buf_debug;
54
52struct snd_efw_phys_grp { 55struct snd_efw_phys_grp {
53 u8 type; /* see enum snd_efw_grp_type */ 56 u8 type; /* see enum snd_efw_grp_type */
54 u8 count; 57 u8 count;
@@ -97,23 +100,24 @@ struct snd_efw {
97 int dev_lock_count; 100 int dev_lock_count;
98 bool dev_lock_changed; 101 bool dev_lock_changed;
99 wait_queue_head_t hwdep_wait; 102 wait_queue_head_t hwdep_wait;
100};
101 103
102struct snd_efw_transaction { 104 /* response queue */
103 __be32 length; 105 u8 *resp_buf;
104 __be32 version; 106 u8 *pull_ptr;
105 __be32 seqnum; 107 u8 *push_ptr;
106 __be32 category; 108 unsigned int resp_queues;
107 __be32 command;
108 __be32 status;
109 __be32 params[0];
110}; 109};
110
111int snd_efw_transaction_cmd(struct fw_unit *unit,
112 const void *cmd, unsigned int size);
111int snd_efw_transaction_run(struct fw_unit *unit, 113int snd_efw_transaction_run(struct fw_unit *unit,
112 const void *cmd, unsigned int cmd_size, 114 const void *cmd, unsigned int cmd_size,
113 void *resp, unsigned int resp_size); 115 void *resp, unsigned int resp_size);
114int snd_efw_transaction_register(void); 116int snd_efw_transaction_register(void);
115void snd_efw_transaction_unregister(void); 117void snd_efw_transaction_unregister(void);
116void snd_efw_transaction_bus_reset(struct fw_unit *unit); 118void snd_efw_transaction_bus_reset(struct fw_unit *unit);
119void snd_efw_transaction_add_instance(struct snd_efw *efw);
120void snd_efw_transaction_remove_instance(struct snd_efw *efw);
117 121
118struct snd_efw_hwinfo { 122struct snd_efw_hwinfo {
119 u32 flags; 123 u32 flags;
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
index d5ea7051ad0c..166f80584c2a 100644
--- a/sound/firewire/fireworks/fireworks_command.c
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -22,7 +22,8 @@
22 * Information commands. But this module don't use them. 22 * Information commands. But this module don't use them.
23 */ 23 */
24 24
25#define EFW_TRANSACTION_SEQNUM_MAX ((u32)~0) 25#define KERNEL_SEQNUM_MIN (SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 2)
26#define KERNEL_SEQNUM_MAX ((u32)~0)
26 27
27/* for clock source and sampling rate */ 28/* for clock source and sampling rate */
28struct efc_clock { 29struct efc_clock {
@@ -120,8 +121,9 @@ efw_transaction(struct snd_efw *efw, unsigned int category,
120 121
121 /* to keep consistency of sequence number */ 122 /* to keep consistency of sequence number */
122 spin_lock(&efw->lock); 123 spin_lock(&efw->lock);
123 if (efw->seqnum + 2 >= EFW_TRANSACTION_SEQNUM_MAX) 124 if ((efw->seqnum < KERNEL_SEQNUM_MIN) ||
124 efw->seqnum = 0; 125 (efw->seqnum >= KERNEL_SEQNUM_MAX - 2))
126 efw->seqnum = KERNEL_SEQNUM_MIN;
125 else 127 else
126 efw->seqnum += 2; 128 efw->seqnum += 2;
127 seqnum = efw->seqnum; 129 seqnum = efw->seqnum;
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
index 1cf491dc39a3..6b50a6796d22 100644
--- a/sound/firewire/fireworks/fireworks_hwdep.c
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -7,26 +7,101 @@
7 */ 7 */
8 8
9/* 9/*
10 * This codes have three functionalities. 10 * This codes have five functionalities.
11 * 11 *
12 * 1.get information about firewire node 12 * 1.get information about firewire node
13 * 2.get notification about starting/stopping stream 13 * 2.get notification about starting/stopping stream
14 * 3.lock/unlock streaming 14 * 3.lock/unlock streaming
15 * 4.transmit command of EFW transaction
16 * 5.receive response of EFW transaction
17 *
15 */ 18 */
16 19
17#include "fireworks.h" 20#include "fireworks.h"
18 21
19static long 22static long
20hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, 23hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
24 loff_t *offset)
25{
26 unsigned int length, till_end, type;
27 struct snd_efw_transaction *t;
28 long count = 0;
29
30 if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
31 return -ENOSPC;
32
33 /* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
34 type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
35 if (copy_to_user(buf, &type, sizeof(type)))
36 return -EFAULT;
37 remained -= sizeof(type);
38 buf += sizeof(type);
39
40 /* write into buffer as many responses as possible */
41 while (efw->resp_queues > 0) {
42 t = (struct snd_efw_transaction *)(efw->pull_ptr);
43 length = be32_to_cpu(t->length) * sizeof(__be32);
44
45 /* confirm enough space for this response */
46 if (remained < length)
47 break;
48
49 /* copy from ring buffer to user buffer */
50 while (length > 0) {
51 till_end = snd_efw_resp_buf_size -
52 (unsigned int)(efw->pull_ptr - efw->resp_buf);
53 till_end = min_t(unsigned int, length, till_end);
54
55 if (copy_to_user(buf, efw->pull_ptr, till_end))
56 return -EFAULT;
57
58 efw->pull_ptr += till_end;
59 if (efw->pull_ptr >= efw->resp_buf +
60 snd_efw_resp_buf_size)
61 efw->pull_ptr = efw->resp_buf;
62
63 length -= till_end;
64 buf += till_end;
65 count += till_end;
66 remained -= till_end;
67 }
68
69 efw->resp_queues--;
70 }
71
72 return count;
73}
74
75static long
76hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
77 loff_t *offset)
78{
79 union snd_firewire_event event;
80
81 memset(&event, 0, sizeof(event));
82
83 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
84 event.lock_status.status = (efw->dev_lock_count > 0);
85 efw->dev_lock_changed = false;
86
87 count = min_t(long, count, sizeof(event.lock_status));
88
89 if (copy_to_user(buf, &event, count))
90 return -EFAULT;
91
92 return count;
93}
94
95static long
96hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
21 loff_t *offset) 97 loff_t *offset)
22{ 98{
23 struct snd_efw *efw = hwdep->private_data; 99 struct snd_efw *efw = hwdep->private_data;
24 DEFINE_WAIT(wait); 100 DEFINE_WAIT(wait);
25 union snd_firewire_event event;
26 101
27 spin_lock_irq(&efw->lock); 102 spin_lock_irq(&efw->lock);
28 103
29 while (!efw->dev_lock_changed) { 104 while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
30 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE); 105 prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
31 spin_unlock_irq(&efw->lock); 106 spin_unlock_irq(&efw->lock);
32 schedule(); 107 schedule();
@@ -36,20 +111,43 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
36 spin_lock_irq(&efw->lock); 111 spin_lock_irq(&efw->lock);
37 } 112 }
38 113
39 memset(&event, 0, sizeof(event)); 114 if (efw->dev_lock_changed)
40 if (efw->dev_lock_changed) { 115 count = hwdep_read_locked(efw, buf, count, offset);
41 event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; 116 else if (efw->resp_queues > 0)
42 event.lock_status.status = (efw->dev_lock_count > 0); 117 count = hwdep_read_resp_buf(efw, buf, count, offset);
43 efw->dev_lock_changed = false;
44
45 count = min_t(long, count, sizeof(event.lock_status));
46 }
47 118
48 spin_unlock_irq(&efw->lock); 119 spin_unlock_irq(&efw->lock);
49 120
50 if (copy_to_user(buf, &event, count)) 121 return count;
51 return -EFAULT; 122}
52 123
124static long
125hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
126 loff_t *offset)
127{
128 struct snd_efw *efw = hwdep->private_data;
129 u32 seqnum;
130 u8 *buf;
131
132 if (count < sizeof(struct snd_efw_transaction) ||
133 SND_EFW_RESPONSE_MAXIMUM_BYTES < count)
134 return -EINVAL;
135
136 buf = memdup_user(data, count);
137 if (IS_ERR(buf))
138 return PTR_ERR(data);
139
140 /* check seqnum is not for kernel-land */
141 seqnum = be32_to_cpu(((struct snd_efw_transaction *)buf)->seqnum);
142 if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX) {
143 count = -EINVAL;
144 goto end;
145 }
146
147 if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
148 count = -EIO;
149end:
150 kfree(buf);
53 return count; 151 return count;
54} 152}
55 153
@@ -62,13 +160,13 @@ hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
62 poll_wait(file, &efw->hwdep_wait, wait); 160 poll_wait(file, &efw->hwdep_wait, wait);
63 161
64 spin_lock_irq(&efw->lock); 162 spin_lock_irq(&efw->lock);
65 if (efw->dev_lock_changed) 163 if (efw->dev_lock_changed || (efw->resp_queues > 0))
66 events = POLLIN | POLLRDNORM; 164 events = POLLIN | POLLRDNORM;
67 else 165 else
68 events = 0; 166 events = 0;
69 spin_unlock_irq(&efw->lock); 167 spin_unlock_irq(&efw->lock);
70 168
71 return events; 169 return events | POLLOUT;
72} 170}
73 171
74static int 172static int
@@ -174,6 +272,7 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
174 272
175static const struct snd_hwdep_ops hwdep_ops = { 273static const struct snd_hwdep_ops hwdep_ops = {
176 .read = hwdep_read, 274 .read = hwdep_read,
275 .write = hwdep_write,
177 .release = hwdep_release, 276 .release = hwdep_release,
178 .poll = hwdep_poll, 277 .poll = hwdep_poll,
179 .ioctl = hwdep_ioctl, 278 .ioctl = hwdep_ioctl,
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
index 631c91f64db4..f29d4aaf56a1 100644
--- a/sound/firewire/fireworks/fireworks_proc.c
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -176,6 +176,23 @@ end:
176} 176}
177 177
178static void 178static void
179proc_read_queues_state(struct snd_info_entry *entry,
180 struct snd_info_buffer *buffer)
181{
182 struct snd_efw *efw = entry->private_data;
183 unsigned int consumed;
184
185 if (efw->pull_ptr > efw->push_ptr)
186 consumed = snd_efw_resp_buf_size -
187 (unsigned int)(efw->pull_ptr - efw->push_ptr);
188 else
189 consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
190
191 snd_iprintf(buffer, "%d %d/%d\n",
192 efw->resp_queues, consumed, snd_efw_resp_buf_size);
193}
194
195static void
179add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, 196add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
180 void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b)) 197 void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
181{ 198{
@@ -211,4 +228,5 @@ void snd_efw_proc_init(struct snd_efw *efw)
211 add_node(efw, root, "clock", proc_read_clock); 228 add_node(efw, root, "clock", proc_read_clock);
212 add_node(efw, root, "firmware", proc_read_hwinfo); 229 add_node(efw, root, "firmware", proc_read_hwinfo);
213 add_node(efw, root, "meters", proc_read_phys_meters); 230 add_node(efw, root, "meters", proc_read_phys_meters);
231 add_node(efw, root, "queues", proc_read_queues_state);
214} 232}
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 @@
38#define ERROR_DELAY_MS 5 38#define ERROR_DELAY_MS 5
39#define EFC_TIMEOUT_MS 125 39#define EFC_TIMEOUT_MS 125
40 40
41static DEFINE_SPINLOCK(instances_lock);
42static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
43
41static DEFINE_SPINLOCK(transaction_queues_lock); 44static DEFINE_SPINLOCK(transaction_queues_lock);
42static LIST_HEAD(transaction_queues); 45static LIST_HEAD(transaction_queues);
43 46
@@ -57,6 +60,14 @@ struct transaction_queue {
57 wait_queue_head_t wait; 60 wait_queue_head_t wait;
58}; 61};
59 62
63int snd_efw_transaction_cmd(struct fw_unit *unit,
64 const void *cmd, unsigned int size)
65{
66 return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
67 MEMORY_SPACE_EFW_COMMAND,
68 (void *)cmd, size, 0);
69}
70
60int snd_efw_transaction_run(struct fw_unit *unit, 71int snd_efw_transaction_run(struct fw_unit *unit,
61 const void *cmd, unsigned int cmd_size, 72 const void *cmd, unsigned int cmd_size,
62 void *resp, unsigned int resp_size) 73 void *resp, unsigned int resp_size)
@@ -78,9 +89,7 @@ int snd_efw_transaction_run(struct fw_unit *unit,
78 89
79 tries = 0; 90 tries = 0;
80 do { 91 do {
81 ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST, 92 ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
82 MEMORY_SPACE_EFW_COMMAND,
83 (void *)cmd, cmd_size, 0);
84 if (ret < 0) 93 if (ret < 0)
85 break; 94 break;
86 95
@@ -107,27 +116,92 @@ int snd_efw_transaction_run(struct fw_unit *unit,
107} 116}
108 117
109static void 118static void
110efw_response(struct fw_card *card, struct fw_request *request, 119copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
111 int tcode, int destination, int source,
112 int generation, unsigned long long offset,
113 void *data, size_t length, void *callback_data)
114{ 120{
115 struct fw_device *device; 121 size_t capacity, till_end;
116 struct transaction_queue *t; 122 struct snd_efw_transaction *t;
117 unsigned long flags;
118 int rcode;
119 u32 seqnum;
120 123
121 rcode = RCODE_TYPE_ERROR; 124 spin_lock_irq(&efw->lock);
122 if (length < sizeof(struct snd_efw_transaction)) { 125
123 rcode = RCODE_DATA_ERROR; 126 t = (struct snd_efw_transaction *)data;
124 goto end; 127 length = min_t(size_t, t->length * sizeof(t->length), length);
125 } else if (offset != MEMORY_SPACE_EFW_RESPONSE) { 128
126 rcode = RCODE_ADDRESS_ERROR; 129 if (efw->push_ptr < efw->pull_ptr)
130 capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
131 else
132 capacity = snd_efw_resp_buf_size -
133 (unsigned int)(efw->push_ptr - efw->pull_ptr);
134
135 /* confirm enough space for this response */
136 if (capacity < length) {
137 *rcode = RCODE_CONFLICT_ERROR;
127 goto end; 138 goto end;
128 } 139 }
129 140
130 seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum); 141 /* copy to ring buffer */
142 while (length > 0) {
143 till_end = snd_efw_resp_buf_size -
144 (unsigned int)(efw->push_ptr - efw->resp_buf);
145 till_end = min_t(unsigned int, length, till_end);
146
147 memcpy(efw->push_ptr, data, till_end);
148
149 efw->push_ptr += till_end;
150 if (efw->push_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
151 efw->push_ptr = efw->resp_buf;
152
153 length -= till_end;
154 data += till_end;
155 }
156
157 /* for hwdep */
158 efw->resp_queues++;
159 wake_up(&efw->hwdep_wait);
160
161 *rcode = RCODE_COMPLETE;
162end:
163 spin_unlock_irq(&efw->lock);
164}
165
166static void
167handle_resp_for_user(struct fw_card *card, int generation, int source,
168 void *data, size_t length, int *rcode)
169{
170 struct fw_device *device;
171 struct snd_efw *efw;
172 unsigned int i;
173
174 spin_lock_irq(&instances_lock);
175
176 for (i = 0; i < SNDRV_CARDS; i++) {
177 efw = instances[i];
178 if (efw == NULL)
179 continue;
180 device = fw_parent_device(efw->unit);
181 if ((device->card != card) ||
182 (device->generation != generation))
183 continue;
184 smp_rmb(); /* node id vs. generation */
185 if (device->node_id != source)
186 continue;
187
188 break;
189 }
190 if (i == SNDRV_CARDS)
191 goto end;
192
193 copy_resp_to_buf(efw, data, length, rcode);
194end:
195 spin_unlock_irq(&instances_lock);
196}
197
198static void
199handle_resp_for_kernel(struct fw_card *card, int generation, int source,
200 void *data, size_t length, int *rcode, u32 seqnum)
201{
202 struct fw_device *device;
203 struct transaction_queue *t;
204 unsigned long flags;
131 205
132 spin_lock_irqsave(&transaction_queues_lock, flags); 206 spin_lock_irqsave(&transaction_queues_lock, flags);
133 list_for_each_entry(t, &transaction_queues, list) { 207 list_for_each_entry(t, &transaction_queues, list) {
@@ -144,14 +218,76 @@ efw_response(struct fw_card *card, struct fw_request *request,
144 t->size = min_t(unsigned int, length, t->size); 218 t->size = min_t(unsigned int, length, t->size);
145 memcpy(t->buf, data, t->size); 219 memcpy(t->buf, data, t->size);
146 wake_up(&t->wait); 220 wake_up(&t->wait);
147 rcode = RCODE_COMPLETE; 221 *rcode = RCODE_COMPLETE;
148 } 222 }
149 } 223 }
150 spin_unlock_irqrestore(&transaction_queues_lock, flags); 224 spin_unlock_irqrestore(&transaction_queues_lock, flags);
225}
226
227static void
228efw_response(struct fw_card *card, struct fw_request *request,
229 int tcode, int destination, int source,
230 int generation, unsigned long long offset,
231 void *data, size_t length, void *callback_data)
232{
233 int rcode, dummy;
234 u32 seqnum;
235
236 rcode = RCODE_TYPE_ERROR;
237 if (length < sizeof(struct snd_efw_transaction)) {
238 rcode = RCODE_DATA_ERROR;
239 goto end;
240 } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
241 rcode = RCODE_ADDRESS_ERROR;
242 goto end;
243 }
244
245 seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
246 if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
247 handle_resp_for_kernel(card, generation, source,
248 data, length, &rcode, seqnum);
249 if (snd_efw_resp_buf_debug)
250 handle_resp_for_user(card, generation, source,
251 data, length, &dummy);
252 } else {
253 handle_resp_for_user(card, generation, source,
254 data, length, &rcode);
255 }
151end: 256end:
152 fw_send_response(card, request, rcode); 257 fw_send_response(card, request, rcode);
153} 258}
154 259
260void snd_efw_transaction_add_instance(struct snd_efw *efw)
261{
262 unsigned int i;
263
264 spin_lock_irq(&instances_lock);
265
266 for (i = 0; i < SNDRV_CARDS; i++) {
267 if (instances[i] != NULL)
268 continue;
269 instances[i] = efw;
270 break;
271 }
272
273 spin_unlock_irq(&instances_lock);
274}
275
276void snd_efw_transaction_remove_instance(struct snd_efw *efw)
277{
278 unsigned int i;
279
280 spin_lock_irq(&instances_lock);
281
282 for (i = 0; i < SNDRV_CARDS; i++) {
283 if (instances[i] != efw)
284 continue;
285 instances[i] = NULL;
286 }
287
288 spin_unlock_irq(&instances_lock);
289}
290
155void snd_efw_transaction_bus_reset(struct fw_unit *unit) 291void snd_efw_transaction_bus_reset(struct fw_unit *unit)
156{ 292{
157 struct transaction_queue *t; 293 struct transaction_queue *t;