WIP FPC-III support
[linux/fpc-iii.git] / sound / usb / usx2y / usx2yhwdeppcm.c
blob8253669c6a7d7f759460d1a5b9ebe64183fe8fe0
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 */
5 /* USX2Y "rawusb" aka hwdep_pcm implementation
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).
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.
41 #include <linux/delay.h>
42 #include <linux/gfp.h>
43 #include "usbusx2yaudio.c"
45 #if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
47 #include <sound/hwdep.h>
50 static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
52 struct urb *urb = subs->completed_urb;
53 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
54 int i, lens = 0, hwptr_done = subs->hwptr_done;
55 struct usX2Ydev *usX2Y = subs->usX2Y;
56 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) { //FIXME
57 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
58 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
59 head = 0;
60 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
61 snd_printdd("cap start %i\n", head);
63 for (i = 0; i < nr_of_packs(); i++) {
64 if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */
65 snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
66 return urb->iso_frame_desc[i].status;
68 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
70 if ((hwptr_done += lens) >= runtime->buffer_size)
71 hwptr_done -= runtime->buffer_size;
72 subs->hwptr_done = hwptr_done;
73 subs->transfer_done += lens;
74 /* update the pointer, call callback if necessary */
75 if (subs->transfer_done >= runtime->period_size) {
76 subs->transfer_done -= runtime->period_size;
77 snd_pcm_period_elapsed(subs->pcm_substream);
79 return 0;
82 static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
83 struct usX2Ydev * usX2Y)
85 return (runtime->buffer_size * 1000) / usX2Y->rate + 1; //FIXME: so far only correct period_size == 2^x ?
89 * prepare urb for playback data pipe
91 * we copy the data directly from the pcm buffer.
92 * the current position to be copied is held in hwptr field.
93 * since a urb can handle only a single linear buffer, if the total
94 * transferred area overflows the buffer boundary, we cannot send
95 * it directly from the buffer. thus the data is once copied to
96 * a temporary buffer and urb points to that.
98 static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
99 struct urb *urb)
101 int count, counts, pack;
102 struct usX2Ydev *usX2Y = subs->usX2Y;
103 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
104 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
106 if (0 > shm->playback_iso_start) {
107 shm->playback_iso_start = shm->captured_iso_head -
108 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
109 if (0 > shm->playback_iso_start)
110 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
111 shm->playback_iso_head = shm->playback_iso_start;
114 count = 0;
115 for (pack = 0; pack < nr_of_packs(); pack++) {
116 /* calculate the size of a packet */
117 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
118 if (counts < 43 || counts > 50) {
119 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
120 return -EPIPE;
122 /* set up descriptor */
123 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
124 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
125 if (atomic_read(&subs->state) != state_RUNNING)
126 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
127 urb->iso_frame_desc[pack].length);
128 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
129 shm->playback_iso_head = 0;
130 count += counts;
132 urb->transfer_buffer_length = count * usX2Y->stride;
133 return 0;
137 static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
138 struct urb *urb)
140 int pack;
141 for (pack = 0; pack < nr_of_packs(); ++pack) {
142 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
143 if (NULL != subs) {
144 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
145 int head = shm->captured_iso_head + 1;
146 if (head >= ARRAY_SIZE(shm->captured_iso))
147 head = 0;
148 shm->captured_iso[head].frame = urb->start_frame + pack;
149 shm->captured_iso[head].offset = desc->offset;
150 shm->captured_iso[head].length = desc->actual_length;
151 shm->captured_iso_head = head;
152 shm->captured_iso_frames++;
154 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
155 desc->length >= SSS)
156 desc->offset -= (SSS - desc->length);
160 static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
161 struct snd_usX2Y_substream *capsubs2,
162 struct snd_usX2Y_substream *playbacksubs,
163 int frame)
165 int err, state;
166 struct urb *urb = playbacksubs->completed_urb;
168 state = atomic_read(&playbacksubs->state);
169 if (NULL != urb) {
170 if (state == state_RUNNING)
171 usX2Y_urb_play_retire(playbacksubs, urb);
172 else if (state >= state_PRERUNNING)
173 atomic_inc(&playbacksubs->state);
174 } else {
175 switch (state) {
176 case state_STARTING1:
177 urb = playbacksubs->urb[0];
178 atomic_inc(&playbacksubs->state);
179 break;
180 case state_STARTING2:
181 urb = playbacksubs->urb[1];
182 atomic_inc(&playbacksubs->state);
183 break;
186 if (urb) {
187 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
188 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
189 return err;
193 playbacksubs->completed_urb = NULL;
195 state = atomic_read(&capsubs->state);
196 if (state >= state_PREPARED) {
197 if (state == state_RUNNING) {
198 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
199 return err;
200 } else if (state >= state_PRERUNNING)
201 atomic_inc(&capsubs->state);
202 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
203 if (NULL != capsubs2)
204 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
205 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
206 return err;
207 if (NULL != capsubs2)
208 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
209 return err;
211 capsubs->completed_urb = NULL;
212 if (NULL != capsubs2)
213 capsubs2->completed_urb = NULL;
214 return 0;
218 static void i_usX2Y_usbpcm_urb_complete(struct urb *urb)
220 struct snd_usX2Y_substream *subs = urb->context;
221 struct usX2Ydev *usX2Y = subs->usX2Y;
222 struct snd_usX2Y_substream *capsubs, *capsubs2, *playbacksubs;
224 if (unlikely(atomic_read(&subs->state) < state_PREPARED)) {
225 snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n",
226 usb_get_current_frame_number(usX2Y->dev),
227 subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out",
228 urb->status, urb->start_frame);
229 return;
231 if (unlikely(urb->status)) {
232 usX2Y_error_urb_status(usX2Y, subs, urb);
233 return;
236 subs->completed_urb = urb;
237 capsubs = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
238 capsubs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
239 playbacksubs = usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
240 if (capsubs->completed_urb && atomic_read(&capsubs->state) >= state_PREPARED &&
241 (NULL == capsubs2 || capsubs2->completed_urb) &&
242 (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < state_PREPARED)) {
243 if (!usX2Y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame))
244 usX2Y->wait_iso_frame += nr_of_packs();
245 else {
246 snd_printdd("\n");
247 usX2Y_clients_stop(usX2Y);
253 static void usX2Y_hwdep_urb_release(struct urb **urb)
255 usb_kill_urb(*urb);
256 usb_free_urb(*urb);
257 *urb = NULL;
261 * release a substream
263 static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
265 int i;
266 snd_printdd("snd_usX2Y_urbs_release() %i\n", subs->endpoint);
267 for (i = 0; i < NRURBS; i++)
268 usX2Y_hwdep_urb_release(subs->urb + i);
271 static void usX2Y_usbpcm_subs_startup_finish(struct usX2Ydev * usX2Y)
273 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_urb_complete);
274 usX2Y->prepare_subs = NULL;
277 static void i_usX2Y_usbpcm_subs_startup(struct urb *urb)
279 struct snd_usX2Y_substream *subs = urb->context;
280 struct usX2Ydev *usX2Y = subs->usX2Y;
281 struct snd_usX2Y_substream *prepare_subs = usX2Y->prepare_subs;
282 if (NULL != prepare_subs &&
283 urb->start_frame == prepare_subs->urb[0]->start_frame) {
284 atomic_inc(&prepare_subs->state);
285 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
286 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
287 if (cap_subs2 != NULL)
288 atomic_inc(&cap_subs2->state);
290 usX2Y_usbpcm_subs_startup_finish(usX2Y);
291 wake_up(&usX2Y->prepare_wait_queue);
294 i_usX2Y_usbpcm_urb_complete(urb);
298 * initialize a substream's urbs
300 static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
302 int i;
303 unsigned int pipe;
304 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
305 struct usb_device *dev = subs->usX2Y->dev;
307 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
308 usb_rcvisocpipe(dev, subs->endpoint);
309 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
310 if (!subs->maxpacksize)
311 return -EINVAL;
313 /* allocate and initialize data urbs */
314 for (i = 0; i < NRURBS; i++) {
315 struct urb **purb = subs->urb + i;
316 if (*purb) {
317 usb_kill_urb(*purb);
318 continue;
320 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
321 if (NULL == *purb) {
322 usX2Y_usbpcm_urbs_release(subs);
323 return -ENOMEM;
325 (*purb)->transfer_buffer = is_playback ?
326 subs->usX2Y->hwdep_pcm_shm->playback : (
327 subs->endpoint == 0x8 ?
328 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
329 subs->usX2Y->hwdep_pcm_shm->capture0xA);
331 (*purb)->dev = dev;
332 (*purb)->pipe = pipe;
333 (*purb)->number_of_packets = nr_of_packs();
334 (*purb)->context = subs;
335 (*purb)->interval = 1;
336 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
338 return 0;
342 * free the buffer
344 static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
346 struct snd_pcm_runtime *runtime = substream->runtime;
347 struct snd_usX2Y_substream *subs = runtime->private_data,
348 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
349 mutex_lock(&subs->usX2Y->pcm_mutex);
350 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
352 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
353 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
354 atomic_set(&subs->state, state_STOPPED);
355 usX2Y_usbpcm_urbs_release(subs);
356 if (!cap_subs->pcm_substream ||
357 !cap_subs->pcm_substream->runtime ||
358 !cap_subs->pcm_substream->runtime->status ||
359 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
360 atomic_set(&cap_subs->state, state_STOPPED);
361 if (NULL != cap_subs2)
362 atomic_set(&cap_subs2->state, state_STOPPED);
363 usX2Y_usbpcm_urbs_release(cap_subs);
364 if (NULL != cap_subs2)
365 usX2Y_usbpcm_urbs_release(cap_subs2);
367 } else {
368 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
369 if (atomic_read(&playback_subs->state) < state_PREPARED) {
370 atomic_set(&subs->state, state_STOPPED);
371 if (NULL != cap_subs2)
372 atomic_set(&cap_subs2->state, state_STOPPED);
373 usX2Y_usbpcm_urbs_release(subs);
374 if (NULL != cap_subs2)
375 usX2Y_usbpcm_urbs_release(cap_subs2);
378 mutex_unlock(&subs->usX2Y->pcm_mutex);
379 return 0;
382 static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
384 struct usX2Ydev * usX2Y = subs->usX2Y;
385 usX2Y->prepare_subs = subs;
386 subs->urb[0]->start_frame = -1;
387 smp_wmb(); // Make sure above modifications are seen by i_usX2Y_subs_startup()
388 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
391 static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
393 int p, u, err,
394 stream = subs->pcm_substream->stream;
395 struct usX2Ydev *usX2Y = subs->usX2Y;
397 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
398 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
399 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
402 for (p = 0; 3 >= (stream + p); p += 2) {
403 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
404 if (subs != NULL) {
405 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
406 return err;
407 subs->completed_urb = NULL;
411 for (p = 0; p < 4; p++) {
412 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
413 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
414 goto start;
417 start:
418 usX2Y_usbpcm_subs_startup(subs);
419 for (u = 0; u < NRURBS; u++) {
420 for (p = 0; 3 >= (stream + p); p += 2) {
421 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
422 if (subs != NULL) {
423 struct urb *urb = subs->urb[u];
424 if (usb_pipein(urb->pipe)) {
425 unsigned long pack;
426 if (0 == u)
427 atomic_set(&subs->state, state_STARTING3);
428 urb->dev = usX2Y->dev;
429 for (pack = 0; pack < nr_of_packs(); pack++) {
430 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
431 urb->iso_frame_desc[pack].length = subs->maxpacksize;
433 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
434 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
435 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
436 err = -EPIPE;
437 goto cleanup;
438 } else {
439 snd_printdd("%i\n", urb->start_frame);
440 if (u == 0)
441 usX2Y->wait_iso_frame = urb->start_frame;
443 urb->transfer_flags = 0;
444 } else {
445 atomic_set(&subs->state, state_STARTING1);
446 break;
451 err = 0;
452 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
453 if (atomic_read(&subs->state) != state_PREPARED)
454 err = -EPIPE;
456 cleanup:
457 if (err) {
458 usX2Y_subs_startup_finish(usX2Y); // Call it now
459 usX2Y_clients_stop(usX2Y); // something is completely wroong > stop evrything
461 return err;
465 * prepare callback
467 * set format and initialize urbs
469 static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
471 struct snd_pcm_runtime *runtime = substream->runtime;
472 struct snd_usX2Y_substream *subs = runtime->private_data;
473 struct usX2Ydev *usX2Y = subs->usX2Y;
474 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
475 int err = 0;
476 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
478 if (NULL == usX2Y->hwdep_pcm_shm) {
479 usX2Y->hwdep_pcm_shm = alloc_pages_exact(sizeof(struct snd_usX2Y_hwdep_pcm_shm),
480 GFP_KERNEL);
481 if (!usX2Y->hwdep_pcm_shm)
482 return -ENOMEM;
483 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
486 mutex_lock(&usX2Y->pcm_mutex);
487 usX2Y_subs_prepare(subs);
488 // Start hardware streams
489 // SyncStream first....
490 if (atomic_read(&capsubs->state) < state_PREPARED) {
491 if (usX2Y->format != runtime->format)
492 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
493 goto up_prepare_mutex;
494 if (usX2Y->rate != runtime->rate)
495 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
496 goto up_prepare_mutex;
497 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
498 "self" : "playpipe");
499 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
500 goto up_prepare_mutex;
503 if (subs != capsubs) {
504 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
505 if (atomic_read(&subs->state) < state_PREPARED) {
506 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
507 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
508 snd_printdd("Wait: iso_frames_per_buffer=%i,"
509 "captured_iso_frames=%i\n",
510 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
511 usX2Y->hwdep_pcm_shm->captured_iso_frames);
512 if (msleep_interruptible(10)) {
513 err = -ERESTARTSYS;
514 goto up_prepare_mutex;
517 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
518 goto up_prepare_mutex;
520 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
521 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
522 usX2Y->hwdep_pcm_shm->captured_iso_frames);
523 } else
524 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
526 up_prepare_mutex:
527 mutex_unlock(&usX2Y->pcm_mutex);
528 return err;
531 static const struct snd_pcm_hardware snd_usX2Y_4c =
533 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
534 SNDRV_PCM_INFO_BLOCK_TRANSFER |
535 SNDRV_PCM_INFO_MMAP_VALID),
536 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
537 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
538 .rate_min = 44100,
539 .rate_max = 48000,
540 .channels_min = 2,
541 .channels_max = 4,
542 .buffer_bytes_max = (2*128*1024),
543 .period_bytes_min = 64,
544 .period_bytes_max = (128*1024),
545 .periods_min = 2,
546 .periods_max = 1024,
547 .fifo_size = 0
552 static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
554 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
555 snd_pcm_substream_chip(substream))[substream->stream];
556 struct snd_pcm_runtime *runtime = substream->runtime;
558 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
559 return -EBUSY;
561 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
562 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
563 runtime->private_data = subs;
564 subs->pcm_substream = substream;
565 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
566 return 0;
570 static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
572 struct snd_pcm_runtime *runtime = substream->runtime;
573 struct snd_usX2Y_substream *subs = runtime->private_data;
575 subs->pcm_substream = NULL;
576 return 0;
580 static const struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
582 .open = snd_usX2Y_usbpcm_open,
583 .close = snd_usX2Y_usbpcm_close,
584 .hw_params = snd_usX2Y_pcm_hw_params,
585 .hw_free = snd_usX2Y_usbpcm_hw_free,
586 .prepare = snd_usX2Y_usbpcm_prepare,
587 .trigger = snd_usX2Y_pcm_trigger,
588 .pointer = snd_usX2Y_pcm_pointer,
592 static int usX2Y_pcms_busy_check(struct snd_card *card)
594 struct usX2Ydev *dev = usX2Y(card);
595 int i;
597 for (i = 0; i < dev->pcm_devs * 2; i++) {
598 struct snd_usX2Y_substream *subs = dev->subs[i];
599 if (subs && subs->pcm_substream &&
600 SUBSTREAM_BUSY(subs->pcm_substream))
601 return -EBUSY;
603 return 0;
606 static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
608 struct snd_card *card = hw->card;
609 int err;
611 mutex_lock(&usX2Y(card)->pcm_mutex);
612 err = usX2Y_pcms_busy_check(card);
613 if (!err)
614 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
615 mutex_unlock(&usX2Y(card)->pcm_mutex);
616 return err;
620 static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
622 struct snd_card *card = hw->card;
623 int err;
625 mutex_lock(&usX2Y(card)->pcm_mutex);
626 err = usX2Y_pcms_busy_check(card);
627 if (!err)
628 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
629 mutex_unlock(&usX2Y(card)->pcm_mutex);
630 return err;
634 static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
639 static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
644 static vm_fault_t snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
646 unsigned long offset;
647 void *vaddr;
649 offset = vmf->pgoff << PAGE_SHIFT;
650 vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
651 vmf->page = virt_to_page(vaddr);
652 get_page(vmf->page);
653 return 0;
657 static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
658 .open = snd_usX2Y_hwdep_pcm_vm_open,
659 .close = snd_usX2Y_hwdep_pcm_vm_close,
660 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
664 static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
666 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
667 struct usX2Ydev *usX2Y = hw->private_data;
669 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
670 return -EBUSY;
672 /* if userspace tries to mmap beyond end of our buffer, fail */
673 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
674 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
675 return -EINVAL;
678 if (!usX2Y->hwdep_pcm_shm) {
679 return -ENODEV;
681 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
682 area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
683 area->vm_private_data = hw->private_data;
684 return 0;
688 static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
690 struct usX2Ydev *usX2Y = hwdep->private_data;
691 if (NULL != usX2Y->hwdep_pcm_shm)
692 free_pages_exact(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
696 int usX2Y_hwdep_pcm_new(struct snd_card *card)
698 int err;
699 struct snd_hwdep *hw;
700 struct snd_pcm *pcm;
701 struct usb_device *dev = usX2Y(card)->dev;
702 if (1 != nr_of_packs())
703 return 0;
705 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
706 return err;
708 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
709 hw->private_data = usX2Y(card);
710 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
711 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
712 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
713 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
714 hw->exclusive = 1;
715 sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
717 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
718 if (err < 0) {
719 return err;
721 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
722 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
724 pcm->private_data = usX2Y(card)->subs;
725 pcm->info_flags = 0;
727 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
728 snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
729 SNDRV_DMA_TYPE_CONTINUOUS,
730 NULL,
731 64*1024, 128*1024);
732 snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
733 SNDRV_DMA_TYPE_CONTINUOUS,
734 NULL,
735 64*1024, 128*1024);
737 return 0;
740 #else
742 int usX2Y_hwdep_pcm_new(struct snd_card *card)
744 return 0;
747 #endif