10
10
import micropython
11
11
import ustruct
12
12
from machine import Timer
13
+ import gc
13
14
14
15
_INTERFACE_CLASS_MSC = const (0x08 )
15
16
_INTERFACE_SUBCLASS_SCSI = const (0x06 )
@@ -317,25 +318,21 @@ def handle_cbw(self):
317
318
self .log (f"Error: { exc } " )
318
319
self .prepare_for_csw (status = exc .status )
319
320
return micropython .schedule (self .send_csw , None )
320
- return self .send_csw ()
321
321
322
322
if response is None :
323
323
self .log ("None response" )
324
324
self .prepare_for_csw ()
325
325
return micropython .schedule (self .send_csw , None )
326
- return self .send_csw ()
327
326
328
327
if len (response ) > self .cbw .dCBWDataTransferLength :
329
328
self .log ("Wrong size" )
330
329
self .prepare_for_csw (status = CSW .STATUS_FAILED )
331
330
return micropython .schedule (self .send_csw , None )
332
- return self .send_csw ()
333
331
334
332
if len (response ) == 0 :
335
333
self .log ("Empty response" )
336
334
self .prepare_for_csw ()
337
335
return micropython .schedule (self .send_csw , None )
338
- return self .send_csw ()
339
336
340
337
try :
341
338
self .data = bytearray (response )
@@ -355,28 +352,40 @@ def proc_transfer_data(self, args):
355
352
"""Actual handler for transferring non-CSW data"""
356
353
(ep_addr , result , xferred_bytes ) = args
357
354
self .log ("proc_transfer_data" )
355
+ self .transferred_length += xferred_bytes
358
356
359
357
if self .stage != type (self ).MSC_STAGE_DATA :
360
358
self .log ("Wrong stage" )
361
359
return False
362
360
363
- self .data = self .data [xferred_bytes :]
364
- self .transferred_length += xferred_bytes
365
- if not self .data :
366
- self .log ("We're done" )
367
- self .prepare_for_csw ()
368
- return micropython .schedule (self .send_csw , None )
369
- return self .send_csw ()
370
-
371
- residue = self .cbw .dCBWDataTransferLength - len (self .data )
372
- if residue :
373
- self .csw .dCSWDataResidue = len (self .data )
374
- self .data .extend ("\0 " * residue )
361
+ if len (self .data ) > xferred_bytes :
362
+ self .data = self .data [xferred_bytes :]
363
+ else :
364
+ self .data = bytearray ()
365
+
366
+ if not self .data and self .storage_device .long_operation :
367
+ self .data = self .storage_device .long_operation ["operation" ]()
368
+
369
+ # The above call will have cleared this if it was the last bit of data to send
370
+ if not self .storage_device .long_operation :
371
+ # We don't have more data to fetch...
372
+ if not self .data :
373
+ # We've already sent our final actual data packet
374
+ self .log ("We're done" )
375
+ self .prepare_for_csw ()
376
+ return micropython .schedule (self .send_csw , None )
377
+
378
+ # This is the last data we're sending, pad it out
379
+ residue = self .cbw .dCBWDataTransferLength - (
380
+ self .transferred_length + len (self .data )
381
+ )
382
+ if residue :
383
+ self .log (f"Adding { residue } bytes of padding for residue" )
384
+ self .csw .dCSWDataResidue = residue
385
+ self .data .extend ("\0 " * residue )
375
386
376
387
self .log (f"Preparing to submit data transfer, { len (self .data )} bytes" )
377
- self .submit_xfer (
378
- ep_addr , self .data [: self .cbw .dCBWDataTransferLength ], self .transfer_data
379
- )
388
+ self .submit_xfer (ep_addr , self .data , self .transfer_data )
380
389
381
390
def validate_cbw (self ) -> bool :
382
391
"""Perform Valid and Meaningful checks on a CBW"""
@@ -462,7 +471,8 @@ class StorageDevice:
462
471
Properties:
463
472
filesystem -- a bytes-like thing representing the data this device is handling. If set to None, then the
464
473
object will behave as if there is no medium inserted. This can be changed at runtime.
465
- block_size -- what size the blocks are for SCSI commands. This should probably be left as-is, at 512.
474
+ block_size -- what size the blocks are for SCSI commands. This should probably be left as-is, at 512. If
475
+ the device provides its own block size, that will be used instead
466
476
"""
467
477
468
478
class StorageError (OSError ):
@@ -483,6 +493,7 @@ def __init__(self, filesystem):
483
493
self .block_size = 512
484
494
self .sense = None
485
495
self .additional_sense_code = None
496
+ self .long_operation = {}
486
497
487
498
# A dict of SCSI commands and their handlers; the key is the opcode for the command
488
499
self .scsi_commands = {
@@ -529,6 +540,7 @@ def validate_cmd(self, cmd):
529
540
if self .scsi_commands [cmd [0 ]]["name" ] != "REQUEST_SENSE" :
530
541
self .sense = type (self ).NO_SENSE
531
542
543
+ # Windows seems to possibly send oversized CBDs by these rules in some circumstances?
532
544
return True
533
545
534
546
# 0x00 to 0x1F should have 6-byte CBDs
@@ -550,7 +562,8 @@ def handle_cmd(self, cmd):
550
562
return self .scsi_commands [cmd [0 ]]["handler" ](cmd )
551
563
except Exception as exc :
552
564
raise StorageDevice .StorageError (
553
- f"Error handling command: { str (exc )} " , CSW .STATUS_FAILED
565
+ f"Error handling command { self .scsi_commands [cmd [0 ]]['name' ]} : { str (exc )} " ,
566
+ CSW .STATUS_FAILED ,
554
567
)
555
568
556
569
# Below here are the SCSI command handlers
@@ -587,18 +600,30 @@ def handle_read_capacity_10(self, cmd):
587
600
if self .filesystem is None :
588
601
self .sense = type (self ).MEDIUM_NOT_PRESENT
589
602
raise StorageDevice .StorageError ("No filesystem" , status = CSW .STATUS_FAILED )
603
+
604
+ # Do we have an AbstractBlockDev?
605
+ if getattr (self .filesystem , "ioctl" , False ):
606
+ max_lba = self .filesystem .ioctl (4 , None ) - 1
607
+ block_size = self .filesystem .ioctl (5 , None ) or 512
590
608
else :
591
609
max_lba = int (len (bytes (self .filesystem )) / self .block_size ) - 1
610
+ block_size = self .block_size
592
611
593
- return ustruct .pack (">LL" , max_lba , self . block_size )
612
+ return ustruct .pack (">LL" , max_lba , block_size )
594
613
595
614
def handle_read_format_capacity (self , cmd ):
596
615
block_num = 0
597
616
list_length = 8
598
617
descriptor_type = 3 # 3 = no media present
618
+ block_size = self .block_size
599
619
if self .filesystem is not None :
600
620
descriptor_type = 2 # 2 = formatted media
601
- block_num = int (len (bytes (self .filesystem )) / self .block_size )
621
+ # Do we have an AbstractBlockDev?
622
+ if getattr (self .filesystem , "ioctl" , False ):
623
+ block_num = self .filesystem .ioctl (4 , None )
624
+ block_size = self .filesystem .ioctl (5 , None ) or 512
625
+ else :
626
+ block_num = int (len (bytes (self .filesystem )) / self .block_size )
602
627
603
628
return ustruct .pack (
604
629
">BBBBLBBH" ,
@@ -609,11 +634,41 @@ def handle_read_format_capacity(self, cmd):
609
634
block_num ,
610
635
descriptor_type ,
611
636
0x00 , # Reserved
612
- self . block_size ,
637
+ block_size ,
613
638
)
614
639
615
- def handle_read10 (self , cmd ):
616
- (read10 , flags , lba , group , length , control ) = ustruct .unpack (">BBLBHB" , cmd )
640
+ def handle_read10 (self , cmd = None ):
641
+ if cmd is None :
642
+ if not self .long_operation :
643
+ raise StorageDevice .StorageError (
644
+ "handle_read10 called with no cmd, but we are not in an existing command"
645
+ )
646
+
647
+ length = self .long_operation ["remaining_length" ]
648
+ lba = self .long_operation ["current_lba" ]
649
+ else :
650
+ (read10 , flags , lba , group , length , control ) = ustruct .unpack (
651
+ ">BBLBHB" , cmd
652
+ )
653
+
654
+ # Do we have an AbstractBlockDev?
655
+ if getattr (self .filesystem , "readblocks" , False ):
656
+ gc .collect ()
657
+ # Will we be able to comfortably fit this in RAM?
658
+ block_size = self .filesystem .ioctl (5 , None ) or 512
659
+ max_size = int ((gc .mem_free () / block_size ) / 10 ) or 1
660
+ if length > max_size :
661
+ self .long_operation ["remaining_length" ] = length - max_size
662
+ length = max_size
663
+ self .long_operation ["current_lba" ] = lba + max_size
664
+ self .long_operation ["operation" ] = self .handle_read10
665
+ else :
666
+ self .long_operation = {}
667
+
668
+ read_data = bytearray (length * block_size )
669
+ self .filesystem .readblocks (lba , read_data )
670
+ return read_data
671
+
617
672
return self .filesystem [
618
673
lba * self .block_size : lba * self .block_size + length * self .block_size
619
674
]
0 commit comments