Skip to main content

dfu_libusb/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::cell::RefCell;
4use std::marker;
5use thiserror::Error;
6
7pub type Dfu<C> = dfu_core::synchronous::DfuSync<DfuLibusb<C>, Error>;
8
9#[derive(Debug, Error)]
10pub enum Error {
11    #[error("Could not find device or an error occurred.")]
12    CouldNotOpenDevice,
13    #[error(transparent)]
14    Dfu(#[from] dfu_core::Error),
15    #[error(transparent)]
16    Io(#[from] std::io::Error),
17    #[error("rusb: {0}")]
18    LibUsb(#[from] rusb::Error),
19    #[error("The device has no languages.")]
20    MissingLanguage,
21    #[error("Could not find interface.")]
22    InvalidInterface,
23    #[error("Could not find alt interface.")]
24    InvalidAlt,
25    #[error("Could not parse functional descriptor: {0}")]
26    FunctionalDescriptor(#[from] dfu_core::functional_descriptor::Error),
27    #[error("No DFU capable device found.")]
28    NoDfuCapableDeviceFound,
29}
30
31pub struct DfuLibusb<C: rusb::UsbContext> {
32    usb: RefCell<rusb::DeviceHandle<C>>,
33    protocol: dfu_core::DfuProtocol<dfu_core::memory_layout::MemoryLayout>,
34    timeout: std::time::Duration,
35    iface: u16,
36    functional_descriptor: dfu_core::functional_descriptor::FunctionalDescriptor,
37    marker: marker::PhantomData<C>,
38}
39
40impl<C: rusb::UsbContext> dfu_core::DfuIo for DfuLibusb<C> {
41    type Read = usize;
42    type Write = usize;
43    type Reset = ();
44    type Error = Error;
45    type MemoryLayout = dfu_core::memory_layout::MemoryLayout;
46
47    #[allow(unused_variables)]
48    fn read_control(
49        &self,
50        request_type: u8,
51        request: u8,
52        value: u16,
53        buffer: &mut [u8],
54    ) -> Result<Self::Read, Self::Error> {
55        // TODO: do or do not? there is no try
56        let request_type = request_type | rusb::constants::LIBUSB_ENDPOINT_IN;
57        let res = self.usb.borrow().read_control(
58            request_type,
59            request,
60            value,
61            self.iface,
62            buffer,
63            self.timeout,
64        );
65        assert!(
66            !matches!(res, Err(rusb::Error::InvalidParam)),
67            "invalid param: {:08b} {:?}",
68            request_type,
69            res,
70        );
71        Ok(res?)
72    }
73
74    #[allow(unused_variables)]
75    fn write_control(
76        &self,
77        request_type: u8,
78        request: u8,
79        value: u16,
80        buffer: &[u8],
81    ) -> Result<Self::Write, Self::Error> {
82        let res = self.usb.borrow().write_control(
83            request_type,
84            request,
85            value,
86            self.iface,
87            buffer,
88            self.timeout,
89        );
90        assert!(
91            !matches!(res, Err(rusb::Error::InvalidParam)),
92            "invalid param: {:08b}",
93            request_type,
94        );
95        Ok(res?)
96    }
97
98    fn usb_reset(self) -> Result<Self::Reset, Self::Error> {
99        // Release the interface before resetting the device. On macOS,
100        // libusb's reset fails if an interface is still claimed. Explicitly
101        // releasing here (rather than relying on DeviceHandle's Drop) makes
102        // the intent clear and ensures the interface is released before the
103        // reset call.
104        let handle = self.usb.into_inner();
105        handle.release_interface(self.iface as u8)?;
106        Ok(handle.reset()?)
107    }
108
109    fn protocol(&self) -> &dfu_core::DfuProtocol<Self::MemoryLayout> {
110        &self.protocol
111    }
112
113    fn functional_descriptor(&self) -> &dfu_core::functional_descriptor::FunctionalDescriptor {
114        &self.functional_descriptor
115    }
116}
117
118impl<C: rusb::UsbContext> DfuLibusb<C> {
119    pub fn open(context: &C, vid: u16, pid: u16, iface: u8, alt: u8) -> Result<Dfu<C>, Error> {
120        let (device, handle) = Self::open_device(context, vid, pid)?;
121        Self::from_usb_device(device, handle, iface, alt)
122    }
123
124    pub fn from_usb_device(
125        device: rusb::Device<C>,
126        handle: rusb::DeviceHandle<C>,
127        iface: u8,
128        alt: u8,
129    ) -> Result<Dfu<C>, Error> {
130        let timeout = std::time::Duration::from_secs(3);
131        handle.claim_interface(iface)?;
132        handle.set_alternate_setting(iface, alt)?;
133        let device_descriptor = device.device_descriptor()?;
134        let languages = handle.read_languages(timeout)?;
135        let lang = languages.first().ok_or(Error::MissingLanguage)?;
136
137        for index in 0..device_descriptor.num_configurations() {
138            let config_descriptor = device.config_descriptor(index)?;
139
140            if let Some(functional_descriptor) =
141                Self::find_functional_descriptor(&handle, &config_descriptor, timeout)
142                    .transpose()?
143            {
144                let interface = config_descriptor
145                    .interfaces()
146                    .find(|x| x.number() == iface)
147                    .ok_or(Error::InvalidInterface)?;
148                let iface_desc = interface
149                    .descriptors()
150                    .find(|x| x.setting_number() == alt)
151                    .ok_or(Error::InvalidAlt)?;
152
153                let interface_string = handle.read_interface_string(*lang, &iface_desc, timeout)?;
154                let protocol = dfu_core::DfuProtocol::new(
155                    &interface_string,
156                    functional_descriptor.dfu_version,
157                )?;
158
159                let io = DfuLibusb {
160                    usb: RefCell::new(handle),
161                    protocol,
162                    timeout,
163                    iface: iface as u16,
164                    functional_descriptor,
165                    marker: marker::PhantomData,
166                };
167
168                return Ok(Dfu::new(io));
169            }
170        }
171
172        Err(Error::NoDfuCapableDeviceFound)
173    }
174
175    fn open_device(
176        context: &C,
177        vid: u16,
178        pid: u16,
179    ) -> Result<(rusb::Device<C>, rusb::DeviceHandle<C>), Error> {
180        for device in context.devices()?.iter() {
181            let device_desc = match device.device_descriptor() {
182                Ok(x) => x,
183                Err(_) => continue,
184            };
185
186            if device_desc.vendor_id() == vid && device_desc.product_id() == pid {
187                let handle = device.open()?;
188                return Ok((device, handle));
189            }
190        }
191
192        Err(Error::CouldNotOpenDevice)
193    }
194
195    fn find_functional_descriptor(
196        handle: &rusb::DeviceHandle<C>,
197        config: &rusb::ConfigDescriptor,
198        timeout: std::time::Duration,
199    ) -> Option<Result<dfu_core::functional_descriptor::FunctionalDescriptor, Error>> {
200        macro_rules! find_func_desc {
201            ($data:expr) => {{
202                if let Some(func_desc) =
203                    dfu_core::functional_descriptor::FunctionalDescriptor::from_bytes($data)
204                {
205                    return Some(func_desc.map_err(Into::into));
206                }
207            }};
208        }
209
210        find_func_desc!(config.extra());
211
212        for if_desc in config.interfaces().flat_map(|x| x.descriptors()) {
213            find_func_desc!(if_desc.extra());
214        }
215
216        let mut buffer = [0x00; 9];
217        match handle.read_control(
218            rusb::constants::LIBUSB_ENDPOINT_IN,
219            rusb::constants::LIBUSB_REQUEST_GET_DESCRIPTOR,
220            0x2100,
221            0,
222            &mut buffer,
223            timeout,
224        ) {
225            Ok(n) => find_func_desc!(&buffer[..n]),
226            Err(err) => return Some(Err(err.into())),
227        }
228
229        None
230    }
231}