flatUSB/desc-gen/ConfigGenerator.py
Epagris dcf2c2b808 - CMake target on generating descriptors from JSON
added
- basic flatUSB_config.h concept added
- example JSONs added
- CDC request replies fixed
2024-11-14 13:19:09 +01:00

303 lines
11 KiB
Python

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)