usbd_picotool_reset/
lib.rs1#![forbid(missing_docs)]
10#![no_std]
11
12use core::marker::PhantomData;
13use usb_device::class_prelude::{InterfaceNumber, StringIndex, UsbBus, UsbBusAllocator};
14use usb_device::LangID;
15
16const CLASS_VENDOR_SPECIFIC: u8 = 0xFF;
18const RESET_INTERFACE_SUBCLASS: u8 = 0x00;
20const RESET_INTERFACE_PROTOCOL: u8 = 0x01;
21const RESET_REQUEST_BOOTSEL: u8 = 0x01;
22#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26#[derive(Debug, PartialEq, Eq, Clone, Copy)]
27pub enum DisableInterface {
28 None,
30 DisableMassStorage,
32 DisablePicoBoot,
34}
35impl DisableInterface {
36 const fn into(self) -> u32 {
37 match self {
38 DisableInterface::None => 0,
39 DisableInterface::DisableMassStorage => 1,
40 DisableInterface::DisablePicoBoot => 2,
41 }
42 }
43}
44
45pub trait Config {
47 const INTERFACE_DISABLE: DisableInterface;
49 const BOOTSEL_ACTIVITY_LED: Option<usize>;
51}
52
53pub enum DefaultConfig {}
58impl Config for DefaultConfig {
59 const INTERFACE_DISABLE: DisableInterface = DisableInterface::None;
60
61 const BOOTSEL_ACTIVITY_LED: Option<usize> = None;
62}
63
64pub struct PicoToolReset<'a, B: UsbBus, C: Config = DefaultConfig> {
66 intf: InterfaceNumber,
67 str_idx: StringIndex,
68 _bus: PhantomData<&'a B>,
69 _cnf: PhantomData<C>,
70}
71impl<'a, B: UsbBus, C: Config> PicoToolReset<'a, B, C> {
72 pub fn new(alloc: &'a UsbBusAllocator<B>) -> PicoToolReset<'a, B, C> {
74 Self {
75 intf: alloc.interface(),
76 str_idx: alloc.string(),
77 _bus: PhantomData,
78 _cnf: PhantomData,
79 }
80 }
81}
82
83impl<B: UsbBus, C: Config> usb_device::class::UsbClass<B> for PicoToolReset<'_, B, C> {
84 fn get_configuration_descriptors(
85 &self,
86 writer: &mut usb_device::descriptor::DescriptorWriter,
87 ) -> usb_device::Result<()> {
88 writer.interface_alt(
89 self.intf,
90 0,
91 CLASS_VENDOR_SPECIFIC,
92 RESET_INTERFACE_SUBCLASS,
93 RESET_INTERFACE_PROTOCOL,
94 Some(self.str_idx),
95 )
96 }
97
98 fn get_string(&self, index: StringIndex, _lang_id: LangID) -> Option<&str> {
99 (index == self.str_idx).then_some("Reset")
100 }
101
102 fn control_out(&mut self, xfer: usb_device::class_prelude::ControlOut<B>) {
103 let req = xfer.request();
104 if !(req.request_type == usb_device::control::RequestType::Class
105 && req.recipient == usb_device::control::Recipient::Interface
106 && req.index == u8::from(self.intf) as u16)
107 {
108 return;
109 }
110
111 match req.request {
112 RESET_REQUEST_BOOTSEL => {
113 let mut gpio_mask = C::BOOTSEL_ACTIVITY_LED.map(|led| 1 << led).unwrap_or(0);
114 if req.value & 0x100 != 0 {
115 gpio_mask = 1 << (req.value >> 9);
116 }
117 rp2040_hal::rom_data::reset_to_usb_boot(
118 gpio_mask,
119 u32::from(req.value & 0x7F) | C::INTERFACE_DISABLE.into(),
120 );
121 unreachable!()
123 }
124 _ => {
126 let _ = xfer.reject();
128 }
129 }
130 }
131
132 fn control_in(&mut self, xfer: usb_device::class_prelude::ControlIn<B>) {
133 let req = xfer.request();
134 if !(req.request_type == usb_device::control::RequestType::Class
135 && req.recipient == usb_device::control::Recipient::Interface
136 && req.index == u8::from(self.intf) as u16)
137 {
138 return;
139 }
140 let _ = xfer.reject();
142 }
143}