[PATCH RFC v2] usb: gadget: audio-video-hid functions and composite gadget

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



History:
	v2: fixed line-wrapping problem

This adds support for three gadget functions and a composite gadget
driver to use them together.

The main objective of the g_avh composite is to bundle the audio, video
and hid function implementations into one coherent gadget that allows
the concept of 'streaming user interface'. 

For all these functions, it has been a design goal to allow transparent
access from user-space. We know that for video, audio and hid
alternatives have already been submitted to this mailing list; we feel
it is right to publish this work anyway and welcome a technical
discussion on whether (and if so how) to integrate similar function
implementations.

f_vdc
-----
The video function is an implementation of the Video Device Class. It
supports Bulk video mode only. At initialization, it attaches itself to
a framebuffer device (defaults to the first), and takes its properties
(currently only width and height) and exposes these to the USB host.
This implementation requires the framebuffer to hold pixel data in
RGB565 format.

A video frame is send over a Bulk endpoint each time the framebuffer
'pans', so it can be used 'transparently' by applications that already
deal with a double-buffered framebuffer.

f_adc
-----
This audio function is an implementation of the Audio Device Class and
implements bi-directional audio streaming. The user-space side of this
function is an ALSA soundcard with two devices, one for input and one
for output. 

f_hidc
------
This is an 'implementation' of the Human Interface Device Class. There
is a small framework that supports multiple reports. Added are two
report implementation. One is for a report that allows delivery of 'raw
key' events, and one is for delivery of 'raw touchpanel' events. Both
feed received events directly into the input framework.

Using these functions all together allows an embedded device to use the
abstractions provided by the linux framebuffer, ALSA soundcard interface
and input framework to work with user-interface elements that are
implemented on the USB host. 

Signed-off-by: Robert Lukassen <rolk@ehv-nb-001.(none)>
---
 drivers/usb/gadget/Kconfig           |   57 +-
 drivers/usb/gadget/Makefile          |    2 +
 drivers/usb/gadget/avh.c             |  324 +++++
 drivers/usb/gadget/f_adc.c           | 2157 ++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/f_adc.h           |  729 ++++++++++++
 drivers/usb/gadget/f_hidc.c          |  745 ++++++++++++
 drivers/usb/gadget/f_hidc.h          |   97 ++
 drivers/usb/gadget/f_hidc_keyboard.c |  187 +++
 drivers/usb/gadget/f_hidc_panel.c    |  156 +++
 drivers/usb/gadget/f_vdc.c           | 1863 +++++++++++++++++++++++++++++
 drivers/usb/gadget/f_vdc.h           |  345 ++++++
 11 files changed, 6661 insertions(+), 1 deletions(-)
 create mode 100644 drivers/usb/gadget/avh.c
 create mode 100644 drivers/usb/gadget/f_adc.c
 create mode 100644 drivers/usb/gadget/f_adc.h
 create mode 100644 drivers/usb/gadget/f_hidc.c
 create mode 100644 drivers/usb/gadget/f_hidc.h
 create mode 100644 drivers/usb/gadget/f_hidc_keyboard.c
 create mode 100644 drivers/usb/gadget/f_hidc_panel.c
 create mode 100644 drivers/usb/gadget/f_vdc.c
 create mode 100644 drivers/usb/gadget/f_vdc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 11a3e0f..5954f43 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -863,11 +863,66 @@ config USB_G_MULTI_CDC
 
 	  If unsure, say "y".
 
+config USB_AVH_COMPOSITE
+    tristate "Audio Video HID Composite Device"
+    help
+      The composite Audio Video HID device enables the device to stream
+      video and audio over USB and receive HID touchscreen events.
+
+config USB_AVH_FUNCTION_ECM
+	bool "Enable AVH ECM function"
+	depends on (USB_AVH_COMPOSITE != n)
+	default n
+	help
+	  This includes the ethernet function in the AVH composite USB gadget.
+
+config USB_AVH_FUNCTION_VDC
+	bool "Enable AVH VDC function"
+	depends on (USB_AVH_COMPOSITE != n)
+	default n
+	help
+	  This includes the video function in the AVH composite USB gadget.
+
+config USB_AVH_FUNCTION_HIDC
+	bool "Enable AVH HIDC function"
+	depends on (USB_AVH_COMPOSITE != n)
+	default n
+	help
+	  This includes the event function in the AVH composite USB gadget.
+
+config USB_AVH_PANEL
+	bool "Enable AVH HID (touch) panel event support"
+	depends on (USB_AVH_FUNCTION_HIDC != n)
+	default y
+	help
+	  Enabling this feature allows HID (touch) panel events to be received by
+	  the AVH gadget. The events are forwarded to the linux input framework.
+
+config USB_AVH_KEYBOARD
+	bool "Enable AVH HID keyboard event support"
+	depends on (USB_AVH_FUNCTION_HIDC != n)
+	default y
+	help
+	  Enabling this feature allows HID keyboard events to be received by the
+	  AVH gadget. The events are forwarded to the linux input framework.
 
 # put drivers that need isochronous transfer support (for audio
 # or video class gadget drivers), or specific hardware, here.
 
-# - none yet
+config USB_AVH_FUNCTION_ADC
+	bool "Enable AVH ADC function"
+	depends on (USB_AVH_COMPOSITE != n)
+	default n
+	help
+	  This includes the audio function in the AVH composite USB gadget.
+
+config USB_AVH_FUNCTION_ADC_MIXER_CONTROL
+    bool "Enable USB audio mixer control"
+    depends on (USB_AVH_FUNCTION_ADC != n)
+    default n
+    help
+      This enables ALSA mixer controls for controlling volume of playback and capture
+      device.
 
 endchoice
 
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 43b51da..3b3cc8a 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -44,6 +44,7 @@ g_printer-objs			:= printer.o
 g_cdc-objs			:= cdc2.o
 g_multi-objs			:= multi.o
 g_nokia-objs			:= nokia.o
+g_avh-objs			:= avh.o
 
 obj-$(CONFIG_USB_ZERO)		+= g_zero.o
 obj-$(CONFIG_USB_AUDIO)		+= g_audio.o
@@ -57,4 +58,5 @@ obj-$(CONFIG_USB_MIDI_GADGET)	+= g_midi.o
 obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
 obj-$(CONFIG_USB_G_MULTI)	+= g_multi.o
 obj-$(CONFIG_USB_G_NOKIA)	+= g_nokia.o
+obj-$(CONFIG_USB_AVH_COMPOSITE) += g_avh.o
 
diff --git a/drivers/usb/gadget/avh.c b/drivers/usb/gadget/avh.c
new file mode 100644
index 0000000..3c3e3f7
--- /dev/null
+++ b/drivers/usb/gadget/avh.c
@@ -0,0 +1,324 @@
+/* drivers/usb/gadget/avh.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/fb.h>
+#include "gadget_chips.h"
+
+#include "f_vdc.h"
+#include "f_adc.h"
+#include "f_hidc.h"
+#include "u_ether.h"
+
+/*
+ * 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 "config.c"
+#include "epautoconf.c"
+#include "usbstring.c"
+
+#ifdef CONFIG_USB_AVH_FUNCTION_VDC
+#include "f_vdc.c"
+#endif
+#ifdef CONFIG_USB_AVH_FUNCTION_ADC
+#include "f_adc.c"
+#endif
+#ifdef CONFIG_USB_AVH_FUNCTION_HIDC
+#include "f_hidc.c"
+#include "f_hidc_panel.c"
+#include "f_hidc_keyboard.c"
+#endif
+#ifdef CONFIG_USB_AVH_FUNCTION_ECM
+#include "f_ecm.c"
+#include "u_ether.c"
+
+static unsigned char ecm_ethaddr[ETH_ALEN] = { 0 };
+#endif
+
+#define DRIVER_DESC				"Composite ADC/VDC/HIDC Gadget"
+#define DRIVER_VERSION				"v1.1"
+#define DRIVER_MANUFACTURER			"TomTom International (Spits)"
+
+/* fb_index=<frame buffer nr>	; if specified, the video driver
+				; will attach to this frame buffer
+				; regardless of other parameters
+*/
+#define DRIVER_DEFAULT_FRAMEBUFFER_INDEX	0
+
+#define DRIVER_DEFAULT_CHANNELS_PLAYBACK	2
+#define DRIVER_DEFAULT_SAMPLERATE_PLAYBACK	44100
+#define DRIVER_DEFAULT_INTERVAL_PLAYBACK	1
+
+#define DRIVER_DEFAULT_CHANNELS_CAPTURE		1
+#define DRIVER_DEFAULT_SAMPLERATE_CAPTURE	16000
+#define DRIVER_DEFAULT_INTERVAL_CAPTURE		1
+
+#define DRIVER_VENDOR_NUM			0x1390
+#define DRIVER_PRODUCT_NUM			0x0005
+#define DRIVER_VERSION_NUM			0x0100
+
+#define STRING_MANUFACTURER_IDX			0
+#define STRING_PRODUCT_IDX			1
+#define STRING_SERIAL_IDX			2
+
+static struct {
+	int					fb_index;
+    struct fadc_device_parameters		playback;
+    struct fadc_device_parameters		capture;
+} modparam = {            // Default values
+    .fb_index 		= DRIVER_DEFAULT_FRAMEBUFFER_INDEX,
+    .playback = {
+	.channels 	= DRIVER_DEFAULT_CHANNELS_PLAYBACK,
+	.samplerate 	= DRIVER_DEFAULT_SAMPLERATE_PLAYBACK,
+    },
+    .capture = {
+	.channels 	= DRIVER_DEFAULT_CHANNELS_CAPTURE,
+	.samplerate 	= DRIVER_DEFAULT_SAMPLERATE_CAPTURE,
+    },
+};
+
+module_param_named(fb_index, modparam.fb_index, int, S_IRUGO);
+MODULE_PARM_DESC(fb_index, "(video) framebuffer to attach to");
+
+module_param_named(playback_channels, modparam.playback.channels, ulong, S_IRUGO);
+MODULE_PARM_DESC(playback_channels, "(audio) playback / number of channels");
+
+module_param_named(playback_samplerate, modparam.playback.samplerate, ulong, S_IRUGO);
+MODULE_PARM_DESC(to_host_samplerate, "(audio) playback / samplerate");
+
+module_param_named(capture_channels, modparam.capture.channels, ulong, S_IRUGO);
+MODULE_PARM_DESC(from_host_channels, "(audio) capture / number of channels");
+
+module_param_named(capture_samplerate, modparam.capture.samplerate, ulong, S_IRUGO);
+MODULE_PARM_DESC(capture_samplerate, "(audio) capture / samplerate");
+
+static char manufacturer[]	= DRIVER_MANUFACTURER;
+static char product[]		= DRIVER_DESC;
+static char serial[]		= "0000000000";
+
+static struct usb_string strings_dev[] = {
+	[STRING_MANUFACTURER_IDX].s	= manufacturer,
+	[STRING_PRODUCT_IDX].s		= product,
+	[STRING_SERIAL_IDX].s		= serial,
+	{}
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+	.language	= 0x0409,
+	.strings	= strings_dev,
+};
+
+static struct usb_gadget_strings *avh_strings[] = {
+	&stringtab_dev,
+	NULL,
+};
+
+static struct usb_device_descriptor device_desc = {
+	.bLength              =	sizeof device_desc,
+	.bDescriptorType      =	USB_DT_DEVICE,
+
+	.bcdUSB               =	__constant_cpu_to_le16 (0x0200),
+
+	.bDeviceClass         =	0xEF,
+	.bDeviceSubClass      =	0x02,
+	.bDeviceProtocol      =	0x01,
+
+	.idVendor             =	__constant_cpu_to_le16 (DRIVER_VENDOR_NUM),
+	.idProduct            =	__constant_cpu_to_le16 (DRIVER_PRODUCT_NUM),
+	.bcdDevice	      = __constant_cpu_to_le16 (DRIVER_VERSION_NUM),
+
+	.bNumConfigurations   =	0x01,
+};
+
+
+static void avh_config_unbind(struct usb_configuration *c)
+{
+	/* normally, the composite framework will make sure all added functions
+	   will have had their unbind function called when we arrive here,
+	   but when something fails during initialisation, it won't;
+	   so we need to redo that work here */
+
+	while (!list_empty(&c->functions)) {
+		struct usb_function *f;
+
+		f = list_first_entry(&c->functions,
+				struct usb_function, list);
+		list_del(&f->list);
+		if (f->unbind) {
+			f->unbind(c, f);
+		}
+	}
+}
+
+
+/* called when a host has selected a (the) configuration for this device */
+static int __init avh_config_bind(struct usb_configuration *c)
+{
+	int ret;
+
+	/* first, those functions that need bulk endpoints */
+#ifdef CONFIG_USB_AVH_FUNCTION_VDC
+	struct fb_info* fb;
+	fvdc_function_driver.fb_index = modparam.fb_index;
+
+	ret = usb_add_function(c, &fvdc_function_driver.func);
+	if(ret) {
+		goto fail;
+	}
+	fb = fvdc_attached_framebuffer();
+#endif
+
+#ifdef CONFIG_USB_AVH_FUNCTION_ECM
+	ret = ecm_bind_config(c, ecm_ethaddr);
+	if(ret) {
+		goto fail;
+	}
+#endif
+
+	/* then those that use no bulk, but interrupt endpoints */
+
+#ifdef CONFIG_USB_AVH_FUNCTION_HIDC
+	if (fb) {
+		fhidc_function_driver.param.limits.x = fb->var.xres;
+		fhidc_function_driver.param.limits.y = fb->var.yres;
+	}
+
+	fhidc_function_driver.param.vendor  = device_desc.idVendor;
+	fhidc_function_driver.param.product = device_desc.idProduct;
+	fhidc_function_driver.param.version = device_desc.bcdDevice;
+
+	ret = usb_add_function(c, &fhidc_function_driver.func);
+	if(ret) {
+		goto fail;
+	}
+#endif
+
+#ifdef CONFIG_USB_AVH_FUNCTION_ADC
+	fadc_function_driver.playback = modparam.playback;
+	fadc_function_driver.capture  = modparam.capture;
+
+	ret = usb_add_function(c, &fadc_function_driver.func);
+	if(ret) {
+		goto fail;
+	}
+#endif
+
+	return 0;
+
+fail:
+	avh_config_unbind(c);
+	return ret;
+};
+
+static struct usb_configuration composite_avh_config_driver = {
+	.bind			= avh_config_bind,
+	.unbind			= avh_config_unbind,
+	.bConfigurationValue	= 1,
+	.bmAttributes		= USB_CONFIG_ATT_ONE,
+	.bMaxPower		= 250,
+};
+
+static int __init avh_bind(struct usb_composite_dev *cdev) {
+	int gcnum = 0;
+	int status = 0;
+	struct usb_gadget *gadget = cdev->gadget;
+
+	gcnum = usb_gadget_controller_number(gadget);
+	if(gcnum >= 0) {
+		device_desc.bcdDevice = cpu_to_le16(0x0200 | gcnum);
+	} else {
+		device_desc.bcdDevice = cpu_to_le16(0x0200 | 0x0099);
+	}
+
+	status = usb_string_id(cdev);
+	if(status < 0) goto fail;
+
+	strings_dev[STRING_PRODUCT_IDX].id = status;
+	device_desc.iProduct = status;
+
+	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_SERIAL_IDX].id = status;
+	device_desc.iSerialNumber = status;
+
+
+#ifdef CONFIG_USB_AVH_FUNCTION_ECM
+	status = gether_setup(gadget, ecm_ethaddr);
+	if (status) {
+		goto fail;
+	} else {
+		printk(KERN_INFO "fhidc: ecm mac address {%02x%02x%02x%02x%02x%02x}\n",
+			ecm_ethaddr[0],ecm_ethaddr[1],ecm_ethaddr[2],ecm_ethaddr[3],ecm_ethaddr[4],ecm_ethaddr[5]);
+	}
+#endif
+
+	status = usb_add_config(cdev, &composite_avh_config_driver);
+	if(status < 0) {
+		goto fail;
+	}
+
+
+fail:
+	return status;
+}
+
+static int avh_unbind(struct usb_composite_dev *cdev)
+{
+#ifdef CONFIG_USB_AVH_FUNCTION_ECM
+	gether_cleanup();
+#endif
+	return 0;
+}
+
+
+static struct usb_composite_driver avh_driver = {
+	.name	 = "composite_avh",
+	.dev	 = &device_desc,
+	.strings = avh_strings,
+	.bind	 = avh_bind,
+	.unbind	 = __exit_p(avh_unbind),
+};
+
+int __init composite_avh_init(void)
+{
+	composite_avh_config_driver.label = strings_dev[STRING_PRODUCT_IDX].s;
+
+	return usb_composite_register(&avh_driver);
+}
+module_init(composite_avh_init);
+
+void __exit composite_avh_exit(void)
+{
+	usb_composite_unregister(&avh_driver);
+	/* a fix for badly behaving gadget controller drivers
+	   that do not call driver->unbind (== composite->unbind)
+	   when unregistering the gadget */
+
+	avh_config_unbind(&composite_avh_config_driver);
+}
+module_exit(composite_avh_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Maarten Heidenrath, Robert Lukassen, Jan Willem van den Brand");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_adc.c b/drivers/usb/gadget/f_adc.c
new file mode 100644
index 0000000..e85f98e
--- /dev/null
+++ b/drivers/usb/gadget/f_adc.c
@@ -0,0 +1,2157 @@
+/* drivers/usb/gadget/f_adc.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/timer.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/composite.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include <sound/soc.h>
+#include "gadget_chips.h"
+#include "f_adc.h"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+/* BINTERVAL:
+
+	The interval between packets is given by:
+
+	2 ^ (bInterval - 1) * time-unit
+
+	Where time-unit is 1 millisecond on a full speed bus, and 1/8
+	millisecond on  a high speed bus.
+				bInterval value
+	time interval	high speed	full speed
+	------------------------------------------
+	1 millisec	4		1
+	2 millisec	5		2
+	4 millisec	6		3
+	8 millisec	7		4
+
+	For 44KHz stereo 16 bit PCM sound, 4 millisecond is the
+	longest period that will 'fit', otherwise the packets
+	need to be larger than the allowed maximum.
+*/
+
+#define FADC_DEFAULT_CHANNELS_PLAYBACK			2
+#define FADC_DEFAULT_SAMPLERATE_PLAYBACK		44100
+#define FADC_DEFAULT_FULL_SPEED_BINTERVAL_PLAYBACK	1
+#define FADC_DEFAULT_HIGH_SPEED_BINTERVAL_PLAYBACK	\
+	(FADC_DEFAULT_FULL_SPEED_BINTERVAL_PLAYBACK + 3)
+#define FADC_DEFAULT_INTERVAL_PLAYBACK		\
+	( 1 << (FADC_DEFAULT_FULL_SPEED_BINTERVAL_PLAYBACK - 1))
+#define FADC_DEFAULT_CHANNELS_CAPTURE			1
+
+#define FADC_DEFAULT_SAMPLERATE_CAPTURE			16000
+#define FADC_DEFAULT_FULL_SPEED_BINTERVAL_CAPTURE	1
+#define FADC_DEFAULT_HIGH_SPEED_BINTERVAL_CAPTURE	\
+	(FADC_DEFAULT_FULL_SPEED_BINTERVAL_CAPTURE + 3)
+#define FADC_DEFAULT_INTERVAL_CAPTURE			\
+	( 1 << (FADC_DEFAULT_FULL_SPEED_BINTERVAL_CAPTURE - 1))
+
+#define FADC_MAX_ISO_PACKET_SIZE			1023
+
+/* The USB packet transfers need to support the ALSA data rate as close as
+   possible. ALSA produces data in 'periods', with a data rate that equals:
+
+	channels * sample_width * sample_rate
+
+  For the to-host device, this amount of data needs to be transported in a given
+  number of packet transactions. The size of these packets may vary (within the
+  limits imposed by the endpoint), but the host will ensure enough timeslots to
+  transfer the data, and will poll the isochronous endpoint at regular
+  intervals.
+
+  On a full speed bus, the amount of isochronous packets that may be send is
+  limited to at most 1 per frame (1ms), on a high speed bus it is limited to at
+  most 3 per microframe.
+
+  If the maximum amount of transactions is used for a full speed bus (1000), the
+  same amount of transactions can be used on a high speed bus. The maximum
+  packet sizes do not really matter, (1023 vs. 1024).
+
+  As each isochronous packet should contain an integral number of 'audio frames'
+  (= samples for all channels), it is unlikely that the amount of data to be
+  transferred in these 1000 transactions corresponds to an exact integral number
+  of packets.
+
+  To accommodate the excess audio frames that are build up over a series of
+  isochronous packets, an alignment packet will be send after a certain amount
+  of nominal packets.
+
+  A precomputed table is used to determine: the size of a nominal packet, the
+  size of an alignment packet, the number of packets in an alignment sequence.
+  The last packet of an alignment sequence is an alignment packet.
+
+  The table lists the settings for a mono-source. The actual nominal and
+  alignment packet size need to be multiplied by the number of channels. It is
+  important that the following always holds:
+
+  channels * alignment_size <= FADC_MAX_ISO_PACKET_SIZE
+
+  That will ensure that the packet never exceeds the full speed limit of 1023
+  bytes. The high speed limit is 1024 bytes, but it does not make sense to make
+  use of that difference.
+
+  For the capture device, the data comes in as packets whose length is
+  determined by the host.
+ */
+
+static struct fadc_packet_setting {
+	unsigned int channels;
+	unsigned int samplerate;
+	unsigned int alsa_rate;
+	unsigned int nominal_size;
+	unsigned int alignment_size;
+	unsigned int align_length;
+} fadc_packet_settings[] = {
+	{ 1, 8000, SNDRV_PCM_RATE_8000, 16, 16, 0},
+	{ 1, 11025, SNDRV_PCM_RATE_11025, 22, 24, 40},
+	{ 1, 16000, SNDRV_PCM_RATE_16000, 32, 32, 0},
+	{ 1, 22050, SNDRV_PCM_RATE_22050, 44, 46, 20},
+	{ 1, 32000, SNDRV_PCM_RATE_32000, 64, 64, 0},
+	{ 1, 44100, SNDRV_PCM_RATE_44100, 88, 90, 10},
+	{ 1, 48000, SNDRV_PCM_RATE_48000, 96, 96, 0},
+	{ 0},
+};
+
+struct fadc_volume_setting {
+	long int gain;
+	unsigned long int shift;
+	long int db;
+};
+
+#define FADC_IDENT_GAIN				32768
+#define FADC_IDENT_SHIFT			15
+#define FADC_TIMER_TICKS			(HZ / 10)
+#define FADC_ALSA_DRIVER_NAME			"snd_usb_audio"
+#define FADC_HELPBUF_SIZE			1024
+#define FADC_PLAYBACK_RINGBUFFER_SIZE		(48*1024)
+#define FADC_CAPTURE_RINGBUFFER_SIZE		(16*1024)
+#define FADC_PREALLOC_SIZE			\
+	(FADC_PLAYBACK_RINGBUFFER_SIZE + FADC_CAPTURE_RINGBUFFER_SIZE)
+
+enum fadc_dev_state {
+	FADC_STATE_VOID,
+	FADC_STATE_IDLE,
+	FADC_STATE_CONFIGURED,
+	FADC_STATE_SELECTED,
+	FADC_STATE_PREPARED,	// received prepare trigger from ALSA
+	FADC_STATE_READY,	// configured && prepared
+	FADC_STATE_STREAMING,	// ready && start trigger && no stop trigger yet
+	FADC_STATE_VIRTUAL,	// prepared && start trigger (no USB connection)
+};
+
+struct fadc_dev_pcm {
+	unsigned int ring_size;
+	unsigned int period_size;
+	unsigned int period_pos;
+	unsigned int ring_pos;
+};
+
+static struct snd_card *fadc_card = 0;
+
+static struct fadc_dev {
+	char *name;
+	int alt;
+	struct fadc_dev_pcm pcm;
+	struct snd_pcm_substream *substream;
+	enum fadc_dev_state state;
+
+	struct usb_request *request;
+	unsigned char *databuf;
+	struct usb_ep *iso;
+	struct usb_endpoint_descriptor *iso_desc;
+	struct usb_endpoint_descriptor *fs_iso_desc;
+	struct usb_endpoint_descriptor *hs_iso_desc;
+
+	struct fadc_packet_setting settings;
+	struct fadc_volume_setting volume;
+
+	struct timer_list timer;
+
+	unsigned int packet_index;
+} *fadc_playback_dev = 0, *fadc_capture_dev = 0;
+
+static inline void
+fadc_set_dev_state(struct fadc_dev *dev, enum fadc_dev_state state)
+{
+	if (!dev)
+		return;
+
+	dev->state = state;
+}
+
+#define FADC_STRING_IDX			0
+#define FADC_PLAYBACK_STRING_IDX	1
+#define FADC_CAPTURE_STRING_IDX		2
+
+static char fadc_name[] = "USB Audio";
+static char fadc_func_name[] = "faudio";
+
+/* the following names are used in USB descriptors, 'capture' and 'playback'
+   should be understood in 'host context' */
+static char fadc_playback_name[] = "USB Audio Capture (Device-To-Host)";
+static char fadc_capture_name[] = "USB Audio Playback (Host-To-Device)";
+
+/* ########################################################################## */
+/* #################### USB specific part of the driver ##################### */
+/* ########################################################################## */
+
+#define FADC_ALTERNATE_IDLE	0x00
+#define FADC_ALTERNATE_ACTIVE	0x01
+
+static struct usb_string fadc_strings_dev[] = {
+	[FADC_STRING_IDX].s = fadc_name,
+	[FADC_PLAYBACK_STRING_IDX].s = fadc_playback_name,
+	[FADC_CAPTURE_STRING_IDX].s = fadc_capture_name,
+	{},
+};
+
+static struct usb_gadget_strings fadc_english_strings = {
+	.language = 0x0409,
+	.strings = fadc_strings_dev,
+};
+
+static struct usb_gadget_strings *fadc_strings[] = {
+	&fadc_english_strings,
+	0,
+};
+
+static struct usb_interface_assoc_descriptor ac_intf_assoc_desc = {
+	.bLength = sizeof ac_intf_assoc_desc,
+	.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+	.bFirstInterface = 0x00,
+	.bInterfaceCount = 0x03,
+	.bFunctionClass = AUDIO,
+	.bFunctionSubClass = FUNCTION_SUBCLASS_UNDEFINED,
+	.bFunctionProtocol = 0x00,
+};
+
+static struct usb_interface_descriptor audio_control_interface_desc = {
+	.bLength = sizeof audio_control_interface_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0x00,
+	.bAlternateSetting = FADC_ALTERNATE_IDLE,
+	.bNumEndpoints = 0x00,
+	.bInterfaceClass = AUDIO,
+	.bInterfaceSubClass = AUDIOCONTROL,
+	.bInterfaceProtocol = 0,
+};
+
+static struct input_terminal_descriptor_10 playback_input_terminal_desc10 = {
+	.bLength = sizeof playback_input_terminal_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = INPUT_TERMINAL,
+	.bTerminalID = 0x01,
+	.wTerminalType = __constant_cpu_to_le16(0x0201),
+	.bAssocTerminal = 0x02,
+	.bNrChannels = FADC_DEFAULT_CHANNELS_PLAYBACK,
+	.wChannelConfig = 0x0000,
+};
+
+static struct output_terminal_descriptor_10 playback_output_terminal_desc10 = {
+	.bLength = sizeof playback_output_terminal_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = OUTPUT_TERMINAL,
+	.bTerminalID = 0x02,
+	.wTerminalType = __constant_cpu_to_le16(0x0101),
+	.bAssocTerminal = 0x01,
+	.bSourceID = 0x01,
+};
+
+static struct input_terminal_descriptor_10 capture_input_terminal_desc10 = {
+	.bLength = sizeof capture_input_terminal_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = INPUT_TERMINAL,
+	.bTerminalID = 0x04,
+	.wTerminalType = __constant_cpu_to_le16(0x0101),
+	.bAssocTerminal = 0x03,
+	.bNrChannels = FADC_DEFAULT_CHANNELS_CAPTURE,
+	.wChannelConfig = 0x0000,
+};
+
+static struct output_terminal_descriptor_10 capture_output_terminal_desc10 = {
+	.bLength = sizeof capture_output_terminal_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = OUTPUT_TERMINAL,
+	.bTerminalID = 0x03,
+	.wTerminalType = __constant_cpu_to_le16(0x0301),
+	.bAssocTerminal = 0x04,
+	.bSourceID = 0x04,
+};
+
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+static struct uac_ac_header_descriptor_v1_2 ac_header_desc2 = {
+	.bLength = sizeof ac_header_desc2,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = HEADER,
+	.bcdADC = __constant_cpu_to_le16(0x0100),
+	.wTotalLength = __constant_cpu_to_le16(sizeof ac_header_desc2 +
+					       sizeof
+					       playback_input_terminal_desc10 +
+					       sizeof
+					       playback_output_terminal_desc10 +
+					       sizeof
+					       capture_input_terminal_desc10 +
+					       sizeof
+					       capture_output_terminal_desc10),
+	.bInCollection = 0x02,
+	.baInterfaceNr[0] = 0x01,
+	.baInterfaceNr[1] = 0x02,
+};
+
+static struct standard_as_interface_descriptor pb_audio_stream_intf_alt0_desc = {
+	.bLength = sizeof pb_audio_stream_intf_alt0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0x01,
+	.bAlternateSetting = FADC_ALTERNATE_IDLE,
+	.bNumEndpoints = 0x00,
+	.bInterfaceClass = AUDIO,
+	.bInterfaceSubClass = AUDIOSTREAMING,
+	.bInterfaceProtocol = 0,
+};
+
+static struct standard_as_interface_descriptor pb_audio_stream_intf_alt1_desc = {
+	.bLength = sizeof pb_audio_stream_intf_alt1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0x01,
+	.bAlternateSetting = FADC_ALTERNATE_ACTIVE,
+	.bNumEndpoints = 0x01,
+	.bInterfaceClass = AUDIO,
+	.bInterfaceSubClass = AUDIOSTREAMING,
+	.bInterfaceProtocol = 0,
+};
+
+static struct cs_as_interface_descriptor_10 pb_cs_audio_stream_intf_desc10 = {
+	.bLength = sizeof pb_cs_audio_stream_intf_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = AS_GENERAL,
+	.bTerminalLink = 0x02,
+	.bDelay = 0xFF,
+	.wFormatTag = __constant_cpu_to_le16(0x0001),	/* PCM */
+};
+
+static struct format_type_descriptor_disc pb_format_type_desc = {
+/* this struct is defined to hold 4 tSamFreq's, we use it holding just one,
+   so do not use 'sizeof' to determine bLength */
+	.bLength = 0x0B,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = FORMAT_TYPE,
+	.bFormatType = FORMAT_TYPE_I,
+	.bSubFrameSize = 0x02,
+	.bBitResolution = 0x10,
+};
+
+static struct standard_as_isochronous_audio_data_endpoint_descriptor_10
+ pb_as_hs_iso_audio_data_ep_desc = {
+	.bLength = sizeof pb_as_hs_iso_audio_data_ep_desc,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = 0x0D,
+	.wMaxPacketSize = __constant_cpu_to_le16(0x400),
+	.bInterval = FADC_DEFAULT_HIGH_SPEED_BINTERVAL_PLAYBACK,
+	.bRefresh = 0x00,
+	.bSyncAddress = 0x00,
+};
+
+static struct standard_as_isochronous_audio_data_endpoint_descriptor_10
+ pb_as_fs_iso_audio_data_ep_desc = {
+	.bLength = sizeof pb_as_fs_iso_audio_data_ep_desc,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = 0x0D,
+	.wMaxPacketSize = __constant_cpu_to_le16(0x3FF),
+	.bInterval = FADC_DEFAULT_FULL_SPEED_BINTERVAL_PLAYBACK,
+	.bRefresh = 0x00,
+	.bSyncAddress = 0x00,
+};
+
+static struct cs_as_isochronous_audio_data_endpoint_descriptor_10
+ pb_cs_as_iso_audio_data_ep_desc10 = {
+	.bLength = sizeof pb_cs_as_iso_audio_data_ep_desc10,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype = EP_GENERAL,
+	.bmAttributes = 0x00,
+	.bLockDelayUnits = 0x00,
+	.wLockDelay = 0x0000,
+};
+
+static struct standard_as_interface_descriptor cp_audio_stream_intf_alt0_desc = {
+	.bLength = sizeof cp_audio_stream_intf_alt0_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0x02,
+	.bAlternateSetting = FADC_ALTERNATE_IDLE,
+	.bNumEndpoints = 0x00,
+	.bInterfaceClass = AUDIO,
+	.bInterfaceSubClass = AUDIOSTREAMING,
+	.bInterfaceProtocol = 0,
+};
+
+static struct standard_as_interface_descriptor
+ cp_audio_stream_intf_alt1_desc = {
+	.bLength = sizeof cp_audio_stream_intf_alt1_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0x02,
+	.bAlternateSetting = FADC_ALTERNATE_ACTIVE,
+	.bNumEndpoints = 0x01,
+	.bInterfaceClass = AUDIO,
+	.bInterfaceSubClass = AUDIOSTREAMING,
+	.bInterfaceProtocol = 0,
+};
+
+static struct cs_as_interface_descriptor_10 cp_cs_audio_stream_intf_desc10 = {
+	.bLength = sizeof cp_cs_audio_stream_intf_desc10,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = AS_GENERAL,
+	.bTerminalLink = 0x04,
+	.bDelay = 0xFF,
+	.wFormatTag = __constant_cpu_to_le16(0x0001),	/* PCM */
+};
+
+static struct format_type_descriptor_disc cp_format_type_desc = {
+/* this struct is defined to hold 4 tSamFreq's, we use it holding just one,
+   so do not use 'sizeof' to determine bLength */
+	.bLength = 0x0B,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = FORMAT_TYPE,
+	.bFormatType = FORMAT_TYPE_I,
+	.bSubFrameSize = 0x02,
+	.bBitResolution = 0x10,
+};
+
+/* bInterval:
+
+	For full/high speed isochronous endpoints 'bInterval' determines the
+	time interval between to transactions.
+	The time is expressed in units of frames (full speed) or microframe
+	(high speed) and according to the formula: 2^(bInterval-1).
+
+	bInterval should be between 1 and 16 */
+
+static struct standard_as_isochronous_audio_data_endpoint_descriptor_10
+ cp_as_hs_iso_audio_data_ep_desc = {
+	.bLength = sizeof cp_as_hs_iso_audio_data_ep_desc,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_OUT,
+	.bmAttributes = 0x0D,
+	.wMaxPacketSize = __constant_cpu_to_le16(0x400),
+	.bInterval = FADC_DEFAULT_HIGH_SPEED_BINTERVAL_CAPTURE,
+	.bRefresh = 0x00,
+	.bSyncAddress = 0x00,
+};
+
+static struct standard_as_isochronous_audio_data_endpoint_descriptor_10
+ cp_as_fs_iso_audio_data_ep_desc = {
+	.bLength = sizeof cp_as_fs_iso_audio_data_ep_desc,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_OUT,
+	.bmAttributes = 0x0D,
+	.wMaxPacketSize = __constant_cpu_to_le16(0x3FF),
+	.bInterval = FADC_DEFAULT_FULL_SPEED_BINTERVAL_CAPTURE,
+	.bRefresh = 0x00,
+	.bSyncAddress = 0x00,
+};
+
+static struct cs_as_isochronous_audio_data_endpoint_descriptor_10
+ cp_cs_as_iso_audio_data_ep_desc10 = {
+	.bLength = sizeof cp_cs_as_iso_audio_data_ep_desc10,
+	.bDescriptorType = USB_DT_CS_ENDPOINT,
+	.bDescriptorSubtype = EP_GENERAL,
+	.bmAttributes = 0x00,
+	.bLockDelayUnits = 0x00,
+	.wLockDelay = 0x0000,
+};
+
+static struct usb_descriptor_header *fadc_fs_descriptors[] = {
+	(struct usb_descriptor_header *)&ac_intf_assoc_desc,
+	(struct usb_descriptor_header *)&audio_control_interface_desc,
+	(struct usb_descriptor_header *)&ac_header_desc2,
+	(struct usb_descriptor_header *)&playback_input_terminal_desc10,
+	(struct usb_descriptor_header *)&playback_output_terminal_desc10,
+	(struct usb_descriptor_header *)&capture_input_terminal_desc10,
+	(struct usb_descriptor_header *)&capture_output_terminal_desc10,
+	(struct usb_descriptor_header *)&pb_audio_stream_intf_alt0_desc,
+	(struct usb_descriptor_header *)&pb_audio_stream_intf_alt1_desc,
+	(struct usb_descriptor_header *)&pb_cs_audio_stream_intf_desc10,
+	(struct usb_descriptor_header *)&pb_format_type_desc,
+	(struct usb_descriptor_header *)&pb_as_fs_iso_audio_data_ep_desc,
+	(struct usb_descriptor_header *)&pb_cs_as_iso_audio_data_ep_desc10,
+	(struct usb_descriptor_header *)&cp_audio_stream_intf_alt0_desc,
+	(struct usb_descriptor_header *)&cp_audio_stream_intf_alt1_desc,
+	(struct usb_descriptor_header *)&cp_cs_audio_stream_intf_desc10,
+	(struct usb_descriptor_header *)&cp_format_type_desc,
+	(struct usb_descriptor_header *)&cp_as_fs_iso_audio_data_ep_desc,
+	(struct usb_descriptor_header *)&cp_cs_as_iso_audio_data_ep_desc10,
+	0,
+};
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+static struct usb_descriptor_header *fadc_hs_descriptors[] = {
+	(struct usb_descriptor_header *)&ac_intf_assoc_desc,
+	(struct usb_descriptor_header *)&audio_control_interface_desc,
+	(struct usb_descriptor_header *)&ac_header_desc2,
+	(struct usb_descriptor_header *)&playback_input_terminal_desc10,
+	(struct usb_descriptor_header *)&playback_output_terminal_desc10,
+	(struct usb_descriptor_header *)&capture_input_terminal_desc10,
+	(struct usb_descriptor_header *)&capture_output_terminal_desc10,
+	(struct usb_descriptor_header *)&pb_audio_stream_intf_alt0_desc,
+	(struct usb_descriptor_header *)&pb_audio_stream_intf_alt1_desc,
+	(struct usb_descriptor_header *)&pb_cs_audio_stream_intf_desc10,
+	(struct usb_descriptor_header *)&pb_format_type_desc,
+	(struct usb_descriptor_header *)&pb_as_hs_iso_audio_data_ep_desc,
+	(struct usb_descriptor_header *)&pb_cs_as_iso_audio_data_ep_desc10,
+	(struct usb_descriptor_header *)&cp_audio_stream_intf_alt0_desc,
+	(struct usb_descriptor_header *)&cp_audio_stream_intf_alt1_desc,
+	(struct usb_descriptor_header *)&cp_cs_audio_stream_intf_desc10,
+	(struct usb_descriptor_header *)&cp_format_type_desc,
+	(struct usb_descriptor_header *)&cp_as_hs_iso_audio_data_ep_desc,
+	(struct usb_descriptor_header *)&cp_cs_as_iso_audio_data_ep_desc10,
+	0,
+};
+#endif
+
+static void fadc_playback_complete(struct usb_ep *ep, struct usb_request *rq);
+static void fadc_capture_complete(struct usb_ep *ep, struct usb_request *rq);
+static int fadc_dev_set_alt(struct usb_function *f,
+				struct fadc_dev* dev,
+				unsigned alt);
+
+/* Alsa mixer controls */
+
+/* note: the volume in this driver uses a generalized scale, from -90 dB
+	(attenuation), 0 dB (no change to PCM data) to 90 dB (gain).
+
+	playback volume control in ALSA user space is a non-negative value,
+	with higher values signalling a higher attenuation.
+
+	capture volume control in ALSA user space is a non-negative value,
+	with higher values signalling a higher gain.
+
+	key to this table is a db value -127 < db <= 0
+
+	gain = gains[-db % 6)
+	shift = 15 + floor(-db/6);
+
+	gain represents the fixed-point multiplier, while
+	shift represents the fixed-point scaling factor
+*/
+long int fadc_attenuate_gains[] = {
+	32768,	/*  0dB */
+	29205,	/* -1dB */
+	26029,	/* -2dB */
+	23198,	/* -3dB */
+	20675,	/* -4dB */
+	18427,	/* -5dB */
+};
+
+/*	key to this table is a db value 127 <= db <= 0
+
+	gain = gains[db % 6]
+	shift = 15 - floor(db/6);
+*/
+long int fadc_boost_gains[] = {
+	32768,	/*  0dB */
+	36766,	/*  1dB */
+	41252,	/*  2dB */
+	46286,	/*  3dB */
+	51934,	/*  4dB */
+	58271,	/*  5dB */
+};
+
+static void fadc_set_volume(struct fadc_volume_setting *volume, int db)
+{
+	if (!volume)
+		return;
+
+	if (db < -90)
+		db = -90;
+	if (db > 90)
+		db = 90;
+
+	if (db < 0) {
+		/* attenuate */
+		volume->gain = fadc_attenuate_gains[(-db) % 6];
+		volume->shift = 15 + (-db / 6);
+		volume->db = db;
+	} else {
+		/* gain */
+		volume->gain = fadc_boost_gains[db % 6];
+		volume->shift = 15 - (db / 6);
+		volume->db = db;
+	}
+}
+
+static void fadc_boost(struct fadc_volume_setting *boost,
+			 short int *samples, unsigned long int count)
+{
+	long int gain;
+	unsigned long int shift;
+
+	gain = boost->gain;
+	shift = boost->shift;
+
+	if ((gain == FADC_IDENT_GAIN) && (shift == FADC_IDENT_SHIFT))
+		return;
+
+	while (count) {
+		*samples = (gain * (*samples)) >> shift;
+		samples++;
+		count--;
+	}
+}
+
+#ifdef USB_AVH_FUNCTION_ADC_MIXER_CONTROL
+
+#define FADC_PLAYBACK_VOLUME_CONTROL_NAME	"TT Playback Volume dB"
+#define FADC_CAPTURE_VOLUME_CONTROL_NAME	"TT Capture Volume dB"
+
+static int fadc_get_playback_volume_db(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = -fadc_playback_dev->volume.db;
+	return 0;
+}
+
+static int fadc_set_playback_volume_db(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	fadc_playback_dev->volume.db = -ucontrol->value.integer.value[0];
+	fadc_set_volume(&(fadc_playback_dev->volume),
+			  fadc_playback_dev->volume.db);
+
+	return 1;
+}
+
+static int fadc_get_capture_volume_db(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = fadc_capture_dev->volume.db;
+	return 0;
+}
+
+static int fadc_set_capture_volume_db(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	fadc_capture_dev->volume.db = ucontrol->value.integer.value[0];
+	fadc_set_volume(&(fadc_capture_dev->volume),
+			  fadc_capture_dev->volume.db);
+
+	return 1;
+}
+
+static const struct snd_kcontrol_new fadc_controls[] = {
+	SOC_SINGLE_EXT(FADC_PLAYBACK_VOLUME_CONTROL_NAME, 0, 0, 0x7f, 0,
+		       fadc_get_playback_volume_db,
+		       fadc_set_playback_volume_db),
+	SOC_SINGLE_EXT(FADC_CAPTURE_VOLUME_CONTROL_NAME, 0, 0, 0x7f, 0,
+		       fadc_get_capture_volume_db,
+		       fadc_set_capture_volume_db)
+};
+
+static int fadc_register_controls(struct snd_card *card)
+{
+	int idx;
+	int err;
+	struct snd_kcontrol *control;
+
+	for (idx = 0; idx < ARRAY_SIZE(fadc_controls); idx++) {
+		control = snd_ctl_new1(&fadc_controls[idx], 0);
+
+		if (!control) {
+			return -ENOENT;
+		}
+
+		err = snd_ctl_add(card, control);
+
+		if (err) {
+			return err;
+		}
+	}
+
+	return 0;
+}
+#else
+static int fadc_register_controls(struct snd_card *card)
+{
+	return 0;
+}
+#endif
+
+static int fadc_setup(struct usb_function *f,
+			const struct usb_ctrlrequest *ctrl)
+{
+	return -EOPNOTSUPP;
+}
+
+static int __devexit fadc_alsa_exit(void)
+{
+	if (fadc_card) {
+		snd_card_disconnect(fadc_card);
+		snd_card_free(fadc_card);
+		fadc_card = 0;
+	}
+
+	return 0;
+}
+
+static void fadc_dev_disable(struct fadc_dev *dev)
+{
+	fadc_dev_set_alt(&fadc_function_driver.func, dev, FADC_ALTERNATE_IDLE);
+}
+
+static void fadc_disable(struct usb_function *f)
+{
+	fadc_dev_disable(fadc_playback_dev);
+	fadc_dev_disable(fadc_capture_dev);
+}
+
+
+static int fadc_start_transfer(struct fadc_dev *dev)
+{
+	switch (dev->state) {
+	case FADC_STATE_PREPARED:
+		fadc_set_dev_state(dev, FADC_STATE_VIRTUAL);
+
+		del_timer_sync(&dev->timer);
+
+		dev->timer.expires = jiffies + FADC_TIMER_TICKS;
+		dev->timer.data = jiffies;	/* to track how much time elapsed */
+
+		add_timer(&dev->timer);
+		break;
+
+	case FADC_STATE_READY:
+		fadc_set_dev_state(dev, FADC_STATE_STREAMING);
+
+		dev->request->status = 0;
+		dev->request->length = dev->iso->maxpacket;
+		dev->request->actual = 0;
+		dev->request->buf = dev->databuf;
+		dev->request->dma = DMA_ADDR_INVALID;
+		dev->packet_index = 1;
+		dev->request->complete(dev->iso, dev->request);
+		break;
+
+	default:
+		return -EPIPE;
+	}
+
+	return 0;
+}
+
+static int fadc_stop_transfer(struct fadc_dev *dev)
+{
+	switch (dev->state) {
+	case FADC_STATE_READY:
+	case FADC_STATE_STREAMING:
+	case FADC_STATE_SELECTED:
+		fadc_set_dev_state(dev, FADC_STATE_SELECTED);
+		break;
+
+	case FADC_STATE_VIRTUAL:
+		del_timer_sync(&dev->timer);
+		/* fall through intentional */
+	case FADC_STATE_PREPARED:
+	case FADC_STATE_CONFIGURED:
+		fadc_set_dev_state(dev, FADC_STATE_CONFIGURED);
+		break;
+
+	default:
+		break;
+	}
+	dev->pcm.period_pos = 0;
+	dev->pcm.ring_pos = 0;
+	return 0;
+}
+
+static void fadc_suspend(struct usb_function *f)
+{
+	fadc_stop_transfer(fadc_playback_dev);
+	fadc_stop_transfer(fadc_capture_dev);
+}
+
+static int fadc_dev_set_alt(struct usb_function *f,
+			      struct fadc_dev *dev,
+				unsigned alt)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if(alt == FADC_ALTERNATE_IDLE) {
+		switch (dev->state) {
+		case FADC_STATE_READY:
+			fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+			break;
+
+		case FADC_STATE_SELECTED:
+			fadc_set_dev_state(dev, FADC_STATE_CONFIGURED);
+			break;
+
+		case FADC_STATE_STREAMING:
+			fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+			fadc_start_transfer(dev);
+			break;
+
+		default:
+			break;
+		}
+		usb_ep_disable(dev->iso);
+		dev->alt = alt;
+	}
+
+	if(alt == FADC_ALTERNATE_ACTIVE) {
+		int result;
+
+		if (dev->state == FADC_STATE_STREAMING) {
+			fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+			usb_ep_disable(dev->iso);
+		}
+
+		dev->iso_desc = ep_choose(cdev->gadget, dev->hs_iso_desc,
+					  dev->fs_iso_desc);
+
+		result = usb_ep_enable(dev->iso, dev->iso_desc);
+
+		if (result) {
+			return result;
+		}
+
+		dev->alt = alt;
+
+		switch (dev->state) {
+		case FADC_STATE_CONFIGURED:
+			fadc_set_dev_state(dev, FADC_STATE_SELECTED);
+			break;
+
+		case FADC_STATE_PREPARED:
+			fadc_set_dev_state(dev, FADC_STATE_READY);
+			break;
+
+		case FADC_STATE_VIRTUAL:
+			fadc_stop_transfer(dev);
+			fadc_set_dev_state(dev, FADC_STATE_READY);
+			fadc_start_transfer(dev);
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int fadc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	if (intf == pb_audio_stream_intf_alt0_desc.bInterfaceNumber) {
+		return fadc_dev_set_alt(f, fadc_playback_dev, alt);
+	}
+
+	if (intf == cp_audio_stream_intf_alt0_desc.bInterfaceNumber) {
+		return fadc_dev_set_alt(f, fadc_capture_dev, alt);
+	}
+
+	return 0;
+}
+
+static int fadc_dev_get_alt(struct fadc_dev *dev)
+{
+	return dev->alt;
+}
+
+static int fadc_get_alt(struct usb_function *f, unsigned intf)
+{
+	if (intf == pb_audio_stream_intf_alt0_desc.bInterfaceNumber) {
+		return fadc_dev_get_alt(fadc_playback_dev);
+	}
+
+	if (intf == cp_audio_stream_intf_alt0_desc.bInterfaceNumber) {
+		return fadc_dev_get_alt(fadc_capture_dev);
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/* ########################################################################## */
+/* ######################### Interface to userspace ######################### */
+/* ########################################################################## */
+
+static unsigned int fadc_playback_samplerate[] = { 0 };
+
+static struct snd_pcm_hw_constraint_list playback_constraint_rates = {
+	.count = ARRAY_SIZE(fadc_playback_samplerate),
+	.list = fadc_playback_samplerate,
+	.mask = 0,
+};
+
+static unsigned int fadc_capture_samplerate[] = { 0 };
+
+static struct snd_pcm_hw_constraint_list capture_constraint_rates = {
+	.count = ARRAY_SIZE(fadc_capture_samplerate),
+	.list = fadc_capture_samplerate,
+	.mask = 0,
+};
+
+#define USE_PERIODS_MIN 	2
+#define USE_PERIODS_MAX 	1024
+
+static void fadc_playback_timer(unsigned long past)
+{
+	struct fadc_dev *dev = fadc_playback_dev;
+
+	switch (dev->state) {
+	case FADC_STATE_READY:
+	case FADC_STATE_STREAMING:
+		fadc_set_dev_state(dev, FADC_STATE_READY);
+		fadc_start_transfer(dev);
+		break;
+
+	case FADC_STATE_VIRTUAL:
+		{
+			unsigned long now;
+			unsigned long ticks;
+			unsigned long silenz;
+			unsigned long appl_pos;
+			unsigned long bytes_to_send;
+			struct snd_pcm_runtime *runtime;
+
+			now = jiffies;
+			ticks = now - past;
+
+			/* now compute the amount of data that would have
+			   been played over USB should there have been
+			   a connection */
+
+			silenz =
+			    (dev->settings.channels * dev->settings.samplerate *
+			     ticks) / HZ;
+
+			/* passing 0 as the buffer pointer causes silence to be
+			   written */
+
+			runtime = dev->substream->runtime;
+			appl_pos =
+			    frames_to_bytes(runtime,
+					    runtime->control->appl_ptr) %
+			    dev->pcm.ring_size;
+
+			if (appl_pos < dev->pcm.ring_pos) {
+				bytes_to_send = dev->pcm.ring_size -
+				    (dev->pcm.ring_pos - appl_pos);
+			} else if (appl_pos > dev->pcm.ring_pos) {
+				bytes_to_send = appl_pos - dev->pcm.ring_pos;
+			} else {
+				bytes_to_send = dev->pcm.ring_size;
+			}
+
+			if (silenz > bytes_to_send)
+				silenz = bytes_to_send;
+
+			dev->pcm.ring_pos = (dev->pcm.ring_pos + silenz);
+			if (dev->pcm.ring_pos > dev->pcm.ring_size)
+				dev->pcm.ring_pos -= dev->pcm.ring_size;
+
+			dev->pcm.period_pos += silenz;
+
+			/* The period_pos is used to check if we have effectively ignored
+			   an 'alsa period' worth of data from the ringbuffer. When that
+			   happens a signal is sent so that userspace can put new audio
+			   data in the ringbuffer. */
+			if (dev->pcm.period_pos >= dev->pcm.period_size) {
+				dev->pcm.period_pos %= dev->pcm.period_size;
+				snd_pcm_period_elapsed(dev->substream);
+			}
+
+			dev->timer.expires = now + FADC_TIMER_TICKS;
+			dev->timer.data = now;
+			add_timer(&dev->timer);
+			break;
+		}
+
+	default:
+		break;
+	}
+}
+
+/* This function is the callback that is called by the USB gadget driver when a
+   request queued by the to_host driver has completed. This may be due to a
+   disconnect, endpoint disable or another anomaly, in which case the status of
+   the request will indicate an error.
+
+   In case no error occurred, this function will do the following:
+
+   - determine whether with the sending of the data included in this completed
+     request, an ALSA 'period' worth of data has been transmitted. If so, this
+     must be signalled to ALSA.
+
+   - determine the next packet to be send, and queue it for transmission (the
+     HOST will come and try to get this data within the next millisecond).
+*/
+
+static void fadc_playback_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+	int is_last = 0;
+	unsigned long int appl_pos;
+	unsigned long int bytes_to_send;
+	struct snd_pcm_runtime *runtime;
+	struct fadc_dev *dev = (struct fadc_dev *)req->context;
+
+	if (dev->state != FADC_STATE_STREAMING) {
+		return;
+	}
+
+	if ((!dev->substream) || (!dev->substream->runtime)) {
+		return;
+	}
+
+	runtime = dev->substream->runtime;
+	appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr) %
+	    dev->pcm.ring_size;
+
+	if (status) {
+		fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+		snd_pcm_stop(dev->substream, SNDRV_PCM_STATE_SUSPENDED);
+		dev->pcm.ring_pos = appl_pos;
+		snd_pcm_period_elapsed(dev->substream);
+		return;
+	}
+
+	/* The period_pos is used to check if we have send an 'alsa period'
+	   worth of data from the ringbuffer. When that happens a signal is sent
+	   so that userspace can put new audio data in the ringbuffer. */
+	if (dev->pcm.period_pos >= dev->pcm.period_size) {
+		dev->pcm.period_pos %= dev->pcm.period_size;
+		snd_pcm_period_elapsed(dev->substream);
+	}
+
+	/* Accumulator check. We can only send whole bytes. For more information
+	   see the comments in the fadc_alsa_prepare function */
+	if (dev->packet_index == dev->settings.align_length) {
+		dev->packet_index = 1;
+
+		req->length = dev->settings.alignment_size;
+	} else {
+		req->length = dev->settings.nominal_size;
+
+		/* note: in case a 1000 packets of nominal size in 1 second
+		   precisely generates the framerate desired, there is no need
+		   for alignment packets. captureIn that case, the alignment packet
+		   size will be equal to the nominal packet size and it does not
+		   matter to distinguish between the two cases.
+
+		   In that particular case, the packet_index counter simply
+		   races to its maximum value and wraps around to start counting
+		   again. While this counting does not do anything useful in
+		   that case, adding a check to prevent this counting is useless
+		   in other situations, and it does not hurt, really. */
+
+		dev->packet_index++;
+	}
+
+	if (appl_pos < dev->pcm.ring_pos) {
+		/* appl. data is split between the end of the ringbuffer
+		   and the beginning of the ringbuffer */
+
+		bytes_to_send = dev->pcm.ring_size -
+		    (dev->pcm.ring_pos - appl_pos);
+	} else if (appl_pos > dev->pcm.ring_pos) {
+		/* appl. data is linear between the ring_pos and appl_pos. */
+
+		bytes_to_send = appl_pos - dev->pcm.ring_pos;
+	} else {		/* the ring is full with application data
+				   (if the state is STREAMING)  */
+
+		bytes_to_send = dev->pcm.ring_size;
+	}
+
+	/* bytes_to_send now holds the number of bytes that the usb audio stream
+	   is lagging behind the application pointer */
+
+	if (bytes_to_send <= req->length) {
+		/* if the remaining data fits in the packet, then this will be
+		   the last packet */
+		is_last = 1;
+		req->length = bytes_to_send;
+	} else {
+		/* there are more bytes to send than the request can do (nominal
+		   or alignment size) */
+		bytes_to_send = req->length;
+	}
+
+	/* bytes_to_send now holds the actual number of bytes to be transmitted
+	   for audio */
+
+	if (dev->pcm.ring_pos + bytes_to_send <= dev->pcm.ring_size) {
+		// the packet is not split
+		req->buf = runtime->dma_area + dev->pcm.ring_pos;
+
+		dev->pcm.ring_pos += bytes_to_send;
+		dev->pcm.period_pos += bytes_to_send;
+	} else {
+		// the packet is split
+		unsigned int part_size = dev->pcm.ring_size - dev->pcm.ring_pos;
+
+		memcpy(dev->databuf,
+		       runtime->dma_area + dev->pcm.ring_pos, part_size);
+
+		dev->pcm.ring_pos = bytes_to_send - part_size;
+
+		memcpy(dev->databuf + part_size,
+		       runtime->dma_area, dev->pcm.ring_pos);
+
+		req->buf = dev->databuf;
+
+		dev->pcm.period_pos += bytes_to_send;
+	}
+	if (bytes_to_send != 0) {
+		int result;
+
+		/* volume adjustment */
+		fadc_boost(&dev->volume, (unsigned short *)req->buf,
+			     bytes_to_send / 2);
+
+		result = usb_ep_queue(dev->iso, dev->request, GFP_ATOMIC);
+		if (result) {
+			dev->pcm.ring_pos = appl_pos;
+			fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+			snd_pcm_period_elapsed(dev->substream);
+		}
+	}
+
+	if (is_last) {
+		fadc_set_dev_state(dev, FADC_STATE_READY);
+		snd_pcm_period_elapsed(dev->substream);
+	}
+
+	return;
+}
+
+static void fadc_capture_transfer(struct snd_pcm_runtime *runtime,
+				    unsigned long amount, void *buf)
+{
+	struct fadc_dev *dev = fadc_capture_dev;
+
+	if (dev->pcm.ring_pos + amount <= dev->pcm.ring_size) {
+		// the space to be written to is not split
+		if (buf) {
+			memcpy(runtime->dma_area + dev->pcm.ring_pos,
+			       dev->databuf, amount);
+		} else {
+			memset(runtime->dma_area + dev->pcm.ring_pos,
+			       0, amount);
+		}
+		dev->pcm.ring_pos += amount;
+		dev->pcm.period_pos += amount;
+	} else {
+		// the packet is split
+		unsigned int part_size = dev->pcm.ring_size - dev->pcm.ring_pos;
+		if (buf) {
+			memcpy(runtime->dma_area + dev->pcm.ring_pos,
+			       dev->databuf, part_size);
+		} else {
+			memset(runtime->dma_area + dev->pcm.ring_pos,
+			       0, part_size);
+		}
+
+		dev->pcm.ring_pos = amount - part_size;
+
+		if (buf) {
+			memcpy(runtime->dma_area,
+			       dev->databuf + part_size, dev->pcm.ring_pos);
+		} else {
+			memset(runtime->dma_area, 0, dev->pcm.ring_pos);
+		}
+
+		dev->pcm.period_pos += amount;
+	}
+
+	/* The period_pos is used to check if we have send an 'alsa period'
+	   worth of data from the ringbuffer. When that happens a signal is sent
+	   so that userspace can get new audio data from the ringbuffer. */
+
+	if (dev->pcm.period_pos >= dev->pcm.period_size) {
+		dev->pcm.period_pos %= dev->pcm.period_size;
+		snd_pcm_period_elapsed(dev->substream);
+	}
+}
+
+static void fadc_capture_timer(unsigned long past)
+{
+	struct fadc_dev *dev = fadc_capture_dev;
+
+	switch (dev->state) {
+	case FADC_STATE_READY:
+	case FADC_STATE_STREAMING:
+		fadc_set_dev_state(dev, FADC_STATE_READY);
+		fadc_start_transfer(dev);
+		break;
+
+	case FADC_STATE_VIRTUAL:
+		{
+			/* unsigned arithmetic will take care of wrap around
+			   should that ever occur */
+			unsigned long now;
+			unsigned long ticks;
+			unsigned long silenz;
+
+			now = jiffies;
+			ticks = now - past;
+
+			/* now compute the amount of data that would have
+			   been captured over USB should there have been
+			   a connection */
+
+			silenz =
+			    (dev->settings.channels * dev->settings.samplerate *
+			     ticks) / HZ;
+
+			/* passing 0 as the buffer pointer causes silence to be
+			   written */
+			fadc_capture_transfer(dev->substream->runtime,
+						silenz, 0);
+
+			dev->timer.expires = now + FADC_TIMER_TICKS;
+			dev->timer.data = now;
+			add_timer(&dev->timer);
+			break;
+		}
+
+	default:
+		break;
+	}
+}
+
+static void fadc_capture_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	int status = req->status;
+	int appl_pos;
+	int bytes_available;
+	struct snd_pcm_runtime *runtime;
+	struct fadc_dev *dev = (struct fadc_dev *)req->context;
+
+	if (dev->state == FADC_STATE_VIRTUAL) {
+		/* no USB connection, but data needs to be produced
+		   at steady pace */
+		return;
+	}
+
+	if (dev->state != FADC_STATE_STREAMING) {
+		return;
+	}
+
+	runtime = dev->substream->runtime;
+	appl_pos = frames_to_bytes(runtime, runtime->control->appl_ptr) %
+	    dev->pcm.ring_size;
+
+	if (status) {
+		fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+		snd_pcm_stop(dev->substream, SNDRV_PCM_STATE_SUSPENDED);
+		dev->pcm.ring_pos = appl_pos;
+		snd_pcm_period_elapsed(dev->substream);
+		return;
+	}
+
+	dev->packet_index++;
+
+	if (req->actual % (dev->settings.channels * 2)) {
+		printk(KERN_ERR "received a packet that contains a split audio sample\n");
+		printk(KERN_ERR "channels = %d\n", dev->settings.channels);
+		printk(KERN_ERR "packet->actual = %d\n", req->actual);
+	}
+
+	/* volume adjustment */
+	fadc_boost(&dev->volume, (unsigned short *)req->buf, req->actual / 2);
+
+	/* copy data from the received USB request buffer to the ringbuffer */
+
+	if (appl_pos < dev->pcm.ring_pos) {
+		/* available space in the ringbuffer is split between the end of
+		   the ringbuffer and the beginning of the ringbuffer */
+
+		bytes_available = dev->pcm.ring_size -
+		    (dev->pcm.ring_pos - appl_pos);
+	} else if (appl_pos > dev->pcm.ring_pos) {
+		/* available space in the ringbuffer is linear between the
+		   ring_pos and appl_pos.   */
+
+		bytes_available = appl_pos - dev->pcm.ring_pos;
+	} else {		/* the ring is empty (if the state is STREAMING) */
+
+		bytes_available = dev->pcm.ring_size;
+	}
+
+	if (bytes_available <= req->actual) {
+		/* the ring will be filled completely; this is not desired
+		   normally, the application should be fast enough to keep up with
+		   the USB stream filling the ringbuffer; now, we start dropping
+		   packets until there is room enough again. */
+		bytes_available = 0;
+	} else {
+		/* there is more space in the ring than can be filled with data
+		   from this packet  */
+		bytes_available = req->actual;
+	}
+
+	/* bytes_available now holds the actual number of bytes to be copied to
+	   the ringbuffer */
+
+	fadc_capture_transfer(runtime, bytes_available, dev->databuf);
+
+	req->status = 0;
+	req->length = ep->maxpacket;
+	req->buf = dev->databuf;
+	req->dma = DMA_ADDR_INVALID;
+	req->actual = 0;
+
+
+	status = usb_ep_queue(ep, req, GFP_ATOMIC);
+	if (status) {
+		dev->pcm.ring_pos = appl_pos;
+		snd_pcm_period_elapsed(dev->substream);
+
+		/* TODO: check whether READY is the correct state. This depends
+		   on the reason the queue operation failed. One way to
+		   determine what to do might be to check the 'alt-setting'
+		   of the device at this point. */
+
+		fadc_set_dev_state(dev, FADC_STATE_READY);
+	}
+}
+
+static int fadc_alsa_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fadc_dev *dev = (struct fadc_dev *)runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		return fadc_start_transfer(dev);
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		return fadc_stop_transfer(dev);
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Calculate the amount of data we have to send each request. */
+static int fadc_alsa_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fadc_dev *dev = (struct fadc_dev *)runtime->private_data;
+
+	if (dev->state == FADC_STATE_STREAMING) {
+		return -EINVAL;
+	}
+
+	if (runtime->rate != dev->settings.samplerate) {
+		return -EINVAL;
+	}
+
+	if (runtime->channels != dev->settings.channels) {
+		return -EINVAL;
+	}
+
+	if (runtime->format != SNDRV_PCM_FORMAT_S16_LE) {
+		return -EINVAL;
+	}
+
+	dev->pcm.ring_size = snd_pcm_lib_buffer_bytes(substream);
+	dev->pcm.ring_pos = 0;
+	dev->pcm.period_size = snd_pcm_lib_period_bytes(substream);
+	dev->pcm.period_pos = 0;
+
+	switch (dev->state) {
+	case FADC_STATE_SELECTED:
+		fadc_set_dev_state(dev, FADC_STATE_READY);
+		break;
+
+	case FADC_STATE_CONFIGURED:
+		fadc_set_dev_state(dev, FADC_STATE_PREPARED);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/* Return hardware pointer in ALSA ring buffer. */
+static snd_pcm_uframes_t
+fadc_alsa_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct fadc_dev *dev = (struct fadc_dev *)runtime->private_data;
+
+	switch (dev->state) {
+	case FADC_STATE_VIRTUAL:
+	case FADC_STATE_STREAMING:
+		return bytes_to_frames(runtime, dev->pcm.ring_pos);
+
+	default:
+		return bytes_to_frames(runtime,
+				       frames_to_bytes(runtime,
+						       runtime->control->
+						       appl_ptr) %
+				       dev->pcm.ring_size);
+	}
+}
+
+static struct snd_pcm_hardware fadc_alsa_playback = { 0 };
+static struct snd_pcm_hardware fadc_alsa_capture = { 0 };
+
+/* This function will free the allocated ring buffer
+ * Non atomic function. */
+static int fadc_alsa_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+}
+
+/* This function will free the allocated/reserved memory pool */
+static int fadc_alsa_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int fadc_alsa_playback_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err = 0;
+
+	if (runtime->private_data != 0)
+		return -EBUSY;
+
+	/* Make sure the device can only be opened with the configured sample
+	   rate Hz */
+	err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					 &playback_constraint_rates);
+	if (err) {
+		return err;
+	}
+
+	fadc_playback_dev->substream = substream;
+	runtime->private_data = fadc_playback_dev;
+	runtime->hw = fadc_alsa_playback;
+
+	return err;
+}
+
+static int fadc_alsa_capture_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err = 0;
+
+	if (runtime->private_data != 0) {
+		return -EBUSY;
+	}
+
+	fadc_capture_dev->substream = substream;
+	runtime->private_data = fadc_capture_dev;
+	runtime->hw = fadc_alsa_capture;
+
+	/* Make sure the device can only be opened with the configured sample
+	   rate Hz */
+	err = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					 &capture_constraint_rates);
+
+	return err;
+}
+
+static int fadc_alsa_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	((struct fadc_dev *)runtime->private_data)->substream = 0;
+	runtime->private_data = 0;
+
+	return 0;
+}
+
+static struct snd_pcm_ops audio_alsa_playback_ops = {
+	.open = fadc_alsa_playback_open,
+	.close = fadc_alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = fadc_alsa_hw_params,
+	.hw_free = fadc_alsa_hw_free,
+	.prepare = fadc_alsa_prepare,
+	.trigger = fadc_alsa_trigger,
+	.pointer = fadc_alsa_pointer,
+};
+
+static struct snd_pcm_ops audio_alsa_capture_ops = {
+	.open = fadc_alsa_capture_open,
+	.close = fadc_alsa_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = fadc_alsa_hw_params,
+	.hw_free = fadc_alsa_hw_free,
+	.prepare = fadc_alsa_prepare,
+	.trigger = fadc_alsa_trigger,
+	.pointer = fadc_alsa_pointer,
+};
+
+/* init/exit - module insertion/removal */
+
+static int __init fadc_alsa_init(void)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_card_create(-1, fadc_name, THIS_MODULE, 0, &fadc_card);
+	if (err) {
+		goto fail;
+	}
+
+	strlcpy(fadc_card->driver, fadc_name, sizeof(fadc_card->driver));
+	strlcpy(fadc_card->shortname, "usb_audio",
+		sizeof(fadc_card->shortname));
+
+	snprintf(fadc_card->longname, sizeof(fadc_card->longname),
+		 "%s%i", fadc_name, 1);
+
+	if ((err = snd_pcm_new(fadc_card, fadc_name, 0, 1, 1, &pcm)) < 0) {
+		goto fail;
+	}
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			&audio_alsa_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &audio_alsa_capture_ops);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, fadc_name);
+
+	if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm,
+							 SNDRV_DMA_TYPE_CONTINUOUS,
+							 snd_dma_continuous_data
+							 (GFP_KERNEL),
+							 FADC_PREALLOC_SIZE,
+							 FADC_PREALLOC_SIZE))
+	    < 0) {
+		goto fail;
+	}
+
+	fadc_playback_dev->name = "playback";
+	fadc_capture_dev->name = "capture";
+
+	/* Register the soundcard */
+
+	err = snd_card_register(fadc_card);
+
+	if (err) {
+		goto fail;
+	}
+
+	fadc_register_controls(fadc_card);
+
+	return 0;
+
+      fail:
+	fadc_alsa_exit();
+	return err;
+}
+
+static __init void fadc_compute_playback_streaming_parameters(void)
+{
+	int index;
+	struct fadc_packet_setting *setting;
+	struct fadc_dev *dev = fadc_playback_dev;
+	struct snd_pcm_hardware *pcm_hw = &fadc_alsa_playback;
+
+	fadc_playback_samplerate[0] = dev->settings.samplerate;
+
+	for (index = 0; index < ARRAY_SIZE(fadc_packet_settings); index++) {
+		if (fadc_packet_settings[index].samplerate ==
+		    dev->settings.samplerate)
+			break;
+	}
+
+	if (index == ARRAY_SIZE(fadc_packet_settings)) {
+		return;
+	}
+
+	setting = &fadc_packet_settings[index];
+
+	dev->settings.alsa_rate = setting->alsa_rate;
+	dev->settings.nominal_size = setting->nominal_size *
+	    dev->settings.channels * FADC_DEFAULT_INTERVAL_PLAYBACK;
+
+	dev->settings.alignment_size = setting->alignment_size *
+	    dev->settings.channels * FADC_DEFAULT_INTERVAL_PLAYBACK;
+
+	dev->settings.align_length = setting->align_length;
+
+	pcm_hw->info = (SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID);
+	pcm_hw->formats = (SNDRV_PCM_FMTBIT_S16_LE);
+	pcm_hw->rates = dev->settings.alsa_rate;
+	pcm_hw->rate_min = dev->settings.samplerate;
+	pcm_hw->rate_max = dev->settings.samplerate;
+	pcm_hw->channels_min = dev->settings.channels;
+	pcm_hw->channels_max = dev->settings.channels;
+	pcm_hw->buffer_bytes_max = FADC_PLAYBACK_RINGBUFFER_SIZE;
+	pcm_hw->period_bytes_min = 64;
+	pcm_hw->period_bytes_max = FADC_PLAYBACK_RINGBUFFER_SIZE;
+	pcm_hw->periods_min = USE_PERIODS_MIN;
+	pcm_hw->periods_max = USE_PERIODS_MAX;
+	pcm_hw->fifo_size = 0;
+
+	/* fix the (static) descriptor values ... */
+
+	/* ... in the format type descriptor ... */
+	pb_format_type_desc.bNrChannels = dev->settings.channels;
+	pb_format_type_desc.bSamFreqType = 0x01;
+	pb_format_type_desc.tSamFreq00 = dev->settings.samplerate & 0xff;
+	pb_format_type_desc.tSamFreq01 = (dev->settings.samplerate >> 8) & 0xff;
+	pb_format_type_desc.tSamFreq02 =
+	    (dev->settings.samplerate >> 16) & 0xff;
+
+	/* ... in the input terminal descriptor ... */
+	playback_input_terminal_desc10.bNrChannels = dev->settings.channels;
+
+	/* ... and in the endpoint descriptor */
+	pb_as_hs_iso_audio_data_ep_desc.wMaxPacketSize =
+	    __cpu_to_le16(dev->settings.alignment_size);
+}
+
+static __init void fadc_compute_capture_streaming_parameters(void)
+{
+	int index;
+	struct fadc_packet_setting *setting;
+	struct fadc_dev *dev = fadc_capture_dev;
+	struct snd_pcm_hardware *pcm_hw = &fadc_alsa_capture;
+
+	fadc_capture_samplerate[0] = fadc_capture_dev->settings.samplerate;
+
+	for (index = 0; index < ARRAY_SIZE(fadc_packet_settings); index++) {
+		if (fadc_packet_settings[index].samplerate ==
+		    dev->settings.samplerate)
+			break;
+	}
+
+	if (index == ARRAY_SIZE(fadc_packet_settings)) {
+		return;
+	}
+
+	setting = &fadc_packet_settings[index];
+
+	dev->settings.alsa_rate = setting->alsa_rate;
+
+	/* these settings are not required for from_host (host-to-device) as
+	   the host determines the packet sizes however, we use them here to
+	   compute the maximum time interval between packets */
+
+	dev->settings.nominal_size = setting->nominal_size *
+	    dev->settings.channels * FADC_DEFAULT_INTERVAL_CAPTURE;
+
+	dev->settings.alignment_size = setting->alignment_size *
+	    dev->settings.channels * FADC_DEFAULT_INTERVAL_CAPTURE;
+
+	dev->settings.align_length = setting->align_length;
+
+	pcm_hw->info = (SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID);
+	pcm_hw->formats = (SNDRV_PCM_FMTBIT_S16_LE);
+	pcm_hw->rates = dev->settings.alsa_rate;
+	pcm_hw->rate_min = dev->settings.samplerate;
+	pcm_hw->rate_max = dev->settings.samplerate;
+	pcm_hw->channels_min = dev->settings.channels;
+	pcm_hw->channels_max = dev->settings.channels;
+	pcm_hw->buffer_bytes_max = FADC_CAPTURE_RINGBUFFER_SIZE;
+	pcm_hw->period_bytes_min = 64;
+	pcm_hw->period_bytes_max = FADC_CAPTURE_RINGBUFFER_SIZE;
+	pcm_hw->periods_min = USE_PERIODS_MIN;
+	pcm_hw->periods_max = USE_PERIODS_MAX;
+	pcm_hw->fifo_size = 0;
+
+	/* fix the (static) descriptor values ... */
+
+	/* ... in the format type descriptor ... */
+	cp_format_type_desc.bNrChannels = dev->settings.channels;
+	cp_format_type_desc.bSamFreqType = 0x01;
+	cp_format_type_desc.tSamFreq00 = dev->settings.samplerate & 0xff;
+	cp_format_type_desc.tSamFreq01 = (dev->settings.samplerate >> 8) & 0xff;
+	cp_format_type_desc.tSamFreq02 =
+	    (dev->settings.samplerate >> 16) & 0xff;
+
+	/* ... in the input terminal descriptor ... */
+	capture_input_terminal_desc10.bNrChannels = dev->settings.channels;
+
+	/* ... and in the endpoint descriptor */
+	{
+		unsigned int max_packet_size = dev->settings.alignment_size;
+		unsigned int ep_max_packet_size =
+		    __le16_to_cpu(cp_as_hs_iso_audio_data_ep_desc.
+				  wMaxPacketSize);
+
+		/* the alignment size is OUR estimate of the maximum packet
+		   size, the host may differ if it uses a different algorithm,
+		   we compensate with 12.5% allowance */
+		/* ROLK: temporary fix
+
+		   max_packet_size += (max_packet_size >> 3);
+
+		 */
+		if (ep_max_packet_size > max_packet_size) {
+			cp_as_hs_iso_audio_data_ep_desc.wMaxPacketSize =
+			    __cpu_to_le16(max_packet_size);
+		}
+	}
+}
+
+static __init void fadc_compute_streaming_parameters(void)
+{
+	fadc_compute_playback_streaming_parameters();
+	fadc_compute_capture_streaming_parameters();
+}
+
+int __init fadc_validate_parameters(struct fadc_device_parameters *p,
+				      int interval)
+{
+	int index;
+
+	for (index = 0; index < ARRAY_SIZE(fadc_packet_settings); index++) {
+		if (fadc_packet_settings[index].samplerate != p->samplerate)
+			continue;
+
+		if (fadc_packet_settings[index].alignment_size * interval *
+		    p->channels <= FADC_MAX_ISO_PACKET_SIZE)
+			return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* sysfs support */
+
+static int fadc_get_state_string(char *buf, struct fadc_dev *dev)
+{
+	int ret = 0;
+
+	if (dev == NULL)
+		return 0;
+	switch (dev->state) {
+	case FADC_STATE_VOID:
+		ret = sprintf(buf, "VOID\n");
+		break;
+	case FADC_STATE_IDLE:
+		ret = sprintf(buf, "IDLE\n");
+		break;
+	case FADC_STATE_CONFIGURED:
+		ret = sprintf(buf, "CONFIGURED\n");
+		break;
+	case FADC_STATE_SELECTED:
+		ret = sprintf(buf, "SELECTED\n");
+		break;
+	case FADC_STATE_PREPARED:
+		ret = sprintf(buf, "PREPARED\n");
+		break;
+	case FADC_STATE_READY:
+		ret = sprintf(buf, "READY\n");
+		break;
+	case FADC_STATE_STREAMING:
+		ret = sprintf(buf, "STREAMING\n");
+		break;
+	case FADC_STATE_VIRTUAL:
+		ret = sprintf(buf, "VIRTUAL\n");
+		break;
+	default:
+		ret = sprintf(buf, "UNKNOWN\n");
+		break;
+	}
+	return ret;
+}
+
+static ssize_t fadc_read_playback_state(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	return fadc_get_state_string(buf, fadc_playback_dev);
+}
+
+static ssize_t fadc_read_capture_state(struct device *dev,
+					 struct device_attribute *attr,
+					 char *buf)
+{
+	return fadc_get_state_string(buf, fadc_capture_dev);
+}
+
+static DEVICE_ATTR(playback_state, S_IRUGO, fadc_read_playback_state, NULL);
+static DEVICE_ATTR(capture_state, S_IRUGO, fadc_read_capture_state, NULL);
+
+static void fadc_release(struct device *dev)
+{
+}
+
+struct device fadc_device = {
+	.init_name = "faudio",
+	.release = fadc_release
+};
+
+static int fadc_init_attr(void)
+{
+	int res = -ENODEV;
+
+	res = device_register(&fadc_device);
+
+	/* Sysfs attributes. */
+	if (res >= 0) {
+		if (device_create_file(&fadc_device, &dev_attr_playback_state)
+		    || device_create_file(&fadc_device,
+					  &dev_attr_capture_state)) {
+			return -ENODEV;
+		}
+	} else {
+		return res;
+	}
+
+	return 0;
+}
+
+static void fadc_destroy_attr(void)
+{
+	device_remove_file(&fadc_device, &dev_attr_playback_state);
+	device_remove_file(&fadc_device, &dev_attr_capture_state);
+	device_unregister(&fadc_device);
+}
+
+static int __init fadc_bind(struct usb_configuration *c,
+			      struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	int status;
+
+	/* Allocate fadc_dev structure to keep track of our administration */
+	fadc_playback_dev = kzalloc(sizeof(struct fadc_dev), GFP_KERNEL);
+	if (!fadc_playback_dev) {
+		return -ENOMEM;
+	}
+
+	/* Allocate fadc_dev structure to keep track of our administration */
+	fadc_capture_dev = kzalloc(sizeof(struct fadc_dev), GFP_KERNEL);
+	if (!fadc_capture_dev) {
+		return -ENOMEM;
+	}
+
+	fadc_set_volume(&fadc_playback_dev->volume, 0);
+	fadc_set_volume(&fadc_capture_dev->volume, 0);
+
+	/* Initialise timer structures */
+	init_timer(&fadc_playback_dev->timer);
+	fadc_playback_dev->timer.function = fadc_playback_timer;
+
+	init_timer(&fadc_capture_dev->timer);
+	fadc_capture_dev->timer.function = fadc_capture_timer;
+
+	/* Allocate buffer, used in fadc_playback_complete. */
+	fadc_playback_dev->databuf = kmalloc(FADC_HELPBUF_SIZE, GFP_KERNEL);
+	if (!fadc_playback_dev->databuf) {
+		return -ENOMEM;
+	}
+
+	/* Allocate buffer, used in fadc_capture_complete. */
+	fadc_capture_dev->databuf = kmalloc(FADC_HELPBUF_SIZE, GFP_KERNEL);
+	if (!fadc_capture_dev->databuf) {
+		return -ENOMEM;
+	}
+
+	if (fadc_validate_parameters(&fadc_function_driver.playback,
+				       FADC_DEFAULT_INTERVAL_PLAYBACK) == 0) {
+		fadc_playback_dev->settings.channels =
+		    fadc_function_driver.playback.channels;
+		fadc_playback_dev->settings.samplerate =
+		    fadc_function_driver.playback.samplerate;
+	} else {
+		fadc_playback_dev->settings.channels =
+		    FADC_DEFAULT_CHANNELS_PLAYBACK;
+		fadc_playback_dev->settings.samplerate =
+		    FADC_DEFAULT_SAMPLERATE_PLAYBACK;
+	}
+
+	if (fadc_validate_parameters(&fadc_function_driver.capture,
+				       FADC_DEFAULT_INTERVAL_CAPTURE) == 0) {
+		fadc_capture_dev->settings.channels =
+		    fadc_function_driver.capture.channels;
+		fadc_capture_dev->settings.samplerate =
+		    fadc_function_driver.capture.samplerate;
+	} else {
+		fadc_capture_dev->settings.channels =
+		    FADC_DEFAULT_CHANNELS_CAPTURE;
+		fadc_capture_dev->settings.samplerate =
+		    FADC_DEFAULT_SAMPLERATE_CAPTURE;
+	}
+
+	fadc_compute_streaming_parameters();
+
+	fadc_playback_dev->alt = 0;
+	fadc_capture_dev->alt = 0;
+
+	fadc_alsa_init();
+
+	fadc_set_dev_state(fadc_playback_dev, FADC_STATE_IDLE);
+	fadc_set_dev_state(fadc_capture_dev, FADC_STATE_IDLE);
+
+	/* Reserve an interface number for the audio control interface */
+	status = usb_interface_id(c, f);
+	if (status < 0) {
+		goto fail;
+	}
+	audio_control_interface_desc.bInterfaceNumber = status;
+	ac_intf_assoc_desc.bFirstInterface = status;
+
+	/* NICE_TO: to support arbitrary number of streams, it is required to
+	   reserve an interface number for each stream, and to dynamically add
+	   the required descriptors to 'fadc_fs_descriptors' and
+	   'fadc_hs_descriptors' */
+
+	/* Reserve an interface number for the audio playback stream interface */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+
+	pb_audio_stream_intf_alt0_desc.bInterfaceNumber = status;
+	pb_audio_stream_intf_alt1_desc.bInterfaceNumber = status;
+	ac_header_desc2.baInterfaceNr[0] = status;
+
+	/* Reserve an interface number for the audio capture stream interface */
+	status = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+
+	cp_audio_stream_intf_alt0_desc.bInterfaceNumber = status;
+	cp_audio_stream_intf_alt1_desc.bInterfaceNumber = status;
+	ac_header_desc2.baInterfaceNr[1] = status;
+
+	/* Reserve strings from the composite framework */
+
+	if (fadc_strings_dev[FADC_STRING_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			goto fail;
+
+		fadc_strings_dev[FADC_STRING_IDX].id = status;
+		ac_intf_assoc_desc.iFunction = status;
+		audio_control_interface_desc.iInterface = status;
+	}
+
+	if (fadc_strings_dev[FADC_PLAYBACK_STRING_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			goto fail;
+
+		fadc_strings_dev[FADC_PLAYBACK_STRING_IDX].id = status;
+		playback_input_terminal_desc10.iChannelNames = status;
+		playback_input_terminal_desc10.iTerminal = status;
+		playback_output_terminal_desc10.iTerminal = status;
+		pb_audio_stream_intf_alt0_desc.iInterface = status;
+		pb_audio_stream_intf_alt1_desc.iInterface = status;
+	}
+
+	if (fadc_strings_dev[FADC_CAPTURE_STRING_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			goto fail;
+
+		fadc_strings_dev[FADC_CAPTURE_STRING_IDX].id = status;
+		capture_input_terminal_desc10.iChannelNames = status;
+		capture_input_terminal_desc10.iTerminal = status;
+		capture_output_terminal_desc10.iTerminal = status;
+		cp_audio_stream_intf_alt0_desc.iInterface = status;
+		cp_audio_stream_intf_alt1_desc.iInterface = status;
+	}
+
+	f->descriptors = fadc_fs_descriptors;
+
+	/* usb to_host driver endpoint */
+
+	fadc_playback_dev->iso = usb_ep_autoconfig(cdev->gadget,
+						     (struct
+						      usb_endpoint_descriptor *)
+						     &pb_as_fs_iso_audio_data_ep_desc);
+
+	if (!fadc_playback_dev->iso) {
+		goto fail;
+	}
+
+	fadc_playback_dev->iso->driver_data = fadc_playback_dev;
+
+
+	fadc_capture_dev->iso = usb_ep_autoconfig(cdev->gadget,
+						    (struct
+						     usb_endpoint_descriptor *)
+						    &cp_as_fs_iso_audio_data_ep_desc);
+	if (!fadc_capture_dev->iso) {
+		goto fail;
+	}
+
+	fadc_capture_dev->iso->driver_data = fadc_capture_dev;
+
+
+	/* Set local endpoint administration */
+	fadc_playback_dev->fs_iso_desc =
+	    (struct usb_endpoint_descriptor *)&pb_as_fs_iso_audio_data_ep_desc;
+	fadc_capture_dev->fs_iso_desc =
+	    (struct usb_endpoint_descriptor *)&cp_as_fs_iso_audio_data_ep_desc;
+
+	/* Allocate USB request for audio data transport */
+	fadc_playback_dev->request =
+	    usb_ep_alloc_request(fadc_playback_dev->iso, GFP_KERNEL);
+
+	if (!fadc_playback_dev->request) {
+		goto fail;
+	}
+
+	fadc_playback_dev->request->context = fadc_playback_dev;
+	fadc_playback_dev->request->complete = fadc_playback_complete;
+
+	fadc_capture_dev->request =
+	    usb_ep_alloc_request(fadc_capture_dev->iso, GFP_KERNEL);
+
+	if (!fadc_capture_dev->request) {
+		goto fail;
+	}
+
+	fadc_capture_dev->request->context = fadc_capture_dev;
+	fadc_capture_dev->request->complete = fadc_capture_complete;
+
+	/* Runtime check if compile time CONFIG_USB_GADGET_DUALSPEED was set. */
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		pb_as_hs_iso_audio_data_ep_desc.bEndpointAddress =
+		    pb_as_fs_iso_audio_data_ep_desc.bEndpointAddress;
+		cp_as_hs_iso_audio_data_ep_desc.bEndpointAddress =
+		    cp_as_fs_iso_audio_data_ep_desc.bEndpointAddress;
+
+		f->hs_descriptors = fadc_hs_descriptors;
+
+		fadc_playback_dev->hs_iso_desc =
+		    (struct usb_endpoint_descriptor *)
+		    &pb_as_hs_iso_audio_data_ep_desc;
+
+		fadc_capture_dev->hs_iso_desc =
+		    (struct usb_endpoint_descriptor *)
+		    &cp_as_hs_iso_audio_data_ep_desc;
+	}
+
+	fadc_set_dev_state(fadc_playback_dev, FADC_STATE_CONFIGURED);
+	fadc_set_dev_state(fadc_capture_dev, FADC_STATE_CONFIGURED);
+
+	fadc_init_attr();
+
+	return 0;
+
+      fail:
+	/* cleanup of allocated resources (memory, descriptors, ...) is done in
+	   the unbind call, called by the composite framework because this
+	   function bind call fails */
+
+	return -ENODEV;
+}
+
+static void fadc_destroy_device(struct fadc_dev *dev)
+{
+	if (!dev)
+		return;
+
+	fadc_set_dev_state(dev, FADC_STATE_IDLE);
+
+	if (dev->iso) {
+		usb_ep_disable(dev->iso);
+	}
+
+	if (dev->request) {
+		usb_ep_free_request(dev->iso, dev->request);
+		dev->request = 0;
+	}
+
+	dev->fs_iso_desc = 0;
+	dev->hs_iso_desc = 0;
+
+	if (dev->iso) {
+		dev->iso->driver_data = 0;
+		dev->iso = 0;
+	}
+
+	del_timer_sync(&dev->timer);
+
+	if (dev->databuf) {
+		kfree(dev->databuf);
+		dev->databuf = 0;
+	}
+
+	fadc_set_dev_state(dev, FADC_STATE_VOID);
+
+	if (dev) {
+		kfree(dev);
+	}
+}
+
+static void fadc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	fadc_destroy_attr();
+
+	fadc_alsa_exit();
+
+	fadc_destroy_device(fadc_playback_dev);
+	fadc_playback_dev = 0;
+
+	fadc_destroy_device(fadc_capture_dev);
+	fadc_capture_dev = 0;
+
+#if 0
+	/* this is required if the descriptors have been copied from static
+	   initialisation data using cusb_copy_descriptors (in which case
+	   ->fs_iso_desc and ->hs_iso_desc have been set to the appropriate
+	   copied descriptors using usb_find_endpoint() */
+
+	if (f->descriptors) {
+		usb_free_descriptors(f->descriptors);
+		f->descriptors = 0;
+	}
+
+	if (f->hs_descriptors) {
+		usb_free_descriptors(f->hs_descriptors);
+		f->hs_descriptors = 0;
+	}
+#endif
+}
+
+/* ------------------------------------------------------------------------- */
+/*                         external interface                                */
+/* ------------------------------------------------------------------------- */
+
+struct fadc_function fadc_function_driver = {
+	.func = {
+		 .name = fadc_func_name,
+		 .strings = fadc_strings,
+		 .bind = fadc_bind,
+		 .unbind = fadc_unbind,
+		 .setup = fadc_setup,
+		 .disable = fadc_disable,
+		 .set_alt = fadc_set_alt,
+		 .get_alt = fadc_get_alt,
+		 .suspend = fadc_suspend,
+		 },
+	.playback = {
+		     .channels = FADC_DEFAULT_CHANNELS_PLAYBACK,
+		     .samplerate = FADC_DEFAULT_SAMPLERATE_PLAYBACK,
+		     },
+	.capture = {
+		    .channels = FADC_DEFAULT_CHANNELS_CAPTURE,
+		    .samplerate = FADC_DEFAULT_SAMPLERATE_CAPTURE,
+		    },
+};
diff --git a/drivers/usb/gadget/f_adc.h b/drivers/usb/gadget/f_adc.h
new file mode 100644
index 0000000..f9186a3
--- /dev/null
+++ b/drivers/usb/gadget/f_adc.h
@@ -0,0 +1,729 @@
+/* drivers/usb/gadget/f_adc.h
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#ifndef __F_ADC_H__
+#define __F_ADC_H__
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+struct fadc_device_parameters {
+	unsigned long int channels;
+	unsigned long int samplerate;
+};
+
+struct fadc_function {
+	struct usb_function func;
+	struct fadc_device_parameters playback;
+	struct fadc_device_parameters capture;
+};
+
+extern struct fadc_function fadc_function_driver;
+
+
+/* the definitions below should be included in include/linux/usb/audio.h */
+#ifndef __LINUX_USB_AUDIO_EXTENSION_H
+#define __LINUX_USB_AUDIO_EXTENSION_H
+
+#define AUDIO						0x01
+
+#define INTERFACE_SUBCLASS_UNDEFINED			0x00
+#define AUDIOCONTROL					0x01
+#define AUDIOSTREAMING					0x02
+#define MIDISTREAMING					0x03
+
+#define INTERFACE_PROTOCOL_UNDEFINED			0x00
+#define IP_VERSION_02_00				0x20
+
+#define FUNCTION_PROTOCOL_UNDEFINED			0x00
+#define AF_VERSION_02_00				IP_VERSION_02_00
+
+#define FUNCTION_SUBCLASS_UNDEFINED			0x00
+#define DESKTOP_SPEAKER					0x01
+#define HOME_THEATER					0x02
+#define MICROPHONE					0x03
+#define HEADSET						0x04
+#define TELEPHONE					0x05
+#define CONVERTER					0x06
+#define VOICESOUND_RECORDER				0x07
+#define IO_BOX						0x08
+#define MUSICAL_INSTRUMENT				0x09
+#define PRO_AUDIO					0x0A
+#define AUDIOVIDEO					0x0B
+#define CONTROL_PANEL					0x0C
+#define OTHER						0xFF
+
+#define CS_UNDEFINED					0x20
+#define CS_DEVICE					0x21
+#define CS_CONFIGURATION				0x22
+#define CS_STRING					0x23
+#define CS_INTERFACE					0x24
+#define CS_ENDPOINT					0x25
+
+#define AC_DESCRIPTOR_UNDEFINED				0x00
+#define HEADER						0x01
+#define INPUT_TERMINAL					0x02
+#define OUTPUT_TERMINAL					0x03
+#define MIXER_UNIT					0x04
+#define SELECTOR_UNIT					0x05
+#define FEATURE_UNIT					0x06
+#define EFFECT_UNIT					0x07
+#define PROCESSING_UNIT					0x08
+#define EXTENSION_UNIT					0x09
+#define CLOCK_SOURCE					0x0A
+#define CLOCK_SELECTOR					0x0B
+#define CLOCK_MULTIPLIER				0x0C
+#define SAMPLE_RATE_CONVERTER				0x0D
+
+#define AS_DESCRIPTOR_UNDEFINED				0x00
+#define AS_GENERAL					0x01
+#define FORMAT_TYPE					0x02
+#define ENCODER						0x03
+#define DECODER						0x04
+
+#define EFFECT_UNDEFINED				0x00
+#define PARAM_EQ_SECTION_EFFECT				0x01
+#define REVERBERATION_EFFECT				0x02
+#define MOD_DELAY_EFFECT				0x03
+#define DYN_RANGE_COMP_EFFECT				0x04
+
+#define PROCESS_UNDEFINED				0x00
+#define UPDOWNMIX_PROCESS				0x01
+#define DOLBY_PROLOGIC_PROCESS				0x02
+#define STEREO_EXTENDER_PROCESS				0x03
+
+#define DESCRIPTOR_UNDEFINED				0x00
+#define EP_GENERAL					0x01
+
+#define REQUEST_CODE_UNDEFINED				0x00
+#define CUR						0x01
+#define RANGE						0x02
+#define MEM						0x03
+
+#define ENCODER_UNDEFINED				0x00
+#define OTHER_ENCODER					0x01
+#define MPEG_ENCODER					0x02
+#define AC3_ENCODER					0x03
+#define WMA_ENCODER					0x04
+#define DTS_ENCODER					0x05
+
+#define DECODER_UNDEFINED				0x00
+#define OTHER_DECODER					0x01
+#define MPEG_DECODER					0x02
+#define AC3_DECODER					0x03
+#define WMA_DECODER					0x04
+#define DTS_DECODER					0x05
+
+#define CS_CONTROL_UNDEFINED				0x00
+#define CS_SAM_FREQ_CONTROL				0x01
+#define CS_CLOCK_VALID_CONTROL				0x02
+
+#define CX_CONTROL_UNDEFINED				0x00
+#define CX_CLOCK_SELECTOR_CONTROL			0x01
+
+#define CM_CONTROL_UNDEFINED				0x00
+#define CM_NUMERATOR_CONTROL				0x01
+#define CM_DENOMINATOR_CONTROL				0x02
+
+#define TE_CONTROL_UNDEFINED				0x00
+#define TE_COPY_PROTECT_CONTROL				0x01
+#define TE_CONNECTOR_CONTROL				0x02
+#define TE_OVERLOAD_CONTROL				0x03
+#define TE_CLUSTER_CONTROL				0x04
+#define TE_UNDERFLOW_CONTROL				0x05
+#define TE_OVERFLOW_CONTROL				0x06
+#define TE_LATENCY_CONTROL				0x07
+
+#define MU_CONTROL_UNDEFINED				0x00
+#define MU_MIXER_CONTROL				0x01
+#define MU_CLUSTER_CONTROL				0x02
+#define MU_UNDERFLOW_CONTROL				0x03
+#define MU_OVERFLOW_CONTROL				0x04
+#define MU_LATENCY_CONTROL				0x05
+
+#define SU_CONTROL_UNDEFINED				0x00
+#define SU_SELECTOR_CONTROL				0x01
+#define SU_LATENCY_CONTROL				0x02
+
+#define FU_CONTROL_UNDEFINED				0x00
+#define FU_MUTE_CONTROL					0x01
+#define FU_VOLUME_CONTROL				0x02
+#define FU_BASS_CONTROL					0x03
+#define FU_MID_CONTROL					0x04
+#define FU_TREBLE_CONTROL				0x05
+#define FU_GRAPHIC_EQUALIZER_CONTROL			0x06
+#define FU_AUTOMATIC_GAIN_CONTROL			0x07
+#define FU_DELAY_CONTROL				0x08
+#define FU_BASS_BOOST_CONTROL				0x09
+#define FU_LOUDNESS_CONTROL				0x0A
+#define FU_INPUT_GAIN_CONTROL				0x0B
+#define FU_INPUT_GAIN_PAD_CONTROL			0x0C
+#define FU_PHASE_INVERTER_CONTROL			0x0D
+#define FU_UNDERFLOW_CONTROL				0x0E
+#define FU_OVERFLOW_CONTROL				0x0F
+#define FU_LATENCY_CONTROL				0x10
+
+#define PE_CONTROL_UNDEFINED				0x00
+#define PE_ENABLE_CONTROL				0x01
+#define PE_CENTERFREQ_CONTROL				0x02
+#define PE_QFACTOR_CONTROL				0x03
+#define PE_GAIN_CONTROL					0x04
+#define PE_UNDERFLOW_CONTROL				0x05
+#define PE_OVERFLOW_CONTROL				0x06
+#define PE_LATENCY_CONTROL				0x07
+
+#define RV_CONTROL_UNDEFINED				0x00
+#define RV_ENABLE_CONTROL				0x01
+#define RV_TYPE_CONTROL					0x02
+#define RV_LEVEL_CONTROL				0x03
+#define RV_TIME_CONTROL					0x04
+#define RV_FEEDBACK_CONTROL				0x05
+#define RV_PREDELAY_CONTROL				0x06
+#define RV_DENSITY_CONTROL				0x07
+#define RV_HIFREQ_ROLLOFF_CONTROLL			0x08
+#define RV_UNDERFLOW_CONTROL				0x09
+#define RV_OVERFLOW_CONTROL				0x0A
+#define RV_LATENCY_CONTROL				0x0B
+
+#define MD_CONTROL_UNDEFINED				0x00
+#define MD_ENABLE_CONTROL				0x01
+#define MD_BALANCE_CONTROL				0x02
+#define MD_RATE_CONTROL					0x03
+#define MD_DEPTH_CONTROL				0x04
+#define MD_TIME_CONTROL					0x05
+#define MD_FEEDBACK_CONTROL				0x06
+#define MD_UNDERFLOW_CONTROL				0x07
+#define MD_OVERFLOW_CONTROL				0x08
+#define MD_LATENCY_CONTROL				0x09
+
+#define DR_CONTROL_UNDEFINED				0x00
+#define DR_ENABLE_CONTROL				0x01
+#define DR_COMPRESSION_RATE_CONTROL			0x02
+#define DR_MAXAMPL_CONTROL				0x03
+#define DR_THRESHOLD_CONTROL				0x04
+#define DR_ATTACK_TIME_CONTROL				0x05
+#define DR_RELEASE_TIME_CONTROL				0x06
+#define DR_UNDERFLOW_CONTROL				0x07
+#define DR_OVERFLOW_CONTROL				0x08
+#define DR_LATENCY_CONTROL				0x09
+
+#define UD_CONTROL_UNDEFINED				0x00
+#define UD_ENABLE_CONTROL				0x01
+#define UD_MODE_SELECT_CONTROL				0x02
+#define UD_CLUSTER_CONTROL				0x03
+#define UD_UNDERFLOW_CONTROL				0x04
+#define UD_OVERFLOW_CONTROL				0x05
+#define UD_LATENCY_CONTROL				0x06
+
+#define DP_CONTROL_UNDEFINED				0x00
+#define DP_ENABLE_CONTROL				0x01
+#define DP_MODE_SELECT_CONTROL				0x02
+#define DP_CLUSTER_CONTROL				0x03
+#define DP_UNDERFLOW_CONTROL				0x04
+#define DP_OVERFLOW_CONTROL				0x05
+#define DP_LATENCY_CONTROL				0x06
+
+#define ST_EXT_CONTROL_UNDEFINED			0x00
+#define ST_EXT_ENABLE_CONTROL				0x01
+#define ST_EXT_WIDTH_CONTROL				0x02
+#define ST_EXT_UNDERFLOW_CONTROL			0x03
+#define ST_EXT_OVERFLOW_CONTROL				0x04
+#define ST_EXT_LATENCY_CONTROL				0x05
+
+#define XU_CONTROL_UNDEFINED				0x00
+#define XU_ENABLE_CONTROL				0x01
+#define XU_CLUSTER_CONTROL				0x02
+#define XU_UNDERFLOW_CONTROL				0x03
+#define XU_OVERFLOW_CONTROL				0x04
+#define XU_LATENCY_CONTROL				0x05
+
+#define AS_CONTROL_UNDEFINED				0x00
+#define AS_ACT_ALT_SETTING_CONTROL			0x01
+#define AS_VAL_ALT_SETTINGS_CONTROL			0x02
+#define AS_AUDIO_DATA_FORMAT_CONTROL			0x03
+
+#define EN_CONTROL_UNDEFINED				0x00
+#define EN_BIT_RATE_CONTROL				0x01
+#define EN_QUALITY_CONTROL				0x02
+#define EN_VBR_CONTROL					0x03
+#define EN_TYPE_CONTROL					0x04
+#define EN_UNDERFLOW_CONTROL				0x05
+#define EN_OVERFLOW_CONTROL				0x06
+#define EN_ENCODER_ERROR_CONTROL			0x07
+#define EN_PARAM1_CONTROL				0x08
+#define EN_PARAM2_CONTROL				0x09
+#define EN_PARAM3_CONTROL				0x0A
+#define EN_PARAM4_CONTROL				0x0B
+#define EN_PARAM5_CONTROL				0x0C
+#define EN_PARAM6_CONTROL				0x0D
+#define EN_PARAM7_CONTROL				0x0E
+#define EN_PARAM8_CONTROL				0x0F
+
+#define MD_CONTROL_UNDEFINED				0x00
+#define MD_DUAL_CHANNEL_CONTROL				0x01
+#define MD_SECOND_STEREO_CONTROL			0x02
+#define MD_MULTILANGUAL_CONTROL				0x03
+#define MD_DYN_RANGE_CONTROL				0x04
+#define MD_SCALING_CONTROL				0x05
+#define MD_HILO_SCALING_CONTROL				0x06
+#define MD_UNDERFLOW_CONTROL				0x07
+#define MD_OVERFLOW_CONTROL				0x08
+#define MD_DECODER_ERROR_CONTROL			0x09
+
+#define AD_CONTROL_UNDEFINED				0x00
+#define AD_MODE_CONTROL					0x01
+#define AD_DYN_RANGE_CONTROL				0x02
+#define AD_SCALING_CONTROL				0x03
+#define AD_HILO_SCALING_CONTROL				0x04
+#define AD_UNDERFLOW_CONTROL				0x05
+#define AD_OVERFLOW_CONTROL				0x06
+#define AD_DECODER_ERROR_CONTROL			0x07
+
+#define WD_CONTROL_UNDEFINED				0x00
+#define WD_UNDERFLOW_CONTROL				0x01
+#define WD_OVERFLOW_CONTROL				0x02
+#define WD_DECODER_ERROR_CONTROL			0x03
+
+#define DD_CONTROL_UNDEFINED				0x00
+#define DD_UNDERFLOW_CONTROL				0x01
+#define DD_OVERFLOW_CONTROL				0x02
+#define DD_DECODER_ERROR_CONTROL			0x03
+
+#define EP_CONTROL_UNDEFINED				0x00
+#define EP_PITCH_CONTROL				0x01
+#define EP_DATA_OVERRUN_CONTROL				0x02
+#define EP_DATA_UNDERRUN_CONTROL			0x03
+
+#define FORMAT_TYPE_UNDEFINED				0x00
+#define FORMAT_TYPE_I					0x01
+#define FORMAT_TYPE_II					0x02
+#define FORMAT_TYPE_III					0x03
+#define FORMAT_TYPE_IV					0x04
+#define EXT_FORMAT_TYPE_I				0x81
+#define EXT_FORMAT_TYPE_II				0x82
+#define EXT_FORMAT_TYPE_III				0x83
+
+struct audio_channel_cluster_descriptor {
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+} __attribute__ ((packed));
+
+struct dolby_prologic_cluster_descriptor {
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+} __attribute__ ((packed));
+
+struct standard_ac_interface_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bInterfaceNumer;
+	__u8 bAlternateSetting;
+	__u8 bNumEndpoints;
+	__u8 bInterfaceClass;
+	__u8 bInterfaceSubClass;
+	__u8 bInterfaceProtocol;
+	__u8 iInterface;
+} __attribute__ ((packed));
+
+struct cs_ac_interface_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u16 bcdADC;
+	__u16 wTotalLength;
+	__u8 bInCollection;
+	__u8 baInterfaceNr;
+} __attribute__ ((packed));
+
+struct cs_ac_interface_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u16 bcdADC;
+	__u8 bCategory;
+	__u16 wTotalLength;
+	__u8 bmControls;
+} __attribute__ ((packed));
+
+struct clock_source_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bmAttributes;
+	__u8 bmControls;
+	__u8 bAssocTerminal;
+	__u8 iClockSource;
+} __attribute__ ((packed));
+
+struct clock_selector_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bNrInPins;
+	__u8 baCSourceID1; /* Add baCSourceID(p) */
+	__u8 bmControls;
+	__u8 iClockSelector;
+} __attribute__ ((packed));
+
+struct clock_multiplier_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bClockID;
+	__u8 bCSourceID;
+	__u8 bmControls;
+	__u8 iClockMultiplier;
+} __attribute__ ((packed));
+
+struct input_terminal_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bNrChannels;
+	__u16 wChannelConfig;
+	__u8 iChannelNames;
+	__u8 iTerminal;
+} __attribute__ ((packed));
+
+struct input_terminal_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bCSourceID;
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+	__u16 bmControls;
+	__u8 iTerminal;
+} __attribute__ ((packed));
+
+struct output_terminal_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bSourceID;
+	__u8 iTerminal;
+} __attribute__ ((packed));
+
+struct output_terminal_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalID;
+	__u16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bSourceID;
+	__u8 bCSourceID;
+	__u16 bmControls;
+	__u8 iTerminal;
+} __attribute__ ((packed));
+
+struct mixer_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bNrInPins;
+	__u8 baSourceID1;	/* Add baSourceID(p) */
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+	/* Add bmMixerControls */
+	__u8 bmControls;
+	__u8 iMixer;
+} __attribute__ ((packed));
+
+struct selector_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bNrInPins;
+	__u8 baSourceID1;	/* Add baSourceID(p) */
+	__u8 bmControls;
+	__u8 iSelector;
+} __attribute__ ((packed));
+
+struct feature_unit_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__u8 bControlSize;
+	__u8 bmaControls0;
+	__u8 iFeature;
+} __attribute__ ((packed));
+
+struct feature_unit_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__u8 bmaControls0;	/* Add bmaControls(1) bmaControls(ch) */
+	__u8 iFeature;
+} __attribute__ ((packed));
+
+struct sampling_rate_converter_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__u8 bCSourceInID;
+	__u8 bCSourceOutID;
+	__u8 iSRC;
+} __attribute__ ((packed));
+
+struct extension_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bUnitID;
+	__u16 wExtensionCode;
+	__u8 bNrInPins;
+	__u8 baSourceID1;	/* Add baSourceID(p) */
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+	__u8 bmControls;
+	__u8 iExtension;
+} __attribute__ ((packed));
+
+struct standard_ac_interrupt_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bEndpointAddress;
+	__u8 bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8 bInterval;
+} __attribute__ ((packed));
+
+struct standard_as_interface_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bInterfaceNumber;
+	__u8 bAlternateSetting;
+	__u8 bNumEndpoints;
+	__u8 bInterfaceClass;
+	__u8 bInterfaceSubClass;
+	__u8 bInterfaceProtocol;
+	__u8 iInterface;
+} __attribute__ ((packed));
+
+struct cs_as_interface_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalLink;
+	__u8 bDelay;
+	__u16 wFormatTag;
+} __attribute__ ((packed));
+
+struct cs_as_interface_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bTerminalLink;
+	__u8 bmControls;
+	__u8 bFormatType;
+	__u8 bmFormats0;
+	__u8 bmFormats1;
+	__u8 bmFormats2;
+	__u8 bmFormats3;
+	__u8 bNrChannels;
+	__u8 bmChannelConfig0;
+	__u8 bmChannelConfig1;
+	__u8 bmChannelConfig2;
+	__u8 bmChannelConfig3;
+	__u8 iChannelNames;
+} __attribute__ ((packed));
+
+struct encoder_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bEncoderID;
+	__u8 bEncoder;
+	__u8 bmControls0;
+	__u8 bmControls1;
+	__u8 bmControls2;
+	__u8 bmControls3;
+	__u8 iParam1;
+	__u8 iParam2;
+	__u8 iParam3;
+	__u8 iParam4;
+	__u8 iParam5;
+	__u8 iParam6;
+	__u8 iParam7;
+	__u8 iParam8;
+	__u8 iEncoder;
+} __attribute__ ((packed));
+
+struct mpeg_decoder_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bDecoderID;
+	__u8 bDecoder;
+	__u16 bmMPEGCapabilities;
+	__u8 bmMPEGFeatures;
+	__u8 bmControls;
+	__u8 iDecoder;
+} __attribute__ ((packed));
+
+struct ac3_dcoder_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bDecoderID;
+	__u8 bDecoder;
+	__u8 bmBSID0;
+	__u8 bmBSID1;
+	__u8 bmBSID2;
+	__u8 bmBSID3;
+	__u8 bmAC3Features;
+	__u8 bmControls;
+	__u8 iDecoder;
+} __attribute__ ((packed));
+
+struct wma_decoder_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bDecoderID;
+	__u8 bDecoder;
+	__u16 bmWMAProfile;
+	__u8 bmControls;
+	__u8 iDecoder;
+} __attribute__  ((packed));
+
+struct dts_decoder_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bDecoderID;
+	__u8 bDecoder;
+	__u8 bmCapabilities;
+	__u8 bmControls;
+	__u8 iDecoder;
+} __attribute__ ((packed));
+
+struct standard_as_isochronous_audio_data_endpoint_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bEndpointAddress;
+	__u8 bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8 bInterval;
+	__u8 bRefresh;
+	__u8 bSyncAddress;
+} __attribute__ ((packed));
+
+struct cs_as_isochronous_audio_data_endpoint_descriptor_10 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bmAttributes;
+	__u8 bLockDelayUnits;
+	__u16 wLockDelay;
+} __attribute__ ((packed));
+
+struct cs_as_isochronous_audio_data_endpoint_descriptor_20 {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bmAttributes;
+	__u8 bmControls;
+	__u8 bLockDelayUnits;
+	__u16 wLockDelay;
+} __attribute__ ((packed));
+
+struct standard_as_isochronous_feedback_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bEndpointAddress;
+	__u8 bmAttributes;
+	__u16 wMaxPacketSize;
+	__u8 bInterval;
+} __attribute__ ((packed));
+
+struct format_type_descriptor_cont {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatType;
+	__u8 bNrChannels;
+	__u8 bSubFrameSize;
+	__u8 bBitResolution;
+	__u8 bSamFreqType;
+	__u8 tLowerSamFreq00;
+	__u8 tLowerSamFreq01;
+	__u8 tLowerSamFreq02;
+	__u8 tUpperSamFreq00;
+	__u8 tUpperSamFreq01;
+	__u8 tUpperSamFreq02;
+} __attribute__ ((packed));
+
+struct format_type_descriptor_disc {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatType;
+	__u8 bNrChannels;
+	__u8 bSubFrameSize;
+	__u8 bBitResolution;
+	__u8 bSamFreqType;
+	__u8 tSamFreq00;
+	__u8 tSamFreq01;
+	__u8 tSamFreq02;
+	__u8 tSamFreq10;
+	__u8 tSamFreq11;
+	__u8 tSamFreq12;
+	__u8 tSamFreq20;
+	__u8 tSamFreq21;
+	__u8 tSamFreq22;
+	__u8 tSamFreq30;
+	__u8 tSamFreq31;
+	__u8 tSamFreq32;
+} __attribute__ ((packed));
+
+#endif	// __LINUX_USB_AUDIO_EXTENSION_H
+
+#endif	//__F_AUDIO_H__
diff --git a/drivers/usb/gadget/f_hidc.c b/drivers/usb/gadget/f_hidc.c
new file mode 100644
index 0000000..07f26ef
--- /dev/null
+++ b/drivers/usb/gadget/f_hidc.c
@@ -0,0 +1,745 @@
+/* drivers/usb/gadget/f_hidc.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/usb/composite.h>
+#include <linux/uaccess.h>
+#include <linux/semaphore.h>
+#include <linux/poll.h>
+
+#include "f_hidc.h"
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/list.h>
+
+#define FHIDC_DEFAULT_VENDOR	(0x0000)
+#define FHIDC_DEFAULT_PRODUCT	(0x0000)
+#define FHIDC_DEFAULT_VERSION	(0x0000)
+#define FHIDC_DEFAULT_LIMITS_X	(0xFFFF)
+#define FHIDC_DEFAULT_LIMITS_Y	(0xFFFF)
+#define FHIDC_DEFAULT_LIMITS_P	(0xFFFF)
+
+#define FHIDC_NAME		"fhid"
+#define FHIDC_MAJOR		249
+#define FHIDC_DEV_MINOR		0
+
+#define FHIDC_STRING_IDX	0
+
+static struct usb_string fhidc_strings_dev[] = {
+	[FHIDC_STRING_IDX].s = "USB Events",
+	{}
+};
+
+static struct usb_gadget_strings fhidc_stringtab_dev = {
+	.language = 0x0409,
+	.strings = fhidc_strings_dev,
+};
+
+static struct usb_gadget_strings *fhidc_strings[] = {
+	&fhidc_stringtab_dev,
+	NULL,
+};
+
+static struct usb_interface_descriptor hid_interface_desc = {
+	.bLength = sizeof hid_interface_desc,  /* size of the descriptor in bytes */
+	.bDescriptorType = USB_DT_INTERFACE,   /* interface descriptor. 0x04 */
+	.bInterfaceNumber = 0x00,              /* interface number */
+	.bAlternateSetting = 0x00,             /* index of this setting */
+	.bNumEndpoints = 0x01,                 /* we don't have any extra endpoints (interrupt EP for example) */
+	.bInterfaceClass = 0x03,               /* HID class: 0x03 */
+	.bInterfaceSubClass = 0x00,            /* no subclass; only used for boot interface */
+	.bInterfaceProtocol = 0x00,            /* not used. */
+};
+
+static struct hid_descriptor hid_desc = {
+	.bLength = sizeof hid_desc,
+	.bHIDDescriptorType	= USB_DT_HID,
+	.bcdHID = __constant_cpu_to_le16(0x111),
+	.bCountryCode		= 0,
+	.bNumDescriptors	= 1,
+	.bReportDescriptorType	= USB_DT_REPORT,
+	.wDescriptorLength 	= __constant_cpu_to_le16(0),
+};
+
+/* this endpoint is not really used, but it is here because the specification
+   demands it so its static characteristics are set to extreme values (small
+   packets, long inter-packet interval */
+
+static struct usb_endpoint_descriptor fs_intr_in_ep_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize = __constant_cpu_to_le16(8), 	/* minimal size               */
+	.bInterval = 0x10,				/* 16 = 2^15 frames (~30 sec) */
+};
+
+static struct usb_endpoint_descriptor hs_intr_in_ep_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize = __constant_cpu_to_le16(8), 	/* minimal size                   */
+	.bInterval = 0x10,				/* 16 = 2^15 microframes (~ 4sec) */
+};
+
+static struct usb_descriptor_header *fhidc_full_speed_descriptors[] = {
+	(struct usb_descriptor_header *)&hid_interface_desc,
+	(struct usb_descriptor_header *)&hid_desc,
+	(struct usb_descriptor_header *)&fs_intr_in_ep_desc,
+	NULL,
+};
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+static struct usb_descriptor_header *fhidc_high_speed_descriptors[] = {
+	(struct usb_descriptor_header *)&hid_interface_desc,
+	(struct usb_descriptor_header *)&hid_desc,
+	(struct usb_descriptor_header *)&hs_intr_in_ep_desc,
+	NULL,
+};
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
+
+/* event specific definitions */
+enum fhidc_dev_state {
+	FHIDC_STATE_VOID,          /* pseudo state when driver is not initialized
+				    (fhidc_dev == 0) */
+	FHIDC_STATE_IDLE,          /* state after config_bind */
+	FHIDC_STATE_CONFIGURED,    /* configuration binding done, necessary
+				     resources claimed, user space access ok */
+	FHIDC_STATE_SELECTED,      /* interface activated */
+};
+
+/* this array determines the report id associated with a certain report
+   handler; note that some of these reports may - according to the kernel
+   configuration - not be supported (these will have desc and all func pointers
+   equal to zero. */
+
+static struct fhidc_report *fhidc_reports[] = {
+	0,
+	&fhidc_panel_report,
+	&fhidc_keyboard_report,
+};
+
+/* this array determines the assignment of minor numbers to report handlers;
+   those that need fops (minor 0 is reserved for fops associated with this
+   generic fhid driver) */
+static struct fhidc_report *fhidc_minors[] = {
+	0,
+};
+
+static struct fhidc_dev {
+	struct semaphore 		mutex;
+	enum fhidc_dev_state 		state;
+	int 				use;
+	int 				alt;
+	struct usb_ep                   *intr_in;
+	struct usb_endpoint_descriptor  *intr_in_ep_desc;
+	dev_t 				devnum;
+} *fhidc_dev = 0;
+
+static inline void fhidc_set_dev_state(struct fhidc_dev *dev,
+						enum fhidc_dev_state state)
+{
+	if (!dev)
+		return;
+
+	dev->state = state;
+}
+
+static inline int fhidc_access_claim(void)
+{
+	return down_interruptible(&fhidc_dev->mutex);
+}
+
+static inline void fhidc_access_release(void)
+{
+	up(&fhidc_dev->mutex);
+}
+
+static void fhidc_disable(struct usb_function *f)
+{
+	if (fhidc_dev->state <= FHIDC_STATE_CONFIGURED)
+		return;
+
+	fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_CONFIGURED);
+
+	usb_ep_disable(fhidc_dev->intr_in);
+}
+
+static int fhidc_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if ((intf == hid_interface_desc.bInterfaceNumber) &&
+	    (alt == hid_interface_desc.bAlternateSetting)) {
+		fhidc_dev->intr_in_ep_desc = ep_choose(cdev->gadget,
+							&fs_intr_in_ep_desc,
+							&hs_intr_in_ep_desc);
+
+		usb_ep_enable(fhidc_dev->intr_in, fhidc_dev->intr_in_ep_desc);
+
+		fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_SELECTED);
+		fhidc_dev->alt = alt;
+	}
+
+	return 0;
+}
+
+static int fhidc_get_alt(struct usb_function *f, unsigned intf)
+{
+	return fhidc_dev->alt;
+}
+
+/* -------------------------------------------------------------------------
+       functions that are safe to call from atomic (non-blocking) context
+   ------------------------------------------------------------------------- */
+
+static void fhidc_set_report_complete(struct usb_ep *ep,
+						struct usb_request *req)
+{
+	struct fhidc_report *r;
+	if (!ep)
+		return;
+	if (!req)
+		return;
+
+	if (req->status)
+		return;
+
+	r = (struct fhidc_report*) req->context;
+
+	if ((r) && (r->set_report))
+		r->set_report(req->buf, req->length);
+}
+
+static int class_setup_req(struct usb_composite_dev *cdev,
+				    const struct usb_ctrlrequest *ctrl)
+{
+	int value = -EOPNOTSUPP;
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+	struct usb_request *req = cdev->req;
+
+	switch (ctrl->bRequest) {
+	case USB_REQ_GET_REPORT:	//Mandatory
+	{
+		int id = w_value & 0xff;
+		unsigned char *buf = req->buf;
+
+		if (id > ARRAY_SIZE(fhidc_reports)) {
+			value = -EINVAL;
+		} else {
+			struct fhidc_report *r = fhidc_reports[id];
+
+			if((r) && (r->get_report)) {
+				value = r->get_report(buf, w_length);
+			} else {
+				value = -EINVAL;
+			}
+		}
+		break;
+	}
+
+	case USB_REQ_SET_REPORT:
+	{
+		int id = w_value & 0xff;
+
+		if (id > ARRAY_SIZE(fhidc_reports)) {
+			value = -EINVAL;
+		} else {
+			struct fhidc_report *r = fhidc_reports[id];
+
+			if((r) && (r->set_report) && (r->size > 0)) {
+				cdev->req->complete =
+				    fhidc_set_report_complete;
+				cdev->req->context = r;
+				value = r->size;
+			} else {
+				value = -EINVAL;
+			}
+		}
+		break;
+	}
+
+	case USB_REQ_SET_IDLE:
+		value = 0;
+		break;
+
+	case USB_REQ_GET_PROTOCOL:
+		value = -EOPNOTSUPP;
+		break;
+
+	case USB_REQ_GET_IDLE:
+		value = -EOPNOTSUPP;
+		break;
+
+	case USB_REQ_SET_PROTOCOL:
+		value = 0;
+		break;
+	}
+
+	return value;
+}
+
+static int fhidc_copy_hid_descriptor(unsigned char* buf, int len)
+{
+	memcpy(buf, &hid_desc, hid_desc.bLength);
+	return (hid_desc.bLength);
+}
+
+static int fhidc_copy_report_descriptors(unsigned char* buf, int len)
+{
+	int i;
+	unsigned char* org = buf;
+
+	for(i = 0; i < ARRAY_SIZE(fhidc_reports); i++) {
+		struct fhidc_report *r = fhidc_reports[i];
+		if ((r) && (r->desc) && (r->desc_size > 0)) {
+			len -= r->desc_size;
+			if (len >= 0) {
+				memcpy(buf, r->desc, r->desc_size);
+			} else
+				break;
+
+			buf += r->desc_size;
+		}
+	}
+
+	return (buf - org);
+}
+
+/* Handle non class specific setup messages */
+static int standard_setup_req(struct usb_composite_dev *cdev,
+				       const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_request *req = cdev->req;
+	int value = -EOPNOTSUPP;
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	switch (ctrl->bRequest) {
+	case USB_REQ_GET_DESCRIPTOR:
+		switch (w_value >> 8) {
+		/* Class specific */
+		case USB_DT_HID:
+			/* HID descriptor */
+			value = fhidc_copy_hid_descriptor(req->buf, w_length);
+			break;
+		case USB_DT_REPORT:
+			value =
+			    fhidc_copy_report_descriptors(req->buf, w_length);
+			break;
+		case USB_DT_PHYSICAL:
+			value = 0;
+			break;
+		}
+		break;
+
+	default:
+		break;
+	}
+	return value;
+}
+
+static int fhidc_setup(struct usb_function *f,
+				const struct usb_ctrlrequest *ctrl)
+{
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request *req = cdev->req;
+	int value = -EOPNOTSUPP;
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
+		value = class_setup_req(cdev, ctrl);
+	else
+		value = standard_setup_req(cdev, ctrl);
+
+	if (value >= 0) {
+		value = min((u16)value, w_length);
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+	}
+	return value;
+}
+
+static unsigned int fhidc_poll(struct file *file, poll_table * wait)
+{
+	int result;
+	unsigned long int flags;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	int idx = iminor(inode);
+
+	result = fhidc_access_claim();
+
+	if (result) {
+		return result;
+	}
+
+	local_irq_save(flags);
+
+	result = -EOPNOTSUPP;
+
+	if (idx != FHIDC_DEV_MINOR) {
+		struct fhidc_report *r =
+		    (struct fhidc_report *)file->private_data;
+		if ((r) && (r->poll))
+			result = r->poll(file, wait);
+	}
+
+	local_irq_restore(flags);
+
+	fhidc_access_release();
+
+	return result;
+}
+
+/* need to acquire device structure lock and disable interrupts */
+static int fhidc_write(struct file *file, const char __user * buffer,
+			size_t count, loff_t* offset)
+{
+	int result;
+	unsigned long int flags;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	int idx = iminor(inode);
+
+	result = fhidc_access_claim();
+
+	if (result) {
+		return result;
+	}
+
+	local_irq_save(flags);
+
+	result = -EOPNOTSUPP;
+
+	if (idx != FHIDC_DEV_MINOR) {
+		struct fhidc_report *r =
+		    (struct fhidc_report *)file->private_data;
+		if ((r) && (r->write))
+			result = r->write(file, buffer, count, offset);
+	}
+
+	local_irq_restore(flags);
+
+	fhidc_access_release();
+
+	return result;
+}
+
+/* needs to acquire device structure lock */
+static int fhidc_open(struct inode *inode, struct file *file)
+{
+	int result;
+	int idx = iminor(inode);
+	int i;
+
+	/* TODO: check open mode - READ/WRITE with availability of operations */
+
+	result = fhidc_access_claim();
+
+	if (result) {
+		return result;
+	}
+
+	file->private_data = 0;
+	/* mutex locked, do not 'return' */
+
+	result = -EOPNOTSUPP;
+
+	for (i = 0; i < ARRAY_SIZE(fhidc_reports); i++) {
+		struct fhidc_report *r = fhidc_reports[i];
+		if ((r) && (r->minor == idx)) {
+			file->private_data = r;
+			result = 0;
+			break;
+		}
+	}
+
+	if (result == -ENODEV) {
+		goto unlock;
+	}
+
+	if (fhidc_dev->use & (1 << idx)) {
+		result = -EBUSY;
+		goto unlock;
+	}
+
+	fhidc_dev->use |= (1 << idx);
+
+ unlock:
+	fhidc_access_release();
+
+	return result;
+}
+
+static int fhidc_release(struct inode *inode, struct file *file)
+{
+	int result;
+	int idx = iminor(inode);
+
+	result = fhidc_access_claim();
+
+	if (result) {
+		return result;
+	}
+
+	if (file->private_data == 0) {
+		result = -ENODEV;
+		goto unlock;
+	}
+
+	if (!(fhidc_dev->use & (1 << idx))) {
+		result = -ENOENT;
+		goto unlock;
+	}
+
+	fhidc_dev->use &= ~(1 << idx);
+
+ unlock:
+	fhidc_access_release();
+
+	return result;
+}
+
+/* need to acquire device structure lock and disable interrupts */
+static ssize_t fhidc_dev_read(struct file *file, char __user * buffer,
+				size_t count, loff_t* offset)
+{
+	/* no locking required, only access to read-only data */
+
+	return fhidc_copy_report_descriptors(buffer, count);
+}
+
+static ssize_t fhidc_read(struct file *file, char __user * buffer,
+			size_t count, loff_t* offset)
+{
+	int result;
+	unsigned long int flags;
+	struct inode *inode = file->f_path.dentry->d_inode;
+	int idx = iminor(inode);
+
+	result = fhidc_access_claim();
+
+	if (result) {
+		return result;
+	}
+
+	local_irq_save(flags);
+
+	result = -EOPNOTSUPP;
+
+	if (idx == FHIDC_DEV_MINOR) {
+		result = fhidc_dev_read(file, buffer, count, offset);
+	} else {
+		struct fhidc_report *r =
+		    (struct fhidc_report *)file->private_data;
+		if ((r) && (r->read))
+			result = r->read(file, buffer, count, offset);
+	}
+	local_irq_restore(flags);
+
+	fhidc_access_release();
+
+	return result;
+}
+
+struct file_operations fhidc_fops = {
+	.owner = THIS_MODULE,
+	.open = fhidc_open,
+	.release = fhidc_release,
+	.write = fhidc_write,
+	.read = fhidc_read,
+	.poll = fhidc_poll,
+};
+
+static void fhidc_init_reports(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fhidc_minors); i++) {
+		struct fhidc_report *r = fhidc_minors[i];
+
+		if (r) {
+			if ((r->desc) && (r->desc_size > 0)) {
+				r->minor = i;
+			} else {
+				r->id = 0;
+			}
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fhidc_reports); i++) {
+		struct fhidc_report *r = fhidc_reports[i];
+
+		if (r) {
+			r->id = i;
+
+			if (r->init)
+				r->init();
+		}
+	}
+}
+
+static void fhidc_exit_reports(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(fhidc_reports); i++) {
+		struct fhidc_report *r = fhidc_reports[i];
+
+		if (r) {
+			if (r->exit)
+				r->exit();
+		}
+	}
+}
+
+static void __init fhidc_prepare_descriptors(void)
+{
+	int i;
+	int size = 0;
+
+	for (i = 0; i < ARRAY_SIZE(fhidc_reports); i++) {
+		struct fhidc_report *r = fhidc_reports[i];
+
+		if (r) {
+			size += r->desc_size;
+		}
+	}
+
+	hid_desc.wDescriptorLength = __cpu_to_le16(size);
+}
+/* init/exit - module insert/removal */
+
+static int __init fhidc_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct usb_ep *ep = 0;
+	int status;
+
+	if (!(fhidc_dev = kzalloc(sizeof *fhidc_dev, GFP_KERNEL)))
+		return -ENOMEM;
+
+	/* reset all report types, support for each report
+	   depends on the kernel configuration */
+
+	fhidc_init_reports();
+
+	sema_init(&fhidc_dev->mutex, 0);
+
+	fhidc_prepare_descriptors();
+
+	fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_IDLE);
+
+	if (register_chrdev(FHIDC_MAJOR, FHIDC_NAME, &fhidc_fops) < 0) {
+		return -ENODEV;
+	}
+
+	fhidc_dev->devnum = MKDEV(FHIDC_MAJOR, 0);
+
+	/* user space access allowed from now */
+	up(&fhidc_dev->mutex);
+
+	status = usb_interface_id(c, f);
+	if (status < 0) {
+		goto fail;
+	}
+
+	hid_interface_desc.bInterfaceNumber = status;
+
+	if (fhidc_strings_dev[0].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0)
+			goto fail;
+
+		fhidc_strings_dev[0].id = status;
+		hid_interface_desc.iInterface = status;
+	}
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_intr_in_ep_desc);
+	if (!ep) {
+		goto fail;
+	}
+
+	f->descriptors = fhidc_full_speed_descriptors;
+	fhidc_dev->intr_in = ep;
+
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_intr_in_ep_desc.bEndpointAddress =
+					fs_intr_in_ep_desc.bEndpointAddress;
+
+		f->hs_descriptors = fhidc_high_speed_descriptors;
+	}
+
+	ep->driver_data = fhidc_dev;
+	fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_CONFIGURED);
+
+	return 0;
+
+ fail:
+	/* cleanup of allocated resources (memory, descriptors, ...) is done in
+	   fhidc_unbind that is called by the composite framework because this
+	   bind call fails */
+	return -ENODEV;
+}
+
+static void fhidc_unbind(struct usb_configuration *c,
+					     struct usb_function *f)
+{
+	fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_IDLE);
+
+	fhidc_exit_reports();
+
+	if (fhidc_dev->intr_in) {
+		usb_ep_disable(fhidc_dev->intr_in);
+	}
+
+	f->descriptors = 0;
+	f->hs_descriptors = 0;
+
+	if (fhidc_dev->intr_in) {
+		fhidc_dev->intr_in->driver_data = 0;
+		fhidc_dev->intr_in = 0;
+	}
+
+	if (fhidc_dev->devnum == MKDEV(FHIDC_MAJOR, 0)) {
+		unregister_chrdev(MAJOR(fhidc_dev->devnum), FHIDC_NAME);
+	}
+
+	fhidc_set_dev_state(fhidc_dev, FHIDC_STATE_VOID);
+
+	kfree(fhidc_dev);
+	fhidc_dev = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*                         external interface                                */
+/* ------------------------------------------------------------------------- */
+
+struct fhidc_function fhidc_function_driver = {
+	.func = {
+		.name = FHIDC_NAME,
+		.strings = fhidc_strings,
+		.bind = fhidc_bind,
+		.unbind = fhidc_unbind,
+		.setup = fhidc_setup,
+		.disable = fhidc_disable,
+		.set_alt = fhidc_set_alt,
+		.get_alt = fhidc_get_alt,
+	},
+	.param = {
+		.vendor = FHIDC_DEFAULT_VENDOR,
+		.product = FHIDC_DEFAULT_PRODUCT,
+		.version = FHIDC_DEFAULT_VERSION,
+		.limits.x = FHIDC_DEFAULT_LIMITS_X,
+		.limits.y = FHIDC_DEFAULT_LIMITS_Y,
+		.limits.p = FHIDC_DEFAULT_LIMITS_P,
+	},
+};
diff --git a/drivers/usb/gadget/f_hidc.h b/drivers/usb/gadget/f_hidc.h
new file mode 100644
index 0000000..66c5116
--- /dev/null
+++ b/drivers/usb/gadget/f_hidc.h
@@ -0,0 +1,97 @@
+/* drivers/usb/gadget/f_hidc.h
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#ifndef __F_HIDC_H__
+#define __F_HIDC_H__
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+
+struct fhidc_device_parameters {
+	unsigned short vendor;
+	unsigned short product;
+	unsigned short version;
+	struct {
+		unsigned short x;
+		unsigned short y;
+		unsigned short p;
+	} limits;
+};
+
+struct fhidc_function {
+	struct usb_function func;
+	struct fhidc_device_parameters param;
+};
+
+extern struct fhidc_function fhidc_function_driver;
+
+struct fhidc_report {
+	unsigned int		id;
+	int 			minor;
+	int			size;
+	int			desc_size;
+	const unsigned char* 	desc;
+
+	int	(*init)(void);
+	int	(*exit)(void);
+
+	int 	(*set_report)(const unsigned char* buf, int len);
+
+	int 	(*get_report)(unsigned char* buf, int len);
+
+	int 	(*read)(struct file *file, char __user *buffer,
+				size_t count, loff_t *offset);
+
+	int 	(*write)(struct file *file, const char __user *buffer,
+				size_t count, loff_t *offset);
+
+	unsigned int 	(*poll)(struct file *file, poll_table *wait);
+};
+
+extern struct fhidc_report fhidc_panel_report;
+extern struct fhidc_report fhidc_keyboard_report;
+
+extern struct fhidc_report fhidc_nmea_report;
+extern struct fhidc_report fhidc_lightsensor_report;
+extern struct fhidc_report fhidc_positionsensor_report;
+extern struct fhidc_report fhidc_routeinformation_report;
+
+/* the definitions below should be included in include/linux/usb/hid.h */
+
+#ifndef __LINUX_USB_HID_EXTENSION_H
+#define __LINUX_USB_HID_EXTENSION_H
+
+#include <linux/types.h>
+
+#define USB_REQ_GET_REPORT		0x01
+#define USB_REQ_GET_IDLE		0x02
+#define USB_REQ_GET_PROTOCOL		0x03
+#define USB_REQ_SET_REPORT		0x09
+#define USB_REQ_SET_IDLE		0x0A
+#define USB_REQ_SET_PROTOCOL		0x0B
+
+#define USB_DT_HID			0x21
+#define USB_DT_REPORT			0x22
+#define USB_DT_PHYSICAL			0x23
+
+struct hid_descriptor {
+	__u8 bLength;
+	__u8 bHIDDescriptorType;
+	__u16 bcdHID;
+	__u8 bCountryCode;
+	__u8 bNumDescriptors;
+	__u8 bReportDescriptorType;
+	__u16 wDescriptorLength;
+} __attribute__ ((packed));
+
+#endif //__LINUX_USB_HID_EXTENSION_H
+
+#endif //__F_HID_H__
diff --git a/drivers/usb/gadget/f_hidc_keyboard.c b/drivers/usb/gadget/f_hidc_keyboard.c
new file mode 100644
index 0000000..3b2ae8e
--- /dev/null
+++ b/drivers/usb/gadget/f_hidc_keyboard.c
@@ -0,0 +1,187 @@
+/* drivers/usb/gadget/f_hidc_keyboard.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#include "f_hidc.h"
+
+#ifdef CONFIG_USB_AVH_KEYBOARD
+
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/usb/composite.h>
+
+#define FHIDC_KEYBOARD_REPORT_SIZE	3
+
+#define UNASSIGNED -1
+
+/* The report is interpreted as a 12 bit key code with
+ * a 4 bit modifier.
+ *
+ * If key code = 0 then the 4 bit modifier is used as the index
+ * in fhidc_keyboard_keycode. Otherwise, the modifier is used as the
+ * state of the key.
+ *
+ * Note that for the events listed in fhidc_keyboard_keycode, per
+ * received report, two input framework events are generated (down/up).
+ * For the other key events (key code != 0), only one event is
+ * generated according to the modifier state.
+ * This allows transmission of modified keys. To transmit ALT-G:
+ *  - LEFT-ALT down
+ *  - LEFT-SHIFT down
+ *  - G down
+ *  - G up
+ *  - LEFT-SHIFT up
+ *  - LEFT-ALT up
+ */
+
+#define	STANDARD_KEY_STATE_LENGTH	(4)
+#define STANDARD_KEY_MASK		(0xffff << STANDARD_KEY_STATE_LENGTH)
+#define	STANDARD_KEY_STATE_MASK		(~STANDARD_KEY_MASK)
+
+/* event specific definitions */
+static int fhidc_keyboard_keycode[] = {
+	KEY_MENU, KEY_C, KEY_X, KEY_UP,
+	KEY_RIGHT, KEY_VOLUMEUP, KEY_Z, KEY_LEFT,
+	KEY_DOWN, KEY_VOLUMEDOWN, KEY_ENTER, KEY_HOME,
+	KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, UNASSIGNED,
+};
+
+static struct input_dev *input_remote_keyboard;
+
+static int fhidc_keyboard_set_report(const unsigned char *buf, int len)
+{
+	unsigned short scancode;
+
+	if (!buf)
+		return -EINVAL;
+
+	if ((len < FHIDC_KEYBOARD_REPORT_SIZE) ||
+	    (*buf++ != fhidc_keyboard_report.id))
+		/* huh? malformed keyboard report */
+		return -EINVAL;
+
+	scancode = *buf++;
+	scancode |= (*buf) << 8;
+
+	if (scancode & STANDARD_KEY_MASK) {
+		int state = scancode & STANDARD_KEY_STATE_MASK;
+		int code = (scancode & STANDARD_KEY_MASK) >>
+				STANDARD_KEY_STATE_LENGTH;
+
+		if ((code > KEY_RESERVED) && (code < KEY_UNKNOWN)) {
+			input_report_key(input_remote_keyboard, code, state);
+			input_sync(input_remote_keyboard);
+		}
+	}
+	else {
+		if (scancode < ARRAY_SIZE(fhidc_keyboard_keycode)) {
+			unsigned int keycode = fhidc_keyboard_keycode[scancode];
+
+			input_report_key(input_remote_keyboard, keycode, 1);
+			input_report_key(input_remote_keyboard, keycode, 0);
+			input_sync(input_remote_keyboard);
+		}
+	}
+
+	return 0;
+}
+
+static unsigned char fhidc_keyboard_desc[] = {
+	/* KEYBOARD */
+	0x06, 0x00, 0xFF,	// USAGE_PAGE (Vendor defined)
+	0x09, 0x02,		// USAGE (Vendor Usage - Remote keyboard)
+	0xA1, 0x01,		// COLLECTION (Application)
+	0x85, 0x00,		// REPORT_ID (?)
+	0x75, 0x10,		// REPORT_SIZE (16)
+	0x95, 0x01,		// REPORT_COUNT (1)
+	0x15, 0x00,		// LOGICAL_MINIMUM (0)
+	0x27, 0xff, 0xff, 0x00, 0x00,	// LOGICAL_MAXIMUM (65535)
+	0x09, 0x01,		// USAGE (Vendor usage - scancode)
+	0x91, 0x02,		// OUTPUT (Data, Var, Abs)
+	0xC0,			//END_COLLECTION
+};
+
+#define FHIDC_KEYBOARD_DESC_ID_OFFSET	8
+
+static void fhidc_keyboard_set_report_id(int id)
+{
+	fhidc_keyboard_desc[FHIDC_KEYBOARD_DESC_ID_OFFSET] = id;
+}
+
+static int fhidc_keyboard_init(void)
+{
+	int i;
+	int error;
+	struct input_dev *input_keyboard;
+
+	input_keyboard = input_allocate_device();
+	if (!input_keyboard)
+		return -ENODEV;
+
+	input_remote_keyboard = input_keyboard;
+
+	/* init input device */
+	input_keyboard->name = "remote_keyboard";
+	input_keyboard->phys = "remote_keyboard/input0";
+	input_keyboard->id.bustype = BUS_USB;
+	input_keyboard->id.vendor = fhidc_function_driver.param.vendor;
+	input_keyboard->id.product = fhidc_function_driver.param.product;
+	input_keyboard->id.version = fhidc_function_driver.param.version;
+
+	/* enable event bits */
+	input_keyboard->evbit[0] = BIT_MASK(EV_KEY);
+
+	/* enable all keys except KEY_RESERVED and KEY_UNKNOWN */
+	for (i = 1; i < KEY_UNKNOWN; i++) {
+		set_bit(i, input_keyboard->keybit);
+	}
+
+	error = input_register_device(input_remote_keyboard);
+	if (error)
+		goto fail;
+
+	fhidc_keyboard_set_report_id(fhidc_keyboard_report.id);
+
+	return 0;
+
+fail:
+	if (input_keyboard) {
+		input_free_device(input_keyboard);
+		input_keyboard = 0;
+	}
+
+	input_remote_keyboard = 0;
+
+	return error;
+}
+
+static int fhidc_keyboard_exit(void)
+{
+	fhidc_keyboard_set_report_id(0);
+
+	if (input_remote_keyboard) {
+		input_unregister_device(input_remote_keyboard);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*                         external interface                                */
+/* ------------------------------------------------------------------------- */
+
+struct fhidc_report fhidc_keyboard_report = {
+	.size = FHIDC_KEYBOARD_REPORT_SIZE,
+	.init = fhidc_keyboard_init,
+	.exit = fhidc_keyboard_exit,
+	.set_report = fhidc_keyboard_set_report,
+	.desc = fhidc_keyboard_desc,
+	.desc_size = sizeof(fhidc_keyboard_desc),
+};
+#else
+struct fhidc_report fhidc_keyboard_report = { 0 };
+#endif
diff --git a/drivers/usb/gadget/f_hidc_panel.c b/drivers/usb/gadget/f_hidc_panel.c
new file mode 100644
index 0000000..f35b15d
--- /dev/null
+++ b/drivers/usb/gadget/f_hidc_panel.c
@@ -0,0 +1,156 @@
+/* drivers/usb/gadget/f_hidc_panel.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+
+#include "f_hidc.h"
+
+#ifdef CONFIG_USB_AVH_PANEL
+
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/usb/composite.h>
+
+#define FHIDC_PANEL_REPORT_SIZE	7
+
+static struct input_dev *input_remote_panel;
+
+static int fhidc_panel_set_report(const unsigned char *buf, int len)
+{
+	unsigned long int x;
+	unsigned long int y;
+	unsigned long int pressure;
+
+	if (!buf)
+		return -EINVAL;
+
+	if ((len < FHIDC_PANEL_REPORT_SIZE) || (*buf++ != fhidc_panel_report.id))
+		/* huh? malformed panel report */
+		return -EINVAL;
+
+	x = *buf++;
+	x |= (*buf++) << 8;
+
+	y = *buf++;
+	y |= (*buf++) << 8;
+
+	pressure = *buf++;
+	pressure |= (*buf++) << 8;
+
+	input_report_abs(input_remote_panel, ABS_X, x);
+	input_report_abs(input_remote_panel, ABS_Y, y);
+	input_report_abs(input_remote_panel, ABS_PRESSURE, pressure);
+	input_sync(input_remote_panel);
+
+	return 0;
+}
+
+static unsigned char fhidc_panel_desc[] = {
+	/* TOUCH_SCREEN */
+	0x06, 0x00, 0xff,	// USAGE_PAGE (Vendor defined)
+	0x09, 0x01,		// USAGE (Vendor Usage - touch screen)
+	0xa1, 0x01,		// COLLECTION (Application) touch screen
+	0x85, 0x00,		//   REPORT_ID (?)
+	0x75, 0x10,		//   REPORT_SIZE (16) // size in bits
+	0x95, 0x03,		//   REPORT_COUNT (3) // X, Y, pressure
+	0x15, 0x00,		//   LOGICAL_MINIMUM (0)
+	0x27, 0xff, 0xff, 0x00, 0x00,	//   LOGICAL_MAXIMUM (65535)
+	0x09, 0x01,		//   USAGE (Vendor Usage - X)
+	0x09, 0x02,		//   USAGE (Vendor Usage - Y)
+	0x09, 0x03,		//   USAGE (Vendor Usage - Pressure)
+	0x91, 0x02,		//   OUTPUT (Data,Var,Abs)
+	0xc0,			// END_COLLECTION touch screen
+};
+
+#define FHIDC_PANEL_DESC_ID_OFFSET	8
+
+static void fhidc_panel_set_report_id(int id)
+{
+	fhidc_panel_desc[FHIDC_PANEL_DESC_ID_OFFSET] = id;
+}
+
+static int fhidc_panel_init(void)
+{
+	int status;
+	struct input_dev *input_panel;
+
+	input_panel = input_allocate_device();
+	if (!input_panel) {
+		status = -ENODEV;
+		goto fail;
+	}
+
+	input_remote_panel = input_panel;
+
+	/* init input device */
+	input_panel->name = "remote_ts";
+	input_panel->phys = "remote_ts/input0";
+	input_panel->id.bustype = BUS_USB;
+	input_panel->id.vendor = fhidc_function_driver.param.vendor;
+	input_panel->id.product = fhidc_function_driver.param.product;
+	input_panel->id.version = fhidc_function_driver.param.version;
+
+	/* enable event bits */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,24)
+	input_panel->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	input_panel->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+#else
+	input_panel->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+	input_panel->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+#endif
+	input_set_abs_params(input_panel, ABS_X, 0,
+			fhidc_function_driver.param.limits.x, 0, 0);
+	input_set_abs_params(input_panel, ABS_Y, 0,
+			fhidc_function_driver.param.limits.y, 0, 0);
+	input_set_abs_params(input_panel, ABS_PRESSURE, 0,
+			fhidc_function_driver.param.limits.p, 0, 0);
+
+	status = input_register_device(input_remote_panel);
+	if (status)
+		goto fail;
+
+	fhidc_panel_set_report_id(fhidc_panel_report.id);
+
+	return 0;
+
+      fail:
+	if (input_panel) {
+		input_free_device(input_panel);
+		input_panel = 0;
+	}
+
+	input_remote_panel = 0;
+
+	return status;
+}
+
+static int fhidc_panel_exit(void)
+{
+	fhidc_panel_set_report_id(0);
+
+	if (input_remote_panel) {
+		input_unregister_device(input_remote_panel);
+	}
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*                         external interface                                */
+/* ------------------------------------------------------------------------- */
+
+struct fhidc_report fhidc_panel_report = {
+	.size = FHIDC_PANEL_REPORT_SIZE,
+	.init = fhidc_panel_init,
+	.exit = fhidc_panel_exit,
+	.set_report = fhidc_panel_set_report,
+	.desc = fhidc_panel_desc,
+	.desc_size = sizeof(fhidc_panel_desc),
+};
+#else
+static struct fhidc_report fhidc_panel_report = { 0 };
+#endif
diff --git a/drivers/usb/gadget/f_vdc.c b/drivers/usb/gadget/f_vdc.c
new file mode 100644
index 0000000..faf021a
--- /dev/null
+++ b/drivers/usb/gadget/f_vdc.c
@@ -0,0 +1,1863 @@
+/* drivers/usb/gadget/f_vdc.c
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+
+#include <linux/usb/composite.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+
+/* includes for sysfs */
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+
+/* Define this to enable the RLE variant of RGBP. This must use frame based descriptors
+   which are *not* supported in Windows XP, and will cause the connected video device
+   to fail to start (code 10). */
+#define AVH_USE_RGBP_RLE
+
+#include "f_vdc.h"
+
+#define FVDC_NAME 			"fvdc"
+#define FVDC_KWORK_NAME			"kvdc"
+
+#define FVDC_URB_SIZE			(PAGE_SIZE)
+#define FVDC_PAYLOAD_HEADER_SIZE	(8)
+#define FVDC_PAYLOAD_FOOTER		(4)
+#define FVDC_MAX_PAYLOAD_SIZE		(FVDC_URB_SIZE - FVDC_PAYLOAD_HEADER_SIZE - FVDC_PAYLOAD_FOOTER)
+
+#ifdef AVH_USE_RGBP_RLE
+#define FVDC_NR_FORMATS			(3)
+#else
+#define FVDC_NR_FORMATS			(2)
+#endif
+#define FVDC_DEFAULT_FB_INDEX		(-1)
+#define FVDC_DEFAULT_WIDTH		(480)
+#define FVDC_DEFAULT_HEIGHT		(272)
+
+static struct usb_interface_assoc_descriptor vc_interface_association_desc = {
+	.bLength = sizeof vc_interface_association_desc,
+	.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+	.bFirstInterface = 0,
+	.bInterfaceCount = 2,
+	.bFunctionClass = CC_VIDEO,
+	.bFunctionSubClass = SC_VIDEO_INTERFACE_COLLECTION,
+	.bFunctionProtocol = PC_PROTOCOL_UNDEFINED,
+};
+
+/*
+ * Video Control Interface Section
+ */
+
+static struct usb_interface_descriptor video_control_interface_desc = {
+	.bLength = sizeof video_control_interface_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0,
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 0,
+	.bInterfaceClass = CC_VIDEO,
+	.bInterfaceSubClass = SC_VIDEOCONTROL,
+	.bInterfaceProtocol = PC_PROTOCOL_UNDEFINED,
+};
+
+/* We only have an Intput Terminal and an Output Terminal. No selector/processing/extension units, etc. */
+static struct usb_input_terminal_descriptor camera_terminal_desc = {
+	.bLength = sizeof camera_terminal_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VC_INPUT_TERMINAL,
+	.bTerminalID = 1,
+	.wTerminalType = __constant_cpu_to_le16(ITT_CAMERA),
+	.bAssocTerminal = 3,
+	.iTerminal = 0,
+	.wObjectiveFocalLengthMin = 0,
+	.wObjectiveFocalLengthMax = 0,
+	.wOcularFocalLength = 0,
+	.bControlSize = 2,
+	.bmControls0 = 0,
+	.bmControls1 = 0,
+};
+
+static struct usb_vc_output_terminal_descriptor output_terminal_desc = {
+	.bLength = sizeof output_terminal_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VC_OUTPUT_TERMINAL,
+	.bTerminalID = 3,
+	.wTerminalType = __constant_cpu_to_le16(TT_STREAMING),
+	.bAssocTerminal = 1,
+	.bSourceID = 1,
+	.iTerminal = 0,
+};
+
+static struct usb_cs_vc_interface_descriptor vc_video_control_interface_desc = {
+	.bLength = sizeof vc_video_control_interface_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VC_HEADER,
+	.bcdUVC = __constant_cpu_to_le16(0x0110),
+	.wTotalLength =
+	    __constant_cpu_to_le16(sizeof(vc_video_control_interface_desc) +
+				   sizeof(camera_terminal_desc) +
+				   sizeof(output_terminal_desc)),
+	.dwClockFrequency = __constant_cpu_to_le32(0x0000BB80),
+	.bInCollection = 1,
+	.baInterfaceNr = 1,
+};
+
+/*
+ * Video Stream Interface Section
+ */
+
+static struct usb_interface_descriptor video_stream_interface_desc = {
+	.bLength = sizeof video_stream_interface_desc,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 1,
+	.bAlternateSetting = 0,
+	.bNumEndpoints = 1,
+	.bInterfaceClass = CC_VIDEO,
+	.bInterfaceSubClass = SC_VIDEOSTREAMING,
+	.bInterfaceProtocol = PC_PROTOCOL_UNDEFINED,
+};
+
+/* the intervals below are in units of 100ns */
+#define VC_FRAME_INTERVAL_DEFAULT		2000000	/* 5 fps */
+#define VC_FRAME_INTERVAL_MIN			500000	/* 20 fps */
+#define VC_FRAME_INTERVAL_MAX			1000000000	/* 0.01 fps */
+#define VC_FRAME_INTERVAL_STEP			(VC_FRAME_INTERVAL_MIN)
+
+#define VC_YUYV_FORMAT_INDEX			1
+#define VC_RGBP_FORMAT_INDEX			2
+#define VC_RGBP_RLE_FORMAT_INDEX		3
+
+#define VC_YUYV_FORMAT_BPP			16
+#define VC_RGBP_RLE_FORMAT_BPP			16
+#define VC_RGBP_FORMAT_BPP			16
+#define VC_MAX_FORMAT_BPP			16
+#define VC_DEFAULT_BPP				16
+#define VC_DELAY_DEFAULT			100
+#define VC_CLOCK_FREQUENCY_DEFAULT		48000
+
+#define VC_EOF_MASK				2
+
+/* YUYV Format and Frame Descriptors */
+
+static struct usb_uncompressed_format_descriptor yuyv_format_desc = {
+	.bLength = sizeof yuyv_format_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_FORMAT_UNCOMPRESSED,
+	.bFormatIndex = VC_YUYV_FORMAT_INDEX,
+	.bNumFrameDescriptors = 1,
+	.guidFormat = {'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
+		       0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}
+	,
+	.bBitsPerPixel = VC_YUYV_FORMAT_BPP,
+	.bDefaultFrameIndex = 1,
+	.bAspectRatioX = 0,
+	.bAspectRatioY = 0,
+	.bmInterlaceflags = 0,
+	.bCopyProtect = 0,
+};
+
+struct usb_uncompressed_frame_descriptor yuyv_frame_desc = {
+	.bLength = sizeof yuyv_frame_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VS_FRAME_UNCOMPRESSED,
+	.bFrameIndex = 1,
+	.bmCapabilities = 0,
+	.wWidth = __constant_cpu_to_le16(FVDC_DEFAULT_WIDTH),
+	.wHeight = __constant_cpu_to_le16(FVDC_DEFAULT_HEIGHT),
+	.dwMinBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT * VC_YUYV_FORMAT_BPP /
+				   100),
+	.dwMaxBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT * VC_YUYV_FORMAT_BPP *
+				   20),
+	.dwMaxVideoFrameBufferSize = __constant_cpu_to_le32(0),
+	.dwDefaultFrameInterval =
+	    __constant_cpu_to_le32(VC_FRAME_INTERVAL_DEFAULT),
+	.bFrameIntervalType = 0,
+	.dwMinFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwMaxFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MAX),
+	.dwFrameIntervalStep = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+};
+
+static struct usb_color_matching_descriptor yuyv_color_matching_desc = {
+	.bLength = sizeof yuyv_color_matching_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_COLORFORMAT,
+	.bColorPrimaries = 1,
+	.bTransferCharacteristics = 1,
+	.bMatrixCoefficients = 4,
+};
+
+/* RGBP Format and Frame Descriptors */
+
+#ifdef AVH_USE_RGBP_RLE
+static struct usb_frame_based_format_descriptor rgbp_format_desc = {
+	.bLength = sizeof rgbp_format_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_FORMAT_FRAME_BASED,
+	.bFormatIndex = VC_RGBP_FORMAT_INDEX,
+	.bNumFrameDescriptors = 1,
+	.guidFormat =
+	    {'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA,
+	     0x00, 0x38, 0x9B, 0x71}
+	,
+	.bBitsPerPixel = VC_RGBP_FORMAT_BPP,
+	.bDefaultFrameIndex = 1,
+	.bAspectRatioX = 0,
+	.bAspectRatioY = 0,
+	.bmInterlaceflags = 0,
+	.bCopyProtect = 1,
+	.bVariableSize = 0,
+};
+
+struct usb_frame_based_frame_descriptor rgbp_frame_desc = {
+	.bLength = sizeof rgbp_frame_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VS_FRAME_FRAME_BASED,
+	.bFrameIndex = 1,
+	.bmCapabilities = 0,
+	.wWidth = __constant_cpu_to_le16(FVDC_DEFAULT_WIDTH),
+	.wHeight = __constant_cpu_to_le16(FVDC_DEFAULT_HEIGHT),
+	.dwMinBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT *
+				   VC_RGBP_RLE_FORMAT_BPP / 100),
+	.dwMaxBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT *
+				   VC_RGBP_RLE_FORMAT_BPP * 20),
+	.dwDefaultFrameInterval =
+	    __constant_cpu_to_le32(VC_FRAME_INTERVAL_DEFAULT),
+	.bFrameIntervalType = 0,
+	.dwMinFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwMaxFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MAX),
+	.dwFrameIntervalStep = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwBytesPerLine =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   VC_RGBP_RLE_FORMAT_BPP / 8),
+};
+
+#else
+static struct usb_uncompressed_format_descriptor rgbp_format_desc = {
+	.bLength = sizeof rgbp_format_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_FORMAT_UNCOMPRESSED,
+	.bFormatIndex = VC_RGBP_FORMAT_INDEX,
+	.bNumFrameDescriptors = 1,
+	.guidFormat = {'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00,
+		       0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71}
+	,
+	.bBitsPerPixel = VC_RGBP_FORMAT_BPP,
+	.bDefaultFrameIndex = 1,
+	.bAspectRatioX = 0,
+	.bAspectRatioY = 0,
+	.bmInterlaceflags = 0,
+	.bCopyProtect = 0,
+};
+
+struct usb_uncompressed_frame_descriptor rgbp_frame_desc = {
+	.bLength = sizeof rgbp_frame_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VS_FRAME_UNCOMPRESSED,
+	.bFrameIndex = 1,
+	.bmCapabilities = 0,
+	.wWidth = __constant_cpu_to_le16(FVDC_DEFAULT_WIDTH),
+	.wHeight = __constant_cpu_to_le16(FVDC_DEFAULT_HEIGHT),
+	.dwMinBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT * VC_RGBP_FORMAT_BPP /
+				   100),
+	.dwMaxBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT * VC_RGBP_FORMAT_BPP *
+				   20),
+	.dwMaxVideoFrameBufferSize = __constant_cpu_to_le32(0),
+	.dwDefaultFrameInterval =
+	    __constant_cpu_to_le32(VC_FRAME_INTERVAL_DEFAULT),
+	.bFrameIntervalType = 0,
+	.dwMinFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwMaxFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MAX),
+	.dwFrameIntervalStep = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+};
+#endif
+
+static struct usb_color_matching_descriptor rgbp_color_matching_desc = {
+	.bLength = sizeof rgbp_color_matching_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_COLORFORMAT,
+	.bColorPrimaries = 1,
+	.bTransferCharacteristics = 1,
+	.bMatrixCoefficients = 4,
+};
+
+#ifdef AVH_USE_RGBP_RLE
+/* RGBp (-RLE) Format and Frame Descriptors */
+
+static struct usb_frame_based_format_descriptor rgbp_rle_format_desc = {
+	.bLength = sizeof rgbp_rle_format_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_FORMAT_FRAME_BASED,
+	.bFormatIndex = VC_RGBP_RLE_FORMAT_INDEX,
+	.bNumFrameDescriptors = 1,
+	.guidFormat =
+	    {'R', 'G', 'B', 'p', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA,
+	     0x00, 0x38, 0x9B, 0x71}
+	,
+	.bBitsPerPixel = VC_RGBP_RLE_FORMAT_BPP,
+	.bDefaultFrameIndex = 1,
+	.bAspectRatioX = 0,
+	.bAspectRatioY = 0,
+	.bmInterlaceflags = 0,
+	.bCopyProtect = 1,
+	.bVariableSize = 1,
+};
+
+struct usb_frame_based_frame_descriptor rgbp_rle_frame_desc = {
+	.bLength = sizeof rgbp_rle_frame_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VS_FRAME_FRAME_BASED,
+	.bFrameIndex = 1,
+	.bmCapabilities = 0,
+	.wWidth = __constant_cpu_to_le16(FVDC_DEFAULT_WIDTH),
+	.wHeight = __constant_cpu_to_le16(FVDC_DEFAULT_HEIGHT),
+	.dwMinBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT *
+				   VC_RGBP_RLE_FORMAT_BPP / 100),
+	.dwMaxBitRate =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT *
+				   VC_RGBP_RLE_FORMAT_BPP * 20),
+	.dwDefaultFrameInterval =
+	    __constant_cpu_to_le32(VC_FRAME_INTERVAL_DEFAULT),
+	.bFrameIntervalType = 0,
+	.dwMinFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwMaxFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MAX),
+	.dwFrameIntervalStep = __constant_cpu_to_le32(VC_FRAME_INTERVAL_MIN),
+	.dwBytesPerLine = __constant_cpu_to_le32(0),
+};
+
+static struct usb_color_matching_descriptor rgbp_rle_color_matching_desc = {
+	.bLength = sizeof rgbp_rle_color_matching_desc,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubtype = VS_COLORFORMAT,
+	.bColorPrimaries = 1,
+	.bTransferCharacteristics = 1,
+	.bMatrixCoefficients = 4,
+};
+
+#endif
+
+static struct usb_cs_vs_interface_input_header_descriptor
+    vc_video_stream_interface_desc = {
+	.bLength = 13 + FVDC_NR_FORMATS,
+	.bDescriptorType = USB_DT_CS_INTERFACE,
+	.bDescriptorSubType = VC_HEADER,
+	.bNumFormats = FVDC_NR_FORMATS,
+	.wTotalLength = __constant_cpu_to_le16(13 + FVDC_NR_FORMATS +
+					       sizeof(yuyv_format_desc) +
+					       sizeof(yuyv_frame_desc) +
+					       sizeof(yuyv_color_matching_desc)
+					       + sizeof(rgbp_format_desc) +
+					       sizeof(rgbp_frame_desc) +
+					       sizeof(rgbp_color_matching_desc)
+#ifdef AVH_USE_RGBP_RLE
+					       +
+					       sizeof(rgbp_rle_format_desc) +
+					       sizeof(rgbp_rle_frame_desc) +
+					       sizeof
+					       (rgbp_rle_color_matching_desc)
+#endif
+	    ),
+	.bEndpointAddress = USB_DIR_IN,
+	.bmInfo = 0,
+	.bTerminalLink = 3,
+	.bStillCaptureMethod = 0,
+	.bTriggerSupport = 0,
+	.bTriggerUsage = 0,
+	.bControlSize = 1,
+	.bmaControls0 = 0,
+	.bmaControls1 = 0,
+	.bmaControls2 = 0,
+	.bmaControls3 = 0,
+	.bmaControls4 = 0,
+	.bmaControls5 = 0,
+	.bmaControls6 = 0,
+	.bmaControls7 = 0,
+};
+
+/*
+ * Video Endpoint Section
+ */
+
+static struct usb_endpoint_descriptor
+ fs_bulk_in_ep_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(64),
+	.bInterval = 0,
+};
+
+static struct usb_endpoint_descriptor
+ hs_bulk_in_ep_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = __constant_cpu_to_le16(512),
+	.bInterval = 0,
+};
+
+static struct usb_video_probe_commit_controls fvdc_default_controls = {
+	.bmHint = __constant_cpu_to_le16(0),
+	.bFormatIndex = 1,
+	.bFrameIndex = 1,
+	.dwFrameInterval = __constant_cpu_to_le32(VC_FRAME_INTERVAL_DEFAULT),
+	.wDelay = __constant_cpu_to_le16(VC_DELAY_DEFAULT),
+	.dwMaxVideoFrameSize =
+	    __constant_cpu_to_le32(FVDC_DEFAULT_WIDTH *
+				   FVDC_DEFAULT_HEIGHT * VC_DEFAULT_BPP / 8),
+	.dwMaxPayloadTransferSize = __constant_cpu_to_le32(FVDC_URB_SIZE),
+	.dwClockFrequency = __constant_cpu_to_le32(VC_CLOCK_FREQUENCY_DEFAULT),
+	.bmFramingInfo = 3,
+	.bPreferedVersion = 1,
+	.bMinVersion = 1,
+	.bMaxVersion = 1,
+};
+
+static struct usb_descriptor_header *fvdc_fs_function[] = {
+	(struct usb_descriptor_header *)&vc_interface_association_desc,
+	(struct usb_descriptor_header *)&video_control_interface_desc,
+	(struct usb_descriptor_header *)&vc_video_control_interface_desc,
+	(struct usb_descriptor_header *)&camera_terminal_desc,
+	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&video_stream_interface_desc,
+	(struct usb_descriptor_header *)&vc_video_stream_interface_desc,
+	(struct usb_descriptor_header *)&yuyv_format_desc,
+	(struct usb_descriptor_header *)&yuyv_frame_desc,
+	(struct usb_descriptor_header *)&yuyv_color_matching_desc,
+	(struct usb_descriptor_header *)&rgbp_format_desc,
+	(struct usb_descriptor_header *)&rgbp_frame_desc,
+	(struct usb_descriptor_header *)&rgbp_color_matching_desc,
+#ifdef AVH_USE_RGBP_RLE
+	(struct usb_descriptor_header *)&rgbp_rle_format_desc,
+	(struct usb_descriptor_header *)&rgbp_rle_frame_desc,
+	(struct usb_descriptor_header *)&rgbp_rle_color_matching_desc,
+#endif
+	(struct usb_descriptor_header *)&fs_bulk_in_ep_desc,
+	0
+};
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+static struct usb_descriptor_header *fvdc_hs_function[] = {
+	(struct usb_descriptor_header *)&vc_interface_association_desc,
+	(struct usb_descriptor_header *)&video_control_interface_desc,
+	(struct usb_descriptor_header *)&vc_video_control_interface_desc,
+	(struct usb_descriptor_header *)&camera_terminal_desc,
+	(struct usb_descriptor_header *)&output_terminal_desc,
+	(struct usb_descriptor_header *)&video_stream_interface_desc,
+	(struct usb_descriptor_header *)&vc_video_stream_interface_desc,
+	(struct usb_descriptor_header *)&yuyv_format_desc,
+	(struct usb_descriptor_header *)&yuyv_frame_desc,
+	(struct usb_descriptor_header *)&yuyv_color_matching_desc,
+	(struct usb_descriptor_header *)&rgbp_format_desc,
+	(struct usb_descriptor_header *)&rgbp_frame_desc,
+	(struct usb_descriptor_header *)&rgbp_color_matching_desc,
+#ifdef AVH_USE_RGBP_RLE
+	(struct usb_descriptor_header *)&rgbp_rle_format_desc,
+	(struct usb_descriptor_header *)&rgbp_rle_frame_desc,
+	(struct usb_descriptor_header *)&rgbp_rle_color_matching_desc,
+#endif
+	(struct usb_descriptor_header *)&hs_bulk_in_ep_desc,
+	0
+};
+#endif /* CONFIG_USB_GADGET_DUALSPEED */
+
+#define STRING_VIDEO_IDX		0
+#define STRING_VIDEO_CONTROL_IDX	1
+#define STRING_VIDEO_STREAM_IDX		2
+
+static struct usb_string fvdc_strings_dev[] = {
+	[STRING_VIDEO_IDX].s = "USB Video (Bulk)",
+	[STRING_VIDEO_CONTROL_IDX].s = "USB Video Control",
+	[STRING_VIDEO_STREAM_IDX].s = "USB Video Stream",
+	{}
+};
+
+static struct usb_gadget_strings fvdc_stringtab_dev = {
+	.language = 0x0409,
+	.strings = fvdc_strings_dev,
+};
+
+static struct usb_gadget_strings *fvdc_strings[] = {
+	&fvdc_stringtab_dev,
+	0,
+};
+
+#define VC_PROBECOMMIT_REQUEST_LENGTH	34
+#define VC_PROBECOMMIT_REQUEST_LENGTH_MIN 26
+
+
+static int fvdc_free_urbs(void);
+static void fvdc_urb_complete(struct usb_ep *ep, struct usb_request *req);
+static void fvdc_urb_final_complete(struct usb_ep *ep,
+				      struct usb_request *req);
+
+static int fvdc_stream_frame(void *dma, void *cpu, int size);
+static void fvdc_halt(void);
+
+static struct usb_video_probe_commit_controls fvdc_commit_controls;
+static struct usb_video_probe_commit_controls fvdc_probe_controls;
+
+enum fvdc_dev_state {
+	FVDC_STATE_VOID,
+	FVDC_STATE_IDLE,
+	FVDC_STATE_CONFIGURED,
+	FVDC_STATE_SELECTED,
+	FVDC_STATE_STREAMABLE,
+	FVDC_STATE_STREAMING
+};
+
+struct fvdc_frame {
+	void *dma;
+	void *cpu;
+	unsigned long size;
+};
+
+static struct fvdc_dev {
+	int alt;
+	struct fb_info *attached_fb;
+	int (*pan_display) (struct fb_var_screeninfo * var,
+			    struct fb_info * info);
+
+	int width;
+	int height;
+
+	struct usb_ep *bulk_in;
+	struct usb_endpoint_descriptor *bulk_in_ep_desc;
+
+	struct usb_request 		*probecommit_req;
+
+	struct usb_request **payload_urb;
+	int payload_urbs;
+
+	enum fvdc_dev_state state;
+
+	struct fvdc_frame pending;
+
+	struct work_struct pending_frame_work;
+	struct work_struct stream_frame_work;
+
+	struct workqueue_struct *work_queue;
+} *fvdc_dev = 0;
+
+static inline void fvdc_set_dev_state(struct fvdc_dev *dev,
+					enum fvdc_dev_state state)
+{
+	if (!dev)
+		return;
+
+	if (state != dev->state) {
+		dev->state = state;
+	}
+}
+
+
+/* ------------------------------------------------------------------------- */
+
+static unsigned char ycomp(unsigned char r, unsigned char g, unsigned char b)
+{
+	unsigned long result;
+
+	result = (((2171 * (r)) + (2122 * g) + (822 * b) + 16896) >> 10);
+
+	return (unsigned char)result;
+}
+
+static unsigned char vcomp(unsigned char r, unsigned char g, unsigned char b)
+{
+	unsigned long result;
+
+	result = (((3701 * (r)) - (1521 * g) - (592 * b) + 131584) >> 10);
+
+	return (unsigned char)result;
+}
+
+static unsigned char ucomp(unsigned char r, unsigned char g, unsigned char b)
+{
+	unsigned long result;
+
+	result = (((-1250 * (r)) - (1198 * g) + (3685 * b) + 131584) >> 10);
+
+	return (unsigned char)result;
+}
+
+static int fvdc_encode_payload_yuy2(unsigned short *src, int nr_of_pixels)
+{
+	int pixels_done = 0;
+	unsigned short int *dest = src;
+	unsigned char red, green, blue;
+	unsigned long int y0, y1, u0, u1, v0, v1, u, v;
+
+	while (pixels_done < nr_of_pixels) {
+		pixels_done += 2;
+
+		blue = (*src) & 0x1f;
+		*src >>= 5;
+
+		green = (*src) & 0x3f;
+		*src >>= 6;
+
+		red = *src;
+
+		src++;
+
+		y0 = ycomp(red, green, blue);
+		u0 = ucomp(red, green, blue);
+		v0 = vcomp(red, green, blue);
+
+		blue = (*src) & 0x1f;
+		*src >>= 5;
+
+		green = (*src) & 0x3f;
+		*src >>= 6;
+
+		red = *src;
+
+		src++;
+
+		y1 = ycomp(red, green, blue);
+		u1 = ucomp(red, green, blue);
+		v1 = vcomp(red, green, blue);
+
+		u = (u0 + u1) >> 1;
+		v = (v0 + v1) >> 1;
+
+		*dest++ = (y0) | (u << 8);
+		*dest++ = (y1) | (v << 8);
+	}
+	return nr_of_pixels * 2;
+}
+
+static int fvdc_encode_payload_rgbp(unsigned short *src, int nr_of_pixels)
+{
+	unsigned short int *dest = src;
+	unsigned short int *orig = src;
+	unsigned short int compare = *src++;
+	unsigned long int runlength = 1;
+
+	while (nr_of_pixels > 0) {
+		if ((runlength < nr_of_pixels) && (compare == *src)) {
+			runlength++;
+			src++;
+			if (runlength == 0xffff) {
+				goto force_run;
+			}
+		} else {
+		      force_run:
+			// runlength >= nr_of_pixels || compare != *frame
+			nr_of_pixels -= runlength;
+
+			if (runlength > 2) {
+				*dest++ = compare & 0xffdf;	// lsb bit green := 0 => runlength encoded color
+				*dest++ = runlength;
+			} else {
+				*dest++ = compare | 0x0020;	// lsb bit green := 1 => normal color
+				if (runlength == 2) {
+					*dest++ = compare | 0x0020;	// lsb bit green := 1 => normal color
+				}
+			}
+
+			compare = *src++;
+			runlength = 1;
+		}
+	}
+
+	return (dest - orig) * sizeof(*dest);
+}
+
+static int fvdc_encode_payload(unsigned char *data, int nr_of_bytes)
+{
+	switch (fvdc_commit_controls.bFormatIndex) {
+	case VC_YUYV_FORMAT_INDEX:
+		return fvdc_encode_payload_yuy2((unsigned short *)data,
+						  nr_of_bytes / 2);
+
+	case VC_RGBP_FORMAT_INDEX:
+		/* data should already be RGB565 */
+		return nr_of_bytes;
+
+	case VC_RGBP_RLE_FORMAT_INDEX:
+		return fvdc_encode_payload_rgbp((unsigned short *)data,
+						  nr_of_bytes / 2);
+
+	default:
+		return -EPROTOTYPE;
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* not to be called from atomic context, this function may block */
+static int fvdc_stream_frame(void *dma, void *cpu, int size)
+{
+	int ret;
+
+	if (size == 0)
+		return 0;
+
+	if (!fvdc_dev)
+		return -ENODEV;
+
+	switch (fvdc_dev->state) {
+	case FVDC_STATE_STREAMING:
+		/* a request for streaming a frame while busy with sending a previous frame
+		   this particular request will be remembered as a pending request, but it
+		   will 'overwrite' any already pending request */
+
+		fvdc_dev->pending.dma = dma;
+		fvdc_dev->pending.cpu = cpu;
+		fvdc_dev->pending.size = (unsigned long)size;
+
+		ret = 0;
+		break;
+
+	case FVDC_STATE_STREAMABLE:
+		{
+			/* we're currently not streaming; kick the work thread after copying all the
+			   data to the video urb buffers */
+
+			int index = 0;
+			int offset = 0;
+
+			/* switching the state to STREAMING prevents further frames to be accepted for
+			   streaming (subsequent frames will be make pending; but only the last of the
+			   pending frames actually will be streamed */
+
+			fvdc_set_dev_state(fvdc_dev,
+					     FVDC_STATE_STREAMING);
+
+			while ((size > 0) && (fvdc_dev)
+			       && (fvdc_dev->state ==
+				   FVDC_STATE_STREAMING)) {
+				struct usb_request *req =
+				    fvdc_dev->payload_urb[index];
+				int length =
+				    (size <
+				     FVDC_MAX_PAYLOAD_SIZE) ? (size)
+				    : (FVDC_MAX_PAYLOAD_SIZE);
+
+				size -= length;
+
+				memcpy(req->buf + FVDC_PAYLOAD_HEADER_SIZE,
+					cpu + offset,
+					length);
+
+				offset += length;
+
+				req->length =
+				    length + FVDC_PAYLOAD_HEADER_SIZE;
+
+				index++;
+
+				if (likely(size > 0)) {
+					req->complete = fvdc_urb_complete;
+				} else {
+					req->complete = fvdc_urb_final_complete;
+				}
+			}
+
+			/* the data to be streamed is now safe in the payload
+			   buffers, the encoding and preparing the buffers for
+			   transmission is done by the video kernel thread */
+
+			ret = queue_work(fvdc_dev->work_queue,
+				       &fvdc_dev->stream_frame_work);
+			break;
+		}
+
+	default:
+		ret = -EHOSTDOWN;
+		break;
+	}
+
+	return ret;
+}
+
+/* -------------------------------------------------------------------------
+   worker functions
+   ------------------------------------------------------------------------- */
+
+static void fvdc_pending_frame_work(struct work_struct *data)
+{
+	unsigned long int flags;
+	local_irq_save(flags);
+	if (fvdc_dev->pending.size > 0) {
+		void *dma = fvdc_dev->pending.dma;
+		void *cpu = fvdc_dev->pending.cpu;
+		int size = fvdc_dev->pending.size;
+
+		fvdc_dev->pending.size = 0;
+
+		local_irq_restore(flags);
+		fvdc_stream_frame(dma, cpu, size);
+		return;
+	}
+
+	local_irq_restore(flags);
+	return;
+}
+
+static void fvdc_stream_frame_work(struct work_struct *data)
+{
+#ifdef INFO
+	static unsigned long int fc = 0;
+#endif
+	static int fid = 0;
+	int index = 0;
+	int eof = 0;
+	int ret = 0;
+	unsigned long int flags;
+
+	if (fvdc_dev->state < FVDC_STATE_STREAMING) {
+		goto early_exit;
+	}
+
+	do {
+		struct usb_request *req = fvdc_dev->payload_urb[index++];
+		unsigned char *buf = req->buf;
+
+		/* encoding payload data may change the size of the payload */
+		req->length =
+		    fvdc_encode_payload(buf + FVDC_PAYLOAD_HEADER_SIZE,
+					  req->length -
+					  FVDC_PAYLOAD_HEADER_SIZE) +
+		    FVDC_PAYLOAD_HEADER_SIZE;
+
+		req->status = 0;
+		req->zero = 1;
+
+		/* if the completion function of the urb is fvdc_urb_final_complete, this
+		   is the last payload of the frame sequence; and the end-of-frame bit
+		   can be set; this also terminates this encoding/queuing loop. */
+		if (req->complete == fvdc_urb_final_complete)
+			eof = VC_EOF_MASK;
+
+		memset(buf, 0, FVDC_PAYLOAD_HEADER_SIZE);
+
+		buf[0] = 8;
+		buf[1] = 0x80 | 0x08 | fid | eof;
+
+		/* check whether the state of the device is still valid */
+		local_irq_save(flags);
+
+		if ((!fvdc_dev)
+		    || (fvdc_dev->state != FVDC_STATE_STREAMING)) {
+			goto abort_locked;
+		}
+
+		if (req->length < 0) {
+			goto abort_locked;
+		}
+
+		/* In case the underlying USB device controller driver
+		 * is using DMA to transfer data from the main memory buffer
+		 * pointed to by 'req->buf/req->dma', it must make sure the
+		 * data is flushed out to main memory before enabling this
+		 * dma transfer. The usual method to ensure this is by
+		 * calling dma_map_single() on the passed virtual address.
+		 */
+
+		if ((ret = usb_ep_queue(fvdc_dev->bulk_in, req, GFP_ATOMIC))) {
+			/* error queuing urb */
+			printk(KERN_ERR "%s: error queueing urb (%d)\n", __FUNCTION__, ret);
+			goto abort_locked;
+		}
+		local_irq_restore(flags);
+	} while (eof == 0);
+
+	/* when successful in queuing all payloads of a frame, toggle the frame identifier
+	   note: this does not happen when the frame transfer was interrupted by
+	   some low-level error
+	 */
+	fid = !fid;
+
+#ifdef INFO
+	fc++;
+#endif
+
+      early_exit:
+	return;
+
+      abort_locked:
+	if ((fvdc_dev) && (fvdc_dev->state == FVDC_STATE_STREAMING))
+		fvdc_set_dev_state(fvdc_dev, FVDC_STATE_STREAMABLE);
+	local_irq_restore(flags);
+	return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void fvdc_abort(void)
+{
+	if (fvdc_dev->state == FVDC_STATE_STREAMING) {
+		/* this aborts the ongoing streaming operation */
+		fvdc_set_dev_state(fvdc_dev, FVDC_STATE_STREAMABLE);
+
+		usb_ep_disable(fvdc_dev->bulk_in);
+
+		mdelay(1);
+
+		usb_ep_enable(fvdc_dev->bulk_in, fvdc_dev->bulk_in_ep_desc);
+
+		mdelay(1);
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int fvdc_pan_display(struct fb_var_screeninfo *var,
+			      struct fb_info *info)
+{
+	/* this must call the actual pan-display function, and then stream the panned frame
+	   if STREAMABLE, or queue the frame as pending if STREAMING */
+	int result = 0;
+	dma_addr_t dma_start = 0;
+	void *cpu_start = 0;
+	int length;
+	int offset;
+
+	/* this is called from the attached framebuffer driver, and should not block with
+	   the exception for the 'pan_display' function (that is the original function
+	   that was replaced in the fb_info stucture by this function) */
+
+	if (fvdc_dev->pan_display) {
+		result = fvdc_dev->pan_display(var, info);
+	}
+
+	if (result)
+		return result;
+
+	if (var->xoffset != 0) {
+		return -EINVAL;
+	}
+
+	offset = var->yoffset * info->fix.line_length;
+	length = info->var.yres * info->fix.line_length;
+	if (info->fix.smem_start) {
+		dma_start = info->fix.smem_start + offset;
+	}
+	if (info->screen_base) {
+		cpu_start = info->screen_base + offset;
+	}
+
+	fvdc_stream_frame((void *)dma_start, cpu_start, length);
+
+	return 0;
+}
+
+static int fvdc_force_frame(struct fvdc_dev *dev)
+{
+	int offset;
+	struct fb_info *info;
+
+	info = dev->attached_fb;
+
+	if (!info)
+		return -ENOENT;
+
+	if (info->var.xoffset) {
+		return -EINVAL;
+	}
+
+	offset = info->var.yoffset * info->fix.line_length;
+	dev->pending.size = info->var.yres * info->fix.line_length;
+	dev->pending.dma =
+	    (void *)((unsigned long int)info->fix.smem_start + offset);
+	dev->pending.cpu =
+	    (void *)((unsigned long int)info->screen_base + offset);
+
+	return queue_work(fvdc_dev->work_queue,
+			  &fvdc_dev->pending_frame_work);
+}
+
+static int fvdc_detach_framebuffer(void)
+{
+	/* remove hook pan display function */
+	if (fvdc_dev->attached_fb) {
+		if (fvdc_dev->attached_fb->fbops->fb_pan_display ==
+		    fvdc_pan_display)
+			fvdc_dev->attached_fb->fbops->fb_pan_display =
+			    fvdc_dev->pan_display;
+	}
+	fvdc_dev->pan_display = 0;
+	fvdc_dev->attached_fb = 0;
+
+	return 0;
+}
+
+static int fvdc_attach_framebuffer(int index)
+{
+	int result = 0;
+	struct fb_info *fb_info;
+
+	if (index >= num_registered_fb) {
+		result = -EINVAL;
+		goto fail;
+	}
+
+	fb_info = registered_fb[index];
+
+	if (!fb_info) {
+		result = -EINVAL;
+		goto fail;
+	}
+
+	if (fvdc_dev->state == FVDC_STATE_IDLE) {
+		/* while the width & height have not been announced
+		   (idle state) its ok to change these */
+		fvdc_dev->width = fb_info->var.xres;
+		fvdc_dev->height = fb_info->var.yres;
+	}
+
+	if ((fb_info->var.xres != fvdc_dev->width) ||
+	    (fb_info->var.yres != fvdc_dev->height)) {
+		/* if these do not match, its not allowed to attach
+		   because we can't change the usb announced resolution
+		   on-the-fly */
+		result = -EINVAL;
+		goto fail;
+	}
+
+	fvdc_detach_framebuffer();
+
+	fvdc_dev->attached_fb = fb_info;
+	fvdc_dev->pan_display = fb_info->fbops->fb_pan_display;
+	fb_info->fbops->fb_pan_display = fvdc_pan_display;
+
+      fail:
+	return result;
+}
+
+struct fb_info* fvdc_attached_framebuffer(void)
+{
+	return (fvdc_dev)?(fvdc_dev->attached_fb):(0);
+}
+
+/* -------------------------------------------------------------------------
+       functions that are safe to call from atomic (non-blocking) context
+   ------------------------------------------------------------------------- */
+
+static void fvdc_probe_complete(struct usb_ep *ep,
+					   struct usb_request *req)
+{
+	int set_format_index;
+	struct usb_video_probe_commit_controls *set_cur =
+	    (struct usb_video_probe_commit_controls *)req->buf;
+
+	if (fvdc_dev->state < FVDC_STATE_SELECTED) {
+		return;
+	}
+
+	if (req->length < VC_PROBECOMMIT_REQUEST_LENGTH_MIN)
+		return;
+
+	if (req->status) {
+		return;
+	}
+
+	/* SET_CUR on PROBE control is only allowed to change the
+	   format index of the probe control */
+
+	set_format_index = set_cur->bFormatIndex;
+
+	switch (set_format_index) {
+	case VC_YUYV_FORMAT_INDEX:
+	case VC_RGBP_FORMAT_INDEX:
+#ifdef AVH_USE_RGBP_RLE
+	case VC_RGBP_RLE_FORMAT_INDEX:
+#endif
+		break;
+
+	default:
+		return;
+	}
+
+	/* all checks done, change the index in the probe control */
+
+	fvdc_probe_controls.bFormatIndex = set_format_index;
+	return;
+}
+
+static void fvdc_commit_complete(struct usb_ep *ep,
+					    struct usb_request *req)
+{
+	int set_format_index;
+	struct usb_video_probe_commit_controls *set_cur =
+	    (struct usb_video_probe_commit_controls *)req->buf;
+
+	if (fvdc_dev->state < FVDC_STATE_SELECTED) {
+		return;
+	}
+
+	if (req->length < VC_PROBECOMMIT_REQUEST_LENGTH_MIN)
+		return;
+
+	if (req->status) {
+		return;
+	}
+
+	set_format_index = set_cur->bFormatIndex;
+
+	switch (set_format_index) {
+	case VC_YUYV_FORMAT_INDEX:
+	case VC_RGBP_FORMAT_INDEX:
+#ifdef AVH_USE_RGBP_RLE
+	case VC_RGBP_RLE_FORMAT_INDEX:
+#endif
+		break;
+
+	default:
+		return;
+	}
+
+	/* committing format choice */
+
+	if (fvdc_commit_controls.bFormatIndex != set_format_index) {
+		fvdc_commit_controls.bFormatIndex = set_format_index;
+
+		/* format index changed....abort a current transfer, if any */
+		fvdc_abort();
+	}
+
+	switch (fvdc_dev->state) {
+	case FVDC_STATE_SELECTED:
+		fvdc_set_dev_state(fvdc_dev, FVDC_STATE_STREAMABLE);
+		break;
+
+	case FVDC_STATE_STREAMABLE:
+		break;
+
+	case FVDC_STATE_STREAMING:
+		fvdc_abort();
+		break;
+
+	default:
+		break;
+	}
+
+	if (fvdc_dev->state == FVDC_STATE_STREAMABLE) {
+		fvdc_force_frame(fvdc_dev);
+	}
+
+	return;
+}
+
+/* this is the callback function that is called by the controller driver
+   for all queued requests; it is called in interrupt context but in some cases also
+   in process context (if the endpoint is disabled, or the request is explicitly dequeued) */
+
+static void fvdc_urb_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	return;
+}
+
+/* this is the callback function that is called by the controller driver
+   for all queued requests; it is called in interrupt context but in some cases also
+   in process context (if the endpoint is disabled, or the request is explicitly dequeued) */
+
+static void fvdc_urb_final_complete(struct usb_ep *ep,
+					       struct usb_request *req)
+{
+	int status = req->status;
+
+	if (fvdc_dev->state <= FVDC_STATE_STREAMABLE) {
+		return;
+	}
+
+	switch (status) {
+	case 0:
+		if (fvdc_dev->state == FVDC_STATE_STREAMING) {
+			fvdc_set_dev_state(fvdc_dev,
+					     FVDC_STATE_STREAMABLE);
+		}
+
+		if ((fvdc_dev->state >= FVDC_STATE_STREAMABLE) &&
+		    (fvdc_dev->pending.size > 0)) {
+			queue_work(fvdc_dev->work_queue,
+				   &fvdc_dev->pending_frame_work);
+		}
+		break;
+
+
+	case -ECONNRESET:
+		fvdc_set_dev_state(fvdc_dev, FVDC_STATE_STREAMABLE);
+		/* clear this error, as this is recoverable from the device side */
+		status = 0;
+		/* fall through intentional */
+
+	default:
+		fvdc_halt();
+		break;
+	}
+
+	return;
+}
+
+/* this is called from composite.c, when it defers an interface specific setup request
+   to this function driver. It should only be called on interrupt context. */
+
+static int fvdc_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_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	/* We only support the videostreaming probe and commit
+	 * control selectors. Anything else is stalled. */
+
+	if (ctrl->wIndex != video_stream_interface_desc.bInterfaceNumber)
+		return value;
+
+	switch (w_value >> 8) {
+	case VS_PROBE_CONTROL:
+		switch (ctrl->bRequest) {
+		case GET_CUR:
+			value =
+			    min(w_length, (u16) sizeof(fvdc_probe_controls));
+			memcpy(req->buf, &fvdc_probe_controls, value);
+			break;
+
+		case GET_MIN:
+			value =
+			    min(w_length, (u16) sizeof(fvdc_probe_controls));
+			memcpy(req->buf, &fvdc_probe_controls, value);
+			break;
+
+		case GET_MAX:
+			value =
+			    min(w_length, (u16) sizeof(fvdc_probe_controls));
+			memcpy(req->buf, &fvdc_probe_controls, value);
+			break;
+
+		case GET_DEF:
+			value =
+			    min(w_length,
+				(u16) sizeof(fvdc_default_controls));
+			memcpy(req->buf, &fvdc_default_controls, value);
+			break;
+
+		case GET_LEN:
+			if ((w_length == 1) || (w_length == 2)) {
+				((unsigned char *)req->buf)[0] =
+				    VC_PROBECOMMIT_REQUEST_LENGTH;
+				((unsigned char *)req->buf)[1] = 0;
+				value = w_length;
+			} else
+				value = 0;
+			break;
+
+		case GET_INFO:
+			if (w_length == 1) {
+				((unsigned char *)req->buf)[0] = 0x03;
+				value = 1;
+			} else
+				value = 0;
+			break;
+
+		case SET_CUR:
+			value = min(w_length, (u16) VC_PROBECOMMIT_REQUEST_LENGTH);
+			fvdc_dev->probecommit_req->length = value;
+			fvdc_dev->probecommit_req->zero = value < w_length;
+			fvdc_dev->probecommit_req->context = 0;
+			fvdc_dev->probecommit_req->complete = fvdc_probe_complete;
+			value = usb_ep_queue(cdev->gadget->ep0, fvdc_dev->probecommit_req, GFP_ATOMIC);
+			return value;
+
+		default:
+			return value;
+		}
+		break;
+
+	case VS_COMMIT_CONTROL:
+		switch (ctrl->bRequest) {
+		case GET_CUR:
+			value =
+			    min(w_length, (u16) sizeof(fvdc_commit_controls));
+			memcpy(req->buf, &fvdc_commit_controls, value);
+			break;
+
+		case GET_LEN:
+			if ((w_length == 1) || (w_length == 2)) {
+				((unsigned char *)req->buf)[0] =
+				    VC_PROBECOMMIT_REQUEST_LENGTH;
+				((unsigned char *)req->buf)[1] = 0;
+				value = w_length;
+			} else
+				value = 0;
+			break;
+
+		case GET_INFO:
+			if (w_length == 1) {
+				((unsigned char *)req->buf)[0] = 0x03;
+				value = 1;
+			} else
+				value = 0;
+			break;
+
+		case SET_CUR:
+			value = min(w_length, (u16) VC_PROBECOMMIT_REQUEST_LENGTH);
+			fvdc_dev->probecommit_req->length = value;
+			fvdc_dev->probecommit_req->zero = value < w_length;
+			fvdc_dev->probecommit_req->context = 0;
+			fvdc_dev->probecommit_req->complete = fvdc_commit_complete;
+			value = usb_ep_queue(cdev->gadget->ep0, fvdc_dev->probecommit_req, GFP_ATOMIC);
+			return value;
+
+		default:
+			return value;
+		}
+		break;
+	default:
+		return value;
+	}
+
+	if (value >= 0) {
+		value = min((u16)value, w_length);
+		req->length = value;
+		req->zero = value < ctrl->wLength;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+	}
+
+	return value;
+}
+
+/* variant of 'disable' without function pointer argument */
+static void fvdc_halt(void) {
+	if (fvdc_dev->state <= FVDC_STATE_CONFIGURED)
+		return;
+
+	fvdc_abort();
+
+	fvdc_set_dev_state(fvdc_dev, FVDC_STATE_CONFIGURED);
+
+	if (fvdc_dev->bulk_in) {
+		usb_ep_disable(fvdc_dev->bulk_in);
+	}
+}
+
+/* called from composite.c, handling a set configuration request, on interrupt context */
+static void fvdc_disable(struct usb_function *f)
+{
+	fvdc_halt();
+}
+
+static int fvdc_set_alt(struct usb_function *f, unsigned intf,
+				   unsigned alt)
+{
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	if ((fvdc_dev->state < FVDC_STATE_SELECTED) &&
+	    (intf == video_stream_interface_desc.bInterfaceNumber)) {
+		int ret;
+
+		fvdc_set_dev_state(fvdc_dev, FVDC_STATE_SELECTED);
+
+		fvdc_dev->bulk_in_ep_desc =
+		    ep_choose(cdev->gadget, &hs_bulk_in_ep_desc,
+			      &fs_bulk_in_ep_desc);
+
+		ret = usb_ep_enable(fvdc_dev->bulk_in,
+				  fvdc_dev->bulk_in_ep_desc);
+
+		fvdc_dev->alt = alt;
+
+		if (ret) {
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int fvdc_get_alt(struct usb_function *f, unsigned intf)
+{
+	return fvdc_dev->alt;
+}
+
+static void fvdc_suspend(struct usb_function *f)
+{
+	fvdc_halt();
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int /* __init_or_exit */ fvdc_free_urbs(void)
+{
+	int index;
+
+	for (index = 0; index < fvdc_dev->payload_urbs; index++) {
+		struct usb_request *req = fvdc_dev->payload_urb[index];
+		fvdc_dev->payload_urb[index] = 0;
+		if (req) {
+			if (req->buf) {
+				ClearPageReserved(virt_to_page
+						  ((void *)req->buf));
+
+				free_page((unsigned long)req->buf);
+
+				req->dma = 0;
+				req->buf = 0;
+			}
+
+			usb_ep_free_request(fvdc_dev->bulk_in, req);
+		}
+	}
+
+	kfree(fvdc_dev->payload_urb);
+	fvdc_dev->payload_urb = 0;
+	fvdc_dev->payload_urbs = 0;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int __init fvdc_request_urbs(void)
+{
+	int ret;
+	int index;
+
+	fvdc_dev->payload_urbs = (FVDC_MAX_PAYLOAD_SIZE - 1 +
+				    ((fvdc_dev->attached_fb->var.xres *
+				      fvdc_dev->attached_fb->var.yres *
+				      fvdc_dev->attached_fb->var.
+				      bits_per_pixel) >> 3))
+	    / FVDC_MAX_PAYLOAD_SIZE;
+
+	fvdc_dev->payload_urb = kmalloc(fvdc_dev->payload_urbs *
+					  sizeof(struct usb_request *),
+					  GFP_KERNEL);
+
+	if (!fvdc_dev->payload_urb) {
+		return -ENOMEM;
+	}
+
+	for (index = 0; index < fvdc_dev->payload_urbs; index++) {
+		struct page *page;
+		struct usb_request *req =
+		    usb_ep_alloc_request(fvdc_dev->bulk_in, GFP_KERNEL);
+
+		if (!req) {
+			ret = -ENOMEM;
+			goto failure;
+		}
+
+		req->length = 0;
+		req->complete = fvdc_urb_complete;
+		req->context = (void *)index;
+
+		page = alloc_page(GFP_KERNEL);
+
+		if (!page) {
+			req->buf = 0;
+			req->dma = 0;
+			usb_ep_free_request(fvdc_dev->bulk_in, req);
+
+			ret = -ENOMEM;
+			goto failure;
+		}
+
+		SetPageReserved(page);
+
+		req->buf = page_address(page);
+		req->dma = virt_to_phys(req->buf);
+
+		fvdc_dev->payload_urb[index] = req;
+	}
+
+	return 0;
+
+      failure:
+	fvdc_free_urbs();
+	return ret;
+}
+
+/* init/exit - module insertion/removal */
+
+static void __init fvdc_prepare_descriptors(void)
+{
+	int width;
+	int height;
+	int size;
+
+	width = fvdc_dev->width;
+	height = fvdc_dev->height;
+	size = width * height;
+
+	fvdc_default_controls.dwMaxVideoFrameSize =
+	    __cpu_to_le32(size * VC_MAX_FORMAT_BPP / 8);
+
+	yuyv_frame_desc.wWidth = __cpu_to_le16(width);
+	yuyv_frame_desc.wHeight = __cpu_to_le16(height);
+	yuyv_frame_desc.dwMinBitRate =
+	    __cpu_to_le32(size * VC_YUYV_FORMAT_BPP / 100);
+	yuyv_frame_desc.dwMaxBitRate =
+	    __cpu_to_le32(size * VC_YUYV_FORMAT_BPP * 100);
+	yuyv_frame_desc.dwMaxVideoFrameBufferSize =
+	    __cpu_to_le32(size * VC_YUYV_FORMAT_BPP / 8);
+
+	rgbp_frame_desc.wWidth = __cpu_to_le16(width);
+	rgbp_frame_desc.wHeight = __cpu_to_le16(height);
+	rgbp_frame_desc.dwMinBitRate =
+	    __cpu_to_le32(size * VC_RGBP_FORMAT_BPP / 100);
+	rgbp_frame_desc.dwMaxBitRate =
+	    __cpu_to_le32(size * VC_RGBP_FORMAT_BPP * 100);
+#ifndef AVH_USE_RGBP_RLE
+	rgbp_frame_desc.dwMaxVideoFrameBufferSize =
+	    __cpu_to_le32(size * VC_RGBP_FORMAT_BPP / 8);
+#else
+	/* this dwBytesPerLine only exists in the frame based descriptor variant of RGBP */
+	rgbp_frame_desc.dwBytesPerLine =
+	    __cpu_to_le32(width * VC_RGBP_FORMAT_BPP / 8);
+
+	rgbp_rle_frame_desc.wWidth = __cpu_to_le16(width);
+	rgbp_rle_frame_desc.wHeight = __cpu_to_le16(height);
+	rgbp_rle_frame_desc.dwMinBitRate =
+	    __cpu_to_le32(size * VC_RGBP_RLE_FORMAT_BPP / 100);
+	rgbp_rle_frame_desc.dwMaxBitRate =
+	    __cpu_to_le32(size * VC_RGBP_RLE_FORMAT_BPP * 100);
+	rgbp_rle_frame_desc.dwBytesPerLine = __cpu_to_le32(0);
+#endif
+}
+
+/* sysfs support */
+
+static ssize_t fvdc_read_resolution(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "%dx%d\n", fvdc_dev->width, fvdc_dev->height);
+}
+
+static ssize_t fvdc_read_format(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	int ret = 0;
+	switch (fvdc_probe_controls.bFormatIndex) {
+	case VC_YUYV_FORMAT_INDEX:
+		ret = sprintf(buf, "YUYV\n");
+		break;
+	case VC_RGBP_FORMAT_INDEX:
+		ret = sprintf(buf, "RGBP\n");
+		break;
+	case VC_RGBP_RLE_FORMAT_INDEX:
+		ret = sprintf(buf, "RGBP_RLE\n");
+		break;
+	default:
+		ret = sprintf(buf, "UNKNOWN\n");
+		break;
+	}
+	return ret;
+}
+
+static ssize_t fvdc_read_state(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+
+	int ret = 0;
+	switch (fvdc_dev->state) {
+	case FVDC_STATE_VOID:
+		ret = sprintf(buf, "VOID\n");
+		break;
+	case FVDC_STATE_IDLE:
+		ret = sprintf(buf, "IDLE\n");
+		break;
+	case FVDC_STATE_CONFIGURED:
+		ret = sprintf(buf, "CONFIGURED\n");
+		break;
+	case FVDC_STATE_SELECTED:
+		ret = sprintf(buf, "SELECTED\n");
+		break;
+	case FVDC_STATE_STREAMABLE:
+		ret = sprintf(buf, "STREAMABLE\n");
+		break;
+	case FVDC_STATE_STREAMING:
+		ret = sprintf(buf, "STREAMING\n");
+		break;
+	default:
+		ret = sprintf(buf, "UNKNOWN\n");
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t fvdc_read_bpp(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	if (fvdc_dev->attached_fb == NULL)
+		return 0;
+	else
+		return sprintf(buf, "%d\n",
+			       fvdc_dev->attached_fb->var.bits_per_pixel);
+}
+
+static DEVICE_ATTR(resolution, S_IRUGO, fvdc_read_resolution, NULL);
+static DEVICE_ATTR(format, S_IRUGO, fvdc_read_format, NULL);
+static DEVICE_ATTR(bpp, S_IRUGO, fvdc_read_bpp, NULL);
+static DEVICE_ATTR(state, S_IRUGO, fvdc_read_state, NULL);
+
+static void fvdc_release(struct device *dev)
+{
+}
+
+struct device fvdc_device = {
+	.init_name = "fvideo",
+	.release = fvdc_release
+};
+
+static int fvdc_init_attr(void)
+{
+	int res = -ENODEV;
+
+	res = device_register(&fvdc_device);
+
+	/* Sysfs attributes. */
+	if (res >= 0) {
+		if (device_create_file(&fvdc_device, &dev_attr_resolution) ||
+		    device_create_file(&fvdc_device, &dev_attr_format) ||
+		    device_create_file(&fvdc_device, &dev_attr_bpp) ||
+		    device_create_file(&fvdc_device, &dev_attr_state)) {
+			return -ENODEV;
+		}
+	} else {
+		return res;
+	}
+
+	return 0;
+}
+
+static void fvdc_destroy_attr(void)
+{
+	device_remove_file(&fvdc_device, &dev_attr_resolution);
+	device_remove_file(&fvdc_device, &dev_attr_format);
+	device_remove_file(&fvdc_device, &dev_attr_bpp);
+	device_remove_file(&fvdc_device, &dev_attr_state);
+	device_unregister(&fvdc_device);
+}
+
+static int __init fvdc_bind(struct usb_configuration *c,
+				       struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct usb_ep *ep = 0;
+	int status;
+
+	fvdc_dev = kzalloc(sizeof *fvdc_dev, GFP_KERNEL);
+	if (!fvdc_dev)
+		return -ENOMEM;
+
+	/* Prepare work structure */
+	INIT_WORK(&fvdc_dev->stream_frame_work, fvdc_stream_frame_work);
+	INIT_WORK(&fvdc_dev->pending_frame_work, fvdc_pending_frame_work);
+
+	/* Create kernel work queue and worker thread */
+	fvdc_dev->work_queue = create_workqueue(FVDC_KWORK_NAME);
+
+	if (!fvdc_dev->work_queue)
+		return -ESRCH;
+
+	fvdc_set_dev_state(fvdc_dev, FVDC_STATE_IDLE);
+
+	/* local things ready now */
+
+	status = fvdc_attach_framebuffer(fvdc_function_driver.fb_index);
+
+	if (status) {
+		return status;
+	}
+
+	fvdc_prepare_descriptors();
+
+	status = usb_interface_id(c, f);
+	if (status < 0) {
+		goto fail;
+	}
+
+	video_control_interface_desc.bInterfaceNumber = status;
+	vc_interface_association_desc.bFirstInterface = status;
+
+	status = usb_interface_id(c, f);
+	if (status < 0) {
+		goto fail;
+	}
+	video_stream_interface_desc.bInterfaceNumber = status;
+	vc_video_control_interface_desc.baInterfaceNr = status;
+
+	if (fvdc_strings_dev[STRING_VIDEO_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0) {
+			goto fail;
+		}
+
+		fvdc_strings_dev[STRING_VIDEO_IDX].id = status;
+		vc_interface_association_desc.iFunction = status;
+	}
+
+	if (fvdc_strings_dev[STRING_VIDEO_CONTROL_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0) {
+			goto fail;
+		}
+
+		fvdc_strings_dev[STRING_VIDEO_CONTROL_IDX].id = status;
+		video_control_interface_desc.iInterface = status;
+	}
+
+	if (fvdc_strings_dev[STRING_VIDEO_STREAM_IDX].id == 0) {
+		status = usb_string_id(c->cdev);
+		if (status < 0) {
+			goto fail;
+		}
+
+		fvdc_strings_dev[STRING_VIDEO_STREAM_IDX].id = status;
+		video_stream_interface_desc.iInterface = status;
+	}
+
+	ep = usb_ep_autoconfig(cdev->gadget, &fs_bulk_in_ep_desc);
+	if (!ep) {
+		goto fail;
+	}
+
+	/* copy the dynamically assigned endpoint address */
+	vc_video_stream_interface_desc.bEndpointAddress =
+	    fs_bulk_in_ep_desc.bEndpointAddress;
+
+	f->descriptors = fvdc_fs_function;
+	fvdc_dev->bulk_in = ep;
+
+	fvdc_dev->probecommit_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+	if (!fvdc_dev->probecommit_req) {
+		goto fail;
+	}
+	fvdc_dev->probecommit_req->buf = kmalloc(VC_PROBECOMMIT_REQUEST_LENGTH, GFP_KERNEL);
+	if (!fvdc_dev->probecommit_req->buf) {
+		goto fail;
+	}
+	fvdc_dev->probecommit_req->complete = 0;
+
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		hs_bulk_in_ep_desc.bEndpointAddress =
+		    fs_bulk_in_ep_desc.bEndpointAddress;
+		f->hs_descriptors = fvdc_hs_function;
+	}
+#endif
+
+	memcpy(&fvdc_commit_controls, &fvdc_default_controls,
+	       sizeof(fvdc_commit_controls));
+	memcpy(&fvdc_probe_controls, &fvdc_default_controls,
+	       sizeof(fvdc_probe_controls));
+
+	if (fvdc_request_urbs()) {
+		goto fail;
+	}
+
+	ep->driver_data = fvdc_dev;
+
+	fvdc_set_dev_state(fvdc_dev, FVDC_STATE_CONFIGURED);
+
+	fvdc_init_attr();
+	return 0;
+
+fail:
+	/* cleanup of allocated resources (memory, descriptors, ...) is
+	   done in the function unbind and config_unbind calls, these are
+	   called by the composite framework because this function bind call
+	   fails */
+
+	return -ENODEV;
+}
+
+/*
+ * Called to release structures/memory/resources still held by the USB function.
+ * It should not be called from interrupt context!
+ */
+static void fvdc_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+
+	fvdc_destroy_attr();
+
+	fvdc_set_dev_state(fvdc_dev, FVDC_STATE_IDLE);
+
+	fvdc_detach_framebuffer();
+
+	flush_workqueue(fvdc_dev->work_queue);
+	destroy_workqueue(fvdc_dev->work_queue);
+
+	if (fvdc_dev->bulk_in) {
+		usb_ep_disable(fvdc_dev->bulk_in);
+	}
+
+	fvdc_free_urbs();
+
+	if(fvdc_dev->probecommit_req) {
+		if(fvdc_dev->probecommit_req->buf) kfree(fvdc_dev->probecommit_req->buf);
+
+		usb_ep_free_request(cdev->gadget->ep0, fvdc_dev->probecommit_req);
+
+		fvdc_dev->probecommit_req = 0;
+	}
+
+	if (fvdc_dev->bulk_in) {
+		fvdc_dev->bulk_in->driver_data = 0;
+	}
+
+	fvdc_dev->bulk_in = 0;
+
+	fvdc_set_dev_state(fvdc_dev, FVDC_STATE_VOID);
+
+	kfree(fvdc_dev);
+	fvdc_dev = 0;
+}
+
+/* ------------------------------------------------------------------------- */
+/*                         external interface                                */
+/* ------------------------------------------------------------------------- */
+
+struct fvdc_function fvdc_function_driver = {
+	.func = {
+		 .name = FVDC_NAME,
+		 .strings = fvdc_strings,
+		 .bind = fvdc_bind,
+		 .unbind = fvdc_unbind,
+		 .setup = fvdc_setup,
+		 .disable = fvdc_disable,
+		 .set_alt = fvdc_set_alt,
+		 .get_alt = fvdc_get_alt,
+		 .suspend = fvdc_suspend,
+		 },
+	.fb_index = 0,
+};
diff --git a/drivers/usb/gadget/f_vdc.h b/drivers/usb/gadget/f_vdc.h
new file mode 100644
index 0000000..3742566
--- /dev/null
+++ b/drivers/usb/gadget/f_vdc.h
@@ -0,0 +1,345 @@
+/* drivers/usb/gadget/f_vdc.h
+ *
+ * Copyright (c) 2009 TomTom BV <http://www.tomtom.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ */
+#ifndef __F_VDC_H__
+#define __F_VDC_H__
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+struct fvdc_function {
+	struct usb_function func;
+	int fb_index;
+};
+
+struct fb_info;
+extern struct fvdc_function fvdc_function_driver;
+extern struct fb_info* fvdc_attached_framebuffer(void);
+
+/* the definitions below should be included in include/linux/usb/video.h */
+
+#ifndef __LINUX_USB_VIDEO_EXTENSION_H
+#define __LINUX_USB_VIDEO_EXTENSION_H
+
+#define SET_CUR				0x01
+#define GET_CUR 			0x81
+#define GET_MIN 			0x82
+#define GET_MAX 			0x83
+#define GET_RES 			0x84
+#define GET_LEN 			0x85
+#define GET_INFO 			0x86
+#define GET_DEF 			0x87
+
+#define CC_VIDEO			0x0E
+
+#define VC_DESCRIPTOR_UNDEFINED		0x00
+#define VC_HEADER			0x01
+#define VC_INPUT_TERMINAL		0x02
+#define VC_OUTPUT_TERMINAL		0x03
+#define VC_SELECTOR_UNIT		0x04
+#define VC_PROCESSING_UNIT		0x05
+#define VC_EXTENSION_UNIT		0x06
+
+#define SC_UNDEFINED			0x00
+#define SC_VIDEOCONTROL			0x01
+#define SC_VIDEOSTREAMING		0x02
+#define SC_VIDEO_INTERFACE_COLLECTION	0x03
+
+#define PC_PROTOCOL_UNDEFINED		0x00
+
+#define TT_VENDOR_SPECIFIC		0x0100
+#define TT_STREAMING			0x0101
+
+#define ITT_VENDOR_SPECIFIC		0x0200
+#define ITT_CAMERA			0x0201
+#define ITT_MEDIA_TRANSPORT_INPUT	0x0202
+
+#define SUP_GET_VAL_REQ			0
+#define SUP_SET_VAL_REQ			1
+#define DISABLED_DUE_TO_AUTO_MODE	2
+#define AUTOUPDATE_CONTROL		3
+#define ASYNC_CONTROL			4
+
+#define VC_INTERFACE_DIGIT		0
+#define VS_INTERFACE_DIGIT		1
+#define VS_PROBE_CONTROL		0x01
+#define VS_COMMIT_CONTROL		0x02
+
+#define VS_UNDEFINED			0x00
+#define VS_INPUT_HEADER			0x01
+#define VS_OUTPUT_HEADER		0x02
+#define VS_STILL_IMAGE_FRAME		0x03
+#define VS_FORMAT_UNCOMPRESSED		0x04
+#define VS_FRAME_UNCOMPRESSED		0x05
+#define VS_FORMAT_MJPEG			0x06
+#define VS_FRAME_MJPEG			0x07
+#define VS_FORMAT_MPEG2TS		0x0A
+#define VS_FORMAT_DV			0x0C
+#define VS_COLORFORMAT			0x0D
+#define VS_FORMAT_FRAME_BASED		0x10
+#define VS_FRAME_FRAME_BASED		0x11
+#define VS_FORMAT_STREAM_BASED		0x12
+
+#define VC_GUID_LENGTH			16
+
+struct usb_cs_vc_interface_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__le16 bcdUVC;
+	__le16 wTotalLength;
+	__le32 dwClockFrequency;
+	__u8 bInCollection;
+	__u8 baInterfaceNr;
+} __attribute__ ((packed));
+
+struct usb_input_terminal_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bTerminalID;
+	__le16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 iTerminal;
+	__le16 wObjectiveFocalLengthMin;
+	__le16 wObjectiveFocalLengthMax;
+	__le16 wOcularFocalLength;
+	__u8 bControlSize;
+	__u8 bmControls0;
+	__u8 bmControls1;
+} __attribute__ ((packed));
+
+struct usb_vc_output_terminal_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bTerminalID;
+	__le16 wTerminalType;
+	__u8 bAssocTerminal;
+	__u8 bSourceID;
+	__u8 iTerminal;
+} __attribute__ ((packed));
+
+struct usb_vc_class_specific_interrupt_endpoint_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__le16 wMaxTransferSize;
+} __attribute__ ((packed));
+
+struct usb_cs_vs_interface_input_header_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bNumFormats;
+	__le16 wTotalLength;
+	__u8 bEndpointAddress;
+	__u8 bmInfo;
+	__u8 bTerminalLink;
+	__u8 bStillCaptureMethod;
+	__u8 bTriggerSupport;
+	__u8 bTriggerUsage;
+	__u8 bControlSize;
+	__u8 bmaControls0;
+	__u8 bmaControls1;
+	__u8 bmaControls2;
+	__u8 bmaControls3;
+	__u8 bmaControls4;
+	__u8 bmaControls5;
+	__u8 bmaControls6;
+	__u8 bmaControls7;
+} __attribute__ ((packed));
+
+struct usb_uncompressed_format_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatIndex;
+	__u8 bNumFrameDescriptors;
+	__u8 guidFormat[VC_GUID_LENGTH];
+	__u8 bBitsPerPixel;
+	__u8 bDefaultFrameIndex;
+	__u8 bAspectRatioX;
+	__u8 bAspectRatioY;
+	__u8 bmInterlaceflags;
+	__u8 bCopyProtect;
+} __attribute__ ((packed));
+
+struct usb_uncompressed_frame_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bFrameIndex;
+	__u8 bmCapabilities;
+	__le16 wWidth;
+	__le16 wHeight;
+	__le32 dwMinBitRate;
+	__le32 dwMaxBitRate;
+	__le32 dwMaxVideoFrameBufferSize;
+	__le32 dwDefaultFrameInterval;
+	__u8 bFrameIntervalType;
+	__le32 dwMinFrameInterval;
+	__le32 dwMaxFrameInterval;
+	__le32 dwFrameIntervalStep;
+} __attribute__ ((packed));
+
+struct usb_frame_based_format_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatIndex;
+	__u8 bNumFrameDescriptors;
+	__u8 guidFormat[VC_GUID_LENGTH];
+	__u8 bBitsPerPixel;
+	__u8 bDefaultFrameIndex;
+	__u8 bAspectRatioX;
+	__u8 bAspectRatioY;
+	__u8 bmInterlaceflags;
+	__u8 bCopyProtect;
+	__u8 bVariableSize;
+} __attribute__ ((packed));
+
+struct usb_frame_based_frame_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bFrameIndex;
+	__u8 bmCapabilities;
+	__le16 wWidth;
+	__le16 wHeight;
+	__le32 dwMinBitRate;
+	__le32 dwMaxBitRate;
+	__le32 dwDefaultFrameInterval;
+	__u8 bFrameIntervalType;
+	__le32 dwBytesPerLine;
+	__le32 dwMinFrameInterval;
+	__le32 dwMaxFrameInterval;
+	__le32 dwFrameIntervalStep;
+} __attribute__ ((packed));
+
+struct usb_mjpeg_format_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bFormatIndex;
+	__u8 bNumFrameDescriptors;
+	__u8 bmFlags;
+	__u8 bDefaultFrameIndex;
+	__u8 bAspectRatioX;
+	__u8 bAspectRatioY;
+	__u8 bmInterlaceflags;
+	__u8 bCopyProtect;
+} __attribute__ ((packed));
+
+struct usb_mjpeg_frame_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bFrameIndex;
+	__u8 bmCapabilities;
+	__le16 wWidth;
+	__le16 wHeight;
+	__le32 dwMinBitRate;
+	__le32 dwMaxBitRate;
+	__le32 dwMaxVideoFrameBufferSize;
+	__le32 dwDefaultFrameInterval;
+	__u8 bFrameIntervalType;
+	__le32 dwMinFrameInterval;
+	__le32 dwMaxFrameInterval;
+	__le32 dwFrameIntervalStep;
+} __attribute__ ((packed));
+
+struct usb_color_matching_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubtype;
+	__u8 bColorPrimaries;
+	__u8 bTransferCharacteristics;
+	__u8 bMatrixCoefficients;
+} __attribute__ ((packed));
+
+struct usb_video_probe_commit_controls {
+	__le16 bmHint;
+	__u8 bFormatIndex;
+	__u8 bFrameIndex;
+	__le32 dwFrameInterval;
+	__le16 wKeyFrameRate;
+	__le16 wPFrameRate;
+	__le16 wCompQuality;
+	__le16 wCompWindowSize;
+	__le16 wDelay;
+	__le32 dwMaxVideoFrameSize;
+	__le32 dwMaxPayloadTransferSize;
+	__le32 dwClockFrequency;
+	__u8 bmFramingInfo;
+	__u8 bPreferedVersion;
+	__u8 bMinVersion;
+	__u8 bMaxVersion;
+} __attribute__ ((packed));
+
+struct usb_vc_processing_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bUnitID;
+	__u8 bSourceID;
+	__le16 wMaxMultiplier;
+	__u8 bControlSize;
+	__le16 bmControls;
+	__u8 iProcessing;
+//      __u8 bmVideoStandards;
+} __attribute__ ((packed));
+
+struct usb_vc_extension_unit_descriptor {
+	__u8 bLength;
+	__u8 bDescriptorType;
+	__u8 bDescriptorSubType;
+	__u8 bUnitID;
+	__u8 guidExtensionCode0;
+	__u8 guidExtensionCode1;
+	__u8 guidExtensionCode2;
+	__u8 guidExtensionCode3;
+	__u8 guidExtensionCode4;
+	__u8 guidExtensionCode5;
+	__u8 guidExtensionCode6;
+	__u8 guidExtensionCode7;
+	__u8 guidExtensionCode8;
+	__u8 guidExtensionCode9;
+	__u8 guidExtensionCode10;
+	__u8 guidExtensionCode11;
+	__u8 guidExtensionCode12;
+	__u8 guidExtensionCode13;
+	__u8 guidExtensionCode14;
+	__u8 guidExtensionCode15;
+	__u8 bNumControls;
+	__u8 bNrInPins;
+	__u8 baSourceID;
+	__u8 bControlSize;
+	__u8 bmControls0;
+	__u8 iExtension;
+} __attribute__ ((packed));
+
+struct video_control {
+	unsigned long long cur;
+	unsigned long long min;
+	unsigned long long max;
+	unsigned long long res;
+	unsigned long long def;
+	__u8 len;
+	__u8 info;
+} __attribute__ ((packed));
+
+struct video_entity {
+	__u8 ControlCount;
+	struct video_control VideoControls[];
+} __attribute__ ((packed));
+
+#endif //__LINUX_USB_VIDEO_EXTENSION_H
+
+#endif //__F_VIDEO_H__
-- 
1.6.3.3
--
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

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux