Compare commits

...

2 Commits

Author SHA1 Message Date
7c43e0f7ab feat: backup restore
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-16 15:22:22 +01:00
4825b699a6 chore: clean up containerinfo
Signed-off-by: Nikolaos Karaolidis <nick@karaolidis.com>
2025-10-16 12:15:09 +01:00
4 changed files with 426 additions and 224 deletions

View File

@@ -2,37 +2,24 @@ pub mod devices;
pub mod error; pub mod error;
pub mod ptp; pub mod ptp;
use std::{cmp::min, io::Cursor, time::Duration}; use std::{io::Cursor, time::Duration};
use anyhow::{anyhow, bail}; use anyhow::{anyhow, bail};
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{LittleEndian, WriteBytesExt};
use devices::SupportedCamera; use devices::SupportedCamera;
use log::{debug, error, trace}; use log::{debug, error};
use ptp::{ use ptp::{
enums::{CommandCode, ContainerType, PropCode, ResponseCode, UsbMode}, Ptp,
structs::{ContainerInfo, DeviceInfo}, enums::{CommandCode, PropCode, UsbMode},
structs::DeviceInfo,
}; };
use rusb::{GlobalContext, constants::LIBUSB_CLASS_IMAGE}; use rusb::{GlobalContext, constants::LIBUSB_CLASS_IMAGE};
const SESSION: u32 = 1; const SESSION: u32 = 1;
pub struct Usb {
bus: u8,
address: u8,
interface: u8,
}
pub struct Ptp {
bulk_in: u8,
bulk_out: u8,
handle: rusb::DeviceHandle<GlobalContext>,
transaction_id: u32,
}
pub struct Camera { pub struct Camera {
pub r#impl: Box<dyn CameraImpl<GlobalContext>>, r#impl: Box<dyn CameraImpl<GlobalContext>>,
usb: Usb, ptp: Ptp,
pub ptp: Ptp,
} }
impl Camera { impl Camera {
@@ -42,9 +29,9 @@ impl Camera {
let bus = device.bus_number(); let bus = device.bus_number();
let address = device.address(); let address = device.address();
let config_desc = device.active_config_descriptor()?; let config_descriptor = device.active_config_descriptor()?;
let interface_descriptor = config_desc let interface_descriptor = config_descriptor
.interfaces() .interfaces()
.flat_map(|i| i.descriptors()) .flat_map(|i| i.descriptors())
.find(|x| x.class_code() == LIBUSB_CLASS_IMAGE) .find(|x| x.class_code() == LIBUSB_CLASS_IMAGE)
@@ -53,12 +40,6 @@ impl Camera {
let interface = interface_descriptor.interface_number(); let interface = interface_descriptor.interface_number();
debug!("Found interface {interface}"); debug!("Found interface {interface}");
let usb = Usb {
bus,
address,
interface,
};
let handle = device.open()?; let handle = device.open()?;
handle.claim_interface(interface)?; handle.claim_interface(interface)?;
@@ -72,20 +53,27 @@ impl Camera {
rusb::Direction::Out, rusb::Direction::Out,
rusb::TransferType::Bulk, rusb::TransferType::Bulk,
)?; )?;
let transaction_id = 0; let transaction_id = 0;
let chunk_size = r#impl.chunk_size();
let mut ptp = Ptp { let mut ptp = Ptp {
bus,
address,
interface,
bulk_in, bulk_in,
bulk_out, bulk_out,
handle, handle,
transaction_id, transaction_id,
chunk_size,
}; };
debug!("Opening session"); debug!("Opening session");
let () = r#impl.open_session(&mut ptp, SESSION)?; let () = r#impl.open_session(&mut ptp, SESSION)?;
debug!("Session opened"); debug!("Session opened");
return Ok(Self { r#impl, usb, ptp }); return Ok(Self { r#impl, ptp });
} }
} }
@@ -117,7 +105,7 @@ impl Camera {
} }
pub fn connected_usb_id(&self) -> String { pub fn connected_usb_id(&self) -> String {
format!("{}.{}", self.usb.bus, self.usb.address) format!("{}.{}", self.ptp.bus, self.ptp.address)
} }
fn prop_value_as_scalar(data: &[u8]) -> anyhow::Result<u32> { fn prop_value_as_scalar(data: &[u8]) -> anyhow::Result<u32> {
@@ -187,200 +175,41 @@ impl Drop for Camera {
error!("Error closing session: {e}"); error!("Error closing session: {e}");
} }
debug!("Session closed"); debug!("Session closed");
debug!("Releasing interface");
if let Err(e) = self.ptp.handle.release_interface(self.usb.interface) {
error!("Error releasing interface: {e}");
}
debug!("Interface released");
} }
} }
pub trait CameraImpl<P: rusb::UsbContext> { pub trait CameraImpl<P: rusb::UsbContext> {
fn supported_camera(&self) -> &'static SupportedCamera<P>; fn supported_camera(&self) -> &'static SupportedCamera<P>;
fn timeout(&self) -> Option<Duration> { fn timeout(&self) -> Duration {
None Duration::default()
} }
fn chunk_size(&self) -> usize { fn chunk_size(&self) -> usize {
1024 * 1024 1024 * 1024
} }
fn send(
&self,
ptp: &mut Ptp,
code: CommandCode,
params: Option<&[u32]>,
data: Option<&[u8]>,
transaction: bool,
) -> anyhow::Result<Vec<u8>> {
let transaction_id = if transaction {
Some(ptp.transaction_id)
} else {
None
};
let params = params.unwrap_or_default();
let mut payload = Vec::with_capacity(params.len() * 4);
for p in params {
payload.write_u32::<LittleEndian>(*p).ok();
}
trace!(
"Sending PTP command: {:?}, transaction: {:?}, parameters ({} bytes): {:x?}",
code,
transaction_id,
payload.len(),
payload,
);
self.write(ptp, ContainerType::Command, code, &payload, transaction_id)?;
if let Some(data) = data {
trace!("Sending PTP data: {} bytes", data.len());
self.write(ptp, ContainerType::Data, code, data, transaction_id)?;
}
let mut data_payload = Vec::new();
loop {
let (container, payload) = self.read(ptp)?;
match container.kind {
ContainerType::Data => {
trace!("Data received: {} bytes", payload.len());
data_payload = payload;
}
ContainerType::Response => {
trace!("Response received: code {:?}", container.code);
let code = ResponseCode::try_from(container.code)?;
if code != ResponseCode::Ok {
bail!(ptp::error::Error::Response(container.code));
}
trace!(
"Command {:?} completed successfully with data payload of {} bytes",
code,
data_payload.len(),
);
return Ok(data_payload);
}
_ => {
debug!("Ignoring unexpected container type: {:?}", container.kind);
}
}
}
}
fn write(
&self,
ptp: &mut Ptp,
kind: ContainerType,
code: CommandCode,
payload: &[u8],
// Fuji, for the love of God don't ever write code again.
transaction_id: Option<u32>,
) -> anyhow::Result<()> {
// Look at what you made me do. Fuck.
let header_len = ContainerInfo::SIZE
- if transaction_id.is_none() {
size_of::<u32>()
} else {
0
};
let first_chunk_len = min(payload.len(), self.chunk_size() - header_len);
let total_len = u32::try_from(payload.len() + header_len)?;
let mut buffer = Vec::with_capacity(first_chunk_len + header_len);
buffer.write_u32::<LittleEndian>(total_len)?;
buffer.write_u16::<LittleEndian>(kind as u16)?;
buffer.write_u16::<LittleEndian>(code as u16)?;
if let Some(transaction_id) = transaction_id {
buffer.write_u32::<LittleEndian>(transaction_id)?;
}
buffer.extend_from_slice(&payload[..first_chunk_len]);
trace!(
"Writing PTP {kind:?} container, code: {code:?}, transaction: {transaction_id:?}, first_chunk: {first_chunk_len} bytes",
);
let timeout = self.timeout().unwrap_or_default();
ptp.handle.write_bulk(ptp.bulk_out, &buffer, timeout)?;
for chunk in payload[first_chunk_len..].chunks(self.chunk_size()) {
trace!("Writing additional chunk ({} bytes)", chunk.len(),);
ptp.handle.write_bulk(ptp.bulk_out, chunk, timeout)?;
}
trace!(
"Write completed for code {:?}, total payload of {} bytes",
code,
payload.len()
);
Ok(())
}
fn read(&self, ptp: &mut Ptp) -> anyhow::Result<(ContainerInfo, Vec<u8>)> {
let timeout = self.timeout().unwrap_or_default();
let mut stack_buf = [0u8; 8 * 1024];
let n = ptp.handle.read_bulk(ptp.bulk_in, &mut stack_buf, timeout)?;
let buf = &stack_buf[..n];
trace!("Read {n} bytes from bulk_in");
let container_info = ContainerInfo::parse(buf)?;
if container_info.payload_len == 0 {
trace!("No payload in container");
return Ok((container_info, Vec::new()));
}
let payload_len = container_info.payload_len as usize;
let mut payload = Vec::with_capacity(payload_len);
if buf.len() > ContainerInfo::SIZE {
payload.extend_from_slice(&buf[ContainerInfo::SIZE..]);
}
while payload.len() < payload_len {
let remaining = payload_len - payload.len();
let mut chunk = vec![0u8; min(remaining, self.chunk_size())];
let n = ptp.handle.read_bulk(ptp.bulk_in, &mut chunk, timeout)?;
trace!("Read additional chunk ({n} bytes)");
if n == 0 {
break;
}
payload.extend_from_slice(&chunk[..n]);
}
trace!(
"Finished reading container, total payload of {} bytes",
payload.len(),
);
Ok((container_info, payload))
}
fn open_session(&self, ptp: &mut Ptp, session_id: u32) -> anyhow::Result<()> { fn open_session(&self, ptp: &mut Ptp, session_id: u32) -> anyhow::Result<()> {
debug!("Sending OpenSession command"); debug!("Sending OpenSession command");
_ = self.send( _ = ptp.send(
ptp,
CommandCode::OpenSession, CommandCode::OpenSession,
Some(&[session_id]), Some(&[session_id]),
None, None,
true, true,
self.timeout(),
)?; )?;
Ok(()) Ok(())
} }
fn close_session(&self, ptp: &mut Ptp, _: u32) -> anyhow::Result<()> { fn close_session(&self, ptp: &mut Ptp, _: u32) -> anyhow::Result<()> {
debug!("Sending CloseSession command"); debug!("Sending CloseSession command");
_ = self.send(ptp, CommandCode::CloseSession, None, None, true)?; _ = ptp.send(CommandCode::CloseSession, None, None, true, self.timeout())?;
Ok(()) Ok(())
} }
fn get_info(&self, ptp: &mut Ptp) -> anyhow::Result<DeviceInfo> { fn get_info(&self, ptp: &mut Ptp) -> anyhow::Result<DeviceInfo> {
debug!("Sending GetDeviceInfo command"); debug!("Sending GetDeviceInfo command");
let response = self.send(ptp, CommandCode::GetDeviceInfo, None, None, true)?; let response = ptp.send(CommandCode::GetDeviceInfo, None, None, true, self.timeout())?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());
let info = DeviceInfo::try_from(response.as_slice())?; let info = DeviceInfo::try_from(response.as_slice())?;
Ok(info) Ok(info)
@@ -388,12 +217,12 @@ pub trait CameraImpl<P: rusb::UsbContext> {
fn get_prop_value(&self, ptp: &mut Ptp, prop: PropCode) -> anyhow::Result<Vec<u8>> { fn get_prop_value(&self, ptp: &mut Ptp, prop: PropCode) -> anyhow::Result<Vec<u8>> {
debug!("Sending GetDevicePropValue command for property {prop:?}"); debug!("Sending GetDevicePropValue command for property {prop:?}");
let response = self.send( let response = ptp.send(
ptp,
CommandCode::GetDevicePropValue, CommandCode::GetDevicePropValue,
Some(&[prop as u32]), Some(&[prop as u32]),
None, None,
true, true,
self.timeout(),
)?; )?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());
Ok(response) Ok(response)
@@ -403,11 +232,23 @@ pub trait CameraImpl<P: rusb::UsbContext> {
const HANDLE: u32 = 0x0; const HANDLE: u32 = 0x0;
debug!("Sending GetObjectInfo command for backup"); debug!("Sending GetObjectInfo command for backup");
let response = self.send(ptp, CommandCode::GetObjectInfo, Some(&[HANDLE]), None, true)?; let response = ptp.send(
CommandCode::GetObjectInfo,
Some(&[HANDLE]),
None,
true,
self.timeout(),
)?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());
debug!("Sending GetObject command for backup"); debug!("Sending GetObject command for backup");
let response = self.send(ptp, CommandCode::GetObject, Some(&[HANDLE]), None, true)?; let response = ptp.send(
CommandCode::GetObject,
Some(&[HANDLE]),
None,
true,
self.timeout(),
)?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());
Ok(response) Ok(response)
@@ -415,30 +256,33 @@ pub trait CameraImpl<P: rusb::UsbContext> {
fn import_backup(&self, ptp: &mut Ptp, buffer: &[u8]) -> anyhow::Result<()> { fn import_backup(&self, ptp: &mut Ptp, buffer: &[u8]) -> anyhow::Result<()> {
debug!("Preparing ObjectInfo header for backup"); debug!("Preparing ObjectInfo header for backup");
let mut obj_info = vec![0u8; 1088];
let mut cursor = Cursor::new(&mut obj_info[..]); let mut header1 = vec![0u8; 1012];
let mut cursor = Cursor::new(&mut header1[..]);
cursor.write_u32::<LittleEndian>(0x0)?; cursor.write_u32::<LittleEndian>(0x0)?;
cursor.write_u16::<LittleEndian>(0x5000)?; cursor.write_u16::<LittleEndian>(0x5000)?;
cursor.write_u16::<LittleEndian>(0x0)?; cursor.write_u16::<LittleEndian>(0x0)?;
cursor.write_u32::<LittleEndian>(u32::try_from(buffer.len())?)?; cursor.write_u32::<LittleEndian>(u32::try_from(buffer.len())?)?;
let header2 = vec![0u8; 64];
debug!("Sending SendObjectInfo command for backup"); debug!("Sending SendObjectInfo command for backup");
let response = self.send( let response = ptp.send_many(
ptp,
CommandCode::SendObjectInfo, CommandCode::SendObjectInfo,
Some(&[0x0, 0x0]), Some(&[0x0, 0x0]),
Some(&obj_info), Some(&[&header1, &header2]),
true, true,
self.timeout(),
)?; )?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());
debug!("Sending SendObject command for backup"); debug!("Sending SendObject command for backup");
let response = self.send( let response = ptp.send(
ptp,
CommandCode::SendObject, CommandCode::SendObject,
Some(&[0x0]), Some(&[0x0]),
Some(buffer), Some(buffer),
false, true,
self.timeout(),
)?; )?;
debug!("Received response with {} bytes", response.len()); debug!("Received response with {} bytes", response.len());

View File

@@ -31,7 +31,7 @@ impl TryFrom<u16> for CommandCode {
0x100D => Ok(Self::SendObject), 0x100D => Ok(Self::SendObject),
0x1015 => Ok(Self::GetDevicePropValue), 0x1015 => Ok(Self::GetDevicePropValue),
0x1016 => Ok(Self::SetDevicePropValue), 0x1016 => Ok(Self::SetDevicePropValue),
v => bail!("Unknown command code '{v}'"), v => bail!("Unknown command code '{v:x?}'"),
} }
} }
} }
@@ -112,11 +112,43 @@ impl std::convert::TryFrom<u16> for ResponseCode {
0x201E => Ok(Self::SessionAlreadyOpen), 0x201E => Ok(Self::SessionAlreadyOpen),
0x201F => Ok(Self::TransactionCancelled), 0x201F => Ok(Self::TransactionCancelled),
0x2020 => Ok(Self::SpecificationOfDestinationUnsupported), 0x2020 => Ok(Self::SpecificationOfDestinationUnsupported),
v => bail!("Unknown response code '{v}'"), v => bail!("Unknown response code '{v:x?}'"),
} }
} }
} }
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContainerCode {
Command(CommandCode),
Response(ResponseCode),
}
impl From<ContainerCode> for u16 {
fn from(code: ContainerCode) -> Self {
match code {
ContainerCode::Command(cmd) => cmd as Self,
ContainerCode::Response(resp) => resp as Self,
}
}
}
impl TryFrom<u16> for ContainerCode {
type Error = anyhow::Error;
fn try_from(value: u16) -> Result<Self, Self::Error> {
if let Ok(cmd) = CommandCode::try_from(value) {
return Ok(Self::Command(cmd));
}
if let Ok(resp) = ResponseCode::try_from(value) {
return Ok(Self::Response(resp));
}
bail!("Unknown container code '{value:x?}'");
}
}
#[repr(u32)] #[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PropCode { pub enum PropCode {

View File

@@ -2,3 +2,273 @@ pub mod enums;
pub mod error; pub mod error;
pub mod read; pub mod read;
pub mod structs; pub mod structs;
use std::{cmp::min, time::Duration};
use anyhow::bail;
use byteorder::{LittleEndian, WriteBytesExt};
use enums::{CommandCode, ContainerCode, ContainerType, ResponseCode};
use log::{debug, error, trace};
use rusb::GlobalContext;
use structs::ContainerInfo;
pub struct Ptp {
pub bus: u8,
pub address: u8,
pub interface: u8,
pub bulk_in: u8,
pub bulk_out: u8,
pub handle: rusb::DeviceHandle<GlobalContext>,
pub transaction_id: u32,
pub chunk_size: usize,
}
impl Ptp {
pub fn send(
&mut self,
code: CommandCode,
params: Option<&[u32]>,
data: Option<&[u8]>,
transaction: bool,
timeout: Duration,
) -> anyhow::Result<Vec<u8>> {
let (params, transaction_id) = self.prepare_send(params, transaction);
self.send_header(code, params, transaction_id, timeout)?;
if let Some(data) = data {
self.write(ContainerType::Data, code, data, transaction_id, timeout)?;
}
self.receive_response(timeout)
}
pub fn send_many(
&mut self,
code: CommandCode,
params: Option<&[u32]>,
data: Option<&[&[u8]]>,
transaction: bool,
timeout: Duration,
) -> anyhow::Result<Vec<u8>> {
let (params, transaction_id) = self.prepare_send(params, transaction);
self.send_header(code, params, transaction_id, timeout)?;
if let Some(data) = data {
self.write_many(ContainerType::Data, code, data, transaction_id, timeout)?;
}
self.receive_response(timeout)
}
fn prepare_send<'a>(
&mut self,
params: Option<&'a [u32]>,
transaction: bool,
) -> (&'a [u32], Option<u32>) {
let params = params.unwrap_or_default();
let transaction_id = if transaction {
let transaction_id = Some(self.transaction_id);
self.transaction_id += 1;
transaction_id
} else {
None
};
(params, transaction_id)
}
fn send_header(
&self,
code: CommandCode,
params: &[u32],
transaction_id: Option<u32>,
timeout: Duration,
) -> anyhow::Result<()> {
let mut payload = Vec::with_capacity(params.len() * 4);
for p in params {
payload.write_u32::<LittleEndian>(*p).ok();
}
trace!(
"Sending PTP command: {:?}, transaction: {:?}, parameters ({} bytes): {:x?}",
code,
transaction_id,
payload.len(),
payload,
);
self.write(
ContainerType::Command,
code,
&payload,
transaction_id,
timeout,
)?;
Ok(())
}
fn receive_response(&self, timeout: Duration) -> anyhow::Result<Vec<u8>> {
let mut response = Vec::new();
loop {
let (container, payload) = self.read(timeout)?;
match container.kind {
ContainerType::Data => {
trace!("Response received: data ({} bytes)", payload.len());
response = payload;
}
ContainerType::Response => {
trace!("Response received: code {:?}", container.code);
match container.code {
ContainerCode::Command(_) | ContainerCode::Response(ResponseCode::Ok) => {}
ContainerCode::Response(code) => {
bail!(error::Error::Response(code as u16));
}
}
trace!(
"Command completed successfully, response payload of {} bytes",
response.len(),
);
return Ok(response);
}
_ => {
debug!("Ignoring unexpected container type: {:?}", container.kind);
}
}
}
}
fn write(
&self,
kind: ContainerType,
code: CommandCode,
payload: &[u8],
transaction_id: Option<u32>,
timeout: Duration,
) -> anyhow::Result<()> {
let container_info = ContainerInfo::new(kind, code, transaction_id, payload.len())?;
let mut buffer: Vec<u8> = container_info.try_into()?;
let first_chunk_len = min(payload.len(), self.chunk_size - container_info.len());
buffer.extend_from_slice(&payload[..first_chunk_len]);
trace!(
"Writing PTP {kind:?} container, code: {code:?}, transaction: {transaction_id:?}, first payload chunk ({first_chunk_len} bytes)",
);
self.handle.write_bulk(self.bulk_out, &buffer, timeout)?;
for chunk in payload[first_chunk_len..].chunks(self.chunk_size) {
trace!("Writing additional payload chunk ({} bytes)", chunk.len(),);
self.handle.write_bulk(self.bulk_out, chunk, timeout)?;
}
trace!(
"Write completed for code {:?}, total payload of {} bytes",
code,
payload.len()
);
Ok(())
}
fn write_many(
&self,
kind: ContainerType,
code: CommandCode,
parts: &[&[u8]],
transaction_id: Option<u32>,
timeout: Duration,
) -> anyhow::Result<()> {
if parts.is_empty() {
return self.write(kind, code, &[], transaction_id, timeout);
}
if parts.len() == 1 {
return self.write(kind, code, parts[0], transaction_id, timeout);
}
let total_len: usize = parts.iter().map(|c| c.len()).sum();
let container_info = ContainerInfo::new(kind, code, transaction_id, total_len)?;
let mut buffer: Vec<u8> = container_info.try_into()?;
let first = parts[0];
let first_part_chunk_len = min(first.len(), self.chunk_size - container_info.len());
buffer.extend_from_slice(&first[..first_part_chunk_len]);
trace!(
"Writing PTP {kind:?} container, code: {code:?}, transaction: {transaction_id:?}, first payload part chunk ({first_part_chunk_len} bytes)",
);
self.handle.write_bulk(self.bulk_out, &buffer, timeout)?;
for chunk in first[first_part_chunk_len..].chunks(self.chunk_size) {
trace!(
"Writing additional payload part chunk ({} bytes)",
chunk.len(),
);
self.handle.write_bulk(self.bulk_out, chunk, timeout)?;
}
for part in &parts[1..] {
trace!("Writing additional payload part");
for chunk in part.chunks(self.chunk_size) {
trace!(
"Writing additional payload part chunk ({} bytes)",
chunk.len(),
);
self.handle.write_bulk(self.bulk_out, chunk, timeout)?;
}
trace!(
"Write completed for part, total payload of {} bytes",
part.len()
);
}
trace!("Write completed for code {code:?}, total payload of {total_len} bytes");
Ok(())
}
fn read(&self, timeout: Duration) -> anyhow::Result<(ContainerInfo, Vec<u8>)> {
let mut stack_buf = [0u8; 8 * 1024];
let n = self
.handle
.read_bulk(self.bulk_in, &mut stack_buf, timeout)?;
let buf = &stack_buf[..n];
trace!("Read chunk ({n} bytes)");
let container_info = ContainerInfo::try_from(buf)?;
let payload_len = container_info.payload_len();
if payload_len == 0 {
trace!("No payload in container");
return Ok((container_info, Vec::new()));
}
let mut payload = Vec::with_capacity(payload_len);
if buf.len() > ContainerInfo::SIZE {
payload.extend_from_slice(&buf[ContainerInfo::SIZE..]);
}
while payload.len() < payload_len {
let remaining = payload_len - payload.len();
let mut chunk = vec![0u8; min(remaining, self.chunk_size)];
let n = self.handle.read_bulk(self.bulk_in, &mut chunk, timeout)?;
trace!("Read additional chunk ({n} bytes)");
if n == 0 {
break;
}
payload.extend_from_slice(&chunk[..n]);
}
trace!(
"Finished reading container, total payload of {} bytes",
payload.len(),
);
Ok((container_info, payload))
}
}
impl Drop for Ptp {
fn drop(&mut self) {
debug!("Releasing interface");
if let Err(e) = self.handle.release_interface(self.interface) {
error!("Error releasing interface: {e}");
}
debug!("Interface released");
}
}

View File

@@ -1,8 +1,11 @@
use std::io::Cursor; use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use super::{enums::ContainerType, read::Read}; use super::{
enums::{CommandCode, ContainerCode, ContainerType},
read::Read,
};
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
@@ -50,28 +53,81 @@ impl TryFrom<&[u8]> for DeviceInfo {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct ContainerInfo { pub struct ContainerInfo {
pub total_len: u32,
pub kind: ContainerType, pub kind: ContainerType,
pub payload_len: u32, pub code: ContainerCode,
pub code: u16, pub transaction_id: Option<u32>,
} }
impl ContainerInfo { impl ContainerInfo {
pub const SIZE: usize = const BASE_SIZE: usize = size_of::<u32>() + size_of::<u16>() + size_of::<u16>();
size_of::<ContainerType>() + size_of::<u32>() + size_of::<u16>() + size_of::<u32>(); pub const SIZE: usize = Self::BASE_SIZE + size_of::<u32>();
}
impl ContainerInfo { pub fn new(
pub fn parse<R: ReadBytesExt>(mut r: R) -> anyhow::Result<Self> { kind: ContainerType,
let payload_len = r.read_u32::<LittleEndian>()? - Self::SIZE as u32; code: CommandCode,
let kind = r.read_u16::<LittleEndian>()?; transaction_id: Option<u32>,
let kind = ContainerType::try_from(kind)?; payload_len: usize,
let code = r.read_u16::<LittleEndian>()?; ) -> anyhow::Result<Self> {
let _transaction_id = r.read_u32::<LittleEndian>()?; let mut total_len = if transaction_id.is_some() {
Self::SIZE
} else {
Self::BASE_SIZE
};
total_len += payload_len;
Ok(Self { Ok(Self {
total_len: u32::try_from(total_len)?,
kind,
code: ContainerCode::Command(code),
transaction_id,
})
}
pub const fn len(&self) -> usize {
if self.transaction_id.is_some() {
Self::SIZE
} else {
Self::BASE_SIZE
}
}
pub const fn payload_len(&self) -> usize {
self.total_len as usize - self.len()
}
}
impl TryFrom<&[u8]> for ContainerInfo {
type Error = anyhow::Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let mut r = Cursor::new(bytes);
let total_len = r.read_u32::<LittleEndian>()?;
let kind = ContainerType::try_from(r.read_u16::<LittleEndian>()?)?;
let code = ContainerCode::try_from(r.read_u16::<LittleEndian>()?)?;
let transaction_id = Some(r.read_u32::<LittleEndian>()?);
Ok(Self {
total_len,
kind, kind,
payload_len,
code, code,
transaction_id,
}) })
} }
} }
impl TryFrom<ContainerInfo> for Vec<u8> {
type Error = anyhow::Error;
fn try_from(val: ContainerInfo) -> Result<Self, Self::Error> {
let mut buf = Self::with_capacity(val.len());
buf.write_u32::<LittleEndian>(val.total_len)?;
buf.write_u16::<LittleEndian>(val.kind as u16)?;
buf.write_u16::<LittleEndian>(val.code.into())?;
if let Some(transaction_id) = val.transaction_id {
buf.write_u32::<LittleEndian>(transaction_id)?;
}
Ok(buf)
}
}