static int f_audio_bind()

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;
}