On Sat, 7 Jul 2001, Brad Hards wrote:

> [EMAIL PROTECTED] wrote:
> >
> > I've written a small program to download firmware to a device (according
> > to the USB DFU specs). This generates several timeouts on the control
> > pipe, which kinda kills the firmware upgrade. I've tried several packet
> > sizes (100, 500, 512, 1024, the interface descriptor says 1024 byte max),
> > with the same result.
>
> Your style is better than my hack, so I fixed yours instead. Looked like you
> were using the firmware block without malloc'ing it first, so I added some
> code for this too.

I malloc'ed the firmware memory in the ReadFirmware function. You're
loosing 32k of memory :)

>
> > Can anyone give me a hand with this? I've got to use DFU to load up the
> > firmware in my Wireless USB adapter. I've included my little program
> > for good measure. If you see anything that might be causing timeouts,
> > please let me know.
>
> Setting timeout parameter too short :) 100 is probably overkill, but time
> isn't really critical with this sort of application, so crude and working
> beats optimal and flakey.

I had the timeout for the Download stuff at 50000. It just didn't work.
I just tried your version, and that doesn't work either. What controller
are you using? Mine is a i815 with usb-uhci on 2.4.6. Should I be trying
this on 2.4.6-ac1?

> See attached. This produces the following:

*grmbl* Doesn't on my box. I keep getting Connection timeouts and Broken
pipes. I've changed my version to match yours (except the GetStatus struct
changes, I don't really like those), with timeout = 100 for all stuff
except the DFUDownload, where I've got a timeout of 5000000.
This results in the following:

Start FW Downloading...
DFU IDLE
DOWNLOADING 1024 Bytes (Block = 0)
STATE_DFU_DOWNLOAD_SYNC
DOWNLOAD DFU IDLE
DOWNLOADING 1024 Bytes (Block = 1)
STATE_DFU_DOWNLOAD_SYNC
DOWNLOAD DFU IDLE
DOWNLOADING 1024 Bytes (Block = 2)
STATE_DFU_DOWNLOAD_SYNC
DOWNLOAD DFU IDLE
DOWNLOADING 1024 Bytes (Block = 3)
STATE_DFU_DOWNLOAD_SYNC
DOWNLOAD DFU IDLE
DOWNLOADING 1024 Bytes (Block = 4)
Write FW Block 4 failed (-1: error sending control message: Connection timed out)!

In my dmesg I see the following:

hub.c: USB new device connect on bus1/1, assigned device number 21
usb.c: USB device 21 (vend/prod 0x3eb/0x7603) is not claimed by any active driver.
usb-uhci.c: interrupt, status 2, frame# 1978
usbdevfs: USBDEVFS_CONTROL failed dev 21 rqt 33 rq 1 len 1024 ret -110

> I am trying to get back to working on the AT76C503A, since it seems we have a
> few interested people.

Yeah, I've seen the mails as well. I'm working on a kernel mode dfu driver
at this point, so I don't have to worry about timeouts because of
userspace. I've theoretically got it working, but it needs a userspace
counterpart and some testing.

The included code is my current base for the dfu module. I think the
ioctl model should work without a problem, but as I said, I need the
userspace counterpart to go with it before I can test this.

Any comments/hints/tips are appreciated,

Bas Vermeulen

-- 
"God, root, what is difference?"
        -- Pitr, User Friendly

"God is more forgiving."
        -- Dave Aronson

/*
 * 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
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
#include <linux/ioctl.h>
#include <linux/delay.h>

MODULE_AUTHOR("Bas Vermeulen <[EMAIL PROTECTED]>");
MODULE_DESCRIPTION("USB Device Firmware Upgrade subsystem v0.0.1");

struct usbdevfs_dfu_download {
        unsigned short index;
        unsigned char buffer[1024];
        unsigned long size;
};

struct usbdevfs_dfu_upload {
        unsigned short index;
        unsigned char buffer[1024];
        unsigned long size;
};

#define USBDEVFS_DFU_DOWNLOAD   _IOR('U', 22, struct usbdevfs_dfu_download)
#define USBDEVFS_DFU_UPLOAD     _IOW('U', 23, struct usbdevfs_dfu_upload)

static void *usb_dfu_probe(struct usb_device *dev, unsigned int ifnum,
                           const struct usb_device_id *id);
static void usb_dfu_disconnect(struct usb_device *dev, void *ptr);
static int usb_dfu_ioctl(struct usb_device *dev, unsigned int ifno,
                         unsigned int code, void *buf);

/* We will attempt to probe anything that has a DFU class.
 * We will sort through them later, when we've got
 * the firmware file from userspace.
 */
static const struct usb_device_id usb_dfu_table[] = {
        {USB_INTERFACE_INFO(0xfe, 1, 0)},
        {}
};

MODULE_DEVICE_TABLE(usb, usb_dfu_table);

static struct usb_driver usb_dfu_driver = {
        name:            "dfu",
        probe:          &usb_dfu_probe,
        disconnect:     &usb_dfu_disconnect,
        ioctl:          &usb_dfu_ioctl,
        id_table:        usb_dfu_table,
};

struct usb_dfu_context {
        struct usb_device *usb;
        unsigned int ifnum;
        unsigned int dfu_download_index;
        unsigned int dfu_download_size;
        unsigned int dfu_download_progress;
        unsigned char *dfu_download_buffer;
        unsigned int dfu_upload_index;
        unsigned int dfu_upload_size;
        unsigned int dfu_upload_progress;
        unsigned char *dfu_upload_buffer;
};

/* DFU routines */

#define STATE_IDLE                      0x00
#define STATE_DETACH                    0x01
#define STATE_DFU_IDLE                  0x02
#define STATE_DFU_DOWNLOAD_SYNC         0x03
#define STATE_DFU_DOWNLOAD_BUSY         0x04
#define STATE_DFU_DOWNLOAD_IDLE         0x05
#define STATE_DFU_MANIFEST_SYNC         0x06
#define STATE_DFU_MANIFEST              0x07
#define STATE_DFU_MANIFEST_WAIT_RESET   0x08
#define STATE_DFU_UPLOAD_IDLE           0x09
#define STATE_DFU_ERROR                 0x0a

#define STATUS_SUCCESS                  1
#define STATUS_UNSUCCESSFUL             -1
#define STATUS_INVALID_PARAMETER        -9999

#define DFU_DETACH                      0
#define DFU_DNLOAD                      1
#define DFU_UPLOAD                      2
#define DFU_GETSTATUS                   3
#define DFU_CLRSTATUS                   4
#define DFU_GETSTATE                    5
#define DFU_ABORT                       6

#define TRUE                            1
#define FALSE                           0

int dfu_detach(struct usb_device *dev)
{
        int result;

        dbg("DFU Detach\n");

        result = usb_control_msg(dev, 
                                usb_sndctrlpipe(dev, 0), 
                                DFU_DETACH, 
                                USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 
                                1000,   /* Value */
                                0,      /* Index */
                                NULL,   /* Buffer */
                                0,      /* Size */
                                5000);

        return result;
}


int dfu_download(struct usb_device *dev, unsigned char *Buffer,
                 unsigned long Bytes, unsigned short Block)
{
        int result;

        dbg("DOWNLOADING %ld Bytes (Block = %d)\n", 
                Bytes, Block);
        result = usb_control_msg(dev, 
                                usb_sndctrlpipe(dev, 0), 
                                DFU_DNLOAD, 
                                USB_TYPE_CLASS|USB_DIR_OUT|USB_RECIP_INTERFACE, 
                                Block,  /* Value */
                                0,      /* Index */
                                Buffer, /* Buffer */
                                Bytes,  /* Size */
                                5000000);

        return result;
}


int dfu_get_status(struct usb_device *dev, unsigned char *status)
{
        int result;

        result = usb_control_msg(dev, 
                                usb_rcvctrlpipe(dev, 0), 
                                DFU_GETSTATUS, 
                                USB_TYPE_CLASS|USB_DIR_IN|USB_RECIP_INTERFACE, 
                                0,      /* Value */
                                0,      /* Index */
                                status, /* Buffer */
                                6,      /* Size */
                                5000);

        return result;
}

unsigned char dfu_get_state(struct usb_device *dev, unsigned char *state)
{
        int result;

        result = usb_control_msg(dev, 
                                usb_rcvctrlpipe(dev, 0), 
                                DFU_GETSTATE,   /* Request */
                                USB_TYPE_CLASS|USB_DIR_IN|USB_RECIP_INTERFACE,
                                0,      /* Value */
                                0,      /* Index */
                                state,  /* Buffer */
                                1,      /* Size */
                                5000);

        return result;
}


int DownloadFirmware(struct usb_device *dev, unsigned char *DfuBuffer,
                     unsigned int DfuLen)
{
        int status = STATUS_SUCCESS;
        int NeedDFUState = TRUE;
        int IsDone = FALSE;
        unsigned char DfuStatusBuffer[6];
        unsigned char DfuState = 0;
        unsigned long DfuTimeout = 0;
        unsigned long DfuBlockBytes, DfuBytesLeft, DfuBufferOffset;
        unsigned long DfuBlockCnt;
        int t;

        DfuBlockCnt = 0;
        DfuBytesLeft = DfuLen;
        DfuBlockBytes = 0;
        DfuBufferOffset = 0;

        if (DfuLen == 0) {
                err("FW Buffer length invalid!\n");
                return STATUS_UNSUCCESSFUL;
        }

        dbg("Start FW Downloading...\n");

        do {
                if (NeedDFUState) {
                        status = dfu_get_state(dev, &DfuState);
                        if (status <= 0) {
                                err("DFU: Failed to get DFU state...\n");
                                return STATUS_UNSUCCESSFUL;
                        }
                        NeedDFUState = FALSE;
                }

                switch (DfuState) {
                case STATE_DFU_DOWNLOAD_SYNC:
                        dbg("STATE_DFU_DOWNLOAD_SYNC\n");
                        if (dfu_get_status(dev, DfuStatusBuffer) > 0) {
                                DfuState = DfuStatusBuffer[4];
                                DfuTimeout = 0;
                                DfuTimeout =
                                    (unsigned long) (DfuStatusBuffer[3] <<
                                                     16);
                                DfuTimeout |=
                                    (unsigned long) (DfuStatusBuffer[2] <<
                                                     8);
                                DfuTimeout |=
                                    (unsigned long) (DfuStatusBuffer[1]);
                                NeedDFUState = FALSE;
                        }
                        break;
                case STATE_DFU_DOWNLOAD_BUSY:
                        dbg("STATE_DFU_DOWNLOAD_BUSY\n");
                        NeedDFUState = TRUE;

                        if (DfuTimeout > 0)
                                info("DFU: Resetting device\n");
                        else
                                info("DFU: In progress\n");

                        for (t = 1; t <= DfuTimeout / 500; t++)
                                mdelay(500);    /* Sleep for 500 ms */
                        break;
                case STATE_DFU_DOWNLOAD_IDLE:
                        dbg("DOWNLOAD ");
                case STATE_DFU_IDLE:
                        dbg("DFU IDLE\n");

                        if (DfuBytesLeft <= 1024 /* DFU_PACKETSIZE */ )
                                DfuBlockBytes = DfuBytesLeft;
                        else
                                DfuBlockBytes = 1024 /* DFU_PACKETSIZE */ ;

                        if (status > 0) {
                                DfuBytesLeft -= DfuBlockBytes;
                                status =
                                    dfu_download(dev,
                                                 DfuBuffer +
                                                 DfuBufferOffset,
                                                 DfuBlockBytes,
                                                 DfuBlockCnt);
                                DfuBufferOffset += DfuBlockBytes;
                                DfuBlockCnt++;
                        }
                        NeedDFUState = TRUE;
                        break;
                case STATE_DFU_MANIFEST_SYNC:
                        dbg("STATE_DFU_MANIFEST_SYNC\n");

                        status = dfu_get_status(dev, DfuStatusBuffer);

                        if (status > 0) {
                                DfuState = DfuStatusBuffer[4];
                                DfuTimeout = 0;
                                DfuTimeout =
                                    (unsigned long) (DfuStatusBuffer[3] <<
                                                     16);
                                DfuTimeout |=
                                    (unsigned long) (DfuStatusBuffer[2] <<
                                                     8);
                                DfuTimeout |=
                                    (unsigned long) (DfuStatusBuffer[1]);
                                NeedDFUState = FALSE;

                                if (DfuTimeout > 0)
                                        dbg("DFU: Resetting device\n");
                                else
                                        dbg("DFU: In progress\n");

                                for (t = 1; t <= DfuTimeout / 500; t++)
                                        mdelay(500);    /* Sleep for 500 ms */
                        }
                        break;
                case STATE_DFU_MANIFEST:
                        dbg("STATE_DFU_MANIFEST\n");
                        IsDone = TRUE;
                        break;
                case STATE_DFU_MANIFEST_WAIT_RESET:
                        dbg("STATE_DFU_MANIFEST_WAIT_RESET\n");
                        usb_reset_device(dev);
                        break;
                case STATE_DFU_UPLOAD_IDLE:
                        dbg("STATE_DFU_UPLOAD_IDLE\n");
                        break;
                case STATE_DFU_ERROR:
                        dbg("STATE_DFU_ERROR\n");
                        status = STATUS_INVALID_PARAMETER;
                        break;
                default:
                        err("DFU UNKNOWN STATE (%d)\n",
                               DfuState);
                        status = STATUS_INVALID_PARAMETER;
                        break;
                }
        } while (!IsDone && status > 0);

        return status;
}

/* USB Entry points */

static void *usb_dfu_probe(struct usb_device *dev, unsigned int ifnum,
                           const struct usb_device_id *id)
{
        struct usb_interface *ifp;
        struct usb_interface_descriptor *if_desc;
        struct usb_dfu_context *dfu;

        dbg("(usb_dfu_probe) called.");

        ifp = &(dev->actconfig->interface[ifnum]);
        if_desc = &(ifp->altsetting[0]);

        if ((if_desc->bInterfaceClass != 0xfe) ||
            (if_desc->bInterfaceSubClass != 0x01) ||
            (if_desc->bInterfaceProtocol != 0x00)) {
                err("probe fails -> Wrong Interface");
                return 0;
        }

        if (usb_interface_claimed(ifp)) {
                err("probe fails -> Interface already claimed");
                return 0;
        }

        if (!(dfu = kmalloc(sizeof(struct usb_dfu_context), GFP_KERNEL))) {
                err("probe fails -> Out of memory allocating device context");
                return 0;
        }
        memset(dfu, 0, sizeof(struct usb_dfu_context));

        dfu->usb = dev;
        dfu->ifnum = ifnum;

        usb_driver_claim_interface(&usb_dfu_driver, ifp, dfu);

        MOD_INC_USE_COUNT;
        usb_inc_dev_use(dev);

        return NULL;
}

static void usb_dfu_disconnect(struct usb_device *dev, void *ptr)
{
        struct usb_dfu_context *dfu = ptr;

        dbg("(usb_dfu_disconnect) called");

        usb_driver_release_interface(&usb_dfu_driver,
                                     &dev->actconfig->interface[dfu->
                                                                ifnum]);

        kfree(dfu);

        usb_dec_dev_use(dev);
        MOD_DEC_USE_COUNT;
}

static int usb_dfu_ioctl(struct usb_device *dev, unsigned int ifno,
                         unsigned int code, void *buf)
{
        int retval = 0;
        struct usb_dfu_context *dfu =
            (struct usb_dfu_context *) dev->actconfig->interface[ifno].
            private_data;

        switch (code) {
        case USBDEVFS_DFU_DOWNLOAD:
                {
                        struct usbdevfs_dfu_download *download =
                            (struct usbdevfs_dfu_download *) buf;
                        if (download->index == 0) {
                                /* Index == 0; 
                                 * - Free any allocated buffer
                                 * - Allocate the new buffer
                                 * - Set size, progress and index
                                 */
                                if (dfu->dfu_download_buffer) {
                                        kfree(dfu->dfu_download_buffer);
                                        dfu->dfu_download_buffer = NULL;
                                }
                                if (download->size > 0) {
                                        if (!
                                            (dfu->dfu_download_buffer =
                                             kmalloc(download->size,
                                                     GFP_DMA))) {
                                                err("ioctl -> could not allocate 
memory!");
                                                return -ENOMEM;
                                        }
                                        dfu->dfu_download_size =
                                            download->size;
                                        dfu->dfu_download_progress = 0;
                                        dfu->dfu_download_index = 1;
                                } else {
                                        err("ioctl -> FW size is 0.");
                                        return -EINVAL;
                                }
                        } else if (download->index ==
                                   dfu->dfu_download_index) {
                                /* Check the size. We're in download mode, so size 
                                   can't be bigger than 1024. */
                                if ((download->size < 0)
                                    || (download->size > 1024)) {
                                        err("ioctl -> invalid size");
                                        return -EINVAL;
                                }
                                if (dfu->dfu_download_progress +
                                    download->size >
                                    dfu->dfu_download_size) {
                                        err("ioctl -> buffer too large!");
                                        return -E2BIG;
                                }
                                memcpy(dfu->dfu_download_buffer +
                                       dfu->dfu_download_progress,
                                       download->buffer, download->size);
                                dfu->dfu_download_progress +=
                                    download->size;

                                if (dfu->dfu_download_progress ==
                                    dfu->dfu_download_size) {
                                        /* We've finished getting the file from 
userspace.
                                         * Now we have to download it to the device.
                                         */

                                        /* Reset the download state. */
                                        dfu->dfu_download_index = 0;
                                        kfree(dfu->dfu_download_buffer);
                                        dfu->dfu_download_buffer = NULL;
                                        return dfu->dfu_download_size;
                                }
                                dfu->dfu_download_index++;
                        } else {
                                err("ioctl -> out of order request");
                                return -EINVAL;
                        }
                        break;
                }
        case USBDEVFS_DFU_UPLOAD:
                break;
        default:
                return -ENOTSUPP;
        }
        return retval;
}

/* Module Entry Points */
static int __init dfu_init(void)
{
        usb_register(&usb_dfu_driver);
        return 0;
}

static void __exit dfu_exit(void)
{
        usb_deregister(&usb_dfu_driver);
}

module_init(dfu_init);
module_exit(dfu_exit);

Reply via email to