xref: /rk3399_rockchip-uboot/cmd/rockusb.c (revision 8dd9db5d1cd5826638c3cdb5f681300ff2f29f3b)
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 		g_dnl_set_serialnumber((char *)s);
180 
181 	rc = g_dnl_register("rkusb_ums_dnl");
182 	if (rc) {
183 		pr_err("g_dnl_register failed");
184 		rc = CMD_RET_FAILURE;
185 		goto cleanup_board;
186 	}
187 
188 	/* Timeout unit: seconds */
189 	cable_ready_timeout = UMS_CABLE_READY_TIMEOUT;
190 
191 	if (!g_dnl_board_usb_cable_connected()) {
192 		puts("Please connect USB cable.\n");
193 
194 		while (!g_dnl_board_usb_cable_connected()) {
195 			if (ctrlc()) {
196 				puts("\rCTRL+C - Operation aborted.\n");
197 				rc = CMD_RET_SUCCESS;
198 				goto cleanup_register;
199 			}
200 			if (!cable_ready_timeout) {
201 				puts("\rUSB cable not detected.\nCommand exit.\n");
202 				rc = CMD_RET_SUCCESS;
203 				goto cleanup_register;
204 			}
205 
206 			printf("\rAuto exit in: %.2d s.", cable_ready_timeout);
207 			mdelay(1000);
208 			cable_ready_timeout--;
209 		}
210 		puts("\r\n");
211 	}
212 
213 	while (1) {
214 		usb_gadget_handle_interrupts(controller_index);
215 
216 		rc = fsg_main_thread(NULL);
217 		if (rc) {
218 			/* Check I/O error */
219 			if (rc == -EIO)
220 				printf("\rCheck USB cable connection\n");
221 
222 			/* Check CTRL+C */
223 			if (rc == -EPIPE)
224 				printf("\rCTRL+C - Operation aborted\n");
225 
226 			rc = CMD_RET_SUCCESS;
227 			goto cleanup_register;
228 		}
229 	}
230 
231 cleanup_register:
232 	g_dnl_unregister();
233 cleanup_board:
234 	board_usb_cleanup(controller_index, USB_INIT_DEVICE);
235 cleanup_rkusb:
236 	rkusb_fini();
237 
238 	return rc;
239 }
240 
241 U_BOOT_CMD(rockusb, 4, 1, do_rkusb,
242 	   "Use the rockusb Protocol",
243 	   "<USB_controller> <devtype> <dev[:part]>  e.g. rockusb 0 mmc 0\n"
244 );
245