teensy4_selfrebootor/
lib.rs

1#![no_std]
2#![deny(missing_docs)]
3#![doc = include_str!("../README.md")]
4#![doc(issue_tracker_base_url = "https://github.com/Finomnis/teensy4-selfrebootor/issues")]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7use teensy4_bsp::hal::usbd::BusAdapter;
8use usb_device::{class_prelude::*, prelude::*};
9use usbd_hid::{descriptor::SerializedDescriptor, hid_class::HIDClass};
10
11mod hid_descriptor;
12mod reboot;
13
14pub use reboot::reboot_to_bootloader;
15
16/// The rebootor USB driver.
17///
18/// Once it receives a reboot request (`teensy_loader_cli -r`), it reboots
19/// the device into the HalfKay bootloader for flashing.
20///
21/// This allows reflashing without having to press the `boot` hardware button.
22pub struct Rebootor<'a> {
23    class: HIDClass<'a, BusAdapter>,
24    device: UsbDevice<'a, BusAdapter>,
25    configured: bool,
26}
27
28impl<'a> Rebootor<'a> {
29    /// Creates a rebootor usb device.
30    ///
31    /// In order for the device to function, its `poll` function has to be called
32    /// periodically.
33    ///
34    /// For more information, see the crate's examples.
35    pub fn new(bus_alloc: &'a UsbBusAllocator<BusAdapter>) -> Self {
36        let class = HIDClass::new(bus_alloc, crate::hid_descriptor::Rebootor::desc(), 10);
37        let device = UsbDeviceBuilder::new(bus_alloc, UsbVidPid(0x16C0, 0x0477))
38            .product("Self-Rebootor")
39            .manufacturer("PJRC")
40            .self_powered(true)
41            .max_packet_size_0(64)
42            .build();
43
44        device.bus().set_interrupts(true);
45
46        Self {
47            class,
48            device,
49            configured: false,
50        }
51    }
52
53    /// Needs to be called every couple of milliseconds for the USB device to work
54    /// properly.
55    ///
56    /// See the crate's examples for more information.
57    pub fn poll(&mut self) {
58        self.device.poll(&mut [&mut self.class]);
59
60        if self.device.state() == UsbDeviceState::Configured {
61            if !self.configured {
62                self.device.bus().configure();
63            }
64            self.configured = true;
65        } else {
66            self.configured = false;
67        }
68
69        if self.configured {
70            let mut buf = [0u8; 6];
71
72            let result = self.class.pull_raw_output(&mut buf);
73            match result {
74                Ok(info) => {
75                    let buf = &buf[..info];
76                    if buf == b"reboot" {
77                        log::info!("Rebooting to HalfKay ...");
78                        reboot::reboot_to_bootloader();
79                    }
80                }
81                Err(usb_device::UsbError::WouldBlock) => (),
82                Err(_) => {}
83            }
84        }
85    }
86}