@@ -171,6 +171,7 @@ def __init__(self, storage_path="/", rx_size=_DEFAULT_RX_BUF_SIZE, tx_size=_DEFA
171
171
172
172
def desc_cfg (self , desc , itf_num , ep_num , strs ):
173
173
"""Build the USB configuration descriptor for this interface."""
174
+ print ("[MTP] Building descriptors: itf_num={}, ep_num={}" .format (itf_num , ep_num ))
174
175
# Add the interface descriptor for MTP (PIMA 15740 Still Image)
175
176
desc .interface (
176
177
itf_num ,
@@ -186,6 +187,9 @@ def desc_cfg(self, desc, itf_num, ep_num, strs):
186
187
self .ep_in = ep_num | _EP_IN_FLAG
187
188
self .ep_intr = (ep_num + 1 ) | _EP_IN_FLAG
188
189
190
+ print ("[MTP] Endpoints assigned: ep_out=0x{:02x}, ep_in=0x{:02x}, ep_intr=0x{:02x}" .format (
191
+ self .ep_out , self .ep_in , self .ep_intr ))
192
+
189
193
desc .endpoint (self .ep_out , "bulk" , _MAX_PACKET_SIZE , 0 )
190
194
desc .endpoint (self .ep_in , "bulk" , _MAX_PACKET_SIZE , 0 )
191
195
desc .endpoint (self .ep_intr , "interrupt" , 8 , 10 ) # 10ms interval for events
@@ -196,12 +200,15 @@ def num_eps(self):
196
200
197
201
def on_open (self ):
198
202
"""Called when the USB host configures the device."""
203
+ print ("[MTP] Device configured by host" )
199
204
super ().on_open ()
200
205
# Start transfers for receiving commands and data
201
206
self ._rx_xfer ()
207
+ print ("[MTP] Interface ready, waiting for MTP commands" )
202
208
203
209
def on_reset (self ):
204
210
"""Called when the USB device is reset by the host."""
211
+ print ("[MTP] Device reset by host" )
205
212
super ().on_reset ()
206
213
# Reset the session state
207
214
self ._session_open = False
@@ -214,34 +221,62 @@ def on_reset(self):
214
221
self ._send_object_handle = None
215
222
self ._send_object_info = None
216
223
self ._get_object_handle = None
224
+ print ("[MTP] Session state cleared" )
217
225
218
226
def _rx_xfer (self ):
219
227
"""Submit a new transfer to receive data from the host."""
220
228
if self .is_open () and not self .xfer_pending (self .ep_out ) and self ._rx .writable ():
221
- self .submit_xfer (self .ep_out , self ._rx .pend_write (), self ._rx_cb )
229
+ buf = self ._rx .pend_write ()
230
+ print ("[MTP] Submitting OUT transfer, buffer size={}" .format (len (buf )))
231
+ self .submit_xfer (self .ep_out , buf , self ._rx_cb )
232
+ else :
233
+ if not self .is_open ():
234
+ print ("[MTP] Cannot submit OUT transfer - interface not open" )
235
+ elif self .xfer_pending (self .ep_out ):
236
+ print ("[MTP] Cannot submit OUT transfer - transfer already pending" )
237
+ elif not self ._rx .writable ():
238
+ print ("[MTP] Cannot submit OUT transfer - RX buffer full ({} bytes)" .format (self ._rx .readable ()))
222
239
223
240
def _rx_cb (self , ep , res , num_bytes ):
224
241
"""Callback when data is received from the host."""
242
+ print ("[MTP] OUT transfer complete: res={}, bytes={}" .format (res , num_bytes ))
225
243
if res == 0 :
226
244
self ._rx .finish_write (num_bytes )
245
+ print ("[MTP] Scheduling data processing" )
227
246
schedule (self ._process_rx , None )
247
+ else :
248
+ print ("[MTP] OUT transfer failed with error {}" .format (res ))
228
249
self ._rx_xfer () # Continue receiving
229
250
230
251
def _tx_xfer (self ):
231
252
"""Submit a new transfer to send data to the host."""
232
253
if self .is_open () and not self .xfer_pending (self .ep_in ) and self ._tx .readable ():
233
- self .submit_xfer (self .ep_in , self ._tx .pend_read (), self ._tx_cb )
254
+ buf = self ._tx .pend_read ()
255
+ print ("[MTP] Submitting IN transfer, data size={}" .format (len (buf )))
256
+ self .submit_xfer (self .ep_in , buf , self ._tx_cb )
257
+ else :
258
+ if not self .is_open ():
259
+ print ("[MTP] Cannot submit IN transfer - interface not open" )
260
+ elif self .xfer_pending (self .ep_in ):
261
+ print ("[MTP] Cannot submit IN transfer - transfer already pending" )
262
+ elif not self ._tx .readable ():
263
+ print ("[MTP] Cannot submit IN transfer - no data in TX buffer" )
234
264
235
265
def _tx_cb (self , ep , res , num_bytes ):
236
266
"""Callback when data has been sent to the host."""
267
+ print ("[MTP] IN transfer complete: res={}, bytes={}" .format (res , num_bytes ))
237
268
if res == 0 :
238
269
self ._tx .finish_read (num_bytes )
270
+ else :
271
+ print ("[MTP] IN transfer failed with error {}" .format (res ))
239
272
self ._tx_xfer () # Send more data if available
240
273
241
274
def _process_rx (self , _ ):
242
275
"""Process received data from the host."""
243
276
# Check if there's enough data for a container header
244
- if self ._rx .readable () < _CONTAINER_HEADER_SIZE :
277
+ readable = self ._rx .readable ()
278
+ if readable < _CONTAINER_HEADER_SIZE :
279
+ print ("[MTP] Not enough data for container header ({} bytes)" .format (readable ))
245
280
return
246
281
247
282
# Peek at the container header without consuming it yet
@@ -253,8 +288,20 @@ def _process_rx(self, _):
253
288
code = struct .unpack_from ("<H" , header , 6 )[0 ]
254
289
transaction_id = struct .unpack_from ("<I" , header , 8 )[0 ]
255
290
291
+ container_types = {
292
+ _MTP_CONTAINER_TYPE_COMMAND : "COMMAND" ,
293
+ _MTP_CONTAINER_TYPE_DATA : "DATA" ,
294
+ _MTP_CONTAINER_TYPE_RESPONSE : "RESPONSE" ,
295
+ _MTP_CONTAINER_TYPE_EVENT : "EVENT"
296
+ }
297
+
298
+ container_type_str = container_types .get (container_type , "UNKNOWN" )
299
+ print ("[MTP] Container header: length={}, type={}, code=0x{:04x}, transaction_id={}" .format (
300
+ length , container_type_str , code , transaction_id ))
301
+
256
302
# Ensure we have the complete container
257
303
if self ._rx .readable () < length :
304
+ print ("[MTP] Waiting for complete container ({}/{} bytes)" .format (self ._rx .readable (), length ))
258
305
return
259
306
260
307
# Now consume the container header
@@ -272,6 +319,25 @@ def _process_rx(self, _):
272
319
params .append (param )
273
320
self ._rx .finish_read (4 )
274
321
322
+ # Map code to operation name for common operations
323
+ operation_names = {
324
+ _MTP_OPERATION_GET_DEVICE_INFO : "GetDeviceInfo" ,
325
+ _MTP_OPERATION_OPEN_SESSION : "OpenSession" ,
326
+ _MTP_OPERATION_CLOSE_SESSION : "CloseSession" ,
327
+ _MTP_OPERATION_GET_STORAGE_IDS : "GetStorageIDs" ,
328
+ _MTP_OPERATION_GET_STORAGE_INFO : "GetStorageInfo" ,
329
+ _MTP_OPERATION_GET_NUM_OBJECTS : "GetNumObjects" ,
330
+ _MTP_OPERATION_GET_OBJECT_HANDLES : "GetObjectHandles" ,
331
+ _MTP_OPERATION_GET_OBJECT_INFO : "GetObjectInfo" ,
332
+ _MTP_OPERATION_GET_OBJECT : "GetObject" ,
333
+ _MTP_OPERATION_DELETE_OBJECT : "DeleteObject" ,
334
+ _MTP_OPERATION_SEND_OBJECT_INFO : "SendObjectInfo" ,
335
+ _MTP_OPERATION_SEND_OBJECT : "SendObject"
336
+ }
337
+
338
+ op_name = operation_names .get (code , "Unknown" )
339
+ print ("[MTP] Received command: {} (0x{:04x}), params={}" .format (op_name , code , params ))
340
+
275
341
# Store operation info for processing
276
342
self ._current_operation = code
277
343
self ._current_params = params
@@ -285,11 +351,15 @@ def _process_rx(self, _):
285
351
elif container_type == _MTP_CONTAINER_TYPE_DATA :
286
352
if not self ._current_operation or self ._data_phase_complete :
287
353
# Unexpected data phase
354
+ print ("[MTP] Unexpected data phase, no operation in progress" )
288
355
self ._send_response (_MTP_RESPONSE_GENERAL_ERROR )
289
356
return
290
357
291
358
# Process the data phase
292
359
data_size = length - _CONTAINER_HEADER_SIZE
360
+ print ("[MTP] Data phase: size={} bytes for operation 0x{:04x}" .format (
361
+ data_size , self ._current_operation ))
362
+
293
363
if self ._rx .readable () >= data_size :
294
364
data = bytearray (data_size )
295
365
view = memoryview (data )
@@ -304,10 +374,12 @@ def _process_rx(self, _):
304
374
position += chunk
305
375
remaining -= chunk
306
376
377
+ print ("[MTP] Data phase complete, processing {} bytes" .format (data_size ))
307
378
self ._process_data_phase (data )
308
379
else :
309
380
# Not enough data received
310
381
# Skip incomplete data
382
+ print ("[MTP] Incomplete data received, skipping" )
311
383
self ._rx .finish_read (self ._rx .readable ())
312
384
self ._send_response (_MTP_RESPONSE_INCOMPLETE_TRANSFER )
313
385
@@ -318,9 +390,28 @@ def _handle_command(self):
318
390
319
391
# Check if session is open (required for most operations)
320
392
if not self ._session_open and op != _MTP_OPERATION_OPEN_SESSION and op != _MTP_OPERATION_GET_DEVICE_INFO :
393
+ print ("[MTP] Rejecting command 0x{:04x} - session not open" .format (op ))
321
394
self ._send_response (_MTP_RESPONSE_SESSION_NOT_OPEN )
322
395
return
323
396
397
+ # Map the operation code to a name for better debug messages
398
+ operation_names = {
399
+ _MTP_OPERATION_GET_DEVICE_INFO : "GetDeviceInfo" ,
400
+ _MTP_OPERATION_OPEN_SESSION : "OpenSession" ,
401
+ _MTP_OPERATION_CLOSE_SESSION : "CloseSession" ,
402
+ _MTP_OPERATION_GET_STORAGE_IDS : "GetStorageIDs" ,
403
+ _MTP_OPERATION_GET_STORAGE_INFO : "GetStorageInfo" ,
404
+ _MTP_OPERATION_GET_NUM_OBJECTS : "GetNumObjects" ,
405
+ _MTP_OPERATION_GET_OBJECT_HANDLES : "GetObjectHandles" ,
406
+ _MTP_OPERATION_GET_OBJECT_INFO : "GetObjectInfo" ,
407
+ _MTP_OPERATION_GET_OBJECT : "GetObject" ,
408
+ _MTP_OPERATION_DELETE_OBJECT : "DeleteObject" ,
409
+ _MTP_OPERATION_SEND_OBJECT_INFO : "SendObjectInfo" ,
410
+ _MTP_OPERATION_SEND_OBJECT : "SendObject"
411
+ }
412
+ op_name = operation_names .get (op , "Unknown" )
413
+ print ("[MTP] Processing command: {} (0x{:04x})" .format (op_name , op ))
414
+
324
415
# Handle operations
325
416
if op == _MTP_OPERATION_GET_DEVICE_INFO :
326
417
self ._cmd_get_device_info ()
@@ -348,6 +439,7 @@ def _handle_command(self):
348
439
self ._cmd_send_object ()
349
440
else :
350
441
# Operation not supported
442
+ print ("[MTP] Unsupported operation: 0x{:04x}" .format (op ))
351
443
self ._send_response (_MTP_RESPONSE_OPERATION_NOT_SUPPORTED )
352
444
353
445
def _process_data_phase (self , data ):
@@ -465,24 +557,34 @@ def _cmd_get_device_info(self):
465
557
def _cmd_open_session (self , params ):
466
558
"""Handle OpenSession command."""
467
559
if not params :
560
+ print ("[MTP] OpenSession: No parameters provided" )
468
561
self ._send_response (_MTP_RESPONSE_INVALID_PARAMETER )
469
562
return
470
563
471
564
session_id = params [0 ]
565
+ print ("[MTP] OpenSession: Requested session_id={}" .format (session_id ))
472
566
473
567
if session_id == 0 :
568
+ print ("[MTP] OpenSession: Rejecting invalid session ID (0)" )
474
569
self ._send_response (_MTP_RESPONSE_INVALID_PARAMETER )
475
570
elif self ._session_open :
571
+ print ("[MTP] OpenSession: Session already open (id={})" .format (self ._session_id ))
476
572
self ._send_response (_MTP_RESPONSE_SESSION_ALREADY_OPEN )
477
573
else :
574
+ print ("[MTP] OpenSession: Opening new session with id={}" .format (session_id ))
478
575
self ._session_open = True
479
576
self ._session_id = session_id
480
- self . _send_response ( _MTP_RESPONSE_OK )
577
+
481
578
# Refresh the object list when opening a session
579
+ print ("[MTP] OpenSession: Refreshing object list" )
482
580
self ._refresh_object_list ()
581
+ print ("[MTP] OpenSession: Found {} objects" .format (len (self ._object_handles )))
582
+
583
+ self ._send_response (_MTP_RESPONSE_OK )
483
584
484
585
def _cmd_close_session (self ):
485
586
"""Handle CloseSession command."""
587
+ print ("[MTP] CloseSession: Closing session {}" .format (self ._session_id ))
486
588
self ._session_open = False
487
589
self ._session_id = 0
488
590
self ._send_response (_MTP_RESPONSE_OK )
@@ -955,6 +1057,7 @@ def _process_send_object(self, data):
955
1057
def _send_data (self , data , final = True ):
956
1058
"""Send data phase of an MTP transaction."""
957
1059
if not self .is_open ():
1060
+ print ("[MTP] Cannot send data - interface not open" )
958
1061
return False
959
1062
960
1063
# Create container header
@@ -967,11 +1070,21 @@ def _send_data(self, data, final=True):
967
1070
self ._current_operation , # Operation code
968
1071
self ._transaction_id ) # Transaction ID
969
1072
1073
+ print ("[MTP] Sending DATA container: length={}, operation=0x{:04x}, transaction_id={}{}" .format (
1074
+ total_len , self ._current_operation , self ._transaction_id ,
1075
+ ", final=True" if final else "" ))
1076
+
970
1077
# Send header
971
1078
self ._tx .write (container )
972
1079
973
1080
# Send data
974
1081
if data :
1082
+ if len (data ) > 64 :
1083
+ print ("[MTP] Data payload: {} bytes (first 64 bytes: {})" .format (
1084
+ len (data ), [hex (b ) for b in data [:64 ]]))
1085
+ else :
1086
+ print ("[MTP] Data payload: {} bytes: {}" .format (
1087
+ len (data ), [hex (b ) for b in data ]))
975
1088
self ._tx .write (data )
976
1089
977
1090
# Start transfer
@@ -982,12 +1095,38 @@ def _send_data(self, data, final=True):
982
1095
def _send_response (self , response_code , params = None ):
983
1096
"""Send response phase of an MTP transaction."""
984
1097
if not self .is_open ():
1098
+ print ("[MTP] Cannot send response - interface not open" )
985
1099
return False
1100
+
1101
+ # Map response code to string for better debug messages
1102
+ response_names = {
1103
+ _MTP_RESPONSE_OK : "OK" ,
1104
+ _MTP_RESPONSE_GENERAL_ERROR : "GeneralError" ,
1105
+ _MTP_RESPONSE_SESSION_NOT_OPEN : "SessionNotOpen" ,
1106
+ _MTP_RESPONSE_INVALID_TRANSACTION_ID : "InvalidTransactionID" ,
1107
+ _MTP_RESPONSE_OPERATION_NOT_SUPPORTED : "OperationNotSupported" ,
1108
+ _MTP_RESPONSE_PARAMETER_NOT_SUPPORTED : "ParameterNotSupported" ,
1109
+ _MTP_RESPONSE_INCOMPLETE_TRANSFER : "IncompleteTransfer" ,
1110
+ _MTP_RESPONSE_INVALID_STORAGE_ID : "InvalidStorageID" ,
1111
+ _MTP_RESPONSE_INVALID_OBJECT_HANDLE : "InvalidObjectHandle" ,
1112
+ _MTP_RESPONSE_STORE_FULL : "StoreFull" ,
1113
+ _MTP_RESPONSE_STORE_READ_ONLY : "StoreReadOnly" ,
1114
+ _MTP_RESPONSE_PARTIAL_DELETION : "PartialDeletion" ,
1115
+ _MTP_RESPONSE_STORE_NOT_AVAILABLE : "StoreNotAvailable" ,
1116
+ _MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED : "SpecificationByFormatUnsupported" ,
1117
+ _MTP_RESPONSE_INVALID_PARENT_OBJECT : "InvalidParentObject" ,
1118
+ _MTP_RESPONSE_INVALID_PARAMETER : "InvalidParameter" ,
1119
+ _MTP_RESPONSE_SESSION_ALREADY_OPEN : "SessionAlreadyOpen"
1120
+ }
1121
+ response_name = response_names .get (response_code , "Unknown" )
986
1122
987
1123
# Calculate response length
988
1124
param_count = len (params ) if params else 0
989
1125
total_len = _CONTAINER_HEADER_SIZE + param_count * 4
990
1126
1127
+ print ("[MTP] Sending RESPONSE: {} (0x{:04x}), transaction_id={}, params={}" .format (
1128
+ response_name , response_code , self ._transaction_id , params if params else "None" ))
1129
+
991
1130
# Create and fill container header
992
1131
container = bytearray (total_len )
993
1132
struct .pack_into ("<IHHI" , container , 0 ,
@@ -1015,6 +1154,8 @@ def _send_response(self, response_code, params=None):
1015
1154
1016
1155
def _refresh_object_list (self ):
1017
1156
"""Scan the filesystem and rebuild the object handle mapping."""
1157
+ print ("[MTP] Refreshing object list from storage path: {}" .format (self ._storage_path ))
1158
+
1018
1159
# Reset object handles
1019
1160
self ._object_handles = {}
1020
1161
self ._parent_map = {}
@@ -1025,9 +1166,12 @@ def _refresh_object_list(self):
1025
1166
self ._next_object_handle += 1
1026
1167
self ._object_handles [root_handle ] = self ._storage_path
1027
1168
self ._parent_map [root_handle ] = 0 # No parent
1169
+ print ("[MTP] Added root directory with handle {}" .format (root_handle ))
1028
1170
1029
1171
# Walk the directory tree
1030
1172
self ._scan_directory (self ._storage_path , root_handle )
1173
+
1174
+ print ("[MTP] Object scan complete, found {} objects" .format (len (self ._object_handles )))
1031
1175
1032
1176
def _scan_directory (self , path , parent_handle ):
1033
1177
"""Recursively scan a directory and add objects to handle maps."""
@@ -1038,6 +1182,7 @@ def _scan_directory(self, path, parent_handle):
1038
1182
1039
1183
# List all entries in this directory
1040
1184
entries = os .listdir (path )
1185
+ print ("[MTP] Scanning directory: {}, found {} entries" .format (path , len (entries )))
1041
1186
1042
1187
for entry in entries :
1043
1188
full_path = path + entry
@@ -1055,15 +1200,19 @@ def _scan_directory(self, path, parent_handle):
1055
1200
self ._object_handles [handle ] = full_path
1056
1201
self ._parent_map [handle ] = parent_handle
1057
1202
1203
+ entry_type = "directory" if is_dir else "file"
1204
+ print ("[MTP] Added {} '{}' with handle {}" .format (entry_type , full_path , handle ))
1205
+
1058
1206
# Recursively scan subdirectories
1059
1207
if is_dir :
1060
1208
self ._scan_directory (full_path , handle )
1061
- except :
1209
+ except Exception as e :
1062
1210
# Skip entries that cause errors
1211
+ print ("[MTP] Error scanning entry '{}': {}" .format (full_path , str (e )))
1063
1212
continue
1064
- except :
1065
- # Ignore errors during directory scan
1066
- pass
1213
+ except Exception as e :
1214
+ # Log errors during directory scan
1215
+ print ( "[MTP] Error scanning directory '{}': {}" . format ( path , str ( e )))
1067
1216
1068
1217
def _get_format_by_path (self , path ):
1069
1218
"""Determine MTP format code based on file extension."""
0 commit comments