in gadget/function/f_uac1.c [1111:1345]
static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct device *dev = &gadget->dev;
struct f_uac1 *uac1 = func_to_uac1(f);
struct g_audio *audio = func_to_g_audio(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;
status = f_audio_validate_opts(audio, dev);
if (status)
return status;
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
ac_header_desc = build_ac_header_desc(audio_opts);
if (!ac_header_desc)
return -ENOMEM;
if (FUOUT_EN(audio_opts)) {
out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask);
if (!out_feature_unit_desc) {
status = -ENOMEM;
goto fail;
}
}
if (FUIN_EN(audio_opts)) {
in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask);
if (!in_feature_unit_desc) {
status = -ENOMEM;
goto err_free_fu;
}
}
ac_interface_desc.iInterface = us[STR_AC_IF].id;
usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id;
usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id;
io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id;
as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id;
as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id;
io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id;
io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id;
usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id;
as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id;
as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id;
if (FUOUT_EN(audio_opts)) {
u8 *i_feature;
i_feature = (u8 *)out_feature_unit_desc +
out_feature_unit_desc->bLength - 1;
*i_feature = us[STR_FU_OUT].id;
}
if (FUIN_EN(audio_opts)) {
u8 *i_feature;
i_feature = (u8 *)in_feature_unit_desc +
in_feature_unit_desc->bLength - 1;
*i_feature = us[STR_FU_IN].id;
}
/* Set channel numbers */
usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask);
usb_out_it_desc.wChannelConfig = cpu_to_le16(audio_opts->c_chmask);
as_out_type_i_desc.bNrChannels = num_channels(audio_opts->c_chmask);
as_out_type_i_desc.bSubframeSize = audio_opts->c_ssize;
as_out_type_i_desc.bBitResolution = audio_opts->c_ssize * 8;
io_in_it_desc.bNrChannels = num_channels(audio_opts->p_chmask);
io_in_it_desc.wChannelConfig = cpu_to_le16(audio_opts->p_chmask);
as_in_type_i_desc.bNrChannels = num_channels(audio_opts->p_chmask);
as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize;
as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8;
if (FUOUT_EN(audio_opts)) {
__le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0];
u32 control = 0;
if (audio_opts->c_mute_present)
control |= UAC_FU_MUTE;
if (audio_opts->c_volume_present)
control |= UAC_FU_VOLUME;
*bma = cpu_to_le16(control);
}
if (FUIN_EN(audio_opts)) {
__le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0];
u32 control = 0;
if (audio_opts->p_mute_present)
control |= UAC_FU_MUTE;
if (audio_opts->p_volume_present)
control |= UAC_FU_VOLUME;
*bma = cpu_to_le16(control);
}
/* 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);
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto err_free_fu;
ac_interface_desc.bInterfaceNumber = status;
uac1->ac_intf = status;
uac1->ac_alt = 0;
ba_iface_id = 0;
if (EPOUT_EN(audio_opts)) {
status = usb_interface_id(c, f);
if (status < 0)
goto err_free_fu;
as_out_interface_alt_0_desc.bInterfaceNumber = status;
as_out_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
uac1->as_out_intf = status;
uac1->as_out_alt = 0;
}
if (EPIN_EN(audio_opts)) {
status = usb_interface_id(c, f);
if (status < 0)
goto err_free_fu;
as_in_interface_alt_0_desc.bInterfaceNumber = status;
as_in_interface_alt_1_desc.bInterfaceNumber = status;
ac_header_desc->baInterfaceNr[ba_iface_id++] = status;
uac1->as_in_intf = status;
uac1->as_in_alt = 0;
}
audio->gadget = gadget;
status = -ENODEV;
ac_interface_desc.bNumEndpoints = 0;
/* allocate AC interrupt endpoint */
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc);
if (!ep)
goto err_free_fu;
uac1->int_ep = ep;
uac1->int_ep->desc = &ac_int_ep_desc;
ac_interface_desc.bNumEndpoints = 1;
}
/* allocate instance-specific endpoints */
if (EPOUT_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
if (!ep)
goto err_free_fu;
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
}
if (EPIN_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc);
if (!ep)
goto err_free_fu;
audio->in_ep = ep;
audio->in_ep->desc = &as_in_ep_desc;
}
setup_descriptor(audio_opts);
/* copy descriptors, and track endpoint copies */
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
NULL);
if (status)
goto err_free_fu;
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;
audio->params.c_ssize = audio_opts->c_ssize;
if (FUIN_EN(audio_opts)) {
audio->params.p_fu.id = USB_IN_FU_ID;
audio->params.p_fu.mute_present = audio_opts->p_mute_present;
audio->params.p_fu.volume_present =
audio_opts->p_volume_present;
audio->params.p_fu.volume_min = audio_opts->p_volume_min;
audio->params.p_fu.volume_max = audio_opts->p_volume_max;
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;
audio->params.p_ssize = audio_opts->p_ssize;
if (FUOUT_EN(audio_opts)) {
audio->params.c_fu.id = USB_OUT_FU_ID;
audio->params.c_fu.mute_present = audio_opts->c_mute_present;
audio->params.c_fu.volume_present =
audio_opts->c_volume_present;
audio->params.c_fu.volume_min = audio_opts->c_volume_min;
audio->params.c_fu.volume_max = audio_opts->c_volume_max;
audio->params.c_fu.volume_res = audio_opts->c_volume_res;
}
audio->params.req_number = audio_opts->req_number;
audio->params.fb_max = FBACK_FAST_MAX;
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts))
audio->notify = audio_notify;
status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget");
if (status)
goto err_card_register;
return 0;
err_card_register:
usb_free_all_descriptors(f);
err_free_fu:
kfree(out_feature_unit_desc);
out_feature_unit_desc = NULL;
kfree(in_feature_unit_desc);
in_feature_unit_desc = NULL;
fail:
kfree(ac_header_desc);
ac_header_desc = NULL;
return status;
}