xref: /rk3399_rockchip-uboot/drivers/usb/gadget/f_fastboot.c (revision de78ceae23299481131da3c2ec8d8867acf3ad4e)
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
403aab70afSSebastian Siewior 
413aab70afSSebastian Siewior #define FASTBOOT_VERSION		"0.4"
423aab70afSSebastian Siewior 
433aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_CLASS	0xff
443aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_SUB_CLASS	0x42
453aab70afSSebastian Siewior #define FASTBOOT_INTERFACE_PROTOCOL	0x03
463aab70afSSebastian Siewior 
473aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_2_0  (0x0200)
483aab70afSSebastian Siewior #define RX_ENDPOINT_MAXIMUM_PACKET_SIZE_1_1  (0x0040)
493aab70afSSebastian Siewior #define TX_ENDPOINT_MAXIMUM_PACKET_SIZE      (0x0040)
503aab70afSSebastian Siewior 
513aab70afSSebastian Siewior #define EP_BUFFER_SIZE			4096
52*de78ceaeSJason Zhu #define SLEEP_COUNT 20000
53ac484c5aSRoger Quadros /*
54ac484c5aSRoger Quadros  * EP_BUFFER_SIZE must always be an integral multiple of maxpacket size
55ac484c5aSRoger Quadros  * (64 or 512 or 1024), else we break on certain controllers like DWC3
56ac484c5aSRoger Quadros  * that expect bulk OUT requests to be divisible by maxpacket size.
57ac484c5aSRoger Quadros  */
583aab70afSSebastian Siewior 
593aab70afSSebastian Siewior struct f_fastboot {
603aab70afSSebastian Siewior 	struct usb_function usb_function;
613aab70afSSebastian Siewior 
62593cbd93SSteve Rae 	/* IN/OUT EP's and corresponding requests */
633aab70afSSebastian Siewior 	struct usb_ep *in_ep, *out_ep;
643aab70afSSebastian Siewior 	struct usb_request *in_req, *out_req;
653aab70afSSebastian Siewior };
663aab70afSSebastian Siewior 
673aab70afSSebastian Siewior static inline struct f_fastboot *func_to_fastboot(struct usb_function *f)
683aab70afSSebastian Siewior {
693aab70afSSebastian Siewior 	return container_of(f, struct f_fastboot, usb_function);
703aab70afSSebastian Siewior }
713aab70afSSebastian Siewior 
723aab70afSSebastian Siewior static struct f_fastboot *fastboot_func;
733aab70afSSebastian Siewior static unsigned int download_size;
743aab70afSSebastian Siewior static unsigned int download_bytes;
75367cce4dSXu Hongfei static unsigned int upload_size;
76367cce4dSXu Hongfei static unsigned int upload_bytes;
77367cce4dSXu Hongfei static bool start_upload;
78*de78ceaeSJason Zhu static unsigned intthread_wakeup_needed;
793aab70afSSebastian Siewior 
803aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_in = {
813aab70afSSebastian Siewior 	.bLength            = USB_DT_ENDPOINT_SIZE,
823aab70afSSebastian Siewior 	.bDescriptorType    = USB_DT_ENDPOINT,
833aab70afSSebastian Siewior 	.bEndpointAddress   = USB_DIR_IN,
843aab70afSSebastian Siewior 	.bmAttributes       = USB_ENDPOINT_XFER_BULK,
85718156adSRoger Quadros 	.wMaxPacketSize     = cpu_to_le16(64),
863aab70afSSebastian Siewior };
873aab70afSSebastian Siewior 
883aab70afSSebastian Siewior static struct usb_endpoint_descriptor fs_ep_out = {
893aab70afSSebastian Siewior 	.bLength		= USB_DT_ENDPOINT_SIZE,
903aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_ENDPOINT,
913aab70afSSebastian Siewior 	.bEndpointAddress	= USB_DIR_OUT,
923aab70afSSebastian Siewior 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
93718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(64),
94718156adSRoger Quadros };
95718156adSRoger Quadros 
96718156adSRoger Quadros static struct usb_endpoint_descriptor hs_ep_in = {
97718156adSRoger Quadros 	.bLength		= USB_DT_ENDPOINT_SIZE,
98718156adSRoger Quadros 	.bDescriptorType	= USB_DT_ENDPOINT,
99718156adSRoger Quadros 	.bEndpointAddress	= USB_DIR_IN,
100718156adSRoger Quadros 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
101718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(512),
1023aab70afSSebastian Siewior };
1033aab70afSSebastian Siewior 
1043aab70afSSebastian Siewior static struct usb_endpoint_descriptor hs_ep_out = {
1053aab70afSSebastian Siewior 	.bLength		= USB_DT_ENDPOINT_SIZE,
1063aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_ENDPOINT,
1073aab70afSSebastian Siewior 	.bEndpointAddress	= USB_DIR_OUT,
1083aab70afSSebastian Siewior 	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
109718156adSRoger Quadros 	.wMaxPacketSize		= cpu_to_le16(512),
1103aab70afSSebastian Siewior };
1113aab70afSSebastian Siewior 
1123aab70afSSebastian Siewior static struct usb_interface_descriptor interface_desc = {
1133aab70afSSebastian Siewior 	.bLength		= USB_DT_INTERFACE_SIZE,
1143aab70afSSebastian Siewior 	.bDescriptorType	= USB_DT_INTERFACE,
1153aab70afSSebastian Siewior 	.bInterfaceNumber	= 0x00,
1163aab70afSSebastian Siewior 	.bAlternateSetting	= 0x00,
1173aab70afSSebastian Siewior 	.bNumEndpoints		= 0x02,
1183aab70afSSebastian Siewior 	.bInterfaceClass	= FASTBOOT_INTERFACE_CLASS,
1193aab70afSSebastian Siewior 	.bInterfaceSubClass	= FASTBOOT_INTERFACE_SUB_CLASS,
1203aab70afSSebastian Siewior 	.bInterfaceProtocol	= FASTBOOT_INTERFACE_PROTOCOL,
1213aab70afSSebastian Siewior };
1223aab70afSSebastian Siewior 
123718156adSRoger Quadros static struct usb_descriptor_header *fb_fs_function[] = {
1243aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&interface_desc,
1253aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&fs_ep_in,
126718156adSRoger Quadros 	(struct usb_descriptor_header *)&fs_ep_out,
127718156adSRoger Quadros };
128718156adSRoger Quadros 
129718156adSRoger Quadros static struct usb_descriptor_header *fb_hs_function[] = {
130718156adSRoger Quadros 	(struct usb_descriptor_header *)&interface_desc,
131718156adSRoger Quadros 	(struct usb_descriptor_header *)&hs_ep_in,
1323aab70afSSebastian Siewior 	(struct usb_descriptor_header *)&hs_ep_out,
1333aab70afSSebastian Siewior 	NULL,
1343aab70afSSebastian Siewior };
1353aab70afSSebastian Siewior 
1368b704a0eSRoger Quadros static struct usb_endpoint_descriptor *
1378b704a0eSRoger Quadros fb_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
1388b704a0eSRoger Quadros 	    struct usb_endpoint_descriptor *hs)
1398b704a0eSRoger Quadros {
1408b704a0eSRoger Quadros 	if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
1418b704a0eSRoger Quadros 		return hs;
1428b704a0eSRoger Quadros 	return fs;
1438b704a0eSRoger Quadros }
1448b704a0eSRoger Quadros 
1453aab70afSSebastian Siewior /*
1463aab70afSSebastian Siewior  * static strings, in UTF-8
1473aab70afSSebastian Siewior  */
1483aab70afSSebastian Siewior static const char fastboot_name[] = "Android Fastboot";
1493aab70afSSebastian Siewior 
1503aab70afSSebastian Siewior static struct usb_string fastboot_string_defs[] = {
1513aab70afSSebastian Siewior 	[0].s = fastboot_name,
1523aab70afSSebastian Siewior 	{  }			/* end of list */
1533aab70afSSebastian Siewior };
1543aab70afSSebastian Siewior 
1553aab70afSSebastian Siewior static struct usb_gadget_strings stringtab_fastboot = {
1563aab70afSSebastian Siewior 	.language	= 0x0409,	/* en-us */
1573aab70afSSebastian Siewior 	.strings	= fastboot_string_defs,
1583aab70afSSebastian Siewior };
1593aab70afSSebastian Siewior 
1603aab70afSSebastian Siewior static struct usb_gadget_strings *fastboot_strings[] = {
1613aab70afSSebastian Siewior 	&stringtab_fastboot,
1623aab70afSSebastian Siewior 	NULL,
1633aab70afSSebastian Siewior };
1643aab70afSSebastian Siewior 
1653aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
166e2ec3e46SAlexey Firago static int strcmp_l1(const char *s1, const char *s2);
167*de78ceaeSJason Zhu static void wakeup_thread(void)
168*de78ceaeSJason Zhu {
169*de78ceaeSJason Zhu 	intthread_wakeup_needed = false;
170*de78ceaeSJason Zhu }
171*de78ceaeSJason Zhu 
172*de78ceaeSJason Zhu static void busy_indicator(void)
173*de78ceaeSJason Zhu {
174*de78ceaeSJason Zhu 	static int state;
175*de78ceaeSJason Zhu 
176*de78ceaeSJason Zhu 	switch (state) {
177*de78ceaeSJason Zhu 	case 0:
178*de78ceaeSJason Zhu 		puts("\r|"); break;
179*de78ceaeSJason Zhu 	case 1:
180*de78ceaeSJason Zhu 		puts("\r/"); break;
181*de78ceaeSJason Zhu 	case 2:
182*de78ceaeSJason Zhu 		puts("\r-"); break;
183*de78ceaeSJason Zhu 	case 3:
184*de78ceaeSJason Zhu 		puts("\r\\"); break;
185*de78ceaeSJason Zhu 	case 4:
186*de78ceaeSJason Zhu 		puts("\r|"); break;
187*de78ceaeSJason Zhu 	case 5:
188*de78ceaeSJason Zhu 		puts("\r/"); break;
189*de78ceaeSJason Zhu 	case 6:
190*de78ceaeSJason Zhu 		puts("\r-"); break;
191*de78ceaeSJason Zhu 	case 7:
192*de78ceaeSJason Zhu 		puts("\r\\"); break;
193*de78ceaeSJason Zhu 	default:
194*de78ceaeSJason Zhu 		state = 0;
195*de78ceaeSJason Zhu 	}
196*de78ceaeSJason Zhu 	if (state++ == 8)
197*de78ceaeSJason Zhu 		state = 0;
198*de78ceaeSJason Zhu }
199*de78ceaeSJason Zhu 
200*de78ceaeSJason Zhu static int sleep_thread(void)
201*de78ceaeSJason Zhu {
202*de78ceaeSJason Zhu 	int rc = 0;
203*de78ceaeSJason Zhu 	int i = 0, k = 0;
204*de78ceaeSJason Zhu 
205*de78ceaeSJason Zhu 	/* Wait until a signal arrives or we are woken up */
206*de78ceaeSJason Zhu 	for (;;) {
207*de78ceaeSJason Zhu 		if (!intthread_wakeup_needed)
208*de78ceaeSJason Zhu 			break;
209*de78ceaeSJason Zhu 
210*de78ceaeSJason Zhu 		if (++i == SLEEP_COUNT) {
211*de78ceaeSJason Zhu 			busy_indicator();
212*de78ceaeSJason Zhu 			i = 0;
213*de78ceaeSJason Zhu 			k++;
214*de78ceaeSJason Zhu 		}
215*de78ceaeSJason Zhu 
216*de78ceaeSJason Zhu 		if (k == 10) {
217*de78ceaeSJason Zhu 			/* Handle CTRL+C */
218*de78ceaeSJason Zhu 			if (ctrlc())
219*de78ceaeSJason Zhu 				return -EPIPE;
220*de78ceaeSJason Zhu 
221*de78ceaeSJason Zhu 			/* Check cable connection */
222*de78ceaeSJason Zhu 			if (!g_dnl_board_usb_cable_connected())
223*de78ceaeSJason Zhu 				return -EIO;
224*de78ceaeSJason Zhu 
225*de78ceaeSJason Zhu 			k = 0;
226*de78ceaeSJason Zhu 		}
227*de78ceaeSJason Zhu 
228*de78ceaeSJason Zhu 		usb_gadget_handle_interrupts(0);
229*de78ceaeSJason Zhu 	}
230*de78ceaeSJason Zhu 	intthread_wakeup_needed = true;
231*de78ceaeSJason Zhu 	return rc;
232*de78ceaeSJason Zhu }
2333aab70afSSebastian Siewior 
2343aab70afSSebastian Siewior static void fastboot_complete(struct usb_ep *ep, struct usb_request *req)
2353aab70afSSebastian Siewior {
2363aab70afSSebastian Siewior 	int status = req->status;
237*de78ceaeSJason Zhu 
238*de78ceaeSJason Zhu 	wakeup_thread();
2393aab70afSSebastian Siewior 	if (!status)
2403aab70afSSebastian Siewior 		return;
2413aab70afSSebastian Siewior 	printf("status: %d ep '%s' trans: %d\n", status, ep->name, req->actual);
2423aab70afSSebastian Siewior }
2433aab70afSSebastian Siewior 
2443aab70afSSebastian Siewior static int fastboot_bind(struct usb_configuration *c, struct usb_function *f)
2453aab70afSSebastian Siewior {
2463aab70afSSebastian Siewior 	int id;
2473aab70afSSebastian Siewior 	struct usb_gadget *gadget = c->cdev->gadget;
2483aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
249537cd072SDileep Katta 	const char *s;
2503aab70afSSebastian Siewior 
2513aab70afSSebastian Siewior 	/* DYNAMIC interface numbers assignments */
2523aab70afSSebastian Siewior 	id = usb_interface_id(c, f);
2533aab70afSSebastian Siewior 	if (id < 0)
2543aab70afSSebastian Siewior 		return id;
2553aab70afSSebastian Siewior 	interface_desc.bInterfaceNumber = id;
2563aab70afSSebastian Siewior 
2573aab70afSSebastian Siewior 	id = usb_string_id(c->cdev);
2583aab70afSSebastian Siewior 	if (id < 0)
2593aab70afSSebastian Siewior 		return id;
2603aab70afSSebastian Siewior 	fastboot_string_defs[0].id = id;
2613aab70afSSebastian Siewior 	interface_desc.iInterface = id;
2623aab70afSSebastian Siewior 
2633aab70afSSebastian Siewior 	f_fb->in_ep = usb_ep_autoconfig(gadget, &fs_ep_in);
2643aab70afSSebastian Siewior 	if (!f_fb->in_ep)
2653aab70afSSebastian Siewior 		return -ENODEV;
2663aab70afSSebastian Siewior 	f_fb->in_ep->driver_data = c->cdev;
2673aab70afSSebastian Siewior 
2683aab70afSSebastian Siewior 	f_fb->out_ep = usb_ep_autoconfig(gadget, &fs_ep_out);
2693aab70afSSebastian Siewior 	if (!f_fb->out_ep)
2703aab70afSSebastian Siewior 		return -ENODEV;
2713aab70afSSebastian Siewior 	f_fb->out_ep->driver_data = c->cdev;
2723aab70afSSebastian Siewior 
273718156adSRoger Quadros 	f->descriptors = fb_fs_function;
274718156adSRoger Quadros 
275718156adSRoger Quadros 	if (gadget_is_dualspeed(gadget)) {
276718156adSRoger Quadros 		/* Assume endpoint addresses are the same for both speeds */
277718156adSRoger Quadros 		hs_ep_in.bEndpointAddress = fs_ep_in.bEndpointAddress;
2783aab70afSSebastian Siewior 		hs_ep_out.bEndpointAddress = fs_ep_out.bEndpointAddress;
279718156adSRoger Quadros 		/* copy HS descriptors */
280718156adSRoger Quadros 		f->hs_descriptors = fb_hs_function;
281718156adSRoger Quadros 	}
2823aab70afSSebastian Siewior 
28300caae6dSSimon Glass 	s = env_get("serial#");
284537cd072SDileep Katta 	if (s)
285537cd072SDileep Katta 		g_dnl_set_serialnumber((char *)s);
286537cd072SDileep Katta 
2873aab70afSSebastian Siewior 	return 0;
2883aab70afSSebastian Siewior }
2893aab70afSSebastian Siewior 
2903aab70afSSebastian Siewior static void fastboot_unbind(struct usb_configuration *c, struct usb_function *f)
2913aab70afSSebastian Siewior {
2923aab70afSSebastian Siewior 	memset(fastboot_func, 0, sizeof(*fastboot_func));
2933aab70afSSebastian Siewior }
2943aab70afSSebastian Siewior 
2953aab70afSSebastian Siewior static void fastboot_disable(struct usb_function *f)
2963aab70afSSebastian Siewior {
2973aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
2983aab70afSSebastian Siewior 
2993aab70afSSebastian Siewior 	usb_ep_disable(f_fb->out_ep);
3003aab70afSSebastian Siewior 	usb_ep_disable(f_fb->in_ep);
3013aab70afSSebastian Siewior 
3023aab70afSSebastian Siewior 	if (f_fb->out_req) {
3033aab70afSSebastian Siewior 		free(f_fb->out_req->buf);
3043aab70afSSebastian Siewior 		usb_ep_free_request(f_fb->out_ep, f_fb->out_req);
3053aab70afSSebastian Siewior 		f_fb->out_req = NULL;
3063aab70afSSebastian Siewior 	}
3073aab70afSSebastian Siewior 	if (f_fb->in_req) {
3083aab70afSSebastian Siewior 		free(f_fb->in_req->buf);
3093aab70afSSebastian Siewior 		usb_ep_free_request(f_fb->in_ep, f_fb->in_req);
3103aab70afSSebastian Siewior 		f_fb->in_req = NULL;
3113aab70afSSebastian Siewior 	}
3123aab70afSSebastian Siewior }
3133aab70afSSebastian Siewior 
3143aab70afSSebastian Siewior static struct usb_request *fastboot_start_ep(struct usb_ep *ep)
3153aab70afSSebastian Siewior {
3163aab70afSSebastian Siewior 	struct usb_request *req;
3173aab70afSSebastian Siewior 
3183aab70afSSebastian Siewior 	req = usb_ep_alloc_request(ep, 0);
3193aab70afSSebastian Siewior 	if (!req)
3203aab70afSSebastian Siewior 		return NULL;
3213aab70afSSebastian Siewior 
3223aab70afSSebastian Siewior 	req->length = EP_BUFFER_SIZE;
3233aab70afSSebastian Siewior 	req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, EP_BUFFER_SIZE);
3243aab70afSSebastian Siewior 	if (!req->buf) {
3253aab70afSSebastian Siewior 		usb_ep_free_request(ep, req);
3263aab70afSSebastian Siewior 		return NULL;
3273aab70afSSebastian Siewior 	}
3283aab70afSSebastian Siewior 
3293aab70afSSebastian Siewior 	memset(req->buf, 0, req->length);
3303aab70afSSebastian Siewior 	return req;
3313aab70afSSebastian Siewior }
3323aab70afSSebastian Siewior 
3333aab70afSSebastian Siewior static int fastboot_set_alt(struct usb_function *f,
3343aab70afSSebastian Siewior 			    unsigned interface, unsigned alt)
3353aab70afSSebastian Siewior {
3363aab70afSSebastian Siewior 	int ret;
3373aab70afSSebastian Siewior 	struct usb_composite_dev *cdev = f->config->cdev;
3383aab70afSSebastian Siewior 	struct usb_gadget *gadget = cdev->gadget;
3393aab70afSSebastian Siewior 	struct f_fastboot *f_fb = func_to_fastboot(f);
3408b704a0eSRoger Quadros 	const struct usb_endpoint_descriptor *d;
3413aab70afSSebastian Siewior 
3423aab70afSSebastian Siewior 	debug("%s: func: %s intf: %d alt: %d\n",
3433aab70afSSebastian Siewior 	      __func__, f->name, interface, alt);
3443aab70afSSebastian Siewior 
3458b704a0eSRoger Quadros 	d = fb_ep_desc(gadget, &fs_ep_out, &hs_ep_out);
3468b704a0eSRoger Quadros 	ret = usb_ep_enable(f_fb->out_ep, d);
3473aab70afSSebastian Siewior 	if (ret) {
3483aab70afSSebastian Siewior 		puts("failed to enable out ep\n");
3493aab70afSSebastian Siewior 		return ret;
3503aab70afSSebastian Siewior 	}
3513aab70afSSebastian Siewior 
3523aab70afSSebastian Siewior 	f_fb->out_req = fastboot_start_ep(f_fb->out_ep);
3533aab70afSSebastian Siewior 	if (!f_fb->out_req) {
3543aab70afSSebastian Siewior 		puts("failed to alloc out req\n");
3553aab70afSSebastian Siewior 		ret = -EINVAL;
3563aab70afSSebastian Siewior 		goto err;
3573aab70afSSebastian Siewior 	}
3583aab70afSSebastian Siewior 	f_fb->out_req->complete = rx_handler_command;
3593aab70afSSebastian Siewior 
3608b704a0eSRoger Quadros 	d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in);
3618b704a0eSRoger Quadros 	ret = usb_ep_enable(f_fb->in_ep, d);
3623aab70afSSebastian Siewior 	if (ret) {
3633aab70afSSebastian Siewior 		puts("failed to enable in ep\n");
3643aab70afSSebastian Siewior 		goto err;
3653aab70afSSebastian Siewior 	}
3663aab70afSSebastian Siewior 
3673aab70afSSebastian Siewior 	f_fb->in_req = fastboot_start_ep(f_fb->in_ep);
3683aab70afSSebastian Siewior 	if (!f_fb->in_req) {
3693aab70afSSebastian Siewior 		puts("failed alloc req in\n");
3703aab70afSSebastian Siewior 		ret = -EINVAL;
3713aab70afSSebastian Siewior 		goto err;
3723aab70afSSebastian Siewior 	}
3733aab70afSSebastian Siewior 	f_fb->in_req->complete = fastboot_complete;
3743aab70afSSebastian Siewior 
3753aab70afSSebastian Siewior 	ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
3763aab70afSSebastian Siewior 	if (ret)
3773aab70afSSebastian Siewior 		goto err;
3783aab70afSSebastian Siewior 
3793aab70afSSebastian Siewior 	return 0;
3803aab70afSSebastian Siewior err:
3813aab70afSSebastian Siewior 	fastboot_disable(f);
3823aab70afSSebastian Siewior 	return ret;
3833aab70afSSebastian Siewior }
3843aab70afSSebastian Siewior 
3853aab70afSSebastian Siewior static int fastboot_add(struct usb_configuration *c)
3863aab70afSSebastian Siewior {
3873aab70afSSebastian Siewior 	struct f_fastboot *f_fb = fastboot_func;
3883aab70afSSebastian Siewior 	int status;
3893aab70afSSebastian Siewior 
3903aab70afSSebastian Siewior 	debug("%s: cdev: 0x%p\n", __func__, c->cdev);
3913aab70afSSebastian Siewior 
3923aab70afSSebastian Siewior 	if (!f_fb) {
3933aab70afSSebastian Siewior 		f_fb = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*f_fb));
3943aab70afSSebastian Siewior 		if (!f_fb)
3953aab70afSSebastian Siewior 			return -ENOMEM;
3963aab70afSSebastian Siewior 
3973aab70afSSebastian Siewior 		fastboot_func = f_fb;
3983aab70afSSebastian Siewior 		memset(f_fb, 0, sizeof(*f_fb));
3993aab70afSSebastian Siewior 	}
4003aab70afSSebastian Siewior 
4013aab70afSSebastian Siewior 	f_fb->usb_function.name = "f_fastboot";
4023aab70afSSebastian Siewior 	f_fb->usb_function.bind = fastboot_bind;
4033aab70afSSebastian Siewior 	f_fb->usb_function.unbind = fastboot_unbind;
4043aab70afSSebastian Siewior 	f_fb->usb_function.set_alt = fastboot_set_alt;
4053aab70afSSebastian Siewior 	f_fb->usb_function.disable = fastboot_disable;
4063aab70afSSebastian Siewior 	f_fb->usb_function.strings = fastboot_strings;
4073aab70afSSebastian Siewior 
4083aab70afSSebastian Siewior 	status = usb_add_function(c, &f_fb->usb_function);
4093aab70afSSebastian Siewior 	if (status) {
4103aab70afSSebastian Siewior 		free(f_fb);
4113aab70afSSebastian Siewior 		fastboot_func = f_fb;
4123aab70afSSebastian Siewior 	}
4133aab70afSSebastian Siewior 
4143aab70afSSebastian Siewior 	return status;
4153aab70afSSebastian Siewior }
4163aab70afSSebastian Siewior DECLARE_GADGET_BIND_CALLBACK(usb_dnl_fastboot, fastboot_add);
4173aab70afSSebastian Siewior 
418593cbd93SSteve Rae static int fastboot_tx_write(const char *buffer, unsigned int buffer_size)
4193aab70afSSebastian Siewior {
4203aab70afSSebastian Siewior 	struct usb_request *in_req = fastboot_func->in_req;
4213aab70afSSebastian Siewior 	int ret;
4223aab70afSSebastian Siewior 
4233aab70afSSebastian Siewior 	memcpy(in_req->buf, buffer, buffer_size);
4243aab70afSSebastian Siewior 	in_req->length = buffer_size;
425bc9071c9SPaul Kocialkowski 
426bc9071c9SPaul Kocialkowski 	usb_ep_dequeue(fastboot_func->in_ep, in_req);
427bc9071c9SPaul Kocialkowski 
4283aab70afSSebastian Siewior 	ret = usb_ep_queue(fastboot_func->in_ep, in_req, 0);
4293aab70afSSebastian Siewior 	if (ret)
4303aab70afSSebastian Siewior 		printf("Error %d on queue\n", ret);
4313aab70afSSebastian Siewior 	return 0;
4323aab70afSSebastian Siewior }
4333aab70afSSebastian Siewior 
4343aab70afSSebastian Siewior static int fastboot_tx_write_str(const char *buffer)
4353aab70afSSebastian Siewior {
436*de78ceaeSJason Zhu 	int ret;
437*de78ceaeSJason Zhu 
438*de78ceaeSJason Zhu 	ret = sleep_thread();
439*de78ceaeSJason Zhu 	if (ret < 0)
440*de78ceaeSJason Zhu 		printf("warning: 0x%x, usb transmission is abnormal!\n", ret);
441*de78ceaeSJason Zhu 
4423aab70afSSebastian Siewior 	return fastboot_tx_write(buffer, strlen(buffer));
4433aab70afSSebastian Siewior }
4443aab70afSSebastian Siewior 
4453aab70afSSebastian Siewior static void compl_do_reset(struct usb_ep *ep, struct usb_request *req)
4463aab70afSSebastian Siewior {
4473aab70afSSebastian Siewior 	do_reset(NULL, 0, 0, NULL);
4483aab70afSSebastian Siewior }
4493aab70afSSebastian Siewior 
450e2ec3e46SAlexey Firago int __weak fb_set_reboot_flag(void)
451e2ec3e46SAlexey Firago {
452e2ec3e46SAlexey Firago 	return -ENOSYS;
453e2ec3e46SAlexey Firago }
454e2ec3e46SAlexey Firago 
4553aab70afSSebastian Siewior static void cb_reboot(struct usb_ep *ep, struct usb_request *req)
4563aab70afSSebastian Siewior {
457e2ec3e46SAlexey Firago 	char *cmd = req->buf;
458e2ec3e46SAlexey Firago 	if (!strcmp_l1("reboot-bootloader", cmd)) {
459e2ec3e46SAlexey Firago 		if (fb_set_reboot_flag()) {
460e2ec3e46SAlexey Firago 			fastboot_tx_write_str("FAILCannot set reboot flag");
461e2ec3e46SAlexey Firago 			return;
462e2ec3e46SAlexey Firago 		}
463e2ec3e46SAlexey Firago 	}
4643aab70afSSebastian Siewior 	fastboot_func->in_req->complete = compl_do_reset;
4653aab70afSSebastian Siewior 	fastboot_tx_write_str("OKAY");
4663aab70afSSebastian Siewior }
4673aab70afSSebastian Siewior 
4683aab70afSSebastian Siewior static int strcmp_l1(const char *s1, const char *s2)
4693aab70afSSebastian Siewior {
4703aab70afSSebastian Siewior 	if (!s1 || !s2)
4713aab70afSSebastian Siewior 		return -1;
4723aab70afSSebastian Siewior 	return strncmp(s1, s2, strlen(s1));
4733aab70afSSebastian Siewior }
4743aab70afSSebastian Siewior 
4753aab70afSSebastian Siewior static void cb_getvar(struct usb_ep *ep, struct usb_request *req)
4763aab70afSSebastian Siewior {
4773aab70afSSebastian Siewior 	char *cmd = req->buf;
4783c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
4793aab70afSSebastian Siewior 	const char *s;
48029425be4SJeroen Hofstee 	size_t chars_left;
4813aab70afSSebastian Siewior 
4823aab70afSSebastian Siewior 	strcpy(response, "OKAY");
48329425be4SJeroen Hofstee 	chars_left = sizeof(response) - strlen(response) - 1;
48429425be4SJeroen Hofstee 
4853aab70afSSebastian Siewior 	strsep(&cmd, ":");
4863aab70afSSebastian Siewior 	if (!cmd) {
48790aa625cSMasahiro Yamada 		pr_err("missing variable");
4883aab70afSSebastian Siewior 		fastboot_tx_write_str("FAILmissing var");
4893aab70afSSebastian Siewior 		return;
4903aab70afSSebastian Siewior 	}
4913aab70afSSebastian Siewior 
4923aab70afSSebastian Siewior 	if (!strcmp_l1("version", cmd)) {
49329425be4SJeroen Hofstee 		strncat(response, FASTBOOT_VERSION, chars_left);
4943aab70afSSebastian Siewior 	} else if (!strcmp_l1("bootloader-version", cmd)) {
49529425be4SJeroen Hofstee 		strncat(response, U_BOOT_VERSION, chars_left);
496374a9995SCody Xie 	} else if (!strcmp_l1("product", cmd)) {
497374a9995SCody Xie 		strncat(response, CONFIG_SYS_BOARD, chars_left);
498374a9995SCody Xie 	} else if (!strcmp_l1("variant", cmd)) {
499374a9995SCody Xie 		strncat(response, "userdebug", chars_left);
500374a9995SCody Xie 	} else if (!strcmp_l1("secure", cmd)) {
501374a9995SCody Xie 		strncat(response, "no", chars_left);
502374a9995SCody Xie 	} else if (!strcmp_l1("unlocked", cmd)) {
503374a9995SCody Xie 		strncat(response, "yes", chars_left);
504374a9995SCody Xie 	} else if (!strcmp_l1("off-mode-charge", cmd)) {
505374a9995SCody Xie 		strncat(response, "0", chars_left);
506374a9995SCody Xie 	} else if (!strcmp_l1("battery-voltage", cmd)) {
507374a9995SCody Xie 		strncat(response, "7.4", chars_left);
508374a9995SCody Xie 	} else if (!strcmp_l1("battery-soc-ok", cmd)) {
509374a9995SCody Xie 		strncat(response, "yes", chars_left);
510c674a666SEric Nelson 	} else if (!strcmp_l1("downloadsize", cmd) ||
511c674a666SEric Nelson 		!strcmp_l1("max-download-size", cmd)) {
5123aab70afSSebastian Siewior 		char str_num[12];
5133aab70afSSebastian Siewior 
514a588d99aSPaul Kocialkowski 		sprintf(str_num, "0x%08x", CONFIG_FASTBOOT_BUF_SIZE);
51529425be4SJeroen Hofstee 		strncat(response, str_num, chars_left);
5163aab70afSSebastian Siewior 	} else if (!strcmp_l1("serialno", cmd)) {
51700caae6dSSimon Glass 		s = env_get("serial#");
5183aab70afSSebastian Siewior 		if (s)
51929425be4SJeroen Hofstee 			strncat(response, s, chars_left);
5203aab70afSSebastian Siewior 		else
5213aab70afSSebastian Siewior 			strcpy(response, "FAILValue not set");
522367cce4dSXu Hongfei 	} else if (strncmp("at-attest-dh", cmd, 12) == 0) {
5234d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
5244d0fc665SAndy Ye 		char dhbuf[8];
5254d0fc665SAndy Ye 		uint32_t dh_len = 8;
5264d0fc665SAndy Ye 		uint32_t res = trusty_attest_dh((uint8_t *)dhbuf, &dh_len);
5274d0fc665SAndy Ye 		if (res)
5284d0fc665SAndy Ye 			strcpy(response, "FAILdh not set");
5294d0fc665SAndy Ye 		else
5304d0fc665SAndy Ye 			strncat(response, dhbuf, chars_left);
5314d0fc665SAndy Ye #else
5324d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
5334d0fc665SAndy Ye 		return;
5344d0fc665SAndy Ye #endif
535367cce4dSXu Hongfei 	} else if (strncmp("at-attest-uuid", cmd, 14) == 0) {
5364d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
537367cce4dSXu Hongfei 		char uuid[32] = {0};
5384d0fc665SAndy Ye 		uint32_t uuid_len = 32;
5394d0fc665SAndy Ye 		uint32_t res = trusty_attest_uuid((uint8_t *)uuid, &uuid_len);
5404d0fc665SAndy Ye 		if (res)
5414d0fc665SAndy Ye 			strcpy(response, "FAILuuid not set");
5424d0fc665SAndy Ye 		else
543367cce4dSXu Hongfei 			strncat(response, uuid, chars_left);
5444d0fc665SAndy Ye #else
5454d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
5464d0fc665SAndy Ye 		return;
5474d0fc665SAndy Ye #endif
548367cce4dSXu Hongfei 	} else if (strncmp("at-vboot-state", cmd, 14) == 0) {
549367cce4dSXu Hongfei 		char uuid[32] = {0};
550367cce4dSXu Hongfei 
551367cce4dSXu Hongfei 		strncat(response, uuid, chars_left);
552367cce4dSXu Hongfei 	} else if (!strcmp_l1("slot-count", cmd)) {
55337a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
554367cce4dSXu Hongfei 		char slot_count[2];
555367cce4dSXu Hongfei 		char temp;
556367cce4dSXu Hongfei 
557367cce4dSXu Hongfei 		slot_count[1] = '\0';
55837a7bc39SJason Zhu 		rk_avb_read_slot_count(&temp);
559367cce4dSXu Hongfei 		slot_count[0] = temp + 0x30;
560367cce4dSXu Hongfei 		strncat(response, slot_count, chars_left);
561367cce4dSXu Hongfei #else
562367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
563367cce4dSXu Hongfei 		return;
564367cce4dSXu Hongfei #endif
565367cce4dSXu Hongfei 	} else if (!strcmp_l1("current-slot", cmd)) {
56637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
567367cce4dSXu Hongfei 		char slot_surrent[8] = {0};
568367cce4dSXu Hongfei 
56937a7bc39SJason Zhu 		if (!rk_avb_get_current_slot(slot_surrent))
570374a9995SCody Xie 			strncat(response, slot_surrent+1, chars_left);
571367cce4dSXu Hongfei 		else
572367cce4dSXu Hongfei 			strcpy(response, "FAILgeterror");
573367cce4dSXu Hongfei #else
574367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
575367cce4dSXu Hongfei 		return;
576367cce4dSXu Hongfei #endif
577367cce4dSXu Hongfei 	} else if (!strcmp_l1("slot-suffixes", cmd)) {
57837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
579367cce4dSXu Hongfei 		char slot_suffixes_temp[4];
580367cce4dSXu Hongfei 		char slot_suffixes[9];
581367cce4dSXu Hongfei 		int slot_cnt = 0;
582367cce4dSXu Hongfei 
583367cce4dSXu Hongfei 		memset(slot_suffixes_temp, 0, 4);
584367cce4dSXu Hongfei 		memset(slot_suffixes, 0, 9);
58537a7bc39SJason Zhu 		rk_avb_read_slot_suffixes(slot_suffixes_temp);
586367cce4dSXu Hongfei 		while (slot_suffixes_temp[slot_cnt] != '\0') {
587367cce4dSXu Hongfei 			slot_suffixes[slot_cnt * 2]
588367cce4dSXu Hongfei 				= slot_suffixes_temp[slot_cnt];
589367cce4dSXu Hongfei 			slot_suffixes[slot_cnt * 2 + 1] = ',';
590367cce4dSXu Hongfei 			slot_cnt++;
591367cce4dSXu Hongfei 		}
592367cce4dSXu Hongfei 		strncat(response, slot_suffixes, chars_left);
593367cce4dSXu Hongfei #else
594367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
595367cce4dSXu Hongfei 		return;
596367cce4dSXu Hongfei #endif
597367cce4dSXu Hongfei 	} else if (!strncmp("has-slot", cmd, 8)) {
59837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
599367cce4dSXu Hongfei 		char *part_name = cmd;
600367cce4dSXu Hongfei 
601367cce4dSXu Hongfei 		cmd = strsep(&part_name, ":");
602367cce4dSXu Hongfei 		if (!strcmp(part_name, "boot") ||
603367cce4dSXu Hongfei 		    !strcmp(part_name, "system") ||
604374a9995SCody Xie 		    !strcmp(part_name, "vendor") ||
605374a9995SCody Xie 		    !strcmp(part_name, "vbmeta") ||
606374a9995SCody Xie 		    !strcmp(part_name, "oem")) {
607367cce4dSXu Hongfei 			strncat(response, "yes", chars_left);
608367cce4dSXu Hongfei 		} else {
609367cce4dSXu Hongfei 			strcpy(response, "FAILno");
610367cce4dSXu Hongfei 		}
611367cce4dSXu Hongfei #else
612367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
613367cce4dSXu Hongfei 		return;
614367cce4dSXu Hongfei #endif
615374a9995SCody Xie 	} else if (!strncmp("slot-unbootable", cmd, 15)) {
61637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
617374a9995SCody Xie 		char *slot_name = cmd;
618374a9995SCody Xie 
619374a9995SCody Xie 		cmd = strsep(&slot_name, ":");
620374a9995SCody Xie 		if (!strcmp(slot_name, "a") ||
621374a9995SCody Xie 		    !strcmp(slot_name, "b")) {
622374a9995SCody Xie 			strncat(response, "no", chars_left);
623374a9995SCody Xie 		} else {
624374a9995SCody Xie 			strcpy(response, "FAILno");
625374a9995SCody Xie 		}
626374a9995SCody Xie #else
627374a9995SCody Xie 		fastboot_tx_write_str("FAILnot implemented");
628374a9995SCody Xie 		return;
629374a9995SCody Xie #endif
630374a9995SCody Xie 	} else if (!strncmp("slot-successful", cmd, 15)) {
63137a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
632374a9995SCody Xie 		char *slot_name = cmd;
633374a9995SCody Xie 
634374a9995SCody Xie 		cmd = strsep(&slot_name, ":");
635374a9995SCody Xie 		if (!strcmp(slot_name, "a") ||
636374a9995SCody Xie 		    !strcmp(slot_name, "b")) {
637374a9995SCody Xie 			strncat(response, "no", chars_left);
638374a9995SCody Xie 		} else {
639374a9995SCody Xie 			strcpy(response, "FAILno");
640374a9995SCody Xie 		}
641374a9995SCody Xie #else
642374a9995SCody Xie 		fastboot_tx_write_str("FAILnot implemented");
643374a9995SCody Xie 		return;
644374a9995SCody Xie #endif
645374a9995SCody Xie 	} else if (!strncmp("slot-retry-count", cmd, 16)) {
64637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
647374a9995SCody Xie 		char *slot_name = cmd;
648374a9995SCody Xie 		char count[10] = {0};
649374a9995SCody Xie 		static int cnt[2] = {0};
650374a9995SCody Xie 
651374a9995SCody Xie 		cmd = strsep(&slot_name, ":");
652374a9995SCody Xie 		if (!strcmp(slot_name, "a")) {
653374a9995SCody Xie 			sprintf(count, "%c", 0x30+cnt[0]);
654374a9995SCody Xie 			strncat(response, count, chars_left);
655374a9995SCody Xie 			if (cnt[0] > 0)
656374a9995SCody Xie 				cnt[0]--;
657374a9995SCody Xie 		} else if (!strcmp(slot_name, "b")) {
658374a9995SCody Xie 			sprintf(count, "%c", 0x30+cnt[1]);
659374a9995SCody Xie 			strncat(response, count, chars_left);
660374a9995SCody Xie 			if (cnt[1] > 0)
661374a9995SCody Xie 				cnt[1]--;
662374a9995SCody Xie 		} else {
663374a9995SCody Xie 			strcpy(response, "FAILno");
664374a9995SCody Xie 		}
665374a9995SCody Xie #else
666374a9995SCody Xie 		fastboot_tx_write_str("FAILnot implemented");
667374a9995SCody Xie 		return;
668374a9995SCody Xie #endif
669367cce4dSXu Hongfei 	} else if (!strncmp("partition-type", cmd, 14) ||
670367cce4dSXu Hongfei 		   !strncmp("partition-size", cmd, 14)) {
671367cce4dSXu Hongfei 		disk_partition_t part_info;
672367cce4dSXu Hongfei 		struct blk_desc *dev_desc;
673367cce4dSXu Hongfei 		char *part_name = cmd;
674367cce4dSXu Hongfei 		char part_size_str[20];
675367cce4dSXu Hongfei 
676367cce4dSXu Hongfei 		cmd = strsep(&part_name, ":");
677367cce4dSXu Hongfei 		dev_desc = blk_get_dev("mmc", 0);
678367cce4dSXu Hongfei 		if (!dev_desc) {
679367cce4dSXu Hongfei 			strcpy(response, "FAILblock device not found");
680367cce4dSXu Hongfei 		} else if (part_get_info_by_name(dev_desc, part_name, &part_info) < 0) {
681367cce4dSXu Hongfei 			strcpy(response, "FAILpartition not found");
682367cce4dSXu Hongfei 		} else if (!strncmp("partition-type", cmd, 14)) {
683367cce4dSXu Hongfei 			strncat(response, (char *)part_info.type, chars_left);
684367cce4dSXu Hongfei 		} else if (!strncmp("partition-size", cmd, 14)) {
685367cce4dSXu Hongfei 			sprintf(part_size_str, "0x%016x", (int)part_info.size);
686367cce4dSXu Hongfei 			strncat(response, part_size_str, chars_left);
687367cce4dSXu Hongfei 		}
6883aab70afSSebastian Siewior 	} else {
689b1f2a17cSnicolas.le.bayon@st.com 		char *envstr;
69074322201SRob Herring 
691b1f2a17cSnicolas.le.bayon@st.com 		envstr = malloc(strlen("fastboot.") + strlen(cmd) + 1);
692b1f2a17cSnicolas.le.bayon@st.com 		if (!envstr) {
693b1f2a17cSnicolas.le.bayon@st.com 			fastboot_tx_write_str("FAILmalloc error");
694b1f2a17cSnicolas.le.bayon@st.com 			return;
695b1f2a17cSnicolas.le.bayon@st.com 		}
696b1f2a17cSnicolas.le.bayon@st.com 
697b1f2a17cSnicolas.le.bayon@st.com 		sprintf(envstr, "fastboot.%s", cmd);
69800caae6dSSimon Glass 		s = env_get(envstr);
69974322201SRob Herring 		if (s) {
70074322201SRob Herring 			strncat(response, s, chars_left);
70174322201SRob Herring 		} else {
702a18c2706SSteve Rae 			printf("WARNING: unknown variable: %s\n", cmd);
7033aab70afSSebastian Siewior 			strcpy(response, "FAILVariable not implemented");
7043aab70afSSebastian Siewior 		}
705b1f2a17cSnicolas.le.bayon@st.com 
706b1f2a17cSnicolas.le.bayon@st.com 		free(envstr);
70774322201SRob Herring 	}
7083aab70afSSebastian Siewior 	fastboot_tx_write_str(response);
7093aab70afSSebastian Siewior }
7103aab70afSSebastian Siewior 
711ac484c5aSRoger Quadros static unsigned int rx_bytes_expected(struct usb_ep *ep)
7123aab70afSSebastian Siewior {
7133aab70afSSebastian Siewior 	int rx_remain = download_size - download_bytes;
714ac484c5aSRoger Quadros 	unsigned int rem;
715ac484c5aSRoger Quadros 	unsigned int maxpacket = ep->maxpacket;
716ac484c5aSRoger Quadros 
717ac484c5aSRoger Quadros 	if (rx_remain <= 0)
7183aab70afSSebastian Siewior 		return 0;
719ac484c5aSRoger Quadros 	else if (rx_remain > EP_BUFFER_SIZE)
7203aab70afSSebastian Siewior 		return EP_BUFFER_SIZE;
721ac484c5aSRoger Quadros 
722ac484c5aSRoger Quadros 	/*
723ac484c5aSRoger Quadros 	 * Some controllers e.g. DWC3 don't like OUT transfers to be
724ac484c5aSRoger Quadros 	 * not ending in maxpacket boundary. So just make them happy by
725ac484c5aSRoger Quadros 	 * always requesting for integral multiple of maxpackets.
726ac484c5aSRoger Quadros 	 * This shouldn't bother controllers that don't care about it.
727ac484c5aSRoger Quadros 	 */
7289e4b510dSDileep Katta 	rem = rx_remain % maxpacket;
729ac484c5aSRoger Quadros 	if (rem > 0)
7309e4b510dSDileep Katta 		rx_remain = rx_remain + (maxpacket - rem);
731ac484c5aSRoger Quadros 
7323aab70afSSebastian Siewior 	return rx_remain;
7333aab70afSSebastian Siewior }
7343aab70afSSebastian Siewior 
7353aab70afSSebastian Siewior #define BYTES_PER_DOT	0x20000
7363aab70afSSebastian Siewior static void rx_handler_dl_image(struct usb_ep *ep, struct usb_request *req)
7373aab70afSSebastian Siewior {
7383c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
7393aab70afSSebastian Siewior 	unsigned int transfer_size = download_size - download_bytes;
7403aab70afSSebastian Siewior 	const unsigned char *buffer = req->buf;
7413aab70afSSebastian Siewior 	unsigned int buffer_size = req->actual;
74223d1d10cSBo Shen 	unsigned int pre_dot_num, now_dot_num;
7433aab70afSSebastian Siewior 
7443aab70afSSebastian Siewior 	if (req->status != 0) {
7453aab70afSSebastian Siewior 		printf("Bad status: %d\n", req->status);
7463aab70afSSebastian Siewior 		return;
7473aab70afSSebastian Siewior 	}
7483aab70afSSebastian Siewior 
7493aab70afSSebastian Siewior 	if (buffer_size < transfer_size)
7503aab70afSSebastian Siewior 		transfer_size = buffer_size;
7513aab70afSSebastian Siewior 
752a588d99aSPaul Kocialkowski 	memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR + download_bytes,
7533aab70afSSebastian Siewior 	       buffer, transfer_size);
7543aab70afSSebastian Siewior 
75523d1d10cSBo Shen 	pre_dot_num = download_bytes / BYTES_PER_DOT;
7563aab70afSSebastian Siewior 	download_bytes += transfer_size;
75723d1d10cSBo Shen 	now_dot_num = download_bytes / BYTES_PER_DOT;
75823d1d10cSBo Shen 
75923d1d10cSBo Shen 	if (pre_dot_num != now_dot_num) {
76023d1d10cSBo Shen 		putc('.');
76123d1d10cSBo Shen 		if (!(now_dot_num % 74))
76223d1d10cSBo Shen 			putc('\n');
76323d1d10cSBo Shen 	}
7643aab70afSSebastian Siewior 
7653aab70afSSebastian Siewior 	/* Check if transfer is done */
7663aab70afSSebastian Siewior 	if (download_bytes >= download_size) {
7673aab70afSSebastian Siewior 		/*
7683aab70afSSebastian Siewior 		 * Reset global transfer variable, keep download_bytes because
7693aab70afSSebastian Siewior 		 * it will be used in the next possible flashing command
7703aab70afSSebastian Siewior 		 */
7713aab70afSSebastian Siewior 		download_size = 0;
7723aab70afSSebastian Siewior 		req->complete = rx_handler_command;
7733aab70afSSebastian Siewior 		req->length = EP_BUFFER_SIZE;
7743aab70afSSebastian Siewior 
775192bc694SBen Whitten 		strcpy(response, "OKAY");
7763aab70afSSebastian Siewior 		fastboot_tx_write_str(response);
7773aab70afSSebastian Siewior 
7783aab70afSSebastian Siewior 		printf("\ndownloading of %d bytes finished\n", download_bytes);
7793aab70afSSebastian Siewior 	} else {
780ac484c5aSRoger Quadros 		req->length = rx_bytes_expected(ep);
7813aab70afSSebastian Siewior 	}
7823aab70afSSebastian Siewior 
7833aab70afSSebastian Siewior 	req->actual = 0;
7843aab70afSSebastian Siewior 	usb_ep_queue(ep, req, 0);
7853aab70afSSebastian Siewior }
7863aab70afSSebastian Siewior 
7873aab70afSSebastian Siewior static void cb_download(struct usb_ep *ep, struct usb_request *req)
7883aab70afSSebastian Siewior {
7893aab70afSSebastian Siewior 	char *cmd = req->buf;
7903c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
7913aab70afSSebastian Siewior 
7923aab70afSSebastian Siewior 	strsep(&cmd, ":");
7933aab70afSSebastian Siewior 	download_size = simple_strtoul(cmd, NULL, 16);
7943aab70afSSebastian Siewior 	download_bytes = 0;
7953aab70afSSebastian Siewior 
7963aab70afSSebastian Siewior 	printf("Starting download of %d bytes\n", download_size);
7973aab70afSSebastian Siewior 
7983aab70afSSebastian Siewior 	if (0 == download_size) {
799192bc694SBen Whitten 		strcpy(response, "FAILdata invalid size");
800a588d99aSPaul Kocialkowski 	} else if (download_size > CONFIG_FASTBOOT_BUF_SIZE) {
8013aab70afSSebastian Siewior 		download_size = 0;
802192bc694SBen Whitten 		strcpy(response, "FAILdata too large");
8033aab70afSSebastian Siewior 	} else {
8043aab70afSSebastian Siewior 		sprintf(response, "DATA%08x", download_size);
8053aab70afSSebastian Siewior 		req->complete = rx_handler_dl_image;
806ac484c5aSRoger Quadros 		req->length = rx_bytes_expected(ep);
8073aab70afSSebastian Siewior 	}
808367cce4dSXu Hongfei 
809367cce4dSXu Hongfei 	fastboot_tx_write_str(response);
810367cce4dSXu Hongfei }
811367cce4dSXu Hongfei 
812367cce4dSXu Hongfei static void tx_handler_ul(struct usb_ep *ep, struct usb_request *req)
813367cce4dSXu Hongfei {
814367cce4dSXu Hongfei 	unsigned int xfer_size = 0;
815367cce4dSXu Hongfei 	unsigned int pre_dot_num, now_dot_num;
816367cce4dSXu Hongfei 	unsigned int remain_size = 0;
817367cce4dSXu Hongfei 	unsigned int transferred_size = req->actual;
818367cce4dSXu Hongfei 
819367cce4dSXu Hongfei 	if (req->status != 0) {
820367cce4dSXu Hongfei 		printf("Bad status: %d\n", req->status);
821367cce4dSXu Hongfei 		return;
822367cce4dSXu Hongfei 	}
823367cce4dSXu Hongfei 
824367cce4dSXu Hongfei 	if (start_upload) {
825367cce4dSXu Hongfei 		pre_dot_num = upload_bytes / BYTES_PER_DOT;
826367cce4dSXu Hongfei 		upload_bytes += transferred_size;
827367cce4dSXu Hongfei 		now_dot_num = upload_bytes / BYTES_PER_DOT;
828367cce4dSXu Hongfei 
829367cce4dSXu Hongfei 		if (pre_dot_num != now_dot_num) {
830367cce4dSXu Hongfei 			putc('.');
831367cce4dSXu Hongfei 			if (!(now_dot_num % 74))
832367cce4dSXu Hongfei 				putc('\n');
833367cce4dSXu Hongfei 		}
834367cce4dSXu Hongfei 	}
835367cce4dSXu Hongfei 
836367cce4dSXu Hongfei 	remain_size = upload_size - upload_bytes;
837367cce4dSXu Hongfei 	xfer_size = (remain_size > EP_BUFFER_SIZE) ?
838367cce4dSXu Hongfei 		    EP_BUFFER_SIZE : remain_size;
839367cce4dSXu Hongfei 
840367cce4dSXu Hongfei 	debug("%s: remain_size=%d, transferred_size=%d",
841367cce4dSXu Hongfei 	      __func__, remain_size, transferred_size);
842367cce4dSXu Hongfei 	debug("xfer_size=%d, upload_bytes=%d, upload_size=%d!\n",
843367cce4dSXu Hongfei 	      xfer_size, upload_bytes, upload_size);
844367cce4dSXu Hongfei 
845367cce4dSXu Hongfei 	if (remain_size <= 0) {
846367cce4dSXu Hongfei 		fastboot_func->in_req->complete = fastboot_complete;
847367cce4dSXu Hongfei 		fastboot_tx_write_str("OKAY");
848367cce4dSXu Hongfei 		printf("\nuploading of %d bytes finished\n", upload_bytes);
849367cce4dSXu Hongfei 		upload_bytes = 0;
850367cce4dSXu Hongfei 		upload_size = 0;
851367cce4dSXu Hongfei 		start_upload = false;
852367cce4dSXu Hongfei 		return;
853367cce4dSXu Hongfei 	}
854367cce4dSXu Hongfei 
855367cce4dSXu Hongfei 	/* Remove the transfer callback which response the upload */
856367cce4dSXu Hongfei 	/* request from host */
857367cce4dSXu Hongfei 	if (!upload_bytes)
858367cce4dSXu Hongfei 		start_upload = true;
859367cce4dSXu Hongfei 
860174b4544SKever Yang 	fastboot_tx_write((char *)((phys_addr_t)CONFIG_FASTBOOT_BUF_ADDR + \
861174b4544SKever Yang 			  upload_bytes),
862367cce4dSXu Hongfei 			  xfer_size);
863367cce4dSXu Hongfei }
864367cce4dSXu Hongfei 
865367cce4dSXu Hongfei static void cb_upload(struct usb_ep *ep, struct usb_request *req)
866367cce4dSXu Hongfei {
867367cce4dSXu Hongfei 	char response[FASTBOOT_RESPONSE_LEN];
868367cce4dSXu Hongfei 
869367cce4dSXu Hongfei 	printf("Starting upload of %d bytes\n", upload_size);
870367cce4dSXu Hongfei 
871367cce4dSXu Hongfei 	if (0 == upload_size) {
872367cce4dSXu Hongfei 		strcpy(response, "FAILdata invalid size");
873367cce4dSXu Hongfei 	} else {
874367cce4dSXu Hongfei 		start_upload = false;
875367cce4dSXu Hongfei 		sprintf(response, "DATA%08x", upload_size);
876367cce4dSXu Hongfei 		fastboot_func->in_req->complete = tx_handler_ul;
877367cce4dSXu Hongfei 	}
878367cce4dSXu Hongfei 
8793aab70afSSebastian Siewior 	fastboot_tx_write_str(response);
8803aab70afSSebastian Siewior }
8813aab70afSSebastian Siewior 
8823aab70afSSebastian Siewior static void do_bootm_on_complete(struct usb_ep *ep, struct usb_request *req)
8833aab70afSSebastian Siewior {
8843aab70afSSebastian Siewior 	char boot_addr_start[12];
8853aab70afSSebastian Siewior 	char *bootm_args[] = { "bootm", boot_addr_start, NULL };
8863aab70afSSebastian Siewior 
8873aab70afSSebastian Siewior 	puts("Booting kernel..\n");
8883aab70afSSebastian Siewior 
88990ae53ceSTom Rini 	sprintf(boot_addr_start, "0x%lx", (long)CONFIG_FASTBOOT_BUF_ADDR);
8903aab70afSSebastian Siewior 	do_bootm(NULL, 0, 2, bootm_args);
8913aab70afSSebastian Siewior 
8923aab70afSSebastian Siewior 	/* This only happens if image is somehow faulty so we start over */
8933aab70afSSebastian Siewior 	do_reset(NULL, 0, 0, NULL);
8943aab70afSSebastian Siewior }
8953aab70afSSebastian Siewior 
8963aab70afSSebastian Siewior static void cb_boot(struct usb_ep *ep, struct usb_request *req)
8973aab70afSSebastian Siewior {
8983aab70afSSebastian Siewior 	fastboot_func->in_req->complete = do_bootm_on_complete;
8993aab70afSSebastian Siewior 	fastboot_tx_write_str("OKAY");
9003aab70afSSebastian Siewior }
9013aab70afSSebastian Siewior 
902267abc62SRob Herring static void do_exit_on_complete(struct usb_ep *ep, struct usb_request *req)
903267abc62SRob Herring {
904267abc62SRob Herring 	g_dnl_trigger_detach();
905267abc62SRob Herring }
906267abc62SRob Herring 
907267abc62SRob Herring static void cb_continue(struct usb_ep *ep, struct usb_request *req)
908267abc62SRob Herring {
909267abc62SRob Herring 	fastboot_func->in_req->complete = do_exit_on_complete;
910267abc62SRob Herring 	fastboot_tx_write_str("OKAY");
911267abc62SRob Herring }
912267abc62SRob Herring 
913367cce4dSXu Hongfei static void cb_set_active(struct usb_ep *ep, struct usb_request *req)
914367cce4dSXu Hongfei {
915367cce4dSXu Hongfei 	char *cmd = req->buf;
916367cce4dSXu Hongfei 
917367cce4dSXu Hongfei 	debug("%s: %s\n", __func__, cmd);
918367cce4dSXu Hongfei 
919367cce4dSXu Hongfei 	strsep(&cmd, ":");
920367cce4dSXu Hongfei 	if (!cmd) {
92190aa625cSMasahiro Yamada 		pr_err("missing slot name");
9222e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILmissing slot name");
923367cce4dSXu Hongfei 		return;
924367cce4dSXu Hongfei 	}
92537a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
926367cce4dSXu Hongfei 	unsigned int slot_number;
927367cce4dSXu Hongfei 	if (strncmp("a", cmd, 1) == 0) {
928367cce4dSXu Hongfei 		slot_number = 0;
92937a7bc39SJason Zhu 		rk_avb_set_slot_active(&slot_number);
930367cce4dSXu Hongfei 	} else if (strncmp("b", cmd, 1) == 0) {
931367cce4dSXu Hongfei 		slot_number = 1;
93237a7bc39SJason Zhu 		rk_avb_set_slot_active(&slot_number);
933367cce4dSXu Hongfei 	} else {
9342e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILunkown slot name");
935367cce4dSXu Hongfei 		return;
936367cce4dSXu Hongfei 	}
937367cce4dSXu Hongfei 
938367cce4dSXu Hongfei 	fastboot_tx_write_str("OKAY");
939367cce4dSXu Hongfei 	return;
940367cce4dSXu Hongfei #else
941367cce4dSXu Hongfei 	fastboot_tx_write_str("FAILnot implemented");
942367cce4dSXu Hongfei 	return;
943367cce4dSXu Hongfei #endif
944367cce4dSXu Hongfei }
945367cce4dSXu Hongfei 
946d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH
947d1b5ed07SSteve Rae static void cb_flash(struct usb_ep *ep, struct usb_request *req)
948d1b5ed07SSteve Rae {
949d1b5ed07SSteve Rae 	char *cmd = req->buf;
950374a9995SCody Xie 	char response[FASTBOOT_RESPONSE_LEN] = {0};
95137a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
9527bc1707dSJason Zhu 	uint8_t flash_lock_state;
953d1b5ed07SSteve Rae 
95437a7bc39SJason Zhu 	if (rk_avb_read_flash_lock_state(&flash_lock_state)) {
9552e40c2c1SJason Zhu 		/* write the device flashing unlock when first read */
9562e40c2c1SJason Zhu 		if (rk_avb_write_flash_lock_state(1)) {
9572e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state write failure");
9587bc1707dSJason Zhu 			return;
959ef52a073SJason Zhu 		}
9602e40c2c1SJason Zhu 		if (rk_avb_read_flash_lock_state(&flash_lock_state)) {
9612e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state read failure");
9622e40c2c1SJason Zhu 			return;
9632e40c2c1SJason Zhu 		}
9642e40c2c1SJason Zhu 	}
965ef52a073SJason Zhu 
9667bc1707dSJason Zhu 	if (flash_lock_state == 0) {
9677bc1707dSJason Zhu 		fastboot_tx_write_str("FAILThe device is locked, can not flash!");
9687bc1707dSJason Zhu 		printf("The device is locked, can not flash!\n");
9697bc1707dSJason Zhu 		return;
9707bc1707dSJason Zhu 	}
9717bc1707dSJason Zhu #endif
972d1b5ed07SSteve Rae 	strsep(&cmd, ":");
973d1b5ed07SSteve Rae 	if (!cmd) {
97490aa625cSMasahiro Yamada 		pr_err("missing partition name");
975d1b5ed07SSteve Rae 		fastboot_tx_write_str("FAILmissing partition name");
976d1b5ed07SSteve Rae 		return;
977d1b5ed07SSteve Rae 	}
978d1b5ed07SSteve Rae 
9798b464fa9SJocelyn Bohr 	fastboot_fail("no flash device defined", response);
980d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
98164ece848SSteve Rae 	fb_mmc_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
9828b464fa9SJocelyn Bohr 				download_bytes, response);
983d1b5ed07SSteve Rae #endif
984bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
9858b464fa9SJocelyn Bohr 	fb_nand_flash_write(cmd, (void *)CONFIG_FASTBOOT_BUF_ADDR,
9868b464fa9SJocelyn Bohr 				download_bytes, response);
987bf8940d3SMaxime Ripard #endif
988d1b5ed07SSteve Rae 	fastboot_tx_write_str(response);
989d1b5ed07SSteve Rae }
990d1b5ed07SSteve Rae 
991367cce4dSXu Hongfei static void cb_flashing(struct usb_ep *ep, struct usb_request *req)
992367cce4dSXu Hongfei {
993367cce4dSXu Hongfei 	char *cmd = req->buf;
994367cce4dSXu Hongfei 
995367cce4dSXu Hongfei 	if (strncmp("lock", cmd + 9, 4) == 0) {
99637a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
9977bc1707dSJason Zhu 		uint8_t flash_lock_state;
9987bc1707dSJason Zhu 		flash_lock_state = 0;
99937a7bc39SJason Zhu 		if (rk_avb_write_flash_lock_state(flash_lock_state))
10002e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state"
10012e40c2c1SJason Zhu 					      " write failure");
10027bc1707dSJason Zhu 		else
10037bc1707dSJason Zhu 			fastboot_tx_write_str("OKAY");
10047bc1707dSJason Zhu #else
1005367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
10067bc1707dSJason Zhu #endif
1007367cce4dSXu Hongfei 	} else if (strncmp("unlock", cmd + 9, 6) == 0) {
100837a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
10097bc1707dSJason Zhu 		uint8_t flash_lock_state;
10107bc1707dSJason Zhu 		flash_lock_state = 1;
101137a7bc39SJason Zhu 		if (rk_avb_write_flash_lock_state(flash_lock_state))
10122e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILflash lock state"
10132e40c2c1SJason Zhu 					      " write failure");
10147bc1707dSJason Zhu 		else
10157bc1707dSJason Zhu 			fastboot_tx_write_str("OKAY");
10167bc1707dSJason Zhu #else
1017367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
10187bc1707dSJason Zhu #endif
1019367cce4dSXu Hongfei 	} else if (strncmp("lock_critical", cmd + 9, 12) == 0) {
1020367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1021367cce4dSXu Hongfei 	} else if (strncmp("unlock_critical", cmd + 9, 14) == 0) {
1022367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1023367cce4dSXu Hongfei 	} else if (strncmp("get_unlock_ability", cmd + 9, 17) == 0) {
1024367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1025367cce4dSXu Hongfei 	} else if (strncmp("get_unlock_bootloader_nonce", cmd + 4, 27) == 0) {
1026367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1027367cce4dSXu Hongfei 	} else if (strncmp("unlock_bootloader", cmd + 9, 17) == 0) {
1028367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1029367cce4dSXu Hongfei 	} else if (strncmp("lock_bootloader", cmd + 9, 15) == 0) {
1030367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1031367cce4dSXu Hongfei 	} else {
1032367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILunknown flashing command");
1033367cce4dSXu Hongfei 	}
1034367cce4dSXu Hongfei }
10356f3eb474SKever Yang #endif
1036367cce4dSXu Hongfei 
10372e40c2c1SJason Zhu static void cb_oem_perm_attr(void)
10382e40c2c1SJason Zhu {
10392e40c2c1SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
10402e40c2c1SJason Zhu 	sha256_context ctx;
10412e40c2c1SJason Zhu 	uint8_t digest[SHA256_SUM_LEN] = {0};
10422e40c2c1SJason Zhu 	uint8_t digest_temp[SHA256_SUM_LEN] = {0};
10432e40c2c1SJason Zhu 	uint8_t perm_attr_temp[PERM_ATTR_TOTAL_SIZE] = {0};
10442e40c2c1SJason Zhu 	uint8_t flag = 0;
10452e40c2c1SJason Zhu 
10462e40c2c1SJason Zhu 	if (PERM_ATTR_TOTAL_SIZE != download_bytes) {
10472e40c2c1SJason Zhu 		printf("Permanent attribute size is not equal!\n");
10482e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILincorrect perm attribute size");
10492e40c2c1SJason Zhu 		return;
10502e40c2c1SJason Zhu 	}
10512e40c2c1SJason Zhu 
10522e40c2c1SJason Zhu 	if (rk_avb_read_perm_attr_flag(&flag)) {
10532e40c2c1SJason Zhu 		printf("rk_avb_read_perm_attr_flag error!\n");
10542e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr read failed");
10552e40c2c1SJason Zhu 		return;
10562e40c2c1SJason Zhu 	}
10572e40c2c1SJason Zhu 
10582e40c2c1SJason Zhu 	if (flag == PERM_ATTR_SUCCESS_FLAG) {
10592e40c2c1SJason Zhu 		if (rk_avb_read_attribute_hash(digest_temp,
10602e40c2c1SJason Zhu 					       SHA256_SUM_LEN)) {
10612e40c2c1SJason Zhu 			printf("The efuse IO can not be used!\n");
10622e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILefuse IO can not be used");
10632e40c2c1SJason Zhu 			return;
10642e40c2c1SJason Zhu 		}
10652e40c2c1SJason Zhu 
10662e40c2c1SJason Zhu 		if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) {
10672e40c2c1SJason Zhu 			if (rk_avb_read_permanent_attributes(perm_attr_temp,
10682e40c2c1SJason Zhu 							     PERM_ATTR_TOTAL_SIZE)) {
10692e40c2c1SJason Zhu 				printf("rk_avb_write_permanent_attributes error!\n");
10702e40c2c1SJason Zhu 				fastboot_tx_write_str("FAILread perm attr error");
10712e40c2c1SJason Zhu 				return;
10722e40c2c1SJason Zhu 			}
10732e40c2c1SJason Zhu 
10742e40c2c1SJason Zhu 			sha256_starts(&ctx);
10752e40c2c1SJason Zhu 			sha256_update(&ctx,
10762e40c2c1SJason Zhu 				      (const uint8_t *)perm_attr_temp,
10772e40c2c1SJason Zhu 				      PERM_ATTR_TOTAL_SIZE);
10782e40c2c1SJason Zhu 			sha256_finish(&ctx, digest);
10792e40c2c1SJason Zhu 			if (memcmp(digest, digest_temp, SHA256_SUM_LEN) == 0) {
10802e40c2c1SJason Zhu 				printf("The hash has been written!\n");
10812e40c2c1SJason Zhu 				fastboot_tx_write_str("OKAY");
10822e40c2c1SJason Zhu 				return;
10832e40c2c1SJason Zhu 			}
10842e40c2c1SJason Zhu 		}
10852e40c2c1SJason Zhu 
10862e40c2c1SJason Zhu 		if (rk_avb_write_perm_attr_flag(0)) {
10872e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILperm attr flag write failure");
10882e40c2c1SJason Zhu 			return;
10892e40c2c1SJason Zhu 		}
10902e40c2c1SJason Zhu 	}
10912e40c2c1SJason Zhu 
10922e40c2c1SJason Zhu 	if (rk_avb_write_permanent_attributes((uint8_t *)
10932e40c2c1SJason Zhu 					      CONFIG_FASTBOOT_BUF_ADDR,
10942e40c2c1SJason Zhu 					      download_bytes)) {
10952e40c2c1SJason Zhu 		if (rk_avb_write_perm_attr_flag(0)) {
10962e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILperm attr flag write failure");
10972e40c2c1SJason Zhu 			return;
10982e40c2c1SJason Zhu 		}
10992e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr write failed");
11002e40c2c1SJason Zhu 		return;
11012e40c2c1SJason Zhu 	}
11022e40c2c1SJason Zhu 
11032e40c2c1SJason Zhu 	memset(digest, 0, SHA256_SUM_LEN);
11042e40c2c1SJason Zhu 	sha256_starts(&ctx);
11052e40c2c1SJason Zhu 	sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
11062e40c2c1SJason Zhu 		      PERM_ATTR_TOTAL_SIZE);
11072e40c2c1SJason Zhu 	sha256_finish(&ctx, digest);
11082e40c2c1SJason Zhu 
11092e40c2c1SJason Zhu 	if (rk_avb_write_attribute_hash((uint8_t *)digest,
11102e40c2c1SJason Zhu 					SHA256_SUM_LEN)) {
11112e40c2c1SJason Zhu 		if (rk_avb_read_attribute_hash(digest_temp,
11122e40c2c1SJason Zhu 						SHA256_SUM_LEN)) {
11132e40c2c1SJason Zhu 			printf("The efuse IO can not be used!\n");
11142e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILefuse IO can not be used");
11152e40c2c1SJason Zhu 			return;
11162e40c2c1SJason Zhu 		}
11172e40c2c1SJason Zhu 		if (memcmp(digest, digest_temp, SHA256_SUM_LEN) != 0) {
11182e40c2c1SJason Zhu 			if (rk_avb_write_perm_attr_flag(0)) {
11192e40c2c1SJason Zhu 				fastboot_tx_write_str("FAILperm attr flag write failure");
11202e40c2c1SJason Zhu 				return;
11212e40c2c1SJason Zhu 			}
11222e40c2c1SJason Zhu 			printf("The hash has been written, but is different!\n");
11232e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILhash comparison failure");
11242e40c2c1SJason Zhu 			return;
11252e40c2c1SJason Zhu 		}
11262e40c2c1SJason Zhu 	}
11272e40c2c1SJason Zhu 
11282e40c2c1SJason Zhu 	if (rk_avb_write_perm_attr_flag(PERM_ATTR_SUCCESS_FLAG)) {
11292e40c2c1SJason Zhu 		fastboot_tx_write_str("FAILperm attr flag write failure");
11302e40c2c1SJason Zhu 		return;
11312e40c2c1SJason Zhu 	}
11322e40c2c1SJason Zhu 
11332e40c2c1SJason Zhu 	fastboot_tx_write_str("OKAY");
11342e40c2c1SJason Zhu #else
11352e40c2c1SJason Zhu 	fastboot_tx_write_str("FAILnot implemented");
11362e40c2c1SJason Zhu #endif
11372e40c2c1SJason Zhu }
11382e40c2c1SJason Zhu 
1139de195620SMichael Scott static void cb_oem(struct usb_ep *ep, struct usb_request *req)
1140de195620SMichael Scott {
1141de195620SMichael Scott 	char *cmd = req->buf;
1142367cce4dSXu Hongfei 
11434adef270SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
1144372d7decSRob Herring 	if (strncmp("format", cmd + 4, 6) == 0) {
1145372d7decSRob Herring 		char cmdbuf[32];
1146372d7decSRob Herring                 sprintf(cmdbuf, "gpt write mmc %x $partitions",
1147372d7decSRob Herring 			CONFIG_FASTBOOT_FLASH_MMC_DEV);
1148372d7decSRob Herring                 if (run_command(cmdbuf, 0))
11492e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILmmc write failure");
1150372d7decSRob Herring                 else
1151372d7decSRob Herring 			fastboot_tx_write_str("OKAY");
1152372d7decSRob Herring 	} else
1153372d7decSRob Herring #endif
1154de195620SMichael Scott 	if (strncmp("unlock", cmd + 4, 8) == 0) {
1155de195620SMichael Scott 		fastboot_tx_write_str("FAILnot implemented");
1156367cce4dSXu Hongfei 	} else if (strncmp("at-get-ca-request", cmd + 4, 17) == 0) {
11574d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
11582e40c2c1SJason Zhu 		uint8_t out[ATTEST_CA_OUT_SIZE];
11594d0fc665SAndy Ye 		uint32_t operation_size = download_bytes;
11602e40c2c1SJason Zhu 		uint32_t out_len = ATTEST_CA_OUT_SIZE;
11614d0fc665SAndy Ye 		uint32_t res = 0;
11622e40c2c1SJason Zhu 
11632e40c2c1SJason Zhu 		res = trusty_attest_get_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
11642e40c2c1SJason Zhu 					   &operation_size, out, &out_len);
11654d0fc665SAndy Ye 		if (res) {
11664d0fc665SAndy Ye 			fastboot_tx_write_str("FAILtrusty_attest_get_ca failed");
11674d0fc665SAndy Ye 			return;
11684d0fc665SAndy Ye 		}
11694d0fc665SAndy Ye 		upload_size = out_len;
11704d0fc665SAndy Ye 		memcpy((void *)CONFIG_FASTBOOT_BUF_ADDR, out, out_len);
1171367cce4dSXu Hongfei 		fastboot_tx_write_str("OKAY");
11724d0fc665SAndy Ye #else
11734d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
11744d0fc665SAndy Ye 		return;
11754d0fc665SAndy Ye #endif
1176367cce4dSXu Hongfei 	} else if (strncmp("at-set-ca-response", cmd + 4, 18) == 0) {
11774d0fc665SAndy Ye #ifdef CONFIG_OPTEE_CLIENT
11784d0fc665SAndy Ye 		uint32_t ca_response_size = download_bytes;
11794d0fc665SAndy Ye 		uint32_t res = 0;
11802e40c2c1SJason Zhu 
11812e40c2c1SJason Zhu 		res = trusty_attest_set_ca((uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
11822e40c2c1SJason Zhu 					   &ca_response_size);
11832e40c2c1SJason Zhu 		if (res)
11844d0fc665SAndy Ye 			fastboot_tx_write_str("FAILtrusty_attest_set_ca failed");
11852e40c2c1SJason Zhu 		else
1186367cce4dSXu Hongfei 			fastboot_tx_write_str("OKAY");
11874d0fc665SAndy Ye #else
11884d0fc665SAndy Ye 		fastboot_tx_write_str("FAILnot implemented");
11894d0fc665SAndy Ye 		return;
11904d0fc665SAndy Ye #endif
1191367cce4dSXu Hongfei 	} else if (strncmp("at-lock-vboot", cmd + 4, 13) == 0) {
119237a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
1193d8bd6e97SJason Zhu 		uint8_t lock_state;
1194d8bd6e97SJason Zhu 		lock_state = 0;
119537a7bc39SJason Zhu 		if (rk_avb_write_lock_state(lock_state))
11962e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILwrite lock state failed");
1197d8bd6e97SJason Zhu 		else
1198d8bd6e97SJason Zhu 			fastboot_tx_write_str("OKAY");
1199d8bd6e97SJason Zhu #else
1200367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1201d8bd6e97SJason Zhu #endif
1202367cce4dSXu Hongfei 	} else if (strncmp("at-unlock-vboot", cmd + 4, 15) == 0) {
120337a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
1204d8bd6e97SJason Zhu 		uint8_t lock_state;
120537a7bc39SJason Zhu 		if (rk_avb_read_lock_state(&lock_state))
12062e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILlock sate read failure");
1207d8bd6e97SJason Zhu 		if (lock_state >> 1 == 1) {
1208d8bd6e97SJason Zhu 			fastboot_tx_write_str("FAILThe vboot is disable!");
1209d8bd6e97SJason Zhu 		} else {
1210d8bd6e97SJason Zhu 			lock_state = 1;
121137a7bc39SJason Zhu 			if (rk_avb_write_lock_state(lock_state))
12122e40c2c1SJason Zhu 				fastboot_tx_write_str("FAILwrite lock state failed");
1213d8bd6e97SJason Zhu 			else
1214d8bd6e97SJason Zhu 				fastboot_tx_write_str("OKAY");
1215d8bd6e97SJason Zhu 		}
1216d8bd6e97SJason Zhu #else
1217367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1218d8bd6e97SJason Zhu #endif
1219367cce4dSXu Hongfei 	} else if (strncmp("at-disable-unlock-vboot", cmd + 4, 23) == 0) {
122037a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
1221d8bd6e97SJason Zhu 		uint8_t lock_state;
1222d8bd6e97SJason Zhu 		lock_state = 2;
122337a7bc39SJason Zhu 		if (rk_avb_write_lock_state(lock_state))
12242e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILwrite lock state failed");
1225d8bd6e97SJason Zhu 		else
1226d8bd6e97SJason Zhu 			fastboot_tx_write_str("OKAY");
1227d8bd6e97SJason Zhu #else
1228367cce4dSXu Hongfei 		fastboot_tx_write_str("FAILnot implemented");
1229d8bd6e97SJason Zhu #endif
1230367cce4dSXu Hongfei 	} else if (strncmp("fuse at-perm-attr", cmd + 4, 16) == 0) {
12312e40c2c1SJason Zhu 		cb_oem_perm_attr();
12324e1bbe84SJason Zhu 	} else if (strncmp("fuse at-bootloader-vboot-key", cmd + 4, 27) == 0) {
123337a7bc39SJason Zhu #ifdef CONFIG_RK_AVB_LIBAVB_USER
12342e40c2c1SJason Zhu 		sha256_context ctx;
12352e40c2c1SJason Zhu 		uint8_t digest[SHA256_SUM_LEN];
12362e40c2c1SJason Zhu 
12374e1bbe84SJason Zhu 		if (download_bytes != VBOOT_KEY_HASH_SIZE) {
12382e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILinvalid vboot key length");
12394e1bbe84SJason Zhu 			printf("The vboot key size error!\n");
12402e40c2c1SJason Zhu 			return;
12414e1bbe84SJason Zhu 		}
12424e1bbe84SJason Zhu 
12432e40c2c1SJason Zhu 		sha256_starts(&ctx);
12442e40c2c1SJason Zhu 		sha256_update(&ctx, (const uint8_t *)CONFIG_FASTBOOT_BUF_ADDR,
12452e40c2c1SJason Zhu 			      VBOOT_KEY_SIZE);
12462e40c2c1SJason Zhu 		sha256_finish(&ctx, digest);
12472e40c2c1SJason Zhu 
12482e40c2c1SJason Zhu 		if (rk_avb_write_vbootkey_hash((uint8_t *)digest,
12492e40c2c1SJason Zhu 					       SHA256_SUM_LEN)) {
12502e40c2c1SJason Zhu 			fastboot_tx_write_str("FAILvbootkey hash write failure");
12514e1bbe84SJason Zhu 			return;
12524e1bbe84SJason Zhu 		}
12534e1bbe84SJason Zhu 		fastboot_tx_write_str("OKAY");
12544e1bbe84SJason Zhu #else
12554e1bbe84SJason Zhu 		fastboot_tx_write_str("FAILnot implemented");
12564e1bbe84SJason Zhu #endif
1257367cce4dSXu Hongfei 	} else {
1258de195620SMichael Scott 		fastboot_tx_write_str("FAILunknown oem command");
1259de195620SMichael Scott 	}
1260de195620SMichael Scott }
1261de195620SMichael Scott 
126289792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH
126389792381SDileep Katta static void cb_erase(struct usb_ep *ep, struct usb_request *req)
126489792381SDileep Katta {
126589792381SDileep Katta 	char *cmd = req->buf;
12663c8f98f5SMaxime Ripard 	char response[FASTBOOT_RESPONSE_LEN];
126789792381SDileep Katta 
126889792381SDileep Katta 	strsep(&cmd, ":");
126989792381SDileep Katta 	if (!cmd) {
127090aa625cSMasahiro Yamada 		pr_err("missing partition name");
127189792381SDileep Katta 		fastboot_tx_write_str("FAILmissing partition name");
127289792381SDileep Katta 		return;
127389792381SDileep Katta 	}
127489792381SDileep Katta 
12758b464fa9SJocelyn Bohr 	fastboot_fail("no flash device defined", response);
127689792381SDileep Katta #ifdef CONFIG_FASTBOOT_FLASH_MMC_DEV
12778b464fa9SJocelyn Bohr 	fb_mmc_erase(cmd, response);
127889792381SDileep Katta #endif
1279bf8940d3SMaxime Ripard #ifdef CONFIG_FASTBOOT_FLASH_NAND_DEV
12808b464fa9SJocelyn Bohr 	fb_nand_erase(cmd, response);
1281bf8940d3SMaxime Ripard #endif
128289792381SDileep Katta 	fastboot_tx_write_str(response);
128389792381SDileep Katta }
128489792381SDileep Katta #endif
128589792381SDileep Katta 
12863aab70afSSebastian Siewior struct cmd_dispatch_info {
12873aab70afSSebastian Siewior 	char *cmd;
12883aab70afSSebastian Siewior 	void (*cb)(struct usb_ep *ep, struct usb_request *req);
12893aab70afSSebastian Siewior };
12903aab70afSSebastian Siewior 
12913aab70afSSebastian Siewior static const struct cmd_dispatch_info cmd_dispatch_info[] = {
12923aab70afSSebastian Siewior 	{
12933aab70afSSebastian Siewior 		.cmd = "reboot",
12943aab70afSSebastian Siewior 		.cb = cb_reboot,
12953aab70afSSebastian Siewior 	}, {
12963aab70afSSebastian Siewior 		.cmd = "getvar:",
12973aab70afSSebastian Siewior 		.cb = cb_getvar,
12983aab70afSSebastian Siewior 	}, {
12993aab70afSSebastian Siewior 		.cmd = "download:",
13003aab70afSSebastian Siewior 		.cb = cb_download,
13013aab70afSSebastian Siewior 	}, {
1302367cce4dSXu Hongfei 		.cmd = "upload",
1303367cce4dSXu Hongfei 		.cb = cb_upload,
1304367cce4dSXu Hongfei 	}, {
13053aab70afSSebastian Siewior 		.cmd = "boot",
13063aab70afSSebastian Siewior 		.cb = cb_boot,
1307267abc62SRob Herring 	}, {
1308267abc62SRob Herring 		.cmd = "continue",
1309267abc62SRob Herring 		.cb = cb_continue,
1310367cce4dSXu Hongfei 	}, {
1311367cce4dSXu Hongfei 		.cmd = "set_active",
1312367cce4dSXu Hongfei 		.cb = cb_set_active,
13133aab70afSSebastian Siewior 	},
1314d1b5ed07SSteve Rae #ifdef CONFIG_FASTBOOT_FLASH
1315d1b5ed07SSteve Rae 	{
1316367cce4dSXu Hongfei 		.cmd = "flashing",
1317367cce4dSXu Hongfei 		.cb = cb_flashing,
1318367cce4dSXu Hongfei 	},
1319367cce4dSXu Hongfei 	{
1320d1b5ed07SSteve Rae 		.cmd = "flash",
1321d1b5ed07SSteve Rae 		.cb = cb_flash,
132289792381SDileep Katta 	}, {
132389792381SDileep Katta 		.cmd = "erase",
132489792381SDileep Katta 		.cb = cb_erase,
1325d1b5ed07SSteve Rae 	},
1326d1b5ed07SSteve Rae #endif
1327de195620SMichael Scott 	{
1328de195620SMichael Scott 		.cmd = "oem",
1329de195620SMichael Scott 		.cb = cb_oem,
1330de195620SMichael Scott 	},
13313aab70afSSebastian Siewior };
13323aab70afSSebastian Siewior 
13333aab70afSSebastian Siewior static void rx_handler_command(struct usb_ep *ep, struct usb_request *req)
13343aab70afSSebastian Siewior {
13353aab70afSSebastian Siewior 	char *cmdbuf = req->buf;
13363aab70afSSebastian Siewior 	void (*func_cb)(struct usb_ep *ep, struct usb_request *req) = NULL;
13373aab70afSSebastian Siewior 	int i;
13383aab70afSSebastian Siewior 
133994b385faSPaul Kocialkowski 	if (req->status != 0 || req->length == 0)
134094b385faSPaul Kocialkowski 		return;
134194b385faSPaul Kocialkowski 
13423aab70afSSebastian Siewior 	for (i = 0; i < ARRAY_SIZE(cmd_dispatch_info); i++) {
13433aab70afSSebastian Siewior 		if (!strcmp_l1(cmd_dispatch_info[i].cmd, cmdbuf)) {
13443aab70afSSebastian Siewior 			func_cb = cmd_dispatch_info[i].cb;
13453aab70afSSebastian Siewior 			break;
13463aab70afSSebastian Siewior 		}
13473aab70afSSebastian Siewior 	}
13483aab70afSSebastian Siewior 
1349593cbd93SSteve Rae 	if (!func_cb) {
135090aa625cSMasahiro Yamada 		pr_err("unknown command: %.*s", req->actual, cmdbuf);
13513aab70afSSebastian Siewior 		fastboot_tx_write_str("FAILunknown command");
1352593cbd93SSteve Rae 	} else {
1353e2140588SEric Nelson 		if (req->actual < req->length) {
1354e2140588SEric Nelson 			u8 *buf = (u8 *)req->buf;
1355e2140588SEric Nelson 			buf[req->actual] = 0;
13563aab70afSSebastian Siewior 			func_cb(ep, req);
1357e2140588SEric Nelson 		} else {
135890aa625cSMasahiro Yamada 			pr_err("buffer overflow");
1359e2140588SEric Nelson 			fastboot_tx_write_str("FAILbuffer overflow");
1360e2140588SEric Nelson 		}
1361593cbd93SSteve Rae 	}
13623aab70afSSebastian Siewior 
13633aab70afSSebastian Siewior 	*cmdbuf = '\0';
13643aab70afSSebastian Siewior 	req->actual = 0;
13653aab70afSSebastian Siewior 	usb_ep_queue(ep, req, 0);
13663aab70afSSebastian Siewior }
1367