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

TOMOYO Linux Cross Reference
Linux/sound/usb/usx2y/usx2yhwdeppcm.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  */
  4 
  5 /* USX2Y "rawusb" aka hwdep_pcm implementation
  6 
  7  Its usb's unableness to atomically handle power of 2 period sized data chuncs
  8  at standard samplerates,
  9  what led to this part of the usx2y module:
 10  It provides the alsa kernel half of the usx2y-alsa-jack driver pair.
 11  The pair uses a hardware dependent alsa-device for mmaped pcm transport.
 12  Advantage achieved:
 13          The usb_hc moves pcm data from/into memory via DMA.
 14          That memory is mmaped by jack's usx2y driver.
 15          Jack's usx2y driver is the first/last to read/write pcm data.
 16          Read/write is a combination of power of 2 period shaping and
 17          float/int conversation.
 18          Compared to mainline alsa/jack we leave out power of 2 period shaping inside
 19          snd-usb-usx2y which needs memcpy() and additional buffers.
 20          As a side effect possible unwanted pcm-data coruption resulting of
 21          standard alsa's snd-usb-usx2y period shaping scheme falls away.
 22          Result is sane jack operation at buffering schemes down to 128frames,
 23          2 periods.
 24          plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the
 25          cost of easier triggered i.e. aeolus xruns (128 or 256frames,
 26          2periods works but is useless cause of crackling).
 27 
 28  This is a first "proof of concept" implementation.
 29  Later, functionalities should migrate to more appropriate places:
 30  Userland:
 31  - The jackd could mmap its float-pcm buffers directly from alsa-lib.
 32  - alsa-lib could provide power of 2 period sized shaping combined with int/float
 33    conversation.
 34    Currently the usx2y jack driver provides above 2 services.
 35  Kernel:
 36  - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio
 37    devices can use it.
 38    Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y.
 39 */
 40 
 41 #include <linux/delay.h>
 42 #include <linux/gfp.h>
 43 #include "usbusx2yaudio.c"
 44 
 45 #if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
 46 
 47 #include <sound/hwdep.h>
 48 
 49 static int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs)
 50 {
 51         struct urb      *urb = subs->completed_urb;
 52         struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
 53         int             i, lens = 0, hwptr_done = subs->hwptr_done;
 54         struct usx2ydev *usx2y = subs->usx2y;
 55         int head;
 56 
 57         if (usx2y->hwdep_pcm_shm->capture_iso_start < 0) { //FIXME
 58                 head = usx2y->hwdep_pcm_shm->captured_iso_head + 1;
 59                 if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso))
 60                         head = 0;
 61                 usx2y->hwdep_pcm_shm->capture_iso_start = head;
 62                 snd_printdd("cap start %i\n", head);
 63         }
 64         for (i = 0; i < nr_of_packs(); i++) {
 65                 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
 66                         snd_printk(KERN_ERR
 67                                    "active frame status %i. Most probably some hardware problem.\n",
 68                                    urb->iso_frame_desc[i].status);
 69                         return urb->iso_frame_desc[i].status;
 70                 }
 71                 lens += urb->iso_frame_desc[i].actual_length / usx2y->stride;
 72         }
 73         hwptr_done += lens;
 74         if (hwptr_done >= runtime->buffer_size)
 75                 hwptr_done -= runtime->buffer_size;
 76         subs->hwptr_done = hwptr_done;
 77         subs->transfer_done += lens;
 78         /* update the pointer, call callback if necessary */
 79         if (subs->transfer_done >= runtime->period_size) {
 80                 subs->transfer_done -= runtime->period_size;
 81                 snd_pcm_period_elapsed(subs->pcm_substream);
 82         }
 83         return 0;
 84 }
 85 
 86 static int usx2y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
 87                                               struct usx2ydev *usx2y)
 88 {
 89         return (runtime->buffer_size * 1000) / usx2y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
 90 }
 91 
 92 /*
 93  * prepare urb for playback data pipe
 94  *
 95  * we copy the data directly from the pcm buffer.
 96  * the current position to be copied is held in hwptr field.
 97  * since a urb can handle only a single linear buffer, if the total
 98  * transferred area overflows the buffer boundary, we cannot send
 99  * it directly from the buffer.  thus the data is once copied to
100  * a temporary buffer and urb points to that.
101  */
102 static int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs,
103                                         struct urb *urb)
104 {
105         int count, counts, pack;
106         struct usx2ydev *usx2y = subs->usx2y;
107         struct snd_usx2y_hwdep_pcm_shm *shm = usx2y->hwdep_pcm_shm;
108         struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
109 
110         if (shm->playback_iso_start < 0) {
111                 shm->playback_iso_start = shm->captured_iso_head -
112                         usx2y_iso_frames_per_buffer(runtime, usx2y);
113                 if (shm->playback_iso_start < 0)
114                         shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
115                 shm->playback_iso_head = shm->playback_iso_start;
116         }
117 
118         count = 0;
119         for (pack = 0; pack < nr_of_packs(); pack++) {
120                 /* calculate the size of a packet */
121                 counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride;
122                 if (counts < 43 || counts > 50) {
123                         snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
124                         return -EPIPE;
125                 }
126                 /* set up descriptor */
127                 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
128                 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
129                 if (atomic_read(&subs->state) != STATE_RUNNING)
130                         memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
131                                urb->iso_frame_desc[pack].length);
132                 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
133                         shm->playback_iso_head = 0;
134                 count += counts;
135         }
136         urb->transfer_buffer_length = count * usx2y->stride;
137         return 0;
138 }
139 
140 static void usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream *subs,
141                                               struct urb *urb)
142 {
143         struct usb_iso_packet_descriptor *desc;
144         struct snd_usx2y_hwdep_pcm_shm *shm;
145         int pack, head;
146 
147         for (pack = 0; pack < nr_of_packs(); ++pack) {
148                 desc = urb->iso_frame_desc + pack;
149                 if (subs) {
150                         shm = subs->usx2y->hwdep_pcm_shm;
151                         head = shm->captured_iso_head + 1;
152                         if (head >= ARRAY_SIZE(shm->captured_iso))
153                                 head = 0;
154                         shm->captured_iso[head].frame = urb->start_frame + pack;
155                         shm->captured_iso[head].offset = desc->offset;
156                         shm->captured_iso[head].length = desc->actual_length;
157                         shm->captured_iso_head = head;
158                         shm->captured_iso_frames++;
159                 }
160                 desc->offset += desc->length * NRURBS * nr_of_packs();
161                 if (desc->offset + desc->length >= SSS)
162                         desc->offset -= (SSS - desc->length);
163         }
164 }
165 
166 static int usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream *capsubs,
167                                           struct snd_usx2y_substream *capsubs2,
168                                           struct snd_usx2y_substream *playbacksubs,
169                                           int frame)
170 {
171         int err, state;
172         struct urb *urb = playbacksubs->completed_urb;
173 
174         state = atomic_read(&playbacksubs->state);
175         if (urb) {
176                 if (state == STATE_RUNNING)
177                         usx2y_urb_play_retire(playbacksubs, urb);
178                 else if (state >= STATE_PRERUNNING)
179                         atomic_inc(&playbacksubs->state);
180         } else {
181                 switch (state) {
182                 case STATE_STARTING1:
183                         urb = playbacksubs->urb[0];
184                         atomic_inc(&playbacksubs->state);
185                         break;
186                 case STATE_STARTING2:
187                         urb = playbacksubs->urb[1];
188                         atomic_inc(&playbacksubs->state);
189                         break;
190                 }
191         }
192         if (urb) {
193                 err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
194                 if (err)
195                         return err;
196                 err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb);
197                 if (err)
198                         return err;
199         }
200 
201         playbacksubs->completed_urb = NULL;
202 
203         state = atomic_read(&capsubs->state);
204         if (state >= STATE_PREPARED) {
205                 if (state == STATE_RUNNING) {
206                         err = usx2y_usbpcm_urb_capt_retire(capsubs);
207                         if (err)
208                                 return err;
209                 } else if (state >= STATE_PRERUNNING) {
210                         atomic_inc(&capsubs->state);
211                 }
212                 usx2y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
213                 if (capsubs2)
214                         usx2y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
215                 err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame);
216                 if (err)
217                         return err;
218                 if (capsubs2) {
219                         err = usx2y_urb_submit(capsubs2, capsubs2->completed_urb, frame);
220                         if (err)
221                                 return err;
222                 }
223         }
224         capsubs->completed_urb = NULL;
225         if (capsubs2)
226                 capsubs2->completed_urb = NULL;
227         return 0;
228 }
229 
230 static void i_usx2y_usbpcm_urb_complete(struct urb *urb)
231 {
232         struct snd_usx2y_substream *subs = urb->context;
233         struct usx2ydev *usx2y = subs->usx2y;
234         struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs;
235 
236         if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) {
237                 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
238                             usb_get_current_frame_number(usx2y->dev),
239                             subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
240                             urb->status, urb->start_frame);
241                 return;
242         }
243         if (unlikely(urb->status)) {
244                 usx2y_error_urb_status(usx2y, subs, urb);
245                 return;
246         }
247 
248         subs->completed_urb = urb;
249         capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
250         capsubs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
251         playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
252         if (capsubs->completed_urb && atomic_read(&capsubs->state) >= STATE_PREPARED &&
253             (!capsubs2 || capsubs2->completed_urb) &&
254             (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) {
255                 if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) {
256                         usx2y->wait_iso_frame += nr_of_packs();
257                 } else {
258                         snd_printdd("\n");
259                         usx2y_clients_stop(usx2y);
260                 }
261         }
262 }
263 
264 static void usx2y_hwdep_urb_release(struct urb **urb)
265 {
266         usb_kill_urb(*urb);
267         usb_free_urb(*urb);
268         *urb = NULL;
269 }
270 
271 /*
272  * release a substream
273  */
274 static void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs)
275 {
276         int i;
277 
278         snd_printdd("snd_usx2y_urbs_release() %i\n", subs->endpoint);
279         for (i = 0; i < NRURBS; i++)
280                 usx2y_hwdep_urb_release(subs->urb + i);
281 }
282 
283 static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y)
284 {
285         usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete);
286         usx2y->prepare_subs = NULL;
287 }
288 
289 static void i_usx2y_usbpcm_subs_startup(struct urb *urb)
290 {
291         struct snd_usx2y_substream *subs = urb->context;
292         struct usx2ydev *usx2y = subs->usx2y;
293         struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs;
294         struct snd_usx2y_substream *cap_subs2;
295 
296         if (prepare_subs &&
297             urb->start_frame == prepare_subs->urb[0]->start_frame) {
298                 atomic_inc(&prepare_subs->state);
299                 if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
300                         cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
301                         if (cap_subs2)
302                                 atomic_inc(&cap_subs2->state);
303                 }
304                 usx2y_usbpcm_subs_startup_finish(usx2y);
305                 wake_up(&usx2y->prepare_wait_queue);
306         }
307 
308         i_usx2y_usbpcm_urb_complete(urb);
309 }
310 
311 /*
312  * initialize a substream's urbs
313  */
314 static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs)
315 {
316         int i;
317         unsigned int pipe;
318         int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
319         struct usb_device *dev = subs->usx2y->dev;
320         struct urb **purb;
321 
322         pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
323                         usb_rcvisocpipe(dev, subs->endpoint);
324         subs->maxpacksize = usb_maxpacket(dev, pipe);
325         if (!subs->maxpacksize)
326                 return -EINVAL;
327 
328         /* allocate and initialize data urbs */
329         for (i = 0; i < NRURBS; i++) {
330                 purb = subs->urb + i;
331                 if (*purb) {
332                         usb_kill_urb(*purb);
333                         continue;
334                 }
335                 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
336                 if (!*purb) {
337                         usx2y_usbpcm_urbs_release(subs);
338                         return -ENOMEM;
339                 }
340                 (*purb)->transfer_buffer = is_playback ?
341                         subs->usx2y->hwdep_pcm_shm->playback : (
342                                 subs->endpoint == 0x8 ?
343                                 subs->usx2y->hwdep_pcm_shm->capture0x8 :
344                                 subs->usx2y->hwdep_pcm_shm->capture0xA);
345 
346                 (*purb)->dev = dev;
347                 (*purb)->pipe = pipe;
348                 (*purb)->number_of_packets = nr_of_packs();
349                 (*purb)->context = subs;
350                 (*purb)->interval = 1;
351                 (*purb)->complete = i_usx2y_usbpcm_subs_startup;
352         }
353         return 0;
354 }
355 
356 /*
357  * free the buffer
358  */
359 static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream)
360 {
361         struct snd_pcm_runtime *runtime = substream->runtime;
362         struct snd_usx2y_substream *subs = runtime->private_data;
363         struct snd_usx2y_substream *cap_subs;
364         struct snd_usx2y_substream *playback_subs;
365         struct snd_usx2y_substream *cap_subs2;
366 
367         mutex_lock(&subs->usx2y->pcm_mutex);
368         snd_printdd("%s(%p)\n", __func__, substream);
369 
370         cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
371         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
372                 cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
373                 atomic_set(&subs->state, STATE_STOPPED);
374                 usx2y_usbpcm_urbs_release(subs);
375                 if (!cap_subs->pcm_substream ||
376                     !cap_subs->pcm_substream->runtime ||
377                     cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) {
378                         atomic_set(&cap_subs->state, STATE_STOPPED);
379                         if (cap_subs2)
380                                 atomic_set(&cap_subs2->state, STATE_STOPPED);
381                         usx2y_usbpcm_urbs_release(cap_subs);
382                         if (cap_subs2)
383                                 usx2y_usbpcm_urbs_release(cap_subs2);
384                 }
385         } else {
386                 playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
387                 if (atomic_read(&playback_subs->state) < STATE_PREPARED) {
388                         atomic_set(&subs->state, STATE_STOPPED);
389                         if (cap_subs2)
390                                 atomic_set(&cap_subs2->state, STATE_STOPPED);
391                         usx2y_usbpcm_urbs_release(subs);
392                         if (cap_subs2)
393                                 usx2y_usbpcm_urbs_release(cap_subs2);
394                 }
395         }
396         mutex_unlock(&subs->usx2y->pcm_mutex);
397         return 0;
398 }
399 
400 static void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs)
401 {
402         struct usx2ydev *usx2y = subs->usx2y;
403 
404         usx2y->prepare_subs = subs;
405         subs->urb[0]->start_frame = -1;
406         smp_wmb();      // Make sure above modifications are seen by i_usx2y_subs_startup()
407         usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup);
408 }
409 
410 static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs)
411 {
412         int     p, u, err, stream = subs->pcm_substream->stream;
413         struct usx2ydev *usx2y = subs->usx2y;
414         struct urb *urb;
415         unsigned long pack;
416 
417         if (stream == SNDRV_PCM_STREAM_CAPTURE) {
418                 usx2y->hwdep_pcm_shm->captured_iso_head = -1;
419                 usx2y->hwdep_pcm_shm->captured_iso_frames = 0;
420         }
421 
422         for (p = 0; 3 >= (stream + p); p += 2) {
423                 struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
424                 if (subs) {
425                         err = usx2y_usbpcm_urbs_allocate(subs);
426                         if (err < 0)
427                                 return err;
428                         subs->completed_urb = NULL;
429                 }
430         }
431 
432         for (p = 0; p < 4; p++) {
433                 struct snd_usx2y_substream *subs = usx2y->subs[p];
434 
435                 if (subs && atomic_read(&subs->state) >= STATE_PREPARED)
436                         goto start;
437         }
438 
439  start:
440         usx2y_usbpcm_subs_startup(subs);
441         for (u = 0; u < NRURBS; u++) {
442                 for (p = 0; 3 >= (stream + p); p += 2) {
443                         struct snd_usx2y_substream *subs = usx2y->subs[stream + p];
444 
445                         if (!subs)
446                                 continue;
447                         urb = subs->urb[u];
448                         if (usb_pipein(urb->pipe)) {
449                                 if (!u)
450                                         atomic_set(&subs->state, STATE_STARTING3);
451                                 urb->dev = usx2y->dev;
452                                 for (pack = 0; pack < nr_of_packs(); pack++) {
453                                         urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
454                                         urb->iso_frame_desc[pack].length = subs->maxpacksize;
455                                 }
456                                 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
457                                 err = usb_submit_urb(urb, GFP_KERNEL);
458                                 if (err < 0) {
459                                         snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
460                                         err = -EPIPE;
461                                         goto cleanup;
462                                 }  else {
463                                         snd_printdd("%i\n", urb->start_frame);
464                                         if (!u)
465                                                 usx2y->wait_iso_frame = urb->start_frame;
466                                 }
467                                 urb->transfer_flags = 0;
468                         } else {
469                                 atomic_set(&subs->state, STATE_STARTING1);
470                                 break;
471                         }
472                 }
473         }
474         err = 0;
475         wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs);
476         if (atomic_read(&subs->state) != STATE_PREPARED)
477                 err = -EPIPE;
478 
479  cleanup:
480         if (err) {
481                 usx2y_subs_startup_finish(usx2y);       // Call it now
482                 usx2y_clients_stop(usx2y);      // something is completely wrong > stop everything
483         }
484         return err;
485 }
486 
487 #define USX2Y_HWDEP_PCM_PAGES   \
488         PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm))
489 
490 /*
491  * prepare callback
492  *
493  * set format and initialize urbs
494  */
495 static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream)
496 {
497         struct snd_pcm_runtime *runtime = substream->runtime;
498         struct snd_usx2y_substream *subs = runtime->private_data;
499         struct usx2ydev *usx2y = subs->usx2y;
500         struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE];
501         int err = 0;
502 
503         snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream);
504 
505         mutex_lock(&usx2y->pcm_mutex);
506 
507         if (!usx2y->hwdep_pcm_shm) {
508                 usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES,
509                                                          GFP_KERNEL);
510                 if (!usx2y->hwdep_pcm_shm) {
511                         err = -ENOMEM;
512                         goto up_prepare_mutex;
513                 }
514                 memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES);
515         }
516 
517         usx2y_subs_prepare(subs);
518         // Start hardware streams
519         // SyncStream first....
520         if (atomic_read(&capsubs->state) < STATE_PREPARED) {
521                 if (usx2y->format != runtime->format) {
522                         err = usx2y_format_set(usx2y, runtime->format);
523                         if (err < 0)
524                                 goto up_prepare_mutex;
525                 }
526                 if (usx2y->rate != runtime->rate) {
527                         err = usx2y_rate_set(usx2y, runtime->rate);
528                         if (err < 0)
529                                 goto up_prepare_mutex;
530                 }
531                 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
532                             "self" : "playpipe");
533                 err = usx2y_usbpcm_urbs_start(capsubs);
534                 if (err < 0)
535                         goto up_prepare_mutex;
536         }
537 
538         if (subs != capsubs) {
539                 usx2y->hwdep_pcm_shm->playback_iso_start = -1;
540                 if (atomic_read(&subs->state) < STATE_PREPARED) {
541                         while (usx2y_iso_frames_per_buffer(runtime, usx2y) >
542                                usx2y->hwdep_pcm_shm->captured_iso_frames) {
543                                 snd_printdd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
544                                             usx2y_iso_frames_per_buffer(runtime, usx2y),
545                                             usx2y->hwdep_pcm_shm->captured_iso_frames);
546                                 if (msleep_interruptible(10)) {
547                                         err = -ERESTARTSYS;
548                                         goto up_prepare_mutex;
549                                 }
550                         }
551                         err = usx2y_usbpcm_urbs_start(subs);
552                         if (err < 0)
553                                 goto up_prepare_mutex;
554                 }
555                 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
556                             usx2y_iso_frames_per_buffer(runtime, usx2y),
557                             usx2y->hwdep_pcm_shm->captured_iso_frames);
558         } else {
559                 usx2y->hwdep_pcm_shm->capture_iso_start = -1;
560         }
561 
562  up_prepare_mutex:
563         mutex_unlock(&usx2y->pcm_mutex);
564         return err;
565 }
566 
567 static const struct snd_pcm_hardware snd_usx2y_4c = {
568         .info =                 (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
569                                  SNDRV_PCM_INFO_BLOCK_TRANSFER |
570                                  SNDRV_PCM_INFO_MMAP_VALID),
571         .formats =                 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
572         .rates =                   SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
573         .rate_min =                44100,
574         .rate_max =                48000,
575         .channels_min =            2,
576         .channels_max =            4,
577         .buffer_bytes_max =     (2*128*1024),
578         .period_bytes_min =     64,
579         .period_bytes_max =     (128*1024),
580         .periods_min =          2,
581         .periods_max =          1024,
582         .fifo_size =              0
583 };
584 
585 static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream)
586 {
587         struct snd_usx2y_substream      *subs =
588                 ((struct snd_usx2y_substream **)
589                  snd_pcm_substream_chip(substream))[substream->stream];
590         struct snd_pcm_runtime  *runtime = substream->runtime;
591 
592         if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
593                 return -EBUSY;
594 
595         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
596                 runtime->hw = snd_usx2y_2c;
597         else
598                 runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c);
599         runtime->private_data = subs;
600         subs->pcm_substream = substream;
601         snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
602         return 0;
603 }
604 
605 static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream)
606 {
607         struct snd_pcm_runtime *runtime = substream->runtime;
608         struct snd_usx2y_substream *subs = runtime->private_data;
609 
610         subs->pcm_substream = NULL;
611         return 0;
612 }
613 
614 static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = {
615         .open =         snd_usx2y_usbpcm_open,
616         .close =        snd_usx2y_usbpcm_close,
617         .hw_params =    snd_usx2y_pcm_hw_params,
618         .hw_free =      snd_usx2y_usbpcm_hw_free,
619         .prepare =      snd_usx2y_usbpcm_prepare,
620         .trigger =      snd_usx2y_pcm_trigger,
621         .pointer =      snd_usx2y_pcm_pointer,
622 };
623 
624 static int usx2y_pcms_busy_check(struct snd_card *card)
625 {
626         struct usx2ydev *dev = usx2y(card);
627         struct snd_usx2y_substream *subs;
628         int i;
629 
630         for (i = 0; i < dev->pcm_devs * 2; i++) {
631                 subs = dev->subs[i];
632                 if (subs && subs->pcm_substream &&
633                     SUBSTREAM_BUSY(subs->pcm_substream))
634                         return -EBUSY;
635         }
636         return 0;
637 }
638 
639 static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
640 {
641         struct snd_card *card = hw->card;
642         int err;
643 
644         mutex_lock(&usx2y(card)->pcm_mutex);
645         err = usx2y_pcms_busy_check(card);
646         if (!err)
647                 usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
648         mutex_unlock(&usx2y(card)->pcm_mutex);
649         return err;
650 }
651 
652 static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
653 {
654         struct snd_card *card = hw->card;
655         int err;
656 
657         mutex_lock(&usx2y(card)->pcm_mutex);
658         err = usx2y_pcms_busy_check(card);
659         if (!err)
660                 usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
661         mutex_unlock(&usx2y(card)->pcm_mutex);
662         return err;
663 }
664 
665 static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
666 {
667 }
668 
669 static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
670 {
671 }
672 
673 static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
674 {
675         unsigned long offset;
676         void *vaddr;
677 
678         offset = vmf->pgoff << PAGE_SHIFT;
679         vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
680         vmf->page = virt_to_page(vaddr);
681         get_page(vmf->page);
682         return 0;
683 }
684 
685 static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = {
686         .open = snd_usx2y_hwdep_pcm_vm_open,
687         .close = snd_usx2y_hwdep_pcm_vm_close,
688         .fault = snd_usx2y_hwdep_pcm_vm_fault,
689 };
690 
691 static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area)
692 {
693         unsigned long   size = (unsigned long)(area->vm_end - area->vm_start);
694         struct usx2ydev *usx2y = hw->private_data;
695 
696         if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT))
697                 return -EBUSY;
698 
699         /* if userspace tries to mmap beyond end of our buffer, fail */
700         if (size > USX2Y_HWDEP_PCM_PAGES) {
701                 snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES);
702                 return -EINVAL;
703         }
704 
705         if (!usx2y->hwdep_pcm_shm)
706                 return -ENODEV;
707 
708         area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops;
709         vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP);
710         area->vm_private_data = hw->private_data;
711         return 0;
712 }
713 
714 static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
715 {
716         struct usx2ydev *usx2y = hwdep->private_data;
717 
718         if (usx2y->hwdep_pcm_shm)
719                 free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
720 }
721 
722 int usx2y_hwdep_pcm_new(struct snd_card *card)
723 {
724         int err;
725         struct snd_hwdep *hw;
726         struct snd_pcm *pcm;
727         struct usb_device *dev = usx2y(card)->dev;
728 
729         if (nr_of_packs() != 1)
730                 return 0;
731 
732         err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw);
733         if (err < 0)
734                 return err;
735 
736         hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
737         hw->private_data = usx2y(card);
738         hw->private_free = snd_usx2y_hwdep_pcm_private_free;
739         hw->ops.open = snd_usx2y_hwdep_pcm_open;
740         hw->ops.release = snd_usx2y_hwdep_pcm_release;
741         hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap;
742         hw->exclusive = 1;
743         sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
744 
745         err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
746         if (err < 0)
747                 return err;
748 
749         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops);
750         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops);
751 
752         pcm->private_data = usx2y(card)->subs;
753         pcm->info_flags = 0;
754 
755         sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
756         snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
757                                    SNDRV_DMA_TYPE_CONTINUOUS,
758                                    NULL,
759                                    64*1024, 128*1024);
760         snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
761                                    SNDRV_DMA_TYPE_CONTINUOUS,
762                                    NULL,
763                                    64*1024, 128*1024);
764 
765         return 0;
766 }
767 
768 #else
769 
770 int usx2y_hwdep_pcm_new(struct snd_card *card)
771 {
772         return 0;
773 }
774 
775 #endif
776 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php