diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 3af8a1cd172e..7b586f57af3f 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 + * Copyright (C) 2017 Julian Scheel * * This driver doesn't expect any real Audio codec to be present * on the device - the audio streams are simply sinked to and @@ -182,16 +183,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 */ @@ -215,14 +218,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 */ @@ -321,23 +324,57 @@ 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_uac *uac1 = func_to_uac(fn); + struct f_uac_opts *opts = g_audio_to_uac_opts(agdev); + 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 == agdev->in_ep->address) { + opts->p_srate_active = val; + u_audio_set_playback_srate(agdev, opts->p_srate_active); + } else if (uac1->ctl_id == agdev->out_ep->address) { + opts->c_srate_active = val; + u_audio_set_capture_srate(agdev, opts->c_srate_active); + } +} + 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_uac *uac1 = func_to_uac(f); int value = -EOPNOTSUPP; u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff; 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; @@ -361,16 +398,34 @@ 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 g_audio *agdev = func_to_g_audio(f); + struct f_uac_opts *opts = g_audio_to_uac_opts(agdev); + u8 *buf = (u8 *)req->buf; int value = -EOPNOTSUPP; u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff; 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 == agdev->in_ep->address) + val = opts->p_srate_active; + else if (ep == agdev->out_ep->address) + val = opts->c_srate_active; + 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: @@ -518,9 +573,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) struct f_uac_opts *audio_opts; struct usb_ep *ep = NULL; struct usb_string *us; - u8 *sam_freq; - int rate; int status; + int idx, i; audio_opts = container_of(f->fi, struct f_uac_opts, func_inst); @@ -554,12 +608,23 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; /* 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_srate[i] == 0) + break; + memcpy(as_out_type_i_desc.tSamFreq[idx++], + &audio_opts->c_srate[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_srate[i] == 0) + break; + memcpy(as_in_type_i_desc.tSamFreq[idx++], + &audio_opts->p_srate[i], 3); + } + as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); + as_in_type_i_desc.bSamFreqType = idx; /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); @@ -614,10 +679,14 @@ 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_srate, audio_opts->c_srate, + sizeof(audio->params.c_srate)); + audio->params.c_srate_active = audio_opts->c_srate_active; audio->params.c_ssize = audio_opts->c_ssize; audio->params.p_chmask = audio_opts->p_chmask; - audio->params.p_srate = audio_opts->p_srate; + memcpy(audio->params.p_srate, audio_opts->p_srate, + sizeof(audio->params.p_srate)); + audio->params.p_srate_active = audio_opts->p_srate_active; audio->params.p_ssize = audio_opts->p_ssize; audio->params.req_number = audio_opts->req_number; @@ -640,13 +709,14 @@ static struct configfs_item_operations f_uac1_item_ops = { }; UAC_ATTRIBUTE(c_chmask); -UAC_ATTRIBUTE(c_srate); UAC_ATTRIBUTE(c_ssize); UAC_ATTRIBUTE(p_chmask); -UAC_ATTRIBUTE(p_srate); UAC_ATTRIBUTE(p_ssize); UAC_ATTRIBUTE(req_number); +UAC_RATE_ATTRIBUTE(p_srate); +UAC_RATE_ATTRIBUTE(c_srate); + static struct configfs_attribute *f_uac1_attrs[] = { &f_uac_opts_attr_c_chmask, &f_uac_opts_attr_c_srate, @@ -687,10 +757,12 @@ static struct usb_function_instance *f_audio_alloc_inst(void) &f_uac1_func_type); opts->c_chmask = UAC_DEF_CCHMASK; - opts->c_srate = UAC_DEF_CSRATE; + opts->c_srate[0] = UAC_DEF_CSRATE; + opts->c_srate_active = UAC_DEF_CSRATE; opts->c_ssize = UAC_DEF_CSSIZE; opts->p_chmask = UAC_DEF_PCHMASK; - opts->p_srate = UAC_DEF_PSRATE; + opts->p_srate[0] = UAC_DEF_PSRATE; + opts->p_srate_active = UAC_DEF_PSRATE; opts->p_ssize = UAC_DEF_PSSIZE; opts->req_number = UAC_DEF_REQ_NUM; return &opts->func_inst; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9a2df37fc87d..b64985c6453d 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -5,6 +5,7 @@ * Copyright (C) 2011 * Yadwinder Singh (yadi.brar01@gmail.com) * Jaswinder Singh (jaswinder.singh@linaro.org) + * Copyright (C) 2017 Julian Scheel */ #include @@ -59,14 +60,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_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", @@ -119,7 +117,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, }; @@ -131,7 +129,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, }; @@ -424,19 +422,26 @@ struct cntrl_cur_lay3 { }; struct cntrl_range_lay3 { - __le16 wNumSubRanges; __le32 dMIN; __le32 dMAX; __le32 dRES; } __packed; +#define ranges_size(c) (sizeof(c.wNumSubRanges) + c.wNumSubRanges \ + * sizeof(struct cntrl_ranges_lay3)) +struct cntrl_ranges_lay3 { + __u16 wNumSubRanges; + struct cntrl_range_lay3 r[UAC_MAX_RATES]; +} __packed; + static int set_ep_max_packet_size(const struct f_uac_opts *uac2_opts, struct usb_endpoint_descriptor *ep_desc, enum usb_device_speed speed, bool is_playback) { - int chmask, srate, ssize; + int chmask, srate = 0, ssize; u16 max_size_bw, max_size_ep; unsigned int factor; + int i; switch (speed) { case USB_SPEED_FULL: @@ -455,11 +460,21 @@ static int set_ep_max_packet_size(const struct f_uac_opts *uac2_opts, if (is_playback) { chmask = uac2_opts->p_chmask; - srate = uac2_opts->p_srate; + for (i = 0; i < UAC_MAX_RATES; i++) { + if (uac2_opts->p_srate[i] == 0) + break; + if (uac2_opts->p_srate[i] > srate) + srate = uac2_opts->p_srate[i]; + } ssize = uac2_opts->p_ssize; } else { chmask = uac2_opts->c_chmask; - srate = uac2_opts->c_srate; + for (i = 0; i < UAC_MAX_RATES; i++) { + if (uac2_opts->c_srate[i] == 0) + break; + if (uac2_opts->c_srate[i] > srate) + srate = uac2_opts->c_srate[i]; + } ssize = uac2_opts->c_ssize; } @@ -635,9 +650,6 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; - 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__); @@ -738,10 +750,14 @@ 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_srate, uac2_opts->p_srate, + sizeof(agdev->params.p_srate)); + agdev->params.p_srate_active = uac2_opts->p_srate_active; agdev->params.p_ssize = uac2_opts->p_ssize; agdev->params.c_chmask = uac2_opts->c_chmask; - agdev->params.c_srate = uac2_opts->c_srate; + memcpy(agdev->params.c_srate, uac2_opts->c_srate, + sizeof(agdev->params.c_srate)); + agdev->params.c_srate_active = uac2_opts->c_srate_active; agdev->params.c_ssize = uac2_opts->c_ssize; agdev->params.req_number = uac2_opts->req_number; ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget"); @@ -847,8 +863,8 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) int p_srate, c_srate; opts = g_audio_to_uac_opts(agdev); - p_srate = opts->p_srate; - c_srate = opts->c_srate; + p_srate = opts->p_srate_active; + c_srate = opts->c_srate_active; if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { struct cntrl_cur_lay3 c; @@ -859,6 +875,7 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) else if (entity_id == USB_OUT_CLK_ID) c.dCUR = cpu_to_le32(c_srate); + DBG(fn->config->cdev, "%s(): %d\n", __func__, c.dCUR); value = min_t(unsigned, w_length, sizeof c); memcpy(req->buf, &c, value); } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { @@ -884,28 +901,40 @@ in_rq_range(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; - struct cntrl_range_lay3 r; + struct cntrl_ranges_lay3 rs; int value = -EOPNOTSUPP; - int p_srate, c_srate; + int srate = 0; + int i; opts = g_audio_to_uac_opts(agdev); - p_srate = opts->p_srate; - c_srate = opts->c_srate; if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { - if (entity_id == USB_IN_CLK_ID) - r.dMIN = cpu_to_le32(p_srate); - else if (entity_id == USB_OUT_CLK_ID) - r.dMIN = cpu_to_le32(c_srate); - else - return -EOPNOTSUPP; + rs.wNumSubRanges = 0; + for (i = 0; i < UAC_MAX_RATES; i++) { + if (entity_id == USB_IN_CLK_ID) + srate = opts->p_srate[i]; + else if (entity_id == USB_OUT_CLK_ID) + srate = opts->c_srate[i]; + else + return -EOPNOTSUPP; - r.dMAX = r.dMIN; - r.dRES = 0; - r.wNumSubRanges = cpu_to_le16(1); + if (srate == 0) + break; - value = min_t(unsigned, w_length, sizeof r); - memcpy(req->buf, &r, value); + rs.r[rs.wNumSubRanges].dMIN = srate; + rs.r[rs.wNumSubRanges].dMAX = srate; + rs.r[rs.wNumSubRanges].dRES = 0; + rs.wNumSubRanges++; + DBG(fn->config->cdev, + "%s(): clk %d: report rate %d. %d\n", + __func__, entity_id, rs.wNumSubRanges, + srate); + } + + value = min_t(unsigned int, w_length, ranges_size(rs)); + DBG(fn->config->cdev, "%s(): send %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", @@ -918,6 +947,7 @@ in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) static int ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) { + DBG(fn->config->cdev, "%s(): %d\n", __func__, cr->bRequest); if (cr->bRequest == UAC2_CS_CUR) return in_rq_cur(fn, cr); else if (cr->bRequest == UAC2_CS_RANGE) @@ -926,15 +956,50 @@ 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 usb_composite_dev *cdev = fn->config->cdev; + struct g_audio *agdev = func_to_g_audio(fn); + struct f_uac *uac2 = func_to_uac(fn); + struct f_uac_opts *opts = g_audio_to_uac_opts(agdev); + u32 val; + + if (req->actual != 4) { + WARN(cdev, "Invalid data size for UAC2_CS_CONTROL_SAM_FREQ.\n"); + return; + } + + val = le32_to_cpu(*((u32 *)req->buf)); + if (uac2->ctl_id == USB_IN_CLK_ID) { + opts->p_srate_active = val; + u_audio_set_playback_srate(agdev, opts->p_srate_active); + } else if (uac2->ctl_id == USB_OUT_CLK_ID) { + opts->c_srate_active = val; + u_audio_set_capture_srate(agdev, opts->c_srate_active); + } +} + 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 = cdev->req; u16 w_length = le16_to_cpu(cr->wLength); + struct f_uac *uac2 = func_to_uac(fn); u16 w_value = le16_to_cpu(cr->wValue); + u16 w_index = le16_to_cpu(cr->wIndex); u8 control_selector = w_value >> 8; + u8 clock_id = w_index >> 8; - if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) + if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { + DBG(cdev, "control_selector UAC2_CS_CONTROL_SAM_FREQ, clock: %d\n", + clock_id); + cdev->gadget->ep0->driver_data = fn; + uac2->ctl_id = clock_id; + req->complete = uac2_cs_control_sam_freq; return w_length; + } return -EOPNOTSUPP; } @@ -999,13 +1064,14 @@ static struct configfs_item_operations f_uac2_item_ops = { }; UAC_ATTRIBUTE(p_chmask); -UAC_ATTRIBUTE(p_srate); UAC_ATTRIBUTE(p_ssize); UAC_ATTRIBUTE(c_chmask); -UAC_ATTRIBUTE(c_srate); UAC_ATTRIBUTE(c_ssize); UAC_ATTRIBUTE(req_number); +UAC_RATE_ATTRIBUTE(p_srate); +UAC_RATE_ATTRIBUTE(c_srate); + static struct configfs_attribute *f_uac2_attrs[] = { &f_uac_opts_attr_p_chmask, &f_uac_opts_attr_p_srate, @@ -1046,10 +1112,12 @@ static struct usb_function_instance *afunc_alloc_inst(void) &f_uac2_func_type); opts->p_chmask = UAC_DEF_PCHMASK; - opts->p_srate = UAC_DEF_PSRATE; + opts->p_srate[0] = UAC_DEF_PSRATE; + opts->p_srate_active = UAC_DEF_PSRATE; opts->p_ssize = UAC_DEF_PSSIZE; opts->c_chmask = UAC_DEF_CCHMASK; - opts->c_srate = UAC_DEF_CSRATE; + opts->c_srate[0] = UAC_DEF_CSRATE; + opts->c_srate_active = UAC_DEF_CSRATE; opts->c_ssize = UAC_DEF_CSSIZE; opts->req_number = UAC_DEF_REQ_NUM; return &opts->func_inst; diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 828452c74684..eeffbbf9f1b3 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -248,18 +249,17 @@ 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; + struct g_audio *audio_dev = uac->audio_dev; struct uac_params *params; 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_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; @@ -288,6 +288,52 @@ static int uac_pcm_open(struct snd_pcm_substream *substream) 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) { @@ -335,6 +381,59 @@ static inline void free_ep(struct uac_rtd_params *prm, struct usb_ep *ep) 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) +{ + struct snd_ctl_elem_id elem_id; + + 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); +} + +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; + int i; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (params->c_srate[i] == srate) { + params->c_srate_active = srate; + snd_ctl_notify(audio_dev->uac->card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + return 0; + } + if (params->c_srate[i] == 0) + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(u_audio_set_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; + int i; + + for (i = 0; i < UAC_MAX_RATES; i++) { + if (params->p_srate[i] == srate) { + params->p_srate_active = srate; + snd_ctl_notify(audio_dev->uac->card, + SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); + return 0; + } + if (params->p_srate[i] == 0) + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(u_audio_set_playback_srate); + int u_audio_start_capture(struct g_audio *audio_dev) { struct snd_uac_chip *uac = audio_dev->uac; @@ -394,10 +493,11 @@ int u_audio_start_playback(struct g_audio *audio_dev) struct usb_ep *ep; struct uac_rtd_params *prm; struct uac_params *params = &audio_dev->params; - unsigned int factor; + unsigned int factor, rate; const struct usb_endpoint_descriptor *ep_desc; int req_len, i; + dev_dbg(dev, "start playback with rate %d\n", params->p_srate_active); ep = audio_dev->in_ep; prm = &uac->p_prm; config_ep_by_speed(gadget, &audio_dev->func, ep); @@ -413,15 +513,13 @@ int u_audio_start_playback(struct g_audio *audio_dev) /* pre-compute some values for iso_complete() */ uac->p_framesize = params->p_ssize * num_channels(params->p_chmask); + rate = params->p_srate_active * uac->p_framesize; uac->p_interval = factor / (1 << (ep_desc->bInterval - 1)); - uac->p_pktsize = min_t(unsigned int, - uac->p_framesize * - (params->p_srate / uac->p_interval), + uac->p_pktsize = min_t(unsigned int, rate / uac->p_interval, ep->maxpacket); if (uac->p_pktsize < ep->maxpacket) - uac->p_pktsize_residue = uac->p_framesize * - (params->p_srate % uac->p_interval); + uac->p_pktsize_residue = rate % uac->p_interval; else uac->p_pktsize_residue = 0; @@ -471,6 +569,7 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, struct uac_params *params; int p_chmask, c_chmask; int err; + int i; if (!g_audio) return -EINVAL; @@ -562,6 +661,14 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name, snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, NULL, 0, BUFF_SIZE_MAX); + /* 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)); + if (err < 0) + goto snd_fail; + } + err = snd_card_register(card); if (!err) diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index 5ea6b86f1fda..cd31dd292e50 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -11,15 +11,18 @@ #include +#define UAC_MAX_RATES 10 struct uac_params { /* playback */ int p_chmask; /* channel mask */ - int p_srate; /* rate in Hz */ + int p_srate[UAC_MAX_RATES]; /* rate in Hz */ + int p_srate_active; /* selected rate in Hz */ int p_ssize; /* sample size */ /* capture */ int c_chmask; /* channel mask */ - int c_srate; /* rate in Hz */ + int c_srate[UAC_MAX_RATES]; /* rate in Hz */ + int c_srate_active; /* selected rate in Hz */ int c_ssize; /* sample size */ int req_number; /* number of preallocated requests */ @@ -81,5 +84,7 @@ int u_audio_start_capture(struct g_audio *g_audio); 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_set_capture_srate(struct g_audio *audio_dev, int srate); +int u_audio_set_playback_srate(struct g_audio *audio_dev, int srate); #endif /* __U_AUDIO_H */ diff --git a/drivers/usb/gadget/function/u_uac.h b/drivers/usb/gadget/function/u_uac.h index c473c8e58c26..ae64a3b373cf 100644 --- a/drivers/usb/gadget/function/u_uac.h +++ b/drivers/usb/gadget/function/u_uac.h @@ -13,6 +13,7 @@ #define __U_UAC_H #include +#include "u_audio.h" #define UAC_DEF_CCHMASK 0x3 #define UAC_DEF_CSRATE 48000 @@ -27,10 +28,12 @@ struct f_uac_opts { struct usb_function_instance func_inst; int c_chmask; - int c_srate; + int c_srate[UAC_MAX_RATES]; + int c_srate_active; int c_ssize; int p_chmask; - int p_srate; + int p_srate[UAC_MAX_RATES]; + int p_srate_active; int p_ssize; int req_number; unsigned bound:1; @@ -82,10 +85,71 @@ end: \ \ CONFIGFS_ATTR(f_uac_opts_, name) +#define UAC_RATE_ATTRIBUTE(name) \ +static ssize_t f_uac_opts_##name##_show(struct config_item *item, \ + char *page) \ +{ \ + struct f_uac_opts *opts = to_f_uac_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[i] == 0) \ + continue; \ + result += sprintf(page + strlen(page), "%u,", \ + opts->name[i]); \ + } \ + if (strlen(page) > 0) \ + page[strlen(page) - 1] = '\n'; \ + mutex_unlock(&opts->lock); \ + \ + return result; \ +} \ + \ +static ssize_t f_uac_opts_##name##_store(struct config_item *item, \ + const char *page, size_t len) \ +{ \ + struct f_uac_opts *opts = to_f_uac_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, 0x00, sizeof(opts->name)); \ + split_page = kstrdup(page, GFP_KERNEL); \ + while ((token = strsep(&split_page, ",")) != NULL) { \ + ret = kstrtou32(token, 0, &num); \ + if (ret) \ + goto end; \ + \ + opts->name[i++] = num; \ + opts->name##_active = num; \ + ret = len; \ + }; \ + \ +end: \ + kfree(split_page); \ + mutex_unlock(&opts->lock); \ + return ret; \ +} \ + \ +CONFIGFS_ATTR(f_uac_opts_, name) + struct f_uac { struct g_audio g_audio; u8 ac_intf, as_in_intf, as_out_intf; u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ + int ctl_id; }; static inline struct f_uac *func_to_uac(struct usb_function *f)