command: Remove the cmd_tbl_t typedef
[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
21 static int ums_read_sector(struct ums *ums_dev,
22                            ulong start, lbaint_t blkcnt, void *buf)
23 {
24         struct blk_desc *block_dev = &ums_dev->block_dev;
25         lbaint_t blkstart = start + ums_dev->start_sector;
26
27         return blk_dread(block_dev, blkstart, blkcnt, buf);
28 }
29
30 static int ums_write_sector(struct ums *ums_dev,
31                             ulong start, lbaint_t blkcnt, const void *buf)
32 {
33         struct blk_desc *block_dev = &ums_dev->block_dev;
34         lbaint_t blkstart = start + ums_dev->start_sector;
35
36         return blk_dwrite(block_dev, blkstart, blkcnt, buf);
37 }
38
39 static struct ums *ums;
40 static int ums_count;
41
42 static void ums_fini(void)
43 {
44         int i;
45
46         for (i = 0; i < ums_count; i++)
47                 free((void *)ums[i].name);
48         free(ums);
49         ums = NULL;
50         ums_count = 0;
51 }
52
53 #define UMS_NAME_LEN 16
54
55 static int ums_init(const char *devtype, const char *devnums_part_str)
56 {
57         char *s, *t, *devnum_part_str, *name;
58         struct blk_desc *block_dev;
59         struct disk_partition info;
60         int partnum;
61         int ret = -1;
62         struct ums *ums_new;
63
64         s = strdup(devnums_part_str);
65         if (!s)
66                 return -1;
67
68         t = s;
69         ums_count = 0;
70
71         for (;;) {
72                 devnum_part_str = strsep(&t, ",");
73                 if (!devnum_part_str)
74                         break;
75
76                 partnum = blk_get_device_part_str(devtype, devnum_part_str,
77                                         &block_dev, &info, 1);
78
79                 if (partnum < 0)
80                         goto cleanup;
81
82                 /* Check if the argument is in legacy format. If yes,
83                  * expose all partitions by setting the partnum = 0
84                  * e.g. ums 0 mmc 0
85                  */
86                 if (!strchr(devnum_part_str, ':'))
87                         partnum = 0;
88
89                 /* f_mass_storage.c assumes SECTOR_SIZE sectors */
90                 if (block_dev->blksz != SECTOR_SIZE)
91                         goto cleanup;
92
93                 ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums));
94                 if (!ums_new)
95                         goto cleanup;
96                 ums = ums_new;
97
98                 /* if partnum = 0, expose all partitions */
99                 if (partnum == 0) {
100                         ums[ums_count].start_sector = 0;
101                         ums[ums_count].num_sectors = block_dev->lba;
102                 } else {
103                         ums[ums_count].start_sector = info.start;
104                         ums[ums_count].num_sectors = info.size;
105                 }
106
107                 ums[ums_count].read_sector = ums_read_sector;
108                 ums[ums_count].write_sector = ums_write_sector;
109
110                 name = malloc(UMS_NAME_LEN);
111                 if (!name)
112                         goto cleanup;
113                 snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count);
114                 ums[ums_count].name = name;
115                 ums[ums_count].block_dev = *block_dev;
116
117                 printf("UMS: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
118                        ums_count, ums[ums_count].block_dev.devnum,
119                        ums[ums_count].block_dev.hwpart,
120                        ums[ums_count].start_sector,
121                        ums[ums_count].num_sectors);
122
123                 ums_count++;
124         }
125
126         if (ums_count)
127                 ret = 0;
128
129 cleanup:
130         free(s);
131
132         if (ret < 0)
133                 ums_fini();
134
135         return ret;
136 }
137
138 static int do_usb_mass_storage(struct cmd_tbl *cmdtp, int flag,
139                                int argc, char *const argv[])
140 {
141         const char *usb_controller;
142         const char *devtype;
143         const char *devnum;
144         unsigned int controller_index;
145         int rc;
146         int cable_ready_timeout __maybe_unused;
147
148         if (argc < 3)
149                 return CMD_RET_USAGE;
150
151         usb_controller = argv[1];
152         if (argc >= 4) {
153                 devtype = argv[2];
154                 devnum  = argv[3];
155         } else {
156                 devtype = "mmc";
157                 devnum  = argv[2];
158         }
159
160         rc = ums_init(devtype, devnum);
161         if (rc < 0)
162                 return CMD_RET_FAILURE;
163
164         controller_index = (unsigned int)(simple_strtoul(
165                                 usb_controller, NULL, 0));
166         if (usb_gadget_initialize(controller_index)) {
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);
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                 usb_gadget_handle_interrupts(controller_index);
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                 WATCHDOG_RESET();
234         }
235
236 cleanup_register:
237         g_dnl_unregister();
238 cleanup_board:
239         usb_gadget_release(controller_index);
240 cleanup_ums_init:
241         ums_fini();
242
243         return rc;
244 }
245
246 U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage,
247         "Use the UMS [USB Mass Storage]",
248         "<USB_controller> [<devtype>] <dev[:part]>  e.g. ums 0 mmc 0\n"
249         "    devtype defaults to mmc"
250 );