diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_uac2.c')
| -rw-r--r-- | drivers/usb/gadget/function/f_uac2.c | 263 | 
1 files changed, 196 insertions, 67 deletions
| diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 6f03e944e0e3..7aa4c8bc5a1a 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -14,6 +14,9 @@  #include "u_audio.h"  #include "u_uac2.h" +/* UAC2 spec: 4.1 Audio Channel Cluster Descriptor */ +#define UAC2_CHANNEL_MASK 0x07FFFFFF +  /*   * The driver implements a simple UAC_2 topology.   * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture @@ -284,6 +287,24 @@ static struct usb_endpoint_descriptor hs_epout_desc = {  	.bInterval = 4,  }; +static struct usb_endpoint_descriptor ss_epout_desc = { +	.bLength = USB_DT_ENDPOINT_SIZE, +	.bDescriptorType = USB_DT_ENDPOINT, + +	.bEndpointAddress = USB_DIR_OUT, +	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, +	/* .wMaxPacketSize = DYNAMIC */ +	.bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_epout_desc_comp = { +	.bLength		= sizeof(ss_epout_desc_comp), +	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP, +	.bMaxBurst		= 0, +	.bmAttributes		= 0, +	/* wBytesPerInterval = DYNAMIC */ +}; +  /* CS AS ISO OUT Endpoint */  static struct uac2_iso_endpoint_descriptor as_iso_out_desc = {  	.bLength = sizeof as_iso_out_desc, @@ -361,6 +382,24 @@ static struct usb_endpoint_descriptor hs_epin_desc = {  	.bInterval = 4,  }; +static struct usb_endpoint_descriptor ss_epin_desc = { +	.bLength = USB_DT_ENDPOINT_SIZE, +	.bDescriptorType = USB_DT_ENDPOINT, + +	.bEndpointAddress = USB_DIR_IN, +	.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, +	/* .wMaxPacketSize = DYNAMIC */ +	.bInterval = 4, +}; + +static struct usb_ss_ep_comp_descriptor ss_epin_desc_comp = { +	.bLength		= sizeof(ss_epin_desc_comp), +	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP, +	.bMaxBurst		= 0, +	.bmAttributes		= 0, +	/* wBytesPerInterval = DYNAMIC */ +}; +  /* CS AS ISO IN Endpoint */  static struct uac2_iso_endpoint_descriptor as_iso_in_desc = {  	.bLength = sizeof as_iso_in_desc, @@ -433,6 +472,38 @@ static struct usb_descriptor_header *hs_audio_desc[] = {  	NULL,  }; +static struct usb_descriptor_header *ss_audio_desc[] = { +	(struct usb_descriptor_header *)&iad_desc, +	(struct usb_descriptor_header *)&std_ac_if_desc, + +	(struct usb_descriptor_header *)&ac_hdr_desc, +	(struct usb_descriptor_header *)&in_clk_src_desc, +	(struct usb_descriptor_header *)&out_clk_src_desc, +	(struct usb_descriptor_header *)&usb_out_it_desc, +	(struct usb_descriptor_header *)&io_in_it_desc, +	(struct usb_descriptor_header *)&usb_in_ot_desc, +	(struct usb_descriptor_header *)&io_out_ot_desc, + +	(struct usb_descriptor_header *)&std_as_out_if0_desc, +	(struct usb_descriptor_header *)&std_as_out_if1_desc, + +	(struct usb_descriptor_header *)&as_out_hdr_desc, +	(struct usb_descriptor_header *)&as_out_fmt1_desc, +	(struct usb_descriptor_header *)&ss_epout_desc, +	(struct usb_descriptor_header *)&ss_epout_desc_comp, +	(struct usb_descriptor_header *)&as_iso_out_desc, + +	(struct usb_descriptor_header *)&std_as_in_if0_desc, +	(struct usb_descriptor_header *)&std_as_in_if1_desc, + +	(struct usb_descriptor_header *)&as_in_hdr_desc, +	(struct usb_descriptor_header *)&as_in_fmt1_desc, +	(struct usb_descriptor_header *)&ss_epin_desc, +	(struct usb_descriptor_header *)&ss_epin_desc_comp, +	(struct usb_descriptor_header *)&as_iso_in_desc, +	NULL, +}; +  struct cntrl_cur_lay3 {  	__le32	dCUR;  }; @@ -459,6 +530,7 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,  		break;  	case USB_SPEED_HIGH: +	case USB_SPEED_SUPER:  		max_size_ep = 1024;  		factor = 8000;  		break; @@ -488,6 +560,72 @@ static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,  /* Use macro to overcome line length limitation */  #define USBDHDR(p) (struct usb_descriptor_header *)(p) +static void setup_headers(struct f_uac2_opts *opts, +			  struct usb_descriptor_header **headers, +			  enum usb_device_speed speed) +{ +	struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; +	struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; +	struct usb_endpoint_descriptor *epout_desc; +	struct usb_endpoint_descriptor *epin_desc; +	int i; + +	switch (speed) { +	case USB_SPEED_FULL: +		epout_desc = &fs_epout_desc; +		epin_desc = &fs_epin_desc; +		break; +	case USB_SPEED_HIGH: +		epout_desc = &hs_epout_desc; +		epin_desc = &hs_epin_desc; +		break; +	default: +		epout_desc = &ss_epout_desc; +		epin_desc = &ss_epin_desc; +		epout_desc_comp = &ss_epout_desc_comp; +		epin_desc_comp = &ss_epin_desc_comp; +	} + +	i = 0; +	headers[i++] = USBDHDR(&iad_desc); +	headers[i++] = USBDHDR(&std_ac_if_desc); +	headers[i++] = USBDHDR(&ac_hdr_desc); +	if (EPIN_EN(opts)) +		headers[i++] = USBDHDR(&in_clk_src_desc); +	if (EPOUT_EN(opts)) { +		headers[i++] = USBDHDR(&out_clk_src_desc); +		headers[i++] = USBDHDR(&usb_out_it_desc); +	} +	if (EPIN_EN(opts)) { +		headers[i++] = USBDHDR(&io_in_it_desc); +		headers[i++] = USBDHDR(&usb_in_ot_desc); +	} +	if (EPOUT_EN(opts)) { +		headers[i++] = USBDHDR(&io_out_ot_desc); +		headers[i++] = USBDHDR(&std_as_out_if0_desc); +		headers[i++] = USBDHDR(&std_as_out_if1_desc); +		headers[i++] = USBDHDR(&as_out_hdr_desc); +		headers[i++] = USBDHDR(&as_out_fmt1_desc); +		headers[i++] = USBDHDR(epout_desc); +		if (epout_desc_comp) +			headers[i++] = USBDHDR(epout_desc_comp); + +		headers[i++] = USBDHDR(&as_iso_out_desc); +	} +	if (EPIN_EN(opts)) { +		headers[i++] = USBDHDR(&std_as_in_if0_desc); +		headers[i++] = USBDHDR(&std_as_in_if1_desc); +		headers[i++] = USBDHDR(&as_in_hdr_desc); +		headers[i++] = USBDHDR(&as_in_fmt1_desc); +		headers[i++] = USBDHDR(epin_desc); +		if (epin_desc_comp) +			headers[i++] = USBDHDR(epin_desc_comp); + +		headers[i++] = USBDHDR(&as_iso_in_desc); +	} +	headers[i] = NULL; +} +  static void setup_descriptor(struct f_uac2_opts *opts)  {  	/* patch descriptors */ @@ -537,71 +675,39 @@ static void setup_descriptor(struct f_uac2_opts *opts)  		iad_desc.bInterfaceCount++;  	} -	i = 0; -	fs_audio_desc[i++] = USBDHDR(&iad_desc); -	fs_audio_desc[i++] = USBDHDR(&std_ac_if_desc); -	fs_audio_desc[i++] = USBDHDR(&ac_hdr_desc); -	if (EPIN_EN(opts)) -		fs_audio_desc[i++] = USBDHDR(&in_clk_src_desc); -	if (EPOUT_EN(opts)) { -		fs_audio_desc[i++] = USBDHDR(&out_clk_src_desc); -		fs_audio_desc[i++] = USBDHDR(&usb_out_it_desc); -	} -	if (EPIN_EN(opts)) { -		fs_audio_desc[i++] = USBDHDR(&io_in_it_desc); -		fs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); -	} -	if (EPOUT_EN(opts)) { -		fs_audio_desc[i++] = USBDHDR(&io_out_ot_desc); -		fs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc); -		fs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc); -		fs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc); -		fs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc); -		fs_audio_desc[i++] = USBDHDR(&fs_epout_desc); -		fs_audio_desc[i++] = USBDHDR(&as_iso_out_desc); -	} -	if (EPIN_EN(opts)) { -		fs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc); -		fs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc); -		fs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc); -		fs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc); -		fs_audio_desc[i++] = USBDHDR(&fs_epin_desc); -		fs_audio_desc[i++] = USBDHDR(&as_iso_in_desc); -	} -	fs_audio_desc[i] = NULL; +	setup_headers(opts, fs_audio_desc, USB_SPEED_FULL); +	setup_headers(opts, hs_audio_desc, USB_SPEED_HIGH); +	setup_headers(opts, ss_audio_desc, USB_SPEED_SUPER); +} -	i = 0; -	hs_audio_desc[i++] = USBDHDR(&iad_desc); -	hs_audio_desc[i++] = USBDHDR(&std_ac_if_desc); -	hs_audio_desc[i++] = USBDHDR(&ac_hdr_desc); -	if (EPIN_EN(opts)) -		hs_audio_desc[i++] = USBDHDR(&in_clk_src_desc); -	if (EPOUT_EN(opts)) { -		hs_audio_desc[i++] = USBDHDR(&out_clk_src_desc); -		hs_audio_desc[i++] = USBDHDR(&usb_out_it_desc); -	} -	if (EPIN_EN(opts)) { -		hs_audio_desc[i++] = USBDHDR(&io_in_it_desc); -		hs_audio_desc[i++] = USBDHDR(&usb_in_ot_desc); -	} -	if (EPOUT_EN(opts)) { -		hs_audio_desc[i++] = USBDHDR(&io_out_ot_desc); -		hs_audio_desc[i++] = USBDHDR(&std_as_out_if0_desc); -		hs_audio_desc[i++] = USBDHDR(&std_as_out_if1_desc); -		hs_audio_desc[i++] = USBDHDR(&as_out_hdr_desc); -		hs_audio_desc[i++] = USBDHDR(&as_out_fmt1_desc); -		hs_audio_desc[i++] = USBDHDR(&hs_epout_desc); -		hs_audio_desc[i++] = USBDHDR(&as_iso_out_desc); -	} -	if (EPIN_EN(opts)) { -		hs_audio_desc[i++] = USBDHDR(&std_as_in_if0_desc); -		hs_audio_desc[i++] = USBDHDR(&std_as_in_if1_desc); -		hs_audio_desc[i++] = USBDHDR(&as_in_hdr_desc); -		hs_audio_desc[i++] = USBDHDR(&as_in_fmt1_desc); -		hs_audio_desc[i++] = USBDHDR(&hs_epin_desc); -		hs_audio_desc[i++] = USBDHDR(&as_iso_in_desc); +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"); +		return -EINVAL; +	} else if (!opts->c_srate) { +		dev_err(dev, "Error: incorrect capture sampling rate\n"); +		return -EINVAL;  	} -	hs_audio_desc[i] = NULL; + +	return 0;  }  static int @@ -612,11 +718,13 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)  	struct usb_composite_dev *cdev = cfg->cdev;  	struct usb_gadget *gadget = cdev->gadget;  	struct device *dev = &gadget->dev; -	struct f_uac2_opts *uac2_opts; +	struct f_uac2_opts *uac2_opts = g_audio_to_uac2_opts(agdev);  	struct usb_string *us;  	int ret; -	uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); +	ret = afunc_validate_opts(agdev, dev); +	if (ret) +		return ret;  	us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn));  	if (IS_ERR(us)) @@ -716,6 +824,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)  		return ret;  	} +	ret = set_ep_max_packet_size(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); +	if (ret < 0) { +		dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); +		return ret; +	} +  	if (EPOUT_EN(uac2_opts)) {  		agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);  		if (!agdev->out_ep) { @@ -739,13 +861,20 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)  				le16_to_cpu(fs_epout_desc.wMaxPacketSize),  				le16_to_cpu(hs_epout_desc.wMaxPacketSize)); +	agdev->in_ep_maxpsize = max_t(u16, agdev->in_ep_maxpsize, +				le16_to_cpu(ss_epin_desc.wMaxPacketSize)); +	agdev->out_ep_maxpsize = max_t(u16, agdev->out_ep_maxpsize, +				le16_to_cpu(ss_epout_desc.wMaxPacketSize)); +  	hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;  	hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; +	ss_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; +	ss_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;  	setup_descriptor(uac2_opts); -	ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL, -				     NULL); +	ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, ss_audio_desc, +				     ss_audio_desc);  	if (ret)  		return ret; | 
