xref: /rk3399_rockchip-uboot/cmd/rockusb.c (revision 14569d26731af93622d8fcf87429b06ece85047b)
1 /*
2  * Copyright 2017 Rockchip Electronics Co., Ltd
3  * Frank Wang <frank.wang@rock-chips.com>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <errno.h>
9 #include <common.h>
10 #include <command.h>
11 #include <console.h>
12 #include <g_dnl.h>
13 #include <part.h>
14 #include <usb.h>
15 #include <usb_mass_storage.h>
16 #include <rockusb.h>
17 
18 static struct rockusb rkusb;
19 static struct rockusb *g_rkusb;
20 
21 static int rkusb_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 	if ((blkstart + blkcnt) > RKUSB_READ_LIMIT_ADDR) {
28 		memset(buf, 0xcc, blkcnt * SECTOR_SIZE);
29 		return blkcnt;
30 	} else {
31 		return blk_dread(block_dev, blkstart, blkcnt, buf);
32 	}
33 }
34 
35 static int rkusb_write_sector(struct ums *ums_dev,
36 			      ulong start, lbaint_t blkcnt, const void *buf)
37 {
38 	struct blk_desc *block_dev = &ums_dev->block_dev;
39 	lbaint_t blkstart = start + ums_dev->start_sector;
40 
41 	return blk_dwrite(block_dev, blkstart, blkcnt, buf);
42 }
43 
44 static int rkusb_erase_sector(struct ums *ums_dev,
45 			      ulong start, lbaint_t blkcnt)
46 {
47 	struct blk_desc *block_dev = &ums_dev->block_dev;
48 	lbaint_t blkstart = start + ums_dev->start_sector;
49 
50 	return blk_derase(block_dev, blkstart, blkcnt);
51 }
52 
53 static void rkusb_fini(void)
54 {
55 	int i;
56 
57 	for (i = 0; i < g_rkusb->ums_cnt; i++)
58 		free((void *)g_rkusb->ums[i].name);
59 	free(g_rkusb->ums);
60 	g_rkusb->ums = NULL;
61 	g_rkusb = NULL;
62 	g_rkusb->ums_cnt = 0;
63 }
64 
65 #define RKUSB_NAME_LEN 16
66 
67 static int rkusb_init(const char *devtype, const char *devnums_part_str)
68 {
69 	char *s, *t, *devnum_part_str, *name;
70 	struct blk_desc *block_dev;
71 	disk_partition_t info;
72 	int partnum, cnt;
73 	int ret = -1;
74 	struct ums *ums_new;
75 
76 	s = strdup(devnums_part_str);
77 	if (!s)
78 		return -1;
79 
80 	t = s;
81 	g_rkusb->ums_cnt = 0;
82 
83 	for (;;) {
84 		devnum_part_str = strsep(&t, ",");
85 		if (!devnum_part_str)
86 			break;
87 
88 		partnum = blk_get_device_part_str(devtype, devnum_part_str,
89 					&block_dev, &info, 1);
90 		if (partnum < 0)
91 			goto cleanup;
92 
93 		/* f_mass_storage.c assumes SECTOR_SIZE sectors */
94 		if (block_dev->blksz != SECTOR_SIZE)
95 			goto cleanup;
96 
97 		ums_new = realloc(g_rkusb->ums, (g_rkusb->ums_cnt + 1) *
98 				  sizeof(*g_rkusb->ums));
99 		if (!ums_new)
100 			goto cleanup;
101 		g_rkusb->ums = ums_new;
102 		cnt = g_rkusb->ums_cnt;
103 
104 		/* Expose all partitions for rockusb command */
105 		g_rkusb->ums[cnt].start_sector = 0;
106 		g_rkusb->ums[cnt].num_sectors = block_dev->lba;
107 
108 		g_rkusb->ums[cnt].read_sector = rkusb_read_sector;
109 		g_rkusb->ums[cnt].write_sector = rkusb_write_sector;
110 		g_rkusb->ums[cnt].erase_sector = rkusb_erase_sector;
111 
112 		name = malloc(RKUSB_NAME_LEN);
113 		if (!name)
114 			goto cleanup;
115 		snprintf(name, RKUSB_NAME_LEN, "rkusb disk %d", cnt);
116 		g_rkusb->ums[cnt].name = name;
117 		g_rkusb->ums[cnt].block_dev = *block_dev;
118 
119 		printf("RKUSB: LUN %d, dev %d, hwpart %d, sector %#x, count %#x\n",
120 		       g_rkusb->ums_cnt,
121 		       g_rkusb->ums[cnt].block_dev.devnum,
122 		       g_rkusb->ums[cnt].block_dev.hwpart,
123 		       g_rkusb->ums[cnt].start_sector,
124 		       g_rkusb->ums[cnt].num_sectors);
125 
126 		g_rkusb->ums_cnt++;
127 	}
128 
129 	if (g_rkusb->ums_cnt)
130 		ret = 0;
131 
132 cleanup:
133 	free(s);
134 	if (ret < 0)
135 		rkusb_fini();
136 
137 	return ret;
138 }
139 
140 static int do_rkusb(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
141 {
142 	const char *usb_controller;
143 	const char *devtype;
144 	const char *devnum;
145 	unsigned int controller_index;
146 	int rc;
147 	int cable_ready_timeout __maybe_unused;
148 	const char *s;
149 
150 	if (argc != 4)
151 		return CMD_RET_USAGE;
152 
153 	usb_controller = argv[1];
154 	devtype = argv[2];
155 	devnum	= argv[3];
156 
157 	if (!strcmp(devtype, "mmc") && !strcmp(devnum, "1")) {
158 		pr_err("Forbid to flash mmc 1(sdcard)\n");
159 		return CMD_RET_FAILURE;
160 	}
161 
162 	g_rkusb = &rkusb;
163 	rc = rkusb_init(devtype, devnum);
164 	if (rc < 0)
165 		return CMD_RET_FAILURE;
166 
167 	controller_index = (unsigned int)(simple_strtoul(
168 				usb_controller,	NULL, 0));
169 	rc = usb_gadget_initialize(controller_index);
170 	if (rc) {
171 		pr_err("Couldn't init USB controller.");
172 		rc = CMD_RET_FAILURE;
173 		goto cleanup_rkusb;
174 	}
175 
176 	rc = fsg_init(g_rkusb->ums, g_rkusb->ums_cnt);
177 	if (rc) {
178 		pr_err("fsg_init failed");
179 		rc = CMD_RET_FAILURE;
180 		goto cleanup_board;
181 	}
182 
183 	s = env_get("serial#");
184 	if (s) {
185 		char *sn = (char *)calloc(strlen(s) + 1, sizeof(char));
186 		char *sn_p = sn;
187 
188 		if (!sn)
189 			goto cleanup_board;
190 
191 		memcpy(sn, s, strlen(s));
192 		while (*sn_p) {
193 			if (*sn_p == '\\' || *sn_p == '/')
194 				*sn_p = '_';
195 			sn_p++;
196 		}
197 
198 		g_dnl_set_serialnumber(sn);
199 		free(sn);
200 	}
201 
202 	rc = g_dnl_register("rkusb_ums_dnl");
203 	if (rc) {
204 		pr_err("g_dnl_register failed");
205 		rc = CMD_RET_FAILURE;
206 		goto cleanup_board;
207 	}
208 
209 	/* Timeout unit: seconds */
210 	cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
211 
212 	if (!g_dnl_board_usb_cable_connected()) {
213 		puts("Please connect USB cable.\n");
214 
215 		while (!g_dnl_board_usb_cable_connected()) {
216 			if (ctrlc()) {
217 				puts("\rCTRL+C - Operation aborted.\n");
218 				rc = CMD_RET_SUCCESS;
219 				goto cleanup_register;
220 			}
221 			if (!cable_ready_timeout) {
222 				puts("\rUSB cable not detected.\nCommand exit.\n");
223 				rc = CMD_RET_SUCCESS;
224 				goto cleanup_register;
225 			}
226 
227 			printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
228 			mdelay(1000);
229 			cable_ready_timeout--;
230 		}
231 		puts("\r\n");
232 	}
233 
234 	while (1) {
235 		usb_gadget_handle_interrupts(controller_index);
236 
237 		rc = fsg_main_thread(NULL);
238 		if (rc) {
239 			/* Check I/O error */
240 			if (rc == -EIO)
241 				printf("\rCheck USB cable connection\n");
242 
243 			/* Check CTRL+C */
244 			if (rc == -EPIPE)
245 				printf("\rCTRL+C - Operation aborted\n");
246 
247 			rc = CMD_RET_SUCCESS;
248 			goto cleanup_register;
249 		}
250 	}
251 
252 cleanup_register:
253 	g_dnl_unregister();
254 cleanup_board:
255 	usb_gadget_release(controller_index);
256 cleanup_rkusb:
257 	rkusb_fini();
258 
259 	return rc;
260 }
261 
262 U_BOOT_CMD(rockusb, 4, 1, do_rkusb,
263 	   "Use the rockusb Protocol",
264 	   "<USB_controller> <devtype> <dev[:part]>  e.g. rockusb 0 mmc 0\n"
265 );
266