12e192b24SSimon Glass /* 22e192b24SSimon Glass * Copyright (C) 2011 Samsung Electronics 32e192b24SSimon Glass * Lukasz Majewski <l.majewski@samsung.com> 42e192b24SSimon Glass * 52e192b24SSimon Glass * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 62e192b24SSimon Glass * 72e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 82e192b24SSimon Glass */ 92e192b24SSimon Glass 102e192b24SSimon Glass #include <errno.h> 112e192b24SSimon Glass #include <common.h> 122e192b24SSimon Glass #include <command.h> 132e192b24SSimon Glass #include <console.h> 142e192b24SSimon Glass #include <g_dnl.h> 152e192b24SSimon Glass #include <part.h> 162e192b24SSimon Glass #include <usb.h> 172e192b24SSimon Glass #include <usb_mass_storage.h> 182e192b24SSimon Glass 192e192b24SSimon Glass static int ums_read_sector(struct ums *ums_dev, 202e192b24SSimon Glass ulong start, lbaint_t blkcnt, void *buf) 212e192b24SSimon Glass { 224101f687SSimon Glass struct blk_desc *block_dev = &ums_dev->block_dev; 232e192b24SSimon Glass lbaint_t blkstart = start + ums_dev->start_sector; 242e192b24SSimon Glass 252e192b24SSimon Glass return block_dev->block_read(block_dev, blkstart, blkcnt, buf); 262e192b24SSimon Glass } 272e192b24SSimon Glass 282e192b24SSimon Glass static int ums_write_sector(struct ums *ums_dev, 292e192b24SSimon Glass ulong start, lbaint_t blkcnt, const void *buf) 302e192b24SSimon Glass { 314101f687SSimon Glass struct blk_desc *block_dev = &ums_dev->block_dev; 322e192b24SSimon Glass lbaint_t blkstart = start + ums_dev->start_sector; 332e192b24SSimon Glass 342e192b24SSimon Glass return block_dev->block_write(block_dev, blkstart, blkcnt, buf); 352e192b24SSimon Glass } 362e192b24SSimon Glass 372e192b24SSimon Glass static struct ums *ums; 382e192b24SSimon Glass static int ums_count; 392e192b24SSimon Glass 402e192b24SSimon Glass static void ums_fini(void) 412e192b24SSimon Glass { 422e192b24SSimon Glass int i; 432e192b24SSimon Glass 442e192b24SSimon Glass for (i = 0; i < ums_count; i++) 452e192b24SSimon Glass free((void *)ums[i].name); 462e192b24SSimon Glass free(ums); 472e192b24SSimon Glass ums = 0; 482e192b24SSimon Glass ums_count = 0; 492e192b24SSimon Glass } 502e192b24SSimon Glass 512e192b24SSimon Glass #define UMS_NAME_LEN 16 522e192b24SSimon Glass 53*a2e3a1d8SJohn Tobias static int ums_init(const char *devtype, const char *devnums_part_str) 542e192b24SSimon Glass { 55*a2e3a1d8SJohn Tobias char *s, *t, *devnum_part_str, *name; 564101f687SSimon Glass struct blk_desc *block_dev; 57*a2e3a1d8SJohn Tobias disk_partition_t info; 58*a2e3a1d8SJohn Tobias int partnum; 592e192b24SSimon Glass int ret; 602e192b24SSimon Glass struct ums *ums_new; 612e192b24SSimon Glass 62*a2e3a1d8SJohn Tobias s = strdup(devnums_part_str); 632e192b24SSimon Glass if (!s) 642e192b24SSimon Glass return -1; 652e192b24SSimon Glass 662e192b24SSimon Glass t = s; 672e192b24SSimon Glass ums_count = 0; 682e192b24SSimon Glass 692e192b24SSimon Glass for (;;) { 70*a2e3a1d8SJohn Tobias devnum_part_str = strsep(&t, ","); 71*a2e3a1d8SJohn Tobias if (!devnum_part_str) 722e192b24SSimon Glass break; 732e192b24SSimon Glass 74*a2e3a1d8SJohn Tobias partnum = blk_get_device_part_str(devtype, devnum_part_str, 75*a2e3a1d8SJohn Tobias &block_dev, &info, 1); 76*a2e3a1d8SJohn Tobias 77*a2e3a1d8SJohn Tobias if (partnum < 0) 782e192b24SSimon Glass goto cleanup; 792e192b24SSimon Glass 80*a2e3a1d8SJohn Tobias /* Check if the argument is in legacy format. If yes, 81*a2e3a1d8SJohn Tobias * expose all partitions by setting the partnum = 0 82*a2e3a1d8SJohn Tobias * e.g. ums 0 mmc 0 83*a2e3a1d8SJohn Tobias */ 84*a2e3a1d8SJohn Tobias if (!strchr(devnum_part_str, ':')) 85*a2e3a1d8SJohn Tobias partnum = 0; 86*a2e3a1d8SJohn Tobias 872e192b24SSimon Glass /* f_mass_storage.c assumes SECTOR_SIZE sectors */ 882e192b24SSimon Glass if (block_dev->blksz != SECTOR_SIZE) { 892e192b24SSimon Glass ret = -1; 902e192b24SSimon Glass goto cleanup; 912e192b24SSimon Glass } 922e192b24SSimon Glass 932e192b24SSimon Glass ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums)); 942e192b24SSimon Glass if (!ums_new) { 952e192b24SSimon Glass ret = -1; 962e192b24SSimon Glass goto cleanup; 972e192b24SSimon Glass } 982e192b24SSimon Glass ums = ums_new; 992e192b24SSimon Glass 100*a2e3a1d8SJohn Tobias /* if partnum = 0, expose all partitions */ 101*a2e3a1d8SJohn Tobias if (partnum == 0) { 1022e192b24SSimon Glass ums[ums_count].start_sector = 0; 1032e192b24SSimon Glass ums[ums_count].num_sectors = block_dev->lba; 104*a2e3a1d8SJohn Tobias } else { 105*a2e3a1d8SJohn Tobias ums[ums_count].start_sector = info.start; 106*a2e3a1d8SJohn Tobias ums[ums_count].num_sectors = info.size; 107*a2e3a1d8SJohn Tobias } 108*a2e3a1d8SJohn Tobias 109*a2e3a1d8SJohn Tobias ums[ums_count].read_sector = ums_read_sector; 110*a2e3a1d8SJohn Tobias ums[ums_count].write_sector = ums_write_sector; 111*a2e3a1d8SJohn Tobias 1122e192b24SSimon Glass name = malloc(UMS_NAME_LEN); 1132e192b24SSimon Glass if (!name) { 1142e192b24SSimon Glass ret = -1; 1152e192b24SSimon Glass goto cleanup; 1162e192b24SSimon Glass } 1172e192b24SSimon Glass snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count); 1182e192b24SSimon Glass ums[ums_count].name = name; 1192e192b24SSimon Glass ums[ums_count].block_dev = *block_dev; 1202e192b24SSimon Glass 1212e192b24SSimon Glass printf("UMS: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n", 122bcce53d0SSimon Glass ums_count, ums[ums_count].block_dev.devnum, 1232e192b24SSimon Glass ums[ums_count].block_dev.hwpart, 1242e192b24SSimon Glass ums[ums_count].start_sector, 1252e192b24SSimon Glass ums[ums_count].num_sectors); 1262e192b24SSimon Glass 1272e192b24SSimon Glass ums_count++; 1282e192b24SSimon Glass } 1292e192b24SSimon Glass 1302e192b24SSimon Glass if (!ums_count) 1312e192b24SSimon Glass ret = -1; 1322e192b24SSimon Glass else 1332e192b24SSimon Glass ret = 0; 1342e192b24SSimon Glass 1352e192b24SSimon Glass cleanup: 1362e192b24SSimon Glass free(s); 1372e192b24SSimon Glass 1382e192b24SSimon Glass if (ret < 0) 1392e192b24SSimon Glass ums_fini(); 1402e192b24SSimon Glass 1412e192b24SSimon Glass return ret; 1422e192b24SSimon Glass } 1432e192b24SSimon Glass 1442e192b24SSimon Glass int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag, 1452e192b24SSimon Glass int argc, char * const argv[]) 1462e192b24SSimon Glass { 1472e192b24SSimon Glass const char *usb_controller; 1482e192b24SSimon Glass const char *devtype; 1492e192b24SSimon Glass const char *devnum; 1502e192b24SSimon Glass unsigned int controller_index; 1512e192b24SSimon Glass int rc; 1522e192b24SSimon Glass int cable_ready_timeout __maybe_unused; 1532e192b24SSimon Glass 1542e192b24SSimon Glass if (argc < 3) 1552e192b24SSimon Glass return CMD_RET_USAGE; 1562e192b24SSimon Glass 1572e192b24SSimon Glass usb_controller = argv[1]; 1582e192b24SSimon Glass if (argc >= 4) { 1592e192b24SSimon Glass devtype = argv[2]; 1602e192b24SSimon Glass devnum = argv[3]; 1612e192b24SSimon Glass } else { 1622e192b24SSimon Glass devtype = "mmc"; 1632e192b24SSimon Glass devnum = argv[2]; 1642e192b24SSimon Glass } 1652e192b24SSimon Glass 1662e192b24SSimon Glass rc = ums_init(devtype, devnum); 1672e192b24SSimon Glass if (rc < 0) 1682e192b24SSimon Glass return CMD_RET_FAILURE; 1692e192b24SSimon Glass 1702e192b24SSimon Glass controller_index = (unsigned int)(simple_strtoul( 1712e192b24SSimon Glass usb_controller, NULL, 0)); 1722e192b24SSimon Glass if (board_usb_init(controller_index, USB_INIT_DEVICE)) { 1732e192b24SSimon Glass error("Couldn't init USB controller."); 1742e192b24SSimon Glass rc = CMD_RET_FAILURE; 1752e192b24SSimon Glass goto cleanup_ums_init; 1762e192b24SSimon Glass } 1772e192b24SSimon Glass 1782e192b24SSimon Glass rc = fsg_init(ums, ums_count); 1792e192b24SSimon Glass if (rc) { 1802e192b24SSimon Glass error("fsg_init failed"); 1812e192b24SSimon Glass rc = CMD_RET_FAILURE; 1822e192b24SSimon Glass goto cleanup_board; 1832e192b24SSimon Glass } 1842e192b24SSimon Glass 1852e192b24SSimon Glass rc = g_dnl_register("usb_dnl_ums"); 1862e192b24SSimon Glass if (rc) { 1872e192b24SSimon Glass error("g_dnl_register failed"); 1882e192b24SSimon Glass rc = CMD_RET_FAILURE; 1892e192b24SSimon Glass goto cleanup_board; 1902e192b24SSimon Glass } 1912e192b24SSimon Glass 1922e192b24SSimon Glass /* Timeout unit: seconds */ 1932e192b24SSimon Glass cable_ready_timeout = UMS_CABLE_READY_TIMEOUT; 1942e192b24SSimon Glass 1952e192b24SSimon Glass if (!g_dnl_board_usb_cable_connected()) { 1962e192b24SSimon Glass /* 1972e192b24SSimon Glass * Won't execute if we don't know whether the cable is 1982e192b24SSimon Glass * connected. 1992e192b24SSimon Glass */ 2002e192b24SSimon Glass puts("Please connect USB cable.\n"); 2012e192b24SSimon Glass 2022e192b24SSimon Glass while (!g_dnl_board_usb_cable_connected()) { 2032e192b24SSimon Glass if (ctrlc()) { 2042e192b24SSimon Glass puts("\rCTRL+C - Operation aborted.\n"); 2052e192b24SSimon Glass rc = CMD_RET_SUCCESS; 2062e192b24SSimon Glass goto cleanup_register; 2072e192b24SSimon Glass } 2082e192b24SSimon Glass if (!cable_ready_timeout) { 2092e192b24SSimon Glass puts("\rUSB cable not detected.\n" \ 2102e192b24SSimon Glass "Command exit.\n"); 2112e192b24SSimon Glass rc = CMD_RET_SUCCESS; 2122e192b24SSimon Glass goto cleanup_register; 2132e192b24SSimon Glass } 2142e192b24SSimon Glass 2152e192b24SSimon Glass printf("\rAuto exit in: %.2d s.", cable_ready_timeout); 2162e192b24SSimon Glass mdelay(1000); 2172e192b24SSimon Glass cable_ready_timeout--; 2182e192b24SSimon Glass } 2192e192b24SSimon Glass puts("\r\n"); 2202e192b24SSimon Glass } 2212e192b24SSimon Glass 2222e192b24SSimon Glass while (1) { 2232e192b24SSimon Glass usb_gadget_handle_interrupts(controller_index); 2242e192b24SSimon Glass 2252e192b24SSimon Glass rc = fsg_main_thread(NULL); 2262e192b24SSimon Glass if (rc) { 2272e192b24SSimon Glass /* Check I/O error */ 2282e192b24SSimon Glass if (rc == -EIO) 2292e192b24SSimon Glass printf("\rCheck USB cable connection\n"); 2302e192b24SSimon Glass 2312e192b24SSimon Glass /* Check CTRL+C */ 2322e192b24SSimon Glass if (rc == -EPIPE) 2332e192b24SSimon Glass printf("\rCTRL+C - Operation aborted\n"); 2342e192b24SSimon Glass 2352e192b24SSimon Glass rc = CMD_RET_SUCCESS; 2362e192b24SSimon Glass goto cleanup_register; 2372e192b24SSimon Glass } 2382e192b24SSimon Glass } 2392e192b24SSimon Glass 2402e192b24SSimon Glass cleanup_register: 2412e192b24SSimon Glass g_dnl_unregister(); 2422e192b24SSimon Glass cleanup_board: 2432e192b24SSimon Glass board_usb_cleanup(controller_index, USB_INIT_DEVICE); 2442e192b24SSimon Glass cleanup_ums_init: 2452e192b24SSimon Glass ums_fini(); 2462e192b24SSimon Glass 2472e192b24SSimon Glass return rc; 2482e192b24SSimon Glass } 2492e192b24SSimon Glass 2502e192b24SSimon Glass U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage, 2512e192b24SSimon Glass "Use the UMS [USB Mass Storage]", 252*a2e3a1d8SJohn Tobias "<USB_controller> [<devtype>] <dev[:part]> e.g. ums 0 mmc 0\n" 2532e192b24SSimon Glass " devtype defaults to mmc" 2542e192b24SSimon Glass ); 255