From 2348ece29c510be2ad5e1a4ad72e01f87708cd98 Mon Sep 17 00:00:00 2001 From: Epagris Date: Mon, 8 Apr 2024 19:25:47 +0200 Subject: [PATCH] initial --- class/cdc.c | 91 +++++ class/cdc.h | 78 ++++ desc/.gitignore | 4 + desc/ConfigGenerator.py | 244 ++++++++++++ desc/Descriptor.py | 240 ++++++++++++ desc/StructGenerator.py | 235 ++++++++++++ desc/main.py | 42 +++ desc/usb_config.json | 75 ++++ desc/usb_desc.c | 189 ++++++++++ desc/usb_desc.h | 91 +++++ usb.c | 227 +++++++++++ usb.h | 9 + usb_callback_event.h | 33 ++ usb_common.h | 18 + usb_common_defs.h | 14 + usb_common_types.h | 9 + usb_core_types.h | 16 + usb_device_types.h | 210 +++++++++++ usb_driver.c | 819 ++++++++++++++++++++++++++++++++++++++++ usb_driver.h | 152 ++++++++ utils/gen_queue.c | 51 +++ utils/gen_queue.h | 72 ++++ 22 files changed, 2919 insertions(+) create mode 100644 class/cdc.c create mode 100644 class/cdc.h create mode 100644 desc/.gitignore create mode 100644 desc/ConfigGenerator.py create mode 100644 desc/Descriptor.py create mode 100644 desc/StructGenerator.py create mode 100644 desc/main.py create mode 100644 desc/usb_config.json create mode 100644 desc/usb_desc.c create mode 100644 desc/usb_desc.h create mode 100644 usb.c create mode 100644 usb.h create mode 100644 usb_callback_event.h create mode 100644 usb_common.h create mode 100644 usb_common_defs.h create mode 100644 usb_common_types.h create mode 100644 usb_core_types.h create mode 100644 usb_device_types.h create mode 100644 usb_driver.c create mode 100644 usb_driver.h create mode 100644 utils/gen_queue.c create mode 100644 utils/gen_queue.h diff --git a/class/cdc.c b/class/cdc.c new file mode 100644 index 0000000..0bb78db --- /dev/null +++ b/class/cdc.c @@ -0,0 +1,91 @@ +#include "cdc.h" + +#include + +#include "../usb_device_types.h" + +#include "../usb.h" + +// state +static USB_CdcState cdcs; +static uint8_t cdc_buffer[USB_CDC_BUFSIZE]; + +void usb_cdc_init(const USB_CdcAssignments *as) { + // clear the structure + memset(&cdcs, 0, sizeof(USB_CdcState)); + + // fill-in default values + USB_Cdc_LineCodingStruct *lc = &cdcs.line_coding; + lc->dwDTERate = USB_CDC_DEFAULT_BITRATE; + lc->bCharFormat = USB_CDC_DEFAULT_CHAR_FORMAT; + lc->bParityType = USB_CDC_DEFAULT_PARITY_TYPE; + lc->bDataBits = USB_CDC_DEFAULT_DATA_BITS; + + // fill-in assigments + cdcs.ep_assignments = *as; + + // assign buffer pointer + USB_CdcBuffer * buf = &cdcs.buffer; + buf->p = cdc_buffer; + buf->size = USB_CDC_BUFSIZE; +} + +int usb_cdc_process_and_return(USB_CallbackEvent *cbevt) { + int ret = -1; + switch (cbevt->type) { + case USB_CBEVT_UNKNOWN_REQ: { + switch (cbevt->setup_request->bRequest) { + case USB_CDC_SET_LINE_CODING: // set line coding + memcpy(&cdcs.line_coding, cbevt->data, sizeof(USB_Cdc_LineCodingStruct)); + MSG("%u\n", cdcs.line_coding.dwDTERate); + ret = 0; + break; + case USB_CDC_SET_CONTROL_LINE_STATE: // set control line state + memcpy(&cdcs.control_line_state, cbevt->data, sizeof(USB_Cdc_ControlLineStateStruct)); + MSG("%u\n", cdcs.control_line_state.D); + ret = 0; + break; + default: + break; + } + + // send a ZLP reply + if (ret != -1) { + cbevt->reply_data = NULL; + cbevt->reply_size = 0; + cbevt->reply_valid = true; + } + + break; + } + + case USB_CBEVT_OUT: { + if (cbevt->ep == cdcs.ep_assignments.data_ep) { + //MSG("%c\n", cbevt->data[0]); + ret = 0; + usbcore_write(cdcs.ep_assignments.data_ep, cbevt->data, cbevt->size); // echo + } + break; + } + + case USB_CBEVT_IN: { + if (cbevt->ep == cdcs.ep_assignments.control_ep) { // if notification feeding is requested + //usbcore_write(cdcs.ep_assignments.control_ep, NULL, 0); // send ZLP + ret = 0; + } else if (cbevt->ep == cdcs.ep_assignments.data_ep) { // if data are requested + //usbcore_write(cdcs.ep_assignments.data_ep, NULL, 0); // send ZLP + ret = 0; + // TODO!! + } + } + + default: + break; + } + + return ret; +} + +void usb_cdc_write(const uint8_t * data, uint32_t size) { + +} \ No newline at end of file diff --git a/class/cdc.h b/class/cdc.h new file mode 100644 index 0000000..2919532 --- /dev/null +++ b/class/cdc.h @@ -0,0 +1,78 @@ +#ifndef CORE_USB_CLASS_CDC +#define CORE_USB_CLASS_CDC + +#include "../usb_callback_event.h" + +// CDC request codes +typedef enum { + USB_CDC_SET_COMM_FEATURE = 0x2, + USB_CDC_GET_COMM_FEATURE = 0x3, + USB_CDC_CLEAR_COMM_FEATURE = 0x4, + USB_CDC_SET_AUX_LINE_STATE = 0x10, + USB_CDC_SET_HOOK_STATE = 0x11, + USB_CDC_PULSE_SETUP = 0x12, + USB_CDC_SEND_PULSE = 0x13, + USB_CDC_SET_PULSE_TIME = 0x14, + USB_CDC_RING_AUX_JACK = 0x15, + USB_CDC_SET_LINE_CODING = 0x20, + USB_CDC_GET_LINE_CODING = 0x21, + USB_CDC_SET_CONTROL_LINE_STATE = 0x22, + USB_CDC_SEND_BREAK = 0x23, + USB_CDC_SET_RINGER_PARMS = 0x30, + USB_CDC_GET_RINGER_PARMS = 0x31, + USB_CDC_SET_OPERATION_PARMS = 0x32, + USB_CDC_GET_OPERATION_PARMS = 0x33, + USB_CDC_SET_LINE_PARMS = 0x34, +} USB_Cdc_RequestCodes; + +// CDC line coding structure +typedef struct { + uint32_t dwDTERate; // data terminal rate, bits per second + uint8_t bCharFormat; // character format + uint8_t bParityType; // parity type + uint8_t bDataBits; // data bits +} USB_Cdc_LineCodingStruct; + +// CDC control line state struct +typedef struct { + uint16_t D; // settings word +} USB_Cdc_ControlLineStateStruct; + +// ---------------- + +// endpoint assignments +typedef struct { + uint8_t control_ep : 4; // control endpoint + uint8_t data_ep : 4; // data endpoint +} USB_CdcAssignments; + +typedef struct { + uint32_t size; + uint8_t * p; + uint8_t read_idx, write_idx; +} USB_CdcBuffer; + +typedef struct { + USB_CdcAssignments ep_assignments; // endpoint assignments + USB_CdcBuffer buffer; // buffer + USB_Cdc_LineCodingStruct line_coding; // line coding + USB_Cdc_ControlLineStateStruct control_line_state; // control line state +} USB_CdcState; + +// ---------------- + +#define USB_CDC_DEFAULT_BITRATE (115200) +#define USB_CDC_DEFAULT_DATA_BITS (8) +#define USB_CDC_DEFAULT_PARITY_TYPE (0) +#define USB_CDC_DEFAULT_CHAR_FORMAT (0) + +// ---------------- + +#define USB_CDC_BUFSIZE (256) + +// ---------------- + +void usb_cdc_init(const USB_CdcAssignments * as); +int usb_cdc_process_and_return(USB_CallbackEvent * cbevt); + +#endif /* CORE_USB_CLASS_CDC */ diff --git a/desc/.gitignore b/desc/.gitignore new file mode 100644 index 0000000..d3f4da3 --- /dev/null +++ b/desc/.gitignore @@ -0,0 +1,4 @@ +.idea/ +__pycache__/ +*.c~ +*.h~ \ No newline at end of file diff --git a/desc/ConfigGenerator.py b/desc/ConfigGenerator.py new file mode 100644 index 0000000..e9cbf37 --- /dev/null +++ b/desc/ConfigGenerator.py @@ -0,0 +1,244 @@ +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() diff --git a/desc/Descriptor.py b/desc/Descriptor.py new file mode 100644 index 0000000..6dddbf1 --- /dev/null +++ b/desc/Descriptor.py @@ -0,0 +1,240 @@ +import math + +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): + def __init__(self, name, ctype, content, comment=None): + super().__init__(name, ctype, content, comment) + self.qualifiers = "const" + 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): + # 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): + 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): + 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): + records = [ + ["bLength", "u8", 0x05], + ["bDescriptorType", "u8", 0x24], + ["bDescriptorSubType", "u8", 0x06], + ["bMasterInterface", "u8", 0x00], # TODO: control interface + ["bSlaveInterface0", "u8", 0x01]] # TODO: data interface + + super().__init__(name, "struct _USB_UnionFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Abstract Control Management Functional descriptor") + + +class CallManagementFunctionalDescriptor(Descriptor): + def __init__(self, name, ufd): + records = [ + ["bLength", "u8", 0x05], + ["bDescriptorType", "u8", 0x24], + ["bDescriptorSubType", "u8", 0x01], + ["bmCapabilities", "u8", 0x00], + ["dDataInterface", "u8", ufd["data_interface"]]] + + super().__init__(name, "struct _USB_CallManagementFunctionalDescriptor", StructGenerator.RecordFactory.createa(records), comment="Call Management Functional descriptor") + + +class DescriptorFactory: + REGISTRY = { + "hfd": HeaderFunctionalDescriptor, + "acmfd": AbstractControlManagementFunctionalDescriptor, + "ufd": UnionFunctionalDescriptor, + "cmfd": CallManagementFunctionalDescriptor + } + + @staticmethod + def generate(name, varname, data): + return DescriptorFactory.REGISTRY[name](varname, data) + diff --git a/desc/StructGenerator.py b/desc/StructGenerator.py new file mode 100644 index 0000000..b8a81d2 --- /dev/null +++ b/desc/StructGenerator.py @@ -0,0 +1,235 @@ +# base class for printing contents +class ContentPrinter: + def __init__(self): + pass + + def print_content(self, content): + return content + + +# print integer-like values +class IntPrinter(ContentPrinter): + def __init__(self): + super().__init__() + + def print_content(self, content): + return hex(content) + + +# print character +class CharPrinter(ContentPrinter): + def __init__(self): + super().__init__() + + def print_content(self, content): + return "'{:s}'".format(content) + + +# print the struct +class StructPrinter(ContentPrinter): + def __init__(self): + super().__init__() + + def print_content(self, content): + str = "{\n" + for rec in content: + str += rec.print_content() + "\n" + str += "}" + + +class DummyRecord: + def __init__(self, name, ctype, content, printer, comment=None): + self.name = name + self.ctype = ctype + self.content = content + self.printer = printer + + if comment is not None: + self.comment = " /* {:s} */".format(comment) + else: + self.comment = "" + + def print_declaration(self, close_statment=True): + decl = "{:s} {:s}".format(self.ctype, self.name) + if close_statment: + decl += ";" + return decl + + def print_content(self): + return "{:s}".format(self.printer.print_content(self.content)) + + +# base class for a record +class Record(DummyRecord): + def __init__(self, name, ctype, content, printer, comment=None): + super().__init__(name, ctype, content, printer, comment) + + self.array_like = isinstance(content, (list, set, tuple, str,)) + self.type_encapsulates_arrayness = False + self.typedef = None + self.attribute = None + + def print_content(self): + if not self.array_like: + return self.printer.print_content(self.content) + self.comment + else: + str = "{ " + first_line = True + for element in self.content: + if isinstance(element, DummyRecord): # printing non-primitive array-like types (e.g. struct); elements know how they get printed + if first_line: + str += self.comment + str += "\n" + str += "{:s}, //{:s}\n".format(element.print_content(), element.name) + else: # printing primitives; elements need's parent printer + str += "{:s}, ".format(self.printer.print_content(element)) + self.comment + first_line = False + str += "}" + return str + + # get typename: if no typedef exists then return with ctype, if exists, return with typedef + def get_typename(self): + if self.typedef is None: + return self.ctype + else: + return self.typedef + + # print variable declaration + def print_declaration(self, close_statment=True, print_comment=True): + decl = "{:s} {:s}".format(self.get_typename(), self.name) + if self.array_like and not self.type_encapsulates_arrayness: + decl += "[{:d}]".format(len(self.content)) + if close_statment: + decl += ";" + if print_comment: + decl += self.comment + return decl + + # print variable assigment + def print_assigment(self, close_statement=True): + declaration = self.print_declaration(close_statment=False, print_comment=False) + definition = self.print_content() + statement = "{:s} = {:s}".format(declaration, definition) + if close_statement: + statement += ";" + return statement + + # print a type definition based on the contents + def print_typedef(self, tname=None): + if tname is None: + tname = self.typedef + + attribute_compound = "" + if not self.attribute is None: + attribute_compound = "__attribute__(({:s}))".format(self.attribute) + + tdef_clause = "typedef " + if not self.array_like: + tdef_clause += "{:s} {:s};".format(self.ctype, tname) + else: + array_of_primitives = not isinstance(self.content[0], Record) + + if array_of_primitives: + tdef_clause += "{:s} {:s}[{:d}];".format(self.ctype, tname, len(self.content)) + else: # array of structs (non-primitives) + tdef_clause += self.ctype + " {" + for rec in self.content: + # tdef_clause += "{:s} {:s};\n".format(rec.ctype, rec.name) + tdef_clause += rec.print_declaration(True) + tdef_clause += "} " + "{:s} {:s};\n".format(attribute_compound, tname) + + return tdef_clause + + +# structure record +class StructRecord(Record): + def __init__(self, name, ctype, content, comment=None): + super().__init__(name, ctype, content, StructPrinter(), comment) + self.type_encapsulates_arrayness = True + + # add new record to the structure + def add_record(self, rec): + self.content.append(rec) + + +class RecordFactory: + INT_LIKE_TYPES = ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64"] + BOOl_TYPES = ["bool", "b8"] + STR_TYPE = "str" + PLAIN_PRINTER = ContentPrinter() + NUMERIC_PRINTER = IntPrinter() + CHARACTER_PRINTER = CharPrinter() + + # Create record based on content and abbreviated type name + # (also convert non-numeric types to numeric ones being having the same size) + @staticmethod + def create(name_arg0, ctype="", content="", no_autoconvert=False): + # unpack params + if isinstance(name_arg0, (list, set, tuple,)): + name = name_arg0[0] + ctype = name_arg0[1] + content = name_arg0[2] + if len(name_arg0) > 3: + no_autoconvert = name_arg0[3] + else: + name = name_arg0 + + # determine standard c typename + std_type = "error_type" + printer = None + + # convert bool to int + if RecordFactory.BOOl_TYPES.count(ctype) > 0: + ctype = "u8" + + # convert string to character equivalent + is_string = False + if ctype == RecordFactory.STR_TYPE: + is_string = True + ctype = "u16" + printer = CharPrinter() + + # auto-convert types if needed + if is_string: + content = str(content) + elif isinstance(content, str) and not no_autoconvert: + content = int(content, base=0) # auto-recognize base + + if no_autoconvert: + printer = ContentPrinter() + + # loop-up standard c-type + if RecordFactory.INT_LIKE_TYPES.count(ctype) > 0: # integer + storage_size_str = ctype.strip("ui") + if ctype[0] == 'u': + std_type = ctype[0] + "int" + storage_size_str + "_t" + else: + std_type = "int" + storage_size_str + "_t" + + if printer is None: # if printer is not assigned yet + printer = IntPrinter() + + # generate record + rec = Record(name, std_type, content, printer) + if no_autoconvert: + rec.array_like = False + return rec + + @staticmethod + def createa(defs): + recs = [] + for rec in defs: + recs.append(RecordFactory.create(rec)) + + return recs + +# # integer-like record +# class IntRecord(Record): +# def __init__(self, name, ctype, content): +# super().__init__(name, ctype, content, IntPrinter()) +# +# +# # string record +# class StrRecord(Record): +# def __init__(self, name, content): +# super().__init__(name, "uint16_t", content, CharPrinter) diff --git a/desc/main.py b/desc/main.py new file mode 100644 index 0000000..8830e79 --- /dev/null +++ b/desc/main.py @@ -0,0 +1,42 @@ +import json +from sys import argv + +import Descriptor as desc + +import StructGenerator as sg +from itertools import chain + +from ConfigGenerator import ConfigGenerator + +# fetch USB settings +usb_config_file_name = argv[1] +with open(usb_config_file_name, 'r') as usb_config_file: + usb_config_data = usb_config_file.read() +usb_config = json.loads(usb_config_data) + +cfggen = ConfigGenerator(usb_config) +cfggen.generate() + +# devDesc = desc.DeviceDescriptor(usb_config) +# print(devDesc.print_assigment()) +# +# strDesc = desc.StringDescriptor("Testdevice", "StrDevice") +# print(strDesc.print_assigment()) + +#intrec = sg.RecordFactory.create("num", "u32", 127) +#print(intrec.print_typedef("INT")) +# intrec = sg.RecordFactory.create([ "num", "u32", 127, ]) +# print(intrec.print_typedef("INTINT")) +# # +# strrec = sg.RecordFactory.create("some_string", "str", "Some string") +# print(strrec.print_typedef("STR")) +#print(strrec.print_content()) +# +# recs = [ intrec, strrec ] +# +# struct1 = sg.StructRecord("struct1", "struct struct 1", recs) +# struct2 = sg.StructRecord("struct2", "struct struct2", recs) +# print(struct2.print_typedef("struct2")) +# +# structrec = sg.StructRecord("object", "struct compound", [ struct1, struct2, *recs ]) +# print(structrec.print_content()) \ No newline at end of file diff --git a/desc/usb_config.json b/desc/usb_config.json new file mode 100644 index 0000000..2630cf6 --- /dev/null +++ b/desc/usb_config.json @@ -0,0 +1,75 @@ +{ + "class": "0x02", + "subclass": "0x00", + "protocol_code": "0x00", + "max_ep0_packet_size": 64, + "vendor_id": "0x0925", + "product_id": "0x9050", + "device_release_bcd": "0x0100", + "vendor_string": "Epagris", + "product_string": "Testdevice", + "serial_number": "1552", + "configurations": [ + { + "id": 1, + "is_bus_powered": true, + "can_wake_up_host": false, + "max_power_mA": 100, + "interfaces": [ + { + "id": 0, + "class": "0x02", + "subclass": "0x02", + "protocol_code": "0x01", + "additional_interfaces": [ + { + "type": "hfd" + }, + { + "type": "acmfd" + }, + { + "type": "ufd" + }, + { + "type": "cmfd", + "data_interface": 1 + } + ], + "endpoints": [ + { + "n": 2, + "direction": "in", + "transfer_type": "interrupt", + "max_packet_size": 64, + "service_interval": 2 + } + ] + }, + { + "id": 1, + "class": "0x0A", + "subclass": "0x00", + "protocol_code": "0x00", + "additional_interfaces": [], + "endpoints": [ + { + "n": 1, + "direction": "in", + "transfer_type": "bulk", + "max_packet_size": 64, + "service_interval": 0 + }, + { + "n": 1, + "direction": "out", + "transfer_type": "bulk", + "max_packet_size": 64, + "service_interval": 0 + } + ] + } + ] + } + ] +} diff --git a/desc/usb_desc.c b/desc/usb_desc.c new file mode 100644 index 0000000..6f548db --- /dev/null +++ b/desc/usb_desc.c @@ -0,0 +1,189 @@ +#include "usb_desc.h" + +const USB_StrDesc_lang_id lang_id = { + /* String descriptor */ + 0x4, // bLength + UD_String, // bDescriptorType + 0x409, // bString +}; + +const USB_StrDesc_vendor_string vendor_string = { + /* String descriptor */ + 0x10, // bLength + UD_String, // bDescriptorType + { + 'E', + 'p', + 'a', + 'g', + 'r', + 'i', + 's', + }, // bString +}; + +const USB_StrDesc_product_string product_string = { + /* String descriptor */ + 0x16, // bLength + UD_String, // bDescriptorType + { + 'T', + 'e', + 's', + 't', + 'd', + 'e', + 'v', + 'i', + 'c', + 'e', + }, // bString +}; + +const USB_StrDesc_serial_number serial_number = { + /* String descriptor */ + 0xa, // bLength + UD_String, // bDescriptorType + { + '1', + '5', + '5', + '2', + }, // bString +}; + +const StringDescs strDescs = { + (USB_StringDesc *)&lang_id, // lang_id + (USB_StringDesc *)&vendor_string, // vendor_string + (USB_StringDesc *)&product_string, // product_string + (USB_StringDesc *)&serial_number, // serial_number +}; + +const USB_DeviceDesc devDesc = { + /* Device Descriptor */ + 0x12, // bLength + UD_Device, // bDescriptorType + 0x200, // bcdUSB + 0x2, // bDeviceClass + 0x0, // bDeviceSubclass + 0x0, // bDeviceProtocol + 0x40, // bMaxPacketSize0 + 0x925, // idVendor + 0x9050, // idProduct + 0x100, // bcdDevice + 0x1, // iManufacturer + 0x2, // iProduct + 0x3, // iSerialNumber + 0x1, // bNumConfigurations +}; + +const USB_DeviceQualifierDesc devQualDesc = { + /* Device Qualifier descriptor */ + 0x9, // bLength + UD_DeviceQualifier, // bDescriptorType + 0x200, // bcdUSB + 0x2, // bDeviceClass + 0x0, // bDeviceSubclass + 0x0, // bDeviceProtocol + 0x40, // bMaxPacketSize + 0x1, // bNumConfigurations +}; + +const FullConfigurations0 fullConfDescs0 = { + { + /* Configuration descriptor */ + 0x9, // bLength + UD_Configuration, // bDescriptorType + sizeof(struct _FullConfigurations0), // wTotalLength + 0x2, // bNumInterfaces + 0x1, // bConfigurationValue + 0x0, // iConfiguration + USB_CONFIG_ATTR_USB1_1_FLAG, // bmAttributes + 0x32, // bMaxPower + }, // config + { + /* Interface descriptor : 0 */ + 0x9, // bLength + UD_Interface, // bDescriptorType + 0x0, // bInterfaceNumber + 0x0, // bAlternateSetting + 0x1, // bNumEndpoints + 0x2, // bInterfaceClass + 0x2, // bInterfaceSubclass + 0x1, // bInterfaceProtocol + 0x0, // iInterface + }, // intf0 + { + /* Header Functional descriptor */ + 0x5, // bLength + 0x24, // bDescriptorType + 0x0, // bDescriptorSubType + 0x110, // bcdCDC + }, // intf0hfd + { + /* Abstract Control Management Functional descriptor */ + 0x4, // bLength + 0x24, // bDescriptorType + 0x2, // bDescriptorSubType + 0x2, // bmCapabilities + }, // intf0acmfd + { + /* Abstract Control Management Functional descriptor */ + 0x5, // bLength + 0x24, // bDescriptorType + 0x6, // bDescriptorSubType + 0x0, // bMasterInterface + 0x1, // bSlaveInterface0 + }, // intf0ufd + { + /* Call Management Functional descriptor */ + 0x5, // bLength + 0x24, // bDescriptorType + 0x1, // bDescriptorSubType + 0x0, // bmCapabilities + 0x1, // dDataInterface + }, // intf0cmfd + { + /* Endpoint descriptor : 2 in */ + 0x7, // bLength + UD_Endpoint, // bDescriptorType + (USB_IN << 7) | (2), // bEndpointAddress + UT_Interrupt, // bmAttributes + 0x40, // wMaxPacketSize + 0x2, // bInterval + }, // ep2in + { + /* Interface descriptor : 1 */ + 0x9, // bLength + UD_Interface, // bDescriptorType + 0x1, // bInterfaceNumber + 0x0, // bAlternateSetting + 0x2, // bNumEndpoints + 0xa, // bInterfaceClass + 0x0, // bInterfaceSubclass + 0x0, // bInterfaceProtocol + 0x0, // iInterface + }, // intf1 + { + /* Endpoint descriptor : 1 in */ + 0x7, // bLength + UD_Endpoint, // bDescriptorType + (USB_IN << 7) | (1), // bEndpointAddress + UT_Bulk, // bmAttributes + 0x40, // wMaxPacketSize + 0x0, // bInterval + }, // ep1in + { + /* Endpoint descriptor : 1 out */ + 0x7, // bLength + UD_Endpoint, // bDescriptorType + (USB_OUT << 7) | (1), // bEndpointAddress + UT_Bulk, // bmAttributes + 0x40, // wMaxPacketSize + 0x0, // bInterval + }, // ep1out +}; + +const ConfDescs confDescs = { + (USB_ConfigurationDesc *)&fullConfDescs0, // fullConfDescs0 +}; diff --git a/desc/usb_desc.h b/desc/usb_desc.h new file mode 100644 index 0000000..2b526e8 --- /dev/null +++ b/desc/usb_desc.h @@ -0,0 +1,91 @@ +#ifndef CORE_USB_DESC_USB_DESC +#define CORE_USB_DESC_USB_DESC + +#include "../usb_device_types.h" + +#define USBF_NOF_CONFIGURATIONS (1) // Number of configurations + +// --------------------------- + +typedef struct _USB_StrDesc_lang_id { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString; +} __attribute__((packed)) USB_StrDesc_lang_id; + +extern const USB_StrDesc_lang_id lang_id; /* String descriptor */ +typedef struct _USB_StrDesc_vendor_string { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[7]; +} __attribute__((packed)) USB_StrDesc_vendor_string; + +extern const USB_StrDesc_vendor_string vendor_string; /* String descriptor */ +typedef struct _USB_StrDesc_product_string { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[10]; +} __attribute__((packed)) USB_StrDesc_product_string; + +extern const USB_StrDesc_product_string product_string; /* String descriptor */ +typedef struct _USB_StrDesc_serial_number { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bString[4]; +} __attribute__((packed)) USB_StrDesc_serial_number; + +extern const USB_StrDesc_serial_number serial_number; /* String descriptor */ + +// --------------------------- + +typedef USB_StringDesc *StringDescs[4]; +extern const StringDescs strDescs; +extern const USB_DeviceDesc devDesc; /* Device Descriptor */ +extern const USB_DeviceQualifierDesc devQualDesc; /* Device Qualifier descriptor */ +typedef struct _USB_HeaderFunctionalDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint16_t bcdCDC; +} __attribute__((packed)) USB_HeaderFunctionalDescriptor; +typedef struct _USB_AbstractControlManagementFunctionalDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bmCapabilities; +} __attribute__((packed)) USB_AbstractControlManagementFunctionalDescriptor; +typedef struct _USB_UnionFunctionalDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bMasterInterface; + uint8_t bSlaveInterface0; +} __attribute__((packed)) USB_UnionFunctionalDescriptor; +typedef struct _USB_CallManagementFunctionalDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bmCapabilities; + uint8_t dDataInterface; +} __attribute__((packed)) USB_CallManagementFunctionalDescriptor; +typedef struct _FullConfigurations0 { + USB_ConfigurationDesc config; /* Configuration descriptor */ + USB_InterfaceDesc intf0; /* Interface descriptor : 0 */ + USB_HeaderFunctionalDescriptor intf0hfd; /* Header Functional descriptor */ + USB_AbstractControlManagementFunctionalDescriptor intf0acmfd; /* Abstract Control Management Functional descriptor */ + USB_UnionFunctionalDescriptor intf0ufd; /* Abstract Control Management Functional descriptor */ + USB_CallManagementFunctionalDescriptor intf0cmfd; /* Call Management Functional descriptor */ + USB_EndpointDesc ep2in; /* Endpoint descriptor : 2 in */ + USB_InterfaceDesc intf1; /* Interface descriptor : 1 */ + USB_EndpointDesc ep1in; /* Endpoint descriptor : 1 in */ + USB_EndpointDesc ep1out; /* Endpoint descriptor : 1 out */ +} __attribute__((packed)) FullConfigurations0; + +extern const FullConfigurations0 fullConfDescs0; + +// --------------------------- + +typedef USB_ConfigurationDesc *ConfDescs[1]; +extern const ConfDescs confDescs; + +#endif /* CORE_USB_DESC_USB_DESC */ diff --git a/usb.c b/usb.c new file mode 100644 index 0000000..b9e1705 --- /dev/null +++ b/usb.c @@ -0,0 +1,227 @@ +#include "usb.h" + +#include + +#include "usb_common.h" +#include "usb_core_types.h" + +#include "utils/gen_queue.h" + +#include "desc/usb_desc.h" + +#include "usb_driver.h" + +// --------------- + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#ifdef USBDBGMSG +#include "../cli/stdio_uart.h" +#define USBMSG(...) MSG(__VA_ARGS__) +#else +#define USBMSG(...) +#endif + +// --------------- + +static uint8_t tx_assembly_buf[USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS] DWORD_ALIGN; // buffer for assembling packets + +// --------------- + +// // state of changeing address +// typedef enum { +// USB_ADDRCHG_IDLE = 0, // idle state +// USB_ADDRCHG_NEW_ADDR_RECVD, // new address received +// } USB_AddressChangeState; + +// ---------------- + +__weak void usb_event_callback(USB_CallbackEvent *cbevt) { + return; +} + +// ---------------- + +// prepare descriptor for transmission, return with final transmission size +// static uint8_t usb_prepare_descriptor(const uint8_t *desc, uint8_t desc_size, uint8_t req_size) { +// uint8_t sz = (uint8_t)(MIN(req_size, desc_size)); // determine transmission size +// memcpy(gs.tx_assembly_buf, desc, sz); // copy that portion to the assembly buffer +// USB_DescHdr *hdr = (USB_DescHdr *)gs.tx_assembly_buf; // obtain header +// hdr->bLength = MIN(sz, hdr->bLength); // write transmit size +// return sz; +// } + +// #define USB_SETUP_STREAM_BUFFER (72) + +typedef struct { + USB_SetupRequest setup_req; // setup request + uint8_t next_stage; // next expected stage + bool out_complete; // signals if transfer's OUT direction is complete, no later data reception is expected +} USB_SetupTransferState; + +static USB_SetupTransferState stups; + +void usbcore_init() { + // init state + memset(&stups, 0, sizeof(USB_SetupTransferState)); +} + +void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) { + + //MSG("STUP: %u, %s\n", size, stage == UST_SETUP ? "SETUP" : "DATA"); + + // #define USB_PREPARE_DESC_VAR(var, size) sz = usb_prepare_descriptor((const uint8_t *)&(var), sizeof((var)), (size)) + + switch (stups.next_stage) { + case UST_SETUP: { // expecting SETUP stage + if (stage == UST_SETUP) { // SETUP received + // save setup request + stups.setup_req = *((USB_SetupRequest *)data); + + // if data direction is IN, then don't expect an OUT transaction + if ((stups.setup_req.bmRequestType.fields.dir == USB_IN) || + ((stups.setup_req.bmRequestType.fields.dir == USB_OUT) && (stups.setup_req.wLength == 0))) { + stups.out_complete = true; // DATA stage is IN + } else { + stups.out_complete = false; // OUT transaction are not exhausted yet (i.e. DATA stage is OUT) + stups.next_stage = UST_DATA; // next stage is DATA OUT stage + } + } + break; + } + + case UST_DATA: { // expecting DATA stage + if (stage == UST_DATA) { // DATA received + stups.out_complete = true; + } + + // step into SETUP state + stups.next_stage = UST_SETUP; + break; + } + + default: + break; + } + + // expect status transaction to follow the data phase + usbdrv_arm_OUT_endpoint(0, 64); + + // only continue processing if the transaction has concluded + if (!stups.out_complete) { + return; + } + +#define DETERMINE_TRANSFER_SIZE(desc_size) sz = (MIN((stups.setup_req.wLength), (desc_size))) +#define SET_TRANSMISSION_POINTER(ptr) data = (const uint8_t *)(ptr) + + switch (stups.setup_req.bRequest) { + case UREQ_GetDescriptor: // GET DESCRIPTOR + { + // which descriptor? + uint8_t desc_type = (stups.setup_req.wValue >> 8); // get descriptor type + uint8_t desc_index = (stups.setup_req.wValue & 0xFF); // get descriptor index + uint8_t sz = 0; + const uint8_t *data = NULL; + + switch (desc_type) { + case UD_Device: // DEVICE DESCRIPTOR + DETERMINE_TRANSFER_SIZE(devDesc.bLength); + SET_TRANSMISSION_POINTER(&devDesc); + break; + case UD_Configuration: // CONFIGURATION DESCRIPTOR + DETERMINE_TRANSFER_SIZE(confDescs[desc_index]->wTotalLength); + SET_TRANSMISSION_POINTER(confDescs[desc_index]); + break; + case UD_String: // STRING DESCRIPTOR + DETERMINE_TRANSFER_SIZE(strDescs[desc_index]->bLength); + SET_TRANSMISSION_POINTER(strDescs[desc_index]); + break; + case UD_DeviceQualifier: + DETERMINE_TRANSFER_SIZE(devQualDesc.bLength); + SET_TRANSMISSION_POINTER(&devQualDesc); + break; + // Descriptors not implemented + default: + usbdrv_stall_endpoint(0, USB_IN, true); // stall IN, since request in unsupported + break; + } + + // arm data transmission + usbdrv_arm_IN_endpoint(0, data, sz); + break; + } + + case UREQ_SetAddress: { // SET ADDRESS + uint8_t addr = stups.setup_req.wValue & 0x7F; + usbdrv_set_address(addr); + usbdrv_arm_OUT_endpoint(0, 64); // prepare for data OUT stage + USBDRV_ARM_IN_ZLP(0); // ZLP IN + break; + } + case UREQ_SetConfiguration: // SET CONFIGURATION + { + uint8_t config_id = stups.setup_req.wValue & 0xFF; + usbdrv_fetch_endpoint_configuration(config_id - 1); + USBDRV_ARM_IN_ZLP(0); + break; + } + default: { // UNKNOWN REQUEST, pass processing to user application + USB_CallbackEvent cbevt = { 0 }; + cbevt.type = USB_CBEVT_UNKNOWN_REQ; + cbevt.setup_request = &stups.setup_req; + cbevt.data = (const uint8_t *)data; + cbevt.size = size; + cbevt.ep = 0; + cbevt.dir = USB_OUT; + cbevt.reply_valid = false; + usb_event_callback(&cbevt); + + if (cbevt.reply_valid) { + usbdrv_arm_IN_endpoint(0, cbevt.reply_data, cbevt.reply_size); + } + break; + } + } + + stups.out_complete = false; +} + +void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) { + USB_CallbackEvent cbevt = { 0 }; // allocate and clear structure... + cbevt.ep = cbcpd->ep; // later only fill nonzero fields + + bool discard_event = false; // only discard if event does not fit any category above + switch (cbcpd->code) { + case USB_CBC_OUT: { + cbevt.type = USB_CBEVT_OUT; + cbevt.data = cbcpd->data; + cbevt.size = cbcpd->size; + cbevt.dir = USB_OUT; + break; + } + case USB_CBC_IN_FIFOEMPTY: + case USB_CBC_IN_DONE: { + cbevt.type = USB_CBEVT_IN; + if (cbcpd->code == USB_CBC_IN_FIFOEMPTY) { // signals a failed in transfer + cbevt.subtype = USB_CBEVST_IN_REQ; + } + cbevt.dir = USB_IN; + break; + } + + default: + discard_event = true; + break; + } + + // if event is not ment to be discarded, then invoke event callback + if (!discard_event) { + usb_event_callback(&cbevt); + } +} + +void usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size) { + usbdrv_arm_IN_endpoint(ep, data, size); +} \ No newline at end of file diff --git a/usb.h b/usb.h new file mode 100644 index 0000000..15bc28f --- /dev/null +++ b/usb.h @@ -0,0 +1,9 @@ +#ifndef CORE_USB_USB +#define CORE_USB_USB + +#include "usb_callback_event.h" + +void usbcore_init(); // initialize USB core +void usbcore_write(uint8_t ep, const uint8_t *data, uint16_t size); // write data to endpoint + +#endif /* CORE_USB_USB */ diff --git a/usb_callback_event.h b/usb_callback_event.h new file mode 100644 index 0000000..6a41473 --- /dev/null +++ b/usb_callback_event.h @@ -0,0 +1,33 @@ +#ifndef CORE_USB_USB_CALLBACK_EVENT +#define CORE_USB_USB_CALLBACK_EVENT + +#include + +#include "usb_device_types.h" + +typedef enum { + USB_CBEVT_OUT, // OUT event + USB_CBEVT_IN, // IN event + USB_CBEVT_UNKNOWN_REQ // unknown request on a control endpoint +} USB_CallbackEventType; + +typedef enum { + USB_CBEVST_IN_NONE = 0, // no subtype + USB_CBEVST_IN_REQ // IN request was received but could not be responded +} USB_CallbackEventSubType; + +typedef struct { + uint8_t type : 4; // event type + uint8_t subtype : 4; // event subtype + uint8_t ep; // endpoint number + uint8_t dir; // endpoint direction + uint8_t size; // size of accompaining data + const uint8_t * data; // event data + const USB_SetupRequest * setup_request; // corresponding setup request (if exists) + + bool reply_valid; // reply message is valid + const uint8_t * reply_data; // reply data + uint8_t reply_size; // reply size +} USB_CallbackEvent; + +#endif /* CORE_USB_USB_CALLBACK_EVENT */ diff --git a/usb_common.h b/usb_common.h new file mode 100644 index 0000000..da817c0 --- /dev/null +++ b/usb_common.h @@ -0,0 +1,18 @@ +#ifndef CORE_USB_USB_COMMON +#define CORE_USB_USB_COMMON + +#include "usb_common_types.h" +#include "usb_common_defs.h" +#include "usb_device_types.h" + +#define READ_FIELD(r,f) (((r) & (f##_Msk)) >> (f##_Pos)) +#define WRITE_FIELD(r,f,v) ((r) = ((r) & ~(f##_Msk)) | (v << (f##_Pos))) +#define WAIT_FOR_BIT(r,b) while ((r) & (b)) {} +#define WAIT_FOR_nBIT(r,b) while (!((r) & (b))) {} + +#define DWORD_ALIGN __attribute__((aligned(4))) + +#define CEILDIV4(x) (((x) + 3) >> 2) +#define CEIL4(x) (((x) + 3) & (~0b11)) + +#endif /* CORE_USB_USB_COMMON */ diff --git a/usb_common_defs.h b/usb_common_defs.h new file mode 100644 index 0000000..15d8227 --- /dev/null +++ b/usb_common_defs.h @@ -0,0 +1,14 @@ +#ifndef CORE_USB_USB_COMMON_DEFS +#define CORE_USB_USB_COMMON_DEFS + +#include +#include + +#define USBG (USB_OTG_FS) +#define USBD ((USB_OTG_DeviceTypeDef *) ((uint32_t)(USBG) + (uint32_t)(USB_OTG_DEVICE_BASE))) +#define USBINEP ((USB_OTG_INEndpointTypeDef *) ((uint32_t)(USBG) + (uint32_t)(USB_OTG_IN_ENDPOINT_BASE))) +#define USBOUTEP ((USB_OTG_OUTEndpointTypeDef *) ((uint32_t)(USBG) + (uint32_t)(USB_OTG_OUT_ENDPOINT_BASE))) +#define USBFIFO(ep) ((uint32_t *)((uint32_t)(USBG) + USB_OTG_FIFO_BASE + (USB_OTG_FIFO_SIZE) * (ep))) +#define USBPCGCCTL ((uint32_t *)((uint32_t)(USBG) + USB_OTG_PCGCCTL_BASE)) + +#endif /* CORE_USB_USB_COMMON_DEFS */ diff --git a/usb_common_types.h b/usb_common_types.h new file mode 100644 index 0000000..328bb93 --- /dev/null +++ b/usb_common_types.h @@ -0,0 +1,9 @@ +#ifndef CORE_USB_USB_COMMON_TYPES +#define CORE_USB_USB_COMMON_TYPES + +#include +#include + +typedef uint8_t bool8_t; + +#endif /* CORE_USB_USB_COMMON_TYPES */ diff --git a/usb_core_types.h b/usb_core_types.h new file mode 100644 index 0000000..d2b1409 --- /dev/null +++ b/usb_core_types.h @@ -0,0 +1,16 @@ +#ifndef CORE_USB_USB_CORE_TYPES +#define CORE_USB_USB_CORE_TYPES + +#include "usb_common_types.h" + +/* USB linespeed */ +typedef enum { USB_SPD_UNKNOWN = 0b00, USB_SPD_LOW = 0b01, USB_SPD_FULL = 0b11 } USB_Speed; + +/* USB core state */ +typedef struct +{ + uint8_t linespeed; // USB linespeed +} USB_CoreState; + + +#endif /* CORE_USB_USB_CORE_TYPES */ diff --git a/usb_device_types.h b/usb_device_types.h new file mode 100644 index 0000000..3c1c1a5 --- /dev/null +++ b/usb_device_types.h @@ -0,0 +1,210 @@ +#ifndef CORE_USB_USB_DEVICE_TYPES +#define CORE_USB_USB_DEVICE_TYPES + +#include "usb_common_types.h" + +// /* USB endpoint direction */ +// typedef enum { USB_IN = 0, +// USB_OUT = 1 } USB_Ep_Dir; + +/* ------ USB DESCRIPTORS ------- */ + +// USB Descriptor type codes + +typedef enum { + UD_Device = 1, + UD_Configuration = 2, + UD_String = 3, + UD_Interface = 4, + UD_Endpoint = 5, + UD_DeviceQualifier = 6, + UD_OtherSpeedConfiguration = 7 + // NOT FULL! +} USB_DescType; + +// USB descriptor header +#define USB_DESC_HEADER \ + uint8_t bLength; /* length in bytes */ \ + uint8_t bDescriptorType; /* descriptor type */ + +#define USB_DESC_HEADER_SIZE (2) // USB descriptor header size + +// USB Descriptor header + +typedef struct { + USB_DESC_HEADER +} USB_DescHdr; + +// USB Device descriptor + +typedef struct { + USB_DESC_HEADER + uint16_t bcdUSB; // USB specification release number (BCD) + uint8_t bDeviceClass; // Class code + uint8_t bDeviceSubclass; // Subclass code + uint8_t bDeviceProtocol; // Protocol code + uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0 + uint16_t idVendor; // Vendor ID + uint16_t idProduct; // Product ID + uint16_t bcdDevice; // Device release number (BCD) + uint8_t iManufacturer; // Index of string descriptor for manufacturer + uint8_t iProduct; // Index of string descriptor for the product + uint8_t iSerialNumber; // Index of string descriptor for the serial number + uint8_t bNumConfiguration; // Number of possible configurations +} USB_DeviceDesc; + +// USB Device Qualifier descriptor + +typedef struct { + USB_DESC_HEADER + uint16_t bcdUSB; // USB specification release number (BCD) + uint8_t bDeviceClass; // Class code + uint8_t bDeviceSubclass; // Subclass code + uint8_t bDeviceProtocol; // Protocol code + uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0 + uint8_t bNumConfiguration; // Number of possible configurations +} __attribute__((packed)) USB_DeviceQualifierDesc; + +#define USB_CONFIG_ATTR_SELF_POWERED (1 << 6) +#define USB_CONFIG_ATTR_REMOTE_WKUP (1 << 5) +#define USB_CONFIG_ATTR_USB1_1_FLAG (1 << 7) + +// USB Configuration descriptor + +typedef struct { + USB_DESC_HEADER + uint16_t wTotalLength; // The number of bytes in the configuration descriptor and all of its subordinate descriptors + uint8_t bNumInterfaces; // Number of interfaces in the configuration + uint8_t bConfigrationValue; // Identifier for Set Configuration and Get Configuration Requests + uint8_t iConfiguration; // Index of string descriptor for the configuration + uint8_t bmAttributes; // Self/bus power and remote wakeup settings + uint8_t bMaxPower; // Bus power required in units of 2 mA +} __attribute__((packed)) USB_ConfigurationDesc; + +// USB Interface descriptor + +typedef struct { + USB_DESC_HEADER + uint8_t bInterfaceNumber; // Number identifying this interface + uint8_t bAlternateSetting; // A number that identifies a descriptor with alternate settings for this bInterfaceNumber + uint8_t bNumEndpoints; // Number of endpoints supported not counting endpoint zero + uint8_t bInterfaceClass; // Class code + uint8_t bInterfaceSubclass; // Subclass code + uint8_t bInterfaceProtocol; // Protocol code + uint8_t iInterface; // Index of string descriptor for the interface +} __attribute__((packed)) USB_InterfaceDesc; + +// USB Transfer type + +typedef enum { + UT_Control = 0b00, + UT_Isochronous = 0b01, + UT_Bulk = 0b10, + UT_Interrupt = 0b11 +} USB_TransferType; + +// USB Endpoint descriptor + +typedef struct { + + USB_DESC_HEADER + uint8_t bEndpointAddress; // Endpoint number and direction + uint8_t bmAttributes; // Transfer type and supplementary information + uint16_t wMaxPacketSize; // Maximum packet size supported + uint8_t bInterval; // Service interval or NAK rate +} __attribute__((packed)) USB_EndpointDesc; + +/* struct { + uint8_t n : 7; // endpoint number (four bits, but this style no "spacer" needed) + uint8_t dir; // direction + } bEndpointAddress; // Endpoint number and direction*/ + +// USB String descriptor + +typedef struct { + USB_DESC_HEADER +} USB_StringDesc; + +// USB endpoint direction + +typedef enum { + USB_OUT = 0, + USB_IN = 1 +} USB_EndpointDir; + +// USB PIDs + +typedef enum { + PID_EXT = 0, + PID_OUT = 1, + PID_ACK = 2, + PID_DATA0 = 3, + PID_PING = 4, + PID_SOF = 5, + PID_NYET = 6, + PID_DATA2 = 7, + PID_SPLIT = 8, + PID_IN = 9, + PID_NAK = 10, + PID_DATA1 = 11, + PID_PRE_ERR = 12, + PID_SETUP = 13, + PID_STALL = 14, + PID_MDATA = 15, +} USB_PID; + +// USB Request types + +typedef enum { + UR_Standard = 0, + UR_VendorSpec = 1, + UR_ReqDefVendorSpec = 2 +} USB_RequestType; + +// USB Recipients + +typedef enum { + UREC_Device = 0, + UREC_SpecificInterface = 1, + UREC_Endpoint = 2, + UREC_OtherElement = 3 +} USB_Recipient; + +// USB Request types + +typedef enum { + UREQ_GetStatus = 0x00, + UREQ_ClearFeature = 0x01, + UREQ_SetFeature = 0x03, + UREQ_SetAddress = 0x05, + UREQ_GetDescriptor = 0x06, + UREQ_SetDescriptor = 0x07, + UREQ_GetConfiguration = 0x08, + UREQ_SetConfiguration = 0x09, + UREQ_GetInterface = 0x0A, + UREQ_SetInterface = 0x0B, + UREQ_SyncFrame = 0x0C, + UREQ_SetSEL = 0x30, + UREQ_SetIsochronousDelay = 0x31 +} USB_RequestCode; + +// setup request structure + +typedef struct { + + union { + uint8_t bmRequestType; + + struct { + uint8_t recipient : 5; + uint8_t type : 2; + uint8_t dir : 1; + } fields; + } bmRequestType; // request type + uint8_t bRequest; // request + uint16_t wValue; // ... + uint16_t wIndex; // ... + uint16_t wLength; // number of bytes in the data stage +} USB_SetupRequest; + +#endif /* CORE_USB_USB_DEVICE_TYPES */ diff --git a/usb_driver.c b/usb_driver.c new file mode 100644 index 0000000..f297a1a --- /dev/null +++ b/usb_driver.c @@ -0,0 +1,819 @@ + +#include "usb_driver.h" + +#include + +#include "usb_common.h" +#include "usb_core_types.h" + +#include "desc/usb_desc.h" + +// --------------- + +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#ifdef USBDBGMSG +#include "../cli/stdio_uart.h" +#define USBMSG(...) MSG(__VA_ARGS__) +#else +#define USBMSG(...) +#endif + +// --------------- + +static USBDRV_GlobalState gs; // global USB state + +static uint8_t rx_buf[USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS] DWORD_ALIGN; // receive buffer + +#define USB_EVENT_QUEUE_LENGTH (16) + +static uint8_t event_queue_mem[Q_REQ_MEM_SIZE_T(USB_EVENT_QUEUE_LENGTH, USBDRV_EventCompound)] DWORD_ALIGN; // backing memory for the event queue + +static const char *FIFO_STATUS_STR[6] = { + "GLOBAL OUT NAK", + "OUT DATA RECV", + "OUT TRANSFER CPLT", + "OUT SETUP CPLT", + "", + "OUT SETUP RECV"}; + +// --------------- + +// USB pin low level, early peripheral initialization +// PA12: D+, PA11: D- +void usbdrv_gpio_init() { + // turn GPIO-s into AF mode + __HAL_RCC_GPIOA_CLK_ENABLE(); // turn ON GPIOA clocks + + GPIO_InitTypeDef gpio_init; + gpio_init.Mode = GPIO_MODE_AF_PP; + gpio_init.Pin = GPIO_PIN_12; + gpio_init.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + gpio_init.Pull = GPIO_NOPULL; + gpio_init.Alternate = GPIO_AF10_OTG_FS; + + HAL_GPIO_Init(GPIOA, &gpio_init); // USB D+ + + // HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET); + + gpio_init.Pin = GPIO_PIN_11; + gpio_init.Pull = GPIO_NOPULL; + HAL_GPIO_Init(GPIOA, &gpio_init); // USB D- + + // gpio_init.Mode = GPIO_MODE_INPUT; + // gpio_init.Pin = GPIO_PIN_9; + // gpio_init.Speed = GPIO_SPEED_FREQ_HIGH; + // gpio_init.Pull = GPIO_NOPULL; + // gpio_init.Alternate = 0; + + // HAL_GPIO_Init(GPIOA, &gpio_init); // USB VBUSSENSE +} + +// --------------- + +// initialize USB subsystem +void usbdrv_init() { + NVIC_DisableIRQ(OTG_FS_IRQn); + + usbdrv_init_global_state(); + usbdrv_gpio_init(); + usbdrv_periph_init(); + usbdrv_initial_ep0_setup(); + usbdrv_power_and_connect(true); + + NVIC_EnableIRQ(OTG_FS_IRQn); +} + +void usbdrv_reset() { + usbdrv_init(); +} + +// --------------- + +// initialize global state +void usbdrv_init_global_state() { + // clear state + memset(&gs, 0, sizeof(USBDRV_GlobalState)); + + // initialize receive buffer + gs.rx_buf = rx_buf; + gs.rx_buf_level = 0; + + // initialize event queue + gs.event_queue = Q_CREATE_T(USB_EVENT_QUEUE_LENGTH, USBDRV_EventCompound, event_queue_mem); +} + +// --------------- + +#define USB_LINESPEED_FULL_SPEED (0b11) + +// initialize USB peripheral +void usbdrv_periph_init() { + __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); // enable clock on USB peripheral + //__HAL_RCC_USB_OTG_FS_FORCE_RESET(); + //__HAL_RCC_USB_OTG_FS_RELEASE_RESET(); + + CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // power down the peripheral + + CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); // mask all interrupts for now + CLEAR_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP); // disable HNP and SRP + WRITE_FIELD(USBG->GUSBCFG, USB_OTG_GUSBCFG_TRDT, 0x06); // set TRDT according to the RM + WRITE_FIELD(USBG->GUSBCFG, USB_OTG_GUSBCFG_TOCAL, 0x07); // set TOCAL + SET_BIT(USBG->GUSBCFG, USB_OTG_GUSBCFG_FDMOD); // force Device mode + SET_BIT(USBG->GCCFG, USB_OTG_GCCFG_NOVBUSSENS); // turn off VBUSSENSE + + // HAL_Delay(50); // it takes time to forcing Device mode takes effect + + SET_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS); // soft disconnect peripheral (should be upper, but since it's controlled by a Device register, cannot be set before switching to device mode) + WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DSPD, USB_LINESPEED_FULL_SPEED); // there's no other possible option + + // allow specific interrupts + uint32_t intmask = /*USB_OTG_GINTMSK_WUIM | // Wake up */ + USB_OTG_GINTMSK_OEPINT | // OUT EP events + USB_OTG_GINTMSK_IEPINT | // IN EP events + USB_OTG_GINTMSK_ENUMDNEM | // (Linespeed) Enumeration (negotiation) done + USB_OTG_GINTMSK_USBRST | // USB Reset + USB_OTG_GINTMSK_USBSUSPM | // USB Suspend + USB_OTG_GINTMSK_RXFLVLM; // RX FIFO level (signal if non-empty) + USBG->GINTMSK = intmask; + + // set global NAK on all Endpoints + // usbdrv_set_global_NAK(USB_IN, true); + // usbdrv_set_global_NAK(USB_OUT, true); + + // flush Tx and Rx FIFOs + usbdrv_flush_rx_fifo(); + usbdrv_flush_tx_fifo(USB_FLUSH_TX_FIFO_ALL); + + // set masks for endpoint interrupts + SET_BIT(USBD->DIEPMSK, USB_OTG_DIEPMSK_XFRCM | USB_OTG_DIEPMSK_TOM | USB_OTG_DIEPMSK_ITTXFEMSK); // transfer complete, timeout and Tx FIFO empty while receiving IN for IN EPs + SET_BIT(USBD->DOEPMSK, USB_OTG_DOEPMSK_XFRCM | USB_OTG_DOEPMSK_STUPM); // transfer complete and SETUP complete for OUT EPs + + // mask all endpoint interrupts in both directions and also clear flags + USBD->DAINTMSK = 0; + USBD->DAINT = 0; + + // enbale global interrupts + SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); +} + +// connect to or disconnect from the bus +void usbdrv_power_and_connect(bool en) { + if (en) { // ON + CLEAR_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS); + SET_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); // actually, this is power UP + } else { // OFF + SET_BIT(USBD->DCTL, USB_OTG_DCTL_SDIS); + CLEAR_BIT(USBG->GCCFG, USB_OTG_GCCFG_PWRDWN); + } +} + +// --------------- + +// preload usb endpoint config +void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg) { + USBMSG("PRELOAD: %u %s\n", ep, dir ? "IN" : "OUT"); + if (dir == USB_OUT) { + gs.ep_OUT[ep] = *cfg; + gs.ep_OUT[ep].is_configured = true; + gs.ep_OUT[ep].task_commenced = USB_EPEVT_IDLE; + } else { + gs.ep_IN[ep] = *cfg; + gs.ep_IN[ep].is_configured = true; + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } +} + +// clear endpoint config +void usbdrv_clear_endpoint_config() { + memset(&gs.ep_OUT, 0, USB_NUM_OF_ENDPOINTS * sizeof(USBDRV_EpConfig)); + memset(&gs.ep_IN, 0, USB_NUM_OF_ENDPOINTS * sizeof(USBDRV_EpConfig)); +} + +// apply preloaded endpoint configuration +void usbdrv_apply_endpoint_config() { + for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) { + // OUT EPs + if (gs.ep_OUT[i].is_configured) { + usbdrv_configure_endpoint(i, USB_OUT, gs.ep_OUT + i); + } + + // IN EPs + if (gs.ep_IN[i].is_configured) { + usbdrv_configure_endpoint(i, USB_IN, gs.ep_IN + i); + } + } +} + +// fetch endpoint configuration from descriptor dump +void usbdrv_fetch_endpoint_configuration(uint8_t config_index) { + const uint8_t *fullConfDesc = (const uint8_t *)confDescs[config_index]; // point an array to the beginning of the full configuration + const USB_ConfigurationDesc *confDesc = (const USB_ConfigurationDesc *)confDescs[config_index]; // fetch the leading configuration descriptor + + // look up endpoint descriptors + const uint8_t *iter = fullConfDesc; + while (iter < (fullConfDesc + confDesc->wTotalLength)) { + if (iter[1] == UD_Endpoint) { // Endpoint descriptor found + USB_EndpointDesc epDesc; + memcpy(&epDesc, iter, iter[0]); // fetch EP descriptor by copy, since desciptor start address is NOT aligned + USBDRV_EpConfig cfg; // fill-in configuration + cfg.max_packet_size = epDesc.wMaxPacketSize; + cfg.responding_NAK = false; + cfg.type = epDesc.bmAttributes & 0b11; + cfg.service_interval = epDesc.bInterval; + + // fetch endpoint address + uint8_t dir = (epDesc.bEndpointAddress >> 7); + uint8_t n = epDesc.bEndpointAddress & 0x7F; + + // apply configuration + usbdrv_preload_endpoint_config(n, dir, &cfg); + } + + // advance iterator using the bLength field (first byte in a descriptor) + iter += iter[0]; + } + + usbdrv_build_fifo(); + + usbdrv_apply_endpoint_config(); +} + +#define USB_MIN_FIFO_SIZE (64) +#define USB_FIFO_MARGIN (8) +#define USB_RX_FIFO_SETUP_RESERVATION_DWORDS (10) +#define USB_MIN_GROSS_TX_FIFO_SIZE (USB_MIN_EP_FIFO_SIZE + USB_FIFO_MARGIN) +#define USB_MIN_GROSS_RX_FIFO_SIZE (2 * USB_MIN_EP_FIFO_SIZE + USB_RX_FIFO_SETUP_RESERVATION_DWORDS * 4) + +// build FIFO (compute addresses) +void usbdrv_build_fifo() { + // ---- OUT ---- + uint16_t fifo_size = USB_MIN_GROSS_RX_FIFO_SIZE; // at least this large + uint16_t next_fifo_addr = 0x00; // Rx FIFO begins at address zero + for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) { // look for greatest FIFO size + if (gs.ep_OUT[i].is_configured) { + fifo_size = CEIL4(MAX(fifo_size, gs.ep_OUT[i].max_packet_size)); // compare and replace if necessary + } + } + gs.rx_fifo_size = fifo_size; // save Rx FIFO size for later + next_fifo_addr += fifo_size; // advance next FIFO address + usbdrv_set_rx_fifo_size(fifo_size); // set Rx FIFO size in hardware + + // ---- IN ---- + for (uint8_t i = 0; i < USB_NUM_OF_ENDPOINTS; i++) { + USBDRV_EpConfig *cfg = &gs.ep_IN[i]; + if (cfg->is_configured) { + cfg->fifo_size = CEIL4(MAX(USB_MIN_GROSS_TX_FIFO_SIZE, cfg->max_packet_size)); // correct FIFO size if necessary + cfg->fifo_address = next_fifo_addr; // store FIFO address + next_fifo_addr += cfg->fifo_size; // advance next address + } + } +} + +// create an initial setup for EP0 in both directions +void usbdrv_initial_ep0_setup() { + // setup EP0 OUT and IN + USBDRV_EpConfig ep_cfg; + ep_cfg.max_packet_size = 64; + ep_cfg.responding_NAK = false; + usbdrv_preload_endpoint_config(0, USB_OUT, &ep_cfg); + usbdrv_preload_endpoint_config(0, USB_IN, &ep_cfg); + + // build FIFO + usbdrv_build_fifo(); + + // configure endpoints + usbdrv_apply_endpoint_config(); + + // turn off global NAK + usbdrv_set_global_NAK(USB_IN, false); + usbdrv_set_global_NAK(USB_OUT, false); +} + +// --------------- + +// addresses of specific DIEPTXF registers, addresses from the RM +static uint32_t *USB_pDIEPTXF[4] = { + (uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x028), // DIEPTXF0 + (uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x104), // DIEPTXF1 + (uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x108), // DIEPTXF2 + (uint32_t *)(USB_OTG_FS_PERIPH_BASE + 0x10C), // DIEPTXF3 +}; + +// configure USB endpoint +void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg) { + if (dir == USB_OUT) { // ---- OUT ---- + + if (ep == 0) { // SPECIAL handling on EP0 + WRITE_FIELD(USBOUTEP[0].DOEPCTL, USB_OTG_DOEPCTL_MPSIZ, 0); // fix in 64 bytes + WRITE_FIELD(USBOUTEP[0].DOEPTSIZ, USB_OTG_DOEPTSIZ_STUPCNT, 3); // SETUP transaction stands of three packets + } else { + WRITE_FIELD(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPTYP, cfg->type); // program endpoint type + WRITE_FIELD(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_MPSIZ, cfg->max_packet_size); // program maximum packet size + SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_USBAEP); // the endpoint is active in the current configuration + } + + // ---- common for all endpoints ---- + + // program maximum packet size + // WRITE_FIELD(USBOUTEP[ep].DOEPTSIZ, USB_OTG_DOEPTSIZ_XFRSIZ, cfg->max_packet_size); // TODO: + + // enable interrupt + SET_BIT(USBD->DAINTMSK, 1 << (USB_OTG_DAINTMSK_OEPM_Pos + ep)); + + // NAK processing + if (cfg->responding_NAK) { + SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_SNAK); // send NAK + } else { + usbdrv_arm_OUT_endpoint(ep, cfg->max_packet_size); + } + } else { // ---- IN ---- + + if (ep == 0) { // SPECIAL handling on EP0 + WRITE_FIELD(USBINEP[0].DIEPCTL, USB_OTG_DIEPCTL_MPSIZ, 0); // fix in 64 bytes + } else { + WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPTYP, cfg->type); // program endpoint type + WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_MPSIZ, cfg->max_packet_size); // program maximum packet size + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_USBAEP); // the endpoint is active in the current configuration + } + + // ---- common for all endpoints ---- + + // program FIFO corresponding FIFO number + WRITE_FIELD(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_TXFNUM, ep); + + // store Tx FIFO size + uint32_t tx_fifo_config = ((cfg->fifo_size >> 2) << USB_OTG_DIEPTXF_INEPTXFD_Pos) | cfg->fifo_address; // combine size in DWORDs and address + *(USB_pDIEPTXF[ep]) = tx_fifo_config; // save + + // enable interrupt + SET_BIT(USBD->DAINTMSK, 1 << ep); + + // NAK processing + if (cfg->responding_NAK) { + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_SNAK); // send NAK + } + } +} + +// deconfigure USB endpoint +void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir) { + if (ep == 0) { // EP0 cannot be deconfigured + return; + } + + if (dir == USB_OUT) { // ---- OUT ---- + CLEAR_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_USBAEP); // deactivate endpoint + CLEAR_BIT(USBD->DAINTMSK, 1 << (USB_OTG_DAINTMSK_OEPM_Pos + ep)); // disable interrupt + } else { // ---- IN ---- + CLEAR_BIT(USBD->DAINTMSK, 1 << ep); // disable interrupt + usbdrv_flush_tx_fifo(ep); // flush Tx FIFO + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_USBAEP); // deactivate endpoint + } +} + +// --------------- + +// flush specific or all Tx FIFOs +void usbdrv_flush_tx_fifo(uint8_t n) { + WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFFLSH); // wait for previous request to conclude + WRITE_FIELD(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFNUM, n); // issue flush + WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_TXFFLSH); // wait for our request to conclude +} + +// flush the Rx FIFO +void usbdrv_flush_rx_fifo() { + WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH); + SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH); // issue flush + WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_RXFFLSH); +} + +// set Rx FIFO size +void usbdrv_set_rx_fifo_size(uint16_t size) { + USBG->GRXFSIZ = CEILDIV4(size); +} + +// --------------- + +// stall endpoint +void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall) { + USB_OTG_INEndpointTypeDef *inep = USBINEP + ep; + USB_OTG_OUTEndpointTypeDef *outep = USBOUTEP + ep; + + if (stall) { + if (dir == USB_IN) { + if (ep != 0) { // special treatment for EP0 + SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_EPDIS); // disable endpoint + } + SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_STALL); // stall endpoint + } + + if (ep != 0) { // EP0 cannot be disabled + // wait for endpoint disable to get effective + WAIT_FOR_nBIT(inep->DIEPINT, USB_OTG_DIEPINT_EPDISD); + CLEAR_BIT(inep->DIEPINT, USB_OTG_DIEPINT_EPDISD); + } + + // flush trnasmit FIFO + usbdrv_flush_tx_fifo(ep); + + // signal that the endpoint is stalled + gs.ep_IN[ep].task_commenced = USB_EPEVT_STALLED; + + } else { + if (dir == USB_IN) { + if (ep != 0) { // special treatment for EP0 + SET_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_EPENA); // enable endpoint + } + CLEAR_BIT(inep->DIEPCTL, USB_OTG_DIEPCTL_STALL); // clear endpoint stall + } + + // signal that the endpoint stalling is over + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } +} + +// set global NAK +void usbdrv_set_global_NAK(uint8_t dir, bool en) { + if (en) { + if (dir == USB_IN) { + SET_BIT(USBD->DCTL, USB_OTG_DCTL_SGINAK); + } else { + SET_BIT(USBD->DCTL, USB_OTG_DCTL_SGONAK); + } + } else { + if (dir == USB_IN) { + SET_BIT(USBD->DCTL, USB_OTG_DCTL_CGINAK); + } else { + SET_BIT(USBD->DCTL, USB_OTG_DCTL_CGONAK); + } + } +} + +// ------------------- + +// fetch received data from RX FIFO to receive buffer +void usbdrv_fetch_received_data(uint8_t ep, uint16_t len) { + if (len > 0) { + volatile uint32_t *p = USBFIFO(ep); + uint16_t len_dwords = CEILDIV4(len); + for (uint16_t i = 0; i < len_dwords; i++) { + uint32_t dword = p[i]; + uint16_t i0 = i * 4; + gs.rx_buf[i0] = dword & 0xFF; + gs.rx_buf[i0 + 1] = (dword >> 8) & 0xFF; + gs.rx_buf[i0 + 2] = (dword >> 16) & 0xFF; + gs.rx_buf[i0 + 3] = (dword >> 24) & 0xFF; + } + } + gs.rx_buf_level = len; +} + +// write data to specific endpoint FIFO +void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len) { + /*WRITE_FIELD(USBINEP[ep].DIEPTSIZ, USB_OTG_DIEPTSIZ_XFRSIZ, len); + WRITE_FIELD(USBINEP[ep].DIEPTSIZ, USB_OTG_DIEPTSIZ_PKTCNT, 1);*/ + + // endpoint is already armed + if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { + return; + } + + gs.ep_IN[ep].task_commenced = USB_EPEVT_ARMED; + + // calculate packet count based on max packet size + uint16_t mps = gs.ep_IN[ep].max_packet_size; + uint16_t packet_count = 1; // for ZLPs + if (len > 0) { // if length is nonzero + packet_count = len / mps + (((len % mps) > 0) ? 1 : 0); + } + // TODO: ZLP ending + + // program DIEPTSIZ with transfer length (TODO: currently only a single transfer!) + USBINEP[ep].DIEPTSIZ = (packet_count << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | len; + + // enable endpoint and cancel responding NAK + SET_BIT(USBINEP[ep].DIEPCTL, USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK); + + // disable ALL USB interrupts to prevent access to specific registers (see errata) + CLEAR_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); + + // push full dwords + volatile uint32_t *p = (uint32_t *)USBFIFO(ep); + uint32_t floorlen_dwords = len >> 2; + for (uint16_t i = 0; i < floorlen_dwords; i++) { + uint16_t i0 = 4 * i; + uint32_t dword = data[i0] | (data[i0 + 1] << 8) | (data[i0 + 2] << 16) | (data[i0 + 3] << 24); + p[i] = dword; + } + + // push the remaining partial dword + uint8_t rem_bytes = len & 0b11; + if (rem_bytes > 0) { + uint32_t rem_dword = 0; + uint16_t rem_start = len - rem_bytes; + for (int16_t i = len - 1; i >= rem_start; i--) { + rem_dword = (rem_dword << 8) | data[i]; + } + *p = rem_dword; + } + + // unmask USB interrupts + SET_BIT(USBG->GAHBCFG, USB_OTG_GAHBCFG_GINT); +} + +// arm OUT endpoint +void usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size) { + // if (gs.ep_OUT[ep].task_commenced == USB_EPEVT_IDLE) { // only effective if endpointis not armed (no double arming!) + // gs.ep_OUT[ep].task_commenced = USB_EPEVT_ARMED; // step in armed state + + USBOUTEP[ep].DOEPTSIZ |= USB_OTG_DOEPTSIZ_PKTCNT | size; // program DIEPTSIZ with maximum (expected) transfer length and set PCKTCNT to make ready for reception + SET_BIT(USBOUTEP[ep].DOEPCTL, USB_OTG_DOEPCTL_EPENA | USB_OTG_DOEPCTL_CNAK); // enable endpoint and clear NAK + //} +} + +// ---------------- + +void usbdrv_set_address(uint8_t addr) { + gs.address = addr; + WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.address); +} + +// ---------------- + +// push event onto the event queue +void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data) { + USBDRV_EventCompound evt_cpd; + if (evt_data != NULL) { + evt_cpd.data = *evt_data; + } + evt_cpd.code = evt_code; + q_push(gs.event_queue, &evt_cpd); +} + +// call this to process incoming events +void usbdrv_periodic_processing() { + if ((gs.event_queue != NULL) && (q_avail(gs.event_queue))) { + USBDRV_EventCompound evt_cpd; + q_top(gs.event_queue, &evt_cpd); + q_pop(gs.event_queue); + usbdrv_process_event(evt_cpd.code, NULL); + } +} + +// ---------------- + +// receive packet +void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data) { + uint32_t rxstat = USBG->GRXSTSP; // POP (not just read) latest FIFO status word + + uint8_t pckt_status = READ_FIELD(rxstat, USB_OTG_GRXSTSP_PKTSTS); // read packet status + uint8_t data_pid = READ_FIELD(rxstat, USB_OTG_GRXSTSP_DPID); // read data PID + uint8_t byte_count = READ_FIELD(rxstat, USB_OTG_GRXSTSP_BCNT); // byte count + uint8_t ep_num = READ_FIELD(rxstat, USB_OTG_GRXSTSP_EPNUM); // read endpoint number + + // copy to output structure + evt_data->rx.pckt_status = pckt_status; + evt_data->rx.data_pid = data_pid; + evt_data->rx.byte_count = byte_count; + evt_data->rx.ep_num = ep_num; + + USBMSG("%s [%u] %u\n", FIFO_STATUS_STR[pckt_status - 1], ep_num, byte_count); +} + +// always pass ALIGNED data! +__weak void usbcore_process_setup_pckt(const uint8_t *data, uint16_t size, uint8_t stage) { + return; +} + +__weak void usbcore_process_nonsetup_event(USBDRV_CallbackCompound *cbcpd) { + return; +} + +// process USB event +void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data) { + if (evt_code == USB_EVT_USB_RESET) { // reset takes precedence over anything else TODO + SET_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST); + WAIT_FOR_BIT(USBG->GRSTCTL, USB_OTG_GRSTCTL_CSRST); + usbdrv_reset(); + USBMSG("RESET\n"); + return; + } + + switch (gs.state) { + case USB_FSM_INITIAL_WAIT_SPEEDNEG: // wait for speed negotitation to conclude + if (evt_code == USB_EVT_SPEEDNEG_DONE) { + gs.state = USB_FSM_SETUP_OPERATE; // wait for speed negotiation + } + break; + case USB_FSM_SETUP_OPERATE: { // expect SETUP transactions + switch (evt_code) { + case USB_EVT_RECEPTION_DONE: { // reception done + + for (uint32_t i = 0; i < 100000; i++) { + __NOP(); + } + + USBDRV_EventData evt_data = {0}; + usbdrv_process_rx_fifo_top(&evt_data); // process rx fifo top + + // fetch data if data are available + if ((evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_DATA_RECV) || + (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV)) { + usbdrv_fetch_received_data(evt_data.rx.ep_num, evt_data.rx.byte_count); // fetch the data + } + + // act according to what we have received + if (evt_data.rx.ep_num == 0) { // EP0 special treatment + int stage = -1; + if (evt_data.rx.pckt_status == USB_PCKT_STATUS_SETUP_CPLT) { + stage = UST_SETUP; + USBMSG("--SETUP\n"); + } else if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_TRANSFER_CPLT) { + stage = UST_DATA; + + // OUT transaction has fired + if (gs.ep_OUT[0].task_commenced == USB_EPEVT_ARMED) { + gs.ep_OUT[0].task_commenced = USB_EPEVT_IDLE; + } + + USBMSG("--DATA\n"); + } + + // process setup packet + if (stage != -1) { + usbcore_process_setup_pckt(gs.rx_buf, gs.rx_buf_level, stage); + } + + //SET_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // unmask interrupt + } else { // not EP0 + if (evt_data.rx.pckt_status == USB_PCKT_STATUS_OUT_DATA_RECV) { // TODO maybe the + USBDRV_CallbackCompound cbcpd; + cbcpd.ep = evt_data.rx.ep_num; + cbcpd.dir = USB_OUT; + cbcpd.code = USB_CBC_OUT; + cbcpd.data = gs.rx_buf; + cbcpd.size = gs.rx_buf_level; + usbcore_process_nonsetup_event(&cbcpd); + } + } + break; + } + + case USB_EVT_OUT_DONE: { // some OUT operations have finished + for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { + if (gs.ep_OUT[ep].is_configured) { // if the endpoint is running + if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP)) { // setup done + SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_STUP); + USBMSG("SETUP\n"); + } + + if (READ_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC)) { // OUT transaction done + SET_BIT(USBOUTEP[ep].DOEPINT, USB_OTG_DOEPINT_XFRC); + USBMSG("OUT\n"); + + // reset commenced task state + if (gs.ep_OUT[ep].task_commenced == USB_EPEVT_ARMED) { + gs.ep_OUT[ep].task_commenced = USB_EPEVT_IDLE; + } + + usbdrv_arm_OUT_endpoint(ep, gs.ep_OUT[ep].max_packet_size); + } + } + } + } + + case USB_EVT_IN_DONE: { // some IN operations have finished + + // callback compound + USBDRV_CallbackCompound cbcpd; + cbcpd.dir = USB_IN; + cbcpd.data = NULL; + cbcpd.size = 0; + + for (uint8_t ep = 0; ep < USB_NUM_OF_ENDPOINTS; ep++) { + cbcpd.ep = ep; + if (gs.ep_IN[ep].is_configured) { // if the endpoint is running + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC)) { // timeout done + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_TOC); + USBMSG("TO\n"); + + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } + + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC)) { // IN transaction done + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_XFRC); + + // reset commenced task state + if (gs.ep_IN[ep].task_commenced == USB_EPEVT_ARMED) { + gs.ep_IN[ep].task_commenced = USB_EPEVT_IDLE; + } + + USBMSG("IN [%d]\n", ep); + + cbcpd.code = USB_CBC_IN_DONE; + usbcore_process_nonsetup_event(&cbcpd); + } + + if (READ_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE)) { // IN endpoint IN token received with Tx FIFO empty interrupt + SET_BIT(USBINEP[ep].DIEPINT, USB_OTG_DIEPINT_ITTXFE); + + // reset stalled state + if (gs.ep_IN[ep].task_commenced == USB_EPEVT_STALLED) { + usbdrv_stall_endpoint(ep, USB_IN, false); + } + + // USBMSG("IN FIFOEMPTY [%d]\n", ep); + + cbcpd.code = USB_CBC_IN_FIFOEMPTY; + usbcore_process_nonsetup_event(&cbcpd); + } + } + } + + // // set new address if it's already waiting + // if (gs.new_address != 0) { + // WRITE_FIELD(USBD->DCFG, USB_OTG_DCFG_DAD, gs.new_address); + // gs.new_address = 0; + // USBMSG("ADDR SET\n"); + // } + } + + default: + break; + } + } + + default: + break; + } +} + +// ---------------- + +// get endpoint interrupt flag +bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir) { + return (USBD->DAINT & (1 << (ep + ((dir == USB_OUT) ? USB_OTG_DAINT_OEPINT_Pos : 0)))) != 0; +} + +void OTG_FS_IRQHandler() { + uint32_t ints = USBG->GINTSTS; + + // USB reset + if (ints & USB_OTG_GINTSTS_USBRST) { + SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBRST); // clear interrupt + // usb_reset(); // reset the USB subsystem + // return; + // usbdrv_push_event(USB_EVT_USB_RESET, NULL); + return; + } + + // End of enumeration (meaning NOT the USB ENUMERATION PROCESS, + // ST calls speed negotiation the enumeration, normally this + // interrupt fires only before even communication is commenced) + if (ints & USB_OTG_GINTSTS_ENUMDNE) { + SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_ENUMDNE); // clear interrupt + usbdrv_process_event(USB_EVT_SPEEDNEG_DONE, NULL); // process event + // usbdrv_push_event(USB_EVT_SPEEDNEG_DONE, NULL); // push event + } + + // Start of Frame received (like Keep-Alive in LS mode) + // if (ints & USB_OTG_GINTSTS_SOF) { + // SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_SOF); // clear interrupt + // } + + // USB Suspend + if (ints & USB_OTG_GINTSTS_USBSUSP) { + SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_USBSUSP); // clear interrupt + USBMSG("SUSPEND\n"); + } + + // OUT endpoint interrupt + if (ints & USB_OTG_GINTSTS_OEPINT) { + usbdrv_process_event(USB_EVT_OUT_DONE, NULL); + // usbdrv_push_event(USB_EVT_OUT_DONE, NULL); + // if (USBD->DAINT & (1 << 16)) { + // if (USBOUTEP[0].DOEPINT & USB_OTG_DOEPINT_STUP) { + // CLEAR_BIT(USBOUTEP[0].DOEPINT, USB_OTG_DOEPINT_STUP); + // } + // } + } + + // RX FIFO non-empty interrupt + if (ints & USB_OTG_GINTSTS_RXFLVL) { + // SET_BIT(USBG->GINTSTS, USB_OTG_GINTSTS_RXFLVL); // clear interrupt + USBMSG("RX DONE\n"); + //CLEAR_BIT(USBG->GINTMSK, USB_OTG_GINTMSK_RXFLVLM); // mask interrupt until processing is done + usbdrv_process_event(USB_EVT_RECEPTION_DONE, NULL); // process event + // usbdrv_push_event(USB_EVT_RECEPTION_DONE, NULL); // push event + } + + // IN endpoint interrupt + if (ints & USB_OTG_GINTSTS_IEPINT) { + usbdrv_process_event(USB_EVT_IN_DONE, NULL); + // usbdrv_push_event(USB_EVT_IN_DONE, NULL); + } + + return; +} \ No newline at end of file diff --git a/usb_driver.h b/usb_driver.h new file mode 100644 index 0000000..3fcc42c --- /dev/null +++ b/usb_driver.h @@ -0,0 +1,152 @@ +#ifndef CORE_USB_USB_DRIVER +#define CORE_USB_USB_DRIVER + +#include "utils/gen_queue.h" + +#include "usb_common_types.h" + +//#define USBDBGMSG + +#define USB_NUM_OF_ENDPOINTS (4) + +#define USB_MAX_FS_PCKT_SIZE_NON_ISOCHRONOUS (64) +#define USB_MAX_FS_PCKT_SIZE_ISOCHRONOUS (1023) + +#define USB_MIN_EP_FIFO_SIZE (64) + +// endpoint configuration structure +typedef struct { + uint16_t max_packet_size; // maximum packet size + bool8_t responding_NAK; // indicates if endpoint responds with NAK + uint8_t type; // endpoint type + uint8_t service_interval; // service interval + + // calculated values + + uint8_t is_configured; // the endpoint is in use + uint16_t fifo_address; // address in the FIFO + uint16_t fifo_size; // FIFO size + + // management variables + uint8_t task_commenced; // task commenced on endpoint +} USBDRV_EpConfig; + +typedef enum { + USB_FSM_INITIAL_WAIT_SPEEDNEG = 0, // initial state, right after reset, wait for speed negotiation, must be ZERO! + USB_FSM_SETUP_OPERATE, // ready to perform setup operations +} USBDRV_FsmState; + +typedef enum { + USB_EVT_USB_RESET, // bus reset received + USB_EVT_SPEEDNEG_DONE, // linespeed negotiation (ST calls "enumeration") done + USB_EVT_RECEPTION_DONE, // received packet is waiting to be fetched from the Rx FIFO + USB_EVT_OUT_DONE, // something has happened on an OUT endpoint + USB_EVT_IN_DONE, // previously armed IN endpoint has finished packet transmission +} USBDRV_EventCode; + +// endpoint event +typedef enum { + USB_EPEVT_IDLE = 0x00, // endpoint is IDLE + USB_EPEVT_ARMED, // endpoint is armed for transmission + USB_EPEVT_STALLED, // endpoint is stalled + USB_EPEVT_NAK // endpoint responds NAK regardless of FIFO level +} USBDRV_EndpointEvent; + +// USB device state +typedef struct { + USBDRV_EpConfig ep_OUT[USB_NUM_OF_ENDPOINTS]; // OUT endpoint configs + USBDRV_EpConfig ep_IN[USB_NUM_OF_ENDPOINTS]; // IN endpoint configs + uint16_t rx_fifo_size; // Rx FIFO size in bytes + uint16_t state; // FSM state + uint8_t *rx_buf; // pointer to the receive buffer (this way declaration can be separated) + uint16_t rx_buf_level; // fill level of the rx buffer + Queue *event_queue; // event queue + uint8_t address; // device address +} USBDRV_GlobalState; + +// USB received packet status +typedef enum { USB_PCKT_STATUS_GLOBAL_OUT_NAK = 0b0001, + USB_PCKT_STATUS_OUT_DATA_RECV = 0b0010, + USB_PCKT_STATUS_OUT_TRANSFER_CPLT = 0b0011, + USB_PCKT_STATUS_SETUP_CPLT = 0b0100, + USB_PCKT_STATUS_SETUP_DATA_RECV = 0b0110, +} USBDRV_PcktStatus; + +#define USB_FLUSH_TX_FIFO_ALL (0b10000) + +// event data +typedef union { + struct { + uint8_t pckt_status; // read packet status + uint8_t data_pid; // data PID + uint8_t byte_count; // byte count + uint8_t ep_num; // read endpoint number + } rx; // receive event data +} USBDRV_EventData; + +// event compound for queueing +typedef struct { + uint32_t code; // event code + USBDRV_EventData data; // accompaining event data +} USBDRV_EventCompound; + +// callback codes +typedef enum { + USB_CBC_OUT, // OUT done! + USB_CBC_IN_DONE, // IN done! + USB_CBC_IN_FIFOEMPTY, // could not serve IN request, since Tx FIFO was empty +} USBDRV_CallbackCode; + +// callback compound +typedef struct { + uint8_t ep : 4; // endpoint number + uint8_t dir : 1; // direction + uint8_t code : 3; // event code + uint16_t size; // data size + const uint8_t *data; // data +} USBDRV_CallbackCompound; + +typedef enum { UST_SETUP, + UST_DATA } USBDRV_ControlTfStage; + +// ------------ + +void usbdrv_gpio_init(); // USB pin low level, early peripheral initialization +void usbdrv_init_global_state(); // initialize global state +void usbdrv_periph_init(); // initialize USB peripheral +void usbdrv_initial_ep0_setup(); // create an initial setup for EP0 in both directions +void usbdrv_power_and_connect(bool en); // connect to or disconnect from the bus + +void usbdrv_init(); // initialize USB subsystem +void usbdrv_reset(); // reset USB subsystem + +void usbdrv_preload_endpoint_config(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg); // preload usb endpoint config +void usbdrv_clear_endpoint_config(); // clear endpoint config +void usbdrv_apply_endpoint_config(); // apply preloaded endpoint configuration +void usbdrv_build_fifo(); // build FIFO (compute addresses) + +void usbdrv_fetch_endpoint_configuration(uint8_t config_index); // fetch endpoint configuration from descriptor dump +void usbdrv_configure_endpoint(uint8_t ep, uint8_t dir, const USBDRV_EpConfig *cfg); // configure USB endpoint +void usbdrv_deconfigure_endpoint(uint8_t ep, uint8_t dir); // deconfigure USB endpoint +void usbdrv_stall_endpoint(uint8_t ep, uint8_t dir, bool stall); // stall endpoint +void usbdrv_set_global_NAK(uint8_t dir, bool en); // set global NAK + +void usbdrv_flush_tx_fifo(uint8_t n); // flush specific or all Tx FIFOs +void usbdrv_flush_rx_fifo(); // flush the Rx FIFO +void usbdrv_set_rx_fifo_size(uint16_t size); // set Rx FIFO size + +void usbdrv_fetch_received_data(uint8_t ep, uint16_t len); // fetch received data from RX FIFO to receive buffer +void usbdrv_process_rx_fifo_top(USBDRV_EventData *evt_data); // see what's on top of Rx FIFO + +void usbdrv_arm_IN_endpoint(uint8_t ep, const uint8_t *data, uint16_t len); // write data to specific endpoint FIFO +#define USBDRV_ARM_IN_ZLP(ep) usbdrv_arm_IN_endpoint((ep), NULL, 0) +void usbdrv_arm_OUT_endpoint(uint8_t ep, uint8_t size); // arm OUT endpoint +bool usbdrv_get_endpoint_interrupt_flag(uint8_t ep, uint8_t dir); // get endpoint interrupt flag + +void usbdrv_set_address(uint8_t addr); // set device address + +void usbdrv_process_event(uint8_t evt_code, USBDRV_EventData *evt_data); // process USB event +void usbdrv_push_event(uint32_t evt_code, USBDRV_EventData *evt_data); // push event onto the event queue +void usbdrv_periodic_processing(); // call this to process incoming events + +#endif /* CORE_USB_USB_DRIVER */ diff --git a/utils/gen_queue.c b/utils/gen_queue.c new file mode 100644 index 0000000..d697bcf --- /dev/null +++ b/utils/gen_queue.c @@ -0,0 +1,51 @@ +#include "gen_queue.h" + +#include +#include + +Queue *q_create(uint32_t length, uint32_t elemSize, uint8_t *mem) { + Queue *q = (Queue *)mem; + q->length = length; + q->elemSize = elemSize; + q->readIdx = 0; + q->writeIdx = 0; + memset(q->elements, 0, length * elemSize); + + return q; +} + +void q_clear(Queue *q) { + q->readIdx = 0; + q->writeIdx = 0; + memset(q->elements, 0, q->length * q->elemSize); +} + +uint32_t q_avail(const Queue *q) { + return q->writeIdx - q->readIdx; +} + +#define MQ_NEXT(size, current) (((current) + 1) % (size)) + +bool q_push(Queue *q, const void *src) { + if (MQ_NEXT(q->length, q->writeIdx) == q->readIdx) { // cannot push, queue is full + return false; + } + + // can push + memcpy(q->elements + q->writeIdx * q->elemSize, src, q->elemSize); + + // advance write pointer + q->writeIdx = MQ_NEXT(q->length, q->writeIdx); + + return true; +} + +void q_top(Queue *q, void *dest) { + memcpy(dest, q->elements + q->readIdx, q->elemSize); +} + +void q_pop(Queue *q) { + if (q_avail(q) > 0) { // if there's something to pop + q->readIdx = MQ_NEXT(q->length, q->readIdx); + } +} diff --git a/utils/gen_queue.h b/utils/gen_queue.h new file mode 100644 index 0000000..364f15f --- /dev/null +++ b/utils/gen_queue.h @@ -0,0 +1,72 @@ +#ifndef CORE_USB_UTILS_GE_QUEUE +#define CORE_USB_UTILS_GE_QUEUE + +#include +#include + +/** + * Generic Queue. + */ +typedef struct { + uint32_t writeIdx; ///< Next block to write + uint32_t readIdx; ///< Next block to read + uint32_t length; ///< Size of circular buffer + uint32_t elemSize; ///< Element size + uint8_t elements[]; ///< Array of packets +} Queue; + +/** + * Create Queue. + * @param length length of circular buffer + * @param elemSize element size + * @param mem backing memory, should be DWORD-aligned! + * @return pointer to Queue instance OR NULL on failure + */ +Queue *q_create(uint32_t length, uint32_t elemSize, uint8_t *mem); + +/** + * Create Queue based on storage type. + */ +#define Q_CREATE_T(length, T, mem) q_create((length), sizeof(T), (mem)) + +/** + * @brief Calculate required memory size for allocating a new queue fitting + * length elements of type T. + */ +#define Q_REQ_MEM_SIZE_T(length, T) (sizeof(Queue) + (length * sizeof(T))) + +/** + * Clear circular buffer. + * @param q pointer to Queue + */ +void q_clear(Queue *q); + +/** + * Get number of available elements. + * @param q pointer to Queue + * @return number of available elements + */ +uint32_t q_avail(const Queue *q); + +/** + * Push element to the Queue. + * @param q pointer to Queue + * @param raw pointer to raw packet + * @return true on success, false on failure (e.g.: queue full) + */ +bool q_push(Queue *q, const void *src); + +/** + * Get top element. + * @param q pointer to Queue + * @return top element (COPY, NOT POINTER!) + */ +void q_top(Queue *q, void *dest); + +/** + * Pop top element. + * @param q pointer to Queue + */ +void q_pop(Queue *q); + +#endif /* CORE_USB_UTILS_GE_QUEUE */