[haiku-commits] haiku: hrev56215 - src/add-ons/kernel/drivers/disk/usb/usb_disk

  • From: Jérôme Duval <jerome.duval@xxxxxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Sat, 25 Jun 2022 07:05:30 +0000 (UTC)

hrev56215 adds 1 changeset to branch 'master'
old head: e49b671dcf36bcf8959719d3db5f7d4501f21b99
new head: d2e5fc7fa40cebf58935806e9e8ab17e178e2bb6
overview: 
https://git.haiku-os.org/haiku/log/?qt=range&q=d2e5fc7fa40c+%5Ee49b671dcf36

----------------------------------------------------------------------------

d2e5fc7fa40c: 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
  
  Change-Id: Ic005dfb7ef94b50a2d6fc8099ef5c83ec6b4a730
  Reviewed-on: https://review.haiku-os.org/c/haiku/+/5397
  Tested-by: Commit checker robot <no-reply+buildbot@xxxxxxxxxxxx>
  Reviewed-by: waddlesplash <waddlesplash@xxxxxxxxx>

                                   [ Jérôme Duval <jerome.duval@xxxxxxxxx> ]

----------------------------------------------------------------------------

Revision:    hrev56215
Commit:      d2e5fc7fa40cebf58935806e9e8ab17e178e2bb6
URL:         https://git.haiku-os.org/haiku/commit/?id=d2e5fc7fa40c
Author:      Jérôme Duval <jerome.duval@xxxxxxxxx>
Date:        Fri Jun 24 12:58:13 2022 UTC

Ticket:      https://dev.haiku-os.org/ticket/14670

----------------------------------------------------------------------------

3 files changed, 127 insertions(+), 22 deletions(-)
.../drivers/disk/usb/usb_disk/usb_disk.cpp       | 134 ++++++++++++++++---
.../kernel/drivers/disk/usb/usb_disk/usb_disk.h  |   2 +-
.../drivers/disk/usb/usb_disk/usb_disk_scsi.h    |  13 ++

----------------------------------------------------------------------------

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 00ae1b2744..f14f0ff57c 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 @@ usb_disk_reset_capacity(device_lun *lun)
 }
 
 
+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 @@ usb_disk_update_capacity(device_lun *lun)
        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 @@ usb_disk_device_removed(void *cookie)
 
 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 @@ usb_disk_needs_partial_buffer(device_lun *lun, off_t 
position, size_t length,
 
 
 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 @@ usb_disk_block_read(device_lun *lun, uint32 
blockPosition, uint16 blockCount,
                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 @@ usb_disk_block_read(device_lun *lun, uint32 
blockPosition, uint16 blockCount,
                                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 @@ usb_disk_block_read(device_lun *lun, uint32 
blockPosition, uint16 blockCount,
                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 @@ usb_disk_block_write(device_lun *lun, uint32 
blockPosition, uint16 blockCount,
                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 @@ usb_disk_block_write(device_lun *lun, uint32 
blockPosition, uint16 blockCount,
                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 @@ usb_disk_read(void *cookie, off_t position, void *buffer, 
size_t *length)
        }
 
        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 @@ usb_disk_write(void *cookie, off_t position, const void 
*buffer,
        }
 
        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 8008032251..4b2a78bafc 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 @@ struct device_lun_s {
        // 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 413e907793..5061d0b5fe 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 @@ typedef enum {
        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 @@ typedef struct scsi_read_capacity_10_parameter_s {
        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,


Other related posts:

  • » [haiku-commits] haiku: hrev56215 - src/add-ons/kernel/drivers/disk/usb/usb_disk - Jérôme Duval