use std::array::TryFromSliceError;
use std::cell::{Ref, RefCell};
use std::fmt::{self, Debug, Display, Formatter};
use std::io::Read;
use std::time::{Duration, Instant};
use std::{mem, thread};
use clap::ValueEnum;
use clap::builder::PossibleValue;
use color_eyre::eyre::{Context, Error, OptionExt, Report, Result, eyre};
use dfu_core::{DfuIo, DfuProtocol, Error as DfuCoreError, State as DfuState};
use dfu_nusb::{DfuNusb, DfuSync, Error as DfuNusbError};
use log::{debug, error, trace, warn};
use nusb::descriptors::Descriptor;
use nusb::transfer::{Control, ControlType, Direction, Recipient, TransferError};
use nusb::{Device, DeviceInfo, Interface, list_devices};
use crate::BmpParams;
use crate::error::ErrorKind;
use crate::probe_identity::ProbeIdentity;
use crate::serial::bmd_rsp::BmdRspInterface;
use crate::serial::gdb_rsp::GdbRspInterface;
use crate::serial::interface::ProbeInterface;
use crate::usb::{
DfuFunctionalDescriptor, DfuOperatingMode, DfuRequest, GenericDescriptorRef, InterfaceClass, InterfaceSubClass,
Pid, PortId, Vid,
};
pub struct BmpDevice
{
device_info: Option<DeviceInfo>,
device: Option<Device>,
product_string_idx: u8,
serial_string_idx: u8,
language: u16,
interface: Option<Interface>,
mode: DfuOperatingMode,
platform: BmpPlatform,
serial: RefCell<Option<String>>,
port: PortId,
}
fn request_type(direction: Direction, control_type: ControlType, recipient: Recipient) -> u8
{
((direction as u8) << 7) | ((control_type as u8) << 5) | (recipient as u8)
}
fn handle_detach_errors<T>(result: std::result::Result<T, DfuNusbError>) -> Result<()>
{
if let Err(err) = result {
match err {
DfuNusbError::Transfer(error) => match error {
#[cfg(any(target_os = "linux", target_os = "android", target_os = "windows"))]
TransferError::Disconnected => Ok(()),
TransferError::Stall => Ok(()),
#[cfg(target_os = "macos")]
TransferError::Unknown => Ok(()),
_ => {
warn!("Possibly spurious error from OS while rebooting probe: {}", err);
Err(err.into())
},
},
_ => {
warn!("Possibly spurious error from OS while rebooting probe: {}", err);
Err(err.into())
},
}
} else {
Ok(())
}
}
impl BmpDevice
{
pub fn from_usb_device(device_info: DeviceInfo) -> Result<Self>
{
let vid = Vid(device_info.vendor_id());
let pid = Pid(device_info.product_id());
let (platform, mode) = BmpPlatform::from_vid_pid(vid, pid).ok_or_else(|| {
warn!("Device passed to BmpDevice::from_usb_device() does not seem to be a BMP device!");
warn!("The logic for finding this device is probably incorrect!");
ErrorKind::DeviceNotFound.error()
})?;
let device = device_info.open()?;
let device_desc = device.get_descriptor(
1, 0,
0,
Duration::from_secs(2),
)?;
let device_desc = match Descriptor::new(device_desc.as_slice()) {
None => {
return Err(ErrorKind::DeviceSeemsInvalid("no usable device descriptor".into())
.error()
.into());
},
Some(descriptor) => descriptor,
};
let mut languages = device.get_string_descriptor_supported_languages(Duration::from_secs(2))?;
let language = match languages.nth(0) {
Some(language) => language,
None => {
return Err(ErrorKind::DeviceSeemsInvalid("no string descriptor languages".into())
.error()
.into());
},
};
let interface = device.active_configuration()?
.interfaces()
.find(|interface|
interface.alt_settings()
.filter(|alt_mode|
InterfaceClass(alt_mode.class()) == InterfaceClass::APPLICATION_SPECIFIC &&
InterfaceSubClass(alt_mode.subclass()) == InterfaceSubClass::DFU
)
.count() > 0
)
.map(|interface| device.claim_interface(interface.interface_number()))
.ok_or_else(|| ErrorKind::DeviceSeemsInvalid("could not find DFU interface".into()).error())??;
let port = PortId::new(&device_info);
Ok(Self {
device_info: Some(device_info),
device: Some(device),
product_string_idx: device_desc[15],
serial_string_idx: device_desc[16],
interface: Some(interface),
language,
mode,
platform,
serial: RefCell::new(None),
port,
})
}
pub fn device_info(&self) -> &DeviceInfo
{
self.device_info
.as_ref()
.expect("Unreachable: self.device_info is None")
}
pub fn device(&self) -> &Device
{
self.device.as_ref().expect("Unreachable: self.device is None")
}
pub fn operating_mode(&self) -> DfuOperatingMode
{
self.mode
}
pub fn platform(&self) -> BmpPlatform
{
self.platform
}
pub fn serial_number(&self) -> Result<Ref<str>>
{
let serial = self.serial.borrow();
if serial.is_some() {
return Ok(Ref::map(serial, |s| s.as_deref().unwrap()));
}
drop(serial);
let serial =
self.device()
.get_string_descriptor(self.serial_string_idx, self.language, Duration::from_secs(2))?;
*self.serial.borrow_mut() = Some(serial);
Ok(Ref::map(self.serial.borrow(), |s| s.as_deref().unwrap()))
}
pub fn firmware_identity(&self) -> Result<ProbeIdentity>
{
self.device()
.get_string_descriptor(self.product_string_idx, self.language, Duration::from_secs(2))
.map_err(|e| {
ErrorKind::DeviceSeemsInvalid("no product string descriptor".into())
.error_from(e)
.into()
})
.and_then(|identity| identity.try_into())
}
pub fn port(&self) -> PortId
{
self.port.clone()
}
pub fn display(&self) -> Result<String>
{
let identity = self.firmware_identity()?;
let serial = self.serial_number()?;
Ok(format!("{}\n Serial: {}\n Port: {}", identity, serial, self.port()))
}
pub fn dfu_descriptors(&self) -> Result<(u8, DfuFunctionalDescriptor)>
{
let interface = self.device().active_configuration()?
.interfaces()
.find(|interface|
interface.alt_settings()
.filter(|alt_mode|
InterfaceClass(alt_mode.class()) == InterfaceClass::APPLICATION_SPECIFIC &&
InterfaceSubClass(alt_mode.subclass()) == InterfaceSubClass::DFU
)
.count() > 0
)
.ok_or_else(|| ErrorKind::DeviceSeemsInvalid("could not find DFU interface".into()).error())?;
let dfu_interface_descriptor = interface
.alt_settings()
.nth(0)
.ok_or_else(|| ErrorKind::DeviceSeemsInvalid("no DFU interfaces".into()).error())?;
let extra_descriptors: Vec<_> =
GenericDescriptorRef::multiple_from_bytes(dfu_interface_descriptor.descriptors().as_bytes());
let dfu_func_desc_bytes: &[u8; DfuFunctionalDescriptor::LENGTH as usize] = extra_descriptors
.into_iter()
.find(|descriptor| descriptor.descriptor_type() == DfuFunctionalDescriptor::TYPE)
.expect("DFU interface does not have a DFU functional descriptor! This shouldn't be possible!")
.raw[0..DfuFunctionalDescriptor::LENGTH as usize]
.try_into() .unwrap();
let dfu_func_desc = DfuFunctionalDescriptor::copy_from_bytes(dfu_func_desc_bytes)
.map_err(|source| ErrorKind::DeviceSeemsInvalid("DFU functional descriptor".into()).error_from(source))?;
Ok((dfu_interface_descriptor.interface_number(), dfu_func_desc))
}
fn leave_dfu_mode(&mut self) -> Result<()>
{
debug!("Attempting to leave DFU mode...");
let dfu = DfuNusb::open(
self.device().clone(),
self.interface
.clone()
.ok_or_eyre("BmpDevice does not have valid interface")?,
0,
)?;
let descriptor = dfu.functional_descriptor();
if descriptor.manifestation_tolerant {
dfu.write_control(
request_type(Direction::Out, ControlType::Class, Recipient::Interface),
DfuRequest::Dnload as u8,
0,
&[],
)?;
let mut buf: [u8; 6] = [0; 6];
let status = dfu.read_control(
request_type(Direction::In, ControlType::Class, Recipient::Interface),
DfuRequest::GetStatus as u8,
0,
&mut buf,
)?;
trace!("Device status after zero-length DNLOAD is 0x{:02x}", status);
debug!("DFU_GETSTATUS request completed. Device should now re-enumerate into runtime mode.");
} else {
let timeout_ms = dfu.functional_descriptor().detach_timeout;
handle_detach_errors(dfu.write_control(
request_type(Direction::Out, ControlType::Class, Recipient::Interface),
DfuRequest::Detach as u8,
timeout_ms,
&[],
))?;
debug!("DFU_DETACH request completed. Device should now re-enumerate into runtime mode.");
}
if !dfu.functional_descriptor().will_detach {
dfu.usb_reset()?;
}
self.interface = None;
Ok(())
}
fn enter_dfu_mode(&mut self) -> Result<()>
{
let (iface_number, func_desc) = self.dfu_descriptors()?;
let timeout_ms = func_desc.wDetachTimeOut;
let request = Control {
control_type: ControlType::Class,
recipient: Recipient::Interface,
request: DfuRequest::Detach as u8,
value: timeout_ms,
index: iface_number as u16,
};
handle_detach_errors(
self.interface
.as_ref()
.unwrap()
.control_out_blocking(
request,
&[], Duration::from_secs(1), )
.map_err(DfuNusbError::Transfer),
)?;
debug!("DFU_DETACH request completed. Device should now re-enumerate into DFU mode.");
self.interface = None;
Ok(())
}
pub fn request_detach(&mut self) -> Result<()>
{
use DfuOperatingMode::*;
match self.mode {
Runtime => self.enter_dfu_mode(),
FirmwareUpgrade => self.leave_dfu_mode(),
}
}
pub fn detach_and_enumerate(&mut self) -> Result<()>
{
let port = self.port();
self.request_detach()?;
drop(self.device_info.take());
drop(self.device.take());
thread::sleep(Duration::from_millis(500));
let dev = wait_for_probe_reboot(port, Duration::from_secs(5), "flash")?;
*self = dev;
Ok(())
}
pub fn detach_and_destroy(mut self) -> Result<()>
{
self.request_detach()
}
pub fn reboot(&self, dfu_iface: DfuSync) -> Result<()>
{
if !dfu_iface.manifestation_tolerant() {
handle_detach_errors(dfu_iface.detach())?;
}
if !dfu_iface.will_detach() {
Ok(dfu_iface.usb_reset()?)
} else {
Ok(())
}
}
fn try_download<'r, R>(&mut self, firmware: &'r R, length: u32, dfu_iface: &mut dfu_nusb::DfuSync) -> Result<()>
where
&'r R: Read,
R: ?Sized,
{
dfu_iface.download(firmware, length).map_err(|source| match source {
DfuNusbError::Transfer(nusb::transfer::TransferError::Disconnected) => {
error!("Black Magic Probe device disconnected during the flash process!");
warn!(
"If the device now fails to enumerate, try holding down the button while plugging the device in \
order to enter the bootloader."
);
ErrorKind::DeviceDisconnectDuringOperation.error_from(source).into()
},
_ => source.into(),
})
}
pub fn download<'r, R, P>(
&mut self,
firmware: &'r R,
length: u32,
firmware_type: FirmwareType,
progress: P,
) -> Result<DfuSync>
where
&'r R: Read,
R: ?Sized,
P: Fn(usize) + 'static,
{
if self.mode == DfuOperatingMode::Runtime {
self.detach_and_enumerate().wrap_err("detaching device for download")?;
}
let load_address = self.platform.load_address(firmware_type);
let dfu_dev = DfuNusb::open(
self.device.take().expect("Must have a valid device handle"),
self.interface.as_ref().unwrap().clone(),
0,
)?;
if let DfuProtocol::Dfuse {
..
} = dfu_dev.protocol()
{
println!("Erasing flash...");
}
let mut dfu_iface = dfu_dev.into_sync_dfu();
dfu_iface.with_progress(progress).override_address(load_address);
debug!("Load address: 0x{:08x}", load_address);
match self.try_download(firmware, length, &mut dfu_iface) {
Err(error) => {
if let Some(DfuNusbError::Dfu(DfuCoreError::StateError(DfuState::DfuError))) =
error.downcast_ref::<DfuNusbError>()
{
warn!(
"Device reported an error when trying to flash; going to clear status and try one more time..."
);
thread::sleep(Duration::from_millis(250));
let request = Control {
control_type: ControlType::Class,
recipient: Recipient::Interface,
request: DfuRequest::ClrStatus as u8,
value: 0,
index: 0, };
self.interface
.as_ref()
.unwrap()
.control_out_blocking(request, &[], Duration::from_secs(2))?;
self.try_download(firmware, length, &mut dfu_iface)
} else {
Err(error)
}
},
result => result,
}?;
Ok(dfu_iface)
}
pub fn into_inner_parts(self) -> (DeviceInfo, Device, DfuOperatingMode)
{
(
self.device_info.expect("Unreachable: self.device_info is None"),
self.device.expect("Unreachable: self.device is None"),
self.mode,
)
}
pub fn gdb_serial_interface(&self) -> Result<GdbRspInterface>
{
let serial_interface = ProbeInterface::from_device(self)?;
serial_interface.gdb_interface()
}
pub fn bmd_serial_interface(&self) -> Result<BmdRspInterface>
{
let serial_interface = ProbeInterface::from_device(self)?;
serial_interface.bmd_interface()
}
}
impl Debug for BmpDevice
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
writeln!(f, "BmpDevice {{")?;
writeln!(f, " {:?}", self.device_info)?;
writeln!(f, " {:?}", self.mode)?;
writeln!(f, " {:?}", self.platform)?;
writeln!(f, " {:?}", self.serial)?;
writeln!(f, " {:?}", self.port)?;
writeln!(f, "}}")?;
Ok(())
}
}
impl Display for BmpDevice
{
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error>
{
let display_str = match self.display() {
Ok(s) => s,
Err(e) => {
error!("Error formatting BlackMagicProbeDevice: {}", e);
"Unknown Black Magic Probe (error occurred fetching device details)".into()
},
};
write!(f, "{}", display_str)?;
Ok(())
}
}
pub struct Armv7mVectorTable<'b>
{
bytes: &'b [u8],
}
impl<'b> Armv7mVectorTable<'b>
{
fn word(&self, index: usize) -> Result<u32, TryFromSliceError>
{
let start = index * 4;
let array: [u8; 4] = self.bytes[(start)..(start + 4)].try_into()?;
Ok(u32::from_le_bytes(array))
}
pub fn from_bytes(bytes: &'b [u8]) -> Self
{
if bytes.len() < (4 * 2) {
panic!("Data passed is not long enough for an Armv7m Vector Table!");
}
Self {
bytes,
}
}
pub fn stack_pointer(&self) -> Result<u32, TryFromSliceError>
{
self.word(0)
}
pub fn reset_vector(&self) -> Result<u32, TryFromSliceError>
{
self.word(1)
}
pub fn exception(&self, exception_number: u32) -> Result<u32, TryFromSliceError>
{
self.word((exception_number + 1) as usize)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FirmwareType
{
Bootloader,
Application,
}
impl FirmwareType
{
pub fn detect_from_firmware(platform: BmpPlatform, firmware: &[u8]) -> Result<Self>
{
let buffer = &firmware[0..(4 * 2)];
let vector_table = Armv7mVectorTable::from_bytes(buffer);
let reset_vector = vector_table
.reset_vector()
.wrap_err("Firmware file does not seem valid: vector table too short")?;
debug!("Detected reset vector in firmware file: 0x{:08x}", reset_vector);
if (reset_vector & 0x0800_0000) != 0x0800_0000 {
return Err(eyre!(
"Firmware file does not seem valid: reset vector address seems to be outside of reasonable bounds - \
0x{:08x}",
reset_vector
));
}
let app_start = platform.load_address(Self::Application);
if reset_vector > app_start {
Ok(Self::Application)
} else {
Ok(Self::Bootloader)
}
}
}
impl Display for FirmwareType
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result
{
match self {
Self::Bootloader => write!(f, "bootloader")?,
Self::Application => write!(f, "application")?,
};
Ok(())
}
}
impl Default for FirmwareType
{
fn default() -> Self
{
FirmwareType::Application
}
}
impl ValueEnum for FirmwareType
{
fn value_variants<'a>() -> &'a [Self]
{
&[Self::Application, Self::Bootloader]
}
fn to_possible_value(&self) -> Option<PossibleValue>
{
match self {
Self::Bootloader => Some("bootloader".into()),
Self::Application => Some("application".into()),
}
}
}
pub enum FirmwareFormat
{
Binary,
Elf,
IntelHex,
}
impl FirmwareFormat
{
pub fn detect_from_firmware(firmware: &[u8]) -> Self
{
if &firmware[0..4] == b"\x7fELF" {
FirmwareFormat::Elf
} else if &firmware[0..1] == b":" {
FirmwareFormat::IntelHex
} else {
FirmwareFormat::Binary
}
}
}
#[derive(Debug, Clone, Default)]
pub struct BmpMatcher
{
index: Option<usize>,
serial: Option<String>,
port: Option<PortId>,
}
impl BmpMatcher
{
pub fn new() -> Self
{
Default::default()
}
pub fn from_params<Params>(params: &Params) -> Self
where
Params: BmpParams,
{
Self::new()
.index(params.index())
.serial(params.serial_number())
.port(None)
}
#[must_use]
pub fn index(mut self, idx: Option<usize>) -> Self
{
self.index = idx;
self
}
#[must_use]
pub fn serial<'s, IntoOptStrT>(mut self, serial: IntoOptStrT) -> Self
where
IntoOptStrT: Into<Option<&'s str>>,
{
self.serial = serial.into().map(|s| s.to_string());
self
}
#[must_use]
pub fn port(mut self, port: Option<PortId>) -> Self
{
self.port = port;
self
}
pub fn get_index(&self) -> Option<usize>
{
self.index
}
pub fn get_serial(&self) -> Option<&str>
{
self.serial.as_deref()
}
pub fn get_port(&self) -> Option<PortId>
{
self.port.clone()
}
pub fn find_matching_probes(&self) -> BmpMatchResults
{
let mut results = BmpMatchResults {
found: Vec::new(),
filtered_out: Vec::new(),
errors: Vec::new(),
};
let devices = match list_devices() {
Ok(d) => d,
Err(e) => {
results.errors.push(e.into());
return results;
},
};
let devices = devices.filter(|dev| {
let vid = dev.vendor_id();
let pid = dev.product_id();
BmpPlatform::from_vid_pid(Vid(vid), Pid(pid)).is_some()
});
for (index, device_info) in devices.enumerate() {
let serial_matches = match &self.serial {
Some(serial) => {
match device_info.serial_number() {
Some(s) => s == serial.as_str(),
None => false,
}
},
None => true,
};
let index_matches = self.index.is_none_or(|needle| needle == index);
let port_matches = self.port.as_ref().is_none_or(|p| {
let port = PortId::new(&device_info);
p == &port
});
if index_matches && port_matches && serial_matches {
match BmpDevice::from_usb_device(device_info) {
Ok(bmpdev) => results.found.push(bmpdev),
Err(e) => {
results.errors.push(e);
continue;
},
};
} else {
results.filtered_out.push(device_info);
}
}
results
}
}
#[derive(Debug, Default)]
pub struct BmpMatchResults
{
pub found: Vec<BmpDevice>,
pub filtered_out: Vec<DeviceInfo>,
pub errors: Vec<Report>,
}
impl BmpMatchResults
{
pub fn pop_all(&mut self) -> Result<Vec<BmpDevice>>
{
if self.found.is_empty() {
if self.filtered_out.len() == 1 {
if let Ok(bmpdev) = BmpDevice::from_usb_device(self.filtered_out.pop().unwrap()) {
warn!(
"Matching device not found, but and the following Black Magic Probe device was filtered out: \
{}",
&bmpdev,
);
} else {
warn!("Matching device not found but 1 Black Magic Probe device was filtered out.");
}
warn!("Filter arguments (--serial, --index, --port) may be incorrect.");
} else if self.filtered_out.len() > 1 {
warn!(
"Matching devices not found but {} Black Magic Probe devices were filtered out.",
self.filtered_out.len(),
);
warn!("Filter arguments (--serial, --index, --port) may be incorrect.");
}
if !self.errors.is_empty() {
warn!("Device not found and errors occurred when searching for devices.");
warn!(
"One of these may be why the Black Magic Probe device was not found: {:?}",
self.errors.as_slice()
);
}
return Err(ErrorKind::DeviceNotFound.error().into());
}
if !self.errors.is_empty() {
warn!("Matching device found but errors occurred when searching for devices.");
warn!("It is unlikely but possible that the incorrect device was selected!");
warn!("Other device errors: {:?}", self.errors.as_slice());
}
Ok(mem::take(&mut self.found))
}
pub fn pop_single(&mut self, operation: &str) -> Result<BmpDevice, ErrorKind>
{
if self.found.is_empty() {
if !self.filtered_out.is_empty() {
let (suffix, verb) = if self.filtered_out.len() > 1 {
("s", "were")
} else {
("", "was")
};
warn!(
"Matching device not found and {} Black Magic Probe device{} {} filtered out.",
self.filtered_out.len(),
suffix,
verb,
);
warn!("Filter arguments (--serial, --index, --port may be incorrect.");
}
if !self.errors.is_empty() {
warn!("Device not found and errors occurred when searching for devices.");
warn!(
"One of these may be why the Black Magic Probe device was not found: {:?}",
self.errors.as_slice()
);
}
return Err(ErrorKind::DeviceNotFound);
}
if self.found.len() > 1 {
error!(
"{} operation only accepts one Black Magic Probe device, but {} were found!",
operation,
self.found.len()
);
error!("Hint: try bmputil info and revise your filter arguments (--serial, --index, --port).");
return Err(ErrorKind::TooManyDevices);
}
if !self.errors.is_empty() {
warn!("Matching device found but errors occurred when searching for devices.");
warn!("It is unlikely but possible that the incorrect device was selected!");
warn!("Other device errors: {:?}", self.errors.as_slice());
}
Ok(self.found.remove(0))
}
pub(crate) fn pop_single_silent(&mut self) -> Result<BmpDevice, ErrorKind>
{
if self.found.len() > 1 {
Err(ErrorKind::TooManyDevices)
} else if self.found.is_empty() {
Err(ErrorKind::DeviceNotFound)
} else {
Ok(self.found.remove(0))
}
}
}
pub fn wait_for_probe_reboot(port: PortId, timeout: Duration, operation: &str) -> Result<BmpDevice>
{
let silence_timeout = timeout / 2;
let matcher = BmpMatcher {
index: None,
serial: None,
port: Some(port),
};
let start = Instant::now();
let mut dev = matcher.find_matching_probes().pop_single_silent();
while let Err(ErrorKind::DeviceNotFound) = dev {
trace!(
"Waiting for probe reboot: {} ms",
Instant::now().duration_since(start).as_millis()
);
if Instant::now().duration_since(start) > timeout {
error!("Timed-out waiting for Black Magic Probe to re-enumerate!");
return dev.map_err(|kind| {
Error::from(kind.error())
.wrap_err("Black Magic Probe device did not come back online (invalid firmware?)")
});
}
thread::sleep(Duration::from_millis(200));
if Instant::now().duration_since(start) > silence_timeout {
dev = matcher.find_matching_probes().pop_single(operation);
} else {
dev = matcher.find_matching_probes().pop_single_silent();
}
}
let dev = dev.map_err(|kind| kind.error())?;
Ok(dev)
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum BmpPlatform
{
BlackMagicDebug,
DragonBoot,
STM32DeviceDFU,
}
impl BmpPlatform
{
pub const BMD_DFU_VID_PID: (Vid, Pid) = (Vid(0x1d50), Pid(0x6017));
pub const BMD_RUNTIME_VID_PID: (Vid, Pid) = (Vid(0x1d50), Pid(0x6018));
pub const DRAGON_BOOT_VID_PID: (Vid, Pid) = (Vid(0x1209), Pid(0xbadb));
pub const STM32_DFU_VID_PID: (Vid, Pid) = (Vid(0x0483), Pid(0xdf11));
pub const fn from_vid_pid(vid: Vid, pid: Pid) -> Option<(Self, DfuOperatingMode)>
{
use BmpPlatform::*;
use DfuOperatingMode::*;
match (vid, pid) {
Self::BMD_RUNTIME_VID_PID => Some((BlackMagicDebug, Runtime)),
Self::BMD_DFU_VID_PID => Some((BlackMagicDebug, FirmwareUpgrade)),
Self::DRAGON_BOOT_VID_PID => Some((DragonBoot, FirmwareUpgrade)),
Self::STM32_DFU_VID_PID => Some((STM32DeviceDFU, FirmwareUpgrade)),
_ => None,
}
}
#[allow(dead_code)]
pub const fn runtime_ids(self) -> (Vid, Pid)
{
Self::BMD_RUNTIME_VID_PID
}
#[allow(dead_code)]
pub const fn dfu_ids(self) -> (Vid, Pid)
{
use BmpPlatform::*;
match self {
BlackMagicDebug => Self::BMD_DFU_VID_PID,
DragonBoot => Self::DRAGON_BOOT_VID_PID,
STM32DeviceDFU => Self::STM32_DFU_VID_PID,
}
}
#[allow(dead_code)]
pub const fn ids_for_mode(self, mode: DfuOperatingMode) -> (Vid, Pid)
{
use DfuOperatingMode::*;
match mode {
Runtime => self.runtime_ids(),
FirmwareUpgrade => self.dfu_ids(),
}
}
pub const fn load_address(self, firm_type: FirmwareType) -> u32
{
use BmpPlatform::*;
use FirmwareType::*;
match self {
BlackMagicDebug => match firm_type {
Bootloader => 0x0800_0000,
Application => 0x0800_2000,
},
DragonBoot => 0x0800_2000,
STM32DeviceDFU => 0x0800_0000,
}
}
}
impl Default for BmpPlatform
{
fn default() -> Self
{
BmpPlatform::BlackMagicDebug
}
}