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
49static 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(substream: subs->pcm_substream);
82 }
83 return 0;
84}
85
86static 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 */
102static 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(v: &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
140static 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
166static 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(v: &playbacksubs->state);
175 if (urb) {
176 if (state == STATE_RUNNING)
177 usx2y_urb_play_retire(subs: playbacksubs, urb);
178 else if (state >= STATE_PRERUNNING)
179 atomic_inc(v: &playbacksubs->state);
180 } else {
181 switch (state) {
182 case STATE_STARTING1:
183 urb = playbacksubs->urb[0];
184 atomic_inc(v: &playbacksubs->state);
185 break;
186 case STATE_STARTING2:
187 urb = playbacksubs->urb[1];
188 atomic_inc(v: &playbacksubs->state);
189 break;
190 }
191 }
192 if (urb) {
193 err = usx2y_hwdep_urb_play_prepare(subs: playbacksubs, urb);
194 if (err)
195 return err;
196 err = usx2y_hwdep_urb_play_prepare(subs: playbacksubs, urb);
197 if (err)
198 return err;
199 }
200
201 playbacksubs->completed_urb = NULL;
202
203 state = atomic_read(v: &capsubs->state);
204 if (state >= STATE_PREPARED) {
205 if (state == STATE_RUNNING) {
206 err = usx2y_usbpcm_urb_capt_retire(subs: capsubs);
207 if (err)
208 return err;
209 } else if (state >= STATE_PRERUNNING) {
210 atomic_inc(v: &capsubs->state);
211 }
212 usx2y_usbpcm_urb_capt_iso_advance(subs: capsubs, urb: capsubs->completed_urb);
213 if (capsubs2)
214 usx2y_usbpcm_urb_capt_iso_advance(NULL, urb: capsubs2->completed_urb);
215 err = usx2y_urb_submit(subs: capsubs, urb: capsubs->completed_urb, frame);
216 if (err)
217 return err;
218 if (capsubs2) {
219 err = usx2y_urb_submit(subs: capsubs2, urb: 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
230static 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(v: &capsubs->state) >= STATE_PREPARED &&
253 (!capsubs2 || capsubs2->completed_urb) &&
254 (playbacksubs->completed_urb || atomic_read(v: &playbacksubs->state) < STATE_PREPARED)) {
255 if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, frame: 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
264static void usx2y_hwdep_urb_release(struct urb **urb)
265{
266 usb_kill_urb(urb: *urb);
267 usb_free_urb(urb: *urb);
268 *urb = NULL;
269}
270
271/*
272 * release a substream
273 */
274static 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(urb: subs->urb + i);
281}
282
283static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y)
284{
285 usx2y_urbs_set_complete(usx2y, complete: i_usx2y_usbpcm_urb_complete);
286 usx2y->prepare_subs = NULL;
287}
288
289static 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(v: &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(v: &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 */
314static 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(udev: 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(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 */
359static 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(v: &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(v: &cap_subs->state, STATE_STOPPED);
379 if (cap_subs2)
380 atomic_set(v: &cap_subs2->state, STATE_STOPPED);
381 usx2y_usbpcm_urbs_release(subs: cap_subs);
382 if (cap_subs2)
383 usx2y_usbpcm_urbs_release(subs: cap_subs2);
384 }
385 } else {
386 playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK];
387 if (atomic_read(v: &playback_subs->state) < STATE_PREPARED) {
388 atomic_set(v: &subs->state, STATE_STOPPED);
389 if (cap_subs2)
390 atomic_set(v: &cap_subs2->state, STATE_STOPPED);
391 usx2y_usbpcm_urbs_release(subs);
392 if (cap_subs2)
393 usx2y_usbpcm_urbs_release(subs: cap_subs2);
394 }
395 }
396 mutex_unlock(lock: &subs->usx2y->pcm_mutex);
397 return 0;
398}
399
400static 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, complete: i_usx2y_usbpcm_subs_startup);
408}
409
410static 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(v: &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(v: &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(v: &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(v: &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 */
495static 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(v: &capsubs->state) < STATE_PREPARED) {
521 if (usx2y->format != runtime->format) {
522 err = usx2y_format_set(usx2y, format: runtime->format);
523 if (err < 0)
524 goto up_prepare_mutex;
525 }
526 if (usx2y->rate != runtime->rate) {
527 err = usx2y_rate_set(usx2y, rate: 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(subs: 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(v: &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(msecs: 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(lock: &usx2y->pcm_mutex);
564 return err;
565}
566
567static 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
585static 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, min: 1000, max: 200000);
602 return 0;
603}
604
605static 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
614static 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
624static 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
639static 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(lock: &usx2y(card)->pcm_mutex);
649 return err;
650}
651
652static 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(lock: &usx2y(card)->pcm_mutex);
662 return err;
663}
664
665static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area)
666{
667}
668
669static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area)
670{
671}
672
673static 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(page: vmf->page);
682 return 0;
683}
684
685static 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
691static 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(vma: area, VM_DONTEXPAND | VM_DONTDUMP);
710 area->vm_private_data = hw->private_data;
711 return 0;
712}
713
714static 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(virt: usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES);
720}
721
722int 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, device: 1, rhwdep: &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(buf: hw->name, fmt: "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
744
745 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", device: 2, playback_count: 1, capture_count: 1, rpcm: &pcm);
746 if (err < 0)
747 return err;
748
749 snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_PLAYBACK, ops: &snd_usx2y_usbpcm_ops);
750 snd_pcm_set_ops(pcm, direction: SNDRV_PCM_STREAM_CAPTURE, ops: &snd_usx2y_usbpcm_ops);
751
752 pcm->private_data = usx2y(card)->subs;
753 pcm->info_flags = 0;
754
755 sprintf(buf: pcm->name, NAME_ALLCAPS" hwdep Audio");
756 snd_pcm_set_managed_buffer(substream: pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
757 SNDRV_DMA_TYPE_CONTINUOUS,
758 NULL,
759 size: 64*1024, max: 128*1024);
760 snd_pcm_set_managed_buffer(substream: pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
761 SNDRV_DMA_TYPE_CONTINUOUS,
762 NULL,
763 size: 64*1024, max: 128*1024);
764
765 return 0;
766}
767
768#else
769
770int usx2y_hwdep_pcm_new(struct snd_card *card)
771{
772 return 0;
773}
774
775#endif
776

source code of linux/sound/usb/usx2y/usx2yhwdeppcm.c