diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-06-09 03:36:48 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-06-22 21:12:10 +0200 |
| commit | 6c123596b7d48ee66ea93e8b1515e91231e9bced (patch) | |
| tree | 7f657d78d2fac5a24982d74b909789d60a2dfee7 /embassy-net-esp-hosted/src/lib.rs | |
| parent | 1f2be2dac5eeed739d2866b9b63ca06fdd84c276 (diff) | |
wip: esp-hosted net driver.
Diffstat (limited to 'embassy-net-esp-hosted/src/lib.rs')
| -rw-r--r-- | embassy-net-esp-hosted/src/lib.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs new file mode 100644 index 000000000..2cf05a7df --- /dev/null +++ b/embassy-net-esp-hosted/src/lib.rs | |||
| @@ -0,0 +1,300 @@ | |||
| 1 | #![no_std] | ||
| 2 | |||
| 3 | use control::Control; | ||
| 4 | use embassy_futures::select::{select3, Either3}; | ||
| 5 | use embassy_net_driver_channel as ch; | ||
| 6 | use embassy_time::{Duration, Instant, Timer}; | ||
| 7 | use embedded_hal::digital::{InputPin, OutputPin}; | ||
| 8 | use embedded_hal_async::digital::Wait; | ||
| 9 | use embedded_hal_async::spi::SpiDevice; | ||
| 10 | use ioctl::IoctlState; | ||
| 11 | |||
| 12 | use crate::ioctl::PendingIoctl; | ||
| 13 | |||
| 14 | mod proto; | ||
| 15 | |||
| 16 | // must be first | ||
| 17 | mod fmt; | ||
| 18 | |||
| 19 | mod control; | ||
| 20 | mod ioctl; | ||
| 21 | |||
| 22 | const MTU: usize = 1514; | ||
| 23 | |||
| 24 | macro_rules! impl_bytes { | ||
| 25 | ($t:ident) => { | ||
| 26 | impl $t { | ||
| 27 | pub const SIZE: usize = core::mem::size_of::<Self>(); | ||
| 28 | |||
| 29 | #[allow(unused)] | ||
| 30 | pub fn to_bytes(&self) -> [u8; Self::SIZE] { | ||
| 31 | unsafe { core::mem::transmute(*self) } | ||
| 32 | } | ||
| 33 | |||
| 34 | #[allow(unused)] | ||
| 35 | pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self { | ||
| 36 | let alignment = core::mem::align_of::<Self>(); | ||
| 37 | assert_eq!( | ||
| 38 | bytes.as_ptr().align_offset(alignment), | ||
| 39 | 0, | ||
| 40 | "{} is not aligned", | ||
| 41 | core::any::type_name::<Self>() | ||
| 42 | ); | ||
| 43 | unsafe { core::mem::transmute(bytes) } | ||
| 44 | } | ||
| 45 | |||
| 46 | #[allow(unused)] | ||
| 47 | pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self { | ||
| 48 | let alignment = core::mem::align_of::<Self>(); | ||
| 49 | assert_eq!( | ||
| 50 | bytes.as_ptr().align_offset(alignment), | ||
| 51 | 0, | ||
| 52 | "{} is not aligned", | ||
| 53 | core::any::type_name::<Self>() | ||
| 54 | ); | ||
| 55 | |||
| 56 | unsafe { core::mem::transmute(bytes) } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | } | ||
| 61 | |||
| 62 | #[repr(C, packed)] | ||
| 63 | #[derive(Clone, Copy, Debug, Default)] | ||
| 64 | struct PayloadHeader { | ||
| 65 | /// InterfaceType on lower 4 bits, number on higher 4 bits. | ||
| 66 | if_type_and_num: u8, | ||
| 67 | |||
| 68 | /// Flags. | ||
| 69 | /// | ||
| 70 | /// bit 0: more fragments. | ||
| 71 | flags: u8, | ||
| 72 | |||
| 73 | len: u16, | ||
| 74 | offset: u16, | ||
| 75 | checksum: u16, | ||
| 76 | seq_num: u16, | ||
| 77 | reserved2: u8, | ||
| 78 | |||
| 79 | /// Packet type for HCI or PRIV interface, reserved otherwise | ||
| 80 | hci_priv_packet_type: u8, | ||
| 81 | } | ||
| 82 | impl_bytes!(PayloadHeader); | ||
| 83 | |||
| 84 | #[repr(u8)] | ||
| 85 | enum InterfaceType { | ||
| 86 | Sta = 0, | ||
| 87 | Ap = 1, | ||
| 88 | Serial = 2, | ||
| 89 | Hci = 3, | ||
| 90 | Priv = 4, | ||
| 91 | Test = 5, | ||
| 92 | } | ||
| 93 | |||
| 94 | const MAX_SPI_BUFFER_SIZE: usize = 1600; | ||
| 95 | |||
| 96 | pub struct State { | ||
| 97 | ioctl_state: IoctlState, | ||
| 98 | ch: ch::State<MTU, 4, 4>, | ||
| 99 | } | ||
| 100 | |||
| 101 | impl State { | ||
| 102 | pub fn new() -> Self { | ||
| 103 | Self { | ||
| 104 | ioctl_state: IoctlState::new(), | ||
| 105 | ch: ch::State::new(), | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 111 | |||
| 112 | pub async fn new<'a, SPI, IN, OUT>( | ||
| 113 | state: &'a mut State, | ||
| 114 | spi: SPI, | ||
| 115 | handshake: IN, | ||
| 116 | ready: IN, | ||
| 117 | reset: OUT, | ||
| 118 | ) -> (NetDriver<'a>, Control<'a>, Runner<'a, SPI, IN, OUT>) | ||
| 119 | where | ||
| 120 | SPI: SpiDevice, | ||
| 121 | IN: InputPin + Wait, | ||
| 122 | OUT: OutputPin, | ||
| 123 | { | ||
| 124 | let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); | ||
| 125 | let state_ch = ch_runner.state_runner(); | ||
| 126 | |||
| 127 | let mut runner = Runner { | ||
| 128 | ch: ch_runner, | ||
| 129 | ioctl_state: &state.ioctl_state, | ||
| 130 | next_seq: 1, | ||
| 131 | handshake, | ||
| 132 | ready, | ||
| 133 | reset, | ||
| 134 | spi, | ||
| 135 | }; | ||
| 136 | runner.init().await; | ||
| 137 | |||
| 138 | (device, Control::new(state_ch, &state.ioctl_state), runner) | ||
| 139 | } | ||
| 140 | |||
| 141 | pub struct Runner<'a, SPI, IN, OUT> { | ||
| 142 | ch: ch::Runner<'a, MTU>, | ||
| 143 | ioctl_state: &'a IoctlState, | ||
| 144 | |||
| 145 | next_seq: u16, | ||
| 146 | |||
| 147 | spi: SPI, | ||
| 148 | handshake: IN, | ||
| 149 | ready: IN, | ||
| 150 | reset: OUT, | ||
| 151 | } | ||
| 152 | |||
| 153 | impl<'a, SPI, IN, OUT> Runner<'a, SPI, IN, OUT> | ||
| 154 | where | ||
| 155 | SPI: SpiDevice, | ||
| 156 | IN: InputPin + Wait, | ||
| 157 | OUT: OutputPin, | ||
| 158 | { | ||
| 159 | async fn init(&mut self) {} | ||
| 160 | |||
| 161 | pub async fn run(mut self) -> ! { | ||
| 162 | debug!("resetting..."); | ||
| 163 | self.reset.set_low().unwrap(); | ||
| 164 | Timer::after(Duration::from_millis(100)).await; | ||
| 165 | self.reset.set_high().unwrap(); | ||
| 166 | Timer::after(Duration::from_millis(1000)).await; | ||
| 167 | |||
| 168 | let mut tx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; | ||
| 169 | let mut rx_buf = [0u8; MAX_SPI_BUFFER_SIZE]; | ||
| 170 | |||
| 171 | loop { | ||
| 172 | self.handshake.wait_for_high().await.unwrap(); | ||
| 173 | |||
| 174 | let ioctl = self.ioctl_state.wait_pending(); | ||
| 175 | let tx = self.ch.tx_buf(); | ||
| 176 | let ev = async { self.ready.wait_for_high().await.unwrap() }; | ||
| 177 | |||
| 178 | match select3(ioctl, tx, ev).await { | ||
| 179 | Either3::First(PendingIoctl { buf, req_len }) => { | ||
| 180 | tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02"); | ||
| 181 | tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes()); | ||
| 182 | tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]); | ||
| 183 | |||
| 184 | let mut header = PayloadHeader { | ||
| 185 | if_type_and_num: InterfaceType::Serial as _, | ||
| 186 | len: (req_len + 14) as _, | ||
| 187 | offset: PayloadHeader::SIZE as _, | ||
| 188 | seq_num: self.next_seq, | ||
| 189 | ..Default::default() | ||
| 190 | }; | ||
| 191 | self.next_seq = self.next_seq.wrapping_add(1); | ||
| 192 | |||
| 193 | // Calculate checksum | ||
| 194 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); | ||
| 195 | header.checksum = checksum(&tx_buf[..26 + req_len]); | ||
| 196 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); | ||
| 197 | |||
| 198 | debug!("====== SENDING IOCTL"); | ||
| 199 | } | ||
| 200 | Either3::Second(packet) => { | ||
| 201 | tx_buf[12..][..packet.len()].copy_from_slice(packet); | ||
| 202 | |||
| 203 | let mut header = PayloadHeader { | ||
| 204 | if_type_and_num: InterfaceType::Sta as _, | ||
| 205 | len: packet.len() as _, | ||
| 206 | offset: PayloadHeader::SIZE as _, | ||
| 207 | seq_num: self.next_seq, | ||
| 208 | ..Default::default() | ||
| 209 | }; | ||
| 210 | self.next_seq = self.next_seq.wrapping_add(1); | ||
| 211 | |||
| 212 | // Calculate checksum | ||
| 213 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); | ||
| 214 | header.checksum = checksum(&tx_buf[..12 + packet.len()]); | ||
| 215 | tx_buf[0..12].copy_from_slice(&header.to_bytes()); | ||
| 216 | |||
| 217 | self.ch.tx_done(); | ||
| 218 | } | ||
| 219 | Either3::Third(()) => { | ||
| 220 | tx_buf[..PayloadHeader::SIZE].fill(0); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | if tx_buf[0] != 0 { | ||
| 225 | trace!("tx: {:02x}", &tx_buf[..40]); | ||
| 226 | } | ||
| 227 | |||
| 228 | self.spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); | ||
| 229 | let delay_until = Instant::now() + Duration::from_millis(1); | ||
| 230 | self.handle_rx(&mut rx_buf); | ||
| 231 | Timer::at(delay_until).await; | ||
| 232 | } | ||
| 233 | } | ||
| 234 | |||
| 235 | fn handle_rx(&mut self, buf: &mut [u8]) { | ||
| 236 | trace!("rx: {:02x}", &buf[..40]); | ||
| 237 | |||
| 238 | let buf_len = buf.len(); | ||
| 239 | let h = PayloadHeader::from_bytes_mut((&mut buf[..PayloadHeader::SIZE]).try_into().unwrap()); | ||
| 240 | |||
| 241 | if h.len == 0 || h.offset as usize != PayloadHeader::SIZE { | ||
| 242 | return; | ||
| 243 | } | ||
| 244 | |||
| 245 | let payload_len = h.len as usize; | ||
| 246 | if buf_len < PayloadHeader::SIZE + payload_len { | ||
| 247 | warn!("rx: len too big"); | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | |||
| 251 | let if_type_and_num = h.if_type_and_num; | ||
| 252 | let want_checksum = h.checksum; | ||
| 253 | h.checksum = 0; | ||
| 254 | let got_checksum = checksum(&buf[..PayloadHeader::SIZE + payload_len]); | ||
| 255 | if want_checksum != got_checksum { | ||
| 256 | warn!("rx: bad checksum. Got {:04x}, want {:04x}", got_checksum, want_checksum); | ||
| 257 | return; | ||
| 258 | } | ||
| 259 | |||
| 260 | let payload = &mut buf[PayloadHeader::SIZE..][..payload_len]; | ||
| 261 | |||
| 262 | match if_type_and_num & 0x0f { | ||
| 263 | // STA | ||
| 264 | 0 => match self.ch.try_rx_buf() { | ||
| 265 | Some(buf) => { | ||
| 266 | buf[..payload.len()].copy_from_slice(payload); | ||
| 267 | self.ch.rx_done(payload.len()) | ||
| 268 | } | ||
| 269 | None => warn!("failed to push rxd packet to the channel."), | ||
| 270 | }, | ||
| 271 | // serial | ||
| 272 | 2 => { | ||
| 273 | debug!("serial rx: {:02x}", payload); | ||
| 274 | if payload.len() < 14 { | ||
| 275 | warn!("serial rx: too short"); | ||
| 276 | return; | ||
| 277 | } | ||
| 278 | if &payload[..12] != b"\x01\x08\x00ctrlResp\x02" { | ||
| 279 | warn!("serial rx: bad tlv"); | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | let len = u16::from_le_bytes(payload[12..14].try_into().unwrap()) as usize; | ||
| 283 | if payload.len() < 14 + len { | ||
| 284 | warn!("serial rx: too short 2"); | ||
| 285 | return; | ||
| 286 | } | ||
| 287 | self.ioctl_state.ioctl_done(&payload[14..][..len]); | ||
| 288 | } | ||
| 289 | _ => warn!("unknown iftype {}", if_type_and_num), | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | fn checksum(buf: &[u8]) -> u16 { | ||
| 295 | let mut res = 0u16; | ||
| 296 | for &b in buf { | ||
| 297 | res = res.wrapping_add(b as _); | ||
| 298 | } | ||
| 299 | res | ||
| 300 | } | ||
