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

TOMOYO Linux Cross Reference
Linux/sound/firewire/dice/dice-transaction.c

Version: ~ [ linux-5.3 ] ~ [ linux-5.2.14 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.72 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.143 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.192 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.192 ] ~ [ 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.73 ] ~ [ 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  * dice_transaction.c - a part of driver for Dice based devices
  3  *
  4  * Copyright (c) Clemens Ladisch
  5  * Copyright (c) 2014 Takashi Sakamoto
  6  *
  7  * Licensed under the terms of the GNU General Public License, version 2.
  8  */
  9 
 10 #include "dice.h"
 11 
 12 #define NOTIFICATION_TIMEOUT_MS 100
 13 
 14 static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
 15                        u64 offset)
 16 {
 17         switch (type) {
 18         case SND_DICE_ADDR_TYPE_TX:
 19                 offset += dice->tx_offset;
 20                 break;
 21         case SND_DICE_ADDR_TYPE_RX:
 22                 offset += dice->rx_offset;
 23                 break;
 24         case SND_DICE_ADDR_TYPE_SYNC:
 25                 offset += dice->sync_offset;
 26                 break;
 27         case SND_DICE_ADDR_TYPE_RSRV:
 28                 offset += dice->rsrv_offset;
 29                 break;
 30         case SND_DICE_ADDR_TYPE_GLOBAL:
 31         default:
 32                 offset += dice->global_offset;
 33                 break;
 34         }
 35         offset += DICE_PRIVATE_SPACE;
 36         return offset;
 37 }
 38 
 39 int snd_dice_transaction_write(struct snd_dice *dice,
 40                                enum snd_dice_addr_type type,
 41                                unsigned int offset, void *buf, unsigned int len)
 42 {
 43         return snd_fw_transaction(dice->unit,
 44                                   (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
 45                                                TCODE_WRITE_BLOCK_REQUEST,
 46                                   get_subaddr(dice, type, offset), buf, len, 0);
 47 }
 48 
 49 int snd_dice_transaction_read(struct snd_dice *dice,
 50                               enum snd_dice_addr_type type, unsigned int offset,
 51                               void *buf, unsigned int len)
 52 {
 53         return snd_fw_transaction(dice->unit,
 54                                   (len == 4) ? TCODE_READ_QUADLET_REQUEST :
 55                                                TCODE_READ_BLOCK_REQUEST,
 56                                   get_subaddr(dice, type, offset), buf, len, 0);
 57 }
 58 
 59 static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
 60 {
 61         return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
 62                                                 info, 4);
 63 }
 64 
 65 static int set_clock_info(struct snd_dice *dice,
 66                           unsigned int rate, unsigned int source)
 67 {
 68         unsigned int retries = 3;
 69         unsigned int i;
 70         __be32 info;
 71         u32 mask;
 72         u32 clock;
 73         int err;
 74 retry:
 75         err = get_clock_info(dice, &info);
 76         if (err < 0)
 77                 goto end;
 78 
 79         clock = be32_to_cpu(info);
 80         if (source != UINT_MAX) {
 81                 mask = CLOCK_SOURCE_MASK;
 82                 clock &= ~mask;
 83                 clock |= source;
 84         }
 85         if (rate != UINT_MAX) {
 86                 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
 87                         if (snd_dice_rates[i] == rate)
 88                                 break;
 89                 }
 90                 if (i == ARRAY_SIZE(snd_dice_rates)) {
 91                         err = -EINVAL;
 92                         goto end;
 93                 }
 94 
 95                 mask = CLOCK_RATE_MASK;
 96                 clock &= ~mask;
 97                 clock |= i << CLOCK_RATE_SHIFT;
 98         }
 99         info = cpu_to_be32(clock);
100 
101         if (completion_done(&dice->clock_accepted))
102                 reinit_completion(&dice->clock_accepted);
103 
104         err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
105                                                 &info, 4);
106         if (err < 0)
107                 goto end;
108 
109         /* Timeout means it's invalid request, probably bus reset occurred. */
110         if (wait_for_completion_timeout(&dice->clock_accepted,
111                         msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
112                 if (retries-- == 0) {
113                         err = -ETIMEDOUT;
114                         goto end;
115                 }
116 
117                 err = snd_dice_transaction_reinit(dice);
118                 if (err < 0)
119                         goto end;
120 
121                 msleep(500);    /* arbitrary */
122                 goto retry;
123         }
124 end:
125         return err;
126 }
127 
128 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
129                                           unsigned int *source)
130 {
131         __be32 info;
132         int err;
133 
134         err = get_clock_info(dice, &info);
135         if (err >= 0)
136                 *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
137 
138         return err;
139 }
140 
141 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
142 {
143         __be32 info;
144         unsigned int index;
145         int err;
146 
147         err = get_clock_info(dice, &info);
148         if (err < 0)
149                 goto end;
150 
151         index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
152         if (index >= SND_DICE_RATES_COUNT) {
153                 err = -ENOSYS;
154                 goto end;
155         }
156 
157         *rate = snd_dice_rates[index];
158 end:
159         return err;
160 }
161 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
162 {
163         return set_clock_info(dice, rate, UINT_MAX);
164 }
165 
166 int snd_dice_transaction_set_enable(struct snd_dice *dice)
167 {
168         __be32 value;
169         int err = 0;
170 
171         if (dice->global_enabled)
172                 goto end;
173 
174         value = cpu_to_be32(1);
175         err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
176                                  get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
177                                              GLOBAL_ENABLE),
178                                  &value, 4,
179                                  FW_FIXED_GENERATION | dice->owner_generation);
180         if (err < 0)
181                 goto end;
182 
183         dice->global_enabled = true;
184 end:
185         return err;
186 }
187 
188 void snd_dice_transaction_clear_enable(struct snd_dice *dice)
189 {
190         __be32 value;
191 
192         value = 0;
193         snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
194                            get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
195                                        GLOBAL_ENABLE),
196                            &value, 4, FW_QUIET |
197                            FW_FIXED_GENERATION | dice->owner_generation);
198 
199         dice->global_enabled = false;
200 }
201 
202 static void dice_notification(struct fw_card *card, struct fw_request *request,
203                               int tcode, int destination, int source,
204                               int generation, unsigned long long offset,
205                               void *data, size_t length, void *callback_data)
206 {
207         struct snd_dice *dice = callback_data;
208         u32 bits;
209         unsigned long flags;
210 
211         if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
212                 fw_send_response(card, request, RCODE_TYPE_ERROR);
213                 return;
214         }
215         if ((offset & 3) != 0) {
216                 fw_send_response(card, request, RCODE_ADDRESS_ERROR);
217                 return;
218         }
219 
220         bits = be32_to_cpup(data);
221 
222         spin_lock_irqsave(&dice->lock, flags);
223         dice->notification_bits |= bits;
224         spin_unlock_irqrestore(&dice->lock, flags);
225 
226         fw_send_response(card, request, RCODE_COMPLETE);
227 
228         if (bits & NOTIFY_CLOCK_ACCEPTED)
229                 complete(&dice->clock_accepted);
230         wake_up(&dice->hwdep_wait);
231 }
232 
233 static int register_notification_address(struct snd_dice *dice, bool retry)
234 {
235         struct fw_device *device = fw_parent_device(dice->unit);
236         __be64 *buffer;
237         unsigned int retries;
238         int err;
239 
240         retries = (retry) ? 3 : 0;
241 
242         buffer = kmalloc(2 * 8, GFP_KERNEL);
243         if (!buffer)
244                 return -ENOMEM;
245 
246         for (;;) {
247                 buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
248                 buffer[1] = cpu_to_be64(
249                         ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
250                         dice->notification_handler.offset);
251 
252                 dice->owner_generation = device->generation;
253                 smp_rmb(); /* node_id vs. generation */
254                 err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
255                                          get_subaddr(dice,
256                                                      SND_DICE_ADDR_TYPE_GLOBAL,
257                                                      GLOBAL_OWNER),
258                                          buffer, 2 * 8,
259                                          FW_FIXED_GENERATION |
260                                                         dice->owner_generation);
261                 if (err == 0) {
262                         /* success */
263                         if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
264                                 break;
265                         /* The address seems to be already registered. */
266                         if (buffer[0] == buffer[1])
267                                 break;
268 
269                         dev_err(&dice->unit->device,
270                                 "device is already in use\n");
271                         err = -EBUSY;
272                 }
273                 if (err != -EAGAIN || retries-- > 0)
274                         break;
275 
276                 msleep(20);
277         }
278 
279         kfree(buffer);
280 
281         if (err < 0)
282                 dice->owner_generation = -1;
283 
284         return err;
285 }
286 
287 static void unregister_notification_address(struct snd_dice *dice)
288 {
289         struct fw_device *device = fw_parent_device(dice->unit);
290         __be64 *buffer;
291 
292         buffer = kmalloc(2 * 8, GFP_KERNEL);
293         if (buffer == NULL)
294                 return;
295 
296         buffer[0] = cpu_to_be64(
297                 ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
298                 dice->notification_handler.offset);
299         buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
300         snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
301                            get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
302                                        GLOBAL_OWNER),
303                            buffer, 2 * 8, FW_QUIET |
304                            FW_FIXED_GENERATION | dice->owner_generation);
305 
306         kfree(buffer);
307 
308         dice->owner_generation = -1;
309 }
310 
311 void snd_dice_transaction_destroy(struct snd_dice *dice)
312 {
313         struct fw_address_handler *handler = &dice->notification_handler;
314 
315         if (handler->callback_data == NULL)
316                 return;
317 
318         unregister_notification_address(dice);
319 
320         fw_core_remove_address_handler(handler);
321         handler->callback_data = NULL;
322 }
323 
324 int snd_dice_transaction_reinit(struct snd_dice *dice)
325 {
326         struct fw_address_handler *handler = &dice->notification_handler;
327 
328         if (handler->callback_data == NULL)
329                 return -EINVAL;
330 
331         return register_notification_address(dice, false);
332 }
333 
334 int snd_dice_transaction_init(struct snd_dice *dice)
335 {
336         struct fw_address_handler *handler = &dice->notification_handler;
337         __be32 *pointers;
338         int err;
339 
340         /* Use the same way which dice_interface_check() does. */
341         pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
342         if (pointers == NULL)
343                 return -ENOMEM;
344 
345         /* Get offsets for sub-addresses */
346         err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
347                                  DICE_PRIVATE_SPACE,
348                                  pointers, sizeof(__be32) * 10, 0);
349         if (err < 0)
350                 goto end;
351 
352         /* Allocation callback in address space over host controller */
353         handler->length = 4;
354         handler->address_callback = dice_notification;
355         handler->callback_data = dice;
356         err = fw_core_add_address_handler(handler, &fw_high_memory_region);
357         if (err < 0) {
358                 handler->callback_data = NULL;
359                 goto end;
360         }
361 
362         /* Register the address space */
363         err = register_notification_address(dice, true);
364         if (err < 0) {
365                 fw_core_remove_address_handler(handler);
366                 handler->callback_data = NULL;
367                 goto end;
368         }
369 
370         dice->global_offset = be32_to_cpu(pointers[0]) * 4;
371         dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
372         dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
373         dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
374         dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
375 
376         /* Set up later. */
377         if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
378                 dice->clock_caps = 1;
379 end:
380         kfree(pointers);
381         return err;
382 }
383 

~ [ 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