diff options
Diffstat (limited to 'sound/firewire/fireworks')
-rw-r--r-- | sound/firewire/fireworks/fireworks.c | 21 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks.h | 22 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_command.c | 8 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_hwdep.c | 131 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_proc.c | 18 | ||||
-rw-r--r-- | sound/firewire/fireworks/fireworks_transaction.c | 176 |
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"); | |||
24 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; | 24 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; |
25 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; | 25 | static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; |
26 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; | 26 | static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; |
27 | unsigned int snd_efw_resp_buf_size = 1024; | ||
28 | bool snd_efw_resp_buf_debug = false; | ||
27 | 29 | ||
28 | module_param_array(index, int, NULL, 0444); | 30 | module_param_array(index, int, NULL, 0444); |
29 | MODULE_PARM_DESC(index, "card index"); | 31 | MODULE_PARM_DESC(index, "card index"); |
@@ -31,6 +33,11 @@ module_param_array(id, charp, NULL, 0444); | |||
31 | MODULE_PARM_DESC(id, "ID string"); | 33 | MODULE_PARM_DESC(id, "ID string"); |
32 | module_param_array(enable, bool, NULL, 0444); | 34 | module_param_array(enable, bool, NULL, 0444); |
33 | MODULE_PARM_DESC(enable, "enable Fireworks sound card"); | 35 | MODULE_PARM_DESC(enable, "enable Fireworks sound card"); |
36 | module_param_named(resp_buf_size, snd_efw_resp_buf_size, uint, 0444); | ||
37 | MODULE_PARM_DESC(resp_buf_size, | ||
38 | "response buffer size (max 4096, default 1024)"); | ||
39 | module_param_named(resp_buf_debug, snd_efw_resp_buf_debug, bool, 0444); | ||
40 | MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer"); | ||
34 | 41 | ||
35 | static DEFINE_MUTEX(devices_mutex); | 42 | static DEFINE_MUTEX(devices_mutex); |
36 | static DECLARE_BITMAP(devices_used, SNDRV_CARDS); | 43 | static 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 | ||
187 | static int | 195 | static 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; |
258 | error: | 277 | error: |
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 | ||
52 | extern unsigned int snd_efw_resp_buf_size; | ||
53 | extern bool snd_efw_resp_buf_debug; | ||
54 | |||
52 | struct snd_efw_phys_grp { | 55 | struct 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 | ||
102 | struct 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 | |||
111 | int snd_efw_transaction_cmd(struct fw_unit *unit, | ||
112 | const void *cmd, unsigned int size); | ||
111 | int snd_efw_transaction_run(struct fw_unit *unit, | 113 | int 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); |
114 | int snd_efw_transaction_register(void); | 116 | int snd_efw_transaction_register(void); |
115 | void snd_efw_transaction_unregister(void); | 117 | void snd_efw_transaction_unregister(void); |
116 | void snd_efw_transaction_bus_reset(struct fw_unit *unit); | 118 | void snd_efw_transaction_bus_reset(struct fw_unit *unit); |
119 | void snd_efw_transaction_add_instance(struct snd_efw *efw); | ||
120 | void snd_efw_transaction_remove_instance(struct snd_efw *efw); | ||
117 | 121 | ||
118 | struct snd_efw_hwinfo { | 122 | struct 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 */ |
28 | struct efc_clock { | 29 | struct 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 | ||
19 | static long | 22 | static long |
20 | hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, | 23 | hwdep_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 | |||
75 | static long | ||
76 | hwdep_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 | |||
95 | static long | ||
96 | hwdep_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 | ||
124 | static long | ||
125 | hwdep_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; | ||
149 | end: | ||
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 | ||
74 | static int | 172 | static int |
@@ -174,6 +272,7 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file, | |||
174 | 272 | ||
175 | static const struct snd_hwdep_ops hwdep_ops = { | 273 | static 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 | ||
178 | static void | 178 | static void |
179 | proc_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 | |||
195 | static void | ||
179 | add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, | 196 | add_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 | ||
41 | static DEFINE_SPINLOCK(instances_lock); | ||
42 | static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR; | ||
43 | |||
41 | static DEFINE_SPINLOCK(transaction_queues_lock); | 44 | static DEFINE_SPINLOCK(transaction_queues_lock); |
42 | static LIST_HEAD(transaction_queues); | 45 | static 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 | ||
63 | int 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 | |||
60 | int snd_efw_transaction_run(struct fw_unit *unit, | 71 | int 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 | ||
109 | static void | 118 | static void |
110 | efw_response(struct fw_card *card, struct fw_request *request, | 119 | copy_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; | ||
162 | end: | ||
163 | spin_unlock_irq(&efw->lock); | ||
164 | } | ||
165 | |||
166 | static void | ||
167 | handle_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); | ||
194 | end: | ||
195 | spin_unlock_irq(&instances_lock); | ||
196 | } | ||
197 | |||
198 | static void | ||
199 | handle_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 | |||
227 | static void | ||
228 | efw_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 | } | ||
151 | end: | 256 | end: |
152 | fw_send_response(card, request, rcode); | 257 | fw_send_response(card, request, rcode); |
153 | } | 258 | } |
154 | 259 | ||
260 | void 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 | |||
276 | void 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 | |||
155 | void snd_efw_transaction_bus_reset(struct fw_unit *unit) | 291 | void snd_efw_transaction_bus_reset(struct fw_unit *unit) |
156 | { | 292 | { |
157 | struct transaction_queue *t; | 293 | struct transaction_queue *t; |