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