Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d272589
neccessary -> necessary and fix linter bot issue
piotrfila Jan 10, 2026
3e3b6de
scan unhandled buffer linearly
piotrfila Jan 10, 2026
b587b46
redo endpoint handling
piotrfila Jan 10, 2026
a653711
separate getting usb rx data and requesting more
piotrfila Jan 10, 2026
9504866
make CdcClassDriver rx interrupt-safe
piotrfila Jan 10, 2026
52ea727
add usb packet length type
piotrfila Jan 10, 2026
b655989
make CdcClass driver tx interrupt-safe
piotrfila Jan 10, 2026
503c998
allow partial usb tx and require calling ep_writev and ep_listen only…
piotrfila Jan 10, 2026
93ce596
documentation and configurable handler names
piotrfila Jan 10, 2026
f210076
first draft of an example driver that explains the comptime driver in…
piotrfila Jan 10, 2026
942aa42
more error checking around interfaces and string descriptors
piotrfila Jan 10, 2026
340d9c3
endpoint_open -> ep_open for consistency
piotrfila Jan 10, 2026
9eabcab
style fixes
piotrfila Jan 10, 2026
870f0d6
appease linter bot
piotrfila Jan 11, 2026
2a79c78
add u32 little endian wrapper
piotrfila Jan 11, 2026
f553d34
respect endianness in setup packet and shortcuts for bulk and interru…
piotrfila Jan 11, 2026
2ed6b4d
cleanum usb examples
piotrfila Jan 13, 2026
cd4e348
change the example usb driver to a simple echo
piotrfila Jan 13, 2026
0959d37
assign endpoints and interfaces (more) automatically
piotrfila Jan 13, 2026
36f02ea
better handling of device class, subclass and protocol
piotrfila Jan 13, 2026
b7bed05
separate usb device and controller
piotrfila Jan 13, 2026
93cbc87
more convenient descriptor creation
piotrfila Jan 13, 2026
55921cc
handle bus resets correctly
piotrfila Jan 13, 2026
bbe968f
more examples cleanup
piotrfila Jan 13, 2026
baee980
add BOS descriptor
piotrfila Jan 17, 2026
ae7d203
simplify setup packet handling
piotrfila Jan 17, 2026
a6e8c89
add usb device logging
piotrfila Jan 17, 2026
66f0116
better use scoped logging
piotrfila Jan 17, 2026
f07fe5c
reorganize ClassSubclassProtocol
piotrfila Jan 17, 2026
c40f907
adhere more to style guidelines
piotrfila Jan 17, 2026
b9f61f5
...again
piotrfila Jan 17, 2026
4c399ef
add enums to CDC
piotrfila Jan 18, 2026
3f494d0
cleanup
piotrfila Jan 18, 2026
5079a5b
Merge branch 'main' into usb-driver-rework
piotrfila Jan 18, 2026
6474eab
EpNum -> EP_Num and fix pins in examples
piotrfila Jan 18, 2026
6f6ba02
implement GetStatus
piotrfila Jan 18, 2026
872e794
driver handler type safety
piotrfila Jan 19, 2026
aa8034d
inline get_setup_packet()
piotrfila Jan 19, 2026
7667ea7
fix ep_writev only using the first buffer
piotrfila Jan 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
579 changes: 349 additions & 230 deletions core/src/core/usb.zig

Large diffs are not rendered by default.

95 changes: 60 additions & 35 deletions core/src/core/usb/descriptor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ pub const Type = enum(u8) {
Interface = 0x04,
Endpoint = 0x05,
DeviceQualifier = 0x06,
InterfaceAssociation = 0x0b,
InterfaceAssociation = 0x0B,
BOS = 0x0F,
CsDevice = 0x21,
CsConfig = 0x22,
CsString = 0x23,
Expand All @@ -29,22 +30,6 @@ pub const Type = enum(u8) {
/// Describes a device. This is the most broad description in USB and is
/// typically the first thing the host asks for.
pub const Device = extern struct {
/// Class, subclass and protocol of device.
pub const DeviceTriple = extern struct {
/// Class of device, giving a broad functional area.
class: types.ClassCode,
/// Subclass of device, refining the class.
subclass: u8,
/// Protocol within the subclass.
protocol: u8,

pub const unspecified: @This() = .{
.class = .Unspecified,
.subclass = 0,
.protocol = 0,
};
};

/// USB Device Qualifier Descriptor
/// This descriptor is a subset of the DeviceDescriptor
pub const Qualifier = extern struct {
Expand All @@ -57,9 +42,9 @@ pub const Device = extern struct {
/// Type of this descriptor, must be `DeviceQualifier`.
descriptor_type: Type = .DeviceQualifier,
/// Specification version as Binary Coded Decimal
bcd_usb: types.U16Le,
bcd_usb: types.U16_Le,
/// Class, subclass and protocol of device.
device_triple: DeviceTriple,
device_triple: types.ClassSubclassProtocol,
/// Maximum unit of data this device can move.
max_packet_size0: u8,
/// Number of configurations supported by this device.
Expand All @@ -77,17 +62,17 @@ pub const Device = extern struct {
/// Type of this descriptor, must be `Device`.
descriptor_type: Type = .Device,
/// Specification version as Binary Coded Decimal
bcd_usb: types.U16Le,
bcd_usb: types.U16_Le,
/// Class, subclass and protocol of device.
device_triple: DeviceTriple,
device_triple: types.ClassSubclassProtocol,
/// Maximum length of data this device can move.
max_packet_size0: u8,
/// ID of product vendor.
vendor: types.U16Le,
vendor: types.U16_Le,
/// ID of product.
product: types.U16Le,
product: types.U16_Le,
/// Device version number as Binary Coded Decimal.
bcd_device: types.U16Le,
bcd_device: types.U16_Le,
/// Index of manufacturer name in string descriptor table.
manufacturer_s: u8,
/// Index of product name in string descriptor table.
Expand Down Expand Up @@ -144,7 +129,7 @@ pub const Configuration = extern struct {
/// Total length of all descriptors in this configuration, concatenated.
/// This will include this descriptor, plus at least one interface
/// descriptor, plus each interface descriptor's endpoint descriptors.
total_length: types.U16Le,
total_length: types.U16_Le,
/// Number of interface descriptors in this configuration.
num_interfaces: u8,
/// Number to use when requesting this configuration via a
Expand All @@ -167,13 +152,13 @@ pub const String = struct {

data: []const u8,

pub fn from_lang(lang: Language) @This() {
pub fn from_lang(comptime lang: Language) @This() {
const ret: *const extern struct {
length: u8 = @sizeOf(@This()),
descriptor_type: Type = .String,
lang: types.U16Le,
lang: types.U16_Le,
} = comptime &.{ .lang = .from(@intFromEnum(lang)) };
return .{ .data = @ptrCast(ret) };
return .{ .data = std.mem.asBytes(ret) };
}

pub fn from_str(comptime string: []const u8) @This() {
Expand Down Expand Up @@ -221,10 +206,37 @@ pub const Endpoint = extern struct {
/// control the transfer type using the values from `TransferType`.
attributes: Attributes,
/// Maximum packet size this endpoint can accept/produce.
max_packet_size: types.U16Le,
max_packet_size: types.U16_Le,
/// Interval for polling interrupt/isochronous endpoints (which we don't
/// currently support) in milliseconds.
interval: u8,

pub fn control(ep: types.Endpoint, max_packet_size: types.Len) @This() {
return .{
.endpoint = ep,
.attributes = .{ .transfer_type = .Control, .usage = .data },
.max_packet_size = .from(max_packet_size),
.interval = 0, // Unused for bulk endpoints
};
}

pub fn bulk(ep: types.Endpoint, max_packet_size: types.Len) @This() {
return .{
.endpoint = ep,
.attributes = .{ .transfer_type = .Bulk, .usage = .data },
.max_packet_size = .from(max_packet_size),
.interval = 0, // Unused for bulk endpoints
};
}

pub fn interrupt(ep: types.Endpoint, max_packet_size: types.Len, poll_interval: u8) @This() {
return .{
.endpoint = ep,
.attributes = .{ .transfer_type = .Interrupt, .usage = .data },
.max_packet_size = .from(max_packet_size),
.interval = poll_interval,
};
}
};

/// Description of an interface within a configuration.
Expand All @@ -245,12 +257,8 @@ pub const Interface = extern struct {
alternate_setting: u8,
/// Number of endpoint descriptors in this interface.
num_endpoints: u8,
/// Interface class code, distinguishing the type of interface.
interface_class: u8,
/// Interface subclass code, refining the class of interface.
interface_subclass: u8,
/// Protocol within the interface class/subclass.
interface_protocol: u8,
/// Interface class, subclass and protocol.
interface_triple: types.ClassSubclassProtocol,
/// Index of interface name within string descriptor table.
interface_s: u8,
};
Expand Down Expand Up @@ -280,3 +288,20 @@ pub const InterfaceAssociation = extern struct {
// Index of the string descriptor describing the associated interfaces.
function: u8,
};

pub const BOS = struct {
pub const Object = union(enum) {};

data: []const u8,

pub fn from(comptime objects: []const Object) @This() {
const data: []const u8 = "";
const header: []const u8 = std.mem.asBytes(&extern struct {
length: u8 = @sizeOf(@This()),
descriptor_type: Type = .BOS,
total_length: types.U16_Le = .from(@sizeOf(@This()) + data.len),
num_descriptors: u8 = @intCast(objects.len),
}{});
return .{ .data = header ++ data };
}
};
32 changes: 29 additions & 3 deletions core/src/core/usb/descriptor/cdc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,21 @@ pub const Header = extern struct {
descriptor_subtype: SubType = .Header,
// USB Class Definitions for Communication Devices Specification release
// number in binary-coded decimal. Typically 0x01_10.
bcd_cdc: types.U16Le,
bcd_cdc: types.U16_Le,
};

pub const CallManagement = extern struct {
pub const Capabilities = packed struct(u8) {
handles_call_mgmt: bool,
call_mgmt_over_data: bool,
reserved: u6 = 0,

pub const none: @This() = .{
.handles_call_mgmt = false,
.call_mgmt_over_data = false,
};
};

comptime {
assert(@alignOf(@This()) == 1);
assert(@sizeOf(@This()) == 5);
Expand All @@ -37,12 +48,27 @@ pub const CallManagement = extern struct {
// Subtype of this descriptor, must be `CallManagement`.
descriptor_subtype: SubType = .CallManagement,
// Capabilities. Should be 0x00 for use as a serial device.
capabilities: u8,
capabilities: Capabilities,
// Data interface number.
data_interface: u8,
};

pub const AbstractControlModel = extern struct {
pub const Capabilities = packed struct(u8) {
/// Device supports the request combination of Set_Comm_Feature,
/// Clear_Comm_Feature, and Get_Comm_Feature
comm_feature: bool,
/// Device supports the request combination of
/// Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding,
/// and the notification Serial_State
line_coding: bool,
/// Device supports the request Send_Break
send_break: bool,
/// Device supports the notification Network_Connection
network_connection: bool,
reserved: u4 = 0,
};

comptime {
assert(@alignOf(@This()) == 1);
assert(@sizeOf(@This()) == 4);
Expand All @@ -54,7 +80,7 @@ pub const AbstractControlModel = extern struct {
// Subtype of this descriptor, must be `AbstractControlModel`.
descriptor_subtype: SubType = .AbstractControlModel,
// Capabilities. Should be 0x02 for use as a serial device.
capabilities: u8,
capabilities: Capabilities,
};

pub const Union = extern struct {
Expand Down
11 changes: 7 additions & 4 deletions core/src/core/usb/descriptor/hid.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ pub const RequestType = enum(u8) {
SetReport = 0x09,
SetIdle = 0x0a,
SetProtocol = 0x0b,
_,
};

/// USB HID descriptor
pub const Hid = extern struct {
pub const HID = extern struct {
/// HID country codes
pub const CountryCode = enum(u8) {
NotSupported = 0,
Expand Down Expand Up @@ -53,12 +54,14 @@ pub const Hid = extern struct {
Us,
Yugoslavia,
TurkishF,
_,
};

pub const Type = enum(u8) {
Hid = 0x21,
HID = 0x21,
Report = 0x22,
Physical = 0x23,
_,
};

comptime {
Expand All @@ -70,15 +73,15 @@ pub const Hid = extern struct {
/// Type of this descriptor
descriptor_type: descriptor.Type = .CsDevice,
/// Numeric expression identifying the HID Class Specification release
bcd_hid: types.U16Le,
bcd_hid: types.U16_Le,
/// Numeric expression identifying country code of the localized hardware
country_code: CountryCode,
/// Numeric expression specifying the number of class descriptors
num_descriptors: u8,
/// Type of HID class report
report_type: Type = .Report,
/// The total size of the Report descriptor
report_length: types.U16Le,
report_length: types.U16_Le,
};

// +++++++++++++++++++++++++++++++++++++++++++++++++
Expand Down
Loading
Loading