This commit is contained in:
Wiesner András 2024-04-08 19:25:47 +02:00
commit 2348ece29c
22 changed files with 2919 additions and 0 deletions

91
class/cdc.c Normal file
View 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
View 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
View File

@ -0,0 +1,4 @@
.idea/
__pycache__/
*.c~
*.h~

244
desc/ConfigGenerator.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */