~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/firewire/fireworks/fireworks_transaction.c

Version: ~ [ linux-5.5-rc2 ] ~ [ linux-5.4.3 ] ~ [ linux-5.3.16 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.89 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.158 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.206 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.206 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.79 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * fireworks_transaction.c - a part of driver for Fireworks based devices
  3  *
  4  * Copyright (c) 2013-2014 Takashi Sakamoto
  5  *
  6  * Licensed under the terms of the GNU General Public License, version 2.
  7  */
  8 
  9 /*
 10  * Fireworks have its own transaction. The transaction can be delivered by AV/C
 11  * Vendor Specific command frame or usual asynchronous transaction. At least,
 12  * Windows driver and firmware version 5.5 or later don't use AV/C command.
 13  *
 14  * Transaction substance:
 15  *  At first, 6 data exist. Following to the data, parameters for each command
 16  *  exist. All of the parameters are 32 bit aligned to big endian.
 17  *   data[0]:   Length of transaction substance
 18  *   data[1]:   Transaction version
 19  *   data[2]:   Sequence number. This is incremented by the device
 20  *   data[3]:   Transaction category
 21  *   data[4]:   Transaction command
 22  *   data[5]:   Return value in response.
 23  *   data[6-]:  Parameters
 24  *
 25  * Transaction address:
 26  *  command:    0xecc000000000
 27  *  response:   0xecc080000000 (default)
 28  *
 29  * I note that the address for response can be changed by command. But this
 30  * module uses the default address.
 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 
 41 static DEFINE_SPINLOCK(instances_lock);
 42 static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
 43 
 44 static DEFINE_SPINLOCK(transaction_queues_lock);
 45 static LIST_HEAD(transaction_queues);
 46 
 47 enum transaction_queue_state {
 48         STATE_PENDING,
 49         STATE_BUS_RESET,
 50         STATE_COMPLETE
 51 };
 52 
 53 struct 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 
 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 
 71 int 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 
118 static void
119 copy_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         t = (struct snd_efw_transaction *)data;
125         length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
126 
127         spin_lock_irq(&efw->lock);
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         /* confirm enough space for this response */
136         if (capacity < length) {
137                 *rcode = RCODE_CONFLICT_ERROR;
138                 goto end;
139         }
140 
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 -= snd_efw_resp_buf_size;
152 
153                 length -= till_end;
154                 data += till_end;
155         }
156 
157         /* for hwdep */
158         wake_up(&efw->hwdep_wait);
159 
160         *rcode = RCODE_COMPLETE;
161 end:
162         spin_unlock_irq(&efw->lock);
163 }
164 
165 static void
166 handle_resp_for_user(struct fw_card *card, int generation, int source,
167                      void *data, size_t length, int *rcode)
168 {
169         struct fw_device *device;
170         struct snd_efw *efw;
171         unsigned int i;
172 
173         spin_lock_irq(&instances_lock);
174 
175         for (i = 0; i < SNDRV_CARDS; i++) {
176                 efw = instances[i];
177                 if (efw == NULL)
178                         continue;
179                 device = fw_parent_device(efw->unit);
180                 if ((device->card != card) ||
181                     (device->generation != generation))
182                         continue;
183                 smp_rmb();      /* node id vs. generation */
184                 if (device->node_id != source)
185                         continue;
186 
187                 break;
188         }
189         if (i == SNDRV_CARDS)
190                 goto end;
191 
192         copy_resp_to_buf(efw, data, length, rcode);
193 end:
194         spin_unlock_irq(&instances_lock);
195 }
196 
197 static void
198 handle_resp_for_kernel(struct fw_card *card, int generation, int source,
199                        void *data, size_t length, int *rcode, u32 seqnum)
200 {
201         struct fw_device *device;
202         struct transaction_queue *t;
203         unsigned long flags;
204 
205         spin_lock_irqsave(&transaction_queues_lock, flags);
206         list_for_each_entry(t, &transaction_queues, list) {
207                 device = fw_parent_device(t->unit);
208                 if ((device->card != card) ||
209                     (device->generation != generation))
210                         continue;
211                 smp_rmb();      /* node_id vs. generation */
212                 if (device->node_id != source)
213                         continue;
214 
215                 if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
216                         t->state = STATE_COMPLETE;
217                         t->size = min_t(unsigned int, length, t->size);
218                         memcpy(t->buf, data, t->size);
219                         wake_up(&t->wait);
220                         *rcode = RCODE_COMPLETE;
221                 }
222         }
223         spin_unlock_irqrestore(&transaction_queues_lock, flags);
224 }
225 
226 static void
227 efw_response(struct fw_card *card, struct fw_request *request,
228              int tcode, int destination, int source,
229              int generation, unsigned long long offset,
230              void *data, size_t length, void *callback_data)
231 {
232         int rcode, dummy;
233         u32 seqnum;
234 
235         rcode = RCODE_TYPE_ERROR;
236         if (length < sizeof(struct snd_efw_transaction)) {
237                 rcode = RCODE_DATA_ERROR;
238                 goto end;
239         } else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
240                 rcode = RCODE_ADDRESS_ERROR;
241                 goto end;
242         }
243 
244         seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
245         if (seqnum > SND_EFW_TRANSACTION_USER_SEQNUM_MAX + 1) {
246                 handle_resp_for_kernel(card, generation, source,
247                                        data, length, &rcode, seqnum);
248                 if (snd_efw_resp_buf_debug)
249                         handle_resp_for_user(card, generation, source,
250                                              data, length, &dummy);
251         } else {
252                 handle_resp_for_user(card, generation, source,
253                                      data, length, &rcode);
254         }
255 end:
256         fw_send_response(card, request, rcode);
257 }
258 
259 void snd_efw_transaction_add_instance(struct snd_efw *efw)
260 {
261         unsigned int i;
262 
263         spin_lock_irq(&instances_lock);
264 
265         for (i = 0; i < SNDRV_CARDS; i++) {
266                 if (instances[i] != NULL)
267                         continue;
268                 instances[i] = efw;
269                 break;
270         }
271 
272         spin_unlock_irq(&instances_lock);
273 }
274 
275 void snd_efw_transaction_remove_instance(struct snd_efw *efw)
276 {
277         unsigned int i;
278 
279         spin_lock_irq(&instances_lock);
280 
281         for (i = 0; i < SNDRV_CARDS; i++) {
282                 if (instances[i] != efw)
283                         continue;
284                 instances[i] = NULL;
285         }
286 
287         spin_unlock_irq(&instances_lock);
288 }
289 
290 void snd_efw_transaction_bus_reset(struct fw_unit *unit)
291 {
292         struct transaction_queue *t;
293 
294         spin_lock_irq(&transaction_queues_lock);
295         list_for_each_entry(t, &transaction_queues, list) {
296                 if ((t->unit == unit) &&
297                     (t->state == STATE_PENDING)) {
298                         t->state = STATE_BUS_RESET;
299                         wake_up(&t->wait);
300                 }
301         }
302         spin_unlock_irq(&transaction_queues_lock);
303 }
304 
305 static struct fw_address_handler resp_register_handler = {
306         .length = SND_EFW_RESPONSE_MAXIMUM_BYTES,
307         .address_callback = efw_response
308 };
309 
310 int snd_efw_transaction_register(void)
311 {
312         static const struct fw_address_region resp_register_region = {
313                 .start  = MEMORY_SPACE_EFW_RESPONSE,
314                 .end    = MEMORY_SPACE_EFW_RESPONSE +
315                           SND_EFW_RESPONSE_MAXIMUM_BYTES
316         };
317         return fw_core_add_address_handler(&resp_register_handler,
318                                            &resp_register_region);
319 }
320 
321 void snd_efw_transaction_unregister(void)
322 {
323         WARN_ON(!list_empty(&transaction_queues));
324         fw_core_remove_address_handler(&resp_register_handler);
325 }
326 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp