X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fusb%2Fgadget%2Ff_dfu.c;h=1b1e1793d920f49076b5ccac37d25075a9e07060;hb=e7f9350525d73233d4eaf1793f8fe618e9fd4910;hp=d7ae0c0c6a9f6a95ca3a702d92636eb25e21253c;hpb=81c065d2a0fafe137078e4575d1e6a9d8f90ba3b;p=u-boot diff --git a/drivers/usb/gadget/f_dfu.c b/drivers/usb/gadget/f_dfu.c index d7ae0c0c6a..1b1e1793d9 100644 --- a/drivers/usb/gadget/f_dfu.c +++ b/drivers/usb/gadget/f_dfu.c @@ -12,19 +12,7 @@ * based on existing SAM7DFU code from OpenPCD: * (C) Copyright 2006 by Harald Welte * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -36,6 +24,7 @@ #include #include +#include #include "f_dfu.h" struct f_dfu { @@ -52,6 +41,7 @@ struct f_dfu { /* Send/received block number is handy for data integrity check */ int blk_seq_num; + unsigned int poll_timeout; }; typedef int (*dfu_state_fn) (struct f_dfu *, @@ -140,6 +130,33 @@ static struct usb_gadget_strings *dfu_strings[] = { NULL, }; +static void dfu_set_poll_timeout(struct dfu_status *dstat, unsigned int ms) +{ + /* + * The bwPollTimeout DFU_GETSTATUS request payload provides information + * about minimum time, in milliseconds, that the host should wait before + * sending a subsequent DFU_GETSTATUS request + * + * This permits the device to vary the delay depending on its need to + * erase or program the memory + * + */ + + unsigned char *p = (unsigned char *)&ms; + + if (!ms || (ms & ~DFU_POLL_TIMEOUT_MASK)) { + dstat->bwPollTimeout[0] = 0; + dstat->bwPollTimeout[1] = 0; + dstat->bwPollTimeout[2] = 0; + + return; + } + + dstat->bwPollTimeout[0] = *p++; + dstat->bwPollTimeout[1] = *p++; + dstat->bwPollTimeout[2] = *p; +} + /*-------------------------------------------------------------------------*/ static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) @@ -148,9 +165,14 @@ static void dnload_request_complete(struct usb_ep *ep, struct usb_request *req) dfu_write(dfu_get_entity(f_dfu->altsetting), req->buf, req->length, f_dfu->blk_seq_num); +} - if (req->length == 0) - puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); +static void dnload_request_flush(struct usb_ep *ep, struct usb_request *req) +{ + struct f_dfu *f_dfu = req->context; + + dfu_flush(dfu_get_entity(f_dfu->altsetting), req->buf, + req->length, f_dfu->blk_seq_num); } static void handle_getstatus(struct usb_request *req) @@ -158,22 +180,29 @@ static void handle_getstatus(struct usb_request *req) struct dfu_status *dstat = (struct dfu_status *)req->buf; struct f_dfu *f_dfu = req->context; + dfu_set_poll_timeout(dstat, 0); + switch (f_dfu->dfu_state) { case DFU_STATE_dfuDNLOAD_SYNC: case DFU_STATE_dfuDNBUSY: f_dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE; break; case DFU_STATE_dfuMANIFEST_SYNC: + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; break; + case DFU_STATE_dfuMANIFEST: + dfu_set_poll_timeout(dstat, DFU_MANIFEST_POLL_TIMEOUT); default: break; } + if (f_dfu->poll_timeout) + if (!(f_dfu->blk_seq_num % + (dfu_get_buf_size() / DFU_USB_BUFSIZ))) + dfu_set_poll_timeout(dstat, f_dfu->poll_timeout); + /* send status response */ dstat->bStatus = f_dfu->dfu_status; - dstat->bwPollTimeout[0] = 0; - dstat->bwPollTimeout[1] = 0; - dstat->bwPollTimeout[2] = 0; dstat->bState = f_dfu->dfu_state; dstat->iString = 0; } @@ -324,6 +353,8 @@ static int state_dfu_idle(struct f_dfu *f_dfu, DFU_STATE_dfuMANIFEST_WAIT_RST; to_runtime_mode(f_dfu); f_dfu->dfu_state = DFU_STATE_appIDLE; + + dfu_trigger_reset(); break; default: f_dfu->dfu_state = DFU_STATE_dfuERROR; @@ -424,10 +455,11 @@ static int state_dfu_manifest_sync(struct f_dfu *f_dfu, switch (ctrl->bRequest) { case USB_REQ_DFU_GETSTATUS: /* We're MainfestationTolerant */ - f_dfu->dfu_state = DFU_STATE_dfuIDLE; + f_dfu->dfu_state = DFU_STATE_dfuMANIFEST; handle_getstatus(req); f_dfu->blk_seq_num = 0; value = RET_STAT_LEN; + req->complete = dnload_request_flush; break; case USB_REQ_DFU_GETSTATE: handle_getstate(req); @@ -441,6 +473,33 @@ static int state_dfu_manifest_sync(struct f_dfu *f_dfu, return value; } +static int state_dfu_manifest(struct f_dfu *f_dfu, + const struct usb_ctrlrequest *ctrl, + struct usb_gadget *gadget, + struct usb_request *req) +{ + int value = 0; + + switch (ctrl->bRequest) { + case USB_REQ_DFU_GETSTATUS: + /* We're MainfestationTolerant */ + f_dfu->dfu_state = DFU_STATE_dfuIDLE; + handle_getstatus(req); + f_dfu->blk_seq_num = 0; + value = RET_STAT_LEN; + puts("DOWNLOAD ... OK\nCtrl+C to exit ...\n"); + break; + case USB_REQ_DFU_GETSTATE: + handle_getstate(req); + break; + default: + f_dfu->dfu_state = DFU_STATE_dfuERROR; + value = RET_STALL; + break; + } + return value; +} + static int state_dfu_upload_idle(struct f_dfu *f_dfu, const struct usb_ctrlrequest *ctrl, struct usb_gadget *gadget, @@ -517,7 +576,7 @@ static dfu_state_fn dfu_state[] = { state_dfu_dnbusy, /* DFU_STATE_dfuDNBUSY */ state_dfu_dnload_idle, /* DFU_STATE_dfuDNLOAD_IDLE */ state_dfu_manifest_sync, /* DFU_STATE_dfuMANIFEST_SYNC */ - NULL, /* DFU_STATE_dfuMANIFEST */ + state_dfu_manifest, /* DFU_STATE_dfuMANIFEST */ NULL, /* DFU_STATE_dfuMANIFEST_WAIT_RST */ state_dfu_upload_idle, /* DFU_STATE_dfuUPLOAD_IDLE */ state_dfu_error /* DFU_STATE_dfuERROR */ @@ -733,8 +792,9 @@ static int dfu_bind_config(struct usb_configuration *c) f_dfu->usb_function.unbind = dfu_unbind; f_dfu->usb_function.set_alt = dfu_set_alt; f_dfu->usb_function.disable = dfu_disable; - f_dfu->usb_function.strings = dfu_generic_strings, - f_dfu->usb_function.setup = dfu_handle, + f_dfu->usb_function.strings = dfu_generic_strings; + f_dfu->usb_function.setup = dfu_handle; + f_dfu->poll_timeout = DFU_DEFAULT_POLL_TIMEOUT; status = usb_add_function(c, &f_dfu->usb_function); if (status) @@ -758,3 +818,5 @@ int dfu_add(struct usb_configuration *c) return dfu_bind_config(c); } + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_dfu, dfu_add);