[haiku-commits] Change in haiku[master]: usb_disk: add support for drives bigger than 2TB

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Fri, 24 Jun 2022 13:12:25 +0000

From Jérôme Duval <jerome.duval@xxxxxxxxx>:

Jérôme Duval has uploaded this change for review. ( 
https://review.haiku-os.org/c/haiku/+/5397 ;)


Change subject: usb_disk: add support for drives bigger than 2TB
......................................................................

usb_disk: add support for drives bigger than 2TB

* tested read/write with a NTFS partition at the end of a 5TB USB hard disk.
* fix #14670
---
M src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp
M src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.h
M src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk_scsi.h
3 files changed, 127 insertions(+), 22 deletions(-)



  git pull ssh://git.haiku-os.org:22/haiku refs/changes/97/5397/1

diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp 
b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp
index 00ae1b2..f14f0ff 100644
--- a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp
@@ -1152,6 +1152,56 @@
 }


+static status_t
+usb_disk_update_capacity_16(device_lun *lun)
+{
+       size_t dataLength = sizeof(scsi_read_capacity_16_parameter);
+       scsi_read_capacity_16_parameter parameter;
+       status_t result = B_ERROR;
+       err_act action = err_act_ok;
+
+       uint8 commandBlock[16];
+       memset(commandBlock, 0, sizeof(commandBlock));
+
+       commandBlock[0] = SCSI_SERVICE_ACTION_IN;
+       commandBlock[1] = SCSI_SAI_READ_CAPACITY_16;
+       commandBlock[10] = dataLength >> 24;
+       commandBlock[11] = dataLength >> 16;
+       commandBlock[12] = dataLength >> 8;
+       commandBlock[13] = dataLength;
+
+       // Retry reading the capacity up to three times. The first try might 
only
+       // yield a unit attention telling us that the device or media status
+       // changed, which is more or less expected if it is the first operation
+       // on the device or the device only clears the unit atention for 
capacity
+       // reads.
+       for (int32 i = 0; i < 5; i++) {
+               result = usb_disk_operation(lun, commandBlock, 16, &parameter,
+                       &dataLength, true, &action);
+
+               if (result == B_OK || (action != err_act_retry
+                               && action != err_act_many_retries)) {
+                       break;
+               }
+       }
+
+       if (result != B_OK) {
+               TRACE_ALWAYS("failed to update capacity: %s\n", 
strerror(result));
+               lun->media_present = false;
+               lun->media_changed = false;
+               usb_disk_reset_capacity(lun);
+               return result;
+       }
+
+       lun->media_present = true;
+       lun->media_changed = false;
+       lun->block_size = 
B_BENDIAN_TO_HOST_INT32(parameter.logical_block_length);
+       lun->block_count =
+               B_BENDIAN_TO_HOST_INT64(parameter.last_logical_block_address) + 
1;
+       return B_OK;
+}
+
+
 status_t
 usb_disk_update_capacity(device_lun *lun)
 {
@@ -1200,6 +1250,10 @@
        lun->block_size = 
B_BENDIAN_TO_HOST_INT32(parameter.logical_block_length);
        lun->block_count =
                B_BENDIAN_TO_HOST_INT32(parameter.last_logical_block_address) + 
1;
+       if (lun->block_count == 0) {
+               // try SCSI_READ_CAPACITY_16
+               return usb_disk_update_capacity_16(lun);
+       }
        return B_OK;
 }

@@ -1521,14 +1575,14 @@

 static bool
 usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
-       uint32 &blockPosition, uint16 &blockCount)
+       uint64 &blockPosition, size_t &blockCount)
 {
-       blockPosition = (uint32)(position / lun->block_size);
+       blockPosition = (uint64)(position / lun->block_size);
        if ((off_t)blockPosition * lun->block_size != position)
                return true;

-       blockCount = (uint16)(length / lun->block_size);
-       if ((size_t)blockCount * lun->block_size != length)
+       blockCount = length / lun->block_size;
+       if (blockCount * lun->block_size != length)
                return true;

        return false;
@@ -1536,10 +1590,10 @@


 static status_t
-usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
+usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
        void *buffer, size_t *length)
 {
-       uint8 commandBlock[12];
+       uint8 commandBlock[16];
        memset(commandBlock, 0, sizeof(commandBlock));
        if (lun->device->is_ufi) {
                commandBlock[0] = SCSI_READ_12;
@@ -1548,8 +1602,8 @@
                commandBlock[3] = blockPosition >> 16;
                commandBlock[4] = blockPosition >> 8;
                commandBlock[5] = blockPosition;
-               commandBlock[6] = 0; // blockCount >> 24;
-               commandBlock[7] = 0; // blockCount >> 16;
+               commandBlock[6] = blockCount >> 24;
+               commandBlock[7] = blockCount >> 16;
                commandBlock[8] = blockCount >> 8;
                commandBlock[9] = blockCount;

@@ -1563,7 +1617,7 @@
                                snooze(10000);
                }
                return result;
-       } else {
+       } else if (blockPosition + blockCount < 0x100000000LL && blockCount <= 
0x10000) {
                commandBlock[0] = SCSI_READ_10;
                commandBlock[1] = 0;
                commandBlock[2] = blockPosition >> 24;
@@ -1575,15 +1629,33 @@
                status_t result = usb_disk_operation(lun, commandBlock, 10,
                        buffer, length, true);
                return result;
+       } else {
+               commandBlock[0] = SCSI_READ_16;
+               commandBlock[1] = 0;
+               commandBlock[2] = blockPosition >> 56;
+               commandBlock[3] = blockPosition >> 48;
+               commandBlock[4] = blockPosition >> 40;
+               commandBlock[5] = blockPosition >> 32;
+               commandBlock[6] = blockPosition >> 24;
+               commandBlock[7] = blockPosition >> 16;
+               commandBlock[8] = blockPosition >> 8;
+               commandBlock[9] = blockPosition;
+               commandBlock[10] = blockCount >> 24;
+               commandBlock[11] = blockCount >> 16;
+               commandBlock[12] = blockCount >> 8;
+               commandBlock[13] = blockCount;
+               status_t result = usb_disk_operation(lun, commandBlock, 16,
+                       buffer, length, true);
+               return result;
        }
 }


 static status_t
-usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
+usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
        void *buffer, size_t *length)
 {
-       uint8 commandBlock[12];
+       uint8 commandBlock[16];
        memset(commandBlock, 0, sizeof(commandBlock));

        if (lun->device->is_ufi) {
@@ -1613,7 +1685,7 @@
                if (result == B_OK)
                        lun->should_sync = true;
                return result;
-       } else {
+       } else if (blockPosition + blockCount < 0x100000000LL && blockCount <= 
0x10000) {
                commandBlock[0] = SCSI_WRITE_10;
                commandBlock[2] = blockPosition >> 24;
                commandBlock[3] = blockPosition >> 16;
@@ -1626,18 +1698,38 @@
                if (result == B_OK)
                        lun->should_sync = true;
                return result;
+       } else {
+               commandBlock[0] = SCSI_WRITE_16;
+               commandBlock[1] = 0;
+               commandBlock[2] = blockPosition >> 56;
+               commandBlock[3] = blockPosition >> 48;
+               commandBlock[4] = blockPosition >> 40;
+               commandBlock[5] = blockPosition >> 32;
+               commandBlock[6] = blockPosition >> 24;
+               commandBlock[7] = blockPosition >> 16;
+               commandBlock[8] = blockPosition >> 8;
+               commandBlock[9] = blockPosition;
+               commandBlock[10] = blockCount >> 24;
+               commandBlock[11] = blockCount >> 16;
+               commandBlock[12] = blockCount >> 8;
+               commandBlock[13] = blockCount;
+               status_t result = usb_disk_operation(lun, commandBlock, 16,
+                       buffer, length, false);
+               if (result == B_OK)
+                       lun->should_sync = true;
+               return result;
        }
 }


 static status_t
 usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
-       void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
-       uint16 &blockCount)
+       void *&partialBuffer, void *&blockBuffer, uint64 &blockPosition,
+       size_t &blockCount)
 {
-       blockPosition = (uint32)(position / lun->block_size);
-       blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
-               / lun->block_size) - blockPosition);
+       blockPosition = position / lun->block_size;
+       blockCount = (position + length + lun->block_size - 1)
+               / lun->block_size - blockPosition;
        size_t blockLength = blockCount * lun->block_size;
        blockBuffer = malloc(blockLength);
        if (blockBuffer == NULL) {
@@ -2005,8 +2097,8 @@
        }

        status_t result = B_ERROR;
-       uint32 blockPosition = 0;
-       uint16 blockCount = 0;
+       uint64 blockPosition = 0;
+       size_t blockCount = 0;
        bool needsPartial = usb_disk_needs_partial_buffer(lun, position, 
*length,
                blockPosition, blockCount);
        if (needsPartial) {
@@ -2056,8 +2148,8 @@
        }

        status_t result = B_ERROR;
-       uint32 blockPosition = 0;
-       uint16 blockCount = 0;
+       uint64 blockPosition = 0;
+       size_t blockCount = 0;
        bool needsPartial = usb_disk_needs_partial_buffer(lun, position,
                *length, blockPosition, blockCount);
        if (needsPartial) {
diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.h 
b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.h
index 8008032..4b2a78b 100644
--- a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.h
+++ b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.h
@@ -68,7 +68,7 @@
        // device information through read capacity/inquiry
        bool            media_present;
        bool            media_changed;
-       uint32          block_count;
+       uint64          block_count;
        uint32          block_size;
        uint8           device_type;
        bool            removable;
diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk_scsi.h 
b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk_scsi.h
index 413e907..5061d0b 100644
--- a/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk_scsi.h
+++ b/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk_scsi.h
@@ -22,8 +22,15 @@
        SCSI_SYNCHRONIZE_CACHE_10 = 0x35,
        SCSI_READ_12 = 0xA8,
        SCSI_WRITE_12 = 0xAA,
+       SCSI_READ_16 = 0x88,
+       SCSI_WRITE_16 = 0x8A,
+       SCSI_SERVICE_ACTION_IN = 0x9E,
 } scsi_operations;

+typedef enum {
+       SCSI_SAI_READ_CAPACITY_16 = 0x10,
+} scsi_service_actions;
+
 // common command structures
 typedef struct scsi_command_6_s {
        uint8   operation;
@@ -89,6 +96,12 @@
        uint32  logical_block_length;
 } _PACKED scsi_read_capacity_10_parameter;

+typedef struct scsi_read_capacity_16_parameter_s {
+       uint64  last_logical_block_address;
+       uint32  logical_block_length;
+       uint8   reserved[20];
+} _PACKED scsi_read_capacity_16_parameter;
+
 // request sense keys/codes
 typedef enum {
        SCSI_SENSE_KEY_NO_SENSE = 0x00,

--
To view, visit https://review.haiku-os.org/c/haiku/+/5397
To unsubscribe, or for help writing mail filters, visit 
https://review.haiku-os.org/settings

Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: Ic005dfb7ef94b50a2d6fc8099ef5c83ec6b4a730
Gerrit-Change-Number: 5397
Gerrit-PatchSet: 1
Gerrit-Owner: Jérôme Duval <jerome.duval@xxxxxxxxx>
Gerrit-MessageType: newchange

Other related posts:

  • » [haiku-commits] Change in haiku[master]: usb_disk: add support for drives bigger than 2TB - Gerrit