diff --git a/justfile b/justfile index 481ed9bf..1ecbecff 100644 --- a/justfile +++ b/justfile @@ -1,5 +1,7 @@ build: + # just build a single os before building all, so we can fail faster + zig-ashet build arm-ashet-vhc zig-ashet build [working-directory: 'src/kernel'] diff --git a/src/kernel/build.zig b/src/kernel/build.zig index 3163e7e8..c46e4853 100644 --- a/src/kernel/build.zig +++ b/src/kernel/build.zig @@ -32,6 +32,7 @@ pub fn build(b: *std.Build) void { const args_dep = b.dependency("args", .{}); const network_dep = b.dependency("network", .{}); const vnc_dep = b.dependency("vnc", .{}); + const tinyusb_dep = b.dependency("tinyusb", .{}); const lwip_dep = b.dependency("lwip", .{ .target = kernel_target, .optimize = .ReleaseSafe }); const libc_dep = b.dependency("foundation-libc", .{ .target = kernel_target, @@ -75,6 +76,7 @@ pub fn build(b: *std.Build) void { const turtlefont_mod = turtlefont_dep.module("turtlefont"); const ashex_mod = ashex_dep.module("ashex"); const xcvt_mod = xcvt_dep.module("cvt"); + const tinyusb_mod = tinyusb_dep.module("tinyusb"); // Build: @@ -114,6 +116,7 @@ pub fn build(b: *std.Build) void { .{ .name = "turtlefont", .module = turtlefont_mod }, .{ .name = "ashex", .module = ashex_mod }, .{ .name = "cvt", .module = xcvt_mod }, + .{ .name = "tinyusb", .module = tinyusb_mod }, // resources: .{ @@ -127,6 +130,9 @@ pub fn build(b: *std.Build) void { }, }); + const tinyusb_headers = tinyusb_dep.namedWriteFiles("include"); + kernel_mod.addIncludePath(tinyusb_headers.getDirectory()); + kernel_mod.addImport("lwip", lwip_mod); kernel_mod.addIncludePath(b.path("components/network/include")); lwip_mod.addIncludePath(b.path("components/network/include")); @@ -222,6 +228,7 @@ pub fn build(b: *std.Build) void { kernel_exe.step.dependOn(machine_info_module.root_source_file.?.generated.file.step); kernel_exe.root_module.addImport("kernel", kernel_mod); + kernel_exe.step.dependOn(&tinyusb_headers.step); // TODO(fqu): kernel_exe.root_module.code_model = .small; kernel_exe.bundle_compiler_rt = true; @@ -252,10 +259,14 @@ pub fn build(b: *std.Build) void { } else { const libc = libc_dep.artifact("foundation"); - lwip_mod.addIncludePath(libc.getEmittedIncludeTree()); - zfat_mod.addIncludePath(libc.getEmittedIncludeTree()); + const libc_include_path = libc.getEmittedIncludeTree(); + + lwip_mod.addIncludePath(libc_include_path); + zfat_mod.addIncludePath(libc_include_path); + tinyusb_mod.addIncludePath(libc_include_path); kernel_exe.linkLibrary(libc); + kernel_exe.step.dependOn(&libc.step); } b.installArtifact(kernel_exe); diff --git a/src/kernel/build.zig.zon b/src/kernel/build.zig.zon index 654361a0..e13b1c5c 100644 --- a/src/kernel/build.zig.zon +++ b/src/kernel/build.zig.zon @@ -16,6 +16,10 @@ .path = "../../vendor/lwip", }, + .tinyusb = .{ + .path = "../../vendor/tinyusb", + }, + .ashet_fs = .{ .path = "../../vendor/ashet-fs", }, diff --git a/src/kernel/components/usb.zig b/src/kernel/components/usb.zig new file mode 100644 index 00000000..9858f2ee --- /dev/null +++ b/src/kernel/components/usb.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const ashet = @import("../main.zig"); +const tinyusb = @import("usb/tinyusb.zig"); + +comptime { + // Ensure TinyUSB glue is compiled: + _ = tinyusb; +} diff --git a/src/kernel/components/usb/tinyusb.zig b/src/kernel/components/usb/tinyusb.zig new file mode 100644 index 00000000..e4aa471a --- /dev/null +++ b/src/kernel/components/usb/tinyusb.zig @@ -0,0 +1,141 @@ +const std = @import("std"); +const ashet = @import("../../main.zig"); + +const c = @cImport({ + @cInclude("tusb.h"); +}); + +const tusb_desc_endpoint_t = c.tusb_desc_endpoint_t; +const tusb_rhport_init_t = c.tusb_rhport_init_t; +const tusb_speed_t = c.tusb_speed_t; + +pub const RHPort = enum(u8) { + first = 0, + _, +}; + +pub const DeviceAddr = enum(u8) { + _, +}; + +pub const EndPointAddr = enum(u8) { + _, +}; + +// Abort a queued transfer. Note: it can only abort transfer that has not been started +// Return true if a queued transfer is aborted, false if there is no transfer to abort +export fn hcd_edpt_abort_xfer(rhport: RHPort, dev_addr: DeviceAddr, ep_addr: EndPointAddr) bool { + _ = rhport; + _ = dev_addr; + _ = ep_addr; + @panic("hcd_edpt_abort_xfer not implemented yet!"); +} + +// Open an endpoint +export fn hcd_edpt_open(rhport: RHPort, daddr: DeviceAddr, ep_desc: *const tusb_desc_endpoint_t) bool { + _ = rhport; + _ = daddr; + _ = ep_desc; + @panic("hcd_edpt_open not implemented yet!"); +} + +// Submit a transfer, when complete hcd_event_xfer_complete() must be invoked +export fn hcd_edpt_xfer(rhport: RHPort, daddr: DeviceAddr, ep_addr: EndPointAddr, buffer: [*]u8, buflen: u16) bool { + _ = rhport; + _ = daddr; + _ = ep_addr; + _ = buffer; + _ = buflen; + @panic("hcd_edpt_xfer not implemented yet!"); +} + +// Initialize controller to host mode +export fn hcd_init(rhport: RHPort, rh_init: *const tusb_rhport_init_t) bool { + _ = rhport; + _ = rh_init; + @panic("hcd_init not implemented yet!"); +} + +// HCD closes all opened endpoints belong to this device +export fn hcd_device_close(rhport: RHPort, dev_addr: DeviceAddr) void { + _ = rhport; + _ = dev_addr; + @panic("hcd_device_close not implemented yet!"); +} + +// Disable USB interrupt +export fn hcd_int_disable(rhport: RHPort) void { + _ = rhport; + @panic("hcd_int_disable not implemented yet!"); +} + +// Enable USB interrupt +export fn hcd_int_enable(rhport: RHPort) void { + _ = rhport; + @panic("hcd_int_enable not implemented yet!"); +} + +// Interrupt Handler +export fn hcd_int_handler(rhport: RHPort, in_isr: bool) void { + _ = rhport; + _ = in_isr; + @panic("hcd_int_handler not implemented yet!"); +} + +// Get the current connect status of roothub port +export fn hcd_port_connect_status(rhport: RHPort) bool { + _ = rhport; + @panic("hcd_port_connect_status not implemented yet!"); +} + +// Reset USB bus on the port. Return immediately, bus reset sequence may not be complete. +// Some port would require hcd_port_reset_end() to be invoked after 10ms to complete the reset sequence. +export fn hcd_port_reset(rhport: RHPort) void { + _ = rhport; + @panic("hcd_port_reset not implemented yet!"); +} + +// Complete bus reset sequence, may be required by some controllers +export fn hcd_port_reset_end(rhport: RHPort) void { + _ = rhport; + @panic("hcd_port_reset_end not implemented yet!"); +} + +// Get port link speed +export fn hcd_port_speed_get(rhport: RHPort) tusb_speed_t { + _ = rhport; + @panic("hcd_port_speed_get not implemented yet!"); +} + +// Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked +export fn hcd_setup_send(rhport: RHPort, daddr: DeviceAddr, setup_packet: *const [8]u8) void { + _ = rhport; + _ = daddr; + _ = setup_packet; + @panic("hcd_setup_send not implemented yet!"); +} + +// Invoked when received report from device via interrupt endpoint +// Note: if there is report ID (composite), it is 1st byte of report +export fn tuh_hid_report_received_cb(dev_addr: DeviceAddr, idx: u8, report: [*]const u8, len: u16) void { + _ = dev_addr; + _ = idx; + _ = report; + _ = len; + @panic("tuh_hid_report_received_cb not implemented yet!"); +} + +// Get current milliseconds, required by some port/configuration without RTOS +export fn tusb_time_millis_api() u32 { + return ashet.time.Instant.now().ms_since_start(); +} + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +export fn tusb_time_delay_ms_api(ms: u32) void { + // TODO: Implement thread suspending here: + + const start = tusb_time_millis_api(); + while ((tusb_time_millis_api() - start) < ms) { + // + } +} diff --git a/src/kernel/main.zig b/src/kernel/main.zig index 2da97b39..5896aa66 100644 --- a/src/kernel/main.zig +++ b/src/kernel/main.zig @@ -26,6 +26,7 @@ pub const random = @import("components/random.zig"); pub const sync = @import("components/sync.zig"); pub const pipes = @import("components/pipes.zig"); pub const ipc = @import("components/ipc.zig"); +pub const usb = @import("components/usb.zig"); pub const ports = @import("port/targets.zig"); @@ -129,8 +130,10 @@ comptime { } comptime { - // export the syscalls: + // the following all export symbols: _ = syscalls; + _ = usb; + _ = filesystem; } fn ashet_kernelMain() callconv(.C) noreturn { diff --git a/vendor/tinyusb/build.zig b/vendor/tinyusb/build.zig new file mode 100644 index 00000000..1d25984c --- /dev/null +++ b/vendor/tinyusb/build.zig @@ -0,0 +1,79 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const upstream = b.dependency("upstream", .{}); + const src_folder = upstream.path("src"); + + const tinyusb_config_h = b.addConfigHeader(.{ + .style = .blank, + .include_path = "tusb_config.h", + }, .{ + .CFG_TUSB_MCU = .OPT_MCU_NONE, + .CFG_TUSB_OS = .OPT_OS_NONE, + .CFG_TUSB_DEBUG = 0, // 1 + + .CFG_TUH_MEM_SECTION = null, // __attribute__ (( section(".usb_ram") )) + .CFG_TUH_MEM_ALIGN = .@"__attribute__ ((aligned(4)))", + + .CFG_TUH_ENABLED = true, + .CFG_TUH_MAX_SPEED = .BOARD_TUH_MAX_SPEED, + + .BOARD_TUH_RHPORT = 0, + .BOARD_TUH_MAX_SPEED = .OPT_MODE_DEFAULT_SPEED, + + .CFG_TUH_ENUMERATION_BUFSIZE = 256, + .CFG_TUH_HUB = 1, // number of supported hubs + .CFG_TUH_CDC = 1, // CDC ACM + .CFG_TUH_CDC_FTDI = 1, // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API + .CFG_TUH_CDC_CP210X = 1, // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API + .CFG_TUH_CDC_CH34X = 1, // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API + .CFG_TUH_HID = 4, // typical keyboard + mouse device can have 3-4 HID interfaces + .CFG_TUH_MSC = 2, + .CFG_TUH_VENDOR = 0, + .CFG_TUH_DEVICE_MAX = 8, + + .CFG_TUH_HID_EPIN_BUFSIZE = 64, + .CFG_TUH_HID_EPOUT_BUFSIZE = 64, + + .CFG_TUH_CDC_LINE_CONTROL_ON_ENUM = 0x03, + .CFG_TUH_CDC_LINE_CODING_ON_ENUM = .@"{ 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }", + }); + + const headers = b.addNamedWriteFiles("include"); + const deployed_config_h = headers.addCopyFile(tinyusb_config_h.getOutput(), "tusb_config.h"); + + const tinyusb_mod = b.addModule("tinyusb", .{}); + tinyusb_mod.addIncludePath(src_folder); + tinyusb_mod.addIncludePath(deployed_config_h.dirname()); + tinyusb_mod.addIncludePath(b.path("include")); + tinyusb_mod.addCSourceFiles(.{ + .root = src_folder, + .files = &tinyusb_sources, + }); +} + +const tinyusb_sources = [_][]const u8{ + "class/audio/audio_device.c", + "class/cdc/cdc_device.c", + "class/cdc/cdc_host.c", + "class/dfu/dfu_device.c", + "class/dfu/dfu_rt_device.c", + "class/hid/hid_device.c", + "class/hid/hid_host.c", + "class/midi/midi_device.c", + "class/msc/msc_device.c", + "class/msc/msc_host.c", + "class/net/ecm_rndis_device.c", + "class/net/ncm_device.c", + "class/usbtmc/usbtmc_device.c", + "class/vendor/vendor_device.c", + "class/vendor/vendor_host.c", + "class/video/video_device.c", + "common/tusb_fifo.c", + "device/usbd_control.c", + "device/usbd.c", + "host/hub.c", + "host/usbh.c", + "tusb.c", + "typec/usbc.c", +}; diff --git a/vendor/tinyusb/build.zig.zon b/vendor/tinyusb/build.zig.zon new file mode 100644 index 00000000..6e1495b3 --- /dev/null +++ b/vendor/tinyusb/build.zig.zon @@ -0,0 +1,16 @@ +.{ + .name = "tinyusb", + .version = "0.18.0", + + .dependencies = .{ + .upstream = .{ + .url = "git+https://github.com/hathach/tinyusb#0.18.0", + .hash = "1220d8ab111d7b1c3b45fa7d60436559bc250cf42ed1b09f2b1c2cf52c6a2fc6bea1", + }, + }, + + .paths = .{ + "build.zig", + "build.zig.zon", + }, +} diff --git a/vendor/tinyusb/include/stdio.h b/vendor/tinyusb/include/stdio.h new file mode 100644 index 00000000..84a1624c --- /dev/null +++ b/vendor/tinyusb/include/stdio.h @@ -0,0 +1,2 @@ +#pragma once +