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