[PATCH 2/2] usb: storage: add support for drivers larger than 2TiB

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Consumer USB disks usually have emulated 512 byte sectors at the
USB/SCSI level, which means SCSI Read/Write/Capacity 10 can only
handle up to 2TiB USB disks. Add support for the optional 16 byte
command variants to handle disks larger than that.

Disks smaller than 2 TiB should not be affected.

Tested with 2 different 4TiB disks as well as one 2TiB disk.

Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx>
---
 drivers/usb/storage/usb.c | 94 +++++++++++++++++++++++++++++++++------
 include/scsi.h            |  5 +++
 2 files changed, 85 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 122af659820f..b417640186a2 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -157,6 +157,47 @@ static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev)
 	return 0;
 }
 
+static int read_capacity_16(struct us_blk_dev *usb_blkdev)
+{
+	struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
+	unsigned char cmd[16];
+	const u8 datalen = 32;
+	u8 *data = xzalloc(datalen);
+	int ret;
+	sector_t lba;
+	unsigned sector_size;
+
+	memset(cmd, 0, 16);
+	cmd[0] = SERVICE_ACTION_IN_16;
+	cmd[1] = SAI_READ_CAPACITY_16;
+	cmd[13] = datalen;
+
+	ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen,
+				 3, USB_STOR_NO_REQUEST_SENSE);
+
+	if (ret < 0) {
+		dev_warn(dev, "Read Capacity(16) failed\n");
+		return ret;
+	}
+
+	/* Note this is logical, not physical sector size */
+	sector_size = be32_to_cpup((u32 *)&data[8]);
+	lba = be64_to_cpup((u64 *)&data[0]);
+
+	dev_dbg(dev, "LBA (16) = 0x%llx w/ sector size = %u\n",
+		lba, sector_size);
+
+	if ((data[12] & 1) == 1) {
+		dev_warn(dev, "Protection unsupported\n");
+		return -ENOTSUPP;
+	}
+
+	usb_blkdev->blk.blockbits = SECTOR_SHIFT;
+	usb_blkdev->blk.num_blocks = lba + 1;
+
+	return sector_size;
+}
+
 static int read_capacity_10(struct us_blk_dev *usb_blkdev)
 {
 	struct device_d *dev = &usb_blkdev->us->pusb_dev->dev;
@@ -174,7 +215,7 @@ static int read_capacity_10(struct us_blk_dev *usb_blkdev)
 				 3, USB_STOR_NO_REQUEST_SENSE);
 
 	if (ret < 0) {
-		dev_dbg(dev, "Cannot read device capacity\n");
+		dev_warn(dev, "Read Capacity(10) failed\n");
 		return ret;
 	}
 
@@ -184,15 +225,6 @@ static int read_capacity_10(struct us_blk_dev *usb_blkdev)
 	dev_dbg(dev, "LBA (10) = 0x%llx w/ sector size = %u\n",
 		lba, sector_size);
 
-
-	if (lba == U32_MAX) {
-		lba = U32_MAX - 1;
-		dev_warn(dev,
-			 "Limiting device size due to 32 bit constraints\n");
-		/* To support LBA >= U32_MAX, a READ CAPACITY (16) should be issued instead */
-	}
-
-
 	if (sector_size != SECTOR_SIZE)
 		dev_warn(dev, "Support only %d bytes sectors\n", SECTOR_SIZE);
 
@@ -202,6 +234,20 @@ static int read_capacity_10(struct us_blk_dev *usb_blkdev)
 	return SECTOR_SIZE;
 }
 
+static int usb_stor_io_16(struct us_blk_dev *usb_blkdev, u8 opcode,
+			  sector_t start, u8 *data, u16 blocks)
+{
+	u8 cmd[16];
+
+	memset(cmd, 0, sizeof(cmd));
+	cmd[0] = opcode;
+	put_unaligned_be64(start, &cmd[2]);
+	put_unaligned_be32(blocks, &cmd[10]);
+
+	return usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data,
+				  blocks * SECTOR_SIZE, 10, 0);
+}
+
 static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode,
 			  sector_t start, u8 *data, u16 blocks)
 {
@@ -232,6 +278,7 @@ static int usb_stor_blk_io(struct block_device *disk_dev,
 						   blk);
 	struct us_data *us = pblk_dev->us;
 	struct device_d *dev = &us->pusb_dev->dev;
+	int result;
 
 	/* ensure unit ready */
 	dev_dbg(dev, "Testing for unit ready\n");
@@ -248,13 +295,24 @@ static int usb_stor_blk_io(struct block_device *disk_dev,
 	while (sector_count > 0) {
 		u16 n = min_t(blkcnt_t, sector_count, US_MAX_IO_BLK);
 
-		if (usb_stor_io_10(pblk_dev,
-				   read ? SCSI_READ10 : SCSI_WRITE10,
-				   sector_start,
-				   buffer, n)) {
+		if (disk_dev->num_blocks > 0xffffffff) {
+			result = usb_stor_io_16(pblk_dev,
+						read ? SCSI_READ16 : SCSI_WRITE16,
+						sector_start,
+						buffer, n);
+		} else {
+
+			result = usb_stor_io_10(pblk_dev,
+						read ? SCSI_READ10 : SCSI_WRITE10,
+						sector_start,
+						buffer, n);
+		}
+
+		if (result) {
 			dev_dbg(dev, "I/O error at sector %llu\n", sector_start);
 			break;
 		}
+
 		sector_start += n;
 		sector_count -= n;
 		buffer += n * SECTOR_SIZE;
@@ -320,6 +378,14 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev)
 	if (result < 0)
 		return result;
 
+	if (pblk_dev->blk.num_blocks > 0xffffffff) {
+		result = read_capacity_16(pblk_dev);
+		if (result < 0) {
+			dev_notice(dev, "Using 0xffffffff as device size\n");
+			pblk_dev->blk.num_blocks = 1 + (sector_t) 0xffffffff;
+		}
+	}
+
 	dev_dbg(dev, "Capacity = 0x%llx, blockshift = 0x%x\n",
 		 pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits);
 
diff --git a/include/scsi.h b/include/scsi.h
index e2397489ead9..f84513b813e9 100644
--- a/include/scsi.h
+++ b/include/scsi.h
@@ -109,6 +109,7 @@
 #define SCSI_MED_REMOVL	0x1E		/* Prevent/Allow medium Removal (O) */
 #define SCSI_READ6	0x08		/* Read 6-byte (MANDATORY) */
 #define SCSI_READ10	0x28		/* Read 10-byte (MANDATORY) */
+#define SCSI_READ16	0x88		/* Read 16-byte (O) */
 #define SCSI_RD_CAPAC	0x25		/* Read Capacity (MANDATORY) */
 #define SCSI_RD_DEFECT	0x37		/* Read Defect Data (O) */
 #define SCSI_READ_LONG	0x3E		/* Read Long (O) */
@@ -128,10 +129,14 @@
 #define SCSI_VERIFY	0x2F		/* Verify (O) */
 #define SCSI_WRITE6	0x0A		/* Write 6-Byte (MANDATORY) */
 #define SCSI_WRITE10	0x2A		/* Write 10-Byte (MANDATORY) */
+#define SCSI_WRITE16	0x8A		/* Write 16-Byte (O) */
 #define SCSI_WRT_VERIFY	0x2E		/* Write and Verify (O) */
 #define SCSI_WRITE_LONG	0x3F		/* Write Long (O) */
 #define SCSI_WRITE_SAME	0x41		/* Write Same (O) */
 
+#define SERVICE_ACTION_IN_16	0x9e
+/* values for service action in */
+#define	SAI_READ_CAPACITY_16	0x10
 
 /****************************************************************************
  * decleration of functions which have to reside in the LowLevel Part Driver
-- 
2.29.2


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox



[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux