summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/cmd_usb.c24
-rw-r--r--common/usb_storage.c99
2 files changed, 123 insertions, 0 deletions
diff --git a/common/cmd_usb.c b/common/cmd_usb.c
index 1e297d5..9de515c 100644
--- a/common/cmd_usb.c
+++ b/common/cmd_usb.c
@@ -642,6 +642,28 @@ int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
return 1;
}
}
+ if (strcmp(argv[1], "write") == 0) {
+ if (usb_stor_curr_dev < 0) {
+ printf("no current device selected\n");
+ return 1;
+ }
+ if (argc == 5) {
+ unsigned long addr = simple_strtoul(argv[2], NULL, 16);
+ unsigned long blk = simple_strtoul(argv[3], NULL, 16);
+ unsigned long cnt = simple_strtoul(argv[4], NULL, 16);
+ unsigned long n;
+ printf("\nUSB write: device %d block # %ld, count %ld"
+ " ... ", usb_stor_curr_dev, blk, cnt);
+ stor_dev = usb_stor_get_dev(usb_stor_curr_dev);
+ n = stor_dev->block_write(usb_stor_curr_dev, blk, cnt,
+ (ulong *)addr);
+ printf("%ld blocks write: %s\n", n,
+ (n == cnt) ? "OK" : "ERROR");
+ if (n == cnt)
+ return 0;
+ return 1;
+ }
+ }
if (strncmp(argv[1], "dev", 3) == 0) {
if (argc == 3) {
int dev = (int)simple_strtoul(argv[2], NULL, 10);
@@ -687,6 +709,8 @@ U_BOOT_CMD(
" devices\n"
"usb read addr blk# cnt - read `cnt' blocks starting at block `blk#'\n"
" to memory address `addr'"
+ "usb write addr blk# cnt - write `cnt' blocks starting at block `blk#'\n"
+ " from memory address `addr'"
);
diff --git a/common/usb_storage.c b/common/usb_storage.c
index 391948b..a8642c9 100644
--- a/common/usb_storage.c
+++ b/common/usb_storage.c
@@ -168,6 +168,8 @@ int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,
struct us_data *ss);
unsigned long usb_stor_read(int device, unsigned long blknr,
unsigned long blkcnt, void *buffer);
+unsigned long usb_stor_write(int device, unsigned long blknr,
+ unsigned long blkcnt, const void *buffer);
struct usb_device * usb_get_dev_index(int index);
void uhci_show_temp_int_td(void);
@@ -227,6 +229,7 @@ int usb_stor_scan(int mode)
usb_dev_desc[i].dev = i;
usb_dev_desc[i].part_type = PART_TYPE_UNKNOWN;
usb_dev_desc[i].block_read = usb_stor_read;
+ usb_dev_desc[i].block_write = usb_stor_write;
}
usb_max_devs = 0;
@@ -964,6 +967,22 @@ static int usb_read_10(ccb *srb, struct us_data *ss, unsigned long start,
return ss->transport(srb, ss);
}
+static int usb_write_10(ccb *srb, struct us_data *ss, unsigned long start,
+ unsigned short blocks)
+{
+ memset(&srb->cmd[0], 0, 12);
+ srb->cmd[0] = SCSI_WRITE10;
+ srb->cmd[2] = ((unsigned char) (start >> 24)) & 0xff;
+ srb->cmd[3] = ((unsigned char) (start >> 16)) & 0xff;
+ srb->cmd[4] = ((unsigned char) (start >> 8)) & 0xff;
+ srb->cmd[5] = ((unsigned char) (start)) & 0xff;
+ srb->cmd[7] = ((unsigned char) (blocks >> 8)) & 0xff;
+ srb->cmd[8] = (unsigned char) blocks & 0xff;
+ srb->cmdlen = 12;
+ USB_STOR_PRINTF("write10: start %lx blocks %x\n", start, blocks);
+ return ss->transport(srb, ss);
+}
+
#ifdef CONFIG_USB_BIN_FIXUP
/*
@@ -1065,6 +1084,86 @@ retry_it:
return blkcnt;
}
+#define USB_MAX_WRITE_BLK 20
+
+unsigned long usb_stor_write(int device, unsigned long blknr,
+ unsigned long blkcnt, const void *buffer)
+{
+ unsigned long start, blks, buf_addr;
+ unsigned short smallblks;
+ struct usb_device *dev;
+ int retry, i;
+ ccb *srb = &usb_ccb;
+
+ if (blkcnt == 0)
+ return 0;
+
+ device &= 0xff;
+ /* Setup device */
+ USB_STOR_PRINTF("\nusb_write: dev %d \n", device);
+ dev = NULL;
+ for (i = 0; i < USB_MAX_DEVICE; i++) {
+ dev = usb_get_dev_index(i);
+ if (dev == NULL)
+ return 0;
+ if (dev->devnum == usb_dev_desc[device].target)
+ break;
+ }
+
+ usb_disable_asynch(1); /* asynch transfer not allowed */
+
+ srb->lun = usb_dev_desc[device].lun;
+ buf_addr = (unsigned long)buffer;
+ start = blknr;
+ blks = blkcnt;
+ if (usb_test_unit_ready(srb, (struct us_data *)dev->privptr)) {
+ printf("Device NOT ready\n Request Sense returned %02X %02X"
+ " %02X\n", srb->sense_buf[2], srb->sense_buf[12],
+ srb->sense_buf[13]);
+ return 0;
+ }
+
+ USB_STOR_PRINTF("\nusb_write: dev %d startblk %lx, blccnt %lx"
+ " buffer %lx\n", device, start, blks, buf_addr);
+
+ do {
+ /* If write fails retry for max retry count else
+ * return with number of blocks written successfully.
+ */
+ retry = 2;
+ srb->pdata = (unsigned char *)buf_addr;
+ if (blks > USB_MAX_WRITE_BLK)
+ smallblks = USB_MAX_WRITE_BLK;
+ else
+ smallblks = (unsigned short) blks;
+retry_it:
+ if (smallblks == USB_MAX_WRITE_BLK)
+ usb_show_progress();
+ srb->datalen = usb_dev_desc[device].blksz * smallblks;
+ srb->pdata = (unsigned char *)buf_addr;
+ if (usb_write_10(srb, (struct us_data *)dev->privptr, start,
+ smallblks)) {
+ USB_STOR_PRINTF("Write ERROR\n");
+ usb_request_sense(srb, (struct us_data *)dev->privptr);
+ if (retry--)
+ goto retry_it;
+ blkcnt -= blks;
+ break;
+ }
+ start += smallblks;
+ blks -= smallblks;
+ buf_addr += srb->datalen;
+ } while (blks != 0);
+
+ USB_STOR_PRINTF("usb_write: end startblk %lx, blccnt %x buffer %lx\n",
+ start, smallblks, buf_addr);
+
+ usb_disable_asynch(0); /* asynch transfer allowed */
+ if (blkcnt >= USB_MAX_WRITE_BLK)
+ printf("\n");
+ return blkcnt;
+
+}
/* Probe to see if a new device is actually a Storage device */
int usb_storage_probe(struct usb_device *dev, unsigned int ifnum,