1#![no_std]
2#![no_main]
3
4use core::cell::RefCell;
5use core::ops::DerefMut;
6
7extern crate alloc;
8use alloc::boxed::Box;
9use alloc::{vec, vec::Vec};
10
11use rp2040_hal as hal;
12
13use hal::{
14 fugit::{ExtU64, RateExtU32},
15 timer::Instant,
16 uart::{DataBits, StopBits, UartConfig, UartPeripheral},
17 usb::UsbBus,
18 Clock,
19};
20
21use embedded_alloc::LlffHeap as Heap;
22use embedded_hal::digital::OutputPin;
23use embedded_io::Write;
24
25use usb_device::class_prelude::*;
26use usb_device::device::{StringDescriptors, UsbDeviceBuilder, UsbDeviceState, UsbVidPid};
27
28use bp35c0_j11::*;
29use route_b_secrets::*;
30
31#[global_allocator]
32static ALLOCATOR: Heap = Heap::empty();
33
34#[link_section = ".boot2"]
35#[no_mangle]
36#[used]
37static BOOT2_FIRMWARE: [u8; 256] = {
38 #[cfg(feature = "adafruit-feather-rp2040")]
39 {
40 rp2040_boot2::BOOT_LOADER_GD25Q64CS
41 }
42 #[cfg(feature = "rp-pico")]
43 {
44 rp2040_boot2::BOOT_LOADER_W25Q080
45 }
46};
47
48const XOSC_CRYSTAL_FREQ: u32 = 12_000_000;
49
50#[cfg(not(feature = "defmt"))]
51#[panic_handler]
52fn panic(_: &core::panic::PanicInfo) -> ! {
53 loop {}
54}
55
56#[cfg(feature = "defmt")]
57use {defmt_rtt as _, panic_probe as _};
58
59enum State<'a> {
60 Start,
61 ReleaseReset,
62 GetVersionInformation,
63 SetOperationModeForActiveScan,
64 ActiveScan,
65 SetOperationModeForRouteB(Channel),
66 SetRouteBPanaAuthenticationInformation,
67 StartRouteBOperation,
68 OpenUdpPort,
69 StartRouteBPana,
70 Loop(u64),
71
72 Wait(Instant, Box<dyn FnOnce() -> State<'a> + 'a>),
73 WaitForResponse(Box<dyn FnMut(Response) -> Option<State<'a>> + 'a>),
74 SendCommand {
75 buf: Vec<u8>,
76 sent: usize,
77 next_state: Box<State<'a>>,
78 },
79}
80
81fn configure_pins(
82 pins: hal::gpio::Pins,
83) -> (
84 impl hal::uart::ValidUartPinout<hal::pac::UART1>,
85 impl OutputPin,
86 impl OutputPin,
87) {
88 #[cfg(feature = "adafruit-feather-rp2040")]
89 return (
90 (pins.gpio24.into_function(), pins.gpio25.into_function()),
91 pins.gpio11.into_push_pull_output(),
92 pins.gpio10.into_push_pull_output(),
93 );
94
95 #[cfg(feature = "rp-pico")]
96 return (
97 (pins.gpio4.into_function(), pins.gpio5.into_function()),
98 pins.gpio10.into_push_pull_output(),
99 pins.gpio11.into_push_pull_output(),
100 );
101}
102
103#[hal::entry]
104fn main() -> ! {
105 {
106 use core::mem::MaybeUninit;
107 use core::ptr::addr_of_mut;
108 const HEAP_SIZE: usize = 8 * 1024;
109 static mut HEAP: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
110 unsafe { ALLOCATOR.init(addr_of_mut!(HEAP) as usize, HEAP_SIZE) }
111 }
112
113 let mut pac = hal::pac::Peripherals::take().unwrap();
114 let _core = hal::pac::CorePeripherals::take().unwrap();
115
116 let mut watchdog = hal::watchdog::Watchdog::new(pac.WATCHDOG);
117
118 let clocks = hal::clocks::init_clocks_and_plls(
119 XOSC_CRYSTAL_FREQ,
120 pac.XOSC,
121 pac.CLOCKS,
122 pac.PLL_SYS,
123 pac.PLL_USB,
124 &mut pac.RESETS,
125 &mut watchdog,
126 )
127 .unwrap();
128
129 let timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
130
131 let sio = hal::Sio::new(pac.SIO);
132 let pins = hal::gpio::Pins::new(
133 pac.IO_BANK0,
134 pac.PADS_BANK0,
135 sio.gpio_bank0,
136 &mut pac.RESETS,
137 );
138 let (pins_uart, mut pin_reset_n, mut pin_txs0108e_oe) = configure_pins(pins);
139
140 let mut uart = UartPeripheral::new(pac.UART1, pins_uart, &mut pac.RESETS)
141 .enable(
142 UartConfig::new(115_200.Hz(), DataBits::Eight, None, StopBits::One),
143 clocks.peripheral_clock.freq(),
144 )
145 .unwrap();
146
147 let usb_bus = UsbBusAllocator::new(UsbBus::new(
148 pac.USBCTRL_REGS,
149 pac.USBCTRL_DPRAM,
150 clocks.usb_clock,
151 true,
152 &mut pac.RESETS,
153 ));
154 let usb_cdc = RefCell::new(usbd_serial::SerialPort::new(&usb_bus));
155 let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
156 .strings(&[StringDescriptors::default().product("Serial port")])
157 .unwrap()
158 .device_class(usbd_serial::USB_CLASS_CDC)
159 .build();
160
161 let mut state = State::Start;
162
163 let mut parser = Parser::default();
164
165 loop {
166 let _ = usb_dev.poll(&mut [usb_cdc.borrow_mut().deref_mut()]);
167
168 if usb_dev.state() != UsbDeviceState::Configured {
169 continue;
170 }
171
172 let mut rx_buf = [0; 32];
173 if let Ok(len) = uart.read_raw(&mut rx_buf) {
174 for resp in rx_buf[0..len].iter().flat_map(|&c| parser.parse(c)) {
175 write!(usb_cdc.borrow_mut(), "[+] Rx: {resp:#x?}\r\n").unwrap();
176 if let State::WaitForResponse(ref mut f) = state {
177 if let Some(new_state) = f(resp) {
178 state = new_state;
179 }
180 }
181 }
182 }
183
184 let send = |cmd, f| {
185 write!(usb_cdc.borrow_mut(), "[+] Tx: {cmd:#x?}\r\n").unwrap();
186 let mut buf = vec![0; 128];
187 let len = serialize_to_bytes(&cmd, &mut buf).unwrap();
188 buf.resize(len, 0);
189 State::SendCommand {
190 buf,
191 sent: 0,
192 next_state: Box::new(State::WaitForResponse(f)),
193 }
194 };
195
196 let tick = timer.get_counter();
197 match state {
198 State::Start => {
199 write!(
200 usb_cdc.borrow_mut(),
201 "\r\n\
202 [+] ================================\r\n\
203 [+] (✗╹◡╹)ノ {}\r\n\
204 [+] ================================\r\n\
205 \r\n",
206 env!("CARGO_BIN_NAME")
207 )
208 .unwrap();
209
210 pin_txs0108e_oe.set_high().unwrap();
211 state = State::Wait(tick + 500.millis(), Box::new(|| State::ReleaseReset));
212 }
213
214 State::ReleaseReset => {
215 pin_reset_n.set_high().unwrap();
216 state = State::WaitForResponse(Box::new(|resp| match resp {
217 Response::NotificationPoweredOn => {
218 write!(usb_cdc.borrow_mut(), "[+] Ready\r\n").unwrap();
219 Some(State::GetVersionInformation)
220 }
221 _ => None,
222 }));
223 }
224
225 State::GetVersionInformation => {
226 state = send(
227 Command::GetVersionInformation,
228 Box::new(|resp| match resp {
229 Response::GetVersionInformation { .. } => {
230 Some(State::SetOperationModeForActiveScan)
231 }
232 _ => None,
233 }),
234 );
235 }
236
237 State::SetOperationModeForActiveScan => {
238 state = send(
239 Command::SetOperationMode {
240 mode: OperationMode::Dual,
241 han_sleep: false,
242 channel: Channel::Ch4F922p5MHz,
243 tx_power: TxPower::P20mW,
244 },
245 Box::new(|resp| match resp {
246 Response::SetOperationMode { .. } => Some(State::ActiveScan),
247 _ => None,
248 }),
249 );
250 }
251
252 State::ActiveScan => {
253 let mut scan_result = None;
254 state = send(
255 Command::DoActiveScan {
256 duration: ScanDuration::T616p96ms,
257 mask_channels: 0x3fff0,
258 pairing_id: Some(u64::from_be_bytes(
259 ROUTE_B_ID[24..32].try_into().unwrap(),
260 )),
261 },
262 Box::new(move |resp| match resp {
263 Response::DoActiveScan { .. } => match scan_result.take() {
264 Some(channel) => Some(State::SetOperationModeForRouteB(channel)),
265 _ => Some(State::ActiveScan),
266 },
267 Response::NotificationActiveScan { channel, terminal } => {
268 if !terminal.is_empty() {
269 scan_result.replace(channel);
270 }
271 None
272 }
273 _ => None,
274 }),
275 );
276 }
277
278 State::SetOperationModeForRouteB(channel) => {
279 state = send(
280 Command::SetOperationMode {
281 mode: OperationMode::Dual,
282 han_sleep: false,
283 channel,
284 tx_power: TxPower::P20mW,
285 },
286 Box::new(|resp| match resp {
287 Response::SetOperationMode { .. } => {
288 Some(State::SetRouteBPanaAuthenticationInformation)
289 }
290 _ => None,
291 }),
292 );
293 }
294
295 State::SetRouteBPanaAuthenticationInformation => {
296 state = send(
297 Command::SetRouteBPanaAuthenticationInformation {
298 id: ROUTE_B_ID,
299 password: ROUTE_B_PASSWORD,
300 },
301 Box::new(|resp| match resp {
302 Response::SetRouteBPanaAuthenticationInformation { .. } => {
303 Some(State::StartRouteBOperation)
304 }
305 _ => None,
306 }),
307 );
308 }
309
310 State::StartRouteBOperation => {
311 state = send(
312 Command::StartRouteBOperation,
313 Box::new(|resp| match resp {
314 Response::StartRouteBOperation { result, .. } => {
315 if result == 0x01 {
316 Some(State::OpenUdpPort)
317 } else {
318 Some(State::StartRouteBOperation)
319 }
320 }
321 _ => None,
322 }),
323 );
324 }
325
326 State::OpenUdpPort => {
327 state = send(
328 Command::OpenUdpPort(0x0e1a),
329 Box::new(|resp| match resp {
330 Response::OpenUdpPort { .. } => Some(State::StartRouteBPana),
331 _ => None,
332 }),
333 );
334 }
335
336 State::StartRouteBPana => {
337 state = send(
338 Command::StartRouteBPana,
339 Box::new(|resp| match resp {
340 Response::StartRouteBPana { .. } => None,
341 Response::NotificationPanaAuthentication {
342 result: _,
343 mac_address,
344 } => Some(State::Loop(mac_address)),
345 _ => None,
346 }),
347 );
348 }
349
350 State::Loop(mac_address) => {
351 let destination_address = 0xfe800000000000000000000000000000_u128
352 | (mac_address ^ 0x02000000_00000000) as u128;
353 state = State::Wait(
354 tick + 10.secs(),
355 Box::new(move || {
356 send(
357 Command::TransmitData {
358 destination_address,
359 source_port: 0x0e1a,
360 destination_port: 0x0e1a,
361 data: &[
362 0x10, 0x81, 0x00, 0x06, 0x05, 0xff, 0x01, 0x02, 0x88, 0x01, 0x62, 0x04, 0x97, 0x00, 0x98, 0x00, 0xd3, 0x00, 0xe7, 0x00, ],
378 },
379 Box::new(move |resp| match resp {
380 Response::TransmitData { .. } => Some(State::Loop(mac_address)),
381 _ => None,
382 }),
383 )
384 }),
385 );
386 }
387
388 State::Wait(t, next_state) if tick >= t => {
389 state = next_state();
390 }
391
392 State::SendCommand {
393 buf,
394 sent,
395 next_state,
396 } => match uart.write(&buf[sent..]) {
397 Ok(len) => {
398 if buf.len() >= sent + len {
399 state = *next_state;
400 } else {
401 state = State::SendCommand {
402 buf,
403 sent: sent + len,
404 next_state,
405 };
406 }
407 }
408 _ => todo!(),
409 },
410
411 _ => (),
412 }
413 }
414}