Drivers Multifunction Composite Gadget



  1. Drivers Multifunction Composite Gadget Watch Band
  2. Drivers Multifunction Composite Gadget Price

Configfs Composite Gadget is new type of gadget, work on which are made by Andrzej from Kernel group. This page contains information about how to configure, test and use Configfs Composite Gadget. With functionfs interface one can create usb multifunction composite gadget with some functions implemented in kernel space (like USB Ethernet) and other (like the PTP) in userspace. I've tested the new driver with WindowsXP SP3, Windows Vista and Windows.


original by jsmith7342, cc by-sa, others cc0/PD

Hi, Hackaday! 😀

Want your Pi Zero to emulate a keyboard, ethernet adapter, serial device, mass storage, and many more at the same time? This tutorial is for you!

The Pi Zero is all the rage – and I too am working on a Zero-based project.
[LadyAda] and [gbaman] both have written tutorials on the old series of drivers, g_{hid,ether,serial,*}. Those are not flexible at all, only allow for one loaded at each time and in case of the hid-gadget even require you to modify and recompile the kernel module.

LibComposite solves those problems by putting the configuration into userland (ConfigFS).
Keep in mind though, that at the time of writing, no windows drivers are available – you need to use Linux or Mac OS X to connect to your Pi this way.

Step 0 – configuring the SD card

Download and install the latest Raspbian Jessie onto a suitably large SD card, and expand the root partition. This has been described enough already, so I’ll just link you to the guide on Adafruit!

Step 1 – Kernel stuff

We need to use the 4.4 Kernel, which is currently (Feb. 2016) not installed on the default Raspbian image. But upgrading is easy: just run
sudo BRANCH=next rpi-update
(the ‚$‘ indicates this should be typed into the shell – don’t try to copy it)

Now, we need to enable a special „device tree overlay“:
echo 'dtoverlay=dwc2' | sudo tee -a /boot/config.txt
echo 'dwc2' | sudo tee -a /etc/modules

Finally, we need to enable the libcomposite driver by running:
sudo echo 'libcomposite' | sudo tee -a /etc/modules

Drivers Multifunction Composite Gadget

Step 2 – Configuring the gadget(s)

Now you have to define, what you want your gadget to do – just want ethernet? a keyboard? all of the above?
Configuring is done via ConfigFS, a virtual file system in /sys/. It is automatically mounted on the Pi on startup, so we can dig right in.
For a gadget called ‚USBArmory‘, a nice starting point exists, on which I will base the rest of this tutorial.

Create the config script

The configuration is volatile, so it must be run on each startup.
Create a file isticktoit_usb in /usr/bin/ using your favourite text editor. Type the following:
sudo touch /usr/bin/isticktoit_usb #create the file
sudo chmod +x /usr/bin/isticktoit_usb #make it executable
sudo nano /usr/bin/isticktoit_usb #edit the file

Afterwards, you need to run this script automatically at startup. For best performance, you can create a systemd unit file, but for now, we’ll stick to rc.local. (this is part of the old sysvinit system, but is still executed on the pi by default)
Open /etc/rc.local as root and add the following line before(!!!!) the line containing the word ‚exit‘.
sudo nano /etc/rc.local
/etc/rc.local...
/usr/bin/isticktoit_usb # libcomposite configuration
exit

Creating the gadget

Drivers Multifunction Composite Gadget

This is the global configuration; so it does not matter how many USB features your Pi will use.
Feel free to change serial number, manufacturer and product name in this block.
/usr/bin/isticktoit_usb#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p isticktoit
cd isticktoit
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2
mkdir -p strings/0x409
echo 'fedcba9876543210' > strings/0x409/serialnumber
echo 'Tobias Girstmair' > strings/0x409/manufacturer
echo 'iSticktoit.net USB Device' > strings/0x409/product
mkdir -p configs/c.1/strings/0x409
echo 'Config 1: ECM network' > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower
# Add functions here
# see gadget configurations below
# End functions
ls /sys/class/udc > UDC

Now scroll down to the headings that interest you and configure those functions 😉
A full example can be found at my hardpass repository – here

Serial Adapter

This is quite useful for debugging and is the easiest to setup.
to our config-file insert the following between the ‚functions‘ comments:
/usr/bin/isticktoit_usb# Add functions here
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/
# End functions

Then, enable a console on the USB serial:
sudo systemctl enable getty@ttyGS0.service
To connect to your Pi on a Linux computer, make sure screen is installed (sudo apt-get install screen) and then run:
sudo screen /dev/ttyACM0 115200
if screen terminates automatically, you need to change the device file. Consult for example dmesg:
dmesg|grep 'USB ACM device'

Ethernet Adapter

first, lets add to our configuration file:
sudo nano /usr/bin/isticktoit_usb
/usr/bin/isticktoit_usb# Add functions here
mkdir -p functions/ecm.usb0
# first byte of address must be even
HOST='48:6f:73:74:50:43' # 'HostPC'
SELF='42:61:64:55:53:42' # 'BadUSB'
echo $HOST > functions/ecm.usb0/host_addr
echo $SELF > functions/ecm.usb0/dev_addr
ln -s functions/ecm.usb0 configs/c.1/
# End functions
ls /sys/class/udc > UDC
#put this at the very end of the file:
ifconfig usb0 10.0.0.1 netmask 255.255.255.252 up
route add -net default gw 10.0.0.2

Save and exit nano, then head over to your host PC:
When you have problems with the automatic connection (e.g. „Wired Connection 2“ on my Thinkpad), disconnect and execute this:
dmesg|grep cdc_ether
[13890.668557] cdc_ether 1-1:1.2 eth0: register 'cdc_ether' at usb-0000:00:14.0-1, CDC Ethernet Device, 48:6f:73:74:50:43
[13890.674117] usbcore: registered new interface driver cdc_ether
[13890.687619] cdc_ether 1-1:1.2 enp0s20u1i2: renamed from eth0

will tell you, how your ethernet adapter is named (and maybe renamed afterwards. Plug the interface name (enp0s20u1i2) into this line:
sudo ifconfig enp0s20u1i2 10.0.0.2 netmask 255.255.255.252 up
Then, connect via ssh to your Pi:
ssh 10.0.0.1 -l pi

Keyboard / Mouse / Joystick (HID)

Firstly, our configuration script.
sudo nano /usr/bin/isticktoit_usb
/usr/bin/isticktoit_usb# Add functions here
mkdir -p functions/hid.usb0
echo 1 > functions/hid.usb0/protocol
echo 1 > functions/hid.usb0/subclass
echo 8 > functions/hid.usb0/report_length
echo -ne x05x01x09x06xa1x01x05x07x19xe0x29xe7x15x00x25x01x75x01x95x08x81x02x95x01x75x08x81x03x95x05x75x01x05x08x19x01x29x05x91x02x95x01x75x03x91x03x95x06x75x08x15x00x25x65x05x07x19x00x29x65x81x00xc0 > functions/hid.usb0/report_desc
ln -s functions/hid.usb0 configs/c.1/
# End functions

The simplest way to send keystrokes is by echoing HID packets to the device file:
sudo su
echo -ne '00x400000' > /dev/hidg0 #press the A-button
echo -ne '00000000' > /dev/hidg0 #release all keys

This isn’t practicable however, so head over to my GitHub page and download the code onto your computer.
extract it onto the Pi’s SD card and boot it up. On the Pi:
cd PATH_TO_HARDPASS_REPO
make #compile the program
echo -n 'hello world!' | sudo ./scan /dev/hidg0 1 2

the last line will write the piped in string over the HID protocol. The ‚1‘ means US-Layout, a ‚2‘ in its place would be German/Austrian layout. The second number is for entering characters not available on your keyboard (2=Linux, 3=Windows(but no windows drivers))

Mass storage

Mass storage is somewhat difficult. You cannot share the Pi’s partition with the host computer, but only a disk image file. I created a very small one to store the ethernet host configuration (sudo ifconfig …) on it.
First, lets make a disk. This is quite a long process, so follow this tutorial here. Run those commads (count=1024 describes the size in kilobytes):
dd if=/dev/zero of=~/usbdisk.img bs=1024 count=1024
mkdosfs ~/usbdisk.img

Then, it we go to our configuration again:
sudo nano /usr/bin/isticktoit_usb
/usr/bin/isticktoit_usb# Add functions here
FILE=/home/pi/usbdisk.img
mkdir -p ${FILE/img/d}
# mount -o loop,ro,offset=1048576 -t ext4 $FILE ${FILE/img/d} # FOR OLD WAY OF MAKING THE IMAGE
mount -o loop,ro, -t vfat $FILE ${FILE/img/d} # FOR IMAGE CREATED WITH DD
mkdir -p functions/mass_storage.usb0
echo 1 > functions/mass_storage.usb0/stall
echo 0 > functions/mass_storage.usb0/lun.0/cdrom
echo 0 > functions/mass_storage.usb0/lun.0/ro
echo 0 > functions/mass_storage.usb0/lun.0/nofua
echo $FILE > functions/mass_storage.usb0/lun.0/file
ln -s functions/mass_storage.usb0 configs/c.1/
# End functions

A FAT32 formatted removable drive should show up, when you plug the Pi into a computer the next time. To get access to the files stored on the disk-image from the Pi, you can unmount it completely (in the host first, then in the pi), and remount it somewhere else.

Others

There are about 20 different USB gadgets the Linux Kernel can emulate. Have a look at the Kernel documentation to find out more!

Links

Tutorial by gbaman
libcomposite in the Kernel documentation
–> hardpass – my PiZero project (Keyboad emulation) <–
–> hardpass – my PiZero project (Password Manager) <–
USBArmory Wiki Page

Revisions

Added an Install Mode to the Multifunction Composite Gadget. This
mode makes the gadget appear as a mass storage device with first
logical unit simulating CD-ROM until an eject on that logical unit
DriversDeviceis requested because then gadget switches to the 'full flagged'
gadget.

The intend is that in Install Mode the gadget will provide only
a CD-ROM with drivers for host platform. After the drivers are
intstalled the gadget will switch to the proper gadget and the
newly installed drivers will handle it.

Drivers Multifunction Composite Gadget Watch Band

When the device is disconnected form the host machine (or host
reboots or whatever that couses suspend or disconnect) the gadget
will switch to Install Mode again.

Because disconnect is a normal situation on re-enumeration gadget
'ignores' all disconnects and suspends during the first 10 seconds
after an eject.

Signed-off-by: Michal Nazarewicz <m.naz...@samsung.com>
Signed-off-by: Kyungmin Park <kyungm...@samsung.com>
---
Documentation/usb/gadget_multi.txt | 61 ++++
drivers/usb/gadget/Kconfig | 22 ++
drivers/usb/gadget/multi.c | 596 +++++++++++++++++++++++++++++-------
3 files changed, 567 insertions(+), 112 deletions(-)

diff --git a/Documentation/usb/gadget_multi.txt b/Documentation/usb/gadget_multi.txt
index 65bccd8..9319f1d 100644
--- a/Documentation/usb/gadget_multi.txt
+++ b/Documentation/usb/gadget_multi.txt
@@ -18,6 +18,11 @@ RNDIS and another with CDC ECM[3].
Please not that if you use non-standard configuration you may need to
change vendor and/or product ID.
+The driver provides also an 'install mode' which you may also call
+NoCD ore ZereCD (expect the later is a trademark) which lets one
+develop an image with drivers which install automatically on systems
+like Windows.
+
* Host drivers
To make use of the gadget one needs to make it work on host side --
@@ -35,6 +40,10 @@ This is also true for two configuration set-up with RNDIS
configuration being the first one. Linux host will use the second
configuration with CDC ECM which should work better under Linux.
+The only exception is when install mode is enabled in which case the
+gadget will appear as a plain mass storage device unless it is
+ejected. Read appropriate section of this document to find out more.
+
** Windows host drivers
For the gadget two work under Windown two conditions have to be met:
@@ -129,6 +138,54 @@ For more exotic systems I have even less to say...
Any testing and drivers *are* *welcome*!
+* Install mode
+
+The install mode makes the gadget appear as a plain mass storage
+device the first time it is connected (and after each disconnect).
+This lets one develop an 'autorun' CD-ROM image with drivers and put
+it as the first logical unit.
+
+** Workings of the install mode
+
+As you may know, mass storage gadget may provide several logical units
+and its easier to think of them as separate drives. When install mode
+is enabled, g_multi forces the first logical unit to be a read-only
+CD-ROM. When install mode is enabled but mass storage itself is not
+then exactly one logical unit is set.
+
+When an eject request is made on that logical unit, the file is not
+really closed but the gadget switches it's mode to the full flagged
+gadget with all the other functions. If mass storage is among them,
+the firs logical unit will be the CD-ROM image with drivers (which may
+be seen as a bad thing).
+
+When gadget is disconnected and connected afterwards it will work
+again in install mode. Some heuristics are used here -- if
+disconnection (or suspend) happens no longer then 10 seconds after
+last eject on the first logical unit then on next enumeration gadget
+will claim to be full flagged otherwise it'll stick to install mode.
+
+** Interoperability with host
+
+As said, the idea behind install mode is that hosts that require
+drivers will be able to get them without the need for additional
+CD-ROM or another medium provided with the device.
+
+CD-ROM image should provide an 'autorun' functionality witch will
+install drivers and eject the emulated CD-ROM to switch gadget into
+the other mode which will be handled by newly installed drivers. If
+drivers are installed already, they should 'catch' the install mode
+device by product and vendor IDs and issue an eject.
+
+This mode is not very Linux-friendly though since Linux and Linux
+based systems have no notion of autorun (which from security point of
+view is a good thing) and there's no way of adding some file on the
+image which will make gadget eject the device.
+
+Fortunately, there's USB_ModeSwitch[8] and/or udev[9] which
+should handle it just fine. A single rule need to be added and
+everything should work fine.
+
* Authors
This document has been written by Michal Nazarewicz
@@ -158,3 +215,7 @@ any user input.
[7] Possibility to say `git rev-list --author='Your Name'
linus/master|wc -l` returns non-zero -- priceless. :]
+[8] [[http://www.draisberghof.de/usb_modeswitch/]]
+
+[9] [[http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html]]
+
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 8052643..21500eb 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -920,6 +920,28 @@ config USB_G_MULTI_MSF
If unsure, say 'y'.
+config USB_G_MULTI_INSTALL
+ bool 'Install Mode'
+ depends on USB_G_MULTI && BLOCK
+ default n
+ help
+ This option enables an 'Install Mode' configuration. You may
+ also refer to in as NoCD or ZeroCD (although the later is
+ a trademark).
+
+ This mode makes gadget appear as an USB Mass Storage device
+ emulating a CD-ROM the first time it is connected. The intend
+ is that you can put drivers for your gadget on the disk image.
+
+ When eject request is sent to the logical translation unit
+ gadget switches its mode to the full flagged gadget with all the
+ other functions.
+
+ When device is disconnected, gadget once again switches to the
+ Install Mode configuration.
+
+ If unsure, say 'n'.
+
config USB_G_HID
tristate 'HID Gadget'
help
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 6f6fd3e..4aab815 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -25,6 +25,8 @@
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/jiffies.h>
#if defined USB_ETH_RNDIS
@@ -57,18 +59,27 @@ MODULE_LICENSE('GPL');
#include 'config.c'
#include 'epautoconf.c'
-/* Mass storage */
-#ifdef CONFIG_USB_G_MULTI_MSF
+/* Mass storage & Install Mode */
+#if defined CONFIG_USB_G_MULTI_MSF || defined CONFIG_USB_G_MULTI_INSTALL
# include 'f_mass_storage.c'
-
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
-
static struct fsg_common fsg_common;
#else
-# define fsg_common_from_params(common, cdev, data) NULL
# define fsg_bind_config(cdev, conf, common) ((int)0)
-# define fsg_common_put(common) do { } while (0)
+#endif
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+static unsigned install_mode = 1, next_install_mode = 1;
+#else
+# define install_mode false
+# define next_install_mode false
+#endif
+
+#ifdef CONFIG_USB_G_MULTI_MSF
+# define have_fsg true
+#else
+# define have_fsg false
#endif
/* CDC ACM */
@@ -76,8 +87,6 @@ static struct fsg_common fsg_common;
# include 'u_serial.c'
# include 'f_acm.c'
#else
-# define gserial_setup(conf, ports) ((int)0)
-# define gserial_cleanup() do { } while (0)
# define acm_bind_config(conf, ports) ((int)0)
#endif
@@ -95,9 +104,6 @@ static struct fsg_common fsg_common;
#if defined CONFIG_USB_G_MULTI_ECM || defined CONFIG_USB_G_MULTI_RNDIS
# include 'u_ether.c'
static u8 hostaddr[ETH_ALEN];
-#else
-# define gether_setup(cdev, hostaddr) ((int)0)
-# define gether_cleanup() do { } while (0)
#endif
#ifndef CONFIG_USB_G_MULTI_ECM
@@ -110,14 +116,34 @@ static u8 hostaddr[ETH_ALEN];
#endif
+/******************************** Prototypes ********************************/
+
+static unsigned long multi_initialised;
+
+static int multi_setup(struct usb_composite_dev *cdev);
+static void multi_cleanup(void);
+static int multi_bind(struct usb_composite_dev *cdev);
+static int multi_register(void);
+static void multi_unregister(void);
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+static int multi_eject(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+static void multi_disconnect(struct usb_composite_dev *cdev);
+#else
+# define multi_disconnect NULL
+#endif
+
+
+
/***************************** Device Descriptor ****************************/
+/* Main device */
#define MULTI_VENDOR_NUM 0x0525 /* XXX NetChip */
#define MULTI_PRODUCT_NUM 0xa4ab /* XXX */
-
-static struct usb_device_descriptor device_desc = {
- .bLength = sizeof device_desc,
+static struct usb_device_descriptor multi_device_desc = {
+ .bLength = sizeof multi_device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
@@ -136,7 +162,31 @@ static struct usb_device_descriptor device_desc = {
#endif
};
+/* Install mode */
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+
+#define MULTI_INSTALL_VENDOR_NUM 0x0525 /* XXX NetChip */
+#define MULTI_INSTALL_PRODUCT_NUM 0xa4ad /* XXX */
+
+static struct usb_device_descriptor install_mode_device_desc = {
+ .bLength = sizeof install_mode_device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+ .bcdUSB = cpu_to_le16(0x0200),
+
+ .bDeviceClass = USB_CLASS_MASS_STORAGE,
+ .bDeviceSubClass = USB_SC_SCSI,
+ .bDeviceProtocol = USB_PR_BULK,
+ /* Vendor and product id can be overridden by module parameters. */
+ .idVendor = cpu_to_le16(MULTI_INSTALL_VENDOR_NUM),
+ .idProduct = cpu_to_le16(MULTI_INSTALL_PRODUCT_NUM),
+ .bNumConfigurations = 1,
+};
+
+#endif
+
+/* Other descs */
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &(struct usb_otg_descriptor){
.bLength = sizeof(struct usb_otg_descriptor),
@@ -150,9 +200,17 @@ static const struct usb_descriptor_header *otg_desc[] = {
};
+/* Strings */
enum {
MULTI_STRING_MANUFACTURER_IDX,
MULTI_STRING_PRODUCT_IDX,
+ MULTI_STRING_FIRST_CFG_IDX,
+#if defined CONFIG_USB_G_MULTI_RNDIS && defined CONFIG_USB_G_MULTI_ECM
+ MULTI_STRING_SECOND_CFG_IDX,
+#endif
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ MULTI_STRING_INSTALL_MODE_IDX,
+#endif
};
static char manufacturer[50];
@@ -160,24 +218,42 @@ static char manufacturer[50];
static struct usb_string strings_dev[] = {
[MULTI_STRING_MANUFACTURER_IDX].s = manufacturer,
[MULTI_STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [MULTI_STRING_FIRST_CFG_IDX].s = 'First Configuration',
+#if defined CONFIG_USB_G_MULTI_RNDIS && defined CONFIG_USB_G_MULTI_ECM
+ [MULTI_STRING_SECOND_CFG_IDX].s = 'Second Configuration',
+#endif
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ [MULTI_STRING_INSTALL_MODE_IDX].s = 'Install Mode [NoCD]',
+#endif
{ } /* end of list */
};
-static struct usb_gadget_strings *dev_strings[] = {
- &(struct usb_gadget_strings){
- .language = 0x0409, /* en-us */
- .strings = strings_dev,
+/* The driver */
+static struct usb_composite_driver multi_driver = {
+ .name = 'g_multi',
+ .dev = &multi_device_desc,
+ .strings = (struct usb_gadget_strings *[]) {
+ &(struct usb_gadget_strings) {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+ },
+ NULL,
},
- NULL,
+ .bind = multi_bind,
+ .disconnect = multi_disconnect,
+ .suspend = multi_disconnect,
};
-
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+# define device_desc (*(struct usb_device_descriptor *)multi_driver.dev)
+#else
+# define device_desc multi_device_desc
+#endif
/****************************** Configurations ******************************/
-
-static __ref int first_do_config(struct usb_configuration *c)
+static int first_do_config(struct usb_configuration *c)
{
int ret;
@@ -186,36 +262,51 @@ static __ref int first_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
+ /* First configuration can have either RNDIS or ECM. This
+ * depends on wthether RNDIS is turned on. If it is then
+ * first config is always RNDIS because even if ECM is on as
+ * well it is the second config. */
#ifdef CONFIG_USB_G_MULTI_RNDIS
ret = rndis_bind_config(c, hostaddr);
#else
ret = ecm_bind_config(c, hostaddr);
#endif
- if (ret < 0)
+ if (unlikely(ret < 0))
return ret;
ret = acm_bind_config(c, 0);
- if (ret < 0)
+ if (unlikely(ret < 0))
return ret;
- ret = fsg_bind_config(c->cdev, c, &fsg_common);
- if (ret < 0)
- return ret;
+ if (have_fsg) {
+ /* We need to check if we want mass storage since it
+ * may have been forced on by the install mode even
+ * though user does not want it in the proper USB
+ * configurations. */
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
+ if (unlikely(ret < 0))
+ return ret;
+ }
return 0;
}
-static struct usb_configuration first_config_driver = {
- .label = 'First Configuration',
- .bind = first_do_config,
- .bConfigurationValue = 1,
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
-
+static int add_first_config(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration driver = {
+ .bind = first_do_config,
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ driver.iConfiguration = strings_dev[MULTI_STRING_FIRST_CFG_IDX].id;
+ driver.label = strings_dev[MULTI_STRING_FIRST_CFG_IDX].s;
+ return usb_add_config(cdev, &driver);
+}
#if defined CONFIG_USB_G_MULTI_RNDIS && defined CONFIG_USB_G_MULTI_ECM
-static __ref int second_do_config(struct usb_configuration *c)
+static int second_do_config(struct usb_configuration *c)
{
int ret;
@@ -225,144 +316,425 @@ static __ref int second_do_config(struct usb_configuration *c)
}
ret = ecm_bind_config(c, hostaddr);
- if (ret < 0)
+ if (unlikely(ret < 0))
return ret;
ret = acm_bind_config(c, 0);
- if (ret < 0)
+ if (unlikely(ret < 0))
return ret;
- ret = fsg_bind_config(c->cdev, c, &fsg_common);
- if (ret < 0)
- return ret;
+ if (have_fsg) {
+ /* We need to check if we want mass storage since it
+ * may have been forced on by the install mode even
+ * though user does not want it in the proper USB
+ * configurations. */
+ ret = fsg_bind_config(c->cdev, c, &fsg_common);
+ if (unlikely(ret < 0))
+ return ret;
+ }
return 0;
}
-static struct usb_configuration second_config_driver = {
- .label = 'Second Configuration',
- .bind = second_do_config,
- .bConfigurationValue = 2,
- .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
-};
+static int add_second_config(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration driver = {
+ .bind = second_do_config,
+ .bConfigurationValue = 2,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ driver.iConfiguration = strings_dev[MULTI_STRING_FIRST_CFG_IDX].id;
+ driver.label = strings_dev[MULTI_STRING_FIRST_CFG_IDX].s;
+ return usb_add_config(cdev, &driver);
+}
+
+#else
+
+static int add_second_config(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+#endif
+
+
+/********************************* Worker ********************************/
+
+#if defined CONFIG_USB_G_MULTI_INSTALL
+
+#ifdef MODULE
+static unsigned multi_exiting;
+#else
+# define multi_exiting false
+#endif
+
+
+static void multi_worker_func(struct work_struct *work)
+{
+ /* Make sure, the next state is read corretly. */
+ smp_rmb();
+
+ /* multi_exit() has been called -- no need to do anything. */
+ if (multi_exiting)
+ return;
+
+ /* Switch only if anything actually changes. */
+ if (!test_bit(0, &multi_initialised))
+ goto unregistered;
+ if (install_mode != next_install_mode)
+ goto registered;
+
+ /* Ther's no reason to re-enumerate. */
+ return;
+
+
+registered:
+ /* Unregister the driver to force re-enumeration. */
+ multi_unregister();
+ msleep(5);
+
+unregistered:
+ /* While we were waiting the next state could change, so make
+ * sure we are reading the changed state. This is not critical
+ * since another worker will be scheduled anyways (see
+ * multi_worker_schedule()) but we could avoid unnecesary
+ * switch. On the other hand this barier is critical for
+ * cheking multi_exiting, read further. */
+ smp_rmb();
+
+ /* As we were waiting, multi_exit() has been called. */
+ if (multi_exiting)
+ return;
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ install_mode = next_install_mode;
#endif
+ multi_register();
+}
+
+/* Configuration switching can be requested from different contexts so
+ * to avoid any troubles which may arrise from the fact that IRQs are
+ * disabled, USB functions are in unknown state, etc. we introduce
+ * a worker which does all that. It also allows the job to be done
+ * after some delay. For instance after eject let the mass storage
+ * function settle down. */
+static DECLARE_DELAYED_WORK(multi_worker, multi_worker_func);
+
+static void multi_worker_exit(void)
+{
+ multi_exiting = 1;
+ /* See description of the usage of smp_rmb() in
+ * multi_worker_func(). */
+ smp_wmb();
+ cancel_delayed_work_sync(&multi_worker);
+}
+
+static void multi_worker_schedule(void)
+{
+ /* Make sure the new stats is written before worker starts. */
+ smp_wmb();
+ /* Cancel or wait for completion if worker is scheduled. */
+ cancel_delayed_work(&multi_worker);
+ /* Run the worker with a 1/64 of a second (~15 ms) delay to
+ * let everything settle up. */
+ schedule_delayed_work(&multi_worker, HZ >> 6);
+}
+
+# define __dyn_init
+# define __dyn_ref
+# define __dyn_exit
+#else
+# define __dyn_init __init
+# define __dyn_ref __ref
+# define __dyn_exit __exit
+# define multi_worker_exit() do { } while (0)
+#endif
+
+
+/****************************** Install Mode *****************************/
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+
+static int install_mode_do_config(struct usb_configuration *c)
+{
+ if (gadget_is_otg(c->cdev->gadget)) {
+ c->descriptors = otg_desc;
+ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ return fsg_bind_config(c->cdev, c, &fsg_common);
+}
+
+static int add_install_mode_config(struct usb_composite_dev *cdev)
+{
+ static struct usb_configuration driver = {
+ .bind = install_mode_do_config,
+ .bConfigurationValue = 1,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ };
+
+ driver.iConfiguration = strings_dev[MULTI_STRING_INSTALL_MODE_IDX].id;
+ driver.label = strings_dev[MULTI_STRING_INSTALL_MODE_IDX].s;
+ return usb_add_config(cdev, &driver);
+}
+
+
+/* Jiffies of the last eject request on LUN 0. */
+static unsigned long multi_eject_jiffies;
+
+static int multi_eject(struct fsg_common *common,
+ struct fsg_lun *lun, int num)
+{
+ if (num)
+ return 0;
+
+ multi_eject_jiffies = jiffies;
+ next_install_mode = 0;
+ multi_worker_schedule();
+
+ return 1; /* Prevent realy unmounting the device */
+}
+
+static void multi_disconnect(struct usb_composite_dev *cdev)
+{
+ printk(KERN_INFO 'multi_disconnect()n');
+
+ /* Change back to install mode only if there was an eject
+ * (this is checked by looking if multi_eject_jiffies is
+ * non-zero), we are not switching to install mode already (no
+ * point in doing anything if next_install_mode is aleady one)
+ * and at least 10 seconds passed since last eject. */
+ /* Funky stuff may happen when jiffies wrap but we do not
+ * care. */
+ if (multi_eject_jiffies && !next_install_mode &&
+ jiffies >= multi_eject_jiffies + 10 * HZ) {
+ next_install_mode = 1;
+ multi_worker_schedule();
+ }
+}
+
+#else
+
+static int add_install_mode_config(struct usb_composite_dev *cdev)
+{
+ return 0;
+}
+
+#endif
/****************************** Gadget Bind ******************************/
+#if defined CONFIG_USB_G_MULTI_MSF || defined CONFIG_USB_G_MULTI_INSTALL
-static int __ref multi_bind(struct usb_composite_dev *cdev)
+static int __dyn_init multi_fsg_setup(struct usb_composite_dev *cdev)
{
- struct usb_gadget *gadget = cdev->gadget;
- int status, gcnum;
+ struct fsg_config cfg;
+ void *ret;
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ /* In install mode, make the first logical unit a read
+ * only removable CD-ROM. In addition if mass storage
+ * is used only for install mode, sot number of
+ * logical units to 1. */
+ if (!have_fsg)
+ fsg_mod_data.luns = 1;
+ fsg_mod_data.ro[0] = 1;
+ fsg_mod_data.removable[0] = 1;
+ fsg_mod_data.cdrom[0] = 1;
+ fsg_mod_data.removable_count =
+ max(fsg_mod_data.removable_count, 1u);
+#endif
- if (!can_support_ecm(cdev->gadget)) {
- dev_err(&gadget->dev, 'controller '%s' not usablen',
- gadget->name);
- return -EINVAL;
+ fsg_config_from_params(&cfg, &fsg_mod_data);
+
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ {
+ static const struct fsg_operations ops = {
+ .pre_eject = multi_eject,
+ };
+ cfg.ops = &ops;
}
+#endif
+
+ ret = fsg_common_init(&fsg_common, cdev, &cfg);
+ return unlikely(IS_ERR(ret)) ? PTR_ERR(ret) : 0;
+}
+
+#endif
+
+static int __dyn_init multi_setup(struct usb_composite_dev *cdev)
+{
+ int ret;
+
+#if defined CONFIG_USB_G_MULTI_RNDIS || defined CONFIG_USB_G_MULTI_ECM
/* set up network link layer */
- status = gether_setup(cdev->gadget, hostaddr);
- if (status < 0)
- return status;
+ if (!test_and_set_bit(1, &multi_initialised)) {
+ ret = gether_setup(cdev->gadget, hostaddr);
+ if (unlikely(ret < 0)) {
+ clear_bit(1, &multi_initialised);
+ goto fail;
+ }
+ }
+#endif
+#if defined CONFIG_USB_G_MULTI_ACM
/* set up serial link layer */
- status = gserial_setup(cdev->gadget, 1);
- if (status < 0)
- goto fail0;
+ if (!test_and_set_bit(2, &multi_initialised)) {
+ ret = gserial_setup(cdev->gadget, 1);
+ if (unlikely(ret < 0)) {
+ clear_bit(2, &multi_initialised);
+ goto fail;
+ }
+ }
+#endif
- /* set up mass storage function */
- {
- void *retp;
- retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
- if (IS_ERR(retp)) {
- status = PTR_ERR(retp);
- goto fail1;
+#if defined CONFIG_USB_G_MULTI_MSF || defined CONFIG_USB_G_MULTI_INSTALL
+ /* set up mass storage */
+ if (!test_and_set_bit(3, &multi_initialised)) {
+ ret = multi_fsg_setup(cdev);
+ if (unlikely(ret < 0)) {
+ clear_bit(3, &multi_initialised);
+ goto fail;
}
}
+#endif
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, 'controller '%s' not recognizedn', gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
+ return 0;
+
+fail:
+ multi_cleanup();
+ return ret;
+}
+
+static void multi_cleanup(void)
+{
+#if defined CONFIG_USB_G_MULTI_MSF || defined CONFIG_USB_G_MULTI_INSTALL
+ if (test_and_clear_bit(3, &multi_initialised))
+ fsg_common_put(&fsg_common);
+#endif
+
+#if defined CONFIG_USB_G_MULTI_ACM
+ if (test_and_clear_bit(2, &multi_initialised))
+ gserial_cleanup();
+#endif
+
+#if defined CONFIG_USB_G_MULTI_RNDIS || defined CONFIG_USB_G_MULTI_ECM
+ if (test_and_clear_bit(1, &multi_initialised))
+ gether_cleanup();
+#endif
+}
+
+
+static int __dyn_ref multi_bind(struct usb_composite_dev *cdev)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
+
+ if (!can_support_ecm(cdev->gadget)) {
+ dev_err(&gadget->dev, 'controller '%s' not usablen',
+ gadget->name);
+ return -EINVAL;
}
- /* allocate string descriptor numbers */
+ /* Set up functions */
+ status = multi_setup(cdev);
+ if (unlikely(status < 0))
+ return status;
- snprintf(manufacturer, sizeof manufacturer, '%s %s with %s',
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
+ /* allocate string descriptor numbers */
+ if (!*manufacturer)
+ snprintf(manufacturer, sizeof manufacturer, '%s %s with %s',
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
- goto fail2;
+ goto fail;
+
+ printk(KERN_INFO 'install_mode = %dn', install_mode);
+ /* register configurations */
+ if (install_mode) {
+ status = add_install_mode_config(cdev);
+ } else {
+ status = add_first_config(cdev);
+ if (unlikely(status < 0))
+ goto fail;
+
+ status = add_second_config(cdev);
+ if (unlikely(status < 0))
+ goto fail;
+ }
+
+ /* Fill the rest of the device descriptor */
device_desc.iManufacturer =
strings_dev[MULTI_STRING_MANUFACTURER_IDX].id;
device_desc.iProduct =
strings_dev[MULTI_STRING_PRODUCT_IDX].id;
- /* register configurations */
- status = usb_add_config(cdev, &first_config_driver);
- if (unlikely(status < 0))
- goto fail2;
-
-#if defined CONFIG_USB_G_MULTI_RNDIS && defined CONFIG_USB_G_MULTI_ECM
- status = usb_add_config(cdev, &second_config_driver);
- if (unlikely(status < 0))
- goto fail2;
-#endif
+ status = usb_gadget_controller_number(cdev->gadget);
+ device_desc.bcdDevice =
+ cpu_to_le16(0x300 | (status < 0 ? 0x99 : status));
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC 'n');
- fsg_common_put(&fsg_common);
return 0;
/* error recovery */
-fail2:
- fsg_common_put(&fsg_common);
-fail1:
- gserial_cleanup();
-fail0:
- gether_cleanup();
+fail:
+ multi_cleanup();
return status;
}
-static int __exit multi_unbind(struct usb_composite_dev *cdev)
+
+/*************************** Other init/exit ****************************/
+
+static int __dyn_init multi_register(void)
{
- gserial_cleanup();
- gether_cleanup();
- return 0;
-}
+ int ret = 0;
+ if (!test_and_set_bit(0, &multi_initialised)) {
+#ifdef CONFIG_USB_G_MULTI_INSTALL
+ multi_driver.dev = install_mode
+ ? &install_mode_device_desc
+ : &multi_device_desc;
+#endif
-/****************************** Some noise ******************************/
+ ret = usb_composite_register(&multi_driver);
+ if (unlikely(ret)) {
+ clear_bit(0, &multi_initialised);
+ printk(KERN_ERR
+ 'g_multi: failed registering the driver: %dn',
+ ret);
+ }
+ }
+ return ret;
+}
-static struct usb_composite_driver multi_driver = {
- .name = 'g_multi',
- .dev = &device_desc,
- .strings = dev_strings,
- .bind = multi_bind,
- .unbind = __exit_p(multi_unbind),
-};
+static void __dyn_exit multi_unregister(void)
+{
+ if (test_and_clear_bit(0, &multi_initialised))
+ usb_composite_unregister(&multi_driver);
+}
-static int __init multi_init(void)
+static __init int multi_init(void)
{
- return usb_composite_register(&multi_driver);
+ return multi_register();
}
module_init(multi_init);
-static void __exit multi_exit(void)
+static __exit void multi_exit(void)
{
- usb_composite_unregister(&multi_driver);
+ multi_worker_exit();
+ multi_unregister();
+ multi_cleanup();
}
module_exit(multi_exit);
--
1.7.1

Drivers Multifunction Composite Gadget Price

--
To unsubscribe from this list: send the line 'unsubscribe linux-kernel' in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/