1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#include "./fireworks.h"
33
34#define MEMORY_SPACE_EFW_COMMAND 0xecc000000000ULL
35#define MEMORY_SPACE_EFW_RESPONSE 0xecc080000000ULL
36
37#define ERROR_RETRIES 3
38#define ERROR_DELAY_MS 5
39#define EFC_TIMEOUT_MS 125
40
41static DEFINE_SPINLOCK(instances_lock);
42static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
43
44static DEFINE_SPINLOCK(transaction_queues_lock);
45static LIST_HEAD(transaction_queues);
46
47enum transaction_queue_state {
48 STATE_PENDING,
49 STATE_BUS_RESET,
50 STATE_COMPLETE
51};
52
53struct transaction_queue {
54 struct list_head list;
55 struct fw_unit *unit;
56 void *buf;
57 unsigned int size;
58 u32 seqnum;
59 enum transaction_queue_state state;
60 wait_queue_head_t wait;
61};
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
71int snd_efw_transaction_run(struct fw_unit *unit,
72 const void *cmd, unsigned int cmd_size,
73 void *resp, unsigned int resp_size)
74{
75 struct transaction_queue t;
76 unsigned int tries;
77 int ret;
78
79 t.unit = unit;
80 t.buf = resp;
81 t.size = resp_size;
82 t.seqnum = be32_to_cpu(((struct snd_efw_transaction *)cmd)->seqnum) + 1;
83 t.state = STATE_PENDING;
84 init_waitqueue_head(&t.wait);
85
86 spin_lock_irq(&transaction_queues_lock);
87 list_add_tail(&t.list, &transaction_queues);
88 spin_unlock_irq(&transaction_queues_lock);
89
90 tries = 0;
91 do {
92 ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
93 if (ret < 0)
94 break;
95
96 wait_event_timeout(t.wait, t.state != STATE_PENDING,
97 msecs_to_jiffies(EFC_TIMEOUT_MS));
98
99 if (t.state == STATE_COMPLETE) {
100 ret = t.size;
101 break;
102 } else if (t.state == STATE_BUS_RESET) {
103 msleep(ERROR_DELAY_MS);
104 } else if (++tries >= ERROR_RETRIES) {
105 dev_err(&t.unit->device, "EFW transaction timed out\n");
106 ret = -EIO;
107 break;
108 }
109 } while (1);
110
111 spin_lock_irq(&transaction_queues_lock);
112 list_del(&t.list);
113 spin_unlock_irq(&transaction_queues_lock);
114
115 return ret;
116}
117
118static void
119copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
120{
121 size_t capacity, till_end;
122 struct snd_efw_transaction *t;
123
124 spin_lock_irq(&efw->lock);
125
126 t = (struct snd_efw_transaction *)data;
127 length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
128
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
136 if (capacity < length) {
137 *rcode = RCODE_CONFLICT_ERROR;
138 goto end;
139 }
140
141
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 -= snd_efw_resp_buf_size;
152
153 length -= till_end;
154 data += till_end;
155 }
156
157
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();
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;
205
206 spin_lock_irqsave(&transaction_queues_lock, flags);
207 list_for_each_entry(t, &transaction_queues, list) {
208 device = fw_parent_device(t->unit);
209 if ((device->card != card) ||
210 (device->generation != generation))
211 continue;
212 smp_rmb();
213 if (device->node_id != source)
214 continue;
215
216 if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
217 t->state = STATE_COMPLETE;
218 t->size = min_t(unsigned int, length, t->size);
219 memcpy(t->buf, data, t->size);
220 wake_up(&t->wait);
221 *rcode = RCODE_COMPLETE;
222 }
223 }
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 }
256end:
257 fw_send_response(card, request, rcode);
258}
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
291void snd_efw_transaction_bus_reset(struct fw_unit *unit)
292{
293 struct transaction_queue *t;
294
295 spin_lock_irq(&transaction_queues_lock);
296 list_for_each_entry(t, &transaction_queues, list) {
297 if ((t->unit == unit) &&
298 (t->state == STATE_PENDING)) {
299 t->state = STATE_BUS_RESET;
300 wake_up(&t->wait);
301 }
302 }
303 spin_unlock_irq(&transaction_queues_lock);
304}
305
306static struct fw_address_handler resp_register_handler = {
307 .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
308 .address_callback = efw_response
309};
310
311int snd_efw_transaction_register(void)
312{
313 static const struct fw_address_region resp_register_region = {
314 .start = MEMORY_SPACE_EFW_RESPONSE,
315 .end = MEMORY_SPACE_EFW_RESPONSE +
316 SND_EFW_RESPONSE_MAXIMUM_BYTES
317 };
318 return fw_core_add_address_handler(&resp_register_handler,
319 &resp_register_region);
320}
321
322void snd_efw_transaction_unregister(void)
323{
324 WARN_ON(!list_empty(&transaction_queues));
325 fw_core_remove_address_handler(&resp_register_handler);
326}
327