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