diff options
Diffstat (limited to 'drivers/usb/storage/usb.c')
-rw-r--r-- | drivers/usb/storage/usb.c | 230 |
1 files changed, 149 insertions, 81 deletions
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index e0ef4f5ef3..cc241e69be 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,30 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Most of this source has been derived from the Linux and * U-Boot USB Mass Storage driver implementations. * * Adapted for barebox: * Copyright (c) 2011, AMK Drives & Controls Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * */ #include <common.h> #include <init.h> #include <malloc.h> +#include <dma.h> #include <errno.h> #include <scsi.h> -#include <usb/usb.h> -#include <usb/usb_defs.h> +#include <linux/usb/usb.h> +#include <linux/usb/usb_defs.h> #include <asm/unaligned.h> @@ -41,10 +31,10 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; u8 cmd[6]; const u8 datalen = 18; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); dev_dbg(dev, "SCSI_REQ_SENSE\n"); @@ -55,7 +45,7 @@ static int usb_stor_request_sense(struct us_blk_dev *usb_blkdev) dev_dbg(dev, "Request Sense returned %02X %02X %02X\n", data[2], data[12], data[13]); - free(data); + dma_free(data); return 0; } @@ -84,7 +74,7 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, int retries, int request_sense_delay_ms) { struct us_data *us = usb_blkdev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; int i, ret; for (i = 0; i <= retries; i++) { @@ -111,11 +101,11 @@ static int usb_stor_transport(struct us_blk_dev *usb_blkdev, static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; int ret; u8 cmd[6]; const u16 datalen = 36; - u8 *data = xzalloc(datalen); + u8 *data = dma_alloc(datalen); memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_INQUIRY; @@ -137,55 +127,129 @@ static int usb_stor_inquiry(struct us_blk_dev *usb_blkdev) // TODO: process and store device info exit: - free(data); + dma_free(data); return ret; } -static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev) +static int usb_stor_test_unit_ready(struct us_blk_dev *usb_blkdev, u64 timeout_ns) { + u64 start; u8 cmd[6]; int ret; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_TST_U_RDY; - ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, - 10, 100); - if (ret < 0) - return -ENODEV; + start = get_time_ns(); - return 0; + do { + ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), NULL, 0, + 10, 100); + } while (ret < 0 && !is_timeout(start, timeout_ns)); + + return ret ? -ENODEV : 0; } -static int usb_stor_read_capacity(struct us_blk_dev *usb_blkdev, - u32 *last_lba, u32 *block_length) +static int read_capacity_16(struct us_blk_dev *usb_blkdev) { - struct device_d *dev = &usb_blkdev->us->pusb_dev->dev; + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; + const u8 datalen = 32; + u8 *data = dma_alloc(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"); + goto fail; + } + + /* 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"); + ret = -ENOTSUPP; + goto fail; + } + + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + usb_blkdev->blk.num_blocks = lba + 1; + + ret = sector_size; +fail: + dma_free(data); + return ret; +} + +static int read_capacity_10(struct us_blk_dev *usb_blkdev) +{ + struct device *dev = &usb_blkdev->us->pusb_dev->dev; + unsigned char cmd[16]; const u32 datalen = 8; - u32 *data = xzalloc(datalen); - u8 cmd[10]; + __be32 *data = dma_alloc(datalen); int ret; + sector_t lba; + unsigned sector_size; memset(cmd, 0, sizeof(cmd)); cmd[0] = SCSI_RD_CAPAC; ret = usb_stor_transport(usb_blkdev, cmd, sizeof(cmd), data, datalen, 3, USB_STOR_NO_REQUEST_SENSE); - if (ret < 0) - goto exit; - dev_dbg(dev, "Read Capacity returns: 0x%x, 0x%x\n", - data[0], data[1]); - *last_lba = be32_to_cpu(data[0]); - *block_length = be32_to_cpu(data[1]); + if (ret < 0) { + dev_warn(dev, "Read Capacity(10) failed\n"); + goto fail; + } -exit: - free(data); + sector_size = be32_to_cpu(data[1]); + lba = be32_to_cpu(data[0]); + + dev_dbg(dev, "LBA (10) = 0x%llx w/ sector size = %u\n", + lba, sector_size); + + if (sector_size != SECTOR_SIZE) + dev_warn(dev, "Support only %d bytes sectors\n", SECTOR_SIZE); + + usb_blkdev->blk.num_blocks = lba + 1; + usb_blkdev->blk.blockbits = SECTOR_SHIFT; + + ret = SECTOR_SIZE; +fail: + dma_free(data); return ret; } +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, - u32 start, u8 *data, u16 blocks) + sector_t start, u8 *data, u16 blocks) { u8 cmd[10]; @@ -206,37 +270,49 @@ static int usb_stor_io_10(struct us_blk_dev *usb_blkdev, u8 opcode, /* Read / write a chunk of sectors on media */ static int usb_stor_blk_io(struct block_device *disk_dev, - int sector_start, int sector_count, void *buffer, + sector_t sector_start, blkcnt_t sector_count, void *buffer, bool read) { struct us_blk_dev *pblk_dev = container_of(disk_dev, struct us_blk_dev, blk); struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; + int result; /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - if (usb_stor_test_unit_ready(pblk_dev)) { + if (usb_stor_test_unit_ready(pblk_dev, 0)) { dev_dbg(dev, "Device NOT ready\n"); return -EIO; } /* read / write the requested data */ - dev_dbg(dev, "%s %u block(s), starting from %d\n", + dev_dbg(dev, "%s %llu block(s), starting from %llu\n", read ? "Read" : "Write", sector_count, sector_start); while (sector_count > 0) { - unsigned n = min(sector_count, US_MAX_IO_BLK); + u16 n = min_t(blkcnt_t, sector_count, US_MAX_IO_BLK); + + 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 (usb_stor_io_10(pblk_dev, - read ? SCSI_READ10 : SCSI_WRITE10, - sector_start, - buffer, n)) { - dev_dbg(dev, "I/O error at sector %d\n", sector_start); + 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; @@ -247,14 +323,14 @@ static int usb_stor_blk_io(struct block_device *disk_dev, /* Write a chunk of sectors to media */ static int __maybe_unused usb_stor_blk_write(struct block_device *blk, - const void *buffer, int block, int num_blocks) + const void *buffer, sector_t block, blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, (void *)buffer, false); } /* Read a chunk of sectors from media */ -static int usb_stor_blk_read(struct block_device *blk, void *buffer, int block, - int num_blocks) +static int usb_stor_blk_read(struct block_device *blk, void *buffer, sector_t block, + blkcnt_t num_blocks) { return usb_stor_blk_io(blk, block, num_blocks, buffer, true); } @@ -274,8 +350,7 @@ static struct block_device_ops usb_mass_storage_ops = { static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) { struct us_data *us = pblk_dev->us; - struct device_d *dev = &us->pusb_dev->dev; - u32 last_lba = 0, block_length = 0; + struct device *dev = &us->pusb_dev->dev; int result; /* get device info */ @@ -290,7 +365,8 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* ensure unit ready */ dev_dbg(dev, "Testing for unit ready\n"); - result = usb_stor_test_unit_ready(pblk_dev); + /* retry a bit longer than usual as some HDDs take longer to spin up */ + result = usb_stor_test_unit_ready(pblk_dev, 10ULL * NSEC_PER_SEC); if (result) { dev_dbg(dev, "Device NOT ready\n"); return result; @@ -299,23 +375,19 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* read capacity */ dev_dbg(dev, "Reading capacity\n"); - result = usb_stor_read_capacity(pblk_dev, &last_lba, &block_length); - if (result < 0) { - dev_dbg(dev, "Cannot read device capacity\n"); + result = read_capacity_10(pblk_dev); + if (result < 0) return result; - } - if (last_lba > INT_MAX - 1) { - last_lba = INT_MAX - 1; - dev_warn(dev, - "Limiting device size due to 31 bit contraints\n"); + 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; + } } - pblk_dev->blk.num_blocks = last_lba + 1; - if (block_length != SECTOR_SIZE) - pr_warn("Support only %d bytes sectors\n", SECTOR_SIZE); - pblk_dev->blk.blockbits = SECTOR_SHIFT; - dev_dbg(dev, "Capacity = 0x%x, blockshift = 0x%x\n", + dev_dbg(dev, "Capacity = 0x%llx, blockshift = 0x%x\n", pblk_dev->blk.num_blocks, pblk_dev->blk.blockbits); return 0; @@ -324,7 +396,7 @@ static int usb_stor_init_blkdev(struct us_blk_dev *pblk_dev) /* Create and register a disk device for the specified LUN */ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; struct us_blk_dev *pblk_dev; int result; @@ -345,10 +417,11 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) result = cdev_find_free_index("disk"); if (result == -1) pr_err("Cannot find a free number for the disk node\n"); - pr_info("Using index %d for the new disk\n", result); + dev_info(dev, "registering as disk%d\n", result); pblk_dev->blk.cdev.name = basprintf("disk%d", result); pblk_dev->blk.blockbits = SECTOR_SHIFT; + pblk_dev->blk.type = BLK_TYPE_USB; result = blockdevice_register(&pblk_dev->blk); if (result != 0) { @@ -356,11 +429,6 @@ static int usb_stor_add_blkdev(struct us_data *us, unsigned char lun) goto BadDevice; } - /* create partitions on demand */ - result = parse_partition_table(&pblk_dev->blk); - if (result != 0) - dev_warn(dev, "No partition table found\n"); - list_add_tail(&pblk_dev->list, &us->blk_dev_list); dev_dbg(dev, "USB disk device successfully added\n"); @@ -379,7 +447,7 @@ BadDevice: /* Get the transport settings */ static void get_transport(struct us_data *us) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; switch (us->protocol) { case US_PR_BULK: us->transport_name = "Bulk"; @@ -394,7 +462,7 @@ static void get_transport(struct us_data *us) /* Get the endpoint settings */ static int get_pipes(struct us_data *us, struct usb_interface *intf) { - struct device_d *dev = &us->pusb_dev->dev; + struct device *dev = &us->pusb_dev->dev; unsigned int i; struct usb_endpoint_descriptor *ep; struct usb_endpoint_descriptor *ep_in = NULL; @@ -435,7 +503,7 @@ static int get_pipes(struct us_data *us, struct usb_interface *intf) /* Scan device's LUNs, registering a disk device for each LUN */ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; unsigned char lun; int num_devs = 0; @@ -459,7 +527,7 @@ static int usb_stor_scan(struct usb_device *usbdev, struct us_data *us) static int usb_stor_probe(struct usb_device *usbdev, const struct usb_device_id *id) { - struct device_d *dev = &usbdev->dev; + struct device *dev = &usbdev->dev; struct us_data *us; int result; int ifno; |