flatUSB/desc/ConfigGenerator.py
2024-04-08 19:25:47 +02:00

245 lines
9.5 KiB
Python

import subprocess
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):
DECLARATIONS = "usb_desc.h"
DEFINITIONS = "usb_desc.c"
with open(DEFINITIONS, 'w') as f_c:
f_c.write("#include \"" + DECLARATIONS + "\"\n\n")
f_c.write(self.c)
include_guard_tag = "_" + DECLARATIONS.replace(".", "_").upper()
with open(DECLARATIONS, 'w') as f_h:
f_h.writelines(("#ifndef " + include_guard_tag + "\n", "#define " + include_guard_tag + "\n\n", "#include \"../usb_device_types.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])
subprocess.run(["indent", "-linux", "-l120", "-i4", "-nut", DEFINITIONS])
# 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)
# -----------------
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
# if [intf.keys()].count("additional_interfaces") > 0:
for addintf in intf["additional_interfaces"]:
type = addintf["type"]
addintf_struct = desc.DescriptorFactory.generate(type, intf_varname + type, addintf)
full_conf_struct.add_record(addintf_struct)
self.h += addintf_struct.print_typedef()
# 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():
# create descriptor
self.str_mgr.change_text(text_id, self.config[text_id])
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):
self.generate_macros() # generate macro definitions
self.generate_string_descriptors() # generate string descriptors
self.generate_device_descriptor() # generate device descriptor
self.generate_device_qualifier_descriptor() # generate device qualifier descriptor
self.generate_configuration_descriptors() # generate configuration descriptors
# save generated files
self.save()