* 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 #377crypto-aes
parent
1893669833
commit
4aeef100f6
@ -1 +1 @@
|
||||
Subproject commit e1d02fde5d23a704d33d74e18fcf624eb56132c5
|
||||
Subproject commit 1ca9e1f86921d99f0fbc907698f864a60caf675a
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of the Micro Python project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__
|
||||
#define __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__
|
||||
|
||||
#include "usb/device/usbdc.h"
|
||||
|
||||
struct usbd_descriptors descriptor_bounds;
|
||||
uint8_t* serial_number;
|
||||
uint8_t serial_number_length;
|
||||
|
||||
#endif // __MICROPY_INCLUDED_ATMEL_SAMD_TOOLS_AUTOGEN_USB_DESCRIPTOR_H__
|
@ -0,0 +1,150 @@
|
||||
import argparse
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# path hacking
|
||||
sys.path.append("../../tools")
|
||||
|
||||
from usb_descriptor import core
|
||||
from usb_descriptor import cdc
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate USB descriptors.')
|
||||
parser.add_argument('--manufacturer', type=str,
|
||||
help='manufacturer of the device')
|
||||
parser.add_argument('--product', type=str,
|
||||
help='product name of the device')
|
||||
parser.add_argument('--vid', type=lambda x: int(x, 16),
|
||||
help='vendor id')
|
||||
parser.add_argument('--pid', type=lambda x: int(x, 16),
|
||||
help='product id')
|
||||
parser.add_argument('--serial_number_length', type=int, default=32,
|
||||
help='length needed for the serial number in digits')
|
||||
parser.add_argument('output_file', type=argparse.FileType('w'))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
langid = core.StringDescriptor("\u0409")
|
||||
manufacturer = core.StringDescriptor(args.manufacturer)
|
||||
product = core.StringDescriptor(args.product)
|
||||
serial_number = core.StringDescriptor("serial number. you should fill in a unique serial number here."[:args.serial_number_length])
|
||||
strings = [langid, manufacturer, product, serial_number]
|
||||
|
||||
# vid = 0x239A
|
||||
# pid = 0x8021
|
||||
|
||||
device = core.DeviceDescriptor(
|
||||
idVendor=args.vid,
|
||||
idProduct=args.pid,
|
||||
iManufacturer=strings.index(manufacturer),
|
||||
iProduct=strings.index(product),
|
||||
iSerialNumber=strings.index(serial_number))
|
||||
|
||||
# Interface numbers are interface set local and endpoints are interface local
|
||||
# until core.join_interfaces renumbers them.
|
||||
cdc_interfaces = [
|
||||
core.InterfaceDescriptor(
|
||||
bInterfaceClass=0x2, # Communications Device Class
|
||||
bInterfaceSubClass=0x02, # Abstract control model
|
||||
bInterfaceProtocol=0x01, # Common AT Commands
|
||||
subdescriptors=[
|
||||
# Working 2.x
|
||||
# radix: hexadecimal
|
||||
# 05 24 00 10 01 header
|
||||
# 05 24 01 03 01 call manage
|
||||
# 04 24 02 06 acm
|
||||
# 05 24 06 00 01 union
|
||||
cdc.Header(bcdCDC=0x0110),
|
||||
cdc.CallManagement(bmCapabilities=0x03, bDataInterface=0x01),
|
||||
cdc.AbstractControlManagement(bmCapabilities=0x02),
|
||||
cdc.Union(bMasterInterface=0x00,
|
||||
bSlaveInterface=[0x01]),
|
||||
core.EndpointDescriptor(
|
||||
bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN,
|
||||
bmAttributes=core.EndpointDescriptor.TYPE_INTERRUPT,
|
||||
wMaxPacketSize=0x8,
|
||||
bInterval=10)
|
||||
]
|
||||
),
|
||||
core.InterfaceDescriptor(
|
||||
bInterfaceClass=0x0a,
|
||||
subdescriptors=[
|
||||
core.EndpointDescriptor(
|
||||
bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN,
|
||||
bmAttributes=core.EndpointDescriptor.TYPE_BULK),
|
||||
core.EndpointDescriptor(
|
||||
bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_OUT,
|
||||
bmAttributes=core.EndpointDescriptor.TYPE_BULK)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
msc_interfaces = [
|
||||
core.InterfaceDescriptor(
|
||||
bInterfaceClass=0x08,
|
||||
bInterfaceSubClass=0x06,
|
||||
bInterfaceProtocol=0x50,
|
||||
subdescriptors=[
|
||||
core.EndpointDescriptor(
|
||||
bEndpointAddress=0x0 | core.EndpointDescriptor.DIRECTION_IN,
|
||||
bmAttributes=core.EndpointDescriptor.TYPE_BULK),
|
||||
core.EndpointDescriptor(
|
||||
bEndpointAddress=0x1 | core.EndpointDescriptor.DIRECTION_OUT,
|
||||
bmAttributes=core.EndpointDescriptor.TYPE_BULK)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
interfaces = core.join_interfaces(cdc_interfaces, msc_interfaces)
|
||||
|
||||
cdc_function = core.InterfaceAssociationDescriptor(
|
||||
bFirstInterface=interfaces.index(cdc_interfaces[0]),
|
||||
bInterfaceCount=len(cdc_interfaces),
|
||||
bFunctionClass=0x2, # Communications Device Class
|
||||
bFunctionSubClass=0x2, # Abstract control model
|
||||
bFunctionProtocol=0x1) # Common AT Commands
|
||||
|
||||
configuration = core.ConfigurationDescriptor(
|
||||
wTotalLength=(core.ConfigurationDescriptor.bLength +
|
||||
cdc_function.bLength +
|
||||
sum([len(bytes(x)) for x in interfaces])),
|
||||
bNumInterfaces=len(interfaces))
|
||||
|
||||
descriptor_list = [device, configuration, cdc_function]
|
||||
descriptor_list.extend(interfaces)
|
||||
descriptor_list.extend(strings)
|
||||
|
||||
output_file = args.output_file
|
||||
|
||||
output_file.write("#include <stdint.h>\n\n")
|
||||
output_file.write("#include \"tools/autogen_usb_descriptor.h\"\n\n")
|
||||
output_file.write("uint8_t usb_descriptors[] = {\n")
|
||||
descriptor_length = 0
|
||||
serial_number_offset = None
|
||||
for descriptor in descriptor_list:
|
||||
output_file.write("// " + str(descriptor) + "\n")
|
||||
b = bytes(descriptor)
|
||||
i = 0
|
||||
if descriptor == serial_number:
|
||||
# Add two for the length and descriptor type bytes.
|
||||
serial_number_offset = descriptor_length + 2
|
||||
# This prints each subdescriptor on a separate line.
|
||||
while i < len(b):
|
||||
length = b[i]
|
||||
for j in range(length):
|
||||
output_file.write("0x{:02x}, ".format(b[i + j]))
|
||||
output_file.write("\n")
|
||||
i += length
|
||||
descriptor_length += length
|
||||
|
||||
output_file.write("\n")
|
||||
output_file.write("};\n\n")
|
||||
output_file.write("struct usbd_descriptors descriptor_bounds = " +
|
||||
"{usb_descriptors," +
|
||||
" usb_descriptors + sizeof(usb_descriptors)};\n")
|
||||
output_file.write("uint8_t* serial_number = usb_descriptors + " +
|
||||
str(serial_number_offset) + ";\n")
|
||||
output_file.write("uint8_t serial_number_length = " +
|
||||
str(args.serial_number_length) + ";\n")
|
||||
|
||||
output_file.close()
|
@ -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)
|
@ -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
|
Loading…
Reference in new issue