xref: /rk3399_rockchip-uboot/drivers/usb/gadget/f_fastboot.c (revision c2ba77d93f696c0ccb8f2b653571104e7b4afb4e)
13aab70afSSebastian Siewior /*
23aab70afSSebastian Siewior  * (C) Copyright 2008 - 2009
33aab70afSSebastian Siewior  * Windriver, <www.windriver.com>
43aab70afSSebastian Siewior  * Tom Rix <Tom.Rix@windriver.com>
53aab70afSSebastian Siewior  *
63aab70afSSebastian Siewior  * Copyright 2011 Sebastian Andrzej Siewior <bigeasy@linutronix.de>
73aab70afSSebastian Siewior  *
83aab70afSSebastian Siewior  * Copyright 2014 Linaro, Ltd.
93aab70afSSebastian Siewior  * Rob Herring <robh@kernel.org>
103aab70afSSebastian Siewior  *
113aab70afSSebastian Siewior  * SPDX-License-Identifier:	GPL-2.0+
123aab70afSSebastian Siewior  */
13593cbd93SSteve Rae #include <config.h>
143aab70afSSebastian Siewior #include <common.h>
152e40c2c1SJason Zhu #include <console.h>
163aab70afSSebastian Siewior #include <errno.h>
173c8f98f5SMaxime Ripard #include <fastboot.h>
183aab70afSSebastian Siewior #include <malloc.h>
193aab70afSSebastian Siewior #include <linux/usb/ch9.h>
203aab70afSSebastian Siewior #include <linux/usb/gadget.h>
213aab70afSSebastian Siewior #include <linux/usb/composite.h>
223aab70afSSebastian Siewior #include <linux/compiler.h>
232e40c2c1SJason Zhu #include <u-boot/sha256.h>
243aab70afSSebastian Siewior #include <version.h>
253aab70afSSebastian Siewior #include <g_dnl.h>
262e40c2c1SJason Zhu #include <fs.h>
27367cce4dSXu Hongfei #include <android_avb/avb_ops_user.h>
2837a7bc39SJason Zhu #include <android_avb/rk_avb_ops_user.h>
292e40c2c1SJason Zhu #include <dm/uclass.h>
302e40c2c1SJason Zhu #include <power/fuel_gauge.h>
31d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
32d1b5ed07SSteve Rae #include <fb_mmc.h>
33d1b5ed07SSteve Rae #endif
34bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
35bf8940d3SMaxime Ripard #include <fb_nand.h>
36bf8940d3SMaxime Ripard #endif
374d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
384d0fc665SAndy Ye #include <optee_include/OpteeClientInterface.h>
394d0fc665SAndy Ye #endif
40f61a997eSqiujian #include <boot_rkimg.h>
41f61a997eSqiujian #include <optee_include/tee_client_api.h>
42*c2ba77d9SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK
43*c2ba77d9SJian Qiu #include <keymaster.h>
44*c2ba77d9SJian Qiu #endif
453aab70afSSebastian Siewior 
463aab70afSSebastian Siewior #define FASTBOOT_VERSION		"0.4"
473aab70afSSebastian Siewior 
483aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_CLASS	0xff
493aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_SUB_CLASS	0x42
503aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_PROTOCOL	0x03
513aab70afSSebastian Siewior 
523aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0  (0x0200)
533aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1  (0x0040)
543aab70afSSebastian Siewior #define TX_ENDPOINT_MAXIMUM_PACKET_SIZE      (0x0040)
553aab70afSSebastian Siewior 
563aab70afSSebastian Siewior #define EP_BUFFER_SIZE			4096
57de78ceaeSJason Zhu #define SLEEP_COUNT 20000
5890d27e7fSJason Zhu #define MAX_PART_NUM_STR_SIZE 4
5990d27e7fSJason Zhu #define PARTITION_TYPE_STRINGS "partition-type"
6090d27e7fSJason Zhu 
61ac484c5aSRoger Quadros /*
62ac484c5aSRoger Quadros  * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size
63ac484c5aSRoger Quadros  * (64 or 512 or 1024), else we break on certain controllers like DWC3
64ac484c5aSRoger Quadros  * that expect bulk OUT requests to be divisible by maxpacket size.
65ac484c5aSRoger Quadros  */
663aab70afSSebastian Siewior 
673aab70afSSebastian Siewior struct f_fastboot {
683aab70afSSebastian Siewior 	struct usb_function usb_function;
693aab70afSSebastian Siewior 
70593cbd93SSteve Rae 	/* IN/OUT EP's and corresponding requests */
713aab70afSSebastian Siewior 	struct usb_ep *in_ep, *out_ep;
723aab70afSSebastian Siewior 	struct usb_request *in_req, *out_req;
733aab70afSSebastian Siewior };
743aab70afSSebastian Siewior 
753aab70afSSebastian Siewior static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
763aab70afSSebastian Siewior {
773aab70afSSebastian Siewior 	return container_of(f, struct f_fastboot, usb_function);
783aab70afSSebastian Siewior }
793aab70afSSebastian Siewior 
803aab70afSSebastian Siewior static struct f_fastboot *fastboot_func;
813aab70afSSebastian Siewior static unsigned int download_size;
823aab70afSSebastian Siewior static unsigned int download_bytes;
83367cce4dSXu Hongfei static unsigned int upload_size;
84367cce4dSXu Hongfei static unsigned int upload_bytes;
85367cce4dSXu Hongfei static bool start_upload;
86de78ceaeSJason Zhu static unsigned intthread_wakeup_needed;
873aab70afSSebastian Siewior 
883aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_in = {
893aab70afSSebastian Siewior 	.bLength            = USB_DT_ENDPOINT_SIZE,
903aab70afSSebastian Siewior 	.bDescriptorType    = USB_DT_ENDPOINT,
913aab70afSSebastian Siewior 	.bEndpointAddress   = USB_DIR_IN,
923aab70afSSebastian Siewior 	.bmAttributes       = USB_ENDPOINT_XFER_BULK,
93718156adSRoger Quadros 	.wMaxPacketSize     = cpu_to_le16(64),
943aab70afSSebastian Siewior };
953aab70afSSebastian Siewior 
963aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_out = {
973aab70afSSebastian Siewior 	.bLength		= USB_DT_ENDPOINT_SIZE,
983aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_ENDPOINT,
993aab70afSSebastian Siewior 	.bEndpointAddress	= USB_DIR_OUT,
1003aab70afSSebastian Siewior 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
101718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(64),
102718156adSRoger Quadros };
103718156adSRoger Quadros 
104718156adSRoger Quadros static struct usb_endpoint_descriptor hs_ep_in = {
105718156adSRoger Quadros 	.bLength		= USB_DT_ENDPOINT_SIZE,
106718156adSRoger Quadros 	.bDescriptorType	= USB_DT_ENDPOINT,
107718156adSRoger Quadros 	.bEndpointAddress	= USB_DIR_IN,
108718156adSRoger Quadros 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
109718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(512),
1103aab70afSSebastian Siewior };
1113aab70afSSebastian Siewior 
1123aab70afSSebastian Siewior static struct usb_endpoint_descriptor hs_ep_out = {
1133aab70afSSebastian Siewior 	.bLength		= USB_DT_ENDPOINT_SIZE,
1143aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_ENDPOINT,
1153aab70afSSebastian Siewior 	.bEndpointAddress	= USB_DIR_OUT,
1163aab70afSSebastian Siewior 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
117718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(512),
1183aab70afSSebastian Siewior };
1193aab70afSSebastian Siewior 
12026dd3474SWilliam Wu static struct usb_endpoint_descriptor ss_ep_in = {
12126dd3474SWilliam Wu 	.bLength		= USB_DT_ENDPOINT_SIZE,
12226dd3474SWilliam Wu 	.bDescriptorType	= USB_DT_ENDPOINT,
12326dd3474SWilliam Wu 	.bEndpointAddress	= USB_DIR_IN,
12426dd3474SWilliam Wu 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
12526dd3474SWilliam Wu 	.wMaxPacketSize		= cpu_to_le16(1024),
12626dd3474SWilliam Wu };
12726dd3474SWilliam Wu 
12826dd3474SWilliam Wu static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
12926dd3474SWilliam Wu 	.bLength		= sizeof(ss_ep_in_comp_desc),
13026dd3474SWilliam Wu 	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
13126dd3474SWilliam Wu 	/* .bMaxBurst		= DYNAMIC, */
13226dd3474SWilliam Wu };
13326dd3474SWilliam Wu 
13426dd3474SWilliam Wu static struct usb_endpoint_descriptor ss_ep_out = {
13526dd3474SWilliam Wu 	.bLength		= USB_DT_ENDPOINT_SIZE,
13626dd3474SWilliam Wu 	.bDescriptorType	= USB_DT_ENDPOINT,
13726dd3474SWilliam Wu 	.bEndpointAddress	= USB_DIR_OUT,
13826dd3474SWilliam Wu 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
13926dd3474SWilliam Wu 	.wMaxPacketSize		= cpu_to_le16(1024),
14026dd3474SWilliam Wu };
14126dd3474SWilliam Wu 
14226dd3474SWilliam Wu static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
14326dd3474SWilliam Wu 	.bLength		= sizeof(ss_ep_out_comp_desc),
14426dd3474SWilliam Wu 	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
14526dd3474SWilliam Wu 	/* .bMaxBurst		= DYNAMIC, */
14626dd3474SWilliam Wu };
14726dd3474SWilliam Wu 
1483aab70afSSebastian Siewior static struct usb_interface_descriptor interface_desc = {
1493aab70afSSebastian Siewior 	.bLength		= USB_DT_INTERFACE_SIZE,
1503aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_INTERFACE,
1513aab70afSSebastian Siewior 	.bInterfaceNumber	= 0x00,
1523aab70afSSebastian Siewior 	.bAlternateSetting	= 0x00,
1533aab70afSSebastian Siewior 	.bNumEndpoints		= 0x02,
1543aab70afSSebastian Siewior 	.bInterfaceClass	= FASTBOOT_INTERFACE_CLASS,
1553aab70afSSebastian Siewior 	.bInterfaceSubClass	= FASTBOOT_INTERFACE_SUB_CLASS,
1563aab70afSSebastian Siewior 	.bInterfaceProtocol	= FASTBOOT_INTERFACE_PROTOCOL,
1573aab70afSSebastian Siewior };
1583aab70afSSebastian Siewior 
159718156adSRoger Quadros static struct usb_descriptor_header *fb_fs_function[] = {
1603aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&interface_desc,
1613aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&fs_ep_in,
162718156adSRoger Quadros 	(struct usb_descriptor_header *)&fs_ep_out,
163718156adSRoger Quadros };
164718156adSRoger Quadros 
165718156adSRoger Quadros static struct usb_descriptor_header *fb_hs_function[] = {
166718156adSRoger Quadros 	(struct usb_descriptor_header *)&interface_desc,
167718156adSRoger Quadros 	(struct usb_descriptor_header *)&hs_ep_in,
1683aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&hs_ep_out,
1693aab70afSSebastian Siewior 	NULL,
1703aab70afSSebastian Siewior };
1713aab70afSSebastian Siewior 
17226dd3474SWilliam Wu static struct usb_descriptor_header *fb_ss_function[] = {
17326dd3474SWilliam Wu 	(struct usb_descriptor_header *)&interface_desc,
17426dd3474SWilliam Wu 	(struct usb_descriptor_header *)&ss_ep_in,
17526dd3474SWilliam Wu 	(struct usb_descriptor_header *)&ss_ep_in_comp_desc,
17626dd3474SWilliam Wu 	(struct usb_descriptor_header *)&ss_ep_out,
17726dd3474SWilliam Wu 	(struct usb_descriptor_header *)&ss_ep_out_comp_desc,
17826dd3474SWilliam Wu 	NULL,
17926dd3474SWilliam Wu };
18026dd3474SWilliam Wu 
1818b704a0eSRoger Quadros static struct usb_endpoint_descriptor *
1828b704a0eSRoger Quadros fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
18326dd3474SWilliam Wu 	   struct usb_endpoint_descriptor *hs,
18426dd3474SWilliam Wu 	   struct usb_endpoint_descriptor *ss,
18526dd3474SWilliam Wu 	   struct usb_ss_ep_comp_descriptor *comp_desc,
18626dd3474SWilliam Wu 	   struct usb_ep *ep)
1878b704a0eSRoger Quadros {
18826dd3474SWilliam Wu 	struct usb_endpoint_descriptor *speed_desc = NULL;
18926dd3474SWilliam Wu 
19026dd3474SWilliam Wu 	/* select desired speed */
19126dd3474SWilliam Wu 	switch (g->speed) {
19226dd3474SWilliam Wu 	case USB_SPEED_SUPER:
19326dd3474SWilliam Wu 		if (gadget_is_superspeed(g)) {
19426dd3474SWilliam Wu 			speed_desc = ss;
19526dd3474SWilliam Wu 			ep->comp_desc = comp_desc;
19626dd3474SWilliam Wu 			break;
19726dd3474SWilliam Wu 		}
19826dd3474SWilliam Wu 		/* else: Fall trough */
19926dd3474SWilliam Wu 	case USB_SPEED_HIGH:
20026dd3474SWilliam Wu 		if (gadget_is_dualspeed(g)) {
20126dd3474SWilliam Wu 			speed_desc = hs;
20226dd3474SWilliam Wu 			break;
20326dd3474SWilliam Wu 		}
20426dd3474SWilliam Wu 		/* else: fall through */
20526dd3474SWilliam Wu 	default:
20626dd3474SWilliam Wu 		speed_desc = fs;
20726dd3474SWilliam Wu 	}
20826dd3474SWilliam Wu 
20926dd3474SWilliam Wu 	return speed_desc;
2108b704a0eSRoger Quadros }
2118b704a0eSRoger Quadros 
2123aab70afSSebastian Siewior /*
2133aab70afSSebastian Siewior  * static strings, in UTF-8
2143aab70afSSebastian Siewior  */
2153aab70afSSebastian Siewior static const char fastboot_name[] = "Android Fastboot";
2163aab70afSSebastian Siewior 
2173aab70afSSebastian Siewior static struct usb_string fastboot_string_defs[] = {
2183aab70afSSebastian Siewior 	[0].s = fastboot_name,
2193aab70afSSebastian Siewior 	{  }			/* end of list */
2203aab70afSSebastian Siewior };
2213aab70afSSebastian Siewior 
2223aab70afSSebastian Siewior static struct usb_gadget_strings stringtab_fastboot = {
2233aab70afSSebastian Siewior 	.language	= 0x0409,	/* en-us */
2243aab70afSSebastian Siewior 	.strings	= fastboot_string_defs,
2253aab70afSSebastian Siewior };
2263aab70afSSebastian Siewior 
2273aab70afSSebastian Siewior static struct usb_gadget_strings *fastboot_strings[] = {
2283aab70afSSebastian Siewior 	&stringtab_fastboot,
2293aab70afSSebastian Siewior 	NULL,
2303aab70afSSebastian Siewior };
2313aab70afSSebastian Siewior 
2323aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
233e2ec3e46SAlexey Firago static int strcmp_l1(const char *s1, const char *s2);
234de78ceaeSJason Zhu static void wakeup_thread(void)
235de78ceaeSJason Zhu {
236de78ceaeSJason Zhu 	intthread_wakeup_needed = false;
237de78ceaeSJason Zhu }
238de78ceaeSJason Zhu 
239de78ceaeSJason Zhu static void busy_indicator(void)
240de78ceaeSJason Zhu {
241de78ceaeSJason Zhu 	static int state;
242de78ceaeSJason Zhu 
243de78ceaeSJason Zhu 	switch (state) {
244de78ceaeSJason Zhu 	case 0:
245de78ceaeSJason Zhu 		puts("\r|"); break;
246de78ceaeSJason Zhu 	case 1:
247de78ceaeSJason Zhu 		puts("\r/"); break;
248de78ceaeSJason Zhu 	case 2:
249de78ceaeSJason Zhu 		puts("\r-"); break;
250de78ceaeSJason Zhu 	case 3:
251de78ceaeSJason Zhu 		puts("\r\\"); break;
252de78ceaeSJason Zhu 	case 4:
253de78ceaeSJason Zhu 		puts("\r|"); break;
254de78ceaeSJason Zhu 	case 5:
255de78ceaeSJason Zhu 		puts("\r/"); break;
256de78ceaeSJason Zhu 	case 6:
257de78ceaeSJason Zhu 		puts("\r-"); break;
258de78ceaeSJason Zhu 	case 7:
259de78ceaeSJason Zhu 		puts("\r\\"); break;
260de78ceaeSJason Zhu 	default:
261de78ceaeSJason Zhu 		state = 0;
262de78ceaeSJason Zhu 	}
263de78ceaeSJason Zhu 	if (state++ == 8)
264de78ceaeSJason Zhu 		state = 0;
265de78ceaeSJason Zhu }
266de78ceaeSJason Zhu 
26790d27e7fSJason Zhu static int fb_get_fstype(const char *ifname, const int part_num,
26890d27e7fSJason Zhu 			 const char **fs_type)
26990d27e7fSJason Zhu {
27090d27e7fSJason Zhu 	char part_num_str[MAX_PART_NUM_STR_SIZE] = {0};
27190d27e7fSJason Zhu 
27290d27e7fSJason Zhu 	snprintf(part_num_str, ARRAY_SIZE(part_num_str), ":%x", part_num);
27390d27e7fSJason Zhu 
27490d27e7fSJason Zhu 	if (fs_set_blk_dev(ifname, part_num_str, FS_TYPE_ANY))
27590d27e7fSJason Zhu 		return -1;
27690d27e7fSJason Zhu 
27790d27e7fSJason Zhu 	if (fs_get_fstype(fs_type))
27890d27e7fSJason Zhu 		return -1;
27990d27e7fSJason Zhu 
28090d27e7fSJason Zhu 	return 0;
28190d27e7fSJason Zhu }
28290d27e7fSJason Zhu 
283de78ceaeSJason Zhu static int sleep_thread(void)
284de78ceaeSJason Zhu {
285de78ceaeSJason Zhu 	int rc = 0;
286de78ceaeSJason Zhu 	int i = 0, k = 0;
287de78ceaeSJason Zhu 
288de78ceaeSJason Zhu 	/* Wait until a signal arrives or we are woken up */
289de78ceaeSJason Zhu 	for (;;) {
290de78ceaeSJason Zhu 		if (!intthread_wakeup_needed)
291de78ceaeSJason Zhu 			break;
292de78ceaeSJason Zhu 
293de78ceaeSJason Zhu 		if (++i == SLEEP_COUNT) {
294de78ceaeSJason Zhu 			busy_indicator();
295de78ceaeSJason Zhu 			i = 0;
296de78ceaeSJason Zhu 			k++;
297de78ceaeSJason Zhu 		}
298de78ceaeSJason Zhu 
299de78ceaeSJason Zhu 		if (k == 10) {
300de78ceaeSJason Zhu 			/* Handle CTRL+C */
301de78ceaeSJason Zhu 			if (ctrlc())
302de78ceaeSJason Zhu 				return -EPIPE;
303de78ceaeSJason Zhu 
304de78ceaeSJason Zhu 			/* Check cable connection */
305de78ceaeSJason Zhu 			if (!g_dnl_board_usb_cable_connected())
306de78ceaeSJason Zhu 				return -EIO;
307de78ceaeSJason Zhu 
308de78ceaeSJason Zhu 			k = 0;
309de78ceaeSJason Zhu 		}
310de78ceaeSJason Zhu 
311de78ceaeSJason Zhu 		usb_gadget_handle_interrupts(0);
312de78ceaeSJason Zhu 	}
313de78ceaeSJason Zhu 	intthread_wakeup_needed = true;
314de78ceaeSJason Zhu 	return rc;
315de78ceaeSJason Zhu }
3163aab70afSSebastian Siewior 
3173aab70afSSebastian Siewior static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
3183aab70afSSebastian Siewior {
3193aab70afSSebastian Siewior 	int status = req->status;
320de78ceaeSJason Zhu 
321de78ceaeSJason Zhu 	wakeup_thread();
3223aab70afSSebastian Siewior 	if (!status)
3233aab70afSSebastian Siewior 		return;
3243aab70afSSebastian Siewior 	printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
3253aab70afSSebastian Siewior }
3263aab70afSSebastian Siewior 
3273aab70afSSebastian Siewior static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
3283aab70afSSebastian Siewior {
3293aab70afSSebastian Siewior 	int id;
3303aab70afSSebastian Siewior 	struct usb_gadget *gadget = c->cdev->gadget;
3313aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
332537cd072SDileep Katta 	const char *s;
3333aab70afSSebastian Siewior 
3343aab70afSSebastian Siewior 	/* DYNAMIC interface numbers assignments */
3353aab70afSSebastian Siewior 	id = usb_interface_id(c, f);
3363aab70afSSebastian Siewior 	if (id < 0)
3373aab70afSSebastian Siewior 		return id;
3383aab70afSSebastian Siewior 	interface_desc.bInterfaceNumber = id;
3393aab70afSSebastian Siewior 
3403aab70afSSebastian Siewior 	id = usb_string_id(c->cdev);
3413aab70afSSebastian Siewior 	if (id < 0)
3423aab70afSSebastian Siewior 		return id;
3433aab70afSSebastian Siewior 	fastboot_string_defs[0].id = id;
3443aab70afSSebastian Siewior 	interface_desc.iInterface = id;
3453aab70afSSebastian Siewior 
3463aab70afSSebastian Siewior 	f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
3473aab70afSSebastian Siewior 	if (!f_fb->in_ep)
3483aab70afSSebastian Siewior 		return -ENODEV;
3493aab70afSSebastian Siewior 	f_fb->in_ep->driver_data = c->cdev;
3503aab70afSSebastian Siewior 
3513aab70afSSebastian Siewior 	f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
3523aab70afSSebastian Siewior 	if (!f_fb->out_ep)
3533aab70afSSebastian Siewior 		return -ENODEV;
3543aab70afSSebastian Siewior 	f_fb->out_ep->driver_data = c->cdev;
3553aab70afSSebastian Siewior 
356718156adSRoger Quadros 	f->descriptors = fb_fs_function;
357718156adSRoger Quadros 
358718156adSRoger Quadros 	if (gadget_is_dualspeed(gadget)) {
359718156adSRoger Quadros 		/* Assume endpoint addresses are the same for both speeds */
360718156adSRoger Quadros 		hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
3613aab70afSSebastian Siewior 		hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
362718156adSRoger Quadros 		/* copy HS descriptors */
363718156adSRoger Quadros 		f->hs_descriptors = fb_hs_function;
364718156adSRoger Quadros 	}
3653aab70afSSebastian Siewior 
36626dd3474SWilliam Wu 	if (gadget_is_superspeed(gadget)) {
36726dd3474SWilliam Wu 		/* Assume endpoint addresses are the same as full speed */
36826dd3474SWilliam Wu 		ss_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
36926dd3474SWilliam Wu 		ss_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
37026dd3474SWilliam Wu 		/* copy SS descriptors */
37126dd3474SWilliam Wu 		f->ss_descriptors = fb_ss_function;
37226dd3474SWilliam Wu 	}
37326dd3474SWilliam Wu 
37400caae6dSSimon Glass 	s = env_get("serial#");
375537cd072SDileep Katta 	if (s)
376537cd072SDileep Katta 		g_dnl_set_serialnumber((char *)s);
377537cd072SDileep Katta 
3783aab70afSSebastian Siewior 	return 0;
3793aab70afSSebastian Siewior }
3803aab70afSSebastian Siewior 
3813aab70afSSebastian Siewior static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
3823aab70afSSebastian Siewior {
3833aab70afSSebastian Siewior 	memset(fastboot_func, 0, sizeof(*fastboot_func));
3843aab70afSSebastian Siewior }
3853aab70afSSebastian Siewior 
3863aab70afSSebastian Siewior static void fastboot_disable(struct usb_function *f)
3873aab70afSSebastian Siewior {
3883aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
3893aab70afSSebastian Siewior 
3903aab70afSSebastian Siewior 	usb_ep_disable(f_fb->out_ep);
3913aab70afSSebastian Siewior 	usb_ep_disable(f_fb->in_ep);
3923aab70afSSebastian Siewior 
3933aab70afSSebastian Siewior 	if (f_fb->out_req) {
3943aab70afSSebastian Siewior 		free(f_fb->out_req->buf);
3953aab70afSSebastian Siewior 		usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
3963aab70afSSebastian Siewior 		f_fb->out_req = NULL;
3973aab70afSSebastian Siewior 	}
3983aab70afSSebastian Siewior 	if (f_fb->in_req) {
3993aab70afSSebastian Siewior 		free(f_fb->in_req->buf);
4003aab70afSSebastian Siewior 		usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
4013aab70afSSebastian Siewior 		f_fb->in_req = NULL;
4023aab70afSSebastian Siewior 	}
4033aab70afSSebastian Siewior }
4043aab70afSSebastian Siewior 
4053aab70afSSebastian Siewior static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
4063aab70afSSebastian Siewior {
4073aab70afSSebastian Siewior 	struct usb_request *req;
4083aab70afSSebastian Siewior 
4093aab70afSSebastian Siewior 	req = usb_ep_alloc_request(ep, 0);
4103aab70afSSebastian Siewior 	if (!req)
4113aab70afSSebastian Siewior 		return NULL;
4123aab70afSSebastian Siewior 
4133aab70afSSebastian Siewior 	req->length = EP_BUFFER_SIZE;
4143aab70afSSebastian Siewior 	req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
4153aab70afSSebastian Siewior 	if (!req->buf) {
4163aab70afSSebastian Siewior 		usb_ep_free_request(ep, req);
4173aab70afSSebastian Siewior 		return NULL;
4183aab70afSSebastian Siewior 	}
4193aab70afSSebastian Siewior 
4203aab70afSSebastian Siewior 	memset(req->buf, 0, req->length);
4213aab70afSSebastian Siewior 	return req;
4223aab70afSSebastian Siewior }
4233aab70afSSebastian Siewior 
4243aab70afSSebastian Siewior static int fastboot_set_alt(struct usb_function *f,
4253aab70afSSebastian Siewior 			    unsigned interface, unsigned alt)
4263aab70afSSebastian Siewior {
4273aab70afSSebastian Siewior 	int ret;
4283aab70afSSebastian Siewior 	struct usb_composite_dev *cdev = f->config->cdev;
4293aab70afSSebastian Siewior 	struct usb_gadget *gadget = cdev->gadget;
4303aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
4318b704a0eSRoger Quadros 	const struct usb_endpoint_descriptor *d;
4323aab70afSSebastian Siewior 
4333aab70afSSebastian Siewior 	debug("%s: func: %s intf: %d alt: %d\n",
4343aab70afSSebastian Siewior 	      __func__, f->name, interface, alt);
4353aab70afSSebastian Siewior 
43626dd3474SWilliam Wu 	d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out, &ss_ep_out,
43726dd3474SWilliam Wu 		       &ss_ep_out_comp_desc, f_fb->out_ep);
4388b704a0eSRoger Quadros 	ret = usb_ep_enable(f_fb->out_ep, d);
4393aab70afSSebastian Siewior 	if (ret) {
4403aab70afSSebastian Siewior 		puts("failed to enable out ep\n");
4413aab70afSSebastian Siewior 		return ret;
4423aab70afSSebastian Siewior 	}
4433aab70afSSebastian Siewior 
4443aab70afSSebastian Siewior 	f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
4453aab70afSSebastian Siewior 	if (!f_fb->out_req) {
4463aab70afSSebastian Siewior 		puts("failed to alloc out req\n");
4473aab70afSSebastian Siewior 		ret = -EINVAL;
4483aab70afSSebastian Siewior 		goto err;
4493aab70afSSebastian Siewior 	}
4503aab70afSSebastian Siewior 	f_fb->out_req->complete = rx_handler_command;
4513aab70afSSebastian Siewior 
45226dd3474SWilliam Wu 	d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in,
45326dd3474SWilliam Wu 		       &ss_ep_in_comp_desc, f_fb->in_ep);
4548b704a0eSRoger Quadros 	ret = usb_ep_enable(f_fb->in_ep, d);
4553aab70afSSebastian Siewior 	if (ret) {
4563aab70afSSebastian Siewior 		puts("failed to enable in ep\n");
4573aab70afSSebastian Siewior 		goto err;
4583aab70afSSebastian Siewior 	}
4593aab70afSSebastian Siewior 
4603aab70afSSebastian Siewior 	f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
4613aab70afSSebastian Siewior 	if (!f_fb->in_req) {
4623aab70afSSebastian Siewior 		puts("failed alloc req in\n");
4633aab70afSSebastian Siewior 		ret = -EINVAL;
4643aab70afSSebastian Siewior 		goto err;
4653aab70afSSebastian Siewior 	}
4663aab70afSSebastian Siewior 	f_fb->in_req->complete = fastboot_complete;
4673aab70afSSebastian Siewior 
4683aab70afSSebastian Siewior 	ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
4693aab70afSSebastian Siewior 	if (ret)
4703aab70afSSebastian Siewior 		goto err;
4713aab70afSSebastian Siewior 
4723aab70afSSebastian Siewior 	return 0;
4733aab70afSSebastian Siewior err:
4743aab70afSSebastian Siewior 	fastboot_disable(f);
4753aab70afSSebastian Siewior 	return ret;
4763aab70afSSebastian Siewior }
4773aab70afSSebastian Siewior 
4783aab70afSSebastian Siewior static int fastboot_add(struct usb_configuration *c)
4793aab70afSSebastian Siewior {
4803aab70afSSebastian Siewior 	struct f_fastboot *f_fb = fastboot_func;
4813aab70afSSebastian Siewior 	int status;
4823aab70afSSebastian Siewior 
4833aab70afSSebastian Siewior 	debug("%s: cdev: 0x%p\n", __func__, c->cdev);
4843aab70afSSebastian Siewior 
4853aab70afSSebastian Siewior 	if (!f_fb) {
4863aab70afSSebastian Siewior 		f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
4873aab70afSSebastian Siewior 		if (!f_fb)
4883aab70afSSebastian Siewior 			return -ENOMEM;
4893aab70afSSebastian Siewior 
4903aab70afSSebastian Siewior 		fastboot_func = f_fb;
4913aab70afSSebastian Siewior 		memset(f_fb, 0, sizeof(*f_fb));
4923aab70afSSebastian Siewior 	}
4933aab70afSSebastian Siewior 
4943aab70afSSebastian Siewior 	f_fb->usb_function.name = "f_fastboot";
4953aab70afSSebastian Siewior 	f_fb->usb_function.bind = fastboot_bind;
4963aab70afSSebastian Siewior 	f_fb->usb_function.unbind = fastboot_unbind;
4973aab70afSSebastian Siewior 	f_fb->usb_function.set_alt = fastboot_set_alt;
4983aab70afSSebastian Siewior 	f_fb->usb_function.disable = fastboot_disable;
4993aab70afSSebastian Siewior 	f_fb->usb_function.strings = fastboot_strings;
5003aab70afSSebastian Siewior 
5013aab70afSSebastian Siewior 	status = usb_add_function(c, &f_fb->usb_function);
5023aab70afSSebastian Siewior 	if (status) {
5033aab70afSSebastian Siewior 		free(f_fb);
5043aab70afSSebastian Siewior 		fastboot_func = f_fb;
5053aab70afSSebastian Siewior 	}
5063aab70afSSebastian Siewior 
5073aab70afSSebastian Siewior 	return status;
5083aab70afSSebastian Siewior }
5093aab70afSSebastian Siewior DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
5103aab70afSSebastian Siewior 
511593cbd93SSteve Rae static int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
5123aab70afSSebastian Siewior {
5133aab70afSSebastian Siewior 	struct usb_request *in_req = fastboot_func->in_req;
5143aab70afSSebastian Siewior 	int ret;
5153aab70afSSebastian Siewior 
5163aab70afSSebastian Siewior 	memcpy(in_req->buf, buffer, buffer_size);
5173aab70afSSebastian Siewior 	in_req->length = buffer_size;
518bc9071c9SPaul Kocialkowski 
519bc9071c9SPaul Kocialkowski 	usb_ep_dequeue(fastboot_func->in_ep, in_req);
520bc9071c9SPaul Kocialkowski 
5213aab70afSSebastian Siewior 	ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
5223aab70afSSebastian Siewior 	if (ret)
5233aab70afSSebastian Siewior 		printf("Error %d on queue\n", ret);
5243aab70afSSebastian Siewior 	return 0;
5253aab70afSSebastian Siewior }
5263aab70afSSebastian Siewior 
5273aab70afSSebastian Siewior static int fastboot_tx_write_str(const char *buffer)
5283aab70afSSebastian Siewior {
529de78ceaeSJason Zhu 	int ret;
530de78ceaeSJason Zhu 
531de78ceaeSJason Zhu 	ret = sleep_thread();
532de78ceaeSJason Zhu 	if (ret < 0)
533de78ceaeSJason Zhu 		printf("warning: 0x%x, usb transmission is abnormal!\n", ret);
534de78ceaeSJason Zhu 
5353aab70afSSebastian Siewior 	return fastboot_tx_write(buffer, strlen(buffer));
5363aab70afSSebastian Siewior }
5373aab70afSSebastian Siewior 
5383aab70afSSebastian Siewior static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
5393aab70afSSebastian Siewior {
5403aab70afSSebastian Siewior 	do_reset(NULL, 0, 0, NULL);
5413aab70afSSebastian Siewior }
5423aab70afSSebastian Siewior 
543e2ec3e46SAlexey Firago int __weak fb_set_reboot_flag(void)
544e2ec3e46SAlexey Firago {
545e2ec3e46SAlexey Firago 	return -ENOSYS;
546e2ec3e46SAlexey Firago }
547e2ec3e46SAlexey Firago 
5483aab70afSSebastian Siewior static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
5493aab70afSSebastian Siewior {
550e2ec3e46SAlexey Firago 	char *cmd = req->buf;
551e2ec3e46SAlexey Firago 	if (!strcmp_l1("reboot-bootloader", cmd)) {
552e2ec3e46SAlexey Firago 		if (fb_set_reboot_flag()) {
553e2ec3e46SAlexey Firago 			fastboot_tx_write_str("FAILCannot set reboot flag");
554e2ec3e46SAlexey Firago 			return;
555e2ec3e46SAlexey Firago 		}
556e2ec3e46SAlexey Firago 	}
5573aab70afSSebastian Siewior 	fastboot_func->in_req->complete = compl_do_reset;
5583aab70afSSebastian Siewior 	fastboot_tx_write_str("OKAY");
5593aab70afSSebastian Siewior }
5603aab70afSSebastian Siewior 
5613aab70afSSebastian Siewior static int strcmp_l1(const char *s1, const char *s2)
5623aab70afSSebastian Siewior {
5633aab70afSSebastian Siewior 	if (!s1 || !s2)
5643aab70afSSebastian Siewior 		return -1;
5653aab70afSSebastian Siewior 	return strncmp(s1, s2, strlen(s1));
5663aab70afSSebastian Siewior }
5673aab70afSSebastian Siewior 
56890d27e7fSJason Zhu struct name_string {
56990d27e7fSJason Zhu 	const char *str;
57090d27e7fSJason Zhu 	int expects_args;
57190d27e7fSJason Zhu 	char delim;
57290d27e7fSJason Zhu };
57390d27e7fSJason Zhu 
57490d27e7fSJason Zhu #define NAME_NO_ARGS(s)	{.str = s, .expects_args = 0}
57590d27e7fSJason Zhu #define NAME_ARGS(s, d)	{.str = s, .expects_args = 1, .delim = d}
57690d27e7fSJason Zhu 
57790d27e7fSJason Zhu static size_t name_check_match(const char *str, size_t len,
57890d27e7fSJason Zhu 			       const struct name_string *name)
5793aab70afSSebastian Siewior {
58090d27e7fSJason Zhu 	size_t str_len = strlen(name->str);
5813aab70afSSebastian Siewior 
58290d27e7fSJason Zhu 	/* If name len is greater than input, return 0. */
58390d27e7fSJason Zhu 	if (str_len > len)
58490d27e7fSJason Zhu 		return 0;
58529425be4SJeroen Hofstee 
58690d27e7fSJason Zhu 	/* If name str does not match input string, return 0. */
58790d27e7fSJason Zhu 	if (memcmp(name->str, str, str_len))
58890d27e7fSJason Zhu 		return 0;
58990d27e7fSJason Zhu 
59090d27e7fSJason Zhu 	if (name->expects_args) {
59190d27e7fSJason Zhu 		/* string should have space for delim */
59290d27e7fSJason Zhu 		if (len == str_len)
59390d27e7fSJason Zhu 			return 0;
59490d27e7fSJason Zhu 
59590d27e7fSJason Zhu 		/* Check delim match */
59690d27e7fSJason Zhu 		if (name->delim != str[str_len])
59790d27e7fSJason Zhu 			return 0;
59890d27e7fSJason Zhu 	} else {
59990d27e7fSJason Zhu 		/* Name str len should match input len */
60090d27e7fSJason Zhu 		if (str_len != len)
60190d27e7fSJason Zhu 			return 0;
6023aab70afSSebastian Siewior 	}
6033aab70afSSebastian Siewior 
60490d27e7fSJason Zhu 	return str_len + name->expects_args;
60590d27e7fSJason Zhu }
6063aab70afSSebastian Siewior 
60790d27e7fSJason Zhu static void fb_add_string(char *dst, size_t chars_left,
60890d27e7fSJason Zhu 			  const char *str, const char *args)
60990d27e7fSJason Zhu {
61090d27e7fSJason Zhu 	if (!str)
61190d27e7fSJason Zhu 		return;
61290d27e7fSJason Zhu 
61390d27e7fSJason Zhu 	int ret = snprintf(dst, chars_left, str, args);
61490d27e7fSJason Zhu 
61590d27e7fSJason Zhu 	if (ret < 0)
61690d27e7fSJason Zhu 		pr_err("snprintf is error!");
61790d27e7fSJason Zhu }
61890d27e7fSJason Zhu 
61990d27e7fSJason Zhu static void fb_add_number(char *dst, size_t chars_left,
62090d27e7fSJason Zhu 			  const char *format, size_t num)
62190d27e7fSJason Zhu {
62290d27e7fSJason Zhu 	if (!format)
62390d27e7fSJason Zhu 		return;
62490d27e7fSJason Zhu 
62590d27e7fSJason Zhu 	int ret = snprintf(dst, chars_left, format, num);
62690d27e7fSJason Zhu 
62790d27e7fSJason Zhu 	if (ret > chars_left)
62890d27e7fSJason Zhu 		pr_err("snprintf is error!");
62990d27e7fSJason Zhu }
63090d27e7fSJason Zhu 
63190d27e7fSJason Zhu static int fb_read_var(char *cmd, char *response,
63290d27e7fSJason Zhu 		       fb_getvar_t var, size_t chars_left)
63390d27e7fSJason Zhu {
63490d27e7fSJason Zhu 	const char *s;
63590d27e7fSJason Zhu 	int ret = 0;
63690d27e7fSJason Zhu 
63790d27e7fSJason Zhu 	switch (var) {
63890d27e7fSJason Zhu 	case FB_VERSION:
63990d27e7fSJason Zhu 		fb_add_string(response, chars_left, FASTBOOT_VERSION, NULL);
64090d27e7fSJason Zhu 		break;
64190d27e7fSJason Zhu 	case FB_BOOTLOADER_VERSION:
64290d27e7fSJason Zhu 		fb_add_string(response, chars_left, U_BOOT_VERSION, NULL);
64390d27e7fSJason Zhu 		break;
64490d27e7fSJason Zhu 	case FB_BASEBAND_VERSION:
64590d27e7fSJason Zhu 		fb_add_string(response, chars_left, "N/A", NULL);
64690d27e7fSJason Zhu 		break;
64790d27e7fSJason Zhu 	case FB_PRODUCT:
64890d27e7fSJason Zhu 		fb_add_string(response, chars_left, CONFIG_SYS_BOARD, NULL);
64990d27e7fSJason Zhu 		break;
65090d27e7fSJason Zhu 	case FB_SERIAL_NO:
65100caae6dSSimon Glass 		s = env_get("serial#");
6523aab70afSSebastian Siewior 		if (s)
65390d27e7fSJason Zhu 			fb_add_string(response, chars_left, s, NULL);
6543aab70afSSebastian Siewior 		else
65590d27e7fSJason Zhu 			ret = -1;
65690d27e7fSJason Zhu 		break;
65790d27e7fSJason Zhu 	case FB_SECURE:
65890d27e7fSJason Zhu 		fb_add_string(response, chars_left, "yes", NULL);
65990d27e7fSJason Zhu 		break;
66090d27e7fSJason Zhu 	case FB_VARIANT:
66190d27e7fSJason Zhu 		fb_add_string(response, chars_left, "userdebug", NULL);
66290d27e7fSJason Zhu 		break;
66390d27e7fSJason Zhu 	case FB_DWNLD_SIZE:
66490d27e7fSJason Zhu 		fb_add_number(response, chars_left, "0x%08x",
66590d27e7fSJason Zhu 			      CONFIG_FASTBOOT_BUF_SIZE);
66690d27e7fSJason Zhu 		break;
66790d27e7fSJason Zhu 	case FB_PART_SIZE:
66890d27e7fSJason Zhu 	case FB_PART_TYPE: {
66990d27e7fSJason Zhu 		char *part_name = cmd;
670367cce4dSXu Hongfei 
67190d27e7fSJason Zhu 		cmd = strsep(&part_name, ":");
67290d27e7fSJason Zhu 		if (!cmd || !part_name) {
67390d27e7fSJason Zhu 			fb_add_string(response, chars_left,
67490d27e7fSJason Zhu 				      "argument Invalid!", NULL);
67590d27e7fSJason Zhu 			ret = -1;
67690d27e7fSJason Zhu 			break;
67790d27e7fSJason Zhu 		}
67890d27e7fSJason Zhu 
67990d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
68090d27e7fSJason Zhu 		disk_partition_t part_info;
68190d27e7fSJason Zhu 		struct blk_desc *dev_desc;
68290d27e7fSJason Zhu 		int part_num = -1;
68390d27e7fSJason Zhu 		const char *fs_type = NULL;
68490d27e7fSJason Zhu 
68590d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER
68690d27e7fSJason Zhu 		dev_desc = rockchip_get_bootdev();
68790d27e7fSJason Zhu #else
68890d27e7fSJason Zhu 		dev_desc = NULL;
68990d27e7fSJason Zhu #endif
69090d27e7fSJason Zhu 		if (!dev_desc) {
69190d27e7fSJason Zhu 			fb_add_string(response, chars_left,
69290d27e7fSJason Zhu 				      "block device not found", NULL);
69390d27e7fSJason Zhu 			ret = -1;
69490d27e7fSJason Zhu 			break;
69590d27e7fSJason Zhu 		}
69690d27e7fSJason Zhu 
69790d27e7fSJason Zhu 		part_num = part_get_info_by_name(dev_desc, part_name,
69890d27e7fSJason Zhu 						 &part_info);
69990d27e7fSJason Zhu 		if (part_num < 0) {
70090d27e7fSJason Zhu 			fb_add_string(response, chars_left,
70190d27e7fSJason Zhu 				      "partition not found", NULL);
70290d27e7fSJason Zhu 			ret = -1;
70390d27e7fSJason Zhu 		} else if (!strncmp(PARTITION_TYPE_STRINGS, cmd,
70490d27e7fSJason Zhu 					strlen(PARTITION_TYPE_STRINGS))) {
70590d27e7fSJason Zhu 			if (fb_get_fstype("mmc", part_num, &fs_type)) {
70690d27e7fSJason Zhu 				fb_add_string(response, chars_left,
70790d27e7fSJason Zhu 					      (char *)part_info.type, NULL);
70890d27e7fSJason Zhu 			} else {
70990d27e7fSJason Zhu 				fb_add_string(response, chars_left,
71090d27e7fSJason Zhu 					      fs_type, NULL);
71190d27e7fSJason Zhu 			}
71290d27e7fSJason Zhu 		} else if (!strncmp("partition-size", cmd, 14)) {
71390d27e7fSJason Zhu 			u64 part_size;
71490d27e7fSJason Zhu 
71590d27e7fSJason Zhu 			part_size = (uint64_t)part_info.size;
71690d27e7fSJason Zhu 			snprintf(response, chars_left, "0x%llx",
71790d27e7fSJason Zhu 				 part_size * dev_desc->blksz);
71890d27e7fSJason Zhu 		}
71990d27e7fSJason Zhu #else
72090d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
72190d27e7fSJason Zhu 		ret = -1;
72290d27e7fSJason Zhu #endif
72390d27e7fSJason Zhu 		break;
72490d27e7fSJason Zhu 	}
72590d27e7fSJason Zhu 	case FB_BLK_SIZE: {
72690d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
72790d27e7fSJason Zhu 		struct blk_desc *dev_desc;
72890d27e7fSJason Zhu 
72990d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER
73090d27e7fSJason Zhu 		dev_desc = rockchip_get_bootdev();
73190d27e7fSJason Zhu #else
73290d27e7fSJason Zhu 		dev_desc = NULL;
73390d27e7fSJason Zhu #endif
73490d27e7fSJason Zhu 		if (!dev_desc) {
73590d27e7fSJason Zhu 			fb_add_string(response, chars_left,
73690d27e7fSJason Zhu 				      "block device not found", NULL);
73790d27e7fSJason Zhu 			ret = -1;
73890d27e7fSJason Zhu 		} else {
73990d27e7fSJason Zhu 			fb_add_number(response, chars_left,
74090d27e7fSJason Zhu 				      "0x%lx", dev_desc->blksz);
74190d27e7fSJason Zhu 		}
74290d27e7fSJason Zhu #else
74390d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
74490d27e7fSJason Zhu 		ret = -1;
74590d27e7fSJason Zhu #endif
74690d27e7fSJason Zhu 		break;
74790d27e7fSJason Zhu 	}
74890d27e7fSJason Zhu 	case FB_ERASE_SIZE: {
74990d27e7fSJason Zhu #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
75090d27e7fSJason Zhu 		lbaint_t erase_grp_size;
75190d27e7fSJason Zhu 
75290d27e7fSJason Zhu 		erase_grp_size = fb_mmc_get_erase_grp_size();
75390d27e7fSJason Zhu 		if (erase_grp_size < 0) {
75490d27e7fSJason Zhu 			fb_add_string(response, chars_left,
75590d27e7fSJason Zhu 				      "block device not found", NULL);
75690d27e7fSJason Zhu 			ret = -1;
75790d27e7fSJason Zhu 		} else {
75890d27e7fSJason Zhu 			fb_add_number(response, chars_left, "0x"LBAF"",
75990d27e7fSJason Zhu 				      erase_grp_size);
76090d27e7fSJason Zhu 		}
76190d27e7fSJason Zhu #else
76290d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
76390d27e7fSJason Zhu 		ret = -1;
76490d27e7fSJason Zhu #endif
76590d27e7fSJason Zhu 		break;
76690d27e7fSJason Zhu 	}
76790d27e7fSJason Zhu 	case FB_UNLOCKED: {
76837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
76990d27e7fSJason Zhu 		uint8_t flash_lock_state = 0;
77090d27e7fSJason Zhu 
77190d27e7fSJason Zhu 		if (rk_avb_read_flash_lock_state(&flash_lock_state))
77290d27e7fSJason Zhu 			fb_add_string(response, chars_left, "yes", NULL);
77390d27e7fSJason Zhu 		else
77490d27e7fSJason Zhu 			fb_add_string(response, chars_left, "no", NULL);
77590d27e7fSJason Zhu #else
77690d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
77790d27e7fSJason Zhu 		ret = -1;
77890d27e7fSJason Zhu #endif
77990d27e7fSJason Zhu 		break;
78090d27e7fSJason Zhu 	}
78190d27e7fSJason Zhu 	case  FB_OFF_MODE_CHARGE: {
78290d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
78390d27e7fSJason Zhu 		break;
78490d27e7fSJason Zhu 	}
78590d27e7fSJason Zhu 	case FB_BATT_VOLTAGE: {
78690d27e7fSJason Zhu 		fb_add_string(response, chars_left, "not implemented", NULL);
78790d27e7fSJason Zhu 		break;
78890d27e7fSJason Zhu 	}
78990d27e7fSJason Zhu 	case FB_BATT_SOC_OK: {
79090d27e7fSJason Zhu 		fb_add_string(response, chars_left, "no", NULL);
79190d27e7fSJason Zhu 		break;
79290d27e7fSJason Zhu 	}
79390d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
79490d27e7fSJason Zhu 	case FB_HAS_COUNT: {
795367cce4dSXu Hongfei 		char slot_count[2];
796367cce4dSXu Hongfei 		char temp;
797367cce4dSXu Hongfei 
798367cce4dSXu Hongfei 		slot_count[1] = '\0';
79990d27e7fSJason Zhu 		if (rk_avb_read_slot_count(&temp) < 0) {
80090d27e7fSJason Zhu 			fb_add_number(response, chars_left, "%d", 0);
80190d27e7fSJason Zhu 			ret = -1;
80290d27e7fSJason Zhu 			break;
80390d27e7fSJason Zhu 		}
804367cce4dSXu Hongfei 		slot_count[0] = temp + 0x30;
80590d27e7fSJason Zhu 		fb_add_string(response, chars_left, slot_count, NULL);
80690d27e7fSJason Zhu 		break;
80790d27e7fSJason Zhu 	}
80890d27e7fSJason Zhu 	case FB_HAS_SLOT: {
80990d27e7fSJason Zhu 		char *part_name = cmd;
81090d27e7fSJason Zhu 		int has_slot = -1;
81190d27e7fSJason Zhu 
81290d27e7fSJason Zhu 		cmd = strsep(&part_name, ":");
81390d27e7fSJason Zhu 		if (!cmd || !part_name) {
81490d27e7fSJason Zhu 			fb_add_string(response, chars_left,
81590d27e7fSJason Zhu 				      "argument Invalid!", NULL);
81690d27e7fSJason Zhu 			ret = -1;
81790d27e7fSJason Zhu 			break;
81890d27e7fSJason Zhu 		}
81990d27e7fSJason Zhu 
82090d27e7fSJason Zhu 		has_slot = rk_avb_get_part_has_slot_info(part_name);
82190d27e7fSJason Zhu 		if (has_slot < 0)
82290d27e7fSJason Zhu 			fb_add_string(response, chars_left, "no", NULL);
82390d27e7fSJason Zhu 		else
82490d27e7fSJason Zhu 			fb_add_string(response, chars_left, "yes", NULL);
82590d27e7fSJason Zhu 		break;
82690d27e7fSJason Zhu 	}
82790d27e7fSJason Zhu 	case FB_CURR_SLOT: {
828367cce4dSXu Hongfei 		char slot_surrent[8] = {0};
829367cce4dSXu Hongfei 
83090d27e7fSJason Zhu 		if (!rk_avb_get_current_slot(slot_surrent)) {
83190d27e7fSJason Zhu 			fb_add_string(response, chars_left,
83290d27e7fSJason Zhu 				      slot_surrent + 1, NULL);
83390d27e7fSJason Zhu 		} else {
83490d27e7fSJason Zhu 			fb_add_string(response, chars_left, "get error", NULL);
83590d27e7fSJason Zhu 			ret = -1;
83690d27e7fSJason Zhu 		}
83790d27e7fSJason Zhu 		break;
83890d27e7fSJason Zhu 	}
83990d27e7fSJason Zhu 	case FB_SLOT_SUFFIXES: {
84090d27e7fSJason Zhu 		char slot_suffixes_temp[4] = {0};
84190d27e7fSJason Zhu 		char slot_suffixes[9] = {0};
842367cce4dSXu Hongfei 		int slot_cnt = 0;
843367cce4dSXu Hongfei 
84437a7bc39SJason Zhu 		rk_avb_read_slot_suffixes(slot_suffixes_temp);
845367cce4dSXu Hongfei 		while (slot_suffixes_temp[slot_cnt] != '\0') {
846367cce4dSXu Hongfei 			slot_suffixes[slot_cnt * 2]
847367cce4dSXu Hongfei 				= slot_suffixes_temp[slot_cnt];
848367cce4dSXu Hongfei 			slot_suffixes[slot_cnt * 2 + 1] = ',';
849367cce4dSXu Hongfei 			slot_cnt++;
850367cce4dSXu Hongfei 		}
851367cce4dSXu Hongfei 
85290d27e7fSJason Zhu 		slot_suffixes[(slot_cnt - 1) * 2 + 1] = '\0';
85390d27e7fSJason Zhu 		fb_add_string(response, chars_left, slot_suffixes, NULL);
85490d27e7fSJason Zhu 		break;
855367cce4dSXu Hongfei 	}
85690d27e7fSJason Zhu 	case FB_SLOT_SUCCESSFUL:{
857374a9995SCody Xie 		char *slot_name = cmd;
85890d27e7fSJason Zhu 		AvbABData ab_info;
859374a9995SCody Xie 
860374a9995SCody Xie 		cmd = strsep(&slot_name, ":");
86190d27e7fSJason Zhu 		if (!cmd || !slot_name) {
86290d27e7fSJason Zhu 			fb_add_string(response, chars_left,
86390d27e7fSJason Zhu 				      "argument Invalid!", NULL);
86490d27e7fSJason Zhu 			ret = -1;
86590d27e7fSJason Zhu 			break;
866374a9995SCody Xie 		}
867374a9995SCody Xie 
86890d27e7fSJason Zhu 		if (rk_avb_get_ab_info(&ab_info) < 0) {
86990d27e7fSJason Zhu 			fb_add_string(response, chars_left,
87090d27e7fSJason Zhu 				      "get ab info failed!", NULL);
87190d27e7fSJason Zhu 			ret = -1;
87290d27e7fSJason Zhu 			break;
873374a9995SCody Xie 		}
874374a9995SCody Xie 
875374a9995SCody Xie 		if (!strcmp(slot_name, "a")) {
87690d27e7fSJason Zhu 			if (ab_info.slots[0].successful_boot)
87790d27e7fSJason Zhu 				fb_add_string(response, chars_left,
87890d27e7fSJason Zhu 					      "yes", NULL);
87990d27e7fSJason Zhu 			else
88090d27e7fSJason Zhu 				fb_add_string(response, chars_left,
88190d27e7fSJason Zhu 					      "no", NULL);
882374a9995SCody Xie 		} else if (!strcmp(slot_name, "b")) {
88390d27e7fSJason Zhu 			if (ab_info.slots[1].successful_boot)
88490d27e7fSJason Zhu 				fb_add_string(response, chars_left,
88590d27e7fSJason Zhu 					      "yes", NULL);
88690d27e7fSJason Zhu 			else
88790d27e7fSJason Zhu 				fb_add_string(response, chars_left,
88890d27e7fSJason Zhu 					      "no", NULL);
88990d27e7fSJason Zhu 		} else {
89090d27e7fSJason Zhu 			fb_add_string(response, chars_left, "no", NULL);
89190d27e7fSJason Zhu 		}
89290d27e7fSJason Zhu 		break;
89390d27e7fSJason Zhu 	}
89490d27e7fSJason Zhu 	case FB_SLOT_UNBOOTABLE: {
89590d27e7fSJason Zhu 		char *slot_name = cmd;
89690d27e7fSJason Zhu 		AvbABData ab_info;
89790d27e7fSJason Zhu 
89890d27e7fSJason Zhu 		cmd = strsep(&slot_name, ":");
89990d27e7fSJason Zhu 
90090d27e7fSJason Zhu 		if (!cmd || !slot_name) {
90190d27e7fSJason Zhu 			fb_add_string(response, chars_left,
90290d27e7fSJason Zhu 				      "argument Invalid!", NULL);
90390d27e7fSJason Zhu 			ret = -1;
90490d27e7fSJason Zhu 			break;
90590d27e7fSJason Zhu 		}
90690d27e7fSJason Zhu 
90790d27e7fSJason Zhu 		if (rk_avb_get_ab_info(&ab_info) < 0) {
90890d27e7fSJason Zhu 			fb_add_string(response, chars_left,
90990d27e7fSJason Zhu 				      "get ab info failed!", NULL);
91090d27e7fSJason Zhu 			ret = -1;
91190d27e7fSJason Zhu 			break;
91290d27e7fSJason Zhu 		}
91390d27e7fSJason Zhu 
91490d27e7fSJason Zhu 		if (!strcmp(slot_name, "a")) {
91590d27e7fSJason Zhu 			if (!ab_info.slots[0].successful_boot &&
91690d27e7fSJason Zhu 			    !ab_info.slots[0].tries_remaining &&
91790d27e7fSJason Zhu 			    !ab_info.slots[0].priority)
91890d27e7fSJason Zhu 				fb_add_string(response, chars_left,
91990d27e7fSJason Zhu 					      "yes", NULL);
92090d27e7fSJason Zhu 			else
92190d27e7fSJason Zhu 				fb_add_string(response, chars_left, "no", NULL);
92290d27e7fSJason Zhu 		} else if (!strcmp(slot_name, "b")) {
92390d27e7fSJason Zhu 			if (!ab_info.slots[1].successful_boot &&
92490d27e7fSJason Zhu 			    !ab_info.slots[1].tries_remaining &&
92590d27e7fSJason Zhu 			    !ab_info.slots[1].priority)
92690d27e7fSJason Zhu 				fb_add_string(response, chars_left,
92790d27e7fSJason Zhu 					      "yes", NULL);
92890d27e7fSJason Zhu 			else
92990d27e7fSJason Zhu 				fb_add_string(response, chars_left, "no", NULL);
93090d27e7fSJason Zhu 		} else {
93190d27e7fSJason Zhu 			fb_add_string(response, chars_left, "no", NULL);
93290d27e7fSJason Zhu 		}
93390d27e7fSJason Zhu 		break;
93490d27e7fSJason Zhu 	}
93590d27e7fSJason Zhu 	case FB_SLOT_RETRY_COUNT: {
93690d27e7fSJason Zhu 		char *slot_name = cmd;
93790d27e7fSJason Zhu 		AvbABData ab_info;
93890d27e7fSJason Zhu 
93990d27e7fSJason Zhu 		cmd = strsep(&slot_name, ":");
94090d27e7fSJason Zhu 		if (!cmd || !slot_name) {
94190d27e7fSJason Zhu 			fb_add_string(response, chars_left,
94290d27e7fSJason Zhu 				      "argument Invalid!", NULL);
94390d27e7fSJason Zhu 			ret = -1;
94490d27e7fSJason Zhu 			break;
94590d27e7fSJason Zhu 		}
94690d27e7fSJason Zhu 
94790d27e7fSJason Zhu 		if (rk_avb_get_ab_info(&ab_info) < 0) {
94890d27e7fSJason Zhu 			fb_add_string(response, chars_left,
94990d27e7fSJason Zhu 				      "get ab info failed!", NULL);
95090d27e7fSJason Zhu 			ret = -1;
95190d27e7fSJason Zhu 			break;
95290d27e7fSJason Zhu 		}
95390d27e7fSJason Zhu 
95490d27e7fSJason Zhu 		if (!strcmp(slot_name, "a")) {
95590d27e7fSJason Zhu 			fb_add_number(response, chars_left,
95690d27e7fSJason Zhu 				      "%d", ab_info.slots[0].tries_remaining);
95790d27e7fSJason Zhu 		} else if (!strcmp(slot_name, "b")) {
95890d27e7fSJason Zhu 			fb_add_number(response, chars_left, "%d",
95990d27e7fSJason Zhu 				      ab_info.slots[1].tries_remaining);
96090d27e7fSJason Zhu 
961374a9995SCody Xie 		} else {
962374a9995SCody Xie 			strcpy(response, "FAILno");
963374a9995SCody Xie 		}
96490d27e7fSJason Zhu 		break;
965367cce4dSXu Hongfei 	}
96690d27e7fSJason Zhu 	case FB_AT_VBST: {
96790d27e7fSJason Zhu 		char vbst[VBOOT_STATE_SIZE] = {0};
96890d27e7fSJason Zhu 		char *p_vbst;
969f61a997eSqiujian 
97090d27e7fSJason Zhu 		strcpy(response, "INFO");
97190d27e7fSJason Zhu 		rk_avb_get_at_vboot_state(vbst);
97290d27e7fSJason Zhu 		p_vbst = vbst;
97390d27e7fSJason Zhu 		do {
97490d27e7fSJason Zhu 			cmd = strsep(&p_vbst, "\n");
97590d27e7fSJason Zhu 			if (strlen(cmd) > 0) {
97690d27e7fSJason Zhu 				memcpy(&response[4], cmd, chars_left);
97790d27e7fSJason Zhu 				fastboot_tx_write_str(response);
978f61a997eSqiujian 			}
97990d27e7fSJason Zhu 		} while (strlen(cmd));
98090d27e7fSJason Zhu 		break;
98190d27e7fSJason Zhu 	}
98290d27e7fSJason Zhu #endif
98390d27e7fSJason Zhu #ifdef CONFIG_OPTEE_CLIENT
98490d27e7fSJason Zhu 	case FB_AT_DH: {
98590d27e7fSJason Zhu 		char dhbuf[ATTEST_DH_SIZE];
98690d27e7fSJason Zhu 		uint32_t dh_len = ATTEST_DH_SIZE;
98790d27e7fSJason Zhu 		uint32_t res = trusty_attest_dh((uint8_t *)dhbuf, &dh_len);
988f61a997eSqiujian 
98990d27e7fSJason Zhu 		if (res) {
99090d27e7fSJason Zhu 			fb_add_string(response, chars_left, "dh not set", NULL);
99190d27e7fSJason Zhu 			ret = -1;
9923aab70afSSebastian Siewior 		} else {
99390d27e7fSJason Zhu 			fb_add_string(response, chars_left, dhbuf, NULL);
99490d27e7fSJason Zhu 		}
99590d27e7fSJason Zhu 		break;
99690d27e7fSJason Zhu 	}
99790d27e7fSJason Zhu 	case FB_AT_UUID: {
99890d27e7fSJason Zhu 		char uuid[ATTEST_UUID_SIZE] = {0};
99990d27e7fSJason Zhu 		uint32_t uuid_len = ATTEST_UUID_SIZE;
100090d27e7fSJason Zhu 		uint32_t res = trusty_attest_uuid((uint8_t *)uuid, &uuid_len);
100190d27e7fSJason Zhu 
100290d27e7fSJason Zhu 		uuid[ATTEST_UUID_SIZE - 1] = 0;
100390d27e7fSJason Zhu 		if (res) {
100490d27e7fSJason Zhu 			fb_add_string(response, chars_left, "dh not set", NULL);
100590d27e7fSJason Zhu 			ret = -1;
100690d27e7fSJason Zhu 		} else {
100790d27e7fSJason Zhu 			fb_add_string(response, chars_left, uuid, NULL);
100890d27e7fSJason Zhu 		}
100990d27e7fSJason Zhu 		break;
101090d27e7fSJason Zhu 	}
101190d27e7fSJason Zhu #endif
101290d27e7fSJason Zhu 	default: {
1013b1f2a17cSnicolas.le.bayon@st.com 			char *envstr;
101474322201SRob Herring 
1015b1f2a17cSnicolas.le.bayon@st.com 			envstr = malloc(strlen("fastboot.") + strlen(cmd) + 1);
1016b1f2a17cSnicolas.le.bayon@st.com 			if (!envstr) {
101790d27e7fSJason Zhu 				fb_add_string(response, chars_left,
101890d27e7fSJason Zhu 					      "malloc error", NULL);
101990d27e7fSJason Zhu 				ret = -1;
102090d27e7fSJason Zhu 				break;
1021b1f2a17cSnicolas.le.bayon@st.com 			}
1022b1f2a17cSnicolas.le.bayon@st.com 
1023b1f2a17cSnicolas.le.bayon@st.com 			sprintf(envstr, "fastboot.%s", cmd);
102400caae6dSSimon Glass 			s = env_get(envstr);
102574322201SRob Herring 			if (s) {
102674322201SRob Herring 				strncat(response, s, chars_left);
102774322201SRob Herring 			} else {
1028a18c2706SSteve Rae 				printf("WARNING: unknown variable: %s\n", cmd);
102990d27e7fSJason Zhu 				fb_add_string(response, chars_left,
103090d27e7fSJason Zhu 					      "not implemented", NULL);
10313aab70afSSebastian Siewior 			}
1032b1f2a17cSnicolas.le.bayon@st.com 
1033b1f2a17cSnicolas.le.bayon@st.com 			free(envstr);
103490d27e7fSJason Zhu 			break;
103590d27e7fSJason Zhu 		}
103690d27e7fSJason Zhu 	}
103790d27e7fSJason Zhu 
103890d27e7fSJason Zhu 	return ret;
103990d27e7fSJason Zhu }
104090d27e7fSJason Zhu 
104190d27e7fSJason Zhu static const struct {
104290d27e7fSJason Zhu 	/*
104390d27e7fSJason Zhu 	 *any changes to this array require an update to the corresponding
104490d27e7fSJason Zhu 	 *enum in fastboot.h
104590d27e7fSJason Zhu 	 */
104690d27e7fSJason Zhu 	struct name_string name;
104790d27e7fSJason Zhu 	fb_getvar_t var;
104890d27e7fSJason Zhu } getvar_table[] = {
104990d27e7fSJason Zhu 	{ NAME_NO_ARGS("version"), FB_VERSION},
105090d27e7fSJason Zhu 	{ NAME_NO_ARGS("version-bootloader"), FB_BOOTLOADER_VERSION},
105190d27e7fSJason Zhu 	{ NAME_NO_ARGS("version-baseband"), FB_BASEBAND_VERSION},
105290d27e7fSJason Zhu 	{ NAME_NO_ARGS("product"), FB_PRODUCT},
105390d27e7fSJason Zhu 	{ NAME_NO_ARGS("serialno"), FB_SERIAL_NO},
105490d27e7fSJason Zhu 	{ NAME_NO_ARGS("secure"), FB_SECURE},
105590d27e7fSJason Zhu 	{ NAME_NO_ARGS("max-download-size"), FB_DWNLD_SIZE},
105690d27e7fSJason Zhu 	{ NAME_NO_ARGS("logical-block-size"), FB_BLK_SIZE},
105790d27e7fSJason Zhu 	{ NAME_NO_ARGS("erase-block-size"), FB_ERASE_SIZE},
105890d27e7fSJason Zhu 	{ NAME_ARGS("partition-type", ':'), FB_PART_TYPE},
105990d27e7fSJason Zhu 	{ NAME_ARGS("partition-size", ':'), FB_PART_SIZE},
106090d27e7fSJason Zhu 	{ NAME_NO_ARGS("unlocked"), FB_UNLOCKED},
106190d27e7fSJason Zhu 	{ NAME_NO_ARGS("off-mode-charge"), FB_OFF_MODE_CHARGE},
106290d27e7fSJason Zhu 	{ NAME_NO_ARGS("battery-voltage"), FB_BATT_VOLTAGE},
106390d27e7fSJason Zhu 	{ NAME_NO_ARGS("variant"), FB_VARIANT},
106490d27e7fSJason Zhu 	{ NAME_NO_ARGS("battery-soc-ok"), FB_BATT_SOC_OK},
106590d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
106690d27e7fSJason Zhu 	/* Slots related */
106790d27e7fSJason Zhu 	{ NAME_NO_ARGS("slot-count"), FB_HAS_COUNT},
106890d27e7fSJason Zhu 	{ NAME_ARGS("has-slot", ':'), FB_HAS_SLOT},
106990d27e7fSJason Zhu 	{ NAME_NO_ARGS("current-slot"), FB_CURR_SLOT},
107090d27e7fSJason Zhu 	{ NAME_NO_ARGS("slot-suffixes"), FB_SLOT_SUFFIXES},
107190d27e7fSJason Zhu 	{ NAME_ARGS("slot-successful", ':'), FB_SLOT_SUCCESSFUL},
107290d27e7fSJason Zhu 	{ NAME_ARGS("slot-unbootable", ':'), FB_SLOT_UNBOOTABLE},
107390d27e7fSJason Zhu 	{ NAME_ARGS("slot-retry-count", ':'), FB_SLOT_RETRY_COUNT},
107490d27e7fSJason Zhu 	{ NAME_NO_ARGS("at-vboot-state"), FB_AT_VBST},
107590d27e7fSJason Zhu #endif
107690d27e7fSJason Zhu 	/*
107790d27e7fSJason Zhu 	 * OEM specific :
107890d27e7fSJason Zhu 	 * Spec says names starting with lowercase letter are reserved.
107990d27e7fSJason Zhu 	 */
108090d27e7fSJason Zhu #ifdef CONFIG_OPTEE_CLIENT
108190d27e7fSJason Zhu 	{ NAME_NO_ARGS("at-attest-dh"), FB_AT_DH},
108290d27e7fSJason Zhu 	{ NAME_NO_ARGS("at-attest-uuid"), FB_AT_UUID},
108390d27e7fSJason Zhu #endif
108490d27e7fSJason Zhu };
108590d27e7fSJason Zhu 
108690d27e7fSJason Zhu static int fb_getvar_single(char *cmd, char *response, size_t chars_left)
108790d27e7fSJason Zhu {
108890d27e7fSJason Zhu 	int i;
108990d27e7fSJason Zhu 	size_t match_len = 0;
109090d27e7fSJason Zhu 	size_t len = strlen(cmd);
109190d27e7fSJason Zhu 
109290d27e7fSJason Zhu 	for (i = 0; i < ARRAY_SIZE(getvar_table); i++) {
109390d27e7fSJason Zhu 		match_len = name_check_match(cmd, len, &getvar_table[i].name);
109490d27e7fSJason Zhu 		if (match_len)
109590d27e7fSJason Zhu 			break;
109690d27e7fSJason Zhu 	}
109790d27e7fSJason Zhu 
109890d27e7fSJason Zhu 	if (match_len == 0) {
109990d27e7fSJason Zhu 		fb_add_string(response, chars_left, "unknown variable", NULL);
110090d27e7fSJason Zhu 		return -1;
110190d27e7fSJason Zhu 	}
110290d27e7fSJason Zhu 
110390d27e7fSJason Zhu 	if (fb_read_var(cmd, response, getvar_table[i].var, chars_left) < 0)
110490d27e7fSJason Zhu 		return -1;
110590d27e7fSJason Zhu 
110690d27e7fSJason Zhu 	return 0;
110790d27e7fSJason Zhu }
110890d27e7fSJason Zhu 
110990d27e7fSJason Zhu static void fb_getvar_all(void)
111090d27e7fSJason Zhu {
111190d27e7fSJason Zhu 	char response[FASTBOOT_RESPONSE_LEN] = {0};
111290d27e7fSJason Zhu 	char resp_tmp[FASTBOOT_RESPONSE_LEN] = {0};
111390d27e7fSJason Zhu 	char *actual_resp;
111490d27e7fSJason Zhu 	size_t chars_left;
111590d27e7fSJason Zhu 	int i, p;
111690d27e7fSJason Zhu 	disk_partition_t part_info;
111790d27e7fSJason Zhu 	struct blk_desc *dev_desc;
111890d27e7fSJason Zhu 
111990d27e7fSJason Zhu 	strcpy(response, "INFO");
112090d27e7fSJason Zhu 	chars_left = sizeof(response) - strlen(response) - 1;
112190d27e7fSJason Zhu 	actual_resp = response + strlen(response);
112290d27e7fSJason Zhu 
112390d27e7fSJason Zhu 	for (i = 0; i < ARRAY_SIZE(getvar_table); i++) {
112490d27e7fSJason Zhu 		fb_getvar_t var = getvar_table[i].var;
112590d27e7fSJason Zhu 
112690d27e7fSJason Zhu 		switch (var) {
112790d27e7fSJason Zhu 		case FB_PART_TYPE:
112890d27e7fSJason Zhu 		case FB_PART_SIZE: {
112990d27e7fSJason Zhu 			const char *fs_type = NULL;
113090d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER
113190d27e7fSJason Zhu 			dev_desc = rockchip_get_bootdev();
113290d27e7fSJason Zhu #else
113390d27e7fSJason Zhu 			dev_desc = NULL;
113490d27e7fSJason Zhu #endif
113590d27e7fSJason Zhu 			if (!dev_desc) {
113690d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
113790d27e7fSJason Zhu 					      "%s:block device not found",
113890d27e7fSJason Zhu 					      getvar_table[i].name.str);
113990d27e7fSJason Zhu 				fastboot_tx_write_str(response);
114090d27e7fSJason Zhu 				break;
114190d27e7fSJason Zhu 			}
114290d27e7fSJason Zhu 
114390d27e7fSJason Zhu 			for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
114490d27e7fSJason Zhu 				if (part_get_info(dev_desc, p,
114590d27e7fSJason Zhu 						  &part_info) < 0) {
114690d27e7fSJason Zhu 					break;
114790d27e7fSJason Zhu 				}
114890d27e7fSJason Zhu 
114990d27e7fSJason Zhu 				if (var == FB_PART_TYPE) {
115090d27e7fSJason Zhu 					fs_type = NULL;
115190d27e7fSJason Zhu 					if (fb_get_fstype("mmc", p,
115290d27e7fSJason Zhu 							  &fs_type)) {
115390d27e7fSJason Zhu 						fb_add_string(
115490d27e7fSJason Zhu 							resp_tmp,
115590d27e7fSJason Zhu 							FASTBOOT_RESPONSE_LEN,
115690d27e7fSJason Zhu 							(char *)part_info.type,
115790d27e7fSJason Zhu 							NULL);
115890d27e7fSJason Zhu 					} else {
115990d27e7fSJason Zhu 						fb_add_string(
116090d27e7fSJason Zhu 							resp_tmp,
116190d27e7fSJason Zhu 							FASTBOOT_RESPONSE_LEN,
116290d27e7fSJason Zhu 							fs_type,
116390d27e7fSJason Zhu 							NULL);
116490d27e7fSJason Zhu 					}
116590d27e7fSJason Zhu 
116690d27e7fSJason Zhu 					snprintf(actual_resp,
116790d27e7fSJason Zhu 						 chars_left,
116890d27e7fSJason Zhu 						 "%s:%s:%s",
116990d27e7fSJason Zhu 						 getvar_table[i].name.str,
117090d27e7fSJason Zhu 						 part_info.name,
117190d27e7fSJason Zhu 						 resp_tmp);
117290d27e7fSJason Zhu 				} else {
117390d27e7fSJason Zhu 					uint64_t part_size;
117490d27e7fSJason Zhu 
117590d27e7fSJason Zhu 					part_size = (uint64_t)part_info.size;
117690d27e7fSJason Zhu 					snprintf(actual_resp,
117790d27e7fSJason Zhu 						 chars_left,
117890d27e7fSJason Zhu 						 "%s:%s:0x%llx",
117990d27e7fSJason Zhu 						 getvar_table[i].name.str,
118090d27e7fSJason Zhu 						 part_info.name,
118190d27e7fSJason Zhu 						 part_size * dev_desc->blksz);
118274322201SRob Herring 				}
11833aab70afSSebastian Siewior 				fastboot_tx_write_str(response);
11843aab70afSSebastian Siewior 			}
118590d27e7fSJason Zhu 			break;
118690d27e7fSJason Zhu 		}
118790d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
118890d27e7fSJason Zhu 		case FB_HAS_SLOT: {
118990d27e7fSJason Zhu 			uchar *ptr_name_tmp;
119090d27e7fSJason Zhu 			char c = '_';
119190d27e7fSJason Zhu 			int has_slot = -1;
119290d27e7fSJason Zhu 
119390d27e7fSJason Zhu #ifdef CONFIG_RKIMG_BOOTLOADER
119490d27e7fSJason Zhu 			dev_desc = rockchip_get_bootdev();
119590d27e7fSJason Zhu #else
119690d27e7fSJason Zhu 			dev_desc = NULL;
119790d27e7fSJason Zhu #endif
119890d27e7fSJason Zhu 			if (!dev_desc) {
119990d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
120090d27e7fSJason Zhu 					      "%s:block device not found",
120190d27e7fSJason Zhu 					      getvar_table[i].name.str);
120290d27e7fSJason Zhu 				fastboot_tx_write_str(response);
120390d27e7fSJason Zhu 				break;
120490d27e7fSJason Zhu 			}
120590d27e7fSJason Zhu 
120690d27e7fSJason Zhu 			for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
120790d27e7fSJason Zhu 				if (part_get_info(dev_desc, p,
120890d27e7fSJason Zhu 						  &part_info) < 0) {
120990d27e7fSJason Zhu 					break;
121090d27e7fSJason Zhu 				} else {
121190d27e7fSJason Zhu 					ptr_name_tmp = (uchar *)strrchr(
121290d27e7fSJason Zhu 						(char *)part_info.name, c);
121390d27e7fSJason Zhu 					if (ptr_name_tmp &&
121490d27e7fSJason Zhu 					    part_info.name[ptr_name_tmp -
121590d27e7fSJason Zhu 						part_info.name + 2] == '\0')
121690d27e7fSJason Zhu 						fb_add_string(
121790d27e7fSJason Zhu 							resp_tmp,
121890d27e7fSJason Zhu 							ptr_name_tmp -
121990d27e7fSJason Zhu 							part_info.name + 1,
122090d27e7fSJason Zhu 							(char *)part_info.name,
122190d27e7fSJason Zhu 							NULL);
122290d27e7fSJason Zhu 					else
122390d27e7fSJason Zhu 						strcpy(resp_tmp,
122490d27e7fSJason Zhu 						       (char *)part_info.name);
122590d27e7fSJason Zhu 
122690d27e7fSJason Zhu 					has_slot = rk_avb_get_part_has_slot_info(
122790d27e7fSJason Zhu 						resp_tmp);
122890d27e7fSJason Zhu 					if (has_slot < 0) {
122990d27e7fSJason Zhu 						snprintf(actual_resp,
123090d27e7fSJason Zhu 							 chars_left,
123190d27e7fSJason Zhu 							 "%s:%s:no",
123290d27e7fSJason Zhu 							 getvar_table[i].name.str,
123390d27e7fSJason Zhu 							 resp_tmp);
123490d27e7fSJason Zhu 					} else {
123590d27e7fSJason Zhu 						snprintf(actual_resp,
123690d27e7fSJason Zhu 							 chars_left,
123790d27e7fSJason Zhu 							 "%s:%s:yes",
123890d27e7fSJason Zhu 							 getvar_table[i].name.str,
123990d27e7fSJason Zhu 							 resp_tmp);
124090d27e7fSJason Zhu 						p++;
124190d27e7fSJason Zhu 					}
124290d27e7fSJason Zhu 
124390d27e7fSJason Zhu 					fastboot_tx_write_str(response);
124490d27e7fSJason Zhu 				}
124590d27e7fSJason Zhu 			}
124690d27e7fSJason Zhu 			break;
124790d27e7fSJason Zhu 		}
124890d27e7fSJason Zhu 
124990d27e7fSJason Zhu 		case FB_SLOT_SUCCESSFUL: {
125090d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
125190d27e7fSJason Zhu 			AvbABData ab_info;
125290d27e7fSJason Zhu 
125390d27e7fSJason Zhu 			if (rk_avb_get_ab_info(&ab_info) < 0) {
125490d27e7fSJason Zhu 				fb_add_string(actual_resp,
125590d27e7fSJason Zhu 					      chars_left,
125690d27e7fSJason Zhu 					      "%s:get ab info failed!",
125790d27e7fSJason Zhu 					      getvar_table[i].name.str);
125890d27e7fSJason Zhu 				fastboot_tx_write_str(response);
125990d27e7fSJason Zhu 				break;
126090d27e7fSJason Zhu 			}
126190d27e7fSJason Zhu 
126290d27e7fSJason Zhu 			if (ab_info.slots[0].successful_boot)
126390d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
126490d27e7fSJason Zhu 					      "%s:a:yes",
126590d27e7fSJason Zhu 					      getvar_table[i].name.str);
126690d27e7fSJason Zhu 			else
126790d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
126890d27e7fSJason Zhu 					      "%s:a:no",
126990d27e7fSJason Zhu 					      getvar_table[i].name.str);
127090d27e7fSJason Zhu 			fastboot_tx_write_str(response);
127190d27e7fSJason Zhu 
127290d27e7fSJason Zhu 			if (ab_info.slots[1].successful_boot)
127390d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
127490d27e7fSJason Zhu 					      "%s:b:yes",
127590d27e7fSJason Zhu 					      getvar_table[i].name.str);
127690d27e7fSJason Zhu 			else
127790d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
127890d27e7fSJason Zhu 					      "%s:b:no",
127990d27e7fSJason Zhu 					      getvar_table[i].name.str);
128090d27e7fSJason Zhu 			fastboot_tx_write_str(response);
128190d27e7fSJason Zhu #else
128290d27e7fSJason Zhu 			fb_add_string(actual_resp, chars_left,
128390d27e7fSJason Zhu 				      "%s:not find ab info!",
128490d27e7fSJason Zhu 				      getvar_table[i].name.str);
128590d27e7fSJason Zhu 			fastboot_tx_write_str(response);
128690d27e7fSJason Zhu #endif
128790d27e7fSJason Zhu 			break;
128890d27e7fSJason Zhu 		}
128990d27e7fSJason Zhu 
129090d27e7fSJason Zhu 		case FB_SLOT_UNBOOTABLE: {
129190d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
129290d27e7fSJason Zhu 			AvbABData ab_info;
129390d27e7fSJason Zhu 
129490d27e7fSJason Zhu 			if (rk_avb_get_ab_info(&ab_info) < 0) {
129590d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
129690d27e7fSJason Zhu 					      "%s:not find ab info!",
129790d27e7fSJason Zhu 					      getvar_table[i].name.str);
129890d27e7fSJason Zhu 				fastboot_tx_write_str(response);
129990d27e7fSJason Zhu 				break;
130090d27e7fSJason Zhu 			}
130190d27e7fSJason Zhu 
130290d27e7fSJason Zhu 			if (!ab_info.slots[0].successful_boot &&
130390d27e7fSJason Zhu 			    !ab_info.slots[0].tries_remaining &&
130490d27e7fSJason Zhu 			    !ab_info.slots[0].priority)
130590d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
130690d27e7fSJason Zhu 					      "%s:a:yes",
130790d27e7fSJason Zhu 					      getvar_table[i].name.str);
130890d27e7fSJason Zhu 			else
130990d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
131090d27e7fSJason Zhu 					      "%s:a:no",
131190d27e7fSJason Zhu 					      getvar_table[i].name.str);
131290d27e7fSJason Zhu 			fastboot_tx_write_str(response);
131390d27e7fSJason Zhu 
131490d27e7fSJason Zhu 			if (!ab_info.slots[1].successful_boot &&
131590d27e7fSJason Zhu 			    !ab_info.slots[1].tries_remaining &&
131690d27e7fSJason Zhu 			    !ab_info.slots[1].priority)
131790d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
131890d27e7fSJason Zhu 					      "%s:b:yes",
131990d27e7fSJason Zhu 					      getvar_table[i].name.str);
132090d27e7fSJason Zhu 			else
132190d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
132290d27e7fSJason Zhu 					      "%s:b:no",
132390d27e7fSJason Zhu 					      getvar_table[i].name.str);
132490d27e7fSJason Zhu 
132590d27e7fSJason Zhu 			fastboot_tx_write_str(response);
132690d27e7fSJason Zhu #else
132790d27e7fSJason Zhu 			fb_add_string(actual_resp, chars_left,
132890d27e7fSJason Zhu 				      "%s:not find ab info!",
132990d27e7fSJason Zhu 				      getvar_table[i].name.str);
133090d27e7fSJason Zhu 			fastboot_tx_write_str(response);
133190d27e7fSJason Zhu #endif
133290d27e7fSJason Zhu 			break;
133390d27e7fSJason Zhu 		}
133490d27e7fSJason Zhu 
133590d27e7fSJason Zhu 		case FB_SLOT_RETRY_COUNT: {
133690d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
133790d27e7fSJason Zhu 			AvbABData ab_info;
133890d27e7fSJason Zhu 
133990d27e7fSJason Zhu 			if (rk_avb_get_ab_info(&ab_info) < 0) {
134090d27e7fSJason Zhu 				fb_add_string(actual_resp, chars_left,
134190d27e7fSJason Zhu 					      "%s:not find ab info!",
134290d27e7fSJason Zhu 					      getvar_table[i].name.str);
134390d27e7fSJason Zhu 				fastboot_tx_write_str(response);
134490d27e7fSJason Zhu 				break;
134590d27e7fSJason Zhu 			}
134690d27e7fSJason Zhu 
134790d27e7fSJason Zhu 			snprintf(actual_resp, chars_left, "%s:a:%d",
134890d27e7fSJason Zhu 				 getvar_table[i].name.str,
134990d27e7fSJason Zhu 				 ab_info.slots[1].tries_remaining);
135090d27e7fSJason Zhu 			fastboot_tx_write_str(response);
135190d27e7fSJason Zhu 			snprintf(actual_resp, chars_left, "%s:b:%d",
135290d27e7fSJason Zhu 				 getvar_table[i].name.str,
135390d27e7fSJason Zhu 				 ab_info.slots[1].tries_remaining);
135490d27e7fSJason Zhu 			fastboot_tx_write_str(response);
135590d27e7fSJason Zhu #else
135690d27e7fSJason Zhu 			fb_add_string(actual_resp, chars_left,
135790d27e7fSJason Zhu 				      "%s:not find ab info!",
135890d27e7fSJason Zhu 				      getvar_table[i].name.str);
135990d27e7fSJason Zhu 			fastboot_tx_write_str(response);
136090d27e7fSJason Zhu #endif
136190d27e7fSJason Zhu 			break;
136290d27e7fSJason Zhu 		}
136390d27e7fSJason Zhu #endif
136490d27e7fSJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
136590d27e7fSJason Zhu 		case FB_AT_VBST:
136690d27e7fSJason Zhu 			break;
136790d27e7fSJason Zhu #endif
136890d27e7fSJason Zhu 		default:
136990d27e7fSJason Zhu 			fb_getvar_single((char *)getvar_table[i].name.str,
137090d27e7fSJason Zhu 					 resp_tmp, FASTBOOT_RESPONSE_LEN);
137190d27e7fSJason Zhu 			snprintf(actual_resp, chars_left, "%s:%s",
137290d27e7fSJason Zhu 				 getvar_table[i].name.str, resp_tmp);
137390d27e7fSJason Zhu 			fastboot_tx_write_str(response);
137490d27e7fSJason Zhu 		}
137590d27e7fSJason Zhu 	}
137690d27e7fSJason Zhu }
137790d27e7fSJason Zhu 
137890d27e7fSJason Zhu static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
137990d27e7fSJason Zhu {
138090d27e7fSJason Zhu 	char *cmd = req->buf;
138190d27e7fSJason Zhu 	char response[FASTBOOT_RESPONSE_LEN] = {0};
138290d27e7fSJason Zhu 	const char *str_read_all = "all";
138390d27e7fSJason Zhu 	size_t len = 0;
138490d27e7fSJason Zhu 	size_t chars_left;
138590d27e7fSJason Zhu 
138690d27e7fSJason Zhu 	strsep(&cmd, ":");
138790d27e7fSJason Zhu 	if (!cmd) {
138890d27e7fSJason Zhu 		pr_err("missing variable");
138990d27e7fSJason Zhu 		fastboot_tx_write_str("FAILmissing var");
139090d27e7fSJason Zhu 		return;
139190d27e7fSJason Zhu 	}
139290d27e7fSJason Zhu 
139390d27e7fSJason Zhu 	len = strlen(cmd);
139490d27e7fSJason Zhu 	if (len == strlen(str_read_all) &&
139590d27e7fSJason Zhu 	    (strncmp(cmd, str_read_all, len) == 0)) {
139690d27e7fSJason Zhu 		fb_getvar_all();
139790d27e7fSJason Zhu 		fastboot_tx_write_str("OKAYDone!");
139890d27e7fSJason Zhu 	} else {
139990d27e7fSJason Zhu 		strcpy(response, "OKAY");
140090d27e7fSJason Zhu 		chars_left = sizeof(response) - strlen(response) - 1;
140190d27e7fSJason Zhu 
140290d27e7fSJason Zhu 		if (fb_getvar_single(cmd, &response[strlen(response)],
140390d27e7fSJason Zhu 				     chars_left) < 0) {
140490d27e7fSJason Zhu 			strcpy(cmd, "FAILunknown variable");
140590d27e7fSJason Zhu 			strncat(cmd, &response[strlen(response)], chars_left);
140690d27e7fSJason Zhu 			fastboot_tx_write_str(cmd);
140790d27e7fSJason Zhu 			return;
140890d27e7fSJason Zhu 		}
140990d27e7fSJason Zhu 		fastboot_tx_write_str(response);
141090d27e7fSJason Zhu 	}
141190d27e7fSJason Zhu 
141290d27e7fSJason Zhu 	return;
141390d27e7fSJason Zhu }
14143aab70afSSebastian Siewior 
1415ac484c5aSRoger Quadros static unsigned int rx_bytes_expected(struct usb_ep *ep)
14163aab70afSSebastian Siewior {
14173aab70afSSebastian Siewior 	int rx_remain = download_size - download_bytes;
1418ac484c5aSRoger Quadros 	unsigned int rem;
1419ac484c5aSRoger Quadros 	unsigned int maxpacket = ep->maxpacket;
1420ac484c5aSRoger Quadros 
1421ac484c5aSRoger Quadros 	if (rx_remain <= 0)
14223aab70afSSebastian Siewior 		return 0;
1423ac484c5aSRoger Quadros 	else if (rx_remain > EP_BUFFER_SIZE)
14243aab70afSSebastian Siewior 		return EP_BUFFER_SIZE;
1425ac484c5aSRoger Quadros 
1426ac484c5aSRoger Quadros 	/*
1427ac484c5aSRoger Quadros 	 * Some controllers e.g. DWC3 don't like OUT transfers to be
1428ac484c5aSRoger Quadros 	 * not ending in maxpacket boundary. So just make them happy by
1429ac484c5aSRoger Quadros 	 * always requesting for integral multiple of maxpackets.
1430ac484c5aSRoger Quadros 	 * This shouldn't bother controllers that don't care about it.
1431ac484c5aSRoger Quadros 	 */
14329e4b510dSDileep Katta 	rem = rx_remain % maxpacket;
1433ac484c5aSRoger Quadros 	if (rem > 0)
14349e4b510dSDileep Katta 		rx_remain = rx_remain + (maxpacket - rem);
1435ac484c5aSRoger Quadros 
14363aab70afSSebastian Siewior 	return rx_remain;
14373aab70afSSebastian Siewior }
14383aab70afSSebastian Siewior 
14393aab70afSSebastian Siewior #define BYTES_PER_DOT	0x20000
14403aab70afSSebastian Siewior static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
14413aab70afSSebastian Siewior {
14423c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
14433aab70afSSebastian Siewior 	unsigned int transfer_size = download_size - download_bytes;
14443aab70afSSebastian Siewior 	const unsigned char *buffer = req->buf;
14453aab70afSSebastian Siewior 	unsigned int buffer_size = req->actual;
144623d1d10cSBo Shen 	unsigned int pre_dot_num, now_dot_num;
14473aab70afSSebastian Siewior 
14483aab70afSSebastian Siewior 	if (req->status != 0) {
14493aab70afSSebastian Siewior 		printf("Bad status: %d\n", req->status);
14503aab70afSSebastian Siewior 		return;
14513aab70afSSebastian Siewior 	}
14523aab70afSSebastian Siewior 
14533aab70afSSebastian Siewior 	if (buffer_size < transfer_size)
14543aab70afSSebastian Siewior 		transfer_size = buffer_size;
14553aab70afSSebastian Siewior 
1456a588d99aSPaul Kocialkowski 	memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes,
14573aab70afSSebastian Siewior 	       buffer, transfer_size);
14583aab70afSSebastian Siewior 
145923d1d10cSBo Shen 	pre_dot_num = download_bytes / BYTES_PER_DOT;
14603aab70afSSebastian Siewior 	download_bytes += transfer_size;
146123d1d10cSBo Shen 	now_dot_num = download_bytes / BYTES_PER_DOT;
146223d1d10cSBo Shen 
146323d1d10cSBo Shen 	if (pre_dot_num != now_dot_num) {
146423d1d10cSBo Shen 		putc('.');
146523d1d10cSBo Shen 		if (!(now_dot_num % 74))
146623d1d10cSBo Shen 			putc('\n');
146723d1d10cSBo Shen 	}
14683aab70afSSebastian Siewior 
14693aab70afSSebastian Siewior 	/* Check if transfer is done */
14703aab70afSSebastian Siewior 	if (download_bytes >= download_size) {
14713aab70afSSebastian Siewior 		/*
14723aab70afSSebastian Siewior 		 * Reset global transfer variable, keep download_bytes because
14733aab70afSSebastian Siewior 		 * it will be used in the next possible flashing command
14743aab70afSSebastian Siewior 		 */
14753aab70afSSebastian Siewior 		download_size = 0;
14763aab70afSSebastian Siewior 		req->complete = rx_handler_command;
14773aab70afSSebastian Siewior 		req->length = EP_BUFFER_SIZE;
14783aab70afSSebastian Siewior 
1479192bc694SBen Whitten 		strcpy(response, "OKAY");
14803aab70afSSebastian Siewior 		fastboot_tx_write_str(response);
14813aab70afSSebastian Siewior 
14823aab70afSSebastian Siewior 		printf("\ndownloading of %d bytes finished\n", download_bytes);
14833aab70afSSebastian Siewior 	} else {
1484ac484c5aSRoger Quadros 		req->length = rx_bytes_expected(ep);
14853aab70afSSebastian Siewior 	}
14863aab70afSSebastian Siewior 
14873aab70afSSebastian Siewior 	req->actual = 0;
14883aab70afSSebastian Siewior 	usb_ep_queue(ep, req, 0);
14893aab70afSSebastian Siewior }
14903aab70afSSebastian Siewior 
14913aab70afSSebastian Siewior static void cb_download(struct usb_ep *ep, struct usb_request *req)
14923aab70afSSebastian Siewior {
14933aab70afSSebastian Siewior 	char *cmd = req->buf;
14943c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
14953aab70afSSebastian Siewior 
14963aab70afSSebastian Siewior 	strsep(&cmd, ":");
14973aab70afSSebastian Siewior 	download_size = simple_strtoul(cmd, NULL, 16);
14983aab70afSSebastian Siewior 	download_bytes = 0;
14993aab70afSSebastian Siewior 
15003aab70afSSebastian Siewior 	printf("Starting download of %d bytes\n", download_size);
15013aab70afSSebastian Siewior 
15023aab70afSSebastian Siewior 	if (0 == download_size) {
1503192bc694SBen Whitten 		strcpy(response, "FAILdata invalid size");
1504a588d99aSPaul Kocialkowski 	} else if (download_size > CONFIG_FASTBOOT_BUF_SIZE) {
15053aab70afSSebastian Siewior 		download_size = 0;
1506192bc694SBen Whitten 		strcpy(response, "FAILdata too large");
15073aab70afSSebastian Siewior 	} else {
15083aab70afSSebastian Siewior 		sprintf(response, "DATA%08x", download_size);
15093aab70afSSebastian Siewior 		req->complete = rx_handler_dl_image;
1510ac484c5aSRoger Quadros 		req->length = rx_bytes_expected(ep);
15113aab70afSSebastian Siewior 	}
1512367cce4dSXu Hongfei 
1513367cce4dSXu Hongfei 	fastboot_tx_write_str(response);
1514367cce4dSXu Hongfei }
1515367cce4dSXu Hongfei 
1516367cce4dSXu Hongfei static void tx_handler_ul(struct usb_ep *ep, struct usb_request *req)
1517367cce4dSXu Hongfei {
1518367cce4dSXu Hongfei 	unsigned int xfer_size = 0;
1519367cce4dSXu Hongfei 	unsigned int pre_dot_num, now_dot_num;
1520367cce4dSXu Hongfei 	unsigned int remain_size = 0;
1521367cce4dSXu Hongfei 	unsigned int transferred_size = req->actual;
1522367cce4dSXu Hongfei 
1523367cce4dSXu Hongfei 	if (req->status != 0) {
1524367cce4dSXu Hongfei 		printf("Bad status: %d\n", req->status);
1525367cce4dSXu Hongfei 		return;
1526367cce4dSXu Hongfei 	}
1527367cce4dSXu Hongfei 
1528367cce4dSXu Hongfei 	if (start_upload) {
1529367cce4dSXu Hongfei 		pre_dot_num = upload_bytes / BYTES_PER_DOT;
1530367cce4dSXu Hongfei 		upload_bytes += transferred_size;
1531367cce4dSXu Hongfei 		now_dot_num = upload_bytes / BYTES_PER_DOT;
1532367cce4dSXu Hongfei 
1533367cce4dSXu Hongfei 		if (pre_dot_num != now_dot_num) {
1534367cce4dSXu Hongfei 			putc('.');
1535367cce4dSXu Hongfei 			if (!(now_dot_num % 74))
1536367cce4dSXu Hongfei 				putc('\n');
1537367cce4dSXu Hongfei 		}
1538367cce4dSXu Hongfei 	}
1539367cce4dSXu Hongfei 
1540367cce4dSXu Hongfei 	remain_size = upload_size - upload_bytes;
1541367cce4dSXu Hongfei 	xfer_size = (remain_size > EP_BUFFER_SIZE) ?
1542367cce4dSXu Hongfei 		    EP_BUFFER_SIZE : remain_size;
1543367cce4dSXu Hongfei 
1544367cce4dSXu Hongfei 	debug("%s: remain_size=%d, transferred_size=%d",
1545367cce4dSXu Hongfei 	      __func__, remain_size, transferred_size);
1546367cce4dSXu Hongfei 	debug("xfer_size=%d, upload_bytes=%d, upload_size=%d!\n",
1547367cce4dSXu Hongfei 	      xfer_size, upload_bytes, upload_size);
1548367cce4dSXu Hongfei 
1549367cce4dSXu Hongfei 	if (remain_size <= 0) {
1550367cce4dSXu Hongfei 		fastboot_func->in_req->complete = fastboot_complete;
1551aa74b607SJason Zhu 		wakeup_thread();
1552367cce4dSXu Hongfei 		fastboot_tx_write_str("OKAY");
1553367cce4dSXu Hongfei 		printf("\nuploading of %d bytes finished\n", upload_bytes);
1554367cce4dSXu Hongfei 		upload_bytes = 0;
1555367cce4dSXu Hongfei 		upload_size = 0;
1556367cce4dSXu Hongfei 		start_upload = false;
1557367cce4dSXu Hongfei 		return;
1558367cce4dSXu Hongfei 	}
1559367cce4dSXu Hongfei 
1560367cce4dSXu Hongfei 	/* Remove the transfer callback which response the upload */
1561367cce4dSXu Hongfei 	/* request from host */
1562367cce4dSXu Hongfei 	if (!upload_bytes)
1563367cce4dSXu Hongfei 		start_upload = true;
1564367cce4dSXu Hongfei 
1565174b4544SKever Yang 	fastboot_tx_write((char *)((phys_addr_t)CONFIG_FASTBOOT_BUF_ADDR + \
1566174b4544SKever Yang 			  upload_bytes),
1567367cce4dSXu Hongfei 			  xfer_size);
1568367cce4dSXu Hongfei }
1569367cce4dSXu Hongfei 
1570367cce4dSXu Hongfei static void cb_upload(struct usb_ep *ep, struct usb_request *req)
1571367cce4dSXu Hongfei {
1572367cce4dSXu Hongfei 	char response[FASTBOOT_RESPONSE_LEN];
1573367cce4dSXu Hongfei 
1574367cce4dSXu Hongfei 	printf("Starting upload of %d bytes\n", upload_size);
1575367cce4dSXu Hongfei 
1576367cce4dSXu Hongfei 	if (0 == upload_size) {
1577367cce4dSXu Hongfei 		strcpy(response, "FAILdata invalid size");
1578367cce4dSXu Hongfei 	} else {
1579367cce4dSXu Hongfei 		start_upload = false;
1580367cce4dSXu Hongfei 		sprintf(response, "DATA%08x", upload_size);
1581367cce4dSXu Hongfei 		fastboot_func->in_req->complete = tx_handler_ul;
1582367cce4dSXu Hongfei 	}
1583367cce4dSXu Hongfei 
15843aab70afSSebastian Siewior 	fastboot_tx_write_str(response);
15853aab70afSSebastian Siewior }
15863aab70afSSebastian Siewior 
15873aab70afSSebastian Siewior static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
15883aab70afSSebastian Siewior {
15893aab70afSSebastian Siewior 	char boot_addr_start[12];
15903aab70afSSebastian Siewior 	char *bootm_args[] = { "bootm", boot_addr_start, NULL };
15913aab70afSSebastian Siewior 
15923aab70afSSebastian Siewior 	puts("Booting kernel..\n");
15933aab70afSSebastian Siewior 
159490ae53ceSTom Rini 	sprintf(boot_addr_start, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
15953aab70afSSebastian Siewior 	do_bootm(NULL, 0, 2, bootm_args);
15963aab70afSSebastian Siewior 
15973aab70afSSebastian Siewior 	/* This only happens if image is somehow faulty so we start over */
15983aab70afSSebastian Siewior 	do_reset(NULL, 0, 0, NULL);
15993aab70afSSebastian Siewior }
16003aab70afSSebastian Siewior 
16013aab70afSSebastian Siewior static void cb_boot(struct usb_ep *ep, struct usb_request *req)
16023aab70afSSebastian Siewior {
16033aab70afSSebastian Siewior 	fastboot_func->in_req->complete = do_bootm_on_complete;
16043aab70afSSebastian Siewior 	fastboot_tx_write_str("OKAY");
16053aab70afSSebastian Siewior }
16063aab70afSSebastian Siewior 
1607267abc62SRob Herring static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req)
1608267abc62SRob Herring {
1609267abc62SRob Herring 	g_dnl_trigger_detach();
1610267abc62SRob Herring }
1611267abc62SRob Herring 
1612267abc62SRob Herring static void cb_continue(struct usb_ep *ep, struct usb_request *req)
1613267abc62SRob Herring {
1614267abc62SRob Herring 	fastboot_func->in_req->complete = do_exit_on_complete;
1615267abc62SRob Herring 	fastboot_tx_write_str("OKAY");
1616267abc62SRob Herring }
1617267abc62SRob Herring 
1618367cce4dSXu Hongfei static void cb_set_active(struct usb_ep *ep, struct usb_request *req)
1619367cce4dSXu Hongfei {
1620367cce4dSXu Hongfei 	char *cmd = req->buf;
1621367cce4dSXu Hongfei 
1622367cce4dSXu Hongfei 	debug("%s: %s\n", __func__, cmd);
1623367cce4dSXu Hongfei 
1624367cce4dSXu Hongfei 	strsep(&cmd, ":");
1625367cce4dSXu Hongfei 	if (!cmd) {
162690aa625cSMasahiro Yamada 		pr_err("missing slot name");
16272e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILmissing slot name");
1628367cce4dSXu Hongfei 		return;
1629367cce4dSXu Hongfei 	}
163037a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
1631367cce4dSXu Hongfei 	unsigned int slot_number;
1632367cce4dSXu Hongfei 	if (strncmp("a", cmd, 1) == 0) {
1633367cce4dSXu Hongfei 		slot_number = 0;
163437a7bc39SJason Zhu 		rk_avb_set_slot_active(&slot_number);
1635367cce4dSXu Hongfei 	} else if (strncmp("b", cmd, 1) == 0) {
1636367cce4dSXu Hongfei 		slot_number = 1;
163737a7bc39SJason Zhu 		rk_avb_set_slot_active(&slot_number);
1638367cce4dSXu Hongfei 	} else {
16392e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILunkown slot name");
1640367cce4dSXu Hongfei 		return;
1641367cce4dSXu Hongfei 	}
1642367cce4dSXu Hongfei 
1643367cce4dSXu Hongfei 	fastboot_tx_write_str("OKAY");
1644367cce4dSXu Hongfei 	return;
1645367cce4dSXu Hongfei #else
1646367cce4dSXu Hongfei 	fastboot_tx_write_str("FAILnot implemented");
1647367cce4dSXu Hongfei 	return;
1648367cce4dSXu Hongfei #endif
1649367cce4dSXu Hongfei }
1650367cce4dSXu Hongfei 
1651d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH
1652d1b5ed07SSteve Rae static void cb_flash(struct usb_ep *ep, struct usb_request *req)
1653d1b5ed07SSteve Rae {
1654d1b5ed07SSteve Rae 	char *cmd = req->buf;
1655374a9995SCody Xie 	char response[FASTBOOT_RESPONSE_LEN] = {0};
165637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
16577bc1707dSJason Zhu 	uint8_t flash_lock_state;
1658d1b5ed07SSteve Rae 
165937a7bc39SJason Zhu 	if (rk_avb_read_flash_lock_state(&flash_lock_state)) {
16602e40c2c1SJason Zhu 		/* write the device flashing unlock when first read */
16612e40c2c1SJason Zhu 		if (rk_avb_write_flash_lock_state(1)) {
16622e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state write failure");
16637bc1707dSJason Zhu 			return;
1664ef52a073SJason Zhu 		}
16652e40c2c1SJason Zhu 		if (rk_avb_read_flash_lock_state(&flash_lock_state)) {
16662e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state read failure");
16672e40c2c1SJason Zhu 			return;
16682e40c2c1SJason Zhu 		}
16692e40c2c1SJason Zhu 	}
1670ef52a073SJason Zhu 
16717bc1707dSJason Zhu 	if (flash_lock_state == 0) {
16727bc1707dSJason Zhu 		fastboot_tx_write_str("FAILThe device is locked, can not flash!");
16737bc1707dSJason Zhu 		printf("The device is locked, can not flash!\n");
16747bc1707dSJason Zhu 		return;
16757bc1707dSJason Zhu 	}
16767bc1707dSJason Zhu #endif
1677d1b5ed07SSteve Rae 	strsep(&cmd, ":");
1678d1b5ed07SSteve Rae 	if (!cmd) {
167990aa625cSMasahiro Yamada 		pr_err("missing partition name");
1680d1b5ed07SSteve Rae 		fastboot_tx_write_str("FAILmissing partition name");
1681d1b5ed07SSteve Rae 		return;
1682d1b5ed07SSteve Rae 	}
1683d1b5ed07SSteve Rae 
16848b464fa9SJocelyn Bohr 	fastboot_fail("no flash device defined", response);
1685d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
168664ece848SSteve Rae 	fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
16878b464fa9SJocelyn Bohr 				download_bytes, response);
1688d1b5ed07SSteve Rae #endif
1689bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
16908b464fa9SJocelyn Bohr 	fb_nand_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
16918b464fa9SJocelyn Bohr 				download_bytes, response);
1692bf8940d3SMaxime Ripard #endif
1693d1b5ed07SSteve Rae 	fastboot_tx_write_str(response);
1694d1b5ed07SSteve Rae }
1695d1b5ed07SSteve Rae 
1696367cce4dSXu Hongfei static void cb_flashing(struct usb_ep *ep, struct usb_request *req)
1697367cce4dSXu Hongfei {
1698367cce4dSXu Hongfei 	char *cmd = req->buf;
1699367cce4dSXu Hongfei 
1700367cce4dSXu Hongfei 	if (strncmp("lock", cmd + 9, 4) == 0) {
170137a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
17027bc1707dSJason Zhu 		uint8_t flash_lock_state;
17037bc1707dSJason Zhu 		flash_lock_state = 0;
170437a7bc39SJason Zhu 		if (rk_avb_write_flash_lock_state(flash_lock_state))
17052e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state"
17062e40c2c1SJason Zhu 					      " write failure");
17077bc1707dSJason Zhu 		else
17087bc1707dSJason Zhu 			fastboot_tx_write_str("OKAY");
17097bc1707dSJason Zhu #else
1710367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
17117bc1707dSJason Zhu #endif
1712367cce4dSXu Hongfei 	} else if (strncmp("unlock", cmd + 9, 6) == 0) {
171337a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
17147bc1707dSJason Zhu 		uint8_t flash_lock_state;
17157bc1707dSJason Zhu 		flash_lock_state = 1;
171637a7bc39SJason Zhu 		if (rk_avb_write_flash_lock_state(flash_lock_state))
17172e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state"
17182e40c2c1SJason Zhu 					      " write failure");
17197bc1707dSJason Zhu 		else
17207bc1707dSJason Zhu 			fastboot_tx_write_str("OKAY");
17217bc1707dSJason Zhu #else
1722367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
17237bc1707dSJason Zhu #endif
1724367cce4dSXu Hongfei 	} else if (strncmp("lock_critical", cmd + 9, 12) == 0) {
1725367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1726367cce4dSXu Hongfei 	} else if (strncmp("unlock_critical", cmd + 9, 14) == 0) {
1727367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1728367cce4dSXu Hongfei 	} else if (strncmp("get_unlock_ability", cmd + 9, 17) == 0) {
1729367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1730367cce4dSXu Hongfei 	} else if (strncmp("get_unlock_bootloader_nonce", cmd + 4, 27) == 0) {
1731367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1732367cce4dSXu Hongfei 	} else if (strncmp("unlock_bootloader", cmd + 9, 17) == 0) {
1733367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1734367cce4dSXu Hongfei 	} else if (strncmp("lock_bootloader", cmd + 9, 15) == 0) {
1735367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1736367cce4dSXu Hongfei 	} else {
1737367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILunknown flashing command");
1738367cce4dSXu Hongfei 	}
1739367cce4dSXu Hongfei }
17406f3eb474SKever Yang #endif
1741367cce4dSXu Hongfei 
17422e40c2c1SJason Zhu static void cb_oem_perm_attr(void)
17432e40c2c1SJason Zhu {
17442e40c2c1SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
17452e40c2c1SJason Zhu 	sha256_context ctx;
17462e40c2c1SJason Zhu 	uint8_t digest[SHA256_SUM_LEN] = {0};
17472e40c2c1SJason Zhu 	uint8_t digest_temp[SHA256_SUM_LEN] = {0};
17482e40c2c1SJason Zhu 	uint8_t perm_attr_temp[PERM_ATTR_TOTAL_SIZE] = {0};
17492e40c2c1SJason Zhu 	uint8_t flag = 0;
17502e40c2c1SJason Zhu 
17512e40c2c1SJason Zhu 	if (PERM_ATTR_TOTAL_SIZE != download_bytes) {
17522e40c2c1SJason Zhu 		printf("Permanent attribute size is not equal!\n");
17532e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILincorrect perm attribute size");
17542e40c2c1SJason Zhu 		return;
17552e40c2c1SJason Zhu 	}
17562e40c2c1SJason Zhu 
17572e40c2c1SJason Zhu 	if (rk_avb_read_perm_attr_flag(&flag)) {
17582e40c2c1SJason Zhu 		printf("rk_avb_read_perm_attr_flag error!\n");
17592e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr read failed");
17602e40c2c1SJason Zhu 		return;
17612e40c2c1SJason Zhu 	}
17622e40c2c1SJason Zhu 
17632e40c2c1SJason Zhu 	if (flag == PERM_ATTR_SUCCESS_FLAG) {
17642e40c2c1SJason Zhu 		if (rk_avb_read_attribute_hash(digest_temp,
17652e40c2c1SJason Zhu 					       SHA256_SUM_LEN)) {
17662e40c2c1SJason Zhu 			printf("The efuse IO can not be used!\n");
17672e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILefuse IO can not be used");
17682e40c2c1SJason Zhu 			return;
17692e40c2c1SJason Zhu 		}
17702e40c2c1SJason Zhu 
17712e40c2c1SJason Zhu 		if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) {
17722e40c2c1SJason Zhu 			if (rk_avb_read_permanent_attributes(perm_attr_temp,
17732e40c2c1SJason Zhu 							     PERM_ATTR_TOTAL_SIZE)) {
17742e40c2c1SJason Zhu 				printf("rk_avb_write_permanent_attributes error!\n");
17752e40c2c1SJason Zhu 				fastboot_tx_write_str("FAILread perm attr error");
17762e40c2c1SJason Zhu 				return;
17772e40c2c1SJason Zhu 			}
17782e40c2c1SJason Zhu 
17792e40c2c1SJason Zhu 			sha256_starts(&ctx);
17802e40c2c1SJason Zhu 			sha256_update(&ctx,
17812e40c2c1SJason Zhu 				      (const uint8_t *)perm_attr_temp,
17822e40c2c1SJason Zhu 				      PERM_ATTR_TOTAL_SIZE);
17832e40c2c1SJason Zhu 			sha256_finish(&ctx, digest);
17842e40c2c1SJason Zhu 			if (memcmp(digest, digest_temp, SHA256_SUM_LEN) == 0) {
17852e40c2c1SJason Zhu 				printf("The hash has been written!\n");
17862e40c2c1SJason Zhu 				fastboot_tx_write_str("OKAY");
17872e40c2c1SJason Zhu 				return;
17882e40c2c1SJason Zhu 			}
17892e40c2c1SJason Zhu 		}
17902e40c2c1SJason Zhu 
17912e40c2c1SJason Zhu 		if (rk_avb_write_perm_attr_flag(0)) {
17922e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILperm attr flag write failure");
17932e40c2c1SJason Zhu 			return;
17942e40c2c1SJason Zhu 		}
17952e40c2c1SJason Zhu 	}
17962e40c2c1SJason Zhu 
17972e40c2c1SJason Zhu 	if (rk_avb_write_permanent_attributes((uint8_t *)
17982e40c2c1SJason Zhu 					      CONFIG_FASTBOOT_BUF_ADDR,
17992e40c2c1SJason Zhu 					      download_bytes)) {
18002e40c2c1SJason Zhu 		if (rk_avb_write_perm_attr_flag(0)) {
18012e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILperm attr flag write failure");
18022e40c2c1SJason Zhu 			return;
18032e40c2c1SJason Zhu 		}
18042e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr write failed");
18052e40c2c1SJason Zhu 		return;
18062e40c2c1SJason Zhu 	}
18072e40c2c1SJason Zhu 
18082e40c2c1SJason Zhu 	memset(digest, 0, SHA256_SUM_LEN);
18092e40c2c1SJason Zhu 	sha256_starts(&ctx);
18102e40c2c1SJason Zhu 	sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
18112e40c2c1SJason Zhu 		      PERM_ATTR_TOTAL_SIZE);
18122e40c2c1SJason Zhu 	sha256_finish(&ctx, digest);
18132e40c2c1SJason Zhu 
18142e40c2c1SJason Zhu 	if (rk_avb_write_attribute_hash((uint8_t *)digest,
18152e40c2c1SJason Zhu 					SHA256_SUM_LEN)) {
18162e40c2c1SJason Zhu 		if (rk_avb_read_attribute_hash(digest_temp,
18172e40c2c1SJason Zhu 						SHA256_SUM_LEN)) {
18182e40c2c1SJason Zhu 			printf("The efuse IO can not be used!\n");
18192e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILefuse IO can not be used");
18202e40c2c1SJason Zhu 			return;
18212e40c2c1SJason Zhu 		}
18222e40c2c1SJason Zhu 		if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) {
18232e40c2c1SJason Zhu 			if (rk_avb_write_perm_attr_flag(0)) {
18242e40c2c1SJason Zhu 				fastboot_tx_write_str("FAILperm attr flag write failure");
18252e40c2c1SJason Zhu 				return;
18262e40c2c1SJason Zhu 			}
18272e40c2c1SJason Zhu 			printf("The hash has been written, but is different!\n");
18282e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILhash comparison failure");
18292e40c2c1SJason Zhu 			return;
18302e40c2c1SJason Zhu 		}
18312e40c2c1SJason Zhu 	}
18322e40c2c1SJason Zhu 
18332e40c2c1SJason Zhu 	if (rk_avb_write_perm_attr_flag(PERM_ATTR_SUCCESS_FLAG)) {
18342e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr flag write failure");
18352e40c2c1SJason Zhu 		return;
18362e40c2c1SJason Zhu 	}
18372e40c2c1SJason Zhu 
18382e40c2c1SJason Zhu 	fastboot_tx_write_str("OKAY");
18392e40c2c1SJason Zhu #else
18402e40c2c1SJason Zhu 	fastboot_tx_write_str("FAILnot implemented");
18412e40c2c1SJason Zhu #endif
18422e40c2c1SJason Zhu }
18432e40c2c1SJason Zhu 
1844de195620SMichael Scott static void cb_oem(struct usb_ep *ep, struct usb_request *req)
1845de195620SMichael Scott {
1846de195620SMichael Scott 	char *cmd = req->buf;
1847367cce4dSXu Hongfei 
18484adef270SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
1849372d7decSRob Herring 	if (strncmp("format", cmd + 4, 6) == 0) {
1850372d7decSRob Herring 		char cmdbuf[32];
1851372d7decSRob Herring 		sprintf(cmdbuf, "gpt write mmc %x $partitions",
1852372d7decSRob Herring 			CONFIG_FASTBOOT_FLASH_MMC_DEV);
1853372d7decSRob Herring 		if (run_command(cmdbuf, 0))
18542e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILmmc write failure");
1855372d7decSRob Herring 		else
1856372d7decSRob Herring 			fastboot_tx_write_str("OKAY");
1857372d7decSRob Herring 	} else
1858372d7decSRob Herring #endif
1859de195620SMichael Scott 	if (strncmp("unlock", cmd + 4, 8) == 0) {
186065413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK
1861f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER
1862de195620SMichael Scott 		fastboot_tx_write_str("FAILnot implemented");
1863f61a997eSqiujian 		return;
1864f61a997eSqiujian #else
1865f61a997eSqiujian 		uint8_t unlock = 0;
1866f61a997eSqiujian 		TEEC_Result result;
1867f61a997eSqiujian 		debug("oem unlock\n");
1868f61a997eSqiujian 		result = trusty_read_oem_unlock(&unlock);
1869f61a997eSqiujian 		if (result) {
1870f61a997eSqiujian 			printf("read oem unlock status with error : 0x%x\n", result);
1871f61a997eSqiujian 			fastboot_tx_write_str("FAILRead oem unlock status failed");
1872f61a997eSqiujian 			return;
1873f61a997eSqiujian 		}
1874f61a997eSqiujian 		if (unlock) {
1875f61a997eSqiujian 			printf("oem unlock ignored, device already unlocked\n");
1876f61a997eSqiujian 			fastboot_tx_write_str("FAILalready unlocked");
1877f61a997eSqiujian 			return;
1878f61a997eSqiujian 		}
1879f61a997eSqiujian 		printf("oem unlock requested:\n");
1880f61a997eSqiujian 		printf("\tUnlocking forces a factory reset and could\n");
1881f61a997eSqiujian 		printf("\topen your device up to a world of hurt.  If you\n");
1882f61a997eSqiujian 		printf("\tare sure you know what you're doing, then accept\n");
1883f61a997eSqiujian 		printf("\tvia 'fastboot oem unlock_accept'.\n");
1884f61a997eSqiujian 		env_set("unlock", "unlock");
1885f61a997eSqiujian 		fastboot_tx_write_str("OKAY");
1886f61a997eSqiujian #endif
1887f61a997eSqiujian #else
1888f61a997eSqiujian 		fastboot_tx_write_str("FAILnot implemented");
1889f61a997eSqiujian 		return;
1890f61a997eSqiujian #endif
1891f61a997eSqiujian 	} else if (strncmp("unlock_accept", cmd + 4, 13) == 0) {
189265413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK
1893f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER
1894f61a997eSqiujian 		fastboot_tx_write_str("FAILnot implemented");
1895f61a997eSqiujian 		return;
1896f61a997eSqiujian #else
1897f61a997eSqiujian 		char *unlock = env_get("unlock");
1898f61a997eSqiujian 		TEEC_Result result;
1899f61a997eSqiujian 		debug("oem unlock_accept\n");
1900f61a997eSqiujian 		if (unlock == NULL || strncmp("unlock", unlock, 6) != 0) {
1901f61a997eSqiujian 			printf("oem unlock_accept ignored, not pending\n");
1902f61a997eSqiujian 			fastboot_tx_write_str("FAILoem unlock not requested");
1903f61a997eSqiujian 			return;
1904f61a997eSqiujian 		}
1905f61a997eSqiujian 		env_set("unlock", "");
1906f61a997eSqiujian 		printf("Erasing userdata partition\n");
1907f61a997eSqiujian 		struct blk_desc *dev_desc;
1908f61a997eSqiujian 		disk_partition_t part_info;
1909f61a997eSqiujian 		dev_desc = rockchip_get_bootdev();
19106651d4c0SJason Zhu 		if (!dev_desc) {
19116651d4c0SJason Zhu 			printf("%s: dev_desc is NULL!\n", __func__);
19126651d4c0SJason Zhu 			return;
19136651d4c0SJason Zhu 		}
1914f61a997eSqiujian 		int ret = part_get_info_by_name(dev_desc, "userdata",
1915f61a997eSqiujian 				&part_info);
1916f61a997eSqiujian 		if (ret < 0) {
1917f61a997eSqiujian 			printf("not found userdata partition");
1918f61a997eSqiujian 			printf("Erase failed with error %d\n", ret);
1919f61a997eSqiujian 			fastboot_tx_write_str("FAILErasing userdata failed");
1920f61a997eSqiujian 			return;
1921f61a997eSqiujian 		}
1922f61a997eSqiujian 		ret = blk_derase(dev_desc, part_info.start, part_info.size);
1923f61a997eSqiujian 		if (ret != part_info.size) {
1924f61a997eSqiujian 			printf("Erase failed with error %d\n", ret);
1925f61a997eSqiujian 			fastboot_tx_write_str("FAILErasing userdata failed");
1926f61a997eSqiujian 			return;
1927f61a997eSqiujian 		}
1928f61a997eSqiujian 		printf("Erasing succeeded\n");
1929f61a997eSqiujian 
1930f61a997eSqiujian 		result = trusty_write_oem_unlock(1);
1931f61a997eSqiujian 		if (result) {
1932f61a997eSqiujian 			printf("write oem unlock status with error : 0x%x\n", result);
1933f61a997eSqiujian 			fastboot_tx_write_str("FAILWrite oem unlock status failed");
1934f61a997eSqiujian 			return;
1935f61a997eSqiujian 		}
1936f61a997eSqiujian 		fastboot_tx_write_str("OKAY");
1937f61a997eSqiujian 
1938f61a997eSqiujian 		/*
1939f61a997eSqiujian 		 * now reboot into recovery to do a format of the
1940f61a997eSqiujian 		 * userdata partition so it's ready to use on next boot
1941f61a997eSqiujian 		 */
1942f61a997eSqiujian 		board_run_recovery_wipe_data();
1943f61a997eSqiujian #endif
1944f61a997eSqiujian #else
1945f61a997eSqiujian 		fastboot_tx_write_str("FAILnot implemented");
1946f61a997eSqiujian 		return;
1947f61a997eSqiujian #endif
1948f61a997eSqiujian 	} else if (strncmp("lock", cmd + 4, 8) == 0) {
194965413a00SJian Qiu #ifdef CONFIG_FASTBOOT_OEM_UNLOCK
1950f61a997eSqiujian #ifdef CONFIG_RK_AVB_LIBAVB_USER
1951f61a997eSqiujian 		fastboot_tx_write_str("FAILnot implemented");
1952f61a997eSqiujian 		return;
1953f61a997eSqiujian #else
1954f61a997eSqiujian 		TEEC_Result result;
1955f61a997eSqiujian 		uint8_t unlock = 0;
1956f61a997eSqiujian 		trusty_read_oem_unlock(&unlock);
1957f61a997eSqiujian 		if (!unlock) {
1958f61a997eSqiujian 			printf("oem lock ignored, already locked\n");
1959f61a997eSqiujian 			fastboot_tx_write_str("FAILalready locked");
1960f61a997eSqiujian 			return;
1961f61a997eSqiujian 		}
1962f61a997eSqiujian 
1963f61a997eSqiujian 		result = trusty_write_oem_unlock(0);
1964f61a997eSqiujian 		if (result) {
1965f61a997eSqiujian 			printf("write oem unlock status with error : 0x%x\n", result);
1966f61a997eSqiujian 			fastboot_tx_write_str("FAILWrite oem unlock status failed");
1967f61a997eSqiujian 			return;
1968f61a997eSqiujian 		}
1969f61a997eSqiujian 		fastboot_tx_write_str("OKAY");
1970f61a997eSqiujian #endif
1971f61a997eSqiujian #else
1972f61a997eSqiujian 		fastboot_tx_write_str("FAILnot implemented");
1973f61a997eSqiujian 		return;
1974f61a997eSqiujian #endif
1975367cce4dSXu Hongfei 	} else if (strncmp("at-get-ca-request", cmd + 4, 17) == 0) {
19764d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
19772e40c2c1SJason Zhu 		uint8_t out[ATTEST_CA_OUT_SIZE];
19784d0fc665SAndy Ye 		uint32_t operation_size = download_bytes;
19792e40c2c1SJason Zhu 		uint32_t out_len = ATTEST_CA_OUT_SIZE;
19804d0fc665SAndy Ye 		uint32_t res = 0;
19812e40c2c1SJason Zhu 
19822e40c2c1SJason Zhu 		res = trusty_attest_get_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
19832e40c2c1SJason Zhu 					   &operation_size, out, &out_len);
19844d0fc665SAndy Ye 		if (res) {
19854d0fc665SAndy Ye 			fastboot_tx_write_str("FAILtrusty_attest_get_ca failed");
19864d0fc665SAndy Ye 			return;
19874d0fc665SAndy Ye 		}
19884d0fc665SAndy Ye 		upload_size = out_len;
19894d0fc665SAndy Ye 		memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR, out, out_len);
1990367cce4dSXu Hongfei 		fastboot_tx_write_str("OKAY");
19914d0fc665SAndy Ye #else
19924d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
19934d0fc665SAndy Ye 		return;
19944d0fc665SAndy Ye #endif
1995367cce4dSXu Hongfei 	} else if (strncmp("at-set-ca-response", cmd + 4, 18) == 0) {
19964d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
19974d0fc665SAndy Ye 		uint32_t ca_response_size = download_bytes;
19984d0fc665SAndy Ye 		uint32_t res = 0;
19992e40c2c1SJason Zhu 
20002e40c2c1SJason Zhu 		res = trusty_attest_set_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
20012e40c2c1SJason Zhu 					   &ca_response_size);
20022e40c2c1SJason Zhu 		if (res)
20034d0fc665SAndy Ye 			fastboot_tx_write_str("FAILtrusty_attest_set_ca failed");
20042e40c2c1SJason Zhu 		else
2005367cce4dSXu Hongfei 			fastboot_tx_write_str("OKAY");
20064d0fc665SAndy Ye #else
20074d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
20084d0fc665SAndy Ye 		return;
20094d0fc665SAndy Ye #endif
201062b11485SJason Zhu 	} else if (strncmp("at-get-vboot-unlock-challenge", cmd + 4, 29) == 0) {
201162b11485SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
201262b11485SJason Zhu 		uint32_t challenge_len = 0;
201362b11485SJason Zhu 		int ret = 0;
201462b11485SJason Zhu 
201562b11485SJason Zhu 		ret = rk_generate_unlock_challenge((void *)CONFIG_FASTBOOT_BUF_ADDR, &challenge_len);
201662b11485SJason Zhu 		if (ret == 0) {
201762b11485SJason Zhu 			upload_size = challenge_len;
201862b11485SJason Zhu 			fastboot_tx_write_str("OKAY");
201962b11485SJason Zhu 		} else {
202062b11485SJason Zhu 			fastboot_tx_write_str("FAILgenerate unlock challenge fail!");
202162b11485SJason Zhu 		}
202262b11485SJason Zhu #else
202362b11485SJason Zhu 		fastboot_tx_write_str("FAILnot implemented");
202462b11485SJason Zhu 		return;
202562b11485SJason Zhu #endif
2026367cce4dSXu Hongfei 	} else if (strncmp("at-lock-vboot", cmd + 4, 13) == 0) {
202737a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
2028d8bd6e97SJason Zhu 		uint8_t lock_state;
2029d8bd6e97SJason Zhu 		lock_state = 0;
203037a7bc39SJason Zhu 		if (rk_avb_write_lock_state(lock_state))
20312e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILwrite lock state failed");
2032d8bd6e97SJason Zhu 		else
2033d8bd6e97SJason Zhu 			fastboot_tx_write_str("OKAY");
2034d8bd6e97SJason Zhu #else
2035367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
2036d8bd6e97SJason Zhu #endif
2037367cce4dSXu Hongfei 	} else if (strncmp("at-unlock-vboot", cmd + 4, 15) == 0) {
203837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
2039d8bd6e97SJason Zhu 		uint8_t lock_state;
204046a8a269SJason Zhu 		bool out_is_trusted = true;
2041542316a9SJason Zhu 
204237a7bc39SJason Zhu 		if (rk_avb_read_lock_state(&lock_state))
20432e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILlock sate read failure");
2044d8bd6e97SJason Zhu 		if (lock_state >> 1 == 1) {
2045d8bd6e97SJason Zhu 			fastboot_tx_write_str("FAILThe vboot is disable!");
2046d8bd6e97SJason Zhu 		} else {
2047d8bd6e97SJason Zhu 			lock_state = 1;
204846a8a269SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_ENABLE_ATH_UNLOCK
2049542316a9SJason Zhu 			if (rk_auth_unlock((void *)CONFIG_FASTBOOT_BUF_ADDR,
2050542316a9SJason Zhu 					   &out_is_trusted)) {
2051542316a9SJason Zhu 				printf("rk_auth_unlock ops error!\n");
2052542316a9SJason Zhu 				fastboot_tx_write_str("FAILrk_auth_unlock ops error!");
2053542316a9SJason Zhu 				return;
2054542316a9SJason Zhu 			}
205546a8a269SJason Zhu #endif
2056542316a9SJason Zhu 			if (out_is_trusted == true) {
205737a7bc39SJason Zhu 				if (rk_avb_write_lock_state(lock_state))
20582e40c2c1SJason Zhu 					fastboot_tx_write_str("FAILwrite lock state failed");
2059d8bd6e97SJason Zhu 				else
2060d8bd6e97SJason Zhu 					fastboot_tx_write_str("OKAY");
2061542316a9SJason Zhu 			} else {
2062542316a9SJason Zhu 				fastboot_tx_write_str("FAILauthenticated unlock fail");
2063542316a9SJason Zhu 			}
2064d8bd6e97SJason Zhu 		}
2065d8bd6e97SJason Zhu #else
2066367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
2067d8bd6e97SJason Zhu #endif
2068367cce4dSXu Hongfei 	} else if (strncmp("at-disable-unlock-vboot", cmd + 4, 23) == 0) {
206937a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
2070d8bd6e97SJason Zhu 		uint8_t lock_state;
2071d8bd6e97SJason Zhu 		lock_state = 2;
207237a7bc39SJason Zhu 		if (rk_avb_write_lock_state(lock_state))
20732e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILwrite lock state failed");
2074d8bd6e97SJason Zhu 		else
2075d8bd6e97SJason Zhu 			fastboot_tx_write_str("OKAY");
2076d8bd6e97SJason Zhu #else
2077367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
2078d8bd6e97SJason Zhu #endif
2079367cce4dSXu Hongfei 	} else if (strncmp("fuse at-perm-attr", cmd + 4, 16) == 0) {
20802e40c2c1SJason Zhu 		cb_oem_perm_attr();
20814e1bbe84SJason Zhu 	} else if (strncmp("fuse at-bootloader-vboot-key", cmd + 4, 27) == 0) {
208237a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
20832e40c2c1SJason Zhu 		sha256_context ctx;
20842e40c2c1SJason Zhu 		uint8_t digest[SHA256_SUM_LEN];
20852e40c2c1SJason Zhu 
20864e1bbe84SJason Zhu 		if (download_bytes != VBOOT_KEY_HASH_SIZE) {
20872e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILinvalid vboot key length");
20884e1bbe84SJason Zhu 			printf("The vboot key size error!\n");
20892e40c2c1SJason Zhu 			return;
20904e1bbe84SJason Zhu 		}
20914e1bbe84SJason Zhu 
20922e40c2c1SJason Zhu 		sha256_starts(&ctx);
20932e40c2c1SJason Zhu 		sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
20942e40c2c1SJason Zhu 			      VBOOT_KEY_SIZE);
20952e40c2c1SJason Zhu 		sha256_finish(&ctx, digest);
20962e40c2c1SJason Zhu 
20972e40c2c1SJason Zhu 		if (rk_avb_write_vbootkey_hash((uint8_t *)digest,
20982e40c2c1SJason Zhu 					       SHA256_SUM_LEN)) {
20992e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILvbootkey hash write failure");
21004e1bbe84SJason Zhu 			return;
21014e1bbe84SJason Zhu 		}
21024e1bbe84SJason Zhu 		fastboot_tx_write_str("OKAY");
21034e1bbe84SJason Zhu #else
21044e1bbe84SJason Zhu 		fastboot_tx_write_str("FAILnot implemented");
21054e1bbe84SJason Zhu #endif
2106367cce4dSXu Hongfei 	} else {
2107de195620SMichael Scott 		fastboot_tx_write_str("FAILunknown oem command");
2108de195620SMichael Scott 	}
2109de195620SMichael Scott }
2110de195620SMichael Scott 
211189792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH
211289792381SDileep Katta static void cb_erase(struct usb_ep *ep, struct usb_request *req)
211389792381SDileep Katta {
211489792381SDileep Katta 	char *cmd = req->buf;
21153c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
211689792381SDileep Katta 
211789792381SDileep Katta 	strsep(&cmd, ":");
211889792381SDileep Katta 	if (!cmd) {
211990aa625cSMasahiro Yamada 		pr_err("missing partition name");
212089792381SDileep Katta 		fastboot_tx_write_str("FAILmissing partition name");
212189792381SDileep Katta 		return;
212289792381SDileep Katta 	}
212389792381SDileep Katta 
21248b464fa9SJocelyn Bohr 	fastboot_fail("no flash device defined", response);
212589792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
21268b464fa9SJocelyn Bohr 	fb_mmc_erase(cmd, response);
212789792381SDileep Katta #endif
2128bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
21298b464fa9SJocelyn Bohr 	fb_nand_erase(cmd, response);
2130bf8940d3SMaxime Ripard #endif
213189792381SDileep Katta 	fastboot_tx_write_str(response);
213289792381SDileep Katta }
213389792381SDileep Katta #endif
213489792381SDileep Katta 
21353aab70afSSebastian Siewior struct cmd_dispatch_info {
21363aab70afSSebastian Siewior 	char *cmd;
21373aab70afSSebastian Siewior 	void (*cb)(struct usb_ep *ep, struct usb_request *req);
21383aab70afSSebastian Siewior };
21393aab70afSSebastian Siewior 
21403aab70afSSebastian Siewior static const struct cmd_dispatch_info cmd_dispatch_info[] = {
21413aab70afSSebastian Siewior 	{
21423aab70afSSebastian Siewior 		.cmd = "reboot",
21433aab70afSSebastian Siewior 		.cb = cb_reboot,
21443aab70afSSebastian Siewior 	}, {
21453aab70afSSebastian Siewior 		.cmd = "getvar:",
21463aab70afSSebastian Siewior 		.cb = cb_getvar,
21473aab70afSSebastian Siewior 	}, {
21483aab70afSSebastian Siewior 		.cmd = "download:",
21493aab70afSSebastian Siewior 		.cb = cb_download,
21503aab70afSSebastian Siewior 	}, {
2151367cce4dSXu Hongfei 		.cmd = "upload",
2152367cce4dSXu Hongfei 		.cb = cb_upload,
2153367cce4dSXu Hongfei 	}, {
21543aab70afSSebastian Siewior 		.cmd = "boot",
21553aab70afSSebastian Siewior 		.cb = cb_boot,
2156267abc62SRob Herring 	}, {
2157267abc62SRob Herring 		.cmd = "continue",
2158267abc62SRob Herring 		.cb = cb_continue,
2159367cce4dSXu Hongfei 	}, {
2160367cce4dSXu Hongfei 		.cmd = "set_active",
2161367cce4dSXu Hongfei 		.cb = cb_set_active,
21623aab70afSSebastian Siewior 	},
2163d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH
2164d1b5ed07SSteve Rae 	{
2165367cce4dSXu Hongfei 		.cmd = "flashing",
2166367cce4dSXu Hongfei 		.cb = cb_flashing,
2167367cce4dSXu Hongfei 	},
2168367cce4dSXu Hongfei 	{
2169d1b5ed07SSteve Rae 		.cmd = "flash",
2170d1b5ed07SSteve Rae 		.cb = cb_flash,
217189792381SDileep Katta 	}, {
217289792381SDileep Katta 		.cmd = "erase",
217389792381SDileep Katta 		.cb = cb_erase,
2174d1b5ed07SSteve Rae 	},
2175d1b5ed07SSteve Rae #endif
2176de195620SMichael Scott 	{
2177de195620SMichael Scott 		.cmd = "oem",
2178de195620SMichael Scott 		.cb = cb_oem,
2179de195620SMichael Scott 	},
21803aab70afSSebastian Siewior };
21813aab70afSSebastian Siewior 
21823aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
21833aab70afSSebastian Siewior {
21843aab70afSSebastian Siewior 	char *cmdbuf = req->buf;
21853aab70afSSebastian Siewior 	void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
21863aab70afSSebastian Siewior 	int i;
21873aab70afSSebastian Siewior 
218894b385faSPaul Kocialkowski 	if (req->status != 0 || req->length == 0)
218994b385faSPaul Kocialkowski 		return;
219094b385faSPaul Kocialkowski 
21913aab70afSSebastian Siewior 	for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
21923aab70afSSebastian Siewior 		if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
21933aab70afSSebastian Siewior 			func_cb = cmd_dispatch_info[i].cb;
21943aab70afSSebastian Siewior 			break;
21953aab70afSSebastian Siewior 		}
21963aab70afSSebastian Siewior 	}
21973aab70afSSebastian Siewior 
2198593cbd93SSteve Rae 	if (!func_cb) {
219990aa625cSMasahiro Yamada 		pr_err("unknown command: %.*s", req->actual, cmdbuf);
22003aab70afSSebastian Siewior 		fastboot_tx_write_str("FAILunknown command");
2201593cbd93SSteve Rae 	} else {
2202e2140588SEric Nelson 		if (req->actual < req->length) {
2203e2140588SEric Nelson 			u8 *buf = (u8 *)req->buf;
2204e2140588SEric Nelson 			buf[req->actual] = 0;
22053aab70afSSebastian Siewior 			func_cb(ep, req);
2206e2140588SEric Nelson 		} else {
220790aa625cSMasahiro Yamada 			pr_err("buffer overflow");
2208e2140588SEric Nelson 			fastboot_tx_write_str("FAILbuffer overflow");
2209e2140588SEric Nelson 		}
2210593cbd93SSteve Rae 	}
22113aab70afSSebastian Siewior 
22123aab70afSSebastian Siewior 	*cmdbuf = '\0';
22133aab70afSSebastian Siewior 	req->actual = 0;
22143aab70afSSebastian Siewior 	usb_ep_queue(ep, req, 0);
22153aab70afSSebastian Siewior }
2216