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.

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.

I'm using a Dell Inspiron 8000 with a i815 chipset with matching USB
controller, with the usb-uhci driver. I've tried the uhci driver, but
that doesn't help either.

If anyone can shed some light on this, I'd appreciate it.

Regards,

Bas Vermeulen

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

"God is more forgiving."
        -- Dave Aronson
/*
 * dfu.c
 *
 * DFU download utility
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <usb.h>

#define USB_DIR_IN                      0x80
#define USB_DIR_OUT                     0x00

#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 USB_SUCCESS(a)                  (a > 0)

#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

#define DFU_PACKETSIZE                  1024

int DFUDetach(usb_dev_handle* dev)
{
    int result;

    printf("DFU Detach\n");

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

    if (!USB_SUCCESS(result))
        printf("DFU Detach FAILED (%s)!\n", usb_strerror());

    return result;
}


int DFUDownload(usb_dev_handle *dev, unsigned char *Buffer, unsigned long Bytes, 
unsigned short Block)
{
    int result;

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

    if (!USB_SUCCESS(result))
        printf("Write FW Block %d failed (%s)!\n", Block, usb_strerror());

    return result;
}


int DFUGetStatus(usb_dev_handle *dev, unsigned char *status)
{
    int result;

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

unsigned char DFUGetState(usb_dev_handle* dev, unsigned char *state)
{
    int result;

    result = usb_control_msg(dev,
                    USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, /* Request Type 
*/
                    0x05, /* Request */
                    0, /* Value */
                    0, /* Index */
                    state, /* Buffer */
                    1, /* Size */
                    5);

    return result;
}


int WriteExtendedFW(usb_dev_handle *dev, unsigned char *Buffer, unsigned long BufLen)
{
    int status;
    unsigned short i, bytes, offset;

    printf("Writing Extended Firmware...\n");

    offset = 0;
    bytes = 1024;

    for (i = 0; i <= (BufLen/1024); i++)
    {
        if (BufLen - offset < 1024)
            bytes = BufLen - offset;

        printf("Writing Extended Firmware Block %d (%d bytes)\n", i, bytes);

        status = usb_control_msg(dev,
                     USB_TYPE_VENDOR | USB_DIR_OUT, /* Request Type */
                     0x0e, /* Request */
                     0x0802, /* Value */
                     i, /* Index */
                     Buffer + offset, /* Buffer */
                     bytes, /* Size */
                     5);

        if (!USB_SUCCESS(status))
        {
            printf("Failed to write Extended Firmware Block %d (%d bytes)\n",
                   i, bytes);
            /* Do Adapter reset */
            return FALSE;
        }
        offset += bytes; 
    }

    status = usb_control_msg(dev,
                    USB_TYPE_VENDOR | USB_DIR_OUT, /* Request Type */
                    0x0e, /* Request */
                    0x0802, /* Value */
                    i, /* Index */
                    NULL, /* Buffer */
                    0, /* Size */
                    5);

    if (!USB_SUCCESS(status))
    {
        printf("Failed to Write Extended Firmware Block %d\n", (i+1));
        /* Do Adapter reset */
        return FALSE;
    }

    return TRUE;
}


int DownloadFirmware(usb_dev_handle* 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)
    {
        printf("FW Buffer length invalid!\n");
        return STATUS_UNSUCCESSFUL;
    }

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

    do
    {
        if (NeedDFUState)
        {
            status = DFUGetState(dev, &DfuState);
            if (!USB_SUCCESS(status))
            {
                printf("DFU: Failed to get DFU state...\n");
                return STATUS_UNSUCCESSFUL;
            }
            NeedDFUState = FALSE;
        }

        switch (DfuState)
        {
            case STATE_DFU_DOWNLOAD_SYNC:
                printf("STATE_DFU_DOWNLOAD_SYNC\n");
                if (USB_SUCCESS(DFUGetStatus(dev, DfuStatusBuffer)))
                {
                    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:
                printf("STATE_DFU_DOWNLOAD_BUSY\n");
                NeedDFUState = TRUE;

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

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

                if (DfuBytesLeft <= DFU_PACKETSIZE)
                    DfuBlockBytes = DfuBytesLeft;
                else
                    DfuBlockBytes = DFU_PACKETSIZE;

                if (USB_SUCCESS(status))
                {
                    DfuBytesLeft -= DfuBlockBytes;
                    status = DFUDownload(dev, DfuBuffer + DfuBufferOffset, 
                                          DfuBlockBytes, DfuBlockCnt);
                    DfuBufferOffset += DfuBlockBytes;
                    DfuBlockCnt++;
                }
                NeedDFUState = TRUE;
                break;
            case STATE_DFU_MANIFEST_SYNC:
                printf("STATE_DFU_MANIFEST_SYNC\n");

                status = DFUGetStatus(dev, DfuStatusBuffer);

                if (USB_SUCCESS(status))
                {
                    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)
                        printf("DFU: Resetting device\n");
                    else
                        printf("DFU: In progress\n");

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

    return status;
}

unsigned long ReadFirmware(char *file, unsigned char **buffer)
{
    struct stat filestat;
    unsigned long fwsize;
    unsigned long bytesread;
    int fd;

    stat(file, &filestat);
    fwsize = filestat.st_size;

    if ((*buffer = malloc(fwsize)) == NULL)
    {
        printf("Unable to allocate %ld bytes!\n", fwsize);
        return -ENOMEM;
    }

    /* Open the firmware file */
    if ((fd = open(file, O_RDONLY)) < 0)
    {
        printf("Unable to open %s!\n", file);
        free(*buffer);
        return -errno;
    }

    /* Read the firmware file into the buffer */
    bytesread = read(fd, *buffer, fwsize);

    /* Close the firmware file */
    close(fd);

    if (bytesread < fwsize) 
    {
        printf("Short read in %s!\n", file);
        free(*buffer);
        return -EINVAL;
    }

    return fwsize;
}    
    
int DeviceFirmwareUpgrade(int idVendor, int idProduct, char *filename)
{
    struct usb_bus *bus;
    struct usb_device *device;
    usb_dev_handle* dev;
    unsigned char *firmware = NULL;
    unsigned long fwsize;

    /* Initialize USB subsystem */
    usb_init();

    /* Enumerate all busses */
    usb_find_busses();

    /* Enumerate all devices */
    usb_find_devices();

    /* Find the right Vendor/Product pair */
    for (bus = usb_busses; bus; bus = bus->next)
        for (device = bus->devices; device; device = device->next)
            if ((device->descriptor.idVendor  == idVendor) &&
                (device->descriptor.idProduct == idProduct))
            {
                /* Read the Firmware file */
                fwsize = ReadFirmware(filename, &firmware);

                /* Open the USB device */
                dev = usb_open(device);

                /* Claim the interface */
                usb_claim_interface(dev, 0);

                /* Download the firmware to the device */
                DownloadFirmware(dev, firmware, fwsize);

                /* Release the interface */
                usb_release_interface(dev, 0);

                /* Close the USB device */
                usb_close(dev);
            }

    return -ENODEV;
}

/* Main */


int main(void)
{
    DeviceFirmwareUpgrade(0x03eb, 0x7603, "internal.rom");

    return 0;
}

Reply via email to