xref: /rk3399_rockchip-uboot/cmd/rockusb.c (revision 331c2375688d79920fb06b8f0c4c52a7df56fb29)
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 	g_rkusb = &rkusb;
158 	rc = rkusb_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 		pr_err("Couldn't init USB controller.");
166 		rc = CMD_RET_FAILURE;
167 		goto cleanup_rkusb;
168 	}
169 
170 	rc = fsg_init(g_rkusb->ums, g_rkusb->ums_cnt);
171 	if (rc) {
172 		pr_err("fsg_init failed");
173 		rc = CMD_RET_FAILURE;
174 		goto cleanup_board;
175 	}
176 
177 	s = env_get("serial#");
178 	if (s) {
179 		char *sn = (char *)calloc(strlen(s) + 1, sizeof(char));
180 		char *sn_p = sn;
181 
182 		if (!sn)
183 			goto cleanup_board;
184 
185 		memcpy(sn, s, strlen(s));
186 		while (*sn_p) {
187 			if (*sn_p == '\\' || *sn_p == '/')
188 				*sn_p = '_';
189 			sn_p++;
190 		}
191 
192 		g_dnl_set_serialnumber(sn);
193 		free(sn);
194 	}
195 
196 	rc = g_dnl_register("rkusb_ums_dnl");
197 	if (rc) {
198 		pr_err("g_dnl_register failed");
199 		rc = CMD_RET_FAILURE;
200 		goto cleanup_board;
201 	}
202 
203 	/* Timeout unit: seconds */
204 	cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
205 
206 	if (!g_dnl_board_usb_cable_connected()) {
207 		puts("Please connect USB cable.\n");
208 
209 		while (!g_dnl_board_usb_cable_connected()) {
210 			if (ctrlc()) {
211 				puts("\rCTRL+C - Operation aborted.\n");
212 				rc = CMD_RET_SUCCESS;
213 				goto cleanup_register;
214 			}
215 			if (!cable_ready_timeout) {
216 				puts("\rUSB cable not detected.\nCommand exit.\n");
217 				rc = CMD_RET_SUCCESS;
218 				goto cleanup_register;
219 			}
220 
221 			printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
222 			mdelay(1000);
223 			cable_ready_timeout--;
224 		}
225 		puts("\r\n");
226 	}
227 
228 	while (1) {
229 		usb_gadget_handle_interrupts(controller_index);
230 
231 		rc = fsg_main_thread(NULL);
232 		if (rc) {
233 			/* Check I/O error */
234 			if (rc == -EIO)
235 				printf("\rCheck USB cable connection\n");
236 
237 			/* Check CTRL+C */
238 			if (rc == -EPIPE)
239 				printf("\rCTRL+C - Operation aborted\n");
240 
241 			rc = CMD_RET_SUCCESS;
242 			goto cleanup_register;
243 		}
244 	}
245 
246 cleanup_register:
247 	g_dnl_unregister();
248 cleanup_board:
249 	board_usb_cleanup(controller_index, USB_INIT_DEVICE);
250 cleanup_rkusb:
251 	rkusb_fini();
252 
253 	return rc;
254 }
255 
256 U_BOOT_CMD(rockusb, 4, 1, do_rkusb,
257 	   "Use the rockusb Protocol",
258 	   "<USB_controller> <devtype> <dev[:part]>  e.g. rockusb 0 mmc 0\n"
259 );
260