use crate::bootloader_info::{BootloaderInfo, Version};
use crate::context::UsbContext;
use crate::error::{Error, Result};
use crate::flash::Page;
use crate::operation::{Erase, Program, Read};
use crate::TIMEOUT;
use crc_any::CRC;
use rusb::DeviceHandle;
use std::convert::{TryFrom, TryInto};
fn read_ne_u32(input: &mut &[u8]) -> u32 {
let (int_bytes, rest) = input.split_at(std::mem::size_of::<u32>());
*input = rest;
u32::from_ne_bytes(int_bytes.try_into().unwrap())
}
fn read_version(input: &mut &[u8]) -> Version {
let (bytes, rest) = input.split_at(3);
*input = rest;
Version {
major: bytes[0],
minor: bytes[1],
patch: bytes[2],
}
}
pub struct TargetHandle<T: UsbContext> {
pub(crate) usb_device_handle: DeviceHandle<T>,
pub(crate) in_buffer_length: u16,
pub(crate) out_buffer_length: u16,
pub(crate) serial: String,
}
impl<T: UsbContext> TargetHandle<T> {
pub fn serial(&self) -> &str {
&self.serial
}
pub fn bootloader_info(&mut self) -> Result<BootloaderInfo> {
use std::ffi::CString;
let mut info_packet = [0u8; 64];
let (_, packet_length) =
self.send_command(Command::BootloaderInfo, &[0; 0], &mut info_packet)?;
let mut info_packet = &info_packet[..packet_length];
let build_date = read_ne_u32(&mut info_packet);
let build_number = read_ne_u32(&mut info_packet);
let application_base = read_ne_u32(&mut info_packet);
let application_size = read_ne_u32(&mut info_packet) as usize;
let mut build_date = build_date.to_string();
build_date.insert(6, '-');
build_date.insert(4, '-');
let version = read_version(&mut info_packet);
let identifier = CString::new(info_packet)
.map_err(|_| Error::MalformedResponse)?
.into_string()
.map_err(|_| Error::MalformedResponse)?;
Ok(BootloaderInfo {
build_number,
build_date,
application_base,
application_size,
version,
identifier,
})
}
pub fn read_crc(&mut self, start: u32, length: usize) -> Result<u32> {
let mut request_packet = vec![0u8; 8];
request_packet[0..4].copy_from_slice(&start.to_le_bytes());
request_packet[4..8].copy_from_slice(&(length as u32).to_le_bytes());
let mut crc_packet = [0u8; 4];
self.send_command(Command::ReadCrc, &request_packet, &mut crc_packet)?;
let crc = u32::from_le_bytes(crc_packet);
Ok(crc)
}
pub fn verify(&mut self, data: &[u8], address: u32) -> Result<()> {
let crc = self.read_crc(address, data.len())?;
if crc == crc32(data) {
Ok(())
} else {
Err(Error::VerificationError)
}
}
pub(crate) fn max_read_chunk_size(&self) -> usize {
self.in_buffer_length as usize
}
pub(crate) fn read_chunk(&mut self, start: u32, buffer: &mut [u8]) -> Result<()> {
let mut request_packet = vec![0u8; 8];
request_packet[0..4].copy_from_slice(&start.to_le_bytes());
request_packet[4..8].copy_from_slice(&(buffer.len() as u32).to_le_bytes());
self.send_command(Command::ReadMemory, &request_packet, buffer)
.map(|_| ())
}
pub(crate) fn erase_page(&mut self, page: Page) -> Result<()> {
let request_packet = [page.into()];
let mut status_packet = [0u8];
self.send_command(Command::ErasePage, &request_packet, &mut status_packet)?;
match status_packet[0] {
0 => Ok(()),
code => Err(Error::EraseError(code.into())),
}
}
pub fn erase_pages(&mut self, pages: &[Page]) -> Result<Erase<'_, T>> {
let bootloader_info = self.bootloader_info()?;
if pages
.iter()
.any(|page| !bootloader_info.application_pages().contains(page))
{
return Err(Error::InvalidRequest);
}
Ok(Erase::pages(self, pages))
}
pub fn erase_area(&mut self, start: u32, length: usize) -> Result<Erase<'_, T>> {
let bootloader_info = self.bootloader_info()?;
if (bootloader_info.application_base > start)
|| (bootloader_info.application_base as usize + bootloader_info.application_size
< start as usize + length)
{
return Err(Error::InvalidRequest);
}
Ok(Erase::area(self, start, length))
}
pub(crate) fn max_program_chunk_size(&self) -> usize {
self.out_buffer_length as usize - 4
}
pub(crate) fn program_chunk(&mut self, start: u32, data: &[u8]) -> Result<()> {
let mut address_packet = vec![0u8; 4];
address_packet[0..4].copy_from_slice(&start.to_le_bytes());
let mut packet = Vec::with_capacity(data.len() + 4);
packet.extend(address_packet);
packet.extend(data);
self.send_command(Command::Program, &packet, &mut [0; 0])
.map(|_| ())
}
pub fn program_at<'d>(&mut self, data: &'d [u8], address: u32) -> Result<Program<'d, '_, T>> {
let bootloader_info = self.bootloader_info()?;
if (bootloader_info.application_base > address)
|| (bootloader_info.application_base as usize + bootloader_info.application_size
< address as usize + data.len())
{
return Err(Error::InvalidRequest);
}
if !address.is_multiple_of(2) {
return Err(Error::InvalidRequest);
}
Ok(Program::at(self, data, address))
}
pub fn read_at<'d>(&mut self, buffer: &'d mut [u8], address: u32) -> Result<Read<'d, '_, T>> {
let bootloader_info = self.bootloader_info()?;
if (bootloader_info.application_base > address)
|| (bootloader_info.application_base as usize + bootloader_info.application_size
< address as usize + buffer.len())
{
return Err(Error::InvalidRequest);
}
Ok(Read::at(self, buffer, address))
}
pub fn exit_bootloader(&mut self) -> Result<()> {
self.send_command(Command::Exit, &[0; 0], &mut [0; 0])
.map(|_| ())
}
fn send_command(
&mut self,
cmd: Command,
write_data: &[u8],
read_data: &mut [u8],
) -> Result<(usize, usize)> {
self.usb_device_handle.claim_interface(0)?;
self.usb_device_handle.write_control(
rusb::request_type(
rusb::Direction::Out,
rusb::RequestType::Vendor,
rusb::Recipient::Device,
),
cmd as u8,
0,
0,
&[0u8; 0],
TIMEOUT,
)?;
let mut written = 0;
let mut read = 0;
if !write_data.is_empty() {
written = self
.usb_device_handle
.write_bulk(0x02, write_data, TIMEOUT)?;
}
if !read_data.is_empty() {
read = self.usb_device_handle.read_bulk(0x81, read_data, TIMEOUT)?;
}
self.usb_device_handle.release_interface(0)?;
Ok((written, read))
}
}
impl<T: UsbContext> TryFrom<rusb::DeviceHandle<T>> for TargetHandle<T> {
type Error = Error;
fn try_from(mut handle: rusb::DeviceHandle<T>) -> Result<Self> {
let device = handle.device();
let config_descriptor = device.active_config_descriptor()?;
let interface_descriptor = config_descriptor
.interfaces()
.next()
.ok_or(Error::IoError(rusb::Error::Io))?
.descriptors()
.next()
.ok_or(Error::IoError(rusb::Error::Io))?;
let mut endpoint_descriptors = interface_descriptor.endpoint_descriptors();
let in_buffer_length = endpoint_descriptors
.next()
.ok_or(Error::IoError(rusb::Error::Io))?
.max_packet_size();
let out_buffer_length = endpoint_descriptors
.next()
.ok_or(Error::IoError(rusb::Error::Io))?
.max_packet_size();
let language = handle.read_languages(TIMEOUT)?[0];
let device_desc = device.device_descriptor()?;
let serial = handle.read_serial_number_string(language, &device_desc, TIMEOUT)?;
handle.reset()?;
Ok(TargetHandle {
usb_device_handle: handle,
in_buffer_length,
out_buffer_length,
serial,
})
}
}
pub(crate) fn crc32(buff: &[u8]) -> u32 {
let mut crc = CRC::crc32mpeg2();
for bytes in buff.chunks(4) {
let mut word = vec![0u8; 4];
word[..bytes.len()].copy_from_slice(bytes);
word.reverse();
crc.digest(&word);
}
crc.get_crc() as u32
}
enum Command {
BootloaderInfo = 0x01,
ReadCrc = 0x02,
ReadMemory = 0x03,
ErasePage = 0x04,
Program = 0x05,
Exit = 0xff,
}