summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget/function
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function')
-rw-r--r--drivers/usb/gadget/function/f_fs.c4
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c70
-rw-r--r--drivers/usb/gadget/function/f_phonet.c4
-rw-r--r--drivers/usb/gadget/function/f_serial.c4
-rw-r--r--drivers/usb/gadget/function/f_uac1.c236
-rw-r--r--drivers/usb/gadget/function/f_uac2.c476
-rw-r--r--drivers/usb/gadget/function/u_audio.c238
-rw-r--r--drivers/usb/gadget/function/u_audio.h14
-rw-r--r--drivers/usb/gadget/function/u_uac1.h7
-rw-r--r--drivers/usb/gadget/function/u_uac2.h11
-rw-r--r--drivers/usb/gadget/function/uac_common.h9
11 files changed, 882 insertions, 191 deletions
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 1922fd02043c..4585ee3a444a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -919,12 +919,12 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile,
data_len, ret);
data_len -= ret;
- buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL);
+ buf = kmalloc(struct_size(buf, storage, data_len), GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf->length = data_len;
buf->data = buf->storage;
- memcpy(buf->storage, data + ret, data_len);
+ memcpy(buf->storage, data + ret, flex_array_size(buf, storage, data_len));
/*
* At this point read_buffer is NULL or READ_BUFFER_DROP (if
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 7371c6e65b10..3a77bca0ebe1 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -1189,6 +1189,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
int msf = common->cmnd[1] & 0x02;
int start_track = common->cmnd[6];
u8 *buf = (u8 *)bh->buf;
+ u8 format;
+ int i, len;
if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
@@ -1196,18 +1198,62 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
return -EINVAL;
}
- memset(buf, 0, 20);
- buf[1] = (20-2); /* TOC data length */
- buf[2] = 1; /* First track number */
- buf[3] = 1; /* Last track number */
- buf[5] = 0x16; /* Data track, copying allowed */
- buf[6] = 0x01; /* Only track is number 1 */
- store_cdrom_address(&buf[8], msf, 0);
+ format = common->cmnd[2] & 0xf;
+ /*
+ * Check if CDB is old style SFF-8020i
+ * i.e. format is in 2 MSBs of byte 9
+ * Mac OS-X host sends us this.
+ */
+ if (format == 0)
+ format = (common->cmnd[9] >> 6) & 0x3;
+
+ switch (format) {
+ case 0:
+ /* Formatted TOC */
+ len = 4 + 2*8; /* 4 byte header + 2 descriptors */
+ memset(buf, 0, len);
+ buf[1] = len - 2; /* TOC Length excludes length field */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+ return len;
+
+ case 2:
+ /* Raw TOC */
+ len = 4 + 3*11; /* 4 byte header + 3 descriptors */
+ memset(buf, 0, len); /* Header + A0, A1 & A2 descriptors */
+ buf[1] = len - 2; /* TOC Length excludes length field */
+ buf[2] = 1; /* First complete session */
+ buf[3] = 1; /* Last complete session */
+
+ buf += 4;
+ /* fill in A0, A1 and A2 points */
+ for (i = 0; i < 3; i++) {
+ buf[0] = 1; /* Session number */
+ buf[1] = 0x16; /* Data track, copying allowed */
+ /* 2 - Track number 0 -> TOC */
+ buf[3] = 0xA0 + i; /* A0, A1, A2 point */
+ /* 4, 5, 6 - Min, sec, frame is zero */
+ buf[8] = 1; /* Pmin: last track number */
+ buf += 11; /* go to next track descriptor */
+ }
+ buf -= 11; /* go back to A2 descriptor */
- buf[13] = 0x16; /* Lead-out track is data */
- buf[14] = 0xAA; /* Lead-out track number */
- store_cdrom_address(&buf[16], msf, curlun->num_sectors);
- return 20;
+ /* For A2, 7, 8, 9, 10 - zero, Pmin, Psec, Pframe of Lead out */
+ store_cdrom_address(&buf[7], msf, curlun->num_sectors);
+ return len;
+
+ default:
+ /* Multi-session, PMA, ATIP, CD-TEXT not supported/required */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
}
static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
@@ -1945,7 +1991,7 @@ static int do_scsi_command(struct fsg_common *common)
common->data_size_from_cmnd =
get_unaligned_be16(&common->cmnd[7]);
reply = check_command(common, 10, DATA_DIR_TO_HOST,
- (7<<6) | (1<<1), 1,
+ (0xf<<6) | (3<<1), 1,
"READ TOC");
if (reply == 0)
reply = do_read_toc(common, bh);
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 068ed8417e5a..0bebbdf3f213 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -668,10 +668,8 @@ static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
{
struct f_phonet *fp;
struct f_phonet_opts *opts;
- int size;
- size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
- fp = kzalloc(size, GFP_KERNEL);
+ fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
if (!fp)
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 1ed8ff0ac2d3..a9480b9e312e 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -345,6 +345,10 @@ static void gser_free(struct usb_function *f)
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
+ struct f_gser *gser = func_to_gser(f);
+
+ /* Ensure port is disconnected before unbinding */
+ gserial_disconnect(&gser->port);
usb_free_all_descriptors(f);
}
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 03f50643fbba..6f0e1d803dc2 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -3,6 +3,7 @@
* f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API)
*
* Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com>
+ * Copyright (C) 2021 Julian Scheel <julian@jusst.de>
*
* This driver doesn't expect any real Audio codec to be present
* on the device - the audio streams are simply sinked to and
@@ -42,6 +43,9 @@ struct f_uac1 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ int ctl_id; /* EP id */
+ int c_srate; /* current capture srate */
+ int p_srate; /* current playback prate */
};
static inline struct f_uac1 *func_to_uac1(struct usb_function *f)
@@ -188,16 +192,18 @@ static struct uac1_as_header_descriptor as_in_header_desc = {
.wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
};
-DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
+DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES);
+#define uac_format_type_i_discrete_descriptor \
+ uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES
-static struct uac_format_type_i_discrete_descriptor_1 as_out_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -221,14 +227,14 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(1),
};
-static struct uac_format_type_i_discrete_descriptor_1 as_in_type_i_desc = {
- .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
+static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
+ .bLength = 0, /* filled on rate setup */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
- .bSamFreqType = 1,
+ .bSamFreqType = 0, /* filled on rate setup */
};
/* Standard ISO OUT Endpoint Descriptor */
@@ -303,7 +309,7 @@ enum {
};
static struct usb_string strings_uac1[] = {
- [STR_AC_IF].s = "AC Interface",
+ /* [STR_AC_IF].s = DYNAMIC, */
[STR_USB_OUT_IT].s = "Playback Input terminal",
[STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels",
[STR_IO_OUT_OT].s = "Playback Output terminal",
@@ -333,6 +339,30 @@ static struct usb_gadget_strings *uac1_strings[] = {
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
+static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct usb_composite_dev *cdev = fn->config->cdev;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac1 *uac1 = func_to_uac1(fn);
+ u8 *buf = (u8 *)req->buf;
+ u32 val = 0;
+
+ if (req->actual != 3) {
+ WARN(cdev, "Invalid data size for UAC_EP_CS_ATTR_SAMPLE_RATE.\n");
+ return;
+ }
+
+ val = buf[0] | (buf[1] << 8) | (buf[2] << 16);
+ if (uac1->ctl_id == (USB_DIR_IN | 2)) {
+ uac1->p_srate = val;
+ u_audio_set_playback_srate(agdev, uac1->p_srate);
+ } else if (uac1->ctl_id == (USB_DIR_OUT | 1)) {
+ uac1->c_srate = val;
+ u_audio_set_capture_srate(agdev, uac1->c_srate);
+ }
+}
+
static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req)
{
struct g_audio *audio = req->context;
@@ -707,18 +737,27 @@ static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_SET_CUR:
+ case UAC_SET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ cdev->gadget->ep0->driver_data = f;
+ uac1->ctl_id = ep;
+ req->complete = uac_cs_attr_sample_rate;
+ }
value = len;
break;
+ }
case UAC_SET_MIN:
break;
@@ -743,16 +782,33 @@ static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = f->config->cdev->req;
+ struct f_uac1 *uac1 = func_to_uac1(f);
+ u8 *buf = (u8 *)req->buf;
int value = -EOPNOTSUPP;
- u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
+ u8 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
+ u8 cs = w_value >> 8;
+ u32 val = 0;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
- case UAC_GET_CUR:
+ case UAC_GET_CUR: {
+ if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) {
+ if (ep == (USB_DIR_IN | 2))
+ val = uac1->p_srate;
+ else if (ep == (USB_DIR_OUT | 1))
+ val = uac1->c_srate;
+ buf[2] = (val >> 16) & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ buf[0] = val & 0xff;
+ }
+ value = len;
+ break;
+ }
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
@@ -905,6 +961,14 @@ static void f_audio_disable(struct usb_function *f)
usb_ep_disable(uac1->int_ep);
}
+static void
+f_audio_suspend(struct usb_function *f)
+{
+ struct f_uac1 *uac1 = func_to_uac1(f);
+
+ u_audio_suspend(&uac1->g_audio);
+}
+
/*-------------------------------------------------------------------------*/
static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
{
@@ -1074,10 +1138,10 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
} else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
dev_err(dev, "Error: incorrect capture sample size\n");
return -EINVAL;
- } else if (!opts->p_srate) {
+ } else if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
- } else if (!opts->c_srate) {
+ } else if (!opts->c_srates[0]) {
dev_err(dev, "Error: incorrect capture sampling rate\n");
return -EINVAL;
}
@@ -1118,10 +1182,9 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- u8 *sam_freq;
- int rate;
int ba_iface_id;
int status;
+ int idx, i;
status = f_audio_validate_opts(audio, dev);
if (status)
@@ -1129,6 +1192,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
+ strings_uac1[STR_AC_IF].s = audio_opts->function_name;
+
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1213,12 +1278,25 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
/* Set sample rates */
- rate = audio_opts->c_srate;
- sam_freq = as_out_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
- rate = audio_opts->p_srate;
- sam_freq = as_in_type_i_desc.tSamFreq[0];
- memcpy(sam_freq, &rate, 3);
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->c_srates[i] == 0)
+ break;
+ memcpy(as_out_type_i_desc.tSamFreq[idx++],
+ &audio_opts->c_srates[i], 3);
+ }
+ as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_out_type_i_desc.bSamFreqType = idx;
+
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (audio_opts->p_srates[i] == 0)
+ break;
+ memcpy(as_in_type_i_desc.tSamFreq[idx++],
+ &audio_opts->p_srates[i], 3);
+ }
+ as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ as_in_type_i_desc.bSamFreqType = idx;
+ uac1->p_srate = audio_opts->p_srates[0];
+ uac1->c_srate = audio_opts->c_srates[0];
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
@@ -1297,7 +1375,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize);
audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize);
audio->params.c_chmask = audio_opts->c_chmask;
- audio->params.c_srate = audio_opts->c_srate;
+ memcpy(audio->params.c_srates, audio_opts->c_srates,
+ sizeof(audio->params.c_srates));
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
@@ -1309,7 +1388,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
audio->params.p_chmask = audio_opts->p_chmask;
- audio->params.p_srate = audio_opts->p_srate;
+ memcpy(audio->params.p_srates, audio_opts->p_srates,
+ sizeof(audio->params.p_srates));
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
@@ -1414,11 +1494,106 @@ end: \
\
CONFIGFS_ATTR(f_uac1_opts_, name)
+#define UAC1_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
+#define UAC1_ATTRIBUTE_STRING(name) \
+static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac1_opts *opts = to_f_uac1_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ "%s", page); \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac1_opts_, name)
+
UAC1_ATTRIBUTE(u32, c_chmask);
-UAC1_ATTRIBUTE(u32, c_srate);
+UAC1_RATE_ATTRIBUTE(c_srate);
UAC1_ATTRIBUTE(u32, c_ssize);
UAC1_ATTRIBUTE(u32, p_chmask);
-UAC1_ATTRIBUTE(u32, p_srate);
+UAC1_RATE_ATTRIBUTE(p_srate);
UAC1_ATTRIBUTE(u32, p_ssize);
UAC1_ATTRIBUTE(u32, req_number);
@@ -1433,6 +1608,7 @@ UAC1_ATTRIBUTE(bool, c_volume_present);
UAC1_ATTRIBUTE(s16, c_volume_min);
UAC1_ATTRIBUTE(s16, c_volume_max);
UAC1_ATTRIBUTE(s16, c_volume_res);
+UAC1_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_chmask,
@@ -1455,6 +1631,8 @@ static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_attr_c_volume_max,
&f_uac1_opts_attr_c_volume_res,
+ &f_uac1_opts_attr_function_name,
+
NULL,
};
@@ -1487,10 +1665,10 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
&f_uac1_func_type);
opts->c_chmask = UAC1_DEF_CCHMASK;
- opts->c_srate = UAC1_DEF_CSRATE;
+ opts->c_srates[0] = UAC1_DEF_CSRATE;
opts->c_ssize = UAC1_DEF_CSSIZE;
opts->p_chmask = UAC1_DEF_PCHMASK;
- opts->p_srate = UAC1_DEF_PSRATE;
+ opts->p_srates[0] = UAC1_DEF_PSRATE;
opts->p_ssize = UAC1_DEF_PSSIZE;
opts->p_mute_present = UAC1_DEF_MUTE_PRESENT;
@@ -1506,6 +1684,9 @@ static struct usb_function_instance *f_audio_alloc_inst(void)
opts->c_volume_res = UAC1_DEF_RES_DB;
opts->req_number = UAC1_DEF_REQ_NUM;
+
+ snprintf(opts->function_name, sizeof(opts->function_name), "AC Interface");
+
return &opts->func_inst;
}
@@ -1562,6 +1743,7 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
uac1->g_audio.func.get_alt = f_audio_get_alt;
uac1->g_audio.func.setup = f_audio_setup;
uac1->g_audio.func.disable = f_audio_disable;
+ uac1->g_audio.func.suspend = f_audio_suspend;
uac1->g_audio.func.free_func = f_audio_free;
return &uac1->g_audio.func;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 097a709549d6..1905a8d8e0c9 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -70,6 +70,8 @@ struct f_uac2 {
/* Interrupt IN endpoint of AC interface */
struct usb_ep *int_ep;
atomic_t int_count;
+ /* transient state, only valid during handling of a single control request */
+ int clock_id;
};
static inline struct f_uac2 *func_to_uac2(struct usb_function *f)
@@ -104,14 +106,11 @@ enum {
STR_AS_IN_ALT1,
};
-static char clksrc_in[8];
-static char clksrc_out[8];
-
static struct usb_string strings_fn[] = {
- [STR_ASSOC].s = "Source/Sink",
+ /* [STR_ASSOC].s = DYNAMIC, */
[STR_IF_CTRL].s = "Topology Control",
- [STR_CLKSRC_IN].s = clksrc_in,
- [STR_CLKSRC_OUT].s = clksrc_out,
+ [STR_CLKSRC_IN].s = "Input Clock",
+ [STR_CLKSRC_OUT].s = "Output Clock",
[STR_USB_IT].s = "USBH Out",
[STR_IO_IT].s = "USBD Out",
[STR_USB_OT].s = "USBH In",
@@ -125,6 +124,16 @@ static struct usb_string strings_fn[] = {
{ },
};
+static const char *const speed_names[] = {
+ [USB_SPEED_UNKNOWN] = "UNKNOWN",
+ [USB_SPEED_LOW] = "LS",
+ [USB_SPEED_FULL] = "FS",
+ [USB_SPEED_HIGH] = "HS",
+ [USB_SPEED_WIRELESS] = "W",
+ [USB_SPEED_SUPER] = "SS",
+ [USB_SPEED_SUPER_PLUS] = "SS+",
+};
+
static struct usb_gadget_strings str_fn = {
.language = 0x0409, /* en-us */
.strings = strings_fn,
@@ -166,7 +175,7 @@ static struct uac_clock_source_descriptor in_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -178,7 +187,7 @@ static struct uac_clock_source_descriptor out_clk_src_desc = {
.bDescriptorSubtype = UAC2_CLOCK_SOURCE,
/* .bClockID = DYNAMIC */
.bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED,
- .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL),
+ .bmControls = (CONTROL_RDWR << CLK_FREQ_CTRL),
.bAssocTerminal = 0,
};
@@ -344,7 +353,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epout_desc = {
@@ -354,7 +363,7 @@ static struct usb_endpoint_descriptor ss_epout_desc = {
.bEndpointAddress = USB_DIR_OUT,
/* .bmAttributes = DYNAMIC */
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = {
@@ -478,7 +487,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_endpoint_descriptor ss_epin_desc = {
@@ -488,7 +497,7 @@ static struct usb_endpoint_descriptor ss_epin_desc = {
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
/* .wMaxPacketSize = DYNAMIC */
- .bInterval = 4,
+ /* .bInterval = DYNAMIC */
};
static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = {
@@ -634,44 +643,50 @@ struct cntrl_cur_lay3 {
__le32 dCUR;
};
-struct cntrl_range_lay3 {
- __le16 wNumSubRanges;
+struct cntrl_subrange_lay3 {
__le32 dMIN;
__le32 dMAX;
__le32 dRES;
} __packed;
-static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
- struct usb_endpoint_descriptor *ep_desc,
- enum usb_device_speed speed, bool is_playback)
-{
- int chmask, srate, ssize;
- u16 max_size_bw, max_size_ep;
- unsigned int factor;
+#define ranges_lay3_size(c) (sizeof(c.wNumSubRanges) \
+ + le16_to_cpu(c.wNumSubRanges) \
+ * sizeof(struct cntrl_subrange_lay3))
- switch (speed) {
- case USB_SPEED_FULL:
- max_size_ep = 1023;
- factor = 1000;
- break;
+#define DECLARE_UAC2_CNTRL_RANGES_LAY3(k, n) \
+ struct cntrl_ranges_lay3_##k { \
+ __le16 wNumSubRanges; \
+ struct cntrl_subrange_lay3 r[n]; \
+} __packed
- case USB_SPEED_HIGH:
- case USB_SPEED_SUPER:
- max_size_ep = 1024;
- factor = 8000;
- break;
+DECLARE_UAC2_CNTRL_RANGES_LAY3(srates, UAC_MAX_RATES);
- default:
- return -EINVAL;
+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_max_bw_for_bint(const struct f_uac2_opts *uac2_opts,
+ u8 bint, unsigned int factor, bool is_playback)
+{
+ int chmask, srate, ssize;
+ u16 max_size_bw;
if (is_playback) {
chmask = uac2_opts->p_chmask;
- srate = uac2_opts->p_srate;
+ srate = get_max_srate(uac2_opts->p_srates);
ssize = uac2_opts->p_ssize;
} else {
chmask = uac2_opts->c_chmask;
- srate = uac2_opts->c_srate;
+ srate = get_max_srate(uac2_opts->c_srates);
ssize = uac2_opts->c_ssize;
}
@@ -681,14 +696,76 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
srate = srate * (1000 + uac2_opts->fb_max) / 1000;
// updated srate is always bigger, therefore DIV_ROUND_UP always yields +1
max_size_bw = num_channels(chmask) * ssize *
- (DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))));
+ (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))));
} else {
// adding 1 frame provision for Win10
max_size_bw = num_channels(chmask) * ssize *
- (DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))) + 1);
+ (DIV_ROUND_UP(srate, factor / (1 << (bint - 1))) + 1);
}
- ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
- max_size_ep));
+ return max_size_bw;
+}
+
+static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac2_opts *uac2_opts,
+ struct usb_endpoint_descriptor *ep_desc,
+ enum usb_device_speed speed, bool is_playback)
+{
+ u16 max_size_bw, max_size_ep;
+ u8 bint, opts_bint;
+ char *dir;
+
+ switch (speed) {
+ case USB_SPEED_FULL:
+ max_size_ep = 1023;
+ // fixed
+ bint = ep_desc->bInterval;
+ max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 1000, is_playback);
+ break;
+
+ case USB_SPEED_HIGH:
+ case USB_SPEED_SUPER:
+ max_size_ep = 1024;
+ if (is_playback)
+ opts_bint = uac2_opts->p_hs_bint;
+ else
+ opts_bint = uac2_opts->c_hs_bint;
+
+ if (opts_bint > 0) {
+ /* fixed bint */
+ bint = opts_bint;
+ max_size_bw = get_max_bw_for_bint(uac2_opts, bint, 8000, is_playback);
+ } else {
+ /* checking bInterval from 4 to 1 whether the required bandwidth fits */
+ for (bint = 4; bint > 0; --bint) {
+ max_size_bw = get_max_bw_for_bint(
+ uac2_opts, bint, 8000, is_playback);
+ if (max_size_bw <= max_size_ep)
+ break;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (is_playback)
+ dir = "Playback";
+ else
+ dir = "Capture";
+
+ if (max_size_bw <= max_size_ep)
+ dev_dbg(dev,
+ "%s %s: Would use wMaxPacketSize %d and bInterval %d\n",
+ speed_names[speed], dir, max_size_bw, bint);
+ else {
+ dev_warn(dev,
+ "%s %s: Req. wMaxPacketSize %d at bInterval %d > max ISOC %d, may drop data!\n",
+ speed_names[speed], dir, max_size_bw, bint, max_size_ep);
+ max_size_bw = max_size_ep;
+ }
+
+ ep_desc->wMaxPacketSize = cpu_to_le16(max_size_bw);
+ ep_desc->bInterval = bint;
return 0;
}
@@ -896,50 +973,45 @@ static void setup_descriptor(struct f_uac2_opts *opts)
static int afunc_validate_opts(struct g_audio *agdev, struct device *dev)
{
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
-
- if (!opts->p_chmask && !opts->c_chmask) {
- dev_err(dev, "Error: no playback and capture channels\n");
- return -EINVAL;
- } else if (opts->p_chmask & ~UAC2_CHANNEL_MASK) {
- dev_err(dev, "Error: unsupported playback channels mask\n");
- return -EINVAL;
- } else if (opts->c_chmask & ~UAC2_CHANNEL_MASK) {
- dev_err(dev, "Error: unsupported capture channels mask\n");
- return -EINVAL;
- } else if ((opts->p_ssize < 1) || (opts->p_ssize > 4)) {
- dev_err(dev, "Error: incorrect playback sample size\n");
- return -EINVAL;
- } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) {
- dev_err(dev, "Error: incorrect capture sample size\n");
- return -EINVAL;
- } else if (!opts->p_srate) {
- dev_err(dev, "Error: incorrect playback sampling rate\n");
+ const char *msg = NULL;
+
+ if (!opts->p_chmask && !opts->c_chmask)
+ msg = "no playback and capture channels";
+ else if (opts->p_chmask & ~UAC2_CHANNEL_MASK)
+ msg = "unsupported playback channels mask";
+ else if (opts->c_chmask & ~UAC2_CHANNEL_MASK)
+ msg = "unsupported capture channels mask";
+ else if ((opts->p_ssize < 1) || (opts->p_ssize > 4))
+ msg = "incorrect playback sample size";
+ else if ((opts->c_ssize < 1) || (opts->c_ssize > 4))
+ msg = "incorrect capture sample size";
+ else if (!opts->p_srates[0])
+ msg = "incorrect playback sampling rate";
+ else if (!opts->c_srates[0])
+ msg = "incorrect capture sampling rate";
+
+ else if (opts->p_volume_max <= opts->p_volume_min)
+ msg = "incorrect playback volume max/min";
+ else if (opts->c_volume_max <= opts->c_volume_min)
+ msg = "incorrect capture volume max/min";
+ else if (opts->p_volume_res <= 0)
+ msg = "negative/zero playback volume resolution";
+ else if (opts->c_volume_res <= 0)
+ msg = "negative/zero capture volume resolution";
+
+ else if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res)
+ msg = "incorrect playback volume resolution";
+ else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res)
+ msg = "incorrect capture volume resolution";
+
+ else if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4))
+ msg = "incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)";
+ else if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4))
+ msg = "incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)";
+
+ if (msg) {
+ dev_err(dev, "Error: %s\n", msg);
return -EINVAL;
- } else if (!opts->c_srate) {
- dev_err(dev, "Error: incorrect capture sampling rate\n");
- return -EINVAL;
- }
-
- if (opts->p_volume_max <= opts->p_volume_min) {
- dev_err(dev, "Error: incorrect playback volume max/min\n");
- return -EINVAL;
- } else if (opts->c_volume_max <= opts->c_volume_min) {
- dev_err(dev, "Error: incorrect capture volume max/min\n");
- return -EINVAL;
- } else if (opts->p_volume_res <= 0) {
- dev_err(dev, "Error: negative/zero playback volume resolution\n");
- return -EINVAL;
- } else if (opts->c_volume_res <= 0) {
- dev_err(dev, "Error: negative/zero capture volume resolution\n");
- return -EINVAL;
- }
-
- if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) {
- dev_err(dev, "Error: incorrect playback volume resolution\n");
- return -EINVAL;
- } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) {
- dev_err(dev, "Error: incorrect capture volume resolution\n");
- return -EINVAL;
}
return 0;
@@ -961,6 +1033,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
if (ret)
return ret;
+ strings_fn[STR_ASSOC].s = uac2_opts->function_name;
+
us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));
if (IS_ERR(us))
return PTR_ERR(us);
@@ -1037,9 +1111,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
*bma = cpu_to_le32(control);
}
- snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate);
- snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate);
-
ret = usb_interface_id(cfg, fn);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
@@ -1103,44 +1174,49 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
std_ac_if_desc.bNumEndpoints = 1;
}
+ hs_epin_desc.bInterval = uac2_opts->p_hs_bint;
+ ss_epin_desc.bInterval = uac2_opts->p_hs_bint;
+ hs_epout_desc.bInterval = uac2_opts->c_hs_bint;
+ ss_epout_desc.bInterval = uac2_opts->c_hs_bint;
+
/* Calculate wMaxPacketSize according to audio bandwidth */
- ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epin_desc,
+ USB_SPEED_FULL, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &fs_epout_desc,
+ USB_SPEED_FULL, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epin_desc,
+ USB_SPEED_HIGH, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &hs_epout_desc,
+ USB_SPEED_HIGH, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &ss_epin_desc, USB_SPEED_SUPER,
- true);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epin_desc,
+ USB_SPEED_SUPER, true);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
}
- ret = set_ep_max_packet_size(uac2_opts, &ss_epout_desc, USB_SPEED_SUPER,
- false);
+ ret = set_ep_max_packet_size_bint(dev, uac2_opts, &ss_epout_desc,
+ USB_SPEED_SUPER, false);
if (ret < 0) {
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
return ret;
@@ -1209,7 +1285,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->gadget = gadget;
agdev->params.p_chmask = uac2_opts->p_chmask;
- agdev->params.p_srate = uac2_opts->p_srate;
+ memcpy(agdev->params.p_srates, uac2_opts->p_srates,
+ sizeof(agdev->params.p_srates));
agdev->params.p_ssize = uac2_opts->p_ssize;
if (FUIN_EN(uac2_opts)) {
agdev->params.p_fu.id = USB_IN_FU_ID;
@@ -1220,7 +1297,8 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
agdev->params.p_fu.volume_res = uac2_opts->p_volume_res;
}
agdev->params.c_chmask = uac2_opts->c_chmask;
- agdev->params.c_srate = uac2_opts->c_srate;
+ memcpy(agdev->params.c_srates, uac2_opts->c_srates,
+ sizeof(agdev->params.c_srates));
agdev->params.c_ssize = uac2_opts->c_ssize;
if (FUOUT_EN(uac2_opts)) {
agdev->params.c_fu.id = USB_OUT_FU_ID;
@@ -1411,6 +1489,14 @@ afunc_disable(struct usb_function *fn)
usb_ep_disable(uac2->int_ep);
}
+static void
+afunc_suspend(struct usb_function *fn)
+{
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+
+ u_audio_suspend(&uac2->g_audio);
+}
+
static int
in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
@@ -1423,10 +1509,10 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
+ u32 p_srate, c_srate;
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
+ u_audio_get_playback_srate(agdev, &p_srate);
+ u_audio_get_capture_srate(agdev, &c_srate);
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
@@ -1500,28 +1586,39 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- int p_srate, c_srate;
-
- p_srate = opts->p_srate;
- c_srate = opts->c_srate;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
- struct cntrl_range_lay3 r;
+ struct cntrl_ranges_lay3_srates rs;
+ int i;
+ int wNumSubRanges = 0;
+ int srate;
+ int *srates;
if (entity_id == USB_IN_CLK_ID)
- r.dMIN = cpu_to_le32(p_srate);
+ srates = opts->p_srates;
else if (entity_id == USB_OUT_CLK_ID)
- r.dMIN = cpu_to_le32(c_srate);
+ srates = opts->c_srates;
else
return -EOPNOTSUPP;
-
- r.dMAX = r.dMIN;
- r.dRES = 0;
- r.wNumSubRanges = cpu_to_le16(1);
-
- value = min_t(unsigned int, w_length, sizeof(r));
- memcpy(req->buf, &r, value);
+ for (i = 0; i < UAC_MAX_RATES; i++) {
+ srate = srates[i];
+ if (srate == 0)
+ break;
+
+ rs.r[wNumSubRanges].dMIN = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dMAX = cpu_to_le32(srate);
+ rs.r[wNumSubRanges].dRES = 0;
+ wNumSubRanges++;
+ dev_dbg(&agdev->gadget->dev,
+ "%s(): clk %d: rate ID %d: %d\n",
+ __func__, entity_id, wNumSubRanges, srate);
+ }
+ rs.wNumSubRanges = cpu_to_le16(wNumSubRanges);
+ value = min_t(unsigned int, w_length, ranges_lay3_size(rs));
+ dev_dbg(&agdev->gadget->dev, "%s(): sending %d rates, size %d\n",
+ __func__, rs.wNumSubRanges, value);
+ memcpy(req->buf, &rs, value);
} else {
dev_err(&agdev->gadget->dev,
"%s:%d control_selector=%d TODO!\n",
@@ -1580,6 +1677,25 @@ ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr)
return -EOPNOTSUPP;
}
+static void uac2_cs_control_sam_freq(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usb_function *fn = ep->driver_data;
+ struct g_audio *agdev = func_to_g_audio(fn);
+ struct f_uac2 *uac2 = func_to_uac2(fn);
+ u32 val;
+
+ if (req->actual != 4)
+ return;
+
+ val = le32_to_cpu(*((__le32 *)req->buf));
+ dev_dbg(&agdev->gadget->dev, "%s val: %d.\n", __func__, val);
+ if (uac2->clock_id == USB_IN_CLK_ID) {
+ u_audio_set_playback_srate(agdev, val);
+ } else if (uac2->clock_id == USB_OUT_CLK_ID) {
+ u_audio_set_capture_srate(agdev, val);
+ }
+}
+
static void
out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
{
@@ -1631,6 +1747,7 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
static int
out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
{
+ struct usb_composite_dev *cdev = fn->config->cdev;
struct usb_request *req = fn->config->cdev->req;
struct g_audio *agdev = func_to_g_audio(fn);
struct f_uac2_opts *opts = g_audio_to_uac2_opts(agdev);
@@ -1640,10 +1757,17 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u16 w_value = le16_to_cpu(cr->wValue);
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
+ u8 clock_id = w_index >> 8;
if ((entity_id == USB_IN_CLK_ID) || (entity_id == USB_OUT_CLK_ID)) {
- if (control_selector == UAC2_CS_CONTROL_SAM_FREQ)
+ if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) {
+ dev_dbg(&agdev->gadget->dev,
+ "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", clock_id);
+ cdev->gadget->ep0->driver_data = fn;
+ uac2->clock_id = clock_id;
+ req->complete = uac2_cs_control_sam_freq;
return w_length;
+ }
} else if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
(FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
memcpy(&uac2->setup_cr, cr, sizeof(*cr));
@@ -1731,10 +1855,12 @@ static struct configfs_item_operations f_uac2_item_ops = {
.release = f_uac2_attr_release,
};
+#define uac2_kstrtou8 kstrtou8
#define uac2_kstrtou32 kstrtou32
#define uac2_kstrtos16 kstrtos16
#define uac2_kstrtobool(s, base, res) kstrtobool((s), (res))
+static const char *u8_fmt = "%u\n";
static const char *u32_fmt = "%u\n";
static const char *s16_fmt = "%hd\n";
static const char *bool_fmt = "%u\n";
@@ -1836,13 +1962,110 @@ end: \
\
CONFIGFS_ATTR(f_uac2_opts_, name)
+#define UAC2_RATE_ATTRIBUTE(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result = 0; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ page[0] = '\0'; \
+ for (i = 0; i < UAC_MAX_RATES; i++) { \
+ if (opts->name##s[i] == 0) \
+ break; \
+ result += sprintf(page + strlen(page), "%u,", \
+ opts->name##s[i]); \
+ } \
+ if (strlen(page) > 0) \
+ page[strlen(page) - 1] = '\n'; \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ char *split_page = NULL; \
+ int ret = -EINVAL; \
+ char *token; \
+ u32 num; \
+ int i; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ i = 0; \
+ memset(opts->name##s, 0x00, sizeof(opts->name##s)); \
+ split_page = kstrdup(page, GFP_KERNEL); \
+ while ((token = strsep(&split_page, ",")) != NULL) { \
+ ret = kstrtou32(token, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ opts->name##s[i++] = num; \
+ ret = len; \
+ }; \
+ \
+end: \
+ kfree(split_page); \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
+#define UAC2_ATTRIBUTE_STRING(name) \
+static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = snprintf(page, sizeof(opts->name), "%s", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
+ const char *page, size_t len) \
+{ \
+ struct f_uac2_opts *opts = to_f_uac2_opts(item); \
+ int ret = 0; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = snprintf(opts->name, min(sizeof(opts->name), len), \
+ "%s", page); \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+CONFIGFS_ATTR(f_uac2_opts_, name)
+
UAC2_ATTRIBUTE(u32, p_chmask);
-UAC2_ATTRIBUTE(u32, p_srate);
+UAC2_RATE_ATTRIBUTE(p_srate);
UAC2_ATTRIBUTE(u32, p_ssize);
+UAC2_ATTRIBUTE(u8, p_hs_bint);
UAC2_ATTRIBUTE(u32, c_chmask);
-UAC2_ATTRIBUTE(u32, c_srate);
+UAC2_RATE_ATTRIBUTE(c_srate);
UAC2_ATTRIBUTE_SYNC(c_sync);
UAC2_ATTRIBUTE(u32, c_ssize);
+UAC2_ATTRIBUTE(u8, c_hs_bint);
UAC2_ATTRIBUTE(u32, req_number);
UAC2_ATTRIBUTE(bool, p_mute_present);
@@ -1857,14 +2080,17 @@ UAC2_ATTRIBUTE(s16, c_volume_min);
UAC2_ATTRIBUTE(s16, c_volume_max);
UAC2_ATTRIBUTE(s16, c_volume_res);
UAC2_ATTRIBUTE(u32, fb_max);
+UAC2_ATTRIBUTE_STRING(function_name);
static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_p_chmask,
&f_uac2_opts_attr_p_srate,
&f_uac2_opts_attr_p_ssize,
+ &f_uac2_opts_attr_p_hs_bint,
&f_uac2_opts_attr_c_chmask,
&f_uac2_opts_attr_c_srate,
&f_uac2_opts_attr_c_ssize,
+ &f_uac2_opts_attr_c_hs_bint,
&f_uac2_opts_attr_c_sync,
&f_uac2_opts_attr_req_number,
&f_uac2_opts_attr_fb_max,
@@ -1881,6 +2107,8 @@ static struct configfs_attribute *f_uac2_attrs[] = {
&f_uac2_opts_attr_c_volume_max,
&f_uac2_opts_attr_c_volume_res,
+ &f_uac2_opts_attr_function_name,
+
NULL,
};
@@ -1913,11 +2141,13 @@ static struct usb_function_instance *afunc_alloc_inst(void)
&f_uac2_func_type);
opts->p_chmask = UAC2_DEF_PCHMASK;
- opts->p_srate = UAC2_DEF_PSRATE;
+ opts->p_srates[0] = UAC2_DEF_PSRATE;
opts->p_ssize = UAC2_DEF_PSSIZE;
+ opts->p_hs_bint = UAC2_DEF_PHSBINT;
opts->c_chmask = UAC2_DEF_CCHMASK;
- opts->c_srate = UAC2_DEF_CSRATE;
+ opts->c_srates[0] = UAC2_DEF_CSRATE;
opts->c_ssize = UAC2_DEF_CSSIZE;
+ opts->c_hs_bint = UAC2_DEF_CHSBINT;
opts->c_sync = UAC2_DEF_CSYNC;
opts->p_mute_present = UAC2_DEF_MUTE_PRESENT;
@@ -1934,6 +2164,9 @@ static struct usb_function_instance *afunc_alloc_inst(void)
opts->req_number = UAC2_DEF_REQ_NUM;
opts->fb_max = FBACK_FAST_MAX;
+
+ snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink");
+
return &opts->func_inst;
}
@@ -1985,6 +2218,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
uac2->g_audio.func.set_alt = afunc_set_alt;
uac2->g_audio.func.get_alt = afunc_get_alt;
uac2->g_audio.func.disable = afunc_disable;
+ uac2->g_audio.func.suspend = afunc_suspend;
uac2->g_audio.func.setup = afunc_setup;
uac2->g_audio.func.free_func = afunc_free;
diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c
index 4561d7a183ff..2bb569895a90 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -32,6 +32,7 @@ enum {
UAC_P_PITCH_CTRL,
UAC_MUTE_CTRL,
UAC_VOLUME_CTRL,
+ UAC_RATE_CTRL,
};
/* Runtime data params for one stream */
@@ -62,6 +63,10 @@ struct uac_rtd_params {
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 */
};
@@ -150,8 +155,6 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
struct snd_pcm_runtime *runtime;
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
- struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
unsigned int frames, p_pktsize;
unsigned long long pitched_rate_mil, p_pktsize_residue_mil,
residue_frames_mil, div_result;
@@ -196,15 +199,14 @@ static void u_audio_iso_complete(struct usb_ep *ep, struct usb_request *req)
*/
unsigned long long p_interval_mil = uac->p_interval * 1000000ULL;
- pitched_rate_mil = (unsigned long long)
- params->p_srate * prm->pitch;
+ 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",
- params->p_srate, prm->pitch, p_interval_mil, frames);
+ prm->srate, prm->pitch, p_interval_mil, frames);
p_pktsize = min_t(unsigned int,
uac->p_framesize * frames,
@@ -281,7 +283,6 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
struct uac_rtd_params *prm = req->context;
struct snd_uac_chip *uac = prm->uac;
struct g_audio *audio_dev = uac->audio_dev;
- struct uac_params *params = &audio_dev->params;
int status = req->status;
/* i/f shutting down */
@@ -303,7 +304,7 @@ static void u_audio_iso_fback_complete(struct usb_ep *ep,
__func__, status, req->actual, req->length);
u_audio_set_fback_frequency(audio_dev->gadget->speed, audio_dev->out_ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req->buf);
if (usb_ep_queue(ep, req, GFP_ATOMIC))
@@ -387,16 +388,14 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
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;
- c_srate = params->c_srate;
p_chmask = params->p_chmask;
c_chmask = params->c_chmask;
uac->p_residue_mil = 0;
@@ -404,19 +403,18 @@ static int uac_pcm_open(struct snd_pcm_substream *substream)
runtime->hw = uac_pcm_hardware;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- runtime->hw.rate_min = p_srate;
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 {
- runtime->hw.rate_min = c_srate;
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;
@@ -493,6 +491,99 @@ static inline void free_ep_fback(struct uac_rtd_params *prm, struct usb_ep *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 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_srates[i] == srate) {
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+ }
+ if (params->c_srates[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(u_audio_set_capture_srate);
+
+int u_audio_get_capture_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->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 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_srates[i] == srate) {
+ spin_lock_irqsave(&prm->lock, flags);
+ prm->srate = srate;
+ spin_unlock_irqrestore(&prm->lock, flags);
+ return 0;
+ }
+ if (params->p_srates[i] == 0)
+ break;
+ }
+
+ return -EINVAL;
+}
+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;
@@ -504,8 +595,9 @@ int u_audio_start_capture(struct g_audio *audio_dev)
struct uac_params *params = &audio_dev->params;
int req_len, i;
- 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 = ep->maxpacket;
@@ -531,6 +623,8 @@ int u_audio_start_capture(struct g_audio *audio_dev)
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;
@@ -562,7 +656,7 @@ int u_audio_start_capture(struct g_audio *audio_dev)
*/
prm->pitch = 1000000;
u_audio_set_fback_frequency(audio_dev->gadget->speed, ep,
- params->c_srate, prm->pitch,
+ prm->srate, prm->pitch,
req_fback->buf);
if (usb_ep_queue(ep_fback, req_fback, GFP_ATOMIC))
@@ -576,6 +670,7 @@ void u_audio_stop_capture(struct g_audio *audio_dev)
{
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);
@@ -596,8 +691,9 @@ int u_audio_start_playback(struct g_audio *audio_dev)
int req_len, i;
unsigned int p_pktsize;
- 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;
@@ -618,7 +714,7 @@ int u_audio_start_playback(struct g_audio *audio_dev)
uac->p_interval = factor / (1 << (ep_desc->bInterval - 1));
p_pktsize = min_t(unsigned int,
uac->p_framesize *
- (params->p_srate / uac->p_interval),
+ (prm->srate / uac->p_interval),
ep->maxpacket);
req_len = p_pktsize;
@@ -646,6 +742,8 @@ int u_audio_start_playback(struct g_audio *audio_dev)
dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
}
+ set_active(&uac->p_prm, true);
+
return 0;
}
EXPORT_SYMBOL_GPL(u_audio_start_playback);
@@ -654,10 +752,20 @@ void u_audio_stop_playback(struct g_audio *audio_dev)
{
struct snd_uac_chip *uac = audio_dev->uac;
+ set_active(&uac->p_prm, false);
free_ep(&uac->p_prm, audio_dev->in_ep);
}
EXPORT_SYMBOL_GPL(u_audio_stop_playback);
+void u_audio_suspend(struct g_audio *audio_dev)
+{
+ struct snd_uac_chip *uac = audio_dev->uac;
+
+ set_active(&uac->p_prm, false);
+ set_active(&uac->c_prm, false);
+}
+EXPORT_SYMBOL_GPL(u_audio_suspend);
+
int u_audio_get_volume(struct g_audio *audio_dev, int playback, s16 *val)
{
struct snd_uac_chip *uac = audio_dev->uac;
@@ -943,6 +1051,68 @@ static int u_audio_volume_put(struct snd_kcontrol *kcontrol,
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] {
@@ -973,6 +1143,13 @@ static struct snd_kcontrol_new u_audio_controls[] = {
.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,
+ },
};
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
@@ -1005,6 +1182,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
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->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@@ -1029,6 +1207,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
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->reqs = kcalloc(params->req_number,
sizeof(struct usb_request *),
@@ -1186,6 +1365,25 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
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));
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 8dfdae1721cd..9512b8fccfaa 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -10,6 +10,7 @@
#define __U_AUDIO_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
/*
* Same maximum frequency deviation on the slower side as in
@@ -40,16 +41,18 @@ struct uac_fu_params {
struct uac_params {
/* playback */
int p_chmask; /* channel mask */
- int p_srate; /* rate in Hz */
+ int p_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int p_ssize; /* sample size */
struct uac_fu_params p_fu; /* Feature Unit parameters */
/* capture */
int c_chmask; /* channel mask */
- int c_srate; /* rate in Hz */
+ int c_srates[UAC_MAX_RATES]; /* available rates in Hz (0 terminated list) */
int c_ssize; /* sample size */
struct uac_fu_params c_fu; /* Feature Unit parameters */
+ /* rates are dynamic, in uac_rtd_params */
+
int req_number; /* number of preallocated requests */
int fb_max; /* upper frequency drift feedback limit per-mil */
};
@@ -117,9 +120,16 @@ void u_audio_stop_capture(struct g_audio *g_audio);
int u_audio_start_playback(struct g_audio *g_audio);
void u_audio_stop_playback(struct g_audio *g_audio);
+int u_audio_get_capture_srate(struct g_audio *audio_dev, u32 *val);
+int u_audio_set_capture_srate(struct g_audio *audio_dev, int srate);
+int u_audio_get_playback_srate(struct g_audio *audio_dev, u32 *val);
+int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate);
+
int u_audio_get_volume(struct g_audio *g_audio, int playback, s16 *val);
int u_audio_set_volume(struct g_audio *g_audio, int playback, s16 val);
int u_audio_get_mute(struct g_audio *g_audio, int playback, int *val);
int u_audio_set_mute(struct g_audio *g_audio, int playback, int val);
+void u_audio_suspend(struct g_audio *g_audio);
+
#endif /* __U_AUDIO_H */
diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 589fae861141..f7a616760e31 100644
--- a/drivers/usb/gadget/function/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
@@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_DEF_CCHMASK 0x3
@@ -30,10 +31,10 @@
struct f_uac1_opts {
struct usb_function_instance func_inst;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
bool p_mute_present;
@@ -51,6 +52,8 @@ struct f_uac1_opts {
int req_number;
unsigned bound:1;
+ char function_name[32];
+
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index e0c8e3513bfd..0510c9bad58d 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -14,13 +14,16 @@
#define U_UAC2_H
#include <linux/usb/composite.h>
+#include "uac_common.h"
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
#define UAC2_DEF_PSSIZE 2
+#define UAC2_DEF_PHSBINT 0
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_CHSBINT 0
#define UAC2_DEF_CSYNC USB_ENDPOINT_SYNC_ASYNC
#define UAC2_DEF_MUTE_PRESENT 1
@@ -35,12 +38,14 @@
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
- int p_srate;
+ int p_srates[UAC_MAX_RATES];
int p_ssize;
+ u8 p_hs_bint;
int c_chmask;
- int c_srate;
+ int c_srates[UAC_MAX_RATES];
int c_ssize;
int c_sync;
+ u8 c_hs_bint;
bool p_mute_present;
bool p_volume_present;
@@ -58,6 +63,8 @@ struct f_uac2_opts {
int fb_max;
bool bound;
+ char function_name[32];
+
struct mutex lock;
int refcnt;
};
diff --git a/drivers/usb/gadget/function/uac_common.h b/drivers/usb/gadget/function/uac_common.h
new file mode 100644
index 000000000000..3ecf89d6e814
--- /dev/null
+++ b/drivers/usb/gadget/function/uac_common.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ */
+
+#ifndef UAC_COMMON_H
+#define UAC_COMMON_H
+
+#define UAC_MAX_RATES 10 /* maximum number of rates configurable by f_uac1/2 */
+#endif