This patch applies to the USB Gadget Audio driver, g_audio.ko. The fixes include -- * driver bind and unbind processing * several configuration structs were incomplete or not packed. The additions include ... * Endpoint ControlSelector support added: handles Sample_Freq. * USB_DT_AC_FEATURE_UNIT_SIZE() macro handles different element sizes. With these changes I was able to get the USB Gadget Audio driver to pass audio playback (speaker) from a WindowXP laptop running WMP-11 to a set of USB-speakers on a Linux (ubuntu) system via the g_audio + net2280 udc driver. Below shows a simple line diagram of this test configuration. [WMP-11] --usb--> [g_audio + net2280] --/dev/snd/pcmC0D0p--> [ALSA] --usb--> USB-speakers [--WindowsXP---][--------------------- Ubuntu (9.04) Linux ---------------][----hw device---] Signed-off-by: Robin Callender <robin_callender@xxxxxxxxxxx> diff -uprN linux-2.6.30.4.orig/drivers/usb/gadget/audio.c linux-2.6.30.4/drivers/usb/gadget/audio.c --- linux-2.6.30.4.orig/drivers/usb/gadget/audio.c 2009-08-15 14:23:40.000000000 -0700 +++ linux-2.6.30.4/drivers/usb/gadget/audio.c 2009-08-15 15:17:25.000000000 -0700 @@ -89,6 +89,21 @@ static const struct usb_descriptor_heade /*-------------------------------------------------------------------------*/ +static void audio_set_endpoint_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_audio *audio = req->context; + 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)); + //printk("%s(%d)\n", audio->set_con->name, le32_to_cpu(data)); + audio->set_con = NULL; + } +} + /** * Handle USB audio endpoint set/get command in setup class request */ @@ -97,31 +112,45 @@ static int audio_set_endpoint_req(struct const struct usb_ctrlrequest *ctrl) { struct usb_composite_dev *cdev = c->cdev; - int value = -EOPNOTSUPP; - u16 ep = le16_to_cpu(ctrl->wIndex); - u16 len = le16_to_cpu(ctrl->wLength); - u16 w_value = le16_to_cpu(ctrl->wValue); + struct usb_request *req = cdev->req; + struct f_audio *audio = req->context; + struct usb_audio_control_selector *cs; + struct usb_audio_control *con; + + int value = -EOPNOTSUPP; + u8 id = le16_to_cpu(ctrl->wIndex) & 0xFF; + 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, endpoint %d\n", - ctrl->bRequest, w_value, len, ep); + ctrl->bRequest, w_value, len, id); + + list_for_each_entry(cs, &audio->ep_cs, list) { + if (cs->id == id) { + list_for_each_entry(con, &cs->control, list) { + if (con->type == con_sel) { + audio->set_con = con; + break; + } + } + break; + } + } switch (ctrl->bRequest) { case SET_CUR: - value = 0; - break; - case SET_MIN: - break; - case SET_MAX: - break; - case SET_RES: + audio->set_cmd = cmd; + req->context = audio; + req->complete = audio_set_endpoint_complete; + value = len; break; - case SET_MEM: break; - default: break; } @@ -146,7 +175,7 @@ static int audio_get_endpoint_req(struct case GET_MIN: case GET_MAX: case GET_RES: - value = 3; + value = len; break; case GET_MEM: break; diff -uprN linux-2.6.30.4.orig/drivers/usb/gadget/f_audio.c linux-2.6.30.4/drivers/usb/gadget/f_audio.c --- linux-2.6.30.4.orig/drivers/usb/gadget/f_audio.c 2009-08-15 14:23:40.000000000 -0700 +++ linux-2.6.30.4/drivers/usb/gadget/f_audio.c 2009-08-15 15:10:59.000000000 -0700 @@ -39,7 +39,7 @@ MODULE_PARM_DESC(audio_buf_size, "Audio */ #define F_AUDIO_AC_INTERFACE 0 #define F_AUDIO_AS_INTERFACE 1 -#define F_AUDIO_NUM_INTERFACES 2 +#define F_AUDIO_NUM_INTERFACES 1 /* B.3.1 Standard AC Interface Descriptor */ static struct usb_interface_descriptor ac_interface_desc __initdata = { @@ -50,20 +50,24 @@ static struct usb_interface_descriptor a .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, }; -DECLARE_USB_AC_HEADER_DESCRIPTOR(2); +DECLARE_USB_AC_HEADER_DESCRIPTOR(1); + +#define USB_DT_AC_HEADER_LENGTH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) -#define USB_DT_AC_HEADER_LENGH USB_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES) /* B.3.2 Class-Specific AC Interface Descriptor */ -static struct usb_ac_header_descriptor_2 ac_header_desc = { - .bLength = USB_DT_AC_HEADER_LENGH, +static struct usb_ac_header_descriptor_1 ac_header_desc = { + .bLength = USB_DT_AC_HEADER_LENGTH, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = HEADER, .bcdADC = __constant_cpu_to_le16(0x0100), - .wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH), + .wTotalLength = __constant_cpu_to_le16( + USB_DT_AC_HEADER_SIZE(1) + + USB_DT_AC_INPUT_TERMINAL_SIZE + + USB_DT_AC_FEATURE_UNIT_SIZE(1,1) + + USB_DT_AC_OUTPUT_TERMINAL_SIZE), .bInCollection = F_AUDIO_NUM_INTERFACES, .baInterfaceNr = { - [0] = F_AUDIO_AC_INTERFACE, - [1] = F_AUDIO_AS_INTERFACE, + [0] = F_AUDIO_AS_INTERFACE, } }; @@ -82,7 +86,7 @@ DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(0 #define FEATURE_UNIT_ID 2 static struct usb_ac_feature_unit_descriptor_0 feature_unit_desc = { - .bLength = USB_DT_AC_FEATURE_UNIT_SIZE(0), + .bLength = USB_DT_AC_FEATURE_UNIT_SIZE(1,1), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = FEATURE_UNIT, .bUnitID = FEATURE_UNIT_ID, @@ -181,22 +185,38 @@ static struct usb_endpoint_descriptor as }; /* Class-specific AS ISO OUT Endpoint Descriptor */ -static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = { +static struct usb_as_iso_endpoint_descriptor as_iso_out_desc = { .bLength = USB_AS_ISO_ENDPOINT_DESC_SIZE, .bDescriptorType = USB_DT_CS_ENDPOINT, .bDescriptorSubtype = EP_GENERAL, - .bmAttributes = 1, + .bmAttributes = SAMPLE_FREQ, .bLockDelayUnits = 1, .wLockDelay = __constant_cpu_to_le16(1), }; +static struct usb_audio_control sample_freq_control = { + .list = LIST_HEAD_INIT(sample_freq_control.list), + .name = "Sampling Frequency Control", + .type = EP_ISO_SAMPLE_FREQ, + .set = generic_set_cmd, + .get = generic_get_cmd, +}; + +static struct usb_audio_control_selector as_iso_out = { + .list = LIST_HEAD_INIT(as_iso_out.list), + .id = EP_GENERAL, + .name = "Iso-out Endpoint Control", + .type = EP_GENERAL, + .desc = (struct usb_descriptor_header *)&as_iso_out_desc, +}; + static struct usb_descriptor_header *f_audio_desc[] __initdata = { (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 *)&output_terminal_desc, (struct usb_descriptor_header *)&as_interface_alt_0_desc, (struct usb_descriptor_header *)&as_interface_alt_1_desc, @@ -280,7 +300,8 @@ struct f_audio { struct list_head play_queue; /* Control Set command */ - struct list_head cs; + struct list_head fu_cs; + struct list_head ep_cs; u8 set_cmd; struct usb_audio_control *set_con; }; @@ -386,7 +407,7 @@ static int audio_set_intf_req(struct usb 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) { @@ -423,7 +444,7 @@ static int audio_get_intf_req(struct usb 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) { @@ -626,17 +647,19 @@ f_audio_unbind(struct usb_configuration { struct f_audio *audio = func_to_audio(f); + gaudio_cleanup(&audio->card); + usb_free_descriptors(f->descriptors); kfree(audio); } /*-------------------------------------------------------------------------*/ -/* 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(&audio->fu_cs); + list_add(&feature_unit.list, &audio->fu_cs); INIT_LIST_HEAD(&feature_unit.control); list_add(&mute_control.list, &feature_unit.control); @@ -647,6 +670,12 @@ int __init control_selector_init(struct volume_control.data[_MAX] = 0xfff0; volume_control.data[_RES] = 0x0030; + INIT_LIST_HEAD(&audio->ep_cs); + list_add(&as_iso_out.list, &audio->ep_cs); + + INIT_LIST_HEAD(&as_iso_out.control); + list_add(&sample_freq_control.list, &as_iso_out.control); + return 0; } diff -uprN linux-2.6.30.4.orig/drivers/usb/gadget/u_audio.c linux-2.6.30.4/drivers/usb/gadget/u_audio.c --- linux-2.6.30.4.orig/drivers/usb/gadget/u_audio.c 2009-08-15 14:57:15.000000000 -0700 +++ linux-2.6.30.4/drivers/usb/gadget/u_audio.c 2009-08-15 14:59:59.000000000 -0700 @@ -231,7 +231,6 @@ static int gaudio_open_snd_dev(struct ga int ret = PTR_ERR(snd->filp); ERROR(card, "unable to open sound control device file: %s\n", fn_cntl); - snd->filp = NULL; return ret; } snd->card = card; @@ -241,20 +240,18 @@ static int gaudio_open_snd_dev(struct ga snd->filp = filp_open(fn_play, O_WRONLY, 0); if (IS_ERR(snd->filp)) { ERROR(card, "No such PCM playback device: %s\n", fn_play); - snd->filp = NULL; + } else { + pcm_file = snd->filp->private_data; + snd->substream = pcm_file->substream; + snd->card = card; + playback_default_hw_params(snd); } - pcm_file = snd->filp->private_data; - snd->substream = pcm_file->substream; - snd->card = card; - playback_default_hw_params(snd); /* Open PCM capture device and setup substream */ snd = &card->capture; snd->filp = filp_open(fn_cap, O_RDONLY, 0); if (IS_ERR(snd->filp)) { ERROR(card, "No such PCM capture device: %s\n", fn_cap); - snd->substream = NULL; - snd->card = NULL; } else { pcm_file = snd->filp->private_data; snd->substream = pcm_file->substream; diff -uprN linux-2.6.30.4.orig/include/linux/usb/audio.h linux-2.6.30.4/include/linux/usb/audio.h --- linux-2.6.30.4.orig/include/linux/usb/audio.h 2009-08-15 14:23:56.000000000 -0700 +++ linux-2.6.30.4/include/linux/usb/audio.h 2009-08-15 15:25:57.000000000 -0700 @@ -162,8 +162,8 @@ struct usb_output_terminal_descriptor { #define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306 #define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307 -/* Set bControlSize = 2 as default setting */ -#define USB_DT_AC_FEATURE_UNIT_SIZE(ch) (7 + ((ch) + 1) * 2) + +#define USB_DT_AC_FEATURE_UNIT_SIZE(ch,n) (7 + (((ch) + 1) * n)) /* As above, but more useful for defining your own descriptors: */ #define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch) \ @@ -255,7 +255,7 @@ struct usb_as_iso_endpoint_descriptor { __u8 bmAttributes; __u8 bLockDelayUnits; __le16 wLockDelay; -}; +} __attribute__ ((packed)); #define USB_AS_ISO_ENDPOINT_DESC_SIZE 7 #define FU_CONTROL_UNDEFINED 0x00 @@ -281,6 +281,13 @@ struct usb_as_iso_endpoint_descriptor { #define FU_BASS_BOOST (1 << (BASS_BOOST_CONTROL - 1)) #define FU_LOUDNESS (1 << (LOUDNESS_CONTROL - 1)) +#define EP_ISO_UNDEFINED 0x00 +#define EP_ISO_SAMPLE_FREQ 0x01 +#define EP_ISO_PITCH_CONTROL 0x02 + +#define SAMPLE_FREQ (1 << (EP_ISO_SAMPLE_FREQ - 1)) +#define PITCH_CONTROL (1 << (EP_ISO_PITCH_CONTROL - 1)) + struct usb_audio_control { struct list_head list; const char *name; -- 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