From 7bfb763e0990aac1b0bc4ad95dcc55df53cdb6d9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 29 Jul 2021 13:44:51 +0200 Subject: Rename embassy-extras to embassy-hal-common --- embassy-hal-common/src/fmt.rs | 225 +++++++++++ embassy-hal-common/src/interrupt.rs | 571 ++++++++++++++++++++++++++++ embassy-hal-common/src/lib.rs | 21 + embassy-hal-common/src/macros.rs | 130 +++++++ embassy-hal-common/src/peripheral.rs | 160 ++++++++ embassy-hal-common/src/peripheral_shared.rs | 122 ++++++ embassy-hal-common/src/ring_buffer.rs | 84 ++++ embassy-hal-common/src/usb/cdc_acm.rs | 338 ++++++++++++++++ embassy-hal-common/src/usb/mod.rs | 258 +++++++++++++ embassy-hal-common/src/usb/usb_serial.rs | 310 +++++++++++++++ 10 files changed, 2219 insertions(+) create mode 100644 embassy-hal-common/src/fmt.rs create mode 100644 embassy-hal-common/src/interrupt.rs create mode 100644 embassy-hal-common/src/lib.rs create mode 100644 embassy-hal-common/src/macros.rs create mode 100644 embassy-hal-common/src/peripheral.rs create mode 100644 embassy-hal-common/src/peripheral_shared.rs create mode 100644 embassy-hal-common/src/ring_buffer.rs create mode 100644 embassy-hal-common/src/usb/cdc_acm.rs create mode 100644 embassy-hal-common/src/usb/mod.rs create mode 100644 embassy-hal-common/src/usb/usb_serial.rs (limited to 'embassy-hal-common/src') diff --git a/embassy-hal-common/src/fmt.rs b/embassy-hal-common/src/fmt.rs new file mode 100644 index 000000000..066970813 --- /dev/null +++ b/embassy-hal-common/src/fmt.rs @@ -0,0 +1,225 @@ +#![macro_use] +#![allow(unused_macros)] + +#[cfg(all(feature = "defmt", feature = "log"))] +compile_error!("You may not enable both `defmt` and `log` features."); + +macro_rules! assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } + }; +} + +macro_rules! assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_eq!($($x)*); + } + }; +} + +macro_rules! assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert_ne!($($x)*); + } + }; +} + +macro_rules! debug_assert { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert!($($x)*); + } + }; +} + +macro_rules! debug_assert_eq { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_eq!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_eq!($($x)*); + } + }; +} + +macro_rules! debug_assert_ne { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::debug_assert_ne!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::debug_assert_ne!($($x)*); + } + }; +} + +macro_rules! todo { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::todo!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::todo!($($x)*); + } + }; +} + +macro_rules! unreachable { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::unreachable!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::unreachable!($($x)*); + } + }; +} + +macro_rules! panic { + ($($x:tt)*) => { + { + #[cfg(not(feature = "defmt"))] + ::core::panic!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::panic!($($x)*); + } + }; +} + +macro_rules! trace { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::trace!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::trace!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! debug { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::debug!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::debug!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! info { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::info!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::info!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! warn { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::warn!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::warn!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +macro_rules! error { + ($s:literal $(, $x:expr)* $(,)?) => { + { + #[cfg(feature = "log")] + ::log::error!($s $(, $x)*); + #[cfg(feature = "defmt")] + ::defmt::error!($s $(, $x)*); + #[cfg(not(any(feature = "log", feature="defmt")))] + let _ = ($( & $x ),*); + } + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unwrap { + ($($x:tt)*) => { + ::defmt::unwrap!($($x)*) + }; +} + +#[cfg(not(feature = "defmt"))] +macro_rules! unwrap { + ($arg:expr) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); + } + } + }; + ($arg:expr, $($msg:expr),+ $(,)? ) => { + match $crate::fmt::Try::into_result($arg) { + ::core::result::Result::Ok(t) => t, + ::core::result::Result::Err(e) => { + ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); + } + } + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct NoneError; + +pub trait Try { + type Ok; + type Error; + fn into_result(self) -> Result; +} + +impl Try for Option { + type Ok = T; + type Error = NoneError; + + #[inline] + fn into_result(self) -> Result { + self.ok_or(NoneError) + } +} + +impl Try for Result { + type Ok = T; + type Error = E; + + #[inline] + fn into_result(self) -> Self { + self + } +} diff --git a/embassy-hal-common/src/interrupt.rs b/embassy-hal-common/src/interrupt.rs new file mode 100644 index 000000000..80b2cad5d --- /dev/null +++ b/embassy-hal-common/src/interrupt.rs @@ -0,0 +1,571 @@ +use core::mem; + +macro_rules! prio { + ($name:ident, $mask:expr, ($($k:ident = $v:expr,)*)) => { + #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + #[repr(u8)] + pub enum $name { + $($k = $v),* + } + + impl From for $name { + fn from(priority: u8) -> Self { + unsafe { mem::transmute(priority & $mask) } + } + } + + impl From<$name> for u8 { + fn from(p: $name) -> Self { + p as u8 + } + } + }; +} + +#[rustfmt::skip] +prio!(Priority0, 0x00, ( + P0 = 0x0, +)); + +#[rustfmt::skip] +prio!(Priority1, 0x80, ( + P0 = 0x0, + P1 = 0x80, +)); + +#[rustfmt::skip] +prio!(Priority2, 0xc0, ( + P0 = 0x0, + P1 = 0x40, + P2 = 0x80, + P3 = 0xc0, +)); + +#[rustfmt::skip] +prio!(Priority3, 0xe0, ( + P0 = 0x0, + P1 = 0x20, + P2 = 0x40, + P3 = 0x60, + P4 = 0x80, + P5 = 0xa0, + P6 = 0xc0, + P7 = 0xe0, +)); + +#[rustfmt::skip] +prio!(Priority4, 0xf0, ( + P0 = 0x0, + P1 = 0x10, + P2 = 0x20, + P3 = 0x30, + P4 = 0x40, + P5 = 0x50, + P6 = 0x60, + P7 = 0x70, + P8 = 0x80, + P9 = 0x90, + P10 = 0xa0, + P11 = 0xb0, + P12 = 0xc0, + P13 = 0xd0, + P14 = 0xe0, + P15 = 0xf0, +)); + +#[rustfmt::skip] +prio!(Priority5, 0xf8, ( + P0 = 0x0, + P1 = 0x8, + P2 = 0x10, + P3 = 0x18, + P4 = 0x20, + P5 = 0x28, + P6 = 0x30, + P7 = 0x38, + P8 = 0x40, + P9 = 0x48, + P10 = 0x50, + P11 = 0x58, + P12 = 0x60, + P13 = 0x68, + P14 = 0x70, + P15 = 0x78, + P16 = 0x80, + P17 = 0x88, + P18 = 0x90, + P19 = 0x98, + P20 = 0xa0, + P21 = 0xa8, + P22 = 0xb0, + P23 = 0xb8, + P24 = 0xc0, + P25 = 0xc8, + P26 = 0xd0, + P27 = 0xd8, + P28 = 0xe0, + P29 = 0xe8, + P30 = 0xf0, + P31 = 0xf8, +)); + +#[rustfmt::skip] +prio!(Priority6, 0xfc, ( + P0 = 0x0, + P1 = 0x4, + P2 = 0x8, + P3 = 0xc, + P4 = 0x10, + P5 = 0x14, + P6 = 0x18, + P7 = 0x1c, + P8 = 0x20, + P9 = 0x24, + P10 = 0x28, + P11 = 0x2c, + P12 = 0x30, + P13 = 0x34, + P14 = 0x38, + P15 = 0x3c, + P16 = 0x40, + P17 = 0x44, + P18 = 0x48, + P19 = 0x4c, + P20 = 0x50, + P21 = 0x54, + P22 = 0x58, + P23 = 0x5c, + P24 = 0x60, + P25 = 0x64, + P26 = 0x68, + P27 = 0x6c, + P28 = 0x70, + P29 = 0x74, + P30 = 0x78, + P31 = 0x7c, + P32 = 0x80, + P33 = 0x84, + P34 = 0x88, + P35 = 0x8c, + P36 = 0x90, + P37 = 0x94, + P38 = 0x98, + P39 = 0x9c, + P40 = 0xa0, + P41 = 0xa4, + P42 = 0xa8, + P43 = 0xac, + P44 = 0xb0, + P45 = 0xb4, + P46 = 0xb8, + P47 = 0xbc, + P48 = 0xc0, + P49 = 0xc4, + P50 = 0xc8, + P51 = 0xcc, + P52 = 0xd0, + P53 = 0xd4, + P54 = 0xd8, + P55 = 0xdc, + P56 = 0xe0, + P57 = 0xe4, + P58 = 0xe8, + P59 = 0xec, + P60 = 0xf0, + P61 = 0xf4, + P62 = 0xf8, + P63 = 0xfc, +)); + +#[rustfmt::skip] +prio!(Priority7, 0xfe, ( + P0 = 0x0, + P1 = 0x2, + P2 = 0x4, + P3 = 0x6, + P4 = 0x8, + P5 = 0xa, + P6 = 0xc, + P7 = 0xe, + P8 = 0x10, + P9 = 0x12, + P10 = 0x14, + P11 = 0x16, + P12 = 0x18, + P13 = 0x1a, + P14 = 0x1c, + P15 = 0x1e, + P16 = 0x20, + P17 = 0x22, + P18 = 0x24, + P19 = 0x26, + P20 = 0x28, + P21 = 0x2a, + P22 = 0x2c, + P23 = 0x2e, + P24 = 0x30, + P25 = 0x32, + P26 = 0x34, + P27 = 0x36, + P28 = 0x38, + P29 = 0x3a, + P30 = 0x3c, + P31 = 0x3e, + P32 = 0x40, + P33 = 0x42, + P34 = 0x44, + P35 = 0x46, + P36 = 0x48, + P37 = 0x4a, + P38 = 0x4c, + P39 = 0x4e, + P40 = 0x50, + P41 = 0x52, + P42 = 0x54, + P43 = 0x56, + P44 = 0x58, + P45 = 0x5a, + P46 = 0x5c, + P47 = 0x5e, + P48 = 0x60, + P49 = 0x62, + P50 = 0x64, + P51 = 0x66, + P52 = 0x68, + P53 = 0x6a, + P54 = 0x6c, + P55 = 0x6e, + P56 = 0x70, + P57 = 0x72, + P58 = 0x74, + P59 = 0x76, + P60 = 0x78, + P61 = 0x7a, + P62 = 0x7c, + P63 = 0x7e, + P64 = 0x80, + P65 = 0x82, + P66 = 0x84, + P67 = 0x86, + P68 = 0x88, + P69 = 0x8a, + P70 = 0x8c, + P71 = 0x8e, + P72 = 0x90, + P73 = 0x92, + P74 = 0x94, + P75 = 0x96, + P76 = 0x98, + P77 = 0x9a, + P78 = 0x9c, + P79 = 0x9e, + P80 = 0xa0, + P81 = 0xa2, + P82 = 0xa4, + P83 = 0xa6, + P84 = 0xa8, + P85 = 0xaa, + P86 = 0xac, + P87 = 0xae, + P88 = 0xb0, + P89 = 0xb2, + P90 = 0xb4, + P91 = 0xb6, + P92 = 0xb8, + P93 = 0xba, + P94 = 0xbc, + P95 = 0xbe, + P96 = 0xc0, + P97 = 0xc2, + P98 = 0xc4, + P99 = 0xc6, + P100 = 0xc8, + P101 = 0xca, + P102 = 0xcc, + P103 = 0xce, + P104 = 0xd0, + P105 = 0xd2, + P106 = 0xd4, + P107 = 0xd6, + P108 = 0xd8, + P109 = 0xda, + P110 = 0xdc, + P111 = 0xde, + P112 = 0xe0, + P113 = 0xe2, + P114 = 0xe4, + P115 = 0xe6, + P116 = 0xe8, + P117 = 0xea, + P118 = 0xec, + P119 = 0xee, + P120 = 0xf0, + P121 = 0xf2, + P122 = 0xf4, + P123 = 0xf6, + P124 = 0xf8, + P125 = 0xfa, + P126 = 0xfc, + P127 = 0xfe, +)); + +#[rustfmt::skip] +prio!(Priority8, 0xff, ( + P0 = 0x0, + P1 = 0x1, + P2 = 0x2, + P3 = 0x3, + P4 = 0x4, + P5 = 0x5, + P6 = 0x6, + P7 = 0x7, + P8 = 0x8, + P9 = 0x9, + P10 = 0xa, + P11 = 0xb, + P12 = 0xc, + P13 = 0xd, + P14 = 0xe, + P15 = 0xf, + P16 = 0x10, + P17 = 0x11, + P18 = 0x12, + P19 = 0x13, + P20 = 0x14, + P21 = 0x15, + P22 = 0x16, + P23 = 0x17, + P24 = 0x18, + P25 = 0x19, + P26 = 0x1a, + P27 = 0x1b, + P28 = 0x1c, + P29 = 0x1d, + P30 = 0x1e, + P31 = 0x1f, + P32 = 0x20, + P33 = 0x21, + P34 = 0x22, + P35 = 0x23, + P36 = 0x24, + P37 = 0x25, + P38 = 0x26, + P39 = 0x27, + P40 = 0x28, + P41 = 0x29, + P42 = 0x2a, + P43 = 0x2b, + P44 = 0x2c, + P45 = 0x2d, + P46 = 0x2e, + P47 = 0x2f, + P48 = 0x30, + P49 = 0x31, + P50 = 0x32, + P51 = 0x33, + P52 = 0x34, + P53 = 0x35, + P54 = 0x36, + P55 = 0x37, + P56 = 0x38, + P57 = 0x39, + P58 = 0x3a, + P59 = 0x3b, + P60 = 0x3c, + P61 = 0x3d, + P62 = 0x3e, + P63 = 0x3f, + P64 = 0x40, + P65 = 0x41, + P66 = 0x42, + P67 = 0x43, + P68 = 0x44, + P69 = 0x45, + P70 = 0x46, + P71 = 0x47, + P72 = 0x48, + P73 = 0x49, + P74 = 0x4a, + P75 = 0x4b, + P76 = 0x4c, + P77 = 0x4d, + P78 = 0x4e, + P79 = 0x4f, + P80 = 0x50, + P81 = 0x51, + P82 = 0x52, + P83 = 0x53, + P84 = 0x54, + P85 = 0x55, + P86 = 0x56, + P87 = 0x57, + P88 = 0x58, + P89 = 0x59, + P90 = 0x5a, + P91 = 0x5b, + P92 = 0x5c, + P93 = 0x5d, + P94 = 0x5e, + P95 = 0x5f, + P96 = 0x60, + P97 = 0x61, + P98 = 0x62, + P99 = 0x63, + P100 = 0x64, + P101 = 0x65, + P102 = 0x66, + P103 = 0x67, + P104 = 0x68, + P105 = 0x69, + P106 = 0x6a, + P107 = 0x6b, + P108 = 0x6c, + P109 = 0x6d, + P110 = 0x6e, + P111 = 0x6f, + P112 = 0x70, + P113 = 0x71, + P114 = 0x72, + P115 = 0x73, + P116 = 0x74, + P117 = 0x75, + P118 = 0x76, + P119 = 0x77, + P120 = 0x78, + P121 = 0x79, + P122 = 0x7a, + P123 = 0x7b, + P124 = 0x7c, + P125 = 0x7d, + P126 = 0x7e, + P127 = 0x7f, + P128 = 0x80, + P129 = 0x81, + P130 = 0x82, + P131 = 0x83, + P132 = 0x84, + P133 = 0x85, + P134 = 0x86, + P135 = 0x87, + P136 = 0x88, + P137 = 0x89, + P138 = 0x8a, + P139 = 0x8b, + P140 = 0x8c, + P141 = 0x8d, + P142 = 0x8e, + P143 = 0x8f, + P144 = 0x90, + P145 = 0x91, + P146 = 0x92, + P147 = 0x93, + P148 = 0x94, + P149 = 0x95, + P150 = 0x96, + P151 = 0x97, + P152 = 0x98, + P153 = 0x99, + P154 = 0x9a, + P155 = 0x9b, + P156 = 0x9c, + P157 = 0x9d, + P158 = 0x9e, + P159 = 0x9f, + P160 = 0xa0, + P161 = 0xa1, + P162 = 0xa2, + P163 = 0xa3, + P164 = 0xa4, + P165 = 0xa5, + P166 = 0xa6, + P167 = 0xa7, + P168 = 0xa8, + P169 = 0xa9, + P170 = 0xaa, + P171 = 0xab, + P172 = 0xac, + P173 = 0xad, + P174 = 0xae, + P175 = 0xaf, + P176 = 0xb0, + P177 = 0xb1, + P178 = 0xb2, + P179 = 0xb3, + P180 = 0xb4, + P181 = 0xb5, + P182 = 0xb6, + P183 = 0xb7, + P184 = 0xb8, + P185 = 0xb9, + P186 = 0xba, + P187 = 0xbb, + P188 = 0xbc, + P189 = 0xbd, + P190 = 0xbe, + P191 = 0xbf, + P192 = 0xc0, + P193 = 0xc1, + P194 = 0xc2, + P195 = 0xc3, + P196 = 0xc4, + P197 = 0xc5, + P198 = 0xc6, + P199 = 0xc7, + P200 = 0xc8, + P201 = 0xc9, + P202 = 0xca, + P203 = 0xcb, + P204 = 0xcc, + P205 = 0xcd, + P206 = 0xce, + P207 = 0xcf, + P208 = 0xd0, + P209 = 0xd1, + P210 = 0xd2, + P211 = 0xd3, + P212 = 0xd4, + P213 = 0xd5, + P214 = 0xd6, + P215 = 0xd7, + P216 = 0xd8, + P217 = 0xd9, + P218 = 0xda, + P219 = 0xdb, + P220 = 0xdc, + P221 = 0xdd, + P222 = 0xde, + P223 = 0xdf, + P224 = 0xe0, + P225 = 0xe1, + P226 = 0xe2, + P227 = 0xe3, + P228 = 0xe4, + P229 = 0xe5, + P230 = 0xe6, + P231 = 0xe7, + P232 = 0xe8, + P233 = 0xe9, + P234 = 0xea, + P235 = 0xeb, + P236 = 0xec, + P237 = 0xed, + P238 = 0xee, + P239 = 0xef, + P240 = 0xf0, + P241 = 0xf1, + P242 = 0xf2, + P243 = 0xf3, + P244 = 0xf4, + P245 = 0xf5, + P246 = 0xf6, + P247 = 0xf7, + P248 = 0xf8, + P249 = 0xf9, + P250 = 0xfa, + P251 = 0xfb, + P252 = 0xfc, + P253 = 0xfd, + P254 = 0xfe, + P255 = 0xff, +)); diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-common/src/lib.rs new file mode 100644 index 000000000..7036986ef --- /dev/null +++ b/embassy-hal-common/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] + +// This mod MUST go first, so that the others see its macros. +pub(crate) mod fmt; + +pub mod interrupt; +mod macros; +pub mod peripheral; +pub mod peripheral_shared; +pub mod ring_buffer; +pub mod usb; + +/// Low power blocking wait loop using WFE/SEV. +pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) { + while !condition() { + // WFE might "eat" an event that would have otherwise woken the executor. + cortex_m::asm::wfe(); + } + // Retrigger an event to be transparent to the executor. + cortex_m::asm::sev(); +} diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-common/src/macros.rs new file mode 100644 index 000000000..771db40f6 --- /dev/null +++ b/embassy-hal-common/src/macros.rs @@ -0,0 +1,130 @@ +#[macro_export] +macro_rules! peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + pub mod peripherals { + $( + $(#[$cfg])? + #[allow(non_camel_case_types)] + pub struct $name { _private: () } + + $(#[$cfg])? + impl embassy::util::Steal for $name { + #[inline] + unsafe fn steal() -> Self { + Self{ _private: ()} + } + } + + $(#[$cfg])? + unsafe impl embassy::util::Unborrow for $name { + type Target = $name; + #[inline] + unsafe fn unborrow(self) -> $name { + self + } + } + + )* + } + + #[allow(non_snake_case)] + pub struct Peripherals { + $( + $(#[$cfg])? + pub $name: peripherals::$name, + )* + } + + impl Peripherals { + ///Returns all the peripherals *once* + #[inline] + pub(crate) fn take() -> Self { + + #[no_mangle] + static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false; + + critical_section::with(|_| unsafe { + if _EMBASSY_DEVICE_PERIPHERALS { + panic!("init called more than once!") + } + _EMBASSY_DEVICE_PERIPHERALS = true; + ::steal() + }) + } + } + + impl embassy::util::Steal for Peripherals { + #[inline] + unsafe fn steal() -> Self { + Self { + $( + $(#[$cfg])? + $name: ::steal(), + )* + } + } + } + + }; +} + +#[macro_export] +macro_rules! unborrow { + ($($name:ident),*) => { + $( + let mut $name = unsafe { $name.unborrow() }; + )* + } +} + +#[macro_export] +macro_rules! unsafe_impl_unborrow { + ($type:ident) => { + unsafe impl ::embassy::util::Unborrow for $type { + type Target = $type; + #[inline] + unsafe fn unborrow(self) -> Self::Target { + self + } + } + }; +} + +#[macro_export] +macro_rules! std_peripherals { + ($($(#[$cfg:meta])? $name:ident),*$(,)?) => { + #[doc = r"All the peripherals"] + #[allow(non_snake_case)] + pub struct Peripherals { + $( + $(#[$cfg])? + pub $name: pac::$name, + )+ + } + + static mut GLOBAL_CLOCKS: Option = None; + + impl Peripherals { + pub fn take() -> Option<(Peripherals, Clocks)> { + match unsafe {GLOBAL_CLOCKS.take()} { + Some(clocks) => { + let dp = unsafe { pac::Peripherals::steal() }; + let peripherals = Peripherals { + $( + $(#[$cfg])? + $name: dp.$name, + )+ + }; + + Some((peripherals, clocks)) + }, + None => None, + } + } + + pub unsafe fn set_peripherals(clocks: Clocks) { + GLOBAL_CLOCKS.replace(clocks); + } + } + }; +} diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-common/src/peripheral.rs new file mode 100644 index 000000000..92512a0f6 --- /dev/null +++ b/embassy-hal-common/src/peripheral.rs @@ -0,0 +1,160 @@ +use core::cell::UnsafeCell; +use core::marker::{PhantomData, PhantomPinned}; +use core::pin::Pin; + +use cortex_m::peripheral::scb::VectActive; +use cortex_m::peripheral::{NVIC, SCB}; +use embassy::interrupt::{Interrupt, InterruptExt}; + +/// A type which can be used as state with `PeripheralMutex`. +/// +/// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt, +/// and `&mut T` is only `Send` where `T: Send`. +/// +/// It also requires `'static` to be used safely with `PeripheralMutex::register_interrupt`, +/// because although `Pin` guarantees that the memory of the state won't be invalidated, +/// it doesn't guarantee that the lifetime will last. +pub trait PeripheralState: Send { + type Interrupt: Interrupt; + fn on_interrupt(&mut self); +} + +pub struct PeripheralMutex { + state: UnsafeCell, + + irq_setup_done: bool, + irq: S::Interrupt, + + _not_send: PhantomData<*mut ()>, + _pinned: PhantomPinned, +} + +/// Whether `irq` can be preempted by the current interrupt. +pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool { + match SCB::vect_active() { + // Thread mode can't preempt anything. + VectActive::ThreadMode => false, + // Exceptions don't always preempt interrupts, + // but there isn't much of a good reason to be keeping a `PeripheralMutex` in an exception anyway. + VectActive::Exception(_) => true, + VectActive::Interrupt { irqn } => { + #[derive(Clone, Copy)] + struct NrWrap(u16); + unsafe impl cortex_m::interrupt::InterruptNumber for NrWrap { + fn number(self) -> u16 { + self.0 + } + } + NVIC::get_priority(NrWrap(irqn.into())) < irq.get_priority().into() + } + } +} + +impl PeripheralMutex { + /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. + /// + /// This requires this `PeripheralMutex`'s `PeripheralState` to live for `'static`, + /// because `Pin` only guarantees that it's memory won't be repurposed, + /// not that it's lifetime will last. + /// + /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`. + /// + /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`; + /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`. + pub fn register_interrupt(self: Pin<&mut Self>) { + // SAFETY: `S: 'static`, so there's no way it's lifetime can expire. + unsafe { self.register_interrupt_unchecked() } + } +} + +impl PeripheralMutex { + /// Create a new `PeripheralMutex` wrapping `irq`, with the initial state `state`. + pub fn new(state: S, irq: S::Interrupt) -> Self { + if can_be_preempted(&irq) { + panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps"); + } + + Self { + irq, + irq_setup_done: false, + + state: UnsafeCell::new(state), + _not_send: PhantomData, + _pinned: PhantomPinned, + } + } + + /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. + /// + /// # Safety + /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler + /// must not end without `Drop` being called on this `PeripheralMutex`. + /// + /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`, + /// or making sure that nothing like `mem::forget` is used on the `PeripheralMutex`. + + // TODO: this name isn't the best. + pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) { + let this = self.get_unchecked_mut(); + if this.irq_setup_done { + return; + } + + this.irq.disable(); + this.irq.set_handler(|p| { + // Safety: it's OK to get a &mut to the state, since + // - We checked that the thread owning the `PeripheralMutex` can't preempt us in `new`. + // Interrupts' priorities can only be changed with raw embassy `Interrupts`, + // which can't safely store a `PeripheralMutex` across invocations. + // - We can't have preempted a with() call because the irq is disabled during it. + let state = unsafe { &mut *(p as *mut S) }; + state.on_interrupt(); + }); + this.irq + .set_handler_context((&mut this.state) as *mut _ as *mut ()); + this.irq.enable(); + + this.irq_setup_done = true; + } + + pub fn with(self: Pin<&mut Self>, f: impl FnOnce(&mut S) -> R) -> R { + let this = unsafe { self.get_unchecked_mut() }; + + this.irq.disable(); + + // Safety: it's OK to get a &mut to the state, since the irq is disabled. + let state = unsafe { &mut *this.state.get() }; + let r = f(state); + + this.irq.enable(); + + r + } + + /// Returns whether the wrapped interrupt is currently in a pending state. + pub fn is_pending(&self) -> bool { + self.irq.is_pending() + } + + /// Forces the wrapped interrupt into a pending state. + pub fn pend(&self) { + self.irq.pend() + } + + /// Forces the wrapped interrupt out of a pending state. + pub fn unpend(&self) { + self.irq.unpend() + } + + /// Gets the priority of the wrapped interrupt. + pub fn priority(&self) -> ::Priority { + self.irq.get_priority() + } +} + +impl Drop for PeripheralMutex { + fn drop(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + } +} diff --git a/embassy-hal-common/src/peripheral_shared.rs b/embassy-hal-common/src/peripheral_shared.rs new file mode 100644 index 000000000..71d746341 --- /dev/null +++ b/embassy-hal-common/src/peripheral_shared.rs @@ -0,0 +1,122 @@ +use core::marker::{PhantomData, PhantomPinned}; +use core::pin::Pin; + +use embassy::interrupt::{Interrupt, InterruptExt}; + +use crate::peripheral::can_be_preempted; + +/// A type which can be used as state with `Peripheral`. +/// +/// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt. +/// +/// It also requires `'static` to be used safely with `Peripheral::register_interrupt`, +/// because although `Pin` guarantees that the memory of the state won't be invalidated, +/// it doesn't guarantee that the lifetime will last. +pub trait PeripheralState: Sync { + type Interrupt: Interrupt; + fn on_interrupt(&self); +} + +pub struct Peripheral { + state: S, + + irq_setup_done: bool, + irq: S::Interrupt, + + _not_send: PhantomData<*mut ()>, + _pinned: PhantomPinned, +} + +impl Peripheral { + /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. + /// + /// This requires this `Peripheral`'s `PeripheralState` to live for `'static`, + /// because `Pin` only guarantees that it's memory won't be repurposed, + /// not that it's lifetime will last. + /// + /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`. + /// + /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`; + /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`. + pub fn register_interrupt(self: Pin<&mut Self>) { + // SAFETY: `S: 'static`, so there's no way it's lifetime can expire. + unsafe { self.register_interrupt_unchecked() } + } +} + +impl Peripheral { + pub fn new(irq: S::Interrupt, state: S) -> Self { + if can_be_preempted(&irq) { + panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps"); + } + + Self { + irq, + irq_setup_done: false, + + state, + _not_send: PhantomData, + _pinned: PhantomPinned, + } + } + + /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. + /// + /// # Safety + /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler + /// must not end without `Drop` being called on this `Peripheral`. + /// + /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`, + /// or making sure that nothing like `mem::forget` is used on the `Peripheral`. + pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) { + let this = self.get_unchecked_mut(); + if this.irq_setup_done { + return; + } + + this.irq.disable(); + this.irq.set_handler(|p| { + // The state can't have been dropped, otherwise the interrupt would have been disabled. + // We checked in `new` that the thread owning the `Peripheral` can't preempt the interrupt, + // so someone can't have preempted us before this point and dropped the `Peripheral`. + let state = unsafe { &*(p as *const S) }; + state.on_interrupt(); + }); + this.irq + .set_handler_context((&this.state) as *const _ as *mut ()); + this.irq.enable(); + + this.irq_setup_done = true; + } + + pub fn state(self: Pin<&mut Self>) -> &S { + &self.into_ref().get_ref().state + } + + /// Returns whether the wrapped interrupt is currently in a pending state. + pub fn is_pending(&self) -> bool { + self.irq.is_pending() + } + + /// Forces the wrapped interrupt into a pending state. + pub fn pend(&self) { + self.irq.pend() + } + + /// Forces the wrapped interrupt out of a pending state. + pub fn unpend(&self) { + self.irq.unpend() + } + + /// Gets the priority of the wrapped interrupt. + pub fn priority(&self) -> ::Priority { + self.irq.get_priority() + } +} + +impl Drop for Peripheral { + fn drop(&mut self) { + self.irq.disable(); + self.irq.remove_handler(); + } +} diff --git a/embassy-hal-common/src/ring_buffer.rs b/embassy-hal-common/src/ring_buffer.rs new file mode 100644 index 000000000..18795787f --- /dev/null +++ b/embassy-hal-common/src/ring_buffer.rs @@ -0,0 +1,84 @@ +pub struct RingBuffer<'a> { + buf: &'a mut [u8], + start: usize, + end: usize, + empty: bool, +} + +impl<'a> RingBuffer<'a> { + pub fn new(buf: &'a mut [u8]) -> Self { + Self { + buf, + start: 0, + end: 0, + empty: true, + } + } + + pub fn push_buf(&mut self) -> &mut [u8] { + if self.start == self.end && !self.empty { + trace!(" ringbuf: push_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.start <= self.end { + self.buf.len() - self.end + } else { + self.start - self.end + }; + + trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n); + &mut self.buf[self.end..self.end + n] + } + + pub fn push(&mut self, n: usize) { + trace!(" ringbuf: push {:?}", n); + if n == 0 { + return; + } + + self.end = self.wrap(self.end + n); + self.empty = false; + } + + pub fn pop_buf(&mut self) -> &mut [u8] { + if self.empty { + trace!(" ringbuf: pop_buf empty"); + return &mut self.buf[..0]; + } + + let n = if self.end <= self.start { + self.buf.len() - self.start + } else { + self.end - self.start + }; + + trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n); + &mut self.buf[self.start..self.start + n] + } + + pub fn pop(&mut self, n: usize) { + trace!(" ringbuf: pop {:?}", n); + if n == 0 { + return; + } + + self.start = self.wrap(self.start + n); + self.empty = self.start == self.end; + } + + pub fn clear(&mut self) { + self.start = 0; + self.end = 0; + self.empty = true; + } + + fn wrap(&self, n: usize) -> usize { + assert!(n <= self.buf.len()); + if n == self.buf.len() { + 0 + } else { + n + } + } +} diff --git a/embassy-hal-common/src/usb/cdc_acm.rs b/embassy-hal-common/src/usb/cdc_acm.rs new file mode 100644 index 000000000..5a85b3846 --- /dev/null +++ b/embassy-hal-common/src/usb/cdc_acm.rs @@ -0,0 +1,338 @@ +// Copied from https://github.com/mvirkkunen/usbd-serial +#![allow(dead_code)] + +use core::convert::TryInto; +use core::mem; +use usb_device::class_prelude::*; +use usb_device::Result; + +/// This should be used as `device_class` when building the `UsbDevice`. +pub const USB_CLASS_CDC: u8 = 0x02; + +const USB_CLASS_CDC_DATA: u8 = 0x0a; +const CDC_SUBCLASS_ACM: u8 = 0x02; +const CDC_PROTOCOL_NONE: u8 = 0x00; + +const CS_INTERFACE: u8 = 0x24; +const CDC_TYPE_HEADER: u8 = 0x00; +const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01; +const CDC_TYPE_ACM: u8 = 0x02; +const CDC_TYPE_UNION: u8 = 0x06; + +const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00; +#[allow(unused)] +const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01; +const REQ_SET_LINE_CODING: u8 = 0x20; +const REQ_GET_LINE_CODING: u8 = 0x21; +const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22; + +/// Packet level implementation of a CDC-ACM serial port. +/// +/// This class can be used directly and it has the least overhead due to directly reading and +/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial +/// port. The following constraints must be followed if you use this class directly: +/// +/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the +/// method will return a `WouldBlock` error if there is no packet to be read. +/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the +/// method will return a `WouldBlock` error if the previous packet has not been sent yet. +/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the +/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP) +/// can be sent if there is no other data to send. This is because USB bulk transactions must be +/// terminated with a short packet, even if the bulk endpoint is used for stream-like data. +pub struct CdcAcmClass<'a, B: UsbBus> { + comm_if: InterfaceNumber, + comm_ep: EndpointIn<'a, B>, + data_if: InterfaceNumber, + read_ep: EndpointOut<'a, B>, + write_ep: EndpointIn<'a, B>, + line_coding: LineCoding, + dtr: bool, + rts: bool, +} + +impl CdcAcmClass<'_, B> { + /// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For + /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. + pub fn new(alloc: &UsbBusAllocator, max_packet_size: u16) -> CdcAcmClass<'_, B> { + CdcAcmClass { + comm_if: alloc.interface(), + comm_ep: alloc.interrupt(8, 255), + data_if: alloc.interface(), + read_ep: alloc.bulk(max_packet_size), + write_ep: alloc.bulk(max_packet_size), + line_coding: LineCoding { + stop_bits: StopBits::One, + data_bits: 8, + parity_type: ParityType::None, + data_rate: 8_000, + }, + dtr: false, + rts: false, + } + } + + /// Gets the maximum packet size in bytes. + pub fn max_packet_size(&self) -> u16 { + // The size is the same for both endpoints. + self.read_ep.max_packet_size() + } + + /// Gets the current line coding. The line coding contains information that's mainly relevant + /// for USB to UART serial port emulators, and can be ignored if not relevant. + pub fn line_coding(&self) -> &LineCoding { + &self.line_coding + } + + /// Gets the DTR (data terminal ready) state + pub fn dtr(&self) -> bool { + self.dtr + } + + /// Gets the RTS (request to send) state + pub fn rts(&self) -> bool { + self.rts + } + + /// Writes a single packet into the IN endpoint. + pub fn write_packet(&mut self, data: &[u8]) -> Result { + self.write_ep.write(data) + } + + /// Reads a single packet from the OUT endpoint. + pub fn read_packet(&mut self, data: &mut [u8]) -> Result { + self.read_ep.read(data) + } + + /// Gets the address of the IN endpoint. + pub fn write_ep_address(&self) -> EndpointAddress { + self.write_ep.address() + } + + /// Gets the address of the OUT endpoint. + pub fn read_ep_address(&self) -> EndpointAddress { + self.read_ep.address() + } +} + +impl UsbClass for CdcAcmClass<'_, B> { + fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { + writer.iad( + self.comm_if, + 2, + USB_CLASS_CDC, + CDC_SUBCLASS_ACM, + CDC_PROTOCOL_NONE, + )?; + + writer.interface( + self.comm_if, + USB_CLASS_CDC, + CDC_SUBCLASS_ACM, + CDC_PROTOCOL_NONE, + )?; + + writer.write( + CS_INTERFACE, + &[ + CDC_TYPE_HEADER, // bDescriptorSubtype + 0x10, + 0x01, // bcdCDC (1.10) + ], + )?; + + writer.write( + CS_INTERFACE, + &[ + CDC_TYPE_ACM, // bDescriptorSubtype + 0x00, // bmCapabilities + ], + )?; + + writer.write( + CS_INTERFACE, + &[ + CDC_TYPE_UNION, // bDescriptorSubtype + self.comm_if.into(), // bControlInterface + self.data_if.into(), // bSubordinateInterface + ], + )?; + + writer.write( + CS_INTERFACE, + &[ + CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype + 0x00, // bmCapabilities + self.data_if.into(), // bDataInterface + ], + )?; + + writer.endpoint(&self.comm_ep)?; + + writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?; + + writer.endpoint(&self.write_ep)?; + writer.endpoint(&self.read_ep)?; + + Ok(()) + } + + fn reset(&mut self) { + self.line_coding = LineCoding::default(); + self.dtr = false; + self.rts = false; + } + + fn control_in(&mut self, xfer: ControlIn) { + let req = xfer.request(); + + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.comm_if) as u16) + { + return; + } + + match req.request { + // REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below. + REQ_GET_LINE_CODING if req.length == 7 => { + xfer.accept(|data| { + data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes()); + data[4] = self.line_coding.stop_bits as u8; + data[5] = self.line_coding.parity_type as u8; + data[6] = self.line_coding.data_bits; + + Ok(7) + }) + .ok(); + } + _ => { + xfer.reject().ok(); + } + } + } + + fn control_out(&mut self, xfer: ControlOut) { + let req = xfer.request(); + + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.comm_if) as u16) + { + return; + } + + match req.request { + REQ_SEND_ENCAPSULATED_COMMAND => { + // We don't actually support encapsulated commands but pretend we do for standards + // compatibility. + xfer.accept().ok(); + } + REQ_SET_LINE_CODING if xfer.data().len() >= 7 => { + self.line_coding.data_rate = + u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap()); + self.line_coding.stop_bits = xfer.data()[4].into(); + self.line_coding.parity_type = xfer.data()[5].into(); + self.line_coding.data_bits = xfer.data()[6]; + + xfer.accept().ok(); + } + REQ_SET_CONTROL_LINE_STATE => { + self.dtr = (req.value & 0x0001) != 0; + self.rts = (req.value & 0x0002) != 0; + + xfer.accept().ok(); + } + _ => { + xfer.reject().ok(); + } + }; + } +} + +/// Number of stop bits for LineCoding +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum StopBits { + /// 1 stop bit + One = 0, + + /// 1.5 stop bits + OnePointFive = 1, + + /// 2 stop bits + Two = 2, +} + +impl From for StopBits { + fn from(value: u8) -> Self { + if value <= 2 { + unsafe { mem::transmute(value) } + } else { + StopBits::One + } + } +} + +/// Parity for LineCoding +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum ParityType { + None = 0, + Odd = 1, + Event = 2, + Mark = 3, + Space = 4, +} + +impl From for ParityType { + fn from(value: u8) -> Self { + if value <= 4 { + unsafe { mem::transmute(value) } + } else { + ParityType::None + } + } +} + +/// Line coding parameters +/// +/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can +/// be ignored if you don't plan to interface with a physical UART. +pub struct LineCoding { + stop_bits: StopBits, + data_bits: u8, + parity_type: ParityType, + data_rate: u32, +} + +impl LineCoding { + /// Gets the number of stop bits for UART communication. + pub fn stop_bits(&self) -> StopBits { + self.stop_bits + } + + /// Gets the number of data bits for UART communication. + pub fn data_bits(&self) -> u8 { + self.data_bits + } + + /// Gets the parity type for UART communication. + pub fn parity_type(&self) -> ParityType { + self.parity_type + } + + /// Gets the data rate in bits per second for UART communication. + pub fn data_rate(&self) -> u32 { + self.data_rate + } +} + +impl Default for LineCoding { + fn default() -> Self { + LineCoding { + stop_bits: StopBits::One, + data_bits: 8, + parity_type: ParityType::None, + data_rate: 8_000, + } + } +} diff --git a/embassy-hal-common/src/usb/mod.rs b/embassy-hal-common/src/usb/mod.rs new file mode 100644 index 000000000..1fb501d7f --- /dev/null +++ b/embassy-hal-common/src/usb/mod.rs @@ -0,0 +1,258 @@ +use core::cell::RefCell; +use core::marker::PhantomData; +use core::pin::Pin; + +use usb_device::bus::UsbBus; +use usb_device::class::UsbClass; +use usb_device::device::UsbDevice; + +mod cdc_acm; +pub mod usb_serial; + +use crate::peripheral::{PeripheralMutex, PeripheralState}; +use embassy::interrupt::Interrupt; +use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; + +/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction. +pub unsafe trait USBInterrupt: Interrupt + Send {} + +pub(crate) struct State<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet, + I: USBInterrupt, +{ + device: UsbDevice<'bus, B>, + pub(crate) classes: T, + _interrupt: PhantomData, +} + +pub struct Usb<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet, + I: USBInterrupt, +{ + // Don't you dare moving out `PeripheralMutex` + inner: RefCell>>, +} + +impl<'bus, B, T, I> Usb<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet, + I: USBInterrupt, +{ + pub fn new>(device: UsbDevice<'bus, B>, class_set: S, irq: I) -> Self { + let state = State { + device, + classes: class_set.into_class_set(), + _interrupt: PhantomData, + }; + let mutex = PeripheralMutex::new(state, irq); + Self { + inner: RefCell::new(mutex), + } + } + + /// # Safety + /// The `UsbDevice` passed to `Self::new` must not be dropped without calling `Drop` on this `Usb` first. + pub unsafe fn start(self: Pin<&mut Self>) { + let this = self.get_unchecked_mut(); + let mut mutex = this.inner.borrow_mut(); + let mutex = Pin::new_unchecked(&mut *mutex); + + // Use inner to register the irq + // SAFETY: the safety contract of this function makes sure the `UsbDevice` won't be invalidated + // without the `PeripheralMutex` being dropped. + mutex.register_interrupt_unchecked(); + } +} + +impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet + SerialState<'bus, 'c, B, Index0>, + I: USBInterrupt, +{ + /// Take a serial class that was passed as the first class in a tuple + pub fn take_serial_0<'a>( + self: Pin<&'a Self>, + ) -> ( + ReadInterface<'a, 'bus, 'c, Index0, B, T, I>, + WriteInterface<'a, 'bus, 'c, Index0, B, T, I>, + ) { + let this = self.get_ref(); + + let r = ReadInterface { + inner: &this.inner, + _buf_lifetime: PhantomData, + _index: PhantomData, + }; + + let w = WriteInterface { + inner: &this.inner, + _buf_lifetime: PhantomData, + _index: PhantomData, + }; + (r, w) + } +} + +impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet + SerialState<'bus, 'c, B, Index1>, + I: USBInterrupt, +{ + /// Take a serial class that was passed as the second class in a tuple + pub fn take_serial_1<'a>( + self: Pin<&'a Self>, + ) -> ( + ReadInterface<'a, 'bus, 'c, Index1, B, T, I>, + WriteInterface<'a, 'bus, 'c, Index1, B, T, I>, + ) { + let this = self.get_ref(); + + let r = ReadInterface { + inner: &this.inner, + _buf_lifetime: PhantomData, + _index: PhantomData, + }; + + let w = WriteInterface { + inner: &this.inner, + _buf_lifetime: PhantomData, + _index: PhantomData, + }; + (r, w) + } +} + +impl<'bus, B, T, I> PeripheralState for State<'bus, B, T, I> +where + B: UsbBus, + T: ClassSet, + I: USBInterrupt, +{ + type Interrupt = I; + fn on_interrupt(&mut self) { + self.classes.poll_all(&mut self.device); + } +} + +pub trait ClassSet: Send { + fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool; +} + +pub trait IntoClassSet> { + fn into_class_set(self) -> C; +} + +pub struct ClassSet1 +where + B: UsbBus, + C1: UsbClass, +{ + class: C1, + _bus: PhantomData, +} + +pub struct ClassSet2 +where + B: UsbBus, + C1: UsbClass, + C2: UsbClass, +{ + class1: C1, + class2: C2, + _bus: PhantomData, +} + +/// The first class into a [`ClassSet`] +pub struct Index0; + +/// The second class into a [`ClassSet`] +pub struct Index1; + +impl ClassSet for ClassSet1 +where + B: UsbBus + Send, + C1: UsbClass + Send, +{ + fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { + device.poll(&mut [&mut self.class]) + } +} + +impl ClassSet for ClassSet2 +where + B: UsbBus + Send, + C1: UsbClass + Send, + C2: UsbClass + Send, +{ + fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool { + device.poll(&mut [&mut self.class1, &mut self.class2]) + } +} + +impl IntoClassSet> for C1 +where + B: UsbBus + Send, + C1: UsbClass + Send, +{ + fn into_class_set(self) -> ClassSet1 { + ClassSet1 { + class: self, + _bus: PhantomData, + } + } +} + +impl IntoClassSet> for (C1, C2) +where + B: UsbBus + Send, + C1: UsbClass + Send, + C2: UsbClass + Send, +{ + fn into_class_set(self) -> ClassSet2 { + ClassSet2 { + class1: self.0, + class2: self.1, + _bus: PhantomData, + } + } +} + +/// Trait for a USB State that has a serial class inside +pub trait SerialState<'bus, 'a, B: UsbBus, I> { + fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>; +} + +impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0> + for ClassSet1> +{ + fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { + &mut self.class + } +} + +impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2, C2> +where + B: UsbBus, + C2: UsbClass, +{ + fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { + &mut self.class1 + } +} + +impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2> +where + B: UsbBus, + C1: UsbClass, +{ + fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> { + &mut self.class2 + } +} diff --git a/embassy-hal-common/src/usb/usb_serial.rs b/embassy-hal-common/src/usb/usb_serial.rs new file mode 100644 index 000000000..a229b2000 --- /dev/null +++ b/embassy-hal-common/src/usb/usb_serial.rs @@ -0,0 +1,310 @@ +use core::cell::RefCell; +use core::marker::{PhantomData, Unpin}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use embassy::io::{self, AsyncBufRead, AsyncWrite}; +use embassy::util::WakerRegistration; +use usb_device::bus::UsbBus; +use usb_device::class_prelude::*; +use usb_device::UsbError; + +use super::cdc_acm::CdcAcmClass; +use crate::peripheral::PeripheralMutex; +use crate::ring_buffer::RingBuffer; +use crate::usb::{ClassSet, SerialState, State, USBInterrupt}; + +pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT> +where + I: Unpin, + B: UsbBus, + T: SerialState<'bus, 'c, B, I> + ClassSet, + INT: USBInterrupt, +{ + // Don't you dare moving out `PeripheralMutex` + pub(crate) inner: &'a RefCell>>, + pub(crate) _buf_lifetime: PhantomData<&'c T>, + pub(crate) _index: PhantomData, +} + +/// Write interface for USB CDC_ACM +/// +/// This interface is buffered, meaning that after the write returns the bytes might not be fully +/// on the wire just yet +pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT> +where + I: Unpin, + B: UsbBus, + T: SerialState<'bus, 'c, B, I> + ClassSet, + INT: USBInterrupt, +{ + // Don't you dare moving out `PeripheralMutex` + pub(crate) inner: &'a RefCell>>, + pub(crate) _buf_lifetime: PhantomData<&'c T>, + pub(crate) _index: PhantomData, +} + +impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT> +where + I: Unpin, + B: UsbBus, + T: SerialState<'bus, 'c, B, I> + ClassSet, + INT: USBInterrupt, +{ + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + let mut mutex = this.inner.borrow_mut(); + let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; + mutex.with(|state| { + let serial = state.classes.get_serial(); + let serial = Pin::new(serial); + + match serial.poll_fill_buf(cx) { + Poll::Ready(Ok(buf)) => { + let buf: &[u8] = buf; + // NOTE(unsafe) This part of the buffer won't be modified until the user calls + // consume, which will invalidate this ref + let buf: &[u8] = unsafe { core::mem::transmute(buf) }; + Poll::Ready(Ok(buf)) + } + Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)), + Poll::Pending => Poll::Pending, + } + }) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + let this = self.get_mut(); + let mut mutex = this.inner.borrow_mut(); + let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; + mutex.with(|state| { + let serial = state.classes.get_serial(); + let serial = Pin::new(serial); + + serial.consume(amt); + }) + } +} + +impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT> +where + I: Unpin, + B: UsbBus, + T: SerialState<'bus, 'c, B, I> + ClassSet, + INT: USBInterrupt, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let this = self.get_mut(); + let mut mutex = this.inner.borrow_mut(); + let mutex = unsafe { Pin::new_unchecked(&mut *mutex) }; + mutex.with(|state| { + let serial = state.classes.get_serial(); + let serial = Pin::new(serial); + + serial.poll_write(cx, buf) + }) + } +} + +pub struct UsbSerial<'bus, 'a, B: UsbBus> { + inner: CdcAcmClass<'bus, B>, + read_buf: RingBuffer<'a>, + write_buf: RingBuffer<'a>, + read_waker: WakerRegistration, + write_waker: WakerRegistration, + write_state: WriteState, + read_error: bool, + write_error: bool, +} + +impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> { + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + + if this.read_error { + this.read_error = false; + return Poll::Ready(Err(io::Error::Other)); + } + + let buf = this.read_buf.pop_buf(); + if buf.is_empty() { + this.read_waker.register(cx.waker()); + return Poll::Pending; + } + Poll::Ready(Ok(buf)) + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + self.get_mut().read_buf.pop(amt); + } +} + +impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + let this = self.get_mut(); + + if this.write_error { + this.write_error = false; + return Poll::Ready(Err(io::Error::Other)); + } + + let write_buf = this.write_buf.push_buf(); + if write_buf.is_empty() { + this.write_waker.register(cx.waker()); + return Poll::Pending; + } + + let count = write_buf.len().min(buf.len()); + write_buf[..count].copy_from_slice(&buf[..count]); + this.write_buf.push(count); + + this.flush_write(); + Poll::Ready(Ok(count)) + } +} + +/// Keeps track of the type of the last written packet. +enum WriteState { + /// No packets in-flight + Idle, + + /// Short packet currently in-flight + Short, + + /// Full packet current in-flight. A full packet must be followed by a short packet for the host + /// OS to see the transaction. The data is the number of subsequent full packets sent so far. A + /// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a + /// timely manner. + Full(usize), +} + +impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> { + pub fn new( + alloc: &'bus UsbBusAllocator, + read_buf: &'a mut [u8], + write_buf: &'a mut [u8], + ) -> Self { + Self { + inner: CdcAcmClass::new(alloc, 64), + read_buf: RingBuffer::new(read_buf), + write_buf: RingBuffer::new(write_buf), + read_waker: WakerRegistration::new(), + write_waker: WakerRegistration::new(), + write_state: WriteState::Idle, + read_error: false, + write_error: false, + } + } + + fn flush_write(&mut self) { + /// If this many full size packets have been sent in a row, a short packet will be sent so that the + /// host sees the data in a timely manner. + const SHORT_PACKET_INTERVAL: usize = 10; + + let full_size_packets = match self.write_state { + WriteState::Full(c) => c, + _ => 0, + }; + + let ep_size = self.inner.max_packet_size() as usize; + let max_size = if full_size_packets > SHORT_PACKET_INTERVAL { + ep_size - 1 + } else { + ep_size + }; + + let buf = { + let buf = self.write_buf.pop_buf(); + if buf.len() > max_size { + &buf[..max_size] + } else { + buf + } + }; + + if !buf.is_empty() { + let count = match self.inner.write_packet(buf) { + Ok(c) => c, + Err(UsbError::WouldBlock) => 0, + Err(_) => { + self.write_error = true; + return; + } + }; + + if buf.len() == ep_size { + self.write_state = WriteState::Full(full_size_packets + 1); + } else { + self.write_state = WriteState::Short; + } + self.write_buf.pop(count); + } else if full_size_packets > 0 { + if let Err(e) = self.inner.write_packet(&[]) { + if !matches!(e, UsbError::WouldBlock) { + self.write_error = true; + } + return; + } + self.write_state = WriteState::Idle; + } + } +} + +impl UsbClass for UsbSerial<'_, '_, B> +where + B: UsbBus, +{ + fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> { + self.inner.get_configuration_descriptors(writer) + } + + fn reset(&mut self) { + self.inner.reset(); + self.read_buf.clear(); + self.write_buf.clear(); + self.write_state = WriteState::Idle; + } + + fn endpoint_in_complete(&mut self, addr: EndpointAddress) { + if addr == self.inner.write_ep_address() { + self.write_waker.wake(); + + self.flush_write(); + } + } + + fn endpoint_out(&mut self, addr: EndpointAddress) { + if addr == self.inner.read_ep_address() { + let buf = self.read_buf.push_buf(); + let count = match self.inner.read_packet(buf) { + Ok(c) => c, + Err(UsbError::WouldBlock) => 0, + Err(_) => { + self.read_error = true; + return; + } + }; + + if count > 0 { + self.read_buf.push(count); + self.read_waker.wake(); + } + } + } + + fn control_in(&mut self, xfer: ControlIn) { + self.inner.control_in(xfer); + } + + fn control_out(&mut self, xfer: ControlOut) { + self.inner.control_out(xfer); + } +} -- cgit