dfu_libusb/
lib.rs

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