mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
FROMLIST: usb: gadget: f_uac1: add different speed transfers support
On page 61 of the UAC1 specification ( https://www.usb.org/sites/default/files/audio10.pdf), bInterval is interval for polling endpoint for data transfers expressed in milliseconds, must be set to 1. On page 47 of the USB2.0 specification ( https://www.usb.org/sites/default/files/usb_20_20211008.zip), An isochronous endpoint must specify its required bus access period. Full-/high-speed endpoints must specify a desired period as (2^(bInterval-1)) x F, where bInterval is in the range one to (and including) 16 and F is 125 μs for high-speed and 1ms for full-speed. On page 362 of the USB3.2 specification ( https://usb.org/sites/default/files/usb_32_20210125.zip), The 'SuperSpeed Endpoint Companion Descriptor' shall only be returned by Enhanced SuperSpeed devices that are operating at Gen X speed. Each endpoint described in an interface is followed by a 'SuperSpeed Endpoint Companion Descriptor'. Currently uac1 driver doesn't set bInterval to 1 in full speed transfer and doesn't have a 'SuperSpeed Endpoint Companion Descriptor' behind 'Standard Endpoint Descriptor'. So we should set bInterval to 1 in full speed transfer and set it to 4 in other speed transfers, and we should add 'SuperSpeed Endpoint Companion Descriptor' behind 'Standard Endpoint Descriptor' for superspeed transfer. Signed-off-by: Jing Leng <jleng@ambarella.com> Link: https://lore.kernel.org/all/20220217051951.7466-1-3090101217@zju.edu.cn/ Change-Id: I53205c1850a0239d1f39f4c44cea77bfb1c00e81 Signed-off-by: Frank Wang <frank.wang@rock-chips.com>
This commit is contained in:
@@ -137,6 +137,15 @@ static struct uac_feature_unit_descriptor *in_feature_unit_desc;
|
||||
static struct uac_feature_unit_descriptor *out_feature_unit_desc;
|
||||
|
||||
/* AC IN Interrupt Endpoint */
|
||||
static struct usb_endpoint_descriptor fs_int_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = cpu_to_le16(2),
|
||||
.bInterval = 1,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor ac_int_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@@ -146,6 +155,12 @@ static struct usb_endpoint_descriptor ac_int_ep_desc = {
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor ac_int_ep_desc_comp = {
|
||||
.bLength = sizeof(ac_int_ep_desc_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
.wBytesPerInterval = cpu_to_le16(2),
|
||||
};
|
||||
|
||||
/* B.4.1 Standard AS Interface Descriptor */
|
||||
static struct usb_interface_descriptor as_out_interface_alt_0_desc = {
|
||||
.bLength = USB_DT_INTERFACE_SIZE,
|
||||
@@ -217,6 +232,16 @@ static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = {
|
||||
};
|
||||
|
||||
/* Standard ISO OUT Endpoint Descriptor */
|
||||
static struct usb_endpoint_descriptor fs_out_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_OUT,
|
||||
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
|
||||
| USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
|
||||
.bInterval = 1,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor as_out_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@@ -227,6 +252,12 @@ static struct usb_endpoint_descriptor as_out_ep_desc = {
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor as_out_ep_desc_comp = {
|
||||
.bLength = sizeof(as_out_ep_desc_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
.wBytesPerInterval = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
|
||||
};
|
||||
|
||||
/* Class-specific AS ISO OUT Endpoint Descriptor */
|
||||
static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
|
||||
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
|
||||
@@ -247,7 +278,17 @@ static struct uac_format_type_i_discrete_descriptor as_in_type_i_desc = {
|
||||
.bSamFreqType = 0, /* filled on rate setup */
|
||||
};
|
||||
|
||||
/* Standard ISO OUT Endpoint Descriptor */
|
||||
/* Standard ISO IN Endpoint Descriptor */
|
||||
static struct usb_endpoint_descriptor fs_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
.bEndpointAddress = USB_DIR_IN,
|
||||
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
|
||||
| USB_ENDPOINT_XFER_ISOC,
|
||||
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
|
||||
.bInterval = 1,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor as_in_ep_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
@@ -258,6 +299,12 @@ static struct usb_endpoint_descriptor as_in_ep_desc = {
|
||||
.bInterval = 4,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor as_in_ep_desc_comp = {
|
||||
.bLength = sizeof(as_in_ep_desc_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
.wBytesPerInterval = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
|
||||
};
|
||||
|
||||
/* Class-specific AS ISO OUT Endpoint Descriptor */
|
||||
static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
|
||||
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
|
||||
@@ -268,7 +315,42 @@ static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
|
||||
.wLockDelay = 0,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *f_audio_desc[] = {
|
||||
static struct usb_descriptor_header *fs_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&iad_desc,
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&usb_out_it_desc,
|
||||
(struct usb_descriptor_header *)&io_out_ot_desc,
|
||||
(struct usb_descriptor_header *)&out_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&io_in_it_desc,
|
||||
(struct usb_descriptor_header *)&usb_in_ot_desc,
|
||||
(struct usb_descriptor_header *)&in_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&fs_int_ep_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_out_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&fs_out_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_iso_out_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_in_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_in_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&fs_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_iso_in_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *hs_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&iad_desc,
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
@@ -303,6 +385,44 @@ static struct usb_descriptor_header *f_audio_desc[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ss_audio_desc[] = {
|
||||
(struct usb_descriptor_header *)&iad_desc,
|
||||
(struct usb_descriptor_header *)&ac_interface_desc,
|
||||
(struct usb_descriptor_header *)&ac_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&usb_out_it_desc,
|
||||
(struct usb_descriptor_header *)&io_out_ot_desc,
|
||||
(struct usb_descriptor_header *)&out_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&io_in_it_desc,
|
||||
(struct usb_descriptor_header *)&usb_in_ot_desc,
|
||||
(struct usb_descriptor_header *)&in_feature_unit_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&ac_int_ep_desc,
|
||||
(struct usb_descriptor_header *)&ac_int_ep_desc_comp,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_out_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_out_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_out_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_out_ep_desc_comp,
|
||||
(struct usb_descriptor_header *)&as_iso_out_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_in_interface_alt_0_desc,
|
||||
(struct usb_descriptor_header *)&as_in_interface_alt_1_desc,
|
||||
(struct usb_descriptor_header *)&as_in_header_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_in_type_i_desc,
|
||||
|
||||
(struct usb_descriptor_header *)&as_in_ep_desc,
|
||||
(struct usb_descriptor_header *)&as_in_ep_desc_comp,
|
||||
(struct usb_descriptor_header *)&as_iso_in_desc,
|
||||
NULL,
|
||||
};
|
||||
|
||||
enum {
|
||||
STR_ASSOC,
|
||||
STR_AC_IF,
|
||||
@@ -348,6 +468,90 @@ static struct usb_gadget_strings *uac1_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/* Use macro to overcome line length limitation */
|
||||
#define USBDHDR(p) ((struct usb_descriptor_header *)(p))
|
||||
|
||||
static void setup_headers(struct f_uac1_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_ss_ep_comp_descriptor *ep_int_desc_comp = NULL;
|
||||
struct usb_endpoint_descriptor *epout_desc;
|
||||
struct usb_endpoint_descriptor *epin_desc;
|
||||
struct usb_endpoint_descriptor *ep_int_desc;
|
||||
int i;
|
||||
|
||||
switch (speed) {
|
||||
case USB_SPEED_FULL:
|
||||
epout_desc = &fs_out_ep_desc;
|
||||
epin_desc = &fs_in_ep_desc;
|
||||
ep_int_desc = &fs_int_ep_desc;
|
||||
break;
|
||||
case USB_SPEED_HIGH:
|
||||
epout_desc = &as_out_ep_desc;
|
||||
epin_desc = &as_in_ep_desc;
|
||||
ep_int_desc = &ac_int_ep_desc;
|
||||
break;
|
||||
default:
|
||||
epout_desc = &as_out_ep_desc;
|
||||
epout_desc_comp = &as_out_ep_desc_comp;
|
||||
epin_desc = &as_in_ep_desc;
|
||||
epin_desc_comp = &as_in_ep_desc_comp;
|
||||
ep_int_desc = &ac_int_ep_desc;
|
||||
ep_int_desc_comp = &ac_int_ep_desc_comp;
|
||||
break;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
headers[i++] = USBDHDR(&iad_desc);
|
||||
headers[i++] = USBDHDR(&ac_interface_desc);
|
||||
headers[i++] = USBDHDR(ac_header_desc);
|
||||
|
||||
if (EPOUT_EN(opts)) {
|
||||
headers[i++] = USBDHDR(&usb_out_it_desc);
|
||||
headers[i++] = USBDHDR(&io_out_ot_desc);
|
||||
if (FUOUT_EN(opts))
|
||||
headers[i++] = USBDHDR(out_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (EPIN_EN(opts)) {
|
||||
headers[i++] = USBDHDR(&io_in_it_desc);
|
||||
headers[i++] = USBDHDR(&usb_in_ot_desc);
|
||||
if (FUIN_EN(opts))
|
||||
headers[i++] = USBDHDR(in_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (FUOUT_EN(opts) || FUIN_EN(opts)) {
|
||||
headers[i++] = USBDHDR(ep_int_desc);
|
||||
if (ep_int_desc_comp)
|
||||
headers[i++] = USBDHDR(ep_int_desc_comp);
|
||||
}
|
||||
|
||||
if (EPOUT_EN(opts)) {
|
||||
headers[i++] = USBDHDR(&as_out_interface_alt_0_desc);
|
||||
headers[i++] = USBDHDR(&as_out_interface_alt_1_desc);
|
||||
headers[i++] = USBDHDR(&as_out_header_desc);
|
||||
headers[i++] = USBDHDR(&as_out_type_i_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(&as_in_interface_alt_0_desc);
|
||||
headers[i++] = USBDHDR(&as_in_interface_alt_1_desc);
|
||||
headers[i++] = USBDHDR(&as_in_header_desc);
|
||||
headers[i++] = USBDHDR(&as_in_type_i_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;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
|
||||
*/
|
||||
@@ -1034,9 +1238,6 @@ uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
|
||||
return ac_desc;
|
||||
}
|
||||
|
||||
/* Use macro to overcome line length limitation */
|
||||
#define USBDHDR(p) (struct usb_descriptor_header *)(p)
|
||||
|
||||
static void setup_descriptor(struct f_uac1_opts *opts)
|
||||
{
|
||||
/* patch descriptors */
|
||||
@@ -1095,45 +1296,9 @@ static void setup_descriptor(struct f_uac1_opts *opts)
|
||||
iad_desc.bInterfaceCount++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
f_audio_desc[i++] = USBDHDR(&iad_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&ac_interface_desc);
|
||||
f_audio_desc[i++] = USBDHDR(ac_header_desc);
|
||||
|
||||
if (EPOUT_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&usb_out_it_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&io_out_ot_desc);
|
||||
if (FUOUT_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(out_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (EPIN_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&io_in_it_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&usb_in_ot_desc);
|
||||
if (FUIN_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(in_feature_unit_desc);
|
||||
}
|
||||
|
||||
if (FUOUT_EN(opts) || FUIN_EN(opts))
|
||||
f_audio_desc[i++] = USBDHDR(&ac_int_ep_desc);
|
||||
|
||||
if (EPOUT_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_0_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_interface_alt_1_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_header_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_type_i_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_out_ep_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_iso_out_desc);
|
||||
}
|
||||
if (EPIN_EN(opts)) {
|
||||
f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_0_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_in_interface_alt_1_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_in_header_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_in_type_i_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_in_ep_desc);
|
||||
f_audio_desc[i++] = USBDHDR(&as_iso_in_desc);
|
||||
}
|
||||
f_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);
|
||||
}
|
||||
|
||||
static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
|
||||
@@ -1361,7 +1526,6 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (!ep)
|
||||
goto err_free_fu;
|
||||
uac1->int_ep = ep;
|
||||
uac1->int_ep->desc = &ac_int_ep_desc;
|
||||
|
||||
ac_interface_desc.bNumEndpoints = 1;
|
||||
}
|
||||
@@ -1372,7 +1536,6 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (!ep)
|
||||
goto err_free_fu;
|
||||
audio->out_ep = ep;
|
||||
audio->out_ep->desc = &as_out_ep_desc;
|
||||
}
|
||||
|
||||
if (EPIN_EN(audio_opts)) {
|
||||
@@ -1380,19 +1543,27 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
if (!ep)
|
||||
goto err_free_fu;
|
||||
audio->in_ep = ep;
|
||||
audio->in_ep->desc = &as_in_ep_desc;
|
||||
}
|
||||
|
||||
/* FS endpoint addresses are copied from autoconfigured HS descriptors */
|
||||
fs_int_ep_desc.bEndpointAddress = ac_int_ep_desc.bEndpointAddress;
|
||||
fs_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress;
|
||||
fs_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress;
|
||||
|
||||
setup_descriptor(audio_opts);
|
||||
|
||||
/* copy descriptors, and track endpoint copies */
|
||||
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
|
||||
NULL);
|
||||
status = usb_assign_descriptors(f, fs_audio_desc, hs_audio_desc,
|
||||
ss_audio_desc, ss_audio_desc);
|
||||
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->out_ep_maxpsize = max_t(u16,
|
||||
le16_to_cpu(fs_out_ep_desc.wMaxPacketSize),
|
||||
le16_to_cpu(as_out_ep_desc.wMaxPacketSize));
|
||||
audio->in_ep_maxpsize = max_t(u16,
|
||||
le16_to_cpu(fs_in_ep_desc.wMaxPacketSize),
|
||||
le16_to_cpu(as_in_ep_desc.wMaxPacketSize));
|
||||
audio->params.c_chmask = audio_opts->c_chmask;
|
||||
memcpy(audio->params.c_srates, audio_opts->c_srates,
|
||||
sizeof(audio->params.c_srates));
|
||||
|
||||
Reference in New Issue
Block a user