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