diff --git a/Cargo.lock b/Cargo.lock index 7a40497..6612d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aligned" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" +checksum = "c19796bd8d477f1a9d4ac2465b464a8b1359474f06a96bb3cda650b4fca309bf" dependencies = [ "as-slice", ] @@ -21,27 +23,33 @@ dependencies = [ [[package]] name = "as-slice" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" dependencies = [ - "generic-array 0.12.3", - "generic-array 0.13.2", + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.4", "stable_deref_trait", ] [[package]] name = "atsamd-hal" version = "0.12.0" -source = "git+https://github.com/atsamd-rs/atsamd#8e507a7b76c441d87d302b107c6b06485e9b6478" +source = "git+https://github.com/atsamd-rs/atsamd#db1f9b70d351ff73546b012e23b48115e6dfed6c" dependencies = [ "atsamd21g", "bitfield", + "bitflags", "cortex-m", "embedded-hal", - "nb", + "modular-bitfield", + "nb 0.1.3", + "num-traits", "paste", "rand_core", + "seq-macro", + "typenum", "usb-device", "vcell", "void", @@ -50,7 +58,7 @@ dependencies = [ [[package]] name = "atsamd21g" version = "0.9.0" -source = "git+https://github.com/atsamd-rs/atsamd#8e507a7b76c441d87d302b107c6b06485e9b6478" +source = "git+https://github.com/atsamd-rs/atsamd#db1f9b70d351ff73546b012e23b48115e6dfed6c" dependencies = [ "bare-metal", "cortex-m", @@ -58,6 +66,12 @@ dependencies = [ "vcell", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + [[package]] name = "bare-metal" version = "0.2.5" @@ -74,7 +88,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "470e1522d257e4c76245980d27d5d279442cb18955cc6341249b904074033f4a" dependencies = [ "embedded-hal", - "nb", + "nb 0.1.3", ] [[package]] @@ -84,10 +98,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" [[package]] -name = "bytemuck" -version = "1.5.0" +name = "bitflags" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a4bad0c5981acc24bc09e532f35160f952e35422603f0563cd7a73c2c2e65a0" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytemuck" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" [[package]] name = "byteorder" @@ -97,20 +117,21 @@ checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cortex-m" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" +checksum = "88cdafeafba636c00c467ded7f1587210725a1adfab0c24028a7844b87738263" dependencies = [ "aligned", "bare-metal", + "bitfield", "volatile-register", ] [[package]] name = "cortex-m-rt" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d518da72bba39496024b62607c1d8e37bcece44b2536664f1132a73a499a28" +checksum = "980c9d0233a909f355ed297ef122f257942de5e0a2cb1c39f60684b65bcb90fb" dependencies = [ "cortex-m-rt-macros", "r0", @@ -143,7 +164,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" dependencies = [ - "nb", + "nb 0.1.3", "void", ] @@ -155,26 +176,36 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "generic-array" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" dependencies = [ "typenum", ] [[package]] name = "generic-array" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check 0.9.3", +] + [[package]] name = "itsybitsy_m0" version = "0.10.0" -source = "git+https://github.com/atsamd-rs/atsamd#8e507a7b76c441d87d302b107c6b06485e9b6478" +source = "git+https://github.com/atsamd-rs/atsamd#db1f9b70d351ff73546b012e23b48115e6dfed6c" dependencies = [ "apa102-spi", "atsamd-hal", @@ -182,7 +213,7 @@ dependencies = [ "cortex-m", "cortex-m-rt", "embedded-hal", - "nb", + "nb 0.1.3", "smart-leds", "usb-device", "usbd-hid", @@ -191,15 +222,45 @@ dependencies = [ [[package]] name = "memchr" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "nb" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.0.0", +] + +[[package]] +name = "nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" [[package]] name = "neowatch" @@ -211,7 +272,7 @@ dependencies = [ "embedded-graphics", "embedded-hal", "itsybitsy_m0", - "nb", + "nb 0.1.3", "ssd1351", "usb-device", "usbd-serial", @@ -229,34 +290,43 @@ dependencies = [ [[package]] name = "nom" -version = "5.1.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ "memchr", - "version_check 0.9.1", + "version_check 0.9.3", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", ] [[package]] name = "paste" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d65c4d95931acda4498f675e332fcbdc9a06705cd07086c510e9b6009cd1c1" +checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.3" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ "proc-macro2", ] @@ -275,9 +345,9 @@ checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" [[package]] name = "rgb" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287f3c3f8236abb92d8b7e36797f19159df4b58f0a658cc3fb6dd3004b1f3bd3" +checksum = "8fddb3b23626145d1776addfc307e1a1851f60ef6ca64f376bcb889697144cf0" dependencies = [ "bytemuck", ] @@ -307,10 +377,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] -name = "serde" -version = "1.0.122" +name = "seq-macro" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "974ef1bd2ad8a507599b336595454081ff68a9599b4890af7643c0c0ed73a62c" +checksum = "d5b3bd665f328d73d7079123bfbd14a6edd74187667b5c6f340adfc65ea9d25a" + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" [[package]] name = "smart-leds" @@ -350,15 +426,21 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "1.0.17" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2", "quote", @@ -371,7 +453,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d12b7f8b271567d6d072c49dee16b22271aabfc473e2066e3353e5af0f5230" dependencies = [ - "nom 5.1.1", + "nom 5.1.2", ] [[package]] @@ -385,21 +467,21 @@ dependencies = [ [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "usb-device" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "849eed9b4dc61a1f17ba1d7a5078ceb095b9410caa38a506eb281ed5eff12fbd" +checksum = "6be90410d4772074ea49525e2e753b65920b94b57eee21a6ef7b6a6fe6296245" [[package]] name = "usbd-hid" @@ -443,15 +525,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db75519b86287f12dcf0d171c7cf4ecc839149fe9f3b720ac4cfce52959e1dfe" dependencies = [ "embedded-hal", - "nb", + "nb 0.1.3", "usb-device", ] [[package]] name = "vcell" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876e32dcadfe563a4289e994f7cb391197f362b6315dc45e8ba4aa6f564a4b3c" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" [[package]] name = "version_check" @@ -461,9 +543,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" [[package]] name = "version_check" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] name = "void" diff --git a/Makefile.toml b/Makefile.toml index d6053ad..36ff875 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -1,20 +1,20 @@ [tasks.build] description = "Build the executable" command = "cargo" -args = ["build"] +args = ["build", "--release"] [tasks.objcopy] -decsription = "Transform the binary for upload" +description = "Transform the binary for upload" command = "arm-none-eabi-objcopy" args = [ "-O", "binary", - "target/thumbv6m-none-eabi/debug/neowatch", - "target/thumbv6m-none-eabi/debug/neowatch.bin", + "target/thumbv6m-none-eabi/release/neowatch", + "target/thumbv6m-none-eabi/release/neowatch.bin", ] dependencies = ["build"] [tasks.wait_for_tty] -decsription = "Wait for serial port to be available for upload" +description = "Wait for serial port to be available for upload" script = [ ''' #!/bin/sh @@ -32,7 +32,7 @@ done disabled = true [tasks.upload] -decsription = "Upload the program to the device" +description = "Upload the program to the device" command = "bossac" args = [ "--info", @@ -42,7 +42,7 @@ args = [ "--erase", "--write", "--verify", - "target/thumbv6m-none-eabi/debug/neowatch.bin", + "target/thumbv6m-none-eabi/release/neowatch.bin", "--reset", ] dependencies = ["objcopy", "wait_for_tty"] diff --git a/src/main.rs b/src/main.rs index 6e91ed6..a8a1280 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,15 +10,16 @@ mod rn4020; mod bl_demo; mod display_demo; +mod ring_buffer; mod usb_shell; +mod util; mod wait_delay; use hal::clock::GenericClockController; -use hal::delay::Delay; use hal::entry; use hal::pac::{interrupt, CorePeripherals, Peripherals}; use hal::prelude::*; -use usb_shell::{init, poll_usb}; +use usb_shell::{init, usb_read}; use wait_delay::WaitDelay; #[entry] @@ -38,37 +39,31 @@ fn main() -> ! { let mut blip = || { red_led.set_low().ok(); - delay.delay_ms(200u8); + delay.delay_ms(100u16); red_led.toggle(); - delay.delay_ms(200u8); + delay.delay_ms(100u16); }; - blip(); - - init( + let mut usb = init( &mut core.NVIC, peripherals.USB, &mut clocks, &mut peripherals.PM, pins.usb_dm, pins.usb_dp, - &mut pins.port, &mut blip, ); - blip(); - blip(); - blip(); - blip(); - // Flash the LED in a spin loop to demonstrate that USB is // entirely interrupt driven. loop { blip(); + usb.poll(); + //usb.println(b"woop"); } } #[interrupt] fn USB() { - poll_usb(); + usb_read(); } diff --git a/src/ring_buffer.rs b/src/ring_buffer.rs new file mode 100644 index 0000000..f02d9d6 --- /dev/null +++ b/src/ring_buffer.rs @@ -0,0 +1,48 @@ +pub struct RingBuffer { + buffer: [T; N], + start: usize, + end: usize, +} + +impl RingBuffer { + pub const fn new() -> Self { + RingBuffer { + buffer: [0; N], + start: 0, + end: 0, + } + } +} + +impl RingBuffer +where + T: Copy, +{ + pub fn push_back(&mut self, element: T) -> Result<(), T> { + let next_end = (self.end + 1) % N; + + if next_end == self.start { + return Err(element); + } + + self.buffer[self.end] = element; + self.end = next_end; + + Ok(()) + } + + pub fn pop_front(&mut self) -> Option { + if self.is_empty() { + return None; + } + + let element = self.buffer[self.start]; + self.start = (self.start + 1) % N; + + Some(element) + } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } +} diff --git a/src/usb_shell.rs b/src/usb_shell.rs index 9d91ba0..9e4dbda 100644 --- a/src/usb_shell.rs +++ b/src/usb_shell.rs @@ -1,5 +1,8 @@ mod cmds; +use crate::ring_buffer::RingBuffer; +use crate::util::cell::RacyCell; +use core::mem::MaybeUninit; use core::str::from_utf8; use core::sync::atomic::AtomicBool; use core::sync::atomic::Ordering; @@ -7,7 +10,7 @@ use cortex_m::peripheral::NVIC; use hal::clock::GenericClockController; use hal::gpio::{ v2::{PA24, PA25}, - Pin, Port, + Pin, }; use hal::pac::{interrupt, PM, USB}; use hal::usb::UsbBus; @@ -15,50 +18,53 @@ use usb_device::bus::UsbBusAllocator; use usb_device::prelude::*; use usbd_serial::{SerialPort, USB_CLASS_CDC}; -struct ShellState { +pub struct UsbShell { line_buf: [u8; 255], - buf_size: usize, + buf_len: usize, } static USB_READY: AtomicBool = AtomicBool::new(false); -static mut USB_ALLOCATOR: Option> = None; + static mut USB_BUS: Option> = None; static mut USB_SERIAL: Option> = None; -static mut SHELL_STATE: Option = None; + +static INPUT_BUFFER: RacyCell> = RacyCell::new(RingBuffer::new()); const PROMPT: &[u8] = b"[neowatch]# "; pub fn init( nvic: &mut NVIC, - //bus_allocator: UsbBusAllocator, usb: USB, clocks: &mut GenericClockController, pm: &mut PM, usb_dm: Pin>, usb_dp: Pin>, - port: &mut Port, f: &mut F, -) { +) -> UsbShell { + if USB_READY.load(Ordering::SeqCst) { + panic!("usb::init called more than once"); + } + let bus_allocator = unsafe { - USB_ALLOCATOR = Some(hal::usb_allocator(usb, clocks, pm, usb_dm, usb_dp, port)); - USB_ALLOCATOR.as_ref().unwrap() + static USB_ALLOCATOR: RacyCell>> = + RacyCell::new(MaybeUninit::uninit()); + + let allocator = &mut *USB_ALLOCATOR.get(); + *allocator = MaybeUninit::new(hal::usb_allocator(usb, clocks, pm, usb_dm, usb_dp)); + &mut *allocator.as_mut_ptr() }; f(); unsafe { - USB_SERIAL = Some(SerialPort::new(&bus_allocator)); + USB_SERIAL = Some(SerialPort::new(bus_allocator)); USB_BUS = Some( - UsbDeviceBuilder::new(&bus_allocator, UsbVidPid(0x16c0, 0x27dd)) + UsbDeviceBuilder::new(bus_allocator, UsbVidPid(0x16c0, 0x27dd)) .manufacturer("Fake company") .product("Serial port") .serial_number("TEST") .device_class(USB_CLASS_CDC) .build(), ); - SHELL_STATE = Some(ShellState { - line_buf: [0; 255], - buf_size: 0, - }); } f(); @@ -72,76 +78,184 @@ pub fn init( NVIC::unmask(interrupt::USB); f(); } + + UsbShell { + buf_len: 0, + line_buf: [0; 255], + } } -pub fn poll_usb() { +impl UsbShell { + /// Output a line to the USB serial + pub fn println(&mut self, s: &[u8]) { + internal::usb_mutex(|_bus, serial| { + internal::println(serial, self, s).ok(); + }); + } + + /// Call periodically to consume input + pub fn poll(&mut self) { + internal::usb_mutex(|_bus, serial| { + let input_buffer = unsafe { &mut *INPUT_BUFFER.get() }; + while let Some(input) = input_buffer.pop_front() { + internal::handle_input(self, serial, input).ok(); + } + }); + } +} + +/// Call from USB interrupt handler +pub fn usb_read() { + // make sure that the usb state was initialized properly if !USB_READY.load(Ordering::SeqCst) { return; } - unsafe { USB_BUS.as_mut() } - .zip(unsafe { USB_SERIAL.as_mut() }) - .zip(unsafe { SHELL_STATE.as_mut() }) - .map(|((bus, serial), state)| { - bus.poll(&mut [serial]); - let mut buf = [0u8; 64]; + fn f(bus: &mut UsbDevice, serial: &mut SerialPort) { + bus.poll(&mut [serial]); + let mut buf = [0u8; 64]; - if let Ok(count) = serial.read(&mut buf) { - for &c in buf.iter().take(count) { - match c { - b'\t' => {} - 127 | 08 /* del or backspace */ => { - if state.buf_size > 0 { - // FIXME: This is not utf-8 compatible. It should decrease - // buf_size by the size of the last unicode scalar. - state.buf_size -= 1; - // erase previous character - serial.write(&[08, b' ', 08]).unwrap(); - } - } - b'\n' | b'\r' => { - if state.buf_size > 0 { - serial.write(b"\r\n").unwrap(); - match from_utf8(&state.line_buf[..state.buf_size]) { - Ok(s) => { - let mut args = s.split(" "); - let arg0 = args.next().unwrap_or(""); + if let Ok(count) = serial.read(&mut buf) { + for &c in buf.iter().take(count) { + let input_buffer = unsafe { &mut *INPUT_BUFFER.get() }; + if let Err(_) = input_buffer.push_back(c) { + //internal::println(serial, b"error: input buffer full").ok(); + } + } - match arg0 { - "help" => cmds::help::exec(args, serial), - "echo" => cmds::echo::exec(args, serial), - _ => { - serial.write(b"Unknown command: ").unwrap(); - serial.write(arg0.as_bytes()).unwrap(); - } - } - } - Err(_) => { - serial.write(b"ERROR: not valid utf-8").unwrap(); - } + crate::wait_delay::wakeup(); + }; + } + + // SAFETY: mutual exclusion is guaranteed if this function is called from an interrupt handler + unsafe { internal::usb_with_state(f) }; +} + +mod internal { + use super::*; + + const NEWLINE: &[u8] = b"\r\n"; + + /// Acquire USB state and execute function + /// + /// User must guarantee mutual exclusion + pub(super) unsafe fn usb_with_state(f: F) + where + F: FnOnce(&mut UsbDevice, &mut SerialPort), + { + USB_BUS + .as_mut() + .zip(USB_SERIAL.as_mut()) + .map(|(bus, serial)| f(bus, serial)); + } + + /// Acquire USB state and execute function with interrupts disabled + pub(super) fn usb_mutex(f: F) + where + F: FnOnce(&mut UsbDevice, &mut SerialPort), + { + // enter critical section + NVIC::mask(interrupt::USB); + + // SAFETY: interrupts are disabled. should be good. + unsafe { usb_with_state(f) }; + + // exit critical section + unsafe { NVIC::unmask(interrupt::USB) }; + } + + pub(super) fn clear_char(serial: &mut SerialPort) -> Result<(), UsbError> { + serial.write(&[08, b' ', 08])?; + Ok(()) + } + + pub(super) fn clear_prompt( + serial: &mut SerialPort, + state: &UsbShell, + ) -> Result<(), UsbError> { + for _ in 0..(state.buf_len + PROMPT.len()) { + // FIXME: This is not utf-8 compatible. It should decrease + // buf_len by the size of the last unicode scalar. + clear_char(serial)?; + } + Ok(()) + } + + pub(super) fn show_prompt( + serial: &mut SerialPort, + state: &UsbShell, + ) -> Result<(), UsbError> { + serial.write(PROMPT)?; + serial.write(&state.line_buf[..state.buf_len])?; + Ok(()) + } + + pub(super) fn println( + serial: &mut SerialPort, + state: &UsbShell, + s: &[u8], + ) -> Result<(), UsbError> { + clear_prompt(serial, state)?; + serial.write(s)?; + serial.write(NEWLINE)?; + show_prompt(serial, state)?; + Ok(()) + } + + pub(super) fn handle_input( + state: &mut UsbShell, + serial: &mut SerialPort, + input: u8, + ) -> Result<(), UsbError> { + match input { + b'\t' => {} + 127 | 08 /* del or backspace */ => { + if state.buf_len > 0 { + // FIXME: This is not utf-8 compatible. It should decrease + // buf_len by the size of the last unicode scalar. + state.buf_len -= 1; + clear_char(serial)?; + } + } + b'\n' | b'\r' => { + if state.buf_len > 0 { + serial.write(NEWLINE)?; + match from_utf8(&state.line_buf[..state.buf_len]) { + Ok(s) => { + let mut args = s.split(" "); + let arg0 = args.next().unwrap_or(""); + + match arg0 { + "help" => cmds::help::exec(args, serial), + "echo" => cmds::echo::exec(args, serial), + _ => { + serial.write(b"Unknown command: ")?; + serial.write(arg0.as_bytes())?; } } - serial.write(b"\r\n").unwrap(); - serial.write(PROMPT).unwrap(); - state.buf_size = 0; } - _ => { - if state.buf_size >= state.line_buf.len() { - state.buf_size = 0; - serial - .write(b"\r\nERROR: line buffer size exceeded\r\n") - .unwrap(); - break; - } - - state.line_buf[state.buf_size] = c; - state.buf_size += 1; - serial.write(&[c]).unwrap(); + Err(_) => { + serial.write(b"ERROR: not valid utf-8")?; } } } + state.buf_len = 0; + serial.write(NEWLINE)?; + show_prompt(serial, state)?; + } + _ => { + if state.buf_len >= state.line_buf.len() { + state.buf_len = 0; + serial + .write(b"\r\nERROR: line buffer size exceeded\r\n")?; + } - crate::wait_delay::wakeup(); - }; - }); + state.line_buf[state.buf_len] = input; + state.buf_len += 1; + serial.write(&[input])?; + } + } + + Ok(()) + } } diff --git a/src/util/cell.rs b/src/util/cell.rs new file mode 100644 index 0000000..aee1e1f --- /dev/null +++ b/src/util/cell.rs @@ -0,0 +1,15 @@ +use core::cell::UnsafeCell; + +pub struct RacyCell(pub UnsafeCell); + +unsafe impl Sync for RacyCell {} + +impl RacyCell { + pub const fn new(inner: T) -> Self { + RacyCell(UnsafeCell::new(inner)) + } + + pub fn get(&self) -> *mut T { + self.0.get() + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs new file mode 100644 index 0000000..48433da --- /dev/null +++ b/src/util/mod.rs @@ -0,0 +1 @@ +pub mod cell; diff --git a/src/wait_delay.rs b/src/wait_delay.rs index 1b1036e..a068c86 100644 --- a/src/wait_delay.rs +++ b/src/wait_delay.rs @@ -1,18 +1,16 @@ //! Delay module for waking the system up +use core::sync::atomic::{AtomicBool, Ordering}; use cortex_m::peripheral::syst::SystClkSource; use cortex_m::peripheral::SYST; - use embedded_hal::blocking::delay::{DelayMs, DelayUs}; use itsybitsy_m0::clock::GenericClockController; use itsybitsy_m0::time::Hertz; -static mut SLEEPING: bool = false; +static SLEEPING: AtomicBool = AtomicBool::new(false); pub fn wakeup() { - unsafe { - SLEEPING = false; - } + SLEEPING.store(false, Ordering::SeqCst); } /// System timer (SysTick) as a delay provider @@ -33,6 +31,7 @@ impl WaitDelay { } /// Releases the system timer (SysTick) resource + #[allow(dead_code)] pub fn free(self) -> SYST { self.syst } @@ -63,11 +62,9 @@ impl DelayUs for WaitDelay { let mut total_rvr = us * (self.sysclock.0 / 1_000_000); - //unsafe { - // SLEEPING = true; - //} + SLEEPING.store(true, Ordering::SeqCst); - while total_rvr != 0 { + while total_rvr != 0 && SLEEPING.load(Ordering::SeqCst) { let current_rvr = if total_rvr <= MAX_RVR { total_rvr } else { @@ -81,7 +78,7 @@ impl DelayUs for WaitDelay { // Update the tracking variable while we are waiting... total_rvr -= current_rvr; - while !self.syst.has_wrapped() {} + while !self.syst.has_wrapped() && SLEEPING.load(Ordering::SeqCst) {} self.syst.disable_counter(); }