1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2011 Samsung Electronics 4 * Lukasz Majewski <l.majewski@samsung.com> 5 * 6 * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 7 */ 8 9#include <common.h> 10#include <blk.h> 11#include <command.h> 12#include <console.h> 13#include <errno.h> 14#include <g_dnl.h> 15#include <malloc.h> 16#include <part.h> 17#include <usb.h> 18#include <usb_mass_storage.h> 19#include <watchdog.h> 20#include <linux/delay.h> 21#include <linux/printk.h> 22 23static int ums_read_sector(struct ums *ums_dev, 24 ulong start, lbaint_t blkcnt, void *buf) 25{ 26 struct blk_desc *block_dev = &ums_dev->block_dev; 27 lbaint_t blkstart = start + ums_dev->start_sector; 28 29 return blk_dread(block_dev, blkstart, blkcnt, buf); 30} 31 32static int ums_write_sector(struct ums *ums_dev, 33 ulong start, lbaint_t blkcnt, const void *buf) 34{ 35 struct blk_desc *block_dev = &ums_dev->block_dev; 36 lbaint_t blkstart = start + ums_dev->start_sector; 37 38 return blk_dwrite(block_dev, blkstart, blkcnt, buf); 39} 40 41static struct ums *ums; 42static int ums_count; 43 44static void ums_fini(void) 45{ 46 int i; 47 48 for (i = 0; i < ums_count; i++) 49 free((void *)ums[i].name); 50 free(ums); 51 ums = NULL; 52 ums_count = 0; 53} 54 55#define UMS_NAME_LEN 16 56 57static int ums_init(const char *devtype, const char *devnums_part_str) 58{ 59 char *s, *t, *devnum_part_str, *name; 60 struct blk_desc *block_dev; 61 struct disk_partition info; 62 int partnum; 63 int ret = -1; 64 struct ums *ums_new; 65 66 s = strdup(devnums_part_str); 67 if (!s) 68 return -1; 69 70 t = s; 71 ums_count = 0; 72 73 for (;;) { 74 devnum_part_str = strsep(&t, ","); 75 if (!devnum_part_str) 76 break; 77 78 partnum = part_get_info_by_dev_and_name_or_num(devtype, devnum_part_str, 79 &block_dev, &info, 1); 80 81 if (partnum < 0) 82 goto cleanup; 83 84 /* Check if the argument is in legacy format. If yes, 85 * expose all partitions by setting the partnum = 0 86 * e.g. ums 0 mmc 0 87 */ 88 if (!strchr(devnum_part_str, ':')) 89 partnum = 0; 90 91 ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums)); 92 if (!ums_new) 93 goto cleanup; 94 ums = ums_new; 95 96 /* if partnum = 0, expose all partitions */ 97 if (partnum == 0) { 98 ums[ums_count].start_sector = 0; 99 ums[ums_count].num_sectors = block_dev->lba; 100 } else { 101 ums[ums_count].start_sector = info.start; 102 ums[ums_count].num_sectors = info.size; 103 } 104 105 ums[ums_count].read_sector = ums_read_sector; 106 ums[ums_count].write_sector = ums_write_sector; 107 108 name = malloc(UMS_NAME_LEN); 109 if (!name) 110 goto cleanup; 111 snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count); 112 ums[ums_count].name = name; 113 ums[ums_count].block_dev = *block_dev; 114 115 printf("UMS: LUN %d, dev %s %d, hwpart %d, sector %#x, count %#x\n", 116 ums_count, devtype, ums[ums_count].block_dev.devnum, 117 ums[ums_count].block_dev.hwpart, 118 ums[ums_count].start_sector, 119 ums[ums_count].num_sectors); 120 121 ums_count++; 122 } 123 124 if (ums_count) 125 ret = 0; 126 127cleanup: 128 free(s); 129 130 if (ret < 0) 131 ums_fini(); 132 133 return ret; 134} 135 136static int do_usb_mass_storage(struct cmd_tbl *cmdtp, int flag, 137 int argc, char *const argv[]) 138{ 139 const char *usb_controller; 140 const char *devtype; 141 const char *devnum; 142 unsigned int controller_index; 143 struct udevice *udc; 144 int rc; 145 int cable_ready_timeout __maybe_unused; 146 147 if (argc < 3) 148 return CMD_RET_USAGE; 149 150 usb_controller = argv[1]; 151 if (argc >= 4) { 152 devtype = argv[2]; 153 devnum = argv[3]; 154 } else { 155 devtype = "mmc"; 156 devnum = argv[2]; 157 } 158 159 rc = ums_init(devtype, devnum); 160 if (rc < 0) 161 return CMD_RET_FAILURE; 162 163 controller_index = (unsigned int)(simple_strtoul( 164 usb_controller, NULL, 0)); 165 rc = udc_device_get_by_index(controller_index, &udc); 166 if (rc) { 167 pr_err("Couldn't init USB controller.\n"); 168 rc = CMD_RET_FAILURE; 169 goto cleanup_ums_init; 170 } 171 172 rc = fsg_init(ums, ums_count, udc); 173 if (rc) { 174 pr_err("fsg_init failed\n"); 175 rc = CMD_RET_FAILURE; 176 goto cleanup_board; 177 } 178 179 rc = g_dnl_register("usb_dnl_ums"); 180 if (rc) { 181 pr_err("g_dnl_register failed\n"); 182 rc = CMD_RET_FAILURE; 183 goto cleanup_board; 184 } 185 186 /* Timeout unit: seconds */ 187 cable_ready_timeout = UMS_CABLE_READY_TIMEOUT; 188 189 if (!g_dnl_board_usb_cable_connected()) { 190 /* 191 * Won't execute if we don't know whether the cable is 192 * connected. 193 */ 194 puts("Please connect USB cable.\n"); 195 196 while (!g_dnl_board_usb_cable_connected()) { 197 if (ctrlc()) { 198 puts("\rCTRL+C - Operation aborted.\n"); 199 rc = CMD_RET_SUCCESS; 200 goto cleanup_register; 201 } 202 if (!cable_ready_timeout) { 203 puts("\rUSB cable not detected.\n" \ 204 "Command exit.\n"); 205 rc = CMD_RET_SUCCESS; 206 goto cleanup_register; 207 } 208 209 printf("\rAuto exit in: %.2d s.", cable_ready_timeout); 210 mdelay(1000); 211 cable_ready_timeout--; 212 } 213 puts("\r\n"); 214 } 215 216 while (1) { 217 dm_usb_gadget_handle_interrupts(udc); 218 219 rc = fsg_main_thread(NULL); 220 if (rc) { 221 /* Check I/O error */ 222 if (rc == -EIO) 223 printf("\rCheck USB cable connection\n"); 224 225 /* Check CTRL+C */ 226 if (rc == -EPIPE) 227 printf("\rCTRL+C - Operation aborted\n"); 228 229 rc = CMD_RET_SUCCESS; 230 goto cleanup_register; 231 } 232 233 if (IS_ENABLED(CONFIG_CMD_UMS_ABORT_KEYED)) { 234 /* Abort by pressing any key */ 235 if (tstc()) { 236 getchar(); 237 printf("\rOperation aborted.\n"); 238 rc = CMD_RET_SUCCESS; 239 goto cleanup_register; 240 } 241 } 242 243 schedule(); 244 } 245 246cleanup_register: 247 g_dnl_unregister(); 248cleanup_board: 249 udc_device_put(udc); 250cleanup_ums_init: 251 ums_fini(); 252 253 return rc; 254} 255 256U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage, 257 "Use the UMS [USB Mass Storage]", 258 "<USB_controller> [<devtype>] <dev[:part]> e.g. ums 0 mmc 0\n" 259 " devtype defaults to mmc" 260); 261