
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 marcan@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);