aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-06-03 12:23:17 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-06-03 12:23:17 -0700
commit02ca5637c5ce812da3b7353f3987cca53c511099 (patch)
tree3fea6196c73923fef504d3720ad14061bb012ff4 /usb
parent437a5b88cc48433da19def175be51028abdfe10d (diff)
downloadpatches-02ca5637c5ce812da3b7353f3987cca53c511099.tar.gz
usb audio gadget
Diffstat (limited to 'usb')
-rw-r--r--usb/usb-audio-add-usb-audio-class-definitions.patch308
-rw-r--r--usb/usb-gadget-add-usb-audio-gadget-driver.patch1484
2 files changed, 1792 insertions, 0 deletions
diff --git a/usb/usb-audio-add-usb-audio-class-definitions.patch b/usb/usb-audio-add-usb-audio-class-definitions.patch
new file mode 100644
index 00000000000000..9619091110805b
--- /dev/null
+++ b/usb/usb-audio-add-usb-audio-class-definitions.patch
@@ -0,0 +1,308 @@
+From vapier@gentoo.org Wed Jun 3 12:16:51 2009
+From: Mike Frysinger <vapier@gentoo.org>
+Date: Wed, 3 Jun 2009 09:17:57 -0400
+Subject: [PATCH 1/2] USB: audio: add USB audio class definitions
+To: linux-usb@vger.kernel.org, alsa-devel@alsa-project.org
+Cc: Bryan Wu <cooloney@kernel.org>
+Message-ID: <1244035078-1775-2-git-send-email-vapier@gentoo.org>
+
+
+From: Bryan Wu <cooloney@kernel.org>
+
+Signed-off-by: Bryan Wu <cooloney@kernel.org>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/linux/usb/audio.h | 265 +++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 263 insertions(+), 2 deletions(-)
+
+--- a/include/linux/usb/audio.h
++++ b/include/linux/usb/audio.h
+@@ -24,10 +24,75 @@
+ #define USB_SUBCLASS_AUDIOCONTROL 0x01
+ #define USB_SUBCLASS_AUDIOSTREAMING 0x02
+ #define USB_SUBCLASS_MIDISTREAMING 0x03
++#define USB_SUBCLASS_VENDOR_SPEC 0xff
+
++/* A.5 Audio Class-Specific AC interface Descriptor Subtypes*/
++#define HEADER 0x01
++#define INPUT_TERMINAL 0x02
++#define OUTPUT_TERMINAL 0x03
++#define MIXER_UNIT 0x04
++#define SELECTOR_UNIT 0x05
++#define FEATURE_UNIT 0x06
++#define PROCESSING_UNIT 0x07
++#define EXTENSION_UNIT 0x08
++
++#define AS_GENERAL 0x01
++#define FORMAT_TYPE 0x02
++#define FORMAT_SPECIFIC 0x03
++
++#define EP_GENERAL 0x01
++
++#define MS_GENERAL 0x01
++#define MIDI_IN_JACK 0x02
++#define MIDI_OUT_JACK 0x03
++
++/* endpoint attributes */
++#define EP_ATTR_MASK 0x0c
++#define EP_ATTR_ASYNC 0x04
++#define EP_ATTR_ADAPTIVE 0x08
++#define EP_ATTR_SYNC 0x0c
++
++/* cs endpoint attributes */
++#define EP_CS_ATTR_SAMPLE_RATE 0x01
++#define EP_CS_ATTR_PITCH_CONTROL 0x02
++#define EP_CS_ATTR_FILL_MAX 0x80
++
++/* Audio Class specific Request Codes */
++#define USB_AUDIO_SET_INTF 0x21
++#define USB_AUDIO_SET_ENDPOINT 0x22
++#define USB_AUDIO_GET_INTF 0xa1
++#define USB_AUDIO_GET_ENDPOINT 0xa2
++
++#define SET_ 0x00
++#define GET_ 0x80
++
++#define _CUR 0x1
++#define _MIN 0x2
++#define _MAX 0x3
++#define _RES 0x4
++#define _MEM 0x5
++
++#define SET_CUR (SET_ | _CUR)
++#define GET_CUR (GET_ | _CUR)
++#define SET_MIN (SET_ | _MIN)
++#define GET_MIN (GET_ | _MIN)
++#define SET_MAX (SET_ | _MAX)
++#define GET_MAX (GET_ | _MAX)
++#define SET_RES (SET_ | _RES)
++#define GET_RES (GET_ | _RES)
++#define SET_MEM (SET_ | _MEM)
++#define GET_MEM (GET_ | _MEM)
++
++#define GET_STAT 0xff
++
++#define USB_AC_TERMINAL_UNDEFINED 0x100
++#define USB_AC_TERMINAL_STREAMING 0x101
++#define USB_AC_TERMINAL_VENDOR_SPEC 0x1FF
++
++/* Terminal Control Selectors */
+ /* 4.3.2 Class-Specific AC Interface Descriptor */
+ struct usb_ac_header_descriptor {
+- __u8 bLength; /* 8+n */
++ __u8 bLength; /* 8 + n */
+ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
+ __u8 bDescriptorSubtype; /* USB_MS_HEADER */
+ __le16 bcdADC; /* 0x0100 */
+@@ -36,7 +101,7 @@ struct usb_ac_header_descriptor {
+ __u8 baInterfaceNr[]; /* [n] */
+ } __attribute__ ((packed));
+
+-#define USB_DT_AC_HEADER_SIZE(n) (8+(n))
++#define USB_DT_AC_HEADER_SIZE(n) (8 + (n))
+
+ /* As above, but more useful for defining your own descriptors: */
+ #define DECLARE_USB_AC_HEADER_DESCRIPTOR(n) \
+@@ -50,4 +115,200 @@ struct usb_ac_header_descriptor_##n {
+ __u8 baInterfaceNr[n]; \
+ } __attribute__ ((packed))
+
++/* 4.3.2.1 Input Terminal Descriptor */
++struct usb_input_terminal_descriptor {
++ __u8 bLength; /* in bytes: 12 */
++ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */
++ __u8 bDescriptorSubtype; /* INPUT_TERMINAL descriptor subtype */
++ __u8 bTerminalID; /* Constant uniquely terminal ID */
++ __le16 wTerminalType; /* USB Audio Terminal Types */
++ __u8 bAssocTerminal; /* ID of the Output Terminal associated */
++ __u8 bNrChannels; /* Number of logical output channels */
++ __le16 wChannelConfig;
++ __u8 iChannelNames;
++ __u8 iTerminal;
++} __attribute__ ((packed));
++
++#define USB_DT_AC_INPUT_TERMINAL_SIZE 12
++
++#define USB_AC_INPUT_TERMINAL_UNDEFINED 0x200
++#define USB_AC_INPUT_TERMINAL_MICROPHONE 0x201
++#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202
++#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203
++#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204
++#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205
++#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206
++
++/* 4.3.2.2 Output Terminal Descriptor */
++struct usb_output_terminal_descriptor {
++ __u8 bLength; /* in bytes: 9 */
++ __u8 bDescriptorType; /* CS_INTERFACE descriptor type */
++ __u8 bDescriptorSubtype; /* OUTPUT_TERMINAL descriptor subtype */
++ __u8 bTerminalID; /* Constant uniquely terminal ID */
++ __le16 wTerminalType; /* USB Audio Terminal Types */
++ __u8 bAssocTerminal; /* ID of the Input Terminal associated */
++ __u8 bSourceID; /* ID of the connected Unit or Terminal*/
++ __u8 iTerminal;
++} __attribute__ ((packed));
++
++#define USB_DT_AC_OUTPUT_TERMINAL_SIZE 9
++
++#define USB_AC_OUTPUT_TERMINAL_UNDEFINED 0x300
++#define USB_AC_OUTPUT_TERMINAL_SPEAKER 0x301
++#define USB_AC_OUTPUT_TERMINAL_HEADPHONES 0x302
++#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303
++#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304
++#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305
++#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)
++
++/* As above, but more useful for defining your own descriptors: */
++#define DECLARE_USB_AC_FEATURE_UNIT_DESCRIPTOR(ch) \
++struct usb_ac_feature_unit_descriptor_##ch { \
++ __u8 bLength; \
++ __u8 bDescriptorType; \
++ __u8 bDescriptorSubtype; \
++ __u8 bUnitID; \
++ __u8 bSourceID; \
++ __u8 bControlSize; \
++ __le16 bmaControls[ch + 1]; \
++ __u8 iFeature; \
++} __attribute__ ((packed))
++
++/* 4.5.2 Class-Specific AS Interface Descriptor */
++struct usb_as_header_descriptor {
++ __u8 bLength; /* in bytes: 7 */
++ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
++ __u8 bDescriptorSubtype; /* AS_GENERAL */
++ __u8 bTerminalLink; /* Terminal ID of connected Terminal */
++ __u8 bDelay; /* Delay introduced by the data path */
++ __le16 wFormatTag; /* The Audio Data Format */
++} __attribute__ ((packed));
++
++#define USB_DT_AS_HEADER_SIZE 7
++
++#define USB_AS_AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0
++#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM 0x1
++#define USB_AS_AUDIO_FORMAT_TYPE_I_PCM8 0x2
++#define USB_AS_AUDIO_FORMAT_TYPE_I_IEEE_FLOAT 0x3
++#define USB_AS_AUDIO_FORMAT_TYPE_I_ALAW 0x4
++#define USB_AS_AUDIO_FORMAT_TYPE_I_MULAW 0x5
++
++struct usb_as_format_type_i_continuous_descriptor {
++ __u8 bLength; /* in bytes: 8 + (ns * 3) */
++ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
++ __u8 bDescriptorSubtype; /* FORMAT_TYPE */
++ __u8 bFormatType; /* FORMAT_TYPE_1 */
++ __u8 bNrChannels; /* physical channels in the stream */
++ __u8 bSubframeSize; /* */
++ __u8 bBitResolution;
++ __u8 bSamFreqType;
++ __u8 tLowerSamFreq[3];
++ __u8 tUpperSamFreq[3];
++} __attribute__ ((packed));
++
++#define USB_AS_FORMAT_TYPE_I_CONTINUOUS_DESC_SIZE 14
++
++struct usb_as_formate_type_i_discrete_descriptor {
++ __u8 bLength; /* in bytes: 8 + (ns * 3) */
++ __u8 bDescriptorType; /* USB_DT_CS_INTERFACE */
++ __u8 bDescriptorSubtype; /* FORMAT_TYPE */
++ __u8 bFormatType; /* FORMAT_TYPE_1 */
++ __u8 bNrChannels; /* physical channels in the stream */
++ __u8 bSubframeSize; /* */
++ __u8 bBitResolution;
++ __u8 bSamFreqType;
++ __u8 tSamFreq[][3];
++} __attribute__ ((packed));
++
++#define DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(n) \
++struct usb_as_formate_type_i_discrete_descriptor_##n { \
++ __u8 bLength; \
++ __u8 bDescriptorType; \
++ __u8 bDescriptorSubtype; \
++ __u8 bFormatType; \
++ __u8 bNrChannels; \
++ __u8 bSubframeSize; \
++ __u8 bBitResolution; \
++ __u8 bSamFreqType; \
++ __u8 tSamFreq[n][3]; \
++} __attribute__ ((packed))
++
++#define USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(n) (8 + (n * 3))
++
++#define USB_AS_FORMAT_TYPE_UNDEFINED 0x0
++#define USB_AS_FORMAT_TYPE_I 0x1
++#define USB_AS_FORMAT_TYPE_II 0x2
++#define USB_AS_FORMAT_TYPE_III 0x3
++
++#define USB_AS_ENDPOINT_ASYNC (1 << 2)
++#define USB_AS_ENDPOINT_ADAPTIVE (2 << 2)
++#define USB_AS_ENDPOINT_SYNC (3 << 2)
++
++struct usb_as_iso_endpoint_descriptor {
++ __u8 bLength; /* in bytes: 7 */
++ __u8 bDescriptorType; /* USB_DT_CS_ENDPOINT */
++ __u8 bDescriptorSubtype; /* EP_GENERAL */
++ __u8 bmAttributes;
++ __u8 bLockDelayUnits;
++ __le16 wLockDelay;
++};
++#define USB_AS_ISO_ENDPOINT_DESC_SIZE 7
++
++#define FU_CONTROL_UNDEFINED 0x00
++#define MUTE_CONTROL 0x01
++#define VOLUME_CONTROL 0x02
++#define BASS_CONTROL 0x03
++#define MID_CONTROL 0x04
++#define TREBLE_CONTROL 0x05
++#define GRAPHIC_EQUALIZER_CONTROL 0x06
++#define AUTOMATIC_GAIN_CONTROL 0x07
++#define DELAY_CONTROL 0x08
++#define BASS_BOOST_CONTROL 0x09
++#define LOUDNESS_CONTROL 0x0a
++
++#define FU_MUTE (1 << (MUTE_CONTROL - 1))
++#define FU_VOLUME (1 << (VOLUME_CONTROL - 1))
++#define FU_BASS (1 << (BASS_CONTROL - 1))
++#define FU_MID (1 << (MID_CONTROL - 1))
++#define FU_TREBLE (1 << (TREBLE_CONTROL - 1))
++#define FU_GRAPHIC_EQ (1 << (GRAPHIC_EQUALIZER_CONTROL - 1))
++#define FU_AUTO_GAIN (1 << (AUTOMATIC_GAIN_CONTROL - 1))
++#define FU_DELAY (1 << (DELAY_CONTROL - 1))
++#define FU_BASS_BOOST (1 << (BASS_BOOST_CONTROL - 1))
++#define FU_LOUDNESS (1 << (LOUDNESS_CONTROL - 1))
++
++struct usb_audio_control {
++ struct list_head list;
++ const char *name;
++ u8 type;
++ int data[5];
++ int (*set)(struct usb_audio_control *con, u8 cmd, int value);
++ int (*get)(struct usb_audio_control *con, u8 cmd);
++};
++
++static inline int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
++{
++ con->data[cmd] = value;
++
++ return 0;
++}
++
++static inline int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
++{
++ return con->data[cmd];
++}
++
++struct usb_audio_control_selector {
++ struct list_head list;
++ struct list_head control;
++ u8 id;
++ const char *name;
++ u8 type;
++ struct usb_descriptor_header *desc;
++};
++
+ #endif /* __LINUX_USB_AUDIO_H */
diff --git a/usb/usb-gadget-add-usb-audio-gadget-driver.patch b/usb/usb-gadget-add-usb-audio-gadget-driver.patch
new file mode 100644
index 00000000000000..6a50f68019e77a
--- /dev/null
+++ b/usb/usb-gadget-add-usb-audio-gadget-driver.patch
@@ -0,0 +1,1484 @@
+From vapier@gentoo.org Wed Jun 3 12:17:18 2009
+From: Mike Frysinger <vapier@gentoo.org>
+Date: Wed, 3 Jun 2009 09:17:58 -0400
+Subject: USB: gadget: add USB Audio Gadget driver
+Cc: Bryan Wu <cooloney@kernel.org>
+Message-ID: <1244035078-1775-3-git-send-email-vapier@gentoo.org>
+
+
+From: Bryan Wu <cooloney@kernel.org>
+
+Funtions added:
+ - setup all the USB audio class device descriptors
+ - handle class specific setup request
+ - receive data from USB host by ISO transfer
+ - play audio data by ALSA sound card
+ - open and setup playback PCM interface
+ - set default playback PCM parameters
+ - provide playback functions for USB audio driver
+ - provide PCM parameters set/get functions
+
+Test on:
+ - Host: Ubuntu 8.10, kernel 2.6.27
+ - Gadget: EZKIT-BF548 with ASoC AD1980 codec
+
+Todo:
+ - add real Mute control code
+ - add real Volume control code
+ - maybe find another way to replace dynamic buffer handling
+ with static buffer allocation
+ - test on Windows system
+ - provide control interface to handle mute/volume control
+ - provide capture interface in the future
+ - test on BF527, other USB device controler and other audio codec
+
+Signed-off-by: Bryan Wu <cooloney@kernel.org>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/Kconfig | 14
+ drivers/usb/gadget/Makefile | 2
+ drivers/usb/gadget/audio.c | 302 ++++++++++++++++++
+ drivers/usb/gadget/f_audio.c | 707 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/usb/gadget/u_audio.c | 319 +++++++++++++++++++
+ drivers/usb/gadget/u_audio.h | 56 +++
+ 6 files changed, 1400 insertions(+)
+
+--- /dev/null
++++ b/drivers/usb/gadget/audio.c
+@@ -0,0 +1,302 @@
++/*
++ * audio.c -- Audio gadget driver
++ *
++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
++ * Copyright (C) 2008 Analog Devices, Inc
++ *
++ * Enter bugs at http://blackfin.uclinux.org/
++ *
++ * Licensed under the GPL-2 or later.
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++
++#include "u_audio.h"
++
++#define DRIVER_DESC "Linux USB Audio Gadget"
++#define DRIVER_VERSION "Dec 18, 2008"
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * Kbuild is not very cooperative with respect to linking separately
++ * compiled library objects into one module. So for now we won't use
++ * separate compilation ... ensuring init/exit sections work to shrink
++ * the runtime footprint, and giving us at least some parts of what
++ * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
++ */
++#include "composite.c"
++#include "usbstring.c"
++#include "config.c"
++#include "epautoconf.c"
++
++#include "u_audio.c"
++#include "f_audio.c"
++
++/*-------------------------------------------------------------------------*/
++
++/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
++ * Instead: allocate your own, using normal USB-IF procedures.
++ */
++
++/* Thanks to NetChip Technologies for donating this product ID. */
++#define AUDIO_VENDOR_NUM 0x0525 /* NetChip */
++#define AUDIO_PRODUCT_NUM 0xa4a1 /* Linux-USB Audio Gadget */
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_device_descriptor device_desc = {
++ .bLength = sizeof device_desc,
++ .bDescriptorType = USB_DT_DEVICE,
++
++ .bcdUSB = __constant_cpu_to_le16(0x200),
++
++ .bDeviceClass = USB_CLASS_PER_INTERFACE,
++ .bDeviceSubClass = 0,
++ .bDeviceProtocol = 0,
++ /* .bMaxPacketSize0 = f(hardware) */
++
++ /* Vendor and product id defaults change according to what configs
++ * we support. (As does bNumConfigurations.) These values can
++ * also be overridden by module parameters.
++ */
++ .idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
++ .idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
++ /* .bcdDevice = f(hardware) */
++ /* .iManufacturer = DYNAMIC */
++ /* .iProduct = DYNAMIC */
++ /* NO SERIAL NUMBER */
++ .bNumConfigurations = 1,
++};
++
++static struct usb_otg_descriptor otg_descriptor = {
++ .bLength = sizeof otg_descriptor,
++ .bDescriptorType = USB_DT_OTG,
++
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
++};
++
++static const struct usb_descriptor_header *otg_desc[] = {
++ (struct usb_descriptor_header *) &otg_descriptor,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++/**
++ * Handle USB audio endpoint set/get command in setup class request
++ */
++
++static int audio_set_endpoint_req(struct usb_configuration *c,
++ 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);
++
++ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
++ ctrl->bRequest, w_value, len, ep);
++
++ switch (ctrl->bRequest) {
++ case SET_CUR:
++ value = 0;
++ break;
++
++ case SET_MIN:
++ break;
++
++ case SET_MAX:
++ break;
++
++ case SET_RES:
++ break;
++
++ case SET_MEM:
++ break;
++
++ default:
++ break;
++ }
++
++ return value;
++}
++
++static int audio_get_endpoint_req(struct usb_configuration *c,
++ const struct usb_ctrlrequest *ctrl)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ int value = -EOPNOTSUPP;
++ u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
++ u16 len = le16_to_cpu(ctrl->wLength);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++
++ DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
++ ctrl->bRequest, w_value, len, ep);
++
++ switch (ctrl->bRequest) {
++ case GET_CUR:
++ case GET_MIN:
++ case GET_MAX:
++ case GET_RES:
++ value = 3;
++ break;
++ case GET_MEM:
++ break;
++ default:
++ break;
++ }
++
++ return value;
++}
++
++static int
++audio_setup(struct usb_configuration *c, const struct usb_ctrlrequest *ctrl)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * Audio class messages; interface activation uses set_alt().
++ */
++ switch (ctrl->bRequestType) {
++ case USB_AUDIO_SET_ENDPOINT:
++ value = audio_set_endpoint_req(c, ctrl);
++ break;
++
++ case USB_AUDIO_GET_ENDPOINT:
++ value = audio_get_endpoint_req(c, ctrl);
++ break;
++
++ default:
++ ERROR(cdev, "Invalid control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ DBG(cdev, "Audio req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(cdev, "Audio response on err %d\n", value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int __init audio_do_config(struct usb_configuration *c)
++{
++ /* FIXME alloc iConfiguration string, set it in c->strings */
++
++ if (gadget_is_otg(c->cdev->gadget)) {
++ c->descriptors = otg_desc;
++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ }
++
++ audio_bind_config(c);
++
++ return 0;
++}
++
++static struct usb_configuration audio_config_driver = {
++ .label = DRIVER_DESC,
++ .bind = audio_do_config,
++ .setup = audio_setup,
++ .bConfigurationValue = 1,
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int __init audio_bind(struct usb_composite_dev *cdev)
++{
++ int gcnum;
++ int status;
++
++ gcnum = usb_gadget_controller_number(cdev->gadget);
++ if (gcnum >= 0)
++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
++ else {
++ ERROR(cdev, "controller '%s' not recognized; trying %s\n",
++ cdev->gadget->name,
++ audio_config_driver.label);
++ device_desc.bcdDevice =
++ __constant_cpu_to_le16(0x0300 | 0x0099);
++ }
++
++ /* device descriptor strings: manufacturer, product */
++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
++ init_utsname()->sysname, init_utsname()->release,
++ cdev->gadget->name);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_MANUFACTURER_IDX].id = status;
++ device_desc.iManufacturer = status;
++
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_PRODUCT_IDX].id = status;
++ device_desc.iProduct = status;
++
++ status = usb_add_config(cdev, &audio_config_driver);
++ if (status < 0)
++ goto fail;
++
++ INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
++ return 0;
++
++fail:
++ return status;
++}
++
++static int __exit audio_unbind(struct usb_composite_dev *cdev)
++{
++ return 0;
++}
++
++static struct usb_composite_driver audio_driver = {
++ .name = "g_audio",
++ .dev = &device_desc,
++ .strings = audio_strings,
++ .bind = audio_bind,
++ .unbind = __exit_p(audio_unbind),
++};
++
++static int __init init(void)
++{
++ return usb_composite_register(&audio_driver);
++}
++module_init(init);
++
++static void __exit cleanup(void)
++{
++ usb_composite_unregister(&audio_driver);
++}
++module_exit(cleanup);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
++MODULE_LICENSE("GPL");
++
+--- /dev/null
++++ b/drivers/usb/gadget/f_audio.c
+@@ -0,0 +1,707 @@
++/*
++ * f_audio.c -- USB Audio class function driver
++ *
++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
++ * Copyright (C) 2008 Analog Devices, Inc
++ *
++ * Enter bugs at http://blackfin.uclinux.org/
++ *
++ * Licensed under the GPL-2 or later.
++ */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <asm/atomic.h>
++
++#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");
++
++/*
++ * DESCRIPTORS ... most are static, but strings and full
++ * configuration descriptors are built on demand.
++ */
++
++/*
++ * 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
++
++/* B.3.1 Standard AC Interface Descriptor */
++static struct usb_interface_descriptor ac_interface_desc __initdata = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ .bNumEndpoints = 0,
++ .bInterfaceClass = USB_CLASS_AUDIO,
++ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
++};
++
++DECLARE_USB_AC_HEADER_DESCRIPTOR(2);
++
++#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,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = HEADER,
++ .bcdADC = __constant_cpu_to_le16(0x0100),
++ .wTotalLength = __constant_cpu_to_le16(USB_DT_AC_HEADER_LENGH),
++ .bInCollection = F_AUDIO_NUM_INTERFACES,
++ .baInterfaceNr = {
++ [0] = F_AUDIO_AC_INTERFACE,
++ [1] = F_AUDIO_AS_INTERFACE,
++ }
++};
++
++#define INPUT_TERMINAL_ID 1
++static struct usb_input_terminal_descriptor input_terminal_desc = {
++ .bLength = USB_DT_AC_INPUT_TERMINAL_SIZE,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = INPUT_TERMINAL,
++ .bTerminalID = INPUT_TERMINAL_ID,
++ .wTerminalType = USB_AC_TERMINAL_STREAMING,
++ .bAssocTerminal = 0,
++ .wChannelConfig = 0x3,
++};
++
++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),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = FEATURE_UNIT,
++ .bUnitID = FEATURE_UNIT_ID,
++ .bSourceID = INPUT_TERMINAL_ID,
++ .bControlSize = 2,
++ .bmaControls[0] = (FU_MUTE | FU_VOLUME),
++};
++
++static struct usb_audio_control mute_control = {
++ .list = LIST_HEAD_INIT(mute_control.list),
++ .name = "Mute Control",
++ .type = 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",
++ .type = 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",
++ .type = FEATURE_UNIT,
++ .desc = (struct usb_descriptor_header *)&feature_unit_desc,
++};
++
++#define OUTPUT_TERMINAL_ID 3
++static struct usb_output_terminal_descriptor output_terminal_desc = {
++ .bLength = USB_DT_AC_OUTPUT_TERMINAL_SIZE,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = OUTPUT_TERMINAL,
++ .bTerminalID = OUTPUT_TERMINAL_ID,
++ .wTerminalType = USB_AC_OUTPUT_TERMINAL_SPEAKER,
++ .bAssocTerminal = FEATURE_UNIT_ID,
++ .bSourceID = FEATURE_UNIT_ID,
++};
++
++/* 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 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 usb_as_header_descriptor as_header_desc = {
++ .bLength = USB_DT_AS_HEADER_SIZE,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = AS_GENERAL,
++ .bTerminalLink = INPUT_TERMINAL_ID,
++ .bDelay = 1,
++ .wFormatTag = USB_AS_AUDIO_FORMAT_TYPE_I_PCM,
++};
++
++DECLARE_USB_AS_FORMAT_TYPE_I_DISCRETE_DESC(1);
++
++static struct usb_as_formate_type_i_discrete_descriptor_1 as_type_i_desc = {
++ .bLength = USB_AS_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubtype = FORMAT_TYPE,
++ .bFormatType = USB_AS_FORMAT_TYPE_I,
++ .bSubframeSize = 2,
++ .bBitResolution = 16,
++ .bSamFreqType = 1,
++};
++
++/* 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_AS_ENDPOINT_ADAPTIVE
++ | USB_ENDPOINT_XFER_ISOC,
++ .wMaxPacketSize = __constant_cpu_to_le16(OUT_EP_MAX_PACKET_SIZE),
++ .bInterval = 4,
++};
++
++/* Class-specific AS ISO OUT Endpoint Descriptor */
++static struct usb_as_iso_endpoint_descriptor as_iso_out_desc __initdata = {
++ .bLength = USB_AS_ISO_ENDPOINT_DESC_SIZE,
++ .bDescriptorType = USB_DT_CS_ENDPOINT,
++ .bDescriptorSubtype = EP_GENERAL,
++ .bmAttributes = 1,
++ .bLockDelayUnits = 1,
++ .wLockDelay = __constant_cpu_to_le16(1),
++};
++
++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 *)&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 *)&as_out_ep_desc,
++ (struct usb_descriptor_header *)&as_iso_out_desc,
++ NULL,
++};
++
++/* string IDs are assigned dynamically */
++
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
++
++static char manufacturer[50];
++
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings stringtab_dev = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_dev,
++};
++
++static struct usb_gadget_strings *audio_strings[] = {
++ &stringtab_dev,
++ NULL,
++};
++
++/*
++ * This function is an ALSA sound card following USB Audio Class Spec 1.0.
++ */
++
++/*-------------------------------------------------------------------------*/
++struct f_audio_buf {
++ u8 *buf;
++ int actual;
++ struct list_head list;
++};
++
++static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
++{
++ struct f_audio_buf *copy_buf;
++
++ copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
++ if (!copy_buf)
++ return (struct f_audio_buf *)-ENOMEM;
++
++ copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
++ if (!copy_buf->buf) {
++ kfree(copy_buf);
++ return (struct f_audio_buf *)-ENOMEM;
++ }
++
++ return copy_buf;
++}
++
++static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
++{
++ 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;
++
++ spinlock_t lock;
++ struct f_audio_buf *copy_buf;
++ struct work_struct playback_work;
++ struct list_head play_queue;
++
++ /* Control Set command */
++ struct list_head 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 void f_audio_playback_work(struct work_struct *data)
++{
++ struct f_audio *audio = container_of(data, struct f_audio,
++ playback_work);
++ struct f_audio_buf *play_buf;
++
++ spin_lock_irq(&audio->lock);
++ if (list_empty(&audio->play_queue)) {
++ spin_unlock_irq(&audio->lock);
++ return;
++ }
++ play_buf = list_first_entry(&audio->play_queue,
++ struct f_audio_buf, list);
++ list_del(&play_buf->list);
++ spin_unlock_irq(&audio->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)
++{
++ 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;
++ 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(&copy_buf->list, &audio->play_queue);
++ schedule_work(&audio->playback_work);
++ copy_buf = f_audio_buffer_alloc(audio_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;
++
++ err = usb_ep_queue(ep, req, GFP_ATOMIC);
++ if (err)
++ ERROR(cdev, "%s queue req: %d\n", ep->name, err);
++
++ return 0;
++
++}
++
++static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_audio *audio = req->context;
++ 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);
++ 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));
++ audio->set_con = NULL;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++static int audio_set_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;
++ u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 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);
++ struct usb_audio_control_selector *cs;
++ struct usb_audio_control *con;
++
++ 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) {
++ if (cs->id == id) {
++ list_for_each_entry(con, &cs->control, list) {
++ if (con->type == con_sel) {
++ audio->set_con = con;
++ break;
++ }
++ }
++ break;
++ }
++ }
++
++ audio->set_cmd = cmd;
++ req->context = audio;
++ req->complete = f_audio_complete;
++
++ return len;
++}
++
++static int audio_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;
++ int value = -EOPNOTSUPP;
++ u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 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);
++ struct usb_audio_control_selector *cs;
++ struct usb_audio_control *con;
++
++ 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) {
++ if (cs->id == id) {
++ list_for_each_entry(con, &cs->control, list) {
++ if (con->type == con_sel && con->get) {
++ value = con->get(con, cmd);
++ break;
++ }
++ }
++ break;
++ }
++ }
++
++ req->context = audio;
++ req->complete = f_audio_complete;
++ memcpy(req->buf, &value, len);
++
++ return len;
++}
++
++static int
++f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
++{
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * Audio class messages; interface activation uses set_alt().
++ */
++ switch (ctrl->bRequestType) {
++ case USB_AUDIO_SET_INTF:
++ value = audio_set_intf_req(f, ctrl);
++ break;
++
++ case USB_AUDIO_GET_INTF:
++ value = audio_get_intf_req(f, ctrl);
++ break;
++
++ default:
++ ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(cdev, "audio response on err %d\n", value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ 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) {
++ 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);
++
++ /*
++ * 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);
++ if (req) {
++ req->buf = kzalloc(req_buf_size,
++ GFP_ATOMIC);
++ if (req->buf) {
++ req->length = req_buf_size;
++ req->context = audio;
++ req->complete =
++ f_audio_complete;
++ err = usb_ep_queue(out_ep,
++ req, GFP_ATOMIC);
++ if (err)
++ ERROR(cdev,
++ "%s queue req: %d\n",
++ out_ep->name, err);
++ } else
++ err = -ENOMEM;
++ } else
++ err = -ENOMEM;
++ }
++
++ } else {
++ struct f_audio_buf *copy_buf = audio->copy_buf;
++ if (copy_buf) {
++ list_add_tail(&copy_buf->list,
++ &audio->play_queue);
++ schedule_work(&audio->playback_work);
++ }
++ }
++ }
++
++ return err;
++}
++
++static void f_audio_disable(struct usb_function *f)
++{
++ return;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void f_audio_build_desc(struct f_audio *audio)
++{
++ struct gaudio *card = &audio->card;
++ u8 *sam_freq;
++ int rate;
++
++ /* Set channel numbers */
++ input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
++ as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
++
++ /* Set sample rates */
++ rate = u_audio_get_playback_rate(card);
++ sam_freq = as_type_i_desc.tSamFreq[0];
++ memcpy(sam_freq, &rate, 3);
++
++ /* Todo: Set Sample bits and other parameters */
++
++ return;
++}
++
++/* audio function driver setup/binding */
++static int __init
++f_audio_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_audio *audio = func_to_audio(f);
++ int status;
++ struct usb_ep *ep;
++
++ f_audio_build_desc(audio);
++
++ /* allocate instance-specific interface IDs, and patch descriptors */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ ac_interface_desc.bInterfaceNumber = status;
++
++ 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;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
++ if (!ep)
++ goto fail;
++ audio->out_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ status = -ENOMEM;
++
++ /* supcard all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++
++ /* copy descriptors, and track endpoint copies */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ c->highspeed = true;
++ f->hs_descriptors = usb_copy_descriptors(f_audio_desc);
++ } else
++ f->descriptors = usb_copy_descriptors(f_audio_desc);
++
++ return 0;
++
++fail:
++
++ return status;
++}
++
++static void
++f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct f_audio *audio = func_to_audio(f);
++
++ usb_free_descriptors(f->descriptors);
++ kfree(audio);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* Todo: add more control selecotor 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[_CUR] = 0xffc0;
++ volume_control.data[_MIN] = 0xe3a0;
++ volume_control.data[_MAX] = 0xfff0;
++ volume_control.data[_RES] = 0x0030;
++
++ return 0;
++}
++
++/**
++ * audio_bind_config - add USB audio fucntion to a configuration
++ * @c: the configuration to supcard the USB audio function
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ */
++int __init audio_bind_config(struct usb_configuration *c)
++{
++ struct f_audio *audio;
++ int status;
++
++ /* allocate and initialize one new instance */
++ audio = kzalloc(sizeof *audio, GFP_KERNEL);
++ if (!audio)
++ return -ENOMEM;
++
++ audio->card.func.name = "g_audio";
++ audio->card.gadget = c->cdev->gadget;
++
++ INIT_LIST_HEAD(&audio->play_queue);
++ spin_lock_init(&audio->lock);
++
++ /* set up ASLA audio devices */
++ status = gaudio_setup(&audio->card);
++ if (status < 0)
++ goto setup_fail;
++
++ audio->card.func.strings = audio_strings;
++ audio->card.func.bind = f_audio_bind;
++ audio->card.func.unbind = f_audio_unbind;
++ 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;
++
++ control_selector_init(audio);
++
++ INIT_WORK(&audio->playback_work, f_audio_playback_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);
++
++ return status;
++
++add_fail:
++ gaudio_cleanup(&audio->card);
++setup_fail:
++ kfree(audio);
++ return status;
++}
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -580,6 +580,20 @@ config USB_ZERO_HNPTEST
+ the "B-Peripheral" role, that device will use HNP to let this
+ one serve as the USB host instead (in the "B-Host" role).
+
++config USB_AUDIO
++ tristate "Audio Gadget (EXPERIMENTAL)"
++ depends on SND
++ help
++ Gadget Audio is compatible with USB Audio Class specification 1.0.
++ It will include at least one AudioControl interface, zero or more
++ AudioStream interface and zero or more MIDIStream interface.
++
++ Gadget Audio will use on-board ALSA (CONFIG_SND) audio card to
++ playback or capture audio stream.
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "g_audio".
++
+ config USB_ETH
+ tristate "Ethernet Gadget (with CDC Ethernet support)"
+ depends on NET
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -31,6 +31,7 @@ obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg
+ # USB gadget drivers
+ #
+ g_zero-objs := zero.o
++g_audio-objs := audio.o
+ g_ether-objs := ether.o
+ g_serial-objs := serial.o
+ g_midi-objs := gmidi.o
+@@ -40,6 +41,7 @@ g_printer-objs := printer.o
+ g_cdc-objs := cdc2.o
+
+ obj-$(CONFIG_USB_ZERO) += g_zero.o
++obj-$(CONFIG_USB_AUDIO) += g_audio.o
+ obj-$(CONFIG_USB_ETH) += g_ether.o
+ obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
+ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
+--- /dev/null
++++ b/drivers/usb/gadget/u_audio.c
+@@ -0,0 +1,319 @@
++/*
++ * u_audio.c -- ALSA audio utilities for Gadget stack
++ *
++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
++ * Copyright (C) 2008 Analog Devices, Inc
++ *
++ * Enter bugs at http://blackfin.uclinux.org/
++ *
++ * Licensed under the GPL-2 or later.
++ */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/ctype.h>
++#include <linux/random.h>
++#include <linux/syscalls.h>
++
++#include "u_audio.h"
++
++/*
++ * This component encapsulates the ALSA devices for USB audio gadget
++ */
++
++#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
++#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
++#define FILE_CONTROL "/dev/snd/controlC0"
++
++static char *fn_play = FILE_PCM_PLAYBACK;
++module_param(fn_play, charp, S_IRUGO);
++MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
++
++static char *fn_cap = FILE_PCM_CAPTURE;
++module_param(fn_cap, charp, S_IRUGO);
++MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
++
++static char *fn_cntl = FILE_CONTROL;
++module_param(fn_cntl, charp, S_IRUGO);
++MODULE_PARM_DESC(fn_cntl, "Control device file name");
++
++/*-------------------------------------------------------------------------*/
++
++/**
++ * Some ALSA internal helper functions
++ */
++static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
++{
++ struct snd_interval t;
++ t.empty = 0;
++ t.min = t.max = val;
++ t.openmin = t.openmax = 0;
++ t.integer = 1;
++ return snd_interval_refine(i, &t);
++}
++
++static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
++ snd_pcm_hw_param_t var, unsigned int val,
++ int dir)
++{
++ int changed;
++ if (hw_is_mask(var)) {
++ struct snd_mask *m = hw_param_mask(params, var);
++ if (val == 0 && dir < 0) {
++ changed = -EINVAL;
++ snd_mask_none(m);
++ } else {
++ if (dir > 0)
++ val++;
++ else if (dir < 0)
++ val--;
++ changed = snd_mask_refine_set(
++ hw_param_mask(params, var), val);
++ }
++ } else if (hw_is_interval(var)) {
++ struct snd_interval *i = hw_param_interval(params, var);
++ if (val == 0 && dir < 0) {
++ changed = -EINVAL;
++ snd_interval_none(i);
++ } else if (dir == 0)
++ changed = snd_interval_refine_set(i, val);
++ else {
++ struct snd_interval t;
++ t.openmin = 1;
++ t.openmax = 1;
++ t.empty = 0;
++ t.integer = 0;
++ if (dir < 0) {
++ t.min = val - 1;
++ t.max = val;
++ } else {
++ t.min = val;
++ t.max = val+1;
++ }
++ changed = snd_interval_refine(i, &t);
++ }
++ } else
++ return -EINVAL;
++ if (changed) {
++ params->cmask |= 1 << var;
++ params->rmask |= 1 << var;
++ }
++ return changed;
++}
++/*-------------------------------------------------------------------------*/
++
++/**
++ * Set default hardware params
++ */
++static int playback_default_hw_params(struct gaudio_snd_dev *snd)
++{
++ struct snd_pcm_substream *substream = snd->substream;
++ struct snd_pcm_hw_params *params;
++ snd_pcm_sframes_t result;
++
++ /*
++ * SNDRV_PCM_ACCESS_RW_INTERLEAVED,
++ * SNDRV_PCM_FORMAT_S16_LE
++ * CHANNELS: 2
++ * RATE: 48000
++ */
++ snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
++ snd->format = SNDRV_PCM_FORMAT_S16_LE;
++ snd->channels = 2;
++ snd->rate = 48000;
++
++ params = kzalloc(sizeof(*params), GFP_KERNEL);
++ if (!params)
++ return -ENOMEM;
++
++ _snd_pcm_hw_params_any(params);
++ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
++ snd->access, 0);
++ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
++ snd->format, 0);
++ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
++ snd->channels, 0);
++ _snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
++ snd->rate, 0);
++
++ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
++ snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
++
++ result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
++ if (result < 0) {
++ ERROR(snd->card,
++ "Preparing sound card failed: %d\n", (int)result);
++ kfree(params);
++ return result;
++ }
++
++ /* Store the hardware parameters */
++ snd->access = params_access(params);
++ snd->format = params_format(params);
++ snd->channels = params_channels(params);
++ snd->rate = params_rate(params);
++
++ kfree(params);
++
++ INFO(snd->card,
++ "Hardware params: access %x, format %x, channels %d, rate %d\n",
++ snd->access, snd->format, snd->channels, snd->rate);
++
++ return 0;
++}
++
++/**
++ * Playback audio buffer data by ALSA PCM device
++ */
++static size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
++{
++ struct gaudio_snd_dev *snd = &card->playback;
++ struct snd_pcm_substream *substream = snd->substream;
++ struct snd_pcm_runtime *runtime = substream->runtime;
++ mm_segment_t old_fs;
++ ssize_t result;
++ snd_pcm_sframes_t frames;
++
++try_again:
++ if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
++ runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
++ result = snd_pcm_kernel_ioctl(substream,
++ SNDRV_PCM_IOCTL_PREPARE, NULL);
++ if (result < 0) {
++ ERROR(card, "Preparing sound card failed: %d\n",
++ (int)result);
++ return result;
++ }
++ }
++
++ frames = bytes_to_frames(runtime, count);
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++ result = snd_pcm_lib_write(snd->substream, buf, frames);
++ if (result != frames) {
++ ERROR(card, "Playback error: %d\n", (int)result);
++ set_fs(old_fs);
++ goto try_again;
++ }
++ set_fs(old_fs);
++
++ return 0;
++}
++
++static int u_audio_get_playback_channels(struct gaudio *card)
++{
++ return card->playback.channels;
++}
++
++static int u_audio_get_playback_rate(struct gaudio *card)
++{
++ return card->playback.rate;
++}
++
++/**
++ * Open ALSA PCM and control device files
++ * Initial the PCM or control device
++ */
++static int gaudio_open_snd_dev(struct gaudio *card)
++{
++ struct snd_pcm_file *pcm_file;
++ struct gaudio_snd_dev *snd;
++
++ if (!card)
++ return -ENODEV;
++
++ /* Open control device */
++ snd = &card->control;
++ snd->filp = filp_open(fn_cntl, O_RDWR, 0);
++ if (IS_ERR(snd->filp)) {
++ 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;
++
++ /* Open PCM playback device and setup substream */
++ snd = &card->playback;
++ 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;
++ }
++ 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->filp = NULL;
++ }
++ pcm_file = snd->filp->private_data;
++ snd->substream = pcm_file->substream;
++ snd->card = card;
++
++ return 0;
++}
++
++/**
++ * Close ALSA PCM and control device files
++ */
++static int gaudio_close_snd_dev(struct gaudio *gau)
++{
++ struct gaudio_snd_dev *snd;
++
++ /* Close control device */
++ snd = &gau->control;
++ if (!IS_ERR(snd->filp))
++ filp_close(snd->filp, current->files);
++
++ /* Close PCM playback device and setup substream */
++ snd = &gau->playback;
++ if (!IS_ERR(snd->filp))
++ filp_close(snd->filp, current->files);
++
++ /* Close PCM capture device and setup substream */
++ snd = &gau->capture;
++ if (!IS_ERR(snd->filp))
++ filp_close(snd->filp, current->files);
++
++ return 0;
++}
++
++/**
++ * gaudio_setup - setup ALSA interface and preparing for USB transfer
++ *
++ * This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
++ *
++ * Returns negative errno, or zero on success
++ */
++int __init gaudio_setup(struct gaudio *card)
++{
++ int ret;
++
++ ret = gaudio_open_snd_dev(card);
++ if (ret)
++ ERROR(card, "we need at least one control device\n");
++
++ return ret;
++
++}
++
++/**
++ * gaudio_cleanup - remove ALSA device interface
++ *
++ * This is called to free all resources allocated by @gaudio_setup().
++ */
++void gaudio_cleanup(struct gaudio *card)
++{
++ if (card)
++ gaudio_close_snd_dev(card);
++}
++
+--- /dev/null
++++ b/drivers/usb/gadget/u_audio.h
+@@ -0,0 +1,56 @@
++/*
++ * u_audio.h -- interface to USB gadget "ALSA AUDIO" utilities
++ *
++ * Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
++ * Copyright (C) 2008 Analog Devices, Inc
++ *
++ * Enter bugs at http://blackfin.uclinux.org/
++ *
++ * Licensed under the GPL-2 or later.
++ */
++
++#ifndef __U_AUDIO_H
++#define __U_AUDIO_H
++
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/usb/audio.h>
++#include <linux/usb/composite.h>
++
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++
++#include "gadget_chips.h"
++
++/*
++ * This represents the USB side of an audio card device, managed by a USB
++ * function which provides control and stream interfaces.
++ */
++
++struct gaudio_snd_dev {
++ struct gaudio *card;
++ struct file *filp;
++ struct snd_pcm_substream *substream;
++ int access;
++ int format;
++ int channels;
++ int rate;
++};
++
++struct gaudio {
++ struct usb_function func;
++ struct usb_gadget *gadget;
++
++ /* ALSA sound device interfaces */
++ struct gaudio_snd_dev control;
++ struct gaudio_snd_dev playback;
++ struct gaudio_snd_dev capture;
++
++ /* TODO */
++};
++
++int gaudio_setup(struct gaudio *card);
++void gaudio_cleanup(struct gaudio *card);
++
++#endif /* __U_AUDIO_H */