This makes things work properly on devices with >= 2 TiB
capacity. If u-boot is built without CONFIG_SYS_64BIT_LBA,
the capacity will be clamped at 2^32 - 1 sectors.

Signed-off-by: Hector Martin <mar...@marcan.st>
---
 common/usb_storage.c | 132 ++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 18 deletions(-)

diff --git a/common/usb_storage.c b/common/usb_storage.c
index 95507ffbce48..3035f2ee9868 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -66,7 +66,7 @@
 static const unsigned char us_direction[256/8] = {
        0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
        0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+       0x00, 0x01, 0x00, 0x40, 0x00, 0x01, 0x00, 0x01,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 };
 #define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
@@ -1073,6 +1073,27 @@ static int usb_read_capacity(struct scsi_cmd *srb, 
struct us_data *ss)
        return -1;
 }
 
+#ifdef CONFIG_SYS_64BIT_LBA
+static int usb_read_capacity64(struct scsi_cmd *srb, struct us_data *ss)
+{
+       int retry;
+       /* XXX retries */
+       retry = 3;
+       do {
+               memset(&srb->cmd[0], 0, 16);
+               srb->cmd[0] = SCSI_SRV_ACTION_IN;
+               srb->cmd[1] = (srb->lun << 5) | SCSI_SAI_RD_CAPAC16;
+               srb->cmd[13] = 32; /* Allocation length */
+               srb->datalen = 32;
+               srb->cmdlen = 16;
+               if (ss->transport(srb, ss) == USB_STOR_TRANSPORT_GOOD)
+                       return 0;
+       } while (retry--);
+
+       return -1;
+}
+#endif
+
 static int usb_read_10(struct scsi_cmd *srb, struct us_data *ss,
                       unsigned long start, unsigned short blocks)
 {
@@ -1107,6 +1128,49 @@ static int usb_write_10(struct scsi_cmd *srb, struct 
us_data *ss,
        return ss->transport(srb, ss);
 }
 
+#ifdef CONFIG_SYS_64BIT_LBA
+static int usb_read_16(struct scsi_cmd *srb, struct us_data *ss,
+                      uint64_t start, unsigned short blocks)
+{
+       memset(&srb->cmd[0], 0, 16);
+       srb->cmd[0] = SCSI_READ16;
+       srb->cmd[1] = srb->lun << 5;
+       srb->cmd[2] = ((unsigned char) (start >> 56)) & 0xff;
+       srb->cmd[3] = ((unsigned char) (start >> 48)) & 0xff;
+       srb->cmd[4] = ((unsigned char) (start >> 40)) & 0xff;
+       srb->cmd[5] = ((unsigned char) (start >> 32)) & 0xff;
+       srb->cmd[6] = ((unsigned char) (start >> 24)) & 0xff;
+       srb->cmd[7] = ((unsigned char) (start >> 16)) & 0xff;
+       srb->cmd[8] = ((unsigned char) (start >> 8)) & 0xff;
+       srb->cmd[9] = ((unsigned char) (start)) & 0xff;
+       srb->cmd[12] = ((unsigned char) (blocks >> 8)) & 0xff;
+       srb->cmd[13] = (unsigned char) blocks & 0xff;
+       srb->cmdlen = 16;
+       debug("read16: start %llx blocks %x\n", (long long)start, blocks);
+       return ss->transport(srb, ss);
+}
+
+static int usb_write_16(struct scsi_cmd *srb, struct us_data *ss,
+                       uint64_t start, unsigned short blocks)
+{
+       memset(&srb->cmd[0], 0, 16);
+       srb->cmd[0] = SCSI_WRITE16;
+       srb->cmd[1] = srb->lun << 5;
+       srb->cmd[2] = ((unsigned char) (start >> 56)) & 0xff;
+       srb->cmd[3] = ((unsigned char) (start >> 48)) & 0xff;
+       srb->cmd[4] = ((unsigned char) (start >> 40)) & 0xff;
+       srb->cmd[5] = ((unsigned char) (start >> 32)) & 0xff;
+       srb->cmd[6] = ((unsigned char) (start >> 24)) & 0xff;
+       srb->cmd[7] = ((unsigned char) (start >> 16)) & 0xff;
+       srb->cmd[8] = ((unsigned char) (start >> 8)) & 0xff;
+       srb->cmd[9] = ((unsigned char) (start)) & 0xff;
+       srb->cmd[12] = ((unsigned char) (blocks >> 8)) & 0xff;
+       srb->cmd[13] = (unsigned char) blocks & 0xff;
+       srb->cmdlen = 16;
+       debug("write16: start %llx blocks %x\n", (long long)start, blocks);
+       return ss->transport(srb, ss);
+}
+#endif
 
 #ifdef CONFIG_USB_BIN_FIXUP
 /*
@@ -1145,6 +1209,7 @@ static unsigned long usb_stor_read(struct blk_desc 
*block_dev, lbaint_t blknr,
        struct usb_device *udev;
        struct us_data *ss;
        int retry;
+       int ret;
        struct scsi_cmd *srb = &usb_ccb;
 #if CONFIG_IS_ENABLED(BLK)
        struct blk_desc *block_dev;
@@ -1190,7 +1255,13 @@ retry_it:
                        usb_show_progress();
                srb->datalen = block_dev->blksz * smallblks;
                srb->pdata = (unsigned char *)buf_addr;
-               if (usb_read_10(srb, ss, start, smallblks)) {
+#ifdef CONFIG_SYS_64BIT_LBA
+               if (block_dev->lba > ((lbaint_t)0x100000000))
+                       ret = usb_read_16(srb, ss, start, smallblks);
+               else
+#endif
+               ret = usb_read_10(srb, ss, start, smallblks);
+               if (ret) {
                        debug("Read ERROR\n");
                        ss->flags &= ~USB_READY;
                        usb_request_sense(srb, ss);
@@ -1228,6 +1299,7 @@ static unsigned long usb_stor_write(struct blk_desc 
*block_dev, lbaint_t blknr,
        struct usb_device *udev;
        struct us_data *ss;
        int retry;
+       int ret;
        struct scsi_cmd *srb = &usb_ccb;
 #if CONFIG_IS_ENABLED(BLK)
        struct blk_desc *block_dev;
@@ -1277,7 +1349,13 @@ retry_it:
                        usb_show_progress();
                srb->datalen = block_dev->blksz * smallblks;
                srb->pdata = (unsigned char *)buf_addr;
-               if (usb_write_10(srb, ss, start, smallblks)) {
+#ifdef CONFIG_SYS_64BIT_LBA
+               if (block_dev->lba > ((lbaint_t)0x100000000))
+                       ret = usb_write_16(srb, ss, start, smallblks);
+               else
+#endif
+               ret = usb_write_10(srb, ss, start, smallblks);
+               if (ret) {
                        debug("Write ERROR\n");
                        ss->flags &= ~USB_READY;
                        usb_request_sense(srb, ss);
@@ -1434,9 +1512,10 @@ int usb_stor_get_info(struct usb_device *dev, struct 
us_data *ss,
                      struct blk_desc *dev_desc)
 {
        unsigned char perq, modi;
-       ALLOC_CACHE_ALIGN_BUFFER(u32, cap, 2);
+       ALLOC_CACHE_ALIGN_BUFFER(u32, cap, 8);
        ALLOC_CACHE_ALIGN_BUFFER(u8, usb_stor_buf, 36);
-       u32 capacity, blksz;
+       lbaint_t capacity;
+       u32 blksz;
        struct scsi_cmd *pccb = &usb_ccb;
 
        pccb->pdata = usb_stor_buf;
@@ -1487,26 +1566,43 @@ int usb_stor_get_info(struct usb_device *dev, struct 
us_data *ss,
                return 0;
        }
        pccb->pdata = (unsigned char *)cap;
-       memset(pccb->pdata, 0, 8);
+       memset(pccb->pdata, 0, 32);
        if (usb_read_capacity(pccb, ss) != 0) {
                printf("READ_CAP ERROR\n");
                ss->flags &= ~USB_READY;
-               cap[0] = 2880;
-               cap[1] = 0x200;
+               capacity = 2880;
+               blksz = 512;
+       } else {
+               debug("Read Capacity returns: 0x%08x, 0x%08x\n",
+                     cap[0], cap[1]);
+               capacity = ((lbaint_t)be32_to_cpu(cap[0])) + 1;
+               blksz = be32_to_cpu(cap[1]);
        }
-       debug("Read Capacity returns: 0x%08x, 0x%08x\n", cap[0], cap[1]);
-#if 0
-       if (cap[0] > (0x200000 * 10)) /* greater than 10 GByte */
-               cap[0] >>= 16;
 
-       cap[0] = cpu_to_be32(cap[0]);
-       cap[1] = cpu_to_be32(cap[1]);
+#ifdef CONFIG_SYS_64BIT_LBA
+       if (capacity == 0x100000000) {
+               if (usb_read_capacity64(pccb, ss) != 0) {
+                       puts("READ_CAP64 ERROR\n");
+               } else {
+                       debug("Read Capacity 64 returns: 0x%08x, 0x%08x, 
0x%08x\n",
+                             cap[0], cap[1], cap[2]);
+                       capacity = be64_to_cpu(*(uint64_t *)cap) + 1;
+                       blksz = be32_to_cpu(cap[2]);
+               }
+       }
+#else
+       /*
+        * READ CAPACITY will return 0xffffffff when limited,
+        * which wraps to 0 with the +1 above
+        */
+       if (!capacity) {
+               puts("LBA exceeds 32 bits but 64-bit LBA is disabled.\n");
+               capacity = ~0;
+       }
 #endif
 
-       capacity = be32_to_cpu(cap[0]) + 1;
-       blksz = be32_to_cpu(cap[1]);
-
-       debug("Capacity = 0x%08x, blocksz = 0x%08x\n", capacity, blksz);
+       debug("Capacity = 0x%llx, blocksz = 0x%08x\n",
+             (long long)capacity, blksz);
        dev_desc->lba = capacity;
        dev_desc->blksz = blksz;
        dev_desc->log2blksz = LOG2(dev_desc->blksz);

-- 
2.41.0

Reply via email to