11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds */ 41da177e4SLinus Torvalds 51da177e4SLinus Torvalds /* USX2Y "rawusb" aka hwdep_pcm implementation 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds Its usb's unableness to atomically handle power of 2 period sized data chuncs 81da177e4SLinus Torvalds at standard samplerates, 91da177e4SLinus Torvalds what led to this part of the usx2y module: 101da177e4SLinus Torvalds It provides the alsa kernel half of the usx2y-alsa-jack driver pair. 1125985edcSLucas De Marchi The pair uses a hardware dependent alsa-device for mmaped pcm transport. 121da177e4SLinus Torvalds Advantage achieved: 131da177e4SLinus Torvalds The usb_hc moves pcm data from/into memory via DMA. 141da177e4SLinus Torvalds That memory is mmaped by jack's usx2y driver. 151da177e4SLinus Torvalds Jack's usx2y driver is the first/last to read/write pcm data. 161da177e4SLinus Torvalds Read/write is a combination of power of 2 period shaping and 171da177e4SLinus Torvalds float/int conversation. 181da177e4SLinus Torvalds Compared to mainline alsa/jack we leave out power of 2 period shaping inside 191da177e4SLinus Torvalds snd-usb-usx2y which needs memcpy() and additional buffers. 201da177e4SLinus Torvalds As a side effect possible unwanted pcm-data coruption resulting of 211da177e4SLinus Torvalds standard alsa's snd-usb-usx2y period shaping scheme falls away. 221da177e4SLinus Torvalds Result is sane jack operation at buffering schemes down to 128frames, 231da177e4SLinus Torvalds 2 periods. 241da177e4SLinus Torvalds plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the 251da177e4SLinus Torvalds cost of easier triggered i.e. aeolus xruns (128 or 256frames, 261da177e4SLinus Torvalds 2periods works but is useless cause of crackling). 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds This is a first "proof of concept" implementation. 2925985edcSLucas De Marchi Later, functionalities should migrate to more appropriate places: 301da177e4SLinus Torvalds Userland: 311da177e4SLinus Torvalds - The jackd could mmap its float-pcm buffers directly from alsa-lib. 321da177e4SLinus Torvalds - alsa-lib could provide power of 2 period sized shaping combined with int/float 331da177e4SLinus Torvalds conversation. 341da177e4SLinus Torvalds Currently the usx2y jack driver provides above 2 services. 351da177e4SLinus Torvalds Kernel: 361da177e4SLinus Torvalds - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio 371da177e4SLinus Torvalds devices can use it. 381da177e4SLinus Torvalds Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. 391da177e4SLinus Torvalds */ 401da177e4SLinus Torvalds 41b27c187fSNishanth Aravamudan #include <linux/delay.h> 425a0e3ad6STejun Heo #include <linux/gfp.h> 431da177e4SLinus Torvalds #include "usbusx2yaudio.c" 441da177e4SLinus Torvalds 451d2019fbSNicolas Kaiser #if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #include <sound/hwdep.h> 481da177e4SLinus Torvalds 49bae3ce49STakashi Iwai static int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs) 501da177e4SLinus Torvalds { 511da177e4SLinus Torvalds struct urb *urb = subs->completed_urb; 52bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 531da177e4SLinus Torvalds int i, lens = 0, hwptr_done = subs->hwptr_done; 54bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 55a829dd5bSTakashi Iwai int head; 564c0a58efSTakashi Iwai 57a829dd5bSTakashi Iwai if (usx2y->hwdep_pcm_shm->capture_iso_start < 0) { //FIXME 58a829dd5bSTakashi Iwai head = usx2y->hwdep_pcm_shm->captured_iso_head + 1; 59bae3ce49STakashi Iwai if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso)) 601da177e4SLinus Torvalds head = 0; 61bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->capture_iso_start = head; 621da177e4SLinus Torvalds snd_printdd("cap start %i\n", head); 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds for (i = 0; i < nr_of_packs(); i++) { 651da177e4SLinus Torvalds if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ 664c0a58efSTakashi Iwai snd_printk(KERN_ERR 674c0a58efSTakashi Iwai "active frame status %i. Most probably some hardware problem.\n", 684c0a58efSTakashi Iwai urb->iso_frame_desc[i].status); 691da177e4SLinus Torvalds return urb->iso_frame_desc[i].status; 701da177e4SLinus Torvalds } 71bae3ce49STakashi Iwai lens += urb->iso_frame_desc[i].actual_length / usx2y->stride; 721da177e4SLinus Torvalds } 73a829dd5bSTakashi Iwai hwptr_done += lens; 74a829dd5bSTakashi Iwai if (hwptr_done >= runtime->buffer_size) 751da177e4SLinus Torvalds hwptr_done -= runtime->buffer_size; 761da177e4SLinus Torvalds subs->hwptr_done = hwptr_done; 771da177e4SLinus Torvalds subs->transfer_done += lens; 781da177e4SLinus Torvalds /* update the pointer, call callback if necessary */ 791da177e4SLinus Torvalds if (subs->transfer_done >= runtime->period_size) { 801da177e4SLinus Torvalds subs->transfer_done -= runtime->period_size; 811da177e4SLinus Torvalds snd_pcm_period_elapsed(subs->pcm_substream); 821da177e4SLinus Torvalds } 831da177e4SLinus Torvalds return 0; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 86a829dd5bSTakashi Iwai static int usx2y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime, 87bae3ce49STakashi Iwai struct usx2ydev *usx2y) 881da177e4SLinus Torvalds { 89bae3ce49STakashi Iwai return (runtime->buffer_size * 1000) / usx2y->rate + 1; //FIXME: so far only correct period_size == 2^x ? 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* 931da177e4SLinus Torvalds * prepare urb for playback data pipe 941da177e4SLinus Torvalds * 951da177e4SLinus Torvalds * we copy the data directly from the pcm buffer. 961da177e4SLinus Torvalds * the current position to be copied is held in hwptr field. 971da177e4SLinus Torvalds * since a urb can handle only a single linear buffer, if the total 981da177e4SLinus Torvalds * transferred area overflows the buffer boundary, we cannot send 991da177e4SLinus Torvalds * it directly from the buffer. thus the data is once copied to 1001da177e4SLinus Torvalds * a temporary buffer and urb points to that. 1011da177e4SLinus Torvalds */ 102bae3ce49STakashi Iwai static int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs, 1031da177e4SLinus Torvalds struct urb *urb) 1041da177e4SLinus Torvalds { 1051da177e4SLinus Torvalds int count, counts, pack; 106bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 107bae3ce49STakashi Iwai struct snd_usx2y_hwdep_pcm_shm *shm = usx2y->hwdep_pcm_shm; 108bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 1091da177e4SLinus Torvalds 110a829dd5bSTakashi Iwai if (shm->playback_iso_start < 0) { 1111da177e4SLinus Torvalds shm->playback_iso_start = shm->captured_iso_head - 112bae3ce49STakashi Iwai usx2y_iso_frames_per_buffer(runtime, usx2y); 113a829dd5bSTakashi Iwai if (shm->playback_iso_start < 0) 1141da177e4SLinus Torvalds shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso); 1151da177e4SLinus Torvalds shm->playback_iso_head = shm->playback_iso_start; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds count = 0; 1191da177e4SLinus Torvalds for (pack = 0; pack < nr_of_packs(); pack++) { 1201da177e4SLinus Torvalds /* calculate the size of a packet */ 121bae3ce49STakashi Iwai counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride; 1221da177e4SLinus Torvalds if (counts < 43 || counts > 50) { 123d3d579f8STakashi Iwai snd_printk(KERN_ERR "should not be here with counts=%i\n", counts); 1241da177e4SLinus Torvalds return -EPIPE; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds /* set up descriptor */ 1271da177e4SLinus Torvalds urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset; 1281da177e4SLinus Torvalds urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length; 129bae3ce49STakashi Iwai if (atomic_read(&subs->state) != STATE_RUNNING) 1301da177e4SLinus Torvalds memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0, 1311da177e4SLinus Torvalds urb->iso_frame_desc[pack].length); 1321da177e4SLinus Torvalds if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso)) 1331da177e4SLinus Torvalds shm->playback_iso_head = 0; 1341da177e4SLinus Torvalds count += counts; 1351da177e4SLinus Torvalds } 136bae3ce49STakashi Iwai urb->transfer_buffer_length = count * usx2y->stride; 1371da177e4SLinus Torvalds return 0; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds 140a829dd5bSTakashi Iwai static void usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream *subs, 141bbe85bbdSTakashi Iwai struct urb *urb) 1421da177e4SLinus Torvalds { 143a829dd5bSTakashi Iwai struct usb_iso_packet_descriptor *desc; 144a829dd5bSTakashi Iwai struct snd_usx2y_hwdep_pcm_shm *shm; 145a829dd5bSTakashi Iwai int pack, head; 1464c0a58efSTakashi Iwai 1471da177e4SLinus Torvalds for (pack = 0; pack < nr_of_packs(); ++pack) { 148a829dd5bSTakashi Iwai desc = urb->iso_frame_desc + pack; 149a829dd5bSTakashi Iwai if (subs) { 150a829dd5bSTakashi Iwai shm = subs->usx2y->hwdep_pcm_shm; 151a829dd5bSTakashi Iwai head = shm->captured_iso_head + 1; 1521da177e4SLinus Torvalds if (head >= ARRAY_SIZE(shm->captured_iso)) 1531da177e4SLinus Torvalds head = 0; 1541da177e4SLinus Torvalds shm->captured_iso[head].frame = urb->start_frame + pack; 1551da177e4SLinus Torvalds shm->captured_iso[head].offset = desc->offset; 1561da177e4SLinus Torvalds shm->captured_iso[head].length = desc->actual_length; 1571da177e4SLinus Torvalds shm->captured_iso_head = head; 1581da177e4SLinus Torvalds shm->captured_iso_frames++; 1591da177e4SLinus Torvalds } 160a829dd5bSTakashi Iwai desc->offset += desc->length * NRURBS * nr_of_packs(); 161a829dd5bSTakashi Iwai if (desc->offset + desc->length >= SSS) 1621da177e4SLinus Torvalds desc->offset -= (SSS - desc->length); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 166a829dd5bSTakashi Iwai static int usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream *capsubs, 167bae3ce49STakashi Iwai struct snd_usx2y_substream *capsubs2, 168bae3ce49STakashi Iwai struct snd_usx2y_substream *playbacksubs, 169bbe85bbdSTakashi Iwai int frame) 1701da177e4SLinus Torvalds { 1711da177e4SLinus Torvalds int err, state; 1721da177e4SLinus Torvalds struct urb *urb = playbacksubs->completed_urb; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds state = atomic_read(&playbacksubs->state); 175a829dd5bSTakashi Iwai if (urb) { 176bae3ce49STakashi Iwai if (state == STATE_RUNNING) 177bae3ce49STakashi Iwai usx2y_urb_play_retire(playbacksubs, urb); 178bae3ce49STakashi Iwai else if (state >= STATE_PRERUNNING) 1791da177e4SLinus Torvalds atomic_inc(&playbacksubs->state); 1801da177e4SLinus Torvalds } else { 1811da177e4SLinus Torvalds switch (state) { 182bae3ce49STakashi Iwai case STATE_STARTING1: 1831da177e4SLinus Torvalds urb = playbacksubs->urb[0]; 1841da177e4SLinus Torvalds atomic_inc(&playbacksubs->state); 1851da177e4SLinus Torvalds break; 186bae3ce49STakashi Iwai case STATE_STARTING2: 1871da177e4SLinus Torvalds urb = playbacksubs->urb[1]; 1881da177e4SLinus Torvalds atomic_inc(&playbacksubs->state); 1891da177e4SLinus Torvalds break; 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds if (urb) { 193a829dd5bSTakashi Iwai err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb); 194a829dd5bSTakashi Iwai if (err) 1951da177e4SLinus Torvalds return err; 196a829dd5bSTakashi Iwai err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb); 197a829dd5bSTakashi Iwai if (err) 198a829dd5bSTakashi Iwai return err; 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds playbacksubs->completed_urb = NULL; 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds state = atomic_read(&capsubs->state); 204bae3ce49STakashi Iwai if (state >= STATE_PREPARED) { 205bae3ce49STakashi Iwai if (state == STATE_RUNNING) { 206a829dd5bSTakashi Iwai err = usx2y_usbpcm_urb_capt_retire(capsubs); 207a829dd5bSTakashi Iwai if (err) 2081da177e4SLinus Torvalds return err; 209a829dd5bSTakashi Iwai } else if (state >= STATE_PRERUNNING) { 2101da177e4SLinus Torvalds atomic_inc(&capsubs->state); 211a829dd5bSTakashi Iwai } 212bae3ce49STakashi Iwai usx2y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb); 213a829dd5bSTakashi Iwai if (capsubs2) 214bae3ce49STakashi Iwai usx2y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb); 215a829dd5bSTakashi Iwai err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame); 216a829dd5bSTakashi Iwai if (err) 2171da177e4SLinus Torvalds return err; 218a829dd5bSTakashi Iwai if (capsubs2) { 219a829dd5bSTakashi Iwai err = usx2y_urb_submit(capsubs2, capsubs2->completed_urb, frame); 220a829dd5bSTakashi Iwai if (err) 2211da177e4SLinus Torvalds return err; 2221da177e4SLinus Torvalds } 223a829dd5bSTakashi Iwai } 2241da177e4SLinus Torvalds capsubs->completed_urb = NULL; 225a829dd5bSTakashi Iwai if (capsubs2) 2261da177e4SLinus Torvalds capsubs2->completed_urb = NULL; 2271da177e4SLinus Torvalds return 0; 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 230bae3ce49STakashi Iwai static void i_usx2y_usbpcm_urb_complete(struct urb *urb) 2311da177e4SLinus Torvalds { 232bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = urb->context; 233bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 234bae3ce49STakashi Iwai struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs; 2351da177e4SLinus Torvalds 236bae3ce49STakashi Iwai if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) { 237bbe85bbdSTakashi Iwai snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", 238bae3ce49STakashi Iwai usb_get_current_frame_number(usx2y->dev), 239bbe85bbdSTakashi Iwai subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", 240bbe85bbdSTakashi Iwai urb->status, urb->start_frame); 2411da177e4SLinus Torvalds return; 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds if (unlikely(urb->status)) { 244bae3ce49STakashi Iwai usx2y_error_urb_status(usx2y, subs, urb); 2451da177e4SLinus Torvalds return; 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 248a9d14bc0SDaniel Mack subs->completed_urb = urb; 249bae3ce49STakashi Iwai capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 250bae3ce49STakashi Iwai capsubs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 251bae3ce49STakashi Iwai playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 252bae3ce49STakashi Iwai if (capsubs->completed_urb && atomic_read(&capsubs->state) >= STATE_PREPARED && 253a829dd5bSTakashi Iwai (!capsubs2 || capsubs2->completed_urb) && 254bae3ce49STakashi Iwai (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) { 255a829dd5bSTakashi Iwai if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) { 256bae3ce49STakashi Iwai usx2y->wait_iso_frame += nr_of_packs(); 257a829dd5bSTakashi Iwai } else { 2581da177e4SLinus Torvalds snd_printdd("\n"); 259bae3ce49STakashi Iwai usx2y_clients_stop(usx2y); 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 264bae3ce49STakashi Iwai static void usx2y_hwdep_urb_release(struct urb **urb) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds usb_kill_urb(*urb); 2671da177e4SLinus Torvalds usb_free_urb(*urb); 2681da177e4SLinus Torvalds *urb = NULL; 2691da177e4SLinus Torvalds } 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds /* 2721da177e4SLinus Torvalds * release a substream 2731da177e4SLinus Torvalds */ 274bae3ce49STakashi Iwai static void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs) 2751da177e4SLinus Torvalds { 2761da177e4SLinus Torvalds int i; 2774c0a58efSTakashi Iwai 278bae3ce49STakashi Iwai snd_printdd("snd_usx2y_urbs_release() %i\n", subs->endpoint); 2791da177e4SLinus Torvalds for (i = 0; i < NRURBS; i++) 280bae3ce49STakashi Iwai usx2y_hwdep_urb_release(subs->urb + i); 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds 283bae3ce49STakashi Iwai static void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y) 2841da177e4SLinus Torvalds { 285bae3ce49STakashi Iwai usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete); 286bae3ce49STakashi Iwai usx2y->prepare_subs = NULL; 2871da177e4SLinus Torvalds } 2881da177e4SLinus Torvalds 289bae3ce49STakashi Iwai static void i_usx2y_usbpcm_subs_startup(struct urb *urb) 2901da177e4SLinus Torvalds { 291bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = urb->context; 292bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 293bae3ce49STakashi Iwai struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs; 294a829dd5bSTakashi Iwai struct snd_usx2y_substream *cap_subs2; 2954c0a58efSTakashi Iwai 296a829dd5bSTakashi Iwai if (prepare_subs && 2971da177e4SLinus Torvalds urb->start_frame == prepare_subs->urb[0]->start_frame) { 2981da177e4SLinus Torvalds atomic_inc(&prepare_subs->state); 299bae3ce49STakashi Iwai if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) { 300a829dd5bSTakashi Iwai cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 301a829dd5bSTakashi Iwai if (cap_subs2) 3021da177e4SLinus Torvalds atomic_inc(&cap_subs2->state); 3031da177e4SLinus Torvalds } 304bae3ce49STakashi Iwai usx2y_usbpcm_subs_startup_finish(usx2y); 305bae3ce49STakashi Iwai wake_up(&usx2y->prepare_wait_queue); 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 308bae3ce49STakashi Iwai i_usx2y_usbpcm_urb_complete(urb); 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds /* 3121da177e4SLinus Torvalds * initialize a substream's urbs 3131da177e4SLinus Torvalds */ 314bae3ce49STakashi Iwai static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs) 3151da177e4SLinus Torvalds { 3161da177e4SLinus Torvalds int i; 3171da177e4SLinus Torvalds unsigned int pipe; 318bae3ce49STakashi Iwai int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 319bae3ce49STakashi Iwai struct usb_device *dev = subs->usx2y->dev; 320a829dd5bSTakashi Iwai struct urb **purb; 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) : 3231da177e4SLinus Torvalds usb_rcvisocpipe(dev, subs->endpoint); 3241da177e4SLinus Torvalds subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback); 3251da177e4SLinus Torvalds if (!subs->maxpacksize) 3261da177e4SLinus Torvalds return -EINVAL; 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds /* allocate and initialize data urbs */ 3291da177e4SLinus Torvalds for (i = 0; i < NRURBS; i++) { 330a829dd5bSTakashi Iwai purb = subs->urb + i; 3311da177e4SLinus Torvalds if (*purb) { 3321da177e4SLinus Torvalds usb_kill_urb(*purb); 3331da177e4SLinus Torvalds continue; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL); 336a829dd5bSTakashi Iwai if (!*purb) { 337bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(subs); 3381da177e4SLinus Torvalds return -ENOMEM; 3391da177e4SLinus Torvalds } 3401da177e4SLinus Torvalds (*purb)->transfer_buffer = is_playback ? 341bae3ce49STakashi Iwai subs->usx2y->hwdep_pcm_shm->playback : ( 3421da177e4SLinus Torvalds subs->endpoint == 0x8 ? 343bae3ce49STakashi Iwai subs->usx2y->hwdep_pcm_shm->capture0x8 : 344bae3ce49STakashi Iwai subs->usx2y->hwdep_pcm_shm->capture0xA); 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds (*purb)->dev = dev; 3471da177e4SLinus Torvalds (*purb)->pipe = pipe; 3481da177e4SLinus Torvalds (*purb)->number_of_packets = nr_of_packs(); 3491da177e4SLinus Torvalds (*purb)->context = subs; 3501da177e4SLinus Torvalds (*purb)->interval = 1; 351bae3ce49STakashi Iwai (*purb)->complete = i_usx2y_usbpcm_subs_startup; 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds return 0; 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds /* 3571da177e4SLinus Torvalds * free the buffer 3581da177e4SLinus Torvalds */ 359bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream) 3601da177e4SLinus Torvalds { 361bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 362a829dd5bSTakashi Iwai struct snd_usx2y_substream *subs = runtime->private_data; 363a829dd5bSTakashi Iwai struct snd_usx2y_substream *cap_subs; 364a829dd5bSTakashi Iwai struct snd_usx2y_substream *playback_subs; 365a829dd5bSTakashi Iwai struct snd_usx2y_substream *cap_subs2; 3664c0a58efSTakashi Iwai 367bae3ce49STakashi Iwai mutex_lock(&subs->usx2y->pcm_mutex); 368a829dd5bSTakashi Iwai snd_printdd("%s(%p)\n", __func__, substream); 3691da177e4SLinus Torvalds 370a829dd5bSTakashi Iwai cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 371a829dd5bSTakashi Iwai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 372a829dd5bSTakashi Iwai cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 373bae3ce49STakashi Iwai atomic_set(&subs->state, STATE_STOPPED); 374bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(subs); 3751da177e4SLinus Torvalds if (!cap_subs->pcm_substream || 3761da177e4SLinus Torvalds !cap_subs->pcm_substream->runtime || 3771da177e4SLinus Torvalds !cap_subs->pcm_substream->runtime->status || 3781da177e4SLinus Torvalds cap_subs->pcm_substream->runtime->status->state < SNDRV_PCM_STATE_PREPARED) { 379bae3ce49STakashi Iwai atomic_set(&cap_subs->state, STATE_STOPPED); 380a829dd5bSTakashi Iwai if (cap_subs2) 381bae3ce49STakashi Iwai atomic_set(&cap_subs2->state, STATE_STOPPED); 382bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(cap_subs); 383a829dd5bSTakashi Iwai if (cap_subs2) 384bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(cap_subs2); 3851da177e4SLinus Torvalds } 3861da177e4SLinus Torvalds } else { 387a829dd5bSTakashi Iwai playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 388bae3ce49STakashi Iwai if (atomic_read(&playback_subs->state) < STATE_PREPARED) { 389bae3ce49STakashi Iwai atomic_set(&subs->state, STATE_STOPPED); 390a829dd5bSTakashi Iwai if (cap_subs2) 391bae3ce49STakashi Iwai atomic_set(&cap_subs2->state, STATE_STOPPED); 392bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(subs); 393a829dd5bSTakashi Iwai if (cap_subs2) 394bae3ce49STakashi Iwai usx2y_usbpcm_urbs_release(cap_subs2); 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds } 397bae3ce49STakashi Iwai mutex_unlock(&subs->usx2y->pcm_mutex); 3983f0c972aSTakashi Iwai return 0; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 401bae3ce49STakashi Iwai static void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs) 4021da177e4SLinus Torvalds { 403bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 4044c0a58efSTakashi Iwai 405bae3ce49STakashi Iwai usx2y->prepare_subs = subs; 4061da177e4SLinus Torvalds subs->urb[0]->start_frame = -1; 407bae3ce49STakashi Iwai smp_wmb(); // Make sure above modifications are seen by i_usx2y_subs_startup() 408bae3ce49STakashi Iwai usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup); 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 411bae3ce49STakashi Iwai static int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs) 4121da177e4SLinus Torvalds { 4134c0a58efSTakashi Iwai int p, u, err, stream = subs->pcm_substream->stream; 414bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 415a829dd5bSTakashi Iwai struct urb *urb; 416a829dd5bSTakashi Iwai unsigned long pack; 4171da177e4SLinus Torvalds 418a829dd5bSTakashi Iwai if (stream == SNDRV_PCM_STREAM_CAPTURE) { 419bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->captured_iso_head = -1; 420bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->captured_iso_frames = 0; 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds for (p = 0; 3 >= (stream + p); p += 2) { 424bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = usx2y->subs[stream + p]; 425a829dd5bSTakashi Iwai if (subs) { 426a829dd5bSTakashi Iwai err = usx2y_usbpcm_urbs_allocate(subs); 427a829dd5bSTakashi Iwai if (err < 0) 4281da177e4SLinus Torvalds return err; 4291da177e4SLinus Torvalds subs->completed_urb = NULL; 4301da177e4SLinus Torvalds } 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds for (p = 0; p < 4; p++) { 434bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = usx2y->subs[p]; 4354c0a58efSTakashi Iwai 436a829dd5bSTakashi Iwai if (subs && atomic_read(&subs->state) >= STATE_PREPARED) 4371da177e4SLinus Torvalds goto start; 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds 4401da177e4SLinus Torvalds start: 441bae3ce49STakashi Iwai usx2y_usbpcm_subs_startup(subs); 4421da177e4SLinus Torvalds for (u = 0; u < NRURBS; u++) { 4431da177e4SLinus Torvalds for (p = 0; 3 >= (stream + p); p += 2) { 444bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = usx2y->subs[stream + p]; 4454c0a58efSTakashi Iwai 446a829dd5bSTakashi Iwai if (!subs) 447a829dd5bSTakashi Iwai continue; 448a829dd5bSTakashi Iwai urb = subs->urb[u]; 4491da177e4SLinus Torvalds if (usb_pipein(urb->pipe)) { 450a829dd5bSTakashi Iwai if (!u) 451bae3ce49STakashi Iwai atomic_set(&subs->state, STATE_STARTING3); 452bae3ce49STakashi Iwai urb->dev = usx2y->dev; 4531da177e4SLinus Torvalds for (pack = 0; pack < nr_of_packs(); pack++) { 4541da177e4SLinus Torvalds urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs()); 4551da177e4SLinus Torvalds urb->iso_frame_desc[pack].length = subs->maxpacksize; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 458a829dd5bSTakashi Iwai err = usb_submit_urb(urb, GFP_KERNEL); 459a829dd5bSTakashi Iwai if (err < 0) { 4601da177e4SLinus Torvalds snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err); 4611da177e4SLinus Torvalds err = -EPIPE; 4621da177e4SLinus Torvalds goto cleanup; 4631da177e4SLinus Torvalds } else { 4641da177e4SLinus Torvalds snd_printdd("%i\n", urb->start_frame); 465a829dd5bSTakashi Iwai if (!u) 466bae3ce49STakashi Iwai usx2y->wait_iso_frame = urb->start_frame; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds urb->transfer_flags = 0; 4691da177e4SLinus Torvalds } else { 470bae3ce49STakashi Iwai atomic_set(&subs->state, STATE_STARTING1); 4711da177e4SLinus Torvalds break; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds err = 0; 476a829dd5bSTakashi Iwai wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs); 477bae3ce49STakashi Iwai if (atomic_read(&subs->state) != STATE_PREPARED) 4781da177e4SLinus Torvalds err = -EPIPE; 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds cleanup: 4811da177e4SLinus Torvalds if (err) { 482bae3ce49STakashi Iwai usx2y_subs_startup_finish(usx2y); // Call it now 483bae3ce49STakashi Iwai usx2y_clients_stop(usx2y); // something is completely wroong > stop evrything 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds return err; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4884e268db7STakashi Iwai #define USX2Y_HWDEP_PCM_PAGES \ 4894e268db7STakashi Iwai PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm)) 4904e268db7STakashi Iwai 4911da177e4SLinus Torvalds /* 4921da177e4SLinus Torvalds * prepare callback 4931da177e4SLinus Torvalds * 4941da177e4SLinus Torvalds * set format and initialize urbs 4951da177e4SLinus Torvalds */ 496bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream) 4971da177e4SLinus Torvalds { 498bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 499bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = runtime->private_data; 500bae3ce49STakashi Iwai struct usx2ydev *usx2y = subs->usx2y; 501bae3ce49STakashi Iwai struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 5021da177e4SLinus Torvalds int err = 0; 5034c0a58efSTakashi Iwai 504bae3ce49STakashi Iwai snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream); 5051da177e4SLinus Torvalds 506*c1f24841STakashi Iwai mutex_lock(&usx2y->pcm_mutex); 507*c1f24841STakashi Iwai 508a829dd5bSTakashi Iwai if (!usx2y->hwdep_pcm_shm) { 5094e268db7STakashi Iwai usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES, 510734b5a0bSTakashi Iwai GFP_KERNEL); 511*c1f24841STakashi Iwai if (!usx2y->hwdep_pcm_shm) { 512*c1f24841STakashi Iwai err = -ENOMEM; 513*c1f24841STakashi Iwai goto up_prepare_mutex; 514*c1f24841STakashi Iwai } 5154e268db7STakashi Iwai memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES); 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 518bae3ce49STakashi Iwai usx2y_subs_prepare(subs); 5191da177e4SLinus Torvalds // Start hardware streams 5201da177e4SLinus Torvalds // SyncStream first.... 521bae3ce49STakashi Iwai if (atomic_read(&capsubs->state) < STATE_PREPARED) { 522a829dd5bSTakashi Iwai if (usx2y->format != runtime->format) { 523a829dd5bSTakashi Iwai err = usx2y_format_set(usx2y, runtime->format); 524a829dd5bSTakashi Iwai if (err < 0) 5251da177e4SLinus Torvalds goto up_prepare_mutex; 526a829dd5bSTakashi Iwai } 527a829dd5bSTakashi Iwai if (usx2y->rate != runtime->rate) { 528a829dd5bSTakashi Iwai err = usx2y_rate_set(usx2y, runtime->rate); 529a829dd5bSTakashi Iwai if (err < 0) 5301da177e4SLinus Torvalds goto up_prepare_mutex; 531a829dd5bSTakashi Iwai } 532bbe85bbdSTakashi Iwai snd_printdd("starting capture pipe for %s\n", subs == capsubs ? 533bbe85bbdSTakashi Iwai "self" : "playpipe"); 534a829dd5bSTakashi Iwai err = usx2y_usbpcm_urbs_start(capsubs); 535a829dd5bSTakashi Iwai if (err < 0) 5361da177e4SLinus Torvalds goto up_prepare_mutex; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds if (subs != capsubs) { 540bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->playback_iso_start = -1; 541bae3ce49STakashi Iwai if (atomic_read(&subs->state) < STATE_PREPARED) { 542bae3ce49STakashi Iwai while (usx2y_iso_frames_per_buffer(runtime, usx2y) > 543bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->captured_iso_frames) { 5444c0a58efSTakashi Iwai snd_printdd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", 545bae3ce49STakashi Iwai usx2y_iso_frames_per_buffer(runtime, usx2y), 546bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->captured_iso_frames); 547b27c187fSNishanth Aravamudan if (msleep_interruptible(10)) { 5481da177e4SLinus Torvalds err = -ERESTARTSYS; 5491da177e4SLinus Torvalds goto up_prepare_mutex; 5501da177e4SLinus Torvalds } 5511da177e4SLinus Torvalds } 552a829dd5bSTakashi Iwai err = usx2y_usbpcm_urbs_start(subs); 553a829dd5bSTakashi Iwai if (err < 0) 5541da177e4SLinus Torvalds goto up_prepare_mutex; 5551da177e4SLinus Torvalds } 556bbe85bbdSTakashi Iwai snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", 557bae3ce49STakashi Iwai usx2y_iso_frames_per_buffer(runtime, usx2y), 558bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->captured_iso_frames); 559a829dd5bSTakashi Iwai } else { 560bae3ce49STakashi Iwai usx2y->hwdep_pcm_shm->capture_iso_start = -1; 561a829dd5bSTakashi Iwai } 5621da177e4SLinus Torvalds 5631da177e4SLinus Torvalds up_prepare_mutex: 564bae3ce49STakashi Iwai mutex_unlock(&usx2y->pcm_mutex); 5651da177e4SLinus Torvalds return err; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds 5684c0a58efSTakashi Iwai static const struct snd_pcm_hardware snd_usx2y_4c = { 5691da177e4SLinus Torvalds .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 5701da177e4SLinus Torvalds SNDRV_PCM_INFO_BLOCK_TRANSFER | 5711da177e4SLinus Torvalds SNDRV_PCM_INFO_MMAP_VALID), 5721da177e4SLinus Torvalds .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, 5731da177e4SLinus Torvalds .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 5741da177e4SLinus Torvalds .rate_min = 44100, 5751da177e4SLinus Torvalds .rate_max = 48000, 5761da177e4SLinus Torvalds .channels_min = 2, 5771da177e4SLinus Torvalds .channels_max = 4, 5781da177e4SLinus Torvalds .buffer_bytes_max = (2*128*1024), 5791da177e4SLinus Torvalds .period_bytes_min = 64, 5801da177e4SLinus Torvalds .period_bytes_max = (128*1024), 5811da177e4SLinus Torvalds .periods_min = 2, 5821da177e4SLinus Torvalds .periods_max = 1024, 5831da177e4SLinus Torvalds .fifo_size = 0 5841da177e4SLinus Torvalds }; 5851da177e4SLinus Torvalds 586bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream) 5871da177e4SLinus Torvalds { 588a829dd5bSTakashi Iwai struct snd_usx2y_substream *subs = 589a829dd5bSTakashi Iwai ((struct snd_usx2y_substream **) 5901da177e4SLinus Torvalds snd_pcm_substream_chip(substream))[substream->stream]; 591bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 5921da177e4SLinus Torvalds 593bae3ce49STakashi Iwai if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS)) 5941da177e4SLinus Torvalds return -EBUSY; 5951da177e4SLinus Torvalds 596a829dd5bSTakashi Iwai if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 597a829dd5bSTakashi Iwai runtime->hw = snd_usx2y_2c; 598a829dd5bSTakashi Iwai else 599a829dd5bSTakashi Iwai runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c); 6001da177e4SLinus Torvalds runtime->private_data = subs; 6011da177e4SLinus Torvalds subs->pcm_substream = substream; 6021da177e4SLinus Torvalds snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000); 6031da177e4SLinus Torvalds return 0; 6041da177e4SLinus Torvalds } 6051da177e4SLinus Torvalds 606bae3ce49STakashi Iwai static int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream) 6071da177e4SLinus Torvalds { 608bbe85bbdSTakashi Iwai struct snd_pcm_runtime *runtime = substream->runtime; 609bae3ce49STakashi Iwai struct snd_usx2y_substream *subs = runtime->private_data; 610cb432379STakashi Iwai 6111da177e4SLinus Torvalds subs->pcm_substream = NULL; 612cb432379STakashi Iwai return 0; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 6154c0a58efSTakashi Iwai static const struct snd_pcm_ops snd_usx2y_usbpcm_ops = { 616bae3ce49STakashi Iwai .open = snd_usx2y_usbpcm_open, 617bae3ce49STakashi Iwai .close = snd_usx2y_usbpcm_close, 618bae3ce49STakashi Iwai .hw_params = snd_usx2y_pcm_hw_params, 619bae3ce49STakashi Iwai .hw_free = snd_usx2y_usbpcm_hw_free, 620bae3ce49STakashi Iwai .prepare = snd_usx2y_usbpcm_prepare, 621bae3ce49STakashi Iwai .trigger = snd_usx2y_pcm_trigger, 622bae3ce49STakashi Iwai .pointer = snd_usx2y_pcm_pointer, 6231da177e4SLinus Torvalds }; 6241da177e4SLinus Torvalds 625bae3ce49STakashi Iwai static int usx2y_pcms_busy_check(struct snd_card *card) 6261da177e4SLinus Torvalds { 627bae3ce49STakashi Iwai struct usx2ydev *dev = usx2y(card); 628a829dd5bSTakashi Iwai struct snd_usx2y_substream *subs; 629e2439a54STakashi Iwai int i; 6301da177e4SLinus Torvalds 631e2439a54STakashi Iwai for (i = 0; i < dev->pcm_devs * 2; i++) { 632a829dd5bSTakashi Iwai subs = dev->subs[i]; 633e2439a54STakashi Iwai if (subs && subs->pcm_substream && 634e2439a54STakashi Iwai SUBSTREAM_BUSY(subs->pcm_substream)) 635e2439a54STakashi Iwai return -EBUSY; 6361da177e4SLinus Torvalds } 637e2439a54STakashi Iwai return 0; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 640bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file) 6411da177e4SLinus Torvalds { 642bbe85bbdSTakashi Iwai struct snd_card *card = hw->card; 643e2439a54STakashi Iwai int err; 644e2439a54STakashi Iwai 645bae3ce49STakashi Iwai mutex_lock(&usx2y(card)->pcm_mutex); 646bae3ce49STakashi Iwai err = usx2y_pcms_busy_check(card); 647e2439a54STakashi Iwai if (!err) 648bae3ce49STakashi Iwai usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS; 649bae3ce49STakashi Iwai mutex_unlock(&usx2y(card)->pcm_mutex); 6501da177e4SLinus Torvalds return err; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds 653bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file) 6541da177e4SLinus Torvalds { 655bbe85bbdSTakashi Iwai struct snd_card *card = hw->card; 656e2439a54STakashi Iwai int err; 657e2439a54STakashi Iwai 658bae3ce49STakashi Iwai mutex_lock(&usx2y(card)->pcm_mutex); 659bae3ce49STakashi Iwai err = usx2y_pcms_busy_check(card); 660e2439a54STakashi Iwai if (!err) 661bae3ce49STakashi Iwai usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS; 662bae3ce49STakashi Iwai mutex_unlock(&usx2y(card)->pcm_mutex); 6631da177e4SLinus Torvalds return err; 6641da177e4SLinus Torvalds } 6651da177e4SLinus Torvalds 666bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area) 6671da177e4SLinus Torvalds { 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds 670bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area) 6711da177e4SLinus Torvalds { 6721da177e4SLinus Torvalds } 6731da177e4SLinus Torvalds 674bae3ce49STakashi Iwai static vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf) 6751da177e4SLinus Torvalds { 6761da177e4SLinus Torvalds unsigned long offset; 6771da177e4SLinus Torvalds void *vaddr; 6781da177e4SLinus Torvalds 679eb415b8fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 680bae3ce49STakashi Iwai vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset; 681eb415b8fSNick Piggin vmf->page = virt_to_page(vaddr); 682eb415b8fSNick Piggin get_page(vmf->page); 683eb415b8fSNick Piggin return 0; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds 686bae3ce49STakashi Iwai static const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = { 687bae3ce49STakashi Iwai .open = snd_usx2y_hwdep_pcm_vm_open, 688bae3ce49STakashi Iwai .close = snd_usx2y_hwdep_pcm_vm_close, 689bae3ce49STakashi Iwai .fault = snd_usx2y_hwdep_pcm_vm_fault, 6901da177e4SLinus Torvalds }; 6911da177e4SLinus Torvalds 692bae3ce49STakashi Iwai static int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area) 6931da177e4SLinus Torvalds { 6941da177e4SLinus Torvalds unsigned long size = (unsigned long)(area->vm_end - area->vm_start); 695bae3ce49STakashi Iwai struct usx2ydev *usx2y = hw->private_data; 6961da177e4SLinus Torvalds 697bae3ce49STakashi Iwai if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT)) 6981da177e4SLinus Torvalds return -EBUSY; 6991da177e4SLinus Torvalds 7001da177e4SLinus Torvalds /* if userspace tries to mmap beyond end of our buffer, fail */ 7014e268db7STakashi Iwai if (size > USX2Y_HWDEP_PCM_PAGES) { 7024e268db7STakashi Iwai snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES); 7031da177e4SLinus Torvalds return -EINVAL; 7041da177e4SLinus Torvalds } 7051da177e4SLinus Torvalds 706a829dd5bSTakashi Iwai if (!usx2y->hwdep_pcm_shm) 7071da177e4SLinus Torvalds return -ENODEV; 708a829dd5bSTakashi Iwai 709bae3ce49STakashi Iwai area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops; 710314e51b9SKonstantin Khlebnikov area->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 7111da177e4SLinus Torvalds area->vm_private_data = hw->private_data; 7121da177e4SLinus Torvalds return 0; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds 715bae3ce49STakashi Iwai static void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep) 7161da177e4SLinus Torvalds { 717bae3ce49STakashi Iwai struct usx2ydev *usx2y = hwdep->private_data; 7184c0a58efSTakashi Iwai 719a829dd5bSTakashi Iwai if (usx2y->hwdep_pcm_shm) 7204e268db7STakashi Iwai free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES); 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds 723bae3ce49STakashi Iwai int usx2y_hwdep_pcm_new(struct snd_card *card) 7241da177e4SLinus Torvalds { 7251da177e4SLinus Torvalds int err; 726bbe85bbdSTakashi Iwai struct snd_hwdep *hw; 727bbe85bbdSTakashi Iwai struct snd_pcm *pcm; 728bae3ce49STakashi Iwai struct usb_device *dev = usx2y(card)->dev; 7294c0a58efSTakashi Iwai 730a829dd5bSTakashi Iwai if (nr_of_packs() != 1) 7311da177e4SLinus Torvalds return 0; 7321da177e4SLinus Torvalds 733a829dd5bSTakashi Iwai err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw); 734a829dd5bSTakashi Iwai if (err < 0) 7351da177e4SLinus Torvalds return err; 736cb432379STakashi Iwai 7371da177e4SLinus Torvalds hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM; 738bae3ce49STakashi Iwai hw->private_data = usx2y(card); 739bae3ce49STakashi Iwai hw->private_free = snd_usx2y_hwdep_pcm_private_free; 740bae3ce49STakashi Iwai hw->ops.open = snd_usx2y_hwdep_pcm_open; 741bae3ce49STakashi Iwai hw->ops.release = snd_usx2y_hwdep_pcm_release; 742bae3ce49STakashi Iwai hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap; 7431da177e4SLinus Torvalds hw->exclusive = 1; 744a5f8661dSMauro Carvalho Chehab sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum); 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm); 747a829dd5bSTakashi Iwai if (err < 0) 7481da177e4SLinus Torvalds return err; 749a829dd5bSTakashi Iwai 750bae3ce49STakashi Iwai snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops); 751bae3ce49STakashi Iwai snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops); 7521da177e4SLinus Torvalds 753bae3ce49STakashi Iwai pcm->private_data = usx2y(card)->subs; 7541da177e4SLinus Torvalds pcm->info_flags = 0; 7551da177e4SLinus Torvalds 7561da177e4SLinus Torvalds sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio"); 7573f0c972aSTakashi Iwai snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 7581da177e4SLinus Torvalds SNDRV_DMA_TYPE_CONTINUOUS, 7590af0a4feSTakashi Iwai NULL, 7604d1b5303STakashi Iwai 64*1024, 128*1024); 7613f0c972aSTakashi Iwai snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 7621da177e4SLinus Torvalds SNDRV_DMA_TYPE_CONTINUOUS, 7630af0a4feSTakashi Iwai NULL, 7644d1b5303STakashi Iwai 64*1024, 128*1024); 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds return 0; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds 7691da177e4SLinus Torvalds #else 7701da177e4SLinus Torvalds 771bae3ce49STakashi Iwai int usx2y_hwdep_pcm_new(struct snd_card *card) 7721da177e4SLinus Torvalds { 7731da177e4SLinus Torvalds return 0; 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds 7761da177e4SLinus Torvalds #endif 777