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