USB Device (#40)

wch-ch32v003
David Sugar 1 year ago committed by GitHub
parent b0b01570c1
commit 812fb44180
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {
const pio_tests = b.addTest(.{ const pio_tests = b.addTest(.{
.root_source_file = .{ .root_source_file = .{
.path = "src/hal/pio.zig", .path = "src/hal.zig",
}, },
.optimize = optimize, .optimize = optimize,
}); });
@ -69,6 +69,8 @@ pub const Examples = struct {
squarewave: *microzig.EmbeddedExecutable, squarewave: *microzig.EmbeddedExecutable,
//uart_pins: microzig.EmbeddedExecutable, //uart_pins: microzig.EmbeddedExecutable,
flash_program: *microzig.EmbeddedExecutable, flash_program: *microzig.EmbeddedExecutable,
usb_device: *microzig.EmbeddedExecutable,
usb_hid: *microzig.EmbeddedExecutable,
random: *microzig.EmbeddedExecutable, random: *microzig.EmbeddedExecutable,
pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples { pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples {

@ -0,0 +1,29 @@
#!/usr/bin/env python3
# Install python3 HID package https://pypi.org/project/hid/
import hid
# default is TinyUSB (0xcafe), Adafruit (0x239a), RaspberryPi (0x2e8a), Espressif (0x303a) VID
USB_VID = (0xcafe, 0x239a, 0x2e8a, 0x303a)
print("VID list: " + ", ".join('%02x' % v for v in USB_VID))
for vid in USB_VID:
for dict in hid.enumerate(vid):
print(dict)
dev = hid.Device(dict['vendor_id'], dict['product_id'])
if dev:
while True:
inp = input("Send text to HID Device : ").encode('utf-8')
dev.write(inp)
x = 0
l = len(inp)
r = b""
while (x < l):
str_in = dev.read(64)
r += str_in
x += 64
print("Received from HID Device:\n", r)
print("hex:\n", r.hex())

@ -0,0 +1,48 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
#
# SPDX-License-Identifier: BSD-3-Clause
#
# sudo pip3 install pyusb
import usb.core
import usb.util
# find our device
dev = usb.core.find(idVendor=0x0000, idProduct=0x0001)
# was it found?
if dev is None:
raise ValueError('Device not found')
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0, 0)]
outep = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
inep = usb.util.find_descriptor(
intf,
# match the first IN endpoint
custom_match= \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_IN)
assert inep is not None
assert outep is not None
test_string = "Hello World!"
outep.write(test_string)
from_device = inep.read(len(test_string))
print("Device Says: {}".format(''.join([chr(x) for x in from_device])))

@ -0,0 +1,173 @@
const std = @import("std");
const microzig = @import("microzig");
const rp2040 = microzig.hal;
const flash = rp2040.flash;
const time = rp2040.time;
const gpio = rp2040.gpio;
const clocks = rp2040.clocks;
const usb = rp2040.usb;
const led = 25;
const uart_id = 0;
const baud_rate = 115200;
const uart_tx_pin = 0;
const uart_rx_pin = 1;
// First we define two callbacks that will be used by the endpoints we define next...
fn ep1_in_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
_ = data;
// The host has collected the data we repeated onto
// EP1! Set up to receive more data on EP1.
usb.Usb.callbacks.usb_start_rx(
dc.endpoints[2], // EP1_OUT_CFG,
64,
);
}
fn ep1_out_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
// We've gotten data from the host on our custom
// EP1! Set up EP1 to repeat it.
usb.Usb.callbacks.usb_start_tx(
dc.endpoints[3], // EP1_IN_CFG,
data,
);
}
// The endpoints EP0_IN and EP0_OUT are already defined but you can
// add your own endpoints to...
pub var EP1_OUT_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.Out.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Bulk),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 2,
.buffer_control_index = 3,
.data_buffer_index = 2,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_OUT
.callback = ep1_out_callback,
};
pub var EP1_IN_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.In.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Bulk),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 1,
.buffer_control_index = 2,
.data_buffer_index = 3,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_IN
.callback = ep1_in_callback,
};
// This is our device configuration
pub var DEVICE_CONFIGURATION: usb.DeviceConfiguration = .{
.device_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.DeviceDescriptor)),
.descriptor_type = usb.DescType.Device,
.bcd_usb = 0x0110,
.device_class = 0,
.device_subclass = 0,
.device_protocol = 0,
.max_packet_size0 = 64,
.vendor = 0,
.product = 1,
.bcd_device = 0,
.manufacturer_s = 1,
.product_s = 2,
.serial_s = 0,
.num_configurations = 1,
},
.interface_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.InterfaceDescriptor)),
.descriptor_type = usb.DescType.Interface,
.interface_number = 0,
.alternate_setting = 0,
// We have two endpoints (EP0 IN/OUT don't count)
.num_endpoints = 2,
.interface_class = 0xff,
.interface_subclass = 0,
.interface_protocol = 0,
.interface_s = 0,
},
.config_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor)),
.descriptor_type = usb.DescType.Config,
.total_length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor) + @sizeOf(usb.InterfaceDescriptor) + @sizeOf(usb.EndpointDescriptor) + @sizeOf(usb.EndpointDescriptor)),
.num_interfaces = 1,
.configuration_value = 1,
.configuration_s = 0,
.attributes = 0xc0,
.max_power = 0x32,
},
.lang_descriptor = "\x04\x03\x09\x04", // length || string descriptor (0x03) || Engl (0x0409)
.descriptor_strings = &.{
// ugly unicode :|
"R\x00a\x00s\x00p\x00b\x00e\x00r\x00r\x00y\x00 \x00P\x00i\x00",
"P\x00i\x00c\x00o\x00 \x00T\x00e\x00s\x00t\x00 \x00D\x00e\x00v\x00i\x00c\x00e\x00",
},
// Here we pass all endpoints to the config
// Dont forget to pass EP0_[IN|OUT] in the order seen below!
.endpoints = .{
&usb.EP0_OUT_CFG,
&usb.EP0_IN_CFG,
&EP1_OUT_CFG,
&EP1_IN_CFG,
},
};
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
std.log.err("panic: {s}", .{message});
@breakpoint();
while (true) {}
}
pub const std_options = struct {
pub const log_level = .debug;
pub const logFn = rp2040.uart.log;
};
pub fn main() !void {
gpio.reset();
gpio.init(led);
gpio.set_direction(led, .out);
gpio.put(led, 1);
const uart = rp2040.uart.UART.init(uart_id, .{
.baud_rate = baud_rate,
.tx_pin = uart_tx_pin,
.rx_pin = uart_rx_pin,
.clock_config = rp2040.clock_config,
});
rp2040.uart.init_logger(uart);
// First we initialize the USB clock
rp2040.usb.Usb.init_clk();
// Then initialize the USB device using the configuration defined above
rp2040.usb.Usb.init_device(&DEVICE_CONFIGURATION) catch unreachable;
var old: u64 = time.get_time_since_boot().us_since_boot;
var new: u64 = 0;
while (true) {
// You can now poll for USB events
rp2040.usb.Usb.task(
false, // debug output over UART [Y/n]
) catch unreachable;
new = time.get_time_since_boot().us_since_boot;
if (new - old > 500000) {
old = new;
gpio.toggle(led);
}
}
}

@ -0,0 +1,188 @@
const std = @import("std");
const microzig = @import("microzig");
const rp2040 = microzig.hal;
const flash = rp2040.flash;
const time = rp2040.time;
const gpio = rp2040.gpio;
const clocks = rp2040.clocks;
const usb = rp2040.usb;
const led = 25;
const uart_id = 0;
const baud_rate = 115200;
const uart_tx_pin = 0;
const uart_rx_pin = 1;
// First we define two callbacks that will be used by the endpoints we define next...
fn ep1_in_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
_ = data;
// The host has collected the data we repeated onto
// EP1! Set up to receive more data on EP1.
usb.Usb.callbacks.usb_start_rx(
dc.endpoints[2], // EP1_OUT_CFG,
64,
);
}
fn ep1_out_callback(dc: *usb.DeviceConfiguration, data: []const u8) void {
// We've gotten data from the host on our custom
// EP1! Set up EP1 to repeat it.
usb.Usb.callbacks.usb_start_tx(
dc.endpoints[3], // EP1_IN_CFG,
data,
);
}
// The endpoints EP0_IN and EP0_OUT are already defined but you can
// add your own endpoints to...
pub var EP1_OUT_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.Out.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Interrupt),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 2,
.buffer_control_index = 3,
.data_buffer_index = 2,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_OUT
.callback = ep1_out_callback,
};
pub var EP1_IN_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.Dir.In.endpoint(1),
.attributes = @enumToInt(usb.TransferType.Interrupt),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = 1,
.buffer_control_index = 2,
.data_buffer_index = 3,
.next_pid_1 = false,
// The callback will be executed if we got an interrupt on EP1_IN
.callback = ep1_in_callback,
};
// This is our device configuration
pub var DEVICE_CONFIGURATION: usb.DeviceConfiguration = .{
.device_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.DeviceDescriptor)),
.descriptor_type = usb.DescType.Device,
.bcd_usb = 0x0200,
.device_class = 0,
.device_subclass = 0,
.device_protocol = 0,
.max_packet_size0 = 64,
.vendor = 0xCafe,
.product = 1,
.bcd_device = 0x0100,
// Those are indices to the descriptor strings
// Make sure to provide enough string descriptors!
.manufacturer_s = 1,
.product_s = 2,
.serial_s = 3,
.num_configurations = 1,
},
.interface_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.InterfaceDescriptor)),
.descriptor_type = usb.DescType.Interface,
.interface_number = 0,
.alternate_setting = 0,
// We have two endpoints (EP0 IN/OUT don't count)
.num_endpoints = 2,
.interface_class = 3,
.interface_subclass = 0,
.interface_protocol = 0,
.interface_s = 0,
},
.config_descriptor = &.{
.length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor)),
.descriptor_type = usb.DescType.Config,
.total_length = @intCast(u8, @sizeOf(usb.ConfigurationDescriptor) + @sizeOf(usb.InterfaceDescriptor) + @sizeOf(usb.EndpointDescriptor) + @sizeOf(usb.EndpointDescriptor)),
.num_interfaces = 1,
.configuration_value = 1,
.configuration_s = 0,
.attributes = 0xc0,
.max_power = 0x32,
},
.lang_descriptor = "\x04\x03\x09\x04", // length || string descriptor (0x03) || Engl (0x0409)
.descriptor_strings = &.{
// ugly unicode :|
//"R\x00a\x00s\x00p\x00b\x00e\x00r\x00r\x00y\x00 \x00P\x00i\x00",
&usb.utf8ToUtf16Le("Raspberry Pi"),
//"P\x00i\x00c\x00o\x00 \x00T\x00e\x00s\x00t\x00 \x00D\x00e\x00v\x00i\x00c\x00e\x00",
&usb.utf8ToUtf16Le("Pico Test Device"),
//"c\x00a\x00f\x00e\x00b\x00a\x00b\x00e\x00",
&usb.utf8ToUtf16Le("cafebabe"),
},
.hid = .{
.hid_descriptor = &.{
.bcd_hid = 0x0111,
.country_code = 0,
.num_descriptors = 1,
.report_length = 34,
},
.report_descriptor = &usb.hid.ReportDescriptorFidoU2f,
},
// Here we pass all endpoints to the config
// Dont forget to pass EP0_[IN|OUT] in the order seen below!
.endpoints = .{
&usb.EP0_OUT_CFG,
&usb.EP0_IN_CFG,
&EP1_OUT_CFG,
&EP1_IN_CFG,
},
};
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
std.log.err("panic: {s}", .{message});
@breakpoint();
while (true) {}
}
pub const std_options = struct {
pub const log_level = .debug;
pub const logFn = rp2040.uart.log;
};
pub fn main() !void {
gpio.reset();
gpio.init(led);
gpio.set_direction(led, .out);
gpio.put(led, 1);
const uart = rp2040.uart.UART.init(uart_id, .{
.baud_rate = baud_rate,
.tx_pin = uart_tx_pin,
.rx_pin = uart_rx_pin,
.clock_config = rp2040.clock_config,
});
rp2040.uart.init_logger(uart);
// First we initialize the USB clock
rp2040.usb.Usb.init_clk();
// Then initialize the USB device using the configuration defined above
rp2040.usb.Usb.init_device(&DEVICE_CONFIGURATION) catch unreachable;
var old: u64 = time.get_time_since_boot().us_since_boot;
var new: u64 = 0;
while (true) {
// You can now poll for USB events
rp2040.usb.Usb.task(
true, // debug output over UART [Y/n]
) catch unreachable;
new = time.get_time_since_boot().us_since_boot;
if (new - old > 500000) {
old = new;
gpio.toggle(led);
}
}
}

@ -16,6 +16,7 @@ pub const irq = @import("hal/irq.zig");
pub const rom = @import("hal/rom.zig"); pub const rom = @import("hal/rom.zig");
pub const flash = @import("hal/flash.zig"); pub const flash = @import("hal/flash.zig");
pub const pio = @import("hal/pio.zig"); pub const pio = @import("hal/pio.zig");
pub const usb = @import("hal/usb.zig");
pub const rand = @import("hal/random.zig"); pub const rand = @import("hal/random.zig");
pub const clock_config = clocks.GlobalConfiguration.init(.{ pub const clock_config = clocks.GlobalConfiguration.init(.{
@ -37,3 +38,8 @@ pub fn init() void {
pub fn get_cpu_id() u32 { pub fn get_cpu_id() u32 {
return SIO.CPUID.*; return SIO.CPUID.*;
} }
test "hal tests" {
_ = pio;
_ = usb;
}

@ -47,9 +47,9 @@ pub const signatures = struct {
/// Signature of memset4: Sets n bytes start at ptr to the value c and returns ptr; must be word (32-bit) aligned! /// Signature of memset4: Sets n bytes start at ptr to the value c and returns ptr; must be word (32-bit) aligned!
const memset4 = fn (ptr: [*]u32, c: u8, n: u32) [*]u32; const memset4 = fn (ptr: [*]u32, c: u8, n: u32) [*]u32;
/// Signature of memcpy: Copies n bytes starting at src to dest and returns dest. The results are undefined if the regions overlap. /// Signature of memcpy: Copies n bytes starting at src to dest and returns dest. The results are undefined if the regions overlap.
const memcpy = fn (dest: [*]u8, src: [*]u8, n: u32) [*]u8; const memcpy = fn (dest: [*]u8, src: [*]const u8, n: u32) [*]u8;
/// Signature of memcpy44: Copies n bytes starting at src to dest and returns dest; must be word (32-bit) aligned! /// Signature of memcpy44: Copies n bytes starting at src to dest and returns dest; must be word (32-bit) aligned!
const memcpy44 = fn (dest: [*]u32, src: [*]u32, n: u32) [*]u8; const memcpy44 = fn (dest: [*]u32, src: [*]const u32, n: u32) [*]u8;
/// Signature of connect_internal_flash: Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads /// Signature of connect_internal_flash: Restore all QSPI pad controls to their default state, and connect the SSI to the QSPI pads
const connect_internal_flash = fn () void; const connect_internal_flash = fn () void;
/// Signature of flash_exit_xip: First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in /// Signature of flash_exit_xip: First set up the SSI for serial-mode operations, then issue the fixed XIP exit sequence described in
@ -189,7 +189,7 @@ pub fn memset(dest: []u8, c: u8) []u8 {
} }
/// Copies n bytes from src to dest; The number of bytes copied is the size of the smaller slice /// Copies n bytes from src to dest; The number of bytes copied is the size of the smaller slice
pub fn memcpy(dest: []u8, src: []u8) []u8 { pub fn memcpy(dest: []u8, src: []const u8) []u8 {
const S = struct { const S = struct {
var f: ?*signatures.memcpy = null; var f: ?*signatures.memcpy = null;
}; };

@ -0,0 +1,647 @@
//! USB device implementation
//!
//! Inspired by cbiffle's Rust [implementation](https://github.com/cbiffle/rp2040-usb-device-in-one-file/blob/main/src/main.rs)
const std = @import("std");
const microzig = @import("microzig");
const peripherals = microzig.chip.peripherals;
/// Human Interface Device (HID)
pub const usb = microzig.core.usb;
pub const hid = usb.hid;
const rom = @import("rom.zig");
const resets = @import("resets.zig");
pub const EP0_OUT_IDX = 0;
pub const EP0_IN_IDX = 1;
// +++++++++++++++++++++++++++++++++++++++++++++++++
// User Interface
// +++++++++++++++++++++++++++++++++++++++++++++++++
/// The rp2040 usb device impl
///
/// We create a concrete implementaion by passing a handful
/// of system specific functions to Usb(). Those functions
/// are used by the abstract USB impl of microzig.
pub const Usb = usb.Usb(F);
pub const DeviceConfiguration = usb.DeviceConfiguration;
pub const DeviceDescriptor = usb.DeviceDescriptor;
pub const DescType = usb.DescType;
pub const InterfaceDescriptor = usb.InterfaceDescriptor;
pub const ConfigurationDescriptor = usb.ConfigurationDescriptor;
pub const EndpointDescriptor = usb.EndpointDescriptor;
pub const EndpointConfiguration = usb.EndpointConfiguration;
pub const Dir = usb.Dir;
pub const TransferType = usb.TransferType;
pub const utf8ToUtf16Le = usb.utf8Toutf16Le;
pub var EP0_OUT_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.EP0_OUT_ADDR,
.attributes = @enumToInt(usb.TransferType.Control),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = null,
.buffer_control_index = 1,
.data_buffer_index = 0,
.next_pid_1 = false,
};
pub var EP0_IN_CFG: usb.EndpointConfiguration = .{
.descriptor = &usb.EndpointDescriptor{
.length = @intCast(u8, @sizeOf(usb.EndpointDescriptor)),
.descriptor_type = usb.DescType.Endpoint,
.endpoint_address = usb.EP0_IN_ADDR,
.attributes = @enumToInt(usb.TransferType.Control),
.max_packet_size = 64,
.interval = 0,
},
.endpoint_control_index = null,
.buffer_control_index = 0,
.data_buffer_index = 0,
.next_pid_1 = false,
};
// +++++++++++++++++++++++++++++++++++++++++++++++++
// Reference to endpoint buffers
// +++++++++++++++++++++++++++++++++++++++++++++++++
/// USB data buffers
pub const buffers = struct {
// Address 0x100-0xfff (3840 bytes) can be used for data buffers.
const USBDPRAM_BASE = 0x50100100;
// Data buffers are 64 bytes long as this is the max normal packet size
const BUFFER_SIZE = 64;
/// EP0 buffer 0 (shared between in and out)
const USB_EP0_BUFFER0 = USBDPRAM_BASE;
/// Optional EP0 buffer 1
const USB_EP0_BUFFER1 = USBDPRAM_BASE + BUFFER_SIZE;
/// Data buffers
const USB_BUFFERS = USBDPRAM_BASE + (2 * BUFFER_SIZE);
/// Mapping to the different data buffers in DPSRAM
pub var B: usb.Buffers = .{
.ep0_buffer0 = @intToPtr([*]u8, USB_EP0_BUFFER0),
.ep0_buffer1 = @intToPtr([*]u8, USB_EP0_BUFFER1),
// We will initialize this comptime in a loop
.rest = .{
@intToPtr([*]u8, USB_BUFFERS + (0 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (1 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (2 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (3 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (4 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (5 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (6 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (7 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (8 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (9 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (10 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (11 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (12 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (13 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (14 * BUFFER_SIZE)),
@intToPtr([*]u8, USB_BUFFERS + (15 * BUFFER_SIZE)),
},
};
};
// +++++++++++++++++++++++++++++++++++++++++++++++++
// Code
// +++++++++++++++++++++++++++++++++++++++++++++++++
/// A set of functions required by the abstract USB impl to
/// create a concrete one.
pub const F = struct {
/// Initialize the USB clock to 48 MHz
///
/// This requres that the system clock has been set up before hand
/// using the 12 MHz crystal.
pub fn usb_init_clk() void {
// Bring PLL_USB up to 48MHz. PLL_USB is clocked from refclk, which we've
// already moved over to the 12MHz XOSC. We just need to make it x4 that
// clock.
//
// PLL_USB out of reset
resets.reset(&.{.pll_usb});
// Configure it:
//
// RFDIV = 1
// FBDIV = 100 => FOUTVC0 = 1200 MHz
peripherals.PLL_USB.CS.modify(.{ .REFDIV = 1 });
peripherals.PLL_USB.FBDIV_INT.modify(.{ .FBDIV_INT = 100 });
peripherals.PLL_USB.PWR.modify(.{ .PD = 0, .VCOPD = 0 });
// Wait for lock
while (peripherals.PLL_USB.CS.read().LOCK == 0) {}
// Set up post dividers to enable output
//
// POSTDIV1 = POSTDIV2 = 5
// PLL_USB FOUT = 1200 MHz / 25 = 48 MHz
peripherals.PLL_USB.PRIM.modify(.{ .POSTDIV1 = 5, .POSTDIV2 = 5 });
peripherals.PLL_USB.PWR.modify(.{ .POSTDIVPD = 0 });
// Switch usbclk to be derived from PLLUSB
peripherals.CLOCKS.CLK_USB_CTRL.modify(.{ .AUXSRC = .{ .value = .clksrc_pll_usb } });
// We now have the stable 48MHz reference clock required for USB:
}
pub fn usb_init_device(device_config: *usb.DeviceConfiguration) void {
// Bring USB out of reset
resets.reset(&.{.usbctrl});
// Clear the control portion of DPRAM. This may not be necessary -- the
// datasheet is ambiguous -- but the C examples do it, and so do we.
peripherals.USBCTRL_DPRAM.SETUP_PACKET_LOW.write_raw(0);
peripherals.USBCTRL_DPRAM.SETUP_PACKET_HIGH.write_raw(0);
peripherals.USBCTRL_DPRAM.EP1_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP1_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP2_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP2_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP3_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP3_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP4_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP4_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP5_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP5_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP6_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP6_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP7_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP7_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP8_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP8_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP9_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP9_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP10_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP10_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP11_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP11_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP12_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP12_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP13_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP13_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP14_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP14_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP15_IN_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP15_OUT_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP0_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP0_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP1_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP1_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP2_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP2_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP3_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP3_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP4_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP4_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP5_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP5_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP6_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP6_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP7_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP7_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP8_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP8_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP9_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP9_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP10_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP10_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP11_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP11_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP12_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP12_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP13_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP13_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP14_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP14_OUT_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP15_IN_BUFFER_CONTROL.write_raw(0);
peripherals.USBCTRL_DPRAM.EP15_OUT_BUFFER_CONTROL.write_raw(0);
// Mux the controller to the onboard USB PHY. I was surprised that there are
// alternatives to this, but, there are.
peripherals.USBCTRL_REGS.USB_MUXING.modify(.{
.TO_PHY = 1,
// This bit is also set in the SDK example, without any discussion. It's
// undocumented (being named does not count as being documented).
.SOFTCON = 1,
});
// Force VBUS detect. Not all RP2040 boards wire up VBUS detect, which would
// let us detect being plugged into a host (the Pi Pico, to its credit,
// does). For maximum compatibility, we'll set the hardware to always
// pretend VBUS has been detected.
peripherals.USBCTRL_REGS.USB_PWR.modify(.{
.VBUS_DETECT = 1,
.VBUS_DETECT_OVERRIDE_EN = 1,
});
// Enable controller in device mode.
peripherals.USBCTRL_REGS.MAIN_CTRL.modify(.{
.CONTROLLER_EN = 1,
.HOST_NDEVICE = 0,
});
// Request to have an interrupt (which really just means setting a bit in
// the `buff_status` register) every time a buffer moves through EP0.
peripherals.USBCTRL_REGS.SIE_CTRL.modify(.{
.EP0_INT_1BUF = 1,
});
// Enable interrupts (bits set in the `ints` register) for other conditions
// we use:
peripherals.USBCTRL_REGS.INTE.modify(.{
// A buffer is done
.BUFF_STATUS = 1,
// The host has reset us
.BUS_RESET = 1,
// We've gotten a setup request on EP0
.SETUP_REQ = 1,
});
// setup endpoints
for (device_config.endpoints) |ep| {
// EP0 doesn't have an endpoint control index; only process the other
// endpoints here.
if (ep.endpoint_control_index) |epci| {
// We need to compute the offset from the base of USB SRAM to the
// buffer we're choosing, because that's how the peripheral do.
const buf_base = @ptrToInt(buffers.B.get(ep.data_buffer_index));
const dpram_base = @ptrToInt(peripherals.USBCTRL_DPRAM);
// The offset _should_ fit in a u16, but if we've gotten something
// wrong in the past few lines, a common symptom will be integer
// overflow producing a Very Large Number,
const dpram_offset = @intCast(u16, buf_base - dpram_base);
// Configure the endpoint!
modify_endpoint_control(epci, .{
.ENABLE = 1,
// Please set the corresponding bit in buff_status when a
// buffer is done, thx.
.INTERRUPT_PER_BUFF = 1,
// Select bulk vs control (or interrupt as soon as implemented).
.ENDPOINT_TYPE = .{ .raw = @intCast(u2, ep.descriptor.attributes) },
// And, designate our buffer by its offset.
.BUFFER_ADDRESS = dpram_offset,
});
}
}
// Present full-speed device by enabling pullup on DP. This is the point
// where the host will notice our presence.
peripherals.USBCTRL_REGS.SIE_CTRL.modify(.{ .PULLUP_EN = 1 });
}
/// Configures a given endpoint to send data (device-to-host, IN) when the host
/// next asks for it.
///
/// The contents of `buffer` will be _copied_ into USB SRAM, so you can
/// reuse `buffer` immediately after this returns. No need to wait for the
/// packet to be sent.
pub fn usb_start_tx(
ep: *usb.EndpointConfiguration,
buffer: []const u8,
) void {
// It is technically possible to support longer buffers but this demo
// doesn't bother.
// TODO: assert!(buffer.len() <= 64);
// You should only be calling this on IN endpoints.
// TODO: assert!(UsbDir::of_endpoint_addr(ep.descriptor.endpoint_address) == UsbDir::In);
// Copy the given data into the corresponding ep buffer
const epbuffer = buffers.B.get(ep.data_buffer_index);
_ = rom.memcpy(epbuffer[0..buffer.len], buffer);
// Configure the IN:
const np: u1 = if (ep.next_pid_1) 1 else 0;
// The AVAILABLE bit in the buffer control register should be set
// separately to the rest of the data in the buffer control register,
// so that the rest of the data in the buffer control register is
// accurate when the AVAILABLE bit is set.
// Write the buffer information to the buffer control register
modify_buffer_control(ep.buffer_control_index, .{
.PID_0 = np, // DATA0/1, depending
.FULL_0 = 1, // We have put data in
.LENGTH_0 = @intCast(u10, buffer.len), // There are this many bytes
});
// Nop for some clock cycles
// use volatile so the compiler doesn't optimize the nops away
asm volatile (
\\ nop
\\ nop
\\ nop
);
// Set available bit
modify_buffer_control(ep.buffer_control_index, .{
.AVAILABLE_0 = 1, // The data is for the computer to use now
});
ep.next_pid_1 = !ep.next_pid_1;
}
pub fn usb_start_rx(
ep: *usb.EndpointConfiguration,
len: usize,
) void {
// It is technically possible to support longer buffers but this demo
// doesn't bother.
// TODO: assert!(len <= 64);
// You should only be calling this on OUT endpoints.
// TODO: assert!(UsbDir::of_endpoint_addr(ep.descriptor.endpoint_address) == UsbDir::Out);
// Check which DATA0/1 PID this endpoint is expecting next.
const np: u1 = if (ep.next_pid_1) 1 else 0;
// Configure the OUT:
modify_buffer_control(ep.buffer_control_index, .{
.PID_0 = np, // DATA0/1 depending
.FULL_0 = 0, // Buffer is NOT full, we want the computer to fill it
.AVAILABLE_0 = 1, // It is, however, available to be filled
.LENGTH_0 = @intCast(u10, len), // Up tho this many bytes
});
// Flip the DATA0/1 PID for the next receive
ep.next_pid_1 = !ep.next_pid_1;
}
/// Check which interrupt flags are set
pub fn get_interrupts() usb.InterruptStatus {
const ints = peripherals.USBCTRL_REGS.INTS.read();
return .{
.BuffStatus = if (ints.BUFF_STATUS == 1) true else false,
.BusReset = if (ints.BUS_RESET == 1) true else false,
.DevConnDis = if (ints.DEV_CONN_DIS == 1) true else false,
.DevSuspend = if (ints.DEV_SUSPEND == 1) true else false,
.DevResumeFromHost = if (ints.DEV_RESUME_FROM_HOST == 1) true else false,
.SetupReq = if (ints.SETUP_REQ == 1) true else false,
};
}
/// Returns a received USB setup packet
///
/// Side effect: The setup request status flag will be cleared
///
/// One can assume that this function is only called if the
/// setup request falg is set.
pub fn get_setup_packet() usb.SetupPacket {
// Clear the status flag (write-one-to-clear)
peripherals.USBCTRL_REGS.SIE_STATUS.modify(.{ .SETUP_REC = 1 });
// This assumes that the setup packet is arriving on EP0, our
// control endpoint. Which it should be. We don't have any other
// Control endpoints.
// Copy the setup packet out of its dedicated buffer at the base of
// USB SRAM. The PAC models this buffer as two 32-bit registers,
// which is, like, not _wrong_ but slightly awkward since it means
// we can't just treat it as bytes. Instead, copy it out to a byte
// array.
var setup_packet: [8]u8 = .{0} ** 8;
const spl: u32 = peripherals.USBCTRL_DPRAM.SETUP_PACKET_LOW.raw;
const sph: u32 = peripherals.USBCTRL_DPRAM.SETUP_PACKET_HIGH.raw;
_ = rom.memcpy(setup_packet[0..4], std.mem.asBytes(&spl));
_ = rom.memcpy(setup_packet[4..8], std.mem.asBytes(&sph));
// Reinterpret as setup packet
return std.mem.bytesToValue(usb.SetupPacket, &setup_packet);
}
/// Called on a bus reset interrupt
pub fn bus_reset() void {
// Acknowledge by writing the write-one-to-clear status bit.
peripherals.USBCTRL_REGS.SIE_STATUS.modify(.{ .BUS_RESET = 1 });
peripherals.USBCTRL_REGS.ADDR_ENDP.modify(.{ .ADDRESS = 0 });
}
pub fn set_address(addr: u7) void {
peripherals.USBCTRL_REGS.ADDR_ENDP.modify(.{ .ADDRESS = addr });
}
pub fn get_EPBIter(dc: *const usb.DeviceConfiguration) usb.EPBIter {
return .{
.bufbits = peripherals.USBCTRL_REGS.BUFF_STATUS.raw,
.device_config = dc,
.next = next,
};
}
};
// +++++++++++++++++++++++++++++++++++++++++++++++++
// Utility functions
// +++++++++++++++++++++++++++++++++++++++++++++++++
/// Check if the corresponding buffer is available
pub fn buffer_available(
ep: *usb.EndpointConfiguration,
) bool {
const rbc = read_raw_buffer_control(ep.buffer_control_index);
// Bit 11 of the EPn_X_BUFFER_CONTROL register represents the AVAILABLE_0 flag
return ((rbc & 0x400) == 0);
}
pub fn modify_buffer_control(
i: usize,
fields: anytype,
) void {
// haven't found a better way to handle this
switch (i) {
0 => peripherals.USBCTRL_DPRAM.EP0_IN_BUFFER_CONTROL.modify(fields),
1 => peripherals.USBCTRL_DPRAM.EP0_OUT_BUFFER_CONTROL.modify(fields),
2 => peripherals.USBCTRL_DPRAM.EP1_IN_BUFFER_CONTROL.modify(fields),
3 => peripherals.USBCTRL_DPRAM.EP1_OUT_BUFFER_CONTROL.modify(fields),
4 => peripherals.USBCTRL_DPRAM.EP2_IN_BUFFER_CONTROL.modify(fields),
5 => peripherals.USBCTRL_DPRAM.EP2_OUT_BUFFER_CONTROL.modify(fields),
6 => peripherals.USBCTRL_DPRAM.EP3_IN_BUFFER_CONTROL.modify(fields),
7 => peripherals.USBCTRL_DPRAM.EP3_OUT_BUFFER_CONTROL.modify(fields),
8 => peripherals.USBCTRL_DPRAM.EP4_IN_BUFFER_CONTROL.modify(fields),
9 => peripherals.USBCTRL_DPRAM.EP4_OUT_BUFFER_CONTROL.modify(fields),
10 => peripherals.USBCTRL_DPRAM.EP5_IN_BUFFER_CONTROL.modify(fields),
11 => peripherals.USBCTRL_DPRAM.EP5_OUT_BUFFER_CONTROL.modify(fields),
12 => peripherals.USBCTRL_DPRAM.EP6_IN_BUFFER_CONTROL.modify(fields),
13 => peripherals.USBCTRL_DPRAM.EP6_OUT_BUFFER_CONTROL.modify(fields),
14 => peripherals.USBCTRL_DPRAM.EP7_IN_BUFFER_CONTROL.modify(fields),
15 => peripherals.USBCTRL_DPRAM.EP7_OUT_BUFFER_CONTROL.modify(fields),
16 => peripherals.USBCTRL_DPRAM.EP8_IN_BUFFER_CONTROL.modify(fields),
17 => peripherals.USBCTRL_DPRAM.EP8_OUT_BUFFER_CONTROL.modify(fields),
18 => peripherals.USBCTRL_DPRAM.EP9_IN_BUFFER_CONTROL.modify(fields),
19 => peripherals.USBCTRL_DPRAM.EP9_OUT_BUFFER_CONTROL.modify(fields),
20 => peripherals.USBCTRL_DPRAM.EP10_IN_BUFFER_CONTROL.modify(fields),
21 => peripherals.USBCTRL_DPRAM.EP10_OUT_BUFFER_CONTROL.modify(fields),
22 => peripherals.USBCTRL_DPRAM.EP11_IN_BUFFER_CONTROL.modify(fields),
23 => peripherals.USBCTRL_DPRAM.EP11_OUT_BUFFER_CONTROL.modify(fields),
24 => peripherals.USBCTRL_DPRAM.EP12_IN_BUFFER_CONTROL.modify(fields),
25 => peripherals.USBCTRL_DPRAM.EP12_OUT_BUFFER_CONTROL.modify(fields),
26 => peripherals.USBCTRL_DPRAM.EP13_IN_BUFFER_CONTROL.modify(fields),
27 => peripherals.USBCTRL_DPRAM.EP13_OUT_BUFFER_CONTROL.modify(fields),
28 => peripherals.USBCTRL_DPRAM.EP14_IN_BUFFER_CONTROL.modify(fields),
29 => peripherals.USBCTRL_DPRAM.EP14_OUT_BUFFER_CONTROL.modify(fields),
30 => peripherals.USBCTRL_DPRAM.EP15_IN_BUFFER_CONTROL.modify(fields),
31 => peripherals.USBCTRL_DPRAM.EP15_OUT_BUFFER_CONTROL.modify(fields),
else => {}, // TODO: We'll just ignore it for now
}
}
pub fn read_raw_buffer_control(
i: usize,
) u32 {
// haven't found a better way to handle this
return switch (i) {
0 => peripherals.USBCTRL_DPRAM.EP0_IN_BUFFER_CONTROL.raw,
1 => peripherals.USBCTRL_DPRAM.EP0_OUT_BUFFER_CONTROL.raw,
2 => peripherals.USBCTRL_DPRAM.EP1_IN_BUFFER_CONTROL.raw,
3 => peripherals.USBCTRL_DPRAM.EP1_OUT_BUFFER_CONTROL.raw,
4 => peripherals.USBCTRL_DPRAM.EP2_IN_BUFFER_CONTROL.raw,
5 => peripherals.USBCTRL_DPRAM.EP2_OUT_BUFFER_CONTROL.raw,
6 => peripherals.USBCTRL_DPRAM.EP3_IN_BUFFER_CONTROL.raw,
7 => peripherals.USBCTRL_DPRAM.EP3_OUT_BUFFER_CONTROL.raw,
8 => peripherals.USBCTRL_DPRAM.EP4_IN_BUFFER_CONTROL.raw,
9 => peripherals.USBCTRL_DPRAM.EP4_OUT_BUFFER_CONTROL.raw,
10 => peripherals.USBCTRL_DPRAM.EP5_IN_BUFFER_CONTROL.raw,
11 => peripherals.USBCTRL_DPRAM.EP5_OUT_BUFFER_CONTROL.raw,
12 => peripherals.USBCTRL_DPRAM.EP6_IN_BUFFER_CONTROL.raw,
13 => peripherals.USBCTRL_DPRAM.EP6_OUT_BUFFER_CONTROL.raw,
14 => peripherals.USBCTRL_DPRAM.EP7_IN_BUFFER_CONTROL.raw,
15 => peripherals.USBCTRL_DPRAM.EP7_OUT_BUFFER_CONTROL.raw,
16 => peripherals.USBCTRL_DPRAM.EP8_IN_BUFFER_CONTROL.raw,
17 => peripherals.USBCTRL_DPRAM.EP8_OUT_BUFFER_CONTROL.raw,
18 => peripherals.USBCTRL_DPRAM.EP9_IN_BUFFER_CONTROL.raw,
19 => peripherals.USBCTRL_DPRAM.EP9_OUT_BUFFER_CONTROL.raw,
20 => peripherals.USBCTRL_DPRAM.EP10_IN_BUFFER_CONTROL.raw,
21 => peripherals.USBCTRL_DPRAM.EP10_OUT_BUFFER_CONTROL.raw,
22 => peripherals.USBCTRL_DPRAM.EP11_IN_BUFFER_CONTROL.raw,
23 => peripherals.USBCTRL_DPRAM.EP11_OUT_BUFFER_CONTROL.raw,
24 => peripherals.USBCTRL_DPRAM.EP12_IN_BUFFER_CONTROL.raw,
25 => peripherals.USBCTRL_DPRAM.EP12_OUT_BUFFER_CONTROL.raw,
26 => peripherals.USBCTRL_DPRAM.EP13_IN_BUFFER_CONTROL.raw,
27 => peripherals.USBCTRL_DPRAM.EP13_OUT_BUFFER_CONTROL.raw,
28 => peripherals.USBCTRL_DPRAM.EP14_IN_BUFFER_CONTROL.raw,
29 => peripherals.USBCTRL_DPRAM.EP14_OUT_BUFFER_CONTROL.raw,
30 => peripherals.USBCTRL_DPRAM.EP15_IN_BUFFER_CONTROL.raw,
31 => peripherals.USBCTRL_DPRAM.EP15_OUT_BUFFER_CONTROL.raw,
else => 0, // TODO: We'll just return 0 for now
};
}
pub fn modify_endpoint_control(
epci: usize,
fields: anytype,
) void {
// haven't found a better way to handle this
switch (epci) {
1 => peripherals.USBCTRL_DPRAM.EP1_IN_CONTROL.modify(fields),
2 => peripherals.USBCTRL_DPRAM.EP1_OUT_CONTROL.modify(fields),
3 => peripherals.USBCTRL_DPRAM.EP2_IN_CONTROL.modify(fields),
4 => peripherals.USBCTRL_DPRAM.EP2_OUT_CONTROL.modify(fields),
5 => peripherals.USBCTRL_DPRAM.EP3_IN_CONTROL.modify(fields),
6 => peripherals.USBCTRL_DPRAM.EP3_OUT_CONTROL.modify(fields),
7 => peripherals.USBCTRL_DPRAM.EP4_IN_CONTROL.modify(fields),
8 => peripherals.USBCTRL_DPRAM.EP4_OUT_CONTROL.modify(fields),
9 => peripherals.USBCTRL_DPRAM.EP5_IN_CONTROL.modify(fields),
10 => peripherals.USBCTRL_DPRAM.EP5_OUT_CONTROL.modify(fields),
11 => peripherals.USBCTRL_DPRAM.EP6_IN_CONTROL.modify(fields),
12 => peripherals.USBCTRL_DPRAM.EP6_OUT_CONTROL.modify(fields),
13 => peripherals.USBCTRL_DPRAM.EP7_IN_CONTROL.modify(fields),
14 => peripherals.USBCTRL_DPRAM.EP7_OUT_CONTROL.modify(fields),
15 => peripherals.USBCTRL_DPRAM.EP8_IN_CONTROL.modify(fields),
16 => peripherals.USBCTRL_DPRAM.EP8_OUT_CONTROL.modify(fields),
17 => peripherals.USBCTRL_DPRAM.EP9_IN_CONTROL.modify(fields),
18 => peripherals.USBCTRL_DPRAM.EP9_OUT_CONTROL.modify(fields),
19 => peripherals.USBCTRL_DPRAM.EP10_IN_CONTROL.modify(fields),
20 => peripherals.USBCTRL_DPRAM.EP10_OUT_CONTROL.modify(fields),
21 => peripherals.USBCTRL_DPRAM.EP11_IN_CONTROL.modify(fields),
22 => peripherals.USBCTRL_DPRAM.EP11_OUT_CONTROL.modify(fields),
23 => peripherals.USBCTRL_DPRAM.EP12_IN_CONTROL.modify(fields),
24 => peripherals.USBCTRL_DPRAM.EP12_OUT_CONTROL.modify(fields),
25 => peripherals.USBCTRL_DPRAM.EP13_IN_CONTROL.modify(fields),
26 => peripherals.USBCTRL_DPRAM.EP13_OUT_CONTROL.modify(fields),
27 => peripherals.USBCTRL_DPRAM.EP14_IN_CONTROL.modify(fields),
28 => peripherals.USBCTRL_DPRAM.EP14_OUT_CONTROL.modify(fields),
29 => peripherals.USBCTRL_DPRAM.EP15_IN_CONTROL.modify(fields),
30 => peripherals.USBCTRL_DPRAM.EP15_OUT_CONTROL.modify(fields),
else => {}, // TODO: We'll just ignore it for now
}
}
// -----------------------------------------------------------
pub fn next(self: *usb.EPBIter) ?usb.EPB {
if (self.last_bit) |lb| {
// Acknowledge the last handled buffer
peripherals.USBCTRL_REGS.BUFF_STATUS.write_raw(lb);
self.last_bit = null;
}
// All input buffers handled?
if (self.bufbits == 0) return null;
// Who's still outstanding? Find their bit index by counting how
// many LSBs are zero.
var lowbit_index: u5 = 0;
while ((self.bufbits >> lowbit_index) & 0x01 == 0) : (lowbit_index += 1) {}
// Remove their bit from our set.
const lowbit = @intCast(u32, 1) << lowbit_index;
self.last_bit = lowbit;
self.bufbits ^= lowbit;
// Here we exploit knowledge of the ordering of buffer control
// registers in the peripheral. Each endpoint has a pair of
// registers, so we can determine the endpoint number by:
const epnum = @intCast(u8, lowbit_index >> 1);
// Of the pair, the IN endpoint comes first, followed by OUT, so
// we can get the direction by:
const dir = if (lowbit_index & 1 == 0) usb.Dir.In else usb.Dir.Out;
const ep_addr = dir.endpoint(epnum);
// Process the buffer-done event.
// Process the buffer-done event.
//
// Scan the device table to figure out which endpoint struct
// corresponds to this address. We could use a smarter
// method here, but in practice, the number of endpoints is
// small so a linear scan doesn't kill us.
var endpoint: ?*usb.EndpointConfiguration = null;
for (self.device_config.endpoints) |ep| {
if (ep.descriptor.endpoint_address == ep_addr) {
endpoint = ep;
break;
}
}
// Buffer event for unknown EP?!
// TODO: if (endpoint == null) return EPBError.UnknownEndpoint;
// Read the buffer control register to check status.
const bc = read_raw_buffer_control(endpoint.?.buffer_control_index);
// We should only get here if we've been notified that
// the buffer is ours again. This is indicated by the hw
// _clearing_ the AVAILABLE bit.
//
// This ensures that we can return a shared reference to
// the databuffer contents without races.
// TODO: if ((bc & (1 << 10)) == 1) return EPBError.NotAvailable;
// Cool. Checks out.
// Get a pointer to the buffer in USB SRAM. This is the
// buffer _contents_. See the safety comments below.
const epbuffer = buffers.B.get(endpoint.?.data_buffer_index);
// Get the actual length of the data, which may be less
// than the buffer size.
const len = @intCast(usize, bc & 0x3ff);
// Copy the data from SRAM
return usb.EPB{
.endpoint = endpoint.?,
.buffer = epbuffer[0..len],
};
}
Loading…
Cancel
Save