From 04dd17822334871b23ea2862f7798fb0e0007777 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 11 May 2024 08:53:19 +0000 Subject: [PATCH] change otg to host mode --- kernel/drivers/usb/gadget/function/u_audio.c | 1218 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 928 insertions(+), 290 deletions(-) diff --git a/kernel/drivers/usb/gadget/function/u_audio.c b/kernel/drivers/usb/gadget/function/u_audio.c index 3476333..cb15ec8 100644 --- a/kernel/drivers/usb/gadget/function/u_audio.c +++ b/kernel/drivers/usb/gadget/function/u_audio.c @@ -12,12 +12,14 @@ * Jaswinder Singh (jaswinder.singh@linaro.org) */ +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/usb/audio.h> -#include <sound/control.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/tlv.h> +#include <linux/usb/audio.h> #include "u_audio.h" @@ -25,14 +27,15 @@ #define PRD_SIZE_MAX PAGE_SIZE #define MIN_PERIODS 4 -#define CLK_PPM_GROUP_SIZE 20 - -static struct class *audio_class; - -struct uac_req { - struct uac_rtd_params *pp; /* parent param */ - struct usb_request *req; +enum { + UAC_FBACK_CTRL, + UAC_P_PITCH_CTRL, + UAC_MUTE_CTRL, + UAC_VOLUME_CTRL, + UAC_RATE_CTRL, }; + +#define CLK_PPM_GROUP_SIZE 20 /* Runtime data params for one stream */ struct uac_rtd_params { @@ -46,10 +49,28 @@ void *rbuf; - unsigned max_psize; /* MaxPacketSize of endpoint */ - struct uac_req *ureq; + unsigned int pitch; /* Stream pitch ratio to 1000000 */ + unsigned int max_psize; /* MaxPacketSize of endpoint */ - spinlock_t lock; + struct usb_request **reqs; + + struct usb_request *req_fback; /* Feedback endpoint request */ + bool fb_ep_enabled; /* if the ep is enabled */ + + /* Volume/Mute controls and their state */ + int fu_id; /* Feature Unit ID */ + struct snd_kcontrol *snd_kctl_volume; + struct snd_kcontrol *snd_kctl_mute; + s16 volume_min, volume_max, volume_res; + s16 volume; + int mute; + + struct snd_kcontrol *snd_kctl_rate; /* read-only current rate */ + int srate; /* selected samplerate */ + int active; /* playback/capture running */ + + spinlock_t lock; /* lock for control transfers */ + }; struct snd_uac_chip { @@ -61,13 +82,9 @@ struct snd_card *card; struct snd_pcm *pcm; - /* timekeeping for the playback endpoint */ - unsigned int p_interval; - unsigned int p_residue; - /* pre-calculated values for playback iso completion */ - unsigned int p_pktsize; - unsigned int p_pktsize_residue; + unsigned long long p_residue_mil; + unsigned int p_interval; unsigned int p_framesize; }; @@ -82,17 +99,69 @@ .periods_min = MIN_PERIODS, }; +static struct class *audio_class; + +static void u_audio_set_fback_frequency(enum usb_device_speed speed, + struct usb_ep *out_ep, + unsigned long long freq, + unsigned int pitch, + void *buf) +{ + u32 ff = 0; + const struct usb_endpoint_descriptor *ep_desc; + + /* + * Because the pitch base is 1000000, the final divider here + * will be 1000 * 1000000 = 1953125 << 9 + * + * Instead of dealing with big numbers lets fold this 9 left shift + */ + + if (speed == USB_SPEED_FULL) { + /* + * Full-speed feedback endpoints report frequency + * in samples/frame + * Format is encoded in Q10.10 left-justified in the 24 bits, + * so that it has a Q10.14 format. + * + * ff = (freq << 14) / 1000 + */ + freq <<= 5; + } else { + /* + * High-speed feedback endpoints report frequency + * in samples/microframe. + * Format is encoded in Q12.13 fitted into four bytes so that + * the binary point is located between the second and the third + * byte fromat (that is Q16.16) + * + * ff = (freq << 16) / 8000 + * + * Win10 and OSX UAC2 drivers require number of samples per packet + * in order to honor the feedback value. + * Linux snd-usb-audio detects the applied bit-shift automatically. + */ + ep_desc = out_ep->desc; + freq <<= 4 + (ep_desc->bInterval - 1); + } + + ff = DIV_ROUND_CLOSEST_ULL((freq * pitch), 1953125); + + *(__le32 *)buf = cpu_to_le32(ff); +} + static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req) { - unsigned pending; - unsigned long flags, flags2; + unsigned int pending; unsigned int hw_ptr; int status = req->status; - struct uac_req *ur = req->context; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; - struct uac_rtd_params *prm = ur->pp; + struct uac_rtd_params *prm = req->context; struct snd_uac_chip *uac = prm->uac; + unsigned int frames, p_pktsize; + unsigned long long pitched_rate_mil, p_pktsize_residue_mil, + residue_frames_mil, div_result; /* i/f shutting down */ if (!prm->ep_enabled) { @@ -117,15 +186,13 @@ if (!substream) goto exit; - snd_pcm_stream_lock_irqsave(substream, flags2); + snd_pcm_stream_lock(substream); runtime = substream->runtime; if (!runtime || !snd_pcm_running(substream)) { - snd_pcm_stream_unlock_irqrestore(substream, flags2); + snd_pcm_stream_unlock(substream); goto exit; } - - spin_lock_irqsave(&prm->lock, flags); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* @@ -134,26 +201,49 @@ * If there is a residue from this division, add it to the * residue accumulator. */ - req->length = uac->p_pktsize; - uac->p_residue += uac->p_pktsize_residue; + unsigned long long p_interval_mil = uac->p_interval * 1000000ULL; + + pitched_rate_mil = (unsigned long long) prm->srate * prm->pitch; + div_result = pitched_rate_mil; + do_div(div_result, uac->p_interval); + do_div(div_result, 1000000); + frames = (unsigned int) div_result; + + pr_debug("p_srate %d, pitch %d, interval_mil %llu, frames %d\n", + prm->srate, prm->pitch, p_interval_mil, frames); + + p_pktsize = min_t(unsigned int, + uac->p_framesize * frames, + ep->maxpacket); + + if (p_pktsize < ep->maxpacket) { + residue_frames_mil = pitched_rate_mil - frames * p_interval_mil; + p_pktsize_residue_mil = uac->p_framesize * residue_frames_mil; + } else + p_pktsize_residue_mil = 0; + + req->length = p_pktsize; + uac->p_residue_mil += p_pktsize_residue_mil; /* - * Whenever there are more bytes in the accumulator than we + * Whenever there are more bytes in the accumulator p_residue_mil than we * need to add one more sample frame, increase this packet's * size and decrease the accumulator. */ - if (uac->p_residue / uac->p_interval >= uac->p_framesize) { + div_result = uac->p_residue_mil; + do_div(div_result, uac->p_interval); + do_div(div_result, 1000000); + if ((unsigned int) div_result >= uac->p_framesize) { req->length += uac->p_framesize; - uac->p_residue -= uac->p_framesize * - uac->p_interval; + uac->p_residue_mil -= uac->p_framesize * p_interval_mil; + pr_debug("increased req length to %d\n", req->length); } + pr_debug("remains uac->p_residue_mil %llu\n", uac->p_residue_mil); req->actual = req->length; } hw_ptr = prm->hw_ptr; - - spin_unlock_irqrestore(&prm->lock, flags); /* Pack USB load in ALSA ring buffer */ pending = runtime->dma_bytes - hw_ptr; @@ -178,17 +268,49 @@ } } - spin_lock_irqsave(&prm->lock, flags); /* update hw_ptr after data is copied to memory */ prm->hw_ptr = (hw_ptr + req->actual) % runtime->dma_bytes; hw_ptr = prm->hw_ptr; - spin_unlock_irqrestore(&prm->lock, flags); - snd_pcm_stream_unlock_irqrestore(substream, flags2); + snd_pcm_stream_unlock(substream); if ((hw_ptr % snd_pcm_lib_period_bytes(substream)) < req->actual) snd_pcm_period_elapsed(substream); exit: + if (usb_ep_queue(ep, req, GFP_ATOMIC)) + dev_err(uac->card->dev, "%d Error!\n", __LINE__); +} + +static void u_audio_iso_fback_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct uac_rtd_params *prm = req->context; + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + int status = req->status; + + /* i/f shutting down */ + if (!prm->fb_ep_enabled) { + kfree(req->buf); + usb_ep_free_request(ep, req); + return; + } + + if (req->status == -ESHUTDOWN) + return; + + /* + * We can't really do much about bad xfers. + * Afterall, the ISOCH xfers could fail legitimately. + */ + if (status) + pr_debug("%s: iso_complete status(%d) %d/%d\n", + __func__, status, req->actual, req->length); + + u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep, + prm->srate, prm->pitch, + req->buf); + if (usb_ep_queue(ep, req, GFP_ATOMIC)) dev_err(uac->card->dev, "%d Error!\n", __LINE__); } @@ -199,7 +321,6 @@ struct uac_rtd_params *prm; struct g_audio *audio_dev; struct uac_params *params; - unsigned long flags; int err = 0; audio_dev = uac->audio_dev; @@ -209,8 +330,6 @@ prm = &uac->p_prm; else prm = &uac->c_prm; - - spin_lock_irqsave(&prm->lock, flags); /* Reset */ prm->hw_ptr = 0; @@ -227,8 +346,6 @@ default: err = -EINVAL; } - - spin_unlock_irqrestore(&prm->lock, flags); /* Clear buffer after Play stops */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !prm->ss) @@ -250,75 +367,58 @@ return bytes_to_frames(substream->runtime, prm->hw_ptr); } -static int uac_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) +static u64 uac_ssize_to_fmt(int ssize) { - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); -} + u64 ret; -static int uac_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); + switch (ssize) { + case 3: + ret = SNDRV_PCM_FMTBIT_S24_3LE; + break; + case 4: + ret = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + ret = SNDRV_PCM_FMTBIT_S16_LE; + break; + } + + return ret; } static int uac_pcm_open(struct snd_pcm_substream *substream) { struct snd_uac_chip *uac = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; - struct g_audio *audio_dev = uac->audio_dev; + struct g_audio *audio_dev; struct uac_params *params; + struct uac_rtd_params *prm; int p_ssize, c_ssize; - int p_srate, c_srate; int p_chmask, c_chmask; + audio_dev = uac->audio_dev; params = &audio_dev->params; p_ssize = params->p_ssize; c_ssize = params->c_ssize; - p_srate = params->p_srate_active; - c_srate = params->c_srate_active; p_chmask = params->p_chmask; c_chmask = params->c_chmask; - uac->p_residue = 0; + uac->p_residue_mil = 0; runtime->hw = uac_pcm_hardware; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - spin_lock_init(&uac->p_prm.lock); - runtime->hw.rate_min = p_srate; - switch (p_ssize) { - case 3: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; - break; - case 4: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; - break; - default: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; - break; - } + runtime->hw.formats = uac_ssize_to_fmt(p_ssize); runtime->hw.channels_min = num_channels(p_chmask); - runtime->hw.period_bytes_min = 2 * uac->p_prm.max_psize - / runtime->hw.periods_min; + prm = &uac->p_prm; } else { - spin_lock_init(&uac->c_prm.lock); - runtime->hw.rate_min = c_srate; - switch (c_ssize) { - case 3: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE; - break; - case 4: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE; - break; - default: - runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; - break; - } + runtime->hw.formats = uac_ssize_to_fmt(c_ssize); runtime->hw.channels_min = num_channels(c_chmask); - runtime->hw.period_bytes_min = 2 * uac->c_prm.max_psize - / runtime->hw.periods_min; + prm = &uac->c_prm; } + runtime->hw.period_bytes_min = 2 * prm->max_psize + / runtime->hw.periods_min; + runtime->hw.rate_min = prm->srate; runtime->hw.rate_max = runtime->hw.rate_min; runtime->hw.channels_max = runtime->hw.channels_min; @@ -326,52 +426,6 @@ return 0; } - -static int uac_pcm_rate_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 324000; - return 0; -} - -static int uac_pcm_rate_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_uac_chip *uac = snd_kcontrol_chip(kcontrol); - struct g_audio *audio_dev = uac->audio_dev; - struct uac_params *params = &audio_dev->params; - - if (kcontrol->private_value == SNDRV_PCM_STREAM_CAPTURE) - ucontrol->value.integer.value[0] = params->c_srate_active; - else if (kcontrol->private_value == SNDRV_PCM_STREAM_PLAYBACK) - ucontrol->value.integer.value[0] = params->p_srate_active; - else - return -EINVAL; - - return 0; -} - -static struct snd_kcontrol_new uac_pcm_controls[] = { -{ - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "Capture Rate", - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .info = uac_pcm_rate_info, - .get = uac_pcm_rate_get, - .private_value = SNDRV_PCM_STREAM_CAPTURE, -}, -{ - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = "Playback Rate", - .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, - .info = uac_pcm_rate_info, - .get = uac_pcm_rate_get, - .private_value = SNDRV_PCM_STREAM_PLAYBACK, -}, -}; /* ALSA cries without these function pointers */ static int uac_pcm_null(struct snd_pcm_substream *substream) @@ -382,9 +436,6 @@ static const struct snd_pcm_ops uac_pcm_ops = { .open = uac_pcm_open, .close = uac_pcm_null, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = uac_pcm_hw_params, - .hw_free = uac_pcm_hw_free, .trigger = uac_pcm_trigger, .pointer = uac_pcm_pointer, .prepare = uac_pcm_null, @@ -404,16 +455,16 @@ params = &audio_dev->params; for (i = 0; i < params->req_number; i++) { - if (prm->ureq[i].req) { - if (usb_ep_dequeue(ep, prm->ureq[i].req)) - usb_ep_free_request(ep, prm->ureq[i].req); + if (prm->reqs[i]) { + if (usb_ep_dequeue(ep, prm->reqs[i])) + usb_ep_free_request(ep, prm->reqs[i]); /* * If usb_ep_dequeue() cannot successfully dequeue the * request, the request will be freed by the completion * callback. */ - prm->ureq[i].req = NULL; + prm->reqs[i] = NULL; } } @@ -423,34 +474,62 @@ dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); } -static struct snd_kcontrol *u_audio_get_ctl(struct g_audio *audio_dev, - const char *name) +static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *ep) { - struct snd_ctl_elem_id elem_id; + struct snd_uac_chip *uac = prm->uac; - memset(&elem_id, 0, sizeof(elem_id)); - elem_id.iface = SNDRV_CTL_ELEM_IFACE_PCM; - strlcpy(elem_id.name, name, sizeof(elem_id.name)); - return snd_ctl_find_id(audio_dev->uac->card, &elem_id); + if (!prm->fb_ep_enabled) + return; + + prm->fb_ep_enabled = false; + + if (prm->req_fback) { + if (usb_ep_dequeue(ep, prm->req_fback)) { + kfree(prm->req_fback->buf); + usb_ep_free_request(ep, prm->req_fback); + } + prm->req_fback = NULL; + } + + if (usb_ep_disable(ep)) + dev_err(uac->card->dev, "%s:%d Error!\n", __func__, __LINE__); +} + +static void set_active(struct uac_rtd_params *prm, bool active) +{ + // notifying through the Rate ctrl + struct snd_kcontrol *kctl = prm->snd_kctl_rate; + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + if (prm->active != active) { + prm->active = active; + snd_ctl_notify(prm->uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &kctl->id); + } + spin_unlock_irqrestore(&prm->lock, flags); } int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate) { - struct snd_kcontrol *ctl = u_audio_get_ctl(audio_dev, "Capture Rate"); struct uac_params *params = &audio_dev->params; + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; int i; + unsigned long flags; + dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); + prm = &uac->c_prm; for (i = 0; i < UAC_MAX_RATES; i++) { - if (params->c_srate[i] == srate) { + if (params->c_srates[i] == srate) { + spin_lock_irqsave(&prm->lock, flags); + prm->srate = srate; audio_dev->usb_state[SET_SAMPLE_RATE_OUT] = true; schedule_work(&audio_dev->work); - - params->c_srate_active = srate; - snd_ctl_notify(audio_dev->uac->card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + spin_unlock_irqrestore(&prm->lock, flags); return 0; } - if (params->c_srate[i] == 0) + if (params->c_srates[i] == 0) break; } @@ -458,62 +537,40 @@ } EXPORT_SYMBOL_GPL(u_audio_set_capture_srate); -static void u_audio_set_playback_pktsize(struct g_audio *audio_dev, int srate) +int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val) { - struct uac_params *params = &audio_dev->params; struct snd_uac_chip *uac = audio_dev->uac; - struct usb_gadget *gadget = audio_dev->gadget; - const struct usb_endpoint_descriptor *ep_desc; struct uac_rtd_params *prm; - unsigned int factor; + unsigned long flags; - prm = &uac->p_prm; - /* set srate before starting playback, epin is not configured */ - if (!prm->ep_enabled) - return; - - ep_desc = audio_dev->in_ep->desc; - - /* pre-calculate the playback endpoint's interval */ - if (gadget->speed == USB_SPEED_FULL) - factor = 1000; - else - factor = 8000; - - /* pre-compute some values for iso_complete() */ - uac->p_framesize = params->p_ssize * - num_channels(params->p_chmask); - uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); - uac->p_pktsize = min_t(unsigned int, - uac->p_framesize * - (params->p_srate_active / uac->p_interval), - prm->max_psize); - - if (uac->p_pktsize < prm->max_psize) - uac->p_pktsize_residue = uac->p_framesize * - (params->p_srate_active % uac->p_interval); - else - uac->p_pktsize_residue = 0; + prm = &uac->c_prm; + spin_lock_irqsave(&prm->lock, flags); + *val = prm->srate; + spin_unlock_irqrestore(&prm->lock, flags); + return 0; } +EXPORT_SYMBOL_GPL(u_audio_get_capture_srate); int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate) { - struct snd_kcontrol *ctl = u_audio_get_ctl(audio_dev, "Playback Rate"); struct uac_params *params = &audio_dev->params; + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; int i; + unsigned long flags; + dev_dbg(&audio_dev->gadget->dev, "%s: srate %d\n", __func__, srate); + prm = &uac->p_prm; for (i = 0; i < UAC_MAX_RATES; i++) { - if (params->p_srate[i] == srate) { + if (params->p_srates[i] == srate) { + spin_lock_irqsave(&prm->lock, flags); + prm->srate = srate; audio_dev->usb_state[SET_SAMPLE_RATE_IN] = true; schedule_work(&audio_dev->work); - - params->p_srate_active = srate; - u_audio_set_playback_pktsize(audio_dev, srate); - snd_ctl_notify(audio_dev->uac->card, - SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + spin_unlock_irqrestore(&prm->lock, flags); return 0; } - if (params->p_srate[i] == 0) + if (params->p_srates[i] == 0) break; } @@ -521,13 +578,27 @@ } EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); +int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + prm = &uac->p_prm; + spin_lock_irqsave(&prm->lock, flags); + *val = prm->srate; + spin_unlock_irqrestore(&prm->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_playback_srate); + int u_audio_start_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; struct usb_gadget *gadget = audio_dev->gadget; struct device *dev = &gadget->dev; - struct usb_request *req; - struct usb_ep *ep; + struct usb_request *req, *req_fback; + struct usb_ep *ep, *ep_fback; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; int req_len, i; @@ -545,33 +616,72 @@ audio_dev->stream_state[STATE_OUT] = true; schedule_work(&audio_dev->work); - ep = audio_dev->out_ep; prm = &uac->c_prm; + dev_dbg(dev, "start capture with rate %d\n", prm->srate); + ep = audio_dev->out_ep; config_ep_by_speed(gadget, &audio_dev->func, ep); - req_len = prm->max_psize; + req_len = ep->maxpacket; prm->ep_enabled = true; usb_ep_enable(ep); for (i = 0; i < params->req_number; i++) { - if (!prm->ureq[i].req) { + if (!prm->reqs[i]) { req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req == NULL) return -ENOMEM; - prm->ureq[i].req = req; - prm->ureq[i].pp = prm; + prm->reqs[i] = req; req->zero = 0; - req->context = &prm->ureq[i]; + req->context = prm; req->length = req_len; req->complete = u_audio_iso_complete; - req->buf = prm->rbuf + i * prm->max_psize; + req->buf = prm->rbuf + i * ep->maxpacket; } - if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } + + set_active(&uac->c_prm, true); + + ep_fback = audio_dev->in_ep_fback; + if (!ep_fback) + return 0; + + /* Setup feedback endpoint */ + config_ep_by_speed(gadget, &audio_dev->func, ep_fback); + prm->fb_ep_enabled = true; + usb_ep_enable(ep_fback); + req_len = ep_fback->maxpacket; + + req_fback = usb_ep_alloc_request(ep_fback, GFP_ATOMIC); + if (req_fback == NULL) + return -ENOMEM; + + prm->req_fback = req_fback; + req_fback->zero = 0; + req_fback->context = prm; + req_fback->length = req_len; + req_fback->complete = u_audio_iso_fback_complete; + + req_fback->buf = kzalloc(req_len, GFP_ATOMIC); + if (!req_fback->buf) + return -ENOMEM; + + /* + * Configure the feedback endpoint's reported frequency. + * Always start with original frequency since its deviation can't + * be meauserd at start of playback + */ + prm->pitch = 1000000; + u_audio_set_fback_frequency(audio_dev->gadget->speed, ep, + prm->srate, prm->pitch, + req_fback->buf); + + if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC)) + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); return 0; } @@ -581,6 +691,9 @@ { struct snd_uac_chip *uac = audio_dev->uac; + set_active(&uac->c_prm, false); + if (audio_dev->in_ep_fback) + free_ep_fback(&uac->c_prm, audio_dev->in_ep_fback); free_ep(&uac->c_prm, audio_dev->out_ep); audio_dev->usb_state[SET_INTERFACE_OUT] = true; @@ -593,12 +706,15 @@ { struct snd_uac_chip *uac = audio_dev->uac; struct usb_gadget *gadget = audio_dev->gadget; - struct device *dev = audio_dev->device; + struct device *dev = &gadget->dev; struct usb_request *req; struct usb_ep *ep; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; + unsigned int factor; + const struct usb_endpoint_descriptor *ep_desc; int req_len, i; + unsigned int p_pktsize; /* * For better compatibility on some PC Hosts which @@ -613,37 +729,58 @@ audio_dev->stream_state[STATE_IN] = true; schedule_work(&audio_dev->work); - dev_dbg(dev, "start playback with rate %d\n", params->p_srate_active); - ep = audio_dev->in_ep; prm = &uac->p_prm; + dev_dbg(dev, "start playback with rate %d\n", prm->srate); + ep = audio_dev->in_ep; config_ep_by_speed(gadget, &audio_dev->func, ep); + + ep_desc = ep->desc; + /* + * Always start with original frequency + */ + prm->pitch = 1000000; + + /* pre-calculate the playback endpoint's interval */ + if (gadget->speed == USB_SPEED_FULL) + factor = 1000; + else + factor = 8000; + + /* pre-compute some values for iso_complete() */ + uac->p_framesize = params->p_ssize * + num_channels(params->p_chmask); + uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); + p_pktsize = min_t(unsigned int, + uac->p_framesize * + (prm->srate / uac->p_interval), + ep->maxpacket); + + req_len = p_pktsize; + uac->p_residue_mil = 0; prm->ep_enabled = true; usb_ep_enable(ep); - u_audio_set_playback_pktsize(audio_dev, params->p_srate_active); - req_len = uac->p_pktsize; - uac->p_residue = 0; - for (i = 0; i < params->req_number; i++) { - if (!prm->ureq[i].req) { + if (!prm->reqs[i]) { req = usb_ep_alloc_request(ep, GFP_ATOMIC); if (req == NULL) return -ENOMEM; - prm->ureq[i].req = req; - prm->ureq[i].pp = prm; + prm->reqs[i] = req; req->zero = 0; - req->context = &prm->ureq[i]; + req->context = prm; req->length = req_len; req->complete = u_audio_iso_complete; - req->buf = prm->rbuf + i * prm->max_psize; + req->buf = prm->rbuf + i * ep->maxpacket; } - if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC)) + if (usb_ep_queue(ep, prm->reqs[i], GFP_ATOMIC)) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); } + + set_active(&uac->p_prm, true); return 0; } @@ -653,6 +790,7 @@ { struct snd_uac_chip *uac = audio_dev->uac; + set_active(&uac->p_prm, false); free_ep(&uac->p_prm, audio_dev->in_ep); audio_dev->usb_state[SET_INTERFACE_IN] = true; @@ -661,57 +799,424 @@ } EXPORT_SYMBOL_GPL(u_audio_stop_playback); -int u_audio_fu_set_cmd(struct usb_audio_control *con, u8 cmd, int value) +void u_audio_suspend(struct g_audio *audio_dev) { - struct g_audio *audio_dev = (struct g_audio *)con->context; - struct uac_params *params = &audio_dev->params; + struct snd_uac_chip *uac = audio_dev->uac; - switch (cmd) { - case UAC_SET_CUR: - if (!strncmp(con->name, "Capture Mute", 12)) { - params->c_mute = value; - audio_dev->usb_state[SET_MUTE_OUT] = true; - } else if (!strncmp(con->name, "Capture Volume", 14)) { - params->c_volume = value; - audio_dev->usb_state[SET_VOLUME_OUT] = true; - } else if (!strncmp(con->name, "Playback Mute", 13)) { - params->p_mute = value; - audio_dev->usb_state[SET_MUTE_IN] = true; - } else if (!strncmp(con->name, "Playback Volume", 15)) { - params->p_volume = value; - audio_dev->usb_state[SET_VOLUME_IN] = true; - } - break; - case UAC_SET_RES: - /* fall through */ - default: - return 0; - } + set_active(&uac->p_prm, false); + set_active(&uac->c_prm, false); +} +EXPORT_SYMBOL_GPL(u_audio_suspend); - con->data[cmd] = value; - schedule_work(&audio_dev->work); +int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + *val = prm->volume; + spin_unlock_irqrestore(&prm->lock, flags); return 0; } -EXPORT_SYMBOL_GPL(u_audio_fu_set_cmd); +EXPORT_SYMBOL_GPL(u_audio_get_volume); -int u_audio_fu_get_cmd(struct usb_audio_control *con, u8 cmd) +int u_audio_set_volume(struct g_audio *audio_dev, int playback, s16 val) { - struct g_audio *audio_dev = (struct g_audio *)con->context; + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; - dev_dbg(audio_dev->device, "GET_CMD con %s cmd %d data %d\n", - con->name, cmd, (int16_t)con->data[cmd]); - return con->data[cmd]; + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + val = clamp(val, prm->volume_min, prm->volume_max); + if (prm->volume != val) { + prm->volume = val; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change) { + if (playback) + audio_dev->usb_state[SET_VOLUME_IN] = true; + else + audio_dev->usb_state[SET_VOLUME_OUT] = true; + schedule_work(&audio_dev->work); + + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &prm->snd_kctl_volume->id); + } + + return 0; } -EXPORT_SYMBOL_GPL(u_audio_fu_get_cmd); +EXPORT_SYMBOL_GPL(u_audio_set_volume); + +int u_audio_get_mute(struct g_audio *audio_dev, int playback, int *val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + spin_lock_irqsave(&prm->lock, flags); + *val = prm->mute; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_get_mute); + +int u_audio_set_mute(struct g_audio *audio_dev, int playback, int val) +{ + struct snd_uac_chip *uac = audio_dev->uac; + struct uac_rtd_params *prm; + unsigned long flags; + int change = 0; + int mute; + + if (playback) + prm = &uac->p_prm; + else + prm = &uac->c_prm; + + mute = val ? 1 : 0; + + spin_lock_irqsave(&prm->lock, flags); + if (prm->mute != mute) { + prm->mute = mute; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change) { + if (playback) + audio_dev->usb_state[SET_MUTE_IN] = true; + else + audio_dev->usb_state[SET_MUTE_OUT] = true; + schedule_work(&audio_dev->work); + + snd_ctl_notify(uac->card, SNDRV_CTL_EVENT_MASK_VALUE, + &prm->snd_kctl_mute->id); + } + + return 0; +} +EXPORT_SYMBOL_GPL(u_audio_set_mute); + + +static int u_audio_pitch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = &audio_dev->params; + unsigned int pitch_min, pitch_max; + + pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; + pitch_max = (1000 + params->fb_max) * 1000; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = pitch_min; + uinfo->value.integer.max = pitch_max; + uinfo->value.integer.step = 1; + return 0; +} + +static int u_audio_pitch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = prm->pitch; + + return 0; +} + +static int u_audio_pitch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = &audio_dev->params; + unsigned int val; + unsigned int pitch_min, pitch_max; + int change = 0; + + pitch_min = (1000 - FBACK_SLOW_MAX) * 1000; + pitch_max = (1000 + params->fb_max) * 1000; + + val = ucontrol->value.integer.value[0]; + + if (val < pitch_min) + val = pitch_min; + if (val > pitch_max) + val = pitch_max; + + if (prm->pitch != val) { + prm->pitch = val; + change = 1; + } + + return change; +} + +static int u_audio_mute_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->value.integer.step = 1; + + return 0; +} + +static int u_audio_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + ucontrol->value.integer.value[0] = !prm->mute; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} + +static int u_audio_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + unsigned int val; + unsigned long flags; + int change = 0; + + val = !ucontrol->value.integer.value[0]; + + spin_lock_irqsave(&prm->lock, flags); + if (val != prm->mute) { + prm->mute = val; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change && audio_dev->notify) + audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_MUTE); + + return change; +} + +/* + * TLV callback for mixer volume controls + */ +static int u_audio_volume_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *_tlv) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + DECLARE_TLV_DB_MINMAX(scale, 0, 0); + + if (size < sizeof(scale)) + return -ENOMEM; + + /* UAC volume resolution is 1/256 dB, TLV is 1/100 dB */ + scale[2] = (prm->volume_min * 100) / 256; + scale[3] = (prm->volume_max * 100) / 256; + if (copy_to_user(_tlv, scale, sizeof(scale))) + return -EFAULT; + + return 0; +} + +static int u_audio_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = + (prm->volume_max - prm->volume_min + prm->volume_res - 1) + / prm->volume_res; + uinfo->value.integer.step = 1; + + return 0; +} + +static int u_audio_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + ucontrol->value.integer.value[0] = + (prm->volume - prm->volume_min) / prm->volume_res; + spin_unlock_irqrestore(&prm->lock, flags); + + return 0; +} + +static int u_audio_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + unsigned int val; + s16 volume; + unsigned long flags; + int change = 0; + + val = ucontrol->value.integer.value[0]; + + spin_lock_irqsave(&prm->lock, flags); + volume = (val * prm->volume_res) + prm->volume_min; + volume = clamp(volume, prm->volume_min, prm->volume_max); + if (volume != prm->volume) { + prm->volume = volume; + change = 1; + } + spin_unlock_irqrestore(&prm->lock, flags); + + if (change && audio_dev->notify) + audio_dev->notify(audio_dev, prm->fu_id, UAC_FU_VOLUME); + + return change; +} + +static int get_max_srate(const int *srates) +{ + int i, max_srate = 0; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (srates[i] == 0) + break; + if (srates[i] > max_srate) + max_srate = srates[i]; + } + return max_srate; +} + +static int get_min_srate(const int *srates) +{ + int i, min_srate = INT_MAX; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (srates[i] == 0) + break; + if (srates[i] < min_srate) + min_srate = srates[i]; + } + return min_srate; +} + +static int u_audio_rate_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const int *srates; + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + struct snd_uac_chip *uac = prm->uac; + struct g_audio *audio_dev = uac->audio_dev; + struct uac_params *params = &audio_dev->params; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + + if (prm == &uac->c_prm) + srates = params->c_srates; + else + srates = params->p_srates; + uinfo->value.integer.min = get_min_srate(srates); + uinfo->value.integer.max = get_max_srate(srates); + return 0; +} + +static int u_audio_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct uac_rtd_params *prm = snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&prm->lock, flags); + if (prm->active) + ucontrol->value.integer.value[0] = prm->srate; + else + /* not active: reporting zero rate */ + ucontrol->value.integer.value[0] = 0; + spin_unlock_irqrestore(&prm->lock, flags); + return 0; +} + +static struct snd_kcontrol_new u_audio_controls[] = { + [UAC_FBACK_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Capture Pitch 1000000", + .info = u_audio_pitch_info, + .get = u_audio_pitch_get, + .put = u_audio_pitch_put, + }, + [UAC_P_PITCH_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Playback Pitch 1000000", + .info = u_audio_pitch_info, + .get = u_audio_pitch_get, + .put = u_audio_pitch_put, + }, + [UAC_MUTE_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = u_audio_mute_info, + .get = u_audio_mute_get, + .put = u_audio_mute_put, + }, + [UAC_VOLUME_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "", /* will be filled later */ + .info = u_audio_volume_info, + .get = u_audio_volume_get, + .put = u_audio_volume_put, + }, + [UAC_RATE_CTRL] { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "", /* will be filled later */ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = u_audio_rate_info, + .get = u_audio_rate_get, + }, +}; static void g_audio_work(struct work_struct *data) { struct g_audio *audio = container_of(data, struct g_audio, work); - struct uac_params *params = &audio->params; + struct usb_gadget *gadget = audio->gadget; + struct snd_uac_chip *uac = audio->uac; + struct uac_rtd_params *prm; + struct device *dev = &gadget->dev; char *uac_event[4] = { NULL, NULL, NULL, NULL }; char str[19]; - signed short volume; int i; for (i = 0; i < SET_USB_STATE_MAX; i++) { @@ -734,48 +1239,50 @@ case SET_SAMPLE_RATE_OUT: uac_event[0] = "USB_STATE=SET_SAMPLE_RATE"; uac_event[1] = "STREAM_DIRECTION=OUT"; + prm = &uac->c_prm; snprintf(str, sizeof(str), "SAMPLE_RATE=%d", - params->c_srate_active); + prm->srate); uac_event[2] = str; break; case SET_SAMPLE_RATE_IN: uac_event[0] = "USB_STATE=SET_SAMPLE_RATE"; uac_event[1] = "STREAM_DIRECTION=IN"; + prm = &uac->p_prm; snprintf(str, sizeof(str), "SAMPLE_RATE=%d", - params->p_srate_active); + prm->srate); uac_event[2] = str; break; case SET_MUTE_OUT: uac_event[0] = "USB_STATE=SET_MUTE"; uac_event[1] = "STREAM_DIRECTION=OUT"; - snprintf(str, sizeof(str), "MUTE=%d", params->c_mute); + prm = &uac->c_prm; + snprintf(str, sizeof(str), "MUTE=%d", prm->mute); uac_event[2] = str; break; case SET_MUTE_IN: uac_event[0] = "USB_STATE=SET_MUTE"; uac_event[1] = "STREAM_DIRECTION=IN"; - snprintf(str, sizeof(str), "MUTE=%d", params->p_mute); + prm = &uac->p_prm; + snprintf(str, sizeof(str), "MUTE=%d", prm->mute); uac_event[2] = str; break; case SET_VOLUME_OUT: uac_event[0] = "USB_STATE=SET_VOLUME"; uac_event[1] = "STREAM_DIRECTION=OUT"; - volume = (signed short)params->c_volume; - volume /= UAC_VOLUME_RES; - snprintf(str, sizeof(str), "VOLUME=%d%%", volume + 50); + prm = &uac->c_prm; + snprintf(str, sizeof(str), "VOLUME=0x%hx", prm->volume); uac_event[2] = str; break; case SET_VOLUME_IN: uac_event[0] = "USB_STATE=SET_VOLUME"; uac_event[1] = "STREAM_DIRECTION=IN"; - volume = (signed short)params->p_volume; - volume /= UAC_VOLUME_RES; - snprintf(str, sizeof(str), "VOLUME=%d%%", volume + 50); + prm = &uac->p_prm; + snprintf(str, sizeof(str), "VOLUME=0x%hx", prm->volume); uac_event[2] = str; break; case SET_AUDIO_CLK: uac_event[0] = "USB_STATE=SET_AUDIO_CLK"; - snprintf(str, sizeof(str), "PPM=%d", params->ppm); + snprintf(str, sizeof(str), "PPM=%d", audio->params.ppm); uac_event[1] = str; default: break; @@ -784,8 +1291,8 @@ audio->usb_state[i] = false; kobject_uevent_env(&audio->device->kobj, KOBJ_CHANGE, uac_event); - dev_dbg(audio->device, "%s: sent uac uevent %s %s %s\n", - __func__, uac_event[0], uac_event[1], uac_event[2]); + dev_dbg(dev, "%s: sent uac uevent %s %s %s\n", __func__, + uac_event[0], uac_event[1], uac_event[2]); } } @@ -896,10 +1403,10 @@ struct snd_uac_chip *uac; struct snd_card *card; struct snd_pcm *pcm; + struct snd_kcontrol *kctl; struct uac_params *params; int p_chmask, c_chmask; - int err; - int i; + int i, err; if (!g_audio) return -EINVAL; @@ -923,12 +1430,15 @@ if (c_chmask) { struct uac_rtd_params *prm = &uac->c_prm; - uac->c_prm.uac = uac; + spin_lock_init(&prm->lock); + uac->c_prm.uac = uac; prm->max_psize = g_audio->out_ep_maxpsize; + prm->srate = params->c_srates[0]; - prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req), - GFP_KERNEL); - if (!prm->ureq) { + prm->reqs = kcalloc(params->req_number, + sizeof(struct usb_request *), + GFP_KERNEL); + if (!prm->reqs) { err = -ENOMEM; goto fail; } @@ -945,12 +1455,15 @@ if (p_chmask) { struct uac_rtd_params *prm = &uac->p_prm; + spin_lock_init(&prm->lock); uac->p_prm.uac = uac; prm->max_psize = g_audio->in_ep_maxpsize; + prm->srate = params->p_srates[0]; - prm->ureq = kcalloc(params->req_number, sizeof(struct uac_req), - GFP_KERNEL); - if (!prm->ureq) { + prm->reqs = kcalloc(params->req_number, + sizeof(struct usb_request *), + GFP_KERNEL); + if (!prm->reqs) { err = -ENOMEM; goto fail; } @@ -981,27 +1494,155 @@ if (err < 0) goto snd_fail; - strlcpy(pcm->name, pcm_name, sizeof(pcm->name)); + strscpy(pcm->name, pcm_name, sizeof(pcm->name)); pcm->private_data = uac; uac->pcm = pcm; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &uac_pcm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &uac_pcm_ops); - strlcpy(card->driver, card_name, sizeof(card->driver)); - strlcpy(card->shortname, card_name, sizeof(card->shortname)); - sprintf(card->longname, "%s %i", card_name, card->dev->id); + /* + * Create mixer and controls + * Create only if it's required on USB side + */ + if ((c_chmask && g_audio->in_ep_fback) + || (p_chmask && params->p_fu.id) + || (c_chmask && params->c_fu.id)) + strscpy(card->mixername, card_name, sizeof(card->driver)); - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), 0, BUFF_SIZE_MAX); + if (c_chmask && g_audio->in_ep_fback) { + kctl = snd_ctl_new1(&u_audio_controls[UAC_FBACK_CTRL], + &uac->c_prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } - /* Add controls */ - for (i = 0; i < ARRAY_SIZE(uac_pcm_controls); i++) { - err = snd_ctl_add(card, - snd_ctl_new1(&uac_pcm_controls[i], uac)); + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + err = snd_ctl_add(card, kctl); if (err < 0) goto snd_fail; } + + if (p_chmask) { + kctl = snd_ctl_new1(&u_audio_controls[UAC_P_PITCH_CTRL], + &uac->p_prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + } + + for (i = 0; i <= SNDRV_PCM_STREAM_LAST; i++) { + struct uac_rtd_params *prm; + struct uac_fu_params *fu; + char ctrl_name[24]; + char *direction; + + if (!pcm->streams[i].substream_count) + continue; + + if (i == SNDRV_PCM_STREAM_PLAYBACK) { + prm = &uac->p_prm; + fu = ¶ms->p_fu; + direction = "Playback"; + } else { + prm = &uac->c_prm; + fu = ¶ms->c_fu; + direction = "Capture"; + } + + prm->fu_id = fu->id; + + if (fu->mute_present) { + snprintf(ctrl_name, sizeof(ctrl_name), + "PCM %s Switch", direction); + + u_audio_controls[UAC_MUTE_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_MUTE_CTRL], + prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_mute = kctl; + prm->mute = 0; + } + + if (fu->volume_present) { + snprintf(ctrl_name, sizeof(ctrl_name), + "PCM %s Volume", direction); + + u_audio_controls[UAC_VOLUME_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_VOLUME_CTRL], + prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + + kctl->tlv.c = u_audio_volume_tlv; + kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_volume = kctl; + prm->volume = fu->volume_max; + prm->volume_max = fu->volume_max; + prm->volume_min = fu->volume_min; + prm->volume_res = fu->volume_res; + } + + /* Add rate control */ + snprintf(ctrl_name, sizeof(ctrl_name), + "%s Rate", direction); + u_audio_controls[UAC_RATE_CTRL].name = ctrl_name; + + kctl = snd_ctl_new1(&u_audio_controls[UAC_RATE_CTRL], prm); + if (!kctl) { + err = -ENOMEM; + goto snd_fail; + } + + kctl->id.device = pcm->device; + kctl->id.subdevice = 0; + + err = snd_ctl_add(card, kctl); + if (err < 0) + goto snd_fail; + prm->snd_kctl_rate = kctl; + } + + strscpy(card->driver, card_name, sizeof(card->driver)); + strscpy(card->shortname, card_name, sizeof(card->shortname)); + sprintf(card->longname, "%s %i", card_name, card->dev->id); + + snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, + NULL, 0, BUFF_SIZE_MAX); err = snd_card_register(card); if (err < 0) @@ -1024,8 +1665,8 @@ snd_fail: snd_card_free(card); fail: - kfree(uac->p_prm.ureq); - kfree(uac->c_prm.ureq); + kfree(uac->p_prm.reqs); + kfree(uac->c_prm.reqs); kfree(uac->p_prm.rbuf); kfree(uac->c_prm.rbuf); kfree(uac); @@ -1051,13 +1692,10 @@ uac = g_audio->uac; card = uac->card; if (card) - snd_card_free(card); + snd_card_free_when_closed(card); - free_ep(&uac->c_prm, g_audio->out_ep); - free_ep(&uac->p_prm, g_audio->in_ep); - - kfree(uac->p_prm.ureq); - kfree(uac->c_prm.ureq); + kfree(uac->p_prm.reqs); + kfree(uac->c_prm.reqs); kfree(uac->p_prm.rbuf); kfree(uac->c_prm.rbuf); kfree(uac); -- Gitblit v1.6.2