use usb_device::class_prelude::*;
use usb_device::Result;
const USB_CLASS_APPLICATION_SPECIFIC: u8 = 0xfe;
const DFU_SUBCLASS_FIRMWARE_UPGRADE: u8 = 0x01;
const DFU_PROTOCOL_RUNTIME: u8 = 0x01;
const DFU_TYPE_FUNCTIONAL: u8 = 0x21;
const DFU_WILL_DETACH: u8 = 1 << 3;
const DFU_MANIFESTATION_TOLERANT: u8 = 1 << 2;
const DFU_CAN_UPLOAD: u8 = 1 << 1;
const DFU_CAN_DNLOAD: u8 = 1 << 0;
const DFU_REQ_DETACH: u8 = 0;
pub struct DfuRuntimeClass<T: DfuRuntimeOps> {
dfu_ops: T,
iface: InterfaceNumber,
timeout: Option<u16>,
}
pub trait DfuRuntimeOps {
fn enter(&mut self);
}
impl<T: DfuRuntimeOps> DfuRuntimeClass<T> {
pub fn new<B: UsbBus>(alloc: &UsbBusAllocator<B>, dfu_ops: T) -> Self {
Self {
dfu_ops,
iface: alloc.interface(),
timeout: None,
}
}
}
impl<T: DfuRuntimeOps, B: UsbBus> UsbClass<B> for DfuRuntimeClass<T> {
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
writer.iad(
self.iface,
1,
USB_CLASS_APPLICATION_SPECIFIC,
DFU_SUBCLASS_FIRMWARE_UPGRADE,
DFU_PROTOCOL_RUNTIME)?;
writer.interface(
self.iface,
USB_CLASS_APPLICATION_SPECIFIC,
DFU_SUBCLASS_FIRMWARE_UPGRADE,
DFU_PROTOCOL_RUNTIME)?;
let detach_timeout: u16 = 255;
let transfer_size: u16 = 2048;
let dfu_version: u16 = 0x011a;
writer.write(
DFU_TYPE_FUNCTIONAL, &[
(DFU_WILL_DETACH | DFU_CAN_UPLOAD | DFU_CAN_DNLOAD) & !DFU_MANIFESTATION_TOLERANT, detach_timeout.to_le_bytes()[0], detach_timeout.to_le_bytes()[1], transfer_size.to_le_bytes()[0], transfer_size.to_le_bytes()[1], dfu_version.to_le_bytes()[0], dfu_version.to_le_bytes()[1], ])
}
fn poll(&mut self) {
if let Some(_timeout) = self.timeout.take() {
self.dfu_ops.enter();
}
}
fn control_out(&mut self, xfer: ControlOut<B>) {
let req = xfer.request();
if !(req.request_type == control::RequestType::Class
&& req.recipient == control::Recipient::Interface
&& req.index == u8::from(self.iface) as u16)
{
return;
}
match req.request {
DFU_REQ_DETACH => {
self.timeout = Some(req.value);
xfer.accept().ok();
},
_ => { xfer.reject().ok(); },
}
}
}