xref: /openbmc/u-boot/cmd/usb_mass_storage.c (revision 9ecb0c41)
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 );
236