@@ -27,16 +27,36 @@
/* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */
#define UAC1_CHANNEL_MASK 0x0FFF
-#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID)
-#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID)
+#define USB_OUT_FU_ID(_opts) (_opts->c_alt_1_opts.fu_id)
+#define USB_IN_FU_ID(_opts) (_opts->p_alt_1_opts.fu_id)
-#define EPIN_EN(_opts) ((_opts)->p_chmask != 0)
-#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0)
+#define EP_EN(_alt_opts) ((_alt_opts) && ((_alt_opts)->chmask != 0))
#define FUIN_EN(_opts) ((_opts)->p_mute_present \
|| (_opts)->p_volume_present)
#define FUOUT_EN(_opts) ((_opts)->c_mute_present \
|| (_opts)->c_volume_present)
-#define EPOUT_FBACK_IN_EN(_opts) ((_opts)->c_sync == USB_ENDPOINT_SYNC_ASYNC)
+#define EPOUT_FBACK_IN_EN(_alt_opts) ((_alt_opts)->sync == USB_ENDPOINT_SYNC_ASYNC)
+
+/* Check if any alt mode has option enabled */
+#define EN_ANY(single, fn, cp) \
+static int fn(struct f_uac1_opts *opts) \
+{ \
+ struct f_uac1_alt_opts *alt_opts; \
+ \
+ if (single(&opts->cp##_alt_1_opts)) \
+ return 1; \
+ \
+ list_for_each_entry(alt_opts, &opts->cp##_alt_opts, list) { \
+ if (single(alt_opts)) \
+ return 1; \
+ } \
+ \
+ return 0; \
+}
+
+EN_ANY(EP_EN, epout_en_any, c)
+EN_ANY(EP_EN, epin_en_any, p)
+EN_ANY(EPOUT_FBACK_IN_EN, epout_fback_in_en_any, p)
struct f_uac1 {
struct g_audio g_audio;
@@ -88,49 +108,6 @@ static struct usb_interface_descriptor ac_interface_desc = {
/* B.3.2 Class-Specific AC Interface Descriptor */
static struct uac1_ac_header_descriptor *ac_header_desc;
-static struct uac_input_terminal_descriptor usb_out_it_desc = {
- .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_INPUT_TERMINAL,
- /* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
- .bAssocTerminal = 0,
- .wChannelConfig = cpu_to_le16(0x3),
-};
-
-static struct uac1_output_terminal_descriptor io_out_ot_desc = {
- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
- /* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER),
- .bAssocTerminal = 0,
- /* .bSourceID = DYNAMIC */
-};
-
-static struct uac_input_terminal_descriptor io_in_it_desc = {
- .bLength = UAC_DT_INPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_INPUT_TERMINAL,
- /* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE),
- .bAssocTerminal = 0,
- .wChannelConfig = cpu_to_le16(0x3),
-};
-
-static struct uac1_output_terminal_descriptor usb_in_ot_desc = {
- .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
- /* .bTerminalID = DYNAMIC */
- .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING),
- .bAssocTerminal = 0,
- /* .bSourceID = DYNAMIC */
-};
-
-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_ac_int_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -167,97 +144,6 @@ static struct usb_ss_ep_comp_descriptor ss_ac_int_ep_desc_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,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 0,
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_out_interface_alt_1_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 1,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_in_interface_alt_0_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 0,
- .bNumEndpoints = 0,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-static struct usb_interface_descriptor as_in_interface_alt_1_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 1,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_AUDIO,
- .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
-};
-
-/* B.4.2 Class-Specific AS Interface Descriptor */
-static struct uac1_as_header_descriptor as_out_header_desc = {
- .bLength = UAC_DT_AS_HEADER_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_AS_GENERAL,
- /* .bTerminalLink = DYNAMIC */
- .bDelay = 1,
- .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
-};
-
-static struct uac1_as_header_descriptor as_in_header_desc = {
- .bLength = UAC_DT_AS_HEADER_SIZE,
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubtype = UAC_AS_GENERAL,
- /* .bTerminalLink = DYNAMIC */
- .bDelay = 1,
- .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM),
-};
-
-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 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 = 0, /* filled on rate setup */
-};
-
-/* Standard ISO OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor fs_as_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 hs_as_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 = 4,
-};
-
/* Class-specific AS ISO OUT Endpoint Descriptor */
static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
@@ -268,37 +154,6 @@ static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.wLockDelay = cpu_to_le16(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 = 0, /* filled on rate setup */
-};
-
-/* Standard ISO IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor fs_as_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 hs_as_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 = 4,
-};
-
/* Class-specific AS ISO IN Endpoint Descriptor */
static struct uac_iso_endpoint_descriptor as_iso_in_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
@@ -351,153 +206,6 @@ static struct usb_ss_ep_comp_descriptor ss_as_in_fback_desc_comp = {
.wBytesPerInterval = cpu_to_le16(4),
};
-static struct usb_descriptor_header *f_audio_fs_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_ac_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_as_out_ep_desc,
- (struct usb_descriptor_header *)&as_iso_out_desc,
- (struct usb_descriptor_header *)&fs_as_in_fback_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_as_in_ep_desc,
- (struct usb_descriptor_header *)&as_iso_in_desc,
- NULL,
-};
-
-static struct usb_descriptor_header *f_audio_hs_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 *)&hs_ac_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 *)&hs_as_out_ep_desc,
- (struct usb_descriptor_header *)&as_iso_out_desc,
- (struct usb_descriptor_header *)&hs_as_in_fback_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 *)&hs_as_in_ep_desc,
- (struct usb_descriptor_header *)&as_iso_in_desc,
- NULL,
-};
-
-/* Standard ISO OUT Endpoint Descriptor */
-static struct usb_endpoint_descriptor ss_as_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 = 4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_as_out_ep_desc_comp = {
- .bLength = sizeof(ss_as_out_ep_desc_comp),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 0,
- .bmAttributes = 0,
- /* wBytesPerInterval = DYNAMIC */
-};
-
-/* Standard ISO IN Endpoint Descriptor */
-static struct usb_endpoint_descriptor ss_as_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 = 4,
-};
-
-static struct usb_ss_ep_comp_descriptor ss_as_in_ep_desc_comp = {
- .bLength = sizeof(ss_as_in_ep_desc_comp),
- .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
- .bMaxBurst = 0,
- .bmAttributes = 0,
- /* wBytesPerInterval = DYNAMIC */
-};
-
-static struct usb_descriptor_header *f_audio_ss_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 *)&ss_ac_int_ep_desc,
- (struct usb_descriptor_header *)&ss_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 *)&ss_as_out_ep_desc,
- (struct usb_descriptor_header *)&ss_as_out_ep_desc_comp,
- (struct usb_descriptor_header *)&as_iso_out_desc,
- (struct usb_descriptor_header *)&ss_as_in_fback_desc,
- (struct usb_descriptor_header *)&ss_as_in_fback_desc_comp,
-
- (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 *)&ss_as_in_ep_desc,
- (struct usb_descriptor_header *)&ss_as_in_ep_desc_comp,
- (struct usb_descriptor_header *)&as_iso_in_desc,
- NULL,
-};
-
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
@@ -602,11 +310,11 @@ in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
unsigned int is_playback = 0;
- if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
is_playback = 1;
if (control_selector == UAC_FU_MUTE) {
@@ -653,11 +361,11 @@ in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
unsigned int is_playback = 0;
- if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
@@ -700,11 +408,11 @@ in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
unsigned int is_playback = 0;
- if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
@@ -747,11 +455,11 @@ in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 control_selector = w_value >> 8;
int value = -EOPNOTSUPP;
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
unsigned int is_playback = 0;
- if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
is_playback = 1;
if (control_selector == UAC_FU_VOLUME) {
@@ -799,11 +507,11 @@ out_rq_cur_complete(struct usb_ep *ep, struct usb_request *req)
return;
}
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
unsigned int is_playback = 0;
- if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID))
+ if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts)))
is_playback = 1;
if (control_selector == UAC_FU_MUTE) {
@@ -848,8 +556,8 @@ out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr)
u8 entity_id = (w_index >> 8) & 0xff;
u8 control_selector = w_value >> 8;
- if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) ||
- (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) {
+ if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID(opts))) ||
+ (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID(opts)))) {
memcpy(&uac1->setup_cr, cr, sizeof(*cr));
req->context = audio;
req->complete = out_rq_cur_complete;
@@ -1221,6 +929,10 @@ static struct usb_string *attach_strings(struct usb_composite_dev *cdev,
/*-------------------------------------------------------------------------*/
+/*
+ * Descriptor building functions
+ */
+
static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_alt_opts *alt_opts,
struct usb_endpoint_descriptor *ep_desc,
enum usb_device_speed speed, bool is_playback)
@@ -1231,11 +943,59 @@ static int set_ep_max_packet_size_bint(struct device *dev, const struct f_uac1_a
alt_opts->ssize, alt_opts->sync, alt_opts->c.opts->fb_max);
}
-static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
+struct path_params {
+ int dir;
+ int id;
+ struct f_uac1_opts *opts;
+ struct usb_string *strings;
+};
+
+/* 4.3.2.1 Input Terminal Descriptor */
+static void init_it_desc(struct uac_input_terminal_descriptor *it_desc,
+ struct f_uac1_alt_opts *alt_opts,
+ struct path_params *params)
+{
+ it_desc->bLength = UAC_DT_INPUT_TERMINAL_SIZE;
+ it_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+ it_desc->bDescriptorSubtype = UAC_INPUT_TERMINAL;
+ it_desc->bTerminalID = params->id++;
+ it_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE)
+ ? UAC_TERMINAL_STREAMING
+ : alt_opts->terminal_type);
+ it_desc->bAssocTerminal = 0;
+ it_desc->bNrChannels = num_channels(alt_opts->chmask);
+ it_desc->wChannelConfig = cpu_to_le16(alt_opts->chmask);
+ it_desc->iTerminal = add_string(params->strings, alt_opts->it_name);
+ it_desc->iChannelNames = add_string(params->strings, alt_opts->it_ch_name);
+}
+
+/* 4.3.2.2 Output Terminal Descriptor */
+static void init_ot_desc(struct uac1_output_terminal_descriptor *ot_desc,
+ struct f_uac1_alt_opts *alt_opts,
+ struct path_params *params, int src_id)
+{
+ ot_desc->bLength = UAC_DT_OUTPUT_TERMINAL_SIZE;
+ ot_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+ ot_desc->bDescriptorSubtype = UAC_OUTPUT_TERMINAL;
+ ot_desc->bTerminalID = params->id++;
+ ot_desc->wTerminalType = cpu_to_le16((params->dir == HOST_TO_DEVICE)
+ ? alt_opts->terminal_type
+ : UAC_TERMINAL_STREAMING);
+ ot_desc->bAssocTerminal = 0;
+ ot_desc->bSourceID = src_id;
+ ot_desc->iTerminal = add_string(params->strings, alt_opts->ot_name);
+}
+
+/* 4.3.2.5 Feature Unit Descriptor */
+static struct uac_feature_unit_descriptor *build_fu_desc(struct f_uac1_alt_opts *alt_opts,
+ struct path_params *params,
+ int src_id)
{
struct uac_feature_unit_descriptor *fu_desc;
- int channels = num_channels(chmask);
+ int channels = num_channels(alt_opts->chmask);
int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels);
+ u32 control = 0;
+ u8 *i_feature;
fu_desc = kzalloc(fu_desc_size, GFP_KERNEL);
if (!fu_desc)
@@ -1243,29 +1003,46 @@ static struct uac_feature_unit_descriptor *build_fu_desc(int chmask)
fu_desc->bLength = fu_desc_size;
fu_desc->bDescriptorType = USB_DT_CS_INTERFACE;
-
fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT;
+ fu_desc->bUnitID = params->id++;
+ fu_desc->bSourceID = src_id;
fu_desc->bControlSize = 2;
- /* bUnitID, bSourceID and bmaControls will be defined later */
+ if (params->dir == HOST_TO_DEVICE) {
+ if (params->opts->c_mute_present)
+ control |= UAC_FU_MUTE;
+ if (params->opts->c_volume_present)
+ control |= UAC_FU_VOLUME;
+ }
+
+ if (params->dir == DEVICE_TO_HOST) {
+ if (params->opts->p_mute_present)
+ control |= UAC_FU_MUTE;
+ if (params->opts->p_volume_present)
+ control |= UAC_FU_VOLUME;
+ }
+
+ /* Only master volume/mute is supported. Per-channel controls are all zero. */
+ fu_desc->bmaControls[0] = cpu_to_le16(control);
+
+ /* iFeature is located after all channel controls */
+ i_feature = (u8 *)fu_desc + fu_desc->bLength - 1;
+ *i_feature = add_string(params->strings, alt_opts->fu_vol_name);
return fu_desc;
}
-/* B.3.2 Class-Specific AC Interface Descriptor */
+/* 4.3.2 Class-Specific AC Interface Descriptor */
static struct
-uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
+uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1 *uac1, struct f_uac1_opts *opts)
{
struct uac1_ac_header_descriptor *ac_desc;
int ac_header_desc_size;
- int num_ifaces = 0;
-
- if (EPOUT_EN(opts))
- num_ifaces++;
- if (EPIN_EN(opts))
- num_ifaces++;
+ int capture = epout_en_any(opts);
+ int playback = epin_en_any(opts);
+ int ba_iface_id = 0;
- ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces);
+ ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(capture + playback);
ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL);
if (!ac_desc)
@@ -1275,195 +1052,530 @@ uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts)
ac_desc->bDescriptorType = USB_DT_CS_INTERFACE;
ac_desc->bDescriptorSubtype = UAC_HEADER;
ac_desc->bcdADC = cpu_to_le16(0x0100);
- ac_desc->bInCollection = num_ifaces;
+ ac_desc->bInCollection = capture + playback;
+
+ if (capture)
+ ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_out_intf;
+
+ if (playback)
+ ac_desc->baInterfaceNr[ba_iface_id++] = uac1->as_in_intf;
- /* wTotalLength and baInterfaceNr will be defined later */
+ /* wTotalLength will be defined later */
return ac_desc;
}
+/* 4.5.1 Standard AS Interface Descriptor */
+static void init_as_interface_desc(struct usb_interface_descriptor *iface_desc,
+ u8 ifnum, u8 alt, u8 endpoints, const char *name,
+ struct usb_string *strings)
+{
+ iface_desc->bLength = USB_DT_INTERFACE_SIZE;
+ iface_desc->bDescriptorType = USB_DT_INTERFACE;
+ iface_desc->bInterfaceNumber = ifnum;
+ iface_desc->bAlternateSetting = alt;
+ iface_desc->bNumEndpoints = endpoints;
+ iface_desc->bInterfaceClass = USB_CLASS_AUDIO;
+ iface_desc->bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING;
+ iface_desc->bInterfaceProtocol = 0;
+ iface_desc->iInterface = add_string(strings, name);
+}
+
+/* 4.5.2 Class-Specific AS Interface Descriptor */
+static void init_as_header_desc(struct uac1_as_header_descriptor *as_header_desc, int terminalId)
+{
+ as_header_desc->bLength = UAC_DT_AS_HEADER_SIZE;
+ as_header_desc->bDescriptorType = USB_DT_CS_INTERFACE;
+ as_header_desc->bDescriptorSubtype = UAC_AS_GENERAL;
+ as_header_desc->bTerminalLink = terminalId;
+ as_header_desc->bDelay = 1;
+ as_header_desc->wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM);
+}
+
+/* 4.5.3 Class-Specific AS Format Type Descriptor */
+static void init_uac_format_type_i_discrete_desc(struct f_uac1_alt_opts *alt_opts)
+{
+ int idx, i;
+
+ alt_opts->fmt_desc.bDescriptorType = USB_DT_CS_INTERFACE;
+ alt_opts->fmt_desc.bDescriptorSubtype = UAC_FORMAT_TYPE;
+ alt_opts->fmt_desc.bFormatType = UAC_FORMAT_TYPE_I;
+ alt_opts->fmt_desc.bNrChannels = num_channels(alt_opts->chmask);
+ alt_opts->fmt_desc.bSubframeSize = alt_opts->ssize;
+ alt_opts->fmt_desc.bBitResolution = alt_opts->ssize * 8;
+
+ /* Set sample rates */
+ for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
+ if (alt_opts->srates[i] == 0)
+ break;
+ memcpy(alt_opts->fmt_desc.tSamFreq[idx++],
+ &alt_opts->srates[i], 3);
+ }
+ alt_opts->fmt_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
+ alt_opts->fmt_desc.bSamFreqType = idx;
+}
+
+static int init_isoc_ep_descriptor(struct device *dev, struct usb_endpoint_descriptor *ep_desc,
+ struct f_uac1_alt_opts *alt_opts, int dir,
+ enum usb_device_speed speed, u8 addr)
+{
+ ep_desc->bLength = USB_DT_ENDPOINT_AUDIO_SIZE;
+ ep_desc->bDescriptorType = USB_DT_ENDPOINT;
+ ep_desc->bEndpointAddress = addr;
+ ep_desc->bmAttributes = USB_ENDPOINT_XFER_ISOC |
+ (((dir == HOST_TO_DEVICE) && !EPOUT_FBACK_IN_EN(alt_opts))
+ ? USB_ENDPOINT_SYNC_ADAPTIVE
+ : USB_ENDPOINT_SYNC_ASYNC);
+ ep_desc->bInterval = 1; /* For FS. For HS/SS, this is set later from hs_bint. */
+ ep_desc->bRefresh = 0;
+ ep_desc->bSynchAddress = EPOUT_FBACK_IN_EN(alt_opts)
+ ? fs_as_in_fback_desc.bEndpointAddress : 0;
+
+ return set_ep_max_packet_size_bint(dev, alt_opts, ep_desc, speed, (dir == DEVICE_TO_HOST));
+}
+
+static void init_isoc_ep_descriptor_comp(struct usb_ss_ep_comp_descriptor *ep_desc_comp,
+ struct usb_endpoint_descriptor *ep_desc)
+{
+ ep_desc_comp->bLength = sizeof(*ep_desc_comp),
+ ep_desc_comp->bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ ep_desc_comp->bMaxBurst = 0,
+ ep_desc_comp->bmAttributes = 0,
+ ep_desc_comp->wBytesPerInterval = ep_desc->wMaxPacketSize;
+}
+
+static int init_alt_descriptors(struct device *dev, struct f_uac1_alt_opts *alt_opts, int ifnum,
+ u8 epaddr, int endpoints, int terminalID, int dir,
+ struct usb_string *strings)
+{
+ int status = 0;
+
+ init_as_header_desc(&alt_opts->as_header_desc, terminalID);
+ init_as_interface_desc(&alt_opts->intf_desc, ifnum, alt_opts->c.alt_num, endpoints,
+ alt_opts->name, strings);
+ init_uac_format_type_i_discrete_desc(alt_opts);
+
+ status = init_isoc_ep_descriptor(dev, &alt_opts->fs_iso_ep_desc, alt_opts, dir,
+ USB_SPEED_FULL, epaddr);
+ if (!status)
+ status = init_isoc_ep_descriptor(dev, &alt_opts->hs_iso_ep_desc, alt_opts, dir,
+ USB_SPEED_HIGH, epaddr);
+ if (!status)
+ status = init_isoc_ep_descriptor(dev, &alt_opts->ss_iso_ep_desc, alt_opts, dir,
+ USB_SPEED_SUPER, epaddr);
+
+ init_isoc_ep_descriptor_comp(&alt_opts->ss_iso_ep_desc_comp, &alt_opts->ss_iso_ep_desc);
+
+ return status;
+}
+
+static struct f_uac1_path_descriptors *
+build_path_descriptors(struct path_params *params, struct f_uac1_alt_opts *alt_opts)
+{
+ struct f_uac1_path_descriptors *path_descs;
+ u8 srcId;
+
+ path_descs = kzalloc(sizeof(*path_descs), GFP_KERNEL);
+ if (!path_descs)
+ return NULL;
+
+ path_descs->dir = params->dir;
+ path_descs->alt_opts = alt_opts;
+
+ init_it_desc(&path_descs->it_desc, alt_opts, params);
+ srcId = path_descs->it_desc.bTerminalID;
+
+ if (((params->dir == HOST_TO_DEVICE) && FUOUT_EN(params->opts)) ||
+ ((params->dir == DEVICE_TO_HOST) && FUIN_EN(params->opts))) {
+ path_descs->fu_desc = build_fu_desc(alt_opts, params,
+ path_descs->it_desc.bTerminalID);
+ srcId = path_descs->fu_desc->bUnitID;
+ }
+
+ init_ot_desc(&path_descs->ot_desc, alt_opts, params, srcId);
+
+ return path_descs;
+}
+
+static void free_path_descriptors(struct f_uac1_path_descriptors *path_descs)
+{
+ kfree(path_descs->fu_desc);
+ kfree(path_descs);
+}
+
+static struct f_uac1_path_descriptors *find_path_descriptors(struct list_head *list,
+ struct f_uac1_alt_opts *alt_opts,
+ int dir)
+{
+ struct f_uac1_path_descriptors *path_descs;
+
+ list_for_each_entry(path_descs, list, list) {
+ /* Check that all options used in the path descriptors are the same */
+ if ((path_descs->dir == dir) &&
+ (!strncmp(path_descs->alt_opts->name, alt_opts->name,
+ sizeof(alt_opts->name))) &&
+ (!strncmp(path_descs->alt_opts->it_name, alt_opts->it_name,
+ sizeof(alt_opts->it_name))) &&
+ (!strncmp(path_descs->alt_opts->it_ch_name, alt_opts->it_ch_name,
+ sizeof(alt_opts->it_ch_name))) &&
+ (!strncmp(path_descs->alt_opts->ot_name, alt_opts->ot_name,
+ sizeof(alt_opts->ot_name))) &&
+ (path_descs->alt_opts->chmask == alt_opts->chmask) &&
+ (path_descs->alt_opts->terminal_type == alt_opts->terminal_type))
+ return path_descs;
+ }
+ return NULL;
+}
+
+static int add_path_descriptors(struct list_head *list,
+ struct path_params *params,
+ struct f_uac1_alt_opts *alt_opts)
+{
+ int len = 0;
+ struct f_uac1_path_descriptors *path_descs;
+
+ if (!EP_EN(alt_opts))
+ return 0;
+
+ path_descs = find_path_descriptors(list, alt_opts, params->dir);
+
+ if (!path_descs) {
+ path_descs = build_path_descriptors(params, alt_opts);
+ if (path_descs) {
+ list_add_tail(&path_descs->list, list);
+ len += sizeof(path_descs->it_desc);
+ len += sizeof(path_descs->ot_desc);
+ if (path_descs->fu_desc)
+ len += path_descs->fu_desc->bLength;
+ }
+ }
+
+ if (path_descs) {
+ alt_opts->as_header_desc.bTerminalLink =
+ (params->dir == HOST_TO_DEVICE) ? path_descs->it_desc.bTerminalID
+ : path_descs->ot_desc.bTerminalID;
+ alt_opts->it_id = path_descs->it_desc.bTerminalID;
+ alt_opts->fu_id = path_descs->fu_desc ? path_descs->fu_desc->bUnitID : 0;
+ alt_opts->ot_id = path_descs->ot_desc.bTerminalID;
+ }
+
+ return len;
+}
+
/* 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);
+static int setup_headers(struct usb_descriptor_header **desc_list,
+ struct f_uac1 *uac1,
+ struct f_uac1_opts *opts,
+ struct list_head *path_descs,
+ enum usb_device_speed speed);
-static void setup_descriptor(struct f_uac1_opts *opts)
+static int setup_descriptor(struct device *dev, struct f_uac1 *uac1, struct f_uac1_opts *opts,
+ struct usb_string *strings)
{
+ int status;
+ struct usb_descriptor_header **fs_desc_list, **hs_desc_list, **ss_ssp_desc_list;
+
/* patch descriptors */
- int i = 1; /* ID's start with 1 */
-
- if (EPOUT_EN(opts))
- usb_out_it_desc.bTerminalID = i++;
- if (EPIN_EN(opts))
- io_in_it_desc.bTerminalID = i++;
- if (EPOUT_EN(opts))
- io_out_ot_desc.bTerminalID = i++;
- if (EPIN_EN(opts))
- usb_in_ot_desc.bTerminalID = i++;
- if (FUOUT_EN(opts))
- out_feature_unit_desc->bUnitID = i++;
- if (FUIN_EN(opts))
- in_feature_unit_desc->bUnitID = i++;
-
- if (FUIN_EN(opts)) {
- usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID;
- in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID;
- } else {
- usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID;
- }
- if (FUOUT_EN(opts)) {
- io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID;
- out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID;
- } else {
- io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID;
+ int len;
+ struct list_head path_descs = LIST_HEAD_INIT(path_descs);
+ int fs_num, hs_num, ss_ssp_num;
+ struct f_uac1_alt_opts *alt_opts;
+ struct list_head *path_desc, *tmp;
+ struct path_params params;
+
+ params.id = 1; /* ID's start with 1 */
+ params.opts = opts;
+ params.strings = strings;
+
+ ac_header_desc = build_ac_header_desc(uac1, opts);
+ if (!ac_header_desc)
+ return -ENOMEM;
+
+ len = ac_header_desc->bLength;
+
+ if (uac1->g_audio.out_ep) {
+ params.dir = HOST_TO_DEVICE;
+ init_as_interface_desc(&opts->c_alt_0_opts.intf_desc, uac1->as_out_intf, 0, 0,
+ opts->c_alt_0_opts.name, strings);
+
+ /* Audio path descriptors (input terminal -> <feature unit> -> output terminal) */
+ len += add_path_descriptors(&path_descs, ¶ms, &opts->c_alt_1_opts);
+
+ status = init_alt_descriptors(dev, &opts->c_alt_1_opts, uac1->as_out_intf,
+ uac1->g_audio.out_ep->address,
+ EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts) ? 2 : 1,
+ opts->c_alt_1_opts.it_id, HOST_TO_DEVICE, strings);
+ if (status) {
+ dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n",
+ 1, status);
+ goto cleanup;
+ }
+
+ list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+ len += add_path_descriptors(&path_descs, ¶ms, alt_opts);
+
+ status = init_alt_descriptors(dev, alt_opts, uac1->as_out_intf,
+ uac1->g_audio.out_ep->address,
+ EPOUT_FBACK_IN_EN(alt_opts) ? 2 : 1,
+ alt_opts->it_id, HOST_TO_DEVICE, strings);
+ if (status) {
+ dev_err(dev, "Failed to init alt descs for capture alt %d (%d)\n",
+ alt_opts->c.alt_num, status);
+ goto cleanup;
+ }
+ }
}
- as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID;
- as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
+ if (uac1->g_audio.in_ep) {
+ params.dir = DEVICE_TO_HOST;
+ init_as_interface_desc(&opts->p_alt_0_opts.intf_desc, uac1->as_in_intf, 0, 0,
+ opts->p_alt_0_opts.name, strings);
- io_in_it_desc.wTerminalType = cpu_to_le16(opts->c_terminal_type);
- io_out_ot_desc.wTerminalType = cpu_to_le16(opts->p_terminal_type);
- ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength);
+ /* Audio path descriptors (input terminal -> <feature unit> -> output terminal) */
+ len += add_path_descriptors(&path_descs, ¶ms, &opts->p_alt_1_opts);
- if (EPIN_EN(opts)) {
- u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
+ status = init_alt_descriptors(dev, &opts->p_alt_1_opts, uac1->as_in_intf,
+ uac1->g_audio.in_ep->address, 1,
+ opts->p_alt_1_opts.ot_id, DEVICE_TO_HOST, strings);
+ if (status) {
+ dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n",
+ 1, status);
+ goto cleanup;
+ }
- len += sizeof(usb_in_ot_desc);
- len += sizeof(io_in_it_desc);
- if (FUIN_EN(opts))
- len += in_feature_unit_desc->bLength;
- ac_header_desc->wTotalLength = cpu_to_le16(len);
+ list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+ len += add_path_descriptors(&path_descs, ¶ms, alt_opts);
+
+ status = init_alt_descriptors(dev, alt_opts, uac1->as_in_intf,
+ uac1->g_audio.in_ep->address, 1,
+ alt_opts->ot_id, DEVICE_TO_HOST, strings);
+ if (status) {
+ dev_err(dev, "Failed to init alt descs for playback alt %d (%d)\n",
+ alt_opts->c.alt_num, status);
+ goto cleanup;
+ }
+ }
}
- if (EPOUT_EN(opts)) {
- u16 len = le16_to_cpu(ac_header_desc->wTotalLength);
- len += sizeof(usb_out_it_desc);
- len += sizeof(io_out_ot_desc);
- if (FUOUT_EN(opts))
- len += out_feature_unit_desc->bLength;
- ac_header_desc->wTotalLength = cpu_to_le16(len);
+ ac_header_desc->wTotalLength = cpu_to_le16(len);
+
+ /* Count how many descriptors we have and then allocate and populate */
+ fs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_FULL);
+ hs_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_HIGH);
+ ss_ssp_num = setup_headers(NULL, uac1, opts, &path_descs, USB_SPEED_SUPER);
+
+ fs_desc_list = kzalloc((fs_num + hs_num + ss_ssp_num) * sizeof(*fs_desc_list), GFP_KERNEL);
+ if (!fs_desc_list) {
+ status = -ENOMEM;
+ goto cleanup;
}
+ hs_desc_list = fs_desc_list + fs_num;
+ ss_ssp_desc_list = hs_desc_list + hs_num;
- setup_headers(opts, f_audio_fs_desc, USB_SPEED_FULL);
- setup_headers(opts, f_audio_hs_desc, USB_SPEED_HIGH);
- setup_headers(opts, f_audio_ss_desc, USB_SPEED_SUPER);
+ (void) setup_headers(fs_desc_list, uac1, opts, &path_descs, USB_SPEED_FULL);
+ (void) setup_headers(hs_desc_list, uac1, opts, &path_descs, USB_SPEED_HIGH);
+ (void) setup_headers(ss_ssp_desc_list, uac1, opts, &path_descs, USB_SPEED_SUPER);
+
+ /* copy descriptors, and track endpoint copies */
+ status = usb_assign_descriptors(&uac1->g_audio.func, fs_desc_list, hs_desc_list,
+ ss_ssp_desc_list, ss_ssp_desc_list);
+
+ if (status)
+ dev_err(dev, "Failed to assign descriptors (%d)\n", status);
+
+ kfree(fs_desc_list);
+
+cleanup:
+ list_for_each_safe(path_desc, tmp, &path_descs) {
+ free_path_descriptors(
+ container_of(path_desc, struct f_uac1_path_descriptors, list));
+ }
+ kfree(ac_header_desc);
+ ac_header_desc = NULL;
+
+ return status;
}
-static void setup_headers(struct f_uac1_opts *opts,
- struct usb_descriptor_header **headers,
- enum usb_device_speed speed)
+static inline void add_descriptor(int i, struct usb_descriptor_header **desc_list,
+ struct usb_descriptor_header *desc)
+{
+ if (desc_list)
+ desc_list[i] = desc;
+}
+
+static int add_alt_descriptors(int i, struct usb_descriptor_header **desc_list,
+ struct f_uac1_alt_opts *alt_opts, enum usb_device_speed speed)
+{
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->intf_desc));
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->as_header_desc));
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fmt_desc));
+ if (speed == USB_SPEED_FULL)
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->fs_iso_ep_desc));
+ else if (speed == USB_SPEED_HIGH)
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->hs_iso_ep_desc));
+ else if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc));
+ add_descriptor(i++, desc_list, USBDHDR(&alt_opts->ss_iso_ep_desc_comp));
+ }
+
+ return i;
+}
+
+static int setup_headers(struct usb_descriptor_header **desc_list,
+ struct f_uac1 *uac1,
+ struct f_uac1_opts *opts,
+ struct list_head *path_descs,
+ 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 *epin_fback_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 *epin_fback_desc;
struct usb_endpoint_descriptor *ep_int_desc;
- int i;
+
+ int i = 0;
+ struct list_head *pos;
switch (speed) {
case USB_SPEED_FULL:
- epout_desc = &fs_as_out_ep_desc;
- epin_desc = &fs_as_in_ep_desc;
epin_fback_desc = &fs_as_in_fback_desc;
ep_int_desc = &fs_ac_int_ep_desc;
break;
case USB_SPEED_HIGH:
- epout_desc = &hs_as_out_ep_desc;
- epin_desc = &hs_as_in_ep_desc;
epin_fback_desc = &hs_as_in_fback_desc;
ep_int_desc = &hs_ac_int_ep_desc;
break;
default:
- epout_desc = &ss_as_out_ep_desc;
- epin_desc = &ss_as_in_ep_desc;
- epout_desc_comp = &ss_as_out_ep_desc_comp;
- epin_desc_comp = &ss_as_in_ep_desc_comp;
epin_fback_desc = &ss_as_in_fback_desc;
epin_fback_desc_comp = &ss_as_in_fback_desc_comp;
ep_int_desc = &ss_ac_int_ep_desc;
ep_int_desc_comp = &ss_ac_int_ep_desc_comp;
}
- i = 0;
- headers[i++] = USBDHDR(&ac_interface_desc);
- headers[i++] = USBDHDR(ac_header_desc);
+ add_descriptor(i++, desc_list, USBDHDR(&ac_interface_desc));
+ add_descriptor(i++, desc_list, 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);
+ list_for_each(pos, path_descs) {
+ struct f_uac1_path_descriptors *path_desc =
+ container_of(pos, struct f_uac1_path_descriptors, list);
+ add_descriptor(i++, desc_list, USBDHDR(&path_desc->it_desc));
+ add_descriptor(i++, desc_list, USBDHDR(&path_desc->ot_desc));
+ if (path_desc->fu_desc)
+ add_descriptor(i++, desc_list, USBDHDR(path_desc->fu_desc));
}
+ // If any FU exists, add the interrupt endpoint descriptor
if (FUOUT_EN(opts) || FUIN_EN(opts)) {
- headers[i++] = USBDHDR(ep_int_desc);
+ add_descriptor(i++, desc_list, USBDHDR(ep_int_desc));
if (ep_int_desc_comp)
- headers[i++] = USBDHDR(ep_int_desc_comp);
+ add_descriptor(i++, desc_list, 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);
+ // If any capture interface is active
+ if (epout_en_any(opts)) {
+ struct f_uac1_alt_opts *alt_opts;
+
+ add_descriptor(i++, desc_list, USBDHDR(&opts->c_alt_0_opts.intf_desc));
- headers[i++] = USBDHDR(&as_iso_out_desc);
+ if (EP_EN(&opts->c_alt_1_opts)) {
+ i = add_alt_descriptors(i, desc_list, &opts->c_alt_1_opts, speed);
- if (EPOUT_FBACK_IN_EN(opts)) {
- headers[i++] = USBDHDR(epin_fback_desc);
- if (epin_fback_desc_comp)
- headers[i++] = USBDHDR(epin_fback_desc_comp);
+ add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc));
+ if (EPOUT_FBACK_IN_EN(&opts->c_alt_1_opts)) {
+ add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc));
+ if (epin_fback_desc_comp)
+ add_descriptor(i++, desc_list,
+ USBDHDR(epin_fback_desc_comp));
+ }
+ }
+
+ list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+ if (EP_EN(alt_opts)) {
+ i = add_alt_descriptors(i, desc_list, alt_opts, speed);
+
+ add_descriptor(i++, desc_list, USBDHDR(&as_iso_out_desc));
+ if (EPOUT_FBACK_IN_EN(alt_opts)) {
+ add_descriptor(i++, desc_list, USBDHDR(epin_fback_desc));
+ if (epin_fback_desc_comp)
+ add_descriptor(i++, desc_list,
+ USBDHDR(epin_fback_desc_comp));
+ }
+ }
}
}
- 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);
+ // If any playback interface is active
+ if (epin_en_any(opts)) {
+ struct f_uac1_alt_opts *alt_opts;
+
+ add_descriptor(i++, desc_list, USBDHDR(&opts->p_alt_0_opts.intf_desc));
+
+ if (EP_EN(&opts->p_alt_1_opts)) {
+ i = add_alt_descriptors(i, desc_list, &opts->p_alt_1_opts, speed);
+
+ add_descriptor(i++, desc_list, USBDHDR(&as_iso_in_desc));
+ }
+
+ list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+ if (EP_EN(alt_opts)) {
+ i = add_alt_descriptors(i, desc_list, alt_opts, speed);
+
+ add_descriptor(i++, desc_list, USBDHDR(&as_iso_in_desc));
+ }
+ }
}
- headers[i] = NULL;
+
+ add_descriptor(i++, desc_list, NULL);
+
+ return i;
}
static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
{
struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio);
+ struct f_uac1_alt_opts *alt_opts;
- if (!opts->p_chmask && !opts->c_chmask) {
+ if (!epin_en_any(opts) && !epout_en_any(opts)) {
dev_err(dev, "Error: no playback and capture channels\n");
return -EINVAL;
- } else if (opts->p_chmask & ~UAC1_CHANNEL_MASK) {
- dev_err(dev, "Error: unsupported playback channels mask\n");
- return -EINVAL;
- } else if (opts->c_chmask & ~UAC1_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_srates[0]) {
+ }
+
+ list_for_each_entry(alt_opts, &opts->p_alt_opts, list) {
+ if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) {
+ dev_err(dev, "Error: unsupported playback channels mask for alt %d\n",
+ alt_opts->c.alt_num);
+ return -EINVAL;
+ } else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) {
+ dev_err(dev, "Error: incorrect playback sample size for alt %d\n",
+ alt_opts->c.alt_num);
+ return -EINVAL;
+ } else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) {
+ dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n",
+ alt_opts->c.alt_num);
+
+ return -EINVAL;
+ }
+ }
+
+ list_for_each_entry(alt_opts, &opts->c_alt_opts, list) {
+ if (alt_opts->chmask & ~UAC1_CHANNEL_MASK) {
+ dev_err(dev, "Error: unsupported capture channels mask for alt %d\n",
+ alt_opts->c.alt_num);
+ return -EINVAL;
+ } else if ((alt_opts->ssize < 1) || (alt_opts->ssize > 4)) {
+ dev_err(dev, "Error: incorrect capture sample size for alt %d\n",
+ alt_opts->c.alt_num);
+ return -EINVAL;
+ } else if ((alt_opts->hs_bint < 0) || (alt_opts->hs_bint > 4)) {
+ dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto) for alt %d\n",
+ alt_opts->c.alt_num);
+
+ return -EINVAL;
+ }
+ }
+
+ if (!opts->p_srates[0]) {
dev_err(dev, "Error: incorrect playback sampling rate\n");
return -EINVAL;
} else if (!opts->c_srates[0]) {
@@ -1493,15 +1605,6 @@ static int f_audio_validate_opts(struct g_audio *audio, struct device *dev)
return -EINVAL;
}
- if ((opts->p_hs_bint < 0) || (opts->p_hs_bint > 4)) {
- dev_err(dev, "Error: incorrect playback HS/SS bInterval (1-4: fixed, 0: auto)\n");
- return -EINVAL;
- }
- if ((opts->c_hs_bint < 0) || (opts->c_hs_bint > 4)) {
- dev_err(dev, "Error: incorrect capture HS/SS bInterval (1-4: fixed, 0: auto)\n");
- return -EINVAL;
- }
-
return 0;
}
@@ -1551,6 +1654,26 @@ static void init_alt_opts(struct f_uac1_alt_opts *alt_opts, struct f_uac1_opts *
alt_opts->terminal_type = (playback) ? opts->p_terminal_type : opts->c_terminal_type;
}
+static u16 get_max_packet_size(struct f_uac1_alt_opts *alt_opts, struct list_head *list)
+{
+ u16 max_psize = max_t(u16,
+ le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize),
+ le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize));
+ max_psize = max_t(u16, max_psize,
+ le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize));
+
+ list_for_each_entry(alt_opts, list, list) {
+ max_psize = max_t(u16, max_psize,
+ le16_to_cpu(alt_opts->fs_iso_ep_desc.wMaxPacketSize));
+ max_psize = max_t(u16, max_psize,
+ le16_to_cpu(alt_opts->hs_iso_ep_desc.wMaxPacketSize));
+ max_psize = max_t(u16, max_psize,
+ le16_to_cpu(alt_opts->ss_iso_ep_desc.wMaxPacketSize));
+ }
+
+ return max_psize;
+}
+
/* audio function driver setup/binding */
static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
{
@@ -1562,9 +1685,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
struct f_uac1_opts *audio_opts;
struct usb_ep *ep = NULL;
struct usb_string *us;
- int ba_iface_id;
int status;
- int idx, i;
+ struct list_head strings = LIST_HEAD_INIT(strings);
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
@@ -1588,214 +1710,41 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
* be used from their alt mode opts.
*/
- us = attach_strings(cdev, audio_opts);
- 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 = add_string(us, audio_opts->function_name);
- usb_out_it_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.it_name);
- usb_out_it_desc.iChannelNames = add_string(us, audio_opts->c_alt_1_opts.it_ch_name);
- io_out_ot_desc.iTerminal = add_string(us, audio_opts->c_alt_1_opts.ot_name);
- as_out_interface_alt_0_desc.iInterface = add_string(us, audio_opts->c_alt_0_opts.name);
- as_out_interface_alt_1_desc.iInterface = add_string(us, audio_opts->c_alt_1_opts.name);
- io_in_it_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.it_name);
- io_in_it_desc.iChannelNames = add_string(us, audio_opts->p_alt_1_opts.it_ch_name);
- usb_in_ot_desc.iTerminal = add_string(us, audio_opts->p_alt_1_opts.ot_name);
- as_in_interface_alt_0_desc.iInterface = add_string(us, audio_opts->p_alt_0_opts.name);
- as_in_interface_alt_1_desc.iInterface = add_string(us, audio_opts->p_alt_1_opts.name);
-
- if (FUOUT_EN(audio_opts)) {
- u8 *i_feature;
-
- i_feature = (u8 *)out_feature_unit_desc +
- out_feature_unit_desc->bLength - 1;
- *i_feature = add_string(us, audio_opts->c_alt_1_opts.fu_vol_name);
- }
- if (FUIN_EN(audio_opts)) {
- u8 *i_feature;
-
- i_feature = (u8 *)in_feature_unit_desc +
- in_feature_unit_desc->bLength - 1;
- *i_feature = add_string(us, audio_opts->p_alt_1_opts.fu_vol_name);
- }
-
us = attach_strings(cdev, audio_opts);
if (IS_ERR(us)) {
status = PTR_ERR(us);
goto fail;
}
- /* 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 */
- for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
- if (audio_opts->c_srates[i] == 0)
- break;
- memcpy(as_out_type_i_desc.tSamFreq[idx++],
- &audio_opts->c_srates[i], 3);
- }
- as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
- as_out_type_i_desc.bSamFreqType = idx;
+ ac_interface_desc.iInterface = add_string(us, audio_opts->function_name);
- for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) {
- if (audio_opts->p_srates[i] == 0)
- break;
- memcpy(as_in_type_i_desc.tSamFreq[idx++],
- &audio_opts->p_srates[i], 3);
- }
- as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx);
- as_in_type_i_desc.bSamFreqType = idx;
uac1->p_srate = audio_opts->p_srates[0];
uac1->c_srate = audio_opts->c_srates[0];
- /* allocate instance-specific interface IDs, and patch descriptors */
+ /* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
- goto err_free_fu;
+ goto fail;
ac_interface_desc.bInterfaceNumber = status;
- ac_interface_desc.bNumEndpoints = 1;
uac1->ac_intf = status;
uac1->ac_alt = 0;
- ba_iface_id = 0;
-
- if (EPOUT_EN(audio_opts)) {
+ if (epout_en_any(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;
+ goto fail;
uac1->as_out_intf = status;
uac1->as_out_alt = 0;
-
- if (EPOUT_FBACK_IN_EN(audio_opts)) {
- fs_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
- hs_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
- ss_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC;
- ac_interface_desc.bNumEndpoints++;
- } else {
- fs_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
- hs_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
- ss_as_out_ep_desc.bmAttributes =
- USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE;
- }
}
- if (EPIN_EN(audio_opts)) {
+ if (epin_en_any(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;
+ goto fail;
uac1->as_in_intf = status;
uac1->as_in_alt = 0;
}
- hs_as_in_ep_desc.bInterval = audio_opts->p_hs_bint;
- ss_as_in_ep_desc.bInterval = audio_opts->p_hs_bint;
- hs_as_out_ep_desc.bInterval = audio_opts->c_hs_bint;
- ss_as_out_ep_desc.bInterval = audio_opts->c_hs_bint;
-
- /* Calculate wMaxPacketSize according to audio bandwidth */
- status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &fs_as_in_ep_desc,
- USB_SPEED_FULL, true);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
- status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &fs_as_out_ep_desc,
- USB_SPEED_FULL, false);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
- status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &hs_as_in_ep_desc,
- USB_SPEED_HIGH, true);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
- status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc,
- USB_SPEED_HIGH, false);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
- status = set_ep_max_packet_size_bint(dev, &audio_opts->p_alt_1_opts, &ss_as_in_ep_desc,
- USB_SPEED_SUPER, true);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
- status = set_ep_max_packet_size_bint(dev, &audio_opts->c_alt_1_opts, &hs_as_out_ep_desc,
- USB_SPEED_SUPER, false);
- if (status < 0) {
- dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
- goto err_free_fu;
- }
-
audio->gadget = gadget;
status = -ENODEV;
@@ -1805,36 +1754,49 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
/* allocate AC interrupt endpoint */
if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &fs_ac_int_ep_desc);
- if (!ep)
- goto err_free_fu;
-
+ if (!ep) {
+ status = -EINVAL;
+ dev_err(dev, "Failed to allocate interrupt endpoint\n");
+ goto fail;
+ }
hs_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress;
ss_ac_int_ep_desc.bEndpointAddress = fs_ac_int_ep_desc.bEndpointAddress;
uac1->int_ep = ep;
- uac1->int_ep->desc = &fs_ac_int_ep_desc;
ac_interface_desc.bNumEndpoints = 1;
}
- /* allocate instance-specific endpoints */
- if (EPOUT_EN(audio_opts)) {
- ep = usb_ep_autoconfig(cdev->gadget, &fs_as_out_ep_desc);
- if (!ep)
- goto err_free_fu;
-
- hs_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress;
- ss_as_out_ep_desc.bEndpointAddress = fs_as_out_ep_desc.bEndpointAddress;
- ss_as_out_ep_desc_comp.wBytesPerInterval = ss_as_out_ep_desc.wMaxPacketSize;
+ /* Allocate instance-specific endpoints. These use the FS version for alt mode 1.
+ * All other alt modes and speeds will be initialized to the same endpoint address
+ * during the setup_descriptor() call. The u_audio code will update the currently
+ * selected endpoint descriptor when the alt mode changes.
+ */
+ if (epout_en_any(audio_opts)) {
+ status = init_isoc_ep_descriptor(dev, &audio_opts->c_alt_1_opts.fs_iso_ep_desc,
+ &audio_opts->c_alt_1_opts, HOST_TO_DEVICE,
+ USB_SPEED_FULL, USB_DIR_OUT);
+ if (status) {
+ dev_err(dev, "Failed to init FS isoc ep descriptor for capture (%d)\n",
+ status);
+ goto fail;
+ }
+ ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->c_alt_1_opts.fs_iso_ep_desc);
+ if (!ep) {
+ status = -EINVAL;
+ dev_err(dev, "Failed to allocate isoc endpoint for capture\n");
+ goto fail;
+ }
audio->out_ep = ep;
- audio->out_ep->desc = &fs_as_out_ep_desc;
- if (EPOUT_FBACK_IN_EN(audio_opts)) {
+ if (epout_fback_in_en_any(audio_opts)) {
ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_fback_desc);
- if (!ep)
- goto err_free_fu;
-
+ if (!ep) {
+ status = -EINVAL;
+ dev_err(dev, "Failed to allocate feedback endpoint for capture\n");
+ goto fail;
+ }
hs_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
ss_as_in_fback_desc.bEndpointAddress = fs_as_in_fback_desc.bEndpointAddress;
@@ -1842,45 +1804,44 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
}
}
- if (EPIN_EN(audio_opts)) {
- ep = usb_ep_autoconfig(cdev->gadget, &fs_as_in_ep_desc);
- if (!ep)
- goto err_free_fu;
-
- hs_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress;
- ss_as_in_ep_desc.bEndpointAddress = fs_as_in_ep_desc.bEndpointAddress;
- ss_as_in_ep_desc_comp.wBytesPerInterval = ss_as_in_ep_desc.wMaxPacketSize;
+ if (epin_en_any(audio_opts)) {
+ status = init_isoc_ep_descriptor(dev, &audio_opts->p_alt_1_opts.fs_iso_ep_desc,
+ &audio_opts->p_alt_1_opts, HOST_TO_DEVICE,
+ USB_SPEED_FULL, USB_DIR_IN);
+ if (status) {
+ dev_err(dev, "Failed to init FS isoc ep descriptor for playback (%d)\n",
+ status);
+ goto fail;
+ }
+ ep = usb_ep_autoconfig(cdev->gadget, &audio_opts->p_alt_1_opts.fs_iso_ep_desc);
+ if (!ep) {
+ status = -EINVAL;
+ dev_err(dev, "Failed to allocate isoc endpoint for playback\n");
+ goto fail;
+ }
audio->in_ep = ep;
- audio->in_ep->desc = &fs_as_in_ep_desc;
}
- setup_descriptor(audio_opts);
+ status = setup_descriptor(dev, uac1, audio_opts, us);
- /* copy descriptors, and track endpoint copies */
- status = usb_assign_descriptors(f, f_audio_fs_desc, f_audio_hs_desc, f_audio_ss_desc,
- f_audio_ss_desc);
if (status)
- goto err_free_fu;
-
- audio->in_ep_maxpsize = max_t(u16,
- le16_to_cpu(fs_as_in_ep_desc.wMaxPacketSize),
- le16_to_cpu(hs_as_in_ep_desc.wMaxPacketSize));
- audio->out_ep_maxpsize = max_t(u16,
- le16_to_cpu(fs_as_out_ep_desc.wMaxPacketSize),
- le16_to_cpu(hs_as_out_ep_desc.wMaxPacketSize));
+ goto fail;
- audio->in_ep_maxpsize = max_t(u16, audio->in_ep_maxpsize,
- le16_to_cpu(ss_as_in_ep_desc.wMaxPacketSize));
- audio->out_ep_maxpsize = max_t(u16, audio->out_ep_maxpsize,
- le16_to_cpu(ss_as_out_ep_desc.wMaxPacketSize));
+ // Set max packet size for all alt modes. These are used to allocate the buffers in u_audio.
+ audio->out_ep_maxpsize = get_max_packet_size(&audio_opts->c_alt_1_opts,
+ &audio_opts->c_alt_opts);
+ audio->in_ep_maxpsize = get_max_packet_size(&audio_opts->p_alt_1_opts,
+ &audio_opts->p_alt_opts);
+ // TODO: This may need some change with the audio params for the current alt mode
audio->params.c_chmask = audio_opts->c_chmask;
memcpy(audio->params.c_srates, audio_opts->c_srates,
sizeof(audio->params.c_srates));
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.id = USB_IN_FU_ID(audio_opts);
audio->params.p_fu.mute_present = audio_opts->p_mute_present;
audio->params.p_fu.volume_present =
audio_opts->p_volume_present;
@@ -1888,12 +1849,15 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
audio->params.p_fu.volume_max = audio_opts->p_volume_max;
audio->params.p_fu.volume_res = audio_opts->p_volume_res;
}
+
+ // TODO: This may need some change with the audio params for the current alt mode
audio->params.p_chmask = audio_opts->p_chmask;
memcpy(audio->params.p_srates, audio_opts->p_srates,
sizeof(audio->params.p_srates));
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.id = USB_OUT_FU_ID(audio_opts);
audio->params.c_fu.mute_present = audio_opts->c_mute_present;
audio->params.c_fu.volume_present =
audio_opts->c_volume_present;
@@ -1914,14 +1878,7 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f)
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;
}
@@ -2342,11 +2299,6 @@ static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
g_audio_cleanup(audio);
usb_free_all_descriptors(f);
- kfree(out_feature_unit_desc);
- out_feature_unit_desc = NULL;
- kfree(in_feature_unit_desc);
- in_feature_unit_desc = NULL;
-
kfree(ac_header_desc);
ac_header_desc = NULL;
@@ -9,6 +9,7 @@
#define __U_UAC1_H
#include <linux/usb/composite.h>
+#include <linux/usb/audio.h>
#include "uac_common.h"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
@@ -47,8 +48,15 @@ struct f_uac1_alt_0_opts {
struct f_uac1_alt_opts_common c;
char name[USB_MAX_STRING_LEN];
+
+ /* Descriptors */
+ struct usb_interface_descriptor intf_desc;
};
+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
+
/* Alt modes 1+ */
struct f_uac1_alt_opts {
struct f_uac1_alt_opts_common c;
@@ -70,6 +78,36 @@ struct f_uac1_alt_opts {
u8 hs_bint;
s16 terminal_type;
+ /* Descriptors */
+ struct usb_interface_descriptor intf_desc;
+ struct uac1_as_header_descriptor as_header_desc;
+ struct uac_format_type_i_discrete_descriptor fmt_desc;
+
+ struct usb_endpoint_descriptor fs_iso_ep_desc;
+ struct usb_endpoint_descriptor hs_iso_ep_desc;
+ struct usb_endpoint_descriptor ss_iso_ep_desc;
+ struct usb_ss_ep_comp_descriptor ss_iso_ep_desc_comp;
+
+ u8 it_id; /* Input Terminal Descriptor bTerminalID */
+ u8 fu_id; /* Feature Unit Descriptor bUnitID */
+ u8 ot_id; /* Output Terminal Descriptor bTerminalID */
+};
+
+#undef uac_format_type_i_discrete_descriptor
+
+struct f_uac1_path_descriptors {
+ struct list_head list;
+
+ int dir; /* HOST_TO_DEVICE or DEVICE_TO_HOST */
+
+ /* Alt mode opts this path descriptor is from */
+ struct f_uac1_alt_opts *alt_opts;
+
+ struct uac_input_terminal_descriptor it_desc;
+ struct uac1_output_terminal_descriptor ot_desc;
+
+ /* Feature unit is optional */
+ struct uac_feature_unit_descriptor *fu_desc;
};
struct f_uac1_opts {