1#![no_std]
2
3use core::cell::RefCell;
4use core::fmt::Write;
5use core::sync::atomic::{AtomicBool, Ordering};
6
7use critical_section::Mutex;
8use heapless::spsc::Queue;
9use static_cell::StaticCell;
10use usb_device::prelude::*;
11use usb_device::{class_prelude::UsbBusAllocator, device::UsbDeviceState};
12use usbd_serial::SerialPort;
13#[cfg(feature = "rp2040")]
112use rp2040_hal as hal;
113#[cfg(feature = "rp2350")]
114use rp235x_hal as hal;
115
116use hal::pac;
117use hal::usb::UsbBus;
118
119#[cfg(any(
121 target_arch = "arm",
122 all(feature = "rp2350", target_arch = "riscv32", target_os = "none")
123))]
124use hal::pac::interrupt;
125
126#[cfg(all(feature = "rp2040", feature = "rp2350"))]
131compile_error!("features `rp2040` and `rp2350` cannot be enabled at the same time");
132
133#[cfg(not(any(feature = "rp2040", feature = "rp2350")))]
134compile_error!("either feature `rp2040` or `rp2350` must be enabled");
135
136#[cfg(all(feature = "rp2040", not(target_arch = "arm")))]
137compile_error!("`rp2040` only supports ARM targets");
138
139#[cfg(all(
140 feature = "rp2350",
141 not(any(target_arch = "arm", all(target_arch = "riscv32", target_os = "none")))
142))]
143compile_error!("`rp2350` supports ARM or bare-metal riscv32 targets only");
144
145const TX_BUF_SIZE: usize = 1024;
148const RX_BUF_SIZE: usize = 1024;
149
150const AUTO_ECHO: bool = true;
153
154static USB_ALLOCATOR: StaticCell<UsbBusAllocator<UsbBus>> = StaticCell::new();
155static USB_CONSOLE: Mutex<RefCell<Option<RpUsbConsole>>> = Mutex::new(RefCell::new(None));
156static USB_INIT: AtomicBool = AtomicBool::new(false);
157
158#[cfg(feature = "rp2040")]
159const USB_CHIP_NAME: &str = "RP2040";
160#[cfg(feature = "rp2040")]
161const SERIAL_NUM: &str = "B8ED93AE";
162#[cfg(feature = "rp2040")]
163const USB_VID_PID: (u16, u16) = (0x2E8A, 0x0003);
164
165#[cfg(feature = "rp2350")]
166const USB_CHIP_NAME: &str = "RP2350";
167#[cfg(feature = "rp2350")]
168const SERIAL_NUM: &str = "A74D15D8";
169#[cfg(feature = "rp2350")]
170const USB_VID_PID: (u16, u16) = (0x2E8A, 0x10B0);
171
172pub struct RpUsbConsole {
173 device: UsbDevice<'static, UsbBus>,
174 serial: SerialPort<'static, UsbBus>,
175 tx_buf: Queue<u8, TX_BUF_SIZE>,
176 rx_buf: Queue<u8, RX_BUF_SIZE>,
177}
178
179impl RpUsbConsole {
180 pub fn init(
181 #[cfg(feature = "rp2040")] usbctrl_reg: pac::USBCTRL_REGS,
182 #[cfg(feature = "rp2350")] usbctrl_reg: pac::USB,
183 #[cfg(feature = "rp2040")] usbctrl_dpram: pac::USBCTRL_DPRAM,
184 #[cfg(feature = "rp2350")] usbctrl_dpram: pac::USB_DPRAM,
185 usb_reset: &mut pac::RESETS,
186 clocks: hal::clocks::UsbClock,
187 ) {
188 if USB_INIT.load(Ordering::SeqCst) {
189 return;
190 }
191
192 let usb_bus = UsbBus::new(usbctrl_reg, usbctrl_dpram, clocks, true, usb_reset);
193 let alloc = USB_ALLOCATOR.init(UsbBusAllocator::new(usb_bus));
194
195 let serial = SerialPort::new(alloc);
196 let device = UsbDeviceBuilder::new(alloc, UsbVidPid(USB_VID_PID.0, USB_VID_PID.1))
197 .strings(&[StringDescriptors::default()
198 .manufacturer("Raspberry Pi")
199 .product(USB_CHIP_NAME)
200 .serial_number(SERIAL_NUM)])
201 .unwrap()
202 .device_class(2)
203 .build();
204
205 critical_section::with(|cs| {
206 USB_CONSOLE.borrow(cs).replace(Some(Self {
207 device,
208 serial,
209 tx_buf: Queue::new(),
210 rx_buf: Queue::new(),
211 }));
212 });
213
214 USB_INIT.store(true, Ordering::SeqCst);
215 }
216
217 pub fn poll() {
224 Self::force_poll();
225 Self::pump_rx_echo();
226 Self::flush_tx();
227 }
228
229 #[inline(always)]
231 fn force_poll() {
232 if !USB_INIT.load(Ordering::SeqCst) {
233 return;
234 }
235
236 critical_section::with(|cs| {
237 let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
238 if let Some(usb) = opt.as_mut() {
239 usb.device.poll(&mut [&mut usb.serial]);
240 }
241 });
242 }
243
244 fn pump_rx_echo() {
246 if !USB_INIT.load(Ordering::SeqCst) {
247 return;
248 }
249
250 critical_section::with(|cs| {
251 let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
252 let Some(usb) = opt.as_mut() else { return };
253
254 if usb.device.state() != UsbDeviceState::Configured {
255 return;
256 }
257
258 let mut temp = [0u8; 64];
259
260 loop {
261 match usb.serial.read(&mut temp) {
262 Ok(0) => break,
263 Ok(n) => {
264 for &b in &temp[..n] {
265 let _ = usb.rx_buf.enqueue(b);
267
268 if AUTO_ECHO {
270 let _ = usb.tx_buf.enqueue(b);
271 }
272 }
273 }
274 Err(usb_device::UsbError::WouldBlock) => break,
275 Err(_) => break,
276 }
277 }
278 });
279 }
280
281 fn flush_tx() {
283 if !USB_INIT.load(Ordering::SeqCst) {
284 return;
285 }
286
287 critical_section::with(|cs| {
288 let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
289 let Some(usb) = opt.as_mut() else { return };
290
291 if usb.device.state() != UsbDeviceState::Configured {
292 return;
293 }
294
295 while let Some(b) = usb.tx_buf.dequeue() {
296 if usb.serial.write(&[b]).is_err() {
297 let _ = usb.tx_buf.enqueue(b);
298 break;
299 }
300 }
301 });
302 }
303
304 pub fn write(data: &[u8]) {
306 if !USB_INIT.load(Ordering::SeqCst) {
307 return;
308 }
309
310 critical_section::with(|cs| {
311 let mut binding = USB_CONSOLE.borrow(cs).borrow_mut();
312 if let Some(usb) = binding.as_mut() {
313 for &b in data {
314 let _ = usb.tx_buf.enqueue(b);
315 }
316 }
317 });
318
319 Self::poll();
321 }
322
323 pub fn print(s: &str) {
324 Self::write(s.as_bytes());
325 }
326
327 pub fn println(s: &str) {
328 Self::print(s);
329 Self::write(b"\r\n");
330 }
331
332 pub fn fmt_write(args: core::fmt::Arguments<'_>) {
333 struct Wrapper;
334
335 impl Write for Wrapper {
336 fn write_str(&mut self, s: &str) -> core::fmt::Result {
337 RpUsbConsole::print(s);
338 Ok(())
339 }
340 }
341
342 let _ = Wrapper.write_fmt(args);
343 }
344
345 pub fn read(buf: &mut [u8]) -> usize {
348 if !USB_INIT.load(Ordering::SeqCst) {
349 return 0;
350 }
351
352 critical_section::with(|cs| {
353 let mut opt = USB_CONSOLE.borrow(cs).borrow_mut();
354 let Some(usb) = opt.as_mut() else { return 0 };
355
356 let mut n = 0;
357 while n < buf.len() {
358 match usb.rx_buf.dequeue() {
359 Some(b) => {
360 buf[n] = b;
361 n += 1;
362 }
363 None => break,
364 }
365 }
366 n
367 })
368 }
369}
370
371#[inline(always)]
376fn usbctrl_irq_impl() {
377 RpUsbConsole::poll();
378}
379
380#[cfg(target_arch = "arm")]
382#[interrupt]
383fn USBCTRL_IRQ() {
384 usbctrl_irq_impl();
385}
386
387#[cfg(all(feature = "rp2350", target_arch = "riscv32", target_os = "none"))]
389#[no_mangle]
390pub extern "C" fn USBCTRL_IRQ() {
391 usbctrl_irq_impl();
392}
393
394#[macro_export]
399macro_rules! usb_print {
400 ($($tt:tt)*) => {
401 $crate::RpUsbConsole::fmt_write(format_args!($($tt)*));
402 };
403}
404
405#[macro_export]
406macro_rules! usb_println {
407 ($($tt:tt)*) => {
408 $crate::RpUsbConsole::fmt_write(format_args!($($tt)*));
409 $crate::RpUsbConsole::write(b"\r\n");
410 };
411}