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

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

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

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