1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * f_dfu.c -- Device Firmware Update USB function
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (C) 2012 Samsung Electronics
5*4882a593Smuzhiyun * authors: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
6*4882a593Smuzhiyun * Lukasz Majewski <l.majewski@samsung.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Based on OpenMoko u-boot: drivers/usb/usbdfu.c
9*4882a593Smuzhiyun * (C) 2007 by OpenMoko, Inc.
10*4882a593Smuzhiyun * Author: Harald Welte <laforge@openmoko.org>
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * based on existing SAM7DFU code from OpenPCD:
13*4882a593Smuzhiyun * (C) Copyright 2006 by Harald Welte <hwelte at hmw-consulting.de>
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
16*4882a593Smuzhiyun */
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #include <errno.h>
19*4882a593Smuzhiyun #include <common.h>
20*4882a593Smuzhiyun #include <malloc.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <linux/usb/ch9.h>
23*4882a593Smuzhiyun #include <linux/usb/gadget.h>
24*4882a593Smuzhiyun #include <linux/usb/composite.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun #include <dfu.h>
27*4882a593Smuzhiyun #include <g_dnl.h>
28*4882a593Smuzhiyun #include "f_dfu.h"
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun struct f_dfu {
31*4882a593Smuzhiyun struct usb_function usb_function;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun struct usb_descriptor_header **function;
34*4882a593Smuzhiyun struct usb_string *strings;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* when configured, we have one config */
37*4882a593Smuzhiyun u8 config;
38*4882a593Smuzhiyun u8 altsetting;
39*4882a593Smuzhiyun enum dfu_state dfu_state;
40*4882a593Smuzhiyun unsigned int dfu_status;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun /* Send/received block number is handy for data integrity check */
43*4882a593Smuzhiyun int blk_seq_num;
44*4882a593Smuzhiyun unsigned int poll_timeout;
45*4882a593Smuzhiyun };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun struct dfu_entity *dfu_defer_flush;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun typedef int (*dfu_state_fn) (struct f_dfu *,
50*4882a593Smuzhiyun const struct usb_ctrlrequest *,
51*4882a593Smuzhiyun struct usb_gadget *,
52*4882a593Smuzhiyun struct usb_request *);
53*4882a593Smuzhiyun
func_to_dfu(struct usb_function * f)54*4882a593Smuzhiyun static inline struct f_dfu *func_to_dfu(struct usb_function *f)
55*4882a593Smuzhiyun {
56*4882a593Smuzhiyun return container_of(f, struct f_dfu, usb_function);
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun static const struct dfu_function_descriptor dfu_func = {
60*4882a593Smuzhiyun .bLength = sizeof dfu_func,
61*4882a593Smuzhiyun .bDescriptorType = DFU_DT_FUNC,
62*4882a593Smuzhiyun .bmAttributes = DFU_BIT_WILL_DETACH |
63*4882a593Smuzhiyun DFU_BIT_MANIFESTATION_TOLERANT |
64*4882a593Smuzhiyun DFU_BIT_CAN_UPLOAD |
65*4882a593Smuzhiyun DFU_BIT_CAN_DNLOAD,
66*4882a593Smuzhiyun .wDetachTimeOut = 0,
67*4882a593Smuzhiyun .wTransferSize = DFU_USB_BUFSIZ,
68*4882a593Smuzhiyun .bcdDFUVersion = __constant_cpu_to_le16(0x0110),
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun static struct usb_interface_descriptor dfu_intf_runtime = {
72*4882a593Smuzhiyun .bLength = sizeof dfu_intf_runtime,
73*4882a593Smuzhiyun .bDescriptorType = USB_DT_INTERFACE,
74*4882a593Smuzhiyun .bNumEndpoints = 0,
75*4882a593Smuzhiyun .bInterfaceClass = USB_CLASS_APP_SPEC,
76*4882a593Smuzhiyun .bInterfaceSubClass = 1,
77*4882a593Smuzhiyun .bInterfaceProtocol = 1,
78*4882a593Smuzhiyun /* .iInterface = DYNAMIC */
79*4882a593Smuzhiyun };
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun static struct usb_descriptor_header *dfu_runtime_descs[] = {
82*4882a593Smuzhiyun (struct usb_descriptor_header *) &dfu_intf_runtime,
83*4882a593Smuzhiyun NULL,
84*4882a593Smuzhiyun };
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun static const char dfu_name[] = "Device Firmware Upgrade";
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun /*
89*4882a593Smuzhiyun * static strings, in UTF-8
90*4882a593Smuzhiyun *
91*4882a593Smuzhiyun * dfu_generic configuration
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun static struct usb_string strings_dfu_generic[] = {
94*4882a593Smuzhiyun [0].s = dfu_name,
95*4882a593Smuzhiyun { } /* end of list */
96*4882a593Smuzhiyun };
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static struct usb_gadget_strings stringtab_dfu_generic = {
99*4882a593Smuzhiyun .language = 0x0409, /* en-us */
100*4882a593Smuzhiyun .strings = strings_dfu_generic,
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun static struct usb_gadget_strings *dfu_generic_strings[] = {
104*4882a593Smuzhiyun &stringtab_dfu_generic,
105*4882a593Smuzhiyun NULL,
106*4882a593Smuzhiyun };
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun /*
109*4882a593Smuzhiyun * usb_function specific
110*4882a593Smuzhiyun */
111*4882a593Smuzhiyun static struct usb_gadget_strings stringtab_dfu = {
112*4882a593Smuzhiyun .language = 0x0409, /* en-us */
113*4882a593Smuzhiyun /*
114*4882a593Smuzhiyun * .strings
115*4882a593Smuzhiyun *
116*4882a593Smuzhiyun * assigned during initialization,
117*4882a593Smuzhiyun * depends on number of flash entities
118*4882a593Smuzhiyun *
119*4882a593Smuzhiyun */
120*4882a593Smuzhiyun };
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun static struct usb_gadget_strings *dfu_strings[] = {
123*4882a593Smuzhiyun &stringtab_dfu,
124*4882a593Smuzhiyun NULL,
125*4882a593Smuzhiyun };
126*4882a593Smuzhiyun
dfu_set_poll_timeout(struct dfu_status * dstat,unsigned int ms)127*4882a593Smuzhiyun static void dfu_set_poll_timeout(struct dfu_status *dstat, unsigned int ms)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun /*
130*4882a593Smuzhiyun * The bwPollTimeout DFU_GETSTATUS request payload provides information
131*4882a593Smuzhiyun * about minimum time, in milliseconds, that the host should wait before
132*4882a593Smuzhiyun * sending a subsequent DFU_GETSTATUS request
133*4882a593Smuzhiyun *
134*4882a593Smuzhiyun * This permits the device to vary the delay depending on its need to
135*4882a593Smuzhiyun * erase or program the memory
136*4882a593Smuzhiyun *
137*4882a593Smuzhiyun */
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun unsigned char *p = (unsigned char *)&ms;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun if (!ms || (ms & ~DFU_POLL_TIMEOUT_MASK)) {
142*4882a593Smuzhiyun dstat->bwPollTimeout[0] = 0;
143*4882a593Smuzhiyun dstat->bwPollTimeout[1] = 0;
144*4882a593Smuzhiyun dstat->bwPollTimeout[2] = 0;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun return;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun dstat->bwPollTimeout[0] = *p++;
150*4882a593Smuzhiyun dstat->bwPollTimeout[1] = *p++;
151*4882a593Smuzhiyun dstat->bwPollTimeout[2] = *p;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun /*-------------------------------------------------------------------------*/
155*4882a593Smuzhiyun
dnload_request_complete(struct usb_ep * ep,struct usb_request * req)156*4882a593Smuzhiyun static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
159*4882a593Smuzhiyun int ret;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun ret = dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf,
162*4882a593Smuzhiyun req->actual, f_dfu->blk_seq_num);
163*4882a593Smuzhiyun if (ret) {
164*4882a593Smuzhiyun f_dfu->dfu_status = DFU_STATUS_errUNKNOWN;
165*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
dnload_request_flush(struct usb_ep * ep,struct usb_request * req)169*4882a593Smuzhiyun static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
172*4882a593Smuzhiyun dfu_set_defer_flush(dfu_get_entity(f_dfu->altsetting));
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
dfu_get_manifest_timeout(struct dfu_entity * dfu)175*4882a593Smuzhiyun static inline int dfu_get_manifest_timeout(struct dfu_entity *dfu)
176*4882a593Smuzhiyun {
177*4882a593Smuzhiyun return dfu->poll_timeout ? dfu->poll_timeout(dfu) :
178*4882a593Smuzhiyun DFU_MANIFEST_POLL_TIMEOUT;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
handle_getstatus(struct usb_request * req)181*4882a593Smuzhiyun static int handle_getstatus(struct usb_request *req)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun struct dfu_status *dstat = (struct dfu_status *)req->buf;
184*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
185*4882a593Smuzhiyun struct dfu_entity *dfu = dfu_get_entity(f_dfu->altsetting);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun dfu_set_poll_timeout(dstat, 0);
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun switch (f_dfu->dfu_state) {
190*4882a593Smuzhiyun case DFU_STATE_dfuDNLOAD_SYNC:
191*4882a593Smuzhiyun case DFU_STATE_dfuDNBUSY:
192*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
193*4882a593Smuzhiyun break;
194*4882a593Smuzhiyun case DFU_STATE_dfuMANIFEST_SYNC:
195*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
196*4882a593Smuzhiyun break;
197*4882a593Smuzhiyun case DFU_STATE_dfuMANIFEST:
198*4882a593Smuzhiyun dfu_set_poll_timeout(dstat, dfu_get_manifest_timeout(dfu));
199*4882a593Smuzhiyun break;
200*4882a593Smuzhiyun default:
201*4882a593Smuzhiyun break;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun if (f_dfu->poll_timeout)
205*4882a593Smuzhiyun if (!(f_dfu->blk_seq_num %
206*4882a593Smuzhiyun (dfu_get_buf_size() / DFU_USB_BUFSIZ)))
207*4882a593Smuzhiyun dfu_set_poll_timeout(dstat, f_dfu->poll_timeout);
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun /* send status response */
210*4882a593Smuzhiyun dstat->bStatus = f_dfu->dfu_status;
211*4882a593Smuzhiyun dstat->bState = f_dfu->dfu_state;
212*4882a593Smuzhiyun dstat->iString = 0;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun return sizeof(struct dfu_status);
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun
handle_getstate(struct usb_request * req)217*4882a593Smuzhiyun static int handle_getstate(struct usb_request *req)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun ((u8 *)req->buf)[0] = f_dfu->dfu_state;
222*4882a593Smuzhiyun return sizeof(u8);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
to_dfu_mode(struct f_dfu * f_dfu)225*4882a593Smuzhiyun static inline void to_dfu_mode(struct f_dfu *f_dfu)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun f_dfu->usb_function.strings = dfu_strings;
228*4882a593Smuzhiyun f_dfu->usb_function.hs_descriptors = f_dfu->function;
229*4882a593Smuzhiyun f_dfu->usb_function.descriptors = f_dfu->function;
230*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
231*4882a593Smuzhiyun }
232*4882a593Smuzhiyun
to_runtime_mode(struct f_dfu * f_dfu)233*4882a593Smuzhiyun static inline void to_runtime_mode(struct f_dfu *f_dfu)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun f_dfu->usb_function.strings = NULL;
236*4882a593Smuzhiyun f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
237*4882a593Smuzhiyun f_dfu->usb_function.descriptors = dfu_runtime_descs;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
handle_upload(struct usb_request * req,u16 len)240*4882a593Smuzhiyun static int handle_upload(struct usb_request *req, u16 len)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun return dfu_read(dfu_get_entity(f_dfu->altsetting), req->buf,
245*4882a593Smuzhiyun req->length, f_dfu->blk_seq_num);
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun
handle_dnload(struct usb_gadget * gadget,u16 len)248*4882a593Smuzhiyun static int handle_dnload(struct usb_gadget *gadget, u16 len)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun struct usb_composite_dev *cdev = get_gadget_data(gadget);
251*4882a593Smuzhiyun struct usb_request *req = cdev->req;
252*4882a593Smuzhiyun struct f_dfu *f_dfu = req->context;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (len == 0)
255*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun req->complete = dnload_request_complete;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun return len;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun /*-------------------------------------------------------------------------*/
263*4882a593Smuzhiyun /* DFU state machine */
state_app_idle(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)264*4882a593Smuzhiyun static int state_app_idle(struct f_dfu *f_dfu,
265*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
266*4882a593Smuzhiyun struct usb_gadget *gadget,
267*4882a593Smuzhiyun struct usb_request *req)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun int value = 0;
270*4882a593Smuzhiyun
271*4882a593Smuzhiyun switch (ctrl->bRequest) {
272*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
273*4882a593Smuzhiyun value = handle_getstatus(req);
274*4882a593Smuzhiyun break;
275*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
276*4882a593Smuzhiyun value = handle_getstate(req);
277*4882a593Smuzhiyun break;
278*4882a593Smuzhiyun case USB_REQ_DFU_DETACH:
279*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_appDETACH;
280*4882a593Smuzhiyun to_dfu_mode(f_dfu);
281*4882a593Smuzhiyun value = RET_ZLP;
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun default:
284*4882a593Smuzhiyun value = RET_STALL;
285*4882a593Smuzhiyun break;
286*4882a593Smuzhiyun }
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun return value;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
state_app_detach(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)291*4882a593Smuzhiyun static int state_app_detach(struct f_dfu *f_dfu,
292*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
293*4882a593Smuzhiyun struct usb_gadget *gadget,
294*4882a593Smuzhiyun struct usb_request *req)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun int value = 0;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun switch (ctrl->bRequest) {
299*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
300*4882a593Smuzhiyun value = handle_getstatus(req);
301*4882a593Smuzhiyun break;
302*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
303*4882a593Smuzhiyun value = handle_getstate(req);
304*4882a593Smuzhiyun break;
305*4882a593Smuzhiyun default:
306*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_appIDLE;
307*4882a593Smuzhiyun value = RET_STALL;
308*4882a593Smuzhiyun break;
309*4882a593Smuzhiyun }
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun return value;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
state_dfu_idle(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)314*4882a593Smuzhiyun static int state_dfu_idle(struct f_dfu *f_dfu,
315*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
316*4882a593Smuzhiyun struct usb_gadget *gadget,
317*4882a593Smuzhiyun struct usb_request *req)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun u16 w_value = le16_to_cpu(ctrl->wValue);
320*4882a593Smuzhiyun u16 len = le16_to_cpu(ctrl->wLength);
321*4882a593Smuzhiyun int value = 0;
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun switch (ctrl->bRequest) {
324*4882a593Smuzhiyun case USB_REQ_DFU_DNLOAD:
325*4882a593Smuzhiyun if (len == 0) {
326*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
327*4882a593Smuzhiyun value = RET_STALL;
328*4882a593Smuzhiyun break;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
331*4882a593Smuzhiyun f_dfu->blk_seq_num = w_value;
332*4882a593Smuzhiyun value = handle_dnload(gadget, len);
333*4882a593Smuzhiyun break;
334*4882a593Smuzhiyun case USB_REQ_DFU_UPLOAD:
335*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
336*4882a593Smuzhiyun f_dfu->blk_seq_num = 0;
337*4882a593Smuzhiyun value = handle_upload(req, len);
338*4882a593Smuzhiyun break;
339*4882a593Smuzhiyun case USB_REQ_DFU_ABORT:
340*4882a593Smuzhiyun /* no zlp? */
341*4882a593Smuzhiyun value = RET_ZLP;
342*4882a593Smuzhiyun break;
343*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
344*4882a593Smuzhiyun value = handle_getstatus(req);
345*4882a593Smuzhiyun break;
346*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
347*4882a593Smuzhiyun value = handle_getstate(req);
348*4882a593Smuzhiyun break;
349*4882a593Smuzhiyun case USB_REQ_DFU_DETACH:
350*4882a593Smuzhiyun /*
351*4882a593Smuzhiyun * Proprietary extension: 'detach' from idle mode and
352*4882a593Smuzhiyun * get back to runtime mode in case of USB Reset. As
353*4882a593Smuzhiyun * much as I dislike this, we just can't use every USB
354*4882a593Smuzhiyun * bus reset to switch back to runtime mode, since at
355*4882a593Smuzhiyun * least the Linux USB stack likes to send a number of
356*4882a593Smuzhiyun * resets in a row :(
357*4882a593Smuzhiyun */
358*4882a593Smuzhiyun f_dfu->dfu_state =
359*4882a593Smuzhiyun DFU_STATE_dfuMANIFEST_WAIT_RST;
360*4882a593Smuzhiyun to_runtime_mode(f_dfu);
361*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_appIDLE;
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun g_dnl_trigger_detach();
364*4882a593Smuzhiyun break;
365*4882a593Smuzhiyun default:
366*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
367*4882a593Smuzhiyun value = RET_STALL;
368*4882a593Smuzhiyun break;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun return value;
372*4882a593Smuzhiyun }
373*4882a593Smuzhiyun
state_dfu_dnload_sync(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)374*4882a593Smuzhiyun static int state_dfu_dnload_sync(struct f_dfu *f_dfu,
375*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
376*4882a593Smuzhiyun struct usb_gadget *gadget,
377*4882a593Smuzhiyun struct usb_request *req)
378*4882a593Smuzhiyun {
379*4882a593Smuzhiyun int value = 0;
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun switch (ctrl->bRequest) {
382*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
383*4882a593Smuzhiyun value = handle_getstatus(req);
384*4882a593Smuzhiyun break;
385*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
386*4882a593Smuzhiyun value = handle_getstate(req);
387*4882a593Smuzhiyun break;
388*4882a593Smuzhiyun default:
389*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
390*4882a593Smuzhiyun value = RET_STALL;
391*4882a593Smuzhiyun break;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun return value;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
state_dfu_dnbusy(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)397*4882a593Smuzhiyun static int state_dfu_dnbusy(struct f_dfu *f_dfu,
398*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
399*4882a593Smuzhiyun struct usb_gadget *gadget,
400*4882a593Smuzhiyun struct usb_request *req)
401*4882a593Smuzhiyun {
402*4882a593Smuzhiyun int value = 0;
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun switch (ctrl->bRequest) {
405*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
406*4882a593Smuzhiyun value = handle_getstatus(req);
407*4882a593Smuzhiyun break;
408*4882a593Smuzhiyun default:
409*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
410*4882a593Smuzhiyun value = RET_STALL;
411*4882a593Smuzhiyun break;
412*4882a593Smuzhiyun }
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun return value;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun
state_dfu_dnload_idle(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)417*4882a593Smuzhiyun static int state_dfu_dnload_idle(struct f_dfu *f_dfu,
418*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
419*4882a593Smuzhiyun struct usb_gadget *gadget,
420*4882a593Smuzhiyun struct usb_request *req)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun u16 w_value = le16_to_cpu(ctrl->wValue);
423*4882a593Smuzhiyun u16 len = le16_to_cpu(ctrl->wLength);
424*4882a593Smuzhiyun int value = 0;
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun switch (ctrl->bRequest) {
427*4882a593Smuzhiyun case USB_REQ_DFU_DNLOAD:
428*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_SYNC;
429*4882a593Smuzhiyun f_dfu->blk_seq_num = w_value;
430*4882a593Smuzhiyun value = handle_dnload(gadget, len);
431*4882a593Smuzhiyun break;
432*4882a593Smuzhiyun case USB_REQ_DFU_ABORT:
433*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
434*4882a593Smuzhiyun value = RET_ZLP;
435*4882a593Smuzhiyun break;
436*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
437*4882a593Smuzhiyun value = handle_getstatus(req);
438*4882a593Smuzhiyun break;
439*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
440*4882a593Smuzhiyun value = handle_getstate(req);
441*4882a593Smuzhiyun break;
442*4882a593Smuzhiyun default:
443*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
444*4882a593Smuzhiyun value = RET_STALL;
445*4882a593Smuzhiyun break;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun return value;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
state_dfu_manifest_sync(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)451*4882a593Smuzhiyun static int state_dfu_manifest_sync(struct f_dfu *f_dfu,
452*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
453*4882a593Smuzhiyun struct usb_gadget *gadget,
454*4882a593Smuzhiyun struct usb_request *req)
455*4882a593Smuzhiyun {
456*4882a593Smuzhiyun int value = 0;
457*4882a593Smuzhiyun
458*4882a593Smuzhiyun switch (ctrl->bRequest) {
459*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
460*4882a593Smuzhiyun /* We're MainfestationTolerant */
461*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuMANIFEST;
462*4882a593Smuzhiyun value = handle_getstatus(req);
463*4882a593Smuzhiyun f_dfu->blk_seq_num = 0;
464*4882a593Smuzhiyun req->complete = dnload_request_flush;
465*4882a593Smuzhiyun break;
466*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
467*4882a593Smuzhiyun value = handle_getstate(req);
468*4882a593Smuzhiyun break;
469*4882a593Smuzhiyun default:
470*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
471*4882a593Smuzhiyun value = RET_STALL;
472*4882a593Smuzhiyun break;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun return value;
476*4882a593Smuzhiyun }
477*4882a593Smuzhiyun
state_dfu_manifest(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)478*4882a593Smuzhiyun static int state_dfu_manifest(struct f_dfu *f_dfu,
479*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
480*4882a593Smuzhiyun struct usb_gadget *gadget,
481*4882a593Smuzhiyun struct usb_request *req)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun int value = 0;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun switch (ctrl->bRequest) {
486*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
487*4882a593Smuzhiyun /* We're MainfestationTolerant */
488*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
489*4882a593Smuzhiyun value = handle_getstatus(req);
490*4882a593Smuzhiyun f_dfu->blk_seq_num = 0;
491*4882a593Smuzhiyun puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n");
492*4882a593Smuzhiyun break;
493*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
494*4882a593Smuzhiyun value = handle_getstate(req);
495*4882a593Smuzhiyun break;
496*4882a593Smuzhiyun default:
497*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
498*4882a593Smuzhiyun value = RET_STALL;
499*4882a593Smuzhiyun break;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun return value;
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun
state_dfu_upload_idle(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)504*4882a593Smuzhiyun static int state_dfu_upload_idle(struct f_dfu *f_dfu,
505*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
506*4882a593Smuzhiyun struct usb_gadget *gadget,
507*4882a593Smuzhiyun struct usb_request *req)
508*4882a593Smuzhiyun {
509*4882a593Smuzhiyun u16 w_value = le16_to_cpu(ctrl->wValue);
510*4882a593Smuzhiyun u16 len = le16_to_cpu(ctrl->wLength);
511*4882a593Smuzhiyun int value = 0;
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun switch (ctrl->bRequest) {
514*4882a593Smuzhiyun case USB_REQ_DFU_UPLOAD:
515*4882a593Smuzhiyun /* state transition if less data then requested */
516*4882a593Smuzhiyun f_dfu->blk_seq_num = w_value;
517*4882a593Smuzhiyun value = handle_upload(req, len);
518*4882a593Smuzhiyun if (value >= 0 && value < len)
519*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
520*4882a593Smuzhiyun break;
521*4882a593Smuzhiyun case USB_REQ_DFU_ABORT:
522*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
523*4882a593Smuzhiyun /* no zlp? */
524*4882a593Smuzhiyun value = RET_ZLP;
525*4882a593Smuzhiyun break;
526*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
527*4882a593Smuzhiyun value = handle_getstatus(req);
528*4882a593Smuzhiyun break;
529*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
530*4882a593Smuzhiyun value = handle_getstate(req);
531*4882a593Smuzhiyun break;
532*4882a593Smuzhiyun default:
533*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
534*4882a593Smuzhiyun value = RET_STALL;
535*4882a593Smuzhiyun break;
536*4882a593Smuzhiyun }
537*4882a593Smuzhiyun
538*4882a593Smuzhiyun return value;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun
state_dfu_error(struct f_dfu * f_dfu,const struct usb_ctrlrequest * ctrl,struct usb_gadget * gadget,struct usb_request * req)541*4882a593Smuzhiyun static int state_dfu_error(struct f_dfu *f_dfu,
542*4882a593Smuzhiyun const struct usb_ctrlrequest *ctrl,
543*4882a593Smuzhiyun struct usb_gadget *gadget,
544*4882a593Smuzhiyun struct usb_request *req)
545*4882a593Smuzhiyun {
546*4882a593Smuzhiyun int value = 0;
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun switch (ctrl->bRequest) {
549*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATUS:
550*4882a593Smuzhiyun value = handle_getstatus(req);
551*4882a593Smuzhiyun break;
552*4882a593Smuzhiyun case USB_REQ_DFU_GETSTATE:
553*4882a593Smuzhiyun value = handle_getstate(req);
554*4882a593Smuzhiyun break;
555*4882a593Smuzhiyun case USB_REQ_DFU_CLRSTATUS:
556*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
557*4882a593Smuzhiyun f_dfu->dfu_status = DFU_STATUS_OK;
558*4882a593Smuzhiyun /* no zlp? */
559*4882a593Smuzhiyun value = RET_ZLP;
560*4882a593Smuzhiyun break;
561*4882a593Smuzhiyun default:
562*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuERROR;
563*4882a593Smuzhiyun value = RET_STALL;
564*4882a593Smuzhiyun break;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun return value;
568*4882a593Smuzhiyun }
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun static dfu_state_fn dfu_state[] = {
571*4882a593Smuzhiyun state_app_idle, /* DFU_STATE_appIDLE */
572*4882a593Smuzhiyun state_app_detach, /* DFU_STATE_appDETACH */
573*4882a593Smuzhiyun state_dfu_idle, /* DFU_STATE_dfuIDLE */
574*4882a593Smuzhiyun state_dfu_dnload_sync, /* DFU_STATE_dfuDNLOAD_SYNC */
575*4882a593Smuzhiyun state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */
576*4882a593Smuzhiyun state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */
577*4882a593Smuzhiyun state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */
578*4882a593Smuzhiyun state_dfu_manifest, /* DFU_STATE_dfuMANIFEST */
579*4882a593Smuzhiyun NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */
580*4882a593Smuzhiyun state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */
581*4882a593Smuzhiyun state_dfu_error /* DFU_STATE_dfuERROR */
582*4882a593Smuzhiyun };
583*4882a593Smuzhiyun
584*4882a593Smuzhiyun static int
dfu_handle(struct usb_function * f,const struct usb_ctrlrequest * ctrl)585*4882a593Smuzhiyun dfu_handle(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
586*4882a593Smuzhiyun {
587*4882a593Smuzhiyun struct usb_gadget *gadget = f->config->cdev->gadget;
588*4882a593Smuzhiyun struct usb_request *req = f->config->cdev->req;
589*4882a593Smuzhiyun struct f_dfu *f_dfu = f->config->cdev->req->context;
590*4882a593Smuzhiyun u16 len = le16_to_cpu(ctrl->wLength);
591*4882a593Smuzhiyun u16 w_value = le16_to_cpu(ctrl->wValue);
592*4882a593Smuzhiyun int value = 0;
593*4882a593Smuzhiyun u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun debug("w_value: 0x%x len: 0x%x\n", w_value, len);
596*4882a593Smuzhiyun debug("req_type: 0x%x ctrl->bRequest: 0x%x f_dfu->dfu_state: 0x%x\n",
597*4882a593Smuzhiyun req_type, ctrl->bRequest, f_dfu->dfu_state);
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun if (req_type == USB_TYPE_STANDARD) {
600*4882a593Smuzhiyun if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR &&
601*4882a593Smuzhiyun (w_value >> 8) == DFU_DT_FUNC) {
602*4882a593Smuzhiyun value = min(len, (u16) sizeof(dfu_func));
603*4882a593Smuzhiyun memcpy(req->buf, &dfu_func, value);
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun } else /* DFU specific request */
606*4882a593Smuzhiyun value = dfu_state[f_dfu->dfu_state] (f_dfu, ctrl, gadget, req);
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun if (value >= 0) {
609*4882a593Smuzhiyun req->length = value;
610*4882a593Smuzhiyun req->zero = value < len;
611*4882a593Smuzhiyun value = usb_ep_queue(gadget->ep0, req, 0);
612*4882a593Smuzhiyun if (value < 0) {
613*4882a593Smuzhiyun debug("ep_queue --> %d\n", value);
614*4882a593Smuzhiyun req->status = 0;
615*4882a593Smuzhiyun }
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun return value;
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun
621*4882a593Smuzhiyun /*-------------------------------------------------------------------------*/
622*4882a593Smuzhiyun
623*4882a593Smuzhiyun static int
dfu_prepare_strings(struct f_dfu * f_dfu,int n)624*4882a593Smuzhiyun dfu_prepare_strings(struct f_dfu *f_dfu, int n)
625*4882a593Smuzhiyun {
626*4882a593Smuzhiyun struct dfu_entity *de = NULL;
627*4882a593Smuzhiyun int i = 0;
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun f_dfu->strings = calloc(sizeof(struct usb_string), n + 1);
630*4882a593Smuzhiyun if (!f_dfu->strings)
631*4882a593Smuzhiyun return -ENOMEM;
632*4882a593Smuzhiyun
633*4882a593Smuzhiyun for (i = 0; i < n; ++i) {
634*4882a593Smuzhiyun de = dfu_get_entity(i);
635*4882a593Smuzhiyun f_dfu->strings[i].s = de->name;
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun f_dfu->strings[i].id = 0;
639*4882a593Smuzhiyun f_dfu->strings[i].s = NULL;
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun return 0;
642*4882a593Smuzhiyun }
643*4882a593Smuzhiyun
dfu_prepare_function(struct f_dfu * f_dfu,int n)644*4882a593Smuzhiyun static int dfu_prepare_function(struct f_dfu *f_dfu, int n)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun struct usb_interface_descriptor *d;
647*4882a593Smuzhiyun int i = 0;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun f_dfu->function = calloc(sizeof(struct usb_descriptor_header *), n + 2);
650*4882a593Smuzhiyun if (!f_dfu->function)
651*4882a593Smuzhiyun goto enomem;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun for (i = 0; i < n; ++i) {
654*4882a593Smuzhiyun d = calloc(sizeof(*d), 1);
655*4882a593Smuzhiyun if (!d)
656*4882a593Smuzhiyun goto enomem;
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun d->bLength = sizeof(*d);
659*4882a593Smuzhiyun d->bDescriptorType = USB_DT_INTERFACE;
660*4882a593Smuzhiyun d->bAlternateSetting = i;
661*4882a593Smuzhiyun d->bNumEndpoints = 0;
662*4882a593Smuzhiyun d->bInterfaceClass = USB_CLASS_APP_SPEC;
663*4882a593Smuzhiyun d->bInterfaceSubClass = 1;
664*4882a593Smuzhiyun d->bInterfaceProtocol = 2;
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun f_dfu->function[i] = (struct usb_descriptor_header *)d;
667*4882a593Smuzhiyun }
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun /* add DFU Functional Descriptor */
670*4882a593Smuzhiyun f_dfu->function[i] = calloc(sizeof(dfu_func), 1);
671*4882a593Smuzhiyun if (!f_dfu->function[i])
672*4882a593Smuzhiyun goto enomem;
673*4882a593Smuzhiyun memcpy(f_dfu->function[i], &dfu_func, sizeof(dfu_func));
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun i++;
676*4882a593Smuzhiyun f_dfu->function[i] = NULL;
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun return 0;
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun enomem:
681*4882a593Smuzhiyun while (i) {
682*4882a593Smuzhiyun free(f_dfu->function[--i]);
683*4882a593Smuzhiyun f_dfu->function[i] = NULL;
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun free(f_dfu->function);
686*4882a593Smuzhiyun
687*4882a593Smuzhiyun return -ENOMEM;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun
dfu_bind(struct usb_configuration * c,struct usb_function * f)690*4882a593Smuzhiyun static int dfu_bind(struct usb_configuration *c, struct usb_function *f)
691*4882a593Smuzhiyun {
692*4882a593Smuzhiyun struct usb_composite_dev *cdev = c->cdev;
693*4882a593Smuzhiyun struct f_dfu *f_dfu = func_to_dfu(f);
694*4882a593Smuzhiyun const char *s;
695*4882a593Smuzhiyun int alt_num = dfu_get_alt_number();
696*4882a593Smuzhiyun int rv, id, i;
697*4882a593Smuzhiyun
698*4882a593Smuzhiyun id = usb_interface_id(c, f);
699*4882a593Smuzhiyun if (id < 0)
700*4882a593Smuzhiyun return id;
701*4882a593Smuzhiyun dfu_intf_runtime.bInterfaceNumber = id;
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_appIDLE;
704*4882a593Smuzhiyun f_dfu->dfu_status = DFU_STATUS_OK;
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun rv = dfu_prepare_function(f_dfu, alt_num);
707*4882a593Smuzhiyun if (rv)
708*4882a593Smuzhiyun goto error;
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun rv = dfu_prepare_strings(f_dfu, alt_num);
711*4882a593Smuzhiyun if (rv)
712*4882a593Smuzhiyun goto error;
713*4882a593Smuzhiyun for (i = 0; i < alt_num; i++) {
714*4882a593Smuzhiyun id = usb_string_id(cdev);
715*4882a593Smuzhiyun if (id < 0)
716*4882a593Smuzhiyun return id;
717*4882a593Smuzhiyun f_dfu->strings[i].id = id;
718*4882a593Smuzhiyun ((struct usb_interface_descriptor *)f_dfu->function[i])
719*4882a593Smuzhiyun ->iInterface = id;
720*4882a593Smuzhiyun }
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun to_dfu_mode(f_dfu);
723*4882a593Smuzhiyun
724*4882a593Smuzhiyun stringtab_dfu.strings = f_dfu->strings;
725*4882a593Smuzhiyun
726*4882a593Smuzhiyun cdev->req->context = f_dfu;
727*4882a593Smuzhiyun
728*4882a593Smuzhiyun s = env_get("serial#");
729*4882a593Smuzhiyun if (s)
730*4882a593Smuzhiyun g_dnl_set_serialnumber((char *)s);
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun error:
733*4882a593Smuzhiyun return rv;
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun
dfu_unbind(struct usb_configuration * c,struct usb_function * f)736*4882a593Smuzhiyun static void dfu_unbind(struct usb_configuration *c, struct usb_function *f)
737*4882a593Smuzhiyun {
738*4882a593Smuzhiyun struct f_dfu *f_dfu = func_to_dfu(f);
739*4882a593Smuzhiyun int alt_num = dfu_get_alt_number();
740*4882a593Smuzhiyun int i;
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun if (f_dfu->strings) {
743*4882a593Smuzhiyun i = alt_num;
744*4882a593Smuzhiyun while (i)
745*4882a593Smuzhiyun f_dfu->strings[--i].s = NULL;
746*4882a593Smuzhiyun
747*4882a593Smuzhiyun free(f_dfu->strings);
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun if (f_dfu->function) {
751*4882a593Smuzhiyun i = alt_num;
752*4882a593Smuzhiyun i++; /* free DFU Functional Descriptor */
753*4882a593Smuzhiyun while (i) {
754*4882a593Smuzhiyun free(f_dfu->function[--i]);
755*4882a593Smuzhiyun f_dfu->function[i] = NULL;
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun free(f_dfu->function);
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun free(f_dfu);
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun
dfu_set_alt(struct usb_function * f,unsigned intf,unsigned alt)763*4882a593Smuzhiyun static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
764*4882a593Smuzhiyun {
765*4882a593Smuzhiyun struct f_dfu *f_dfu = func_to_dfu(f);
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun debug("%s: intf:%d alt:%d\n", __func__, intf, alt);
768*4882a593Smuzhiyun
769*4882a593Smuzhiyun f_dfu->altsetting = alt;
770*4882a593Smuzhiyun f_dfu->dfu_state = DFU_STATE_dfuIDLE;
771*4882a593Smuzhiyun f_dfu->dfu_status = DFU_STATUS_OK;
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun return 0;
774*4882a593Smuzhiyun }
775*4882a593Smuzhiyun
__dfu_get_alt(struct usb_function * f,unsigned intf)776*4882a593Smuzhiyun static int __dfu_get_alt(struct usb_function *f, unsigned intf)
777*4882a593Smuzhiyun {
778*4882a593Smuzhiyun struct f_dfu *f_dfu = func_to_dfu(f);
779*4882a593Smuzhiyun
780*4882a593Smuzhiyun return f_dfu->altsetting;
781*4882a593Smuzhiyun }
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun /* TODO: is this really what we need here? */
dfu_disable(struct usb_function * f)784*4882a593Smuzhiyun static void dfu_disable(struct usb_function *f)
785*4882a593Smuzhiyun {
786*4882a593Smuzhiyun struct f_dfu *f_dfu = func_to_dfu(f);
787*4882a593Smuzhiyun if (f_dfu->config == 0)
788*4882a593Smuzhiyun return;
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun debug("%s: reset config\n", __func__);
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun f_dfu->config = 0;
793*4882a593Smuzhiyun }
794*4882a593Smuzhiyun
dfu_bind_config(struct usb_configuration * c)795*4882a593Smuzhiyun static int dfu_bind_config(struct usb_configuration *c)
796*4882a593Smuzhiyun {
797*4882a593Smuzhiyun struct f_dfu *f_dfu;
798*4882a593Smuzhiyun int status;
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun f_dfu = calloc(sizeof(*f_dfu), 1);
801*4882a593Smuzhiyun if (!f_dfu)
802*4882a593Smuzhiyun return -ENOMEM;
803*4882a593Smuzhiyun f_dfu->usb_function.name = "dfu";
804*4882a593Smuzhiyun f_dfu->usb_function.hs_descriptors = dfu_runtime_descs;
805*4882a593Smuzhiyun f_dfu->usb_function.descriptors = dfu_runtime_descs;
806*4882a593Smuzhiyun f_dfu->usb_function.bind = dfu_bind;
807*4882a593Smuzhiyun f_dfu->usb_function.unbind = dfu_unbind;
808*4882a593Smuzhiyun f_dfu->usb_function.set_alt = dfu_set_alt;
809*4882a593Smuzhiyun f_dfu->usb_function.get_alt = __dfu_get_alt;
810*4882a593Smuzhiyun f_dfu->usb_function.disable = dfu_disable;
811*4882a593Smuzhiyun f_dfu->usb_function.strings = dfu_generic_strings;
812*4882a593Smuzhiyun f_dfu->usb_function.setup = dfu_handle;
813*4882a593Smuzhiyun f_dfu->poll_timeout = DFU_DEFAULT_POLL_TIMEOUT;
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun status = usb_add_function(c, &f_dfu->usb_function);
816*4882a593Smuzhiyun if (status)
817*4882a593Smuzhiyun free(f_dfu);
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun return status;
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun
dfu_add(struct usb_configuration * c)822*4882a593Smuzhiyun int dfu_add(struct usb_configuration *c)
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun int id;
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun id = usb_string_id(c->cdev);
827*4882a593Smuzhiyun if (id < 0)
828*4882a593Smuzhiyun return id;
829*4882a593Smuzhiyun strings_dfu_generic[0].id = id;
830*4882a593Smuzhiyun dfu_intf_runtime.iInterface = id;
831*4882a593Smuzhiyun
832*4882a593Smuzhiyun debug("%s: cdev: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
833*4882a593Smuzhiyun c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun return dfu_bind_config(c);
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun DECLARE_GADGET_BIND_CALLBACK(usb_dnl_dfu, dfu_add);
839