summaryrefslogtreecommitdiffstats
path: root/drivers/usb/storage/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/storage/usb.c')
-rw-r--r--drivers/usb/storage/usb.c230
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;