# Copyright 2014 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Linux gadgetfs glue. Exposes a USB gadget using a USB peripheral controller on Linux. The userspace ABI is documented here: https://github.com/torvalds/linux/blob/master/drivers/usb/gadget/inode.c """ import errno import multiprocessing import os import struct from tornado import ioloop import usb_constants import usb_descriptors GADGETFS_NOP = 0 GADGETFS_CONNECT = 1 GADGETFS_DISCONNECT = 2 GADGETFS_SETUP = 3 GADGETFS_SUSPEND = 4 BULK = 0x01 INTERRUPT = 0x02 ISOCHRONOUS = 0x04 USB_TRANSFER_TYPE_TO_MASK = { usb_constants.TransferType.BULK: BULK, usb_constants.TransferType.INTERRUPT: INTERRUPT, usb_constants.TransferType.ISOCHRONOUS: ISOCHRONOUS } IN = 0x01 OUT = 0x02 HARDWARE = { 'beaglebone-black': ( 'musb-hdrc', # Gadget controller name, { 0x01: ('ep1out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x81: ('ep1in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x02: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x82: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x03: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x83: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x04: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x84: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x05: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x85: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x06: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x86: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x07: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x87: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x08: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x88: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x09: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x89: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x0A: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x8A: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x0B: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x8B: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x0C: ('ep2out', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x8C: ('ep2in', BULK | INTERRUPT | ISOCHRONOUS, 512), 0x0D: ('ep13', BULK | INTERRUPT | ISOCHRONOUS, 4096), 0x8D: ('ep13', BULK | INTERRUPT | ISOCHRONOUS, 4096), 0x0E: ('ep14', BULK | INTERRUPT | ISOCHRONOUS, 1024), 0x8E: ('ep14', BULK | INTERRUPT | ISOCHRONOUS, 1024), 0x0F: ('ep15', BULK | INTERRUPT | ISOCHRONOUS, 1024), 0x8F: ('ep15', BULK | INTERRUPT | ISOCHRONOUS, 1024), } ) } class LinuxGadgetfs(object): """Linux gadgetfs-based gadget driver. """ def __init__(self, hardware, mountpoint='/dev/gadget'): """Initialize bindings to the Linux gadgetfs interface. Args: hardware: Hardware type. mountpoint: Gadget filesystem mount point. """ self._chip, self._hw_eps = HARDWARE[hardware] self._ep_dir = mountpoint self._gadget = None self._fd = None # map from bEndpointAddress to hardware ep name and open file descriptor self._ep_fds = {} self._io_loop = ioloop.IOLoop.current() def Create(self, gadget): """Bind a gadget to the USB peripheral controller.""" self._gadget = gadget self._fd = os.open(os.path.join(self._ep_dir, self._chip), os.O_RDWR) buf = ''.join([struct.pack('=I', 0), gadget.GetFullSpeedConfigurationDescriptor().Encode(), gadget.GetHighSpeedConfigurationDescriptor().Encode(), gadget.GetDeviceDescriptor().Encode()]) os.write(self._fd, buf) self._io_loop.add_handler(self._fd, self.HandleEvent, self._io_loop.READ) def Destroy(self): """Unbind the gadget from the USB peripheral controller.""" self.Disconnected() self._io_loop.remove_handler(self._fd) os.close(self._fd) self._gadget = None self._fd = None def IsConfigured(self): return self._gadget is not None def HandleEvent(self, unused_fd, unused_events): buf = os.read(self._fd, 12) event_type, = struct.unpack_from('=I', buf, 8) if event_type == GADGETFS_NOP: print 'NOP' elif event_type == GADGETFS_CONNECT: speed, = struct.unpack('=Ixxxxxxxx', buf) self.Connected(speed) elif event_type == GADGETFS_DISCONNECT: self.Disconnected() elif event_type == GADGETFS_SETUP: request_type, request, value, index, length = struct.unpack( '