6
6
import os
7
7
import io
8
8
import errno
9
+ import uctypes
9
10
10
11
from .core import Interface , Buffer , split_bmRequestType
11
12
120
121
_DEFAULT_RX_BUF_SIZE = const (4096 )
121
122
_CONTAINER_HEADER_SIZE = const (12 )
122
123
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
+
123
164
124
165
class MTPInterface (Interface ):
125
166
"""USB MTP device interface for MicroPython.
@@ -298,11 +339,15 @@ def _process_rx(self, _):
298
339
# Peek at the container header without consuming it yet
299
340
header = self ._rx .pend_read ()
300
341
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
306
351
307
352
container_types = {
308
353
_MTP_CONTAINER_TYPE_COMMAND : "COMMAND" ,
@@ -476,38 +521,35 @@ def _cmd_get_device_info(self):
476
521
"""Handle GetDeviceInfo command."""
477
522
self ._log ("Generating device info response" )
478
523
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
482
526
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 )
486
529
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
491
534
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
495
537
496
538
# MTP extensions description string - Microsoft extension
497
539
# MTP extension strings are ASCII strings in PIMA format (8-bit length + 8-bit chars with null terminator)
498
540
ext_string = "microsoft.com: 1.0" # Standard Microsoft extension string
499
541
500
542
# 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
502
544
offset += 1
503
545
504
546
# String data as ASCII
505
547
for c in ext_string :
506
- struct . pack_into ( "<B" , data , offset , ord (c ) )
548
+ data [ offset ] = ord (c )
507
549
offset += 1
508
550
509
551
# ASCII null terminator
510
- struct . pack_into ( "<B" , data , offset , 0 )
552
+ data [ offset ] = 0
511
553
offset += 1
512
554
513
555
# Functional mode
@@ -529,8 +571,12 @@ def _cmd_get_device_info(self):
529
571
_MTP_OPERATION_SEND_OBJECT_INFO ,
530
572
_MTP_OPERATION_SEND_OBJECT ,
531
573
]
574
+
575
+ # Number of operations
532
576
struct .pack_into ("<H" , data , offset , len (operations ))
533
577
offset += 2
578
+
579
+ # List of operation codes
534
580
for op in operations :
535
581
struct .pack_into ("<H" , data , offset , op )
536
582
offset += 2
@@ -553,8 +599,12 @@ def _cmd_get_device_info(self):
553
599
_MTP_FORMAT_TEXT , # text files
554
600
_MTP_FORMAT_UNDEFINED # all other files
555
601
]
602
+
603
+ # Number of formats
556
604
struct .pack_into ("<H" , data , offset , len (formats ))
557
605
offset += 2
606
+
607
+ # List of format codes
558
608
for fmt in formats :
559
609
struct .pack_into ("<H" , data , offset , fmt )
560
610
offset += 2
@@ -618,11 +668,15 @@ def _cmd_get_storage_ids(self):
618
668
# We only support a single storage
619
669
self ._log ("GetStorageIDs: Reporting storage ID: 0x{:08x}" , self ._storage_id )
620
670
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
623
673
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
626
680
627
681
# Send the storage IDs array
628
682
self ._send_data (data )
@@ -649,33 +703,22 @@ def _cmd_get_storage_info(self, params):
649
703
free_bytes = 1024 * 1024 # 1MB
650
704
total_bytes = 4 * 1024 * 1024 # 4MB
651
705
652
- # Prepare storage info dataset
706
+ # Create a buffer for storage info (fixed part is 26 bytes, plus variable-length strings)
653
707
data = bytearray (128 )
654
- offset = 0
655
708
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 )
671
711
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
675
719
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
679
722
680
723
# Storage description
681
724
offset += self ._write_mtp_string (data , offset , "MicroPython Flash Storage" )
@@ -733,11 +776,24 @@ def _cmd_get_object_handles(self, params):
733
776
if format_code == 0 or self ._get_format_by_path (self ._object_handles [handle ]) == format_code :
734
777
handles .append (handle )
735
778
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 )
739
795
for i , handle in enumerate (handles ):
740
- struct . pack_into ( "<I" , data , 4 + i * 4 , handle )
796
+ obj_handles . handles [ i ] = handle
741
797
742
798
self ._send_data (data )
743
799
self ._send_response (_MTP_RESPONSE_OK )
@@ -1119,11 +1175,14 @@ def _send_data(self, data, final=True):
1119
1175
container = bytearray (_CONTAINER_HEADER_SIZE )
1120
1176
total_len = _CONTAINER_HEADER_SIZE + len (data )
1121
1177
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
1127
1186
1128
1187
self ._log ("Sending DATA container: length={}, operation=0x{:04x}, transaction_id={}{}" ,
1129
1188
total_len , self ._current_operation , self ._transaction_id ,
@@ -1186,17 +1245,22 @@ def _send_response(self, response_code, params=None):
1186
1245
self ._log ("Sending RESPONSE: {} (0x{:04x}), transaction_id={}, params={}" ,
1187
1246
response_name , response_code , self ._transaction_id , params if params else "None" )
1188
1247
1189
- # Create and fill container header
1248
+ # Create container buffer for header + params
1190
1249
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
1196
1259
1197
1260
# Add parameters if any
1198
1261
if params :
1199
1262
for i , param in enumerate (params ):
1263
+ # Pack parameters directly after header
1200
1264
struct .pack_into ("<I" , container , _CONTAINER_HEADER_SIZE + i * 4 , param )
1201
1265
1202
1266
# Send the response
0 commit comments