1use crate::ptp::{
4 container_type, CommandContainer, ContainerType, DataContainer, DeviceInfo, OperationCode,
5 PtpSession, ResponseCode, ResponseContainer,
6};
7use crate::transport::{NusbTransport, Transport};
8use crate::Error;
9use std::sync::Arc;
10use std::time::Duration;
11
12pub struct PtpDevice {
17 transport: Arc<NusbTransport>,
18}
19
20impl PtpDevice {
21 pub async fn open_by_location(location_id: u64) -> Result<Self, Error> {
23 Self::open_by_location_with_timeout(location_id, NusbTransport::DEFAULT_TIMEOUT).await
24 }
25
26 pub async fn open_by_location_with_timeout(
28 location_id: u64,
29 timeout: Duration,
30 ) -> Result<Self, Error> {
31 let devices = NusbTransport::list_mtp_devices()?;
32 let device_info = devices
33 .into_iter()
34 .find(|d| d.location_id == location_id)
35 .ok_or(Error::NoDevice)?;
36 Self::open_device(device_info, timeout).await
37 }
38
39 pub async fn open_by_serial(serial: &str) -> Result<Self, Error> {
41 Self::open_by_serial_with_timeout(serial, NusbTransport::DEFAULT_TIMEOUT).await
42 }
43
44 pub async fn open_by_serial_with_timeout(
46 serial: &str,
47 timeout: Duration,
48 ) -> Result<Self, Error> {
49 let devices = NusbTransport::list_mtp_devices()?;
50 let device_info = devices
51 .into_iter()
52 .find(|d| d.serial_number.as_deref() == Some(serial))
53 .ok_or(Error::NoDevice)?;
54 Self::open_device(device_info, timeout).await
55 }
56
57 pub async fn open_first() -> Result<Self, Error> {
59 Self::open_first_with_timeout(NusbTransport::DEFAULT_TIMEOUT).await
60 }
61
62 pub async fn open_first_with_timeout(timeout: Duration) -> Result<Self, Error> {
64 let devices = NusbTransport::list_mtp_devices()?;
65 let device_info = devices.into_iter().next().ok_or(Error::NoDevice)?;
66 Self::open_device(device_info, timeout).await
67 }
68
69 async fn open_device(
70 device_info: crate::transport::UsbDeviceInfo,
71 timeout: Duration,
72 ) -> Result<Self, Error> {
73 let device = device_info.open().map_err(Error::Usb)?;
74 let transport = NusbTransport::open_with_timeout(device, timeout).await?;
75 Ok(Self {
76 transport: Arc::new(transport),
77 })
78 }
79
80 pub async fn get_device_info(&self) -> Result<DeviceInfo, Error> {
84 let cmd = CommandContainer {
86 code: OperationCode::GetDeviceInfo,
87 transaction_id: 0,
88 params: vec![],
89 };
90 self.transport.send_bulk(&cmd.to_bytes()).await?;
91
92 let mut data_payload = Vec::new();
94 loop {
95 let bytes = self.transport.receive_bulk(64 * 1024).await?;
96 if bytes.is_empty() {
97 return Err(Error::invalid_data("Empty response"));
98 }
99
100 let ct = container_type(&bytes)?;
101 match ct {
102 ContainerType::Data => {
103 let container = DataContainer::from_bytes(&bytes)?;
104 data_payload.extend_from_slice(&container.payload);
105 }
106 ContainerType::Response => {
107 let response = ResponseContainer::from_bytes(&bytes)?;
108 if response.code != ResponseCode::Ok {
109 return Err(Error::Protocol {
110 code: response.code,
111 operation: OperationCode::GetDeviceInfo,
112 });
113 }
114 break;
115 }
116 _ => {
117 return Err(Error::invalid_data(format!(
118 "Unexpected container type: {:?}",
119 ct
120 )));
121 }
122 }
123 }
124
125 DeviceInfo::from_bytes(&data_payload)
126 }
127
128 pub async fn open_session(&self) -> Result<PtpSession, Error> {
132 self.open_session_with_id(1).await
133 }
134
135 pub async fn open_session_with_id(&self, session_id: u32) -> Result<PtpSession, Error> {
137 let transport: Arc<dyn Transport> = self.transport.clone();
138 PtpSession::open(transport, session_id).await
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[tokio::test]
147 #[ignore] async fn test_open_first() {
149 let device = PtpDevice::open_first().await.unwrap();
150 let info = device.get_device_info().await.unwrap();
151 println!("Model: {}", info.model);
152 }
153
154 #[tokio::test]
155 #[ignore] async fn test_open_session() {
157 let device = PtpDevice::open_first().await.unwrap();
158 let session = device.open_session().await.unwrap();
159
160 let info = session.get_device_info().await.unwrap();
161 println!("Model: {}", info.model);
162
163 session.close().await.unwrap();
164 }
165}