Skip to main content

mtp_rs/ptp/
mod.rs

1//! Low-level PTP (Picture Transfer Protocol) implementation.
2//!
3//! This module provides direct access to the PTP/MTP protocol layer. Use this module when:
4//!
5//! - Working with digital cameras that use PTP
6//! - You need fine-grained control over protocol operations
7//! - Implementing custom MTP extensions or vendor operations
8//! - You need access to raw response codes for error handling
9//! - Building your own high-level abstractions
10//!
11//! ## When to use `mtp` instead
12//!
13//! Most users working with Android devices should prefer the high-level [`crate::mtp`] module,
14//! which provides a simpler API for common operations like listing files, uploading, and
15//! downloading.
16//!
17//! ## Module structure
18//!
19//! - `codes`: Operation, response, event, and format code enums
20//! - `pack`: Binary serialization/deserialization primitives
21//! - `container`: USB container format for PTP messages
22//! - `types`: DeviceInfo, StorageInfo, ObjectInfo structures
23//! - `session`: PTP session management
24//! - `device`: PtpDevice public API
25//!
26//! ## Example
27//!
28//! ```rust,no_run
29//! use mtp_rs::ptp::PtpDevice;
30//!
31//! # async fn example() -> Result<(), mtp_rs::Error> {
32//! // Open device and start a session
33//! let device = PtpDevice::open_first().await?;
34//! let session = device.open_session().await?;
35//!
36//! // Get device info
37//! let info = session.get_device_info().await?;
38//! println!("Model: {}", info.model);
39//!
40//! // List storage IDs
41//! let storage_ids = session.get_storage_ids().await?;
42//! # Ok(())
43//! # }
44//! ```
45
46mod codes;
47mod container;
48mod device;
49mod pack;
50mod session;
51#[cfg(test)]
52mod test_utils;
53mod types;
54
55pub use codes::{
56    DevicePropertyCode, EventCode, ObjectFormatCode, ObjectPropertyCode, OperationCode,
57    PropertyDataType, ResponseCode,
58};
59pub use container::{
60    container_type, CommandContainer, ContainerType, DataContainer, EventContainer,
61    ResponseContainer,
62};
63pub use device::PtpDevice;
64pub use pack::{
65    pack_datetime, pack_i16, pack_i32, pack_i64, pack_i8, pack_string, pack_u16, pack_u16_array,
66    pack_u32, pack_u32_array, pack_u64, pack_u8, unpack_datetime, unpack_i16, unpack_i32,
67    unpack_i64, unpack_i8, unpack_string, unpack_u16, unpack_u16_array, unpack_u32,
68    unpack_u32_array, unpack_u64, unpack_u8, DateTime,
69};
70pub use session::{receive_stream_to_stream, PtpSession, ReceiveStream};
71pub use types::{
72    AccessCapability, AssociationType, DeviceInfo, DevicePropDesc, FilesystemType, ObjectInfo,
73    PropertyFormType, PropertyRange, PropertyValue, ProtectionStatus, StorageInfo, StorageType,
74};
75
76/// 32-bit object handle assigned by the device.
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
78pub struct ObjectHandle(pub u32);
79
80impl ObjectHandle {
81    /// Root folder (parent = root means object is in storage root).
82    pub const ROOT: Self = ObjectHandle(0x00000000);
83    /// All objects (used in GetObjectHandles to list recursively).
84    pub const ALL: Self = ObjectHandle(0xFFFFFFFF);
85}
86
87/// 32-bit storage identifier.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
89pub struct StorageId(pub u32);
90
91impl StorageId {
92    /// All storages (used in GetObjectHandles to search all).
93    pub const ALL: Self = StorageId(0xFFFFFFFF);
94}
95
96/// 32-bit session identifier.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
98pub struct SessionId(pub u32);
99
100/// 32-bit transaction identifier.
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
102pub struct TransactionId(pub u32);
103
104impl TransactionId {
105    /// The first valid transaction ID in a session.
106    pub const FIRST: Self = TransactionId(0x00000001);
107
108    /// Invalid transaction ID (must never be used).
109    pub const INVALID: Self = TransactionId(0xFFFFFFFF);
110
111    /// Transaction ID for session-less operations (e.g., GetDeviceInfo before OpenSession).
112    pub const SESSION_LESS: Self = TransactionId(0x00000000);
113
114    /// Get the next transaction ID, wrapping correctly.
115    ///
116    /// Wraps from 0xFFFFFFFE to 0x00000001 (skipping both 0x00000000 and 0xFFFFFFFF).
117    #[must_use]
118    pub fn next(self) -> Self {
119        let next = self.0.wrapping_add(1);
120        if next == 0 || next == 0xFFFFFFFF {
121            TransactionId(0x00000001)
122        } else {
123            TransactionId(next)
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn transaction_id_next() {
134        assert_eq!(TransactionId(1).next(), TransactionId(2));
135        assert_eq!(TransactionId(100).next(), TransactionId(101));
136    }
137
138    #[test]
139    fn transaction_id_wrapping() {
140        // Should wrap from 0xFFFFFFFE to 0x00000001, skipping 0xFFFFFFFF and 0x00000000
141        assert_eq!(TransactionId(0xFFFFFFFE).next(), TransactionId(1));
142        assert_eq!(TransactionId(0xFFFFFFFD).next(), TransactionId(0xFFFFFFFE));
143    }
144
145    #[test]
146    fn object_handle_constants() {
147        assert_eq!(ObjectHandle::ROOT.0, 0);
148        assert_eq!(ObjectHandle::ALL.0, 0xFFFFFFFF);
149    }
150
151    #[test]
152    fn storage_id_constants() {
153        assert_eq!(StorageId::ALL.0, 0xFFFFFFFF);
154    }
155}