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