We need a kernel driver that lies between the slave (gadget, MUSB) port and the master (EHCI) port. This "proxy" driver will be registered both as a USB device driver, and as a USB gadget driver. It will then "simply" forward USB packets back and forth between the 2 interfaces.
First, the sniffed device is connected to the master port of the BeagleBoard. The Linux kernel assign an address to it, reads its descriptors, select a configuration, and tries to find a driver for the new device. Reading the descriptors and assigning the address are probably fine (it needs to be done anyway, and it should be a rather standard operation), but we probably do not want to set a configuration, and clearly do not want any driver to initialize the device.
Then, we want to bind the device to our proxy driver (this can be done using echo [device] > /sys/bus/usb/drivers/xxx/[un]bind ).
The proxy driver will then copy the descriptors, and activate the slave port. The host PC enumerates the new device, sets an address to it, and obtains the copied descriptors (and hopefully believes that it is connected to the actual device).
Some additional control transfers (i.e. endpoint 0) may happen, such as configuration selection. These need to be forwarded to the real device.
To be able to actually use the device, other endpoints (in addition to the mandatory endpoint 0) need to be configured in the gadget driver, and registered on the device side, so that the data can be forwarded. The required endpoints can be obtained from the device descriptor (below, the (very simple) descriptor for a mouse):
Bus 006 Device 003: ID 046d:c018 Logitech, Inc. Optical Wheel Mouse
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0 (Defined at Interface level)
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x046d Logitech, Inc.
idProduct 0xc018 Optical Wheel Mouse
bcdDevice 43.01
iManufacturer 1 Logitech
iProduct 2 USB Optical Mouse
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 34
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0xa0
(Bus Powered)
Remote Wakeup
MaxPower 100mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 1 Boot Interface Subclass
bInterfaceProtocol 2 Mouse
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.11
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 52
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0005 1x 5 bytes
bInterval 10
Device Status: 0x0000
(Bus Powered)
From that descriptor, we can see that we need to configure the gadget driver to handle outgoing interrupt transfers on endpoint 1, and that we need to listen for interrupt transfers on endpoint 1, coming from the device.
My plan for the following weeks:
- Manually copy the descriptor of a simple device (a mouse/keyboard would do)
- Handle control transfers forwarding
- Connect one endpoint on the slave and master sides (EP 1 IN, interrupt for mouse/keyboard)
- Forward interrupt transfers
- Mice and keyboards are usually Low Speed devices, which is not supported by many gadget controllers (including MUSB), does it hurt if we advertise them as Full Speed devices on the gadget-host link?
- If a device has multiple configurations, an endpoint may have different types and/or directions depending on the configuration, we need to be able to handle that (and intercept SET_CONFIGURATION requests).
- Some devices change descriptor (and disconnect/reconnect) after a firmware load (e.g., FX2-based devices), we need to find a way to handle that, and reattach to the "new" device automatically.