1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53#include <linux/delay.h>
54#include <linux/gfp.h>
55#include "usbusx2yaudio.c"
56
57#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1
58
59#include <sound/hwdep.h>
60
61
62static int usX2Y_usbpcm_urb_capt_retire(struct snd_usX2Y_substream *subs)
63{
64 struct urb *urb = subs->completed_urb;
65 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
66 int i, lens = 0, hwptr_done = subs->hwptr_done;
67 struct usX2Ydev *usX2Y = subs->usX2Y;
68 if (0 > usX2Y->hwdep_pcm_shm->capture_iso_start) {
69 int head = usX2Y->hwdep_pcm_shm->captured_iso_head + 1;
70 if (head >= ARRAY_SIZE(usX2Y->hwdep_pcm_shm->captured_iso))
71 head = 0;
72 usX2Y->hwdep_pcm_shm->capture_iso_start = head;
73 snd_printdd("cap start %i\n", head);
74 }
75 for (i = 0; i < nr_of_packs(); i++) {
76 if (urb->iso_frame_desc[i].status) {
77 snd_printk(KERN_ERR "active frame status %i. Most probably some hardware problem.\n", urb->iso_frame_desc[i].status);
78 return urb->iso_frame_desc[i].status;
79 }
80 lens += urb->iso_frame_desc[i].actual_length / usX2Y->stride;
81 }
82 if ((hwptr_done += lens) >= runtime->buffer_size)
83 hwptr_done -= runtime->buffer_size;
84 subs->hwptr_done = hwptr_done;
85 subs->transfer_done += lens;
86
87 if (subs->transfer_done >= runtime->period_size) {
88 subs->transfer_done -= runtime->period_size;
89 snd_pcm_period_elapsed(subs->pcm_substream);
90 }
91 return 0;
92}
93
94static inline int usX2Y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime,
95 struct usX2Ydev * usX2Y)
96{
97 return (runtime->buffer_size * 1000) / usX2Y->rate + 1;
98}
99
100
101
102
103
104
105
106
107
108
109
110static int usX2Y_hwdep_urb_play_prepare(struct snd_usX2Y_substream *subs,
111 struct urb *urb)
112{
113 int count, counts, pack;
114 struct usX2Ydev *usX2Y = subs->usX2Y;
115 struct snd_usX2Y_hwdep_pcm_shm *shm = usX2Y->hwdep_pcm_shm;
116 struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
117
118 if (0 > shm->playback_iso_start) {
119 shm->playback_iso_start = shm->captured_iso_head -
120 usX2Y_iso_frames_per_buffer(runtime, usX2Y);
121 if (0 > shm->playback_iso_start)
122 shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso);
123 shm->playback_iso_head = shm->playback_iso_start;
124 }
125
126 count = 0;
127 for (pack = 0; pack < nr_of_packs(); pack++) {
128
129 counts = shm->captured_iso[shm->playback_iso_head].length / usX2Y->stride;
130 if (counts < 43 || counts > 50) {
131 snd_printk(KERN_ERR "should not be here with counts=%i\n", counts);
132 return -EPIPE;
133 }
134
135 urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset;
136 urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length;
137 if (atomic_read(&subs->state) != state_RUNNING)
138 memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0,
139 urb->iso_frame_desc[pack].length);
140 if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso))
141 shm->playback_iso_head = 0;
142 count += counts;
143 }
144 urb->transfer_buffer_length = count * usX2Y->stride;
145 return 0;
146}
147
148
149static inline void usX2Y_usbpcm_urb_capt_iso_advance(struct snd_usX2Y_substream *subs,
150 struct urb *urb)
151{
152 int pack;
153 for (pack = 0; pack < nr_of_packs(); ++pack) {
154 struct usb_iso_packet_descriptor *desc = urb->iso_frame_desc + pack;
155 if (NULL != subs) {
156 struct snd_usX2Y_hwdep_pcm_shm *shm = subs->usX2Y->hwdep_pcm_shm;
157 int head = shm->captured_iso_head + 1;
158 if (head >= ARRAY_SIZE(shm->captured_iso))
159 head = 0;
160 shm->captured_iso[head].frame = urb->start_frame + pack;
161 shm->captured_iso[head].offset = desc->offset;
162 shm->captured_iso[head].length = desc->actual_length;
163 shm->captured_iso_head = head;
164 shm->captured_iso_frames++;
165 }
166 if ((desc->offset += desc->length * NRURBS*nr_of_packs()) +
167 desc->length >= SSS)
168 desc->offset -= (SSS - desc->length);
169 }
170}
171
172static inline int usX2Y_usbpcm_usbframe_complete(struct snd_usX2Y_substream *capsubs,
173 struct snd_usX2Y_substream *capsubs2,
174 struct snd_usX2Y_substream *playbacksubs,
175 int frame)
176{
177 int err, state;
178 struct urb *urb = playbacksubs->completed_urb;
179
180 state = atomic_read(&playbacksubs->state);
181 if (NULL != urb) {
182 if (state == state_RUNNING)
183 usX2Y_urb_play_retire(playbacksubs, urb);
184 else if (state >= state_PRERUNNING)
185 atomic_inc(&playbacksubs->state);
186 } else {
187 switch (state) {
188 case state_STARTING1:
189 urb = playbacksubs->urb[0];
190 atomic_inc(&playbacksubs->state);
191 break;
192 case state_STARTING2:
193 urb = playbacksubs->urb[1];
194 atomic_inc(&playbacksubs->state);
195 break;
196 }
197 }
198 if (urb) {
199 if ((err = usX2Y_hwdep_urb_play_prepare(playbacksubs, urb)) ||
200 (err = usX2Y_urb_submit(playbacksubs, urb, frame))) {
201 return err;
202 }
203 }
204
205 playbacksubs->completed_urb = NULL;
206
207 state = atomic_read(&capsubs->state);
208 if (state >= state_PREPARED) {
209 if (state == state_RUNNING) {
210 if ((err = usX2Y_usbpcm_urb_capt_retire(capsubs)))
211 return err;
212 } else if (state >= state_PRERUNNING)
213 atomic_inc(&capsubs->state);
214 usX2Y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb);
215 if (NULL != capsubs2)
216 usX2Y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb);
217 if ((err = usX2Y_urb_submit(capsubs, capsubs->completed_urb, frame)))
218 return err;
219 if (NULL != capsubs2)
220 if ((err = usX2Y_urb_submit(capsubs2, capsubs2->completed_urb, frame)))
221 return err;
222 }
223 capsubs->completed_urb = NULL;
224 if (NULL != capsubs2)
225 capsubs2->completed_urb = NULL;
226 return 0;
227}
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(&capsubs->state) >= state_PREPARED &&
253 (NULL == 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
265static void usX2Y_hwdep_urb_release(struct urb **urb)
266{
267 usb_kill_urb(*urb);
268 usb_free_urb(*urb);
269 *urb = NULL;
270}
271
272
273
274
275static void usX2Y_usbpcm_urbs_release(struct snd_usX2Y_substream *subs)
276{
277 int i;
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
283static 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
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 if (NULL != prepare_subs &&
295 urb->start_frame == prepare_subs->urb[0]->start_frame) {
296 atomic_inc(&prepare_subs->state);
297 if (prepare_subs == usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE]) {
298 struct snd_usX2Y_substream *cap_subs2 = usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
299 if (cap_subs2 != NULL)
300 atomic_inc(&cap_subs2->state);
301 }
302 usX2Y_usbpcm_subs_startup_finish(usX2Y);
303 wake_up(&usX2Y->prepare_wait_queue);
304 }
305
306 i_usX2Y_usbpcm_urb_complete(urb);
307}
308
309
310
311
312static int usX2Y_usbpcm_urbs_allocate(struct snd_usX2Y_substream *subs)
313{
314 int i;
315 unsigned int pipe;
316 int is_playback = subs == subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
317 struct usb_device *dev = subs->usX2Y->dev;
318
319 pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
320 usb_rcvisocpipe(dev, subs->endpoint);
321 subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
322 if (!subs->maxpacksize)
323 return -EINVAL;
324
325
326 for (i = 0; i < NRURBS; i++) {
327 struct urb **purb = subs->urb + i;
328 if (*purb) {
329 usb_kill_urb(*purb);
330 continue;
331 }
332 *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL);
333 if (NULL == *purb) {
334 usX2Y_usbpcm_urbs_release(subs);
335 return -ENOMEM;
336 }
337 (*purb)->transfer_buffer = is_playback ?
338 subs->usX2Y->hwdep_pcm_shm->playback : (
339 subs->endpoint == 0x8 ?
340 subs->usX2Y->hwdep_pcm_shm->capture0x8 :
341 subs->usX2Y->hwdep_pcm_shm->capture0xA);
342
343 (*purb)->dev = dev;
344 (*purb)->pipe = pipe;
345 (*purb)->number_of_packets = nr_of_packs();
346 (*purb)->context = subs;
347 (*purb)->interval = 1;
348 (*purb)->complete = i_usX2Y_usbpcm_subs_startup;
349 }
350 return 0;
351}
352
353
354
355
356static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
357{
358 struct snd_pcm_runtime *runtime = substream->runtime;
359 struct snd_usX2Y_substream *subs = runtime->private_data,
360 *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
361 mutex_lock(&subs->usX2Y->pcm_mutex);
362 snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
363
364 if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
365 struct snd_usX2Y_substream *cap_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
366 atomic_set(&subs->state, state_STOPPED);
367 usX2Y_usbpcm_urbs_release(subs);
368 if (!cap_subs->pcm_substream ||
369 !cap_subs->pcm_substream->runtime ||
370 !cap_subs->pcm_substream->runtime->status ||
371 cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) {
372 atomic_set(&cap_subs->state, state_STOPPED);
373 if (NULL != cap_subs2)
374 atomic_set(&cap_subs2->state, state_STOPPED);
375 usX2Y_usbpcm_urbs_release(cap_subs);
376 if (NULL != cap_subs2)
377 usX2Y_usbpcm_urbs_release(cap_subs2);
378 }
379 } else {
380 struct snd_usX2Y_substream *playback_subs = subs->usX2Y->subs[SNDRV_PCM_STREAM_PLAYBACK];
381 if (atomic_read(&playback_subs->state) < state_PREPARED) {
382 atomic_set(&subs->state, state_STOPPED);
383 if (NULL != cap_subs2)
384 atomic_set(&cap_subs2->state, state_STOPPED);
385 usX2Y_usbpcm_urbs_release(subs);
386 if (NULL != cap_subs2)
387 usX2Y_usbpcm_urbs_release(cap_subs2);
388 }
389 }
390 mutex_unlock(&subs->usX2Y->pcm_mutex);
391 return snd_pcm_lib_free_pages(substream);
392}
393
394static void usX2Y_usbpcm_subs_startup(struct snd_usX2Y_substream *subs)
395{
396 struct usX2Ydev * usX2Y = subs->usX2Y;
397 usX2Y->prepare_subs = subs;
398 subs->urb[0]->start_frame = -1;
399 smp_wmb();
400 usX2Y_urbs_set_complete(usX2Y, i_usX2Y_usbpcm_subs_startup);
401}
402
403static int usX2Y_usbpcm_urbs_start(struct snd_usX2Y_substream *subs)
404{
405 int p, u, err,
406 stream = subs->pcm_substream->stream;
407 struct usX2Ydev *usX2Y = subs->usX2Y;
408
409 if (SNDRV_PCM_STREAM_CAPTURE == stream) {
410 usX2Y->hwdep_pcm_shm->captured_iso_head = -1;
411 usX2Y->hwdep_pcm_shm->captured_iso_frames = 0;
412 }
413
414 for (p = 0; 3 >= (stream + p); p += 2) {
415 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
416 if (subs != NULL) {
417 if ((err = usX2Y_usbpcm_urbs_allocate(subs)) < 0)
418 return err;
419 subs->completed_urb = NULL;
420 }
421 }
422
423 for (p = 0; p < 4; p++) {
424 struct snd_usX2Y_substream *subs = usX2Y->subs[p];
425 if (subs != NULL && atomic_read(&subs->state) >= state_PREPARED)
426 goto start;
427 }
428
429 start:
430 usX2Y_usbpcm_subs_startup(subs);
431 for (u = 0; u < NRURBS; u++) {
432 for (p = 0; 3 >= (stream + p); p += 2) {
433 struct snd_usX2Y_substream *subs = usX2Y->subs[stream + p];
434 if (subs != NULL) {
435 struct urb *urb = subs->urb[u];
436 if (usb_pipein(urb->pipe)) {
437 unsigned long pack;
438 if (0 == u)
439 atomic_set(&subs->state, state_STARTING3);
440 urb->dev = usX2Y->dev;
441 for (pack = 0; pack < nr_of_packs(); pack++) {
442 urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs());
443 urb->iso_frame_desc[pack].length = subs->maxpacksize;
444 }
445 urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs();
446 if ((err = usb_submit_urb(urb, GFP_KERNEL)) < 0) {
447 snd_printk (KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err);
448 err = -EPIPE;
449 goto cleanup;
450 } else {
451 snd_printdd("%i\n", urb->start_frame);
452 if (u == 0)
453 usX2Y->wait_iso_frame = urb->start_frame;
454 }
455 urb->transfer_flags = 0;
456 } else {
457 atomic_set(&subs->state, state_STARTING1);
458 break;
459 }
460 }
461 }
462 }
463 err = 0;
464 wait_event(usX2Y->prepare_wait_queue, NULL == usX2Y->prepare_subs);
465 if (atomic_read(&subs->state) != state_PREPARED)
466 err = -EPIPE;
467
468 cleanup:
469 if (err) {
470 usX2Y_subs_startup_finish(usX2Y);
471 usX2Y_clients_stop(usX2Y);
472 }
473 return err;
474}
475
476
477
478
479
480
481static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
482{
483 struct snd_pcm_runtime *runtime = substream->runtime;
484 struct snd_usX2Y_substream *subs = runtime->private_data;
485 struct usX2Ydev *usX2Y = subs->usX2Y;
486 struct snd_usX2Y_substream *capsubs = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE];
487 int err = 0;
488 snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
489
490 if (NULL == usX2Y->hwdep_pcm_shm) {
491 if (NULL == (usX2Y->hwdep_pcm_shm = snd_malloc_pages(sizeof(struct snd_usX2Y_hwdep_pcm_shm), GFP_KERNEL)))
492 return -ENOMEM;
493 memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
494 }
495
496 mutex_lock(&usX2Y->pcm_mutex);
497 usX2Y_subs_prepare(subs);
498
499
500 if (atomic_read(&capsubs->state) < state_PREPARED) {
501 if (usX2Y->format != runtime->format)
502 if ((err = usX2Y_format_set(usX2Y, runtime->format)) < 0)
503 goto up_prepare_mutex;
504 if (usX2Y->rate != runtime->rate)
505 if ((err = usX2Y_rate_set(usX2Y, runtime->rate)) < 0)
506 goto up_prepare_mutex;
507 snd_printdd("starting capture pipe for %s\n", subs == capsubs ?
508 "self" : "playpipe");
509 if (0 > (err = usX2Y_usbpcm_urbs_start(capsubs)))
510 goto up_prepare_mutex;
511 }
512
513 if (subs != capsubs) {
514 usX2Y->hwdep_pcm_shm->playback_iso_start = -1;
515 if (atomic_read(&subs->state) < state_PREPARED) {
516 while (usX2Y_iso_frames_per_buffer(runtime, usX2Y) >
517 usX2Y->hwdep_pcm_shm->captured_iso_frames) {
518 snd_printdd("Wait: iso_frames_per_buffer=%i,"
519 "captured_iso_frames=%i\n",
520 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
521 usX2Y->hwdep_pcm_shm->captured_iso_frames);
522 if (msleep_interruptible(10)) {
523 err = -ERESTARTSYS;
524 goto up_prepare_mutex;
525 }
526 }
527 if (0 > (err = usX2Y_usbpcm_urbs_start(subs)))
528 goto up_prepare_mutex;
529 }
530 snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n",
531 usX2Y_iso_frames_per_buffer(runtime, usX2Y),
532 usX2Y->hwdep_pcm_shm->captured_iso_frames);
533 } else
534 usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
535
536 up_prepare_mutex:
537 mutex_unlock(&usX2Y->pcm_mutex);
538 return err;
539}
540
541static struct snd_pcm_hardware snd_usX2Y_4c =
542{
543 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
544 SNDRV_PCM_INFO_BLOCK_TRANSFER |
545 SNDRV_PCM_INFO_MMAP_VALID),
546 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE,
547 .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
548 .rate_min = 44100,
549 .rate_max = 48000,
550 .channels_min = 2,
551 .channels_max = 4,
552 .buffer_bytes_max = (2*128*1024),
553 .period_bytes_min = 64,
554 .period_bytes_max = (128*1024),
555 .periods_min = 2,
556 .periods_max = 1024,
557 .fifo_size = 0
558};
559
560
561
562static int snd_usX2Y_usbpcm_open(struct snd_pcm_substream *substream)
563{
564 struct snd_usX2Y_substream *subs = ((struct snd_usX2Y_substream **)
565 snd_pcm_substream_chip(substream))[substream->stream];
566 struct snd_pcm_runtime *runtime = substream->runtime;
567
568 if (!(subs->usX2Y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS))
569 return -EBUSY;
570
571 runtime->hw = SNDRV_PCM_STREAM_PLAYBACK == substream->stream ? snd_usX2Y_2c :
572 (subs->usX2Y->subs[3] ? snd_usX2Y_4c : snd_usX2Y_2c);
573 runtime->private_data = subs;
574 subs->pcm_substream = substream;
575 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000);
576 return 0;
577}
578
579
580static int snd_usX2Y_usbpcm_close(struct snd_pcm_substream *substream)
581{
582 struct snd_pcm_runtime *runtime = substream->runtime;
583 struct snd_usX2Y_substream *subs = runtime->private_data;
584
585 subs->pcm_substream = NULL;
586 return 0;
587}
588
589
590static const struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
591{
592 .open = snd_usX2Y_usbpcm_open,
593 .close = snd_usX2Y_usbpcm_close,
594 .ioctl = snd_pcm_lib_ioctl,
595 .hw_params = snd_usX2Y_pcm_hw_params,
596 .hw_free = snd_usX2Y_usbpcm_hw_free,
597 .prepare = snd_usX2Y_usbpcm_prepare,
598 .trigger = snd_usX2Y_pcm_trigger,
599 .pointer = snd_usX2Y_pcm_pointer,
600};
601
602
603static int usX2Y_pcms_busy_check(struct snd_card *card)
604{
605 struct usX2Ydev *dev = usX2Y(card);
606 int i;
607
608 for (i = 0; i < dev->pcm_devs * 2; i++) {
609 struct snd_usX2Y_substream *subs = dev->subs[i];
610 if (subs && subs->pcm_substream &&
611 SUBSTREAM_BUSY(subs->pcm_substream))
612 return -EBUSY;
613 }
614 return 0;
615}
616
617static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
618{
619 struct snd_card *card = hw->card;
620 int err;
621
622 mutex_lock(&usX2Y(card)->pcm_mutex);
623 err = usX2Y_pcms_busy_check(card);
624 if (!err)
625 usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
626 mutex_unlock(&usX2Y(card)->pcm_mutex);
627 return err;
628}
629
630
631static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
632{
633 struct snd_card *card = hw->card;
634 int err;
635
636 mutex_lock(&usX2Y(card)->pcm_mutex);
637 err = usX2Y_pcms_busy_check(card);
638 if (!err)
639 usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
640 mutex_unlock(&usX2Y(card)->pcm_mutex);
641 return err;
642}
643
644
645static void snd_usX2Y_hwdep_pcm_vm_open(struct vm_area_struct *area)
646{
647}
648
649
650static void snd_usX2Y_hwdep_pcm_vm_close(struct vm_area_struct *area)
651{
652}
653
654
655static vm_fault_t snd_usX2Y_hwdep_pcm_vm_fault(struct vm_fault *vmf)
656{
657 unsigned long offset;
658 void *vaddr;
659
660 offset = vmf->pgoff << PAGE_SHIFT;
661 vaddr = (char *)((struct usX2Ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset;
662 vmf->page = virt_to_page(vaddr);
663 get_page(vmf->page);
664 return 0;
665}
666
667
668static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = {
669 .open = snd_usX2Y_hwdep_pcm_vm_open,
670 .close = snd_usX2Y_hwdep_pcm_vm_close,
671 .fault = snd_usX2Y_hwdep_pcm_vm_fault,
672};
673
674
675static int snd_usX2Y_hwdep_pcm_mmap(struct snd_hwdep * hw, struct file *filp, struct vm_area_struct *area)
676{
677 unsigned long size = (unsigned long)(area->vm_end - area->vm_start);
678 struct usX2Ydev *usX2Y = hw->private_data;
679
680 if (!(usX2Y->chip_status & USX2Y_STAT_CHIP_INIT))
681 return -EBUSY;
682
683
684 if (size > PAGE_ALIGN(sizeof(struct snd_usX2Y_hwdep_pcm_shm))) {
685 snd_printd("%lu > %lu\n", size, (unsigned long)sizeof(struct snd_usX2Y_hwdep_pcm_shm));
686 return -EINVAL;
687 }
688
689 if (!usX2Y->hwdep_pcm_shm) {
690 return -ENODEV;
691 }
692 area->vm_ops = &snd_usX2Y_hwdep_pcm_vm_ops;
693 area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
694 area->vm_private_data = hw->private_data;
695 return 0;
696}
697
698
699static void snd_usX2Y_hwdep_pcm_private_free(struct snd_hwdep *hwdep)
700{
701 struct usX2Ydev *usX2Y = hwdep->private_data;
702 if (NULL != usX2Y->hwdep_pcm_shm)
703 snd_free_pages(usX2Y->hwdep_pcm_shm, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
704}
705
706
707int usX2Y_hwdep_pcm_new(struct snd_card *card)
708{
709 int err;
710 struct snd_hwdep *hw;
711 struct snd_pcm *pcm;
712 struct usb_device *dev = usX2Y(card)->dev;
713 if (1 != nr_of_packs())
714 return 0;
715
716 if ((err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw)) < 0)
717 return err;
718
719 hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM;
720 hw->private_data = usX2Y(card);
721 hw->private_free = snd_usX2Y_hwdep_pcm_private_free;
722 hw->ops.open = snd_usX2Y_hwdep_pcm_open;
723 hw->ops.release = snd_usX2Y_hwdep_pcm_release;
724 hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
725 hw->exclusive = 1;
726 sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
727
728 err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
729 if (err < 0) {
730 return err;
731 }
732 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usX2Y_usbpcm_ops);
733 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usX2Y_usbpcm_ops);
734
735 pcm->private_data = usX2Y(card)->subs;
736 pcm->info_flags = 0;
737
738 sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio");
739 if (0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
740 SNDRV_DMA_TYPE_CONTINUOUS,
741 snd_dma_continuous_data(GFP_KERNEL),
742 64*1024, 128*1024)) ||
743 0 > (err = snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
744 SNDRV_DMA_TYPE_CONTINUOUS,
745 snd_dma_continuous_data(GFP_KERNEL),
746 64*1024, 128*1024))) {
747 return err;
748 }
749
750
751 return 0;
752}
753
754#else
755
756int usX2Y_hwdep_pcm_new(struct snd_card *card)
757{
758 return 0;
759}
760
761#endif
762