Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

506
drivers/usb/gadget/Kconfig Normal file
View file

@ -0,0 +1,506 @@
#
# USB Gadget support on a system involves
# (a) a peripheral controller, and
# (b) the gadget driver using it.
#
# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
#
# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
# - Some systems have both kinds of controllers.
#
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
menuconfig USB_GADGET
tristate "USB Gadget Support"
select NLS
help
USB is a master/slave protocol, organized with one master
host (such as a PC) controlling up to 127 peripheral devices.
The USB hardware is asymmetric, which makes it easier to set up:
you can't connect a "to-the-host" connector to a peripheral.
Linux can run in the host, or in the peripheral. In both cases
you need a low level bus controller driver, and some software
talking to it. Peripheral controllers are often discrete silicon,
or are integrated with the CPU in a microcontroller. The more
familiar host side controllers have names like "EHCI", "OHCI",
or "UHCI", and are usually integrated into southbridges on PC
motherboards.
Enable this configuration option if you want to run Linux inside
a USB peripheral device. Configure one hardware driver for your
peripheral/device side bus controller, and a "gadget driver" for
your peripheral protocol. (If you use modular gadget drivers,
you may configure more than one.)
If in doubt, say "N" and don't enable these drivers; most people
don't have this kind of hardware (except maybe inside Linux PDAs).
For more information, see <http://www.linux-usb.org/gadget> and
the kernel DocBook documentation for this API.
if USB_GADGET
config USB_GADGET_DEBUG
boolean "Debugging messages (DEVELOPMENT)"
depends on DEBUG_KERNEL
help
Many controller and gadget drivers will print some debugging
messages if you use this option to ask for those messages.
Avoid enabling these messages, even if you're actively
debugging such a driver. Many drivers will emit so many
messages that the driver timings are affected, which will
either create new failure modes or remove the one you're
trying to track down. Never enable these messages for a
production build.
config USB_GADGET_VERBOSE
bool "Verbose debugging Messages (DEVELOPMENT)"
depends on USB_GADGET_DEBUG
help
Many controller and gadget drivers will print verbose debugging
messages if you use this option to ask for those messages.
Avoid enabling these messages, even if you're actively
debugging such a driver. Many drivers will emit so many
messages that the driver timings are affected, which will
either create new failure modes or remove the one you're
trying to track down. Never enable these messages for a
production build.
config USB_GADGET_DEBUG_FILES
boolean "Debugging information files (DEVELOPMENT)"
depends on PROC_FS
help
Some of the drivers in the "gadget" framework can expose
debugging information in files such as /proc/driver/udc
(for a peripheral controller). The information in these
files may help when you're troubleshooting or bringing up a
driver on a new board. Enable these files by choosing "Y"
here. If in doubt, or to conserve kernel memory, say "N".
config USB_GADGET_DEBUG_FS
boolean "Debugging information files in debugfs (DEVELOPMENT)"
depends on DEBUG_FS
help
Some of the drivers in the "gadget" framework can expose
debugging information in files under /sys/kernel/debug/.
The information in these files may help when you're
troubleshooting or bringing up a driver on a new board.
Enable these files by choosing "Y" here. If in doubt, or
to conserve kernel memory, say "N".
config USB_GADGET_VBUS_DRAW
int "Maximum VBUS Power usage (2-500 mA)"
range 2 500
default 2
help
Some devices need to draw power from USB when they are
configured, perhaps to operate circuitry or to recharge
batteries. This is in addition to any local power supply,
such as an AC adapter or batteries.
Enter the maximum power your device draws through USB, in
milliAmperes. The permitted range of values is 2 - 500 mA;
0 mA would be legal, but can make some hosts misbehave.
This value will be used except for system-specific gadget
drivers that have more specific information.
config USB_GADGET_STORAGE_NUM_BUFFERS
int "Number of storage pipeline buffers"
range 2 4
default 2
help
Usually 2 buffers are enough to establish a good buffering
pipeline. The number may be increased in order to compensate
for a bursty VFS behaviour. For instance there may be CPU wake up
latencies that makes the VFS to appear bursty in a system with
an CPU on-demand governor. Especially if DMA is doing IO to
offload the CPU. In this case the CPU will go into power
save often and spin up occasionally to move data within VFS.
If selecting USB_GADGET_DEBUG_FILES this value may be set by
a module parameter as well.
If unsure, say 2.
source "drivers/usb/gadget/udc/Kconfig"
#
# USB Gadget Drivers
#
# composite based drivers
config USB_LIBCOMPOSITE
tristate
select CONFIGFS_FS
depends on USB_GADGET
config USB_F_ACM
tristate
config USB_F_SS_LB
tristate
config USB_U_SERIAL
tristate
config USB_U_ETHER
tristate
config USB_F_SERIAL
tristate
config USB_F_OBEX
tristate
config USB_F_NCM
tristate
config USB_F_ECM
tristate
config USB_F_PHONET
tristate
config USB_F_EEM
tristate
config USB_F_SUBSET
tristate
config USB_F_RNDIS
tristate
config USB_F_MASS_STORAGE
tristate
config USB_F_FS
tristate
config USB_F_UAC1
tristate
config USB_F_UAC2
tristate
config USB_F_UVC
tristate
config USB_F_MTP
tristate
config USB_F_PTP
tristate
config USB_F_AUDIO_SRC
tristate
config USB_F_ACC
tristate
choice
tristate "USB Gadget Drivers"
default USB_ETH
help
A Linux "Gadget Driver" talks to the USB Peripheral Controller
driver through the abstract "gadget" API. Some other operating
systems call these "client" drivers, of which "class drivers"
are a subset (implementing a USB device class specification).
A gadget driver implements one or more USB functions using
the peripheral hardware.
Gadget drivers are hardware-neutral, or "platform independent",
except that they sometimes must understand quirks or limitations
of the particular controllers they work with. For example, when
a controller doesn't support alternate configurations or provide
enough of the right types of endpoints, the gadget driver might
not be able work with that controller, or might need to implement
a less common variant of a device class protocol.
# this first set of drivers all depend on bulk-capable hardware.
config USB_CONFIGFS
tristate "USB functions configurable through configfs"
select USB_LIBCOMPOSITE
help
A Linux USB "gadget" can be set up through configfs.
If this is the case, the USB functions (which from the host's
perspective are seen as interfaces) and configurations are
specified simply by creating appropriate directories in configfs.
Associating functions with configurations is done by creating
appropriate symbolic links.
For more information see Documentation/usb/gadget_configfs.txt.
config USB_CONFIGFS_SERIAL
boolean "Generic serial bulk in/out"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
select USB_F_SERIAL
help
The function talks to the Linux-USB generic serial driver.
config USB_CONFIGFS_ACM
boolean "Abstract Control Model (CDC ACM)"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
select USB_F_ACM
help
ACM serial link. This function can be used to interoperate with
MS-Windows hosts or with the Linux-USB "cdc-acm" driver.
config USB_CONFIGFS_OBEX
boolean "Object Exchange Model (CDC OBEX)"
depends on USB_CONFIGFS
depends on TTY
select USB_U_SERIAL
select USB_F_OBEX
help
You will need a user space OBEX server talking to /dev/ttyGS*,
since the kernel itself doesn't implement the OBEX protocol.
config USB_CONFIGFS_NCM
boolean "Network Control Model (CDC NCM)"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
select USB_F_NCM
help
NCM is an advanced protocol for Ethernet encapsulation, allows
grouping of several ethernet frames into one USB transfer and
different alignment possibilities.
config USB_CONFIGFS_ECM
boolean "Ethernet Control Model (CDC ECM)"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
select USB_F_ECM
help
The "Communication Device Class" (CDC) Ethernet Control Model.
That protocol is often avoided with pure Ethernet adapters, in
favor of simpler vendor-specific hardware, but is widely
supported by firmware for smart network devices.
config USB_CONFIGFS_ECM_SUBSET
boolean "Ethernet Control Model (CDC ECM) subset"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
select USB_F_SUBSET
help
On hardware that can't implement the full protocol,
a simple CDC subset is used, placing fewer demands on USB.
config USB_CONFIGFS_RNDIS
bool "RNDIS"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
select USB_F_RNDIS
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
and Microsoft provides redistributable binary RNDIS drivers for
older versions of Windows.
To make MS-Windows work with this, use Documentation/usb/linux.inf
as the "driver info file". For versions of MS-Windows older than
XP, you'll need to download drivers from Microsoft's website; a URL
is given in comments found in that info file.
config USB_CONFIGFS_EEM
bool "Ethernet Emulation Model (EEM)"
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
select USB_F_EEM
help
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
and therefore can be supported by more hardware. Technically ECM and
EEM are designed for different applications. The ECM model extends
the network interface to the target (e.g. a USB cable modem), and the
EEM model is for mobile devices to communicate with hosts using
ethernet over USB. For Linux gadgets, however, the interface with
the host is the same (a usbX device), so the differences are minimal.
config USB_CONFIGFS_PHONET
boolean "Phonet protocol"
depends on USB_CONFIGFS
depends on NET
depends on PHONET
select USB_U_ETHER
select USB_F_PHONET
help
The Phonet protocol implementation for USB device.
config USB_CONFIGFS_MASS_STORAGE
boolean "Mass storage"
depends on USB_CONFIGFS
depends on BLOCK
select USB_F_MASS_STORAGE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
config USB_CONFIGFS_F_LB_SS
boolean "Loopback and sourcesink function (for testing)"
depends on USB_CONFIGFS
select USB_F_SS_LB
help
Loopback function loops back a configurable number of transfers.
Sourcesink function either sinks and sources bulk data.
It also implements control requests, for "chapter 9" conformance.
Make this be the first driver you try using on top of any new
USB peripheral controller driver. Then you can use host-side
test software, like the "usbtest" driver, to put your hardware
and its driver through a basic set of functional tests.
config USB_CONFIGFS_F_FS
boolean "Function filesystem (FunctionFS)"
depends on USB_CONFIGFS
select USB_F_FS
help
The Function Filesystem (FunctionFS) lets one create USB
composite functions in user space in the same way GadgetFS
lets one create USB gadgets in user space. This allows creation
of composite gadgets such that some of the functions are
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
config USB_CONFIGFS_F_DM
boolean "DM gadget"
depends on USB_CONFIGFS
select USB_F_DM
help
USB gadget DM(Diagnosis Monitor) support
config USB_CONFIGFS_F_ADB
boolean "ADB gadget"
depends on USB_CONFIGFS
select USB_F_ADB
help
USB gadget ADB support
config USB_CONFIGFS_F_MTP
boolean "MTP gadget"
depends on USB_CONFIGFS
select USB_F_MTP
help
USB gadget MTP support
config USB_CONFIGFS_F_PTP
boolean "PTP gadget"
depends on USB_CONFIGFS && USB_CONFIGFS_F_MTP
select USB_F_PTP
help
USB gadget PTP support
config USB_CONFIGFS_F_ACC
boolean "Accessory gadget"
depends on USB_CONFIGFS
select USB_F_ACC
help
USB gadget Accessory support
config USB_CONFIGFS_F_AUDIO_SRC
boolean "Audio Source gadget"
depends on USB_CONFIGFS && USB_CONFIGFS_F_ACC
select USB_F_AUDIO_SRC
help
USB gadget Audio Source support
config USB_CONFIGFS_UEVENT
boolean "Uevent notification of Gadget state"
depends on USB_CONFIGFS
help
Enable uevent notifications to userspace when the gadget
state changes. The gadget can be in any of the following
three states: "CONNECTED/DISCONNECTED/CONFIGURED"
config USB_RNDIS_MULTIPACKET
boolean "Samsung Rndis Multi Packet support"
depends on USB_CONFIGFS_UEVENT
help
Rndis Multipacket Support.
config USB_G_ANDROID
boolean "Android Composite Gadget"
select USB_F_ACM
select USB_LIBCOMPOSITE
select USB_U_SERIAL
help
The Android Composite Gadget supports multiple USB
functions: adb, acm, mass storage, mtp, accessory
and rndis.
Each function can be configured and enabled/disabled
dynamically from userspace through a sysfs interface.
config USB_ANDROID_SAMSUNG_COMPOSITE
boolean "Samsung Composite function"
depends on USB_G_ANDROID
help
Provides SAMSUNG composite driver.
Multi Configuration.
If you enable this option, android composite will be changed.
config USB_ANDROID_SAMSUNG_MTP
boolean "Samsung MTP function"
depends on USB_G_ANDROID && !SEC_FACTORY
help
Provides Media Transfer Protocol (MTP) support
for samsung gadget driver.
If you enable this option,
google mtp will be changed to samsung mtp.
config USB_LOCK_SUPPORT_FOR_MDM
boolean "Samsung USB_LOCK function for MDM"
depends on USB_G_ANDROID
help
Support USB lock for SDS MDM function.
If you enable this option,
USB is not connected to the work in office.
Of employees who have installed MDM(SDS).
config USB_DUN_SUPPORT
boolean "DUN support function"
depends on USB_G_ANDROID
help
Provides USB modem serial driver.
This function makes connection to acm from data router.
It uses misc register.
Support fops : open, close, release, read, poll, llseek, ioctl
config USB_NCM_SUPPORT_MTU_CHANGE
boolean "Samsung NCM MTU Change function"
depends on USB_G_ANDROID
help
Provides NCM MTU Change support
for samsung gadget driver.
If you enable this option,
NCM MTU Change will be able to be used.
config USB_RNDIS_MULTIPACKET
boolean "Samsung Rndis Multi Packet support"
depends on USB_G_ANDROID
help
Rndis Multipacket Support.
config USB_ANDROID_RNDIS_DWORD_ALIGNED
boolean "Use double word aligned"
depends on USB_G_ANDROID
help
Provides dword aligned for DMA controller.
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
endif # USB_GADGET

View file

@ -0,0 +1,16 @@
#
# USB peripheral controller drivers
#
subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y += composite.o functions.o configfs.o u_f.o
g_android-y := android.o
obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
obj-$(CONFIG_USB_G_ANDROID) += g_android.o

2034
drivers/usb/gadget/android.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

197
drivers/usb/gadget/config.c Normal file
View file

@ -0,0 +1,197 @@
/*
* usb/gadget/config.c -- simplify building config descriptors
*
* Copyright (C) 2003 David Brownell
*
* 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, or
* (at your option) any later version.
*/
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/composite.h>
/**
* usb_descriptor_fillbuf - fill buffer with descriptors
* @buf: Buffer to be filled
* @buflen: Size of buf
* @src: Array of descriptor pointers, terminated by null pointer.
*
* Copies descriptors into the buffer, returning the length or a
* negative error code if they can't all be copied. Useful when
* assembling descriptors for an associated set of interfaces used
* as part of configuring a composite device; or in other cases where
* sets of descriptors need to be marshaled.
*/
int
usb_descriptor_fillbuf(void *buf, unsigned buflen,
const struct usb_descriptor_header **src)
{
u8 *dest = buf;
if (!src)
return -EINVAL;
/* fill buffer from src[] until null descriptor ptr */
for (; NULL != *src; src++) {
unsigned len = (*src)->bLength;
if (len > buflen)
return -EINVAL;
memcpy(dest, *src, len);
buflen -= len;
dest += len;
}
return dest - (u8 *)buf;
}
EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
* @config: Header for the descriptor, including characteristics such
* as power requirements and number of interfaces.
* @desc: Null-terminated vector of pointers to the descriptors (interface,
* endpoint, etc) defining all functions in this device configuration.
* @buf: Buffer for the resulting configuration descriptor.
* @length: Length of buffer. If this is not big enough to hold the
* entire configuration descriptor, an error code will be returned.
*
* This copies descriptors into the response buffer, building a descriptor
* for that configuration. It returns the buffer length or a negative
* status code. The config.wTotalLength field is set to match the length
* of the result, but other descriptor fields (including power usage and
* interface count) must be set by the caller.
*
* Gadget drivers could use this when constructing a config descriptor
* in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
* resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
*/
int usb_gadget_config_buf(
const struct usb_config_descriptor *config,
void *buf,
unsigned length,
const struct usb_descriptor_header **desc
)
{
struct usb_config_descriptor *cp = buf;
int len;
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
*cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
length - USB_DT_CONFIG_SIZE, desc);
if (len < 0)
return len;
len += USB_DT_CONFIG_SIZE;
if (len > 0xffff)
return -EINVAL;
/* patch up the config descriptor */
cp->bLength = USB_DT_CONFIG_SIZE;
cp->bDescriptorType = USB_DT_CONFIG;
cp->wTotalLength = cpu_to_le16(len);
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}
EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
/**
* usb_copy_descriptors - copy a vector of USB descriptors
* @src: null-terminated vector to copy
* Context: initialization code, which may sleep
*
* This makes a copy of a vector of USB descriptors. Its primary use
* is to support usb_function objects which can have multiple copies,
* each needing different descriptors. Functions may have static
* tables of descriptors, which are used as templates and customized
* with identifiers (for interfaces, strings, endpoints, and more)
* as needed by a given function instance.
*/
struct usb_descriptor_header **
usb_copy_descriptors(struct usb_descriptor_header **src)
{
struct usb_descriptor_header **tmp;
unsigned bytes;
unsigned n_desc;
void *mem;
struct usb_descriptor_header **ret;
/* count descriptors and their sizes; then add vector size */
for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
bytes += (*tmp)->bLength;
bytes += (n_desc + 1) * sizeof(*tmp);
mem = kmalloc(bytes, GFP_KERNEL);
if (!mem)
return NULL;
/* fill in pointers starting at "tmp",
* to descriptors copied starting at "mem";
* and return "ret"
*/
tmp = mem;
ret = mem;
mem += (n_desc + 1) * sizeof(*tmp);
while (*src) {
memcpy(mem, *src, (*src)->bLength);
*tmp = mem;
tmp++;
mem += (*src)->bLength;
src++;
}
*tmp = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(usb_copy_descriptors);
int usb_assign_descriptors(struct usb_function *f,
struct usb_descriptor_header **fs,
struct usb_descriptor_header **hs,
struct usb_descriptor_header **ss)
{
struct usb_gadget *g = f->config->cdev->gadget;
if (fs) {
f->fs_descriptors = usb_copy_descriptors(fs);
if (!f->fs_descriptors)
goto err;
}
if (hs && gadget_is_dualspeed(g)) {
f->hs_descriptors = usb_copy_descriptors(hs);
if (!f->hs_descriptors)
goto err;
}
if (ss && gadget_is_superspeed(g)) {
f->ss_descriptors = usb_copy_descriptors(ss);
if (!f->ss_descriptors)
goto err;
}
return 0;
err:
usb_free_all_descriptors(f);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(usb_assign_descriptors);
void usb_free_all_descriptors(struct usb_function *f)
{
usb_free_descriptors(f->fs_descriptors);
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->ss_descriptors);
}
EXPORT_SYMBOL_GPL(usb_free_all_descriptors);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
#ifndef USB__GADGET__CONFIGFS__H
#define USB__GADGET__CONFIGFS__H
#include <linux/configfs.h>
void unregister_gadget_item(struct config_item *item);
int usb_os_desc_prepare_interf_dir(struct config_group *parent,
int n_interf,
struct usb_os_desc **desc,
char **names,
struct module *owner);
static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
{
return container_of(to_config_group(item), struct usb_os_desc, group);
}
#endif /* USB__GADGET__CONFIGFS__H */

View file

@ -0,0 +1,379 @@
/*
* epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
*
* Copyright (C) 2004 David Brownell
*
* 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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
*
* - ep1, ep2, ... address is fixed, not direction or type
* - ep1in, ep2out, ... address and direction are fixed, not type
* - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
* - ep1in-bulk, ep2out-iso, ... all three are fixed
* - ep-* ... no functionality restrictions
*
* Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
* Less common restrictions are implied by gadget_is_*().
*
* NOTE: each endpoint is unidirectional, as specified by its USB
* descriptor; and isn't specific to a configuration or altsetting.
*/
static int
ep_matches (
struct usb_gadget *gadget,
struct usb_ep *ep,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
u8 type;
const char *tmp;
u16 max;
int num_req_streams = 0;
/* endpoint already claimed? */
if (NULL != ep->driver_data)
return 0;
/* only support ep0 for portable CONTROL traffic */
type = usb_endpoint_type(desc);
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
/* some other naming convention */
if ('e' != ep->name[0])
return 0;
/* type-restriction: "-iso", "-bulk", or "-int".
* direction-restriction: "in", "out".
*/
if ('-' != ep->name[2]) {
tmp = strrchr (ep->name, '-');
if (tmp) {
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* bulk endpoints handle interrupt transfers,
* except the toggle-quirky iso-synch kind
*/
if ('s' == tmp[2]) // == "-iso"
return 0;
/* for now, avoid PXA "interrupt-in";
* it's documented as never using DATA1.
*/
if (gadget_is_pxa (gadget)
&& 'i' == tmp [1])
return 0;
break;
case USB_ENDPOINT_XFER_BULK:
if ('b' != tmp[1]) // != "-bulk"
return 0;
break;
case USB_ENDPOINT_XFER_ISOC:
if ('s' != tmp[2]) // != "-iso"
return 0;
}
} else {
tmp = ep->name + strlen (ep->name);
}
/* direction-restriction: "..in-..", "out-.." */
tmp--;
if (!isdigit (*tmp)) {
if (desc->bEndpointAddress & USB_DIR_IN) {
if ('n' != *tmp)
return 0;
} else {
if ('t' != *tmp)
return 0;
}
}
}
/*
* Get the number of required streams from the EP companion
* descriptor and see if the EP matches it
*/
if (usb_endpoint_xfer_bulk(desc)) {
if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
num_req_streams = ep_comp->bmAttributes & 0x1f;
if (num_req_streams > ep->max_streams)
return 0;
}
}
/*
* If the protocol driver hasn't yet decided on wMaxPacketSize
* and wants to know the maximum possible, provide the info.
*/
if (desc->wMaxPacketSize == 0)
desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
* the usb spec fixes high speed bulk maxpacket at 512 bytes.
*/
max = 0x7ff & usb_endpoint_maxp(desc);
switch (type) {
case USB_ENDPOINT_XFER_INT:
/* INT: limit 64 bytes full speed, 1024 high/super speed */
if (!gadget_is_dualspeed(gadget) && max > 64)
return 0;
/* FALLTHROUGH */
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
if (ep->maxpacket_limit < max)
return 0;
if (!gadget_is_dualspeed(gadget) && max > 1023)
return 0;
/* BOTH: "high bandwidth" works only at high speed */
if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
if (!gadget_is_dualspeed(gadget))
return 0;
/* configure your hardware with enough buffering!! */
}
break;
}
/* MATCH!! */
/* report address */
desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
} else if (desc->bEndpointAddress & USB_DIR_IN) {
if (++gadget->in_epnum > 15)
return 0;
desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
} else {
if (++gadget->out_epnum > 15)
return 0;
desc->bEndpointAddress |= gadget->out_epnum;
}
/* report (variable) full speed bulk maxpacket */
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
size = 64;
desc->wMaxPacketSize = cpu_to_le16(size);
}
ep->address = desc->bEndpointAddress;
return 1;
}
static struct usb_ep *
find_ep (struct usb_gadget *gadget, const char *name)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (0 == strcmp (ep->name, name))
return ep;
}
return NULL;
}
/**
* usb_ep_autoconfig_ss() - choose an endpoint matching the ep
* descriptor and ep companion descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on
* success.
* @ep_comp: Endpoint companion descriptor, with the required
* number of streams. Will be modified when the chosen EP
* supports a different number of streams.
*
* This routine replaces the usb_ep_autoconfig when needed
* superspeed enhancments. If such enhancemnets are required,
* the FD should call usb_ep_autoconfig_ss directly and provide
* the additional ep_comp parameter.
*
* By choosing an endpoint to use with the specified descriptor,
* this routine simplifies writing gadget drivers that work with
* multiple USB device controllers. The endpoint would be
* passed later to usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration
* on your hardware. This code may not make the best choices
* about how to use the USB controller, and it can't know all
* the restrictions that may apply. Some combinations of driver
* and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed and
* the bmAttribute field in the ep companion descriptor is
* updated with the assigned number of streams if it is
* different from the original value. To prevent the endpoint
* from being returned by a later autoconfig call, claim it by
* assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp
)
{
struct usb_ep *ep;
u8 type;
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
/* ep-e, ep-f are PIO with only 64 byte fifos */
ep = find_ep (gadget, "ep-e");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
ep = find_ep (gadget, "ep-f");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
} else if (gadget_is_goku (gadget)) {
if (USB_ENDPOINT_XFER_INT == type) {
/* single buffering is enough */
ep = find_ep(gadget, "ep3-bulk");
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
} else if (USB_ENDPOINT_XFER_BULK == type
&& (USB_DIR_IN & desc->bEndpointAddress)) {
/* DMA may be available */
ep = find_ep(gadget, "ep2-bulk");
if (ep && ep_matches(gadget, ep, desc,
ep_comp))
goto found_ep;
}
#ifdef CONFIG_BLACKFIN
} else if (gadget_is_musbhdrc(gadget)) {
if ((USB_ENDPOINT_XFER_BULK == type) ||
(USB_ENDPOINT_XFER_ISOC == type)) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep (gadget, "ep5in");
else
ep = find_ep (gadget, "ep6out");
} else if (USB_ENDPOINT_XFER_INT == type) {
if (USB_DIR_IN & desc->bEndpointAddress)
ep = find_ep(gadget, "ep1in");
else
ep = find_ep(gadget, "ep2out");
} else
ep = NULL;
if (ep && ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
#endif
}
/* Second, look at endpoints until an unclaimed one looks usable */
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
if (ep_matches(gadget, ep, desc, ep_comp))
goto found_ep;
}
/* Fail */
return NULL;
found_ep:
ep->desc = NULL;
ep->comp_desc = NULL;
return ep;
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
/**
* usb_ep_autoconfig() - choose an endpoint matching the
* descriptor
* @gadget: The device to which the endpoint must belong.
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
* initialized. For periodic transfers, the maximum packet
* size must also be initialized. This is modified on success.
*
* By choosing an endpoint to use with the specified descriptor, this
* routine simplifies writing gadget drivers that work with multiple
* USB device controllers. The endpoint would be passed later to
* usb_ep_enable(), along with some descriptor.
*
* That second descriptor won't always be the same as the first one.
* For example, isochronous endpoints can be autoconfigured for high
* bandwidth, and then used in several lower bandwidth altsettings.
* Also, high and full speed descriptors will be different.
*
* Be sure to examine and test the results of autoconfiguration on your
* hardware. This code may not make the best choices about how to use the
* USB controller, and it can't know all the restrictions that may apply.
* Some combinations of driver and hardware won't be able to autoconfigure.
*
* On success, this returns an un-claimed usb_ep, and modifies the endpoint
* descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
* is initialized as if the endpoint were used at full speed. To prevent
* the endpoint from being returned by a later autoconfig call, claim it
* by assigning ep->driver_data to some non-null value.
*
* On failure, this returns a null endpoint descriptor.
*/
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
struct usb_endpoint_descriptor *desc
)
{
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
* @gadget: device for which autoconfig state will be reset
*
* Use this for devices where one configuration may need to assign
* endpoint resources very differently from the next one. It clears
* state such as ep->driver_data and the record of assigned endpoints
* used by usb_ep_autoconfig().
*/
void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
gadget->in_epnum = 0;
gadget->out_epnum = 0;
}
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);

View file

@ -0,0 +1,54 @@
#
# USB peripheral controller drivers
#
ccflags-y := -I$(srctree)/drivers/usb/gadget/
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
# USB Functions
usb_f_acm-y := f_acm.o
obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
usb_f_serial-y := f_serial.o
obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
usb_f_obex-y := f_obex.o
obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
obj-$(CONFIG_USB_U_ETHER) += u_ether.o
usb_f_ncm-y := f_ncm.o
obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
usb_f_ecm-y := f_ecm.o
obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
usb_f_phonet-y := f_phonet.o
obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
usb_f_eem-y := f_eem.o
obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
usb_f_ecm_subset-y := f_subset.o
obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
usb_f_rndis-y := f_rndis.o rndis.o
obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
usb_f_mass_storage-y := f_mass_storage.o storage_common.o
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
usb_f_fs-y := f_fs.o
obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
usb_f_uac1-y := f_uac1.o u_uac1.o
obj-$(CONFIG_USB_F_UAC1) += usb_f_uac1.o
usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
usb_f_dm-y := f_dm.o
obj-$(CONFIG_USB_CONFIGFS_F_DM) += usb_f_dm.o
usb_f_adb-y := f_adb.o
obj-$(CONFIG_USB_CONFIGFS_F_ADB)+= usb_f_adb.o
usb_f_mtp-y := f_mtp.o
obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o
usb_f_ptp-y := f_ptp.o
obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o
usb_f_audio_source-y := f_audio_source.o
obj-$(CONFIG_USB_F_AUDIO_SRC) += usb_f_audio_source.o
usb_f_accessory-y := f_accessory.o
obj-$(CONFIG_USB_F_ACC) += usb_f_accessory.o
obj-$(CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE) += multi_config.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,929 @@
/*
* f_acm.c -- USB CDC serial (ACM) function driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
* Copyright (C) 2009 by Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.com)
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include "u_serial.h"
#include "gadget_chips.h"
#ifdef CONFIG_USB_DUN_SUPPORT
#include "serial_acm.c"
#endif
/*
* This CDC ACM function support just wraps control functions and
* notifications around the generic serial-over-usb code.
*
* Because CDC ACM is standardized by the USB-IF, many host operating
* systems have drivers for it. Accordingly, ACM is the preferred
* interop solution for serial-port type connections. The control
* models are often not necessary, and in any case don't do much in
* this bare-bones implementation.
*
* Note that even MS-Windows has some support for ACM. However, that
* support is somewhat broken because when you use ACM in a composite
* device, having multiple interfaces confuses the poor OS. It doesn't
* seem to understand CDC Union descriptors. The new "association"
* descriptors (roughly equivalent to CDC Unions) may sometimes help.
*/
struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
u8 pending;
/* lock is mostly for pending and notify_req ... they get accessed
* by callbacks both from tty (open/close/break) under its spinlock,
* and notify_req.complete() which can't use that lock.
*/
spinlock_t lock;
struct usb_ep *notify;
struct usb_request *notify_req;
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
u16 port_handshake_bits;
#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
u16 serial_state;
#define ACM_CTRL_OVERRUN (1 << 6)
#define ACM_CTRL_PARITY (1 << 5)
#define ACM_CTRL_FRAMING (1 << 4)
#define ACM_CTRL_RI (1 << 3)
#define ACM_CTRL_BRK (1 << 2)
#define ACM_CTRL_DSR (1 << 1)
#define ACM_CTRL_DCD (1 << 0)
};
static inline struct f_acm *func_to_acm(struct usb_function *f)
{
return container_of(f, struct f_acm, port.func);
}
static inline struct f_acm *port_to_acm(struct gserial *p)
{
return container_of(p, struct f_acm, port);
}
/*-------------------------------------------------------------------------*/
/* notification endpoint uses smallish and infrequent fixed-size messages */
#define GS_NOTIFY_INTERVAL_MS 32
#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
/* interface and class descriptors: */
static struct usb_interface_assoc_descriptor
acm_iad_descriptor = {
.bLength = sizeof acm_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
/* .bFirstInterface = DYNAMIC, */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
.bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iFunction = DYNAMIC */
};
static struct usb_interface_descriptor acm_control_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iInterface = DYNAMIC */
};
static struct usb_interface_descriptor acm_data_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc acm_header_desc = {
.bLength = sizeof(acm_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0110),
};
static struct usb_cdc_call_mgmt_descriptor
acm_call_mgmt_descriptor = {
.bLength = sizeof(acm_call_mgmt_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
.bmCapabilities = 0,
/* .bDataInterface = DYNAMIC */
};
static struct usb_cdc_acm_descriptor acm_descriptor = {
.bLength = sizeof(acm_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = USB_CDC_CAP_LINE,
};
static struct usb_cdc_union_desc acm_union_desc = {
.bLength = sizeof(acm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor acm_fs_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = GS_NOTIFY_INTERVAL_MS,
};
static struct usb_endpoint_descriptor acm_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor acm_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *acm_fs_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_fs_notify_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_fs_in_desc,
(struct usb_descriptor_header *) &acm_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor acm_hs_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
};
static struct usb_endpoint_descriptor acm_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor acm_hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *acm_hs_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_hs_notify_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_hs_in_desc,
(struct usb_descriptor_header *) &acm_hs_out_desc,
NULL,
};
static struct usb_endpoint_descriptor acm_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor acm_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
.bLength = sizeof acm_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *acm_ss_function[] = {
(struct usb_descriptor_header *) &acm_iad_descriptor,
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_hs_notify_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_ss_in_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &acm_ss_out_desc,
(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
#define ACM_CTRL_IDX 0
#define ACM_DATA_IDX 1
#define ACM_IAD_IDX 2
/* static strings, in UTF-8 */
static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data",
[ACM_IAD_IDX ].s = "CDC Serial",
{ } /* end of list */
};
static struct usb_gadget_strings acm_string_table = {
.language = 0x0409, /* en-us */
.strings = acm_string_defs,
};
static struct usb_gadget_strings *acm_strings[] = {
&acm_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
/* ACM control ... data handling is delegated to tty library code.
* The main task of this function is to activate and deactivate
* that code based on device state; track parameters like line
* speed, handshake state, and so on; and issue notifications.
*/
static void acm_complete_set_line_coding(struct usb_ep *ep,
struct usb_request *req)
{
struct f_acm *acm = ep->driver_data;
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
if (req->status != 0) {
DBG(cdev, "acm ttyGS%d completion, err %d\n",
acm->port_num, req->status);
return;
}
/* normal completion */
if (req->actual != sizeof(acm->port_line_coding)) {
DBG(cdev, "acm ttyGS%d short resp, len %d\n",
acm->port_num, req->actual);
usb_ep_set_halt(ep);
} else {
struct usb_cdc_line_coding *value = req->buf;
/* REVISIT: we currently just remember this data.
* If we change that, (a) validate it first, then
* (b) update whatever hardware needs updating,
* (c) worry about locking. This is information on
* the order of 9600-8-N-1 ... most of which means
* nothing unless we control a real RS232 line.
*/
acm->port_line_coding = *value;
}
}
static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*
* Note CDC spec table 4 lists the ACM request profile. It requires
* encapsulated command support ... we don't handle any, and respond
* to them by stalling. Options include get/set/clear comm features
* (not that useful) and SEND_BREAK.
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
/* SET_LINE_CODING ... just read and save what the host sends */
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_LINE_CODING:
if (w_length != sizeof(struct usb_cdc_line_coding)
|| w_index != acm->ctrl_id)
goto invalid;
value = w_length;
cdev->gadget->ep0->driver_data = acm;
req->complete = acm_complete_set_line_coding;
break;
/* GET_LINE_CODING ... return what host sent, or initial value */
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_GET_LINE_CODING:
if (w_index != acm->ctrl_id)
goto invalid;
value = min_t(unsigned, w_length,
sizeof(struct usb_cdc_line_coding));
memcpy(req->buf, &acm->port_line_coding, value);
break;
/* SET_CONTROL_LINE_STATE ... save what the host sent */
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
if (w_index != acm->ctrl_id)
goto invalid;
value = 0;
/* FIXME we should not allow data to flow until the
* host sets the ACM_CTRL_DTR bit; and when it clears
* that bit, we should return to that no-flow state.
*/
acm->port_handshake_bits = w_value;
#ifdef CONFIG_USB_DUN_SUPPORT
notify_control_line_state((unsigned long)w_value);
#endif
break;
default:
invalid:
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
acm->port_num, ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "acm response on ttyGS%d, err %d\n",
acm->port_num, value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0, so this is an activation or a reset */
if (intf == acm->ctrl_id) {
if (acm->notify->driver_data) {
VDBG(cdev, "reset acm control interface %d\n", intf);
usb_ep_disable(acm->notify);
}
if (!acm->notify->desc)
if (config_ep_by_speed(cdev->gadget, f, acm->notify))
return -EINVAL;
usb_ep_enable(acm->notify);
acm->notify->driver_data = acm;
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
gserial_disconnect(&acm->port);
}
if (!acm->port.in->desc || !acm->port.out->desc) {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
if (config_ep_by_speed(cdev->gadget, f,
acm->port.in) ||
config_ep_by_speed(cdev->gadget, f,
acm->port.out)) {
acm->port.in->desc = NULL;
acm->port.out->desc = NULL;
return -EINVAL;
}
}
gserial_connect(&acm->port, acm->port_num);
} else
return -EINVAL;
return 0;
}
static void acm_disable(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
gserial_disconnect(&acm->port);
usb_ep_disable(acm->notify);
acm->notify->driver_data = NULL;
}
/*-------------------------------------------------------------------------*/
/**
* acm_cdc_notify - issue CDC notification to host
* @acm: wraps host to be notified
* @type: notification type
* @value: Refer to cdc specs, wValue field.
* @data: data to be sent
* @length: size of data
* Context: irqs blocked, acm->lock held, acm_notify_req non-null
*
* Returns zero on success or a negative errno.
*
* See section 6.3.5 of the CDC 1.1 specification for information
* about the only notification we issue: SerialState change.
*/
static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
void *data, unsigned length)
{
struct usb_ep *ep = acm->notify;
struct usb_request *req;
struct usb_cdc_notification *notify;
const unsigned len = sizeof(*notify) + length;
void *buf;
int status;
req = acm->notify_req;
acm->notify_req = NULL;
acm->pending = false;
req->length = len;
notify = req->buf;
buf = notify + 1;
notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
| USB_RECIP_INTERFACE;
notify->bNotificationType = type;
notify->wValue = cpu_to_le16(value);
notify->wIndex = cpu_to_le16(acm->ctrl_id);
notify->wLength = cpu_to_le16(length);
memcpy(buf, data, length);
/* ep_queue() can complete immediately if it fills the fifo... */
spin_unlock(&acm->lock);
status = usb_ep_queue(ep, req, GFP_ATOMIC);
spin_lock(&acm->lock);
if (status < 0) {
ERROR(acm->port.func.config->cdev,
"acm ttyGS%d can't notify serial state, %d\n",
acm->port_num, status);
acm->notify_req = req;
}
return status;
}
static int acm_notify_serial_state(struct f_acm *acm)
{
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
int status;
unsigned long flags;
spin_lock_irqsave(&acm->lock, flags);
if (acm->notify_req) {
DBG(cdev, "acm ttyGS%d serial state %04x\n",
acm->port_num, acm->serial_state);
status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
0, &acm->serial_state, sizeof(acm->serial_state));
} else {
acm->pending = true;
status = 0;
}
spin_unlock_irqrestore(&acm->lock, flags);
return status;
}
static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_acm *acm = req->context;
u8 doit = false;
unsigned long flags;
/* on this call path we do NOT hold the port spinlock,
* which is why ACM needs its own spinlock
*/
spin_lock_irqsave(&acm->lock, flags);
if (req->status != -ESHUTDOWN)
doit = acm->pending;
acm->notify_req = req;
spin_unlock_irqrestore(&acm->lock, flags);
if (doit)
acm_notify_serial_state(acm);
}
#ifdef CONFIG_USB_DUN_SUPPORT
int acm_notify(void *dev, u16 state)
{
struct f_acm *acm;
if (dev) {
acm = (struct f_acm *)dev;
acm->serial_state = state;
acm_notify_serial_state(acm);
} else {
printk(KERN_DEBUG "usb: %s not ready\n", __func__);
return -EAGAIN;
}
return 0;
}
#endif
/* connect == the TTY link is open */
static void acm_connect(struct gserial *port)
{
struct f_acm *acm = port_to_acm(port);
acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
acm_notify_serial_state(acm);
}
static void acm_disconnect(struct gserial *port)
{
struct f_acm *acm = port_to_acm(port);
acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
acm_notify_serial_state(acm);
}
static int acm_send_break(struct gserial *port, int duration)
{
struct f_acm *acm = port_to_acm(port);
u16 state;
state = acm->serial_state;
state &= ~ACM_CTRL_BRK;
if (duration)
state |= ACM_CTRL_BRK;
acm->serial_state = state;
return acm_notify_serial_state(acm);
}
/*-------------------------------------------------------------------------*/
/* ACM function driver setup/binding */
static int
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_acm *acm = func_to_acm(f);
int status;
struct usb_ep *ep;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string IDs, and patch descriptors */
if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_CTRL_IDX].id = status;
acm_control_interface_desc.iInterface = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_DATA_IDX].id = status;
acm_data_interface_desc.iInterface = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_IAD_IDX].id = status;
acm_iad_descriptor.iFunction = status;
}
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
acm->ctrl_id = status;
acm_iad_descriptor.bFirstInterface = status;
acm_control_interface_desc.bInterfaceNumber = status;
acm_union_desc .bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
acm->data_id = status;
acm_data_interface_desc.bInterfaceNumber = status;
acm_union_desc.bSlaveInterface0 = status;
acm_call_mgmt_descriptor.bDataInterface = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
if (!ep)
goto fail;
acm->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
if (!ep)
goto fail;
acm->port.out = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
if (!ep)
goto fail;
acm->notify = ep;
ep->driver_data = cdev; /* claim */
/* allocate notification */
acm->notify_req = gs_alloc_req(ep,
sizeof(struct usb_cdc_notification) + 2,
GFP_KERNEL);
if (!acm->notify_req)
goto fail;
acm->notify_req->complete = acm_cdc_notify_complete;
acm->notify_req->context = acm;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
acm_hs_notify_desc.bEndpointAddress =
acm_fs_notify_desc.bEndpointAddress;
acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
acm_ss_function);
if (status)
goto fail;
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
acm->port_num,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
acm->port.in->name, acm->port.out->name,
acm->notify->name);
#ifdef CONFIG_USB_DUN_SUPPORT
modem_register(acm);
#endif
return 0;
fail:
if (acm->notify_req)
gs_free_req(acm->notify, acm->notify_req);
/* we might as well release our claims on endpoints */
if (acm->notify)
acm->notify->driver_data = NULL;
if (acm->port.out)
acm->port.out->driver_data = NULL;
if (acm->port.in)
acm->port.in->driver_data = NULL;
ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
return status;
}
static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
/* acm_string_defs[].id is limited to 256
if id is cleared on disconneting, The increased number is allocated on connecting.
ACM driver can't connect to host when id is over 256 */
#ifndef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
acm_string_defs[0].id = 0;
#endif
usb_free_all_descriptors(f);
if (acm->notify_req)
gs_free_req(acm->notify, acm->notify_req);
#ifdef CONFIG_USB_DUN_SUPPORT
modem_unregister();
#endif
}
static void acm_free_func(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
kfree(acm);
}
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
struct f_acm *acm;
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
if (!acm)
return ERR_PTR(-ENOMEM);
spin_lock_init(&acm->lock);
acm->port.connect = acm_connect;
acm->port.disconnect = acm_disconnect;
acm->port.send_break = acm_send_break;
acm->port.func.name = "acm";
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
acm->port.func.disable = acm_disable;
opts = container_of(fi, struct f_serial_opts, func_inst);
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
acm->port.func.name = kasprintf(GFP_KERNEL, "acm%u", opts->port_num);
if (!acm->port.func.name) {
kfree(acm);
return ERR_PTR(-ENOMEM);
}
#else
acm->port.func.name = "acm";
#endif
acm->port_num = opts->port_num;
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
return &acm->port.func;
}
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_serial_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_serial_opts);
static ssize_t f_acm_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
struct f_serial_opts_attribute *f_serial_opts_attr =
container_of(attr, struct f_serial_opts_attribute, attr);
ssize_t ret = 0;
if (f_serial_opts_attr->show)
ret = f_serial_opts_attr->show(opts, page);
return ret;
}
static void acm_attr_release(struct config_item *item)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations acm_item_ops = {
.release = acm_attr_release,
.show_attribute = f_acm_attr_show,
};
static ssize_t f_acm_port_num_show(struct f_serial_opts *opts, char *page)
{
return sprintf(page, "%u\n", opts->port_num);
}
static struct f_serial_opts_attribute f_acm_port_num =
__CONFIGFS_ATTR_RO(port_num, f_acm_port_num_show);
static struct configfs_attribute *acm_attrs[] = {
&f_acm_port_num.attr,
NULL,
};
static struct config_item_type acm_func_type = {
.ct_item_ops = &acm_item_ops,
.ct_attrs = acm_attrs,
.ct_owner = THIS_MODULE,
};
static void acm_free_instance(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
opts = container_of(fi, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
kfree(opts);
}
static struct usb_function_instance *acm_alloc_instance(void)
{
struct f_serial_opts *opts;
int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = acm_free_instance;
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
config_group_init_type_name(&opts->func_inst.group, "",
&acm_func_type);
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
static int __init acm_init(void)
{
#ifdef CONFIG_USB_DUN_SUPPORT
int err;
err = modem_misc_register();
if (err) {
printk(KERN_ERR "usb: %s modem misc register is failed\n",
__func__);
return err;
}
#endif
return usb_function_register(&acmusb_func);
}
static void __exit acm_exit(void)
{
return usb_function_unregister(&acmusb_func);
}
module_init(acm_init);
module_exit(acm_exit);
#endif
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,827 @@
/*
* Gadget Driver for Android ADB
*
* Copyright (C) 2008 Google, Inc.
* Author: Mike Lockwood <lockwood@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/configfs.h>
#include <linux/usb/composite.h>
#include "../configfs.h"
#define ADB_BULK_BUFFER_SIZE 4096
#define MAX_INST_NAME_LEN 40
/* number of tx requests to allocate */
#define TX_REQ_MAX 4
static const char adb_shortname[] = "android_adb";
struct adb_dev {
struct usb_function function;
struct usb_composite_dev *cdev;
spinlock_t lock;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
int online;
int error;
atomic_t read_excl;
atomic_t write_excl;
atomic_t open_excl;
struct list_head tx_idle;
wait_queue_head_t read_wq;
wait_queue_head_t write_wq;
struct usb_request *rx_req;
int rx_done;
};
struct adb_instance {
const char *name;
struct usb_function_instance func_inst;
struct adb_dev *dev;
};
static struct usb_interface_descriptor adb_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0x42,
.bInterfaceProtocol = 1,
};
static struct usb_endpoint_descriptor adb_superspeed_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor adb_superspeed_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor adb_superspeed_bulk_comp_desc = {
.bLength = sizeof adb_superspeed_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_endpoint_descriptor adb_highspeed_in_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),
};
static struct usb_endpoint_descriptor adb_highspeed_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor adb_fullspeed_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor adb_fullspeed_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_adb_descs[] = {
(struct usb_descriptor_header *) &adb_interface_desc,
(struct usb_descriptor_header *) &adb_fullspeed_in_desc,
(struct usb_descriptor_header *) &adb_fullspeed_out_desc,
NULL,
};
static struct usb_descriptor_header *hs_adb_descs[] = {
(struct usb_descriptor_header *) &adb_interface_desc,
(struct usb_descriptor_header *) &adb_highspeed_in_desc,
(struct usb_descriptor_header *) &adb_highspeed_out_desc,
NULL,
};
static struct usb_descriptor_header *ss_adb_descs[] = {
(struct usb_descriptor_header *) &adb_interface_desc,
(struct usb_descriptor_header *) &adb_superspeed_in_desc,
(struct usb_descriptor_header *) &adb_superspeed_bulk_comp_desc,
(struct usb_descriptor_header *) &adb_superspeed_out_desc,
(struct usb_descriptor_header *) &adb_superspeed_bulk_comp_desc,
NULL,
};
#ifndef CONFIG_USB_CONFIGFS_UEVENT
static void adb_ready_callback(void);
static void adb_closed_callback(void);
#endif
/* temporary variable used between adb_open() and adb_gadget_bind() */
static struct adb_dev *_adb_dev;
static inline struct adb_dev *func_to_adb(struct usb_function *f)
{
return container_of(f, struct adb_dev, function);
}
static struct usb_request *adb_request_new(struct usb_ep *ep, int buffer_size)
{
struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!req)
return NULL;
/* now allocate buffers for the requests */
req->buf = kmalloc(buffer_size, GFP_KERNEL);
if (!req->buf) {
usb_ep_free_request(ep, req);
return NULL;
}
return req;
}
static void adb_request_free(struct usb_request *req, struct usb_ep *ep)
{
if (req) {
kfree(req->buf);
usb_ep_free_request(ep, req);
}
}
static inline int adb_lock(atomic_t *excl)
{
if (atomic_inc_return(excl) == 1) {
return 0;
} else {
atomic_dec(excl);
return -1;
}
}
static inline void adb_unlock(atomic_t *excl)
{
atomic_dec(excl);
}
/* add a request to the tail of a list */
void adb_req_put(struct adb_dev *dev, struct list_head *head,
struct usb_request *req)
{
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
list_add_tail(&req->list, head);
spin_unlock_irqrestore(&dev->lock, flags);
}
/* remove a request from the head of a list */
struct usb_request *adb_req_get(struct adb_dev *dev, struct list_head *head)
{
unsigned long flags;
struct usb_request *req;
spin_lock_irqsave(&dev->lock, flags);
if (list_empty(head)) {
req = 0;
} else {
req = list_first_entry(head, struct usb_request, list);
list_del(&req->list);
}
spin_unlock_irqrestore(&dev->lock, flags);
return req;
}
static void adb_complete_in(struct usb_ep *ep, struct usb_request *req)
{
struct adb_dev *dev = _adb_dev;
if (req->status != 0)
dev->error = 1;
adb_req_put(dev, &dev->tx_idle, req);
wake_up(&dev->write_wq);
}
static void adb_complete_out(struct usb_ep *ep, struct usb_request *req)
{
struct adb_dev *dev = _adb_dev;
dev->rx_done = 1;
if (req->status != 0 && req->status != -ECONNRESET)
dev->error = 1;
wake_up(&dev->read_wq);
}
static int adb_create_bulk_endpoints(struct adb_dev *dev,
struct usb_endpoint_descriptor *in_desc,
struct usb_endpoint_descriptor *out_desc)
{
struct usb_composite_dev *cdev = dev->cdev;
struct usb_request *req;
struct usb_ep *ep;
int i;
DBG(cdev, "create_bulk_endpoints dev: %p\n", dev);
ep = usb_ep_autoconfig(cdev->gadget, in_desc);
if (!ep) {
DBG(cdev, "usb_ep_autoconfig for ep_in failed\n");
return -ENODEV;
}
DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name);
ep->driver_data = dev; /* claim the endpoint */
dev->ep_in = ep;
ep = usb_ep_autoconfig(cdev->gadget, out_desc);
if (!ep) {
DBG(cdev, "usb_ep_autoconfig for ep_out failed\n");
return -ENODEV;
}
DBG(cdev, "usb_ep_autoconfig for adb ep_out got %s\n", ep->name);
ep->driver_data = dev; /* claim the endpoint */
dev->ep_out = ep;
/* now allocate requests for our endpoints */
req = adb_request_new(dev->ep_out, ADB_BULK_BUFFER_SIZE);
if (!req)
goto fail;
req->complete = adb_complete_out;
dev->rx_req = req;
for (i = 0; i < TX_REQ_MAX; i++) {
req = adb_request_new(dev->ep_in, ADB_BULK_BUFFER_SIZE);
if (!req)
goto fail;
req->complete = adb_complete_in;
adb_req_put(dev, &dev->tx_idle, req);
}
return 0;
fail:
printk(KERN_ERR "adb_bind() could not allocate requests\n");
return -1;
}
static ssize_t adb_read(struct file *fp, char __user *buf,
size_t count, loff_t *pos)
{
struct adb_dev *dev = fp->private_data;
struct usb_request *req;
int r = count, xfer;
int maxp;
int ret;
pr_debug("adb_read(%d)\n", (int)count);
if (!_adb_dev)
return -ENODEV;
if (adb_lock(&dev->read_excl))
return -EBUSY;
/* we will block until we're online */
while (!(dev->online || dev->error)) {
pr_debug("adb_read: waiting for online state\n");
ret = wait_event_interruptible(dev->read_wq,
(dev->online || dev->error));
if (ret < 0) {
adb_unlock(&dev->read_excl);
return ret;
}
}
if (dev->error) {
r = -EIO;
goto done;
}
maxp = usb_endpoint_maxp(dev->ep_out->desc);
count = round_up(count, maxp);
if (count > ADB_BULK_BUFFER_SIZE)
return -EINVAL;
requeue_req:
/* queue a request */
req = dev->rx_req;
req->length = count;
dev->rx_done = 0;
ret = usb_ep_queue(dev->ep_out, req, GFP_ATOMIC);
if (ret < 0) {
pr_debug("adb_read: failed to queue req %p (%d)\n", req, ret);
r = -EIO;
dev->error = 1;
goto done;
} else {
pr_debug("rx %p queue\n", req);
}
/* wait for a request to complete */
ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
if (ret < 0) {
if (ret != -ERESTARTSYS)
dev->error = 1;
r = ret;
usb_ep_dequeue(dev->ep_out, req);
goto done;
}
if (!dev->error) {
/* If we got a 0-len packet, throw it back and try again. */
if (req->actual == 0)
goto requeue_req;
pr_debug("rx %p %d\n", req, req->actual);
xfer = (req->actual < count) ? req->actual : count;
if (copy_to_user(buf, req->buf, xfer))
r = -EFAULT;
} else
r = -EIO;
done:
adb_unlock(&dev->read_excl);
pr_debug("adb_read returning %d\n", r);
return r;
}
static ssize_t adb_write(struct file *fp, const char __user *buf,
size_t count, loff_t *pos)
{
struct adb_dev *dev = fp->private_data;
struct usb_request *req = 0;
int r = count, xfer;
int ret;
if (!_adb_dev)
return -ENODEV;
pr_debug("adb_write(%d)\n", (int)count);
if (adb_lock(&dev->write_excl))
return -EBUSY;
while (count > 0) {
if (dev->error) {
pr_debug("adb_write dev->error\n");
r = -EIO;
break;
}
/* get an idle tx request to use */
req = 0;
ret = wait_event_interruptible(dev->write_wq,
(req = adb_req_get(dev, &dev->tx_idle)) || dev->error);
if (ret < 0) {
r = ret;
break;
}
if (req != 0) {
if (count > ADB_BULK_BUFFER_SIZE)
xfer = ADB_BULK_BUFFER_SIZE;
else
xfer = count;
if (copy_from_user(req->buf, buf, xfer)) {
r = -EFAULT;
break;
}
req->length = xfer;
ret = usb_ep_queue(dev->ep_in, req, GFP_ATOMIC);
if (ret < 0) {
pr_debug("adb_write: xfer error %d\n", ret);
dev->error = 1;
r = -EIO;
break;
}
buf += xfer;
count -= xfer;
/* zero this so we don't try to free it on error exit */
req = 0;
}
}
if (req)
adb_req_put(dev, &dev->tx_idle, req);
adb_unlock(&dev->write_excl);
pr_debug("adb_write returning %d\n", r);
return r;
}
static int adb_open(struct inode *ip, struct file *fp)
{
pr_info("adb_open\n");
if (!_adb_dev)
return -ENODEV;
if (adb_lock(&_adb_dev->open_excl))
return -EBUSY;
fp->private_data = _adb_dev;
/* clear the error latch */
_adb_dev->error = 0;
#ifndef CONFIG_USB_CONFIGFS_UEVENT
adb_ready_callback();
#endif
return 0;
}
static int adb_release(struct inode *ip, struct file *fp)
{
pr_info("adb_release\n");
#ifndef CONFIG_USB_CONFIGFS_UEVENT
adb_closed_callback();
#endif
adb_unlock(&_adb_dev->open_excl);
return 0;
}
/* file operations for ADB device /dev/android_adb */
static const struct file_operations adb_fops = {
.owner = THIS_MODULE,
.read = adb_read,
.write = adb_write,
.open = adb_open,
.release = adb_release,
};
static struct miscdevice adb_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = adb_shortname,
.fops = &adb_fops,
};
static int
adb_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct adb_dev *dev = func_to_adb(f);
int id;
int ret;
dev->cdev = cdev;
DBG(cdev, "adb_function_bind dev: %p\n", dev);
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
adb_interface_desc.bInterfaceNumber = id;
/* allocate endpoints */
ret = adb_create_bulk_endpoints(dev, &adb_fullspeed_in_desc,
&adb_fullspeed_out_desc);
if (ret)
return ret;
/* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
adb_highspeed_in_desc.bEndpointAddress =
adb_fullspeed_in_desc.bEndpointAddress;
adb_highspeed_out_desc.bEndpointAddress =
adb_fullspeed_out_desc.bEndpointAddress;
}
/* support super speed hardware */
if (gadget_is_superspeed(c->cdev->gadget)) {
adb_superspeed_in_desc.bEndpointAddress =
adb_fullspeed_in_desc.bEndpointAddress;
adb_superspeed_out_desc.bEndpointAddress =
adb_fullspeed_out_desc.bEndpointAddress;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
f->name, dev->ep_in->name, dev->ep_out->name);
return 0;
}
static void
adb_function_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct adb_dev *dev = func_to_adb(f);
struct usb_request *req;
dev->online = 0;
dev->error = 1;
wake_up(&dev->read_wq);
adb_request_free(dev->rx_req, dev->ep_out);
while ((req = adb_req_get(dev, &dev->tx_idle)))
adb_request_free(req, dev->ep_in);
}
static int adb_function_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct adb_dev *dev = func_to_adb(f);
struct usb_composite_dev *cdev = f->config->cdev;
int ret;
DBG(cdev, "adb_function_set_alt intf: %d alt: %d\n", intf, alt);
ret = config_ep_by_speed(cdev->gadget, f, dev->ep_in);
if (ret)
return ret;
ret = usb_ep_enable(dev->ep_in);
if (ret)
return ret;
ret = config_ep_by_speed(cdev->gadget, f, dev->ep_out);
if (ret)
return ret;
ret = usb_ep_enable(dev->ep_out);
if (ret) {
usb_ep_disable(dev->ep_in);
return ret;
}
dev->online = 1;
/* readers may be blocked waiting for us to go online */
wake_up(&dev->read_wq);
return 0;
}
static void adb_function_disable(struct usb_function *f)
{
struct adb_dev *dev = func_to_adb(f);
struct usb_composite_dev *cdev = dev->cdev;
DBG(cdev, "adb_function_disable cdev %p\n", cdev);
dev->online = 0;
dev->error = 1;
usb_ep_disable(dev->ep_in);
usb_ep_disable(dev->ep_out);
/* readers may be blocked waiting for us to go online */
wake_up(&dev->read_wq);
VDBG(cdev, "%s disabled\n", dev->function.name);
}
#ifndef CONFIG_USB_CONFIGFS_UEVENT
static int adb_bind_config(struct usb_configuration *c)
{
struct adb_dev *dev = _adb_dev;
printk(KERN_INFO "adb_bind_config\n");
dev->cdev = c->cdev;
dev->function.name = "adb";
dev->function.fs_descriptors = fs_adb_descs;
dev->function.hs_descriptors = hs_adb_descs;
dev->function.ss_descriptors = ss_adb_descs;
dev->function.bind = adb_function_bind;
dev->function.unbind = adb_function_unbind;
dev->function.set_alt = adb_function_set_alt;
dev->function.disable = adb_function_disable;
return usb_add_function(c, &dev->function);
}
#endif
static int __adb_setup(struct adb_instance *fi_adb)
{
struct adb_dev *dev;
int ret;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
if (fi_adb != NULL)
fi_adb->dev = dev;
spin_lock_init(&dev->lock);
init_waitqueue_head(&dev->read_wq);
init_waitqueue_head(&dev->write_wq);
atomic_set(&dev->open_excl, 0);
atomic_set(&dev->read_excl, 0);
atomic_set(&dev->write_excl, 0);
INIT_LIST_HEAD(&dev->tx_idle);
_adb_dev = dev;
ret = misc_register(&adb_device);
if (ret)
goto err;
return 0;
err:
kfree(dev);
printk(KERN_ERR "adb gadget driver failed to initialize\n");
return ret;
}
static int adb_setup_configfs(struct adb_instance *fi_adb)
{
return __adb_setup(fi_adb);
}
#ifndef CONFIG_USB_CONFIGFS_UEVENT
static int adb_setup(void)
{
return __adb_setup(NULL);
}
#endif
static void adb_cleanup(void)
{
misc_deregister(&adb_device);
kfree(_adb_dev);
_adb_dev = NULL;
}
static struct adb_instance *to_adb_instance(struct config_item *item)
{
return container_of(to_config_group(item), struct adb_instance,
func_inst.group);
}
static void adb_attr_release(struct config_item *item)
{
struct adb_instance *fi_adb = to_adb_instance(item);
usb_put_function_instance(&fi_adb->func_inst);
}
static struct configfs_item_operations adb_item_ops = {
.release = adb_attr_release,
};
static struct config_item_type adb_func_type = {
.ct_item_ops = &adb_item_ops,
.ct_owner = THIS_MODULE,
};
static struct adb_instance *to_fi_adb(struct usb_function_instance *fi)
{
return container_of(fi, struct adb_instance, func_inst);
}
static int adb_set_inst_name(struct usb_function_instance *fi,
const char *name)
{
struct adb_instance *fi_adb;
char *ptr;
int name_len;
name_len = strlen(name) + 1;
if (name_len > MAX_INST_NAME_LEN)
return -ENAMETOOLONG;
ptr = kstrndup(name, name_len, GFP_KERNEL);
if (!ptr)
return -ENOMEM;
fi_adb = to_fi_adb(fi);
fi_adb->name = ptr;
return 0;
}
static void adb_free_inst(struct usb_function_instance *fi)
{
struct adb_instance *fi_adb;
fi_adb = to_fi_adb(fi);
kfree(fi_adb->name);
adb_cleanup();
kfree(fi_adb);
}
struct usb_function_instance *alloc_inst_adb(bool adb_config)
{
struct adb_instance *fi_adb;
int ret = 0;
fi_adb = kzalloc(sizeof(*fi_adb), GFP_KERNEL);
if (!fi_adb)
return ERR_PTR(-ENOMEM);
fi_adb->func_inst.set_inst_name = adb_set_inst_name;
fi_adb->func_inst.free_func_inst = adb_free_inst;
if (adb_config) {
ret = adb_setup_configfs(fi_adb);
if (ret) {
kfree(fi_adb);
pr_err("Error setting adb\n");
return ERR_PTR(ret);
}
} else
fi_adb->dev = _adb_dev;
config_group_init_type_name(&fi_adb->func_inst.group, "",
&adb_func_type);
return &fi_adb->func_inst;
}
EXPORT_SYMBOL_GPL(alloc_inst_adb);
static struct usb_function_instance *adb_alloc_inst(void)
{
return alloc_inst_adb(true);
}
static void adb_free(struct usb_function *f)
{
/*NO-OP: no function specific resource allocation in adb_alloc*/
}
struct usb_function *function_alloc_adb(struct usb_function_instance *fi,
bool adb_config)
{
struct adb_instance *fi_adb = to_fi_adb(fi);
struct adb_dev *dev = fi_adb->dev;
dev->function.name = "adb";
dev->function.fs_descriptors = fs_adb_descs;
dev->function.hs_descriptors = hs_adb_descs;
dev->function.ss_descriptors = ss_adb_descs;
dev->function.bind = adb_function_bind;
dev->function.unbind = adb_function_unbind;
dev->function.set_alt = adb_function_set_alt;
dev->function.disable = adb_function_disable;
dev->function.free_func = adb_free;
return &dev->function;
}
EXPORT_SYMBOL_GPL(function_alloc_adb);
static struct usb_function *adb_alloc(struct usb_function_instance *fi)
{
return function_alloc_adb(fi, true);
}
DECLARE_USB_FUNCTION_INIT(adb, adb_alloc_inst, adb_alloc);
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,43 @@
/*
* Gadget Driver's IOCTL for Android Connectivity Gadget
*
* Copyright (C) 2013 DEVGURU CO.,LTD.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ChangeLog:
* 20140311 - add ioctl to communicate to userland application
*/
#ifndef __CONN_GADGET_IOCTL_DEFINE__
#define __CONN_GADGET_IOCTL_DEFINE__
enum {
CONN_GADGET_IOCTL_BIND_STATUS_UNDEFINED = 0,
CONN_GADGET_IOCTL_BIND_STATUS_BIND = 1,
CONN_GADGET_IOCTL_BIND_STATUS_UNBIND = 2
};
enum {
CONN_GADGET_IOCTL_NR_0 = 0,
CONN_GADGET_IOCTL_NR_1,
CONN_GADGET_IOCTL_NR_2,
CONN_GADGET_IOCTL_NR_MAX
};
#define IOCTL_SUPPORT_LIST_ARRAY_MAX 255
/* ioctl */
#define CONN_GADGET_IOCTL_MAGIC_SIG 's'
#define CONN_GADGET_IOCTL_SUPPORT_LIST _IOR(CONN_GADGET_IOCTL_MAGIC_SIG, CONN_GADGET_IOCTL_NR_0, int*)
#define CONN_GADGET_IOCTL_BIND_WAIT_NOTIFY _IOR(CONN_GADGET_IOCTL_MAGIC_SIG, CONN_GADGET_IOCTL_NR_1, int)
#define CONN_GADGET_IOCTL_BIND_GET_STATUS _IOR(CONN_GADGET_IOCTL_MAGIC_SIG, CONN_GADGET_IOCTL_NR_2, int)
#define CONN_GADGET_IOCTL_MAX_NR CONN_GADGET_IOCTL_NR_MAX
#endif

View file

@ -0,0 +1,351 @@
/*
* f_dm.c - generic USB serial function driver
* modified from f_serial.c and f_diag.c
* ttyGS*
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This function packages a simple "generic serial" port with no real
* control mechanisms, just raw data transfer over two bulk endpoints.
*
* Because it's not standardized, this isn't as interoperable as the
* CDC ACM driver. However, for many purposes it's just as functional
* if you can arrange appropriate host side drivers.
*/
struct dm_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
};
struct f_dm {
struct gserial port;
u8 data_id;
u8 port_num;
struct dm_descs fs;
struct dm_descs hs;
};
static inline struct f_dm *func_to_dm(struct usb_function *f)
{
return container_of(f, struct f_dm, port.func);
}
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
static struct usb_interface_descriptor dm_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0x10,
.bInterfaceProtocol = 0x01,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor dm_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor dm_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *dm_fs_function[] = {
(struct usb_descriptor_header *) &dm_interface_desc,
(struct usb_descriptor_header *) &dm_fs_in_desc,
(struct usb_descriptor_header *) &dm_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor dm_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor dm_hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *dm_hs_function[] = {
(struct usb_descriptor_header *) &dm_interface_desc,
(struct usb_descriptor_header *) &dm_hs_in_desc,
(struct usb_descriptor_header *) &dm_hs_out_desc,
NULL,
};
static struct usb_endpoint_descriptor dm_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor dm_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor dm_ss_bulk_comp_desc = {
.bLength = sizeof dm_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *dm_ss_function[] = {
(struct usb_descriptor_header *) &dm_interface_desc,
(struct usb_descriptor_header *) &dm_ss_in_desc,
(struct usb_descriptor_header *) &dm_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &dm_ss_out_desc,
(struct usb_descriptor_header *) &dm_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
#define F_DM_IDX 0
static struct usb_string dm_string_defs[] = {
[F_DM_IDX].s = "Samsung Android DM",
{ /* ZEROES END LIST */ },
};
static struct usb_gadget_strings dm_string_table = {
.language = 0x0409, /* en-us */
.strings = dm_string_defs,
};
static struct usb_gadget_strings *dm_strings[] = {
&dm_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int dm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_dm *dm = func_to_dm(f);
struct usb_composite_dev *cdev = f->config->cdev;
int status;
/* we know alt == 0, so this is an activation or a reset */
if (dm->port.in->driver_data) {
DBG(cdev, "reset generic ttyGS%d\n", dm->port_num);
gserial_disconnect(&dm->port);
} else {
DBG(cdev, "activate generic ttyGS%d\n", dm->port_num);
}
if (!dm->port.in->desc || !dm->port.out->desc) {
DBG(cdev, "activate dm ttyGS%d\n", dm->port_num);
if (config_ep_by_speed(cdev->gadget, f,
dm->port.in) ||
config_ep_by_speed(cdev->gadget, f,
dm->port.out)) {
dm->port.in->desc = NULL;
dm->port.out->desc = NULL;
return -EINVAL;
}
}
status = gserial_connect(&dm->port, dm->port_num);
printk(KERN_DEBUG "usb: %s run generic_connect(%d)", __func__,
dm->port_num);
if (status < 0) {
printk(KERN_ERR "fail to activate generic ttyGS%d\n",
dm->port_num);
return status;
}
return 0;
}
static void dm_disable(struct usb_function *f)
{
struct f_dm *dm = func_to_dm(f);
printk(KERN_DEBUG "usb: %s generic ttyGS%d deactivated\n", __func__,
dm->port_num);
gserial_disconnect(&dm->port);
}
/*-------------------------------------------------------------------------*/
/* serial function driver setup/binding */
static int
dm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_dm *dm = func_to_dm(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
dm->data_id = status;
dm_interface_desc.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &dm_fs_in_desc);
if (!ep)
goto fail;
dm->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &dm_fs_out_desc);
if (!ep)
goto fail;
dm->port.out = ep;
ep->driver_data = cdev; /* claim */
printk(KERN_INFO "[%s] in =0x%p , out =0x%p\n", __func__,
dm->port.in, dm->port.out);
/* copy descriptors, and track endpoint copies */
f->fs_descriptors = usb_copy_descriptors(dm_fs_function);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
dm_hs_in_desc.bEndpointAddress =
dm_fs_in_desc.bEndpointAddress;
dm_hs_out_desc.bEndpointAddress =
dm_fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(dm_hs_function);
}
if (gadget_is_superspeed(c->cdev->gadget)) {
dm_ss_in_desc.bEndpointAddress =
dm_fs_in_desc.bEndpointAddress;
dm_ss_out_desc.bEndpointAddress =
dm_fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->ss_descriptors = usb_copy_descriptors(dm_ss_function);
if (!f->ss_descriptors)
goto fail;
}
printk(KERN_DEBUG "usb: %s generic ttyGS%d: %s speed IN/%s OUT/%s\n",
__func__,
dm->port_num,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
dm->port.in->name, dm->port.out->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (dm->port.out)
dm->port.out->driver_data = NULL;
if (dm->port.in)
dm->port.in->driver_data = NULL;
printk(KERN_ERR "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
dm_unbind(struct usb_configuration *c, struct usb_function *f)
{
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->fs_descriptors);
kfree(func_to_dm(f));
printk(KERN_DEBUG "usb: %s\n", __func__);
}
/*
* dm_bind_config - add a generic serial function to a configuration
* @c: the configuration to support the serial instance
* @port_num: /dev/ttyGS* port this interface will use
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gserial_setup() with enough ports to
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
int dm_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_dm *dm;
int status;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string ID */
if (dm_string_defs[F_DM_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
dm_string_defs[F_DM_IDX].id = status;
}
/* allocate and initialize one new instance */
dm = kzalloc(sizeof *dm, GFP_KERNEL);
if (!dm)
return -ENOMEM;
dm->port_num = port_num;
dm->port.func.name = "dm";
dm->port.func.strings = dm_strings;
dm->port.func.bind = dm_bind;
dm->port.func.unbind = dm_unbind;
dm->port.func.set_alt = dm_set_alt;
dm->port.func.disable = dm_disable;
status = usb_add_function(c, &dm->port.func);
if (status)
kfree(dm);
return status;
}

View file

@ -0,0 +1,973 @@
/*
* f_ecm.c -- USB CDC Ethernet (ECM) link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_ecm.h"
/*
* This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
* Ethernet link. The data transfer model is simple (packets sent and
* received over bulk endpoints using normal short packet termination),
* and the control model exposes various data and optional notifications.
*
* ECM is well standardized and (except for Microsoft) supported by most
* operating systems with USB host support. It's the preferred interop
* solution for Ethernet over USB, at least for firmware based solutions.
* (Hardware solutions tend to be more minimalist.) A newer and simpler
* "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
*
* Note that ECM requires the use of "alternate settings" for its data
* interface. This means that the set_alt() method has real work to do,
* and also means that a get_alt() method is required.
*/
enum ecm_notify_state {
ECM_NOTIFY_NONE, /* don't notify */
ECM_NOTIFY_CONNECT, /* issue CONNECT next */
ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */
};
struct f_ecm {
struct gether port;
u8 ctrl_id, data_id;
char ethaddr[14];
struct usb_ep *notify;
struct usb_request *notify_req;
u8 notify_state;
bool is_open;
/* FIXME is_open needs some irq-ish locking
* ... possibly the same as port.ioport
*/
};
static inline struct f_ecm *func_to_ecm(struct usb_function *f)
{
return container_of(f, struct f_ecm, port.func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ecm_bitrate(struct usb_gadget *g)
{
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
return 13 * 1024 * 8 * 1000 * 8;
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
/*
* Include the status endpoint if we can, even though it's optional.
*
* Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
* packet, to simplify cancellation; and a big transfer interval, to
* waste less bandwidth.
*
* Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
* if they ignore the connect/disconnect notifications that real aether
* can provide. More advanced cdc configurations might want to support
* encapsulated commands (vendor-specific, using control-OUT).
*/
#define ECM_STATUS_INTERVAL_MS 32
#define ECM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
/* interface descriptor: */
static struct usb_interface_assoc_descriptor
ecm_iad_descriptor = {
.bLength = sizeof ecm_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
/* .bFirstInterface = DYNAMIC, */
.bInterfaceCount = 2, /* control + data */
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bFunctionProtocol = USB_CDC_PROTO_NONE,
/* .iFunction = DYNAMIC */
};
static struct usb_interface_descriptor ecm_control_intf = {
.bLength = sizeof ecm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bInterfaceProtocol = USB_CDC_PROTO_NONE,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc ecm_header_desc = {
.bLength = sizeof ecm_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0110),
};
static struct usb_cdc_union_desc ecm_union_desc = {
.bLength = sizeof(ecm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
static struct usb_cdc_ether_desc ecm_desc = {
.bLength = sizeof ecm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
.wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
/* the default data interface has no endpoints ... */
static struct usb_interface_descriptor ecm_data_nop_intf = {
.bLength = sizeof ecm_data_nop_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* ... but the "real" data interface has two bulk endpoints */
static struct usb_interface_descriptor ecm_data_intf = {
.bLength = sizeof ecm_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_ecm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
.bInterval = ECM_STATUS_INTERVAL_MS,
};
static struct usb_endpoint_descriptor fs_ecm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_ecm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *ecm_fs_function[] = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &fs_ecm_notify_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
(struct usb_descriptor_header *) &fs_ecm_in_desc,
(struct usb_descriptor_header *) &fs_ecm_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_ecm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
.bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
};
static struct usb_endpoint_descriptor hs_ecm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_ecm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *ecm_hs_function[] = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &hs_ecm_notify_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
(struct usb_descriptor_header *) &hs_ecm_in_desc,
(struct usb_descriptor_header *) &hs_ecm_out_desc,
NULL,
};
/* super speed support: */
static struct usb_endpoint_descriptor ss_ecm_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(ECM_STATUS_BYTECOUNT),
.bInterval = USB_MS_TO_HS_INTERVAL(ECM_STATUS_INTERVAL_MS),
};
static struct usb_ss_ep_comp_descriptor ss_ecm_intr_comp_desc = {
.bLength = sizeof ss_ecm_intr_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 3 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
.wBytesPerInterval = cpu_to_le16(ECM_STATUS_BYTECOUNT),
};
static struct usb_endpoint_descriptor ss_ecm_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor ss_ecm_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_ecm_bulk_comp_desc = {
.bLength = sizeof ss_ecm_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_descriptor_header *ecm_ss_function[] = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_iad_descriptor,
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &ecm_header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ecm_desc,
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &ss_ecm_notify_desc,
(struct usb_descriptor_header *) &ss_ecm_intr_comp_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
(struct usb_descriptor_header *) &ss_ecm_in_desc,
(struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
(struct usb_descriptor_header *) &ss_ecm_out_desc,
(struct usb_descriptor_header *) &ss_ecm_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string ecm_string_defs[] = {
[0].s = "CDC Ethernet Control Model (ECM)",
[1].s = "",
[2].s = "CDC Ethernet Data",
[3].s = "CDC ECM",
{ } /* end of list */
};
static struct usb_gadget_strings ecm_string_table = {
.language = 0x0409, /* en-us */
.strings = ecm_string_defs,
};
static struct usb_gadget_strings *ecm_strings[] = {
&ecm_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static void ecm_do_notify(struct f_ecm *ecm)
{
struct usb_request *req = ecm->notify_req;
struct usb_cdc_notification *event;
struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
__le32 *data;
int status;
/* notification already in flight? */
if (!req)
return;
event = req->buf;
switch (ecm->notify_state) {
case ECM_NOTIFY_NONE:
return;
case ECM_NOTIFY_CONNECT:
event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
if (ecm->is_open)
event->wValue = cpu_to_le16(1);
else
event->wValue = cpu_to_le16(0);
event->wLength = 0;
req->length = sizeof *event;
DBG(cdev, "notify connect %s\n",
ecm->is_open ? "true" : "false");
ecm->notify_state = ECM_NOTIFY_SPEED;
break;
case ECM_NOTIFY_SPEED:
event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
event->wValue = cpu_to_le16(0);
event->wLength = cpu_to_le16(8);
req->length = ECM_STATUS_BYTECOUNT;
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof *event;
data[0] = cpu_to_le32(ecm_bitrate(cdev->gadget));
data[1] = data[0];
DBG(cdev, "notify speed %d\n", ecm_bitrate(cdev->gadget));
ecm->notify_state = ECM_NOTIFY_NONE;
break;
}
event->bmRequestType = 0xA1;
event->wIndex = cpu_to_le16(ecm->ctrl_id);
ecm->notify_req = NULL;
status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
if (status < 0) {
ecm->notify_req = req;
DBG(cdev, "notify --> %d\n", status);
}
}
static void ecm_notify(struct f_ecm *ecm)
{
/* NOTE on most versions of Linux, host side cdc-ethernet
* won't listen for notifications until its netdevice opens.
* The first notification then sits in the FIFO for a long
* time, and the second one is queued.
*/
ecm->notify_state = ECM_NOTIFY_CONNECT;
ecm_do_notify(ecm);
}
static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_ecm *ecm = req->context;
struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
struct usb_cdc_notification *event = req->buf;
switch (req->status) {
case 0:
/* no fault */
break;
case -ECONNRESET:
case -ESHUTDOWN:
ecm->notify_state = ECM_NOTIFY_NONE;
break;
default:
DBG(cdev, "event %02x --> %d\n",
event->bNotificationType, req->status);
break;
}
ecm->notify_req = req;
ecm_do_notify(ecm);
}
static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SET_ETHERNET_PACKET_FILTER:
/* see 6.2.30: no data, wIndex = interface,
* wValue = packet filter bitmap
*/
if (w_length != 0 || w_index != ecm->ctrl_id)
goto invalid;
DBG(cdev, "packet filter %02x\n", w_value);
/* REVISIT locking of cdc_filter. This assumes the UDC
* driver won't have a concurrent packet TX irq running on
* another CPU; or that if it does, this write is atomic...
*/
ecm->port.cdc_filter = w_value;
value = 0;
break;
/* and optionally:
* case USB_CDC_SEND_ENCAPSULATED_COMMAND:
* case USB_CDC_GET_ENCAPSULATED_RESPONSE:
* case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
* case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
* case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
* case USB_CDC_GET_ETHERNET_STATISTIC:
*/
default:
invalid:
DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "ecm req %02x.%02x response err %d\n",
ctrl->bRequestType, ctrl->bRequest,
value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
if (intf == ecm->ctrl_id) {
if (alt != 0)
goto fail;
if (ecm->notify->driver_data) {
VDBG(cdev, "reset ecm control %d\n", intf);
usb_ep_disable(ecm->notify);
}
if (!(ecm->notify->desc)) {
VDBG(cdev, "init ecm ctrl %d\n", intf);
if (config_ep_by_speed(cdev->gadget, f, ecm->notify))
goto fail;
}
usb_ep_enable(ecm->notify);
ecm->notify->driver_data = ecm;
/* Data interface has two altsettings, 0 and 1 */
} else if (intf == ecm->data_id) {
if (alt > 1)
goto fail;
if (ecm->port.in_ep->driver_data) {
DBG(cdev, "reset ecm\n");
gether_disconnect(&ecm->port);
}
if (!ecm->port.in_ep->desc ||
!ecm->port.out_ep->desc) {
DBG(cdev, "init ecm\n");
if (config_ep_by_speed(cdev->gadget, f,
ecm->port.in_ep) ||
config_ep_by_speed(cdev->gadget, f,
ecm->port.out_ep)) {
ecm->port.in_ep->desc = NULL;
ecm->port.out_ep->desc = NULL;
goto fail;
}
}
/* CDC Ethernet only sends data in non-default altsettings.
* Changing altsettings resets filters, statistics, etc.
*/
if (alt == 1) {
struct net_device *net;
/* Enable zlps by default for ECM conformance;
* override for musb_hdrc (avoids txdma ovhead).
*/
ecm->port.is_zlp_ok = !(gadget_is_musbhdrc(cdev->gadget)
);
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
net = gether_connect(&ecm->port);
if (IS_ERR(net))
return PTR_ERR(net);
}
/* NOTE this can be a minor disagreement with the ECM spec,
* which says speed notifications will "always" follow
* connection notifications. But we allow one connect to
* follow another (if the first is in flight), and instead
* just guarantee that a speed notification is always sent.
*/
ecm_notify(ecm);
} else
goto fail;
return 0;
fail:
return -EINVAL;
}
/* Because the data interface supports multiple altsettings,
* this ECM function *MUST* implement a get_alt() method.
*/
static int ecm_get_alt(struct usb_function *f, unsigned intf)
{
struct f_ecm *ecm = func_to_ecm(f);
if (intf == ecm->ctrl_id)
return 0;
return ecm->port.in_ep->driver_data ? 1 : 0;
}
static void ecm_disable(struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ecm deactivated\n");
if (ecm->port.in_ep->driver_data)
gether_disconnect(&ecm->port);
if (ecm->notify->driver_data) {
usb_ep_disable(ecm->notify);
ecm->notify->driver_data = NULL;
ecm->notify->desc = NULL;
}
}
/*-------------------------------------------------------------------------*/
/*
* Callbacks let us notify the host about connect/disconnect when the
* net device is opened or closed.
*
* For testing, note that link states on this side include both opened
* and closed variants of:
*
* - disconnected/unconfigured
* - configured but inactive (data alt 0)
* - configured and active (data alt 1)
*
* Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
* SET_INTERFACE (altsetting). Remember also that "configured" doesn't
* imply the host is actually polling the notification endpoint, and
* likewise that "active" doesn't imply it's actually using the data
* endpoints for traffic.
*/
static void ecm_open(struct gether *geth)
{
struct f_ecm *ecm = func_to_ecm(&geth->func);
DBG(ecm->port.func.config->cdev, "%s\n", __func__);
ecm->is_open = true;
ecm_notify(ecm);
}
static void ecm_close(struct gether *geth)
{
struct f_ecm *ecm = func_to_ecm(&geth->func);
DBG(ecm->port.func.config->cdev, "%s\n", __func__);
ecm->is_open = false;
ecm_notify(ecm);
}
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
static int
ecm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ecm *ecm = func_to_ecm(f);
struct usb_string *us;
int status;
struct usb_ep *ep;
struct f_ecm_opts *ecm_opts;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
/*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry,
* in each configuration its functions are bound in sequence
* with list_for_each_entry, so we assume no race condition
* with regard to ecm_opts->bound access
*/
if (!ecm_opts->bound) {
mutex_lock(&ecm_opts->lock);
gether_set_gadget(ecm_opts->net, cdev->gadget);
status = gether_register_netdev(ecm_opts->net);
mutex_unlock(&ecm_opts->lock);
if (status)
return status;
ecm_opts->bound = true;
}
us = usb_gstrings_attach(cdev, ecm_strings,
ARRAY_SIZE(ecm_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
ecm_control_intf.iInterface = us[0].id;
ecm_data_intf.iInterface = us[2].id;
ecm_desc.iMACAddress = us[1].id;
ecm_iad_descriptor.iFunction = us[3].id;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
ecm->ctrl_id = status;
ecm_iad_descriptor.bFirstInterface = status;
ecm_control_intf.bInterfaceNumber = status;
ecm_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
ecm->data_id = status;
ecm_data_nop_intf.bInterfaceNumber = status;
ecm_data_intf.bInterfaceNumber = status;
ecm_union_desc.bSlaveInterface0 = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_in_desc);
if (!ep)
goto fail;
ecm->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc);
if (!ep)
goto fail;
ecm->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is *OPTIONAL* but we
* don't treat it that way. It's simpler, and some newer CDC
* profiles (wireless handsets) no longer treat it as optional.
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_notify_desc);
if (!ep)
goto fail;
ecm->notify = ep;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* allocate notification request and buffer */
ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!ecm->notify_req)
goto fail;
ecm->notify_req->buf = kmalloc(ECM_STATUS_BYTECOUNT, GFP_KERNEL);
if (!ecm->notify_req->buf)
goto fail;
ecm->notify_req->context = ecm;
ecm->notify_req->complete = ecm_notify_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
hs_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
hs_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
hs_ecm_notify_desc.bEndpointAddress =
fs_ecm_notify_desc.bEndpointAddress;
ss_ecm_in_desc.bEndpointAddress = fs_ecm_in_desc.bEndpointAddress;
ss_ecm_out_desc.bEndpointAddress = fs_ecm_out_desc.bEndpointAddress;
ss_ecm_notify_desc.bEndpointAddress =
fs_ecm_notify_desc.bEndpointAddress;
status = usb_assign_descriptors(f, ecm_fs_function, ecm_hs_function,
ecm_ss_function);
if (status)
goto fail;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
ecm->port.open = ecm_open;
ecm->port.close = ecm_close;
DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
return 0;
fail:
if (ecm->notify_req) {
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
}
/* we might as well release our claims on endpoints */
if (ecm->notify)
ecm->notify->driver_data = NULL;
if (ecm->port.out_ep)
ecm->port.out_ep->driver_data = NULL;
if (ecm->port.in_ep)
ecm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_ecm_opts,
func_inst.group);
}
/* f_ecm_item_ops */
USB_ETHERNET_CONFIGFS_ITEM(ecm);
/* f_ecm_opts_dev_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
/* f_ecm_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
/* f_ecm_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
/* f_ecm_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
static struct configfs_attribute *ecm_attrs[] = {
&f_ecm_opts_dev_addr.attr,
&f_ecm_opts_host_addr.attr,
&f_ecm_opts_qmult.attr,
&f_ecm_opts_ifname.attr,
NULL,
};
static struct config_item_type ecm_func_type = {
.ct_item_ops = &ecm_item_ops,
.ct_attrs = ecm_attrs,
.ct_owner = THIS_MODULE,
};
static void ecm_free_inst(struct usb_function_instance *f)
{
struct f_ecm_opts *opts;
opts = container_of(f, struct f_ecm_opts, func_inst);
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
kfree(opts);
}
static struct usb_function_instance *ecm_alloc_inst(void)
{
struct f_ecm_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ecm_free_inst;
opts->net = gether_setup_default();
if (IS_ERR(opts->net)) {
struct net_device *net = opts->net;
kfree(opts);
return ERR_CAST(net);
}
config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
return &opts->func_inst;
}
static void ecm_free(struct usb_function *f)
{
struct f_ecm *ecm;
struct f_ecm_opts *opts;
ecm = func_to_ecm(f);
opts = container_of(f->fi, struct f_ecm_opts, func_inst);
kfree(ecm);
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
}
static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
DBG(c->cdev, "ecm unbind\n");
usb_free_all_descriptors(f);
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
}
static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
{
struct f_ecm *ecm;
struct f_ecm_opts *opts;
int status;
/* allocate and initialize one new instance */
ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
if (!ecm)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ecm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
sizeof(ecm->ethaddr));
if (status < 12) {
kfree(ecm);
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
ecm_string_defs[1].s = ecm->ethaddr;
ecm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ecm->port.cdc_filter = DEFAULT_FILTER;
ecm->port.func.name = "cdc_ethernet";
/* descriptors are per-instance copies */
ecm->port.func.bind = ecm_bind;
ecm->port.func.unbind = ecm_unbind;
ecm->port.func.set_alt = ecm_set_alt;
ecm->port.func.get_alt = ecm_get_alt;
ecm->port.func.setup = ecm_setup;
ecm->port.func.disable = ecm_disable;
ecm->port.func.free_func = ecm_free;
return &ecm->port.func;
}
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View file

@ -0,0 +1,659 @@
/*
* f_eem.c -- USB CDC Ethernet (EEM) link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 EF Johnson Technologies
*
* 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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <linux/slab.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_eem.h"
#define EEM_HLEN 2
/*
* This function is a "CDC Ethernet Emulation Model" (CDC EEM)
* Ethernet link.
*/
struct f_eem {
struct gether port;
u8 ctrl_id;
};
static inline struct f_eem *func_to_eem(struct usb_function *f)
{
return container_of(f, struct f_eem, port.func);
}
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
static struct usb_interface_descriptor eem_intf = {
.bLength = sizeof eem_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_EEM,
.bInterfaceProtocol = USB_CDC_PROTO_EEM,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor eem_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor eem_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *eem_fs_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_fs_in_desc,
(struct usb_descriptor_header *) &eem_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor eem_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor eem_hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *eem_hs_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_hs_in_desc,
(struct usb_descriptor_header *) &eem_hs_out_desc,
NULL,
};
/* super speed support: */
static struct usb_endpoint_descriptor eem_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor eem_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
.bLength = sizeof eem_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_descriptor_header *eem_ss_function[] = {
/* CDC EEM control descriptors */
(struct usb_descriptor_header *) &eem_intf,
(struct usb_descriptor_header *) &eem_ss_in_desc,
(struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &eem_ss_out_desc,
(struct usb_descriptor_header *) &eem_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string eem_string_defs[] = {
[0].s = "CDC Ethernet Emulation Model (EEM)",
{ } /* end of list */
};
static struct usb_gadget_strings eem_string_table = {
.language = 0x0409, /* en-us */
.strings = eem_string_defs,
};
static struct usb_gadget_strings *eem_strings[] = {
&eem_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int eem_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
/* device either stalls (value < 0) or reports success */
return value;
}
static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_eem *eem = func_to_eem(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct net_device *net;
/* we know alt == 0, so this is an activation or a reset */
if (alt != 0)
goto fail;
if (intf == eem->ctrl_id) {
if (eem->port.in_ep->driver_data) {
DBG(cdev, "reset eem\n");
gether_disconnect(&eem->port);
}
if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) {
DBG(cdev, "init eem\n");
if (config_ep_by_speed(cdev->gadget, f,
eem->port.in_ep) ||
config_ep_by_speed(cdev->gadget, f,
eem->port.out_ep)) {
eem->port.in_ep->desc = NULL;
eem->port.out_ep->desc = NULL;
goto fail;
}
}
/* zlps should not occur because zero-length EEM packets
* will be inserted in those cases where they would occur
*/
eem->port.is_zlp_ok = 1;
eem->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate eem\n");
net = gether_connect(&eem->port);
if (IS_ERR(net))
return PTR_ERR(net);
} else
goto fail;
return 0;
fail:
return -EINVAL;
}
static void eem_disable(struct usb_function *f)
{
struct f_eem *eem = func_to_eem(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "eem deactivated\n");
if (eem->port.in_ep->driver_data)
gether_disconnect(&eem->port);
}
/*-------------------------------------------------------------------------*/
/* EEM function driver setup/binding */
static int eem_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_eem *eem = func_to_eem(f);
struct usb_string *us;
int status;
struct usb_ep *ep;
struct f_eem_opts *eem_opts;
eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
/*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry,
* in each configuration its functions are bound in sequence
* with list_for_each_entry, so we assume no race condition
* with regard to eem_opts->bound access
*/
if (!eem_opts->bound) {
mutex_lock(&eem_opts->lock);
gether_set_gadget(eem_opts->net, cdev->gadget);
status = gether_register_netdev(eem_opts->net);
mutex_unlock(&eem_opts->lock);
if (status)
return status;
eem_opts->bound = true;
}
us = usb_gstrings_attach(cdev, eem_strings,
ARRAY_SIZE(eem_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
eem_intf.iInterface = us[0].id;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
eem->ctrl_id = status;
eem_intf.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_in_desc);
if (!ep)
goto fail;
eem->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc);
if (!ep)
goto fail;
eem->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
eem_hs_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
eem_hs_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
eem_ss_in_desc.bEndpointAddress = eem_fs_in_desc.bEndpointAddress;
eem_ss_out_desc.bEndpointAddress = eem_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, eem_fs_function, eem_hs_function,
eem_ss_function);
if (status)
goto fail;
DBG(cdev, "CDC Ethernet (EEM): %s speed IN/%s OUT/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
eem->port.in_ep->name, eem->port.out_ep->name);
return 0;
fail:
if (eem->port.out_ep)
eem->port.out_ep->driver_data = NULL;
if (eem->port.in_ep)
eem->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
struct sk_buff *skb = (struct sk_buff *)req->context;
dev_kfree_skb_any(skb);
}
/*
* Add the EEM header and ethernet checksum.
* We currently do not attempt to put multiple ethernet frames
* into a single USB transfer
*/
static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
{
struct sk_buff *skb2 = NULL;
struct usb_ep *in = port->in_ep;
int padlen = 0;
u16 len = skb->len;
int headroom = skb_headroom(skb);
int tailroom = skb_tailroom(skb);
/* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
* stick two bytes of zero-length EEM packet on the end.
*/
if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
padlen += 2;
if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
(headroom >= EEM_HLEN) && !skb_cloned(skb))
goto done;
skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
dev_kfree_skb_any(skb);
skb = skb2;
if (!skb)
return skb;
done:
/* use the "no CRC" option */
put_unaligned_be32(0xdeadbeef, skb_put(skb, 4));
/* EEM packet header format:
* b0..13: length of ethernet frame
* b14: bmCRC (0 == sentinel CRC)
* b15: bmType (0 == data)
*/
len = skb->len;
put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
/* add a zero-length EEM packet, if needed */
if (padlen)
put_unaligned_le16(0, skb_put(skb, 2));
return skb;
}
/*
* Remove the EEM header. Note that there can be many EEM packets in a single
* USB transfer, so we need to break them out and handle them independently.
*/
static int eem_unwrap(struct gether *port,
struct sk_buff *skb,
struct sk_buff_head *list)
{
struct usb_composite_dev *cdev = port->func.config->cdev;
int status = 0;
do {
struct sk_buff *skb2;
u16 header;
u16 len = 0;
if (skb->len < EEM_HLEN) {
status = -EINVAL;
DBG(cdev, "invalid EEM header\n");
goto error;
}
/* remove the EEM header */
header = get_unaligned_le16(skb->data);
skb_pull(skb, EEM_HLEN);
/* EEM packet header format:
* b0..14: EEM type dependent (data or command)
* b15: bmType (0 == data, 1 == command)
*/
if (header & BIT(15)) {
struct usb_request *req = cdev->req;
u16 bmEEMCmd;
/* EEM command packet format:
* b0..10: bmEEMCmdParam
* b11..13: bmEEMCmd
* b14: reserved (must be zero)
* b15: bmType (1 == command)
*/
if (header & BIT(14))
continue;
bmEEMCmd = (header >> 11) & 0x7;
switch (bmEEMCmd) {
case 0: /* echo */
len = header & 0x7FF;
if (skb->len < len) {
status = -EOVERFLOW;
goto error;
}
skb2 = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!skb2)) {
DBG(cdev, "EEM echo response error\n");
goto next;
}
skb_trim(skb2, len);
put_unaligned_le16(BIT(15) | BIT(11) | len,
skb_push(skb2, 2));
skb_copy_bits(skb2, 0, req->buf, skb2->len);
req->length = skb2->len;
req->complete = eem_cmd_complete;
req->zero = 1;
req->context = skb2;
if (usb_ep_queue(port->in_ep, req, GFP_ATOMIC))
DBG(cdev, "echo response queue fail\n");
break;
case 1: /* echo response */
case 2: /* suspend hint */
case 3: /* response hint */
case 4: /* response complete hint */
case 5: /* tickle */
default: /* reserved */
continue;
}
} else {
u32 crc, crc2;
struct sk_buff *skb3;
/* check for zero-length EEM packet */
if (header == 0)
continue;
/* EEM data packet format:
* b0..13: length of ethernet frame
* b14: bmCRC (0 == sentinel, 1 == calculated)
* b15: bmType (0 == data)
*/
len = header & 0x3FFF;
if ((skb->len < len)
|| (len < (ETH_HLEN + ETH_FCS_LEN))) {
status = -EINVAL;
goto error;
}
/* validate CRC */
if (header & BIT(14)) {
crc = get_unaligned_le32(skb->data + len
- ETH_FCS_LEN);
crc2 = ~crc32_le(~0,
skb->data, len - ETH_FCS_LEN);
} else {
crc = get_unaligned_be32(skb->data + len
- ETH_FCS_LEN);
crc2 = 0xdeadbeef;
}
if (crc != crc2) {
DBG(cdev, "invalid EEM CRC\n");
goto next;
}
skb2 = skb_clone(skb, GFP_ATOMIC);
if (unlikely(!skb2)) {
DBG(cdev, "unable to unframe EEM packet\n");
continue;
}
skb_trim(skb2, len - ETH_FCS_LEN);
skb3 = skb_copy_expand(skb2,
NET_IP_ALIGN,
0,
GFP_ATOMIC);
if (unlikely(!skb3)) {
DBG(cdev, "unable to realign EEM packet\n");
dev_kfree_skb_any(skb2);
continue;
}
dev_kfree_skb_any(skb2);
skb_queue_tail(list, skb3);
}
next:
skb_pull(skb, len);
} while (skb->len);
error:
dev_kfree_skb_any(skb);
return status;
}
static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_eem_opts,
func_inst.group);
}
/* f_eem_item_ops */
USB_ETHERNET_CONFIGFS_ITEM(eem);
/* f_eem_opts_dev_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
/* f_eem_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
/* f_eem_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
/* f_eem_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
static struct configfs_attribute *eem_attrs[] = {
&f_eem_opts_dev_addr.attr,
&f_eem_opts_host_addr.attr,
&f_eem_opts_qmult.attr,
&f_eem_opts_ifname.attr,
NULL,
};
static struct config_item_type eem_func_type = {
.ct_item_ops = &eem_item_ops,
.ct_attrs = eem_attrs,
.ct_owner = THIS_MODULE,
};
static void eem_free_inst(struct usb_function_instance *f)
{
struct f_eem_opts *opts;
opts = container_of(f, struct f_eem_opts, func_inst);
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
kfree(opts);
}
static struct usb_function_instance *eem_alloc_inst(void)
{
struct f_eem_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = eem_free_inst;
opts->net = gether_setup_default();
if (IS_ERR(opts->net)) {
struct net_device *net = opts->net;
kfree(opts);
return ERR_CAST(net);
}
config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
return &opts->func_inst;
}
static void eem_free(struct usb_function *f)
{
struct f_eem *eem;
struct f_eem_opts *opts;
eem = func_to_eem(f);
opts = container_of(f->fi, struct f_eem_opts, func_inst);
kfree(eem);
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
}
static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
{
DBG(c->cdev, "eem unbind\n");
usb_free_all_descriptors(f);
}
static struct usb_function *eem_alloc(struct usb_function_instance *fi)
{
struct f_eem *eem;
struct f_eem_opts *opts;
/* allocate and initialize one new instance */
eem = kzalloc(sizeof(*eem), GFP_KERNEL);
if (!eem)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_eem_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
eem->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
eem->port.cdc_filter = DEFAULT_FILTER;
eem->port.func.name = "cdc_eem";
/* descriptors are per-instance copies */
eem->port.func.bind = eem_bind;
eem->port.func.unbind = eem_unbind;
eem->port.func.set_alt = eem_set_alt;
eem->port.func.setup = eem_setup;
eem->port.func.disable = eem_disable;
eem->port.func.free_func = eem_free;
eem->port.wrap = eem_wrap;
eem->port.unwrap = eem_unwrap;
eem->port.header_len = EEM_HLEN;
return &eem->port.func;
}
DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,764 @@
/*
* f_hid.c -- USB HID function driver
*
* Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/usb/g_hid.h>
#include "u_f.h"
static int major, minors;
static struct class *hidg_class;
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
struct f_hidg_req_list {
struct usb_request *req;
unsigned int pos;
struct list_head list;
};
struct f_hidg {
/* configuration */
unsigned char bInterfaceSubClass;
unsigned char bInterfaceProtocol;
unsigned short report_desc_length;
char *report_desc;
unsigned short report_length;
/* recv report */
struct list_head completed_out_req;
spinlock_t spinlock;
wait_queue_head_t read_queue;
unsigned int qlen;
/* send report */
struct mutex lock;
bool write_pending;
wait_queue_head_t write_queue;
struct usb_request *req;
int minor;
struct cdev cdev;
struct usb_function func;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
};
static inline struct f_hidg *func_to_hidg(struct usb_function *f)
{
return container_of(f, struct f_hidg, func);
}
/*-------------------------------------------------------------------------*/
/* Static descriptors */
static struct usb_interface_descriptor hidg_interface_desc = {
.bLength = sizeof hidg_interface_desc,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
/* .bInterfaceSubClass = DYNAMIC */
/* .bInterfaceProtocol = DYNAMIC */
/* .iInterface = DYNAMIC */
};
static struct hid_descriptor hidg_desc = {
.bLength = sizeof hidg_desc,
.bDescriptorType = HID_DT_HID,
.bcdHID = 0x0101,
.bCountryCode = 0x00,
.bNumDescriptors = 0x1,
/*.desc[0].bDescriptorType = DYNAMIC */
/*.desc[0].wDescriptorLenght = DYNAMIC */
};
/* High-Speed Support */
static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
.bInterval = 4, /* FIXME: Add this field in the
* HID gadget configuration?
* (struct hidg_func_descriptor)
*/
};
static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
.bInterval = 4, /* FIXME: Add this field in the
* HID gadget configuration?
* (struct hidg_func_descriptor)
*/
};
static struct usb_descriptor_header *hidg_hs_descriptors[] = {
(struct usb_descriptor_header *)&hidg_interface_desc,
(struct usb_descriptor_header *)&hidg_desc,
(struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
(struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
NULL,
};
/* Full-Speed Support */
static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
.bInterval = 10, /* FIXME: Add this field in the
* HID gadget configuration?
* (struct hidg_func_descriptor)
*/
};
static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_INT,
/*.wMaxPacketSize = DYNAMIC */
.bInterval = 10, /* FIXME: Add this field in the
* HID gadget configuration?
* (struct hidg_func_descriptor)
*/
};
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
(struct usb_descriptor_header *)&hidg_interface_desc,
(struct usb_descriptor_header *)&hidg_desc,
(struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
(struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
NULL,
};
/*-------------------------------------------------------------------------*/
/* Char Device */
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
size_t count, loff_t *ptr)
{
struct f_hidg *hidg = file->private_data;
struct f_hidg_req_list *list;
struct usb_request *req;
unsigned long flags;
int ret;
if (!count)
return 0;
if (!access_ok(VERIFY_WRITE, buffer, count))
return -EFAULT;
spin_lock_irqsave(&hidg->spinlock, flags);
#define READ_COND (!list_empty(&hidg->completed_out_req))
/* wait for at least one buffer to complete */
while (!READ_COND) {
spin_unlock_irqrestore(&hidg->spinlock, flags);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible(hidg->read_queue, READ_COND))
return -ERESTARTSYS;
spin_lock_irqsave(&hidg->spinlock, flags);
}
/* pick the first one */
list = list_first_entry(&hidg->completed_out_req,
struct f_hidg_req_list, list);
req = list->req;
count = min_t(unsigned int, count, req->actual - list->pos);
spin_unlock_irqrestore(&hidg->spinlock, flags);
/* copy to user outside spinlock */
count -= copy_to_user(buffer, req->buf + list->pos, count);
list->pos += count;
/*
* if this request is completely handled and transfered to
* userspace, remove its entry from the list and requeue it
* again. Otherwise, we will revisit it again upon the next
* call, taking into account its current read position.
*/
if (list->pos == req->actual) {
spin_lock_irqsave(&hidg->spinlock, flags);
list_del(&list->list);
kfree(list);
spin_unlock_irqrestore(&hidg->spinlock, flags);
req->length = hidg->report_length;
ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL);
if (ret < 0)
return ret;
}
return count;
}
static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_hidg *hidg = (struct f_hidg *)ep->driver_data;
if (req->status != 0) {
ERROR(hidg->func.config->cdev,
"End Point Request ERROR: %d\n", req->status);
}
hidg->write_pending = 0;
wake_up(&hidg->write_queue);
}
static ssize_t f_hidg_write(struct file *file, const char __user *buffer,
size_t count, loff_t *offp)
{
struct f_hidg *hidg = file->private_data;
ssize_t status = -ENOMEM;
if (!access_ok(VERIFY_READ, buffer, count))
return -EFAULT;
mutex_lock(&hidg->lock);
#define WRITE_COND (!hidg->write_pending)
/* write queue */
while (!WRITE_COND) {
mutex_unlock(&hidg->lock);
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
if (wait_event_interruptible_exclusive(
hidg->write_queue, WRITE_COND))
return -ERESTARTSYS;
mutex_lock(&hidg->lock);
}
count = min_t(unsigned, count, hidg->report_length);
status = copy_from_user(hidg->req->buf, buffer, count);
if (status != 0) {
ERROR(hidg->func.config->cdev,
"copy_from_user error\n");
mutex_unlock(&hidg->lock);
return -EINVAL;
}
hidg->req->status = 0;
hidg->req->zero = 0;
hidg->req->length = count;
hidg->req->complete = f_hidg_req_complete;
hidg->req->context = hidg;
hidg->write_pending = 1;
status = usb_ep_queue(hidg->in_ep, hidg->req, GFP_ATOMIC);
if (status < 0) {
ERROR(hidg->func.config->cdev,
"usb_ep_queue error on int endpoint %zd\n", status);
hidg->write_pending = 0;
wake_up(&hidg->write_queue);
} else {
status = count;
}
mutex_unlock(&hidg->lock);
return status;
}
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
{
struct f_hidg *hidg = file->private_data;
unsigned int ret = 0;
poll_wait(file, &hidg->read_queue, wait);
poll_wait(file, &hidg->write_queue, wait);
if (WRITE_COND)
ret |= POLLOUT | POLLWRNORM;
if (READ_COND)
ret |= POLLIN | POLLRDNORM;
return ret;
}
#undef WRITE_COND
#undef READ_COND
static int f_hidg_release(struct inode *inode, struct file *fd)
{
fd->private_data = NULL;
return 0;
}
static int f_hidg_open(struct inode *inode, struct file *fd)
{
struct f_hidg *hidg =
container_of(inode->i_cdev, struct f_hidg, cdev);
fd->private_data = hidg;
return 0;
}
/*-------------------------------------------------------------------------*/
/* usb_function */
static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
unsigned length)
{
return alloc_ep_req(ep, length, length);
}
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_hidg *hidg = (struct f_hidg *) req->context;
struct f_hidg_req_list *req_list;
unsigned long flags;
req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC);
if (!req_list)
return;
req_list->req = req;
spin_lock_irqsave(&hidg->spinlock, flags);
list_add_tail(&req_list->list, &hidg->completed_out_req);
spin_unlock_irqrestore(&hidg->spinlock, flags);
wake_up(&hidg->read_queue);
}
static int hidg_setup(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct f_hidg *hidg = func_to_hidg(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int status = 0;
__u16 value, length;
value = __le16_to_cpu(ctrl->wValue);
length = __le16_to_cpu(ctrl->wLength);
VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x "
"Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value);
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_REPORT):
VDBG(cdev, "get_report\n");
/* send an empty report */
length = min_t(unsigned, length, hidg->report_length);
memset(req->buf, 0x0, length);
goto respond;
break;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_GET_PROTOCOL):
VDBG(cdev, "get_protocol\n");
goto stall;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_REPORT):
VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength);
goto stall;
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8
| HID_REQ_SET_PROTOCOL):
VDBG(cdev, "set_protocol\n");
goto stall;
break;
case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8
| USB_REQ_GET_DESCRIPTOR):
switch (value >> 8) {
case HID_DT_HID:
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n");
length = min_t(unsigned short, length,
hidg_desc.bLength);
memcpy(req->buf, &hidg_desc, length);
goto respond;
break;
case HID_DT_REPORT:
VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
length = min_t(unsigned short, length,
hidg->report_desc_length);
memcpy(req->buf, hidg->report_desc, length);
goto respond;
break;
default:
VDBG(cdev, "Unknown descriptor request 0x%x\n",
value >> 8);
goto stall;
break;
}
break;
default:
VDBG(cdev, "Unknown request 0x%x\n",
ctrl->bRequest);
goto stall;
break;
}
stall:
return -EOPNOTSUPP;
respond:
req->zero = 0;
req->length = length;
status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (status < 0)
ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value);
return status;
}
static void hidg_disable(struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
struct f_hidg_req_list *list, *next;
usb_ep_disable(hidg->in_ep);
hidg->in_ep->driver_data = NULL;
usb_ep_disable(hidg->out_ep);
hidg->out_ep->driver_data = NULL;
list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) {
list_del(&list->list);
kfree(list);
}
}
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct f_hidg *hidg = func_to_hidg(f);
int i, status = 0;
VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
if (hidg->in_ep != NULL) {
/* restart endpoint */
if (hidg->in_ep->driver_data != NULL)
usb_ep_disable(hidg->in_ep);
status = config_ep_by_speed(f->config->cdev->gadget, f,
hidg->in_ep);
if (status) {
ERROR(cdev, "config_ep_by_speed FAILED!\n");
goto fail;
}
status = usb_ep_enable(hidg->in_ep);
if (status < 0) {
ERROR(cdev, "Enable IN endpoint FAILED!\n");
goto fail;
}
hidg->in_ep->driver_data = hidg;
}
if (hidg->out_ep != NULL) {
/* restart endpoint */
if (hidg->out_ep->driver_data != NULL)
usb_ep_disable(hidg->out_ep);
status = config_ep_by_speed(f->config->cdev->gadget, f,
hidg->out_ep);
if (status) {
ERROR(cdev, "config_ep_by_speed FAILED!\n");
goto fail;
}
status = usb_ep_enable(hidg->out_ep);
if (status < 0) {
ERROR(cdev, "Enable IN endpoint FAILED!\n");
goto fail;
}
hidg->out_ep->driver_data = hidg;
/*
* allocate a bunch of read buffers and queue them all at once.
*/
for (i = 0; i < hidg->qlen && status == 0; i++) {
struct usb_request *req =
hidg_alloc_ep_req(hidg->out_ep,
hidg->report_length);
if (req) {
req->complete = hidg_set_report_complete;
req->context = hidg;
status = usb_ep_queue(hidg->out_ep, req,
GFP_ATOMIC);
if (status)
ERROR(cdev, "%s queue req --> %d\n",
hidg->out_ep->name, status);
} else {
usb_ep_disable(hidg->out_ep);
hidg->out_ep->driver_data = NULL;
status = -ENOMEM;
goto fail;
}
}
}
fail:
return status;
}
const struct file_operations f_hidg_fops = {
.owner = THIS_MODULE,
.open = f_hidg_open,
.release = f_hidg_release,
.write = f_hidg_write,
.read = f_hidg_read,
.poll = f_hidg_poll,
.llseek = noop_llseek,
};
static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_ep *ep;
struct f_hidg *hidg = func_to_hidg(f);
int status;
dev_t dev;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
hidg_interface_desc.bInterfaceNumber = status;
/* allocate instance-specific endpoints */
status = -ENODEV;
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
if (!ep)
goto fail;
ep->driver_data = c->cdev; /* claim */
hidg->in_ep = ep;
ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
if (!ep)
goto fail;
ep->driver_data = c->cdev; /* claim */
hidg->out_ep = ep;
/* preallocate request and buffer */
status = -ENOMEM;
hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
if (!hidg->req)
goto fail;
hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
if (!hidg->req->buf)
goto fail;
/* set descriptor dynamic values */
hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
hidg_desc.desc[0].wDescriptorLength =
cpu_to_le16(hidg->report_desc_length);
hidg_hs_in_ep_desc.bEndpointAddress =
hidg_fs_in_ep_desc.bEndpointAddress;
hidg_hs_out_ep_desc.bEndpointAddress =
hidg_fs_out_ep_desc.bEndpointAddress;
status = usb_assign_descriptors(f, hidg_fs_descriptors,
hidg_hs_descriptors, NULL);
if (status)
goto fail;
mutex_init(&hidg->lock);
spin_lock_init(&hidg->spinlock);
init_waitqueue_head(&hidg->write_queue);
init_waitqueue_head(&hidg->read_queue);
INIT_LIST_HEAD(&hidg->completed_out_req);
/* create char device */
cdev_init(&hidg->cdev, &f_hidg_fops);
dev = MKDEV(major, hidg->minor);
status = cdev_add(&hidg->cdev, dev, 1);
if (status)
goto fail_free_descs;
device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
return 0;
fail_free_descs:
usb_free_all_descriptors(f);
fail:
ERROR(f->config->cdev, "hidg_bind FAILED\n");
if (hidg->req != NULL) {
kfree(hidg->req->buf);
if (hidg->in_ep != NULL)
usb_ep_free_request(hidg->in_ep, hidg->req);
}
return status;
}
static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_hidg *hidg = func_to_hidg(f);
device_destroy(hidg_class, MKDEV(major, hidg->minor));
cdev_del(&hidg->cdev);
/* disable/free request and end point */
usb_ep_disable(hidg->in_ep);
usb_ep_dequeue(hidg->in_ep, hidg->req);
kfree(hidg->req->buf);
usb_ep_free_request(hidg->in_ep, hidg->req);
usb_free_all_descriptors(f);
kfree(hidg->report_desc);
kfree(hidg);
}
/*-------------------------------------------------------------------------*/
/* Strings */
#define CT_FUNC_HID_IDX 0
static struct usb_string ct_func_string_defs[] = {
[CT_FUNC_HID_IDX].s = "HID Interface",
{}, /* end of list */
};
static struct usb_gadget_strings ct_func_string_table = {
.language = 0x0409, /* en-US */
.strings = ct_func_string_defs,
};
static struct usb_gadget_strings *ct_func_strings[] = {
&ct_func_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
/* usb_configuration */
int __init hidg_bind_config(struct usb_configuration *c,
struct hidg_func_descriptor *fdesc, int index)
{
struct f_hidg *hidg;
int status;
if (index >= minors)
return -ENOENT;
/* maybe allocate device-global string IDs, and patch descriptors */
if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
hidg_interface_desc.iInterface = status;
}
/* allocate and initialize one new instance */
hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
if (!hidg)
return -ENOMEM;
hidg->minor = index;
hidg->bInterfaceSubClass = fdesc->subclass;
hidg->bInterfaceProtocol = fdesc->protocol;
hidg->report_length = fdesc->report_length;
hidg->report_desc_length = fdesc->report_desc_length;
hidg->report_desc = kmemdup(fdesc->report_desc,
fdesc->report_desc_length,
GFP_KERNEL);
if (!hidg->report_desc) {
kfree(hidg);
return -ENOMEM;
}
hidg->func.name = "hid";
hidg->func.strings = ct_func_strings;
hidg->func.bind = hidg_bind;
hidg->func.unbind = hidg_unbind;
hidg->func.set_alt = hidg_set_alt;
hidg->func.disable = hidg_disable;
hidg->func.setup = hidg_setup;
/* this could me made configurable at some point */
hidg->qlen = 4;
status = usb_add_function(c, &hidg->func);
if (status)
kfree(hidg);
return status;
}
int __init ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
hidg_class = class_create(THIS_MODULE, "hidg");
status = alloc_chrdev_region(&dev, 0, count, "hidg");
if (!status) {
major = MAJOR(dev);
minors = count;
}
return status;
}
void ghid_cleanup(void)
{
if (major) {
unregister_chrdev_region(MKDEV(major, 0), minors);
major = minors = 0;
}
class_destroy(hidg_class);
hidg_class = NULL;
}

View file

@ -0,0 +1,569 @@
/*
* f_loopback.c - USB peripheral loopback configuration driver
*
* Copyright (C) 2003-2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>
#include "g_zero.h"
#include "u_f.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
*
* This takes messages of various sizes written OUT to a device, and loops
* them back so they can be read IN from it. It has been used by certain
* test applications. It supports limited testing of data queueing logic.
*
*
* This is currently packaged as a configuration driver, which can't be
* combined with other functions to make composite devices. However, it
* can be combined with other independent configurations.
*/
struct f_loopback {
struct usb_function function;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
};
static inline struct f_loopback *func_to_loop(struct usb_function *f)
{
return container_of(f, struct f_loopback, function);
}
static unsigned qlen;
static unsigned buflen;
/*-------------------------------------------------------------------------*/
static struct usb_interface_descriptor loopback_intf = {
.bLength = sizeof loopback_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_loop_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_loop_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &fs_loop_sink_desc,
(struct usb_descriptor_header *) &fs_loop_source_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_loop_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_loop_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &hs_loop_source_desc,
(struct usb_descriptor_header *) &hs_loop_sink_desc,
NULL,
};
/* super speed support: */
static struct usb_endpoint_descriptor ss_loop_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
.wBytesPerInterval = 0,
};
static struct usb_endpoint_descriptor ss_loop_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
.bmAttributes = 0,
.wBytesPerInterval = 0,
};
static struct usb_descriptor_header *ss_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &ss_loop_source_desc,
(struct usb_descriptor_header *) &ss_loop_source_comp_desc,
(struct usb_descriptor_header *) &ss_loop_sink_desc,
(struct usb_descriptor_header *) &ss_loop_sink_comp_desc,
NULL,
};
/* function-specific strings: */
static struct usb_string strings_loopback[] = {
[0].s = "loop input to output",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_loop = {
.language = 0x0409, /* en-us */
.strings = strings_loopback,
};
static struct usb_gadget_strings *loopback_strings[] = {
&stringtab_loop,
NULL,
};
/*-------------------------------------------------------------------------*/
static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_loopback *loop = func_to_loop(f);
int id;
int ret;
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
loopback_intf.bInterfaceNumber = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_loopback[0].id = id;
loopback_intf.iInterface = id;
/* allocate endpoints */
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
if (!loop->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n",
f->name, cdev->gadget->name);
return -ENODEV;
}
loop->in_ep->driver_data = cdev; /* claim */
loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
if (!loop->out_ep)
goto autoconf_fail;
loop->out_ep->driver_data = cdev; /* claim */
/* support high speed hardware */
hs_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
/* support super speed hardware */
ss_loop_source_desc.bEndpointAddress =
fs_loop_source_desc.bEndpointAddress;
ss_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
ret = usb_assign_descriptors(f, fs_loopback_descs, hs_loopback_descs,
ss_loopback_descs);
if (ret)
return ret;
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
(gadget_is_superspeed(c->cdev->gadget) ? "super" :
(gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full")),
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
static void lb_free_func(struct usb_function *f)
{
struct f_lb_opts *opts;
opts = container_of(f->fi, struct f_lb_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
usb_free_all_descriptors(f);
kfree(func_to_loop(f));
}
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_loopback *loop = ep->driver_data;
struct usb_composite_dev *cdev = loop->function.config->cdev;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == loop->out_ep) {
req->zero = (req->actual < req->length);
req->length = req->actual;
}
/* queue the buffer for some later OUT packet */
req->length = buflen;
status = usb_ep_queue(ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
/* FALLTHROUGH */
default:
ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
/* FALLTHROUGH */
/* NOTE: since this driver doesn't maintain an explicit record
* of requests it submitted (just maintains qlen count), we
* rely on the hardware driver to clean up on disconnect or
* endpoint disable.
*/
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
free_ep_req(ep, req);
return;
}
}
static void disable_loopback(struct f_loopback *loop)
{
struct usb_composite_dev *cdev;
cdev = loop->function.config->cdev;
disable_endpoints(cdev, loop->in_ep, loop->out_ep, NULL, NULL, NULL,
NULL);
VDBG(cdev, "%s disabled\n", loop->function.name);
}
static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
{
return alloc_ep_req(ep, len, buflen);
}
static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
struct usb_ep *ep)
{
struct usb_request *req;
unsigned i;
int result;
/*
* one endpoint writes data back IN to the host while another endpoint
* just reads OUT packets
*/
result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
if (result)
goto fail0;
result = usb_ep_enable(ep);
if (result < 0)
goto fail0;
ep->driver_data = loop;
/*
* allocate a bunch of read buffers and queue them all at once.
* we buffer at most 'qlen' transfers; fewer if any need more
* than 'buflen' bytes each.
*/
for (i = 0; i < qlen && result == 0; i++) {
req = lb_alloc_ep_req(ep, 0);
if (!req)
goto fail1;
req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
if (result) {
ERROR(cdev, "%s queue req --> %d\n",
ep->name, result);
goto fail1;
}
}
return 0;
fail1:
usb_ep_disable(ep);
fail0:
return result;
}
static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
int result = 0;
result = enable_endpoint(cdev, loop, loop->in_ep);
if (result)
return result;
result = enable_endpoint(cdev, loop, loop->out_ep);
if (result)
return result;
DBG(cdev, "%s enabled\n", loop->function.name);
return result;
}
static int loopback_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct f_loopback *loop = func_to_loop(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt is zero */
if (loop->in_ep->driver_data)
disable_loopback(loop);
return enable_loopback(cdev, loop);
}
static void loopback_disable(struct usb_function *f)
{
struct f_loopback *loop = func_to_loop(f);
disable_loopback(loop);
}
static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
{
struct f_loopback *loop;
struct f_lb_opts *lb_opts;
loop = kzalloc(sizeof *loop, GFP_KERNEL);
if (!loop)
return ERR_PTR(-ENOMEM);
lb_opts = container_of(fi, struct f_lb_opts, func_inst);
mutex_lock(&lb_opts->lock);
lb_opts->refcnt++;
mutex_unlock(&lb_opts->lock);
buflen = lb_opts->bulk_buflen;
qlen = lb_opts->qlen;
if (!qlen)
qlen = 32;
loop->function.name = "loopback";
loop->function.bind = loopback_bind;
loop->function.set_alt = loopback_set_alt;
loop->function.disable = loopback_disable;
loop->function.strings = loopback_strings;
loop->function.free_func = lb_free_func;
return &loop->function;
}
static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_lb_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_lb_opts);
CONFIGFS_ATTR_OPS(f_lb_opts);
static void lb_attr_release(struct config_item *item)
{
struct f_lb_opts *lb_opts = to_f_lb_opts(item);
usb_put_function_instance(&lb_opts->func_inst);
}
static struct configfs_item_operations lb_item_ops = {
.release = lb_attr_release,
.show_attribute = f_lb_opts_attr_show,
.store_attribute = f_lb_opts_attr_store,
};
static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
{
int result;
mutex_lock(&opts->lock);
result = sprintf(page, "%d", opts->qlen);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
const char *page, size_t len)
{
int ret;
u32 num;
mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto end;
}
ret = kstrtou32(page, 0, &num);
if (ret)
goto end;
opts->qlen = num;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
static struct f_lb_opts_attribute f_lb_opts_qlen =
__CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
f_lb_opts_qlen_show,
f_lb_opts_qlen_store);
static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
{
int result;
mutex_lock(&opts->lock);
result = sprintf(page, "%d", opts->bulk_buflen);
mutex_unlock(&opts->lock);
return result;
}
static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
const char *page, size_t len)
{
int ret;
u32 num;
mutex_lock(&opts->lock);
if (opts->refcnt) {
ret = -EBUSY;
goto end;
}
ret = kstrtou32(page, 0, &num);
if (ret)
goto end;
opts->bulk_buflen = num;
ret = len;
end:
mutex_unlock(&opts->lock);
return ret;
}
static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
__CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
f_lb_opts_bulk_buflen_show,
f_lb_opts_bulk_buflen_store);
static struct configfs_attribute *lb_attrs[] = {
&f_lb_opts_qlen.attr,
&f_lb_opts_bulk_buflen.attr,
NULL,
};
static struct config_item_type lb_func_type = {
.ct_item_ops = &lb_item_ops,
.ct_attrs = lb_attrs,
.ct_owner = THIS_MODULE,
};
static void lb_free_instance(struct usb_function_instance *fi)
{
struct f_lb_opts *lb_opts;
lb_opts = container_of(fi, struct f_lb_opts, func_inst);
kfree(lb_opts);
}
static struct usb_function_instance *loopback_alloc_instance(void)
{
struct f_lb_opts *lb_opts;
lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
if (!lb_opts)
return ERR_PTR(-ENOMEM);
mutex_init(&lb_opts->lock);
lb_opts->func_inst.free_func_inst = lb_free_instance;
lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
lb_opts->qlen = GZERO_QLEN;
config_group_init_type_name(&lb_opts->func_inst.group, "",
&lb_func_type);
return &lb_opts->func_inst;
}
DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
int __init lb_modinit(void)
{
int ret;
ret = usb_function_register(&Loopbackusb_func);
if (ret)
return ret;
return ret;
}
void __exit lb_modexit(void)
{
usb_function_unregister(&Loopbackusb_func);
}
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,166 @@
#ifndef USB_F_MASS_STORAGE_H
#define USB_F_MASS_STORAGE_H
#include <linux/usb/composite.h>
#include "storage_common.h"
struct fsg_module_parameters {
char *file[FSG_MAX_LUNS];
bool ro[FSG_MAX_LUNS];
bool removable[FSG_MAX_LUNS];
bool cdrom[FSG_MAX_LUNS];
bool nofua[FSG_MAX_LUNS];
unsigned int file_count, ro_count, removable_count, cdrom_count;
unsigned int nofua_count;
unsigned int luns; /* nluns */
bool stall; /* can_stall */
};
#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
module_param_array_named(prefix ## name, params.name, type, \
&prefix ## params.name ## _count, \
S_IRUGO); \
MODULE_PARM_DESC(prefix ## name, desc)
#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
module_param_named(prefix ## name, params.name, type, \
S_IRUGO); \
MODULE_PARM_DESC(prefix ## name, desc)
#define __FSG_MODULE_PARAMETERS(prefix, params) \
_FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
"names of backing files or devices"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
"true to force read-only"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
"true to simulate removable media"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
"true to simulate CD-ROM instead of disk"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \
"true to ignore SCSI WRITE(10,12) FUA bit"); \
_FSG_MODULE_PARAM(prefix, params, luns, uint, \
"number of LUNs"); \
_FSG_MODULE_PARAM(prefix, params, stall, bool, \
"false to prevent bulk stalls")
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#define FSG_MODULE_PARAMETERS(prefix, params) \
__FSG_MODULE_PARAMETERS(prefix, params); \
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
#else
#define FSG_MODULE_PARAMETERS(prefix, params) \
__FSG_MODULE_PARAMETERS(prefix, params)
#endif
struct fsg_common;
/* FSF callback functions */
struct fsg_operations {
/*
* Callback function to call when thread exits. If no
* callback is set or it returns value lower then zero MSF
* will force eject all LUNs it operates on (including those
* marked as non-removable or with prevent_medium_removal flag
* set).
*/
int (*thread_exits)(struct fsg_common *common);
};
struct fsg_lun_opts {
struct config_group group;
struct fsg_lun *lun;
int lun_id;
};
struct fsg_opts {
struct fsg_common *common;
struct usb_function_instance func_inst;
struct fsg_lun_opts lun0;
struct config_group *default_groups[2];
bool no_configfs; /* for legacy gadgets */
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
struct fsg_lun_config {
const char *filename;
char ro;
char removable;
char cdrom;
char nofua;
};
struct fsg_config {
unsigned nluns;
struct fsg_lun_config luns[FSG_MAX_LUNS];
/* Callback functions. */
const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
const char *vendor_name; /* 8 characters or less */
const char *product_name; /* 16 characters or less */
char can_stall;
unsigned int fsg_num_buffers;
};
static inline struct fsg_opts *
fsg_opts_from_func_inst(const struct usb_function_instance *fi)
{
return container_of(fi, struct fsg_opts, func_inst);
}
void fsg_common_get(struct fsg_common *common);
void fsg_common_put(struct fsg_common *common);
void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
void fsg_common_free_buffers(struct fsg_common *common);
int fsg_common_set_cdev(struct fsg_common *common,
struct usb_composite_dev *cdev, bool can_stall);
void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs);
void fsg_common_remove_luns(struct fsg_common *common);
void fsg_common_free_luns(struct fsg_common *common);
int fsg_common_set_nluns(struct fsg_common *common, int nluns);
void fsg_common_set_ops(struct fsg_common *common,
const struct fsg_operations *ops);
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
unsigned int id, const char *name,
const char **name_pfx);
int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
const char *pn);
int fsg_common_run_thread(struct fsg_common *common);
void fsg_config_from_params(struct fsg_config *cfg,
const struct fsg_module_parameters *params,
unsigned int fsg_num_buffers);
#endif /* USB_F_MASS_STORAGE_H */

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2014 Google, Inc.
* Author: Badhri Jagan Sridharan <badhri@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define MTP_MAX_PACKET_LEN_FROM_APP 16
#define MTP_ACM_ENABLE 0
#define MTP_ONLY_ENABLE 1
#define MTP_DISABLE 2
#define MTP_CLEAR_HALT 3
#define MTP_WRITE_INT_DATA 4
#define SET_MTP_USER_PID 5
#define GET_SETUP_DATA 6
#define SET_SETUP_DATA 7
#define SEND_RESET_ACK 8
#define SET_ZLP_DATA 9
#define GET_HIGH_FULL_SPEED 10
#define SEND_FILE_WITH_HEADER 11
#define MTP_VBUS_DISABLE 12
#define SIG_SETUP 44
/*PIMA15740-2000 spec*/
#define USB_PTPREQUEST_CANCELIO 0x64 /* Cancel request */
#define USB_PTPREQUEST_GETEVENT 0x65 /* Get extened event data */
#define USB_PTPREQUEST_RESET 0x66 /* Reset Device */
#define USB_PTPREQUEST_GETSTATUS 0x67 /* Get Device Status */
#define USB_PTPREQUEST_CANCELIO_SIZE 6
#define USB_PTPREQUEST_GETSTATUS_SIZE 12
struct mtp_event {
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
size_t length;
void *data;
};
int mtp_function_add(struct usb_configuration *c);
int mtp_function_config_changed(struct usb_composite_dev *cdev,
struct usb_configuration *c);
int mtp_enable(void);
void mtp_function_enable(int enable);
struct usb_mtp_ctrlrequest {
struct usb_ctrlrequest setup;
};
struct usb_container_header {
uint32_t Length;/* the valid size, in BYTES, of the container */
uint16_t Type;/* Container type */
uint16_t Code;/* Operation code, response code, or Event code */
uint32_t TransactionID;/* host generated number */
};
struct read_send_info {
int Fd;/* Media File fd */
uint64_t Length;/* the valid size, in BYTES, of the container */
uint16_t Code;/* Operation code, response code, or Event code */
uint32_t TransactionID;/* host generated number */
};
extern struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config);
extern struct usb_function *function_alloc_mtp_ptp(
struct usb_function_instance *fi, bool mtp_config);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,538 @@
/*
* f_obex.c -- USB CDC OBEX function driver
*
* Copyright (C) 2008 Nokia Corporation
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
*
* Based on f_acm.c by Al Borchers and David Brownell.
*
* 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, or
* (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This CDC OBEX function support just packages a TTY-ish byte stream.
* A user mode server will put it into "raw" mode and handle all the
* relevant protocol details ... this is just a kernel passthrough.
* When possible, we prevent gadget enumeration until that server is
* ready to handle the commands.
*/
struct f_obex {
struct gserial port;
u8 ctrl_id;
u8 data_id;
u8 cur_alt;
u8 port_num;
u8 can_activate;
};
static inline struct f_obex *func_to_obex(struct usb_function *f)
{
return container_of(f, struct f_obex, port.func);
}
static inline struct f_obex *port_to_obex(struct gserial *p)
{
return container_of(p, struct f_obex, port);
}
/*-------------------------------------------------------------------------*/
#define OBEX_CTRL_IDX 0
#define OBEX_DATA_IDX 1
static struct usb_string obex_string_defs[] = {
[OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)",
[OBEX_DATA_IDX].s = "CDC OBEX Data",
{ }, /* end of list */
};
static struct usb_gadget_strings obex_string_table = {
.language = 0x0409, /* en-US */
.strings = obex_string_defs,
};
static struct usb_gadget_strings *obex_strings[] = {
&obex_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static struct usb_interface_descriptor obex_control_intf = {
.bLength = sizeof(obex_control_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX,
};
static struct usb_interface_descriptor obex_data_nop_intf = {
.bLength = sizeof(obex_data_nop_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
static struct usb_interface_descriptor obex_data_intf = {
.bLength = sizeof(obex_data_intf),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 2,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
static struct usb_cdc_header_desc obex_cdc_header_desc = {
.bLength = sizeof(obex_cdc_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0120),
};
static struct usb_cdc_union_desc obex_cdc_union_desc = {
.bLength = sizeof(obex_cdc_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
.bMasterInterface0 = 1,
.bSlaveInterface0 = 2,
};
static struct usb_cdc_obex_desc obex_desc = {
.bLength = sizeof(obex_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_OBEX_TYPE,
.bcdVersion = cpu_to_le16(0x0100),
};
/* High-Speed Support */
static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_function[] = {
(struct usb_descriptor_header *) &obex_control_intf,
(struct usb_descriptor_header *) &obex_cdc_header_desc,
(struct usb_descriptor_header *) &obex_desc,
(struct usb_descriptor_header *) &obex_cdc_union_desc,
(struct usb_descriptor_header *) &obex_data_nop_intf,
(struct usb_descriptor_header *) &obex_data_intf,
(struct usb_descriptor_header *) &obex_hs_ep_in_desc,
(struct usb_descriptor_header *) &obex_hs_ep_out_desc,
NULL,
};
/* Full-Speed Support */
static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_function[] = {
(struct usb_descriptor_header *) &obex_control_intf,
(struct usb_descriptor_header *) &obex_cdc_header_desc,
(struct usb_descriptor_header *) &obex_desc,
(struct usb_descriptor_header *) &obex_cdc_union_desc,
(struct usb_descriptor_header *) &obex_data_nop_intf,
(struct usb_descriptor_header *) &obex_data_intf,
(struct usb_descriptor_header *) &obex_fs_ep_in_desc,
(struct usb_descriptor_header *) &obex_fs_ep_out_desc,
NULL,
};
/*-------------------------------------------------------------------------*/
static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_obex *obex = func_to_obex(f);
struct usb_composite_dev *cdev = f->config->cdev;
if (intf == obex->ctrl_id) {
if (alt != 0)
goto fail;
/* NOP */
dev_dbg(&cdev->gadget->dev,
"reset obex ttyGS%d control\n", obex->port_num);
} else if (intf == obex->data_id) {
if (alt > 1)
goto fail;
if (obex->port.in->driver_data) {
dev_dbg(&cdev->gadget->dev,
"reset obex ttyGS%d\n", obex->port_num);
gserial_disconnect(&obex->port);
}
if (!obex->port.in->desc || !obex->port.out->desc) {
dev_dbg(&cdev->gadget->dev,
"init obex ttyGS%d\n", obex->port_num);
if (config_ep_by_speed(cdev->gadget, f,
obex->port.in) ||
config_ep_by_speed(cdev->gadget, f,
obex->port.out)) {
obex->port.out->desc = NULL;
obex->port.in->desc = NULL;
goto fail;
}
}
if (alt == 1) {
dev_dbg(&cdev->gadget->dev,
"activate obex ttyGS%d\n", obex->port_num);
gserial_connect(&obex->port, obex->port_num);
}
} else
goto fail;
obex->cur_alt = alt;
return 0;
fail:
return -EINVAL;
}
static int obex_get_alt(struct usb_function *f, unsigned intf)
{
struct f_obex *obex = func_to_obex(f);
return obex->cur_alt;
}
static void obex_disable(struct usb_function *f)
{
struct f_obex *obex = func_to_obex(f);
struct usb_composite_dev *cdev = f->config->cdev;
dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
gserial_disconnect(&obex->port);
}
/*-------------------------------------------------------------------------*/
static void obex_connect(struct gserial *g)
{
struct f_obex *obex = port_to_obex(g);
struct usb_composite_dev *cdev = g->func.config->cdev;
int status;
if (!obex->can_activate)
return;
status = usb_function_activate(&g->func);
if (status)
dev_dbg(&cdev->gadget->dev,
"obex ttyGS%d function activate --> %d\n",
obex->port_num, status);
}
static void obex_disconnect(struct gserial *g)
{
struct f_obex *obex = port_to_obex(g);
struct usb_composite_dev *cdev = g->func.config->cdev;
int status;
if (!obex->can_activate)
return;
status = usb_function_deactivate(&g->func);
if (status)
dev_dbg(&cdev->gadget->dev,
"obex ttyGS%d function deactivate --> %d\n",
obex->port_num, status);
}
/*-------------------------------------------------------------------------*/
/* Some controllers can't support CDC OBEX ... */
static inline bool can_support_obex(struct usb_configuration *c)
{
/* Since the first interface is a NOP, we can ignore the
* issue of multi-interface support on most controllers.
*
* Altsettings are mandatory, however...
*/
if (!gadget_supports_altsettings(c->cdev->gadget))
return false;
/* everything else is *probably* fine ... */
return true;
}
static int obex_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_obex *obex = func_to_obex(f);
struct usb_string *us;
int status;
struct usb_ep *ep;
if (!can_support_obex(c))
return -EINVAL;
us = usb_gstrings_attach(cdev, obex_strings,
ARRAY_SIZE(obex_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
obex->ctrl_id = status;
obex_control_intf.bInterfaceNumber = status;
obex_cdc_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
obex->data_id = status;
obex_data_nop_intf.bInterfaceNumber = status;
obex_data_intf.bInterfaceNumber = status;
obex_cdc_union_desc.bSlaveInterface0 = status;
/* allocate instance-specific endpoints */
status = -ENODEV;
ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
if (!ep)
goto fail;
obex->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
if (!ep)
goto fail;
obex->port.out = ep;
ep->driver_data = cdev; /* claim */
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
obex_hs_ep_in_desc.bEndpointAddress =
obex_fs_ep_in_desc.bEndpointAddress;
obex_hs_ep_out_desc.bEndpointAddress =
obex_fs_ep_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, fs_function, hs_function, NULL);
if (status)
goto fail;
/* Avoid letting this gadget enumerate until the userspace
* OBEX server is active.
*/
status = usb_function_deactivate(f);
if (status < 0)
WARNING(cdev, "obex ttyGS%d: can't prevent enumeration, %d\n",
obex->port_num, status);
else
obex->can_activate = true;
dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
obex->port_num,
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
obex->port.in->name, obex->port.out->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (obex->port.out)
obex->port.out->driver_data = NULL;
if (obex->port.in)
obex->port.in->driver_data = NULL;
ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
return status;
}
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_serial_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_serial_opts);
static ssize_t f_obex_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
struct f_serial_opts_attribute *f_serial_opts_attr =
container_of(attr, struct f_serial_opts_attribute, attr);
ssize_t ret = 0;
if (f_serial_opts_attr->show)
ret = f_serial_opts_attr->show(opts, page);
return ret;
}
static void obex_attr_release(struct config_item *item)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations obex_item_ops = {
.release = obex_attr_release,
.show_attribute = f_obex_attr_show,
};
static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page)
{
return sprintf(page, "%u\n", opts->port_num);
}
static struct f_serial_opts_attribute f_obex_port_num =
__CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show);
static struct configfs_attribute *acm_attrs[] = {
&f_obex_port_num.attr,
NULL,
};
static struct config_item_type obex_func_type = {
.ct_item_ops = &obex_item_ops,
.ct_attrs = acm_attrs,
.ct_owner = THIS_MODULE,
};
static void obex_free_inst(struct usb_function_instance *f)
{
struct f_serial_opts *opts;
opts = container_of(f, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
kfree(opts);
}
static struct usb_function_instance *obex_alloc_inst(void)
{
struct f_serial_opts *opts;
int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = obex_free_inst;
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
config_group_init_type_name(&opts->func_inst.group, "",
&obex_func_type);
return &opts->func_inst;
}
static void obex_free(struct usb_function *f)
{
struct f_obex *obex;
obex = func_to_obex(f);
kfree(obex);
}
static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
{
usb_free_all_descriptors(f);
}
static struct usb_function *obex_alloc(struct usb_function_instance *fi)
{
struct f_obex *obex;
struct f_serial_opts *opts;
/* allocate and initialize one new instance */
obex = kzalloc(sizeof(*obex), GFP_KERNEL);
if (!obex)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_serial_opts, func_inst);
obex->port_num = opts->port_num;
obex->port.connect = obex_connect;
obex->port.disconnect = obex_disconnect;
obex->port.func.name = "obex";
/* descriptors are per-instance copies */
obex->port.func.bind = obex_bind;
obex->port.func.unbind = obex_unbind;
obex->port.func.set_alt = obex_set_alt;
obex->port.func.get_alt = obex_get_alt;
obex->port.func.disable = obex_disable;
obex->port.func.free_func = obex_free;
return &obex->port.func;
}
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
MODULE_AUTHOR("Felipe Balbi");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,759 @@
/*
* f_phonet.c -- USB CDC Phonet function
*
* Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
*
* Author: Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*/
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/netdevice.h>
#include <linux/if_ether.h>
#include <linux/if_phonet.h>
#include <linux/if_arp.h>
#include <linux/usb/ch9.h>
#include <linux/usb/cdc.h>
#include <linux/usb/composite.h>
#include "u_phonet.h"
#include "u_ether.h"
#define PN_MEDIA_USB 0x1B
#define MAXPACKET 512
#if (PAGE_SIZE % MAXPACKET)
#error MAXPACKET must divide PAGE_SIZE!
#endif
/*-------------------------------------------------------------------------*/
struct phonet_port {
struct f_phonet *usb;
spinlock_t lock;
};
struct f_phonet {
struct usb_function function;
struct {
struct sk_buff *skb;
spinlock_t lock;
} rx;
struct net_device *dev;
struct usb_ep *in_ep, *out_ep;
struct usb_request *in_req;
struct usb_request *out_reqv[0];
};
static int phonet_rxq_size = 17;
static inline struct f_phonet *func_to_pn(struct usb_function *f)
{
return container_of(f, struct f_phonet, function);
}
/*-------------------------------------------------------------------------*/
#define USB_CDC_SUBCLASS_PHONET 0xfe
#define USB_CDC_PHONET_TYPE 0xab
static struct usb_interface_descriptor
pn_control_intf_desc = {
.bLength = sizeof pn_control_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC, */
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET,
};
static const struct usb_cdc_header_desc
pn_header_desc = {
.bLength = sizeof pn_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0110),
};
static const struct usb_cdc_header_desc
pn_phonet_desc = {
.bLength = sizeof pn_phonet_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_PHONET_TYPE,
.bcdCDC = cpu_to_le16(0x1505), /* ??? */
};
static struct usb_cdc_union_desc
pn_union_desc = {
.bLength = sizeof pn_union_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC, */
/* .bSlaveInterface0 = DYNAMIC, */
};
static struct usb_interface_descriptor
pn_data_nop_intf_desc = {
.bLength = sizeof pn_data_nop_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC, */
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
static struct usb_interface_descriptor
pn_data_intf_desc = {
.bLength = sizeof pn_data_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC, */
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
};
static struct usb_endpoint_descriptor
pn_fs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor
pn_hs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(MAXPACKET),
};
static struct usb_endpoint_descriptor
pn_fs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor
pn_hs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *fs_pn_function[] = {
(struct usb_descriptor_header *) &pn_control_intf_desc,
(struct usb_descriptor_header *) &pn_header_desc,
(struct usb_descriptor_header *) &pn_phonet_desc,
(struct usb_descriptor_header *) &pn_union_desc,
(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
(struct usb_descriptor_header *) &pn_data_intf_desc,
(struct usb_descriptor_header *) &pn_fs_sink_desc,
(struct usb_descriptor_header *) &pn_fs_source_desc,
NULL,
};
static struct usb_descriptor_header *hs_pn_function[] = {
(struct usb_descriptor_header *) &pn_control_intf_desc,
(struct usb_descriptor_header *) &pn_header_desc,
(struct usb_descriptor_header *) &pn_phonet_desc,
(struct usb_descriptor_header *) &pn_union_desc,
(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
(struct usb_descriptor_header *) &pn_data_intf_desc,
(struct usb_descriptor_header *) &pn_hs_sink_desc,
(struct usb_descriptor_header *) &pn_hs_source_desc,
NULL,
};
/*-------------------------------------------------------------------------*/
static int pn_net_open(struct net_device *dev)
{
netif_wake_queue(dev);
return 0;
}
static int pn_net_close(struct net_device *dev)
{
netif_stop_queue(dev);
return 0;
}
static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_phonet *fp = ep->driver_data;
struct net_device *dev = fp->dev;
struct sk_buff *skb = req->context;
switch (req->status) {
case 0:
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
break;
case -ESHUTDOWN: /* disconnected */
case -ECONNRESET: /* disabled */
dev->stats.tx_aborted_errors++;
default:
dev->stats.tx_errors++;
}
dev_kfree_skb_any(skb);
netif_wake_queue(dev);
}
static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct phonet_port *port = netdev_priv(dev);
struct f_phonet *fp;
struct usb_request *req;
unsigned long flags;
if (skb->protocol != htons(ETH_P_PHONET))
goto out;
spin_lock_irqsave(&port->lock, flags);
fp = port->usb;
if (unlikely(!fp)) /* race with carrier loss */
goto out_unlock;
req = fp->in_req;
req->buf = skb->data;
req->length = skb->len;
req->complete = pn_tx_complete;
req->zero = 1;
req->context = skb;
if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
goto out_unlock;
netif_stop_queue(dev);
skb = NULL;
out_unlock:
spin_unlock_irqrestore(&port->lock, flags);
out:
if (unlikely(skb)) {
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
}
return NETDEV_TX_OK;
}
static int pn_net_mtu(struct net_device *dev, int new_mtu)
{
if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
return -EINVAL;
dev->mtu = new_mtu;
return 0;
}
static const struct net_device_ops pn_netdev_ops = {
.ndo_open = pn_net_open,
.ndo_stop = pn_net_close,
.ndo_start_xmit = pn_net_xmit,
.ndo_change_mtu = pn_net_mtu,
};
static void pn_net_setup(struct net_device *dev)
{
dev->features = 0;
dev->type = ARPHRD_PHONET;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->mtu = PHONET_DEV_MTU;
dev->hard_header_len = 1;
dev->dev_addr[0] = PN_MEDIA_USB;
dev->addr_len = 1;
dev->tx_queue_len = 1;
dev->netdev_ops = &pn_netdev_ops;
dev->destructor = free_netdev;
dev->header_ops = &phonet_header_ops;
}
/*-------------------------------------------------------------------------*/
/*
* Queue buffer for data from the host
*/
static int
pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
{
struct page *page;
int err;
page = __skb_alloc_page(gfp_flags | __GFP_NOMEMALLOC, NULL);
if (!page)
return -ENOMEM;
req->buf = page_address(page);
req->length = PAGE_SIZE;
req->context = page;
err = usb_ep_queue(fp->out_ep, req, gfp_flags);
if (unlikely(err))
put_page(page);
return err;
}
static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_phonet *fp = ep->driver_data;
struct net_device *dev = fp->dev;
struct page *page = req->context;
struct sk_buff *skb;
unsigned long flags;
int status = req->status;
switch (status) {
case 0:
spin_lock_irqsave(&fp->rx.lock, flags);
skb = fp->rx.skb;
if (!skb)
skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
if (req->actual < req->length) /* Last fragment */
fp->rx.skb = NULL;
spin_unlock_irqrestore(&fp->rx.lock, flags);
if (unlikely(!skb))
break;
if (skb->len == 0) { /* First fragment */
skb->protocol = htons(ETH_P_PHONET);
skb_reset_mac_header(skb);
/* Can't use pskb_pull() on page in IRQ */
memcpy(skb_put(skb, 1), page_address(page), 1);
}
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
skb->len <= 1, req->actual, PAGE_SIZE);
page = NULL;
if (req->actual < req->length) { /* Last fragment */
skb->dev = dev;
dev->stats.rx_packets++;
dev->stats.rx_bytes += skb->len;
netif_rx(skb);
}
break;
/* Do not resubmit in these cases: */
case -ESHUTDOWN: /* disconnect */
case -ECONNABORTED: /* hw reset */
case -ECONNRESET: /* dequeued (unlink or netif down) */
req = NULL;
break;
/* Do resubmit in these cases: */
case -EOVERFLOW: /* request buffer overflow */
dev->stats.rx_over_errors++;
default:
dev->stats.rx_errors++;
break;
}
if (page)
put_page(page);
if (req)
pn_rx_submit(fp, req, GFP_ATOMIC | __GFP_COLD);
}
/*-------------------------------------------------------------------------*/
static void __pn_reset(struct usb_function *f)
{
struct f_phonet *fp = func_to_pn(f);
struct net_device *dev = fp->dev;
struct phonet_port *port = netdev_priv(dev);
netif_carrier_off(dev);
port->usb = NULL;
usb_ep_disable(fp->out_ep);
usb_ep_disable(fp->in_ep);
if (fp->rx.skb) {
dev_kfree_skb_irq(fp->rx.skb);
fp->rx.skb = NULL;
}
}
static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_phonet *fp = func_to_pn(f);
struct usb_gadget *gadget = fp->function.config->cdev->gadget;
if (intf == pn_control_intf_desc.bInterfaceNumber)
/* control interface, no altsetting */
return (alt > 0) ? -EINVAL : 0;
if (intf == pn_data_intf_desc.bInterfaceNumber) {
struct net_device *dev = fp->dev;
struct phonet_port *port = netdev_priv(dev);
/* data intf (0: inactive, 1: active) */
if (alt > 1)
return -EINVAL;
spin_lock(&port->lock);
__pn_reset(f);
if (alt == 1) {
int i;
if (config_ep_by_speed(gadget, f, fp->in_ep) ||
config_ep_by_speed(gadget, f, fp->out_ep)) {
fp->in_ep->desc = NULL;
fp->out_ep->desc = NULL;
spin_unlock(&port->lock);
return -EINVAL;
}
usb_ep_enable(fp->out_ep);
usb_ep_enable(fp->in_ep);
port->usb = fp;
fp->out_ep->driver_data = fp;
fp->in_ep->driver_data = fp;
netif_carrier_on(dev);
for (i = 0; i < phonet_rxq_size; i++)
pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC | __GFP_COLD);
}
spin_unlock(&port->lock);
return 0;
}
return -EINVAL;
}
static int pn_get_alt(struct usb_function *f, unsigned intf)
{
struct f_phonet *fp = func_to_pn(f);
if (intf == pn_control_intf_desc.bInterfaceNumber)
return 0;
if (intf == pn_data_intf_desc.bInterfaceNumber) {
struct phonet_port *port = netdev_priv(fp->dev);
u8 alt;
spin_lock(&port->lock);
alt = port->usb != NULL;
spin_unlock(&port->lock);
return alt;
}
return -EINVAL;
}
static void pn_disconnect(struct usb_function *f)
{
struct f_phonet *fp = func_to_pn(f);
struct phonet_port *port = netdev_priv(fp->dev);
unsigned long flags;
/* remain disabled until set_alt */
spin_lock_irqsave(&port->lock, flags);
__pn_reset(f);
spin_unlock_irqrestore(&port->lock, flags);
}
/*-------------------------------------------------------------------------*/
static int pn_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct usb_gadget *gadget = cdev->gadget;
struct f_phonet *fp = func_to_pn(f);
struct usb_ep *ep;
int status, i;
struct f_phonet_opts *phonet_opts;
phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
/*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry,
* in each configuration its functions are bound in sequence
* with list_for_each_entry, so we assume no race condition
* with regard to phonet_opts->bound access
*/
if (!phonet_opts->bound) {
gphonet_set_gadget(phonet_opts->net, gadget);
status = gphonet_register_netdev(phonet_opts->net);
if (status)
return status;
phonet_opts->bound = true;
}
/* Reserve interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto err;
pn_control_intf_desc.bInterfaceNumber = status;
pn_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto err;
pn_data_nop_intf_desc.bInterfaceNumber = status;
pn_data_intf_desc.bInterfaceNumber = status;
pn_union_desc.bSlaveInterface0 = status;
/* Reserve endpoints */
status = -ENODEV;
ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
if (!ep)
goto err;
fp->out_ep = ep;
ep->driver_data = fp; /* Claim */
ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
if (!ep)
goto err;
fp->in_ep = ep;
ep->driver_data = fp; /* Claim */
pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
/* Do not try to bind Phonet twice... */
status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
NULL);
if (status)
goto err;
/* Incoming USB requests */
status = -ENOMEM;
for (i = 0; i < phonet_rxq_size; i++) {
struct usb_request *req;
req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
if (!req)
goto err_req;
req->complete = pn_rx_complete;
fp->out_reqv[i] = req;
}
/* Outgoing USB requests */
fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
if (!fp->in_req)
goto err_req;
INFO(cdev, "USB CDC Phonet function\n");
INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
fp->out_ep->name, fp->in_ep->name);
return 0;
err_req:
for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
usb_free_all_descriptors(f);
err:
if (fp->out_ep)
fp->out_ep->driver_data = NULL;
if (fp->in_ep)
fp->in_ep->driver_data = NULL;
ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
return status;
}
static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_phonet_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_phonet_opts);
static ssize_t f_phonet_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct f_phonet_opts *opts = to_f_phonet_opts(item);
struct f_phonet_opts_attribute *f_phonet_opts_attr =
container_of(attr, struct f_phonet_opts_attribute, attr);
ssize_t ret = 0;
if (f_phonet_opts_attr->show)
ret = f_phonet_opts_attr->show(opts, page);
return ret;
}
static void phonet_attr_release(struct config_item *item)
{
struct f_phonet_opts *opts = to_f_phonet_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations phonet_item_ops = {
.release = phonet_attr_release,
.show_attribute = f_phonet_attr_show,
};
static ssize_t f_phonet_ifname_show(struct f_phonet_opts *opts, char *page)
{
return gether_get_ifname(opts->net, page, PAGE_SIZE);
}
static struct f_phonet_opts_attribute f_phonet_ifname =
__CONFIGFS_ATTR_RO(ifname, f_phonet_ifname_show);
static struct configfs_attribute *phonet_attrs[] = {
&f_phonet_ifname.attr,
NULL,
};
static struct config_item_type phonet_func_type = {
.ct_item_ops = &phonet_item_ops,
.ct_attrs = phonet_attrs,
.ct_owner = THIS_MODULE,
};
static void phonet_free_inst(struct usb_function_instance *f)
{
struct f_phonet_opts *opts;
opts = container_of(f, struct f_phonet_opts, func_inst);
if (opts->bound)
gphonet_cleanup(opts->net);
else
free_netdev(opts->net);
kfree(opts);
}
static struct usb_function_instance *phonet_alloc_inst(void)
{
struct f_phonet_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = phonet_free_inst;
opts->net = gphonet_setup_default();
if (IS_ERR(opts->net)) {
struct net_device *net = opts->net;
kfree(opts);
return ERR_CAST(net);
}
config_group_init_type_name(&opts->func_inst.group, "",
&phonet_func_type);
return &opts->func_inst;
}
static void phonet_free(struct usb_function *f)
{
struct f_phonet *phonet;
phonet = func_to_pn(f);
kfree(phonet);
}
static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_phonet *fp = func_to_pn(f);
int i;
/* We are already disconnected */
if (fp->in_req)
usb_ep_free_request(fp->in_ep, fp->in_req);
for (i = 0; i < phonet_rxq_size; i++)
if (fp->out_reqv[i])
usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
usb_free_all_descriptors(f);
}
static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
{
struct f_phonet *fp;
struct f_phonet_opts *opts;
int size;
size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
fp = kzalloc(size, GFP_KERNEL);
if (!fp)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_phonet_opts, func_inst);
fp->dev = opts->net;
fp->function.name = "phonet";
fp->function.bind = pn_bind;
fp->function.unbind = pn_unbind;
fp->function.set_alt = pn_set_alt;
fp->function.get_alt = pn_get_alt;
fp->function.disable = pn_disconnect;
fp->function.free_func = phonet_free;
spin_lock_init(&fp->rx.lock);
return &fp->function;
}
struct net_device *gphonet_setup_default(void)
{
struct net_device *dev;
struct phonet_port *port;
/* Create net device */
dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN,
pn_net_setup);
if (!dev)
return ERR_PTR(-ENOMEM);
port = netdev_priv(dev);
spin_lock_init(&port->lock);
netif_carrier_off(dev);
return dev;
}
void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
{
SET_NETDEV_DEV(net, &g->dev);
}
int gphonet_register_netdev(struct net_device *net)
{
int status;
status = register_netdev(net);
if (status)
free_netdev(net);
return status;
}
void gphonet_cleanup(struct net_device *dev)
{
unregister_netdev(dev);
}
DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
MODULE_AUTHOR("Rémi Denis-Courmont");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,38 @@
/*
* Gadget Function Driver for PTP
*
* Copyright (C) 2014 Google, Inc.
* Author: Badhri Jagan Sridharan <badhri@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/configfs.h>
#include <linux/usb/composite.h>
#include "f_mtp.h"
static struct usb_function_instance *ptp_alloc_inst(void)
{
return alloc_inst_mtp_ptp(false);
}
static struct usb_function *ptp_alloc(struct usb_function_instance *fi)
{
return function_alloc_mtp_ptp(fi, false);
}
DECLARE_USB_FUNCTION_INIT(ptp, ptp_alloc_inst, ptp_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Badhri Jagan Sridharan");

View file

@ -0,0 +1,952 @@
/*
* f_rndis.c -- RNDIS link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.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, or
* (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/atomic.h>
#include "u_ether.h"
#include "rndis.h"
/*
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
* been promoted instead of the standard CDC Ethernet. The published RNDIS
* spec is ambiguous, incomplete, and needlessly complex. Variants such as
* ActiveSync have even worse status in terms of specification.
*
* In short: it's a protocol controlled by (and for) Microsoft, not for an
* Open ecosystem or markets. Linux supports it *only* because Microsoft
* doesn't support the CDC Ethernet standard.
*
* The RNDIS data transfer model is complex, with multiple Ethernet packets
* per USB message, and out of band data. The control model is built around
* what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
* (modem, not Ethernet) veneer, with those ACM descriptors being entirely
* useless (they're ignored). RNDIS expects to be the only function in its
* configuration, so it's no real help if you need composite devices; and
* it expects to be the first configuration too.
*
* There is a single technical advantage of RNDIS over CDC Ethernet, if you
* discount the fluff that its RPC can be made to deliver: it doesn't need
* a NOP altsetting for the data interface. That lets it work on some of the
* "so smart it's stupid" hardware which takes over configuration changes
* from the software, and adds restrictions like "no altsettings".
*
* Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
* have all sorts of contrary-to-specification oddities that can prevent
* them from working sanely. Since bugfixes (or accurate specs, letting
* Linux work around those bugs) are unlikely to ever come from MSFT, you
* may want to avoid using RNDIS on purely operational grounds.
*
* Omissions from the RNDIS 1.0 specification include:
*
* - Power management ... references data that's scattered around lots
* of other documentation, which is incorrect/incomplete there too.
*
* - There are various undocumented protocol requirements, like the need
* to send garbage in some control-OUT messages.
*
* - MS-Windows drivers sometimes emit undocumented requests.
*/
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
static unsigned int rndis_dl_max_pkt_per_xfer = 10;
#else
static unsigned int rndis_dl_max_pkt_per_xfer = 3;
#endif
module_param(rndis_dl_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rndis_dl_max_pkt_per_xfer,
"Maximum packets per transfer for DL aggregation");
static unsigned int rndis_ul_max_pkt_per_xfer = 3;
module_param(rndis_ul_max_pkt_per_xfer, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(rndis_ul_max_pkt_per_xfer,
"Maximum packets per transfer for UL aggregation");
struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
u32 vendorID;
const char *manufacturer;
int config;
struct usb_ep *notify;
struct usb_request *notify_req;
atomic_t notify_count;
};
static inline struct f_rndis *func_to_rndis(struct usb_function *f)
{
return container_of(f, struct f_rndis, port.func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
static unsigned int bitrate(struct usb_gadget *g)
{
if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
return 13 * 1024 * 8 * 1000 * 8;
else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
/*
*/
#define RNDIS_STATUS_INTERVAL_MS 32
#define STATUS_BYTECOUNT 8 /* 8 bytes data */
/* interface descriptor: */
static struct usb_interface_descriptor rndis_control_intf = {
.bLength = sizeof rndis_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc header_desc = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0110),
};
static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
.bLength = sizeof call_mgmt_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
.bmCapabilities = 0x00,
.bDataInterface = 0x01,
};
static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
.bLength = sizeof rndis_acm_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = 0x00,
};
static struct usb_cdc_union_desc rndis_union_desc = {
.bLength = sizeof(rndis_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
/* the data interface has two bulk endpoints */
static struct usb_interface_descriptor rndis_data_intf = {
.bLength = sizeof rndis_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
static struct usb_interface_assoc_descriptor
rndis_iad_descriptor = {
.bLength = sizeof rndis_iad_descriptor,
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0, /* XXX, hardcoded */
.bInterfaceCount = 2, // control + data
.bFunctionClass = USB_CLASS_COMM,
.bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bFunctionProtocol = USB_CDC_PROTO_NONE,
/* .iFunction = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = RNDIS_STATUS_INTERVAL_MS,
};
static struct usb_endpoint_descriptor fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *eth_fs_function[] = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &fs_notify_desc,
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &fs_in_desc,
(struct usb_descriptor_header *) &fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
};
static struct usb_endpoint_descriptor hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *eth_hs_function[] = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &hs_notify_desc,
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &hs_in_desc,
(struct usb_descriptor_header *) &hs_out_desc,
NULL,
};
/* super speed support: */
static struct usb_endpoint_descriptor ss_notify_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = USB_MS_TO_HS_INTERVAL(RNDIS_STATUS_INTERVAL_MS)
};
static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
.bLength = sizeof ss_intr_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 3 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
.wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
};
static struct usb_endpoint_descriptor ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
.bLength = sizeof ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_descriptor_header *eth_ss_function[] = {
(struct usb_descriptor_header *) &rndis_iad_descriptor,
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &rndis_acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &ss_notify_desc,
(struct usb_descriptor_header *) &ss_intr_comp_desc,
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &ss_in_desc,
(struct usb_descriptor_header *) &ss_bulk_comp_desc,
(struct usb_descriptor_header *) &ss_out_desc,
(struct usb_descriptor_header *) &ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string rndis_string_defs[] = {
[0].s = "RNDIS Communications Control",
[1].s = "RNDIS Ethernet Data",
[2].s = "RNDIS",
{ } /* end of list */
};
static struct usb_gadget_strings rndis_string_table = {
.language = 0x0409, /* en-us */
.strings = rndis_string_defs,
};
static struct usb_gadget_strings *rndis_strings[] = {
&rndis_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static struct sk_buff *rndis_add_header(struct gether *port,
struct sk_buff *skb)
{
struct sk_buff *skb2;
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
struct rndis_packet_msg_type *header = NULL;
struct f_rndis *rndis = func_to_rndis(&port->func);
if (rndis->port.multi_pkt_xfer) {
if (port->header) {
header = port->header;
memset(header, 0, sizeof(*header));
header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
header->MessageLength = cpu_to_le32(skb->len +
sizeof(*header));
header->DataOffset = cpu_to_le32(36);
header->DataLength = cpu_to_le32(skb->len);
pr_debug("MessageLength:%d DataLength:%d\n",
header->MessageLength,
header->DataLength);
return skb;
} else {
pr_err("RNDIS header is NULL.\n");
return NULL;
}
} else
#endif
{
skb2 = skb_realloc_headroom(skb,
sizeof(struct rndis_packet_msg_type));
if (skb2)
rndis_add_hdr(skb2);
dev_kfree_skb_any(skb);
return skb2;
}
}
static void rndis_response_available(void *_rndis)
{
struct f_rndis *rndis = _rndis;
struct usb_request *req = rndis->notify_req;
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
__le32 *data = req->buf;
int status;
if (atomic_inc_return(&rndis->notify_count) != 1)
return;
if (!rndis->notify->driver_data)
return;
/* Send RNDIS RESPONSE_AVAILABLE notification; a
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
*
* This is the only notification defined by RNDIS.
*/
data[0] = cpu_to_le32(1);
data[1] = cpu_to_le32(0);
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
if (status) {
atomic_dec(&rndis->notify_count);
DBG(cdev, "notify/0 --> %d\n", status);
}
}
static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_rndis *rndis = req->context;
struct usb_composite_dev *cdev;
int status = req->status;
if (!rndis->port.func.config || !rndis->port.func.config->cdev)
return;
else
cdev = rndis->port.func.config->cdev;
/* after TX:
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
*/
switch (status) {
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
atomic_set(&rndis->notify_count, 0);
break;
default:
DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
ep->name, status,
req->actual, req->length);
/* FALLTHROUGH */
case 0:
if (ep != rndis->notify)
break;
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
* notifications by resending until we're done
*/
if (atomic_dec_and_test(&rndis->notify_count))
break;
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
if (status) {
atomic_dec(&rndis->notify_count);
DBG(cdev, "notify/1 --> %d\n", status);
}
break;
}
}
static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_rndis *rndis = req->context;
int status;
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
struct usb_composite_dev *cdev;
rndis_init_msg_type *buf;
if (!rndis->port.func.config || !rndis->port.func.config->cdev)
return;
else
cdev = rndis->port.func.config->cdev;
#endif
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
// spin_lock(&dev->lock);
status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
if (status < 0)
pr_err("RNDIS command error %d, %d/%d\n",
status, req->actual, req->length);
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
buf = (rndis_init_msg_type *)req->buf;
if (buf->MessageType == RNDIS_MSG_INIT) {
if (buf->MaxTransferSize > 2048)
rndis->port.multi_pkt_xfer = 1;
else
rndis->port.multi_pkt_xfer = 0;
DBG(cdev, "%s: MaxTransferSize: %d : Multi_pkt_txr: %s\n",
__func__, buf->MaxTransferSize,
rndis->port.multi_pkt_xfer ? "enabled" :
"disabled");
if (rndis_dl_max_pkt_per_xfer <= 1)
rndis->port.multi_pkt_xfer = 0;
}
#endif
// spin_unlock(&dev->lock);
}
static int
rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
/* RNDIS uses the CDC command encapsulation mechanism to implement
* an RPC scheme, with much getting/setting of attributes by OID.
*/
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SEND_ENCAPSULATED_COMMAND:
if (w_value || w_index != rndis->ctrl_id)
goto invalid;
/* read the request; process it later */
value = w_length;
req->complete = rndis_command_complete;
req->context = rndis;
/* later, rndis_response_available() sends a notification */
break;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_GET_ENCAPSULATED_RESPONSE:
if (w_value || w_index != rndis->ctrl_id)
goto invalid;
else {
u8 *buf;
u32 n;
/* return the result */
buf = rndis_get_next_response(rndis->config, &n);
if (buf) {
memcpy(req->buf, buf, n);
req->complete = rndis_response_complete;
req->context = rndis;
rndis_free_response(rndis->config, buf);
value = n;
}
/* else stalls ... spec says to avoid that */
}
break;
default:
invalid:
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = (value < w_length);
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "rndis response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0 */
if (intf == rndis->ctrl_id) {
if (rndis->notify->driver_data) {
VDBG(cdev, "reset rndis control %d\n", intf);
usb_ep_disable(rndis->notify);
}
if (!rndis->notify->desc) {
VDBG(cdev, "init rndis ctrl %d\n", intf);
if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
goto fail;
}
usb_ep_enable(rndis->notify);
rndis->notify->driver_data = rndis;
} else if (intf == rndis->data_id) {
struct net_device *net;
if (rndis->port.in_ep->driver_data) {
DBG(cdev, "reset rndis\n");
gether_disconnect(&rndis->port);
}
if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
DBG(cdev, "init rndis\n");
if (config_ep_by_speed(cdev->gadget, f,
rndis->port.in_ep) ||
config_ep_by_speed(cdev->gadget, f,
rndis->port.out_ep)) {
rndis->port.in_ep->desc = NULL;
rndis->port.out_ep->desc = NULL;
goto fail;
}
}
/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
/* RNDIS should be in the "RNDIS uninitialized" state,
* either never activated or after rndis_uninit().
*
* We don't want data to flow here until a nonzero packet
* filter is set, at which point it enters "RNDIS data
* initialized" state ... but we do want the endpoints
* to be activated. It's a strange little state.
*
* REVISIT the RNDIS gadget code has done this wrong for a
* very long time. We need another call to the link layer
* code -- gether_updown(...bool) maybe -- to do it right.
*/
rndis->port.cdc_filter = 0;
DBG(cdev, "RNDIS RX/TX early activation ... \n");
net = gether_connect(&rndis->port);
if (IS_ERR(net))
return PTR_ERR(net);
rndis_set_param_dev(rndis->config, net,
&rndis->port.cdc_filter);
} else
goto fail;
return 0;
fail:
return -EINVAL;
}
static void rndis_disable(struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
if (!rndis->notify->driver_data)
return;
DBG(cdev, "rndis deactivated\n");
rndis_uninit(rndis->config);
gether_disconnect(&rndis->port);
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
}
/*-------------------------------------------------------------------------*/
/*
* This isn't quite the same mechanism as CDC Ethernet, since the
* notification scheme passes less data, but the same set of link
* states must be tested. A key difference is that altsettings are
* not used to tell whether the link should send packets or not.
*/
static void rndis_open(struct gether *geth)
{
struct f_rndis *rndis = func_to_rndis(&geth->func);
struct usb_composite_dev *cdev = geth->func.config->cdev;
DBG(cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
bitrate(cdev->gadget) / 100);
rndis_signal_connect(rndis->config);
}
static void rndis_close(struct gether *geth)
{
struct f_rndis *rndis = func_to_rndis(&geth->func);
DBG(geth->func.config->cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
rndis_signal_disconnect(rndis->config);
}
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
static int
rndis_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_rndis *rndis = func_to_rndis(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->ctrl_id = status;
rndis_iad_descriptor.bFirstInterface = status;
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->data_id = status;
rndis_data_intf.bInterfaceNumber = status;
rndis_union_desc.bSlaveInterface0 = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
goto fail;
rndis->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
goto fail;
rndis->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is, strictly speaking,
* optional. We don't treat it that way though! It's simpler,
* and some newer profiles don't treat it as optional.
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
if (!ep)
goto fail;
rndis->notify = ep;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* allocate notification request and buffer */
rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!rndis->notify_req)
goto fail;
rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
if (!rndis->notify_req->buf)
goto fail;
rndis->notify_req->length = STATUS_BYTECOUNT;
rndis->notify_req->context = rndis;
rndis->notify_req->complete = rndis_response_complete;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
hs_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
hs_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
hs_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
ss_in_desc.bEndpointAddress = fs_in_desc.bEndpointAddress;
ss_out_desc.bEndpointAddress = fs_out_desc.bEndpointAddress;
ss_notify_desc.bEndpointAddress = fs_notify_desc.bEndpointAddress;
status = usb_assign_descriptors(f, eth_fs_function, eth_hs_function,
eth_ss_function);
if (status)
goto fail;
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
status = rndis_register(rndis_response_available, rndis);
if (status < 0)
goto fail;
rndis->config = status;
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
rndis_set_max_pkt_xfer(rndis->config, rndis_ul_max_pkt_per_xfer);
#endif
if (rndis->manufacturer && rndis->vendorID &&
rndis_set_param_vendor(rndis->config, rndis->vendorID,
rndis->manufacturer))
goto fail;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
fail:
usb_free_all_descriptors(f);
if (rndis->notify_req) {
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
}
/* we might as well release our claims on endpoints */
if (rndis->notify)
rndis->notify->driver_data = NULL;
if (rndis->port.out_ep)
rndis->port.out_ep->driver_data = NULL;
if (rndis->port.in_ep)
rndis->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
rndis_deregister(rndis->config);
rndis_exit();
rndis_string_defs[0].id = 0;
usb_free_all_descriptors(f);
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
kfree(rndis);
}
/* Some controllers can't support RNDIS ... */
static inline bool can_support_rndis(struct usb_configuration *c)
{
/* everything else is *presumably* fine */
return true;
}
int
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
u32 vendorID, const char *manufacturer, struct eth_dev *dev)
{
struct f_rndis *rndis;
int status;
if (!can_support_rndis(c) || !ethaddr)
return -EINVAL;
/* setup RNDIS itself */
status = rndis_init();
if (status < 0)
return status;
if (rndis_string_defs[0].id == 0) {
status = usb_string_ids_tab(c->cdev, rndis_string_defs);
if (status)
return status;
rndis_control_intf.iInterface = rndis_string_defs[0].id;
rndis_data_intf.iInterface = rndis_string_defs[1].id;
rndis_iad_descriptor.iFunction = rndis_string_defs[2].id;
}
/* allocate and initialize one new instance */
status = -ENOMEM;
rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
if (!rndis)
goto fail;
memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
rndis->vendorID = vendorID;
rndis->manufacturer = manufacturer;
rndis->port.ioport = dev;
/* RNDIS activates when the host changes this filter */
rndis->port.cdc_filter = 0;
/* RNDIS has special (and complex) framing */
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
rndis->port.wrap = rndis_add_header;
rndis->port.unwrap = rndis_rm_hdr;
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
rndis->port.ul_max_pkts_per_xfer = rndis_ul_max_pkt_per_xfer;
rndis->port.dl_max_pkts_per_xfer = rndis_dl_max_pkt_per_xfer;
#endif
rndis->port.func.name = "rndis";
rndis->port.func.strings = rndis_strings;
/* descriptors are per-instance copies */
rndis->port.func.bind = rndis_bind;
rndis->port.func.unbind = rndis_unbind;
rndis->port.func.set_alt = rndis_set_alt;
rndis->port.func.setup = rndis_setup;
rndis->port.func.disable = rndis_disable;
status = usb_add_function(c, &rndis->port.func);
if (status) {
kfree(rndis);
fail:
rndis_exit();
}
return status;
}

View file

@ -0,0 +1,388 @@
/*
* f_serial.c - generic USB serial function driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This function packages a simple "generic serial" port with no real
* control mechanisms, just raw data transfer over two bulk endpoints.
*
* Because it's not standardized, this isn't as interoperable as the
* CDC ACM driver. However, for many purposes it's just as functional
* if you can arrange appropriate host side drivers.
*/
struct f_gser {
struct gserial port;
u8 data_id;
u8 port_num;
};
static inline struct f_gser *func_to_gser(struct usb_function *f)
{
return container_of(f, struct f_gser, port.func);
}
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
static struct usb_interface_descriptor gser_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor gser_fs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor gser_fs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *gser_fs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_fs_in_desc,
(struct usb_descriptor_header *) &gser_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor gser_hs_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor gser_hs_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *gser_hs_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_hs_in_desc,
(struct usb_descriptor_header *) &gser_hs_out_desc,
NULL,
};
static struct usb_endpoint_descriptor gser_ss_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor gser_ss_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
.bLength = sizeof gser_ss_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
};
static struct usb_descriptor_header *gser_ss_function[] = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_ss_in_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
(struct usb_descriptor_header *) &gser_ss_out_desc,
(struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string gser_string_defs[] = {
[0].s = "Generic Serial",
{ } /* end of list */
};
static struct usb_gadget_strings gser_string_table = {
.language = 0x0409, /* en-us */
.strings = gser_string_defs,
};
static struct usb_gadget_strings *gser_strings[] = {
&gser_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0, so this is an activation or a reset */
if (gser->port.in->driver_data) {
dev_dbg(&cdev->gadget->dev,
"reset generic ttyGS%d\n", gser->port_num);
gserial_disconnect(&gser->port);
}
if (!gser->port.in->desc || !gser->port.out->desc) {
dev_dbg(&cdev->gadget->dev,
"activate generic ttyGS%d\n", gser->port_num);
if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
gser->port.in->desc = NULL;
gser->port.out->desc = NULL;
return -EINVAL;
}
}
gserial_connect(&gser->port, gser->port_num);
return 0;
}
static void gser_disable(struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
dev_dbg(&cdev->gadget->dev,
"generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
}
/*-------------------------------------------------------------------------*/
/* serial function driver setup/binding */
static int gser_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_gser *gser = func_to_gser(f);
int status;
struct usb_ep *ep;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string ID */
if (gser_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
gser_string_defs[0].id = status;
}
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
gser->data_id = status;
gser_interface_desc.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
if (!ep)
goto fail;
gser->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
if (!ep)
goto fail;
gser->port.out = ep;
ep->driver_data = cdev; /* claim */
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
gser_ss_function);
if (status)
goto fail;
dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
gser->port_num,
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
gser->port.in->name, gser->port.out->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (gser->port.out)
gser->port.out->driver_data = NULL;
if (gser->port.in)
gser->port.in->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_serial_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_serial_opts);
static ssize_t f_serial_attr_show(struct config_item *item,
struct configfs_attribute *attr,
char *page)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
struct f_serial_opts_attribute *f_serial_opts_attr =
container_of(attr, struct f_serial_opts_attribute, attr);
ssize_t ret = 0;
if (f_serial_opts_attr->show)
ret = f_serial_opts_attr->show(opts, page);
return ret;
}
static void serial_attr_release(struct config_item *item)
{
struct f_serial_opts *opts = to_f_serial_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations serial_item_ops = {
.release = serial_attr_release,
.show_attribute = f_serial_attr_show,
};
static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page)
{
return sprintf(page, "%u\n", opts->port_num);
}
static struct f_serial_opts_attribute f_serial_port_num =
__CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show);
static struct configfs_attribute *acm_attrs[] = {
&f_serial_port_num.attr,
NULL,
};
static struct config_item_type serial_func_type = {
.ct_item_ops = &serial_item_ops,
.ct_attrs = acm_attrs,
.ct_owner = THIS_MODULE,
};
static void gser_free_inst(struct usb_function_instance *f)
{
struct f_serial_opts *opts;
opts = container_of(f, struct f_serial_opts, func_inst);
gserial_free_line(opts->port_num);
kfree(opts);
}
static struct usb_function_instance *gser_alloc_inst(void)
{
struct f_serial_opts *opts;
int ret;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = gser_free_inst;
ret = gserial_alloc_line(&opts->port_num);
if (ret) {
kfree(opts);
return ERR_PTR(ret);
}
config_group_init_type_name(&opts->func_inst.group, "",
&serial_func_type);
return &opts->func_inst;
}
static void gser_free(struct usb_function *f)
{
struct f_gser *serial;
serial = func_to_gser(f);
kfree(serial);
}
static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
usb_free_all_descriptors(f);
}
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
struct f_serial_opts *opts;
/* allocate and initialize one new instance */
gser = kzalloc(sizeof(*gser), GFP_KERNEL);
if (!gser)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_serial_opts, func_inst);
gser->port_num = opts->port_num;
gser->port.func.name = "gser";
gser->port.func.strings = gser_strings;
gser->port.func.bind = gser_bind;
gser->port.func.unbind = gser_unbind;
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
return &gser->port.func;
}
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,518 @@
/*
* f_subset.c -- "CDC Subset" Ethernet link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
#include "u_ether_configfs.h"
#include "u_gether.h"
/*
* This function packages a simple "CDC Subset" Ethernet port with no real
* control mechanisms; just raw data transfer over two bulk endpoints.
* The data transfer model is exactly that of CDC Ethernet, which is
* why we call it the "CDC Subset".
*
* Because it's not standardized, this has some interoperability issues.
* They mostly relate to driver binding, since the data transfer model is
* so simple (CDC Ethernet). The original versions of this protocol used
* specific product/vendor IDs: byteswapped IDs for Digital Equipment's
* SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
* daughtercards with USB peripheral connectors. (It was used more often
* with other boards, using the Itsy identifiers.) Linux hosts recognized
* this with CONFIG_USB_ARMLINUX; these devices have only one configuration
* and one interface.
*
* At some point, MCCI defined a (nonconformant) CDC MDLM variant called
* "SAFE", which happens to have a mode which is identical to the "CDC
* Subset" in terms of data transfer and lack of control model. This was
* adopted by later Sharp Zaurus models, and by some other software which
* Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
*
* Because Microsoft's RNDIS drivers are far from robust, we added a few
* descriptors to the CDC Subset code, making this code look like a SAFE
* implementation. This lets you use MCCI's host side MS-Windows drivers
* if you get fed up with RNDIS. It also makes it easier for composite
* drivers to work, since they can use class based binding instead of
* caring about specific product and vendor IDs.
*/
struct f_gether {
struct gether port;
char ethaddr[14];
};
static inline struct f_gether *func_to_geth(struct usb_function *f)
{
return container_of(f, struct f_gether, port.func);
}
/*-------------------------------------------------------------------------*/
/*
* "Simple" CDC-subset option is a simple vendor-neutral model that most
* full speed controllers can handle: one interface, two bulk endpoints.
* To assist host side drivers, we fancy it up a bit, and add descriptors so
* some host side drivers will understand it as a "SAFE" variant.
*
* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
* Data endpoints live in the control interface, there's no data interface.
* And it's not used to talk to a cell phone radio.
*/
/* interface descriptor: */
static struct usb_interface_descriptor subset_data_intf = {
.bLength = sizeof subset_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc mdlm_header_desc = {
.bLength = sizeof mdlm_header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = cpu_to_le16(0x0110),
};
static struct usb_cdc_mdlm_desc mdlm_desc = {
.bLength = sizeof mdlm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_MDLM_TYPE,
.bcdVersion = cpu_to_le16(0x0100),
.bGUID = {
0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
},
};
/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
* can't really use its struct. All we do here is say that we're using
* the submode of "SAFE" which directly matches the CDC Subset.
*/
static u8 mdlm_detail_desc[] = {
6,
USB_DT_CS_INTERFACE,
USB_CDC_MDLM_DETAIL_TYPE,
0, /* "SAFE" */
0, /* network control capabilities (none) */
0, /* network data capabilities ("raw" encapsulation) */
};
static struct usb_cdc_ether_desc ether_desc = {
.bLength = sizeof ether_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = cpu_to_le32(0), /* no statistics */
.wMaxSegmentSize = cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_subset_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_subset_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_eth_function[] = {
(struct usb_descriptor_header *) &subset_data_intf,
(struct usb_descriptor_header *) &mdlm_header_desc,
(struct usb_descriptor_header *) &mdlm_desc,
(struct usb_descriptor_header *) &mdlm_detail_desc,
(struct usb_descriptor_header *) &ether_desc,
(struct usb_descriptor_header *) &fs_subset_in_desc,
(struct usb_descriptor_header *) &fs_subset_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_subset_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_subset_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_eth_function[] = {
(struct usb_descriptor_header *) &subset_data_intf,
(struct usb_descriptor_header *) &mdlm_header_desc,
(struct usb_descriptor_header *) &mdlm_desc,
(struct usb_descriptor_header *) &mdlm_detail_desc,
(struct usb_descriptor_header *) &ether_desc,
(struct usb_descriptor_header *) &hs_subset_in_desc,
(struct usb_descriptor_header *) &hs_subset_out_desc,
NULL,
};
/* super speed support: */
static struct usb_endpoint_descriptor ss_subset_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_endpoint_descriptor ss_subset_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
static struct usb_ss_ep_comp_descriptor ss_subset_bulk_comp_desc = {
.bLength = sizeof ss_subset_bulk_comp_desc,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* the following 2 values can be tweaked if necessary */
/* .bMaxBurst = 0, */
/* .bmAttributes = 0, */
};
static struct usb_descriptor_header *ss_eth_function[] = {
(struct usb_descriptor_header *) &subset_data_intf,
(struct usb_descriptor_header *) &mdlm_header_desc,
(struct usb_descriptor_header *) &mdlm_desc,
(struct usb_descriptor_header *) &mdlm_detail_desc,
(struct usb_descriptor_header *) &ether_desc,
(struct usb_descriptor_header *) &ss_subset_in_desc,
(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
(struct usb_descriptor_header *) &ss_subset_out_desc,
(struct usb_descriptor_header *) &ss_subset_bulk_comp_desc,
NULL,
};
/* string descriptors: */
static struct usb_string geth_string_defs[] = {
[0].s = "CDC Ethernet Subset/SAFE",
[1].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings geth_string_table = {
.language = 0x0409, /* en-us */
.strings = geth_string_defs,
};
static struct usb_gadget_strings *geth_strings[] = {
&geth_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gether *geth = func_to_geth(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct net_device *net;
/* we know alt == 0, so this is an activation or a reset */
if (geth->port.in_ep->driver_data) {
DBG(cdev, "reset cdc subset\n");
gether_disconnect(&geth->port);
}
DBG(cdev, "init + activate cdc subset\n");
if (config_ep_by_speed(cdev->gadget, f, geth->port.in_ep) ||
config_ep_by_speed(cdev->gadget, f, geth->port.out_ep)) {
geth->port.in_ep->desc = NULL;
geth->port.out_ep->desc = NULL;
return -EINVAL;
}
net = gether_connect(&geth->port);
return PTR_ERR_OR_ZERO(net);
}
static void geth_disable(struct usb_function *f)
{
struct f_gether *geth = func_to_geth(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "net deactivated\n");
gether_disconnect(&geth->port);
}
/*-------------------------------------------------------------------------*/
/* serial function driver setup/binding */
static int
geth_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_gether *geth = func_to_geth(f);
struct usb_string *us;
int status;
struct usb_ep *ep;
struct f_gether_opts *gether_opts;
gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
/*
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
* configurations are bound in sequence with list_for_each_entry,
* in each configuration its functions are bound in sequence
* with list_for_each_entry, so we assume no race condition
* with regard to gether_opts->bound access
*/
if (!gether_opts->bound) {
mutex_lock(&gether_opts->lock);
gether_set_gadget(gether_opts->net, cdev->gadget);
status = gether_register_netdev(gether_opts->net);
mutex_unlock(&gether_opts->lock);
if (status)
return status;
gether_opts->bound = true;
}
us = usb_gstrings_attach(cdev, geth_strings,
ARRAY_SIZE(geth_string_defs));
if (IS_ERR(us))
return PTR_ERR(us);
subset_data_intf.iInterface = us[0].id;
ether_desc.iMACAddress = us[1].id;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
subset_data_intf.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_in_desc);
if (!ep)
goto fail;
geth->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc);
if (!ep)
goto fail;
geth->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
hs_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
hs_subset_out_desc.bEndpointAddress =
fs_subset_out_desc.bEndpointAddress;
ss_subset_in_desc.bEndpointAddress = fs_subset_in_desc.bEndpointAddress;
ss_subset_out_desc.bEndpointAddress =
fs_subset_out_desc.bEndpointAddress;
status = usb_assign_descriptors(f, fs_eth_function, hs_eth_function,
ss_eth_function);
if (status)
goto fail;
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
gadget_is_superspeed(c->cdev->gadget) ? "super" :
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (geth->port.out_ep)
geth->port.out_ep->driver_data = NULL;
if (geth->port.in_ep)
geth->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_gether_opts,
func_inst.group);
}
/* f_gether_item_ops */
USB_ETHERNET_CONFIGFS_ITEM(gether);
/* f_gether_opts_dev_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
/* f_gether_opts_host_addr */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
/* f_gether_opts_qmult */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
/* f_gether_opts_ifname */
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
static struct configfs_attribute *gether_attrs[] = {
&f_gether_opts_dev_addr.attr,
&f_gether_opts_host_addr.attr,
&f_gether_opts_qmult.attr,
&f_gether_opts_ifname.attr,
NULL,
};
static struct config_item_type gether_func_type = {
.ct_item_ops = &gether_item_ops,
.ct_attrs = gether_attrs,
.ct_owner = THIS_MODULE,
};
static void geth_free_inst(struct usb_function_instance *f)
{
struct f_gether_opts *opts;
opts = container_of(f, struct f_gether_opts, func_inst);
if (opts->bound)
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
kfree(opts);
}
static struct usb_function_instance *geth_alloc_inst(void)
{
struct f_gether_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = geth_free_inst;
opts->net = gether_setup_default();
if (IS_ERR(opts->net)) {
struct net_device *net = opts->net;
kfree(opts);
return ERR_CAST(net);
}
config_group_init_type_name(&opts->func_inst.group, "",
&gether_func_type);
return &opts->func_inst;
}
static void geth_free(struct usb_function *f)
{
struct f_gether *eth;
eth = func_to_geth(f);
kfree(eth);
}
static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
{
geth_string_defs[0].id = 0;
usb_free_all_descriptors(f);
}
static struct usb_function *geth_alloc(struct usb_function_instance *fi)
{
struct f_gether *geth;
struct f_gether_opts *opts;
int status;
/* allocate and initialize one new instance */
geth = kzalloc(sizeof(*geth), GFP_KERNEL);
if (!geth)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_gether_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
sizeof(geth->ethaddr));
if (status < 12) {
kfree(geth);
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
geth_string_defs[1].s = geth->ethaddr;
geth->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
geth->port.cdc_filter = DEFAULT_FILTER;
geth->port.func.name = "cdc_subset";
geth->port.func.bind = geth_bind;
geth->port.func.unbind = geth_unbind;
geth->port.func.set_alt = geth_set_alt;
geth->port.func.disable = geth_disable;
geth->port.func.free_func = geth_free;
return &geth->port.func;
}
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");

View file

@ -0,0 +1,985 @@
/*
* f_audio.c -- USB Audio class function driver
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/atomic.h>
#include "u_uac1.h"
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value);
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
/*
* DESCRIPTORS ... most are static, but strings and full
* configuration descriptors are built on demand.
*/
/*
* We have two interfaces- AudioControl and AudioStreaming
* TODO: only supcard playback currently
*/
#define F_AUDIO_AC_INTERFACE 0
#define F_AUDIO_AS_INTERFACE 1
#define F_AUDIO_NUM_INTERFACES 2
/* B.3.1 Standard AC Interface Descriptor */
static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
};
DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
#define UAC_DT_AC_HEADER_LENGTH UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
/* 1 input terminal, 1 output terminal and 1 feature unit */
#define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
/* B.3.2 Class-Specific AC Interface Descriptor */
static struct uac1_ac_header_descriptor_2 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_LENGTH,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_HEADER,
.bcdADC = __constant_cpu_to_le16(0x0100),
.wTotalLength = __constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
.bInCollection = F_AUDIO_NUM_INTERFACES,
.baInterfaceNr = {
[0] = F_AUDIO_AC_INTERFACE,
[1] = F_AUDIO_AS_INTERFACE,
}
};
#define INPUT_TERMINAL_ID 1
static struct uac_input_terminal_descriptor input_terminal_desc = {
.bLength = UAC_DT_INPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_INPUT_TERMINAL,
.bTerminalID = INPUT_TERMINAL_ID,
.wTerminalType = UAC_TERMINAL_STREAMING,
.bAssocTerminal = 0,
.wChannelConfig = 0x3,
};
DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0);
#define FEATURE_UNIT_ID 2
static struct uac_feature_unit_descriptor_0 feature_unit_desc = {
.bLength = UAC_DT_FEATURE_UNIT_SIZE(0),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FEATURE_UNIT,
.bUnitID = FEATURE_UNIT_ID,
.bSourceID = INPUT_TERMINAL_ID,
.bControlSize = 2,
.bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME),
};
static struct usb_audio_control mute_control = {
.list = LIST_HEAD_INIT(mute_control.list),
.name = "Mute Control",
.type = UAC_FU_MUTE,
/* Todo: add real Mute control code */
.set = generic_set_cmd,
.get = generic_get_cmd,
};
static struct usb_audio_control volume_control = {
.list = LIST_HEAD_INIT(volume_control.list),
.name = "Volume Control",
.type = UAC_FU_VOLUME,
/* Todo: add real Volume control code */
.set = generic_set_cmd,
.get = generic_get_cmd,
};
static struct usb_audio_control_selector feature_unit = {
.list = LIST_HEAD_INIT(feature_unit.list),
.id = FEATURE_UNIT_ID,
.name = "Mute & Volume Control",
.type = UAC_FEATURE_UNIT,
.desc = (struct usb_descriptor_header *)&feature_unit_desc,
};
#define OUTPUT_TERMINAL_ID 3
static struct uac1_output_terminal_descriptor output_terminal_desc = {
.bLength = UAC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_OUTPUT_TERMINAL,
.bTerminalID = OUTPUT_TERMINAL_ID,
.wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER,
.bAssocTerminal = FEATURE_UNIT_ID,
.bSourceID = FEATURE_UNIT_ID,
};
/* B.4.1 Standard AS Interface Descriptor */
static struct usb_interface_descriptor as_interface_alt_0_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
static struct usb_interface_descriptor as_interface_alt_1_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_AUDIO,
.bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING,
};
/* B.4.2 Class-Specific AS Interface Descriptor */
static struct uac1_as_header_descriptor as_header_desc = {
.bLength = UAC_DT_AS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_AS_GENERAL,
.bTerminalLink = INPUT_TERMINAL_ID,
.bDelay = 1,
.wFormatTag = UAC_FORMAT_TYPE_I_PCM,
};
DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1);
static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = {
.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = UAC_FORMAT_TYPE,
.bFormatType = UAC_FORMAT_TYPE_I,
.bSubframeSize = 2,
.bBitResolution = 16,
.bSamFreqType = 1,
};
/* Standard ISO OUT Endpoint Descriptor */
static struct usb_endpoint_descriptor as_out_ep_desc = {
.bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE
| USB_ENDPOINT_XFER_ISOC,
.wMaxPacketSize = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE),
.bInterval = 4,
};
/* Class-specific AS ISO OUT Endpoint Descriptor */
static struct uac_iso_endpoint_descriptor as_iso_out_desc = {
.bLength = UAC_ISO_ENDPOINT_DESC_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubtype = UAC_EP_GENERAL,
.bmAttributes = 1,
.bLockDelayUnits = 1,
.wLockDelay = __constant_cpu_to_le16(1),
};
static struct usb_descriptor_header *f_audio_desc[] = {
(struct usb_descriptor_header *)&ac_interface_desc,
(struct usb_descriptor_header *)&ac_header_desc,
(struct usb_descriptor_header *)&input_terminal_desc,
(struct usb_descriptor_header *)&output_terminal_desc,
(struct usb_descriptor_header *)&feature_unit_desc,
(struct usb_descriptor_header *)&as_interface_alt_0_desc,
(struct usb_descriptor_header *)&as_interface_alt_1_desc,
(struct usb_descriptor_header *)&as_header_desc,
(struct usb_descriptor_header *)&as_type_i_desc,
(struct usb_descriptor_header *)&as_out_ep_desc,
(struct usb_descriptor_header *)&as_iso_out_desc,
NULL,
};
enum {
STR_AC_IF,
STR_INPUT_TERMINAL,
STR_INPUT_TERMINAL_CH_NAMES,
STR_FEAT_DESC_0,
STR_OUTPUT_TERMINAL,
STR_AS_IF_ALT0,
STR_AS_IF_ALT1,
};
static struct usb_string strings_uac1[] = {
[STR_AC_IF].s = "AC Interface",
[STR_INPUT_TERMINAL].s = "Input terminal",
[STR_INPUT_TERMINAL_CH_NAMES].s = "Channels",
[STR_FEAT_DESC_0].s = "Volume control & mute",
[STR_OUTPUT_TERMINAL].s = "Output terminal",
[STR_AS_IF_ALT0].s = "AS Interface",
[STR_AS_IF_ALT1].s = "AS Interface",
{ },
};
static struct usb_gadget_strings str_uac1 = {
.language = 0x0409, /* en-us */
.strings = strings_uac1,
};
static struct usb_gadget_strings *uac1_strings[] = {
&str_uac1,
NULL,
};
/*
* This function is an ALSA sound card following USB Audio Class Spec 1.0.
*/
/*-------------------------------------------------------------------------*/
struct f_audio_buf {
u8 *buf;
int actual;
struct list_head list;
};
static struct f_audio_buf *f_audio_buffer_alloc(int buf_size)
{
struct f_audio_buf *copy_buf;
copy_buf = kzalloc(sizeof *copy_buf, GFP_ATOMIC);
if (!copy_buf)
return ERR_PTR(-ENOMEM);
copy_buf->buf = kzalloc(buf_size, GFP_ATOMIC);
if (!copy_buf->buf) {
kfree(copy_buf);
return ERR_PTR(-ENOMEM);
}
return copy_buf;
}
static void f_audio_buffer_free(struct f_audio_buf *audio_buf)
{
kfree(audio_buf->buf);
kfree(audio_buf);
}
/*-------------------------------------------------------------------------*/
struct f_audio {
struct gaudio card;
/* endpoints handle full and/or high speeds */
struct usb_ep *out_ep;
spinlock_t lock;
struct f_audio_buf *copy_buf;
struct work_struct playback_work;
struct list_head play_queue;
/* Control Set command */
struct list_head cs;
u8 set_cmd;
struct usb_audio_control *set_con;
};
static inline struct f_audio *func_to_audio(struct usb_function *f)
{
return container_of(f, struct f_audio, card.func);
}
/*-------------------------------------------------------------------------*/
static void f_audio_playback_work(struct work_struct *data)
{
struct f_audio *audio = container_of(data, struct f_audio,
playback_work);
struct f_audio_buf *play_buf;
spin_lock_irq(&audio->lock);
if (list_empty(&audio->play_queue)) {
spin_unlock_irq(&audio->lock);
return;
}
play_buf = list_first_entry(&audio->play_queue,
struct f_audio_buf, list);
list_del(&play_buf->list);
spin_unlock_irq(&audio->lock);
u_audio_playback(&audio->card, play_buf->buf, play_buf->actual);
f_audio_buffer_free(play_buf);
}
static int f_audio_out_ep_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
struct usb_composite_dev *cdev = audio->card.func.config->cdev;
struct f_audio_buf *copy_buf = audio->copy_buf;
struct f_uac1_opts *opts;
int audio_buf_size;
int err;
opts = container_of(audio->card.func.fi, struct f_uac1_opts,
func_inst);
audio_buf_size = opts->audio_buf_size;
if (!copy_buf)
return -EINVAL;
/* Copy buffer is full, add it to the play_queue */
if (audio_buf_size - copy_buf->actual < req->actual) {
list_add_tail(&copy_buf->list, &audio->play_queue);
schedule_work(&audio->playback_work);
copy_buf = f_audio_buffer_alloc(audio_buf_size);
if (IS_ERR(copy_buf))
return -ENOMEM;
}
memcpy(copy_buf->buf + copy_buf->actual, req->buf, req->actual);
copy_buf->actual += req->actual;
audio->copy_buf = copy_buf;
err = usb_ep_queue(ep, req, GFP_ATOMIC);
if (err)
ERROR(cdev, "%s queue req: %d\n", ep->name, err);
return 0;
}
static void f_audio_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_audio *audio = req->context;
int status = req->status;
u32 data = 0;
struct usb_ep *out_ep = audio->out_ep;
switch (status) {
case 0: /* normal completion? */
if (ep == out_ep)
f_audio_out_ep_complete(ep, req);
else if (audio->set_con) {
memcpy(&data, req->buf, req->length);
audio->set_con->set(audio->set_con, audio->set_cmd,
le16_to_cpu(data));
audio->set_con = NULL;
}
break;
default:
break;
}
}
static int audio_set_intf_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct f_audio *audio = func_to_audio(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 con_sel = (w_value >> 8) & 0xFF;
u8 cmd = (ctrl->bRequest & 0x0F);
struct usb_audio_control_selector *cs;
struct usb_audio_control *con;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
ctrl->bRequest, w_value, len, id);
list_for_each_entry(cs, &audio->cs, list) {
if (cs->id == id) {
list_for_each_entry(con, &cs->control, list) {
if (con->type == con_sel) {
audio->set_con = con;
break;
}
}
break;
}
}
audio->set_cmd = cmd;
req->context = audio;
req->complete = f_audio_complete;
return len;
}
static int audio_get_intf_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct f_audio *audio = func_to_audio(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
u8 con_sel = (w_value >> 8) & 0xFF;
u8 cmd = (ctrl->bRequest & 0x0F);
struct usb_audio_control_selector *cs;
struct usb_audio_control *con;
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n",
ctrl->bRequest, w_value, len, id);
list_for_each_entry(cs, &audio->cs, list) {
if (cs->id == id) {
list_for_each_entry(con, &cs->control, list) {
if (con->type == con_sel && con->get) {
value = con->get(con, cmd);
break;
}
}
break;
}
}
req->context = audio;
req->complete = f_audio_complete;
len = min_t(size_t, sizeof(value), len);
memcpy(req->buf, &value, len);
return len;
}
static int audio_set_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
u16 ep = le16_to_cpu(ctrl->wIndex);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_SET_CUR:
value = len;
break;
case UAC_SET_MIN:
break;
case UAC_SET_MAX:
break;
case UAC_SET_RES:
break;
case UAC_SET_MEM:
break;
default:
break;
}
return value;
}
static int audio_get_endpoint_req(struct usb_function *f,
const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
int value = -EOPNOTSUPP;
u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF);
u16 len = le16_to_cpu(ctrl->wLength);
u16 w_value = le16_to_cpu(ctrl->wValue);
DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n",
ctrl->bRequest, w_value, len, ep);
switch (ctrl->bRequest) {
case UAC_GET_CUR:
case UAC_GET_MIN:
case UAC_GET_MAX:
case UAC_GET_RES:
value = len;
break;
case UAC_GET_MEM:
break;
default:
break;
}
return value;
}
static int
f_audio_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything; interface
* activation uses set_alt().
*/
switch (ctrl->bRequestType) {
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = audio_set_intf_req(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE:
value = audio_get_intf_req(f, ctrl);
break;
case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_set_endpoint_req(f, ctrl);
break;
case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT:
value = audio_get_endpoint_req(f, ctrl);
break;
default:
ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "audio req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "audio response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_audio *audio = func_to_audio(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_ep *out_ep = audio->out_ep;
struct usb_request *req;
struct f_uac1_opts *opts;
int req_buf_size, req_count, audio_buf_size;
int i = 0, err = 0;
DBG(cdev, "intf %d, alt %d\n", intf, alt);
opts = container_of(f->fi, struct f_uac1_opts, func_inst);
req_buf_size = opts->req_buf_size;
req_count = opts->req_count;
audio_buf_size = opts->audio_buf_size;
if (intf == 1) {
if (alt == 1) {
usb_ep_enable(out_ep);
out_ep->driver_data = audio;
audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
if (IS_ERR(audio->copy_buf))
return -ENOMEM;
/*
* allocate a bunch of read buffers
* and queue them all at once.
*/
for (i = 0; i < req_count && err == 0; i++) {
req = usb_ep_alloc_request(out_ep, GFP_ATOMIC);
if (req) {
req->buf = kzalloc(req_buf_size,
GFP_ATOMIC);
if (req->buf) {
req->length = req_buf_size;
req->context = audio;
req->complete =
f_audio_complete;
err = usb_ep_queue(out_ep,
req, GFP_ATOMIC);
if (err)
ERROR(cdev,
"%s queue req: %d\n",
out_ep->name, err);
} else
err = -ENOMEM;
} else
err = -ENOMEM;
}
} else {
struct f_audio_buf *copy_buf = audio->copy_buf;
if (copy_buf) {
list_add_tail(&copy_buf->list,
&audio->play_queue);
schedule_work(&audio->playback_work);
}
}
}
return err;
}
static void f_audio_disable(struct usb_function *f)
{
return;
}
/*-------------------------------------------------------------------------*/
static void f_audio_build_desc(struct f_audio *audio)
{
struct gaudio *card = &audio->card;
u8 *sam_freq;
int rate;
/* Set channel numbers */
input_terminal_desc.bNrChannels = u_audio_get_playback_channels(card);
as_type_i_desc.bNrChannels = u_audio_get_playback_channels(card);
/* Set sample rates */
rate = u_audio_get_playback_rate(card);
sam_freq = as_type_i_desc.tSamFreq[0];
memcpy(sam_freq, &rate, 3);
/* Todo: Set Sample bits and other parameters */
return;
}
/* audio function driver setup/binding */
static int
f_audio_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_audio *audio = func_to_audio(f);
struct usb_string *us;
int status;
struct usb_ep *ep = NULL;
struct f_uac1_opts *audio_opts;
audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
audio->card.gadget = c->cdev->gadget;
audio_opts->card = &audio->card;
/* set up ASLA audio devices */
if (!audio_opts->bound) {
status = gaudio_setup(&audio->card);
if (status < 0)
return status;
audio_opts->bound = true;
}
us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1));
if (IS_ERR(us))
return PTR_ERR(us);
ac_interface_desc.iInterface = us[STR_AC_IF].id;
input_terminal_desc.iTerminal = us[STR_INPUT_TERMINAL].id;
input_terminal_desc.iChannelNames = us[STR_INPUT_TERMINAL_CH_NAMES].id;
feature_unit_desc.iFeature = us[STR_FEAT_DESC_0].id;
output_terminal_desc.iTerminal = us[STR_OUTPUT_TERMINAL].id;
as_interface_alt_0_desc.iInterface = us[STR_AS_IF_ALT0].id;
as_interface_alt_1_desc.iInterface = us[STR_AS_IF_ALT1].id;
f_audio_build_desc(audio);
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
ac_interface_desc.bInterfaceNumber = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
as_interface_alt_0_desc.bInterfaceNumber = status;
as_interface_alt_1_desc.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc);
if (!ep)
goto fail;
audio->out_ep = ep;
audio->out_ep->desc = &as_out_ep_desc;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* copy descriptors, and track endpoint copies */
status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL);
if (status)
goto fail;
return 0;
fail:
gaudio_cleanup(&audio->card);
if (ep)
ep->driver_data = NULL;
return status;
}
/*-------------------------------------------------------------------------*/
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
{
con->data[cmd] = value;
return 0;
}
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
{
return con->data[cmd];
}
/* Todo: add more control selecotor dynamically */
static int control_selector_init(struct f_audio *audio)
{
INIT_LIST_HEAD(&audio->cs);
list_add(&feature_unit.list, &audio->cs);
INIT_LIST_HEAD(&feature_unit.control);
list_add(&mute_control.list, &feature_unit.control);
list_add(&volume_control.list, &feature_unit.control);
volume_control.data[UAC__CUR] = 0xffc0;
volume_control.data[UAC__MIN] = 0xe3a0;
volume_control.data[UAC__MAX] = 0xfff0;
volume_control.data[UAC__RES] = 0x0030;
return 0;
}
static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_uac1_opts,
func_inst.group);
}
CONFIGFS_ATTR_STRUCT(f_uac1_opts);
CONFIGFS_ATTR_OPS(f_uac1_opts);
static void f_uac1_attr_release(struct config_item *item)
{
struct f_uac1_opts *opts = to_f_uac1_opts(item);
usb_put_function_instance(&opts->func_inst);
}
static struct configfs_item_operations f_uac1_item_ops = {
.release = f_uac1_attr_release,
.show_attribute = f_uac1_opts_attr_show,
.store_attribute = f_uac1_opts_attr_store,
};
#define UAC1_INT_ATTRIBUTE(name) \
static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%u\n", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \
const char *page, size_t len) \
{ \
int ret; \
u32 num; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto end; \
} \
\
ret = kstrtou32(page, 0, &num); \
if (ret) \
goto end; \
\
opts->name = num; \
ret = len; \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_uac1_opts_attribute f_uac1_opts_##name = \
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
f_uac1_opts_##name##_show, \
f_uac1_opts_##name##_store)
UAC1_INT_ATTRIBUTE(req_buf_size);
UAC1_INT_ATTRIBUTE(req_count);
UAC1_INT_ATTRIBUTE(audio_buf_size);
#define UAC1_STR_ATTRIBUTE(name) \
static ssize_t f_uac1_opts_##name##_show(struct f_uac1_opts *opts, \
char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = sprintf(page, "%s\n", opts->name); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t f_uac1_opts_##name##_store(struct f_uac1_opts *opts, \
const char *page, size_t len) \
{ \
int ret = -EBUSY; \
char *tmp; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) \
goto end; \
\
tmp = kstrndup(page, len, GFP_KERNEL); \
if (tmp) { \
ret = -ENOMEM; \
goto end; \
} \
if (opts->name##_alloc) \
kfree(opts->name); \
opts->name##_alloc = true; \
opts->name = tmp; \
ret = len; \
\
end: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_uac1_opts_attribute f_uac1_opts_##name = \
__CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, \
f_uac1_opts_##name##_show, \
f_uac1_opts_##name##_store)
UAC1_STR_ATTRIBUTE(fn_play);
UAC1_STR_ATTRIBUTE(fn_cap);
UAC1_STR_ATTRIBUTE(fn_cntl);
static struct configfs_attribute *f_uac1_attrs[] = {
&f_uac1_opts_req_buf_size.attr,
&f_uac1_opts_req_count.attr,
&f_uac1_opts_audio_buf_size.attr,
&f_uac1_opts_fn_play.attr,
&f_uac1_opts_fn_cap.attr,
&f_uac1_opts_fn_cntl.attr,
NULL,
};
static struct config_item_type f_uac1_func_type = {
.ct_item_ops = &f_uac1_item_ops,
.ct_attrs = f_uac1_attrs,
.ct_owner = THIS_MODULE,
};
static void f_audio_free_inst(struct usb_function_instance *f)
{
struct f_uac1_opts *opts;
opts = container_of(f, struct f_uac1_opts, func_inst);
gaudio_cleanup(opts->card);
if (opts->fn_play_alloc)
kfree(opts->fn_play);
if (opts->fn_cap_alloc)
kfree(opts->fn_cap);
if (opts->fn_cntl_alloc)
kfree(opts->fn_cntl);
kfree(opts);
}
static struct usb_function_instance *f_audio_alloc_inst(void)
{
struct f_uac1_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = f_audio_free_inst;
config_group_init_type_name(&opts->func_inst.group, "",
&f_uac1_func_type);
opts->req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
opts->req_count = UAC1_REQ_COUNT;
opts->audio_buf_size = UAC1_AUDIO_BUF_SIZE;
opts->fn_play = FILE_PCM_PLAYBACK;
opts->fn_cap = FILE_PCM_CAPTURE;
opts->fn_cntl = FILE_CONTROL;
return &opts->func_inst;
}
static void f_audio_free(struct usb_function *f)
{
struct f_audio *audio = func_to_audio(f);
struct f_uac1_opts *opts;
opts = container_of(f->fi, struct f_uac1_opts, func_inst);
kfree(audio);
mutex_lock(&opts->lock);
--opts->refcnt;
mutex_unlock(&opts->lock);
}
static void f_audio_unbind(struct usb_configuration *c, struct usb_function *f)
{
usb_free_all_descriptors(f);
}
static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
{
struct f_audio *audio;
struct f_uac1_opts *opts;
/* allocate and initialize one new instance */
audio = kzalloc(sizeof(*audio), GFP_KERNEL);
if (!audio)
return ERR_PTR(-ENOMEM);
audio->card.func.name = "g_audio";
opts = container_of(fi, struct f_uac1_opts, func_inst);
mutex_lock(&opts->lock);
++opts->refcnt;
mutex_unlock(&opts->lock);
INIT_LIST_HEAD(&audio->play_queue);
spin_lock_init(&audio->lock);
audio->card.func.bind = f_audio_bind;
audio->card.func.unbind = f_audio_unbind;
audio->card.func.set_alt = f_audio_set_alt;
audio->card.func.setup = f_audio_setup;
audio->card.func.disable = f_audio_disable;
audio->card.func.free_func = f_audio_free;
control_selector_init(audio);
INIT_WORK(&audio->playback_work, f_audio_playback_work);
return &audio->card.func;
}
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bryan Wu");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,844 @@
/*
* uvc_gadget.c -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/video.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>
#include "uvc.h"
#include "uvc_v4l2.h"
#include "uvc_video.h"
#include "u_uvc.h"
unsigned int uvc_gadget_trace_param;
/* --------------------------------------------------------------------------
* Function descriptors
*/
/* string IDs are assigned dynamically */
#define UVC_STRING_CONTROL_IDX 0
#define UVC_STRING_STREAMING_IDX 1
static struct usb_string uvc_en_us_strings[] = {
[UVC_STRING_CONTROL_IDX].s = "UVC Camera",
[UVC_STRING_STREAMING_IDX].s = "Video Streaming",
{ }
};
static struct usb_gadget_strings uvc_stringtab = {
.language = 0x0409, /* en-us */
.strings = uvc_en_us_strings,
};
static struct usb_gadget_strings *uvc_function_strings[] = {
&uvc_stringtab,
NULL,
};
#define UVC_INTF_VIDEO_CONTROL 0
#define UVC_INTF_VIDEO_STREAMING 1
#define UVC_STATUS_MAX_PACKET_SIZE 16 /* 16 bytes status */
static struct usb_interface_assoc_descriptor uvc_iad = {
.bLength = sizeof(uvc_iad),
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
.bFirstInterface = 0,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_VIDEO,
.bFunctionSubClass = UVC_SC_VIDEO_INTERFACE_COLLECTION,
.bFunctionProtocol = 0x00,
.iFunction = 0,
};
static struct usb_interface_descriptor uvc_control_intf = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_CONTROL,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = UVC_SC_VIDEOCONTROL,
.bInterfaceProtocol = 0x00,
.iInterface = 0,
};
static struct usb_endpoint_descriptor uvc_control_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
.bInterval = 8,
};
static struct usb_ss_ep_comp_descriptor uvc_ss_control_comp = {
.bLength = sizeof(uvc_ss_control_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The following 3 values can be tweaked if necessary. */
.bMaxBurst = 0,
.bmAttributes = 0,
.wBytesPerInterval = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
static struct uvc_control_endpoint_descriptor uvc_control_cs_ep = {
.bLength = UVC_DT_CONTROL_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_CS_ENDPOINT,
.bDescriptorSubType = UVC_EP_INTERRUPT,
.wMaxTransferSize = cpu_to_le16(UVC_STATUS_MAX_PACKET_SIZE),
};
static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = UVC_SC_VIDEOSTREAMING,
.bInterfaceProtocol = 0x00,
.iInterface = 0,
};
static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = UVC_INTF_VIDEO_STREAMING,
.bAlternateSetting = 1,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = UVC_SC_VIDEOSTREAMING,
.bInterfaceProtocol = 0x00,
.iInterface = 0,
};
static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC,
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters.
*/
};
static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC,
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters.
*/
};
static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_SYNC_ASYNC
| USB_ENDPOINT_XFER_ISOC,
/* The wMaxPacketSize and bInterval values will be initialized from
* module parameters.
*/
};
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
.bLength = sizeof(uvc_ss_streaming_comp),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
* initialized from module parameters.
*/
};
static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_fs_streaming_ep,
NULL,
};
static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_hs_streaming_ep,
NULL,
};
static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
(struct usb_descriptor_header *) &uvc_ss_streaming_ep,
(struct usb_descriptor_header *) &uvc_ss_streaming_comp,
NULL,
};
void uvc_set_trace_param(unsigned int trace)
{
uvc_gadget_trace_param = trace;
}
EXPORT_SYMBOL(uvc_set_trace_param);
/* --------------------------------------------------------------------------
* Control requests
*/
static void
uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_device *uvc = req->context;
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
if (uvc->event_setup_out) {
uvc->event_setup_out = 0;
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_DATA;
uvc_event->data.length = req->actual;
memcpy(&uvc_event->data.data, req->buf, req->actual);
v4l2_event_queue(uvc->vdev, &v4l2_event);
}
}
static int
uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct uvc_device *uvc = to_uvc(f);
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
/* printk(KERN_INFO "setup request %02x %02x value %04x index %04x %04x\n",
* ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
* le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
*/
if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
INFO(f->config->cdev, "invalid request type\n");
return -EINVAL;
}
/* Stall too big requests. */
if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE)
return -EINVAL;
/* Tell the complete callback to generate an event for the next request
* that will be enqueued by UVCIOC_SEND_RESPONSE.
*/
uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN);
uvc->event_length = le16_to_cpu(ctrl->wLength);
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_SETUP;
memcpy(&uvc_event->req, ctrl, sizeof(uvc_event->req));
v4l2_event_queue(uvc->vdev, &v4l2_event);
return 0;
}
void uvc_function_setup_continue(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
usb_composite_setup_continue(cdev);
}
static int
uvc_function_get_alt(struct usb_function *f, unsigned interface)
{
struct uvc_device *uvc = to_uvc(f);
INFO(f->config->cdev, "uvc_function_get_alt(%u)\n", interface);
if (interface == uvc->control_intf)
return 0;
else if (interface != uvc->streaming_intf)
return -EINVAL;
else
return uvc->video.ep->driver_data ? 1 : 0;
}
static int
uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
{
struct uvc_device *uvc = to_uvc(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct v4l2_event v4l2_event;
struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
int ret;
INFO(cdev, "uvc_function_set_alt(%u, %u)\n", interface, alt);
if (interface == uvc->control_intf) {
if (alt)
return -EINVAL;
if (uvc->control_ep->driver_data) {
INFO(cdev, "reset UVC Control\n");
usb_ep_disable(uvc->control_ep);
uvc->control_ep->driver_data = NULL;
}
if (!uvc->control_ep->desc)
if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep))
return -EINVAL;
usb_ep_enable(uvc->control_ep);
uvc->control_ep->driver_data = uvc;
if (uvc->state == UVC_STATE_DISCONNECTED) {
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_CONNECT;
uvc_event->speed = cdev->gadget->speed;
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_CONNECTED;
}
return 0;
}
if (interface != uvc->streaming_intf)
return -EINVAL;
/* TODO
if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
return alt ? -EINVAL : 0;
*/
switch (alt) {
case 0:
if (uvc->state != UVC_STATE_STREAMING)
return 0;
if (uvc->video.ep) {
usb_ep_disable(uvc->video.ep);
uvc->video.ep->driver_data = NULL;
}
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMOFF;
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_CONNECTED;
return 0;
case 1:
if (uvc->state != UVC_STATE_CONNECTED)
return 0;
if (!uvc->video.ep)
return -EINVAL;
if (uvc->video.ep->driver_data) {
INFO(cdev, "reset UVC\n");
usb_ep_disable(uvc->video.ep);
uvc->video.ep->driver_data = NULL;
}
ret = config_ep_by_speed(f->config->cdev->gadget,
&(uvc->func), uvc->video.ep);
if (ret)
return ret;
usb_ep_enable(uvc->video.ep);
uvc->video.ep->driver_data = uvc;
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_STREAMON;
v4l2_event_queue(uvc->vdev, &v4l2_event);
return USB_GADGET_DELAYED_STATUS;
default:
return -EINVAL;
}
}
static void
uvc_function_disable(struct usb_function *f)
{
struct uvc_device *uvc = to_uvc(f);
struct v4l2_event v4l2_event;
INFO(f->config->cdev, "uvc_function_disable\n");
memset(&v4l2_event, 0, sizeof(v4l2_event));
v4l2_event.type = UVC_EVENT_DISCONNECT;
v4l2_event_queue(uvc->vdev, &v4l2_event);
uvc->state = UVC_STATE_DISCONNECTED;
if (uvc->video.ep->driver_data) {
usb_ep_disable(uvc->video.ep);
uvc->video.ep->driver_data = NULL;
}
if (uvc->control_ep->driver_data) {
usb_ep_disable(uvc->control_ep);
uvc->control_ep->driver_data = NULL;
}
}
/* --------------------------------------------------------------------------
* Connection / disconnection
*/
void
uvc_function_connect(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
int ret;
if ((ret = usb_function_activate(&uvc->func)) < 0)
INFO(cdev, "UVC connect failed with %d\n", ret);
}
void
uvc_function_disconnect(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
int ret;
if ((ret = usb_function_deactivate(&uvc->func)) < 0)
INFO(cdev, "UVC disconnect failed with %d\n", ret);
}
/* --------------------------------------------------------------------------
* USB probe and disconnect
*/
static int
uvc_register_video(struct uvc_device *uvc)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
struct video_device *video;
/* TODO reference counting. */
video = video_device_alloc();
if (video == NULL)
return -ENOMEM;
video->v4l2_dev = &uvc->v4l2_dev;
video->fops = &uvc_v4l2_fops;
video->ioctl_ops = &uvc_v4l2_ioctl_ops;
video->release = video_device_release;
video->vfl_dir = VFL_DIR_TX;
strlcpy(video->name, cdev->gadget->name, sizeof(video->name));
uvc->vdev = video;
video_set_drvdata(video, uvc);
return video_register_device(video, VFL_TYPE_GRABBER, -1);
}
#define UVC_COPY_DESCRIPTOR(mem, dst, desc) \
do { \
memcpy(mem, desc, (desc)->bLength); \
*(dst)++ = mem; \
mem += (desc)->bLength; \
} while (0);
#define UVC_COPY_DESCRIPTORS(mem, dst, src) \
do { \
const struct usb_descriptor_header * const *__src; \
for (__src = src; *__src; ++__src) { \
memcpy(mem, *__src, (*__src)->bLength); \
*dst++ = mem; \
mem += (*__src)->bLength; \
} \
} while (0)
static struct usb_descriptor_header **
uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
{
struct uvc_input_header_descriptor *uvc_streaming_header;
struct uvc_header_descriptor *uvc_control_header;
const struct uvc_descriptor_header * const *uvc_control_desc;
const struct uvc_descriptor_header * const *uvc_streaming_cls;
const struct usb_descriptor_header * const *uvc_streaming_std;
const struct usb_descriptor_header * const *src;
struct usb_descriptor_header **dst;
struct usb_descriptor_header **hdr;
unsigned int control_size;
unsigned int streaming_size;
unsigned int n_desc;
unsigned int bytes;
void *mem;
switch (speed) {
case USB_SPEED_SUPER:
uvc_control_desc = uvc->desc.ss_control;
uvc_streaming_cls = uvc->desc.ss_streaming;
uvc_streaming_std = uvc_ss_streaming;
break;
case USB_SPEED_HIGH:
uvc_control_desc = uvc->desc.fs_control;
uvc_streaming_cls = uvc->desc.hs_streaming;
uvc_streaming_std = uvc_hs_streaming;
break;
case USB_SPEED_FULL:
default:
uvc_control_desc = uvc->desc.fs_control;
uvc_streaming_cls = uvc->desc.fs_streaming;
uvc_streaming_std = uvc_fs_streaming;
break;
}
/* Descriptors layout
*
* uvc_iad
* uvc_control_intf
* Class-specific UVC control descriptors
* uvc_control_ep
* uvc_control_cs_ep
* uvc_ss_control_comp (for SS only)
* uvc_streaming_intf_alt0
* Class-specific UVC streaming descriptors
* uvc_{fs|hs}_streaming
*/
/* Count descriptors and compute their size. */
control_size = 0;
streaming_size = 0;
bytes = uvc_iad.bLength + uvc_control_intf.bLength
+ uvc_control_ep.bLength + uvc_control_cs_ep.bLength
+ uvc_streaming_intf_alt0.bLength;
if (speed == USB_SPEED_SUPER) {
bytes += uvc_ss_control_comp.bLength;
n_desc = 6;
} else {
n_desc = 5;
}
for (src = (const struct usb_descriptor_header **)uvc_control_desc;
*src; ++src) {
control_size += (*src)->bLength;
bytes += (*src)->bLength;
n_desc++;
}
for (src = (const struct usb_descriptor_header **)uvc_streaming_cls;
*src; ++src) {
streaming_size += (*src)->bLength;
bytes += (*src)->bLength;
n_desc++;
}
for (src = uvc_streaming_std; *src; ++src) {
bytes += (*src)->bLength;
n_desc++;
}
mem = kmalloc((n_desc + 1) * sizeof(*src) + bytes, GFP_KERNEL);
if (mem == NULL)
return NULL;
hdr = mem;
dst = mem;
mem += (n_desc + 1) * sizeof(*src);
/* Copy the descriptors. */
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_iad);
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_intf);
uvc_control_header = mem;
UVC_COPY_DESCRIPTORS(mem, dst,
(const struct usb_descriptor_header **)uvc_control_desc);
uvc_control_header->wTotalLength = cpu_to_le16(control_size);
uvc_control_header->bInCollection = 1;
uvc_control_header->baInterfaceNr[0] = uvc->streaming_intf;
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_ep);
if (speed == USB_SPEED_SUPER)
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
uvc_streaming_header = mem;
UVC_COPY_DESCRIPTORS(mem, dst,
(const struct usb_descriptor_header**)uvc_streaming_cls);
uvc_streaming_header->wTotalLength = cpu_to_le16(streaming_size);
uvc_streaming_header->bEndpointAddress = uvc->video.ep->address;
UVC_COPY_DESCRIPTORS(mem, dst, uvc_streaming_std);
*dst = NULL;
return hdr;
}
static int
uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
struct usb_string *us;
unsigned int max_packet_mult;
unsigned int max_packet_size;
struct usb_ep *ep;
struct f_uvc_opts *opts;
int ret = -EINVAL;
INFO(cdev, "uvc_function_bind\n");
opts = to_f_uvc_opts(f->fi);
/* Sanity check the streaming endpoint module parameters.
*/
opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
/* Fill in the FS/HS/SS Video Streaming specific descriptors from the
* module parameters.
*
* NOTE: We assume that the user knows what they are doing and won't
* give parameters that their UDC doesn't support.
*/
if (opts->streaming_maxpacket <= 1024) {
max_packet_mult = 1;
max_packet_size = opts->streaming_maxpacket;
} else if (opts->streaming_maxpacket <= 2048) {
max_packet_mult = 2;
max_packet_size = opts->streaming_maxpacket / 2;
} else {
max_packet_mult = 3;
max_packet_size = opts->streaming_maxpacket / 3;
}
uvc_fs_streaming_ep.wMaxPacketSize =
cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
uvc_hs_streaming_ep.wMaxPacketSize =
cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
uvc_ss_streaming_comp.wBytesPerInterval =
cpu_to_le16(max_packet_size * max_packet_mult *
opts->streaming_maxburst);
/* Allocate endpoints. */
ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
if (!ep) {
INFO(cdev, "Unable to allocate control EP\n");
goto error;
}
uvc->control_ep = ep;
ep->driver_data = uvc;
if (gadget_is_superspeed(c->cdev->gadget))
ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
&uvc_ss_streaming_comp);
else if (gadget_is_dualspeed(cdev->gadget))
ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
else
ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
if (!ep) {
INFO(cdev, "Unable to allocate streaming EP\n");
goto error;
}
uvc->video.ep = ep;
ep->driver_data = uvc;
uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
us = usb_gstrings_attach(cdev, uvc_function_strings,
ARRAY_SIZE(uvc_en_us_strings));
if (IS_ERR(us)) {
ret = PTR_ERR(us);
goto error;
}
uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
ret = us[UVC_STRING_STREAMING_IDX].id;
uvc_streaming_intf_alt0.iInterface = ret;
uvc_streaming_intf_alt1.iInterface = ret;
/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
uvc_iad.bFirstInterface = ret;
uvc_control_intf.bInterfaceNumber = ret;
uvc->control_intf = ret;
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
uvc->streaming_intf = ret;
/* Copy descriptors */
f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
if (gadget_is_dualspeed(cdev->gadget))
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
if (gadget_is_superspeed(c->cdev->gadget))
f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
/* Preallocate control endpoint request. */
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
if (uvc->control_req == NULL || uvc->control_buf == NULL) {
ret = -ENOMEM;
goto error;
}
uvc->control_req->buf = uvc->control_buf;
uvc->control_req->complete = uvc_function_ep0_complete;
uvc->control_req->context = uvc;
/* Avoid letting this gadget enumerate until the userspace server is
* active.
*/
if ((ret = usb_function_deactivate(f)) < 0)
goto error;
if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) {
printk(KERN_INFO "v4l2_device_register failed\n");
goto error;
}
/* Initialise video. */
ret = uvcg_video_init(&uvc->video);
if (ret < 0)
goto error;
/* Register a V4L2 device. */
ret = uvc_register_video(uvc);
if (ret < 0) {
printk(KERN_INFO "Unable to register video device\n");
goto error;
}
return 0;
error:
v4l2_device_unregister(&uvc->v4l2_dev);
if (uvc->vdev)
video_device_release(uvc->vdev);
if (uvc->control_ep)
uvc->control_ep->driver_data = NULL;
if (uvc->video.ep)
uvc->video.ep->driver_data = NULL;
if (uvc->control_req)
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
usb_free_all_descriptors(f);
return ret;
}
/* --------------------------------------------------------------------------
* USB gadget function
*/
static void uvc_free_inst(struct usb_function_instance *f)
{
struct f_uvc_opts *opts = to_f_uvc_opts(f);
kfree(opts);
}
static struct usb_function_instance *uvc_alloc_inst(void)
{
struct f_uvc_opts *opts;
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->func_inst.free_func_inst = uvc_free_inst;
return &opts->func_inst;
}
static void uvc_free(struct usb_function *f)
{
struct uvc_device *uvc = to_uvc(f);
kfree(uvc);
}
static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct uvc_device *uvc = to_uvc(f);
INFO(cdev, "%s\n", __func__);
video_unregister_device(uvc->vdev);
v4l2_device_unregister(&uvc->v4l2_dev);
uvc->control_ep->driver_data = NULL;
uvc->video.ep->driver_data = NULL;
usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
kfree(uvc->control_buf);
usb_free_all_descriptors(f);
}
static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
{
struct uvc_device *uvc;
struct f_uvc_opts *opts;
uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
if (uvc == NULL)
return ERR_PTR(-ENOMEM);
uvc->state = UVC_STATE_DISCONNECTED;
opts = to_f_uvc_opts(fi);
uvc->desc.fs_control = opts->fs_control;
uvc->desc.ss_control = opts->ss_control;
uvc->desc.fs_streaming = opts->fs_streaming;
uvc->desc.hs_streaming = opts->hs_streaming;
uvc->desc.ss_streaming = opts->ss_streaming;
/* Register the function. */
uvc->func.name = "uvc";
uvc->func.bind = uvc_function_bind;
uvc->func.unbind = uvc_unbind;
uvc->func.get_alt = uvc_function_get_alt;
uvc->func.set_alt = uvc_function_set_alt;
uvc->func.disable = uvc_function_disable;
uvc->func.setup = uvc_function_setup;
uvc->func.free_func = uvc_free;
return &uvc->func;
}
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Laurent Pinchart");

View file

@ -0,0 +1,28 @@
/*
* f_uvc.h -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#ifndef _F_UVC_H_
#define _F_UVC_H_
#include <linux/usb/composite.h>
#include <linux/usb/video.h>
#include "uvc.h"
void uvc_function_setup_continue(struct uvc_device *uvc);
void uvc_function_connect(struct uvc_device *uvc);
void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* _F_UVC_H_ */

View file

@ -0,0 +1,78 @@
/*
* This header declares the utility functions used by "Gadget Zero", plus
* interfaces to its two single-configuration function drivers.
*/
#ifndef __G_ZERO_H
#define __G_ZERO_H
#define GZERO_BULK_BUFLEN 4096
#define GZERO_QLEN 32
#define GZERO_ISOC_INTERVAL 4
#define GZERO_ISOC_MAXPACKET 1024
#define GZERO_INT_INTERVAL 1 /* Default interrupt interval = 1 ms */
#define GZERO_INT_MAXPACKET 1024
struct usb_zero_options {
unsigned pattern;
unsigned isoc_interval;
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned int_interval; /* In ms */
unsigned int_maxpacket;
unsigned int_mult;
unsigned int_maxburst;
unsigned bulk_buflen;
unsigned qlen;
};
struct f_ss_opts {
struct usb_function_instance func_inst;
unsigned pattern;
unsigned isoc_interval;
unsigned isoc_maxpacket;
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned int_interval; /* In ms */
unsigned int_maxpacket;
unsigned int_mult;
unsigned int_maxburst;
unsigned bulk_buflen;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
struct f_lb_opts {
struct usb_function_instance func_inst;
unsigned bulk_buflen;
unsigned qlen;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
void lb_modexit(void);
int lb_modinit(void);
/* common utilities */
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
struct usb_ep *iso_in, struct usb_ep *iso_out,
struct usb_ep *int_in, struct usb_ep *int_out);
#endif /* __G_ZERO_H */

View file

@ -0,0 +1,273 @@
/*
* File Name : multi_config.c
*
* Virtual multi configuration utilities for composite USB gadgets.
* This utilitie can support variable interface for variable Host PC.
*
* Copyright (C) 2011 Samsung Electronics
* Author: SoonYong, Cho <soonyong.cho@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "multi_config.h"
static int multi; /* current configuration */
static int is_multi; /* Is multi configuration available ? */
static int stringMode = OTHER_REQUEST;
static int interfaceCount;
/* Description : Set configuration number
* Parameter : unsigned num (host request)
* Return value : always return 0 (It's virtual multiconfiguration)
*/
unsigned set_config_number(unsigned num)
{
if (is_multi_configuration()) {
USB_DBG_ESS("multi config_num=%d(zero base)\n", num);
if (num < MAX_MULTI_CONFIG_NUM)
multi = num; /* save config number from Host request */
} else {
USB_DBG_ESS("single config num=%d\n", num);
multi = 0; /* single config */
}
return 0; /* always return 0 config */
}
/* Description : Get configuration number
* Return value : virtual multiconfiguration number (zero base)
*/
int get_config_number(void)
{
USB_DBG("multi=%d\n", multi);
return multi;
}
/* Description : Check configuration number
* Parameter : unsigned num (host request)
* Return value : 1 (true : virtual multi configuraiton)
0 (false : normal configuration)
*/
int check_config(unsigned num)
{
USB_DBG("num=%d, multi=%d\n", num, multi);
/* multi is zero base, but num is 1 ase */
if (num && num == multi + 1) {
USB_DBG("Use virtual multi configuration\n");
return 1;
} else {
USB_DBG("normal configuration\n");
return 0;
}
}
/* Description : Search number of configuration including virtual configuration
* Parameter : usb_configuration *c (referenced configuration)
unsigned count (real number of configuration)
* Return value : virtual or real number of configuration
*/
unsigned count_multi_config(struct usb_configuration *c, unsigned count)
{
int f_first = 0;
int f_second = 0;
int f_exception = 0;
struct usb_function *f;
is_multi = 0;
if (!c) {
USB_DBG("usb_configuration is not valid\n");
return 0;
}
list_for_each_entry(f, &c->functions, list) {
if (!strcmp(f->name, MULTI_FUNCTION_1)) {
USB_DBG("%s +\n", MULTI_FUNCTION_1);
f_first = 1;
} else if (!strcmp(f->name, MULTI_FUNCTION_2)) {
USB_DBG("%s +\n", MULTI_FUNCTION_2);
f_second = 1;
} else if (!strcmp(f->name, MULTI_EXCEPTION_FUNCTION)) {
USB_DBG("exception %s +\n", MULTI_EXCEPTION_FUNCTION);
f_exception = 1;
}
}
if (f_first && f_second && !f_exception) {
USB_DBG_ESS("ready multi\n");
is_multi = 1;
return 2;
}
return count;
}
/* Description : Is multi configuration available ?
* Return value : 1 (true), 0 (false)
*/
int is_multi_configuration(void)
{
USB_DBG("= %d\n", is_multi);
return is_multi;
}
/* Description : Check function to skip for multi configuration
* Parameter : char* name (function name)
* Return value : 0 (not available), 1 (available)
*/
int is_available_function(const char *name)
{
if (is_multi_configuration()) {
USB_DBG("multi case\n");
if (!multi) {
if (!strcmp(name, MAIN_FUNCTION)) {
USB_DBG("%s is available.\n",
MAIN_FUNCTION);
return 1;
}
return 0; /* anothor function is not available */
} else {
USB_DBG("multi=%d all available\n", multi);
}
}
return 1; /* if single configuration, every function is available */
}
/* Description : Change configuration using virtual multi configuration.
* Parameter : struct usb_funciton f (to be changed function interface)
void *next (next means usb req->buf)
int len (length for to fill buffer)
struct usb_configuration *config
(To reference interface array of current config)
enum usb_device_speed speed (usb speed)
* Return value : "ret < 0" means fillbuffer function is failed.
*/
int change_conf(struct usb_function *f,
void *next, int len,
struct usb_configuration *config,
enum usb_device_speed speed)
{
u8 *dest;
int status = 0;
struct usb_descriptor_header *descriptor;
struct usb_interface_descriptor *intf;
int index_intf = 0;
int change_intf = 0;
struct usb_descriptor_header **descriptors;
if (!f || !config || !next) {
USB_DBG_ESS("one of f, config, next is not valid\n");
return -EFAULT;
}
USB_DBG("f->%s process multi\n", f->name);
if (speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->fs_descriptors;
if (!descriptors) {
USB_DBG_ESS("descriptor is not available\n");
return -EFAULT;
}
if (f->set_config_desc)
f->set_config_desc(stringMode);
/* set interface numbers dynamically */
dest = next;
while ((descriptor = *descriptors++) != NULL) {
intf = (struct usb_interface_descriptor *)dest;
if (intf->bDescriptorType == USB_DT_INTERFACE) {
if (intf->bAlternateSetting == 0) {
intf->bInterfaceNumber = interfaceCount++;
USB_DBG("a=0 intf->bInterfaceNumber=%d\n",
intf->bInterfaceNumber);
} else {
intf->bInterfaceNumber = interfaceCount - 1;
USB_DBG("a!=0 intf->bInterfaceNumber=%d\n",
intf->bInterfaceNumber);
}
config->interface
[intf->bInterfaceNumber]
= f;
if (f->set_intf_num) {
change_intf = 1;
f->set_intf_num(f,
intf->bInterfaceNumber,
index_intf++);
}
}
dest += intf->bLength;
}
if (change_intf) {
if (speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->fs_descriptors;
status = usb_descriptor_fillbuf(
next, len,
(const struct usb_descriptor_header **)
descriptors);
if (status < 0) {
USB_DBG_ESS("usb_descriptor_fillbuf failed\n");
return status;
}
}
return status;
}
/* Description : Set interface count
* Parameter : struct usb_configuration *config
* (To reference interface array of current config)
* struct usb_config_descriptor *c
* (number of interfaces)
* Return value : void
*/
void set_interface_count(struct usb_configuration *config,
struct usb_config_descriptor *c)
{
USB_DBG_ESS("next_interface_id=%d\n", interfaceCount);
config->next_interface_id = interfaceCount;
config->interface[interfaceCount] = 0;
c->bNumInterfaces = interfaceCount;
interfaceCount = 0;
return ;
}
/* Description : Set string mode
* This mode will be used for deciding other interface.
* Parameter : u16 w_length
* - 2 means MAC request.
* - Windows and Linux PC always request 255 size.
*/
void set_string_mode(u16 w_length)
{
if (w_length == 2) {
USB_DBG("mac request\n");
stringMode = MAC_REQUEST;
} else if (w_length == 0) {
USB_DBG("initialize string mode\n");
stringMode = OTHER_REQUEST;
}
}
/* Description : Get Host OS type
* Return value : type - u16
* - 0 : MAC PC
* - 1 : Windows and Linux PC
*/
u16 get_host_os_type(void)
{
return stringMode;
}

View file

@ -0,0 +1,144 @@
/*
* File Name : multi_config.h
*
* Virtual multi configuration utilities for composite USB gadgets.
* This utilitie can support variable interface for variable Host PC.
*
* Copyright (C) 2011 Samsung Electronics
* Author: SoonYong, Cho <soonyong.cho@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __MULTI_CONFIG_H
#define __MULTI_CONFIG_H
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/usb/composite.h>
/*
* Debugging macro and defines
*/
/*#define USB_DEBUG
*#define USB_MORE_DEBUG
*/
#define USB_DEBUG_ESS
#ifdef USB_DEBUG
# ifdef USB_MORE_DEBUG
# define USB_DBG(fmt, args...) \
printk(KERN_INFO "usb: %s "fmt, __func__, ##args)
# else
# define USB_DBG(fmt, args...) \
printk(KERN_DEBUG "usb: %s "fmt, __func__, ##args)
# endif
#else /* DO NOT PRINT LOG */
# define USB_DBG(fmt, args...) do { } while (0)
#endif /* USB_DEBUG */
#ifdef USB_DEBUG_ESS
# ifdef USB_MORE_DEBUG
# define USB_DBG_ESS(fmt, args...) \
printk(KERN_INFO "usb: %s "fmt, __func__, ##args)
# else
# define USB_DBG_ESS(fmt, args...) \
printk(KERN_DEBUG "usb: %s "fmt, __func__, ##args)
# endif
#else /* DO NOT PRINT LOG */
# define USB_DBG_ESS(fmt, args...) do { } while (0)
#endif /* USB_DEBUG_ESS */
#define MAIN_FUNCTION "mtp"
#define MULTI_FUNCTION_1 "mtp"
#define MULTI_FUNCTION_2 "acm0"
#define MULTI_EXCEPTION_FUNCTION "adb"
#define MAC_REQUEST 0
#define OTHER_REQUEST 1
#define MAX_MULTI_CONFIG_NUM 2
/* Description : Set configuration number
* Parameter : unsigned num (host request)
* Return value : always return 0 (It's virtual multiconfiguration)
*/
unsigned set_config_number(unsigned num);
/* Description : Get configuration number
* Return value : virtual multiconfiguration number (zero base)
*/
int get_config_number(void);
/* Description : Check configuration number
* Parameter : unsigned num (host request)
* Return value : 1 (true : virtual multi configuraiton)
0 (false : normal configuration)
*/
int check_config(unsigned num);
/* Description : Search number of configuration including virtual configuration
* Parameter : usb_configuration *c (referenced configuration)
unsigned count (real number of configuration)
* Return value : virtual or real number of configuration
*/
unsigned count_multi_config(struct usb_configuration *c, unsigned count);
/* Description : Is multi configuration available ?
* Return value : 1 (true), 0 (false)
*/
int is_multi_configuration(void);
/* Description : Check function to skip for multi configuration
* Parameter : char* name (function name)
* Return value : 0 (not available), 1 (available)
*/
int is_available_function(const char *name);
/* Description : Change configuration using virtual multi configuration.
* Parameter : struct usb_funciton f (to be changed function interface)
void *next (next means usb req->buf)
int len (length for to fill buffer)
struct usb_configuration *config
(To reference interface array of current config)
enum usb_device_speed speed (usb speed)
* Return value : "ret < 0" means fillbuffer function is failed.
*/
int change_conf(struct usb_function *f,
void *next, int len,
struct usb_configuration *config,
enum usb_device_speed speed);
/* Description : Set interface count
* Parameter : struct usb_configuration *config
(To reference interface array of current config)
struct usb_config_descriptor *c
(number of interfaces)
* Return value : void
*/
void set_interface_count(struct usb_configuration *config,
struct usb_config_descriptor *c);
/* Description : Set string mode
* This mode will be used for deciding other interface.
* Parameter : u16 w_length
* - 2 means MAC request.
* - Windows and Linux PC always request 255 size.
*/
void set_string_mode(u16 w_length);
/* Description : Get Host OS type
* Return value : type - u16
* - 0 : MAC PC
* - 1 : Windows and Linux PC
*/
u16 get_host_os_type(void);
#endif /* __MULTI_CONFIG_H */

View file

@ -0,0 +1,47 @@
/*
* ndis.h
*
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
*/
#ifndef _LINUX_NDIS_H
#define _LINUX_NDIS_H
enum NDIS_DEVICE_POWER_STATE {
NdisDeviceStateUnspecified = 0,
NdisDeviceStateD0,
NdisDeviceStateD1,
NdisDeviceStateD2,
NdisDeviceStateD3,
NdisDeviceStateMaximum
};
struct NDIS_PM_WAKE_UP_CAPABILITIES {
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
};
struct NDIS_PNP_CAPABILITIES {
__le32 Flags;
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
};
struct NDIS_PM_PACKET_PATTERN {
__le32 Priority;
__le32 Reserved;
__le32 MaskSize;
__le32 PatternOffset;
__le32 PatternSize;
__le32 PatternFlags;
};
#endif /* _LINUX_NDIS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,229 @@
/*
* RNDIS Definitions for Remote NDIS
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2, as published by the Free Software Foundation.
*
* This software was originally developed in conformance with
* Microsoft's Remote NDIS Specification License Agreement.
*/
#ifndef _LINUX_RNDIS_H
#define _LINUX_RNDIS_H
#include <linux/rndis.h>
#include "ndis.h"
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
#define RNDIS_MAX_TOTAL_SIZE 1558
typedef struct rndis_init_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 MaxTransferSize;
} rndis_init_msg_type;
typedef struct rndis_init_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 MajorVersion;
__le32 MinorVersion;
__le32 DeviceFlags;
__le32 Medium;
__le32 MaxPacketsPerTransfer;
__le32 MaxTransferSize;
__le32 PacketAlignmentFactor;
__le32 AFListOffset;
__le32 AFListSize;
} rndis_init_cmplt_type;
typedef struct rndis_halt_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_halt_msg_type;
typedef struct rndis_query_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_query_msg_type;
typedef struct rndis_query_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
} rndis_query_cmplt_type;
typedef struct rndis_set_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 OID;
__le32 InformationBufferLength;
__le32 InformationBufferOffset;
__le32 DeviceVcHandle;
} rndis_set_msg_type;
typedef struct rndis_set_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_set_cmplt_type;
typedef struct rndis_reset_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Reserved;
} rndis_reset_msg_type;
typedef struct rndis_reset_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 AddressingReset;
} rndis_reset_cmplt_type;
typedef struct rndis_indicate_status_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 Status;
__le32 StatusBufferLength;
__le32 StatusBufferOffset;
} rndis_indicate_status_msg_type;
typedef struct rndis_keepalive_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
} rndis_keepalive_msg_type;
typedef struct rndis_keepalive_cmplt_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 RequestID;
__le32 Status;
} rndis_keepalive_cmplt_type;
struct rndis_packet_msg_type
{
__le32 MessageType;
__le32 MessageLength;
__le32 DataOffset;
__le32 DataLength;
__le32 OOBDataOffset;
__le32 OOBDataLength;
__le32 NumOOBDataElements;
__le32 PerPacketInfoOffset;
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
} __attribute__ ((packed));
struct rndis_config_parameter
{
__le32 ParameterNameOffset;
__le32 ParameterNameLength;
__le32 ParameterType;
__le32 ParameterValueOffset;
__le32 ParameterValueLength;
};
/* implementation specific */
enum rndis_state
{
RNDIS_UNINITIALIZED,
RNDIS_INITIALIZED,
RNDIS_DATA_INITIALIZED,
};
typedef struct rndis_resp_t
{
struct list_head list;
u8 *buf;
u32 length;
int send;
} rndis_resp_t;
typedef struct rndis_params
{
u8 confignr;
u8 used;
u16 saved_filter;
enum rndis_state state;
u32 medium;
u32 speed;
u32 media_state;
const u8 *host_mac;
u16 *filter;
struct net_device *dev;
u32 vendorID;
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
u8 max_pkt_per_xfer;
u8 pkt_alignment_factor;
#endif
const char *vendorDescr;
void (*resp_avail)(void *v);
void *v;
struct list_head resp_queue;
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser (u8 configNr, u8 *buf);
int rndis_register(void (*resp_avail)(void *v), void *v);
void rndis_deregister (int configNr);
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
u16 *cdc_filter);
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
const char *vendorDescr);
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
void rndis_set_max_pkt_xfer(u8 configNr, u8 max_pkt_per_xfer);
#endif
void rndis_add_hdr (struct sk_buff *skb);
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
struct sk_buff_head *list);
u8 *rndis_get_next_response (int configNr, u32 *length);
void rndis_free_response (int configNr, u8 *buf);
void rndis_uninit (int configNr);
int rndis_signal_connect (int configNr);
int rndis_signal_disconnect (int configNr);
int rndis_state (int configNr);
extern void rndis_set_host_mac (int configNr, const u8 *addr);
int rndis_init(void);
void rndis_exit (void);
#endif /* _LINUX_RNDIS_H */

View file

@ -0,0 +1,174 @@
/*
* serial_acm.c -- USB modem serial driver
*
* Copyright 2008 (C) Samsung Electronics
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/usb/cdc.h>
extern int acm_notify(void *dev, u16 state);
static wait_queue_head_t modem_wait_q;
static unsigned int read_state;
static unsigned int control_line_state;
static void *acm_data;
static int modem_open(struct inode *inode, struct file *file)
{
read_state = 0;
return 0;
}
static int modem_close(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t modem_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int ret = 0;
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
ret = wait_event_interruptible(modem_wait_q, read_state);
if (ret)
return ret;
if (copy_to_user(buf, &control_line_state, sizeof(u32)))
return -EFAULT;
read_state = 0;
return sizeof(u32);
}
static unsigned int modem_poll(struct file *file, poll_table *wait)
{
int ret;
poll_wait(file, &modem_wait_q, wait);
ret = (read_state ? (POLLIN | POLLRDNORM) : 0);
return ret;
}
void notify_control_line_state(u32 value)
{
control_line_state = value;
read_state = 1;
wake_up_interruptible(&modem_wait_q);
}
EXPORT_SYMBOL(notify_control_line_state);
#define GS_CDC_NOTIFY_SERIAL_STATE _IOW('S', 1, int)
#define GS_IOC_NOTIFY_DTR_TEST _IOW('S', 3, int)
static long
modem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
printk(KERN_INFO "modem_ioctl: cmd=0x%x, arg=%lu\n", cmd, arg);
/* handle ioctls */
switch (cmd) {
case GS_CDC_NOTIFY_SERIAL_STATE:
acm_notify(acm_data, __constant_cpu_to_le16(arg));
break;
case GS_IOC_NOTIFY_DTR_TEST:
{
printk(KERN_ALERT"DUN : DTR %d\n", (int)arg);
notify_control_line_state((int)arg);
break;
}
default:
printk(KERN_INFO "modem_ioctl: Unknown ioctl cmd(0x%x).\n",
cmd);
return -ENOIOCTLCMD;
}
return 0;
}
static const struct file_operations modem_fops = {
.owner = THIS_MODULE,
.open = modem_open,
.release = modem_close,
.read = modem_read,
.poll = modem_poll,
.llseek = no_llseek,
.unlocked_ioctl = modem_ioctl,
};
static struct miscdevice modem_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "dun",
.fops = &modem_fops,
};
int modem_register(void *data)
{
if (data == NULL) {
printk(KERN_INFO "DUN register failed. data is null.\n");
return -1;
}
acm_data = data;
printk(KERN_INFO "DUN is registerd\n");
return 0;
}
EXPORT_SYMBOL(modem_register);
int modem_misc_register(void)
{
int ret;
ret = misc_register(&modem_device);
if (ret) {
printk(KERN_ERR "DUN register is failed, ret = %d\n", ret);
return ret;
}
init_waitqueue_head(&modem_wait_q);
return ret;
}
EXPORT_SYMBOL(modem_misc_register);
void modem_unregister(void)
{
acm_data = NULL;
notify_control_line_state(0);
printk(KERN_INFO "DUN is unregisterd\n");
}
EXPORT_SYMBOL(modem_unregister);

View file

@ -0,0 +1,504 @@
/*
* storage_common.c -- Common definitions for mass storage functionality
*
* Copyright (C) 2003-2008 Alan Stern
* Copyeight (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.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, or
* (at your option) any later version.
*/
/*
* This file requires the following identifiers used in USB strings to
* be defined (each of type pointer to char):
* - fsg_string_interface -- name of the interface
*/
/*
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
* sets the number of pipeline buffers (length of the fsg_buffhd array).
* The valid range of num_buffers is: num >= 2 && num <= 4.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/usb/composite.h>
#include "storage_common.h"
/* There is only one interface. */
struct usb_interface_descriptor fsg_intf_desc = {
.bLength = sizeof fsg_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2, /* Adjusted during fsg_bind() */
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
.iInterface = FSG_STRING_INTERFACE,
};
EXPORT_SYMBOL_GPL(fsg_intf_desc);
/*
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
* interrupt-in.
*/
struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
EXPORT_SYMBOL_GPL(fsg_fs_bulk_in_desc);
struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
EXPORT_SYMBOL_GPL(fsg_fs_bulk_out_desc);
struct usb_descriptor_header *fsg_fs_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
NULL,
};
EXPORT_SYMBOL_GPL(fsg_fs_function);
/*
* USB 2.0 devices need to expose both high speed and full speed
* descriptors, unless they only run at full speed.
*
* That means alternate endpoint descriptors (bigger packets)
* and a "device qualifier" ... plus more construction options
* for the configuration descriptor.
*/
struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
EXPORT_SYMBOL_GPL(fsg_hs_bulk_in_desc);
struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 1, /* NAK every 1 uframe */
};
EXPORT_SYMBOL_GPL(fsg_hs_bulk_out_desc);
struct usb_descriptor_header *fsg_hs_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
NULL,
};
EXPORT_SYMBOL_GPL(fsg_hs_function);
struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
/* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_desc);
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
EXPORT_SYMBOL_GPL(fsg_ss_bulk_in_comp_desc);
struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
/* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_desc);
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
EXPORT_SYMBOL_GPL(fsg_ss_bulk_out_comp_desc);
struct usb_descriptor_header *fsg_ss_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
NULL,
};
EXPORT_SYMBOL_GPL(fsg_ss_function);
/*-------------------------------------------------------------------------*/
/*
* If the next two routines are called while the gadget is registered,
* the caller must own fsg->filesem for writing.
*/
void fsg_lun_close(struct fsg_lun *curlun)
{
if (curlun->filp) {
LDBG(curlun, "close backing file\n");
fput(curlun->filp);
curlun->filp = NULL;
}
}
EXPORT_SYMBOL_GPL(fsg_lun_close);
int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
int ro;
struct file *filp = NULL;
int rc = -EINVAL;
struct inode *inode = NULL;
loff_t size;
loff_t num_sectors;
loff_t min_sectors;
unsigned int blkbits;
unsigned int blksize;
/* R/W if we can, R/O if we must */
ro = curlun->initially_ro;
if (!ro) {
filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
ro = 1;
}
if (ro)
filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
if (IS_ERR(filp)) {
LINFO(curlun, "unable to open backing file: %s\n", filename);
return PTR_ERR(filp);
}
if (!(filp->f_mode & FMODE_WRITE))
ro = 1;
inode = file_inode(filp);
if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
LINFO(curlun, "invalid file type: %s\n", filename);
goto out;
}
/*
* If we can't read the file, it's no good.
* If we can't write the file, use it read-only.
*/
if (!(filp->f_mode & FMODE_CAN_READ)) {
LINFO(curlun, "file not readable: %s\n", filename);
goto out;
}
if (!(filp->f_mode & FMODE_CAN_WRITE))
ro = 1;
size = i_size_read(inode->i_mapping->host);
if (size < 0) {
LINFO(curlun, "unable to find file size: %s\n", filename);
rc = (int) size;
goto out;
}
if (curlun->cdrom) {
blksize = 2048;
blkbits = 11;
} else if (inode->i_bdev) {
blksize = bdev_logical_block_size(inode->i_bdev);
blkbits = blksize_bits(blksize);
} else {
blksize = 512;
blkbits = 9;
}
num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
min_sectors = 1;
if (curlun->cdrom) {
min_sectors = 300; /* Smallest track is 300 frames */
if (num_sectors >= 256*60*75) {
num_sectors = 256*60*75 - 1;
LINFO(curlun, "file too big: %s\n", filename);
LINFO(curlun, "using only first %d blocks\n",
(int) num_sectors);
}
}
if (num_sectors < min_sectors) {
LINFO(curlun, "file too small: %s\n", filename);
rc = -ETOOSMALL;
goto out;
}
if (fsg_lun_is_open(curlun))
fsg_lun_close(curlun);
curlun->blksize = blksize;
curlun->blkbits = blkbits;
curlun->ro = ro;
curlun->filp = filp;
curlun->file_length = size;
curlun->num_sectors = num_sectors;
LDBG(curlun, "open backing file: %s\n", filename);
return 0;
out:
fput(filp);
return rc;
}
EXPORT_SYMBOL_GPL(fsg_lun_open);
/*-------------------------------------------------------------------------*/
/*
* Sync the file data, don't bother with the metadata.
* This code was copied from fs/buffer.c:sys_fdatasync().
*/
int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
if (curlun->ro || !filp)
return 0;
return vfs_fsync(filp, 1);
}
EXPORT_SYMBOL_GPL(fsg_lun_fsync_sub);
void store_cdrom_address(u8 *dest, int msf, u32 addr)
{
if (msf) {
/* Convert to Minutes-Seconds-Frames */
addr >>= 2; /* Convert to 2048-byte frames */
addr += 2*75; /* Lead-in occupies 2 seconds */
dest[3] = addr % 75; /* Frames */
addr /= 75;
dest[2] = addr % 60; /* Seconds */
addr /= 60;
dest[1] = addr; /* Minutes */
dest[0] = 0; /* Reserved */
} else {
/* Absolute sector */
put_unaligned_be32(addr, dest);
}
}
EXPORT_SYMBOL_GPL(store_cdrom_address);
/*-------------------------------------------------------------------------*/
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
? curlun->ro
: curlun->initially_ro);
}
EXPORT_SYMBOL_GPL(fsg_show_ro);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%u\n", curlun->nofua);
}
EXPORT_SYMBOL_GPL(fsg_show_nofua);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf)
{
char *p;
ssize_t rc;
down_read(filesem);
if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */
p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
if (IS_ERR(p))
rc = PTR_ERR(p);
else {
rc = strlen(p);
memmove(buf, p, rc);
buf[rc] = '\n'; /* Add a newline */
buf[++rc] = 0;
}
} else { /* No file, return 0 bytes */
*buf = 0;
rc = 0;
}
up_read(filesem);
return rc;
}
EXPORT_SYMBOL_GPL(fsg_show_file);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%u\n", curlun->cdrom);
}
EXPORT_SYMBOL_GPL(fsg_show_cdrom);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%u\n", curlun->removable);
}
EXPORT_SYMBOL_GPL(fsg_show_removable);
/*
* The caller must hold fsg->filesem for reading when calling this function.
*/
static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
{
if (fsg_lun_is_open(curlun)) {
LDBG(curlun, "read-only status change prevented\n");
return -EBUSY;
}
curlun->ro = ro;
curlun->initially_ro = ro;
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
return 0;
}
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
ssize_t rc;
bool ro;
rc = strtobool(buf, &ro);
if (rc)
return rc;
/*
* Allow the write-enable status to change only while the
* backing file is closed.
*/
down_read(filesem);
rc = _fsg_store_ro(curlun, ro);
if (!rc)
rc = count;
up_read(filesem);
return rc;
}
EXPORT_SYMBOL_GPL(fsg_store_ro);
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
{
bool nofua;
int ret;
ret = strtobool(buf, &nofua);
if (ret)
return ret;
/* Sync data when switching from async mode to sync */
if (!nofua && curlun->nofua)
fsg_lun_fsync_sub(curlun);
curlun->nofua = nofua;
return count;
}
EXPORT_SYMBOL_GPL(fsg_store_nofua);
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
int rc = 0;
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
LDBG(curlun, "eject attempt prevented\n");
return -EBUSY; /* "Door is locked" */
}
/* Remove a trailing newline */
if (count > 0 && buf[count-1] == '\n')
((char *) buf)[count-1] = 0; /* Ugh! */
/* Load new medium */
down_write(filesem);
if (count > 0 && buf[0]) {
/* fsg_lun_open() will close existing file if any. */
rc = fsg_lun_open(curlun, buf);
if (rc == 0)
curlun->unit_attention_data =
SS_NOT_READY_TO_READY_TRANSITION;
} else if (fsg_lun_is_open(curlun)) {
fsg_lun_close(curlun);
curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
}
up_write(filesem);
return (rc < 0 ? rc : count);
}
EXPORT_SYMBOL_GPL(fsg_store_file);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
bool cdrom;
int ret;
ret = strtobool(buf, &cdrom);
if (ret)
return ret;
down_read(filesem);
ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
if (!ret) {
curlun->cdrom = cdrom;
ret = count;
}
up_read(filesem);
return ret;
}
EXPORT_SYMBOL_GPL(fsg_store_cdrom);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count)
{
bool removable;
int ret;
ret = strtobool(buf, &removable);
if (ret)
return ret;
curlun->removable = removable;
return count;
}
EXPORT_SYMBOL_GPL(fsg_store_removable);
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,225 @@
#ifndef USB_STORAGE_COMMON_H
#define USB_STORAGE_COMMON_H
#include <linux/device.h>
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
#include <asm/unaligned.h>
#ifndef DEBUG
#undef VERBOSE_DEBUG
#undef DUMP_MSGS
#endif /* !DEBUG */
#ifdef VERBOSE_DEBUG
#define VLDBG LDBG
#else
#define VLDBG(lun, fmt, args...) do { } while (0)
#endif /* VERBOSE_DEBUG */
#define _LMSG(func, lun, fmt, args...) \
do { \
if ((lun)->name_pfx && *(lun)->name_pfx) \
func("%s/%s: " fmt, *(lun)->name_pfx, \
(lun)->name, ## args); \
else \
func("%s: " fmt, (lun)->name, ## args); \
} while (0)
#define LDBG(lun, fmt, args...) _LMSG(pr_debug, lun, fmt, ## args)
#define LERROR(lun, fmt, args...) _LMSG(pr_err, lun, fmt, ## args)
#define LWARN(lun, fmt, args...) _LMSG(pr_warn, lun, fmt, ## args)
#define LINFO(lun, fmt, args...) _LMSG(pr_info, lun, fmt, ## args)
#ifdef DUMP_MSGS
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) \
do { \
if (length < 512) { \
DBG(fsg, "%s, length %u:\n", label, length); \
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
16, 1, buf, length, 0); \
} \
} while (0)
# define dump_cdb(fsg) do { } while (0)
#else
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) do { } while (0)
# ifdef VERBOSE_DEBUG
# define dump_cdb(fsg) \
print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
# else
# define dump_cdb(fsg) do { } while (0)
# endif /* VERBOSE_DEBUG */
#endif /* DUMP_MSGS */
/* Length of a SCSI Command Data Block */
#define MAX_COMMAND_SIZE 16
/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
#define SS_NO_SENSE 0
#define SS_COMMUNICATION_FAILURE 0x040800
#define SS_INVALID_COMMAND 0x052000
#define SS_INVALID_FIELD_IN_CDB 0x052400
#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
#define SS_MEDIUM_NOT_PRESENT 0x023a00
#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
#define SS_RESET_OCCURRED 0x062900
#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
#define SS_UNRECOVERED_READ_ERROR 0x031100
#define SS_WRITE_ERROR 0x030c02
#define SS_WRITE_PROTECTED 0x072700
#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
#define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x))
struct fsg_lun {
struct file *filp;
loff_t file_length;
loff_t num_sectors;
unsigned int initially_ro:1;
unsigned int ro:1;
unsigned int removable:1;
unsigned int cdrom:1;
unsigned int prevent_medium_removal:1;
unsigned int registered:1;
unsigned int info_valid:1;
unsigned int nofua:1;
u32 sense_data;
u32 sense_data_info;
u32 unit_attention_data;
unsigned int blkbits; /* Bits of logical block size
of bound block device */
unsigned int blksize; /* logical block size of bound block device */
struct device dev;
const char *name; /* "lun.name" */
const char **name_pfx; /* "function.name" */
};
static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
{
return curlun->filp != NULL;
}
/* Default size of buffer length. */
#define FSG_BUFLEN ((u32)16384)
/* Maximal number of LUNs supported in mass storage function */
#define FSG_MAX_LUNS 8
enum fsg_buffer_state {
BUF_STATE_EMPTY = 0,
BUF_STATE_FULL,
BUF_STATE_BUSY
};
struct fsg_buffhd {
void *buf;
enum fsg_buffer_state state;
struct fsg_buffhd *next;
/*
* The NetChip 2280 is faster, and handles some protocol faults
* better, if we don't submit any short bulk-out read requests.
* So we will record the intended request length here.
*/
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
int inreq_busy;
struct usb_request *outreq;
int outreq_busy;
};
enum fsg_state {
/* This one isn't used anywhere */
FSG_STATE_COMMAND_PHASE = -10,
FSG_STATE_DATA_PHASE,
FSG_STATE_STATUS_PHASE,
FSG_STATE_IDLE = 0,
FSG_STATE_ABORT_BULK_OUT,
FSG_STATE_RESET,
FSG_STATE_INTERFACE_CHANGE,
FSG_STATE_CONFIG_CHANGE,
FSG_STATE_DISCONNECT,
FSG_STATE_EXIT,
FSG_STATE_TERMINATED
};
enum data_direction {
DATA_DIR_UNKNOWN = 0,
DATA_DIR_FROM_HOST,
DATA_DIR_TO_HOST,
DATA_DIR_NONE
};
static inline u32 get_unaligned_be24(u8 *buf)
{
return 0xffffff & (u32) get_unaligned_be32(buf - 1);
}
static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
{
return container_of(dev, struct fsg_lun, dev);
}
enum {
FSG_STRING_INTERFACE
};
extern struct usb_interface_descriptor fsg_intf_desc;
extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
extern struct usb_descriptor_header *fsg_fs_function[];
extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
extern struct usb_descriptor_header *fsg_hs_function[];
extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
extern struct usb_descriptor_header *fsg_ss_function[];
void fsg_lun_close(struct fsg_lun *curlun);
int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
int fsg_lun_fsync_sub(struct fsg_lun *curlun);
void store_cdrom_address(u8 *dest, int msf, u32 addr);
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count);
#endif /* USB_STORAGE_COMMON_H */

View file

@ -0,0 +1,36 @@
/*
* u_ecm.h
*
* Utility definitions for the ecm function
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_ECM_H
#define U_ECM_H
#include <linux/usb/composite.h>
struct f_ecm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
#endif /* U_ECM_H */

View file

@ -0,0 +1,36 @@
/*
* u_eem.h
*
* Utility definitions for the eem function
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_EEM_H
#define U_EEM_H
#include <linux/usb/composite.h>
struct f_eem_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
#endif /* U_EEM_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,190 @@
/*
* u_ether.h -- interface to USB gadget "ethernet link" utilities
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
#ifndef __U_ETHER_H
#define __U_ETHER_H
#include <linux/err.h>
#include <linux/if_ether.h>
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
/* #define CONFIG_USB_NCM_ACCUMULATE_MULTPKT */
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
#define QMULT_DEFAULT 10
#else
#define QMULT_DEFAULT 5
#endif
#ifdef CONFIG_USB_NCM_ACCUMULATE_MULTPKT
/* #define DEBUG_USB_NCM */
#ifdef DEBUG_USB_NCM
#define DEBUG_NCM(fmt, args...) printk(fmt, ##args)
#else
#define DEBUG_NCM(fmt, args...) do {} while (0)
#endif
#endif
#include "gadget_chips.h"
struct eth_dev;
/*
* This represents the USB side of an "ethernet" link, managed by a USB
* function which provides control and (maybe) framing. Two functions
* in different configurations could share the same ethernet link/netdev,
* using different host interaction models.
*
* There is a current limitation that only one instance of this link may
* be present in any given configuration. When that's a problem, network
* layer facilities can be used to package multiple logical links on this
* single "physical" one.
*/
struct gether {
struct usb_function func;
/* updated by gether_{connect,disconnect} */
struct eth_dev *ioport;
/* endpoints handle full and/or high speeds */
struct usb_ep *in_ep;
struct usb_ep *out_ep;
bool is_zlp_ok;
u16 cdc_filter;
/* hooks for added framing, as needed for RNDIS and EEM. */
u32 header_len;
/* NCM requires fixed size bundles */
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
#ifdef CONFIG_USB_RNDIS_MULTIPACKET
unsigned ul_max_pkts_per_xfer;
unsigned dl_max_pkts_per_xfer;
bool multi_pkt_xfer;
#ifdef CONFIG_USB_NCM_ACCUMULATE_MULTPKT
void *header;
u16 ndp0_offset;
u16 ndp0_blocklengthoffset;
u16 ndp0_defaultBlockLen;
#else
struct rndis_packet_msg_type *header;
#endif /* CONFIG_USB_NCM_ACCUMULATE_MULTPKT */
#endif /* CONFIG_USB_RNDIS_MULTIPACKET */
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);
int (*unwrap)(struct gether *port,
struct sk_buff *skb,
struct sk_buff_head *list);
/* called on network open/close */
void (*open)(struct gether *);
void (*close)(struct gether *);
};
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|USB_CDC_PACKET_TYPE_DIRECTED)
/* variant of gether_setup that allows customizing network device name */
struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
const char *netname);
/* netdev setup/teardown as directed by the gadget driver */
/* gether_setup - initialize one ethernet-over-usb link
* @g: gadget to associated with these links
* @ethaddr: NULL, or a buffer in which the ethernet address of the
* host side of the link is recorded
* Context: may sleep
*
* This sets up the single network link that may be exported by a
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
* Returns negative errno, or zero on success
*/
static inline struct eth_dev *gether_setup(struct usb_gadget *g,
u8 ethaddr[ETH_ALEN])
{
return gether_setup_name(g, ethaddr, "usb");
}
void gether_cleanup(struct eth_dev *dev);
/* connect/disconnect is handled by individual functions */
struct net_device *gether_connect(struct gether *);
void gether_disconnect(struct gether *);
/* Some controllers can't support CDC Ethernet (ECM) ... */
static inline bool can_support_ecm(struct usb_gadget *gadget)
{
if (!gadget_supports_altsettings(gadget))
return false;
/* Everything else is *presumably* fine ... but this is a bit
* chancy, so be **CERTAIN** there are no hardware issues with
* your controller. Add it above if it can't handle CDC.
*/
return true;
}
/* each configuration may bind one instance of an ethernet link */
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
struct eth_dev *dev);
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
struct eth_dev *dev);
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
struct eth_dev *dev);
int eem_bind_config(struct usb_configuration *c, struct eth_dev *dev);
#ifdef USB_ETH_RNDIS
int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
u32 vendorID, const char *manufacturer, struct eth_dev *dev);
#else
static inline int
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
u32 vendorID, const char *manufacturer, struct eth_dev *dev)
{
return 0;
}
#endif
/**
* rndis_bind_config - add RNDIS network link to a configuration
* @c: the configuration to support the network link
* @ethaddr: a buffer in which the ethernet address of the host side
* side of the link was recorded
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
static inline int rndis_bind_config(struct usb_configuration *c,
u8 ethaddr[ETH_ALEN], struct eth_dev *dev)
{
return rndis_bind_config_vendor(c, ethaddr, 0, NULL, dev);
}
#endif /* __U_ETHER_H */

View file

@ -0,0 +1,164 @@
/*
* u_ether_configfs.h
*
* Utility definitions for configfs support in USB Ethernet functions
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __U_ETHER_CONFIGFS_H
#define __U_ETHER_CONFIGFS_H
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
CONFIGFS_ATTR_STRUCT(f_##_f_##_opts); \
CONFIGFS_ATTR_OPS(f_##_f_##_opts); \
\
static void _f_##_attr_release(struct config_item *item) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
\
usb_put_function_instance(&opts->func_inst); \
} \
\
static struct configfs_item_operations _f_##_item_ops = { \
.release = _f_##_attr_release, \
.show_attribute = f_##_f_##_opts_attr_show, \
.store_attribute = f_##_f_##_opts_attr_store, \
}
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_) \
static ssize_t _f_##_opts_dev_addr_show(struct f_##_f_##_opts *opts, \
char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t _f_##_opts_dev_addr_store(struct f_##_f_##_opts *opts, \
const char *page, size_t len)\
{ \
int ret; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
mutex_unlock(&opts->lock); \
return -EBUSY; \
} \
\
ret = gether_set_dev_addr(opts->net, page); \
mutex_unlock(&opts->lock); \
if (!ret) \
ret = len; \
return ret; \
} \
\
static struct f_##_f_##_opts_attribute f_##_f_##_opts_dev_addr = \
__CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, \
_f_##_opts_dev_addr_show, \
_f_##_opts_dev_addr_store)
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_) \
static ssize_t _f_##_opts_host_addr_show(struct f_##_f_##_opts *opts, \
char *page) \
{ \
int result; \
\
mutex_lock(&opts->lock); \
result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \
mutex_unlock(&opts->lock); \
\
return result; \
} \
\
static ssize_t _f_##_opts_host_addr_store(struct f_##_f_##_opts *opts, \
const char *page, size_t len)\
{ \
int ret; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
mutex_unlock(&opts->lock); \
return -EBUSY; \
} \
\
ret = gether_set_host_addr(opts->net, page); \
mutex_unlock(&opts->lock); \
if (!ret) \
ret = len; \
return ret; \
} \
\
static struct f_##_f_##_opts_attribute f_##_f_##_opts_host_addr = \
__CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, \
_f_##_opts_host_addr_show, \
_f_##_opts_host_addr_store)
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_) \
static ssize_t _f_##_opts_qmult_show(struct f_##_f_##_opts *opts, \
char *page) \
{ \
unsigned qmult; \
\
mutex_lock(&opts->lock); \
qmult = gether_get_qmult(opts->net); \
mutex_unlock(&opts->lock); \
return sprintf(page, "%d", qmult); \
} \
\
static ssize_t _f_##_opts_qmult_store(struct f_##_f_##_opts *opts, \
const char *page, size_t len)\
{ \
u8 val; \
int ret; \
\
mutex_lock(&opts->lock); \
if (opts->refcnt) { \
ret = -EBUSY; \
goto out; \
} \
\
ret = kstrtou8(page, 0, &val); \
if (ret) \
goto out; \
\
gether_set_qmult(opts->net, val); \
ret = len; \
out: \
mutex_unlock(&opts->lock); \
return ret; \
} \
\
static struct f_##_f_##_opts_attribute f_##_f_##_opts_qmult = \
__CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, \
_f_##_opts_qmult_show, \
_f_##_opts_qmult_store)
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_) \
static ssize_t _f_##_opts_ifname_show(struct f_##_f_##_opts *opts, \
char *page) \
{ \
int ret; \
\
mutex_lock(&opts->lock); \
ret = gether_get_ifname(opts->net, page, PAGE_SIZE); \
mutex_unlock(&opts->lock); \
\
return ret; \
} \
\
static struct f_##_f_##_opts_attribute f_##_f_##_opts_ifname = \
__CONFIGFS_ATTR_RO(ifname, _f_##_opts_ifname_show)
#endif /* __U_ETHER_CONFIGFS_H */

View file

@ -0,0 +1,274 @@
/*
* u_fs.h
*
* Utility definitions for the FunctionFS
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_FFS_H
#define U_FFS_H
#include <linux/usb/composite.h>
#include <linux/list.h>
#include <linux/mutex.h>
#ifdef VERBOSE_DEBUG
#ifndef pr_vdebug
# define pr_vdebug pr_debug
#endif /* pr_vdebug */
# define ffs_dump_mem(prefix, ptr, len) \
print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
#else
#ifndef pr_vdebug
# define pr_vdebug(...) do { } while (0)
#endif /* pr_vdebug */
# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
#endif /* VERBOSE_DEBUG */
#define ENTER() pr_vdebug("%s()\n", __func__)
struct f_fs_opts;
struct ffs_dev {
const char *name;
bool name_allocated;
bool mounted;
bool desc_ready;
bool single;
struct ffs_data *ffs_data;
struct f_fs_opts *opts;
struct list_head entry;
int (*ffs_ready_callback)(struct ffs_data *ffs);
void (*ffs_closed_callback)(struct ffs_data *ffs);
void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
void (*ffs_release_dev_callback)(struct ffs_dev *dev);
};
extern struct mutex ffs_lock;
static inline void ffs_dev_lock(void)
{
mutex_lock(&ffs_lock);
}
static inline void ffs_dev_unlock(void)
{
mutex_unlock(&ffs_lock);
}
int ffs_name_dev(struct ffs_dev *dev, const char *name);
int ffs_single_dev(struct ffs_dev *dev);
struct ffs_epfile;
struct ffs_function;
enum ffs_state {
/*
* Waiting for descriptors and strings.
*
* In this state no open(2), read(2) or write(2) on epfiles
* may succeed (which should not be the problem as there
* should be no such files opened in the first place).
*/
FFS_READ_DESCRIPTORS,
FFS_READ_STRINGS,
/*
* We've got descriptors and strings. We are or have called
* functionfs_ready_callback(). functionfs_bind() may have
* been called but we don't know.
*
* This is the only state in which operations on epfiles may
* succeed.
*/
FFS_ACTIVE,
/*
* All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
* from user space we fail to initialise epfiles or
* functionfs_ready_callback() returns with error (<0).
*
* In this state no open(2), read(2) or write(2) (both on ep0
* as well as epfile) may succeed (at this point epfiles are
* unlinked and all closed so this is not a problem; ep0 is
* also closed but ep0 file exists and so open(2) on ep0 must
* fail).
*/
FFS_CLOSING
};
enum ffs_setup_state {
/* There is no setup request pending. */
FFS_NO_SETUP,
/*
* User has read events and there was a setup request event
* there. The next read/write on ep0 will handle the
* request.
*/
FFS_SETUP_PENDING,
/*
* There was event pending but before user space handled it
* some other event was introduced which canceled existing
* setup. If this state is set read/write on ep0 return
* -EIDRM. This state is only set when adding event.
*/
FFS_SETUP_CANCELLED
};
struct ffs_data {
struct usb_gadget *gadget;
/*
* Protect access read/write operations, only one read/write
* at a time. As a consequence protects ep0req and company.
* While setup request is being processed (queued) this is
* held.
*/
struct mutex mutex;
/*
* Protect access to endpoint related structures (basically
* usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
* endpoint zero.
*/
spinlock_t eps_lock;
/*
* XXX REVISIT do we need our own request? Since we are not
* handling setup requests immediately user space may be so
* slow that another setup will be sent to the gadget but this
* time not to us but another function and then there could be
* a race. Is that the case? Or maybe we can use cdev->req
* after all, maybe we just need some spinlock for that?
*/
struct usb_request *ep0req; /* P: mutex */
struct completion ep0req_completion; /* P: mutex */
/* reference counter */
atomic_t ref;
/* how many files are opened (EP0 and others) */
atomic_t opened;
/* EP0 state */
enum ffs_state state;
/*
* Possible transitions:
* + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
* happens only in ep0 read which is P: mutex
* + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
* happens only in ep0 i/o which is P: mutex
* + FFS_SETUP_PENDING -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
* + FFS_SETUP_CANCELLED -> FFS_NO_SETUP -- cmpxchg
*
* This field should never be accessed directly and instead
* ffs_setup_state_clear_cancelled function should be used.
*/
enum ffs_setup_state setup_state;
/* Events & such. */
struct {
u8 types[4];
unsigned short count;
/* XXX REVISIT need to update it in some places, or do we? */
unsigned short can_stall;
struct usb_ctrlrequest setup;
wait_queue_head_t waitq;
} ev; /* the whole structure, P: ev.waitq.lock */
/* Flags */
unsigned long flags;
#define FFS_FL_CALL_CLOSED_CALLBACK 0
#define FFS_FL_BOUND 1
/* Active function */
struct ffs_function *func;
/*
* Device name, write once when file system is mounted.
* Intended for user to read if she wants.
*/
const char *dev_name;
/* Private data for our user (ie. gadget). Managed by user. */
void *private_data;
/* filled by __ffs_data_got_descs() */
/*
* raw_descs is what you kfree, real_descs points inside of raw_descs,
* where full speed, high speed and super speed descriptors start.
* real_descs_length is the length of all those descriptors.
*/
const void *raw_descs_data;
const void *raw_descs;
unsigned raw_descs_length;
unsigned fs_descs_count;
unsigned hs_descs_count;
unsigned ss_descs_count;
unsigned ms_os_descs_count;
unsigned ms_os_descs_ext_prop_count;
unsigned ms_os_descs_ext_prop_name_len;
unsigned ms_os_descs_ext_prop_data_len;
void *ms_os_descs_ext_prop_avail;
void *ms_os_descs_ext_prop_name_avail;
void *ms_os_descs_ext_prop_data_avail;
unsigned user_flags;
u8 eps_addrmap[15];
unsigned short strings_count;
unsigned short interfaces_count;
unsigned short eps_count;
unsigned short _pad1;
/* filled by __ffs_data_got_strings() */
/* ids in stringtabs are set in functionfs_bind() */
const void *raw_strings;
struct usb_gadget_strings **stringtabs;
/*
* File system's super block, write once when file system is
* mounted.
*/
struct super_block *sb;
/* File permissions, written once when fs is mounted */
struct ffs_file_perms {
umode_t mode;
kuid_t uid;
kgid_t gid;
} file_perms;
/*
* The endpoint files, filled by ffs_epfiles_create(),
* destroyed by ffs_epfiles_destroy().
*/
struct ffs_epfile *epfiles;
};
struct f_fs_opts {
struct usb_function_instance func_inst;
struct ffs_dev *dev;
unsigned refcnt;
bool no_configfs;
};
static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi)
{
return container_of(fi, struct f_fs_opts, func_inst);
}
#endif /* U_FFS_H */

View file

@ -0,0 +1,36 @@
/*
* u_gether.h
*
* Utility definitions for the subset function
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_GETHER_H
#define U_GETHER_H
#include <linux/usb/composite.h>
struct f_gether_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
#endif /* U_GETHER_H */

View file

@ -0,0 +1,306 @@
/*
* File Name : u_ncm.c
*
* ncm utilities for composite USB gadgets.
* This utilitie can support to connect head unit for mirror link
*
* Copyright (C) 2011 Samsung Electronics
* Author: SoonYong, Cho <soonyong.cho@samsung.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "f_ncm.c"
#include <linux/miscdevice.h>
/* Support dynamic tethering mode.
* if ncm_connect is true, device is received vendor specific request
* from head unit.
*/
struct ncm_dev {
struct work_struct work;
};
static const char mirrorlink_shortname[] = "usb_ncm";
/* Create misc driver for Mirror Link cmd */
static struct miscdevice mirrorlink_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = mirrorlink_shortname,
//.fops = &mirrorlink_fops,
};
static bool ncm_connect;
/* terminal version using vendor specific request */
u16 terminal_mode_version;
u16 terminal_mode_vendor_id;
struct ncm_function_config {
u8 ethaddr[ETH_ALEN];
struct eth_dev *dev;
};
static struct ncm_dev *_ncm_dev;
static void ncm_work(struct work_struct *data)
{
char *ncm_start[2] = { "NCM_DEVICE=START", NULL };
char *ncm_release[2] = { "NCM_DEVICE=RELEASE", NULL };
char **uevent_envp = NULL;
printk(KERN_DEBUG "usb: %s ncm_connect=%d\n", __func__, ncm_connect);
if ( ncm_connect==true )
uevent_envp = ncm_start;
else
uevent_envp = ncm_release;
kobject_uevent_env(&mirrorlink_device.this_device->kobj, KOBJ_CHANGE, uevent_envp);
}
static int ncm_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
struct ncm_dev *dev;
int ret=0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
f->config = kzalloc(sizeof(struct ncm_function_config), GFP_KERNEL);
if (!f->config)
{
kfree(dev);
return -ENOMEM;
}
INIT_WORK(&dev->work, ncm_work);
_ncm_dev = dev;
ret = misc_register(&mirrorlink_device);
if (ret)
printk("usb: %s - usb_ncm misc driver fail \n",__func__);
return 0;
}
static void ncm_function_cleanup(struct android_usb_function *f)
{
misc_deregister(&mirrorlink_device);
kfree(_ncm_dev);
kfree(f->config);
f->config = NULL;
_ncm_dev = NULL;
}
static int ncm_function_bind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
int ret;
int i;
char *src;
struct ncm_function_config *ncm = f->config;
#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
struct eth_dev *e_dev;
#endif
if (!ncm) {
pr_err("%s: ncm_pdata\n", __func__);
return -1;
}
ncm = f->config;
if (!f->config)
return -ENOMEM;
for (i = 0; i < ETH_ALEN; i++)
ncm->ethaddr[i] = 0;
/* create a fake MAC address from our serial number.
* first byte is 0x02 to signify locally administered.
*/
ncm->ethaddr[0] = 0x02;
src = serial_string;
for (i = 0; (i < 256) && *src; i++) {
/* XOR the USB serial across the remaining bytes */
ncm->ethaddr[i % (ETH_ALEN - 1) + 1] ^= *src++;
}
printk(KERN_DEBUG "usb: %s MAC:%02X:%02X:%02X:%02X:%02X:%02X\n",
__func__, ncm->ethaddr[0], ncm->ethaddr[1],
ncm->ethaddr[2], ncm->ethaddr[3], ncm->ethaddr[4],
ncm->ethaddr[5]);
printk(KERN_DEBUG "usb: %s before MAC:%02X:%02X:%02X:%02X:%02X:%02X\n",
__func__, ncm->ethaddr[0], ncm->ethaddr[1],
ncm->ethaddr[2], ncm->ethaddr[3], ncm->ethaddr[4],
ncm->ethaddr[5]);
/* we have to use trick.
* rndis name will be used for ethernet interface name.
*/
e_dev = gether_setup_name(c->cdev->gadget, ncm->ethaddr, "ncm");
if (IS_ERR(e_dev)) {
ret = PTR_ERR(e_dev);
pr_err("%s: gether_setup failed\n", __func__);
return ret;
}
ncm->dev = e_dev;
printk(KERN_DEBUG "usb: %s after MAC:%02X:%02X:%02X:%02X:%02X:%02X\n",
__func__, ncm->ethaddr[0], ncm->ethaddr[1],
ncm->ethaddr[2], ncm->ethaddr[3], ncm->ethaddr[4],
ncm->ethaddr[5]);
return ncm_bind_config(c, ncm->ethaddr, ncm->dev);
}
static void ncm_function_unbind_config(struct android_usb_function *f,
struct usb_configuration *c)
{
struct ncm_function_config *ncm = f->config;
gether_cleanup(ncm->dev);
}
static struct android_usb_function ncm_function = {
.name = "ncm",
.init = ncm_function_init,
.cleanup = ncm_function_cleanup,
.bind_config = ncm_function_bind_config,
.unbind_config = ncm_function_unbind_config,
};
bool is_ncm_ready(char *name)
{
/* Enable ncm function */
if (!strcmp(name, "rndis") || !strcmp(name, "ncm")) {
if (ncm_connect) {
printk(KERN_DEBUG "usb: %s ncm ready (%s)\n",
__func__, name);
return true;
}
}
return false;
}
void set_ncm_device_descriptor(struct usb_device_descriptor *desc)
{
desc->idProduct = 0x685d;
desc->bDeviceClass = USB_CLASS_COMM;
printk(KERN_DEBUG "usb: %s idProduct=0x%x, DeviceClass=0x%x\n",
__func__, desc->idProduct, desc->bDeviceClass);
}
void set_ncm_ready(bool ready)
{
if (ready != ncm_connect)
{
printk(KERN_DEBUG "usb: %s old status=%d, new status=%d\n",
__func__, ncm_connect, ready);
ncm_connect = ready;
schedule_work(&_ncm_dev->work);
}
else
ncm_connect = ready;
if (ready == false) {
terminal_mode_version = 0;
terminal_mode_vendor_id = 0;
}
}
EXPORT_SYMBOL(set_ncm_ready);
static ssize_t terminal_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = sprintf(buf, "major %x minor %x vendor %x\n",
terminal_mode_version & 0xff,
(terminal_mode_version >> 8 & 0xff),
terminal_mode_vendor_id);
if(terminal_mode_version)
printk(KERN_DEBUG "usb: %s terminal_mode %s\n", __func__, buf);
return ret;
}
static ssize_t terminal_version_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
int value;
sscanf(buf, "%x", &value);
terminal_mode_version = (u16)value;
printk(KERN_DEBUG "usb: %s buf=%s\n", __func__, buf);
/* only set ncm ready when terminal verision value is not zero */
if(value)
set_ncm_ready(true);
else
set_ncm_ready(false);
return size;
}
static DEVICE_ATTR(terminal_version, S_IRUGO | S_IWUSR,
terminal_version_show, terminal_version_store);
static int create_terminal_attribute(struct device **pdev)
{
int err;
if (IS_ERR(*pdev)) {
printk(KERN_DEBUG "usb: %s error pdev(%p)\n",
__func__, *pdev);
return PTR_ERR(*pdev);
}
err = device_create_file(*pdev, &dev_attr_terminal_version);
if (err) {
printk(KERN_DEBUG "usb: %s failed to create attr\n",
__func__);
return err;
}
return 0;
}
static int terminal_ctrl_request(struct usb_composite_dev *cdev,
const struct usb_ctrlrequest *ctrl)
{
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
/* Handle Terminal mode request */
if (ctrl->bRequest == 0xf0) {
terminal_mode_version = w_value;
terminal_mode_vendor_id = w_index;
set_ncm_ready(true);
printk(KERN_DEBUG "usb: %s ver=0x%x vendor_id=0x%x\n",
__func__, terminal_mode_version,
terminal_mode_vendor_id);
value = 0;
}
}
/* respond ZLP */
if (value >= 0) {
int rc;
cdev->req->zero = 0;
cdev->req->length = value;
rc = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC);
if (rc < 0)
printk(KERN_DEBUG "usb: %s failed usb_ep_queue\n",
__func__);
}
return value;
}

View file

@ -0,0 +1,36 @@
/*
* u_ncm.h
*
* Utility definitions for the ncm function
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_NCM_H
#define U_NCM_H
#include <linux/usb/composite.h>
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
bool bound;
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
#endif /* U_NCM_H */

View file

@ -0,0 +1,29 @@
/*
* u_phonet.h - interface to Phonet
*
* Copyright (C) 2007-2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#ifndef __U_PHONET_H
#define __U_PHONET_H
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
struct f_phonet_opts {
struct usb_function_instance func_inst;
bool bound;
struct net_device *net;
};
struct net_device *gphonet_setup_default(void);
void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
int gphonet_register_netdev(struct net_device *net);
int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
void gphonet_cleanup(struct net_device *dev);
#endif /* __U_PHONET_H */

View file

@ -0,0 +1,46 @@
/*
* u_rndis.h
*
* Utility definitions for the subset function
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_RNDIS_H
#define U_RNDIS_H
#include <linux/usb/composite.h>
struct f_rndis_opts {
struct usb_function_instance func_inst;
u32 vendor_id;
const char *manufacturer;
struct net_device *net;
bool bound;
bool borrowed_net;
struct usb_os_desc rndis_os_desc;
char rndis_ext_compat_id[16];
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
int rndis_init(void);
void rndis_exit(void);
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
#endif /* U_RNDIS_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
/*
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#ifndef __U_SERIAL_H
#define __U_SERIAL_H
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
#define MAX_U_SERIAL_PORTS 4
struct f_serial_opts {
struct usb_function_instance func_inst;
u8 port_num;
};
/*
* One non-multiplexed "serial" I/O port ... there can be several of these
* on any given USB peripheral device, if it provides enough endpoints.
*
* The "u_serial" utility component exists to do one thing: manage TTY
* style I/O using the USB peripheral endpoints listed here, including
* hookups to sysfs and /dev for each logical "tty" device.
*
* REVISIT at least ACM could support tiocmget() if needed.
*
* REVISIT someday, allow multiplexing several TTYs over these endpoints.
*/
struct gserial {
struct usb_function func;
/* port is managed by gserial_{connect,disconnect} */
struct gs_port *ioport;
struct usb_ep *in;
struct usb_ep *out;
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
/* notification callbacks */
void (*connect)(struct gserial *p);
void (*disconnect)(struct gserial *p);
int (*send_break)(struct gserial *p, int duration);
};
/* utilities to allocate/free request and buffer */
struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
void gs_free_req(struct usb_ep *, struct usb_request *req);
/* management of individual TTY ports */
int gserial_alloc_line(unsigned char *port_line);
void gserial_free_line(unsigned char port_line);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
/* functions are bound to configurations by a config or gadget driver */
int gser_bind_config(struct usb_configuration *c, u8 port_num);
int obex_bind_config(struct usb_configuration *c, u8 port_num);
#endif /* __U_SERIAL_H */

View file

@ -0,0 +1,318 @@
/*
* u_uac1.c -- ALSA audio utilities for Gadget stack
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/random.h>
#include <linux/syscalls.h>
#include "u_uac1.h"
/*
* This component encapsulates the ALSA devices for USB audio gadget
*/
/*-------------------------------------------------------------------------*/
/**
* Some ALSA internal helper functions
*/
static int snd_interval_refine_set(struct snd_interval *i, unsigned int val)
{
struct snd_interval t;
t.empty = 0;
t.min = t.max = val;
t.openmin = t.openmax = 0;
t.integer = 1;
return snd_interval_refine(i, &t);
}
static int _snd_pcm_hw_param_set(struct snd_pcm_hw_params *params,
snd_pcm_hw_param_t var, unsigned int val,
int dir)
{
int changed;
if (hw_is_mask(var)) {
struct snd_mask *m = hw_param_mask(params, var);
if (val == 0 && dir < 0) {
changed = -EINVAL;
snd_mask_none(m);
} else {
if (dir > 0)
val++;
else if (dir < 0)
val--;
changed = snd_mask_refine_set(
hw_param_mask(params, var), val);
}
} else if (hw_is_interval(var)) {
struct snd_interval *i = hw_param_interval(params, var);
if (val == 0 && dir < 0) {
changed = -EINVAL;
snd_interval_none(i);
} else if (dir == 0)
changed = snd_interval_refine_set(i, val);
else {
struct snd_interval t;
t.openmin = 1;
t.openmax = 1;
t.empty = 0;
t.integer = 0;
if (dir < 0) {
t.min = val - 1;
t.max = val;
} else {
t.min = val;
t.max = val+1;
}
changed = snd_interval_refine(i, &t);
}
} else
return -EINVAL;
if (changed) {
params->cmask |= 1 << var;
params->rmask |= 1 << var;
}
return changed;
}
/*-------------------------------------------------------------------------*/
/**
* Set default hardware params
*/
static int playback_default_hw_params(struct gaudio_snd_dev *snd)
{
struct snd_pcm_substream *substream = snd->substream;
struct snd_pcm_hw_params *params;
snd_pcm_sframes_t result;
/*
* SNDRV_PCM_ACCESS_RW_INTERLEAVED,
* SNDRV_PCM_FORMAT_S16_LE
* CHANNELS: 2
* RATE: 48000
*/
snd->access = SNDRV_PCM_ACCESS_RW_INTERLEAVED;
snd->format = SNDRV_PCM_FORMAT_S16_LE;
snd->channels = 2;
snd->rate = 48000;
params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
_snd_pcm_hw_params_any(params);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_ACCESS,
snd->access, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_FORMAT,
snd->format, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_CHANNELS,
snd->channels, 0);
_snd_pcm_hw_param_set(params, SNDRV_PCM_HW_PARAM_RATE,
snd->rate, 0);
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_HW_PARAMS, params);
result = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
if (result < 0) {
ERROR(snd->card,
"Preparing sound card failed: %d\n", (int)result);
kfree(params);
return result;
}
/* Store the hardware parameters */
snd->access = params_access(params);
snd->format = params_format(params);
snd->channels = params_channels(params);
snd->rate = params_rate(params);
kfree(params);
INFO(snd->card,
"Hardware params: access %x, format %x, channels %d, rate %d\n",
snd->access, snd->format, snd->channels, snd->rate);
return 0;
}
/**
* Playback audio buffer data by ALSA PCM device
*/
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count)
{
struct gaudio_snd_dev *snd = &card->playback;
struct snd_pcm_substream *substream = snd->substream;
struct snd_pcm_runtime *runtime = substream->runtime;
mm_segment_t old_fs;
ssize_t result;
snd_pcm_sframes_t frames;
try_again:
if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
result = snd_pcm_kernel_ioctl(substream,
SNDRV_PCM_IOCTL_PREPARE, NULL);
if (result < 0) {
ERROR(card, "Preparing sound card failed: %d\n",
(int)result);
return result;
}
}
frames = bytes_to_frames(runtime, count);
old_fs = get_fs();
set_fs(KERNEL_DS);
result = snd_pcm_lib_write(snd->substream, (void __user *)buf, frames);
if (result != frames) {
ERROR(card, "Playback error: %d\n", (int)result);
set_fs(old_fs);
goto try_again;
}
set_fs(old_fs);
return 0;
}
int u_audio_get_playback_channels(struct gaudio *card)
{
return card->playback.channels;
}
int u_audio_get_playback_rate(struct gaudio *card)
{
return card->playback.rate;
}
/**
* Open ALSA PCM and control device files
* Initial the PCM or control device
*/
static int gaudio_open_snd_dev(struct gaudio *card)
{
struct snd_pcm_file *pcm_file;
struct gaudio_snd_dev *snd;
struct f_uac1_opts *opts;
char *fn_play, *fn_cap, *fn_cntl;
opts = container_of(card->func.fi, struct f_uac1_opts, func_inst);
fn_play = opts->fn_play;
fn_cap = opts->fn_cap;
fn_cntl = opts->fn_cntl;
if (!card)
return -ENODEV;
/* Open control device */
snd = &card->control;
snd->filp = filp_open(fn_cntl, O_RDWR, 0);
if (IS_ERR(snd->filp)) {
int ret = PTR_ERR(snd->filp);
ERROR(card, "unable to open sound control device file: %s\n",
fn_cntl);
snd->filp = NULL;
return ret;
}
snd->card = card;
/* Open PCM playback device and setup substream */
snd = &card->playback;
snd->filp = filp_open(fn_play, O_WRONLY, 0);
if (IS_ERR(snd->filp)) {
int ret = PTR_ERR(snd->filp);
ERROR(card, "No such PCM playback device: %s\n", fn_play);
snd->filp = NULL;
return ret;
}
pcm_file = snd->filp->private_data;
snd->substream = pcm_file->substream;
snd->card = card;
playback_default_hw_params(snd);
/* Open PCM capture device and setup substream */
snd = &card->capture;
snd->filp = filp_open(fn_cap, O_RDONLY, 0);
if (IS_ERR(snd->filp)) {
ERROR(card, "No such PCM capture device: %s\n", fn_cap);
snd->substream = NULL;
snd->card = NULL;
snd->filp = NULL;
} else {
pcm_file = snd->filp->private_data;
snd->substream = pcm_file->substream;
snd->card = card;
}
return 0;
}
/**
* Close ALSA PCM and control device files
*/
static int gaudio_close_snd_dev(struct gaudio *gau)
{
struct gaudio_snd_dev *snd;
/* Close control device */
snd = &gau->control;
if (snd->filp)
filp_close(snd->filp, NULL);
/* Close PCM playback device and setup substream */
snd = &gau->playback;
if (snd->filp)
filp_close(snd->filp, NULL);
/* Close PCM capture device and setup substream */
snd = &gau->capture;
if (snd->filp)
filp_close(snd->filp, NULL);
return 0;
}
/**
* gaudio_setup - setup ALSA interface and preparing for USB transfer
*
* This sets up PCM, mixer or MIDI ALSA devices fore USB gadget using.
*
* Returns negative errno, or zero on success
*/
int gaudio_setup(struct gaudio *card)
{
int ret;
ret = gaudio_open_snd_dev(card);
if (ret)
ERROR(card, "we need at least one control device\n");
return ret;
}
/**
* gaudio_cleanup - remove ALSA device interface
*
* This is called to free all resources allocated by @gaudio_setup().
*/
void gaudio_cleanup(struct gaudio *the_card)
{
if (the_card) {
gaudio_close_snd_dev(the_card);
}
}

View file

@ -0,0 +1,85 @@
/*
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#ifndef __U_AUDIO_H
#define __U_AUDIO_H
#include <linux/device.h>
#include <linux/err.h>
#include <linux/usb/audio.h>
#include <linux/usb/composite.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "gadget_chips.h"
#define FILE_PCM_PLAYBACK "/dev/snd/pcmC0D0p"
#define FILE_PCM_CAPTURE "/dev/snd/pcmC0D0c"
#define FILE_CONTROL "/dev/snd/controlC0"
#define UAC1_OUT_EP_MAX_PACKET_SIZE 200
#define UAC1_REQ_COUNT 256
#define UAC1_AUDIO_BUF_SIZE 48000
/*
* This represents the USB side of an audio card device, managed by a USB
* function which provides control and stream interfaces.
*/
struct gaudio_snd_dev {
struct gaudio *card;
struct file *filp;
struct snd_pcm_substream *substream;
int access;
int format;
int channels;
int rate;
};
struct gaudio {
struct usb_function func;
struct usb_gadget *gadget;
/* ALSA sound device interfaces */
struct gaudio_snd_dev control;
struct gaudio_snd_dev playback;
struct gaudio_snd_dev capture;
/* TODO */
};
struct f_uac1_opts {
struct usb_function_instance func_inst;
int req_buf_size;
int req_count;
int audio_buf_size;
char *fn_play;
char *fn_cap;
char *fn_cntl;
unsigned bound:1;
unsigned fn_play_alloc:1;
unsigned fn_cap_alloc:1;
unsigned fn_cntl_alloc:1;
struct gaudio *card;
struct mutex lock;
int refcnt;
};
int gaudio_setup(struct gaudio *card);
void gaudio_cleanup(struct gaudio *the_card);
size_t u_audio_playback(struct gaudio *card, void *buf, size_t count);
int u_audio_get_playback_channels(struct gaudio *card);
int u_audio_get_playback_rate(struct gaudio *card);
#endif /* __U_AUDIO_H */

View file

@ -0,0 +1,42 @@
/*
* u_uac2.h
*
* Utility definitions for UAC2 function
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_UAC2_H
#define U_UAC2_H
#include <linux/usb/composite.h>
#define UAC2_DEF_PCHMASK 0x3
#define UAC2_DEF_PSRATE 48000
#define UAC2_DEF_PSSIZE 2
#define UAC2_DEF_CCHMASK 0x3
#define UAC2_DEF_CSRATE 64000
#define UAC2_DEF_CSSIZE 2
struct f_uac2_opts {
struct usb_function_instance func_inst;
int p_chmask;
int p_srate;
int p_ssize;
int c_chmask;
int c_srate;
int c_ssize;
bool bound;
struct mutex lock;
int refcnt;
};
#endif

View file

@ -0,0 +1,39 @@
/*
* u_uvc.h
*
* Utility definitions for the uvc function
*
* Copyright (c) 2013-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef U_UVC_H
#define U_UVC_H
#include <linux/usb/composite.h>
#define to_f_uvc_opts(f) container_of(f, struct f_uvc_opts, func_inst)
struct f_uvc_opts {
struct usb_function_instance func_inst;
unsigned int uvc_gadget_trace_param;
unsigned int streaming_interval;
unsigned int streaming_maxpacket;
unsigned int streaming_maxburst;
const struct uvc_descriptor_header * const *fs_control;
const struct uvc_descriptor_header * const *ss_control;
const struct uvc_descriptor_header * const *fs_streaming;
const struct uvc_descriptor_header * const *hs_streaming;
const struct uvc_descriptor_header * const *ss_streaming;
};
void uvc_set_trace_param(unsigned int trace);
#endif /* U_UVC_H */

View file

@ -0,0 +1,200 @@
/*
* uvc_gadget.h -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#ifndef _UVC_GADGET_H_
#define _UVC_GADGET_H_
#include <linux/ioctl.h>
#include <linux/types.h>
#include <linux/usb/ch9.h>
#define UVC_EVENT_FIRST (V4L2_EVENT_PRIVATE_START + 0)
#define UVC_EVENT_CONNECT (V4L2_EVENT_PRIVATE_START + 0)
#define UVC_EVENT_DISCONNECT (V4L2_EVENT_PRIVATE_START + 1)
#define UVC_EVENT_STREAMON (V4L2_EVENT_PRIVATE_START + 2)
#define UVC_EVENT_STREAMOFF (V4L2_EVENT_PRIVATE_START + 3)
#define UVC_EVENT_SETUP (V4L2_EVENT_PRIVATE_START + 4)
#define UVC_EVENT_DATA (V4L2_EVENT_PRIVATE_START + 5)
#define UVC_EVENT_LAST (V4L2_EVENT_PRIVATE_START + 5)
struct uvc_request_data
{
__s32 length;
__u8 data[60];
};
struct uvc_event
{
union {
enum usb_device_speed speed;
struct usb_ctrlrequest req;
struct uvc_request_data data;
};
};
#define UVCIOC_SEND_RESPONSE _IOW('U', 1, struct uvc_request_data)
#define UVC_INTF_CONTROL 0
#define UVC_INTF_STREAMING 1
/* ------------------------------------------------------------------------
* Debugging, printing and logging
*/
#ifdef __KERNEL__
#include <linux/usb.h> /* For usb_endpoint_* */
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/videodev2.h>
#include <linux/version.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-device.h>
#include "uvc_queue.h"
#define UVC_TRACE_PROBE (1 << 0)
#define UVC_TRACE_DESCR (1 << 1)
#define UVC_TRACE_CONTROL (1 << 2)
#define UVC_TRACE_FORMAT (1 << 3)
#define UVC_TRACE_CAPTURE (1 << 4)
#define UVC_TRACE_CALLS (1 << 5)
#define UVC_TRACE_IOCTL (1 << 6)
#define UVC_TRACE_FRAME (1 << 7)
#define UVC_TRACE_SUSPEND (1 << 8)
#define UVC_TRACE_STATUS (1 << 9)
#define UVC_WARN_MINMAX 0
#define UVC_WARN_PROBE_DEF 1
extern unsigned int uvc_gadget_trace_param;
#define uvc_trace(flag, msg...) \
do { \
if (uvc_gadget_trace_param & flag) \
printk(KERN_DEBUG "uvcvideo: " msg); \
} while (0)
#define uvc_warn_once(dev, warn, msg...) \
do { \
if (!test_and_set_bit(warn, &dev->warnings)) \
printk(KERN_INFO "uvcvideo: " msg); \
} while (0)
#define uvc_printk(level, msg...) \
printk(level "uvcvideo: " msg)
/* ------------------------------------------------------------------------
* Driver specific constants
*/
#define UVC_NUM_REQUESTS 4
#define UVC_MAX_REQUEST_SIZE 64
#define UVC_MAX_EVENTS 4
/* ------------------------------------------------------------------------
* Structures
*/
struct uvc_video
{
struct usb_ep *ep;
/* Frame parameters */
u8 bpp;
u32 fcc;
unsigned int width;
unsigned int height;
unsigned int imagesize;
/* Requests */
unsigned int req_size;
struct usb_request *req[UVC_NUM_REQUESTS];
__u8 *req_buffer[UVC_NUM_REQUESTS];
struct list_head req_free;
spinlock_t req_lock;
void (*encode) (struct usb_request *req, struct uvc_video *video,
struct uvc_buffer *buf);
/* Context data used by the completion handler */
__u32 payload_size;
__u32 max_payload_size;
struct uvc_video_queue queue;
unsigned int fid;
};
enum uvc_state
{
UVC_STATE_DISCONNECTED,
UVC_STATE_CONNECTED,
UVC_STATE_STREAMING,
};
struct uvc_device
{
struct video_device *vdev;
struct v4l2_device v4l2_dev;
enum uvc_state state;
struct usb_function func;
struct uvc_video video;
/* Descriptors */
struct {
const struct uvc_descriptor_header * const *fs_control;
const struct uvc_descriptor_header * const *ss_control;
const struct uvc_descriptor_header * const *fs_streaming;
const struct uvc_descriptor_header * const *hs_streaming;
const struct uvc_descriptor_header * const *ss_streaming;
} desc;
unsigned int control_intf;
struct usb_ep *control_ep;
struct usb_request *control_req;
void *control_buf;
unsigned int streaming_intf;
/* Events */
unsigned int event_length;
unsigned int event_setup_out : 1;
};
static inline struct uvc_device *to_uvc(struct usb_function *f)
{
return container_of(f, struct uvc_device, func);
}
struct uvc_file_handle
{
struct v4l2_fh vfh;
struct uvc_video *device;
};
#define to_uvc_file_handle(handle) \
container_of(handle, struct uvc_file_handle, vfh)
/* ------------------------------------------------------------------------
* Functions
*/
extern void uvc_function_setup_continue(struct uvc_device *uvc);
extern void uvc_endpoint_stream(struct uvc_device *dev);
extern void uvc_function_connect(struct uvc_device *uvc);
extern void uvc_function_disconnect(struct uvc_device *uvc);
#endif /* __KERNEL__ */
#endif /* _UVC_GADGET_H_ */

View file

@ -0,0 +1,403 @@
/*
* uvc_queue.c -- USB Video Class driver - Buffers management
*
* Copyright (C) 2005-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <media/v4l2-common.h>
#include <media/videobuf2-vmalloc.h>
#include "uvc.h"
/* ------------------------------------------------------------------------
* Video buffers queue management.
*
* Video queues is initialized by uvcg_queue_init(). The function performs
* basic initialization of the uvc_video_queue struct and never fails.
*
* Video buffers are managed by videobuf2. The driver uses a mutex to protect
* the videobuf2 queue operations by serializing calls to videobuf2 and a
* spinlock to protect the IRQ queue that holds the buffers to be processed by
* the driver.
*/
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], void *alloc_ctxs[])
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_video *video = container_of(queue, struct uvc_video, queue);
if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
*nbuffers = UVC_MAX_VIDEO_BUFFERS;
*nplanes = 1;
sizes[0] = video->imagesize;
return 0;
}
static int uvc_buffer_prepare(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
return -EINVAL;
}
if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
return -ENODEV;
buf->state = UVC_BUF_STATE_QUEUED;
buf->mem = vb2_plane_vaddr(vb, 0);
buf->length = vb2_plane_size(vb, 0);
if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
buf->bytesused = 0;
else
buf->bytesused = vb2_get_plane_payload(vb, 0);
return 0;
}
static void uvc_buffer_queue(struct vb2_buffer *vb)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
list_add_tail(&buf->queue, &queue->irqqueue);
} else {
/* If the device is disconnected return the buffer to userspace
* directly. The next QBUF call will fail with -ENODEV.
*/
buf->state = UVC_BUF_STATE_ERROR;
vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
}
spin_unlock_irqrestore(&queue->irqlock, flags);
}
static void uvc_wait_prepare(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
mutex_unlock(&queue->mutex);
}
static void uvc_wait_finish(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
mutex_lock(&queue->mutex);
}
static struct vb2_ops uvc_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
.wait_prepare = uvc_wait_prepare,
.wait_finish = uvc_wait_finish,
};
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
{
int ret;
queue->queue.type = type;
queue->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
queue->queue.mem_ops = &vb2_vmalloc_memops;
queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
| V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
ret = vb2_queue_init(&queue->queue);
if (ret)
return ret;
mutex_init(&queue->mutex);
spin_lock_init(&queue->irqlock);
INIT_LIST_HEAD(&queue->irqqueue);
queue->flags = 0;
return 0;
}
/*
* Free the video buffers.
*/
void uvcg_free_buffers(struct uvc_video_queue *queue)
{
mutex_lock(&queue->mutex);
vb2_queue_release(&queue->queue);
mutex_unlock(&queue->mutex);
}
/*
* Allocate the video buffers.
*/
int uvcg_alloc_buffers(struct uvc_video_queue *queue,
struct v4l2_requestbuffers *rb)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_reqbufs(&queue->queue, rb);
mutex_unlock(&queue->mutex);
return ret ? ret : rb->count;
}
int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_querybuf(&queue->queue, buf);
mutex_unlock(&queue->mutex);
return ret;
}
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
{
unsigned long flags;
int ret;
mutex_lock(&queue->mutex);
ret = vb2_qbuf(&queue->queue, buf);
if (ret < 0)
goto done;
spin_lock_irqsave(&queue->irqlock, flags);
ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
queue->flags &= ~UVC_QUEUE_PAUSED;
spin_unlock_irqrestore(&queue->irqlock, flags);
done:
mutex_unlock(&queue->mutex);
return ret;
}
/*
* Dequeue a video buffer. If nonblocking is false, block until a buffer is
* available.
*/
int uvcg_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
int nonblocking)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
mutex_unlock(&queue->mutex);
return ret;
}
/*
* Poll the video queue.
*
* This function implements video queue polling and is intended to be used by
* the device poll handler.
*/
unsigned int uvcg_queue_poll(struct uvc_video_queue *queue, struct file *file,
poll_table *wait)
{
unsigned int ret;
mutex_lock(&queue->mutex);
ret = vb2_poll(&queue->queue, file, wait);
mutex_unlock(&queue->mutex);
return ret;
}
int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
{
int ret;
mutex_lock(&queue->mutex);
ret = vb2_mmap(&queue->queue, vma);
mutex_unlock(&queue->mutex);
return ret;
}
#ifndef CONFIG_MMU
/*
* Get unmapped area.
*
* NO-MMU arch need this function to make mmap() work correctly.
*/
unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
unsigned long pgoff)
{
unsigned long ret;
mutex_lock(&queue->mutex);
ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
mutex_unlock(&queue->mutex);
return ret;
}
#endif
/*
* Cancel the video buffers queue.
*
* Cancelling the queue marks all buffers on the irq queue as erroneous,
* wakes them up and removes them from the queue.
*
* If the disconnect parameter is set, further calls to uvc_queue_buffer will
* fail with -ENODEV.
*
* This function acquires the irq spinlock and can be called from interrupt
* context.
*/
void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect)
{
struct uvc_buffer *buf;
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
while (!list_empty(&queue->irqqueue)) {
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
list_del(&buf->queue);
buf->state = UVC_BUF_STATE_ERROR;
vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
}
/* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_queue_buffer and the disconnection event that
* could result in an interruptible wait in uvc_dequeue_buffer. Do not
* blindly replace this logic by checking for the UVC_DEV_DISCONNECTED
* state outside the queue code.
*/
if (disconnect)
queue->flags |= UVC_QUEUE_DISCONNECTED;
spin_unlock_irqrestore(&queue->irqlock, flags);
}
/*
* Enable or disable the video buffers queue.
*
* The queue must be enabled before starting video acquisition and must be
* disabled after stopping it. This ensures that the video buffers queue
* state can be properly initialized before buffers are accessed from the
* interrupt handler.
*
* Enabling the video queue initializes parameters (such as sequence number,
* sync pattern, ...). If the queue is already enabled, return -EBUSY.
*
* Disabling the video queue cancels the queue and removes all buffers from
* the main queue.
*
* This function can't be called from interrupt context. Use
* uvcg_queue_cancel() instead.
*/
int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
{
unsigned long flags;
int ret = 0;
mutex_lock(&queue->mutex);
if (enable) {
ret = vb2_streamon(&queue->queue, queue->queue.type);
if (ret < 0)
goto done;
queue->sequence = 0;
queue->buf_used = 0;
} else {
ret = vb2_streamoff(&queue->queue, queue->queue.type);
if (ret < 0)
goto done;
spin_lock_irqsave(&queue->irqlock, flags);
INIT_LIST_HEAD(&queue->irqqueue);
/*
* FIXME: We need to clear the DISCONNECTED flag to ensure that
* applications will be able to queue buffers for the next
* streaming run. However, clearing it here doesn't guarantee
* that the device will be reconnected in the meantime.
*/
queue->flags &= ~UVC_QUEUE_DISCONNECTED;
spin_unlock_irqrestore(&queue->irqlock, flags);
}
done:
mutex_unlock(&queue->mutex);
return ret;
}
/* called with &queue_irqlock held.. */
struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf)
{
struct uvc_buffer *nextbuf;
if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
buf->length != buf->bytesused) {
buf->state = UVC_BUF_STATE_QUEUED;
vb2_set_plane_payload(&buf->buf, 0, 0);
return buf;
}
list_del(&buf->queue);
if (!list_empty(&queue->irqqueue))
nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
else
nextbuf = NULL;
buf->buf.v4l2_buf.field = V4L2_FIELD_NONE;
buf->buf.v4l2_buf.sequence = queue->sequence++;
v4l2_get_timestamp(&buf->buf.v4l2_buf.timestamp);
vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
return nextbuf;
}
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
{
struct uvc_buffer *buf = NULL;
if (!list_empty(&queue->irqqueue))
buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
queue);
else
queue->flags |= UVC_QUEUE_PAUSED;
return buf;
}

View file

@ -0,0 +1,96 @@
#ifndef _UVC_QUEUE_H_
#define _UVC_QUEUE_H_
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/poll.h>
#include <linux/videodev2.h>
#include <media/videobuf2-core.h>
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of video buffers. */
#define UVC_MAX_VIDEO_BUFFERS 32
/* ------------------------------------------------------------------------
* Structures.
*/
enum uvc_buffer_state {
UVC_BUF_STATE_IDLE = 0,
UVC_BUF_STATE_QUEUED = 1,
UVC_BUF_STATE_ACTIVE = 2,
UVC_BUF_STATE_DONE = 3,
UVC_BUF_STATE_ERROR = 4,
};
struct uvc_buffer {
struct vb2_buffer buf;
struct list_head queue;
enum uvc_buffer_state state;
void *mem;
unsigned int length;
unsigned int bytesused;
};
#define UVC_QUEUE_DISCONNECTED (1 << 0)
#define UVC_QUEUE_DROP_INCOMPLETE (1 << 1)
#define UVC_QUEUE_PAUSED (1 << 2)
struct uvc_video_queue {
struct vb2_queue queue;
struct mutex mutex; /* Protects queue */
unsigned int flags;
__u32 sequence;
unsigned int buf_used;
spinlock_t irqlock; /* Protects flags and irqqueue */
struct list_head irqqueue;
};
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
}
int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
void uvcg_free_buffers(struct uvc_video_queue *queue);
int uvcg_alloc_buffers(struct uvc_video_queue *queue,
struct v4l2_requestbuffers *rb);
int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf);
int uvcg_dequeue_buffer(struct uvc_video_queue *queue,
struct v4l2_buffer *buf, int nonblocking);
unsigned int uvcg_queue_poll(struct uvc_video_queue *queue,
struct file *file, poll_table *wait);
int uvcg_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma);
#ifndef CONFIG_MMU
unsigned long uvcg_queue_get_unmapped_area(struct uvc_video_queue *queue,
unsigned long pgoff);
#endif /* CONFIG_MMU */
void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
#endif /* __KERNEL__ */
#endif /* _UVC_QUEUE_H_ */

View file

@ -0,0 +1,366 @@
/*
* uvc_v4l2.c -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include "f_uvc.h"
#include "uvc.h"
#include "uvc_queue.h"
#include "uvc_video.h"
/* --------------------------------------------------------------------------
* Requests handling
*/
static int
uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
{
struct usb_composite_dev *cdev = uvc->func.config->cdev;
struct usb_request *req = uvc->control_req;
if (data->length < 0)
return usb_ep_set_halt(cdev->gadget->ep0);
req->length = min_t(unsigned int, uvc->event_length, data->length);
req->zero = data->length < uvc->event_length;
memcpy(req->buf, data->data, req->length);
return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
}
/* --------------------------------------------------------------------------
* V4L2 ioctls
*/
struct uvc_format
{
u8 bpp;
u32 fcc;
};
static struct uvc_format uvc_formats[] = {
{ 16, V4L2_PIX_FMT_YUYV },
{ 0, V4L2_PIX_FMT_MJPEG },
};
static int
uvc_v4l2_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct usb_composite_dev *cdev = uvc->func.config->cdev;
strlcpy(cap->driver, "g_uvc", sizeof(cap->driver));
strlcpy(cap->card, cdev->gadget->name, sizeof(cap->card));
strlcpy(cap->bus_info, dev_name(&cdev->gadget->dev),
sizeof(cap->bus_info));
cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
return 0;
}
static int
uvc_v4l2_get_format(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
fmt->fmt.pix.pixelformat = video->fcc;
fmt->fmt.pix.width = video->width;
fmt->fmt.pix.height = video->height;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = video->bpp * video->width / 8;
fmt->fmt.pix.sizeimage = video->imagesize;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
fmt->fmt.pix.priv = 0;
return 0;
}
static int
uvc_v4l2_set_format(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
struct uvc_format *format;
unsigned int imagesize;
unsigned int bpl;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(uvc_formats); ++i) {
format = &uvc_formats[i];
if (format->fcc == fmt->fmt.pix.pixelformat)
break;
}
if (i == ARRAY_SIZE(uvc_formats)) {
printk(KERN_INFO "Unsupported format 0x%08x.\n",
fmt->fmt.pix.pixelformat);
return -EINVAL;
}
bpl = format->bpp * fmt->fmt.pix.width / 8;
imagesize = bpl ? bpl * fmt->fmt.pix.height : fmt->fmt.pix.sizeimage;
video->fcc = format->fcc;
video->bpp = format->bpp;
video->width = fmt->fmt.pix.width;
video->height = fmt->fmt.pix.height;
video->imagesize = imagesize;
fmt->fmt.pix.field = V4L2_FIELD_NONE;
fmt->fmt.pix.bytesperline = bpl;
fmt->fmt.pix.sizeimage = imagesize;
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB;
fmt->fmt.pix.priv = 0;
return 0;
}
static int
uvc_v4l2_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
if (b->type != video->queue.queue.type)
return -EINVAL;
return uvcg_alloc_buffers(&video->queue, b);
}
static int
uvc_v4l2_querybuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
return uvcg_query_buffer(&video->queue, b);
}
static int
uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
int ret;
ret = uvcg_queue_buffer(&video->queue, b);
if (ret < 0)
return ret;
return uvcg_video_pump(video);
}
static int
uvc_v4l2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
return uvcg_dequeue_buffer(&video->queue, b, file->f_flags & O_NONBLOCK);
}
static int
uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
int ret;
if (type != video->queue.queue.type)
return -EINVAL;
/* Enable UVC video. */
ret = uvcg_video_enable(video, 1);
if (ret < 0)
return ret;
/*
* Complete the alternate setting selection setup phase now that
* userspace is ready to provide video frames.
*/
uvc_function_setup_continue(uvc);
uvc->state = UVC_STATE_STREAMING;
return 0;
}
static int
uvc_v4l2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_video *video = &uvc->video;
if (type != video->queue.queue.type)
return -EINVAL;
return uvcg_video_enable(video, 0);
}
static int
uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL;
return v4l2_event_subscribe(fh, sub, 2, NULL);
}
static int
uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
return v4l2_event_unsubscribe(fh, sub);
}
static long
uvc_v4l2_ioctl_default(struct file *file, void *fh, bool valid_prio,
unsigned int cmd, void *arg)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
switch (cmd) {
case UVCIOC_SEND_RESPONSE:
return uvc_send_response(uvc, arg);
default:
return -ENOIOCTLCMD;
}
}
const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops = {
.vidioc_querycap = uvc_v4l2_querycap,
.vidioc_g_fmt_vid_out = uvc_v4l2_get_format,
.vidioc_s_fmt_vid_out = uvc_v4l2_set_format,
.vidioc_reqbufs = uvc_v4l2_reqbufs,
.vidioc_querybuf = uvc_v4l2_querybuf,
.vidioc_qbuf = uvc_v4l2_qbuf,
.vidioc_dqbuf = uvc_v4l2_dqbuf,
.vidioc_streamon = uvc_v4l2_streamon,
.vidioc_streamoff = uvc_v4l2_streamoff,
.vidioc_subscribe_event = uvc_v4l2_subscribe_event,
.vidioc_unsubscribe_event = uvc_v4l2_unsubscribe_event,
.vidioc_default = uvc_v4l2_ioctl_default,
};
/* --------------------------------------------------------------------------
* V4L2
*/
static int
uvc_v4l2_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_file_handle *handle;
handle = kzalloc(sizeof(*handle), GFP_KERNEL);
if (handle == NULL)
return -ENOMEM;
v4l2_fh_init(&handle->vfh, vdev);
v4l2_fh_add(&handle->vfh);
handle->device = &uvc->video;
file->private_data = &handle->vfh;
uvc_function_connect(uvc);
return 0;
}
static int
uvc_v4l2_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
struct uvc_video *video = handle->device;
uvc_function_disconnect(uvc);
uvcg_video_enable(video, 0);
uvcg_free_buffers(&video->queue);
file->private_data = NULL;
v4l2_fh_del(&handle->vfh);
v4l2_fh_exit(&handle->vfh);
kfree(handle);
return 0;
}
static int
uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
return uvcg_queue_mmap(&uvc->video.queue, vma);
}
static unsigned int
uvc_v4l2_poll(struct file *file, poll_table *wait)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
return uvcg_queue_poll(&uvc->video.queue, file, wait);
}
#ifndef CONFIG_MMU
static unsigned long uvcg_v4l2_get_unmapped_area(struct file *file,
unsigned long addr, unsigned long len, unsigned long pgoff,
unsigned long flags)
{
struct video_device *vdev = video_devdata(file);
struct uvc_device *uvc = video_get_drvdata(vdev);
return uvcg_queue_get_unmapped_area(&uvc->video.queue, pgoff);
}
#endif
struct v4l2_file_operations uvc_v4l2_fops = {
.owner = THIS_MODULE,
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.ioctl = video_ioctl2,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
#ifndef CONFIG_MMU
.get_unmapped_area = uvcg_v4l2_get_unmapped_area,
#endif
};

View file

@ -0,0 +1,22 @@
/*
* uvc_v4l2.h -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __UVC_V4L2_H__
#define __UVC_V4L2_H__
extern const struct v4l2_ioctl_ops uvc_v4l2_ioctl_ops;
extern struct v4l2_file_operations uvc_v4l2_fops;
#endif /* __UVC_V4L2_H__ */

View file

@ -0,0 +1,396 @@
/*
* uvc_video.c -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/video.h>
#include <media/v4l2-dev.h>
#include "uvc.h"
#include "uvc_queue.h"
/* --------------------------------------------------------------------------
* Video codecs
*/
static int
uvc_video_encode_header(struct uvc_video *video, struct uvc_buffer *buf,
u8 *data, int len)
{
data[0] = 2;
data[1] = UVC_STREAM_EOH | video->fid;
if (buf->bytesused - video->queue.buf_used <= len - 2)
data[1] |= UVC_STREAM_EOF;
return 2;
}
static int
uvc_video_encode_data(struct uvc_video *video, struct uvc_buffer *buf,
u8 *data, int len)
{
struct uvc_video_queue *queue = &video->queue;
unsigned int nbytes;
void *mem;
/* Copy video data to the USB buffer. */
mem = buf->mem + queue->buf_used;
nbytes = min((unsigned int)len, buf->bytesused - queue->buf_used);
memcpy(data, mem, nbytes);
queue->buf_used += nbytes;
return nbytes;
}
static void
uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
struct uvc_buffer *buf)
{
void *mem = req->buf;
int len = video->req_size;
int ret;
/* Add a header at the beginning of the payload. */
if (video->payload_size == 0) {
ret = uvc_video_encode_header(video, buf, mem, len);
video->payload_size += ret;
mem += ret;
len -= ret;
}
/* Process video data. */
len = min((int)(video->max_payload_size - video->payload_size), len);
ret = uvc_video_encode_data(video, buf, mem, len);
video->payload_size += ret;
len -= ret;
req->length = video->req_size - len;
req->zero = video->payload_size == video->max_payload_size;
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
video->payload_size = 0;
}
if (video->payload_size == video->max_payload_size ||
buf->bytesused == video->queue.buf_used)
video->payload_size = 0;
}
static void
uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
struct uvc_buffer *buf)
{
void *mem = req->buf;
int len = video->req_size;
int ret;
/* Add the header. */
ret = uvc_video_encode_header(video, buf, mem, len);
mem += ret;
len -= ret;
/* Process video data. */
ret = uvc_video_encode_data(video, buf, mem, len);
len -= ret;
req->length = video->req_size - len;
if (buf->bytesused == video->queue.buf_used) {
video->queue.buf_used = 0;
buf->state = UVC_BUF_STATE_DONE;
uvcg_queue_next_buffer(&video->queue, buf);
video->fid ^= UVC_STREAM_FID;
}
}
/* --------------------------------------------------------------------------
* Request handling
*/
/*
* I somehow feel that synchronisation won't be easy to achieve here. We have
* three events that control USB requests submission:
*
* - USB request completion: the completion handler will resubmit the request
* if a video buffer is available.
*
* - USB interface setting selection: in response to a SET_INTERFACE request,
* the handler will start streaming if a video buffer is available and if
* video is not currently streaming.
*
* - V4L2 buffer queueing: the driver will start streaming if video is not
* currently streaming.
*
* Race conditions between those 3 events might lead to deadlocks or other
* nasty side effects.
*
* The "video currently streaming" condition can't be detected by the irqqueue
* being empty, as a request can still be in flight. A separate "queue paused"
* flag is thus needed.
*
* The paused flag will be set when we try to retrieve the irqqueue head if the
* queue is empty, and cleared when we queue a buffer.
*
* The USB request completion handler will get the buffer at the irqqueue head
* under protection of the queue spinlock. If the queue is empty, the streaming
* paused flag will be set. Right after releasing the spinlock a userspace
* application can queue a buffer. The flag will then cleared, and the ioctl
* handler will restart the video stream.
*/
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = req->context;
struct uvc_video_queue *queue = &video->queue;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
switch (req->status) {
case 0:
break;
case -ESHUTDOWN: /* disconnect from host. */
printk(KERN_DEBUG "VS request cancelled.\n");
uvcg_queue_cancel(queue, 1);
goto requeue;
default:
printk(KERN_INFO "VS request completed with status %d.\n",
req->status);
uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_lock_irqsave(&video->queue.irqlock, flags);
buf = uvcg_queue_head(&video->queue);
if (buf == NULL) {
spin_unlock_irqrestore(&video->queue.irqlock, flags);
goto requeue;
}
video->encode(req, video, buf);
if ((ret = usb_ep_queue(ep, req, GFP_ATOMIC)) < 0) {
printk(KERN_INFO "Failed to queue request (%d).\n", ret);
usb_ep_set_halt(ep);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_unlock_irqrestore(&video->queue.irqlock, flags);
return;
requeue:
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
}
static int
uvc_video_free_requests(struct uvc_video *video)
{
unsigned int i;
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
if (video->req[i]) {
usb_ep_free_request(video->ep, video->req[i]);
video->req[i] = NULL;
}
if (video->req_buffer[i]) {
kfree(video->req_buffer[i]);
video->req_buffer[i] = NULL;
}
}
INIT_LIST_HEAD(&video->req_free);
video->req_size = 0;
return 0;
}
static int
uvc_video_alloc_requests(struct uvc_video *video)
{
unsigned int req_size;
unsigned int i;
int ret = -ENOMEM;
BUG_ON(video->req_size);
req_size = video->ep->maxpacket
* max_t(unsigned int, video->ep->maxburst, 1)
* (video->ep->mult + 1);
for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
if (video->req_buffer[i] == NULL)
goto error;
video->req[i] = usb_ep_alloc_request(video->ep, GFP_KERNEL);
if (video->req[i] == NULL)
goto error;
video->req[i]->buf = video->req_buffer[i];
video->req[i]->length = 0;
video->req[i]->complete = uvc_video_complete;
video->req[i]->context = video;
list_add_tail(&video->req[i]->list, &video->req_free);
}
video->req_size = req_size;
return 0;
error:
uvc_video_free_requests(video);
return ret;
}
/* --------------------------------------------------------------------------
* Video streaming
*/
/*
* uvcg_video_pump - Pump video data into the USB requests
*
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
int uvcg_video_pump(struct uvc_video *video)
{
struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
/* FIXME TODO Race between uvcg_video_pump and requests completion
* handler ???
*/
while (1) {
/* Retrieve the first available USB request, protected by the
* request lock.
*/
spin_lock_irqsave(&video->req_lock, flags);
if (list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
return 0;
}
req = list_first_entry(&video->req_free, struct usb_request,
list);
list_del(&req->list);
spin_unlock_irqrestore(&video->req_lock, flags);
/* Retrieve the first available video buffer and fill the
* request, protected by the video queue irqlock.
*/
spin_lock_irqsave(&queue->irqlock, flags);
buf = uvcg_queue_head(queue);
if (buf == NULL) {
spin_unlock_irqrestore(&queue->irqlock, flags);
break;
}
video->encode(req, video, buf);
/* Queue the USB request */
ret = usb_ep_queue(video->ep, req, GFP_ATOMIC);
if (ret < 0) {
printk(KERN_INFO "Failed to queue request (%d)\n", ret);
usb_ep_set_halt(video->ep);
spin_unlock_irqrestore(&queue->irqlock, flags);
uvcg_queue_cancel(queue, 0);
break;
}
spin_unlock_irqrestore(&queue->irqlock, flags);
}
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
return 0;
}
/*
* Enable or disable the video stream.
*/
int uvcg_video_enable(struct uvc_video *video, int enable)
{
unsigned int i;
int ret;
if (video->ep == NULL) {
printk(KERN_INFO "Video enable failed, device is "
"uninitialized.\n");
return -ENODEV;
}
if (!enable) {
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
if (video->req[i])
usb_ep_dequeue(video->ep, video->req[i]);
uvc_video_free_requests(video);
uvcg_queue_enable(&video->queue, 0);
return 0;
}
if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
return ret;
if ((ret = uvc_video_alloc_requests(video)) < 0)
return ret;
if (video->max_payload_size) {
video->encode = uvc_video_encode_bulk;
video->payload_size = 0;
} else
video->encode = uvc_video_encode_isoc;
return uvcg_video_pump(video);
}
/*
* Initialize the UVC video stream.
*/
int uvcg_video_init(struct uvc_video *video)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
video->fcc = V4L2_PIX_FMT_YUYV;
video->bpp = 16;
video->width = 320;
video->height = 240;
video->imagesize = 320 * 240 * 2;
/* Initialize the video buffers queue. */
uvcg_queue_init(&video->queue, V4L2_BUF_TYPE_VIDEO_OUTPUT);
return 0;
}

View file

@ -0,0 +1,24 @@
/*
* uvc_video.h -- USB Video Class Gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __UVC_VIDEO_H__
#define __UVC_VIDEO_H__
int uvcg_video_pump(struct uvc_video *video);
int uvcg_video_enable(struct uvc_video *video, int enable);
int uvcg_video_init(struct uvc_video *video);
#endif /* __UVC_VIDEO_H__ */

View file

@ -0,0 +1,116 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>
static LIST_HEAD(func_list);
static DEFINE_MUTEX(func_lock);
static struct usb_function_instance *try_get_usb_function_instance(const char *name)
{
struct usb_function_driver *fd;
struct usb_function_instance *fi;
fi = ERR_PTR(-ENOENT);
mutex_lock(&func_lock);
list_for_each_entry(fd, &func_list, list) {
if (strcmp(name, fd->name))
continue;
if (!try_module_get(fd->mod)) {
fi = ERR_PTR(-EBUSY);
break;
}
fi = fd->alloc_inst();
if (IS_ERR(fi))
module_put(fd->mod);
else
fi->fd = fd;
break;
}
mutex_unlock(&func_lock);
return fi;
}
struct usb_function_instance *usb_get_function_instance(const char *name)
{
struct usb_function_instance *fi;
int ret;
fi = try_get_usb_function_instance(name);
if (!IS_ERR(fi))
return fi;
ret = PTR_ERR(fi);
if (ret != -ENOENT)
return fi;
ret = request_module("usbfunc:%s", name);
if (ret < 0)
return ERR_PTR(ret);
return try_get_usb_function_instance(name);
}
EXPORT_SYMBOL_GPL(usb_get_function_instance);
struct usb_function *usb_get_function(struct usb_function_instance *fi)
{
struct usb_function *f;
f = fi->fd->alloc_func(fi);
if (IS_ERR(f))
return f;
f->fi = fi;
return f;
}
EXPORT_SYMBOL_GPL(usb_get_function);
void usb_put_function_instance(struct usb_function_instance *fi)
{
struct module *mod;
if (!fi)
return;
mod = fi->fd->mod;
fi->free_func_inst(fi);
module_put(mod);
}
EXPORT_SYMBOL_GPL(usb_put_function_instance);
void usb_put_function(struct usb_function *f)
{
if (!f)
return;
f->free_func(f);
}
EXPORT_SYMBOL_GPL(usb_put_function);
int usb_function_register(struct usb_function_driver *newf)
{
struct usb_function_driver *fd;
int ret;
ret = -EEXIST;
mutex_lock(&func_lock);
list_for_each_entry(fd, &func_list, list) {
if (!strcmp(fd->name, newf->name))
goto out;
}
ret = 0;
list_add_tail(&newf->list, &func_list);
out:
mutex_unlock(&func_lock);
return ret;
}
EXPORT_SYMBOL_GPL(usb_function_register);
void usb_function_unregister(struct usb_function_driver *fd)
{
mutex_lock(&func_lock);
list_del(&fd->list);
mutex_unlock(&func_lock);
}
EXPORT_SYMBOL_GPL(usb_function_unregister);

View file

@ -0,0 +1,478 @@
#
# USB Gadget support on a system involves
# (a) a peripheral controller, and
# (b) the gadget driver using it.
#
# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
#
# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
# - Some systems have both kinds of controllers.
#
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
select USB_LIBCOMPOSITE
select USB_F_SS_LB
help
Gadget Zero is a two-configuration device. It either sinks and
sources bulk data; or it loops back a configurable number of
transfers. It also implements control requests, for "chapter 9"
conformance. The driver needs only two bulk-capable endpoints, so
it can work on top of most device-side usb controllers. It's
useful for testing, and is also a working example showing how
USB "gadget drivers" can be written.
Make this be the first driver you try using on top of any new
USB peripheral controller driver. Then you can use host-side
test software, like the "usbtest" driver, to put your hardware
and its driver through a basic set of functional tests.
Gadget Zero also works with the host-side "usb-skeleton" driver,
and with many kinds of host-side test software. You may need
to tweak product and vendor IDs before host software knows about
this device, and arrange to select an appropriate configuration.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_zero".
config USB_ZERO_HNPTEST
boolean "HNP Test Device"
depends on USB_ZERO && USB_OTG
help
You can configure this device to enumerate using the device
identifiers of the USB-OTG test device. That means that when
this gadget connects to another OTG device, with this one using
the "B-Peripheral" role, that device will use HNP to let this
one serve as the USB host instead (in the "B-Host" role).
config USB_AUDIO
tristate "Audio Gadget"
depends on SND
select USB_LIBCOMPOSITE
select SND_PCM
select USB_F_UAC1 if GADGET_UAC1
select USB_F_UAC2 if !GADGET_UAC1
help
This Gadget Audio driver is compatible with USB Audio Class
specification 2.0. It implements 1 AudioControl interface,
1 AudioStreaming Interface each for USB-OUT and USB-IN.
Number of channels, sample rate and sample size can be
specified as module parameters.
This driver doesn't expect any real Audio codec to be present
on the device - the audio streams are simply sinked to and
sourced from a virtual ALSA sound card created. The user-space
application may choose to do whatever it wants with the data
received from the USB Host and choose to provide whatever it
wants as audio data to the USB Host.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_audio".
config GADGET_UAC1
bool "UAC 1.0 (Legacy)"
depends on USB_AUDIO
help
If you instead want older UAC Spec-1.0 driver that also has audio
paths hardwired to the Audio codec chip on-board and doesn't work
without one.
config USB_ETH
tristate "Ethernet Gadget (with CDC Ethernet support)"
depends on NET
select USB_LIBCOMPOSITE
select USB_U_ETHER
select USB_F_ECM
select USB_F_SUBSET
select CRC32
help
This driver implements Ethernet style communication, in one of
several ways:
- The "Communication Device Class" (CDC) Ethernet Control Model.
That protocol is often avoided with pure Ethernet adapters, in
favor of simpler vendor-specific hardware, but is widely
supported by firmware for smart network devices.
- On hardware can't implement that protocol, a simple CDC subset
is used, placing fewer demands on USB.
- CDC Ethernet Emulation Model (EEM) is a newer standard that has
a simpler interface that can be used by more USB hardware.
RNDIS support is an additional option, more demanding than than
subset.
Within the USB device, this gadget driver exposes a network device
"usbX", where X depends on what other networking devices you have.
Treat it like a two-node Ethernet link: host, and gadget.
The Linux-USB host-side "usbnet" driver interoperates with this
driver, so that deep I/O queues can be supported. On 2.4 kernels,
use "CDCEther" instead, if you're using the CDC option. That CDC
mode should also interoperate with standard CDC Ethernet class
drivers on other host operating systems.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_ether".
config USB_ETH_RNDIS
bool "RNDIS support"
depends on USB_ETH
select USB_LIBCOMPOSITE
select USB_F_RNDIS
default y
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
and Microsoft provides redistributable binary RNDIS drivers for
older versions of Windows.
If you say "y" here, the Ethernet gadget driver will try to provide
a second device configuration, supporting RNDIS to talk to such
Microsoft USB hosts.
To make MS-Windows work with this, use Documentation/usb/linux.inf
as the "driver info file". For versions of MS-Windows older than
XP, you'll need to download drivers from Microsoft's website; a URL
is given in comments found in that info file.
config USB_ETH_EEM
bool "Ethernet Emulation Model (EEM) support"
depends on USB_ETH
select USB_LIBCOMPOSITE
select USB_F_EEM
default n
help
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
and therefore can be supported by more hardware. Technically ECM and
EEM are designed for different applications. The ECM model extends
the network interface to the target (e.g. a USB cable modem), and the
EEM model is for mobile devices to communicate with hosts using
ethernet over USB. For Linux gadgets, however, the interface with
the host is the same (a usbX device), so the differences are minimal.
If you say "y" here, the Ethernet gadget driver will use the EEM
protocol rather than ECM. If unsure, say "n".
config USB_G_NCM
tristate "Network Control Model (NCM) support"
depends on NET
select USB_LIBCOMPOSITE
select USB_U_ETHER
select USB_F_NCM
select CRC32
help
This driver implements USB CDC NCM subclass standard. NCM is
an advanced protocol for Ethernet encapsulation, allows grouping
of several ethernet frames into one USB transfer and different
alignment possibilities.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_ncm".
config USB_GADGETFS
tristate "Gadget Filesystem"
help
This driver provides a filesystem based API that lets user mode
programs implement a single-configuration USB device, including
endpoint I/O and control requests that don't relate to enumeration.
All endpoints, transfer speeds, and transfer types supported by
the hardware are available, through read() and write() calls.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "gadgetfs".
config USB_FUNCTIONFS
tristate "Function Filesystem"
select USB_LIBCOMPOSITE
select USB_F_FS
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
help
The Function Filesystem (FunctionFS) lets one create USB
composite functions in user space in the same way GadgetFS
lets one create USB gadgets in user space. This allows creation
of composite gadgets such that some of the functions are
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
If you say "y" or "m" here you will be able what kind of
configurations the gadget will provide.
Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_ffs".
config USB_FUNCTIONFS_ETH
bool "Include configuration with CDC ECM (Ethernet)"
depends on USB_FUNCTIONFS && NET
select USB_U_ETHER
select USB_F_ECM
select USB_F_SUBSET
help
Include a configuration with CDC ECM function (Ethernet) and the
Function Filesystem.
config USB_FUNCTIONFS_RNDIS
bool "Include configuration with RNDIS (Ethernet)"
depends on USB_FUNCTIONFS && NET
select USB_U_ETHER
select USB_F_RNDIS
help
Include a configuration with RNDIS function (Ethernet) and the Filesystem.
config USB_FUNCTIONFS_GENERIC
bool "Include 'pure' configuration"
depends on USB_FUNCTIONFS
help
Include a configuration with the Function Filesystem alone with
no Ethernet interface.
config USB_MASS_STORAGE
tristate "Mass Storage Gadget"
depends on BLOCK
select USB_LIBCOMPOSITE
select USB_F_MASS_STORAGE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
This driver is a replacement for now removed File-backed
Storage Gadget (g_file_storage).
Say "y" to link the driver statically, or "m" to build
a dynamically linked module called "g_mass_storage".
config USB_GADGET_TARGET
tristate "USB Gadget Target Fabric Module"
depends on TARGET_CORE
select USB_LIBCOMPOSITE
help
This fabric is an USB gadget. Two USB protocols are supported that is
BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
advertised on alternative interface 0 (primary) and UAS is on
alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
UAS utilizes the USB 3.0 feature called streams support.
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
depends on TTY
select USB_U_SERIAL
select USB_F_ACM
select USB_F_SERIAL
select USB_F_OBEX
select USB_LIBCOMPOSITE
help
The Serial Gadget talks to the Linux-USB generic serial driver.
This driver supports a CDC-ACM module option, which can be used
to interoperate with MS-Windows hosts or with the Linux-USB
"cdc-acm" driver.
This driver also supports a CDC-OBEX option. You will need a
user space OBEX server talking to /dev/ttyGS*, since the kernel
itself doesn't implement the OBEX protocol.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_serial".
For more information, see Documentation/usb/gadget_serial.txt
which includes instructions and a "driver info file" needed to
make MS-Windows work with CDC ACM.
config USB_MIDI_GADGET
tristate "MIDI Gadget"
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
a sound "card" in the ALSA sound system. Other MIDI
connections can then be made on the gadget system, using
ALSA's aconnect utility etc.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_midi".
config USB_G_PRINTER
tristate "Printer Gadget"
select USB_LIBCOMPOSITE
help
The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space
program reads and writes the device file /dev/g_printer to
receive or send printer data. It can use ioctl calls to
the device file to get or set printer status.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_printer".
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
if TTY
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_U_ETHER
select USB_F_ACM
select USB_F_ECM
help
This driver provides two functions in one configuration:
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
This driver requires four bulk and two interrupt endpoints,
plus the ability to handle altsettings. Not all peripheral
controllers are that capable.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module.
config USB_G_NOKIA
tristate "Nokia composite gadget"
depends on PHONET
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_U_ETHER
select USB_F_ACM
select USB_F_OBEX
select USB_F_PHONET
select USB_F_ECM
help
The Nokia composite gadget provides support for acm, obex
and phonet in only one composite gadget driver.
It's only really useful for N900 hardware. If you're building
a kernel for N900, say Y or M here. If unsure, say N.
config USB_G_ACM_MS
tristate "CDC Composite Device (ACM and mass storage)"
depends on BLOCK
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_F_ACM
select USB_F_MASS_STORAGE
help
This driver provides two functions in one configuration:
a mass storage, and a CDC ACM (serial port) link.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_acm_ms".
config USB_G_MULTI
tristate "Multifunction Composite Gadget"
depends on BLOCK && NET
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_U_ETHER
select USB_F_ACM
select USB_F_MASS_STORAGE
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
interfaces.
You will be asked to choose which of the two configurations is
to be available in the gadget. At least one configuration must
be chosen to make the gadget usable. Selecting more than one
configuration will prevent Windows from automatically detecting
the gadget as a composite gadget, so an INF file will be needed to
use the gadget.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_multi".
config USB_G_MULTI_RNDIS
bool "RNDIS + CDC Serial + Storage configuration"
depends on USB_G_MULTI
select USB_F_RNDIS
default y
help
This option enables a configuration with RNDIS, CDC Serial and
Mass Storage functions available in the Multifunction Composite
Gadget. This is the configuration dedicated for Windows since RNDIS
is Microsoft's protocol.
If unsure, say "y".
config USB_G_MULTI_CDC
bool "CDC Ethernet + CDC Serial + Storage configuration"
depends on USB_G_MULTI
default n
select USB_F_ECM
help
This option enables a configuration with CDC Ethernet (ECM), CDC
Serial and Mass Storage functions available in the Multifunction
Composite Gadget.
If unsure, say "y".
endif # TTY
config USB_G_HID
tristate "HID Gadget"
select USB_LIBCOMPOSITE
help
The HID gadget driver provides generic emulation of USB
Human Interface Devices (HID).
For more information, see Documentation/usb/gadget_hid.txt which
includes sample code for accessing the device files.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_hid".
# Standalone / single function gadgets
config USB_G_DBGP
tristate "EHCI Debug Device Gadget"
depends on TTY
select USB_LIBCOMPOSITE
help
This gadget emulates an EHCI Debug device. This is useful when you want
to interact with an EHCI Debug Port.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_dbgp".
if USB_G_DBGP
choice
prompt "EHCI Debug Device mode"
default USB_G_DBGP_SERIAL
config USB_G_DBGP_PRINTK
depends on USB_G_DBGP
bool "printk"
help
Directly printk() received data. No interaction.
config USB_G_DBGP_SERIAL
depends on USB_G_DBGP
select USB_U_SERIAL
bool "serial"
help
Userland can interact using /dev/ttyGSxxx.
endchoice
endif
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
config USB_G_WEBCAM
tristate "USB Webcam Gadget"
depends on VIDEO_DEV
select USB_LIBCOMPOSITE
select VIDEOBUF2_VMALLOC
select USB_F_UVC
help
The Webcam Gadget acts as a composite USB Audio and Video Class
device. It provides a userspace API to process UVC control requests
and stream video data to the host.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_webcam".

View file

@ -0,0 +1,44 @@
#
# USB gadget drivers
#
ccflags-y := -I$(srctree)/drivers/usb/gadget/
ccflags-y += -I$(srctree)/drivers/usb/gadget/udc/
ccflags-y += -I$(srctree)/drivers/usb/gadget/function/
g_zero-y := zero.o
g_audio-y := audio.o
g_ether-y := ether.o
g_serial-y := serial.o
g_midi-y := gmidi.o
gadgetfs-y := inode.o
g_mass_storage-y := mass_storage.o
g_printer-y := printer.o
g_cdc-y := cdc2.o
g_multi-y := multi.o
g_hid-y := hid.o
g_dbgp-y := dbgp.o
g_nokia-y := nokia.o
g_webcam-y := webcam.o
g_ncm-y := ncm.o
g_acm_ms-y := acm_ms.o
g_tcm_usb_gadget-y := tcm_usb_gadget.o
obj-$(CONFIG_USB_ZERO) += g_zero.o
obj-$(CONFIG_USB_AUDIO) += g_audio.o
obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
obj-$(CONFIG_USB_G_HID) += g_hid.o
obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
obj-$(CONFIG_USB_G_MULTI) += g_multi.o
obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
obj-$(CONFIG_USB_G_NCM) += g_ncm.o
obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o

View file

@ -0,0 +1,274 @@
/*
* acm_ms.c -- Composite driver, with ACM and mass storage support
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
* Author: David Brownell
* Modified: Klaus Schwarzkopf <schwarzkopf@sensortherm.de>
*
* Heavily based on multi.c and cdc2.c
*
* 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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "u_serial.h"
#define DRIVER_DESC "Composite Gadget (ACM + MS)"
#define DRIVER_VERSION "2011/10/10"
/*-------------------------------------------------------------------------*/
/*
* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
.bDeviceProtocol = 1,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(ACM_MS_VENDOR_NUM),
.idProduct = cpu_to_le16(ACM_MS_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
/*.bNumConfigurations = DYNAMIC*/
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
/*-------------------------------------------------------------------------*/
static struct usb_function *f_acm;
static struct usb_function_instance *f_acm_inst;
static struct usb_function_instance *fi_msg;
static struct usb_function *f_msg;
/*
* We _always_ have both ACM and mass storage functions.
*/
static int __init acm_ms_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int status;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
opts = fsg_opts_from_func_inst(fi_msg);
f_acm = usb_get_function(f_acm_inst);
if (IS_ERR(f_acm))
return PTR_ERR(f_acm);
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg)) {
status = PTR_ERR(f_msg);
goto put_acm;
}
status = usb_add_function(c, f_acm);
if (status < 0)
goto put_msg;
status = fsg_common_run_thread(opts->common);
if (status)
goto remove_acm;
status = usb_add_function(c, f_msg);
if (status)
goto remove_acm;
return 0;
remove_acm:
usb_remove_function(c, f_acm);
put_msg:
usb_put_function(f_msg);
put_acm:
usb_put_function(f_acm);
return status;
}
static struct usb_configuration acm_ms_config_driver = {
.label = DRIVER_DESC,
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct fsg_opts *opts;
struct fsg_config config;
int status;
f_acm_inst = usb_get_function_instance("acm");
if (IS_ERR(f_acm_inst))
return PTR_ERR(f_acm_inst);
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg)) {
status = PTR_ERR(fi_msg);
goto fail_get_msg;
}
/* set up mass storage function */
fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
opts = fsg_opts_from_func_inst(fi_msg);
opts->no_configfs = true;
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
if (status)
goto fail;
status = fsg_common_set_nluns(opts->common, config.nluns);
if (status)
goto fail_set_nluns;
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(opts->common, true);
status = fsg_common_create_luns(opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
config.product_name);
/*
* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail_string_ids;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
if (status < 0)
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
return 0;
/* error recovery */
fail_string_ids:
fsg_common_remove_luns(opts->common);
fail_set_cdev:
fsg_common_free_luns(opts->common);
fail_set_nluns:
fsg_common_free_buffers(opts->common);
fail:
usb_put_function_instance(fi_msg);
fail_get_msg:
usb_put_function_instance(f_acm_inst);
return status;
}
static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_msg);
usb_put_function_instance(fi_msg);
usb_put_function(f_acm);
usb_put_function_instance(f_acm_inst);
return 0;
}
static __refdata struct usb_composite_driver acm_ms_driver = {
.name = "g_acm_ms",
.dev = &device_desc,
.max_speed = USB_SPEED_SUPER,
.strings = dev_strings,
.bind = acm_ms_bind,
.unbind = __exit_p(acm_ms_unbind),
};
module_usb_composite_driver(acm_ms_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,309 @@
/*
* audio.c -- Audio gadget driver
*
* Copyright (C) 2008 Bryan Wu <cooloney@kernel.org>
* Copyright (C) 2008 Analog Devices, Inc
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
#include "gadget_chips.h"
#define DRIVER_DESC "Linux USB Audio Gadget"
#define DRIVER_VERSION "Feb 2, 2012"
USB_GADGET_COMPOSITE_OPTIONS();
#ifndef CONFIG_GADGET_UAC1
#include "u_uac2.h"
/* Playback(USB-IN) Default Stereo - Fl/Fr */
static int p_chmask = UAC2_DEF_PCHMASK;
module_param(p_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(p_chmask, "Playback Channel Mask");
/* Playback Default 48 KHz */
static int p_srate = UAC2_DEF_PSRATE;
module_param(p_srate, uint, S_IRUGO);
MODULE_PARM_DESC(p_srate, "Playback Sampling Rate");
/* Playback Default 16bits/sample */
static int p_ssize = UAC2_DEF_PSSIZE;
module_param(p_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(p_ssize, "Playback Sample Size(bytes)");
/* Capture(USB-OUT) Default Stereo - Fl/Fr */
static int c_chmask = UAC2_DEF_CCHMASK;
module_param(c_chmask, uint, S_IRUGO);
MODULE_PARM_DESC(c_chmask, "Capture Channel Mask");
/* Capture Default 64 KHz */
static int c_srate = UAC2_DEF_CSRATE;
module_param(c_srate, uint, S_IRUGO);
MODULE_PARM_DESC(c_srate, "Capture Sampling Rate");
/* Capture Default 16bits/sample */
static int c_ssize = UAC2_DEF_CSSIZE;
module_param(c_ssize, uint, S_IRUGO);
MODULE_PARM_DESC(c_ssize, "Capture Sample Size(bytes)");
#else
#include "u_uac1.h"
static char *fn_play = FILE_PCM_PLAYBACK;
module_param(fn_play, charp, S_IRUGO);
MODULE_PARM_DESC(fn_play, "Playback PCM device file name");
static char *fn_cap = FILE_PCM_CAPTURE;
module_param(fn_cap, charp, S_IRUGO);
MODULE_PARM_DESC(fn_cap, "Capture PCM device file name");
static char *fn_cntl = FILE_CONTROL;
module_param(fn_cntl, charp, S_IRUGO);
MODULE_PARM_DESC(fn_cntl, "Control device file name");
static int req_buf_size = UAC1_OUT_EP_MAX_PACKET_SIZE;
module_param(req_buf_size, int, S_IRUGO);
MODULE_PARM_DESC(req_buf_size, "ISO OUT endpoint request buffer size");
static int req_count = UAC1_REQ_COUNT;
module_param(req_count, int, S_IRUGO);
MODULE_PARM_DESC(req_count, "ISO OUT endpoint request count");
static int audio_buf_size = UAC1_AUDIO_BUF_SIZE;
module_param(audio_buf_size, int, S_IRUGO);
MODULE_PARM_DESC(audio_buf_size, "Audio buffer size");
#endif
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *audio_strings[] = {
&stringtab_dev,
NULL,
};
#ifndef CONFIG_GADGET_UAC1
static struct usb_function_instance *fi_uac2;
static struct usb_function *f_uac2;
#else
static struct usb_function_instance *fi_uac1;
static struct usb_function *f_uac1;
#endif
/*-------------------------------------------------------------------------*/
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to Linux Foundation for donating this product ID. */
#define AUDIO_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define AUDIO_PRODUCT_NUM 0x0101 /* Linux-USB Audio Gadget */
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x200),
#ifdef CONFIG_GADGET_UAC1
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
#else
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x01,
#endif
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id defaults change according to what configs
* we support. (As does bNumConfigurations.) These values can
* also be overridden by module parameters.
*/
.idVendor = __constant_cpu_to_le16(AUDIO_VENDOR_NUM),
.idProduct = __constant_cpu_to_le16(AUDIO_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/*-------------------------------------------------------------------------*/
static int __init audio_do_config(struct usb_configuration *c)
{
int status;
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
#ifdef CONFIG_GADGET_UAC1
f_uac1 = usb_get_function(fi_uac1);
if (IS_ERR(f_uac1)) {
status = PTR_ERR(f_uac1);
return status;
}
status = usb_add_function(c, f_uac1);
if (status < 0) {
usb_put_function(f_uac1);
return status;
}
#else
f_uac2 = usb_get_function(fi_uac2);
if (IS_ERR(f_uac2)) {
status = PTR_ERR(f_uac2);
return status;
}
status = usb_add_function(c, f_uac2);
if (status < 0) {
usb_put_function(f_uac2);
return status;
}
#endif
return 0;
}
static struct usb_configuration audio_config_driver = {
.label = DRIVER_DESC,
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int __init audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
struct f_uac2_opts *uac2_opts;
#else
struct f_uac1_opts *uac1_opts;
#endif
int status;
#ifndef CONFIG_GADGET_UAC1
fi_uac2 = usb_get_function_instance("uac2");
if (IS_ERR(fi_uac2))
return PTR_ERR(fi_uac2);
#else
fi_uac1 = usb_get_function_instance("uac1");
if (IS_ERR(fi_uac1))
return PTR_ERR(fi_uac1);
#endif
#ifndef CONFIG_GADGET_UAC1
uac2_opts = container_of(fi_uac2, struct f_uac2_opts, func_inst);
uac2_opts->p_chmask = p_chmask;
uac2_opts->p_srate = p_srate;
uac2_opts->p_ssize = p_ssize;
uac2_opts->c_chmask = c_chmask;
uac2_opts->c_srate = c_srate;
uac2_opts->c_ssize = c_ssize;
#else
uac1_opts = container_of(fi_uac1, struct f_uac1_opts, func_inst);
uac1_opts->fn_play = fn_play;
uac1_opts->fn_cap = fn_cap;
uac1_opts->fn_cntl = fn_cntl;
uac1_opts->req_buf_size = req_buf_size;
uac1_opts->req_count = req_count;
uac1_opts->audio_buf_size = audio_buf_size;
#endif
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
if (status < 0)
goto fail;
usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
return 0;
fail:
#ifndef CONFIG_GADGET_UAC1
usb_put_function_instance(fi_uac2);
#else
usb_put_function_instance(fi_uac1);
#endif
return status;
}
static int __exit audio_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_GADGET_UAC1
if (!IS_ERR_OR_NULL(f_uac1))
usb_put_function(f_uac1);
if (!IS_ERR_OR_NULL(fi_uac1))
usb_put_function_instance(fi_uac1);
#else
if (!IS_ERR_OR_NULL(f_uac2))
usb_put_function(f_uac2);
if (!IS_ERR_OR_NULL(fi_uac2))
usb_put_function_instance(fi_uac2);
#endif
return 0;
}
static __refdata struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc,
.strings = audio_strings,
.max_speed = USB_SPEED_HIGH,
.bind = audio_bind,
.unbind = __exit_p(audio_unbind),
};
module_usb_composite_driver(audio_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,238 @@
/*
* cdc2.c -- CDC Composite driver, with ECM and ACM support
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include "u_ether.h"
#include "u_serial.h"
#include "u_ecm.h"
#define DRIVER_DESC "CDC Composite Gadget"
#define DRIVER_VERSION "King Kamehameha Day 2008"
/*-------------------------------------------------------------------------*/
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to NetChip Technologies for donating this product ID.
* It's for devices with only this composite CDC configuration.
*/
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
USB_GADGET_COMPOSITE_OPTIONS();
USB_ETHERNET_MODULE_PARAMETERS();
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(CDC_VENDOR_NUM),
.idProduct = cpu_to_le16(CDC_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
/*-------------------------------------------------------------------------*/
static struct usb_function *f_acm;
static struct usb_function_instance *fi_serial;
static struct usb_function *f_ecm;
static struct usb_function_instance *fi_ecm;
/*
* We _always_ have both CDC ECM and CDC ACM functions.
*/
static int __init cdc_do_config(struct usb_configuration *c)
{
int status;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm)) {
status = PTR_ERR(f_ecm);
goto err_get_ecm;
}
status = usb_add_function(c, f_ecm);
if (status)
goto err_add_ecm;
f_acm = usb_get_function(fi_serial);
if (IS_ERR(f_acm)) {
status = PTR_ERR(f_acm);
goto err_get_acm;
}
status = usb_add_function(c, f_acm);
if (status)
goto err_add_acm;
return 0;
err_add_acm:
usb_put_function(f_acm);
err_get_acm:
usb_remove_function(c, f_ecm);
err_add_ecm:
usb_put_function(f_ecm);
err_get_ecm:
return status;
}
static struct usb_configuration cdc_config_driver = {
.label = "CDC Composite (ECM + ACM)",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int __init cdc_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_ecm_opts *ecm_opts;
int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
gadget->name);
return -EINVAL;
}
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm))
return PTR_ERR(fi_ecm);
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
gether_set_qmult(ecm_opts->net, qmult);
if (!gether_set_host_addr(ecm_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
fi_serial = usb_get_function_instance("acm");
if (IS_ERR(fi_serial)) {
status = PTR_ERR(fi_serial);
goto fail;
}
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
if (status < 0)
goto fail1;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
return 0;
fail1:
usb_put_function_instance(fi_serial);
fail:
usb_put_function_instance(fi_ecm);
return status;
}
static int __exit cdc_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_acm);
usb_put_function_instance(fi_serial);
if (!IS_ERR_OR_NULL(f_ecm))
usb_put_function(f_ecm);
if (!IS_ERR_OR_NULL(fi_ecm))
usb_put_function_instance(fi_ecm);
return 0;
}
static __refdata struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = cdc_bind,
.unbind = __exit_p(cdc_unbind),
};
module_usb_composite_driver(cdc_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,437 @@
/*
* dbgp.c -- EHCI Debug Port device gadget
*
* Copyright (C) 2010 Stephane Duverger
*
* Released under the GPLv2.
*/
/* verbose messages */
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "u_serial.h"
#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
#define USB_DEBUG_MAX_PACKET_SIZE 8
#define DBGP_REQ_EP0_LEN 128
#define DBGP_REQ_LEN 512
static struct dbgp {
struct usb_gadget *gadget;
struct usb_request *req;
struct usb_ep *i_ep;
struct usb_ep *o_ep;
#ifdef CONFIG_USB_G_DBGP_SERIAL
struct gserial *serial;
#endif
} dbgp;
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
.bNumConfigurations = 1,
};
static struct usb_debug_descriptor dbg_desc = {
.bLength = sizeof dbg_desc,
.bDescriptorType = USB_DT_DEBUG,
};
static struct usb_endpoint_descriptor i_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.bEndpointAddress = USB_DIR_IN,
};
static struct usb_endpoint_descriptor o_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.bEndpointAddress = USB_DIR_OUT,
};
#ifdef CONFIG_USB_G_DBGP_PRINTK
static int dbgp_consume(char *buf, unsigned len)
{
char c;
if (!len)
return 0;
c = buf[len-1];
if (c != 0)
buf[len-1] = 0;
printk(KERN_NOTICE "%s%c", buf, c);
return 0;
}
static void __disable_ep(struct usb_ep *ep)
{
if (ep && ep->driver_data == dbgp.gadget) {
usb_ep_disable(ep);
ep->driver_data = NULL;
}
}
static void dbgp_disable_ep(void)
{
__disable_ep(dbgp.i_ep);
__disable_ep(dbgp.o_ep);
}
static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
{
int stp;
int err = 0;
int status = req->status;
if (ep == dbgp.i_ep) {
stp = 1;
goto fail;
}
if (status != 0) {
stp = 2;
goto release_req;
}
dbgp_consume(req->buf, req->actual);
req->length = DBGP_REQ_LEN;
err = usb_ep_queue(ep, req, GFP_ATOMIC);
if (err < 0) {
stp = 3;
goto release_req;
}
return;
release_req:
kfree(req->buf);
usb_ep_free_request(dbgp.o_ep, req);
dbgp_disable_ep();
fail:
dev_dbg(&dbgp.gadget->dev,
"complete: failure (%d:%d) ==> %d\n", stp, err, status);
}
static int dbgp_enable_ep_req(struct usb_ep *ep)
{
int err, stp;
struct usb_request *req;
req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!req) {
err = -ENOMEM;
stp = 1;
goto fail_1;
}
req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
if (!req->buf) {
err = -ENOMEM;
stp = 2;
goto fail_2;
}
req->complete = dbgp_complete;
req->length = DBGP_REQ_LEN;
err = usb_ep_queue(ep, req, GFP_ATOMIC);
if (err < 0) {
stp = 3;
goto fail_3;
}
return 0;
fail_3:
kfree(req->buf);
fail_2:
usb_ep_free_request(dbgp.o_ep, req);
fail_1:
dev_dbg(&dbgp.gadget->dev,
"enable ep req: failure (%d:%d)\n", stp, err);
return err;
}
static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
{
int err;
ep->desc = desc;
err = usb_ep_enable(ep);
ep->driver_data = dbgp.gadget;
return err;
}
static int dbgp_enable_ep(void)
{
int err, stp;
err = __enable_ep(dbgp.i_ep, &i_desc);
if (err < 0) {
stp = 1;
goto fail_1;
}
err = __enable_ep(dbgp.o_ep, &o_desc);
if (err < 0) {
stp = 2;
goto fail_2;
}
err = dbgp_enable_ep_req(dbgp.o_ep);
if (err < 0) {
stp = 3;
goto fail_3;
}
return 0;
fail_3:
__disable_ep(dbgp.o_ep);
fail_2:
__disable_ep(dbgp.i_ep);
fail_1:
dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
return err;
}
#endif
static void dbgp_disconnect(struct usb_gadget *gadget)
{
#ifdef CONFIG_USB_G_DBGP_PRINTK
dbgp_disable_ep();
#else
gserial_disconnect(dbgp.serial);
#endif
}
static void dbgp_unbind(struct usb_gadget *gadget)
{
#ifdef CONFIG_USB_G_DBGP_SERIAL
kfree(dbgp.serial);
dbgp.serial = NULL;
#endif
if (dbgp.req) {
kfree(dbgp.req->buf);
usb_ep_free_request(gadget->ep0, dbgp.req);
dbgp.req = NULL;
}
gadget->ep0->driver_data = NULL;
}
#ifdef CONFIG_USB_G_DBGP_SERIAL
static unsigned char tty_line;
#endif
static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
{
int stp;
usb_ep_autoconfig_reset(gadget);
dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
if (!dbgp.i_ep) {
stp = 1;
goto fail_1;
}
dbgp.i_ep->driver_data = gadget;
i_desc.wMaxPacketSize =
__constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
if (!dbgp.o_ep) {
dbgp.i_ep->driver_data = NULL;
stp = 2;
goto fail_2;
}
dbgp.o_ep->driver_data = gadget;
o_desc.wMaxPacketSize =
__constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress;
dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress;
#ifdef CONFIG_USB_G_DBGP_SERIAL
dbgp.serial->in = dbgp.i_ep;
dbgp.serial->out = dbgp.o_ep;
dbgp.serial->in->desc = &i_desc;
dbgp.serial->out->desc = &o_desc;
if (gserial_alloc_line(&tty_line)) {
stp = 3;
goto fail_3;
}
return 0;
fail_3:
dbgp.o_ep->driver_data = NULL;
#else
return 0;
#endif
fail_2:
dbgp.i_ep->driver_data = NULL;
fail_1:
dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
return -ENODEV;
}
static int __init dbgp_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
int err, stp;
dbgp.gadget = gadget;
dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!dbgp.req) {
err = -ENOMEM;
stp = 1;
goto fail;
}
dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
if (!dbgp.req->buf) {
err = -ENOMEM;
stp = 2;
goto fail;
}
dbgp.req->length = DBGP_REQ_EP0_LEN;
gadget->ep0->driver_data = gadget;
#ifdef CONFIG_USB_G_DBGP_SERIAL
dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
if (!dbgp.serial) {
stp = 3;
err = -ENOMEM;
goto fail;
}
#endif
err = dbgp_configure_endpoints(gadget);
if (err < 0) {
stp = 4;
goto fail;
}
dev_dbg(&dbgp.gadget->dev, "bind: success\n");
return 0;
fail:
dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
dbgp_unbind(gadget);
return err;
}
static void dbgp_setup_complete(struct usb_ep *ep,
struct usb_request *req)
{
dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
req->status, req->actual, req->length);
}
static int dbgp_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl)
{
struct usb_request *req = dbgp.req;
u8 request = ctrl->bRequest;
u16 value = le16_to_cpu(ctrl->wValue);
u16 length = le16_to_cpu(ctrl->wLength);
int err = -EOPNOTSUPP;
void *data = NULL;
u16 len = 0;
gadget->ep0->driver_data = gadget;
if (request == USB_REQ_GET_DESCRIPTOR) {
switch (value>>8) {
case USB_DT_DEVICE:
dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
len = sizeof device_desc;
data = &device_desc;
device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
break;
case USB_DT_DEBUG:
dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
len = sizeof dbg_desc;
data = &dbg_desc;
break;
default:
goto fail;
}
err = 0;
} else if (request == USB_REQ_SET_FEATURE &&
value == USB_DEVICE_DEBUG_MODE) {
dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
#ifdef CONFIG_USB_G_DBGP_PRINTK
err = dbgp_enable_ep();
#else
err = gserial_connect(dbgp.serial, tty_line);
#endif
if (err < 0)
goto fail;
} else
goto fail;
req->length = min(length, len);
req->zero = len < req->length;
if (data && req->length)
memcpy(req->buf, data, req->length);
req->complete = dbgp_setup_complete;
return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
fail:
dev_dbg(&dbgp.gadget->dev,
"setup: failure req %x v %x\n", request, value);
return err;
}
static __refdata struct usb_gadget_driver dbgp_driver = {
.function = "dbgp",
.max_speed = USB_SPEED_HIGH,
.bind = dbgp_bind,
.unbind = dbgp_unbind,
.setup = dbgp_setup,
.reset = dbgp_disconnect,
.disconnect = dbgp_disconnect,
.driver = {
.owner = THIS_MODULE,
.name = "dbgp"
},
};
static int __init dbgp_init(void)
{
return usb_gadget_probe_driver(&dbgp_driver);
}
static void __exit dbgp_exit(void)
{
usb_gadget_unregister_driver(&dbgp_driver);
#ifdef CONFIG_USB_G_DBGP_SERIAL
gserial_free_line(tty_line);
#endif
}
MODULE_AUTHOR("Stephane Duverger");
MODULE_LICENSE("GPL");
module_init(dbgp_init);
module_exit(dbgp_exit);

View file

@ -0,0 +1,482 @@
/*
* ether.c -- Ethernet gadget driver, with CDC and non-CDC options
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/netdevice.h>
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
#ifdef CONFIG_USB_ETH_RNDIS
# define USB_ETH_RNDIS y
#endif
#include "u_ether.h"
/*
* Ethernet gadget driver -- with CDC and non-CDC options
* Builds on hardware support for a full duplex link.
*
* CDC Ethernet is the standard USB solution for sending Ethernet frames
* using USB. Real hardware tends to use the same framing protocol but look
* different for control features. This driver strongly prefers to use
* this USB-IF standard as its open-systems interoperability solution;
* most host side USB stacks (except from Microsoft) support it.
*
* This is sometimes called "CDC ECM" (Ethernet Control Model) to support
* TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new
* "CDC EEM" (Ethernet Emulation Model) is starting to spread.
*
* There's some hardware that can't talk CDC ECM. We make that hardware
* implement a "minimalist" vendor-agnostic CDC core: same framing, but
* link-level setup only requires activating the configuration. Only the
* endpoint descriptors, and product/vendor IDs, are relevant; no control
* operations are available. Linux supports it, but other host operating
* systems may not. (This is a subset of CDC Ethernet.)
*
* It turns out that if you add a few descriptors to that "CDC Subset",
* (Windows) host side drivers from MCCI can treat it as one submode of
* a proprietary scheme called "SAFE" ... without needing to know about
* specific product/vendor IDs. So we do that, making it easier to use
* those MS-Windows drivers. Those added descriptors make it resemble a
* CDC MDLM device, but they don't change device behavior at all. (See
* MCCI Engineering report 950198 "SAFE Networking Functions".)
*
* A third option is also in use. Rather than CDC Ethernet, or something
* simpler, Microsoft pushes their own approach: RNDIS. The published
* RNDIS specs are ambiguous and appear to be incomplete, and are also
* needlessly complex. They borrow more from CDC ACM than CDC ECM.
*/
#define DRIVER_DESC "Ethernet Gadget"
#define DRIVER_VERSION "Memorial Day 2008"
#ifdef USB_ETH_RNDIS
#define PREFIX "RNDIS/"
#else
#define PREFIX ""
#endif
/*
* This driver aims for interoperability by using CDC ECM unless
*
* can_support_ecm()
*
* returns false, in which case it supports the CDC Subset. By default,
* that returns true; most hardware has no problems with CDC ECM, that's
* a good default. Previous versions of this driver had no default; this
* version changes that, removing overhead for new controller support.
*
* IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
*/
static inline bool has_rndis(void)
{
#ifdef USB_ETH_RNDIS
return true;
#else
return false;
#endif
}
#include <linux/module.h>
#include "u_ecm.h"
#include "u_gether.h"
#ifdef USB_ETH_RNDIS
#include "u_rndis.h"
#include "rndis.h"
#else
#define rndis_borrow_net(...) do {} while (0)
#endif
#include "u_eem.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
USB_ETHERNET_MODULE_PARAMETERS();
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to NetChip Technologies for donating this product ID.
* It's for devices with only CDC Ethernet configurations.
*/
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
/* For hardware that can't talk CDC, we use the same vendor ID that
* ARM Linux has used for ethernet-over-usb, both with sa1100 and
* with pxa250. We're protocol-compatible, if the host-side drivers
* use the endpoint descriptors. bcdDevice (version) is nonzero, so
* drivers that need to hard-wire endpoint numbers have a hook.
*
* The protocol is a minimal subset of CDC Ether, which works on any bulk
* hardware that's not deeply broken ... even on hardware that can't talk
* RNDIS (like SA-1100, with no interrupt endpoint, or anything that
* doesn't handle control-OUT).
*/
#define SIMPLE_VENDOR_NUM 0x049f
#define SIMPLE_PRODUCT_NUM 0x505a
/* For hardware that can talk RNDIS and either of the above protocols,
* use this ID ... the windows INF files will know it. Unless it's
* used with CDC Ethernet, Linux 2.4 hosts will need updates to choose
* the non-RNDIS configuration.
*/
#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
/* For EEM gadgets */
#define EEM_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define EEM_PRODUCT_NUM 0x0102 /* EEM Gadget */
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16 (0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id defaults change according to what configs
* we support. (As does bNumConfigurations.) These values can
* also be overridden by module parameters.
*/
.idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
.idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static struct usb_function_instance *fi_ecm;
static struct usb_function *f_ecm;
static struct usb_function_instance *fi_eem;
static struct usb_function *f_eem;
static struct usb_function_instance *fi_geth;
static struct usb_function *f_geth;
static struct usb_function_instance *fi_rndis;
static struct usb_function *f_rndis;
/*-------------------------------------------------------------------------*/
/*
* We may not have an RNDIS configuration, but if we do it needs to be
* the first one present. That's to make Microsoft's drivers happy,
* and to follow DOCSIS 1.0 (cable modem standard).
*/
static int __init rndis_do_config(struct usb_configuration *c)
{
int status;
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_rndis = usb_get_function(fi_rndis);
if (IS_ERR(f_rndis))
return PTR_ERR(f_rndis);
status = usb_add_function(c, f_rndis);
if (status < 0)
usb_put_function(f_rndis);
return status;
}
static struct usb_configuration rndis_config_driver = {
.label = "RNDIS",
.bConfigurationValue = 2,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_USB_ETH_EEM
static bool use_eem = 1;
#else
static bool use_eem;
#endif
module_param(use_eem, bool, 0);
MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
/*
* We _always_ have an ECM, CDC Subset, or EEM configuration.
*/
static int __init eth_do_config(struct usb_configuration *c)
{
int status = 0;
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
if (use_eem) {
f_eem = usb_get_function(fi_eem);
if (IS_ERR(f_eem))
return PTR_ERR(f_eem);
status = usb_add_function(c, f_eem);
if (status < 0)
usb_put_function(f_eem);
return status;
} else if (can_support_ecm(c->cdev->gadget)) {
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm))
return PTR_ERR(f_ecm);
status = usb_add_function(c, f_ecm);
if (status < 0)
usb_put_function(f_ecm);
return status;
} else {
f_geth = usb_get_function(fi_geth);
if (IS_ERR(f_geth))
return PTR_ERR(f_geth);
status = usb_add_function(c, f_geth);
if (status < 0)
usb_put_function(f_geth);
return status;
}
}
static struct usb_configuration eth_config_driver = {
/* .label = f(hardware) */
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int __init eth_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_eem_opts *eem_opts = NULL;
struct f_ecm_opts *ecm_opts = NULL;
struct f_gether_opts *geth_opts = NULL;
struct net_device *net;
int status;
/* set up main config label and device descriptor */
if (use_eem) {
/* EEM */
fi_eem = usb_get_function_instance("eem");
if (IS_ERR(fi_eem))
return PTR_ERR(fi_eem);
eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
net = eem_opts->net;
eth_config_driver.label = "CDC Ethernet (EEM)";
device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
} else if (can_support_ecm(gadget)) {
/* ECM */
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm))
return PTR_ERR(fi_ecm);
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
net = ecm_opts->net;
eth_config_driver.label = "CDC Ethernet (ECM)";
} else {
/* CDC Subset */
fi_geth = usb_get_function_instance("geth");
if (IS_ERR(fi_geth))
return PTR_ERR(fi_geth);
geth_opts = container_of(fi_geth, struct f_gether_opts,
func_inst);
net = geth_opts->net;
eth_config_driver.label = "CDC Subset/SAFE";
device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM);
if (!has_rndis())
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
}
gether_set_qmult(net, qmult);
if (!gether_set_host_addr(net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
if (has_rndis()) {
/* RNDIS plus ECM-or-Subset */
gether_set_gadget(net, cdev->gadget);
status = gether_register_netdev(net);
if (status)
goto fail;
if (use_eem)
eem_opts->bound = true;
else if (can_support_ecm(gadget))
ecm_opts->bound = true;
else
geth_opts->bound = true;
fi_rndis = usb_get_function_instance("rndis");
if (IS_ERR(fi_rndis)) {
status = PTR_ERR(fi_rndis);
goto fail;
}
rndis_borrow_net(fi_rndis, net);
device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
device_desc.bNumConfigurations = 2;
}
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration(s); RNDIS first, if it's used */
if (has_rndis()) {
status = usb_add_config(cdev, &rndis_config_driver,
rndis_do_config);
if (status < 0)
goto fail1;
}
status = usb_add_config(cdev, &eth_config_driver, eth_do_config);
if (status < 0)
goto fail1;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
return 0;
fail1:
if (has_rndis())
usb_put_function_instance(fi_rndis);
fail:
if (use_eem)
usb_put_function_instance(fi_eem);
else if (can_support_ecm(gadget))
usb_put_function_instance(fi_ecm);
else
usb_put_function_instance(fi_geth);
return status;
}
static int __exit eth_unbind(struct usb_composite_dev *cdev)
{
if (has_rndis()) {
usb_put_function(f_rndis);
usb_put_function_instance(fi_rndis);
}
if (use_eem) {
usb_put_function(f_eem);
usb_put_function_instance(fi_eem);
} else if (can_support_ecm(cdev->gadget)) {
usb_put_function(f_ecm);
usb_put_function_instance(fi_ecm);
} else {
usb_put_function(f_geth);
usb_put_function_instance(fi_geth);
}
return 0;
}
static __refdata struct usb_composite_driver eth_driver = {
.name = "g_ether",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.bind = eth_bind,
.unbind = __exit_p(eth_unbind),
};
module_usb_composite_driver(eth_driver);
MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
MODULE_AUTHOR("David Brownell, Benedikt Spanger");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,582 @@
/*
* g_ffs.c -- user mode file system API for USB composite function controllers
*
* Copyright (C) 2010 Samsung Electronics
* Author: Michal Nazarewicz <mina86@mina86.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, or
* (at your option) any later version.
*/
#define pr_fmt(fmt) "g_ffs: " fmt
#include <linux/module.h>
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
#include <linux/netdevice.h>
# if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
# endif
# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
# define USB_ETH_RNDIS y
# endif
# include "u_ecm.h"
# include "u_gether.h"
# ifdef USB_ETH_RNDIS
# include "u_rndis.h"
# include "rndis.h"
# endif
# include "u_ether.h"
USB_ETHERNET_MODULE_PARAMETERS();
# ifdef CONFIG_USB_FUNCTIONFS_ETH
static int eth_bind_config(struct usb_configuration *c);
static struct usb_function_instance *fi_ecm;
static struct usb_function *f_ecm;
static struct usb_function_instance *fi_geth;
static struct usb_function *f_geth;
# endif
# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
static int bind_rndis_config(struct usb_configuration *c);
static struct usb_function_instance *fi_rndis;
static struct usb_function *f_rndis;
# endif
#endif
#include "u_fs.h"
#define DRIVER_NAME "g_ffs"
#define DRIVER_DESC "USB Function Filesystem"
#define DRIVER_VERSION "24 Aug 2004"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
#define GFS_VENDOR_ID 0x1d6b /* Linux Foundation */
#define GFS_PRODUCT_ID 0x0105 /* FunctionFS Gadget */
#define GFS_MAX_DEVS 10
USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(GFS_VENDOR_ID),
.idProduct = cpu_to_le16(GFS_PRODUCT_ID),
};
static char *func_names[GFS_MAX_DEVS];
static unsigned int func_num;
module_param_named(bDeviceClass, gfs_dev_desc.bDeviceClass, byte, 0644);
MODULE_PARM_DESC(bDeviceClass, "USB Device class");
module_param_named(bDeviceSubClass, gfs_dev_desc.bDeviceSubClass, byte, 0644);
MODULE_PARM_DESC(bDeviceSubClass, "USB Device subclass");
module_param_named(bDeviceProtocol, gfs_dev_desc.bDeviceProtocol, byte, 0644);
MODULE_PARM_DESC(bDeviceProtocol, "USB Device protocol");
module_param_array_named(functions, func_names, charp, &func_num, 0);
MODULE_PARM_DESC(functions, "USB Functions list");
static const struct usb_descriptor_header *gfs_otg_desc[] = {
(const struct usb_descriptor_header *)
&(const struct usb_otg_descriptor) {
.bLength = sizeof(struct usb_otg_descriptor),
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
},
NULL
};
/* String IDs are assigned dynamically */
static struct usb_string gfs_strings[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{ .s = "FunctionFS + RNDIS" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
{ .s = "FunctionFS + ECM" },
#endif
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
{ .s = "FunctionFS" },
#endif
{ } /* end of list */
};
static struct usb_gadget_strings *gfs_dev_strings[] = {
&(struct usb_gadget_strings) {
.language = 0x0409, /* en-us */
.strings = gfs_strings,
},
NULL,
};
struct gfs_configuration {
struct usb_configuration c;
int (*eth)(struct usb_configuration *c);
int num;
} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
.eth = bind_rndis_config,
},
#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
{
.eth = eth_bind_config,
},
#endif
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
{
},
#endif
};
static void *functionfs_acquire_dev(struct ffs_dev *dev);
static void functionfs_release_dev(struct ffs_dev *dev);
static int functionfs_ready_callback(struct ffs_data *ffs);
static void functionfs_closed_callback(struct ffs_data *ffs);
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
static __refdata struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
.strings = gfs_dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = gfs_bind,
.unbind = gfs_unbind,
};
static unsigned int missing_funcs;
static bool gfs_registered;
static bool gfs_single_func;
static struct usb_function_instance **fi_ffs;
static struct usb_function **f_ffs[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
NULL,
#endif
#ifdef CONFIG_USB_FUNCTIONFS_ETH
NULL,
#endif
#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
NULL,
#endif
};
#define N_CONF ARRAY_SIZE(f_ffs)
static int __init gfs_init(void)
{
struct f_fs_opts *opts;
int i;
int ret = 0;
ENTER();
if (func_num < 2) {
gfs_single_func = true;
func_num = 1;
}
/*
* Allocate in one chunk for easier maintenance
*/
f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
if (!f_ffs[0]) {
ret = -ENOMEM;
goto no_func;
}
for (i = 1; i < N_CONF; ++i)
f_ffs[i] = f_ffs[0] + i * func_num;
fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
if (!fi_ffs) {
ret = -ENOMEM;
goto no_func;
}
for (i = 0; i < func_num; i++) {
fi_ffs[i] = usb_get_function_instance("ffs");
if (IS_ERR(fi_ffs[i])) {
ret = PTR_ERR(fi_ffs[i]);
--i;
goto no_dev;
}
opts = to_f_fs_opts(fi_ffs[i]);
if (gfs_single_func)
ret = ffs_single_dev(opts->dev);
else
ret = ffs_name_dev(opts->dev, func_names[i]);
if (ret)
goto no_dev;
opts->dev->ffs_ready_callback = functionfs_ready_callback;
opts->dev->ffs_closed_callback = functionfs_closed_callback;
opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev;
opts->dev->ffs_release_dev_callback = functionfs_release_dev;
opts->no_configfs = true;
}
missing_funcs = func_num;
return 0;
no_dev:
while (i >= 0)
usb_put_function_instance(fi_ffs[i--]);
kfree(fi_ffs);
no_func:
kfree(f_ffs[0]);
return ret;
}
module_init(gfs_init);
static void __exit gfs_exit(void)
{
int i;
ENTER();
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
kfree(f_ffs[0]);
for (i = 0; i < func_num; i++)
usb_put_function_instance(fi_ffs[i]);
kfree(fi_ffs);
}
module_exit(gfs_exit);
static void *functionfs_acquire_dev(struct ffs_dev *dev)
{
if (!try_module_get(THIS_MODULE))
return ERR_PTR(-ENOENT);
return 0;
}
static void functionfs_release_dev(struct ffs_dev *dev)
{
module_put(THIS_MODULE);
}
/*
* The caller of this function takes ffs_lock
*/
static int functionfs_ready_callback(struct ffs_data *ffs)
{
int ret = 0;
if (--missing_funcs)
return 0;
if (gfs_registered)
return -EBUSY;
gfs_registered = true;
ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
gfs_registered = false;
return ret;
}
/*
* The caller of this function takes ffs_lock
*/
static void functionfs_closed_callback(struct ffs_data *ffs)
{
missing_funcs++;
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
}
/*
* It is assumed that gfs_bind is called from a context where ffs_lock is held
*/
static int gfs_bind(struct usb_composite_dev *cdev)
{
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
struct net_device *net;
#endif
int ret, i;
ENTER();
if (missing_funcs)
return -ENODEV;
#if defined CONFIG_USB_FUNCTIONFS_ETH
if (can_support_ecm(cdev->gadget)) {
struct f_ecm_opts *ecm_opts;
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm))
return PTR_ERR(fi_ecm);
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
net = ecm_opts->net;
} else {
struct f_gether_opts *geth_opts;
fi_geth = usb_get_function_instance("geth");
if (IS_ERR(fi_geth))
return PTR_ERR(fi_geth);
geth_opts = container_of(fi_geth, struct f_gether_opts,
func_inst);
net = geth_opts->net;
}
#endif
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
struct f_rndis_opts *rndis_opts;
fi_rndis = usb_get_function_instance("rndis");
if (IS_ERR(fi_rndis)) {
ret = PTR_ERR(fi_rndis);
goto error;
}
rndis_opts = container_of(fi_rndis, struct f_rndis_opts,
func_inst);
#ifndef CONFIG_USB_FUNCTIONFS_ETH
net = rndis_opts->net;
#endif
}
#endif
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
gether_set_qmult(net, qmult);
if (!gether_set_host_addr(net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
#endif
#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH
gether_set_gadget(net, cdev->gadget);
ret = gether_register_netdev(net);
if (ret)
goto error_rndis;
if (can_support_ecm(cdev->gadget)) {
struct f_ecm_opts *ecm_opts;
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
ecm_opts->bound = true;
} else {
struct f_gether_opts *geth_opts;
geth_opts = container_of(fi_geth, struct f_gether_opts,
func_inst);
geth_opts->bound = true;
}
rndis_borrow_net(fi_rndis, net);
#endif
/* TODO: gstrings_attach? */
ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
goto error_rndis;
gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
struct gfs_configuration *c = gfs_configurations + i;
int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
c->c.label = gfs_strings[sid].s;
c->c.iConfiguration = gfs_strings[sid].id;
c->c.bConfigurationValue = 1 + i;
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
c->num = i;
ret = usb_add_config(cdev, &c->c, gfs_do_config);
if (unlikely(ret < 0))
goto error_unbind;
}
usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
/* TODO */
error_unbind:
error_rndis:
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
usb_put_function_instance(fi_rndis);
error:
#endif
#if defined CONFIG_USB_FUNCTIONFS_ETH
if (can_support_ecm(cdev->gadget))
usb_put_function_instance(fi_ecm);
else
usb_put_function_instance(fi_geth);
#endif
return ret;
}
/*
* It is assumed that gfs_unbind is called from a context where ffs_lock is held
*/
static int gfs_unbind(struct usb_composite_dev *cdev)
{
int i;
ENTER();
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
usb_put_function(f_rndis);
usb_put_function_instance(fi_rndis);
#endif
#if defined CONFIG_USB_FUNCTIONFS_ETH
if (can_support_ecm(cdev->gadget)) {
usb_put_function(f_ecm);
usb_put_function_instance(fi_ecm);
} else {
usb_put_function(f_geth);
usb_put_function_instance(fi_geth);
}
#endif
for (i = 0; i < N_CONF * func_num; ++i)
usb_put_function(*(f_ffs[0] + i));
return 0;
}
/*
* It is assumed that gfs_do_config is called from a context where
* ffs_lock is held
*/
static int gfs_do_config(struct usb_configuration *c)
{
struct gfs_configuration *gc =
container_of(c, struct gfs_configuration, c);
int i;
int ret;
if (missing_funcs)
return -ENODEV;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = gfs_otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
if (gc->eth) {
ret = gc->eth(c);
if (unlikely(ret < 0))
return ret;
}
for (i = 0; i < func_num; i++) {
f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]);
if (IS_ERR(f_ffs[gc->num][i])) {
ret = PTR_ERR(f_ffs[gc->num][i]);
goto error;
}
ret = usb_add_function(c, f_ffs[gc->num][i]);
if (ret < 0) {
usb_put_function(f_ffs[gc->num][i]);
goto error;
}
}
/*
* After previous do_configs there may be some invalid
* pointers in c->interface array. This happens every time
* a user space function with fewer interfaces than a user
* space function that was run before the new one is run. The
* compasit's set_config() assumes that if there is no more
* then MAX_CONFIG_INTERFACES interfaces in a configuration
* then there is a NULL pointer after the last interface in
* c->interface array. We need to make sure this is true.
*/
if (c->next_interface_id < ARRAY_SIZE(c->interface))
c->interface[c->next_interface_id] = NULL;
return 0;
error:
while (--i >= 0) {
if (!IS_ERR(f_ffs[gc->num][i]))
usb_remove_function(c, f_ffs[gc->num][i]);
usb_put_function(f_ffs[gc->num][i]);
}
return ret;
}
#ifdef CONFIG_USB_FUNCTIONFS_ETH
static int eth_bind_config(struct usb_configuration *c)
{
int status = 0;
if (can_support_ecm(c->cdev->gadget)) {
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm))
return PTR_ERR(f_ecm);
status = usb_add_function(c, f_ecm);
if (status < 0)
usb_put_function(f_ecm);
} else {
f_geth = usb_get_function(fi_geth);
if (IS_ERR(f_geth))
return PTR_ERR(f_geth);
status = usb_add_function(c, f_geth);
if (status < 0)
usb_put_function(f_geth);
}
return status;
}
#endif
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
static int bind_rndis_config(struct usb_configuration *c)
{
int status = 0;
f_rndis = usb_get_function(fi_rndis);
if (IS_ERR(f_rndis))
return PTR_ERR(f_rndis);
status = usb_add_function(c, f_rndis);
if (status < 0)
usb_put_function(f_rndis);
return status;
}
#endif

View file

@ -0,0 +1,166 @@
/*
* gmidi.c -- USB MIDI Gadget Driver
*
* Copyright (C) 2006 Thumtronics Pty Ltd.
* Developed for Thumtronics by Grey Innovation
* Ben Williamson <ben.williamson@greyinnovation.com>
*
* This software is distributed under the terms of the GNU General Public
* License ("GPL") version 2, as published by the Free Software Foundation.
*
* This code is based in part on:
*
* Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
* USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
* USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
*
* Refer to the USB Device Class Definition for MIDI Devices:
* http://www.usb.org/developers/devclass_docs/midi10.pdf
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include "gadget_chips.h"
#include "f_midi.c"
/*-------------------------------------------------------------------------*/
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
USB_GADGET_COMPOSITE_OPTIONS();
static int index = SNDRV_DEFAULT_IDX1;
module_param(index, int, S_IRUGO);
MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
static char *id = SNDRV_DEFAULT_STR1;
module_param(id, charp, S_IRUGO);
MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
static unsigned int buflen = 256;
module_param(buflen, uint, S_IRUGO);
MODULE_PARM_DESC(buflen, "MIDI buffer length");
static unsigned int qlen = 32;
module_param(qlen, uint, S_IRUGO);
MODULE_PARM_DESC(qlen, "USB read request queue length");
static unsigned int in_ports = 1;
module_param(in_ports, uint, S_IRUGO);
MODULE_PARM_DESC(in_ports, "Number of MIDI input ports");
static unsigned int out_ports = 1;
module_param(out_ports, uint, S_IRUGO);
MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
/* Thanks to Grey Innovation for donating this product ID.
*
* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */
#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */
/* string IDs are assigned dynamically */
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
};
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation",
[USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget",
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = "MIDI",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static int __exit midi_unbind(struct usb_composite_dev *dev)
{
return 0;
}
static struct usb_configuration midi_config = {
.label = "MIDI Gadget",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE,
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
static int __init midi_bind_config(struct usb_configuration *c)
{
return f_midi_bind_config(c, index, id,
in_ports, out_ports,
buflen, qlen);
}
static int __init midi_bind(struct usb_composite_dev *cdev)
{
int status;
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
return status;
usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
}
static __refdata struct usb_composite_driver midi_driver = {
.name = (char *) longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = midi_bind,
.unbind = __exit_p(midi_unbind),
};
module_usb_composite_driver(midi_driver);

View file

@ -0,0 +1,266 @@
/*
* hid.c -- HID Composite driver
*
* Based on multi.c
*
* Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
#include "gadget_chips.h"
#define DRIVER_DESC "HID Gadget"
#define DRIVER_VERSION "2010/03/16"
/*-------------------------------------------------------------------------*/
#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
#define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */
/*-------------------------------------------------------------------------*/
/*
* 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 "f_hid.c"
struct hidg_func_node {
struct list_head node;
struct hidg_func_descriptor *func;
};
static LIST_HEAD(hidg_func_list);
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
/* .bDeviceClass = USB_CLASS_COMM, */
/* .bDeviceSubClass = 0, */
/* .bDeviceProtocol = 0, */
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(HIDG_VENDOR_NUM),
.idProduct = cpu_to_le16(HIDG_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
/****************************** Configurations ******************************/
static int __init do_config(struct usb_configuration *c)
{
struct hidg_func_node *e;
int func = 0, status = 0;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
list_for_each_entry(e, &hidg_func_list, node) {
status = hidg_bind_config(c, e->func, func++);
if (status)
break;
}
return status;
}
static struct usb_configuration config_driver = {
.label = "HID Gadget",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/****************************** Gadget Bind ******************************/
static int __init hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
int status, funcs = 0;
list_for_each(tmp, &hidg_func_list)
funcs++;
if (!funcs)
return -ENODEV;
/* set up HID */
status = ghid_setup(cdev->gadget, funcs);
if (status < 0)
return status;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &config_driver, do_config);
if (status < 0)
return status;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
return 0;
}
static int __exit hid_unbind(struct usb_composite_dev *cdev)
{
ghid_cleanup();
return 0;
}
static int __init hidg_plat_driver_probe(struct platform_device *pdev)
{
struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev);
struct hidg_func_node *entry;
if (!func) {
dev_err(&pdev->dev, "Platform data missing\n");
return -ENODEV;
}
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->func = func;
list_add_tail(&entry->node, &hidg_func_list);
return 0;
}
static int hidg_plat_driver_remove(struct platform_device *pdev)
{
struct hidg_func_node *e, *n;
list_for_each_entry_safe(e, n, &hidg_func_list, node) {
list_del(&e->node);
kfree(e);
}
return 0;
}
/****************************** Some noise ******************************/
static __refdata struct usb_composite_driver hidg_driver = {
.name = "g_hid",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = hid_bind,
.unbind = __exit_p(hid_unbind),
};
static struct platform_driver hidg_plat_driver = {
.remove = hidg_plat_driver_remove,
.driver = {
.owner = THIS_MODULE,
.name = "hidg",
},
};
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard");
MODULE_LICENSE("GPL");
static int __init hidg_init(void)
{
int status;
status = platform_driver_probe(&hidg_plat_driver,
hidg_plat_driver_probe);
if (status < 0)
return status;
status = usb_composite_probe(&hidg_driver);
if (status < 0)
platform_driver_unregister(&hidg_plat_driver);
return status;
}
module_init(hidg_init);
static void __exit hidg_cleanup(void)
{
platform_driver_unregister(&hidg_plat_driver);
usb_composite_unregister(&hidg_driver);
}
module_exit(hidg_cleanup);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,276 @@
/*
* mass_storage.c -- Mass Storage USB Gadget
*
* Copyright (C) 2003-2008 Alan Stern
* Copyright (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz <mina86@mina86.com>
* All rights reserved.
*
* 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, or
* (at your option) any later version.
*/
/*
* The Mass Storage Gadget acts as a USB Mass Storage device,
* appearing to the host as a disk drive or as a CD-ROM drive. In
* addition to providing an example of a genuinely useful gadget
* driver for a USB device, it also illustrates a technique of
* double-buffering for increased throughput. Last but not least, it
* gives an easy way to probe the behavior of the Mass Storage drivers
* in a USB host.
*
* Since this file serves only administrative purposes and all the
* business logic is implemented in f_mass_storage.* file. Read
* comments in this file for more detailed description.
*/
#include <linux/kernel.h>
#include <linux/usb/ch9.h>
#include <linux/module.h>
/*-------------------------------------------------------------------------*/
#define DRIVER_DESC "Mass Storage Gadget"
#define DRIVER_VERSION "2009/09/11"
/*
* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#define FSG_VENDOR_ID 0x0525 /* NetChip */
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor msg_device_desc = {
.bLength = sizeof msg_device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_PER_INTERFACE,
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(FSG_VENDOR_ID),
.idProduct = cpu_to_le16(FSG_PRODUCT_ID),
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static struct usb_function_instance *fi_msg;
static struct usb_function *f_msg;
/****************************** Configurations ******************************/
static struct fsg_module_parameters mod_data = {
.stall = 1
};
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
static unsigned long msg_registered;
static void msg_cleanup(void);
static int msg_thread_exits(struct fsg_common *common)
{
msg_cleanup();
return 0;
}
static int __init msg_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
opts = fsg_opts_from_func_inst(fi_msg);
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg))
return PTR_ERR(f_msg);
ret = fsg_common_run_thread(opts->common);
if (ret)
goto put_func;
ret = usb_add_function(c, f_msg);
if (ret)
goto put_func;
return 0;
put_func:
usb_put_function(f_msg);
return ret;
}
static struct usb_configuration msg_config_driver = {
.label = "Linux File-Backed Storage",
.bConfigurationValue = 1,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/****************************** Gadget Bind ******************************/
static int __init msg_bind(struct usb_composite_dev *cdev)
{
static const struct fsg_operations ops = {
.thread_exits = msg_thread_exits,
};
struct fsg_opts *opts;
struct fsg_config config;
int status;
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg))
return PTR_ERR(fi_msg);
fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
opts = fsg_opts_from_func_inst(fi_msg);
opts->no_configfs = true;
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
if (status)
goto fail;
status = fsg_common_set_nluns(opts->common, config.nluns);
if (status)
goto fail_set_nluns;
fsg_common_set_ops(opts->common, &ops);
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(opts->common, true);
status = fsg_common_create_luns(opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
config.product_name);
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail_string_ids;
msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
if (status < 0)
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
set_bit(0, &msg_registered);
return 0;
fail_string_ids:
fsg_common_remove_luns(opts->common);
fail_set_cdev:
fsg_common_free_luns(opts->common);
fail_set_nluns:
fsg_common_free_buffers(opts->common);
fail:
usb_put_function_instance(fi_msg);
return status;
}
static int msg_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR(f_msg))
usb_put_function(f_msg);
if (!IS_ERR(fi_msg))
usb_put_function_instance(fi_msg);
return 0;
}
/****************************** Some noise ******************************/
static __refdata struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
.max_speed = USB_SPEED_SUPER,
.needs_serial = 1,
.strings = dev_strings,
.bind = msg_bind,
.unbind = msg_unbind,
};
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
static int __init msg_init(void)
{
return usb_composite_probe(&msg_driver);
}
module_init(msg_init);
static void msg_cleanup(void)
{
if (test_and_clear_bit(0, &msg_registered))
usb_composite_unregister(&msg_driver);
}
module_exit(msg_cleanup);

View file

@ -0,0 +1,510 @@
/*
* multi.c -- Multifunction Composite driver
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
* Copyright (C) 2009 Samsung Electronics
* Author: Michal Nazarewicz (mina86@mina86.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include "u_serial.h"
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
#ifdef CONFIG_USB_G_MULTI_RNDIS
# define USB_ETH_RNDIS y
#endif
#define DRIVER_DESC "Multifunction Composite Gadget"
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
#include "f_mass_storage.h"
#include "u_ecm.h"
#ifdef USB_ETH_RNDIS
# include "u_rndis.h"
# include "rndis.h"
#endif
#include "u_ether.h"
USB_GADGET_COMPOSITE_OPTIONS();
USB_ETHERNET_MODULE_PARAMETERS();
/***************************** Device Descriptor ****************************/
#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define MULTI_PRODUCT_NUM 0x0104 /* Multifunction Composite Gadget */
enum {
__MULTI_NO_CONFIG,
#ifdef CONFIG_USB_G_MULTI_RNDIS
MULTI_RNDIS_CONFIG_NUM,
#endif
#ifdef CONFIG_USB_G_MULTI_CDC
MULTI_CDC_CONFIG_NUM,
#endif
};
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_MISC /* 0xEF */,
.bDeviceSubClass = 2,
.bDeviceProtocol = 1,
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(MULTI_VENDOR_NUM),
.idProduct = cpu_to_le16(MULTI_PRODUCT_NUM),
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &(struct usb_otg_descriptor){
.bLength = sizeof(struct usb_otg_descriptor),
.bDescriptorType = USB_DT_OTG,
/*
* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
},
NULL,
};
enum {
MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
MULTI_STRING_CDC_CONFIG_IDX,
};
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
[MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
[MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
{ } /* end of list */
};
static struct usb_gadget_strings *dev_strings[] = {
&(struct usb_gadget_strings){
.language = 0x0409, /* en-us */
.strings = strings_dev,
},
NULL,
};
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
static struct usb_function_instance *fi_acm;
static struct usb_function_instance *fi_msg;
/********** RNDIS **********/
#ifdef USB_ETH_RNDIS
static struct usb_function_instance *fi_rndis;
static struct usb_function *f_acm_rndis;
static struct usb_function *f_rndis;
static struct usb_function *f_msg_rndis;
static __init int rndis_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_rndis = usb_get_function(fi_rndis);
if (IS_ERR(f_rndis))
return PTR_ERR(f_rndis);
ret = usb_add_function(c, f_rndis);
if (ret < 0)
goto err_func_rndis;
f_acm_rndis = usb_get_function(fi_acm);
if (IS_ERR(f_acm_rndis)) {
ret = PTR_ERR(f_acm_rndis);
goto err_func_acm;
}
ret = usb_add_function(c, f_acm_rndis);
if (ret)
goto err_conf;
f_msg_rndis = usb_get_function(fi_msg);
if (IS_ERR(f_msg_rndis)) {
ret = PTR_ERR(f_msg_rndis);
goto err_fsg;
}
fsg_opts = fsg_opts_from_func_inst(fi_msg);
ret = fsg_common_run_thread(fsg_opts->common);
if (ret)
goto err_run;
ret = usb_add_function(c, f_msg_rndis);
if (ret)
goto err_run;
return 0;
err_run:
usb_put_function(f_msg_rndis);
err_fsg:
usb_remove_function(c, f_acm_rndis);
err_conf:
usb_put_function(f_acm_rndis);
err_func_acm:
usb_remove_function(c, f_rndis);
err_func_rndis:
usb_put_function(f_rndis);
return ret;
}
static __ref int rndis_config_register(struct usb_composite_dev *cdev)
{
static struct usb_configuration config = {
.bConfigurationValue = MULTI_RNDIS_CONFIG_NUM,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
config.label = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].s;
config.iConfiguration = strings_dev[MULTI_STRING_RNDIS_CONFIG_IDX].id;
return usb_add_config(cdev, &config, rndis_do_config);
}
#else
static __ref int rndis_config_register(struct usb_composite_dev *cdev)
{
return 0;
}
#endif
/********** CDC ECM **********/
#ifdef CONFIG_USB_G_MULTI_CDC
static struct usb_function_instance *fi_ecm;
static struct usb_function *f_acm_multi;
static struct usb_function *f_ecm;
static struct usb_function *f_msg_multi;
static __init int cdc_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm))
return PTR_ERR(f_ecm);
ret = usb_add_function(c, f_ecm);
if (ret < 0)
goto err_func_ecm;
/* implicit port_num is zero */
f_acm_multi = usb_get_function(fi_acm);
if (IS_ERR(f_acm_multi)) {
ret = PTR_ERR(f_acm_multi);
goto err_func_acm;
}
ret = usb_add_function(c, f_acm_multi);
if (ret)
goto err_conf;
f_msg_multi = usb_get_function(fi_msg);
if (IS_ERR(f_msg_multi)) {
ret = PTR_ERR(f_msg_multi);
goto err_fsg;
}
fsg_opts = fsg_opts_from_func_inst(fi_msg);
ret = fsg_common_run_thread(fsg_opts->common);
if (ret)
goto err_run;
ret = usb_add_function(c, f_msg_multi);
if (ret)
goto err_run;
return 0;
err_run:
usb_put_function(f_msg_multi);
err_fsg:
usb_remove_function(c, f_acm_multi);
err_conf:
usb_put_function(f_acm_multi);
err_func_acm:
usb_remove_function(c, f_ecm);
err_func_ecm:
usb_put_function(f_ecm);
return ret;
}
static __ref int cdc_config_register(struct usb_composite_dev *cdev)
{
static struct usb_configuration config = {
.bConfigurationValue = MULTI_CDC_CONFIG_NUM,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
config.label = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].s;
config.iConfiguration = strings_dev[MULTI_STRING_CDC_CONFIG_IDX].id;
return usb_add_config(cdev, &config, cdc_do_config);
}
#else
static __ref int cdc_config_register(struct usb_composite_dev *cdev)
{
return 0;
}
#endif
/****************************** Gadget Bind ******************************/
static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
#ifdef CONFIG_USB_G_MULTI_CDC
struct f_ecm_opts *ecm_opts;
#endif
#ifdef USB_ETH_RNDIS
struct f_rndis_opts *rndis_opts;
#endif
struct fsg_opts *fsg_opts;
struct fsg_config config;
int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
gadget->name);
return -EINVAL;
}
#ifdef CONFIG_USB_G_MULTI_CDC
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm))
return PTR_ERR(fi_ecm);
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
gether_set_qmult(ecm_opts->net, qmult);
if (!gether_set_host_addr(ecm_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
#endif
#ifdef USB_ETH_RNDIS
fi_rndis = usb_get_function_instance("rndis");
if (IS_ERR(fi_rndis)) {
status = PTR_ERR(fi_rndis);
goto fail;
}
rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
gether_set_qmult(rndis_opts->net, qmult);
if (!gether_set_host_addr(rndis_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
#endif
#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
/*
* If both ecm and rndis are selected then:
* 1) rndis borrows the net interface from ecm
* 2) since the interface is shared it must not be bound
* twice - in ecm's _and_ rndis' binds, so do it here.
*/
gether_set_gadget(ecm_opts->net, cdev->gadget);
status = gether_register_netdev(ecm_opts->net);
if (status)
goto fail0;
rndis_borrow_net(fi_rndis, ecm_opts->net);
ecm_opts->bound = true;
#endif
/* set up serial link layer */
fi_acm = usb_get_function_instance("acm");
if (IS_ERR(fi_acm)) {
status = PTR_ERR(fi_acm);
goto fail0;
}
/* set up mass storage function */
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg)) {
status = PTR_ERR(fi_msg);
goto fail1;
}
fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
fsg_opts = fsg_opts_from_func_inst(fi_msg);
fsg_opts->no_configfs = true;
status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
if (status)
goto fail2;
status = fsg_common_set_nluns(fsg_opts->common, config.nluns);
if (status)
goto fail_set_nluns;
status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(fsg_opts->common, true);
status = fsg_common_create_luns(fsg_opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
config.product_name);
/* allocate string IDs */
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
goto fail_string_ids;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register configurations */
status = rndis_config_register(cdev);
if (unlikely(status < 0))
goto fail_string_ids;
status = cdc_config_register(cdev);
if (unlikely(status < 0))
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC "\n");
return 0;
/* error recovery */
fail_string_ids:
fsg_common_remove_luns(fsg_opts->common);
fail_set_cdev:
fsg_common_free_luns(fsg_opts->common);
fail_set_nluns:
fsg_common_free_buffers(fsg_opts->common);
fail2:
usb_put_function_instance(fi_msg);
fail1:
usb_put_function_instance(fi_acm);
fail0:
#ifdef USB_ETH_RNDIS
usb_put_function_instance(fi_rndis);
fail:
#endif
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function_instance(fi_ecm);
#endif
return status;
}
static int __exit multi_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_msg_multi);
#endif
#ifdef USB_ETH_RNDIS
usb_put_function(f_msg_rndis);
#endif
usb_put_function_instance(fi_msg);
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_acm_multi);
#endif
#ifdef USB_ETH_RNDIS
usb_put_function(f_acm_rndis);
#endif
usb_put_function_instance(fi_acm);
#ifdef USB_ETH_RNDIS
usb_put_function(f_rndis);
usb_put_function_instance(fi_rndis);
#endif
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_ecm);
usb_put_function_instance(fi_ecm);
#endif
return 0;
}
/****************************** Some noise ******************************/
static __refdata struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = multi_bind,
.unbind = __exit_p(multi_unbind),
.needs_serial = 1,
};
module_usb_composite_driver(multi_driver);

View file

@ -0,0 +1,211 @@
/*
* ncm.c -- NCM gadget driver
*
* Copyright (C) 2010 Nokia Corporation
* Contact: Yauheni Kaliuta <yauheni.kaliuta@nokia.com>
*
* The driver borrows from ether.c which is:
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
/* #define DEBUG */
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
#include "u_ether.h"
#include "u_ncm.h"
#define DRIVER_DESC "NCM Gadget"
/*-------------------------------------------------------------------------*/
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to NetChip Technologies for donating this product ID.
* It's for devices with only CDC Ethernet configurations.
*/
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
USB_ETHERNET_MODULE_PARAMETERS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16 (0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id defaults change according to what configs
* we support. (As does bNumConfigurations.) These values can
* also be overridden by module parameters.
*/
.idVendor = cpu_to_le16 (CDC_VENDOR_NUM),
.idProduct = cpu_to_le16 (CDC_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
[USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static struct usb_function_instance *f_ncm_inst;
static struct usb_function *f_ncm;
/*-------------------------------------------------------------------------*/
static int __init ncm_do_config(struct usb_configuration *c)
{
int status;
/* FIXME alloc iConfiguration string, set it in c->strings */
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_ncm = usb_get_function(f_ncm_inst);
if (IS_ERR(f_ncm)) {
status = PTR_ERR(f_ncm);
return status;
}
status = usb_add_function(c, f_ncm);
if (status < 0) {
usb_put_function(f_ncm);
return status;
}
return 0;
}
static struct usb_configuration ncm_config_driver = {
/* .label = f(hardware) */
.label = "CDC Ethernet (NCM)",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
/*-------------------------------------------------------------------------*/
static int __init gncm_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct f_ncm_opts *ncm_opts;
int status;
f_ncm_inst = usb_get_function_instance("ncm");
if (IS_ERR(f_ncm_inst))
return PTR_ERR(f_ncm_inst);
ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst);
gether_set_qmult(ncm_opts->net, qmult);
if (!gether_set_host_addr(ncm_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(ncm_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &ncm_config_driver,
ncm_do_config);
if (status < 0)
goto fail;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
return 0;
fail:
usb_put_function_instance(f_ncm_inst);
return status;
}
static int __exit gncm_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_ncm))
usb_put_function(f_ncm);
if (!IS_ERR_OR_NULL(f_ncm_inst))
usb_put_function_instance(f_ncm_inst);
return 0;
}
static __refdata struct usb_composite_driver ncm_driver = {
.name = "g_ncm",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = gncm_bind,
.unbind = __exit_p(gncm_unbind),
};
module_usb_composite_driver(ncm_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Yauheni Kaliuta");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,350 @@
/*
* nokia.c -- Nokia Composite Gadget Driver
*
* Copyright (C) 2008-2010 Nokia Corporation
* Contact: Felipe Balbi <felipe.balbi@nokia.com>
*
* This gadget driver borrows from serial.c which is:
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* version 2 of that License.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include "u_serial.h"
#include "u_ether.h"
#include "u_phonet.h"
#include "u_ecm.h"
#include "gadget_chips.h"
/* Defines */
#define NOKIA_VERSION_NUM 0x0211
#define NOKIA_LONG_NAME "N900 (PC-Suite Mode)"
USB_GADGET_COMPOSITE_OPTIONS();
USB_ETHERNET_MODULE_PARAMETERS();
#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
/* string IDs are assigned dynamically */
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static char manufacturer_nokia[] = "Nokia";
static const char product_nokia[] = NOKIA_LONG_NAME;
static const char description_nokia[] = "PC-Suite Configuration";
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
[USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = description_nokia,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_COMM,
.idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID),
.idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID),
.bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
};
/*-------------------------------------------------------------------------*/
/* Module */
MODULE_DESCRIPTION("Nokia composite gadget driver for N900");
MODULE_AUTHOR("Felipe Balbi");
MODULE_LICENSE("GPL");
/*-------------------------------------------------------------------------*/
static struct usb_function *f_acm_cfg1;
static struct usb_function *f_acm_cfg2;
static struct usb_function *f_ecm_cfg1;
static struct usb_function *f_ecm_cfg2;
static struct usb_function *f_obex1_cfg1;
static struct usb_function *f_obex2_cfg1;
static struct usb_function *f_obex1_cfg2;
static struct usb_function *f_obex2_cfg2;
static struct usb_function *f_phonet_cfg1;
static struct usb_function *f_phonet_cfg2;
static struct usb_configuration nokia_config_500ma_driver = {
.label = "Bus Powered",
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE,
.MaxPower = 500,
};
static struct usb_configuration nokia_config_100ma_driver = {
.label = "Self Powered",
.bConfigurationValue = 2,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
.MaxPower = 100,
};
static struct usb_function_instance *fi_acm;
static struct usb_function_instance *fi_ecm;
static struct usb_function_instance *fi_obex1;
static struct usb_function_instance *fi_obex2;
static struct usb_function_instance *fi_phonet;
static int __init nokia_bind_config(struct usb_configuration *c)
{
struct usb_function *f_acm;
struct usb_function *f_phonet = NULL;
struct usb_function *f_obex1 = NULL;
struct usb_function *f_ecm;
struct usb_function *f_obex2 = NULL;
int status = 0;
int obex1_stat = -1;
int obex2_stat = -1;
int phonet_stat = -1;
if (!IS_ERR(fi_phonet)) {
f_phonet = usb_get_function(fi_phonet);
if (IS_ERR(f_phonet))
pr_debug("could not get phonet function\n");
}
if (!IS_ERR(fi_obex1)) {
f_obex1 = usb_get_function(fi_obex1);
if (IS_ERR(f_obex1))
pr_debug("could not get obex function 0\n");
}
if (!IS_ERR(fi_obex2)) {
f_obex2 = usb_get_function(fi_obex2);
if (IS_ERR(f_obex2))
pr_debug("could not get obex function 1\n");
}
f_acm = usb_get_function(fi_acm);
if (IS_ERR(f_acm)) {
status = PTR_ERR(f_acm);
goto err_get_acm;
}
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm)) {
status = PTR_ERR(f_ecm);
goto err_get_ecm;
}
if (!IS_ERR_OR_NULL(f_phonet)) {
phonet_stat = usb_add_function(c, f_phonet);
if (phonet_stat)
pr_debug("could not add phonet function\n");
}
if (!IS_ERR_OR_NULL(f_obex1)) {
obex1_stat = usb_add_function(c, f_obex1);
if (obex1_stat)
pr_debug("could not add obex function 0\n");
}
if (!IS_ERR_OR_NULL(f_obex2)) {
obex2_stat = usb_add_function(c, f_obex2);
if (obex2_stat)
pr_debug("could not add obex function 1\n");
}
status = usb_add_function(c, f_acm);
if (status)
goto err_conf;
status = usb_add_function(c, f_ecm);
if (status) {
pr_debug("could not bind ecm config %d\n", status);
goto err_ecm;
}
if (c == &nokia_config_500ma_driver) {
f_acm_cfg1 = f_acm;
f_ecm_cfg1 = f_ecm;
f_phonet_cfg1 = f_phonet;
f_obex1_cfg1 = f_obex1;
f_obex2_cfg1 = f_obex2;
} else {
f_acm_cfg2 = f_acm;
f_ecm_cfg2 = f_ecm;
f_phonet_cfg2 = f_phonet;
f_obex1_cfg2 = f_obex1;
f_obex2_cfg2 = f_obex2;
}
return status;
err_ecm:
usb_remove_function(c, f_acm);
err_conf:
if (!obex2_stat)
usb_remove_function(c, f_obex2);
if (!obex1_stat)
usb_remove_function(c, f_obex1);
if (!phonet_stat)
usb_remove_function(c, f_phonet);
usb_put_function(f_ecm);
err_get_ecm:
usb_put_function(f_acm);
err_get_acm:
if (!IS_ERR_OR_NULL(f_obex2))
usb_put_function(f_obex2);
if (!IS_ERR_OR_NULL(f_obex1))
usb_put_function(f_obex1);
if (!IS_ERR_OR_NULL(f_phonet))
usb_put_function(f_phonet);
return status;
}
static int __init nokia_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
int status;
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto err_usb;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = strings_dev[STRING_DESCRIPTION_IDX].id;
nokia_config_500ma_driver.iConfiguration = status;
nokia_config_100ma_driver.iConfiguration = status;
if (!gadget_supports_altsettings(gadget)) {
status = -ENODEV;
goto err_usb;
}
fi_phonet = usb_get_function_instance("phonet");
if (IS_ERR(fi_phonet))
pr_debug("could not find phonet function\n");
fi_obex1 = usb_get_function_instance("obex");
if (IS_ERR(fi_obex1))
pr_debug("could not find obex function 1\n");
fi_obex2 = usb_get_function_instance("obex");
if (IS_ERR(fi_obex2))
pr_debug("could not find obex function 2\n");
fi_acm = usb_get_function_instance("acm");
if (IS_ERR(fi_acm)) {
status = PTR_ERR(fi_acm);
goto err_obex2_inst;
}
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm)) {
status = PTR_ERR(fi_ecm);
goto err_acm_inst;
}
/* finally register the configuration */
status = usb_add_config(cdev, &nokia_config_500ma_driver,
nokia_bind_config);
if (status < 0)
goto err_ecm_inst;
status = usb_add_config(cdev, &nokia_config_100ma_driver,
nokia_bind_config);
if (status < 0)
goto err_put_cfg1;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
return 0;
err_put_cfg1:
usb_put_function(f_acm_cfg1);
if (!IS_ERR_OR_NULL(f_obex1_cfg1))
usb_put_function(f_obex1_cfg1);
if (!IS_ERR_OR_NULL(f_obex2_cfg1))
usb_put_function(f_obex2_cfg1);
if (!IS_ERR_OR_NULL(f_phonet_cfg1))
usb_put_function(f_phonet_cfg1);
usb_put_function(f_ecm_cfg1);
err_ecm_inst:
usb_put_function_instance(fi_ecm);
err_acm_inst:
usb_put_function_instance(fi_acm);
err_obex2_inst:
if (!IS_ERR(fi_obex2))
usb_put_function_instance(fi_obex2);
if (!IS_ERR(fi_obex1))
usb_put_function_instance(fi_obex1);
if (!IS_ERR(fi_phonet))
usb_put_function_instance(fi_phonet);
err_usb:
return status;
}
static int __exit nokia_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_obex1_cfg2))
usb_put_function(f_obex1_cfg2);
if (!IS_ERR_OR_NULL(f_obex2_cfg2))
usb_put_function(f_obex2_cfg2);
if (!IS_ERR_OR_NULL(f_obex1_cfg1))
usb_put_function(f_obex1_cfg1);
if (!IS_ERR_OR_NULL(f_obex2_cfg1))
usb_put_function(f_obex2_cfg1);
if (!IS_ERR_OR_NULL(f_phonet_cfg1))
usb_put_function(f_phonet_cfg1);
if (!IS_ERR_OR_NULL(f_phonet_cfg2))
usb_put_function(f_phonet_cfg2);
usb_put_function(f_acm_cfg1);
usb_put_function(f_acm_cfg2);
usb_put_function(f_ecm_cfg1);
usb_put_function(f_ecm_cfg2);
usb_put_function_instance(fi_ecm);
if (!IS_ERR(fi_obex2))
usb_put_function_instance(fi_obex2);
if (!IS_ERR(fi_obex1))
usb_put_function_instance(fi_obex1);
if (!IS_ERR(fi_phonet))
usb_put_function_instance(fi_phonet);
usb_put_function_instance(fi_acm);
return 0;
}
static __refdata struct usb_composite_driver nokia_driver = {
.name = "g_nokia",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
.bind = nokia_bind,
.unbind = __exit_p(nokia_unbind),
};
module_usb_composite_driver(nokia_driver);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,276 @@
/*
* serial.c -- USB gadget serial driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include "u_serial.h"
#include "gadget_chips.h"
/* Defines */
#define GS_VERSION_STR "v2.4"
#define GS_VERSION_NUM 0x2400
#define GS_LONG_NAME "Gadget Serial"
#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
/* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#define GS_VENDOR_ID 0x0525 /* NetChip */
#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
#define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */
/* string IDs are assigned dynamically */
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
/* .bDeviceClass = f(use_acm) */
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
.idVendor = cpu_to_le16(GS_VENDOR_ID),
/* .idProduct = f(use_acm) */
.bcdDevice = cpu_to_le16(GS_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/*-------------------------------------------------------------------------*/
/* Module */
MODULE_DESCRIPTION(GS_VERSION_NAME);
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
static bool use_acm = true;
module_param(use_acm, bool, 0);
MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
static bool use_obex = false;
module_param(use_obex, bool, 0);
MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no");
static unsigned n_ports = 1;
module_param(n_ports, uint, 0);
MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
/*-------------------------------------------------------------------------*/
static struct usb_configuration serial_config_driver = {
/* .label = f(use_acm) */
/* .bConfigurationValue = f(use_acm) */
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
static struct usb_function_instance *fi_serial[MAX_U_SERIAL_PORTS];
static struct usb_function *f_serial[MAX_U_SERIAL_PORTS];
static int serial_register_ports(struct usb_composite_dev *cdev,
struct usb_configuration *c, const char *f_name)
{
int i;
int ret;
ret = usb_add_config_only(cdev, c);
if (ret)
goto out;
for (i = 0; i < n_ports; i++) {
fi_serial[i] = usb_get_function_instance(f_name);
if (IS_ERR(fi_serial[i])) {
ret = PTR_ERR(fi_serial[i]);
goto fail;
}
f_serial[i] = usb_get_function(fi_serial[i]);
if (IS_ERR(f_serial[i])) {
ret = PTR_ERR(f_serial[i]);
goto err_get_func;
}
ret = usb_add_function(c, f_serial[i]);
if (ret)
goto err_add_func;
}
return 0;
err_add_func:
usb_put_function(f_serial[i]);
err_get_func:
usb_put_function_instance(fi_serial[i]);
fail:
i--;
while (i >= 0) {
usb_remove_function(c, f_serial[i]);
usb_put_function(f_serial[i]);
usb_put_function_instance(fi_serial[i]);
i--;
}
out:
return ret;
}
static int __init gs_bind(struct usb_composite_dev *cdev)
{
int status;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = strings_dev[STRING_DESCRIPTION_IDX].id;
serial_config_driver.iConfiguration = status;
if (gadget_is_otg(cdev->gadget)) {
serial_config_driver.descriptors = otg_desc;
serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
/* register our configuration */
if (use_acm) {
status = serial_register_ports(cdev, &serial_config_driver,
"acm");
usb_ep_autoconfig_reset(cdev->gadget);
} else if (use_obex)
status = serial_register_ports(cdev, &serial_config_driver,
"obex");
else {
status = serial_register_ports(cdev, &serial_config_driver,
"gser");
}
if (status < 0)
goto fail;
usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s\n", GS_VERSION_NAME);
return 0;
fail:
return status;
}
static int gs_unbind(struct usb_composite_dev *cdev)
{
int i;
for (i = 0; i < n_ports; i++) {
usb_put_function(f_serial[i]);
usb_put_function_instance(fi_serial[i]);
}
return 0;
}
static __refdata struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.bind = gs_bind,
.unbind = gs_unbind,
};
static int __init init(void)
{
/* We *could* export two configs; that'd be much cleaner...
* but neither of these product IDs was defined that way.
*/
if (use_acm) {
serial_config_driver.label = "CDC ACM config";
serial_config_driver.bConfigurationValue = 2;
device_desc.bDeviceClass = USB_CLASS_COMM;
device_desc.idProduct =
cpu_to_le16(GS_CDC_PRODUCT_ID);
} else if (use_obex) {
serial_config_driver.label = "CDC OBEX config";
serial_config_driver.bConfigurationValue = 3;
device_desc.bDeviceClass = USB_CLASS_COMM;
device_desc.idProduct =
cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
} else {
serial_config_driver.label = "Generic Serial config";
serial_config_driver.bConfigurationValue = 1;
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
device_desc.idProduct =
cpu_to_le16(GS_PRODUCT_ID);
}
strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
return usb_composite_probe(&gserial_driver);
}
module_init(init);
static void __exit cleanup(void)
{
usb_composite_unregister(&gserial_driver);
}
module_exit(cleanup);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,145 @@
#ifndef __TARGET_USB_GADGET_H__
#define __TARGET_USB_GADGET_H__
#include <linux/kref.h>
/* #include <linux/usb/uas.h> */
#include <linux/usb/composite.h>
#include <linux/usb/uas.h>
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
#include <target/target_core_base.h>
#include <target/target_core_fabric.h>
#define USBG_NAMELEN 32
#define fuas_to_gadget(f) (f->function.config->cdev->gadget)
#define UASP_SS_EP_COMP_LOG_STREAMS 4
#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
enum {
USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX,
USB_G_STR_INT_UAS,
USB_G_STR_INT_BBB,
};
#define USB_G_ALT_INT_BBB 0
#define USB_G_ALT_INT_UAS 1
struct usbg_nacl {
/* Binary World Wide unique Port Name for SAS Initiator port */
u64 iport_wwpn;
/* ASCII formatted WWPN for Sas Initiator port */
char iport_name[USBG_NAMELEN];
/* Returned by usbg_make_nodeacl() */
struct se_node_acl se_node_acl;
};
struct tcm_usbg_nexus {
struct se_session *tvn_se_sess;
};
struct usbg_tpg {
struct mutex tpg_mutex;
/* SAS port target portal group tag for TCM */
u16 tport_tpgt;
/* Pointer back to usbg_tport */
struct usbg_tport *tport;
struct workqueue_struct *workqueue;
/* Returned by usbg_make_tpg() */
struct se_portal_group se_tpg;
u32 gadget_connect;
struct tcm_usbg_nexus *tpg_nexus;
atomic_t tpg_port_count;
};
struct usbg_tport {
/* SCSI protocol the tport is providing */
u8 tport_proto_id;
/* Binary World Wide unique Port Name for SAS Target port */
u64 tport_wwpn;
/* ASCII formatted WWPN for SAS Target port */
char tport_name[USBG_NAMELEN];
/* Returned by usbg_make_tport() */
struct se_wwn tport_wwn;
};
enum uas_state {
UASP_SEND_DATA,
UASP_RECEIVE_DATA,
UASP_SEND_STATUS,
UASP_QUEUE_COMMAND,
};
#define USBG_MAX_CMD 64
struct usbg_cmd {
/* common */
u8 cmd_buf[USBG_MAX_CMD];
u32 data_len;
struct work_struct work;
int unpacked_lun;
struct se_cmd se_cmd;
void *data_buf; /* used if no sg support available */
struct f_uas *fu;
struct completion write_complete;
struct kref ref;
/* UAS only */
u16 tag;
u16 prio_attr;
struct sense_iu sense_iu;
enum uas_state state;
struct uas_stream *stream;
/* BOT only */
__le32 bot_tag;
unsigned int csw_code;
unsigned is_read:1;
};
struct uas_stream {
struct usb_request *req_in;
struct usb_request *req_out;
struct usb_request *req_status;
};
struct usbg_cdb {
struct usb_request *req;
void *buf;
};
struct bot_status {
struct usb_request *req;
struct bulk_cs_wrap csw;
};
struct f_uas {
struct usbg_tpg *tpg;
struct usb_function function;
u16 iface;
u32 flags;
#define USBG_ENABLED (1 << 0)
#define USBG_IS_UAS (1 << 1)
#define USBG_USE_STREAMS (1 << 2)
#define USBG_IS_BOT (1 << 3)
#define USBG_BOT_CMD_PEND (1 << 4)
struct usbg_cdb cmd;
struct usb_ep *ep_in;
struct usb_ep *ep_out;
/* UAS */
struct usb_ep *ep_status;
struct usb_ep *ep_cmd;
struct uas_stream stream[UASP_SS_EP_COMP_NUM_STREAMS];
/* BOT */
struct bot_status bot_status;
struct usb_request *bot_req_in;
struct usb_request *bot_req_out;
};
extern struct usbg_tpg *the_only_tpg_I_currently_have;
#endif

View file

@ -0,0 +1,440 @@
/*
* webcam.c -- USB webcam gadget driver
*
* Copyright (C) 2009-2010
* Laurent Pinchart (laurent.pinchart@ideasonboard.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, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/usb/video.h>
#include "u_uvc.h"
USB_GADGET_COMPOSITE_OPTIONS();
/*-------------------------------------------------------------------------*/
/* module parameters specific to the Video streaming endpoint */
static unsigned int streaming_interval = 1;
module_param(streaming_interval, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_interval, "1 - 16");
static unsigned int streaming_maxpacket = 1024;
module_param(streaming_maxpacket, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxpacket, "1 - 1023 (FS), 1 - 3072 (hs/ss)");
static unsigned int streaming_maxburst;
module_param(streaming_maxburst, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(streaming_maxburst, "0 - 15 (ss only)");
static unsigned int trace;
module_param(trace, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(trace, "Trace level bitmask");
/* --------------------------------------------------------------------------
* Device descriptor
*/
#define WEBCAM_VENDOR_ID 0x1d6b /* Linux Foundation */
#define WEBCAM_PRODUCT_ID 0x0102 /* Webcam A/V gadget */
#define WEBCAM_DEVICE_BCD 0x0010 /* 0.10 */
static char webcam_vendor_label[] = "Linux Foundation";
static char webcam_product_label[] = "Webcam gadget";
static char webcam_config_label[] = "Video";
/* string IDs are assigned dynamically */
#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string webcam_strings[] = {
[USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
[USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
[USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = webcam_config_label,
{ }
};
static struct usb_gadget_strings webcam_stringtab = {
.language = 0x0409, /* en-us */
.strings = webcam_strings,
};
static struct usb_gadget_strings *webcam_device_strings[] = {
&webcam_stringtab,
NULL,
};
static struct usb_function_instance *fi_uvc;
static struct usb_function *f_uvc;
static struct usb_device_descriptor webcam_device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02,
.bDeviceProtocol = 0x01,
.bMaxPacketSize0 = 0, /* dynamic */
.idVendor = cpu_to_le16(WEBCAM_VENDOR_ID),
.idProduct = cpu_to_le16(WEBCAM_PRODUCT_ID),
.bcdDevice = cpu_to_le16(WEBCAM_DEVICE_BCD),
.iManufacturer = 0, /* dynamic */
.iProduct = 0, /* dynamic */
.iSerialNumber = 0, /* dynamic */
.bNumConfigurations = 0, /* dynamic */
};
DECLARE_UVC_HEADER_DESCRIPTOR(1);
static const struct UVC_HEADER_DESCRIPTOR(1) uvc_control_header = {
.bLength = UVC_DT_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VC_HEADER,
.bcdUVC = cpu_to_le16(0x0100),
.wTotalLength = 0, /* dynamic */
.dwClockFrequency = cpu_to_le32(48000000),
.bInCollection = 0, /* dynamic */
.baInterfaceNr[0] = 0, /* dynamic */
};
static const struct uvc_camera_terminal_descriptor uvc_camera_terminal = {
.bLength = UVC_DT_CAMERA_TERMINAL_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VC_INPUT_TERMINAL,
.bTerminalID = 1,
.wTerminalType = cpu_to_le16(0x0201),
.bAssocTerminal = 0,
.iTerminal = 0,
.wObjectiveFocalLengthMin = cpu_to_le16(0),
.wObjectiveFocalLengthMax = cpu_to_le16(0),
.wOcularFocalLength = cpu_to_le16(0),
.bControlSize = 3,
.bmControls[0] = 2,
.bmControls[1] = 0,
.bmControls[2] = 0,
};
static const struct uvc_processing_unit_descriptor uvc_processing = {
.bLength = UVC_DT_PROCESSING_UNIT_SIZE(2),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VC_PROCESSING_UNIT,
.bUnitID = 2,
.bSourceID = 1,
.wMaxMultiplier = cpu_to_le16(16*1024),
.bControlSize = 2,
.bmControls[0] = 1,
.bmControls[1] = 0,
.iProcessing = 0,
};
static const struct uvc_output_terminal_descriptor uvc_output_terminal = {
.bLength = UVC_DT_OUTPUT_TERMINAL_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VC_OUTPUT_TERMINAL,
.bTerminalID = 3,
.wTerminalType = cpu_to_le16(0x0101),
.bAssocTerminal = 0,
.bSourceID = 2,
.iTerminal = 0,
};
DECLARE_UVC_INPUT_HEADER_DESCRIPTOR(1, 2);
static const struct UVC_INPUT_HEADER_DESCRIPTOR(1, 2) uvc_input_header = {
.bLength = UVC_DT_INPUT_HEADER_SIZE(1, 2),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_INPUT_HEADER,
.bNumFormats = 2,
.wTotalLength = 0, /* dynamic */
.bEndpointAddress = 0, /* dynamic */
.bmInfo = 0,
.bTerminalLink = 3,
.bStillCaptureMethod = 0,
.bTriggerSupport = 0,
.bTriggerUsage = 0,
.bControlSize = 1,
.bmaControls[0][0] = 0,
.bmaControls[1][0] = 4,
};
static const struct uvc_format_uncompressed uvc_format_yuv = {
.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1,
.bNumFrameDescriptors = 2,
.guidFormat =
{ 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71},
.bBitsPerPixel = 16,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterfaceFlags = 0,
.bCopyProtect = 0,
};
DECLARE_UVC_FRAME_UNCOMPRESSED(1);
DECLARE_UVC_FRAME_UNCOMPRESSED(3);
static const struct UVC_FRAME_UNCOMPRESSED(3) uvc_frame_yuv_360p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
};
static const struct UVC_FRAME_UNCOMPRESSED(1) uvc_frame_yuv_720p = {
.bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
};
static const struct uvc_format_mjpeg uvc_format_mjpg = {
.bLength = UVC_DT_FORMAT_MJPEG_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FORMAT_MJPEG,
.bFormatIndex = 2,
.bNumFrameDescriptors = 2,
.bmFlags = 0,
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterfaceFlags = 0,
.bCopyProtect = 0,
};
DECLARE_UVC_FRAME_MJPEG(1);
DECLARE_UVC_FRAME_MJPEG(3);
static const struct UVC_FRAME_MJPEG(3) uvc_frame_mjpg_360p = {
.bLength = UVC_DT_FRAME_MJPEG_SIZE(3),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 1,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(640),
.wHeight = cpu_to_le16(360),
.dwMinBitRate = cpu_to_le32(18432000),
.dwMaxBitRate = cpu_to_le32(55296000),
.dwMaxVideoFrameBufferSize = cpu_to_le32(460800),
.dwDefaultFrameInterval = cpu_to_le32(666666),
.bFrameIntervalType = 3,
.dwFrameInterval[0] = cpu_to_le32(666666),
.dwFrameInterval[1] = cpu_to_le32(1000000),
.dwFrameInterval[2] = cpu_to_le32(5000000),
};
static const struct UVC_FRAME_MJPEG(1) uvc_frame_mjpg_720p = {
.bLength = UVC_DT_FRAME_MJPEG_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_FRAME_MJPEG,
.bFrameIndex = 2,
.bmCapabilities = 0,
.wWidth = cpu_to_le16(1280),
.wHeight = cpu_to_le16(720),
.dwMinBitRate = cpu_to_le32(29491200),
.dwMaxBitRate = cpu_to_le32(29491200),
.dwMaxVideoFrameBufferSize = cpu_to_le32(1843200),
.dwDefaultFrameInterval = cpu_to_le32(5000000),
.bFrameIntervalType = 1,
.dwFrameInterval[0] = cpu_to_le32(5000000),
};
static const struct uvc_color_matching_descriptor uvc_color_matching = {
.bLength = UVC_DT_COLOR_MATCHING_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = UVC_VS_COLORFORMAT,
.bColorPrimaries = 1,
.bTransferCharacteristics = 1,
.bMatrixCoefficients = 4,
};
static const struct uvc_descriptor_header * const uvc_fs_control_cls[] = {
(const struct uvc_descriptor_header *) &uvc_control_header,
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
(const struct uvc_descriptor_header *) &uvc_processing,
(const struct uvc_descriptor_header *) &uvc_output_terminal,
NULL,
};
static const struct uvc_descriptor_header * const uvc_ss_control_cls[] = {
(const struct uvc_descriptor_header *) &uvc_control_header,
(const struct uvc_descriptor_header *) &uvc_camera_terminal,
(const struct uvc_descriptor_header *) &uvc_processing,
(const struct uvc_descriptor_header *) &uvc_output_terminal,
NULL,
};
static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
NULL,
};
static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
NULL,
};
static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_input_header,
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
(const struct uvc_descriptor_header *) &uvc_color_matching,
NULL,
};
/* --------------------------------------------------------------------------
* USB configuration
*/
static int __init
webcam_config_bind(struct usb_configuration *c)
{
int status = 0;
f_uvc = usb_get_function(fi_uvc);
if (IS_ERR(f_uvc))
return PTR_ERR(f_uvc);
status = usb_add_function(c, f_uvc);
if (status < 0)
usb_put_function(f_uvc);
return status;
}
static struct usb_configuration webcam_config_driver = {
.label = webcam_config_label,
.bConfigurationValue = 1,
.iConfiguration = 0, /* dynamic */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.MaxPower = CONFIG_USB_GADGET_VBUS_DRAW,
};
static int /* __init_or_exit */
webcam_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR_OR_NULL(f_uvc))
usb_put_function(f_uvc);
if (!IS_ERR_OR_NULL(fi_uvc))
usb_put_function_instance(fi_uvc);
return 0;
}
static int __init
webcam_bind(struct usb_composite_dev *cdev)
{
struct f_uvc_opts *uvc_opts;
int ret;
fi_uvc = usb_get_function_instance("uvc");
if (IS_ERR(fi_uvc))
return PTR_ERR(fi_uvc);
uvc_opts = container_of(fi_uvc, struct f_uvc_opts, func_inst);
uvc_opts->streaming_interval = streaming_interval;
uvc_opts->streaming_maxpacket = streaming_maxpacket;
uvc_opts->streaming_maxburst = streaming_maxburst;
uvc_set_trace_param(trace);
uvc_opts->fs_control = uvc_fs_control_cls;
uvc_opts->ss_control = uvc_ss_control_cls;
uvc_opts->fs_streaming = uvc_fs_streaming_cls;
uvc_opts->hs_streaming = uvc_hs_streaming_cls;
uvc_opts->ss_streaming = uvc_ss_streaming_cls;
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
ret = usb_string_ids_tab(cdev, webcam_strings);
if (ret < 0)
goto error;
webcam_device_descriptor.iManufacturer =
webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
webcam_device_descriptor.iProduct =
webcam_strings[USB_GADGET_PRODUCT_IDX].id;
webcam_config_driver.iConfiguration =
webcam_strings[STRING_DESCRIPTION_IDX].id;
/* Register our configuration. */
if ((ret = usb_add_config(cdev, &webcam_config_driver,
webcam_config_bind)) < 0)
goto error;
usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "Webcam Video Gadget\n");
return 0;
error:
usb_put_function_instance(fi_uvc);
return ret;
}
/* --------------------------------------------------------------------------
* Driver
*/
static __refdata struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
.max_speed = USB_SPEED_SUPER,
.bind = webcam_bind,
.unbind = webcam_unbind,
};
module_usb_composite_driver(webcam_driver);
MODULE_AUTHOR("Laurent Pinchart");
MODULE_DESCRIPTION("Webcam Video Gadget");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1.0");

View file

@ -0,0 +1,438 @@
/*
* zero.c -- Gadget Zero, for USB development
*
* Copyright (C) 2003-2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* 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, or
* (at your option) any later version.
*/
/*
* Gadget Zero only needs two bulk endpoints, and is an example of how you
* can write a hardware-agnostic gadget driver running inside a USB device.
* Some hardware details are visible, but don't affect most of the driver.
*
* Use it with the Linux host/master side "usbtest" driver to get a basic
* functional test of your device-side usb stack, or with "usb-skeleton".
*
* It supports two similar configurations. One sinks whatever the usb host
* writes, and in return sources zeroes. The other loops whatever the host
* writes back, so the host can read it.
*
* Many drivers will only have one configuration, letting them be much
* simpler if they also don't support high speed operation (like this
* driver does).
*
* Why is *this* driver using two configurations, rather than setting up
* two interfaces with different functions? To help verify that multiple
* configuration infrastucture is working correctly; also, so that it can
* work with low capability USB controllers without four bulk endpoints.
*/
/*
* driver assumes self-powered hardware, and
* has no way for users to trigger remote wakeup.
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/usb/composite.h>
#include "g_zero.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_VERSION "Cinco de Mayo 2008"
static const char longname[] = "Gadget Zero";
/*
* Normally the "loopback" configuration is second (index 1) so
* it's not the default. Here's where to change that order, to
* work better with hosts where config changes are problematic or
* controllers (like original superh) that only support one config.
*/
static bool loopdefault = 0;
module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
static struct usb_zero_options gzero_options = {
.isoc_interval = GZERO_ISOC_INTERVAL,
.isoc_maxpacket = GZERO_ISOC_MAXPACKET,
.bulk_buflen = GZERO_BULK_BUFLEN,
.qlen = GZERO_QLEN,
.int_interval = GZERO_INT_INTERVAL,
.int_maxpacket = GZERO_INT_MAXPACKET,
};
/*-------------------------------------------------------------------------*/
/* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#ifndef CONFIG_USB_ZERO_HNPTEST
#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */
#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */
#define DEFAULT_AUTORESUME 0
#else
#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */
#define DRIVER_PRODUCT_NUM 0xbadd
#define DEFAULT_AUTORESUME 5
#endif
/* If the optional "autoresume" mode is enabled, it provides good
* functional coverage for the "USBCV" test harness from USB-IF.
* It's always set if OTG mode is enabled.
*/
static unsigned autoresume = DEFAULT_AUTORESUME;
module_param(autoresume, uint, S_IRUGO);
MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
/* Maximum Autoresume time */
static unsigned max_autoresume;
module_param(max_autoresume, uint, S_IRUGO);
MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup");
/* Interval between two remote wakeups */
static unsigned autoresume_interval_ms;
module_param(autoresume_interval_ms, uint, S_IRUGO);
MODULE_PARM_DESC(autoresume_interval_ms,
"milliseconds to increase successive wakeup delays");
static unsigned autoresume_step_ms;
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
.bNumConfigurations = 2,
};
#ifdef CONFIG_USB_OTG
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
#else
#define otg_desc NULL
#endif
/* string IDs are assigned dynamically */
/* default serial number takes at least two packets */
static char serial[] = "0123456789.0123456789.0123456789";
#define USB_GZERO_SS_DESC (USB_GADGET_FIRST_AVAIL_IDX + 0)
#define USB_GZERO_LB_DESC (USB_GADGET_FIRST_AVAIL_IDX + 1)
static struct usb_string strings_dev[] = {
[USB_GADGET_MANUFACTURER_IDX].s = "",
[USB_GADGET_PRODUCT_IDX].s = longname,
[USB_GADGET_SERIAL_IDX].s = serial,
[USB_GZERO_SS_DESC].s = "source and sink data",
[USB_GZERO_LB_DESC].s = "loop input to output",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
/*-------------------------------------------------------------------------*/
static struct timer_list autoresume_timer;
static void zero_autoresume(unsigned long _c)
{
struct usb_composite_dev *cdev = (void *)_c;
struct usb_gadget *g = cdev->gadget;
/* unconfigured devices can't issue wakeups */
if (!cdev->config)
return;
/* Normally the host would be woken up for something
* more significant than just a timer firing; likely
* because of some direct user request.
*/
if (g->speed != USB_SPEED_UNKNOWN) {
int status = usb_gadget_wakeup(g);
INFO(cdev, "%s --> %d\n", __func__, status);
}
}
static void zero_suspend(struct usb_composite_dev *cdev)
{
if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
if (autoresume) {
if (max_autoresume &&
(autoresume_step_ms > max_autoresume * 1000))
autoresume_step_ms = autoresume * 1000;
mod_timer(&autoresume_timer, jiffies +
msecs_to_jiffies(autoresume_step_ms));
DBG(cdev, "suspend, wakeup in %d milliseconds\n",
autoresume_step_ms);
autoresume_step_ms += autoresume_interval_ms;
} else
DBG(cdev, "%s\n", __func__);
}
static void zero_resume(struct usb_composite_dev *cdev)
{
DBG(cdev, "%s\n", __func__);
del_timer(&autoresume_timer);
}
/*-------------------------------------------------------------------------*/
static struct usb_configuration loopback_driver = {
.label = "loopback",
.bConfigurationValue = 2,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};
static struct usb_function *func_ss;
static struct usb_function_instance *func_inst_ss;
static int ss_config_setup(struct usb_configuration *c,
const struct usb_ctrlrequest *ctrl)
{
switch (ctrl->bRequest) {
case 0x5b:
case 0x5c:
return func_ss->setup(func_ss, ctrl);
default:
return -EOPNOTSUPP;
}
}
static struct usb_configuration sourcesink_driver = {
.label = "source/sink",
.setup = ss_config_setup,
.bConfigurationValue = 3,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};
module_param_named(buflen, gzero_options.bulk_buflen, uint, 0);
module_param_named(pattern, gzero_options.pattern, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63, 2 = none");
module_param_named(isoc_interval, gzero_options.isoc_interval, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_interval, "1 - 16");
module_param_named(isoc_maxpacket, gzero_options.isoc_maxpacket, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
module_param_named(isoc_mult, gzero_options.isoc_mult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_mult, "0 - 2 (hs/ss only)");
module_param_named(isoc_maxburst, gzero_options.isoc_maxburst, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(isoc_maxburst, "0 - 15 (ss only)");
module_param_named(int_interval, gzero_options.int_interval, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(int_interval, "1 - 16");
module_param_named(int_maxpacket, gzero_options.int_maxpacket, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(int_maxpacket, "0 - 1023 (fs), 0 - 1024 (hs/ss)");
module_param_named(int_mult, gzero_options.int_mult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(int_mult, "0 - 2 (hs/ss only)");
module_param_named(int_maxburst, gzero_options.int_maxburst, uint,
S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(int_maxburst, "0 - 15 (ss only)");
static struct usb_function *func_lb;
static struct usb_function_instance *func_inst_lb;
module_param_named(qlen, gzero_options.qlen, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qlen, "depth of loopback queue");
static int __init zero_bind(struct usb_composite_dev *cdev)
{
struct f_ss_opts *ss_opts;
struct f_lb_opts *lb_opts;
int status;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
func_inst_ss = usb_get_function_instance("SourceSink");
if (IS_ERR(func_inst_ss))
return PTR_ERR(func_inst_ss);
ss_opts = container_of(func_inst_ss, struct f_ss_opts, func_inst);
ss_opts->pattern = gzero_options.pattern;
ss_opts->isoc_interval = gzero_options.isoc_interval;
ss_opts->isoc_maxpacket = gzero_options.isoc_maxpacket;
ss_opts->isoc_mult = gzero_options.isoc_mult;
ss_opts->isoc_maxburst = gzero_options.isoc_maxburst;
ss_opts->int_interval = gzero_options.int_interval;
ss_opts->int_maxpacket = gzero_options.int_maxpacket;
ss_opts->int_mult = gzero_options.int_mult;
ss_opts->int_maxburst = gzero_options.int_maxburst;
ss_opts->bulk_buflen = gzero_options.bulk_buflen;
func_ss = usb_get_function(func_inst_ss);
if (IS_ERR(func_ss)) {
status = PTR_ERR(func_ss);
goto err_put_func_inst_ss;
}
func_inst_lb = usb_get_function_instance("Loopback");
if (IS_ERR(func_inst_lb)) {
status = PTR_ERR(func_inst_lb);
goto err_put_func_ss;
}
lb_opts = container_of(func_inst_lb, struct f_lb_opts, func_inst);
lb_opts->bulk_buflen = gzero_options.bulk_buflen;
lb_opts->qlen = gzero_options.qlen;
func_lb = usb_get_function(func_inst_lb);
if (IS_ERR(func_lb)) {
status = PTR_ERR(func_lb);
goto err_put_func_inst_lb;
}
sourcesink_driver.iConfiguration = strings_dev[USB_GZERO_SS_DESC].id;
loopback_driver.iConfiguration = strings_dev[USB_GZERO_LB_DESC].id;
/* support autoresume for remote wakeup testing */
sourcesink_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
loopback_driver.bmAttributes &= ~USB_CONFIG_ATT_WAKEUP;
sourcesink_driver.descriptors = NULL;
loopback_driver.descriptors = NULL;
if (autoresume) {
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
autoresume_step_ms = autoresume * 1000;
}
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
sourcesink_driver.descriptors = otg_desc;
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
loopback_driver.descriptors = otg_desc;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
/* Register primary, then secondary configuration. Note that
* SH3 only allows one config...
*/
if (loopdefault) {
usb_add_config_only(cdev, &loopback_driver);
usb_add_config_only(cdev, &sourcesink_driver);
} else {
usb_add_config_only(cdev, &sourcesink_driver);
usb_add_config_only(cdev, &loopback_driver);
}
status = usb_add_function(&sourcesink_driver, func_ss);
if (status)
goto err_conf_flb;
usb_ep_autoconfig_reset(cdev->gadget);
status = usb_add_function(&loopback_driver, func_lb);
if (status)
goto err_conf_flb;
usb_ep_autoconfig_reset(cdev->gadget);
usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
return 0;
err_conf_flb:
usb_put_function(func_lb);
func_lb = NULL;
err_put_func_inst_lb:
usb_put_function_instance(func_inst_lb);
func_inst_lb = NULL;
err_put_func_ss:
usb_put_function(func_ss);
func_ss = NULL;
err_put_func_inst_ss:
usb_put_function_instance(func_inst_ss);
func_inst_ss = NULL;
return status;
}
static int zero_unbind(struct usb_composite_dev *cdev)
{
del_timer_sync(&autoresume_timer);
if (!IS_ERR_OR_NULL(func_ss))
usb_put_function(func_ss);
usb_put_function_instance(func_inst_ss);
if (!IS_ERR_OR_NULL(func_lb))
usb_put_function(func_lb);
usb_put_function_instance(func_inst_lb);
return 0;
}
static __refdata struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
};
module_usb_composite_driver(zero_driver);
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");

32
drivers/usb/gadget/u_f.c Normal file
View file

@ -0,0 +1,32 @@
/*
* u_f.c -- USB function utilities for Gadget stack
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/usb/gadget.h>
#include "u_f.h"
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req) {
req->length = len ?: default_len;
req->buf = kmalloc(req->length, GFP_ATOMIC);
if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
}
}
return req;
}
EXPORT_SYMBOL_GPL(alloc_ep_req);

52
drivers/usb/gadget/u_f.h Normal file
View file

@ -0,0 +1,52 @@
/*
* u_f.h
*
* Utility definitions for USB functions
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __U_F_H__
#define __U_F_H__
/* Variable Length Array Macros **********************************************/
#define vla_group(groupname) size_t groupname##__next = 0
#define vla_group_size(groupname) groupname##__next
#define vla_item(groupname, type, name, n) \
size_t groupname##_##name##__offset = ({ \
size_t align_mask = __alignof__(type) - 1; \
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
size_t size = (n) * sizeof(type); \
groupname##__next = offset + size; \
offset; \
})
#define vla_item_with_sz(groupname, type, name, n) \
size_t groupname##_##name##__sz = (n) * sizeof(type); \
size_t groupname##_##name##__offset = ({ \
size_t align_mask = __alignof__(type) - 1; \
size_t offset = (groupname##__next + align_mask) & ~align_mask;\
size_t size = groupname##_##name##__sz; \
groupname##__next = offset + size; \
offset; \
})
#define vla_ptr(ptr, groupname, name) \
((void *) ((char *)ptr + groupname##_##name##__offset))
struct usb_ep;
struct usb_request;
struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len);
#endif /* __U_F_H__ */

View file

@ -0,0 +1,123 @@
/*
* u_os_desc.h
*
* Utility definitions for "OS Descriptors" support
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __U_OS_DESC_H__
#define __U_OS_DESC_H__
#include <asm/unaligned.h>
#include <linux/nls.h>
#define USB_EXT_PROP_DW_SIZE 0
#define USB_EXT_PROP_DW_PROPERTY_DATA_TYPE 4
#define USB_EXT_PROP_W_PROPERTY_NAME_LENGTH 8
#define USB_EXT_PROP_B_PROPERTY_NAME 10
#define USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH 10
#define USB_EXT_PROP_B_PROPERTY_DATA 14
#define USB_EXT_PROP_RESERVED 0
#define USB_EXT_PROP_UNICODE 1
#define USB_EXT_PROP_UNICODE_ENV 2
#define USB_EXT_PROP_BINARY 3
#define USB_EXT_PROP_LE32 4
#define USB_EXT_PROP_BE32 5
#define USB_EXT_PROP_UNICODE_LINK 6
#define USB_EXT_PROP_UNICODE_MULTI 7
static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
{
return buf + offset;
}
static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
{
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
}
static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
{
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
}
static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
{
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
}
static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
{
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
}
static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
{
return __usb_ext_prop_ptr(buf,
USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
}
static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
{
return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
}
static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
{
put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
}
static inline void usb_ext_prop_put_type(u8 *buf, int type)
{
put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
}
static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
{
int result;
put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
(wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2);
if (result < 0)
return result;
put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
return pnl;
}
static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
int data_len)
{
put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
}
static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
int data_len)
{
int result;
put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
(wchar_t *) usb_ext_prop_data_ptr(buf, pnl),
data_len - 2);
if (result < 0)
return result;
put_unaligned_le16(0,
&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
return data_len;
}
#endif /* __U_OS_DESC_H__ */

View file

@ -0,0 +1,402 @@
#
# USB Gadget support on a system involves
# (a) a peripheral controller, and
# (b) the gadget driver using it.
#
# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
#
# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
# - Some systems have both kinds of controllers.
#
# With help from a special transceiver and a "Mini-AB" jack, systems with
# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
#
#
# USB Peripheral Controller Support
#
# The order here is alphabetical, except that integrated controllers go
# before discrete ones so they will be the initial/default value:
# - integrated/SOC controllers first
# - licensed IP used in both SOC and discrete versions
# - discrete ones (including all PCI-only controllers)
# - debug/dummy gadget+hcd is last.
#
menu "USB Peripheral Controller"
#
# Integrated controllers
#
config USB_AT91
tristate "Atmel AT91 USB Device Port"
depends on ARCH_AT91
help
Many Atmel AT91 processors (such as the AT91RM2000) have a
full speed USB Device Port with support for five configurable
endpoints (plus endpoint zero).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "at91_udc" and force all
gadget drivers to also be dynamically linked.
config USB_LPC32XX
tristate "LPC32XX USB Peripheral Controller"
depends on ARCH_LPC32XX && I2C
select USB_ISP1301
help
This option selects the USB device controller in the LPC32xx SoC.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "lpc32xx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_ATMEL_USBA
tristate "Atmel USBA"
depends on AVR32 || ARCH_AT91
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
config USB_BCM63XX_UDC
tristate "Broadcom BCM63xx Peripheral Controller"
depends on BCM63XX
help
Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
high speed USB Device Port with support for four fixed endpoints
(plus endpoint zero).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "bcm63xx_udc".
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
select USB_FSL_MPH_DR_OF if OF
help
Some of Freescale PowerPC and i.MX processors have a High Speed
Dual-Role(DR) USB controller, which supports device mode.
The number of programmable endpoints is different through
SOC revisions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "fsl_usb2_udc" and force
all gadget drivers to also be dynamically linked.
config USB_FUSB300
tristate "Faraday FUSB300 USB Peripheral Controller"
depends on !PHYS_ADDR_T_64BIT && HAS_DMA
help
Faraday usb device controller FUSB300 driver
config USB_FOTG210_UDC
depends on HAS_DMA
tristate "Faraday FOTG210 USB Peripheral Controller"
help
Faraday USB2.0 OTG controller which can be configured as
high speed or full speed USB device. This driver supppors
Bulk Transfer so far.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "fotg210_udc".
config USB_GR_UDC
tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
depends on HAS_DMA
help
Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
VHDL IP core library.
config USB_OMAP
tristate "OMAP USB Device Controller"
depends on ARCH_OMAP1
depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
help
Many Texas Instruments OMAP processors have flexible full
speed USB device controllers, with support for up to 30
endpoints (plus endpoint zero). This driver supports the
controller in the OMAP 1611, and should work with controllers
in other OMAP processors too, given minor tweaks.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "omap_udc" and force all
gadget drivers to also be dynamically linked.
config USB_PXA25X
tristate "PXA 25x or IXP 4xx"
depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
help
Intel's PXA 25x series XScale ARM-5TE processors include
an integrated full speed USB 1.1 device controller. The
controller in the IXP 4xx series is register-compatible.
It has fifteen fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "pxa25x_udc" and force all
gadget drivers to also be dynamically linked.
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA25X_SMALL
depends on USB_PXA25X
bool
default n if USB_ETH_RNDIS
default y if USB_ZERO
default y if USB_ETH
default y if USB_G_SERIAL
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
depends on HAS_DMA
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
It has nine configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "r8a66597_udc" and force all
gadget drivers to also be dynamically linked.
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS && HAS_DMA
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
It has nine or more configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "renesas_usbhs" and force all
gadget drivers to also be dynamically linked.
config USB_PXA27X
tristate "PXA 27x"
help
Intel's PXA 27x series XScale ARM v5TE processors include
an integrated full speed USB 1.1 device controller.
It has up to 23 endpoints, as well as endpoint zero (for
control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "pxa27x_udc" and force all
gadget drivers to also be dynamically linked.
config USB_S3C2410
tristate "S3C2410 USB Device Controller"
depends on ARCH_S3C24XX
help
Samsung's S3C2410 is an ARM-4 processor with an integrated
full speed USB 1.1 device controller. It has 4 configurable
endpoints, as well as endpoint zero (for control transfers).
This driver has been tested on the S3C2410, S3C2412, and
S3C2440 processors.
config USB_S3C2410_DEBUG
boolean "S3C2410 udc debug messages"
depends on USB_S3C2410
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
depends on ARCH_S3C24XX
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
integrated with dual speed USB 2.0 device controller. It has
8 endpoints, as well as endpoint zero.
This driver has been tested on S3C2416 and S3C2450 processors.
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
depends on HAS_DMA
help
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
full speed USB peripheral.
config USB_MV_U3D
depends on HAS_DMA
tristate "MARVELL PXA2128 USB 3.0 controller"
help
MARVELL PXA2128 Processor series include a super speed USB3.0 device
controller, which support super speed USB peripheral.
#
# Controllers available in both integrated and discrete versions
#
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
It has seven configurable endpoints, and endpoint zero.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "m66592_udc" and force all
gadget drivers to also be dynamically linked.
#
# Controllers available only in discrete form (and all PCI controllers)
#
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
The UDC port supports OTG operation, and may be used as a host port
if it's not being used to implement peripheral or OTG roles.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "amd5536udc" and force all
gadget drivers to also be dynamically linked.
config USB_FSL_QE
tristate "Freescale QE/CPM USB Device Controller"
depends on FSL_SOC && (QUICC_ENGINE || CPM)
help
Some of Freescale PowerPC processors have a Full Speed
QE/CPM2 USB controller, which support device mode with 4
programmable endpoints. This driver supports the
controller in the MPC8360 and MPC8272, and should work with
controllers having QE or CPM2, given minor tweaks.
Set CONFIG_USB_GADGET to "m" to build this driver as a
dynamically linked module called "fsl_qe_udc".
config USB_NET2272
tristate "PLX NET2272"
help
PLX NET2272 is a USB peripheral controller which supports
both full and high speed USB 2.0 data transfers.
It has three configurable endpoints, as well as endpoint zero
(for control transfer).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "net2272" and force all
gadget drivers to also be dynamically linked.
config USB_NET2272_DMA
boolean "Support external DMA controller"
depends on USB_NET2272 && HAS_DMA
help
The NET2272 part can optionally support an external DMA
controller, but your board has to have support in the
driver itself.
If unsure, say "N" here. The driver works fine in PIO mode.
config USB_NET2280
tristate "NetChip 228x / PLX USB338x"
depends on PCI
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
It has six configurable endpoints, as well as endpoint zero
(for control transfers) and several endpoints with dedicated
functions.
PLX 3380 / 3382 is a PCIe based USB peripheral controller which
supports full, high speed USB 2.0 and super speed USB 3.0
data transfers.
It has eight configurable endpoints, as well as endpoint zero
(for control transfers) and several endpoints with dedicated
functions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "net2280" and force all
gadget drivers to also be dynamically linked.
config USB_GOKU
tristate "Toshiba TC86C001 'Goku-S'"
depends on PCI
help
The Toshiba TC86C001 is a PCI device which includes controllers
for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
The device controller has three configurable (bulk or interrupt)
endpoints, plus endpoint zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "goku_udc" and to force all
gadget drivers to also be dynamically linked.
config USB_EG20T
tristate "Intel QUARK X1000/EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
general embedded platform. EG20T PCH has USB device interface.
Using this interface, it is able to access system devices connected
to USB device.
This driver enables USB device function.
USB device is a USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
This driver supports both control transfer and bulk transfer modes.
This driver dose not support interrupt transfer or isochronous
transfer modes.
This driver also can be used for LAPIS Semiconductor's ML7213 which is
for IVI(In-Vehicle Infotainment) use.
ML7831 is for general purpose use.
ML7213/ML7831 is companion chip for Intel Atom E6xx series.
ML7213/ML7831 is completely compatible for Intel EG20T PCH.
This driver can be used with Intel's Quark X1000 SOC platform
config USB_GADGET_XILINX
tristate "Xilinx USB Driver"
depends on HAS_DMA
depends on OF || COMPILE_TEST
help
USB peripheral controller driver for Xilinx USB2 device.
Xilinx USB2 device is a soft IP which supports both full
and high speed USB 2.0 data transfers. It has seven configurable
endpoints(bulk or interrupt or isochronous), as well as
endpoint zero(for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "udc-xilinx" and force all
gadget drivers to also be dynamically linked.
#
# LAST -- dummy/emulated controller
#
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
side is the master; the gadget side is the slave. Gadget drivers
can be high, full, or low speed; and they have access to endpoints
like those from NET2280, PXA2xx, or SA1100 hardware.
This may help in some stages of creating a driver to embed in a
Linux device, since it lets you debug several parts of the gadget
driver without its hardware or drivers being involved.
Since such a gadget side driver needs to interoperate with a host
side Linux-USB device driver, this may help to debug both sides
of a USB protocol stack.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "dummy_hcd" and force all
gadget drivers to also be dynamically linked.
# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
# first and will be selected by default.
endmenu

View file

@ -0,0 +1,32 @@
#
# USB peripheral controller drivers
#
obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
obj-$(CONFIG_USB_EG20T) += pch_udc.o
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
mv_udc-y := mv_udc_core.o
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more