initial
This commit is contained in:
		
						commit
						2348ece29c
					
				
							
								
								
									
										91
									
								
								class/cdc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								class/cdc.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,91 @@
 | 
			
		||||
#include "cdc.h"
 | 
			
		||||
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
 | 
			
		||||
#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) {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										78
									
								
								class/cdc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								class/cdc.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										4
									
								
								desc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								desc/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
.idea/
 | 
			
		||||
__pycache__/
 | 
			
		||||
*.c~
 | 
			
		||||
*.h~
 | 
			
		||||
							
								
								
									
										244
									
								
								desc/ConfigGenerator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								desc/ConfigGenerator.py
									
									
									
									
									
										Normal file
									
								
							@ -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()
 | 
			
		||||
							
								
								
									
										240
									
								
								desc/Descriptor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								desc/Descriptor.py
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										235
									
								
								desc/StructGenerator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								desc/StructGenerator.py
									
									
									
									
									
										Normal file
									
								
							@ -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)
 | 
			
		||||
							
								
								
									
										42
									
								
								desc/main.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								desc/main.py
									
									
									
									
									
										Normal file
									
								
							@ -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())
 | 
			
		||||
							
								
								
									
										75
									
								
								desc/usb_config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								desc/usb_config.json
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										189
									
								
								desc/usb_desc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								desc/usb_desc.c
									
									
									
									
									
										Normal file
									
								
							@ -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
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										91
									
								
								desc/usb_desc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								desc/usb_desc.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										227
									
								
								usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								usb.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,227 @@
 | 
			
		||||
#include "usb.h"
 | 
			
		||||
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								usb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								usb.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										33
									
								
								usb_callback_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								usb_callback_event.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
#ifndef CORE_USB_USB_CALLBACK_EVENT
 | 
			
		||||
#define CORE_USB_USB_CALLBACK_EVENT
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#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 */
 | 
			
		||||
							
								
								
									
										18
									
								
								usb_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								usb_common.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										14
									
								
								usb_common_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								usb_common_defs.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
#ifndef CORE_USB_USB_COMMON_DEFS
 | 
			
		||||
#define CORE_USB_USB_COMMON_DEFS
 | 
			
		||||
 | 
			
		||||
#include <stm32f407xx.h>
 | 
			
		||||
#include <stm32f4xx_hal.h>
 | 
			
		||||
 | 
			
		||||
#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 */
 | 
			
		||||
							
								
								
									
										9
									
								
								usb_common_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								usb_common_types.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
#ifndef CORE_USB_USB_COMMON_TYPES
 | 
			
		||||
#define CORE_USB_USB_COMMON_TYPES
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
typedef uint8_t bool8_t;
 | 
			
		||||
 | 
			
		||||
#endif /* CORE_USB_USB_COMMON_TYPES */
 | 
			
		||||
							
								
								
									
										16
									
								
								usb_core_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								usb_core_types.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										210
									
								
								usb_device_types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								usb_device_types.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										819
									
								
								usb_driver.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										819
									
								
								usb_driver.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,819 @@
 | 
			
		||||
 | 
			
		||||
#include "usb_driver.h"
 | 
			
		||||
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
 | 
			
		||||
#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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								usb_driver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								usb_driver.h
									
									
									
									
									
										Normal file
									
								
							@ -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 */
 | 
			
		||||
							
								
								
									
										51
									
								
								utils/gen_queue.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								utils/gen_queue.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
#include "gen_queue.h"
 | 
			
		||||
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								utils/gen_queue.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								utils/gen_queue.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
#ifndef CORE_USB_UTILS_GE_QUEUE
 | 
			
		||||
#define CORE_USB_UTILS_GE_QUEUE
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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 */
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user