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