162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci/* USX2Y "rawusb" aka hwdep_pcm implementation 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Its usb's unableness to atomically handle power of 2 period sized data chuncs 862306a36Sopenharmony_ci at standard samplerates, 962306a36Sopenharmony_ci what led to this part of the usx2y module: 1062306a36Sopenharmony_ci It provides the alsa kernel half of the usx2y-alsa-jack driver pair. 1162306a36Sopenharmony_ci The pair uses a hardware dependent alsa-device for mmaped pcm transport. 1262306a36Sopenharmony_ci Advantage achieved: 1362306a36Sopenharmony_ci The usb_hc moves pcm data from/into memory via DMA. 1462306a36Sopenharmony_ci That memory is mmaped by jack's usx2y driver. 1562306a36Sopenharmony_ci Jack's usx2y driver is the first/last to read/write pcm data. 1662306a36Sopenharmony_ci Read/write is a combination of power of 2 period shaping and 1762306a36Sopenharmony_ci float/int conversation. 1862306a36Sopenharmony_ci Compared to mainline alsa/jack we leave out power of 2 period shaping inside 1962306a36Sopenharmony_ci snd-usb-usx2y which needs memcpy() and additional buffers. 2062306a36Sopenharmony_ci As a side effect possible unwanted pcm-data coruption resulting of 2162306a36Sopenharmony_ci standard alsa's snd-usb-usx2y period shaping scheme falls away. 2262306a36Sopenharmony_ci Result is sane jack operation at buffering schemes down to 128frames, 2362306a36Sopenharmony_ci 2 periods. 2462306a36Sopenharmony_ci plain usx2y alsa mode is able to achieve 64frames, 4periods, but only at the 2562306a36Sopenharmony_ci cost of easier triggered i.e. aeolus xruns (128 or 256frames, 2662306a36Sopenharmony_ci 2periods works but is useless cause of crackling). 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci This is a first "proof of concept" implementation. 2962306a36Sopenharmony_ci Later, functionalities should migrate to more appropriate places: 3062306a36Sopenharmony_ci Userland: 3162306a36Sopenharmony_ci - The jackd could mmap its float-pcm buffers directly from alsa-lib. 3262306a36Sopenharmony_ci - alsa-lib could provide power of 2 period sized shaping combined with int/float 3362306a36Sopenharmony_ci conversation. 3462306a36Sopenharmony_ci Currently the usx2y jack driver provides above 2 services. 3562306a36Sopenharmony_ci Kernel: 3662306a36Sopenharmony_ci - rawusb dma pcm buffer transport should go to snd-usb-lib, so also snd-usb-audio 3762306a36Sopenharmony_ci devices can use it. 3862306a36Sopenharmony_ci Currently rawusb dma pcm buffer transport (this file) is only available to snd-usb-usx2y. 3962306a36Sopenharmony_ci*/ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include <linux/delay.h> 4262306a36Sopenharmony_ci#include <linux/gfp.h> 4362306a36Sopenharmony_ci#include "usbusx2yaudio.c" 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#if defined(USX2Y_NRPACKS_VARIABLE) || USX2Y_NRPACKS == 1 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#include <sound/hwdep.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int usx2y_usbpcm_urb_capt_retire(struct snd_usx2y_substream *subs) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct urb *urb = subs->completed_urb; 5262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 5362306a36Sopenharmony_ci int i, lens = 0, hwptr_done = subs->hwptr_done; 5462306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 5562306a36Sopenharmony_ci int head; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (usx2y->hwdep_pcm_shm->capture_iso_start < 0) { //FIXME 5862306a36Sopenharmony_ci head = usx2y->hwdep_pcm_shm->captured_iso_head + 1; 5962306a36Sopenharmony_ci if (head >= ARRAY_SIZE(usx2y->hwdep_pcm_shm->captured_iso)) 6062306a36Sopenharmony_ci head = 0; 6162306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->capture_iso_start = head; 6262306a36Sopenharmony_ci snd_printdd("cap start %i\n", head); 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci for (i = 0; i < nr_of_packs(); i++) { 6562306a36Sopenharmony_ci if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ 6662306a36Sopenharmony_ci snd_printk(KERN_ERR 6762306a36Sopenharmony_ci "active frame status %i. Most probably some hardware problem.\n", 6862306a36Sopenharmony_ci urb->iso_frame_desc[i].status); 6962306a36Sopenharmony_ci return urb->iso_frame_desc[i].status; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci lens += urb->iso_frame_desc[i].actual_length / usx2y->stride; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci hwptr_done += lens; 7462306a36Sopenharmony_ci if (hwptr_done >= runtime->buffer_size) 7562306a36Sopenharmony_ci hwptr_done -= runtime->buffer_size; 7662306a36Sopenharmony_ci subs->hwptr_done = hwptr_done; 7762306a36Sopenharmony_ci subs->transfer_done += lens; 7862306a36Sopenharmony_ci /* update the pointer, call callback if necessary */ 7962306a36Sopenharmony_ci if (subs->transfer_done >= runtime->period_size) { 8062306a36Sopenharmony_ci subs->transfer_done -= runtime->period_size; 8162306a36Sopenharmony_ci snd_pcm_period_elapsed(subs->pcm_substream); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int usx2y_iso_frames_per_buffer(struct snd_pcm_runtime *runtime, 8762306a36Sopenharmony_ci struct usx2ydev *usx2y) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci return (runtime->buffer_size * 1000) / usx2y->rate + 1; //FIXME: so far only correct period_size == 2^x ? 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * prepare urb for playback data pipe 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * we copy the data directly from the pcm buffer. 9662306a36Sopenharmony_ci * the current position to be copied is held in hwptr field. 9762306a36Sopenharmony_ci * since a urb can handle only a single linear buffer, if the total 9862306a36Sopenharmony_ci * transferred area overflows the buffer boundary, we cannot send 9962306a36Sopenharmony_ci * it directly from the buffer. thus the data is once copied to 10062306a36Sopenharmony_ci * a temporary buffer and urb points to that. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_cistatic int usx2y_hwdep_urb_play_prepare(struct snd_usx2y_substream *subs, 10362306a36Sopenharmony_ci struct urb *urb) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int count, counts, pack; 10662306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 10762306a36Sopenharmony_ci struct snd_usx2y_hwdep_pcm_shm *shm = usx2y->hwdep_pcm_shm; 10862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (shm->playback_iso_start < 0) { 11162306a36Sopenharmony_ci shm->playback_iso_start = shm->captured_iso_head - 11262306a36Sopenharmony_ci usx2y_iso_frames_per_buffer(runtime, usx2y); 11362306a36Sopenharmony_ci if (shm->playback_iso_start < 0) 11462306a36Sopenharmony_ci shm->playback_iso_start += ARRAY_SIZE(shm->captured_iso); 11562306a36Sopenharmony_ci shm->playback_iso_head = shm->playback_iso_start; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci count = 0; 11962306a36Sopenharmony_ci for (pack = 0; pack < nr_of_packs(); pack++) { 12062306a36Sopenharmony_ci /* calculate the size of a packet */ 12162306a36Sopenharmony_ci counts = shm->captured_iso[shm->playback_iso_head].length / usx2y->stride; 12262306a36Sopenharmony_ci if (counts < 43 || counts > 50) { 12362306a36Sopenharmony_ci snd_printk(KERN_ERR "should not be here with counts=%i\n", counts); 12462306a36Sopenharmony_ci return -EPIPE; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci /* set up descriptor */ 12762306a36Sopenharmony_ci urb->iso_frame_desc[pack].offset = shm->captured_iso[shm->playback_iso_head].offset; 12862306a36Sopenharmony_ci urb->iso_frame_desc[pack].length = shm->captured_iso[shm->playback_iso_head].length; 12962306a36Sopenharmony_ci if (atomic_read(&subs->state) != STATE_RUNNING) 13062306a36Sopenharmony_ci memset((char *)urb->transfer_buffer + urb->iso_frame_desc[pack].offset, 0, 13162306a36Sopenharmony_ci urb->iso_frame_desc[pack].length); 13262306a36Sopenharmony_ci if (++shm->playback_iso_head >= ARRAY_SIZE(shm->captured_iso)) 13362306a36Sopenharmony_ci shm->playback_iso_head = 0; 13462306a36Sopenharmony_ci count += counts; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci urb->transfer_buffer_length = count * usx2y->stride; 13762306a36Sopenharmony_ci return 0; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void usx2y_usbpcm_urb_capt_iso_advance(struct snd_usx2y_substream *subs, 14162306a36Sopenharmony_ci struct urb *urb) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct usb_iso_packet_descriptor *desc; 14462306a36Sopenharmony_ci struct snd_usx2y_hwdep_pcm_shm *shm; 14562306a36Sopenharmony_ci int pack, head; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci for (pack = 0; pack < nr_of_packs(); ++pack) { 14862306a36Sopenharmony_ci desc = urb->iso_frame_desc + pack; 14962306a36Sopenharmony_ci if (subs) { 15062306a36Sopenharmony_ci shm = subs->usx2y->hwdep_pcm_shm; 15162306a36Sopenharmony_ci head = shm->captured_iso_head + 1; 15262306a36Sopenharmony_ci if (head >= ARRAY_SIZE(shm->captured_iso)) 15362306a36Sopenharmony_ci head = 0; 15462306a36Sopenharmony_ci shm->captured_iso[head].frame = urb->start_frame + pack; 15562306a36Sopenharmony_ci shm->captured_iso[head].offset = desc->offset; 15662306a36Sopenharmony_ci shm->captured_iso[head].length = desc->actual_length; 15762306a36Sopenharmony_ci shm->captured_iso_head = head; 15862306a36Sopenharmony_ci shm->captured_iso_frames++; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci desc->offset += desc->length * NRURBS * nr_of_packs(); 16162306a36Sopenharmony_ci if (desc->offset + desc->length >= SSS) 16262306a36Sopenharmony_ci desc->offset -= (SSS - desc->length); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic int usx2y_usbpcm_usbframe_complete(struct snd_usx2y_substream *capsubs, 16762306a36Sopenharmony_ci struct snd_usx2y_substream *capsubs2, 16862306a36Sopenharmony_ci struct snd_usx2y_substream *playbacksubs, 16962306a36Sopenharmony_ci int frame) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int err, state; 17262306a36Sopenharmony_ci struct urb *urb = playbacksubs->completed_urb; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci state = atomic_read(&playbacksubs->state); 17562306a36Sopenharmony_ci if (urb) { 17662306a36Sopenharmony_ci if (state == STATE_RUNNING) 17762306a36Sopenharmony_ci usx2y_urb_play_retire(playbacksubs, urb); 17862306a36Sopenharmony_ci else if (state >= STATE_PRERUNNING) 17962306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 18062306a36Sopenharmony_ci } else { 18162306a36Sopenharmony_ci switch (state) { 18262306a36Sopenharmony_ci case STATE_STARTING1: 18362306a36Sopenharmony_ci urb = playbacksubs->urb[0]; 18462306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci case STATE_STARTING2: 18762306a36Sopenharmony_ci urb = playbacksubs->urb[1]; 18862306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (urb) { 19362306a36Sopenharmony_ci err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb); 19462306a36Sopenharmony_ci if (err) 19562306a36Sopenharmony_ci return err; 19662306a36Sopenharmony_ci err = usx2y_hwdep_urb_play_prepare(playbacksubs, urb); 19762306a36Sopenharmony_ci if (err) 19862306a36Sopenharmony_ci return err; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci playbacksubs->completed_urb = NULL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci state = atomic_read(&capsubs->state); 20462306a36Sopenharmony_ci if (state >= STATE_PREPARED) { 20562306a36Sopenharmony_ci if (state == STATE_RUNNING) { 20662306a36Sopenharmony_ci err = usx2y_usbpcm_urb_capt_retire(capsubs); 20762306a36Sopenharmony_ci if (err) 20862306a36Sopenharmony_ci return err; 20962306a36Sopenharmony_ci } else if (state >= STATE_PRERUNNING) { 21062306a36Sopenharmony_ci atomic_inc(&capsubs->state); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci usx2y_usbpcm_urb_capt_iso_advance(capsubs, capsubs->completed_urb); 21362306a36Sopenharmony_ci if (capsubs2) 21462306a36Sopenharmony_ci usx2y_usbpcm_urb_capt_iso_advance(NULL, capsubs2->completed_urb); 21562306a36Sopenharmony_ci err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame); 21662306a36Sopenharmony_ci if (err) 21762306a36Sopenharmony_ci return err; 21862306a36Sopenharmony_ci if (capsubs2) { 21962306a36Sopenharmony_ci err = usx2y_urb_submit(capsubs2, capsubs2->completed_urb, frame); 22062306a36Sopenharmony_ci if (err) 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci capsubs->completed_urb = NULL; 22562306a36Sopenharmony_ci if (capsubs2) 22662306a36Sopenharmony_ci capsubs2->completed_urb = NULL; 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void i_usx2y_usbpcm_urb_complete(struct urb *urb) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct snd_usx2y_substream *subs = urb->context; 23362306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 23462306a36Sopenharmony_ci struct snd_usx2y_substream *capsubs, *capsubs2, *playbacksubs; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) { 23762306a36Sopenharmony_ci snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", 23862306a36Sopenharmony_ci usb_get_current_frame_number(usx2y->dev), 23962306a36Sopenharmony_ci subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", 24062306a36Sopenharmony_ci urb->status, urb->start_frame); 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci if (unlikely(urb->status)) { 24462306a36Sopenharmony_ci usx2y_error_urb_status(usx2y, subs, urb); 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci subs->completed_urb = urb; 24962306a36Sopenharmony_ci capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 25062306a36Sopenharmony_ci capsubs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 25162306a36Sopenharmony_ci playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 25262306a36Sopenharmony_ci if (capsubs->completed_urb && atomic_read(&capsubs->state) >= STATE_PREPARED && 25362306a36Sopenharmony_ci (!capsubs2 || capsubs2->completed_urb) && 25462306a36Sopenharmony_ci (playbacksubs->completed_urb || atomic_read(&playbacksubs->state) < STATE_PREPARED)) { 25562306a36Sopenharmony_ci if (!usx2y_usbpcm_usbframe_complete(capsubs, capsubs2, playbacksubs, urb->start_frame)) { 25662306a36Sopenharmony_ci usx2y->wait_iso_frame += nr_of_packs(); 25762306a36Sopenharmony_ci } else { 25862306a36Sopenharmony_ci snd_printdd("\n"); 25962306a36Sopenharmony_ci usx2y_clients_stop(usx2y); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic void usx2y_hwdep_urb_release(struct urb **urb) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci usb_kill_urb(*urb); 26762306a36Sopenharmony_ci usb_free_urb(*urb); 26862306a36Sopenharmony_ci *urb = NULL; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * release a substream 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_cistatic void usx2y_usbpcm_urbs_release(struct snd_usx2y_substream *subs) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci snd_printdd("snd_usx2y_urbs_release() %i\n", subs->endpoint); 27962306a36Sopenharmony_ci for (i = 0; i < NRURBS; i++) 28062306a36Sopenharmony_ci usx2y_hwdep_urb_release(subs->urb + i); 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void usx2y_usbpcm_subs_startup_finish(struct usx2ydev *usx2y) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_urb_complete); 28662306a36Sopenharmony_ci usx2y->prepare_subs = NULL; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void i_usx2y_usbpcm_subs_startup(struct urb *urb) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct snd_usx2y_substream *subs = urb->context; 29262306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 29362306a36Sopenharmony_ci struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs; 29462306a36Sopenharmony_ci struct snd_usx2y_substream *cap_subs2; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (prepare_subs && 29762306a36Sopenharmony_ci urb->start_frame == prepare_subs->urb[0]->start_frame) { 29862306a36Sopenharmony_ci atomic_inc(&prepare_subs->state); 29962306a36Sopenharmony_ci if (prepare_subs == usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]) { 30062306a36Sopenharmony_ci cap_subs2 = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 30162306a36Sopenharmony_ci if (cap_subs2) 30262306a36Sopenharmony_ci atomic_inc(&cap_subs2->state); 30362306a36Sopenharmony_ci } 30462306a36Sopenharmony_ci usx2y_usbpcm_subs_startup_finish(usx2y); 30562306a36Sopenharmony_ci wake_up(&usx2y->prepare_wait_queue); 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci i_usx2y_usbpcm_urb_complete(urb); 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci/* 31262306a36Sopenharmony_ci * initialize a substream's urbs 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci int i; 31762306a36Sopenharmony_ci unsigned int pipe; 31862306a36Sopenharmony_ci int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 31962306a36Sopenharmony_ci struct usb_device *dev = subs->usx2y->dev; 32062306a36Sopenharmony_ci struct urb **purb; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) : 32362306a36Sopenharmony_ci usb_rcvisocpipe(dev, subs->endpoint); 32462306a36Sopenharmony_ci subs->maxpacksize = usb_maxpacket(dev, pipe); 32562306a36Sopenharmony_ci if (!subs->maxpacksize) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* allocate and initialize data urbs */ 32962306a36Sopenharmony_ci for (i = 0; i < NRURBS; i++) { 33062306a36Sopenharmony_ci purb = subs->urb + i; 33162306a36Sopenharmony_ci if (*purb) { 33262306a36Sopenharmony_ci usb_kill_urb(*purb); 33362306a36Sopenharmony_ci continue; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL); 33662306a36Sopenharmony_ci if (!*purb) { 33762306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(subs); 33862306a36Sopenharmony_ci return -ENOMEM; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci (*purb)->transfer_buffer = is_playback ? 34162306a36Sopenharmony_ci subs->usx2y->hwdep_pcm_shm->playback : ( 34262306a36Sopenharmony_ci subs->endpoint == 0x8 ? 34362306a36Sopenharmony_ci subs->usx2y->hwdep_pcm_shm->capture0x8 : 34462306a36Sopenharmony_ci subs->usx2y->hwdep_pcm_shm->capture0xA); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci (*purb)->dev = dev; 34762306a36Sopenharmony_ci (*purb)->pipe = pipe; 34862306a36Sopenharmony_ci (*purb)->number_of_packets = nr_of_packs(); 34962306a36Sopenharmony_ci (*purb)->context = subs; 35062306a36Sopenharmony_ci (*purb)->interval = 1; 35162306a36Sopenharmony_ci (*purb)->complete = i_usx2y_usbpcm_subs_startup; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * free the buffer 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic int snd_usx2y_usbpcm_hw_free(struct snd_pcm_substream *substream) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 36262306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 36362306a36Sopenharmony_ci struct snd_usx2y_substream *cap_subs; 36462306a36Sopenharmony_ci struct snd_usx2y_substream *playback_subs; 36562306a36Sopenharmony_ci struct snd_usx2y_substream *cap_subs2; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci mutex_lock(&subs->usx2y->pcm_mutex); 36862306a36Sopenharmony_ci snd_printdd("%s(%p)\n", __func__, substream); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci cap_subs2 = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; 37162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 37262306a36Sopenharmony_ci cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 37362306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STOPPED); 37462306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(subs); 37562306a36Sopenharmony_ci if (!cap_subs->pcm_substream || 37662306a36Sopenharmony_ci !cap_subs->pcm_substream->runtime || 37762306a36Sopenharmony_ci cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) { 37862306a36Sopenharmony_ci atomic_set(&cap_subs->state, STATE_STOPPED); 37962306a36Sopenharmony_ci if (cap_subs2) 38062306a36Sopenharmony_ci atomic_set(&cap_subs2->state, STATE_STOPPED); 38162306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(cap_subs); 38262306a36Sopenharmony_ci if (cap_subs2) 38362306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(cap_subs2); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 38762306a36Sopenharmony_ci if (atomic_read(&playback_subs->state) < STATE_PREPARED) { 38862306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STOPPED); 38962306a36Sopenharmony_ci if (cap_subs2) 39062306a36Sopenharmony_ci atomic_set(&cap_subs2->state, STATE_STOPPED); 39162306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(subs); 39262306a36Sopenharmony_ci if (cap_subs2) 39362306a36Sopenharmony_ci usx2y_usbpcm_urbs_release(cap_subs2); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci mutex_unlock(&subs->usx2y->pcm_mutex); 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic void usx2y_usbpcm_subs_startup(struct snd_usx2y_substream *subs) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci usx2y->prepare_subs = subs; 40562306a36Sopenharmony_ci subs->urb[0]->start_frame = -1; 40662306a36Sopenharmony_ci smp_wmb(); // Make sure above modifications are seen by i_usx2y_subs_startup() 40762306a36Sopenharmony_ci usx2y_urbs_set_complete(usx2y, i_usx2y_usbpcm_subs_startup); 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int usx2y_usbpcm_urbs_start(struct snd_usx2y_substream *subs) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci int p, u, err, stream = subs->pcm_substream->stream; 41362306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 41462306a36Sopenharmony_ci struct urb *urb; 41562306a36Sopenharmony_ci unsigned long pack; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (stream == SNDRV_PCM_STREAM_CAPTURE) { 41862306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->captured_iso_head = -1; 41962306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->captured_iso_frames = 0; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci for (p = 0; 3 >= (stream + p); p += 2) { 42362306a36Sopenharmony_ci struct snd_usx2y_substream *subs = usx2y->subs[stream + p]; 42462306a36Sopenharmony_ci if (subs) { 42562306a36Sopenharmony_ci err = usx2y_usbpcm_urbs_allocate(subs); 42662306a36Sopenharmony_ci if (err < 0) 42762306a36Sopenharmony_ci return err; 42862306a36Sopenharmony_ci subs->completed_urb = NULL; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci for (p = 0; p < 4; p++) { 43362306a36Sopenharmony_ci struct snd_usx2y_substream *subs = usx2y->subs[p]; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (subs && atomic_read(&subs->state) >= STATE_PREPARED) 43662306a36Sopenharmony_ci goto start; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci start: 44062306a36Sopenharmony_ci usx2y_usbpcm_subs_startup(subs); 44162306a36Sopenharmony_ci for (u = 0; u < NRURBS; u++) { 44262306a36Sopenharmony_ci for (p = 0; 3 >= (stream + p); p += 2) { 44362306a36Sopenharmony_ci struct snd_usx2y_substream *subs = usx2y->subs[stream + p]; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci if (!subs) 44662306a36Sopenharmony_ci continue; 44762306a36Sopenharmony_ci urb = subs->urb[u]; 44862306a36Sopenharmony_ci if (usb_pipein(urb->pipe)) { 44962306a36Sopenharmony_ci if (!u) 45062306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STARTING3); 45162306a36Sopenharmony_ci urb->dev = usx2y->dev; 45262306a36Sopenharmony_ci for (pack = 0; pack < nr_of_packs(); pack++) { 45362306a36Sopenharmony_ci urb->iso_frame_desc[pack].offset = subs->maxpacksize * (pack + u * nr_of_packs()); 45462306a36Sopenharmony_ci urb->iso_frame_desc[pack].length = subs->maxpacksize; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 45762306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_KERNEL); 45862306a36Sopenharmony_ci if (err < 0) { 45962306a36Sopenharmony_ci snd_printk(KERN_ERR "cannot usb_submit_urb() for urb %d, err = %d\n", u, err); 46062306a36Sopenharmony_ci err = -EPIPE; 46162306a36Sopenharmony_ci goto cleanup; 46262306a36Sopenharmony_ci } else { 46362306a36Sopenharmony_ci snd_printdd("%i\n", urb->start_frame); 46462306a36Sopenharmony_ci if (!u) 46562306a36Sopenharmony_ci usx2y->wait_iso_frame = urb->start_frame; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci urb->transfer_flags = 0; 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STARTING1); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci err = 0; 47562306a36Sopenharmony_ci wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs); 47662306a36Sopenharmony_ci if (atomic_read(&subs->state) != STATE_PREPARED) 47762306a36Sopenharmony_ci err = -EPIPE; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci cleanup: 48062306a36Sopenharmony_ci if (err) { 48162306a36Sopenharmony_ci usx2y_subs_startup_finish(usx2y); // Call it now 48262306a36Sopenharmony_ci usx2y_clients_stop(usx2y); // something is completely wrong > stop everything 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci return err; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#define USX2Y_HWDEP_PCM_PAGES \ 48862306a36Sopenharmony_ci PAGE_ALIGN(sizeof(struct snd_usx2y_hwdep_pcm_shm)) 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci/* 49162306a36Sopenharmony_ci * prepare callback 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * set format and initialize urbs 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistatic int snd_usx2y_usbpcm_prepare(struct snd_pcm_substream *substream) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 49862306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 49962306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 50062306a36Sopenharmony_ci struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 50162306a36Sopenharmony_ci int err = 0; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci snd_printdd("snd_usx2y_pcm_prepare(%p)\n", substream); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci mutex_lock(&usx2y->pcm_mutex); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!usx2y->hwdep_pcm_shm) { 50862306a36Sopenharmony_ci usx2y->hwdep_pcm_shm = alloc_pages_exact(USX2Y_HWDEP_PCM_PAGES, 50962306a36Sopenharmony_ci GFP_KERNEL); 51062306a36Sopenharmony_ci if (!usx2y->hwdep_pcm_shm) { 51162306a36Sopenharmony_ci err = -ENOMEM; 51262306a36Sopenharmony_ci goto up_prepare_mutex; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci memset(usx2y->hwdep_pcm_shm, 0, USX2Y_HWDEP_PCM_PAGES); 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci usx2y_subs_prepare(subs); 51862306a36Sopenharmony_ci // Start hardware streams 51962306a36Sopenharmony_ci // SyncStream first.... 52062306a36Sopenharmony_ci if (atomic_read(&capsubs->state) < STATE_PREPARED) { 52162306a36Sopenharmony_ci if (usx2y->format != runtime->format) { 52262306a36Sopenharmony_ci err = usx2y_format_set(usx2y, runtime->format); 52362306a36Sopenharmony_ci if (err < 0) 52462306a36Sopenharmony_ci goto up_prepare_mutex; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci if (usx2y->rate != runtime->rate) { 52762306a36Sopenharmony_ci err = usx2y_rate_set(usx2y, runtime->rate); 52862306a36Sopenharmony_ci if (err < 0) 52962306a36Sopenharmony_ci goto up_prepare_mutex; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci snd_printdd("starting capture pipe for %s\n", subs == capsubs ? 53262306a36Sopenharmony_ci "self" : "playpipe"); 53362306a36Sopenharmony_ci err = usx2y_usbpcm_urbs_start(capsubs); 53462306a36Sopenharmony_ci if (err < 0) 53562306a36Sopenharmony_ci goto up_prepare_mutex; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (subs != capsubs) { 53962306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->playback_iso_start = -1; 54062306a36Sopenharmony_ci if (atomic_read(&subs->state) < STATE_PREPARED) { 54162306a36Sopenharmony_ci while (usx2y_iso_frames_per_buffer(runtime, usx2y) > 54262306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->captured_iso_frames) { 54362306a36Sopenharmony_ci snd_printdd("Wait: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", 54462306a36Sopenharmony_ci usx2y_iso_frames_per_buffer(runtime, usx2y), 54562306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->captured_iso_frames); 54662306a36Sopenharmony_ci if (msleep_interruptible(10)) { 54762306a36Sopenharmony_ci err = -ERESTARTSYS; 54862306a36Sopenharmony_ci goto up_prepare_mutex; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci err = usx2y_usbpcm_urbs_start(subs); 55262306a36Sopenharmony_ci if (err < 0) 55362306a36Sopenharmony_ci goto up_prepare_mutex; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci snd_printdd("Ready: iso_frames_per_buffer=%i,captured_iso_frames=%i\n", 55662306a36Sopenharmony_ci usx2y_iso_frames_per_buffer(runtime, usx2y), 55762306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->captured_iso_frames); 55862306a36Sopenharmony_ci } else { 55962306a36Sopenharmony_ci usx2y->hwdep_pcm_shm->capture_iso_start = -1; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci up_prepare_mutex: 56362306a36Sopenharmony_ci mutex_unlock(&usx2y->pcm_mutex); 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_usx2y_4c = { 56862306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 56962306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 57062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 57162306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, 57262306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 57362306a36Sopenharmony_ci .rate_min = 44100, 57462306a36Sopenharmony_ci .rate_max = 48000, 57562306a36Sopenharmony_ci .channels_min = 2, 57662306a36Sopenharmony_ci .channels_max = 4, 57762306a36Sopenharmony_ci .buffer_bytes_max = (2*128*1024), 57862306a36Sopenharmony_ci .period_bytes_min = 64, 57962306a36Sopenharmony_ci .period_bytes_max = (128*1024), 58062306a36Sopenharmony_ci .periods_min = 2, 58162306a36Sopenharmony_ci .periods_max = 1024, 58262306a36Sopenharmony_ci .fifo_size = 0 58362306a36Sopenharmony_ci}; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int snd_usx2y_usbpcm_open(struct snd_pcm_substream *substream) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct snd_usx2y_substream *subs = 58862306a36Sopenharmony_ci ((struct snd_usx2y_substream **) 58962306a36Sopenharmony_ci snd_pcm_substream_chip(substream))[substream->stream]; 59062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (!(subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS)) 59362306a36Sopenharmony_ci return -EBUSY; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 59662306a36Sopenharmony_ci runtime->hw = snd_usx2y_2c; 59762306a36Sopenharmony_ci else 59862306a36Sopenharmony_ci runtime->hw = (subs->usx2y->subs[3] ? snd_usx2y_4c : snd_usx2y_2c); 59962306a36Sopenharmony_ci runtime->private_data = subs; 60062306a36Sopenharmony_ci subs->pcm_substream = substream; 60162306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int snd_usx2y_usbpcm_close(struct snd_pcm_substream *substream) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 60862306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci subs->pcm_substream = NULL; 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_usx2y_usbpcm_ops = { 61562306a36Sopenharmony_ci .open = snd_usx2y_usbpcm_open, 61662306a36Sopenharmony_ci .close = snd_usx2y_usbpcm_close, 61762306a36Sopenharmony_ci .hw_params = snd_usx2y_pcm_hw_params, 61862306a36Sopenharmony_ci .hw_free = snd_usx2y_usbpcm_hw_free, 61962306a36Sopenharmony_ci .prepare = snd_usx2y_usbpcm_prepare, 62062306a36Sopenharmony_ci .trigger = snd_usx2y_pcm_trigger, 62162306a36Sopenharmony_ci .pointer = snd_usx2y_pcm_pointer, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int usx2y_pcms_busy_check(struct snd_card *card) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct usx2ydev *dev = usx2y(card); 62762306a36Sopenharmony_ci struct snd_usx2y_substream *subs; 62862306a36Sopenharmony_ci int i; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci for (i = 0; i < dev->pcm_devs * 2; i++) { 63162306a36Sopenharmony_ci subs = dev->subs[i]; 63262306a36Sopenharmony_ci if (subs && subs->pcm_substream && 63362306a36Sopenharmony_ci SUBSTREAM_BUSY(subs->pcm_substream)) 63462306a36Sopenharmony_ci return -EBUSY; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci return 0; 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic int snd_usx2y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct snd_card *card = hw->card; 64262306a36Sopenharmony_ci int err; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci mutex_lock(&usx2y(card)->pcm_mutex); 64562306a36Sopenharmony_ci err = usx2y_pcms_busy_check(card); 64662306a36Sopenharmony_ci if (!err) 64762306a36Sopenharmony_ci usx2y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS; 64862306a36Sopenharmony_ci mutex_unlock(&usx2y(card)->pcm_mutex); 64962306a36Sopenharmony_ci return err; 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int snd_usx2y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct snd_card *card = hw->card; 65562306a36Sopenharmony_ci int err; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mutex_lock(&usx2y(card)->pcm_mutex); 65862306a36Sopenharmony_ci err = usx2y_pcms_busy_check(card); 65962306a36Sopenharmony_ci if (!err) 66062306a36Sopenharmony_ci usx2y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS; 66162306a36Sopenharmony_ci mutex_unlock(&usx2y(card)->pcm_mutex); 66262306a36Sopenharmony_ci return err; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_cistatic void snd_usx2y_hwdep_pcm_vm_open(struct vm_area_struct *area) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic void snd_usx2y_hwdep_pcm_vm_close(struct vm_area_struct *area) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic vm_fault_t snd_usx2y_hwdep_pcm_vm_fault(struct vm_fault *vmf) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci unsigned long offset; 67662306a36Sopenharmony_ci void *vaddr; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 67962306a36Sopenharmony_ci vaddr = (char *)((struct usx2ydev *)vmf->vma->vm_private_data)->hwdep_pcm_shm + offset; 68062306a36Sopenharmony_ci vmf->page = virt_to_page(vaddr); 68162306a36Sopenharmony_ci get_page(vmf->page); 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic const struct vm_operations_struct snd_usx2y_hwdep_pcm_vm_ops = { 68662306a36Sopenharmony_ci .open = snd_usx2y_hwdep_pcm_vm_open, 68762306a36Sopenharmony_ci .close = snd_usx2y_hwdep_pcm_vm_close, 68862306a36Sopenharmony_ci .fault = snd_usx2y_hwdep_pcm_vm_fault, 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int snd_usx2y_hwdep_pcm_mmap(struct snd_hwdep *hw, struct file *filp, struct vm_area_struct *area) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci unsigned long size = (unsigned long)(area->vm_end - area->vm_start); 69462306a36Sopenharmony_ci struct usx2ydev *usx2y = hw->private_data; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (!(usx2y->chip_status & USX2Y_STAT_CHIP_INIT)) 69762306a36Sopenharmony_ci return -EBUSY; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* if userspace tries to mmap beyond end of our buffer, fail */ 70062306a36Sopenharmony_ci if (size > USX2Y_HWDEP_PCM_PAGES) { 70162306a36Sopenharmony_ci snd_printd("%lu > %lu\n", size, (unsigned long)USX2Y_HWDEP_PCM_PAGES); 70262306a36Sopenharmony_ci return -EINVAL; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (!usx2y->hwdep_pcm_shm) 70662306a36Sopenharmony_ci return -ENODEV; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci area->vm_ops = &snd_usx2y_hwdep_pcm_vm_ops; 70962306a36Sopenharmony_ci vm_flags_set(area, VM_DONTEXPAND | VM_DONTDUMP); 71062306a36Sopenharmony_ci area->vm_private_data = hw->private_data; 71162306a36Sopenharmony_ci return 0; 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void snd_usx2y_hwdep_pcm_private_free(struct snd_hwdep *hwdep) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct usx2ydev *usx2y = hwdep->private_data; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (usx2y->hwdep_pcm_shm) 71962306a36Sopenharmony_ci free_pages_exact(usx2y->hwdep_pcm_shm, USX2Y_HWDEP_PCM_PAGES); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ciint usx2y_hwdep_pcm_new(struct snd_card *card) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci int err; 72562306a36Sopenharmony_ci struct snd_hwdep *hw; 72662306a36Sopenharmony_ci struct snd_pcm *pcm; 72762306a36Sopenharmony_ci struct usb_device *dev = usx2y(card)->dev; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (nr_of_packs() != 1) 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci err = snd_hwdep_new(card, SND_USX2Y_USBPCM_ID, 1, &hw); 73362306a36Sopenharmony_ci if (err < 0) 73462306a36Sopenharmony_ci return err; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_USX2Y_PCM; 73762306a36Sopenharmony_ci hw->private_data = usx2y(card); 73862306a36Sopenharmony_ci hw->private_free = snd_usx2y_hwdep_pcm_private_free; 73962306a36Sopenharmony_ci hw->ops.open = snd_usx2y_hwdep_pcm_open; 74062306a36Sopenharmony_ci hw->ops.release = snd_usx2y_hwdep_pcm_release; 74162306a36Sopenharmony_ci hw->ops.mmap = snd_usx2y_hwdep_pcm_mmap; 74262306a36Sopenharmony_ci hw->exclusive = 1; 74362306a36Sopenharmony_ci sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm); 74662306a36Sopenharmony_ci if (err < 0) 74762306a36Sopenharmony_ci return err; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_usbpcm_ops); 75062306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_usbpcm_ops); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci pcm->private_data = usx2y(card)->subs; 75362306a36Sopenharmony_ci pcm->info_flags = 0; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci sprintf(pcm->name, NAME_ALLCAPS" hwdep Audio"); 75662306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 75762306a36Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 75862306a36Sopenharmony_ci NULL, 75962306a36Sopenharmony_ci 64*1024, 128*1024); 76062306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 76162306a36Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 76262306a36Sopenharmony_ci NULL, 76362306a36Sopenharmony_ci 64*1024, 128*1024); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci#else 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ciint usx2y_hwdep_pcm_new(struct snd_card *card) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci return 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci#endif 776