import subprocess import datetime import Descriptor import Descriptor as desc import StructGenerator # class for managing string descriptors class StringManager: def __init__(self): self.strings = {} self.num_ids = {} # add a string to the manager def add_string(self, text_id, text): new_id = len(self.strings) + 1 self.strings[text_id] = {"text": text, "id": new_id} self.num_ids[new_id] = text_id return new_id # add empty placeholders, reserve indices def add_placeholders(self, text_id_array): for text_id in text_id_array: self.add_string(text_id, "") # retrieve text based on text or numeric id def get_text(self, id): if isinstance(id, str): return self.strings[id]["text"] elif isinstance(id, int): return self.strings[self.num_ids[id]]["text"] else: return None # change existing entry def change_text(self, text_id, text): self.strings[text_id]["text"] = text # get numeric id based on text id def get_numeric_id(self, text_id): return self.strings[text_id]["id"] # retrieve number of entries def get_size(self): return len(self.strings) # get text ids def get_text_ids(self): return self.strings.keys() class ConfigGenerator: def __init__(self, config): # declaration and definition file contents self.config = config self.h = "" self.c = "" self.DIVIDER = "\n\n// ---------------------------\n\n" # constants self.VAR_FULL_CONF_DESCS = "fullConfDescs" self.VAR_LANG_ID_STRDESC = "langid_str_desc" # self.VAR_MANUF_STRDESC = "manufacturer_str_desc" # self.VAR_PRODUCT_STRDESC = "product_str_desc" # self.VAR_SERIAL_STRDESC = "serial_str_desc" # string management self.str_mgr = StringManager() # string_fields = [ # "vendor_string", # "product_string", # "serial_number" # ] # self.str_mgr.add_placeholders(string_fields) self.LANG_ID_US_EN = "0x0409" self.max_config_str_desc_size = ( 0 # maximal size (wLength) of config descriptors ) self.max_endpoint_index = ( 0 # maximum endpoint index used in at least one of the configurations ) self.max_endpoint_count = 0 # maximum endpoint count across all configurations # add macro to header file def add_def_macro(self, key: str, value: str = "", comment=""): self.h += "#define " + key if value != "": self.h += " (" + value + ")" if comment != "": self.h += " // " + comment self.h += "\n" # save generated config def save(self, target_dir): DECLARATIONS = "usb_desc.h" DEFINITIONS = "usb_desc.c" DECLARATIONS_FULLPATH = target_dir + "/" + DECLARATIONS DEFINITIONS_FULLPATH = target_dir + "/" + DEFINITIONS INFO_HEADER = "//------------\n// Descriptors generated by flatUSB Descriptor Generator by Epagris on " + (str)(datetime.date.today()) + ".\n//------------\n\n" with open(DEFINITIONS_FULLPATH, "w") as f_c: f_c.write(INFO_HEADER) f_c.write('#include "' + DECLARATIONS + '"\n\n') f_c.write(self.c) include_guard_tag = "_" + DECLARATIONS.replace(".", "_").upper() with open(DECLARATIONS_FULLPATH, "w") as f_h: f_h.writelines( ( INFO_HEADER, "#ifndef " + include_guard_tag + "\n", "#define " + include_guard_tag + "\n\n", '#include "usb_descriptor_common.h"\n\n', ) ) f_h.write(self.h) f_h.writelines(("\n\n#endif // " + include_guard_tag)) subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DECLARATIONS_FULLPATH]) subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DEFINITIONS_FULLPATH]) # generate Device descriptor def generate_device_descriptor(self): dev_desc = desc.DeviceDescriptor(self.config, self.str_mgr) self.c += dev_desc.gen_c() self.h += dev_desc.gen_h() # generate Device Qualifier descriptor def generate_device_qualifier_descriptor(self): dev_qual_desc = desc.DeviceQualifierDescriptor(self.config) self.c += dev_qual_desc.gen_c() self.h += dev_qual_desc.gen_h() # generate endpoint descriptor assigned to an interface descriptor def generate_endpoint_descriptors(self, intf, full_conf_struct): for ep in intf["endpoints"]: ep_varname = "ep{:d}{:s}".format(ep["n"], ep["direction"]) ep_struct = desc.EndpointDescriptor(ep_varname, ep) full_conf_struct.add_record(ep_struct) return len(intf["endpoints"]) # generate interface descriptor assigned to parent configuration descriptor def generate_interface_descriptors(self, conf, full_conf_struct): # fetch number of distinct endpoints def get_distinct_endpoint_count(eps): eps_found = set() for ep in eps: eps_found.add(ep["n"]) return len(eps_found) # ----------------- # collect type definitions for all interfaces, so that no type definition gets printed more than once addintf_tdefs = [] for intf in conf["interfaces"]: # generate interface descriptor intf_varname = "intf{:d}".format(intf["id"]) ep_count = get_distinct_endpoint_count(intf["endpoints"]) intf_struct = desc.InterfaceDescriptor( intf_varname, intf ) # endpoint count is unknown full_conf_struct.add_record(intf_struct) # generate additional interface descriptors for addintf in intf["additional_interfaces"]: type = addintf["type"] addintf_struct = desc.DescriptorFactory.generate( type, intf_varname + type, addintf, self.str_mgr ) full_conf_struct.add_record(addintf_struct) # generate typedef only, if not done before if addintf_tdefs.count(type) == 0: self.h += addintf_struct.print_typedef() # print typedef addintf_tdefs.append( type ) # add to already generated typedefs to avoid duplication # generate endpoint descriptors self.generate_endpoint_descriptors( intf, full_conf_struct ) # generate endpoints # generate configuration descriptors and subordinate descriptor def generate_configuration_descriptors(self): # array for referencing configuration descriptors conf_desc_array_struct = desc.Descriptor( "confDescs", "USB_ConfigurationDesc *", [] ) conf_desc_array_struct.typedef = "ConfDescs" # generate full configurations index = 0 for conf in self.config["configurations"]: full_conf_typename = "struct _FullConfigurations{:d}".format(index) full_conf_struct = Descriptor.Descriptor( "fullConfDescs{:d}".format(index), full_conf_typename, [] ) conf_struct = desc.ConfigurationDescriptor( "config", conf, "sizeof({:s})".format(full_conf_typename) ) # TODO: total length full_conf_struct.add_record(conf_struct) self.generate_interface_descriptors(conf, full_conf_struct) self.c += full_conf_struct.gen_c() self.h += full_conf_struct.gen_h(print_typedef=True) # add descriptor's pointer to the list str_desc_ptr = StructGenerator.DummyRecord( full_conf_struct.name, "USB_ConfigurationDesc *", "(USB_ConfigurationDesc *) &" + full_conf_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER, ) conf_desc_array_struct.add_record(str_desc_ptr) self.h += self.DIVIDER # self.h += conf_desc_array_struct.print_typedef() # self.h += conf_desc_array_struct.print_declaration() # self.c += conf_desc_array_struct.print_assigment() self.h += conf_desc_array_struct.gen_h(print_typedef=True) self.c += conf_desc_array_struct.gen_c() # generate string descriptors def generate_string_descriptors(self): # array for referencing string descriptors str_desc_array_struct = desc.Descriptor("strDescs", "USB_StringDesc *", []) str_desc_array_struct.typedef = "StringDescs" # generate lang id descriptor lang_id_name = "lang_id" lang_id_struct = desc.StringDescriptor(0x0409, lang_id_name) # TODO! self.c += lang_id_struct.gen_c() self.h += lang_id_struct.gen_h(print_typedef=True) str_desc_ptr = StructGenerator.DummyRecord( lang_id_struct.name, "USB_StringDesc *", "(USB_StringDesc *) &" + lang_id_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER, ) str_desc_array_struct.add_record(str_desc_ptr) # generate individual string descriptors for text_id in self.str_mgr.get_text_ids(): # # self.str_mgr.change_text(text_id, self.config[text_id]) # create descriptor str_struct = desc.StringDescriptor(self.str_mgr.get_text(text_id), text_id) self.c += str_struct.gen_c() self.h += str_struct.gen_h(print_typedef=True) # add descriptor's pointer to the list str_desc_ptr = StructGenerator.DummyRecord( text_id, "USB_StringDesc *", "(USB_StringDesc *) &" + str_struct.name, StructGenerator.RecordFactory.PLAIN_PRINTER, ) str_desc_array_struct.add_record(str_desc_ptr) self.h += self.DIVIDER # self.h += str_desc_array_struct.print_typedef() # self.h += str_desc_array_struct.print_declaration() # self.c += str_desc_array_struct.print_assigment() self.h += str_desc_array_struct.gen_h(print_typedef=True) self.c += str_desc_array_struct.gen_c() def generate_macros(self): self.add_def_macro( "USBF_NOF_CONFIGURATIONS", str(len(self.config["configurations"])), "Number of configurations", ) self.h += self.DIVIDER def generate(self, target_dir: str): self.generate_macros() # generate macro definitions self.generate_device_descriptor() # generate device descriptor self.generate_device_qualifier_descriptor() # generate device qualifier descriptor self.generate_configuration_descriptors() # generate configuration descriptors self.generate_string_descriptors() # generate string descriptors (should be the last one, since additional interface descriptors may add new string descriptors) # save generated files self.save(target_dir)