summaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/dfu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/dfu.c')
-rw-r--r--drivers/usb/gadget/dfu.c871
1 files changed, 0 insertions, 871 deletions
diff --git a/drivers/usb/gadget/dfu.c b/drivers/usb/gadget/dfu.c
deleted file mode 100644
index ba5fdd5b74..0000000000
--- a/drivers/usb/gadget/dfu.c
+++ /dev/null
@@ -1,871 +0,0 @@
-/*
- * (C) 2007 by OpenMoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- *
- * based on existing SAM7DFU code from OpenPCD:
- * (C) Copyright 2006 by Harald Welte <hwelte@hmw-consulting.de>
- *
- * 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.
- *
- *
- * TODO:
- * - make NAND support reasonably self-contained and put in apropriate
- * ifdefs
- * - add some means of synchronization, i.e. block commandline access
- * while DFU transfer is in progress, and return to commandline once
- * we're finished
- * - add VERIFY support after writing to flash
- * - sanely free() resources allocated during first uppload/download
- * request when aborting
- * - sanely free resources when another alternate interface is selected
- *
- * Maybe:
- * - add something like uImage or some other header that provides CRC
- * checking?
- * - make 'dnstate' attached to 'struct usb_device_instance'
- */
-#define pr_fmt(fmt) "dfu: " fmt
-
-#include <dma.h>
-#include <asm/byteorder.h>
-#include <usb/composite.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <usb/gadget.h>
-#include <linux/stat.h>
-#include <libfile.h>
-#include <linux/err.h>
-#include <usb/ch9.h>
-#include <usb/dfu.h>
-#include <config.h>
-#include <common.h>
-#include <malloc.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libbb.h>
-#include <init.h>
-#include <fs.h>
-#include <ioctl.h>
-#include <linux/mtd/mtd-abi.h>
-#include <work.h>
-
-#define USB_DT_DFU 0x21
-
-struct usb_dfu_func_descriptor {
- u_int8_t bLength;
- u_int8_t bDescriptorType;
- u_int8_t bmAttributes;
-#define USB_DFU_CAN_DOWNLOAD (1 << 0)
-#define USB_DFU_CAN_UPLOAD (1 << 1)
-#define USB_DFU_MANIFEST_TOL (1 << 2)
-#define USB_DFU_WILL_DETACH (1 << 3)
- u_int16_t wDetachTimeOut;
- u_int16_t wTransferSize;
- u_int16_t bcdDFUVersion;
-} __attribute__ ((packed));
-
-#define USB_DT_DFU_SIZE 9
-
-#define USB_TYPE_DFU (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
-
-/* DFU class-specific requests (Section 3, DFU Rev 1.1) */
-#define USB_REQ_DFU_DETACH 0x00
-#define USB_REQ_DFU_DNLOAD 0x01
-#define USB_REQ_DFU_UPLOAD 0x02
-#define USB_REQ_DFU_GETSTATUS 0x03
-#define USB_REQ_DFU_CLRSTATUS 0x04
-#define USB_REQ_DFU_GETSTATE 0x05
-#define USB_REQ_DFU_ABORT 0x06
-
-struct dfu_status {
- u_int8_t bStatus;
- u_int8_t bwPollTimeout[3];
- u_int8_t bState;
- u_int8_t iString;
-} __attribute__((packed));
-
-#define DFU_STATUS_OK 0x00
-#define DFU_STATUS_errTARGET 0x01
-#define DFU_STATUS_errFILE 0x02
-#define DFU_STATUS_errWRITE 0x03
-#define DFU_STATUS_errERASE 0x04
-#define DFU_STATUS_errCHECK_ERASED 0x05
-#define DFU_STATUS_errPROG 0x06
-#define DFU_STATUS_errVERIFY 0x07
-#define DFU_STATUS_errADDRESS 0x08
-#define DFU_STATUS_errNOTDONE 0x09
-#define DFU_STATUS_errFIRMWARE 0x0a
-#define DFU_STATUS_errVENDOR 0x0b
-#define DFU_STATUS_errUSBR 0x0c
-#define DFU_STATUS_errPOR 0x0d
-#define DFU_STATUS_errUNKNOWN 0x0e
-#define DFU_STATUS_errSTALLEDPKT 0x0f
-
-enum dfu_state {
- DFU_STATE_appIDLE = 0,
- DFU_STATE_appDETACH = 1,
- DFU_STATE_dfuIDLE = 2,
- DFU_STATE_dfuDNLOAD_SYNC = 3,
- DFU_STATE_dfuDNBUSY = 4,
- DFU_STATE_dfuDNLOAD_IDLE = 5,
- DFU_STATE_dfuMANIFEST_SYNC = 6,
- DFU_STATE_dfuMANIFEST = 7,
- DFU_STATE_dfuMANIFEST_WAIT_RST = 8,
- DFU_STATE_dfuUPLOAD_IDLE = 9,
- DFU_STATE_dfuERROR = 10,
-};
-
-#define USB_DT_DFU_SIZE 9
-#define USB_DT_DFU 0x21
-
-#define CONFIG_USBD_DFU_XFER_SIZE 4096
-#define DFU_TEMPFILE "/dfu_temp"
-
-struct file_list_entry *dfu_file_entry;
-static int dfufd = -EINVAL;
-static struct file_list *dfu_files;
-static int dfudetach;
-static struct mtd_info_user dfu_mtdinfo;
-static loff_t dfu_written;
-static loff_t dfu_erased;
-static int prog_erase;
-
-/* USB DFU functional descriptor */
-static struct usb_dfu_func_descriptor usb_dfu_func = {
- .bLength = USB_DT_DFU_SIZE,
- .bDescriptorType = USB_DT_DFU,
- .bmAttributes = USB_DFU_CAN_UPLOAD | USB_DFU_CAN_DOWNLOAD | USB_DFU_MANIFEST_TOL,
- .wDetachTimeOut = 0xff00,
- .wTransferSize = CONFIG_USBD_DFU_XFER_SIZE,
- .bcdDFUVersion = 0x0100,
-};
-
-struct f_dfu {
- struct usb_function func;
- u8 port_num;
-
- u8 dfu_state;
- u8 dfu_status;
- struct usb_request *dnreq;
- struct work_queue wq;
-};
-
-static inline struct f_dfu *func_to_dfu(struct usb_function *f)
-{
- return container_of(f, struct f_dfu, func);
-}
-
-/* static strings, in UTF-8 */
-static struct usb_string *dfu_string_defs;
-
-static struct usb_gadget_strings dfu_string_table = {
- .language = 0x0409, /* en-us */
-};
-
-static struct usb_gadget_strings *dfu_strings[] = {
- &dfu_string_table,
- NULL,
-};
-
-static void dn_complete(struct usb_ep *ep, struct usb_request *req);
-static void up_complete(struct usb_ep *ep, struct usb_request *req);
-static void dfu_cleanup(struct f_dfu *dfu);
-
-struct dfu_work {
- struct work_struct work;
- struct f_dfu *dfu;
- void (*task)(struct dfu_work *dw);
- size_t len;
- uint8_t *rbuf;
- uint8_t wbuf[CONFIG_USBD_DFU_XFER_SIZE];
-};
-
-static void dfu_do_work(struct work_struct *w)
-{
- struct dfu_work *dw = container_of(w, struct dfu_work, work);
- struct f_dfu *dfu = dw->dfu;
-
- if (dfu->dfu_state != DFU_STATE_dfuERROR && dfu->dfu_status == DFU_STATUS_OK)
- dw->task(dw);
- else
- pr_debug("skip work\n");
-
- free(dw);
-}
-
-static void dfu_work_cancel(struct work_struct *w)
-{
- struct dfu_work *dw = container_of(w, struct dfu_work, work);
-
- free(dw);
-}
-
-static void dfu_do_write(struct dfu_work *dw)
-{
- struct f_dfu *dfu = dw->dfu;
- ssize_t size, wlen = dw->len;
- ssize_t ret;
-
- pr_debug("do write\n");
-
- if (prog_erase && (dfu_written + wlen) > dfu_erased) {
- size = roundup(wlen, dfu_mtdinfo.erasesize);
- ret = erase(dfufd, size, dfu_erased);
- dfu_erased += size;
- if (ret && ret != -ENOSYS) {
- perror("erase");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errERASE;
- return;
- }
- }
-
- dfu_written += wlen;
- ret = write(dfufd, dw->wbuf, wlen);
- if (ret < wlen) {
- perror("write");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errWRITE;
- }
-}
-
-static void dfu_do_read(struct dfu_work *dw)
-{
- struct f_dfu *dfu = dw->dfu;
- struct usb_composite_dev *cdev = dfu->func.config->cdev;
- ssize_t size, rlen = dw->len;
-
- pr_debug("do read\n");
-
- size = read(dfufd, dfu->dnreq->buf, rlen);
- dfu->dnreq->length = size;
- if (size < 0) {
- perror("read");
- dfu->dnreq->length = 0;
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errFILE;
- } else if (size < rlen) {
- /* this is the last chunk, go to IDLE and close file */
- dfu_cleanup(dfu);
- }
-
- dfu->dnreq->complete = up_complete;
- usb_ep_queue(cdev->gadget->ep0, dfu->dnreq);
-}
-
-static void dfu_do_open_dnload(struct dfu_work *dw)
-{
- struct f_dfu *dfu = dw->dfu;
- int ret;
-
- pr_debug("do open dnload\n");
-
- if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) {
- dfufd = open(DFU_TEMPFILE, O_WRONLY | O_CREAT);
- } else {
- unsigned flags = O_WRONLY;
-
- if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE)
- flags |= O_CREAT | O_TRUNC;
-
- dfufd = open(dfu_file_entry->filename, flags);
- }
-
- if (dfufd < 0) {
- perror("open");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errFILE;
- return;
- }
-
- if (!(dfu_file_entry->flags & FILE_LIST_FLAG_SAFE)) {
- ret = ioctl(dfufd, MEMGETINFO, &dfu_mtdinfo);
- if (!ret) /* file is on a mtd device */
- prog_erase = 1;
- }
-}
-
-static void dfu_do_open_upload(struct dfu_work *dw)
-{
- struct f_dfu *dfu = dw->dfu;
-
- pr_debug("do open upload\n");
-
- dfufd = open(dfu_file_entry->filename, O_RDONLY);
- if (dfufd < 0) {
- perror("open");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errFILE;
- }
-}
-
-static void dfu_do_close(struct dfu_work *dw)
-{
- struct stat s;
-
- pr_debug("do close\n");
-
- if (dfufd > 0) {
- close(dfufd);
- dfufd = -EINVAL;
- }
-
- if (!stat(DFU_TEMPFILE, &s))
- unlink(DFU_TEMPFILE);
-
- dw->dfu->dfu_state = DFU_STATE_dfuIDLE;
-}
-
-static void dfu_do_copy(struct dfu_work *dw)
-{
- struct f_dfu *dfu = dw->dfu;
- unsigned flags = O_WRONLY;
- int ret, fd;
-
- pr_debug("do copy\n");
-
- if (dfu_file_entry->flags & FILE_LIST_FLAG_CREATE)
- flags |= O_CREAT | O_TRUNC;
-
- fd = open(dfu_file_entry->filename, flags);
- if (fd < 0) {
- perror("open");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errERASE;
- return;
- }
-
- ret = erase(fd, ERASE_SIZE_ALL, 0);
- close(fd);
- if (ret && ret != -ENOSYS) {
- perror("erase");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errERASE;
- return;
- }
-
- ret = copy_file(DFU_TEMPFILE, dfu_file_entry->filename, 0);
- if (ret) {
- pr_err("copy file failed\n");
- dfu->dfu_state = DFU_STATE_dfuERROR;
- dfu->dfu_status = DFU_STATUS_errWRITE;
- return;
- }
-
- dfu->dfu_state = DFU_STATE_dfuIDLE;
-}
-
-static int
-dfu_bind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_composite_dev *cdev = c->cdev;
- struct usb_descriptor_header **header;
- struct usb_interface_descriptor *desc;
- struct file_list_entry *fentry;
- struct f_dfu *dfu = func_to_dfu(f);
- int i;
- int status;
- struct usb_string *us;
-
- if (!dfu_files) {
- const struct usb_function_instance *fi = f->fi;
- struct f_dfu_opts *opts = container_of(fi, struct f_dfu_opts, func_inst);
-
- dfu_files = opts->files;
- }
-
- dfu_string_defs = xzalloc(sizeof(struct usb_string) * (dfu_files->num_entries + 2));
- dfu_string_defs[0].s = "Generic DFU";
- i = 0;
- file_list_for_each_entry(dfu_files, fentry) {
- dfu_string_defs[i + 1].s = fentry->name;
- i++;
- }
-
- dfu_string_defs[i + 1].s = NULL;
- dfu_string_table.strings = dfu_string_defs;
-
- dfu->dfu_state = DFU_STATE_dfuIDLE;
- dfu->dfu_status = DFU_STATUS_OK;
-
- dfu->dnreq = usb_ep_alloc_request(c->cdev->gadget->ep0);
- if (!dfu->dnreq) {
- pr_err("usb_ep_alloc_request failed\n");
- status = -ENOMEM;
- goto out;
- }
- dfu->dnreq->buf = dma_alloc(CONFIG_USBD_DFU_XFER_SIZE);
- dfu->dnreq->complete = dn_complete;
- dfu->dnreq->zero = 0;
-
- us = usb_gstrings_attach(cdev, dfu_strings, dfu_files->num_entries + 1);
- if (IS_ERR(us)) {
- status = PTR_ERR(us);
- goto out;
- }
-
- dfu->wq.fn = dfu_do_work;
- dfu->wq.cancel = dfu_work_cancel;
- wq_register(&dfu->wq);
-
- /* allocate instance-specific interface IDs, and patch descriptors */
- status = usb_interface_id(c, f);
- if (status < 0)
- goto out;
-
- header = xzalloc(sizeof(void *) * (dfu_files->num_entries + 2));
- desc = xzalloc(sizeof(struct usb_interface_descriptor) * dfu_files->num_entries);
- for (i = 0; i < dfu_files->num_entries; i++) {
- desc[i].bLength = USB_DT_INTERFACE_SIZE;
- desc[i].bDescriptorType = USB_DT_INTERFACE;
- desc[i].bNumEndpoints = 0;
- desc[i].bInterfaceClass = 0xfe;
- desc[i].bInterfaceSubClass = 1;
- desc[i].bInterfaceProtocol = 2;
- desc[i].bAlternateSetting = i;
- desc[i].iInterface = us[i + 1].id;
- header[i] = (struct usb_descriptor_header *)&desc[i];
- }
- header[i] = (struct usb_descriptor_header *) &usb_dfu_func;
- header[i + 1] = NULL;
-
- status = usb_assign_descriptors(f, header, header, NULL);
-
- free(desc);
- free(header);
-
- if (status)
- goto out;
-
- i = 0;
- file_list_for_each_entry(dfu_files, fentry) {
- pr_info("register alt%d(%s) with device %s\n", i, fentry->name, fentry->filename);
- i++;
- }
-
- return 0;
-out:
- free(dfu_string_defs);
-
- if (status)
- ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
-
- return status;
-}
-
-static void
-dfu_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_dfu *dfu = func_to_dfu(f);
-
- dfu_files = NULL;
- dfu_file_entry = NULL;
- dfudetach = 0;
-
- wq_unregister(&dfu->wq);
-
- usb_free_all_descriptors(f);
-
- dma_free(dfu->dnreq->buf);
- usb_ep_free_request(c->cdev->gadget->ep0, dfu->dnreq);
-}
-
-static int dfu_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
-{
- struct file_list_entry *fentry;
- int i = 0;
-
- file_list_for_each_entry(dfu_files, fentry) {
- if (i == alt) {
- dfu_file_entry = fentry;
- return 0;
- }
-
- i++;
- }
-
- return -EINVAL;
-}
-
-static int dfu_status(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
- struct f_dfu *dfu = func_to_dfu(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = cdev->req;
- struct dfu_status *dstat = (struct dfu_status *) req->buf;
-
- dstat->bStatus = dfu->dfu_status;
- dstat->bState = dfu->dfu_state;
- dstat->iString = 0;
- dstat->bwPollTimeout[0] = 10;
- dstat->bwPollTimeout[1] = 0;
- dstat->bwPollTimeout[2] = 0;
-
- return sizeof(*dstat);
-}
-
-static void dfu_cleanup(struct f_dfu *dfu)
-{
- struct dfu_work *dw;
-
- pr_debug("dfu cleanup\n");
-
- memset(&dfu_mtdinfo, 0, sizeof(dfu_mtdinfo));
- dfu_written = 0;
- dfu_erased = 0;
- prog_erase = 0;
-
- dfu->dfu_state = DFU_STATE_dfuIDLE;
- dfu->dfu_status = DFU_STATUS_OK;
-
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_close;
- wq_queue_work(&dfu->wq, &dw->work);
-}
-
-static void dn_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct f_dfu *dfu = req->context;
- struct dfu_work *dw;
-
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_write;
- dw->len = min_t(unsigned int, req->length, CONFIG_USBD_DFU_XFER_SIZE);
- memcpy(dw->wbuf, req->buf, dw->len);
- wq_queue_work(&dfu->wq, &dw->work);
-}
-
-static int handle_manifest(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
- struct f_dfu *dfu = func_to_dfu(f);
- struct dfu_work *dw;
-
- if (dfu_file_entry->flags & FILE_LIST_FLAG_SAFE) {
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_copy;
- wq_queue_work(&dfu->wq, &dw->work);
- }
-
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_close;
- wq_queue_work(&dfu->wq, &dw->work);
-
- return 0;
-}
-
-static int handle_dnload(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
- struct f_dfu *dfu = func_to_dfu(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- u16 w_length = le16_to_cpu(ctrl->wLength);
-
- if (w_length == 0) {
- handle_manifest(f, ctrl);
- dfu->dfu_state = DFU_STATE_dfuMANIFEST;
- return 0;
- }
-
- dfu->dnreq->length = w_length;
- dfu->dnreq->context = dfu;
- usb_ep_queue(cdev->gadget->ep0, dfu->dnreq);
-
- return 0;
-}
-
-static void up_complete(struct usb_ep *ep, struct usb_request *req)
-{
-}
-
-static int handle_upload(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
- struct f_dfu *dfu = func_to_dfu(f);
- struct dfu_work *dw;
- u16 w_length = le16_to_cpu(ctrl->wLength);
-
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_read;
- dw->len = w_length;
- dw->rbuf = dfu->dnreq->buf;
- wq_queue_work(&dfu->wq, &dw->work);
-
- return 0;
-}
-
-static void dfu_abort(struct f_dfu *dfu)
-{
- wq_cancel_work(&dfu->wq);
-
- dfu_cleanup(dfu);
-}
-
-static int dfu_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
-{
- struct f_dfu *dfu = func_to_dfu(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = cdev->req;
- int value = -EOPNOTSUPP;
- int w_length = le16_to_cpu(ctrl->wLength);
- int w_value = le16_to_cpu(ctrl->wValue);
- struct dfu_work *dw;
-
- if (ctrl->bRequestType == USB_DIR_IN && ctrl->bRequest == USB_REQ_GET_DESCRIPTOR
- && (w_value >> 8) == 0x21) {
- value = min(w_length, (int)sizeof(usb_dfu_func));
- memcpy(req->buf, &usb_dfu_func, value);
- goto out;
- }
-
- switch (dfu->dfu_state) {
- case DFU_STATE_dfuIDLE:
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- case USB_REQ_DFU_DNLOAD:
- if (w_length == 0) {
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- goto out;
- }
- pr_debug("starting download to %s\n", dfu_file_entry->filename);
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_open_dnload;
- wq_queue_work(&dfu->wq, &dw->work);
-
- value = handle_dnload(f, ctrl);
- dfu->dfu_state = DFU_STATE_dfuDNLOAD_IDLE;
- return 0;
- case USB_REQ_DFU_UPLOAD:
- dfu->dfu_state = DFU_STATE_dfuUPLOAD_IDLE;
- pr_debug("starting upload from %s\n", dfu_file_entry->filename);
- if (!(dfu_file_entry->flags & FILE_LIST_FLAG_READBACK)) {
- dfu->dfu_state = DFU_STATE_dfuERROR;
- goto out;
- }
-
- dw = xzalloc(sizeof(*dw));
- dw->dfu = dfu;
- dw->task = dfu_do_open_upload;
- wq_queue_work(&dfu->wq, &dw->work);
-
- handle_upload(f, ctrl);
- return 0;
- case USB_REQ_DFU_ABORT:
- dfu->dfu_status = DFU_STATUS_OK;
- value = 0;
- break;
- case USB_REQ_DFU_DETACH:
- value = 0;
- dfudetach = 1;
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuDNLOAD_IDLE:
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- case USB_REQ_DFU_DNLOAD:
- value = handle_dnload(f, ctrl);
- if (dfu->dfu_state == DFU_STATE_dfuDNLOAD_IDLE) {
- return 0;
- }
- break;
- case USB_REQ_DFU_ABORT:
- dfu_abort(dfu);
- value = 0;
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuUPLOAD_IDLE:
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- case USB_REQ_DFU_UPLOAD:
- handle_upload(f, ctrl);
- return 0;
- case USB_REQ_DFU_ABORT:
- dfu_abort(dfu);
- value = 0;
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuERROR:
- wq_cancel_work(&dfu->wq);
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- case USB_REQ_DFU_CLRSTATUS:
- dfu_abort(dfu);
- /* no zlp? */
- value = 0;
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuMANIFEST_SYNC:
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- dfu->dfu_state = DFU_STATE_dfuMANIFEST;
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuMANIFEST:
- dfu->dfu_state = DFU_STATE_dfuMANIFEST_SYNC;
- switch (ctrl->bRequest) {
- case USB_REQ_DFU_GETSTATUS:
- value = dfu_status(f, ctrl);
- value = min(value, w_length);
- break;
- case USB_REQ_DFU_GETSTATE:
- *(u8 *)req->buf = dfu->dfu_state;
- value = sizeof(u8);
- break;
- default:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- }
- break;
- case DFU_STATE_dfuDNLOAD_SYNC:
- case DFU_STATE_dfuDNBUSY:
- dfu->dfu_state = DFU_STATE_dfuERROR;
- value = -EINVAL;
- break;
- default:
- break;
- }
-out:
- /* respond with data transfer or status phase? */
- if (value >= 0) {
- req->zero = 0;
- req->length = value;
- value = usb_ep_queue(cdev->gadget->ep0, req);
- if (value < 0)
- ERROR(cdev, "dfu response on ttyGS%d, err %d\n",
- dfu->port_num, value);
- }
-
- return value;
-}
-
-static void dfu_disable(struct usb_function *f)
-{
- struct f_dfu *dfu = func_to_dfu(f);
-
- dfu_abort(dfu);
-}
-
-int usb_dfu_detached(void)
-{
- return dfudetach;
-}
-
-static void dfu_free_func(struct usb_function *f)
-{
- struct f_dfu *dfu = func_to_dfu(f);
-
- free(dfu);
-}
-
-static struct usb_function *dfu_alloc_func(struct usb_function_instance *fi)
-{
- struct f_dfu *dfu;
-
- dfu = xzalloc(sizeof(*dfu));
-
- dfu->func.name = "dfu";
- dfu->func.strings = dfu_strings;
- /* descriptors are per-instance copies */
- dfu->func.bind = dfu_bind;
- dfu->func.set_alt = dfu_set_alt;
- dfu->func.setup = dfu_setup;
- dfu->func.disable = dfu_disable;
- dfu->func.unbind = dfu_unbind;
- dfu->func.free_func = dfu_free_func;
-
- return &dfu->func;
-}
-
-static void dfu_free_instance(struct usb_function_instance *fi)
-{
- struct f_dfu_opts *opts;
-
- opts = container_of(fi, struct f_dfu_opts, func_inst);
- kfree(opts);
-}
-
-static struct usb_function_instance *dfu_alloc_instance(void)
-{
- struct f_dfu_opts *opts;
-
- opts = kzalloc(sizeof(*opts), GFP_KERNEL);
- if (!opts)
- return ERR_PTR(-ENOMEM);
- opts->func_inst.free_func_inst = dfu_free_instance;
-
- return &opts->func_inst;
-}
-
-DECLARE_USB_FUNCTION_INIT(dfu, dfu_alloc_instance, dfu_alloc_func);