162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * US-X2Y AUDIO 462306a36Sopenharmony_ci * Copyright (c) 2002-2004 by Karsten Wiese 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * based on 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * (Tentative) USB Audio Driver for ALSA 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Main and PCM part 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Many codes borrowed from audio.c by 1562306a36Sopenharmony_ci * Alan Cox (alan@lxorguk.ukuu.org.uk) 1662306a36Sopenharmony_ci * Thomas Sailer (sailer@ife.ee.ethz.ch) 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/usb.h> 2362306a36Sopenharmony_ci#include <linux/moduleparam.h> 2462306a36Sopenharmony_ci#include <sound/core.h> 2562306a36Sopenharmony_ci#include <sound/info.h> 2662306a36Sopenharmony_ci#include <sound/pcm.h> 2762306a36Sopenharmony_ci#include <sound/pcm_params.h> 2862306a36Sopenharmony_ci#include "usx2y.h" 2962306a36Sopenharmony_ci#include "usbusx2y.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* Default value used for nr of packs per urb. 3262306a36Sopenharmony_ci * 1 to 4 have been tested ok on uhci. 3362306a36Sopenharmony_ci * To use 3 on ohci, you'd need a patch: 3462306a36Sopenharmony_ci * look for "0000425-linux-2.6.9-rc4-mm1_ohci-hcd.patch.gz" on 3562306a36Sopenharmony_ci * "https://bugtrack.alsa-project.org/alsa-bug/bug_view_page.php?bug_id=0000425" 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * 1, 2 and 4 work out of the box on ohci, if I recall correctly. 3862306a36Sopenharmony_ci * Bigger is safer operation, smaller gives lower latencies. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci#define USX2Y_NRPACKS 4 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* If your system works ok with this module's parameter 4362306a36Sopenharmony_ci * nrpacks set to 1, you might as well comment 4462306a36Sopenharmony_ci * this define out, and thereby produce smaller, faster code. 4562306a36Sopenharmony_ci * You'd also set USX2Y_NRPACKS to 1 then. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci#define USX2Y_NRPACKS_VARIABLE 1 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#ifdef USX2Y_NRPACKS_VARIABLE 5062306a36Sopenharmony_cistatic int nrpacks = USX2Y_NRPACKS; /* number of packets per urb */ 5162306a36Sopenharmony_ci#define nr_of_packs() nrpacks 5262306a36Sopenharmony_cimodule_param(nrpacks, int, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(nrpacks, "Number of packets per URB."); 5462306a36Sopenharmony_ci#else 5562306a36Sopenharmony_ci#define nr_of_packs() USX2Y_NRPACKS 5662306a36Sopenharmony_ci#endif 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int usx2y_urb_capt_retire(struct snd_usx2y_substream *subs) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct urb *urb = subs->completed_urb; 6162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 6262306a36Sopenharmony_ci unsigned char *cp; 6362306a36Sopenharmony_ci int i, len, lens = 0, hwptr_done = subs->hwptr_done; 6462306a36Sopenharmony_ci int cnt, blen; 6562306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci for (i = 0; i < nr_of_packs(); i++) { 6862306a36Sopenharmony_ci cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; 6962306a36Sopenharmony_ci if (urb->iso_frame_desc[i].status) { /* active? hmm, skip this */ 7062306a36Sopenharmony_ci snd_printk(KERN_ERR 7162306a36Sopenharmony_ci "active frame status %i. Most probably some hardware problem.\n", 7262306a36Sopenharmony_ci urb->iso_frame_desc[i].status); 7362306a36Sopenharmony_ci return urb->iso_frame_desc[i].status; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci len = urb->iso_frame_desc[i].actual_length / usx2y->stride; 7662306a36Sopenharmony_ci if (!len) { 7762306a36Sopenharmony_ci snd_printd("0 == len ERROR!\n"); 7862306a36Sopenharmony_ci continue; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* copy a data chunk */ 8262306a36Sopenharmony_ci if ((hwptr_done + len) > runtime->buffer_size) { 8362306a36Sopenharmony_ci cnt = runtime->buffer_size - hwptr_done; 8462306a36Sopenharmony_ci blen = cnt * usx2y->stride; 8562306a36Sopenharmony_ci memcpy(runtime->dma_area + hwptr_done * usx2y->stride, cp, blen); 8662306a36Sopenharmony_ci memcpy(runtime->dma_area, cp + blen, len * usx2y->stride - blen); 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci memcpy(runtime->dma_area + hwptr_done * usx2y->stride, cp, 8962306a36Sopenharmony_ci len * usx2y->stride); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci lens += len; 9262306a36Sopenharmony_ci hwptr_done += len; 9362306a36Sopenharmony_ci if (hwptr_done >= runtime->buffer_size) 9462306a36Sopenharmony_ci hwptr_done -= runtime->buffer_size; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci subs->hwptr_done = hwptr_done; 9862306a36Sopenharmony_ci subs->transfer_done += lens; 9962306a36Sopenharmony_ci /* update the pointer, call callback if necessary */ 10062306a36Sopenharmony_ci if (subs->transfer_done >= runtime->period_size) { 10162306a36Sopenharmony_ci subs->transfer_done -= runtime->period_size; 10262306a36Sopenharmony_ci snd_pcm_period_elapsed(subs->pcm_substream); 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * prepare urb for playback data pipe 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * we copy the data directly from the pcm buffer. 11162306a36Sopenharmony_ci * the current position to be copied is held in hwptr field. 11262306a36Sopenharmony_ci * since a urb can handle only a single linear buffer, if the total 11362306a36Sopenharmony_ci * transferred area overflows the buffer boundary, we cannot send 11462306a36Sopenharmony_ci * it directly from the buffer. thus the data is once copied to 11562306a36Sopenharmony_ci * a temporary buffer and urb points to that. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_cistatic int usx2y_urb_play_prepare(struct snd_usx2y_substream *subs, 11862306a36Sopenharmony_ci struct urb *cap_urb, 11962306a36Sopenharmony_ci struct urb *urb) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 12262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 12362306a36Sopenharmony_ci int count, counts, pack, len; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci count = 0; 12662306a36Sopenharmony_ci for (pack = 0; pack < nr_of_packs(); pack++) { 12762306a36Sopenharmony_ci /* calculate the size of a packet */ 12862306a36Sopenharmony_ci counts = cap_urb->iso_frame_desc[pack].actual_length / usx2y->stride; 12962306a36Sopenharmony_ci count += counts; 13062306a36Sopenharmony_ci if (counts < 43 || counts > 50) { 13162306a36Sopenharmony_ci snd_printk(KERN_ERR "should not be here with counts=%i\n", counts); 13262306a36Sopenharmony_ci return -EPIPE; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci /* set up descriptor */ 13562306a36Sopenharmony_ci urb->iso_frame_desc[pack].offset = pack ? 13662306a36Sopenharmony_ci urb->iso_frame_desc[pack - 1].offset + 13762306a36Sopenharmony_ci urb->iso_frame_desc[pack - 1].length : 13862306a36Sopenharmony_ci 0; 13962306a36Sopenharmony_ci urb->iso_frame_desc[pack].length = cap_urb->iso_frame_desc[pack].actual_length; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci if (atomic_read(&subs->state) >= STATE_PRERUNNING) { 14262306a36Sopenharmony_ci if (subs->hwptr + count > runtime->buffer_size) { 14362306a36Sopenharmony_ci /* err, the transferred area goes over buffer boundary. 14462306a36Sopenharmony_ci * copy the data to the temp buffer. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci len = runtime->buffer_size - subs->hwptr; 14762306a36Sopenharmony_ci urb->transfer_buffer = subs->tmpbuf; 14862306a36Sopenharmony_ci memcpy(subs->tmpbuf, runtime->dma_area + 14962306a36Sopenharmony_ci subs->hwptr * usx2y->stride, len * usx2y->stride); 15062306a36Sopenharmony_ci memcpy(subs->tmpbuf + len * usx2y->stride, 15162306a36Sopenharmony_ci runtime->dma_area, (count - len) * usx2y->stride); 15262306a36Sopenharmony_ci subs->hwptr += count; 15362306a36Sopenharmony_ci subs->hwptr -= runtime->buffer_size; 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci /* set the buffer pointer */ 15662306a36Sopenharmony_ci urb->transfer_buffer = runtime->dma_area + subs->hwptr * usx2y->stride; 15762306a36Sopenharmony_ci subs->hwptr += count; 15862306a36Sopenharmony_ci if (subs->hwptr >= runtime->buffer_size) 15962306a36Sopenharmony_ci subs->hwptr -= runtime->buffer_size; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci urb->transfer_buffer = subs->tmpbuf; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci urb->transfer_buffer_length = count * usx2y->stride; 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * process after playback data complete 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * update the current position and call callback if a period is processed. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void usx2y_urb_play_retire(struct snd_usx2y_substream *subs, struct urb *urb) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime; 17662306a36Sopenharmony_ci int len = urb->actual_length / subs->usx2y->stride; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci subs->transfer_done += len; 17962306a36Sopenharmony_ci subs->hwptr_done += len; 18062306a36Sopenharmony_ci if (subs->hwptr_done >= runtime->buffer_size) 18162306a36Sopenharmony_ci subs->hwptr_done -= runtime->buffer_size; 18262306a36Sopenharmony_ci if (subs->transfer_done >= runtime->period_size) { 18362306a36Sopenharmony_ci subs->transfer_done -= runtime->period_size; 18462306a36Sopenharmony_ci snd_pcm_period_elapsed(subs->pcm_substream); 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic int usx2y_urb_submit(struct snd_usx2y_substream *subs, struct urb *urb, int frame) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci int err; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!urb) 19362306a36Sopenharmony_ci return -ENODEV; 19462306a36Sopenharmony_ci urb->start_frame = frame + NRURBS * nr_of_packs(); // let hcd do rollover sanity checks 19562306a36Sopenharmony_ci urb->hcpriv = NULL; 19662306a36Sopenharmony_ci urb->dev = subs->usx2y->dev; /* we need to set this at each time */ 19762306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 19862306a36Sopenharmony_ci if (err < 0) { 19962306a36Sopenharmony_ci snd_printk(KERN_ERR "usb_submit_urb() returned %i\n", err); 20062306a36Sopenharmony_ci return err; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int usx2y_usbframe_complete(struct snd_usx2y_substream *capsubs, 20662306a36Sopenharmony_ci struct snd_usx2y_substream *playbacksubs, 20762306a36Sopenharmony_ci int frame) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci int err, state; 21062306a36Sopenharmony_ci struct urb *urb = playbacksubs->completed_urb; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci state = atomic_read(&playbacksubs->state); 21362306a36Sopenharmony_ci if (urb) { 21462306a36Sopenharmony_ci if (state == STATE_RUNNING) 21562306a36Sopenharmony_ci usx2y_urb_play_retire(playbacksubs, urb); 21662306a36Sopenharmony_ci else if (state >= STATE_PRERUNNING) 21762306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 21862306a36Sopenharmony_ci } else { 21962306a36Sopenharmony_ci switch (state) { 22062306a36Sopenharmony_ci case STATE_STARTING1: 22162306a36Sopenharmony_ci urb = playbacksubs->urb[0]; 22262306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci case STATE_STARTING2: 22562306a36Sopenharmony_ci urb = playbacksubs->urb[1]; 22662306a36Sopenharmony_ci atomic_inc(&playbacksubs->state); 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci if (urb) { 23162306a36Sopenharmony_ci err = usx2y_urb_play_prepare(playbacksubs, capsubs->completed_urb, urb); 23262306a36Sopenharmony_ci if (err) 23362306a36Sopenharmony_ci return err; 23462306a36Sopenharmony_ci err = usx2y_urb_submit(playbacksubs, urb, frame); 23562306a36Sopenharmony_ci if (err) 23662306a36Sopenharmony_ci return err; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci playbacksubs->completed_urb = NULL; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci state = atomic_read(&capsubs->state); 24262306a36Sopenharmony_ci if (state >= STATE_PREPARED) { 24362306a36Sopenharmony_ci if (state == STATE_RUNNING) { 24462306a36Sopenharmony_ci err = usx2y_urb_capt_retire(capsubs); 24562306a36Sopenharmony_ci if (err) 24662306a36Sopenharmony_ci return err; 24762306a36Sopenharmony_ci } else if (state >= STATE_PRERUNNING) { 24862306a36Sopenharmony_ci atomic_inc(&capsubs->state); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci err = usx2y_urb_submit(capsubs, capsubs->completed_urb, frame); 25162306a36Sopenharmony_ci if (err) 25262306a36Sopenharmony_ci return err; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci capsubs->completed_urb = NULL; 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic void usx2y_clients_stop(struct usx2ydev *usx2y) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct snd_usx2y_substream *subs; 26162306a36Sopenharmony_ci struct urb *urb; 26262306a36Sopenharmony_ci int s, u; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (s = 0; s < 4; s++) { 26562306a36Sopenharmony_ci subs = usx2y->subs[s]; 26662306a36Sopenharmony_ci if (subs) { 26762306a36Sopenharmony_ci snd_printdd("%i %p state=%i\n", s, subs, atomic_read(&subs->state)); 26862306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STOPPED); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci for (s = 0; s < 4; s++) { 27262306a36Sopenharmony_ci subs = usx2y->subs[s]; 27362306a36Sopenharmony_ci if (subs) { 27462306a36Sopenharmony_ci if (atomic_read(&subs->state) >= STATE_PRERUNNING) 27562306a36Sopenharmony_ci snd_pcm_stop_xrun(subs->pcm_substream); 27662306a36Sopenharmony_ci for (u = 0; u < NRURBS; u++) { 27762306a36Sopenharmony_ci urb = subs->urb[u]; 27862306a36Sopenharmony_ci if (urb) 27962306a36Sopenharmony_ci snd_printdd("%i status=%i start_frame=%i\n", 28062306a36Sopenharmony_ci u, urb->status, urb->start_frame); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci usx2y->prepare_subs = NULL; 28562306a36Sopenharmony_ci wake_up(&usx2y->prepare_wait_queue); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void usx2y_error_urb_status(struct usx2ydev *usx2y, 28962306a36Sopenharmony_ci struct snd_usx2y_substream *subs, struct urb *urb) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci snd_printk(KERN_ERR "ep=%i stalled with status=%i\n", subs->endpoint, urb->status); 29262306a36Sopenharmony_ci urb->status = 0; 29362306a36Sopenharmony_ci usx2y_clients_stop(usx2y); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void i_usx2y_urb_complete(struct urb *urb) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct snd_usx2y_substream *subs = urb->context; 29962306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 30062306a36Sopenharmony_ci struct snd_usx2y_substream *capsubs, *playbacksubs; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (unlikely(atomic_read(&subs->state) < STATE_PREPARED)) { 30362306a36Sopenharmony_ci snd_printdd("hcd_frame=%i ep=%i%s status=%i start_frame=%i\n", 30462306a36Sopenharmony_ci usb_get_current_frame_number(usx2y->dev), 30562306a36Sopenharmony_ci subs->endpoint, usb_pipein(urb->pipe) ? "in" : "out", 30662306a36Sopenharmony_ci urb->status, urb->start_frame); 30762306a36Sopenharmony_ci return; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci if (unlikely(urb->status)) { 31062306a36Sopenharmony_ci usx2y_error_urb_status(usx2y, subs, urb); 31162306a36Sopenharmony_ci return; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci subs->completed_urb = urb; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci capsubs = usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 31762306a36Sopenharmony_ci playbacksubs = usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (capsubs->completed_urb && 32062306a36Sopenharmony_ci atomic_read(&capsubs->state) >= STATE_PREPARED && 32162306a36Sopenharmony_ci (playbacksubs->completed_urb || 32262306a36Sopenharmony_ci atomic_read(&playbacksubs->state) < STATE_PREPARED)) { 32362306a36Sopenharmony_ci if (!usx2y_usbframe_complete(capsubs, playbacksubs, urb->start_frame)) { 32462306a36Sopenharmony_ci usx2y->wait_iso_frame += nr_of_packs(); 32562306a36Sopenharmony_ci } else { 32662306a36Sopenharmony_ci snd_printdd("\n"); 32762306a36Sopenharmony_ci usx2y_clients_stop(usx2y); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void usx2y_urbs_set_complete(struct usx2ydev *usx2y, 33362306a36Sopenharmony_ci void (*complete)(struct urb *)) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct snd_usx2y_substream *subs; 33662306a36Sopenharmony_ci struct urb *urb; 33762306a36Sopenharmony_ci int s, u; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci for (s = 0; s < 4; s++) { 34062306a36Sopenharmony_ci subs = usx2y->subs[s]; 34162306a36Sopenharmony_ci if (subs) { 34262306a36Sopenharmony_ci for (u = 0; u < NRURBS; u++) { 34362306a36Sopenharmony_ci urb = subs->urb[u]; 34462306a36Sopenharmony_ci if (urb) 34562306a36Sopenharmony_ci urb->complete = complete; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic void usx2y_subs_startup_finish(struct usx2ydev *usx2y) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci usx2y_urbs_set_complete(usx2y, i_usx2y_urb_complete); 35462306a36Sopenharmony_ci usx2y->prepare_subs = NULL; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic void i_usx2y_subs_startup(struct urb *urb) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct snd_usx2y_substream *subs = urb->context; 36062306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 36162306a36Sopenharmony_ci struct snd_usx2y_substream *prepare_subs = usx2y->prepare_subs; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (prepare_subs) { 36462306a36Sopenharmony_ci if (urb->start_frame == prepare_subs->urb[0]->start_frame) { 36562306a36Sopenharmony_ci usx2y_subs_startup_finish(usx2y); 36662306a36Sopenharmony_ci atomic_inc(&prepare_subs->state); 36762306a36Sopenharmony_ci wake_up(&usx2y->prepare_wait_queue); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci i_usx2y_urb_complete(urb); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void usx2y_subs_prepare(struct snd_usx2y_substream *subs) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci snd_printdd("usx2y_substream_prepare(%p) ep=%i urb0=%p urb1=%p\n", 37762306a36Sopenharmony_ci subs, subs->endpoint, subs->urb[0], subs->urb[1]); 37862306a36Sopenharmony_ci /* reset the pointer */ 37962306a36Sopenharmony_ci subs->hwptr = 0; 38062306a36Sopenharmony_ci subs->hwptr_done = 0; 38162306a36Sopenharmony_ci subs->transfer_done = 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void usx2y_urb_release(struct urb **urb, int free_tb) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci if (*urb) { 38762306a36Sopenharmony_ci usb_kill_urb(*urb); 38862306a36Sopenharmony_ci if (free_tb) 38962306a36Sopenharmony_ci kfree((*urb)->transfer_buffer); 39062306a36Sopenharmony_ci usb_free_urb(*urb); 39162306a36Sopenharmony_ci *urb = NULL; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci/* 39662306a36Sopenharmony_ci * release a substreams urbs 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_cistatic void usx2y_urbs_release(struct snd_usx2y_substream *subs) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci int i; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci snd_printdd("%s %i\n", __func__, subs->endpoint); 40362306a36Sopenharmony_ci for (i = 0; i < NRURBS; i++) 40462306a36Sopenharmony_ci usx2y_urb_release(subs->urb + i, 40562306a36Sopenharmony_ci subs != subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci kfree(subs->tmpbuf); 40862306a36Sopenharmony_ci subs->tmpbuf = NULL; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* 41262306a36Sopenharmony_ci * initialize a substream's urbs 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_cistatic int usx2y_urbs_allocate(struct snd_usx2y_substream *subs) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci int i; 41762306a36Sopenharmony_ci unsigned int pipe; 41862306a36Sopenharmony_ci int is_playback = subs == subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 41962306a36Sopenharmony_ci struct usb_device *dev = subs->usx2y->dev; 42062306a36Sopenharmony_ci struct urb **purb; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) : 42362306a36Sopenharmony_ci usb_rcvisocpipe(dev, subs->endpoint); 42462306a36Sopenharmony_ci subs->maxpacksize = usb_maxpacket(dev, pipe); 42562306a36Sopenharmony_ci if (!subs->maxpacksize) 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (is_playback && !subs->tmpbuf) { /* allocate a temporary buffer for playback */ 42962306a36Sopenharmony_ci subs->tmpbuf = kcalloc(nr_of_packs(), subs->maxpacksize, GFP_KERNEL); 43062306a36Sopenharmony_ci if (!subs->tmpbuf) 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci /* allocate and initialize data urbs */ 43462306a36Sopenharmony_ci for (i = 0; i < NRURBS; i++) { 43562306a36Sopenharmony_ci purb = subs->urb + i; 43662306a36Sopenharmony_ci if (*purb) { 43762306a36Sopenharmony_ci usb_kill_urb(*purb); 43862306a36Sopenharmony_ci continue; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci *purb = usb_alloc_urb(nr_of_packs(), GFP_KERNEL); 44162306a36Sopenharmony_ci if (!*purb) { 44262306a36Sopenharmony_ci usx2y_urbs_release(subs); 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci if (!is_playback && !(*purb)->transfer_buffer) { 44662306a36Sopenharmony_ci /* allocate a capture buffer per urb */ 44762306a36Sopenharmony_ci (*purb)->transfer_buffer = 44862306a36Sopenharmony_ci kmalloc_array(subs->maxpacksize, 44962306a36Sopenharmony_ci nr_of_packs(), GFP_KERNEL); 45062306a36Sopenharmony_ci if (!(*purb)->transfer_buffer) { 45162306a36Sopenharmony_ci usx2y_urbs_release(subs); 45262306a36Sopenharmony_ci return -ENOMEM; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci (*purb)->dev = dev; 45662306a36Sopenharmony_ci (*purb)->pipe = pipe; 45762306a36Sopenharmony_ci (*purb)->number_of_packets = nr_of_packs(); 45862306a36Sopenharmony_ci (*purb)->context = subs; 45962306a36Sopenharmony_ci (*purb)->interval = 1; 46062306a36Sopenharmony_ci (*purb)->complete = i_usx2y_subs_startup; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void usx2y_subs_startup(struct snd_usx2y_substream *subs) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci usx2y->prepare_subs = subs; 47062306a36Sopenharmony_ci subs->urb[0]->start_frame = -1; 47162306a36Sopenharmony_ci wmb(); 47262306a36Sopenharmony_ci usx2y_urbs_set_complete(usx2y, i_usx2y_subs_startup); 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int usx2y_urbs_start(struct snd_usx2y_substream *subs) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci int i, err; 47862306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 47962306a36Sopenharmony_ci struct urb *urb; 48062306a36Sopenharmony_ci unsigned long pack; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci err = usx2y_urbs_allocate(subs); 48362306a36Sopenharmony_ci if (err < 0) 48462306a36Sopenharmony_ci return err; 48562306a36Sopenharmony_ci subs->completed_urb = NULL; 48662306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 48762306a36Sopenharmony_ci struct snd_usx2y_substream *subs = usx2y->subs[i]; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (subs && atomic_read(&subs->state) >= STATE_PREPARED) 49062306a36Sopenharmony_ci goto start; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci start: 49462306a36Sopenharmony_ci usx2y_subs_startup(subs); 49562306a36Sopenharmony_ci for (i = 0; i < NRURBS; i++) { 49662306a36Sopenharmony_ci urb = subs->urb[i]; 49762306a36Sopenharmony_ci if (usb_pipein(urb->pipe)) { 49862306a36Sopenharmony_ci if (!i) 49962306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STARTING3); 50062306a36Sopenharmony_ci urb->dev = usx2y->dev; 50162306a36Sopenharmony_ci for (pack = 0; pack < nr_of_packs(); pack++) { 50262306a36Sopenharmony_ci urb->iso_frame_desc[pack].offset = subs->maxpacksize * pack; 50362306a36Sopenharmony_ci urb->iso_frame_desc[pack].length = subs->maxpacksize; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci urb->transfer_buffer_length = subs->maxpacksize * nr_of_packs(); 50662306a36Sopenharmony_ci err = usb_submit_urb(urb, GFP_ATOMIC); 50762306a36Sopenharmony_ci if (err < 0) { 50862306a36Sopenharmony_ci snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err); 50962306a36Sopenharmony_ci err = -EPIPE; 51062306a36Sopenharmony_ci goto cleanup; 51162306a36Sopenharmony_ci } else { 51262306a36Sopenharmony_ci if (!i) 51362306a36Sopenharmony_ci usx2y->wait_iso_frame = urb->start_frame; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci urb->transfer_flags = 0; 51662306a36Sopenharmony_ci } else { 51762306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STARTING1); 51862306a36Sopenharmony_ci break; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci err = 0; 52262306a36Sopenharmony_ci wait_event(usx2y->prepare_wait_queue, !usx2y->prepare_subs); 52362306a36Sopenharmony_ci if (atomic_read(&subs->state) != STATE_PREPARED) 52462306a36Sopenharmony_ci err = -EPIPE; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci cleanup: 52762306a36Sopenharmony_ci if (err) { 52862306a36Sopenharmony_ci usx2y_subs_startup_finish(usx2y); 52962306a36Sopenharmony_ci usx2y_clients_stop(usx2y); // something is completely wrong > stop everything 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci return err; 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci/* 53562306a36Sopenharmony_ci * return the current pcm pointer. just return the hwptr_done value. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_usx2y_pcm_pointer(struct snd_pcm_substream *substream) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct snd_usx2y_substream *subs = substream->runtime->private_data; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci return subs->hwptr_done; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci/* 54562306a36Sopenharmony_ci * start/stop substream 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_cistatic int snd_usx2y_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct snd_usx2y_substream *subs = substream->runtime->private_data; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci switch (cmd) { 55262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 55362306a36Sopenharmony_ci snd_printdd("%s(START)\n", __func__); 55462306a36Sopenharmony_ci if (atomic_read(&subs->state) == STATE_PREPARED && 55562306a36Sopenharmony_ci atomic_read(&subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]->state) >= STATE_PREPARED) { 55662306a36Sopenharmony_ci atomic_set(&subs->state, STATE_PRERUNNING); 55762306a36Sopenharmony_ci } else { 55862306a36Sopenharmony_ci snd_printdd("\n"); 55962306a36Sopenharmony_ci return -EPIPE; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 56362306a36Sopenharmony_ci snd_printdd("%s(STOP)\n", __func__); 56462306a36Sopenharmony_ci if (atomic_read(&subs->state) >= STATE_PRERUNNING) 56562306a36Sopenharmony_ci atomic_set(&subs->state, STATE_PREPARED); 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci default: 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* 57462306a36Sopenharmony_ci * allocate a buffer, setup samplerate 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * so far we use a physically linear buffer although packetize transfer 57762306a36Sopenharmony_ci * doesn't need a continuous area. 57862306a36Sopenharmony_ci * if sg buffer is supported on the later version of alsa, we'll follow 57962306a36Sopenharmony_ci * that. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistruct s_c2 { 58262306a36Sopenharmony_ci char c1, c2; 58362306a36Sopenharmony_ci}; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic const struct s_c2 setrate_44100[] = { 58662306a36Sopenharmony_ci { 0x14, 0x08}, // this line sets 44100, well actually a little less 58762306a36Sopenharmony_ci { 0x18, 0x40}, // only tascam / frontier design knows the further lines ....... 58862306a36Sopenharmony_ci { 0x18, 0x42}, 58962306a36Sopenharmony_ci { 0x18, 0x45}, 59062306a36Sopenharmony_ci { 0x18, 0x46}, 59162306a36Sopenharmony_ci { 0x18, 0x48}, 59262306a36Sopenharmony_ci { 0x18, 0x4A}, 59362306a36Sopenharmony_ci { 0x18, 0x4C}, 59462306a36Sopenharmony_ci { 0x18, 0x4E}, 59562306a36Sopenharmony_ci { 0x18, 0x50}, 59662306a36Sopenharmony_ci { 0x18, 0x52}, 59762306a36Sopenharmony_ci { 0x18, 0x54}, 59862306a36Sopenharmony_ci { 0x18, 0x56}, 59962306a36Sopenharmony_ci { 0x18, 0x58}, 60062306a36Sopenharmony_ci { 0x18, 0x5A}, 60162306a36Sopenharmony_ci { 0x18, 0x5C}, 60262306a36Sopenharmony_ci { 0x18, 0x5E}, 60362306a36Sopenharmony_ci { 0x18, 0x60}, 60462306a36Sopenharmony_ci { 0x18, 0x62}, 60562306a36Sopenharmony_ci { 0x18, 0x64}, 60662306a36Sopenharmony_ci { 0x18, 0x66}, 60762306a36Sopenharmony_ci { 0x18, 0x68}, 60862306a36Sopenharmony_ci { 0x18, 0x6A}, 60962306a36Sopenharmony_ci { 0x18, 0x6C}, 61062306a36Sopenharmony_ci { 0x18, 0x6E}, 61162306a36Sopenharmony_ci { 0x18, 0x70}, 61262306a36Sopenharmony_ci { 0x18, 0x72}, 61362306a36Sopenharmony_ci { 0x18, 0x74}, 61462306a36Sopenharmony_ci { 0x18, 0x76}, 61562306a36Sopenharmony_ci { 0x18, 0x78}, 61662306a36Sopenharmony_ci { 0x18, 0x7A}, 61762306a36Sopenharmony_ci { 0x18, 0x7C}, 61862306a36Sopenharmony_ci { 0x18, 0x7E} 61962306a36Sopenharmony_ci}; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic const struct s_c2 setrate_48000[] = { 62262306a36Sopenharmony_ci { 0x14, 0x09}, // this line sets 48000, well actually a little less 62362306a36Sopenharmony_ci { 0x18, 0x40}, // only tascam / frontier design knows the further lines ....... 62462306a36Sopenharmony_ci { 0x18, 0x42}, 62562306a36Sopenharmony_ci { 0x18, 0x45}, 62662306a36Sopenharmony_ci { 0x18, 0x46}, 62762306a36Sopenharmony_ci { 0x18, 0x48}, 62862306a36Sopenharmony_ci { 0x18, 0x4A}, 62962306a36Sopenharmony_ci { 0x18, 0x4C}, 63062306a36Sopenharmony_ci { 0x18, 0x4E}, 63162306a36Sopenharmony_ci { 0x18, 0x50}, 63262306a36Sopenharmony_ci { 0x18, 0x52}, 63362306a36Sopenharmony_ci { 0x18, 0x54}, 63462306a36Sopenharmony_ci { 0x18, 0x56}, 63562306a36Sopenharmony_ci { 0x18, 0x58}, 63662306a36Sopenharmony_ci { 0x18, 0x5A}, 63762306a36Sopenharmony_ci { 0x18, 0x5C}, 63862306a36Sopenharmony_ci { 0x18, 0x5E}, 63962306a36Sopenharmony_ci { 0x18, 0x60}, 64062306a36Sopenharmony_ci { 0x18, 0x62}, 64162306a36Sopenharmony_ci { 0x18, 0x64}, 64262306a36Sopenharmony_ci { 0x18, 0x66}, 64362306a36Sopenharmony_ci { 0x18, 0x68}, 64462306a36Sopenharmony_ci { 0x18, 0x6A}, 64562306a36Sopenharmony_ci { 0x18, 0x6C}, 64662306a36Sopenharmony_ci { 0x18, 0x6E}, 64762306a36Sopenharmony_ci { 0x18, 0x70}, 64862306a36Sopenharmony_ci { 0x18, 0x73}, 64962306a36Sopenharmony_ci { 0x18, 0x74}, 65062306a36Sopenharmony_ci { 0x18, 0x76}, 65162306a36Sopenharmony_ci { 0x18, 0x78}, 65262306a36Sopenharmony_ci { 0x18, 0x7A}, 65362306a36Sopenharmony_ci { 0x18, 0x7C}, 65462306a36Sopenharmony_ci { 0x18, 0x7E} 65562306a36Sopenharmony_ci}; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci#define NOOF_SETRATE_URBS ARRAY_SIZE(setrate_48000) 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic void i_usx2y_04int(struct urb *urb) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct usx2ydev *usx2y = urb->context; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (urb->status) 66462306a36Sopenharmony_ci snd_printk(KERN_ERR "snd_usx2y_04int() urb->status=%i\n", urb->status); 66562306a36Sopenharmony_ci if (!--usx2y->us04->len) 66662306a36Sopenharmony_ci wake_up(&usx2y->in04_wait_queue); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int usx2y_rate_set(struct usx2ydev *usx2y, int rate) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci int err = 0, i; 67262306a36Sopenharmony_ci struct snd_usx2y_urb_seq *us = NULL; 67362306a36Sopenharmony_ci int *usbdata = NULL; 67462306a36Sopenharmony_ci const struct s_c2 *ra = rate == 48000 ? setrate_48000 : setrate_44100; 67562306a36Sopenharmony_ci struct urb *urb; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (usx2y->rate != rate) { 67862306a36Sopenharmony_ci us = kzalloc(struct_size(us, urb, NOOF_SETRATE_URBS), 67962306a36Sopenharmony_ci GFP_KERNEL); 68062306a36Sopenharmony_ci if (!us) { 68162306a36Sopenharmony_ci err = -ENOMEM; 68262306a36Sopenharmony_ci goto cleanup; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci usbdata = kmalloc_array(NOOF_SETRATE_URBS, sizeof(int), 68562306a36Sopenharmony_ci GFP_KERNEL); 68662306a36Sopenharmony_ci if (!usbdata) { 68762306a36Sopenharmony_ci err = -ENOMEM; 68862306a36Sopenharmony_ci goto cleanup; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci for (i = 0; i < NOOF_SETRATE_URBS; ++i) { 69162306a36Sopenharmony_ci us->urb[i] = usb_alloc_urb(0, GFP_KERNEL); 69262306a36Sopenharmony_ci if (!us->urb[i]) { 69362306a36Sopenharmony_ci err = -ENOMEM; 69462306a36Sopenharmony_ci goto cleanup; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci ((char *)(usbdata + i))[0] = ra[i].c1; 69762306a36Sopenharmony_ci ((char *)(usbdata + i))[1] = ra[i].c2; 69862306a36Sopenharmony_ci usb_fill_bulk_urb(us->urb[i], usx2y->dev, usb_sndbulkpipe(usx2y->dev, 4), 69962306a36Sopenharmony_ci usbdata + i, 2, i_usx2y_04int, usx2y); 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci err = usb_urb_ep_type_check(us->urb[0]); 70262306a36Sopenharmony_ci if (err < 0) 70362306a36Sopenharmony_ci goto cleanup; 70462306a36Sopenharmony_ci us->submitted = 0; 70562306a36Sopenharmony_ci us->len = NOOF_SETRATE_URBS; 70662306a36Sopenharmony_ci usx2y->us04 = us; 70762306a36Sopenharmony_ci wait_event_timeout(usx2y->in04_wait_queue, !us->len, HZ); 70862306a36Sopenharmony_ci usx2y->us04 = NULL; 70962306a36Sopenharmony_ci if (us->len) 71062306a36Sopenharmony_ci err = -ENODEV; 71162306a36Sopenharmony_ci cleanup: 71262306a36Sopenharmony_ci if (us) { 71362306a36Sopenharmony_ci us->submitted = 2*NOOF_SETRATE_URBS; 71462306a36Sopenharmony_ci for (i = 0; i < NOOF_SETRATE_URBS; ++i) { 71562306a36Sopenharmony_ci urb = us->urb[i]; 71662306a36Sopenharmony_ci if (!urb) 71762306a36Sopenharmony_ci continue; 71862306a36Sopenharmony_ci if (urb->status) { 71962306a36Sopenharmony_ci if (!err) 72062306a36Sopenharmony_ci err = -ENODEV; 72162306a36Sopenharmony_ci usb_kill_urb(urb); 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci usb_free_urb(urb); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci usx2y->us04 = NULL; 72662306a36Sopenharmony_ci kfree(usbdata); 72762306a36Sopenharmony_ci kfree(us); 72862306a36Sopenharmony_ci if (!err) 72962306a36Sopenharmony_ci usx2y->rate = rate; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci return err; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int usx2y_format_set(struct usx2ydev *usx2y, snd_pcm_format_t format) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci int alternate, err; 73962306a36Sopenharmony_ci struct list_head *p; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (format == SNDRV_PCM_FORMAT_S24_3LE) { 74262306a36Sopenharmony_ci alternate = 2; 74362306a36Sopenharmony_ci usx2y->stride = 6; 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci alternate = 1; 74662306a36Sopenharmony_ci usx2y->stride = 4; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci list_for_each(p, &usx2y->midi_list) { 74962306a36Sopenharmony_ci snd_usbmidi_input_stop(p); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci usb_kill_urb(usx2y->in04_urb); 75262306a36Sopenharmony_ci err = usb_set_interface(usx2y->dev, 0, alternate); 75362306a36Sopenharmony_ci if (err) { 75462306a36Sopenharmony_ci snd_printk(KERN_ERR "usb_set_interface error\n"); 75562306a36Sopenharmony_ci return err; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci usx2y->in04_urb->dev = usx2y->dev; 75862306a36Sopenharmony_ci err = usb_submit_urb(usx2y->in04_urb, GFP_KERNEL); 75962306a36Sopenharmony_ci list_for_each(p, &usx2y->midi_list) { 76062306a36Sopenharmony_ci snd_usbmidi_input_start(p); 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci usx2y->format = format; 76362306a36Sopenharmony_ci usx2y->rate = 0; 76462306a36Sopenharmony_ci return err; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int snd_usx2y_pcm_hw_params(struct snd_pcm_substream *substream, 76962306a36Sopenharmony_ci struct snd_pcm_hw_params *hw_params) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci int err = 0; 77262306a36Sopenharmony_ci unsigned int rate = params_rate(hw_params); 77362306a36Sopenharmony_ci snd_pcm_format_t format = params_format(hw_params); 77462306a36Sopenharmony_ci struct snd_card *card = substream->pstr->pcm->card; 77562306a36Sopenharmony_ci struct usx2ydev *dev = usx2y(card); 77662306a36Sopenharmony_ci struct snd_usx2y_substream *subs; 77762306a36Sopenharmony_ci struct snd_pcm_substream *test_substream; 77862306a36Sopenharmony_ci int i; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci mutex_lock(&usx2y(card)->pcm_mutex); 78162306a36Sopenharmony_ci snd_printdd("snd_usx2y_hw_params(%p, %p)\n", substream, hw_params); 78262306a36Sopenharmony_ci /* all pcm substreams off one usx2y have to operate at the same 78362306a36Sopenharmony_ci * rate & format 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_ci for (i = 0; i < dev->pcm_devs * 2; i++) { 78662306a36Sopenharmony_ci subs = dev->subs[i]; 78762306a36Sopenharmony_ci if (!subs) 78862306a36Sopenharmony_ci continue; 78962306a36Sopenharmony_ci test_substream = subs->pcm_substream; 79062306a36Sopenharmony_ci if (!test_substream || test_substream == substream || 79162306a36Sopenharmony_ci !test_substream->runtime) 79262306a36Sopenharmony_ci continue; 79362306a36Sopenharmony_ci if ((test_substream->runtime->format && 79462306a36Sopenharmony_ci test_substream->runtime->format != format) || 79562306a36Sopenharmony_ci (test_substream->runtime->rate && 79662306a36Sopenharmony_ci test_substream->runtime->rate != rate)) { 79762306a36Sopenharmony_ci err = -EINVAL; 79862306a36Sopenharmony_ci goto error; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci error: 80362306a36Sopenharmony_ci mutex_unlock(&usx2y(card)->pcm_mutex); 80462306a36Sopenharmony_ci return err; 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/* 80862306a36Sopenharmony_ci * free the buffer 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_cistatic int snd_usx2y_pcm_hw_free(struct snd_pcm_substream *substream) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 81362306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 81462306a36Sopenharmony_ci struct snd_usx2y_substream *cap_subs, *playback_subs; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci mutex_lock(&subs->usx2y->pcm_mutex); 81762306a36Sopenharmony_ci snd_printdd("snd_usx2y_hw_free(%p)\n", substream); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 82062306a36Sopenharmony_ci cap_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 82162306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STOPPED); 82262306a36Sopenharmony_ci usx2y_urbs_release(subs); 82362306a36Sopenharmony_ci if (!cap_subs->pcm_substream || 82462306a36Sopenharmony_ci !cap_subs->pcm_substream->runtime || 82562306a36Sopenharmony_ci cap_subs->pcm_substream->runtime->state < SNDRV_PCM_STATE_PREPARED) { 82662306a36Sopenharmony_ci atomic_set(&cap_subs->state, STATE_STOPPED); 82762306a36Sopenharmony_ci usx2y_urbs_release(cap_subs); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci } else { 83062306a36Sopenharmony_ci playback_subs = subs->usx2y->subs[SNDRV_PCM_STREAM_PLAYBACK]; 83162306a36Sopenharmony_ci if (atomic_read(&playback_subs->state) < STATE_PREPARED) { 83262306a36Sopenharmony_ci atomic_set(&subs->state, STATE_STOPPED); 83362306a36Sopenharmony_ci usx2y_urbs_release(subs); 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci mutex_unlock(&subs->usx2y->pcm_mutex); 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci/* 84162306a36Sopenharmony_ci * prepare callback 84262306a36Sopenharmony_ci * 84362306a36Sopenharmony_ci * set format and initialize urbs 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_cistatic int snd_usx2y_pcm_prepare(struct snd_pcm_substream *substream) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 84862306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 84962306a36Sopenharmony_ci struct usx2ydev *usx2y = subs->usx2y; 85062306a36Sopenharmony_ci struct snd_usx2y_substream *capsubs = subs->usx2y->subs[SNDRV_PCM_STREAM_CAPTURE]; 85162306a36Sopenharmony_ci int err = 0; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci snd_printdd("%s(%p)\n", __func__, substream); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci mutex_lock(&usx2y->pcm_mutex); 85662306a36Sopenharmony_ci usx2y_subs_prepare(subs); 85762306a36Sopenharmony_ci // Start hardware streams 85862306a36Sopenharmony_ci // SyncStream first.... 85962306a36Sopenharmony_ci if (atomic_read(&capsubs->state) < STATE_PREPARED) { 86062306a36Sopenharmony_ci if (usx2y->format != runtime->format) { 86162306a36Sopenharmony_ci err = usx2y_format_set(usx2y, runtime->format); 86262306a36Sopenharmony_ci if (err < 0) 86362306a36Sopenharmony_ci goto up_prepare_mutex; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci if (usx2y->rate != runtime->rate) { 86662306a36Sopenharmony_ci err = usx2y_rate_set(usx2y, runtime->rate); 86762306a36Sopenharmony_ci if (err < 0) 86862306a36Sopenharmony_ci goto up_prepare_mutex; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci snd_printdd("starting capture pipe for %s\n", subs == capsubs ? "self" : "playpipe"); 87162306a36Sopenharmony_ci err = usx2y_urbs_start(capsubs); 87262306a36Sopenharmony_ci if (err < 0) 87362306a36Sopenharmony_ci goto up_prepare_mutex; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (subs != capsubs && atomic_read(&subs->state) < STATE_PREPARED) 87762306a36Sopenharmony_ci err = usx2y_urbs_start(subs); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci up_prepare_mutex: 88062306a36Sopenharmony_ci mutex_unlock(&usx2y->pcm_mutex); 88162306a36Sopenharmony_ci return err; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_usx2y_2c = { 88562306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 88662306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 88762306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID | 88862306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH), 88962306a36Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_3LE, 89062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000, 89162306a36Sopenharmony_ci .rate_min = 44100, 89262306a36Sopenharmony_ci .rate_max = 48000, 89362306a36Sopenharmony_ci .channels_min = 2, 89462306a36Sopenharmony_ci .channels_max = 2, 89562306a36Sopenharmony_ci .buffer_bytes_max = (2*128*1024), 89662306a36Sopenharmony_ci .period_bytes_min = 64, 89762306a36Sopenharmony_ci .period_bytes_max = (128*1024), 89862306a36Sopenharmony_ci .periods_min = 2, 89962306a36Sopenharmony_ci .periods_max = 1024, 90062306a36Sopenharmony_ci .fifo_size = 0 90162306a36Sopenharmony_ci}; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int snd_usx2y_pcm_open(struct snd_pcm_substream *substream) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct snd_usx2y_substream *subs = 90662306a36Sopenharmony_ci ((struct snd_usx2y_substream **) 90762306a36Sopenharmony_ci snd_pcm_substream_chip(substream))[substream->stream]; 90862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (subs->usx2y->chip_status & USX2Y_STAT_CHIP_MMAP_PCM_URBS) 91162306a36Sopenharmony_ci return -EBUSY; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci runtime->hw = snd_usx2y_2c; 91462306a36Sopenharmony_ci runtime->private_data = subs; 91562306a36Sopenharmony_ci subs->pcm_substream = substream; 91662306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 1000, 200000); 91762306a36Sopenharmony_ci return 0; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic int snd_usx2y_pcm_close(struct snd_pcm_substream *substream) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 92362306a36Sopenharmony_ci struct snd_usx2y_substream *subs = runtime->private_data; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci subs->pcm_substream = NULL; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_usx2y_pcm_ops = { 93162306a36Sopenharmony_ci .open = snd_usx2y_pcm_open, 93262306a36Sopenharmony_ci .close = snd_usx2y_pcm_close, 93362306a36Sopenharmony_ci .hw_params = snd_usx2y_pcm_hw_params, 93462306a36Sopenharmony_ci .hw_free = snd_usx2y_pcm_hw_free, 93562306a36Sopenharmony_ci .prepare = snd_usx2y_pcm_prepare, 93662306a36Sopenharmony_ci .trigger = snd_usx2y_pcm_trigger, 93762306a36Sopenharmony_ci .pointer = snd_usx2y_pcm_pointer, 93862306a36Sopenharmony_ci}; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci/* 94162306a36Sopenharmony_ci * free a usb stream instance 94262306a36Sopenharmony_ci */ 94362306a36Sopenharmony_cistatic void usx2y_audio_stream_free(struct snd_usx2y_substream **usx2y_substream) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci int stream; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci for_each_pcm_streams(stream) { 94862306a36Sopenharmony_ci kfree(usx2y_substream[stream]); 94962306a36Sopenharmony_ci usx2y_substream[stream] = NULL; 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic void snd_usx2y_pcm_private_free(struct snd_pcm *pcm) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct snd_usx2y_substream **usx2y_stream = pcm->private_data; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (usx2y_stream) 95862306a36Sopenharmony_ci usx2y_audio_stream_free(usx2y_stream); 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int usx2y_audio_stream_new(struct snd_card *card, int playback_endpoint, int capture_endpoint) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct snd_pcm *pcm; 96462306a36Sopenharmony_ci int err, i; 96562306a36Sopenharmony_ci struct snd_usx2y_substream **usx2y_substream = 96662306a36Sopenharmony_ci usx2y(card)->subs + 2 * usx2y(card)->pcm_devs; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci for (i = playback_endpoint ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; 96962306a36Sopenharmony_ci i <= SNDRV_PCM_STREAM_CAPTURE; ++i) { 97062306a36Sopenharmony_ci usx2y_substream[i] = kzalloc(sizeof(struct snd_usx2y_substream), GFP_KERNEL); 97162306a36Sopenharmony_ci if (!usx2y_substream[i]) 97262306a36Sopenharmony_ci return -ENOMEM; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci usx2y_substream[i]->usx2y = usx2y(card); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (playback_endpoint) 97862306a36Sopenharmony_ci usx2y_substream[SNDRV_PCM_STREAM_PLAYBACK]->endpoint = playback_endpoint; 97962306a36Sopenharmony_ci usx2y_substream[SNDRV_PCM_STREAM_CAPTURE]->endpoint = capture_endpoint; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci err = snd_pcm_new(card, NAME_ALLCAPS" Audio", usx2y(card)->pcm_devs, 98262306a36Sopenharmony_ci playback_endpoint ? 1 : 0, 1, 98362306a36Sopenharmony_ci &pcm); 98462306a36Sopenharmony_ci if (err < 0) { 98562306a36Sopenharmony_ci usx2y_audio_stream_free(usx2y_substream); 98662306a36Sopenharmony_ci return err; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (playback_endpoint) 99062306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_usx2y_pcm_ops); 99162306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_usx2y_pcm_ops); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci pcm->private_data = usx2y_substream; 99462306a36Sopenharmony_ci pcm->private_free = snd_usx2y_pcm_private_free; 99562306a36Sopenharmony_ci pcm->info_flags = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci sprintf(pcm->name, NAME_ALLCAPS" Audio #%d", usx2y(card)->pcm_devs); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci if (playback_endpoint) { 100062306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, 100162306a36Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 100262306a36Sopenharmony_ci NULL, 100362306a36Sopenharmony_ci 64*1024, 128*1024); 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, 100762306a36Sopenharmony_ci SNDRV_DMA_TYPE_CONTINUOUS, 100862306a36Sopenharmony_ci NULL, 100962306a36Sopenharmony_ci 64*1024, 128*1024); 101062306a36Sopenharmony_ci usx2y(card)->pcm_devs++; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return 0; 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci/* 101662306a36Sopenharmony_ci * create a chip instance and set its names. 101762306a36Sopenharmony_ci */ 101862306a36Sopenharmony_ciint usx2y_audio_create(struct snd_card *card) 101962306a36Sopenharmony_ci{ 102062306a36Sopenharmony_ci int err; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci err = usx2y_audio_stream_new(card, 0xA, 0x8); 102362306a36Sopenharmony_ci if (err < 0) 102462306a36Sopenharmony_ci return err; 102562306a36Sopenharmony_ci if (le16_to_cpu(usx2y(card)->dev->descriptor.idProduct) == USB_ID_US428) { 102662306a36Sopenharmony_ci err = usx2y_audio_stream_new(card, 0, 0xA); 102762306a36Sopenharmony_ci if (err < 0) 102862306a36Sopenharmony_ci return err; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci if (le16_to_cpu(usx2y(card)->dev->descriptor.idProduct) != USB_ID_US122) 103162306a36Sopenharmony_ci err = usx2y_rate_set(usx2y(card), 44100); // Lets us428 recognize output-volume settings, disturbs us122. 103262306a36Sopenharmony_ci return err; 103362306a36Sopenharmony_ci} 1034