Merge branch 'master' of git://git.denx.de/u-boot-video
[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 block_dev->block_read(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 block_dev->block_write(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)
54 {
55         char *s, *t, *devnum, *name;
56         struct blk_desc *block_dev;
57         int ret;
58         struct ums *ums_new;
59
60         s = strdup(devnums);
61         if (!s)
62                 return -1;
63
64         t = s;
65         ums_count = 0;
66
67         for (;;) {
68                 devnum = strsep(&t, ",");
69                 if (!devnum)
70                         break;
71
72                 ret = blk_get_device_by_str(devtype, devnum, &block_dev);
73                 if (ret < 0)
74                         goto cleanup;
75
76                 /* f_mass_storage.c assumes SECTOR_SIZE sectors */
77                 if (block_dev->blksz != SECTOR_SIZE) {
78                         ret = -1;
79                         goto cleanup;
80                 }
81
82                 ums_new = realloc(ums, (ums_count + 1) * sizeof(*ums));
83                 if (!ums_new) {
84                         ret = -1;
85                         goto cleanup;
86                 }
87                 ums = ums_new;
88
89                 ums[ums_count].read_sector = ums_read_sector;
90                 ums[ums_count].write_sector = ums_write_sector;
91                 ums[ums_count].start_sector = 0;
92                 ums[ums_count].num_sectors = block_dev->lba;
93                 name = malloc(UMS_NAME_LEN);
94                 if (!name) {
95                         ret = -1;
96                         goto cleanup;
97                 }
98                 snprintf(name, UMS_NAME_LEN, "UMS disk %d", ums_count);
99                 ums[ums_count].name = name;
100                 ums[ums_count].block_dev = *block_dev;
101
102                 printf("UMS: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
103                        ums_count, ums[ums_count].block_dev.devnum,
104                        ums[ums_count].block_dev.hwpart,
105                        ums[ums_count].start_sector,
106                        ums[ums_count].num_sectors);
107
108                 ums_count++;
109         }
110
111         if (!ums_count)
112                 ret = -1;
113         else
114                 ret = 0;
115
116 cleanup:
117         free(s);
118
119         if (ret < 0)
120                 ums_fini();
121
122         return ret;
123 }
124
125 int do_usb_mass_storage(cmd_tbl_t *cmdtp, int flag,
126                                int argc, char * const argv[])
127 {
128         const char *usb_controller;
129         const char *devtype;
130         const char *devnum;
131         unsigned int controller_index;
132         int rc;
133         int cable_ready_timeout __maybe_unused;
134
135         if (argc < 3)
136                 return CMD_RET_USAGE;
137
138         usb_controller = argv[1];
139         if (argc >= 4) {
140                 devtype = argv[2];
141                 devnum  = argv[3];
142         } else {
143                 devtype = "mmc";
144                 devnum  = argv[2];
145         }
146
147         rc = ums_init(devtype, devnum);
148         if (rc < 0)
149                 return CMD_RET_FAILURE;
150
151         controller_index = (unsigned int)(simple_strtoul(
152                                 usb_controller, NULL, 0));
153         if (board_usb_init(controller_index, USB_INIT_DEVICE)) {
154                 error("Couldn't init USB controller.");
155                 rc = CMD_RET_FAILURE;
156                 goto cleanup_ums_init;
157         }
158
159         rc = fsg_init(ums, ums_count);
160         if (rc) {
161                 error("fsg_init failed");
162                 rc = CMD_RET_FAILURE;
163                 goto cleanup_board;
164         }
165
166         rc = g_dnl_register("usb_dnl_ums");
167         if (rc) {
168                 error("g_dnl_register failed");
169                 rc = CMD_RET_FAILURE;
170                 goto cleanup_board;
171         }
172
173         /* Timeout unit: seconds */
174         cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
175
176         if (!g_dnl_board_usb_cable_connected()) {
177                 /*
178                  * Won't execute if we don't know whether the cable is
179                  * connected.
180                  */
181                 puts("Please connect USB cable.\n");
182
183                 while (!g_dnl_board_usb_cable_connected()) {
184                         if (ctrlc()) {
185                                 puts("\rCTRL+C - Operation aborted.\n");
186                                 rc = CMD_RET_SUCCESS;
187                                 goto cleanup_register;
188                         }
189                         if (!cable_ready_timeout) {
190                                 puts("\rUSB cable not detected.\n" \
191                                      "Command exit.\n");
192                                 rc = CMD_RET_SUCCESS;
193                                 goto cleanup_register;
194                         }
195
196                         printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
197                         mdelay(1000);
198                         cable_ready_timeout--;
199                 }
200                 puts("\r\n");
201         }
202
203         while (1) {
204                 usb_gadget_handle_interrupts(controller_index);
205
206                 rc = fsg_main_thread(NULL);
207                 if (rc) {
208                         /* Check I/O error */
209                         if (rc == -EIO)
210                                 printf("\rCheck USB cable connection\n");
211
212                         /* Check CTRL+C */
213                         if (rc == -EPIPE)
214                                 printf("\rCTRL+C - Operation aborted\n");
215
216                         rc = CMD_RET_SUCCESS;
217                         goto cleanup_register;
218                 }
219         }
220
221 cleanup_register:
222         g_dnl_unregister();
223 cleanup_board:
224         board_usb_cleanup(controller_index, USB_INIT_DEVICE);
225 cleanup_ums_init:
226         ums_fini();
227
228         return rc;
229 }
230
231 U_BOOT_CMD(ums, 4, 1, do_usb_mass_storage,
232         "Use the UMS [USB Mass Storage]",
233         "<USB_controller> [<devtype>] <devnum>  e.g. ums 0 mmc 0\n"
234         "    devtype defaults to mmc"
235 );