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);