import math import ConfigGenerator import StructGenerator from enum import Enum # Descriptor type class USB_DescType(Enum): UD_Device = 1 UD_Configuration = 2 UD_String = 3 UD_Interface = 4 UD_Endpoint = 5 UD_DeviceQualifier = 6 # NOT FULL! class USB_DescriptorSize(Enum): Header = 2 Configuration = 9 Interface = 9 Endpoint = 7 Device = 18 DeviceQualifier = 9 # base class for descriptor generator class Descriptor(StructGenerator.StructRecord): QUALIFIERS = "const" def __init__(self, name, ctype, content, comment=None): super().__init__(name, ctype, content, comment) self.qualifiers = Descriptor.QUALIFIERS self.typedef = ctype[8:] self.attribute = "packed" # generate content for .c file def gen_c(self): return "{:s} {:s}".format(self.qualifiers, self.print_assigment()) # generate content for .h file def gen_h(self, print_typedef=False): decl = "extern {:s} {:s}".format(self.qualifiers, self.print_declaration()) tdef = "" if print_typedef: tdef = "{:s}\n".format(self.print_typedef()) return tdef + decl # ----------------------------- class DeviceDescriptor(Descriptor): def __init__(self, dev, str_mgr: ConfigGenerator.StringManager): # add strings to the string manager str_mgr.add_string("vendor_string", dev["vendor_string"]) str_mgr.add_string("product_string", dev["product_string"]) str_mgr.add_string("serial_number", dev["serial_number"]) # generate device descriptor numOfConfs = len(dev["configurations"]) # get number of configurations records = [ ["bLength", "u8", USB_DescriptorSize.Device.value], ["bDescriptorType", "u8", "UD_Device", True], ["bcdUSB", "u16", 0x0200], ["bDeviceClass", "u8", dev["class"]], ["bDeviceSubclass", "u8", dev["subclass"]], ["bDeviceProtocol", "u8", dev["protocol_code"]], ["bMaxPacketSize0", "u8", dev["max_ep0_packet_size"]], ["idVendor", "u16", dev["vendor_id"]], ["idProduct", "u16", dev["product_id"]], ["bcdDevice", "u16", dev["device_release_bcd"]], ["iManufacturer", "u8", str_mgr.get_numeric_id("vendor_string")], ["iProduct", "u8", str_mgr.get_numeric_id("product_string")], ["iSerialNumber", "u8", str_mgr.get_numeric_id("serial_number")], ["bNumConfigurations", "u8", numOfConfs]] super().__init__("devDesc", "struct _USB_DeviceDesc", StructGenerator.RecordFactory.createa(records), comment="Device Descriptor") class DeviceQualifierDescriptor(Descriptor): def __init__(self, dev): # generate device descriptor numOfConfs = len(dev["configurations"]) # get number of configurations records = [ ["bLength", "u8", USB_DescriptorSize.DeviceQualifier.value], ["bDescriptorType", "u8", "UD_DeviceQualifier", True], ["bcdUSB", "u16", 0x0200], ["bDeviceClass", "u8", dev["class"]], ["bDeviceSubclass", "u8", dev["subclass"]], ["bDeviceProtocol", "u8", dev["protocol_code"]], ["bMaxPacketSize", "u8", dev["max_ep0_packet_size"]], ["bNumConfigurations", "u8", numOfConfs]] super().__init__("devQualDesc", "struct _USB_DeviceQualifierDesc", StructGenerator.RecordFactory.createa(records), comment="Device Qualifier descriptor") class StringDescriptor(Descriptor): def __init__(self, text_langid, name): if isinstance(text_langid, str): records = [ ["bLength", "u8", USB_DescriptorSize.Header.value + 2 * len(text_langid)], ["bDescriptorType", "u8", "UD_String", True], ["bString", "str", text_langid]] elif isinstance(text_langid, int): records = [ ["bLength", "u8", USB_DescriptorSize.Header.value + 2], ["bDescriptorType", "u8", "UD_String", True], ["bString", "u16", text_langid]] super().__init__(name, "struct _USB_StrDesc_{:s}".format(name), StructGenerator.RecordFactory.createa(records), comment="String descriptor") class ConfigurationDescriptor(Descriptor): def __init__(self, name, conf, total_length): # fill in attributes attributes = "USB_CONFIG_ATTR_USB1_1_FLAG" if conf["is_bus_powered"] == False: attributes += " | USB_CONFIG_ATTR_SELF_POWERED" elif conf["can_wake_up_host"] == True: attributes += " | USB_CONFIG_ATTR_REMOTE_WKUP" records = [ ["bLength", "u8", USB_DescriptorSize.Configuration.value], ["bDescriptorType", "u8", "UD_Configuration", True], ["wTotalLength", "u16", total_length, True], ["bNumInterfaces", "u8", len(conf["interfaces"])], ["bConfigurationValue", "u8", conf["id"]], ["iConfiguration", "u8", 0], ["bmAttributes", "u8", attributes, True], ["bMaxPower", "u8", math.ceil(conf["max_power_mA"] / 2)]] super().__init__(name, "struct _USB_ConfigurationDesc", StructGenerator.RecordFactory.createa(records), comment="Configuration descriptor") class InterfaceDescriptor(Descriptor): def __init__(self, name, intf): self.ep_count = len(intf["endpoints"]) records = [ ["bLength", "u8", USB_DescriptorSize.Interface.value], ["bDescriptorType", "u8", "UD_Interface", True], ["bInterfaceNumber", "u8", intf["id"]], ["bAlternateSetting", "u8", 0], ["bNumEndpoints", "u8", self.ep_count], ["bInterfaceClass", "u8", intf["class"]], ["bInterfaceSubclass", "u8", intf["subclass"]], ["bInterfaceProtocol", "u8", intf["protocol_code"]], ["iInterface", "u8", 0]] super().__init__(name, "struct _USB_InterfaceDesc", StructGenerator.RecordFactory.createa(records), comment="Interface descriptor : {:d}".format(intf["id"])) class EndpointDescriptor(Descriptor): def __init__(self, name, ep): # endpoint direction endpoint_dir = "" conf_ep_dir_lower = ep["direction"].lower() if conf_ep_dir_lower == "in": endpoint_dir = "USB_IN" elif conf_ep_dir_lower == "out": endpoint_dir = "USB_OUT" # transfer type transfer_type = "" conf_ep_transfer_type_lower = ep["transfer_type"].lower() if conf_ep_transfer_type_lower == "control": transfer_type = "UT_Control" elif conf_ep_transfer_type_lower == "interrupt": transfer_type = "UT_Interrupt" elif conf_ep_transfer_type_lower == "bulk": transfer_type = "UT_Bulk" elif conf_ep_transfer_type_lower == "isochronous": transfer_type = "UT_Isochronous" records = [ ["bLength", "u8", USB_DescriptorSize.Endpoint.value], ["bDescriptorType", "u8", "UD_Endpoint", True], ["bEndpointAddress", "u8", "({:s} << 7) | ({:d})".format(endpoint_dir, ep["n"]), True], ["bmAttributes", "u8", transfer_type, True], ["wMaxPacketSize", "u16", ep["max_packet_size"]], ["bInterval", "u8", ep["service_interval"]] ] super().__init__(name, "struct _USB_EndpointDesc", StructGenerator.RecordFactory.createa(records), comment="Endpoint descriptor : {:d} {:s}".format(ep["n"], ep["direction"])) # -------------------------- # ------ CDC-related ------- # -------------------------- class HeaderFunctionalDescriptor(Descriptor): def __init__(self, name, hfd, str_mgr): records = [ ["bLength", "u8", 0x05], ["bDescriptorType", "u8", 0x24], ["bDescriptorSubType", "u8", 0x00], ["bcdCDC", "u16", 0x0110]] super().__init__(name, "struct _USB_HeaderFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Header Functional descriptor") class AbstractControlManagementFunctionalDescriptor(Descriptor): def __init__(self, name, acmfd, str_mgr): records = [ ["bLength", "u8", 0x04], ["bDescriptorType", "u8", 0x24], ["bDescriptorSubType", "u8", 0x02], ["bmCapabilities", "u8", 0x02]] # TODO: no capabilities super().__init__(name, "struct _USB_AbstractControlManagementFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Abstract Control Management Functional descriptor") class UnionFunctionalDescriptor(Descriptor): def __init__(self, name, ufd, str_mgr): records = [ ["bLength", "u8", 0x05], ["bDescriptorType", "u8", 0x24], ["bDescriptorSubType", "u8", 0x06], ["bMasterInterface", "u8", ufd["master_interface"]], # TODO: control interface ["bSlaveInterface0", "u8", ufd["slave_interface"]]] # TODO: data interface super().__init__(name, "struct _USB_UnionFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Union Functional descriptor") class CallManagementFunctionalDescriptor(Descriptor): def __init__(self, name, cmfd, str_mgr): records = [ ["bLength", "u8", 0x05], ["bDescriptorType", "u8", 0x24], ["bDescriptorSubType", "u8", 0x01], ["bmCapabilities", "u8", 0x00], ["dDataInterface", "u8", cmfd["data_interface"]]] super().__init__(name, "struct _USB_CallManagementFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Call Management Functional descriptor") class EthernetNetworkingFunctionalDescriptor(Descriptor): def __init__(self, name, enfd, str_mgr: ConfigGenerator.StringManager): # add MAC address to the string manager str_mgr.add_string("mac_address", enfd["mac_address"]) # populate fields records = [ ["bLength", "u8", 0x0D], ["bDescriptorType", "u8", 0x24], ["bDescriptorSubType", "u8", 0x0F], ["iMACAddress", "u8", str_mgr.get_numeric_id("mac_address")], # MAC address is stored as a string ["bmEthernetStatistics", "u32", 0x00], # no collection of statistics ["wMaxSegmentSize", "u16", enfd["max_segment_size"]], ["wNumberMCFilters", "u16", 0x00], # we don't support multicast filters ["bNumberPowerFilters", "u8", 0x00], # no pattern for waking up the host ] super().__init__(name, "struct _USB_EthernetNetworkingFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Ethernet Networking Functional descriptor") class DescriptorFactory: REGISTRY = { "hfd": HeaderFunctionalDescriptor, "acmfd": AbstractControlManagementFunctionalDescriptor, "ufd": UnionFunctionalDescriptor, "cmfd": CallManagementFunctionalDescriptor, "enfd": EthernetNetworkingFunctionalDescriptor } @staticmethod def generate(name, varname, data, str_mgr): return DescriptorFactory.REGISTRY[name](varname, data, str_mgr)