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