xref: /rk3399_ARM-atf/plat/st/common/usb_dfu.c (revision efbd65fa7b5cf70f20d6b18152741ccdf8a65bb6)
1*efbd65faSPatrick Delaunay /*
2*efbd65faSPatrick Delaunay  * Copyright (c) 2021, STMicroelectronics - All Rights Reserved
3*efbd65faSPatrick Delaunay  *
4*efbd65faSPatrick Delaunay  * SPDX-License-Identifier: BSD-3-Clause
5*efbd65faSPatrick Delaunay  */
6*efbd65faSPatrick Delaunay 
7*efbd65faSPatrick Delaunay #include <errno.h>
8*efbd65faSPatrick Delaunay #include <string.h>
9*efbd65faSPatrick Delaunay 
10*efbd65faSPatrick Delaunay #include <common/debug.h>
11*efbd65faSPatrick Delaunay 
12*efbd65faSPatrick Delaunay #include <platform_def.h>
13*efbd65faSPatrick Delaunay #include <usb_dfu.h>
14*efbd65faSPatrick Delaunay 
15*efbd65faSPatrick Delaunay /* Device states as defined in DFU spec */
16*efbd65faSPatrick Delaunay #define STATE_APP_IDLE			0
17*efbd65faSPatrick Delaunay #define STATE_APP_DETACH		1
18*efbd65faSPatrick Delaunay #define STATE_DFU_IDLE			2
19*efbd65faSPatrick Delaunay #define STATE_DFU_DNLOAD_SYNC		3
20*efbd65faSPatrick Delaunay #define STATE_DFU_DNLOAD_BUSY		4
21*efbd65faSPatrick Delaunay #define STATE_DFU_DNLOAD_IDLE		5
22*efbd65faSPatrick Delaunay #define STATE_DFU_MANIFEST_SYNC		6
23*efbd65faSPatrick Delaunay #define STATE_DFU_MANIFEST		7
24*efbd65faSPatrick Delaunay #define STATE_DFU_MANIFEST_WAIT_RESET	8
25*efbd65faSPatrick Delaunay #define STATE_DFU_UPLOAD_IDLE		9
26*efbd65faSPatrick Delaunay #define STATE_DFU_ERROR			10
27*efbd65faSPatrick Delaunay 
28*efbd65faSPatrick Delaunay /* DFU errors */
29*efbd65faSPatrick Delaunay #define DFU_ERROR_NONE			0x00
30*efbd65faSPatrick Delaunay #define DFU_ERROR_TARGET		0x01
31*efbd65faSPatrick Delaunay #define DFU_ERROR_FILE			0x02
32*efbd65faSPatrick Delaunay #define DFU_ERROR_WRITE			0x03
33*efbd65faSPatrick Delaunay #define DFU_ERROR_ERASE			0x04
34*efbd65faSPatrick Delaunay #define DFU_ERROR_CHECK_ERASED		0x05
35*efbd65faSPatrick Delaunay #define DFU_ERROR_PROG			0x06
36*efbd65faSPatrick Delaunay #define DFU_ERROR_VERIFY		0x07
37*efbd65faSPatrick Delaunay #define DFU_ERROR_ADDRESS		0x08
38*efbd65faSPatrick Delaunay #define DFU_ERROR_NOTDONE		0x09
39*efbd65faSPatrick Delaunay #define DFU_ERROR_FIRMWARE		0x0A
40*efbd65faSPatrick Delaunay #define DFU_ERROR_VENDOR		0x0B
41*efbd65faSPatrick Delaunay #define DFU_ERROR_USB			0x0C
42*efbd65faSPatrick Delaunay #define DFU_ERROR_POR			0x0D
43*efbd65faSPatrick Delaunay #define DFU_ERROR_UNKNOWN		0x0E
44*efbd65faSPatrick Delaunay #define DFU_ERROR_STALLEDPKT		0x0F
45*efbd65faSPatrick Delaunay 
46*efbd65faSPatrick Delaunay /* DFU request */
47*efbd65faSPatrick Delaunay #define DFU_DETACH			0
48*efbd65faSPatrick Delaunay #define DFU_DNLOAD			1
49*efbd65faSPatrick Delaunay #define DFU_UPLOAD			2
50*efbd65faSPatrick Delaunay #define DFU_GETSTATUS			3
51*efbd65faSPatrick Delaunay #define DFU_CLRSTATUS			4
52*efbd65faSPatrick Delaunay #define DFU_GETSTATE			5
53*efbd65faSPatrick Delaunay #define DFU_ABORT			6
54*efbd65faSPatrick Delaunay 
55*efbd65faSPatrick Delaunay static bool usb_dfu_detach_req;
56*efbd65faSPatrick Delaunay 
57*efbd65faSPatrick Delaunay /*
58*efbd65faSPatrick Delaunay  * usb_dfu_init
59*efbd65faSPatrick Delaunay  *         Initialize the DFU interface
60*efbd65faSPatrick Delaunay  * pdev: device instance
61*efbd65faSPatrick Delaunay  * cfgidx: Configuration index
62*efbd65faSPatrick Delaunay  * return: status
63*efbd65faSPatrick Delaunay  */
64*efbd65faSPatrick Delaunay static uint8_t usb_dfu_init(struct usb_handle *pdev, uint8_t cfgidx)
65*efbd65faSPatrick Delaunay {
66*efbd65faSPatrick Delaunay 	(void)pdev;
67*efbd65faSPatrick Delaunay 	(void)cfgidx;
68*efbd65faSPatrick Delaunay 
69*efbd65faSPatrick Delaunay 	/* Nothing to do in this stage */
70*efbd65faSPatrick Delaunay 	return USBD_OK;
71*efbd65faSPatrick Delaunay }
72*efbd65faSPatrick Delaunay 
73*efbd65faSPatrick Delaunay /*
74*efbd65faSPatrick Delaunay  * usb_dfu_de_init
75*efbd65faSPatrick Delaunay  *         De-Initialize the DFU layer
76*efbd65faSPatrick Delaunay  * pdev: device instance
77*efbd65faSPatrick Delaunay  * cfgidx: Configuration index
78*efbd65faSPatrick Delaunay  * return: status
79*efbd65faSPatrick Delaunay  */
80*efbd65faSPatrick Delaunay static uint8_t usb_dfu_de_init(struct usb_handle *pdev, uint8_t cfgidx)
81*efbd65faSPatrick Delaunay {
82*efbd65faSPatrick Delaunay 	(void)pdev;
83*efbd65faSPatrick Delaunay 	(void)cfgidx;
84*efbd65faSPatrick Delaunay 
85*efbd65faSPatrick Delaunay 	/* Nothing to do in this stage */
86*efbd65faSPatrick Delaunay 	return USBD_OK;
87*efbd65faSPatrick Delaunay }
88*efbd65faSPatrick Delaunay 
89*efbd65faSPatrick Delaunay /*
90*efbd65faSPatrick Delaunay  * usb_dfu_data_in
91*efbd65faSPatrick Delaunay  *         handle data IN Stage
92*efbd65faSPatrick Delaunay  * pdev: device instance
93*efbd65faSPatrick Delaunay  * epnum: endpoint index
94*efbd65faSPatrick Delaunay  * return: status
95*efbd65faSPatrick Delaunay  */
96*efbd65faSPatrick Delaunay static uint8_t usb_dfu_data_in(struct usb_handle *pdev, uint8_t epnum)
97*efbd65faSPatrick Delaunay {
98*efbd65faSPatrick Delaunay 	(void)pdev;
99*efbd65faSPatrick Delaunay 	(void)epnum;
100*efbd65faSPatrick Delaunay 
101*efbd65faSPatrick Delaunay 	return USBD_OK;
102*efbd65faSPatrick Delaunay }
103*efbd65faSPatrick Delaunay 
104*efbd65faSPatrick Delaunay /*
105*efbd65faSPatrick Delaunay  * usb_dfu_ep0_rx_ready
106*efbd65faSPatrick Delaunay  *         handle EP0 Rx Ready event
107*efbd65faSPatrick Delaunay  * pdev: device
108*efbd65faSPatrick Delaunay  * return: status
109*efbd65faSPatrick Delaunay  */
110*efbd65faSPatrick Delaunay static uint8_t usb_dfu_ep0_rx_ready(struct usb_handle *pdev)
111*efbd65faSPatrick Delaunay {
112*efbd65faSPatrick Delaunay 	(void)pdev;
113*efbd65faSPatrick Delaunay 
114*efbd65faSPatrick Delaunay 	return USBD_OK;
115*efbd65faSPatrick Delaunay }
116*efbd65faSPatrick Delaunay 
117*efbd65faSPatrick Delaunay /*
118*efbd65faSPatrick Delaunay  * usb_dfu_ep0_tx_ready
119*efbd65faSPatrick Delaunay  *         handle EP0 TRx Ready event
120*efbd65faSPatrick Delaunay  * pdev: device instance
121*efbd65faSPatrick Delaunay  * return: status
122*efbd65faSPatrick Delaunay  */
123*efbd65faSPatrick Delaunay static uint8_t usb_dfu_ep0_tx_ready(struct usb_handle *pdev)
124*efbd65faSPatrick Delaunay {
125*efbd65faSPatrick Delaunay 	(void)pdev;
126*efbd65faSPatrick Delaunay 
127*efbd65faSPatrick Delaunay 	return USBD_OK;
128*efbd65faSPatrick Delaunay }
129*efbd65faSPatrick Delaunay 
130*efbd65faSPatrick Delaunay /*
131*efbd65faSPatrick Delaunay  * usb_dfu_sof
132*efbd65faSPatrick Delaunay  *         handle SOF event
133*efbd65faSPatrick Delaunay  * pdev: device instance
134*efbd65faSPatrick Delaunay  * return: status
135*efbd65faSPatrick Delaunay  */
136*efbd65faSPatrick Delaunay static uint8_t usb_dfu_sof(struct usb_handle *pdev)
137*efbd65faSPatrick Delaunay {
138*efbd65faSPatrick Delaunay 	(void)pdev;
139*efbd65faSPatrick Delaunay 
140*efbd65faSPatrick Delaunay 	return USBD_OK;
141*efbd65faSPatrick Delaunay }
142*efbd65faSPatrick Delaunay 
143*efbd65faSPatrick Delaunay /*
144*efbd65faSPatrick Delaunay  * usb_dfu_iso_in_incomplete
145*efbd65faSPatrick Delaunay  *         handle data ISO IN Incomplete event
146*efbd65faSPatrick Delaunay  * pdev: device instance
147*efbd65faSPatrick Delaunay  * epnum: endpoint index
148*efbd65faSPatrick Delaunay  * return: status
149*efbd65faSPatrick Delaunay  */
150*efbd65faSPatrick Delaunay static uint8_t usb_dfu_iso_in_incomplete(struct usb_handle *pdev, uint8_t epnum)
151*efbd65faSPatrick Delaunay {
152*efbd65faSPatrick Delaunay 	(void)pdev;
153*efbd65faSPatrick Delaunay 	(void)epnum;
154*efbd65faSPatrick Delaunay 
155*efbd65faSPatrick Delaunay 	return USBD_OK;
156*efbd65faSPatrick Delaunay }
157*efbd65faSPatrick Delaunay 
158*efbd65faSPatrick Delaunay /*
159*efbd65faSPatrick Delaunay  * usb_dfu_iso_out_incomplete
160*efbd65faSPatrick Delaunay  *         handle data ISO OUT Incomplete event
161*efbd65faSPatrick Delaunay  * pdev: device instance
162*efbd65faSPatrick Delaunay  * epnum: endpoint index
163*efbd65faSPatrick Delaunay  * return: status
164*efbd65faSPatrick Delaunay  */
165*efbd65faSPatrick Delaunay static uint8_t usb_dfu_iso_out_incomplete(struct usb_handle *pdev,
166*efbd65faSPatrick Delaunay 					  uint8_t epnum)
167*efbd65faSPatrick Delaunay {
168*efbd65faSPatrick Delaunay 	(void)pdev;
169*efbd65faSPatrick Delaunay 	(void)epnum;
170*efbd65faSPatrick Delaunay 
171*efbd65faSPatrick Delaunay 	return USBD_OK;
172*efbd65faSPatrick Delaunay }
173*efbd65faSPatrick Delaunay 
174*efbd65faSPatrick Delaunay /*
175*efbd65faSPatrick Delaunay  * usb_dfu_data_out
176*efbd65faSPatrick Delaunay  *         handle data OUT Stage
177*efbd65faSPatrick Delaunay  * pdev: device instance
178*efbd65faSPatrick Delaunay  * epnum: endpoint index
179*efbd65faSPatrick Delaunay  * return: status
180*efbd65faSPatrick Delaunay  */
181*efbd65faSPatrick Delaunay static uint8_t usb_dfu_data_out(struct usb_handle *pdev, uint8_t epnum)
182*efbd65faSPatrick Delaunay {
183*efbd65faSPatrick Delaunay 	(void)pdev;
184*efbd65faSPatrick Delaunay 	(void)epnum;
185*efbd65faSPatrick Delaunay 
186*efbd65faSPatrick Delaunay 	return USBD_OK;
187*efbd65faSPatrick Delaunay }
188*efbd65faSPatrick Delaunay 
189*efbd65faSPatrick Delaunay /*
190*efbd65faSPatrick Delaunay  * usb_dfu_detach
191*efbd65faSPatrick Delaunay  *         Handles the DFU DETACH request.
192*efbd65faSPatrick Delaunay  * pdev: device instance
193*efbd65faSPatrick Delaunay  * req: pointer to the request structure.
194*efbd65faSPatrick Delaunay  */
195*efbd65faSPatrick Delaunay static void usb_dfu_detach(struct usb_handle *pdev, struct usb_setup_req *req)
196*efbd65faSPatrick Delaunay {
197*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
198*efbd65faSPatrick Delaunay 
199*efbd65faSPatrick Delaunay 	INFO("Receive DFU Detach\n");
200*efbd65faSPatrick Delaunay 
201*efbd65faSPatrick Delaunay 	if ((hdfu->dev_state == STATE_DFU_IDLE) ||
202*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
203*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
204*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
205*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
206*efbd65faSPatrick Delaunay 		/* Update the state machine */
207*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_IDLE;
208*efbd65faSPatrick Delaunay 		hdfu->dev_status = DFU_ERROR_NONE;
209*efbd65faSPatrick Delaunay 	}
210*efbd65faSPatrick Delaunay 
211*efbd65faSPatrick Delaunay 	usb_dfu_detach_req = true;
212*efbd65faSPatrick Delaunay }
213*efbd65faSPatrick Delaunay 
214*efbd65faSPatrick Delaunay /*
215*efbd65faSPatrick Delaunay  * usb_dfu_download
216*efbd65faSPatrick Delaunay  *         Handles the DFU DNLOAD request.
217*efbd65faSPatrick Delaunay  * pdev: device instance
218*efbd65faSPatrick Delaunay  * req: pointer to the request structure
219*efbd65faSPatrick Delaunay  */
220*efbd65faSPatrick Delaunay static void usb_dfu_download(struct usb_handle *pdev, struct usb_setup_req *req)
221*efbd65faSPatrick Delaunay {
222*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
223*efbd65faSPatrick Delaunay 	uintptr_t data_ptr;
224*efbd65faSPatrick Delaunay 	uint32_t length;
225*efbd65faSPatrick Delaunay 	int ret;
226*efbd65faSPatrick Delaunay 
227*efbd65faSPatrick Delaunay 	/* Data setup request */
228*efbd65faSPatrick Delaunay 	if (req->length > 0) {
229*efbd65faSPatrick Delaunay 		/* Unsupported state */
230*efbd65faSPatrick Delaunay 		if ((hdfu->dev_state != STATE_DFU_IDLE) &&
231*efbd65faSPatrick Delaunay 		    (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE)) {
232*efbd65faSPatrick Delaunay 			/* Call the error management function (command will be nacked) */
233*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
234*efbd65faSPatrick Delaunay 			return;
235*efbd65faSPatrick Delaunay 		}
236*efbd65faSPatrick Delaunay 
237*efbd65faSPatrick Delaunay 		/* Get the data address */
238*efbd65faSPatrick Delaunay 		length = req->length;
239*efbd65faSPatrick Delaunay 		ret = hdfu->callback->download(hdfu->alt_setting, &data_ptr,
240*efbd65faSPatrick Delaunay 					       &length, pdev->user_data);
241*efbd65faSPatrick Delaunay 		if (ret == 0U) {
242*efbd65faSPatrick Delaunay 			/* Update the state machine */
243*efbd65faSPatrick Delaunay 			hdfu->dev_state = STATE_DFU_DNLOAD_SYNC;
244*efbd65faSPatrick Delaunay 			/* Start the transfer */
245*efbd65faSPatrick Delaunay 			usb_core_receive_ep0(pdev, (uint8_t *)data_ptr, length);
246*efbd65faSPatrick Delaunay 		} else {
247*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
248*efbd65faSPatrick Delaunay 		}
249*efbd65faSPatrick Delaunay 	} else {
250*efbd65faSPatrick Delaunay 		/* End of DNLOAD operation*/
251*efbd65faSPatrick Delaunay 		if (hdfu->dev_state != STATE_DFU_DNLOAD_IDLE) {
252*efbd65faSPatrick Delaunay 			/* Call the error management function (command will be nacked) */
253*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
254*efbd65faSPatrick Delaunay 			return;
255*efbd65faSPatrick Delaunay 		}
256*efbd65faSPatrick Delaunay 		/* End of DNLOAD operation*/
257*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
258*efbd65faSPatrick Delaunay 		ret = hdfu->callback->manifestation(hdfu->alt_setting, pdev->user_data);
259*efbd65faSPatrick Delaunay 		if (ret == 0U) {
260*efbd65faSPatrick Delaunay 			hdfu->dev_state = STATE_DFU_MANIFEST_SYNC;
261*efbd65faSPatrick Delaunay 		} else {
262*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
263*efbd65faSPatrick Delaunay 		}
264*efbd65faSPatrick Delaunay 	}
265*efbd65faSPatrick Delaunay }
266*efbd65faSPatrick Delaunay 
267*efbd65faSPatrick Delaunay /*
268*efbd65faSPatrick Delaunay  * usb_dfu_upload
269*efbd65faSPatrick Delaunay  *         Handles the DFU UPLOAD request.
270*efbd65faSPatrick Delaunay  * pdev: instance
271*efbd65faSPatrick Delaunay  * req: pointer to the request structure
272*efbd65faSPatrick Delaunay  */
273*efbd65faSPatrick Delaunay static void usb_dfu_upload(struct usb_handle *pdev, struct usb_setup_req *req)
274*efbd65faSPatrick Delaunay {
275*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
276*efbd65faSPatrick Delaunay 	uintptr_t data_ptr;
277*efbd65faSPatrick Delaunay 	uint32_t length;
278*efbd65faSPatrick Delaunay 	int ret;
279*efbd65faSPatrick Delaunay 
280*efbd65faSPatrick Delaunay 	/* Data setup request */
281*efbd65faSPatrick Delaunay 	if (req->length == 0) {
282*efbd65faSPatrick Delaunay 		/* No Data setup request */
283*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_IDLE;
284*efbd65faSPatrick Delaunay 		return;
285*efbd65faSPatrick Delaunay 	}
286*efbd65faSPatrick Delaunay 
287*efbd65faSPatrick Delaunay 	/* Unsupported state */
288*efbd65faSPatrick Delaunay 	if ((hdfu->dev_state != STATE_DFU_IDLE) && (hdfu->dev_state != STATE_DFU_UPLOAD_IDLE)) {
289*efbd65faSPatrick Delaunay 		ERROR("UPLOAD : Unsupported State\n");
290*efbd65faSPatrick Delaunay 		/* Call the error management function (command will be nacked) */
291*efbd65faSPatrick Delaunay 		usb_core_ctl_error(pdev);
292*efbd65faSPatrick Delaunay 		return;
293*efbd65faSPatrick Delaunay 	}
294*efbd65faSPatrick Delaunay 
295*efbd65faSPatrick Delaunay 	/* Update the data address */
296*efbd65faSPatrick Delaunay 	length = req->length;
297*efbd65faSPatrick Delaunay 	ret = hdfu->callback->upload(hdfu->alt_setting, &data_ptr, &length, pdev->user_data);
298*efbd65faSPatrick Delaunay 	if (ret == 0U) {
299*efbd65faSPatrick Delaunay 		/* Short frame */
300*efbd65faSPatrick Delaunay 		hdfu->dev_state = (req->length > length) ? STATE_DFU_IDLE : STATE_DFU_UPLOAD_IDLE;
301*efbd65faSPatrick Delaunay 
302*efbd65faSPatrick Delaunay 		/* Start the transfer */
303*efbd65faSPatrick Delaunay 		usb_core_transmit_ep0(pdev, (uint8_t *)data_ptr, length);
304*efbd65faSPatrick Delaunay 	} else {
305*efbd65faSPatrick Delaunay 		ERROR("UPLOAD : bad block %i on alt %i\n", req->value, req->index);
306*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_ERROR;
307*efbd65faSPatrick Delaunay 		hdfu->dev_status = DFU_ERROR_STALLEDPKT;
308*efbd65faSPatrick Delaunay 
309*efbd65faSPatrick Delaunay 		/* Call the error management function (command will be nacked) */
310*efbd65faSPatrick Delaunay 		usb_core_ctl_error(pdev);
311*efbd65faSPatrick Delaunay 	}
312*efbd65faSPatrick Delaunay }
313*efbd65faSPatrick Delaunay 
314*efbd65faSPatrick Delaunay /*
315*efbd65faSPatrick Delaunay  * usb_dfu_get_status
316*efbd65faSPatrick Delaunay  *         Handles the DFU GETSTATUS request.
317*efbd65faSPatrick Delaunay  * pdev: instance
318*efbd65faSPatrick Delaunay  */
319*efbd65faSPatrick Delaunay static void usb_dfu_get_status(struct usb_handle *pdev)
320*efbd65faSPatrick Delaunay {
321*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
322*efbd65faSPatrick Delaunay 
323*efbd65faSPatrick Delaunay 	hdfu->status[0] = hdfu->dev_status;	/* bStatus */
324*efbd65faSPatrick Delaunay 	hdfu->status[1] = 0;			/* bwPollTimeout[3] */
325*efbd65faSPatrick Delaunay 	hdfu->status[2] = 0;
326*efbd65faSPatrick Delaunay 	hdfu->status[3] = 0;
327*efbd65faSPatrick Delaunay 	hdfu->status[4] = hdfu->dev_state;	/* bState */
328*efbd65faSPatrick Delaunay 	hdfu->status[5] = 0;			/* iString */
329*efbd65faSPatrick Delaunay 
330*efbd65faSPatrick Delaunay 	/* next step */
331*efbd65faSPatrick Delaunay 	switch (hdfu->dev_state) {
332*efbd65faSPatrick Delaunay 	case STATE_DFU_DNLOAD_SYNC:
333*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_DNLOAD_IDLE;
334*efbd65faSPatrick Delaunay 		break;
335*efbd65faSPatrick Delaunay 	case STATE_DFU_MANIFEST_SYNC:
336*efbd65faSPatrick Delaunay 		/* the device is 'ManifestationTolerant' */
337*efbd65faSPatrick Delaunay 		hdfu->status[4] = STATE_DFU_MANIFEST;
338*efbd65faSPatrick Delaunay 		hdfu->status[1] = 1U; /* bwPollTimeout = 1ms */
339*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_IDLE;
340*efbd65faSPatrick Delaunay 		break;
341*efbd65faSPatrick Delaunay 
342*efbd65faSPatrick Delaunay 	default:
343*efbd65faSPatrick Delaunay 		break;
344*efbd65faSPatrick Delaunay 	}
345*efbd65faSPatrick Delaunay 
346*efbd65faSPatrick Delaunay 	/* Start the transfer */
347*efbd65faSPatrick Delaunay 	usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->status[0], sizeof(hdfu->status));
348*efbd65faSPatrick Delaunay }
349*efbd65faSPatrick Delaunay 
350*efbd65faSPatrick Delaunay /*
351*efbd65faSPatrick Delaunay  * usb_dfu_clear_status
352*efbd65faSPatrick Delaunay  *         Handles the DFU CLRSTATUS request.
353*efbd65faSPatrick Delaunay  * pdev: device instance
354*efbd65faSPatrick Delaunay  */
355*efbd65faSPatrick Delaunay static void usb_dfu_clear_status(struct usb_handle *pdev)
356*efbd65faSPatrick Delaunay {
357*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
358*efbd65faSPatrick Delaunay 
359*efbd65faSPatrick Delaunay 	if (hdfu->dev_state == STATE_DFU_ERROR) {
360*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_IDLE;
361*efbd65faSPatrick Delaunay 		hdfu->dev_status = DFU_ERROR_NONE;
362*efbd65faSPatrick Delaunay 	} else {
363*efbd65faSPatrick Delaunay 		/* State Error */
364*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_ERROR;
365*efbd65faSPatrick Delaunay 		hdfu->dev_status = DFU_ERROR_UNKNOWN;
366*efbd65faSPatrick Delaunay 	}
367*efbd65faSPatrick Delaunay }
368*efbd65faSPatrick Delaunay 
369*efbd65faSPatrick Delaunay /*
370*efbd65faSPatrick Delaunay  * usb_dfu_get_state
371*efbd65faSPatrick Delaunay  *         Handles the DFU GETSTATE request.
372*efbd65faSPatrick Delaunay  * pdev: device instance
373*efbd65faSPatrick Delaunay  */
374*efbd65faSPatrick Delaunay static void usb_dfu_get_state(struct usb_handle *pdev)
375*efbd65faSPatrick Delaunay {
376*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
377*efbd65faSPatrick Delaunay 
378*efbd65faSPatrick Delaunay 	/* Return the current state of the DFU interface */
379*efbd65faSPatrick Delaunay 	usb_core_transmit_ep0(pdev, &hdfu->dev_state, 1);
380*efbd65faSPatrick Delaunay }
381*efbd65faSPatrick Delaunay 
382*efbd65faSPatrick Delaunay /*
383*efbd65faSPatrick Delaunay  * usb_dfu_abort
384*efbd65faSPatrick Delaunay  *         Handles the DFU ABORT request.
385*efbd65faSPatrick Delaunay  * pdev: device instance
386*efbd65faSPatrick Delaunay  */
387*efbd65faSPatrick Delaunay static void usb_dfu_abort(struct usb_handle *pdev)
388*efbd65faSPatrick Delaunay {
389*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
390*efbd65faSPatrick Delaunay 
391*efbd65faSPatrick Delaunay 	if ((hdfu->dev_state == STATE_DFU_IDLE) ||
392*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_DNLOAD_SYNC) ||
393*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_DNLOAD_IDLE) ||
394*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_MANIFEST_SYNC) ||
395*efbd65faSPatrick Delaunay 	    (hdfu->dev_state == STATE_DFU_UPLOAD_IDLE)) {
396*efbd65faSPatrick Delaunay 		hdfu->dev_state = STATE_DFU_IDLE;
397*efbd65faSPatrick Delaunay 		hdfu->dev_status = DFU_ERROR_NONE;
398*efbd65faSPatrick Delaunay 	}
399*efbd65faSPatrick Delaunay }
400*efbd65faSPatrick Delaunay 
401*efbd65faSPatrick Delaunay /*
402*efbd65faSPatrick Delaunay  * usb_dfu_setup
403*efbd65faSPatrick Delaunay  *         Handle the DFU specific requests
404*efbd65faSPatrick Delaunay  * pdev: instance
405*efbd65faSPatrick Delaunay  * req: usb requests
406*efbd65faSPatrick Delaunay  * return: status
407*efbd65faSPatrick Delaunay  */
408*efbd65faSPatrick Delaunay static uint8_t usb_dfu_setup(struct usb_handle *pdev, struct usb_setup_req *req)
409*efbd65faSPatrick Delaunay {
410*efbd65faSPatrick Delaunay 	uint8_t *pbuf = NULL;
411*efbd65faSPatrick Delaunay 	uint16_t len = 0U;
412*efbd65faSPatrick Delaunay 	uint8_t ret = USBD_OK;
413*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
414*efbd65faSPatrick Delaunay 
415*efbd65faSPatrick Delaunay 	switch (req->bm_request & USB_REQ_TYPE_MASK) {
416*efbd65faSPatrick Delaunay 	case USB_REQ_TYPE_CLASS:
417*efbd65faSPatrick Delaunay 		switch (req->b_request) {
418*efbd65faSPatrick Delaunay 		case DFU_DNLOAD:
419*efbd65faSPatrick Delaunay 			usb_dfu_download(pdev, req);
420*efbd65faSPatrick Delaunay 			break;
421*efbd65faSPatrick Delaunay 
422*efbd65faSPatrick Delaunay 		case DFU_UPLOAD:
423*efbd65faSPatrick Delaunay 			usb_dfu_upload(pdev, req);
424*efbd65faSPatrick Delaunay 			break;
425*efbd65faSPatrick Delaunay 
426*efbd65faSPatrick Delaunay 		case DFU_GETSTATUS:
427*efbd65faSPatrick Delaunay 			usb_dfu_get_status(pdev);
428*efbd65faSPatrick Delaunay 			break;
429*efbd65faSPatrick Delaunay 
430*efbd65faSPatrick Delaunay 		case DFU_CLRSTATUS:
431*efbd65faSPatrick Delaunay 			usb_dfu_clear_status(pdev);
432*efbd65faSPatrick Delaunay 			break;
433*efbd65faSPatrick Delaunay 
434*efbd65faSPatrick Delaunay 		case DFU_GETSTATE:
435*efbd65faSPatrick Delaunay 			usb_dfu_get_state(pdev);
436*efbd65faSPatrick Delaunay 			break;
437*efbd65faSPatrick Delaunay 
438*efbd65faSPatrick Delaunay 		case DFU_ABORT:
439*efbd65faSPatrick Delaunay 			usb_dfu_abort(pdev);
440*efbd65faSPatrick Delaunay 			break;
441*efbd65faSPatrick Delaunay 
442*efbd65faSPatrick Delaunay 		case DFU_DETACH:
443*efbd65faSPatrick Delaunay 			usb_dfu_detach(pdev, req);
444*efbd65faSPatrick Delaunay 			break;
445*efbd65faSPatrick Delaunay 
446*efbd65faSPatrick Delaunay 		default:
447*efbd65faSPatrick Delaunay 			ERROR("unknown request %x on alternate %i\n",
448*efbd65faSPatrick Delaunay 			      req->b_request, hdfu->alt_setting);
449*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
450*efbd65faSPatrick Delaunay 			ret = USBD_FAIL;
451*efbd65faSPatrick Delaunay 			break;
452*efbd65faSPatrick Delaunay 		}
453*efbd65faSPatrick Delaunay 		break;
454*efbd65faSPatrick Delaunay 	case USB_REQ_TYPE_STANDARD:
455*efbd65faSPatrick Delaunay 		switch (req->b_request) {
456*efbd65faSPatrick Delaunay 		case USB_REQ_GET_DESCRIPTOR:
457*efbd65faSPatrick Delaunay 			if (HIBYTE(req->value) == DFU_DESCRIPTOR_TYPE) {
458*efbd65faSPatrick Delaunay 				pbuf = pdev->desc->get_config_desc(&len);
459*efbd65faSPatrick Delaunay 				/* DFU descriptor at the end of the USB */
460*efbd65faSPatrick Delaunay 				pbuf += len - 9U;
461*efbd65faSPatrick Delaunay 				len = 9U;
462*efbd65faSPatrick Delaunay 				len = MIN(len, req->length);
463*efbd65faSPatrick Delaunay 			}
464*efbd65faSPatrick Delaunay 
465*efbd65faSPatrick Delaunay 			/* Start the transfer */
466*efbd65faSPatrick Delaunay 			usb_core_transmit_ep0(pdev, pbuf, len);
467*efbd65faSPatrick Delaunay 
468*efbd65faSPatrick Delaunay 			break;
469*efbd65faSPatrick Delaunay 
470*efbd65faSPatrick Delaunay 		case USB_REQ_GET_INTERFACE:
471*efbd65faSPatrick Delaunay 			/* Start the transfer */
472*efbd65faSPatrick Delaunay 			usb_core_transmit_ep0(pdev, (uint8_t *)&hdfu->alt_setting, 1U);
473*efbd65faSPatrick Delaunay 			break;
474*efbd65faSPatrick Delaunay 
475*efbd65faSPatrick Delaunay 		case USB_REQ_SET_INTERFACE:
476*efbd65faSPatrick Delaunay 			hdfu->alt_setting = LOBYTE(req->value);
477*efbd65faSPatrick Delaunay 			break;
478*efbd65faSPatrick Delaunay 
479*efbd65faSPatrick Delaunay 		default:
480*efbd65faSPatrick Delaunay 			usb_core_ctl_error(pdev);
481*efbd65faSPatrick Delaunay 			ret = USBD_FAIL;
482*efbd65faSPatrick Delaunay 			break;
483*efbd65faSPatrick Delaunay 		}
484*efbd65faSPatrick Delaunay 	default:
485*efbd65faSPatrick Delaunay 		break;
486*efbd65faSPatrick Delaunay 	}
487*efbd65faSPatrick Delaunay 
488*efbd65faSPatrick Delaunay 	return ret;
489*efbd65faSPatrick Delaunay }
490*efbd65faSPatrick Delaunay 
491*efbd65faSPatrick Delaunay static const struct usb_class usb_dfu = {
492*efbd65faSPatrick Delaunay 	.init = usb_dfu_init,
493*efbd65faSPatrick Delaunay 	.de_init = usb_dfu_de_init,
494*efbd65faSPatrick Delaunay 	.setup = usb_dfu_setup,
495*efbd65faSPatrick Delaunay 	.ep0_tx_sent = usb_dfu_ep0_tx_ready,
496*efbd65faSPatrick Delaunay 	.ep0_rx_ready = usb_dfu_ep0_rx_ready,
497*efbd65faSPatrick Delaunay 	.data_in = usb_dfu_data_in,
498*efbd65faSPatrick Delaunay 	.data_out = usb_dfu_data_out,
499*efbd65faSPatrick Delaunay 	.sof = usb_dfu_sof,
500*efbd65faSPatrick Delaunay 	.iso_in_incomplete = usb_dfu_iso_in_incomplete,
501*efbd65faSPatrick Delaunay 	.iso_out_incomplete = usb_dfu_iso_out_incomplete,
502*efbd65faSPatrick Delaunay };
503*efbd65faSPatrick Delaunay 
504*efbd65faSPatrick Delaunay void usb_dfu_register(struct usb_handle *pdev, struct usb_dfu_handle *phandle)
505*efbd65faSPatrick Delaunay {
506*efbd65faSPatrick Delaunay 	pdev->class = (struct usb_class *)&usb_dfu;
507*efbd65faSPatrick Delaunay 	pdev->class_data = phandle;
508*efbd65faSPatrick Delaunay 
509*efbd65faSPatrick Delaunay 	phandle->dev_state = STATE_DFU_IDLE;
510*efbd65faSPatrick Delaunay 	phandle->dev_status = DFU_ERROR_NONE;
511*efbd65faSPatrick Delaunay }
512*efbd65faSPatrick Delaunay 
513*efbd65faSPatrick Delaunay int usb_dfu_loop(struct usb_handle *pdev, const struct usb_dfu_media *pmedia)
514*efbd65faSPatrick Delaunay {
515*efbd65faSPatrick Delaunay 	uint32_t it_count;
516*efbd65faSPatrick Delaunay 	enum usb_status ret;
517*efbd65faSPatrick Delaunay 	struct usb_dfu_handle *hdfu = (struct usb_dfu_handle *)pdev->class_data;
518*efbd65faSPatrick Delaunay 
519*efbd65faSPatrick Delaunay 	hdfu->callback = pmedia;
520*efbd65faSPatrick Delaunay 	usb_dfu_detach_req = false;
521*efbd65faSPatrick Delaunay 	/* Continue to handle USB core IT to assure complete data transmission */
522*efbd65faSPatrick Delaunay 	it_count = 100U;
523*efbd65faSPatrick Delaunay 
524*efbd65faSPatrick Delaunay 	/* DFU infinite loop until DETACH_REQ */
525*efbd65faSPatrick Delaunay 	while (it_count != 0U) {
526*efbd65faSPatrick Delaunay 		ret = usb_core_handle_it(pdev);
527*efbd65faSPatrick Delaunay 		if (ret != USBD_OK) {
528*efbd65faSPatrick Delaunay 			return -EIO;
529*efbd65faSPatrick Delaunay 		}
530*efbd65faSPatrick Delaunay 
531*efbd65faSPatrick Delaunay 		/* Detach request received */
532*efbd65faSPatrick Delaunay 		if (usb_dfu_detach_req) {
533*efbd65faSPatrick Delaunay 			it_count--;
534*efbd65faSPatrick Delaunay 		}
535*efbd65faSPatrick Delaunay 	}
536*efbd65faSPatrick Delaunay 
537*efbd65faSPatrick Delaunay 	return 0;
538*efbd65faSPatrick Delaunay }
539