66import os
77import io
88import errno
9+ import uctypes
910
1011from .core import Interface , Buffer , split_bmRequestType
1112
120121_DEFAULT_RX_BUF_SIZE = const (4096 )
121122_CONTAINER_HEADER_SIZE = const (12 )
122123
124+ # MTP struct definitions using uctypes
125+ # Container header struct
126+ _MTP_CONTAINER_HEADER_DESC = {
127+ "length" : (0 , uctypes .UINT32 ),
128+ "type" : (4 , uctypes .UINT16 ),
129+ "code" : (6 , uctypes .UINT16 ),
130+ "transaction_id" : (8 , uctypes .UINT32 )
131+ }
132+
133+ # Device Info struct
134+ _MTP_DEVICE_INFO_DESC = {
135+ "standard_version" : (0 , uctypes .UINT16 ),
136+ "vendor_extension_id" : (2 , uctypes .UINT32 ),
137+ "mtp_version" : (6 , uctypes .UINT16 ),
138+ # Variable length data follows: extension string, operations, events, etc.
139+ }
140+
141+ # Storage IDs struct
142+ _MTP_STORAGE_IDS_DESC = {
143+ "count" : (0 , uctypes .UINT32 ),
144+ "storage_ids" : (4 , uctypes .ARRAY , 1 , uctypes .UINT32 ) # Variable length array
145+ }
146+
147+ # Storage Info struct
148+ _MTP_STORAGE_INFO_DESC = {
149+ "storage_type" : (0 , uctypes .UINT16 ),
150+ "filesystem_type" : (2 , uctypes .UINT16 ),
151+ "access_capability" : (4 , uctypes .UINT16 ),
152+ "max_capacity" : (6 , uctypes .UINT64 ),
153+ "free_space" : (14 , uctypes .UINT64 ),
154+ "free_space_objects" : (22 , uctypes .UINT32 )
155+ # Variable length data follows: storage_description, volume_identifier
156+ }
157+
158+ # Object Handles struct
159+ _MTP_OBJECT_HANDLES_DESC = {
160+ "count" : (0 , uctypes .UINT32 ),
161+ "handles" : (4 , uctypes .ARRAY , 1 , uctypes .UINT32 ) # Variable length array
162+ }
163+
123164
124165class MTPInterface (Interface ):
125166 """USB MTP device interface for MicroPython.
@@ -298,11 +339,15 @@ def _process_rx(self, _):
298339 # Peek at the container header without consuming it yet
299340 header = self ._rx .pend_read ()
300341
301- # Parse container header
302- length = struct .unpack_from ("<I" , header , 0 )[0 ]
303- container_type = struct .unpack_from ("<H" , header , 4 )[0 ]
304- code = struct .unpack_from ("<H" , header , 6 )[0 ]
305- transaction_id = struct .unpack_from ("<I" , header , 8 )[0 ]
342+ # Parse container header using uctypes
343+ # Create a container header struct over the header buffer
344+ hdr = uctypes .struct (uctypes .addressof (header ), _MTP_CONTAINER_HEADER_DESC , uctypes .LITTLE_ENDIAN )
345+
346+ # Extract values from the struct
347+ length = hdr .length
348+ container_type = hdr .type
349+ code = hdr .code
350+ transaction_id = hdr .transaction_id
306351
307352 container_types = {
308353 _MTP_CONTAINER_TYPE_COMMAND : "COMMAND" ,
@@ -476,38 +521,35 @@ def _cmd_get_device_info(self):
476521 """Handle GetDeviceInfo command."""
477522 self ._log ("Generating device info response" )
478523
479- # Prepare the device info dataset
480- data = bytearray (512 ) # Pre-allocate buffer
481- offset = 0
524+ # Allocate a buffer for device info
525+ data = bytearray (512 ) # Pre-allocate buffer - device info has variable length
482526
483- # Standard version
484- struct .pack_into ("<H" , data , offset , 100 ) # Version 1.00
485- offset += 2
527+ # Create a device info struct
528+ dev_info = uctypes .struct (uctypes .addressof (data ), _MTP_DEVICE_INFO_DESC , uctypes .LITTLE_ENDIAN )
486529
487- # MTP vendor extension ID
488- # Use Microsoft's extension ID to better identify as a true MTP device
489- struct . pack_into ( "<I" , data , offset , 0x00000006 ) # Microsoft MTP Extension
490- offset += 4
530+ # Fill in the fixed fields
531+ dev_info . standard_version = 100 # Version 1.00
532+ dev_info . vendor_extension_id = 0x00000006 # Microsoft MTP Extension
533+ dev_info . mtp_version = 100 # Version 1.00
491534
492- # MTP version
493- struct .pack_into ("<H" , data , offset , 100 ) # Version 1.00
494- offset += 2
535+ # Handle variable-length data after the fixed struct
536+ offset = 8 # Start after the fixed part of the struct
495537
496538 # MTP extensions description string - Microsoft extension
497539 # MTP extension strings are ASCII strings in PIMA format (8-bit length + 8-bit chars with null terminator)
498540 ext_string = "microsoft.com: 1.0" # Standard Microsoft extension string
499541
500542 # String length (8-bit, including null terminator)
501- struct . pack_into ( "<B" , data , offset , len (ext_string ) + 1 )
543+ data [ offset ] = len (ext_string ) + 1
502544 offset += 1
503545
504546 # String data as ASCII
505547 for c in ext_string :
506- struct . pack_into ( "<B" , data , offset , ord (c ) )
548+ data [ offset ] = ord (c )
507549 offset += 1
508550
509551 # ASCII null terminator
510- struct . pack_into ( "<B" , data , offset , 0 )
552+ data [ offset ] = 0
511553 offset += 1
512554
513555 # Functional mode
@@ -529,8 +571,12 @@ def _cmd_get_device_info(self):
529571 _MTP_OPERATION_SEND_OBJECT_INFO ,
530572 _MTP_OPERATION_SEND_OBJECT ,
531573 ]
574+
575+ # Number of operations
532576 struct .pack_into ("<H" , data , offset , len (operations ))
533577 offset += 2
578+
579+ # List of operation codes
534580 for op in operations :
535581 struct .pack_into ("<H" , data , offset , op )
536582 offset += 2
@@ -553,8 +599,12 @@ def _cmd_get_device_info(self):
553599 _MTP_FORMAT_TEXT , # text files
554600 _MTP_FORMAT_UNDEFINED # all other files
555601 ]
602+
603+ # Number of formats
556604 struct .pack_into ("<H" , data , offset , len (formats ))
557605 offset += 2
606+
607+ # List of format codes
558608 for fmt in formats :
559609 struct .pack_into ("<H" , data , offset , fmt )
560610 offset += 2
@@ -618,11 +668,15 @@ def _cmd_get_storage_ids(self):
618668 # We only support a single storage
619669 self ._log ("GetStorageIDs: Reporting storage ID: 0x{:08x}" , self ._storage_id )
620670
621- # Format: 4 bytes for count, 4 bytes per storage ID
622- data = bytearray (8 )
671+ # Create a buffer for storage IDs - 4 bytes for count, 4 bytes per storage ID
672+ data = bytearray (8 ) # 4 bytes for count + 4 bytes for one storage ID
623673
624- # Pack count (1) followed by our storage ID
625- struct .pack_into ("<II" , data , 0 , 1 , self ._storage_id ) # Count=1, ID=storage_id
674+ # Create a storage IDs struct
675+ storage_ids = uctypes .struct (uctypes .addressof (data ), _MTP_STORAGE_IDS_DESC , uctypes .LITTLE_ENDIAN )
676+
677+ # Fill the struct
678+ storage_ids .count = 1 # We only support one storage
679+ storage_ids .storage_ids [0 ] = self ._storage_id
626680
627681 # Send the storage IDs array
628682 self ._send_data (data )
@@ -649,33 +703,22 @@ def _cmd_get_storage_info(self, params):
649703 free_bytes = 1024 * 1024 # 1MB
650704 total_bytes = 4 * 1024 * 1024 # 4MB
651705
652- # Prepare storage info dataset
706+ # Create a buffer for storage info (fixed part is 26 bytes, plus variable-length strings)
653707 data = bytearray (128 )
654- offset = 0
655708
656- # Storage type
657- struct .pack_into ("<H" , data , offset , _MTP_STORAGE_FIXED_RAM )
658- offset += 2
659-
660- # Filesystem type
661- struct .pack_into ("<H" , data , offset , 0x0002 ) # Generic hierarchical
662- offset += 2
663-
664- # Access capability
665- struct .pack_into ("<H" , data , offset , _MTP_STORAGE_READ_WRITE ) # Read-write access
666- offset += 2
667-
668- # Max capacity - use 64-bit value (8 bytes)
669- struct .pack_into ("<Q" , data , offset , total_bytes )
670- offset += 8
709+ # Create a storage info struct
710+ storage_info = uctypes .struct (uctypes .addressof (data ), _MTP_STORAGE_INFO_DESC , uctypes .LITTLE_ENDIAN )
671711
672- # Free space - use 64-bit value (8 bytes)
673- struct .pack_into ("<Q" , data , offset , free_bytes )
674- offset += 8
712+ # Fill in the fixed fields
713+ storage_info .storage_type = _MTP_STORAGE_FIXED_RAM
714+ storage_info .filesystem_type = 0x0002 # Generic hierarchical
715+ storage_info .access_capability = _MTP_STORAGE_READ_WRITE # Read-write access
716+ storage_info .max_capacity = total_bytes
717+ storage_info .free_space = free_bytes
718+ storage_info .free_space_objects = 0xFFFFFFFF # Maximum value - unknown
675719
676- # Free space in objects (unknown - use 0xFFFFFFFF)
677- struct .pack_into ("<I" , data , offset , 0xFFFFFFFF ) # Maximum value
678- offset += 4
720+ # Handle variable-length data after the fixed struct
721+ offset = 26 # Start after the fixed part
679722
680723 # Storage description
681724 offset += self ._write_mtp_string (data , offset , "MicroPython Flash Storage" )
@@ -733,11 +776,24 @@ def _cmd_get_object_handles(self, params):
733776 if format_code == 0 or self ._get_format_by_path (self ._object_handles [handle ]) == format_code :
734777 handles .append (handle )
735778
736- # Prepare and send the array of handles
737- data = bytearray (4 + len (handles ) * 4 )
738- struct .pack_into ("<I" , data , 0 , len (handles )) # Count
779+ # Create a buffer for the handles array
780+ data_size = 4 + len (handles ) * 4 # 4 bytes for count + 4 bytes per handle
781+ data = bytearray (data_size )
782+
783+ # For the _MTP_OBJECT_HANDLES_DESC, we need to dynamically adjust the array size
784+ # Create a custom descriptor with the actual array size
785+ obj_handles_desc = {
786+ "count" : (0 , uctypes .UINT32 ),
787+ "handles" : (4 , uctypes .ARRAY , len (handles ), uctypes .UINT32 )
788+ }
789+
790+ # Create the struct
791+ obj_handles = uctypes .struct (uctypes .addressof (data ), obj_handles_desc , uctypes .LITTLE_ENDIAN )
792+
793+ # Fill in the data
794+ obj_handles .count = len (handles )
739795 for i , handle in enumerate (handles ):
740- struct . pack_into ( "<I" , data , 4 + i * 4 , handle )
796+ obj_handles . handles [ i ] = handle
741797
742798 self ._send_data (data )
743799 self ._send_response (_MTP_RESPONSE_OK )
@@ -1119,11 +1175,14 @@ def _send_data(self, data, final=True):
11191175 container = bytearray (_CONTAINER_HEADER_SIZE )
11201176 total_len = _CONTAINER_HEADER_SIZE + len (data )
11211177
1122- struct .pack_into ("<IHHI" , container , 0 ,
1123- total_len , # Container length
1124- _MTP_CONTAINER_TYPE_DATA , # Container type
1125- self ._current_operation , # Operation code
1126- self ._transaction_id ) # Transaction ID
1178+ # Create a container header struct
1179+ header = uctypes .struct (uctypes .addressof (container ), _MTP_CONTAINER_HEADER_DESC , uctypes .LITTLE_ENDIAN )
1180+
1181+ # Fill in the container header fields
1182+ header .length = total_len
1183+ header .type = _MTP_CONTAINER_TYPE_DATA
1184+ header .code = self ._current_operation
1185+ header .transaction_id = self ._transaction_id
11271186
11281187 self ._log ("Sending DATA container: length={}, operation=0x{:04x}, transaction_id={}{}" ,
11291188 total_len , self ._current_operation , self ._transaction_id ,
@@ -1186,17 +1245,22 @@ def _send_response(self, response_code, params=None):
11861245 self ._log ("Sending RESPONSE: {} (0x{:04x}), transaction_id={}, params={}" ,
11871246 response_name , response_code , self ._transaction_id , params if params else "None" )
11881247
1189- # Create and fill container header
1248+ # Create container buffer for header + params
11901249 container = bytearray (total_len )
1191- struct .pack_into ("<IHHI" , container , 0 ,
1192- total_len , # Container length
1193- _MTP_CONTAINER_TYPE_RESPONSE , # Container type
1194- response_code , # Response code
1195- self ._transaction_id ) # Transaction ID
1250+
1251+ # Create a container header struct
1252+ header = uctypes .struct (uctypes .addressof (container ), _MTP_CONTAINER_HEADER_DESC , uctypes .LITTLE_ENDIAN )
1253+
1254+ # Fill in the container header fields
1255+ header .length = total_len
1256+ header .type = _MTP_CONTAINER_TYPE_RESPONSE
1257+ header .code = response_code
1258+ header .transaction_id = self ._transaction_id
11961259
11971260 # Add parameters if any
11981261 if params :
11991262 for i , param in enumerate (params ):
1263+ # Pack parameters directly after header
12001264 struct .pack_into ("<I" , container , _CONTAINER_HEADER_SIZE + i * 4 , param )
12011265
12021266 # Send the response
0 commit comments