This patch section contains the changes to f_audio.c to define multi-interface config and the HID processing. Signed-off-by: Robin Callender <robin_callender@xxxxxxxxxxx> --- diff -Naur linux-2.6.31.orig/drivers/usb/gadget/f_audio.c linux-2.6.31.new/drivers/usb/gadget/f_audio.c --- linux-2.6.31.orig/drivers/usb/gadget/f_audio.c 2009-09-12 11:51:48.000000000 -0700 +++ linux-2.6.31.new/drivers/usb/gadget/f_audio.c 2009-09-18 11:11:36.000000000 -0700 @@ -1,9 +1,11 @@ /* * f_audio.c -- USB Audio class function driver - * + * * Copyright (C) 2008 Bryan Wu <cooloney@xxxxxxxxxx> * Copyright (C) 2008 Analog Devices, Inc * + * Copyright (C) 2009 Robin Callender + * * Enter bugs at http://blackfin.uclinux.org/ * * Licensed under the GPL-2 or later. @@ -15,18 +17,32 @@ #include "u_audio.h" -#define OUT_EP_MAX_PACKET_SIZE 200 -static int req_buf_size = OUT_EP_MAX_PACKET_SIZE; -module_param(req_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size"); - -static int req_count = 256; -module_param(req_count, int, S_IRUGO); -MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count"); - -static int audio_buf_size = 48000; -module_param(audio_buf_size, int, S_IRUGO); -MODULE_PARM_DESC(audio_buf_size, "Audio buffer size"); +#define PLAYBACK_EP_MAX_PACKET_SIZE 200 +static int req_playback_buf_size = PLAYBACK_EP_MAX_PACKET_SIZE; +module_param(req_playback_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_playback_buf_size, "ISO OUT endpoint (playback) request buffer size"); + +static int req_playback_count = 256; +module_param(req_playback_count, int, S_IRUGO); +MODULE_PARM_DESC(req_playback_count, "ISO OUT endpoint (playback) request count"); + +static int audio_playback_buf_size = 48000; +module_param(audio_playback_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_playback_buf_size, "Audio buffer size"); + + +#define CAPTURE_EP_MAX_PACKET_SIZE 200 +static int req_capture_buf_size = CAPTURE_EP_MAX_PACKET_SIZE; +module_param(req_capture_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(req_capture_buf_size, "ISO IN endpoint (capture) request buffer size"); + +static int req_capture_count = 256; +module_param(req_capture_count, int, S_IRUGO); +MODULE_PARM_DESC(req_capture_count, "ISO IN endpoint (capture) request count"); + +static int audio_capture_buf_size = 48000; +module_param(audio_capture_buf_size, int, S_IRUGO); +MODULE_PARM_DESC(audio_capture_buf_size, "Microphone Audio buffer size"); static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value); static int generic_get_cmd(struct usb_audio_control *con, u8 cmd); @@ -36,16 +52,105 @@ * configuration descriptors are built on demand. */ +DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(spkr,2); +DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(mic,1); +DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); + +#define FREQ(n,v) [n] = { ((v>>0)&0xFF), ((v>>8)&0xFF), ((v>>16)&0xFF) } + +#define SPEAKER_INPUT_TERMINAL_ID 1 +#define SPEAKER_OUTPUT_TERMINAL_ID 2 +#define SPEAKER_FEATURE_UNIT_ID 3 + +#define MICROPHONE_INPUT_TERMINAL_ID 4 +#define MICROPHONE_OUTPUT_TERMINAL_ID 5 +#define MICROPHONE_FEATURE_UNIT_ID 6 + +static char * control_types(u8 type) +{ + int i; + static struct _control_type { + u8 t; + char * s; + } control_types [] = { + {UAC__CUR, "CUR"}, + {UAC__MIN, "MIN"}, + {UAC__MAX, "MAX"}, + {UAC__RES, "RES"}, + {UAC__MEM, "MEM"}, + }; + for (i=0; i < sizeof(control_types)/sizeof(struct _control_type); i++) + if (control_types[i].t == type) + return control_types[i].s; + return "???"; +} + +/* + * HID report descriptor as required by MS + */ +static u8 hid_report_desc[] = +{ + 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */ + 0x09, 0x01, /* USAGE (Consumer Control) */ + + 0xA1, 0x01, /* COLLECTION (Application) */ + /* + * Standard Audio Device Controls (Inputs to HOST) + * + * Volume Up + * Volume Down + * MUTE Button + */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x09, 0xE9, /* USAGE (Volume Up) */ + 0x09, 0xEA, /* USAGE (Volume Down) */ + 0x75, 0x01, /* REPORT_SIZE (1) */ + 0x95, 0x02, /* REPORT_COUNT (2) */ + + 0x81, 0x02, /* INPUT (Data, Var, Abs) */ + 0x09, 0xE2, /* USAGE (Mute) */ + 0x09, 0x00, /* USAGE (unassigned) */ + + 0x81, 0x06, /* INPUT (Data, Var, Rel) */ + 0x09, 0x00, /* USAGE (unassigned) */ + 0x95, 0x04, /* REPORT_COUNT(4) */ + + 0x81, 0x02, /* INPUT (Data, Var, Abs) */ + 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */ + 0x09, 0x00, /* USAGE (unassigned) */ + 0x75, 0x08, /* REPORT_SIZE (8) */ + 0x95, 0x03, /* REPORT_COUNT(3) */ + + 0x81, 0x02, /* INPUT (Data, Var, Abs) */ + 0x09, 0x00, /* USAGE (unassigned) */ + 0x95, 0x04, /* REPORT_COUNT(4) */ + + 0x91, 0x02, /* OUTPUT (Data, Var, Abs) */ + + 0xC0, /* END_COLLECTION */ +}; + +#define HID_REPORT_SIZE (4) + +struct audio_report { + u8 data [HID_REPORT_SIZE]; +}; + +static struct audio_report hid_audio_report = { + .data = {0,0,0,0}, +}; + /* * We have two interfaces- AudioControl and AudioStreaming - * TODO: only supcard playback currently */ -#define F_AUDIO_AC_INTERFACE 0 -#define F_AUDIO_AS_INTERFACE 1 -#define F_AUDIO_NUM_INTERFACES 2 +#define F_AUDIO_INTERFACE_SPEAKER 1 +#define F_AUDIO_INTERFACE_MICROPHONE 2 +#define F_AUDIO_NUM_INTERFACES 2 /* B.3.1 Standard AC Interface Descriptor */ -static struct usb_interface_descriptor ac_interface_desc __initdata = { +struct usb_interface_descriptor ac_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 0, @@ -53,162 +158,375 @@ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); +#define TOTAL_LENGTH ( \ + UAC_DT_AC_HEADER_SIZE(2) + \ + UAC_DT_INPUT_TERMINAL_SIZE + \ + UAC_FEATURE_UNIT_SIZE(2,1) + \ + UAC_DT_OUTPUT_TERMINAL_SIZE + \ + UAC_DT_INPUT_TERMINAL_SIZE + \ + UAC_FEATURE_UNIT_SIZE(1,1) + \ + UAC_DT_OUTPUT_TERMINAL_SIZE \ + ) -#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct uac_ac_header_descriptor_2 ac_header_desc = { - .bLength = UAC_DT_AC_HEADER_LENGTH, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_HEADER, - .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(UAC_DT_AC_HEADER_LENGTH), - .bInCollection = F_AUDIO_NUM_INTERFACES, - .baInterfaceNr = { - [0] = F_AUDIO_AC_INTERFACE, - [1] = F_AUDIO_AS_INTERFACE, - } +struct uac_ac_header_descriptor_2 ac_header_desc = { + .bLength = UAC_DT_AC_HEADER_SIZE(2), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_HEADER, + .bcdADC = __constant_cpu_to_le16(0x0100), + .wTotalLength = __constant_cpu_to_le16(TOTAL_LENGTH), + .bInCollection = F_AUDIO_NUM_INTERFACES, + .baInterfaceNr = { + [0] = F_AUDIO_INTERFACE_SPEAKER, + [1] = F_AUDIO_INTERFACE_MICROPHONE, + } +}; + +/*---------------------------------*/ + +struct uac_input_terminal_descriptor speaker_input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = SPEAKER_INPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = SPEAKER_OUTPUT_TERMINAL_ID, + .wChannelConfig = 0x3, +}; + +struct uac_output_terminal_descriptor speaker_output_terminal_desc = { + .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, + .bTerminalID = SPEAKER_OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, + .bAssocTerminal = SPEAKER_INPUT_TERMINAL_ID, + .bSourceID = SPEAKER_FEATURE_UNIT_ID, }; -#define INPUT_TERMINAL_ID 1 -static struct uac_input_terminal_descriptor input_terminal_desc = { - .bLength = UAC_DT_INPUT_TERMINAL_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_INPUT_TERMINAL, - .bTerminalID = INPUT_TERMINAL_ID, - .wTerminalType = UAC_TERMINAL_STREAMING, - .bAssocTerminal = 0, - .wChannelConfig = 0x3, -}; - -DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); - -#define FEATURE_UNIT_ID 2 -static struct uac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), +struct uac_feature_unit_descriptor_spkr speaker_feature_unit_desc = { + .bLength = UAC_FEATURE_UNIT_SIZE(2,1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_FEATURE_UNIT, - .bUnitID = FEATURE_UNIT_ID, - .bSourceID = INPUT_TERMINAL_ID, - .bControlSize = 2, - .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), + .bUnitID = SPEAKER_FEATURE_UNIT_ID, + .bSourceID = SPEAKER_INPUT_TERMINAL_ID, + .bControlSize = 1, + .bmaControls[0] = UAC_FU_MUTE, + .bmaControls[1] = UAC_FU_VOLUME, + .bmaControls[2] = UAC_FU_VOLUME, }; -static struct usb_audio_control mute_control = { - .list = LIST_HEAD_INIT(mute_control.list), - .name = "Mute Control", +static struct usb_audio_control speaker_mute_control = { + .list = LIST_HEAD_INIT(speaker_mute_control.list), + .name = "Speaker Mute Control", .type = UAC_MUTE_CONTROL, /* Todo: add real Mute control code */ .set = generic_set_cmd, .get = generic_get_cmd, }; -static struct usb_audio_control volume_control = { - .list = LIST_HEAD_INIT(volume_control.list), - .name = "Volume Control", +static struct usb_audio_control speaker_volume_control = { + .list = LIST_HEAD_INIT(speaker_volume_control.list), + .name = "Speaker Volume Control", .type = UAC_VOLUME_CONTROL, /* Todo: add real Volume control code */ .set = generic_set_cmd, .get = generic_get_cmd, }; -static struct usb_audio_control_selector feature_unit = { - .list = LIST_HEAD_INIT(feature_unit.list), - .id = FEATURE_UNIT_ID, - .name = "Mute & Volume Control", +static struct usb_audio_control_selector speaker_fu_controls = { + .list = LIST_HEAD_INIT(speaker_fu_controls.list), + .id = SPEAKER_FEATURE_UNIT_ID, + .name = "Speaker Function Unit Controls", .type = UAC_FEATURE_UNIT, - .desc = (struct usb_descriptor_header *)&feature_unit_desc, + .desc = (struct usb_descriptor_header *)&speaker_feature_unit_desc, }; -#define OUTPUT_TERMINAL_ID 3 -static struct uac_output_terminal_descriptor output_terminal_desc = { +/*---------------------------------*/ + +static struct uac_input_terminal_descriptor microphone_input_terminal_desc = { + .bLength = UAC_DT_INPUT_TERMINAL_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_INPUT_TERMINAL, + .bTerminalID = MICROPHONE_INPUT_TERMINAL_ID, + .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .bAssocTerminal = MICROPHONE_OUTPUT_TERMINAL_ID, + .bNrChannels = 1, + .wChannelConfig = UAC_CHANNEL_CONFIG_L, +}; + +static struct uac_output_terminal_descriptor microphone_output_terminal_desc = { .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, - .bTerminalID = OUTPUT_TERMINAL_ID, - .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, - .bAssocTerminal = FEATURE_UNIT_ID, - .bSourceID = FEATURE_UNIT_ID, + .bTerminalID = MICROPHONE_OUTPUT_TERMINAL_ID, + .wTerminalType = UAC_TERMINAL_STREAMING, + .bAssocTerminal = MICROPHONE_INPUT_TERMINAL_ID, + .bSourceID = MICROPHONE_FEATURE_UNIT_ID, +}; + +static struct uac_feature_unit_descriptor_mic microphone_feature_unit_desc = { + .bLength = UAC_FEATURE_UNIT_SIZE(1,1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FEATURE_UNIT, + .bUnitID = MICROPHONE_FEATURE_UNIT_ID, + .bSourceID = MICROPHONE_INPUT_TERMINAL_ID, + .bControlSize = 1, + .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), +}; + +static struct usb_audio_control microphone_mute_control = { + .list = LIST_HEAD_INIT(microphone_mute_control.list), + .name = "Microphone Mute Control", + .type = UAC_MUTE_CONTROL, + /* Todo: add real Mute control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control microphone_volume_control = { + .list = LIST_HEAD_INIT(microphone_volume_control.list), + .name = "Microphone Volume Control", + .type = UAC_VOLUME_CONTROL, + /* Todo: add real Volume control code */ + .set = generic_set_cmd, + .get = generic_get_cmd, }; +static struct usb_audio_control_selector microphone_fu_controls = { + .list = LIST_HEAD_INIT(microphone_fu_controls.list), + .id = MICROPHONE_FEATURE_UNIT_ID, + .name = "Microphone Feature Unit Controls", + .type = UAC_FEATURE_UNIT, + .desc = (struct usb_descriptor_header *)µphone_feature_unit_desc, +}; + +/*---------------------------------*/ + /* B.4.1 Standard AS Interface Descriptor */ -static struct usb_interface_descriptor as_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 speaker_as_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_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 speaker_as_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 uac_as_header_descriptor as_header_desc = { - .bLength = UAC_DT_AS_HEADER_SIZE, - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_AS_GENERAL, - .bTerminalLink = INPUT_TERMINAL_ID, - .bDelay = 1, - .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +static struct uac_as_header_descriptor speaker_as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = SPEAKER_INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, }; -DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); - -static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { - .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), - .bDescriptorType = USB_DT_CS_INTERFACE, - .bDescriptorSubtype = UAC_FORMAT_TYPE, - .bFormatType = UAC_FORMAT_TYPE_I, - .bSubframeSize = 2, - .bBitResolution = 16, - .bSamFreqType = 1, +static struct uac_format_type_i_discrete_descriptor_1 speaker_as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, + .tSamFreq = { + FREQ(0, 48000), /* 48.0 KHz */ + }, }; /* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = USB_DIR_OUT, - .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE - | USB_ENDPOINT_XFER_ISOC, - .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE), - .bInterval = 4, +static struct usb_endpoint_descriptor speaker_as_ep_out_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = UAC_EP_ATTR_ADAPTIVE | + USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = __constant_cpu_to_le16(PLAYBACK_EP_MAX_PACKET_SIZE), + .bInterval = 4, +}; + +/* Class-specific AS ISO OUT Endpoint Descriptor */ +static struct uac_iso_endpoint_descriptor speaker_as_iso_out_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = UAC_EP_SAMPLE_FREQ, + .bLockDelayUnits = 1, + .wLockDelay = __constant_cpu_to_le16(1), +}; + +static struct usb_audio_control speaker_sample_freq_control = { + .list = LIST_HEAD_INIT(speaker_sample_freq_control.list), + .name = "Speaker Sampling Frequency Control", + .type = UAC_EP_CS_ATTR_SAMPLE_RATE, + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control_selector speaker_as_iso_out = { + .list = LIST_HEAD_INIT(speaker_as_iso_out.list), + .name = "Speaker Iso-out Endpoint Control", + .type = UAC_EP_GENERAL, + .desc = (struct usb_descriptor_header *)&speaker_as_iso_out_desc, +}; + +/*---------------------------------*/ + +/* B.4.1 Standard AS Interface Descriptor */ +static struct usb_interface_descriptor microphone_as_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 microphone_as_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 uac_as_header_descriptor microphone_as_header_desc = { + .bLength = UAC_DT_AS_HEADER_SIZE, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_AS_GENERAL, + .bTerminalLink = MICROPHONE_OUTPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = UAC_FORMAT_TYPE_I_PCM, +}; + +static struct uac_format_type_i_discrete_descriptor_1 microphone_as_type_i_desc = { + .bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubtype = UAC_FORMAT_TYPE, + .bFormatType = UAC_FORMAT_TYPE_I, + .bNrChannels = 1, + .bSubframeSize = 2, + .bBitResolution = 16, + .bSamFreqType = 1, + .tSamFreq = { + FREQ(0, 48000), /* 48.0 KHz */ + }, +}; + +/* Standard ISO IN Endpoint Descriptor */ +static struct usb_endpoint_descriptor microphone_as_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_ISOC | UAC_EP_ATTR_ASYNC, + .wMaxPacketSize = __constant_cpu_to_le16(CAPTURE_EP_MAX_PACKET_SIZE), + .bInterval = 4, }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct uac_iso_endpoint_descriptor as_iso_out_desc __initdata = { - .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, - .bDescriptorType = USB_DT_CS_ENDPOINT, - .bDescriptorSubtype = UAC_EP_GENERAL, - .bmAttributes = 1, - .bLockDelayUnits = 1, - .wLockDelay = __constant_cpu_to_le16(1), +static struct uac_iso_endpoint_descriptor microphone_as_iso_in_desc = { + .bLength = UAC_ISO_ENDPOINT_DESC_SIZE, + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubtype = UAC_EP_GENERAL, + .bmAttributes = UAC_EP_SAMPLE_FREQ, + .bLockDelayUnits = 0, + .wLockDelay = __constant_cpu_to_le16(0), +}; + +static struct usb_audio_control microphone_sample_freq_control = { + .list = LIST_HEAD_INIT(microphone_sample_freq_control.list), + .name = "Microphone Sampling Frequency Control", + .type = UAC_EP_CS_ATTR_SAMPLE_RATE, + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control_selector microphone_as_iso_in = { + .list = LIST_HEAD_INIT(microphone_as_iso_in.list), + .name = "Microphone Iso-IN Endpoint Control", + .type = UAC_EP_GENERAL, + .desc = (struct usb_descriptor_header *)µphone_as_iso_in_desc, }; -static struct usb_descriptor_header *f_audio_desc[] __initdata = { +/*---------------------------------*/ + +/* B.3.1 Standard Interface Descriptor */ +static struct usb_interface_descriptor hid_interface_desc = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceProtocol = 0, + .bInterfaceSubClass = 0, +}; + +static struct hid_descriptor hid_desc = { + .bLength = sizeof(struct hid_descriptor), + .bDescriptorType = HID_DT_HID, + .bcdHID = 0x0100, + .bCountryCode = 0, + .bNumDescriptors = 1, + .desc[0] = { + .bDescriptorType = HID_DT_REPORT, + .wDescriptorLength = sizeof(hid_report_desc), + }, +}; + +static struct usb_endpoint_descriptor hid_ep_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(4), + .bInterval = 7, // 8 frames (8ms) +}; + +/*---------------------------------*/ + +static struct usb_descriptor_header *f_audio_desc[] = { (struct usb_descriptor_header *)&ac_interface_desc, (struct usb_descriptor_header *)&ac_header_desc, - (struct usb_descriptor_header *)&input_terminal_desc, - (struct usb_descriptor_header *)&output_terminal_desc, - (struct usb_descriptor_header *)&feature_unit_desc, - - (struct usb_descriptor_header *)&as_interface_alt_0_desc, - (struct usb_descriptor_header *)&as_interface_alt_1_desc, - (struct usb_descriptor_header *)&as_header_desc, - - (struct usb_descriptor_header *)&as_type_i_desc, + (struct usb_descriptor_header *)&speaker_input_terminal_desc, + (struct usb_descriptor_header *)&speaker_output_terminal_desc, + (struct usb_descriptor_header *)&speaker_feature_unit_desc, + + (struct usb_descriptor_header *)µphone_input_terminal_desc, + (struct usb_descriptor_header *)µphone_output_terminal_desc, + (struct usb_descriptor_header *)µphone_feature_unit_desc, + + (struct usb_descriptor_header *)&speaker_as_interface_alt_0_desc, + (struct usb_descriptor_header *)&speaker_as_interface_alt_1_desc, + (struct usb_descriptor_header *)&speaker_as_header_desc, + (struct usb_descriptor_header *)&speaker_as_type_i_desc, + (struct usb_descriptor_header *)&speaker_as_ep_out_desc, + (struct usb_descriptor_header *)&speaker_as_iso_out_desc, + + (struct usb_descriptor_header *)µphone_as_interface_alt_0_desc, + (struct usb_descriptor_header *)µphone_as_interface_alt_1_desc, + (struct usb_descriptor_header *)µphone_as_header_desc, + (struct usb_descriptor_header *)µphone_as_type_i_desc, + (struct usb_descriptor_header *)µphone_as_ep_in_desc, + (struct usb_descriptor_header *)µphone_as_iso_in_desc, + + (struct usb_descriptor_header *)&hid_interface_desc, + (struct usb_descriptor_header *)&hid_desc, + (struct usb_descriptor_header *)&hid_ep_in_desc, - (struct usb_descriptor_header *)&as_out_ep_desc, - (struct usb_descriptor_header *)&as_iso_out_desc, NULL, }; @@ -268,77 +586,169 @@ kfree(audio_buf->buf); kfree(audio_buf); } + /*-------------------------------------------------------------------------*/ struct f_audio { struct gaudio card; /* endpoints handle full and/or high speeds */ - struct usb_ep *out_ep; - struct usb_endpoint_descriptor *out_desc; + struct usb_ep *playback_ep; + struct usb_endpoint_descriptor *playback_desc; - spinlock_t lock; - struct f_audio_buf *copy_buf; - struct work_struct playback_work; - struct list_head play_queue; + struct usb_ep *capture_ep; + struct usb_endpoint_descriptor *capture_desc; + struct usb_request *capture_req; + + struct usb_ep *hid_ep; + struct usb_endpoint_descriptor *hid_desc; + struct usb_request *hid_req; + struct work_struct hid_work; + int hid_req_active; + + spinlock_t playback_lock; + struct f_audio_buf *playback_copy_buf; + struct work_struct playback_work; + struct list_head playback_queue; + + spinlock_t capture_lock; + struct f_audio_buf *capture_copy_buf; + struct work_struct capture_work; + struct list_head capture_queue; /* Control Set command */ - struct list_head cs; - u8 set_cmd; - struct usb_audio_control *set_con; + struct list_head fu_cs; + struct list_head ep_cs; + u8 set_cmd; + struct usb_audio_control *set_con; }; +/*-------------------------------------------------------------------------*/ + static inline struct f_audio *func_to_audio(struct usb_function *f) { return container_of(f, struct f_audio, card.func); } +static struct usb_request * alloc_ep_req(struct usb_ep * ep, unsigned length) +{ + struct usb_request * req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = kmalloc(length, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +#if 0 +static void free_ep_req(struct usb_ep * ep, struct usb_request * req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} +#endif + + /*-------------------------------------------------------------------------*/ +static void f_audio_hid_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_audio *audio = req->context; + int status = req->status; + + switch (status) { + case 0: /* normal completion */ + if (ep == audio->hid_ep) { + audio->hid_req_active = 0; + } + break; + + default: + break; + } +} + +static void f_audio_send_hid_report(struct f_audio *audio) +{ + if (audio->hid_ep == NULL) + return; + + if (audio->hid_req_active == 1) + return; + + audio->hid_req_active = 1; + + /* Build HID report */ + memcpy(audio->hid_req->buf, &hid_audio_report, + sizeof(hid_audio_report)); + + usb_ep_queue(audio->hid_ep, audio->hid_req, GFP_ATOMIC); +} + +static void f_audio_hid_work(struct work_struct *data) +{ + struct f_audio *audio = container_of(data, struct f_audio, hid_work); + + f_audio_send_hid_report(audio); +} + +static void f_audio_hid_report(struct f_audio *audio) +{ + schedule_work(&audio->hid_work); +} + static void f_audio_playback_work(struct work_struct *data) { struct f_audio *audio = container_of(data, struct f_audio, - playback_work); + playback_work); struct f_audio_buf *play_buf; - spin_lock_irq(&audio->lock); - if (list_empty(&audio->play_queue)) { - spin_unlock_irq(&audio->lock); + spin_lock_irq(&audio->playback_lock); + if (list_empty(&audio->playback_queue)) { + spin_unlock_irq(&audio->playback_lock); return; } - play_buf = list_first_entry(&audio->play_queue, - struct f_audio_buf, list); + play_buf = list_first_entry(&audio->playback_queue, + struct f_audio_buf, list); list_del(&play_buf->list); - spin_unlock_irq(&audio->lock); + spin_unlock_irq(&audio->playback_lock); u_audio_playback(&audio->card, play_buf->buf, play_buf->actual); + f_audio_buffer_free(play_buf); return; } -static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req) +static int f_audio_playback_ep_complete(struct usb_ep *ep, + struct usb_request *req) { struct f_audio *audio = req->context; struct usb_composite_dev *cdev = audio->card.func.config->cdev; - struct f_audio_buf *copy_buf = audio->copy_buf; + struct f_audio_buf *copy_buf = audio->playback_copy_buf; int err; if (!copy_buf) return -EINVAL; - /* Copy buffer is full, add it to the play_queue */ - if (audio_buf_size - copy_buf->actual < req->actual) { - list_add_tail(©_buf->list, &audio->play_queue); + /* Copy buffer is full, add it to the playback_queue */ + if (audio_playback_buf_size - copy_buf->actual < req->actual) { + list_add_tail(©_buf->list, &audio->playback_queue); schedule_work(&audio->playback_work); - copy_buf = f_audio_buffer_alloc(audio_buf_size); + copy_buf = f_audio_buffer_alloc(audio_playback_buf_size); if (copy_buf < 0) return -ENOMEM; } memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual); copy_buf->actual += req->actual; - audio->copy_buf = copy_buf; + audio->playback_copy_buf = copy_buf; err = usb_ep_queue(ep, req, GFP_ATOMIC); if (err) @@ -348,22 +758,82 @@ } +static void f_audio_capture_work(struct work_struct *data) +{ + struct f_audio *audio = container_of(data, struct f_audio, capture_work); + struct usb_composite_dev *cdev = audio->card.func.config->cdev; + struct f_audio_buf *capture_buf; + + capture_buf = f_audio_buffer_alloc(audio_capture_buf_size); + if (capture_buf <= 0) { + ERROR(cdev, "%s: buffer alloc failed\n", __func__); + return; + } + + u_audio_capture(&audio->card, capture_buf->buf, + audio_capture_buf_size); + + spin_lock_irq(&audio->capture_lock); + list_add_tail(&capture_buf->list, &audio->capture_queue); + spin_unlock_irq(&audio->capture_lock); +} + +static int f_audio_capture_ep_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_audio *audio = req->context; + struct f_audio_buf *copy_buf = audio->capture_copy_buf; + + if (copy_buf == 0) { + spin_lock_irq(&audio->capture_lock); + if (list_empty(&audio->capture_queue)) { + spin_unlock_irq(&audio->capture_lock); + schedule_work(&audio->capture_work); + goto done; + } + copy_buf = list_first_entry(&audio->capture_queue, + struct f_audio_buf, list); + list_del(©_buf->list); + audio->capture_copy_buf = copy_buf; + spin_unlock_irq(&audio->capture_lock); + } + + memcpy(req->buf, copy_buf->buf + copy_buf->actual, req->actual); + copy_buf->actual += req->actual; + + if (audio_capture_buf_size - copy_buf->actual < req->actual) { + f_audio_buffer_free(copy_buf); + audio->capture_copy_buf = 0; + schedule_work(&audio->capture_work); + } +done: + usb_ep_queue(ep, req, GFP_ATOMIC); + + return 0; +} + static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) { struct f_audio *audio = req->context; + struct usb_composite_dev *cdev = (struct usb_composite_dev *)ep->driver_data; int status = req->status; u32 data = 0; - struct usb_ep *out_ep = audio->out_ep; switch (status) { case 0: /* normal completion? */ - if (ep == out_ep) - f_audio_out_ep_complete(ep, req); + if (ep == audio->playback_ep) + f_audio_playback_ep_complete(ep, req); + else if (ep == audio->capture_ep) + f_audio_capture_ep_complete(ep, req); else if (audio->set_con) { memcpy(&data, req->buf, req->length); audio->set_con->set(audio->set_con, audio->set_cmd, le16_to_cpu(data)); + INFO(cdev, "%s: SET_%s(%d)\n", + audio->set_con->name, + control_types(audio->set_cmd), + data); audio->set_con = NULL; } break; @@ -389,7 +859,7 @@ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", ctrl->bRequest, w_value, len, id); - list_for_each_entry(cs, &audio->cs, list) { + list_for_each_entry(cs, &audio->fu_cs, list) { if (cs->id == id) { list_for_each_entry(con, &cs->control, list) { if (con->type == con_sel) { @@ -426,11 +896,15 @@ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", ctrl->bRequest, w_value, len, id); - list_for_each_entry(cs, &audio->cs, list) { + list_for_each_entry(cs, &audio->fu_cs, list) { if (cs->id == id) { list_for_each_entry(con, &cs->control, list) { if (con->type == con_sel && con->get) { value = con->get(con, cmd); + INFO(cdev, "%s: GET_%s(%d)\n", + con->name, + control_types(cmd), + value); break; } } @@ -445,6 +919,165 @@ return len; } +static int hid_get_intf_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + + u16 w_value = le16_to_cpu(ctrl->wValue); +// u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_length = le16_to_cpu(ctrl->wLength); + + int len; + + switch (w_value) { + + case HID_DT_REPORT << 8 : + len = (w_length < sizeof(hid_report_desc)) ? + w_length : sizeof(hid_report_desc); + + /* Fill buf with HID Report Descriptor */ + memcpy(req->buf, hid_report_desc, len); + + f_audio_hid_report(audio); + + break; + default: + len = -EOPNOTSUPP; + break; + } + + return len; +} + +static void audio_set_endpoint_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_audio *audio = req->context; + struct usb_composite_dev *cdev = audio->card.func.config->cdev; + u32 data = 0; + + if (req->status == 0 && audio->set_con) { + memcpy(&data, req->buf, req->length); + audio->set_con->set(audio->set_con, audio->set_cmd, + le32_to_cpu(data)); + INFO(cdev, "%s: SET_%s(%d)\n", + audio->set_con->name, control_types(audio->set_cmd), + data); + audio->set_con = NULL; + } +} + +/** + * Handle USB audio endpoint set/get command in setup class request + */ +static int audio_set_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + int value = -EOPNOTSUPP; + u8 ep = le16_to_cpu(ctrl->wIndex) & 0xFF; + u8 epnum = ep & ~0x80; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, ep %d\n", + ctrl->bRequest, w_value, len, ep); + + list_for_each_entry(cs, &audio->ep_cs, list) { + if (cs->id != epnum) + continue; + + list_for_each_entry(con, &cs->control, list) { + if (con->type != con_sel) + continue; + + switch (cmd) { + case UAC__CUR: + case UAC__MIN: + case UAC__MAX: + case UAC__RES: + audio->set_con = con; + audio->set_cmd = cmd; + req->context = audio; + req->complete = audio_set_endpoint_complete; + value = len; + break; + case UAC__MEM: + break; + default: + break; + } + break; + } + break; + } + + return value; +} + +static int audio_get_endpoint_req(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct f_audio *audio = func_to_audio(f); + struct usb_composite_dev *cdev = f->config->cdev; + struct usb_request *req = cdev->req; + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + int data; + + int value = -EOPNOTSUPP; + u8 ep = (le16_to_cpu(ctrl->wIndex) & 0x7F); + u8 epnum = ep & ~0x80; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + u8 con_sel = (w_value >> 8) & 0xFF; + u8 cmd = (ctrl->bRequest & 0x0F); + + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, ep %d\n", + ctrl->bRequest, w_value, len, ep); + + list_for_each_entry(cs, &audio->ep_cs, list) { + if (cs->id != epnum) + continue; + + list_for_each_entry(con, &cs->control, list) { + if (con->type != con_sel) + continue; + + switch (cmd) { + case UAC__CUR: + case UAC__MIN: + case UAC__MAX: + case UAC__RES: + data = cpu_to_le32(generic_get_cmd(con, cmd)); + memcpy(req->buf, &data, len); + INFO(cdev, "%s: GET_%s(%d)\n", + con->name, control_types(cmd), data); + value = len; + break; + case UAC__MEM: + break; + default: + break; + } + break; + } + break; + } + + return value; +} + static int f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -467,6 +1100,18 @@ value = audio_get_intf_req(f, ctrl); break; + case USB_DIR_IN | USB_RECIP_INTERFACE: + value = hid_get_intf_req(f, ctrl); + break; + + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_set_endpoint_req(f, ctrl); + break; + + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: + value = audio_get_endpoint_req(f, ctrl); + break; + default: ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, @@ -493,54 +1138,129 @@ { struct f_audio *audio = func_to_audio(f); struct usb_composite_dev *cdev = f->config->cdev; - struct usb_ep *out_ep = audio->out_ep; struct usb_request *req; int i = 0, err = 0; DBG(cdev, "intf %d, alt %d\n", intf, alt); if (intf == 1) { + struct usb_ep *playback_ep = audio->playback_ep; if (alt == 1) { - usb_ep_enable(out_ep, audio->out_desc); - out_ep->driver_data = audio; - audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); + usb_ep_enable(playback_ep, audio->playback_desc); + playback_ep->driver_data = audio; + audio->playback_copy_buf = + f_audio_buffer_alloc(audio_playback_buf_size); /* * allocate a bunch of read buffers * and queue them all at once. */ - for (i = 0; i < req_count && err == 0; i++) { - req = usb_ep_alloc_request(out_ep, GFP_ATOMIC); + for (i = 0; i < req_playback_count && err == 0; i++) { + req = usb_ep_alloc_request(playback_ep, + GFP_ATOMIC); if (req) { - req->buf = kzalloc(req_buf_size, - GFP_ATOMIC); + req->buf = kzalloc( + req_playback_buf_size, + GFP_ATOMIC); if (req->buf) { - req->length = req_buf_size; + req->length = + req_playback_buf_size; req->context = audio; - req->complete = + req->complete = f_audio_complete; - err = usb_ep_queue(out_ep, + err = usb_ep_queue( + playback_ep, req, GFP_ATOMIC); if (err) ERROR(cdev, "%s queue req: %d\n", - out_ep->name, err); + playback_ep->name, + err); } else err = -ENOMEM; } else err = -ENOMEM; } - - } else { - struct f_audio_buf *copy_buf = audio->copy_buf; + } + else { + struct f_audio_buf *copy_buf = audio->playback_copy_buf; if (copy_buf) { list_add_tail(©_buf->list, - &audio->play_queue); + &audio->playback_queue); schedule_work(&audio->playback_work); } } } + if (intf == 2) { + struct usb_ep * capture_ep = audio->capture_ep; + if (alt == 1) { + usb_ep_enable(capture_ep, audio->capture_desc); + capture_ep->driver_data = audio; + audio->capture_copy_buf = 0; + + /* + * Allocate a write buffer. + */ + req = usb_ep_alloc_request(capture_ep, GFP_ATOMIC); + if (req) { + req->buf = kzalloc(req_capture_buf_size, + GFP_ATOMIC); + if (req->buf) { + req->length = req_capture_buf_size; + req->context = audio; + req->complete = f_audio_complete; + audio->capture_req = req; + err = usb_ep_queue(capture_ep, req, + GFP_ATOMIC); + if (err) + ERROR(cdev, + "%s queue req: %d\n", + capture_ep->name, err); + } else + err = -ENOMEM; + } + + schedule_work(&audio->capture_work); + } + else { + struct f_audio_buf *capture_buf; + spin_lock_irq(&audio->capture_lock); + while (!list_empty(&audio->capture_queue)) { + capture_buf = + list_first_entry( + &audio->capture_queue, + struct f_audio_buf, + list); + list_del(&capture_buf->list); + f_audio_buffer_free(capture_buf); + } + spin_unlock_irq(&audio->capture_lock); + } + } + + + if (intf == 3) { + if (alt == 0) { + usb_ep_enable(audio->hid_ep, audio->hid_desc); + + audio->hid_req = alloc_ep_req(audio->hid_ep, + sizeof(hid_audio_report)); + if (!audio->hid_req) { + printk("%s: alloc_ep_req failed\n", __func__); + return -ENOMEM; + } + audio->hid_req->length = sizeof(hid_audio_report); + audio->hid_req->context = audio; + audio->hid_req->complete = f_audio_hid_complete; + + audio->hid_req_active = 0; + } + else { + err = -EINVAL; + } + } + return err; } @@ -556,14 +1276,24 @@ struct gaudio *card = &audio->card; u8 *sam_freq; int rate; + int channels; /* Set channel numbers */ - input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card); - as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card); + channels = u_audio_get_playback_channels(card); + speaker_input_terminal_desc.bNrChannels = channels; + speaker_as_type_i_desc.bNrChannels = channels; + + channels = u_audio_get_capture_channels(card); + microphone_input_terminal_desc.bNrChannels = channels; + microphone_as_type_i_desc.bNrChannels = channels; /* Set sample rates */ rate = u_audio_get_playback_rate(card); - sam_freq = as_type_i_desc.tSamFreq[0]; + sam_freq = speaker_as_type_i_desc.tSamFreq[0]; + memcpy(sam_freq, &rate, 3); + + rate = u_audio_get_capture_rate(card); + sam_freq = microphone_as_type_i_desc.tSamFreq[0]; memcpy(sam_freq, &rate, 3); /* Todo: Set Sample bits and other parameters */ @@ -579,6 +1309,7 @@ struct f_audio *audio = func_to_audio(f); int status; struct usb_ep *ep; + u8 epaddr; f_audio_build_desc(audio); @@ -591,21 +1322,57 @@ status = usb_interface_id(c, f); if (status < 0) goto fail; - as_interface_alt_0_desc.bInterfaceNumber = status; - as_interface_alt_1_desc.bInterfaceNumber = status; + speaker_as_interface_alt_0_desc.bInterfaceNumber = status; + speaker_as_interface_alt_1_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + microphone_as_interface_alt_0_desc.bInterfaceNumber = status; + microphone_as_interface_alt_1_desc.bInterfaceNumber = status; + + status = usb_interface_id(c, f); + if (status < 0) + goto fail; + hid_interface_desc.bInterfaceNumber = status; status = -ENODEV; /* allocate instance-specific endpoints */ - ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); + ep = usb_ep_autoconfig(cdev->gadget, &speaker_as_ep_out_desc); if (!ep) goto fail; - audio->out_ep = ep; ep->driver_data = cdev; /* claim */ + audio->playback_ep = ep; + + ep = usb_ep_autoconfig(cdev->gadget, µphone_as_ep_in_desc); + if (!ep) + goto fail; + ep->driver_data = cdev; /* claim */ + audio->capture_ep = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &hid_ep_in_desc); + if (!ep) + goto fail; + ep->driver_data = cdev; /* claim */ + audio->hid_ep = ep; + + /* associate bEndpointAddress with usb_function */ + epaddr = speaker_as_ep_out_desc.bEndpointAddress & ~USB_DIR_IN; + status = usb_endpoint_id(c, f, epaddr); + if (status < 0) + goto fail; + speaker_as_iso_out.id = epaddr; + + epaddr = microphone_as_ep_in_desc.bEndpointAddress & ~USB_DIR_IN; + status = usb_endpoint_id(c, f, epaddr); + if (status < 0) + goto fail; + microphone_as_iso_in.id = epaddr; status = -ENOMEM; - /* supcard all relevant hardware speeds... we expect that when + /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ @@ -617,6 +1384,10 @@ } else f->descriptors = usb_copy_descriptors(f_audio_desc); + INFO(cdev, "using %s, OUT(%s) IN(%s) INT(%s)\n", + cdev->gadget->name, audio->playback_ep->name, + audio->capture_ep->name, audio->hid_ep->name); + return 0; fail: @@ -629,6 +1400,7 @@ { struct f_audio *audio = func_to_audio(f); + gaudio_cleanup(&audio->card); usb_free_descriptors(f->descriptors); kfree(audio); } @@ -647,20 +1419,46 @@ return con->data[cmd]; } -/* Todo: add more control selecotor dynamically */ +/* Todo: add more control selector dynamically */ int __init control_selector_init(struct f_audio *audio) { - INIT_LIST_HEAD(&audio->cs); - list_add(&feature_unit.list, &audio->cs); - - INIT_LIST_HEAD(&feature_unit.control); - list_add(&mute_control.list, &feature_unit.control); - list_add(&volume_control.list, &feature_unit.control); - - volume_control.data[UAC__CUR] = 0xffc0; - volume_control.data[UAC__MIN] = 0xe3a0; - volume_control.data[UAC__MAX] = 0xfff0; - volume_control.data[UAC__RES] = 0x0030; + INIT_LIST_HEAD(&audio->fu_cs); + list_add(&speaker_fu_controls.list, &audio->fu_cs); + list_add(µphone_fu_controls.list, &audio->fu_cs); + + INIT_LIST_HEAD(&speaker_fu_controls.control); + list_add(&speaker_mute_control.list, + &speaker_fu_controls.control); + list_add(&speaker_volume_control.list, + &speaker_fu_controls.control); + + INIT_LIST_HEAD(µphone_fu_controls.control); + list_add(µphone_mute_control.list, + µphone_fu_controls.control); + list_add(µphone_volume_control.list, + µphone_fu_controls.control); + + speaker_volume_control.data[UAC__CUR] = 0xffc0; + speaker_volume_control.data[UAC__MIN] = 0xe3a0; + speaker_volume_control.data[UAC__MAX] = 0xfff0; + speaker_volume_control.data[UAC__RES] = 0x0030; + + microphone_volume_control.data[UAC__CUR] = 0xffc0; + microphone_volume_control.data[UAC__MIN] = 0xe3a0; + microphone_volume_control.data[UAC__MAX] = 0xfff0; + microphone_volume_control.data[UAC__RES] = 0x0030; + + INIT_LIST_HEAD(&audio->ep_cs); + list_add(&speaker_as_iso_out.list, &audio->ep_cs); + list_add(µphone_as_iso_in.list, &audio->ep_cs); + + INIT_LIST_HEAD(&speaker_as_iso_out.control); + list_add(&speaker_sample_freq_control.list, + &speaker_as_iso_out.control); + + INIT_LIST_HEAD(µphone_as_iso_in.control); + list_add(µphone_sample_freq_control.list, + µphone_as_iso_in.control); return 0; } @@ -685,8 +1483,11 @@ audio->card.func.name = "g_audio"; audio->card.gadget = c->cdev->gadget; - INIT_LIST_HEAD(&audio->play_queue); - spin_lock_init(&audio->lock); + INIT_LIST_HEAD(&audio->playback_queue); + spin_lock_init(&audio->playback_lock); + + INIT_LIST_HEAD(&audio->capture_queue); + spin_lock_init(&audio->capture_lock); /* set up ASLA audio devices */ status = gaudio_setup(&audio->card); @@ -699,18 +1500,28 @@ audio->card.func.set_alt = f_audio_set_alt; audio->card.func.setup = f_audio_setup; audio->card.func.disable = f_audio_disable; - audio->out_desc = &as_out_ep_desc; + + audio->playback_desc = &speaker_as_ep_out_desc; + audio->capture_desc = µphone_as_ep_in_desc; + audio->hid_desc = &hid_ep_in_desc; control_selector_init(audio); INIT_WORK(&audio->playback_work, f_audio_playback_work); + INIT_WORK(&audio->capture_work, f_audio_capture_work); + INIT_WORK(&audio->hid_work, f_audio_hid_work); status = usb_add_function(c, &audio->card.func); if (status) goto add_fail; - INFO(c->cdev, "audio_buf_size %d, req_buf_size %d, req_count %d\n", - audio_buf_size, req_buf_size, req_count); + INFO(c->cdev, "playback: audio_buf_size %d, req_buf_size %d, " + "req_count %d\n", audio_playback_buf_size, + req_playback_buf_size, req_playback_count); + + INFO(c->cdev, "capture: audio_buf_size %d, req_buf_size %d, " + "req_count %d\n", audio_capture_buf_size, + req_capture_buf_size, req_capture_count); return status; -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html