summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorScott Shawcroft <scott.shawcroft@gmail.com>2017-10-26 16:53:25 -0700
committerScott Shawcroft <scott.shawcroft@gmail.com>2017-10-30 18:29:20 -0700
commit4aeef100f6d78313e1cc58fb6ef6d6b0da5c879d (patch)
tree65289dee99b86f0fb7fd8ed25ebac6beee4e1e2a /tools
parent189366983366cf5cad214d15070a0a53ccce24a8 (diff)
atmel-samd: More USB polish
* Introduce a python script to generate the USB descriptor instead of a bunch of C macros. In the future, we can use this dynamically in CircuitPython. * Add support for detecting read-only mass storage mounts. Fixes #377
Diffstat (limited to 'tools')
-rw-r--r--tools/usb_descriptor/cdc.py53
-rw-r--r--tools/usb_descriptor/core.py187
2 files changed, 240 insertions, 0 deletions
diff --git a/tools/usb_descriptor/cdc.py b/tools/usb_descriptor/cdc.py
new file mode 100644
index 000000000..4042e7673
--- /dev/null
+++ b/tools/usb_descriptor/cdc.py
@@ -0,0 +1,53 @@
+from . import core
+import struct
+
+class FunctionalDescriptor(core.Descriptor):
+ bDescriptorType = 0x24
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fmt = "<BBB" + self.fmt[3:]
+
+ def __bytes__(self):
+ return struct.pack(self.fmt, self.bLength, self.bDescriptorType, self.bDescriptorSubtype, *self.data)
+
+
+class Header(FunctionalDescriptor):
+ fields = [('bcdCDC', "H", None)]
+ bLength = 0x05
+ bDescriptorSubtype = 0x0
+
+
+class CallManagement(FunctionalDescriptor):
+ fields = [('bmCapabilities', "b", None),
+ ('bDataInterface', "b", None)]
+ bLength = 0x05
+ bDescriptorSubtype = 0x01
+
+
+class AbstractControlManagement(FunctionalDescriptor):
+ fields = [('bmCapabilities', "b", None)]
+ bLength = 0x04
+ bDescriptorSubtype = 0x02
+
+
+class DirectLineManagement(FunctionalDescriptor):
+ fields = [('bmCapabilities', "b", None)]
+ bLength = 0x04
+ bDescriptorSubtype = 0x03
+
+
+class Union(FunctionalDescriptor):
+ fields = [('bMasterInterface', "b", None)]
+ bDescriptorSubtype = 0x06
+
+ def __init__(self, *args, **kwargs):
+ self.bSlaveInterface = kwargs["bSlaveInterface"]
+ super().__init__(*args, **kwargs)
+
+ def __bytes__(self):
+ return super().__bytes__() + bytes(self.bSlaveInterface)
+
+ @property
+ def bLength(self):
+ return 0x4 + len(self.bSlaveInterface)
diff --git a/tools/usb_descriptor/core.py b/tools/usb_descriptor/core.py
new file mode 100644
index 000000000..03d9c3e63
--- /dev/null
+++ b/tools/usb_descriptor/core.py
@@ -0,0 +1,187 @@
+import struct
+
+
+def join_interfaces(*args):
+ interfaces = []
+ base_endpoint_number = 1
+ for interface_set in args:
+ base_interface_number = len(interfaces)
+ for i, interface in enumerate(interface_set):
+ interfaces.append(interface)
+ interface.bInterfaceNumber = interfaces.index(interface)
+ max_endpoint_address = base_endpoint_number
+ for subdescriptor in interface.subdescriptors:
+ if subdescriptor.bDescriptorType == EndpointDescriptor.bDescriptorType:
+ subdescriptor.bEndpointAddress += base_endpoint_number
+ max_endpoint_address = max(max_endpoint_address, subdescriptor.bEndpointAddress & 0xf)
+ base_endpoint_number = max_endpoint_address + 1
+ return interfaces
+
+
+class Descriptor:
+ def __init__(self, *args, **kwargs):
+ self.fmt = ["<B", "B"]
+ for field in self.fields:
+ self.fmt.append(field[1])
+ self.fmt = "".join(self.fmt)
+ if len(args) == 1:
+ self.data = struct.unpack(self.fmt, args[0])
+ if self.data[1] != self.bDescriptorType:
+ raise RuntimeError("Descriptor type doesn't match.")
+ return
+ elif len(args) > 1:
+ raise TypeError("Only one arg or keyword args expected.")
+ elif len(kwargs) == 0:
+ raise TypeError("Only one arg or keyword args expected.")
+
+ self.data = []
+ for field, _, default in self.fields:
+ if field in kwargs:
+ self.data.append(kwargs[field])
+ elif default is not None:
+ self.data.append(default)
+ else:
+ raise ValueError("Missing {} argument.".format(field))
+
+ def __bytes__(self):
+ return struct.pack(self.fmt, self.bLength, self.bDescriptorType, *self.data)
+
+ @property
+ def bDescriptorType(self):
+ return self._bDescriptorType
+
+class EndpointDescriptor(Descriptor):
+ fields = [('bEndpointAddress', "B", None),
+ ('bmAttributes', "B", None),
+ ('wMaxPacketSize', "H", 0x40),
+ ('bInterval', "B", 0)]
+
+ bLength = 0x07
+ bDescriptorType = 0x5
+
+ TYPE_CONTROL = 0b00
+ TYPE_ISOCHRONOUS = 0b01
+ TYPE_BULK = 0b10
+ TYPE_INTERRUPT = 0b11
+
+ DIRECTION_IN = 0x80
+ DIRECTION_OUT = 0x00
+
+ @property
+ def bEndpointAddress(self):
+ return self.data[0]
+
+ @bEndpointAddress.setter
+ def bEndpointAddress(self, value):
+ self.data[0] = value
+
+
+class InterfaceDescriptor(Descriptor):
+ fields = [('bInterfaceNumber', "B", 0),
+ ('bAlternateSetting', "B", 0),
+ ('bNumEndpoints', "B", 0),
+ ('bInterfaceClass', "B", None),
+ ('bInterfaceSubClass', "B", 0),
+ ('bInterfaceProtocol', "B", 0),
+ ('iInterface', "B", 0)]
+
+ bLength = 0x09
+ bDescriptorType = 0x4
+
+ def __init__(self, *args, **kwargs):
+ self.subdescriptors = []
+ if "subdescriptors" in kwargs:
+ self.subdescriptors = kwargs["subdescriptors"]
+ super().__init__(*args, **kwargs)
+
+ def __bytes__(self):
+ endpoint_count = 0
+ subdescriptor_bytes = []
+ for desc in self.subdescriptors:
+ subdescriptor_bytes.append(bytes(desc))
+ if desc.bDescriptorType == EndpointDescriptor.bDescriptorType:
+ endpoint_count += 1
+ subdescriptor_bytes = b"".join(subdescriptor_bytes)
+ self.data[2] = endpoint_count
+ return super().__bytes__() + subdescriptor_bytes
+
+ @property
+ def bInterfaceNumber(self):
+ return self.data[0]
+
+ @bInterfaceNumber.setter
+ def bInterfaceNumber(self, value):
+ self.data[0] = value
+
+class InterfaceAssociationDescriptor(Descriptor):
+ fields = [('bFirstInterface', "B", None),
+ ('bInterfaceCount', "B", None),
+ ('bFunctionClass', "B", None),
+ ('bFunctionSubClass', "B", None),
+ ('bFunctionProtocol', "B", None),
+ ('iFunction', "B", 0)]
+
+ bLength = 0x08
+ bDescriptorType = 0xB
+
+
+class ConfigurationDescriptor(Descriptor):
+ fields = [('wTotalLength', "H", None),
+ ('bNumInterfaces', "B", None),
+ ('bConfigurationValue', "B", 0x1),
+ ('iConfiguration', "B", 0),
+ # bus powered (bit 6), no remote wakeup (bit 5), bit 7 is always 1 and 0-4 are always 0
+ ('bmAttributes', "B", 0x80),
+ # 100 mA by default
+ ('bMaxPower', "B", 50)]
+
+ bLength = 0x09
+ bDescriptorType = 0x2
+
+class DeviceDescriptor(Descriptor):
+ fields = [('bcdUSB', "H", 0x200),
+ ('bDeviceClass', "B", 0xef),
+ ('bDeviceSubClass', "B", 0x02),
+ ('bDeviceProtocol', "B", 0x01),
+ ('bMaxPacketSize0', "B", 0x40),
+ ('idVendor', "H", None),
+ ('idProduct', "H", None),
+ ('bcdDevice', "H", 0x100),
+ ('iManufacturer', "B", None),
+ ('iProduct', "B", None),
+ ('iSerialNumber', "B", None),
+ ('bNumConfigurations', "B", 1)]
+
+ bLength = 0x12
+ bDescriptorType = 0x1
+
+class StringDescriptor:
+ def __init__(self, value):
+ if type(value) == str:
+ self._bString = value.encode("utf-16-le")
+ self._bLength = len(self._bString) + 2
+ elif len(value) > 1:
+ self._bLength = value[0]
+ if value[1] != 3:
+ raise ValueError("Sequence not a StringDescriptor")
+ self._bString = value[2:2+self.bLength]
+
+ def __bytes__(self):
+ return struct.pack("BB{}s".format(len(self._bString)), self.bLength, self.bDescriptorType, self._bString)
+
+ @property
+ def bString(self):
+ return self._bString.decode("utf-16-le")
+
+ @bString.setter
+ def bString(self, value):
+ self._bString = value.encode("utf-16-le")
+ self._bLength = len(self.encoded) + 2
+
+ @property
+ def bDescriptorType(self):
+ return 3
+
+ @property
+ def bLength(self):
+ return self._bLength