Rename embassy-extras to embassy-hal-common
This commit is contained in:
225
embassy-hal-common/src/fmt.rs
Normal file
225
embassy-hal-common/src/fmt.rs
Normal file
@ -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<Self::Ok, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T> Try for Option<T> {
|
||||
type Ok = T;
|
||||
type Error = NoneError;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Result<T, NoneError> {
|
||||
self.ok_or(NoneError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Try for Result<T, E> {
|
||||
type Ok = T;
|
||||
type Error = E;
|
||||
|
||||
#[inline]
|
||||
fn into_result(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
571
embassy-hal-common/src/interrupt.rs
Normal file
571
embassy-hal-common/src/interrupt.rs
Normal file
@ -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<u8> 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,
|
||||
));
|
21
embassy-hal-common/src/lib.rs
Normal file
21
embassy-hal-common/src/lib.rs
Normal file
@ -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();
|
||||
}
|
130
embassy-hal-common/src/macros.rs
Normal file
130
embassy-hal-common/src/macros.rs
Normal file
@ -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;
|
||||
<Self as embassy::util::Steal>::steal()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl embassy::util::Steal for Peripherals {
|
||||
#[inline]
|
||||
unsafe fn steal() -> Self {
|
||||
Self {
|
||||
$(
|
||||
$(#[$cfg])?
|
||||
$name: <peripherals::$name as embassy::util::Steal>::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<Clocks> = 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
160
embassy-hal-common/src/peripheral.rs
Normal file
160
embassy-hal-common/src/peripheral.rs
Normal file
@ -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<S: PeripheralState> {
|
||||
state: UnsafeCell<S>,
|
||||
|
||||
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<S: PeripheralState + 'static> PeripheralMutex<S> {
|
||||
/// 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<S: PeripheralState> PeripheralMutex<S> {
|
||||
/// 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<R>(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) -> <S::Interrupt as Interrupt>::Priority {
|
||||
self.irq.get_priority()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PeripheralState> Drop for PeripheralMutex<S> {
|
||||
fn drop(&mut self) {
|
||||
self.irq.disable();
|
||||
self.irq.remove_handler();
|
||||
}
|
||||
}
|
122
embassy-hal-common/src/peripheral_shared.rs
Normal file
122
embassy-hal-common/src/peripheral_shared.rs
Normal file
@ -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<S: PeripheralState> {
|
||||
state: S,
|
||||
|
||||
irq_setup_done: bool,
|
||||
irq: S::Interrupt,
|
||||
|
||||
_not_send: PhantomData<*mut ()>,
|
||||
_pinned: PhantomPinned,
|
||||
}
|
||||
|
||||
impl<S: PeripheralState + 'static> Peripheral<S> {
|
||||
/// 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<S: PeripheralState> Peripheral<S> {
|
||||
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) -> <S::Interrupt as Interrupt>::Priority {
|
||||
self.irq.get_priority()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: PeripheralState> Drop for Peripheral<S> {
|
||||
fn drop(&mut self) {
|
||||
self.irq.disable();
|
||||
self.irq.remove_handler();
|
||||
}
|
||||
}
|
84
embassy-hal-common/src/ring_buffer.rs
Normal file
84
embassy-hal-common/src/ring_buffer.rs
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
338
embassy-hal-common/src/usb/cdc_acm.rs
Normal file
338
embassy-hal-common/src/usb/cdc_acm.rs
Normal file
@ -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<B: UsbBus> 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<B>, 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<usize> {
|
||||
self.write_ep.write(data)
|
||||
}
|
||||
|
||||
/// Reads a single packet from the OUT endpoint.
|
||||
pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> {
|
||||
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<B: UsbBus> UsbClass<B> 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<B>) {
|
||||
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<B>) {
|
||||
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<u8> 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<u8> 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,
|
||||
}
|
||||
}
|
||||
}
|
258
embassy-hal-common/src/usb/mod.rs
Normal file
258
embassy-hal-common/src/usb/mod.rs
Normal file
@ -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<B>,
|
||||
I: USBInterrupt,
|
||||
{
|
||||
device: UsbDevice<'bus, B>,
|
||||
pub(crate) classes: T,
|
||||
_interrupt: PhantomData<I>,
|
||||
}
|
||||
|
||||
pub struct Usb<'bus, B, T, I>
|
||||
where
|
||||
B: UsbBus,
|
||||
T: ClassSet<B>,
|
||||
I: USBInterrupt,
|
||||
{
|
||||
// Don't you dare moving out `PeripheralMutex`
|
||||
inner: RefCell<PeripheralMutex<State<'bus, B, T, I>>>,
|
||||
}
|
||||
|
||||
impl<'bus, B, T, I> Usb<'bus, B, T, I>
|
||||
where
|
||||
B: UsbBus,
|
||||
T: ClassSet<B>,
|
||||
I: USBInterrupt,
|
||||
{
|
||||
pub fn new<S: IntoClassSet<B, T>>(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<B> + 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<B> + 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<B>,
|
||||
I: USBInterrupt,
|
||||
{
|
||||
type Interrupt = I;
|
||||
fn on_interrupt(&mut self) {
|
||||
self.classes.poll_all(&mut self.device);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ClassSet<B: UsbBus>: Send {
|
||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
|
||||
}
|
||||
|
||||
pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
|
||||
fn into_class_set(self) -> C;
|
||||
}
|
||||
|
||||
pub struct ClassSet1<B, C1>
|
||||
where
|
||||
B: UsbBus,
|
||||
C1: UsbClass<B>,
|
||||
{
|
||||
class: C1,
|
||||
_bus: PhantomData<B>,
|
||||
}
|
||||
|
||||
pub struct ClassSet2<B, C1, C2>
|
||||
where
|
||||
B: UsbBus,
|
||||
C1: UsbClass<B>,
|
||||
C2: UsbClass<B>,
|
||||
{
|
||||
class1: C1,
|
||||
class2: C2,
|
||||
_bus: PhantomData<B>,
|
||||
}
|
||||
|
||||
/// The first class into a [`ClassSet`]
|
||||
pub struct Index0;
|
||||
|
||||
/// The second class into a [`ClassSet`]
|
||||
pub struct Index1;
|
||||
|
||||
impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
|
||||
where
|
||||
B: UsbBus + Send,
|
||||
C1: UsbClass<B> + Send,
|
||||
{
|
||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
||||
device.poll(&mut [&mut self.class])
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
|
||||
where
|
||||
B: UsbBus + Send,
|
||||
C1: UsbClass<B> + Send,
|
||||
C2: UsbClass<B> + Send,
|
||||
{
|
||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
||||
device.poll(&mut [&mut self.class1, &mut self.class2])
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
|
||||
where
|
||||
B: UsbBus + Send,
|
||||
C1: UsbClass<B> + Send,
|
||||
{
|
||||
fn into_class_set(self) -> ClassSet1<B, C1> {
|
||||
ClassSet1 {
|
||||
class: self,
|
||||
_bus: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
|
||||
where
|
||||
B: UsbBus + Send,
|
||||
C1: UsbClass<B> + Send,
|
||||
C2: UsbClass<B> + Send,
|
||||
{
|
||||
fn into_class_set(self) -> ClassSet2<B, C1, C2> {
|
||||
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<B, UsbSerial<'bus, 'a, B>>
|
||||
{
|
||||
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<B, UsbSerial<'bus, 'a, B>, C2>
|
||||
where
|
||||
B: UsbBus,
|
||||
C2: UsbClass<B>,
|
||||
{
|
||||
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<B, C1, UsbSerial<'bus, 'a, B>>
|
||||
where
|
||||
B: UsbBus,
|
||||
C1: UsbClass<B>,
|
||||
{
|
||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
||||
&mut self.class2
|
||||
}
|
||||
}
|
310
embassy-hal-common/src/usb/usb_serial.rs
Normal file
310
embassy-hal-common/src/usb/usb_serial.rs
Normal file
@ -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<B>,
|
||||
INT: USBInterrupt,
|
||||
{
|
||||
// Don't you dare moving out `PeripheralMutex`
|
||||
pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T, INT>>>,
|
||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
||||
pub(crate) _index: PhantomData<I>,
|
||||
}
|
||||
|
||||
/// 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<B>,
|
||||
INT: USBInterrupt,
|
||||
{
|
||||
// Don't you dare moving out `PeripheralMutex`
|
||||
pub(crate) inner: &'a RefCell<PeripheralMutex<State<'bus, B, T, INT>>>,
|
||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
||||
pub(crate) _index: PhantomData<I>,
|
||||
}
|
||||
|
||||
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<B>,
|
||||
INT: USBInterrupt,
|
||||
{
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
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<B>,
|
||||
INT: USBInterrupt,
|
||||
{
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<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.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<io::Result<&[u8]>> {
|
||||
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<io::Result<usize>> {
|
||||
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<B>,
|
||||
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<B> UsbClass<B> 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<B>) {
|
||||
self.inner.control_in(xfer);
|
||||
}
|
||||
|
||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
||||
self.inner.control_out(xfer);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user