From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 13 May 2024 10:30:14 +0000
Subject: [PATCH] modify sin led gpio
---
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