[Prism54-devel] prism54-usb firmware loading

Helmar Spangenberg hspangenberg@frey.de
Tue, 1 Jun 2004 07:00:25 +0200


--Boundary-00=_p1AvACddcg0Aur4
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Hello Feyd,
since I am not willing to accept that Linux can't control devices Windows is 
able to (I am rather angry to have bought USB dongles for WLAN which do not 
work with Linux), I spent the last 2 days digging through the Linux USB code 
to find the reason for the HC-crash during firmware upload.

I finally came to the conclusion that the problem may be related to the 
userspace/kernelspace issue. I added a "memcpy" to fetch chunks of the 
firmware into local memory of prism54u before it is sent via usb_bulk_msg. 
This seemed to help. At least, I found the complete firmware code printed out 
in the kernel log messages; and even the LED on the dongle showed a reaction 
(at least sometimes; usually it is turned off, but once it began to blink 
like under Windows when it is seeking for an access point).

I will append the modified usb_init.c (which originally was downloaded from 
the prism54 CVS); I marked my changes with "hks". Since I use those Medion 
dongles, I included their id's in the p54u_table, too.

Unfortunately, it is now much more complicated to rmmod the driver, since now 
the network system claims it..... ;-(.

Helmar

--Boundary-00=_p1AvACddcg0Aur4
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="usb_init.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="usb_init.c"

/*
 * Prism54 USB driver
 */

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <linux/completion.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/usb.h>
#include <linux/pci.h>
#include <linux/firmware.h>
#include <asm/uaccess.h>
#include "prism54_usb.h"
#include "isl_38xx.h"

#ifdef CONFIG_USB_DEBUG
int debug = 2;
#else
int debug;
#endif

int load_fw;
static const char driver_name[] = "prism54_usb";

static const struct usb_device_id p54u_table[] = {
	{USB_DEVICE(0x5041, 0x2234)},	/* Linksys WUSB54G */
	{USB_DEVICE(0x0cde, 0x0006)},	/* Linksys Medion (hks) */
	{}
};

static const struct p54u_pipe_desc p54u_pipes[] = {
	{ P54U_PIPE_DATA,		USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_MGMT,		USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_BRG,		USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_DEV,		USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_DATA | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_MGMT | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_3    | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_4    | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_BRG  | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_DEV  | USB_DIR_IN,	USB_ENDPOINT_XFER_BULK },
	{ P54U_PIPE_INT  | USB_DIR_IN,	USB_ENDPOINT_XFER_INT  },
};

static const char p54u_mgmt_0c[16] = {
	0x6c, 0x06, 0x02, 0x00, 0x0c, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

static const char p54u_mgmt_30[16] = {
	0x6c, 0x06, 0x02, 0x00, 0x30, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

MODULE_DESCRIPTION("Prism54 USB Driver");
MODULE_AUTHOR("Feyd <feyd@seznam.cz>");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(usb, p54u_table);
MODULE_PARM(debug, "i");
MODULE_PARM(load_fw, "i");
MODULE_PARM_DESC(debug, "Debug enabled or not");
MODULE_PARM_DESC(load_fw, "Load firmware on probe (1) or open (0)");

static int p54u_reset_usb(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	int err = 0;
	u32 reg;
	
//	return 0;

	p54u_info("%s: Reset USB device.\n", netdev->name);
	err = usb_reset_device(usbdev);
	if(err) {
		p54u_err("%s: Reset USB device failed.\n", netdev->name);
		return err;
	}
//	p54u_mdelay(200);
	p54u_info("%s: Reset USB device done.\n", netdev->name);
	
	return 0;
	
    do_err:
	p54u_err("%s: Reset USB device failed.\n", netdev->name);
	return err;
}

static void p54u_free_pipe(struct net_device *netdev, struct p54u_pipe *pipe)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	int i;

	p54u_info("Freeing pipe.\n");
	
	if(!pipe->buf) {
		p54u_info("pipe->buf == NULL\n");
		goto do_free_urb;
	}

	if(!pipe->urb) {
		p54u_info("pipe->urb == NULL\n");
		goto do_free_size;
	}

	if(!pipe->size) {
		p54u_info("pipe->size == NULL\n");
		goto do_clear;
	}
	
	for(i = 0; i < pipe->len; i++) {
		if(pipe->urb[i]) {
			/* FIXME: unlink the busy one */
			if(pipe->buf[i]) {
				usb_buffer_free(usbdev, pipe->size[i], pipe->buf[i], pipe->urb[i]->transfer_dma);
			} else {
				p54u_info("pipe->buf[%i] == NULL\n", i);

			}
			usb_free_urb(pipe->urb[i]);
		} else {
			p54u_info("pipe->urb[%i] == NULL\n", i);
		}
	}

	kfree(pipe->buf);
	
    do_free_urb:
	if(pipe->urb) {
	    kfree(pipe->urb);
	} else {
		p54u_info("pipe->urb == NULL\n");
	}
	
    do_free_size:
    	if(pipe->size) {
		kfree(pipe->size);
	} else {
		p54u_info("pipe->size == NULL\n");
	}
	
    do_clear:
	p54u_info("Clear the memory.\n");

	memset(pipe, 0, sizeof(*pipe));
	
	p54u_info("Freeing pipe done.\n");
	
	return;
}

static void p54u_free_buffers(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;

	p54u_free_pipe(netdev, &p54u->data_tx);
	p54u_free_pipe(netdev, &p54u->data_rx);
	p54u_free_pipe(netdev, &p54u->mgmt_tx);
	p54u_free_pipe(netdev, &p54u->mgmt_rx);
	p54u_free_pipe(netdev, &p54u->int_rx);
	
	return;
}

static int p54u_alloc_pipe(struct net_device *netdev, struct usb_endpoint_descriptor *desc, struct p54u_pipe *pipe, usb_complete_t callback)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	int err, i, p, e;
	
	if(!pipe)
		return 0;

	p54u_info("%s: Allocate pipe %02x.\n", netdev->name, desc->bEndpointAddress);
	
	pipe->pkt_size = desc->wMaxPacketSize;
	pipe->type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
	pipe->addr = desc->bEndpointAddress;
	pipe->len = P54U_QUEUE_LEN;
	
	pipe->size = kmalloc(sizeof(*pipe->size) * pipe->len, GFP_KERNEL);
	if(!pipe->size) {
		p54u_info("Failed to allocate pipe->size\n");
		goto do_enomem;
	} else {
		p54u_info("pipe->size == %p\n", pipe->size);
	}
	memset(pipe->size, 0, sizeof(*pipe->size) * pipe->len);


	pipe->urb = kmalloc(sizeof(*pipe->urb) * pipe->len, GFP_KERNEL);
	if(!pipe->urb) {
		p54u_info("Failed to allocate pipe->urb\n");
		goto do_enomem;
	} else {
		p54u_info("pipe->urb == %p\n", pipe->urb);
	}
	memset(pipe->urb, 0, sizeof(*pipe->urb) * pipe->len);
	
	pipe->buf = kmalloc(sizeof(*pipe->buf) * pipe->len, GFP_KERNEL);
	if(!pipe->buf) {
		p54u_info("Failed to allocate pipe->buf\n");
		goto do_enomem;
	} else {
		p54u_info("pipe->buf == %p\n", pipe->buf);
	}
	memset(pipe->buf, 0, sizeof(*pipe->buf) * pipe->len);
	
	p54u_info("%s: Allocate buffers.\n", netdev->name);
	
	for(i = 0; i < pipe->len; i++) {
		pipe->size[i] = P54U_MAX_FRAME_SIZE;
		if(pipe->type == USB_ENDPOINT_XFER_INT) {
			pipe->size[i] = 4;
		}
		
		p54u_info("pipe->size [%i] = %i\n", i, pipe->size[i]);
		
		pipe->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
		if(!pipe->urb[i]) {
			p54u_info("Failed to allocate pipe->urb[%i]\n", i);
			goto do_enomem;
		} else {
			p54u_info("pipe->urb[%i] == %p\n", i, pipe->urb[i]);
		}
		pipe->urb[i]->transfer_flags = (URB_NO_TRANSFER_DMA_MAP | URB_ASYNC_UNLINK);

		pipe->buf[i] = usb_buffer_alloc(usbdev, pipe->size[i], GFP_KERNEL, &pipe->urb[i]->transfer_dma);
		if(!pipe->buf[i]) {
			p54u_info("Failed to allocate pipe->buf[%i]\n", i);
			goto do_enomem;
		} else {
			p54u_info("pipe->buf[%i] == %p\n", i, pipe->buf[i]);
		}
		
		e = pipe->addr & USB_ENDPOINT_NUMBER_MASK;
		p54u_info("pipe->addr = %i\n", e);
//		e = pipe->addr;
		switch(pipe->type) {
		case USB_ENDPOINT_XFER_BULK:
			p = pipe->addr & USB_DIR_IN ? usb_rcvbulkpipe(usbdev, e) : usb_sndbulkpipe(usbdev, e);
			usb_fill_bulk_urb(pipe->urb[i], usbdev, p, pipe->buf[i], pipe->size[i], callback, netdev);
		case USB_ENDPOINT_XFER_INT:
			p = usb_rcvintpipe(usbdev, e);
			usb_fill_int_urb(pipe->urb[i], usbdev, p, pipe->buf[i], pipe->size[i], callback, netdev, 6);
//			usb_fill_int_urb(pipe->urb[i], usbdev, p, pipe->buf[i], pipe->size[i], callback, netdev, HZ / 4);
//			p = pipe->addr & USB_DIR_IN ? usb_rcvbulkpipe(usbdev, e) : usb_sndbulkpipe(usbdev, e);
//			usb_fill_bulk_urb(pipe->urb[i], usbdev, p, pipe->buf[i], pipe->size[i], callback, netdev);
		}
		
		init_completion(&pipe->comp);
	}
	
	p54u_info("%s: Allocate pipe done.\n", netdev->name);
	return 0;
	
    do_enomem:
	p54u_info("%s: Not enough memory.\n", netdev->name);
	err = -ENOMEM;
    do_err:
	p54u_info("%s: Allocate pipe failed.\n", netdev->name);
	return err;
}

static int p54u_alloc_buffers(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	struct usb_interface *interface = p54u->interface;
	struct usb_host_interface *iface_desc = &interface->altsetting[0];
	struct usb_endpoint_descriptor *desc;
	struct p54u_pipe *pipe;
	int err = 0, i;
	
	p54u_info("%s: Setup USB structures.\n", netdev->name);
	
	for(i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
		desc = &iface_desc->endpoint[i].desc;
		switch(desc->bEndpointAddress) {
		case P54U_PIPE_DATA:
			err = p54u_alloc_pipe(netdev, desc, &p54u->data_tx, NULL);
			break;
		case P54U_PIPE_MGMT:
			err = p54u_alloc_pipe(netdev, desc, &p54u->mgmt_tx, NULL);
			break;
		case P54U_PIPE_DATA | USB_DIR_IN:
			err = p54u_alloc_pipe(netdev, desc, &p54u->data_rx, p54u_data_rx_cb);
			break;
		case P54U_PIPE_MGMT | USB_DIR_IN:
			err = p54u_alloc_pipe(netdev, desc, &p54u->mgmt_rx, p54u_mgmt_rx_cb);
			break;
		case P54U_PIPE_INT | USB_DIR_IN:
			err = p54u_alloc_pipe(netdev, desc , &p54u->int_rx, p54u_int_rx_cb);
			break;
		default:
			break;
		}
		if(err)
			goto do_err;
	}
	p54u_info("%s: Setup USB structures successful.\n", netdev->name);

	return 0;
	
    do_err:
    	p54u_info("%s: Setup USB structures failed: %i\n", netdev->name, err);
	p54u_free_buffers(netdev);
	
	return err;
}

static int p54u_reset_dev(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	u32 reg, revision;
	int err;
	
	p54u_info("%s: Reset device.\n", netdev->name);
	
	/* Bridge */
	/* Reset the usb<->pci bridge */
	p54u_dbg("%s: Reset bridge\n", netdev->name);
	reg = p54u_brg_readl(netdev, NET2280_GPIOCTL);
	//reg = 0x3e;
	//reg |= 0x3e;
	p54u_brg_writel(netdev, NET2280_GPIOCTL, (reg | P54U_BRG_POWER_DOWN) & ~P54U_BRG_POWER_UP);
	if(p54u->err)
		goto do_err;
	p54u_mdelay(100);
	p54u_brg_writel(netdev, NET2280_GPIOCTL, (reg | P54U_BRG_POWER_UP) & ~P54U_BRG_POWER_DOWN);
	if(p54u->err)
		goto do_err;
	p54u_dbg("%s: Reset bridge done\n", netdev->name);
	p54u_mdelay(100);
	
	/* Magic */
	p54u_dbg("%s: Magic 1\n", netdev->name);
	p54u_brg_writel(netdev, NET2280_DEVINIT, NET2280_CLK_30Mhz | NET2280_PCI_ENABLE | NET2280_PCI_SOFT_RESET);
	if(p54u->err)
		goto do_err;
	p54u_dbg("%s: Magic 1 done\n", netdev->name);
	p54u_mdelay(20);
	
	/* Enable mmio and busmaster on the bridge */
	p54u_dbg("%s: Setup bridge pci resources\n", netdev->name);
	p54u_pcicfg_brg_writew(netdev, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER));
	
	/* Set base address 0 */
	p54u_pcicfg_brg_writel(netdev, PCI_BASE_ADDRESS_0, NET2280_BASE);
	
	/* Set PCI_STATUS_REC_MASTER_ABORT) (why?) */
	reg = p54u_pcicfg_brg_readw(netdev, PCI_STATUS);
	p54u_pcicfg_brg_writew(netdev, PCI_STATUS, (reg | PCI_STATUS_REC_MASTER_ABORT));
	
	/* Read revision? */
	revision = p54u_brg_readl(netdev, NET2280_RELNUM);
	p54u_dbg("%s: Setup bridge pci resources done\n", netdev->name);
	
	/* Magic */
	p54u_dbg("%s: Magic 2\n", netdev->name);
	p54u_brg_writel(netdev, NET2280_EPA_RSP, NET2280_CLEAR_NAK_OUT_PACKETS_MODE);
	p54u_brg_writel(netdev, NET2280_EPC_RSP, NET2280_CLEAR_NAK_OUT_PACKETS_MODE);
	p54u_dbg("%s: Magic 2 done\n", netdev->name);
	
	/* Set base address 2 */
	p54u_dbg("%s: Setup bridge base addr 2\n", netdev->name);
	p54u_pcicfg_brg_writel(netdev, PCI_BASE_ADDRESS_2, NET2280_BASE2);
	p54u_dbg("%s: Setup bridge base addr 2 done\n", netdev->name);
	
	/* Device */
	/* Enable mmio and busmaster on the device */
	p54u_dbg("%s: Setup device pci resources\n", netdev->name);
	p54u_pcicfg_dev_writew(netdev, PCI_COMMAND | 0x10000, (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER));
//	p54u_pcicfg_dev_writew(netdev, PCI_COMMAND, (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER));
	
	/* Set TRDY_TIMEOUT and RETRY_TIMEOUT to 0 */
	p54u_pcicfg_dev_writew(netdev, P54U_TRDY_TIMEOUT | 0x10000, 0);
//	p54u_pcicfg_dev_writew(netdev, P54U_TRDY_TIMEOUT, 0);
	
	/* Set base address 0 */
	p54u_pcicfg_dev_writel(netdev, PCI_BASE_ADDRESS_0 | 0x10000, P54U_DEV_BASE);
//	p54u_pcicfg_dev_writel(netdev, PCI_BASE_ADDRESS_0, P54U_DEV_BASE);
	p54u_dbg("%s: Setup device pci resources done\n", netdev->name);
	
	/* Magic */
	p54u_dbg("%s: Magic 3\n", netdev->name);
	p54u_brg_writel(netdev, NET2280_USBIRQENB1, 0);
//	p54u_brg_writel(netdev, NET2280_USBIRQENB1, NET2280_PCI_INTA_INTERRUPT_ENABLE | NET2280_USB_INTERRUPT_ENABLE);
	p54u_brg_writel(netdev, NET2280_IRQSTAT1, NET2280_PCI_INTA_INTERRUPT);
	p54u_dbg("%s: Magic 3 done\n", netdev->name);
	
	/* Assert wakeup */
	p54u_dbg("%s: Assert device\n", netdev->name);
	p54u_dev_writel(netdev, ISL38XX_DEV_INT_REG,  ISL38XX_DEV_INT_WAKEUP);
	if(p54u->err)
		goto do_err;
	p54u_mdelay(20);
	
	/* Assert something unknown */
	p54u_dev_writel(netdev, ISL38XX_DEV_INT_REG,  0x20);
	if(p54u->err)
		goto do_err;
	p54u_dbg("%s: Assert device done\n", netdev->name);
	p54u_mdelay(20);
	
	/* Reset the device */
	p54u_dbg("%s: Reset device\n", netdev->name);
	reg = p54u_dev_readl(netdev, ISL38XX_CTRL_STAT_REG);
//	reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT);
	reg &= ~(ISL38XX_CTRL_STAT_RESET | ISL38XX_CTRL_STAT_RAMBOOT | ISL38XX_CTRL_STAT_CLKRUN);
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg);
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg | ISL38XX_CTRL_STAT_RESET);
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg);

	if(p54u->err)
		goto do_err;
	p54u_dbg("%s: Reset device done\n", netdev->name);
	p54u_mdelay(100);
	p54u_dbg("%s: Disable irq\n", netdev->name);
	p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0);
//	p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0x00004004);
	p54u_dbg("%s: Disable irq done\n", netdev->name);
	
//	p54u_mdelay(50);
	
	/* Ack the irqs */
	p54u_dbg("%s: Ack irq\n", netdev->name);
	reg = p54u_dev_readl(netdev, ISL38XX_INT_IDENT_REG);
	p54u_dev_writel(netdev, ISL38XX_INT_ACK_REG, reg);
	if(p54u->err)
		goto do_err;
	p54u_dbg("%s: Ack done\n", netdev->name);

	/* Magic */
	p54u_dbg("%s: Magic 4\n", netdev->name);
	p54u_brg_writel(netdev, NET2280_EPA_STAT, NET2280_FIFO_FLUSH);
	p54u_brg_writel(netdev, NET2280_EPB_STAT, NET2280_FIFO_FLUSH);
	p54u_brg_writel(netdev, NET2280_EPC_STAT, NET2280_FIFO_FLUSH);
	p54u_brg_writel(netdev, NET2280_EPD_STAT, NET2280_FIFO_FLUSH);
	p54u_brg_writel(netdev, NET2280_EPA_STAT, NET2280_FIFO_FLUSH);
	p54u_dbg("%s: Magic 4 done\n", netdev->name);

	p54u_info("%s: Reset device done.\n", netdev->name);
	
	return 0;
	
    do_err:
    	p54u_err("%s: Reset error: %i\n", netdev->name, p54u->err);
    	err = p54u->err;
	p54u->err = 0;
	return err;
}

static int p54u_load_firmware(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	unsigned int pipe;
	u32 reg;
	void *data;
	size_t len;
	int err, i, j, k, l;
	u64 t, t1, t2;

	char *rcv;
	
unsigned char hksdata[512];	// temporary buffer in kernel space (hks)
	p54u_info("%s: Load firmware.\n", netdev->name);


	/* Firmware */
	
	pipe = usb_sndbulkpipe(usbdev, P54U_PIPE_DATA);
		
	data = p54u->fw_entry->data;
	i = p54u->fw_entry->size;
	j = 0;
	
	while(i > 0) {
		len = i > P54U_FW_BLOCK ? P54U_FW_BLOCK : i;
		t1 =  p54u_time();
memcpy(hksdata,data,len);	// Copying data from userland? (hks)
		p54u->err = usb_bulk_msg(usbdev, pipe, hksdata, len, &len, HZ);
		t2 =  p54u_time();
		t = t2 - t1;
		
		p54u_data_debug(netdev, P54U_PIPE_DATA, data, len);
		
		if(p54u->err) {
			p54u_dbg("%s: Error writing firmware block: writen %i of %i.\n", netdev->name, j, p54u->fw_entry->size);
			goto do_release;
		}
		
		data += P54U_FW_BLOCK;
		i -= P54U_FW_BLOCK;
		
		/* Magic */
		p54u_dev_writel(netdev, ISL38XX_DIR_MEM_BASE_REG, 0xc0000f00);
		p54u_dev_writel(netdev, 0x1020, 0);
		p54u_dev_writel(netdev, 0x1020, 1);
		p54u_dev_writel(netdev, 0x1024, len);
		p54u_dev_writel(netdev, 0x1028, j | 0x00020000);
		p54u_dev_writel(netdev, 0x0060, 0x20000000);
		p54u_dev_writel(netdev, 0x0064, i > 0 ? 0x80 : 0x2e);
		p54u_dev_writel(netdev, 0x0068, 4);
		reg = p54u_dev_readl(netdev, 0x102c);
		if(i > 0)
			p54u_brg_writel(netdev, NET2280_EPA_STAT, NET2280_FIFO_FLUSH);
		if(p54u->err) {
			p54u_dbg("%s: Error updating registers: writen %i of %i.\n", netdev->name, j, p54u->fw_entry->size);
			goto do_release;
		}
		j += P54U_FW_BLOCK;

	}
	
	reg = p54u_dev_readl(netdev, ISL38XX_CTRL_STAT_REG);
//	reg |= ISL38XX_CTRL_STAT_RAMBOOT | ISL38XX_CTRL_STAT_CLKRUN;
	reg |= ISL38XX_CTRL_STAT_RAMBOOT;
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg);
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg | ISL38XX_CTRL_STAT_RESET);
	p54u_dev_writel(netdev, ISL38XX_CTRL_STAT_REG, reg);
	
	if(p54u->err) {
		p54u_dbg("%s: Error latching reset: %i\n", netdev->name, p54u->err);
	}

	p54u_info("%s: Load firmware done.\n", netdev->name);
	return 0;
	
    do_release:
    do_err:
    	p54u_err("%s: Load error: %i\n", netdev->name, p54u->err);
    	err = p54u->err;
	p54u->err = 0;
	return err;
}

int p54u_setup_dev(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	struct p54u_mgmt_tx *tx1, *tx2;
	u32 reg, rst = 0;
	int i, j, k, pending, err;

	p54u->err = usb_submit_urb(p54u->int_rx.urb[0], GFP_KERNEL);
	if(p54u->err) {
		p54u_dbg("%s: Error submit int 0: %i\n", netdev->name, p54u->err);
	} else {
		p54u_dbg("%s: Submit int 0 ok.\n", netdev->name);
	}

	p54u->err = usb_submit_urb(p54u->data_rx.urb[0], GFP_KERNEL);
	if(p54u->err) {
		p54u_dbg("%s: Error submit data: %i\n", netdev->name, p54u->err);
	} else {
		p54u_dbg("%s: Submit data ok.\n", netdev->name);
	}
	
	p54u->err = usb_submit_urb(p54u->mgmt_rx.urb[0], GFP_KERNEL);
	if(p54u->err) {
		p54u_dbg("%s: Error submit mgmt: %i\n", netdev->name, p54u->err);
	} else {
		p54u_dbg("%s: Submit mgmt ok.\n", netdev->name);
	}

	p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0x80004004);
	p54u_brg_writel(netdev, NET2280_IRQSTAT1, NET2280_PCI_INTA_INTERRUPT);
	p54u_brg_writel(netdev, NET2280_USBIRQENB1, NET2280_PCI_INTA_INTERRUPT_ENABLE | NET2280_USB_INTERRUPT_ENABLE);
//	p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0xffffffff);
//	p54u_dev_writel(netdev, ISL38XX_DEV_INT_REG, ISL38XX_DEV_INT_RESET | ISL38XX_DEV_INT_WAKEUP);
	p54u_dev_writel(netdev, ISL38XX_DEV_INT_REG, ISL38XX_DEV_INT_RESET);
	
	
	do {
		pending = p54u_wait_int(netdev);
		
		p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0);
/*		while((reg = p54u_dev_readl(netdev, ISL38XX_INT_IDENT_REG))) {
			p54u_info("int: %08x\n", reg);
			p54u_dev_writel(netdev, ISL38XX_INT_ACK_REG, reg);
		}
*/		reg = p54u_dev_readl(netdev, ISL38XX_INT_IDENT_REG);
		p54u_dev_writel(netdev, ISL38XX_INT_ACK_REG, reg);
		
		p54u_brg_writel(netdev, NET2280_IRQSTAT1, NET2280_PCI_INTA_INTERRUPT);
		p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0x80004004);
//		p54u_dev_writel(netdev, ISL38XX_INT_EN_REG, 0xffffffff);
//		reg = p54u_brg_readl(netdev, NET2280_IRQSTAT1);
		
		err = usb_submit_urb(p54u->int_rx.urb[0], GFP_KERNEL);
		if(err) {
			p54u_info("int resubmit failed  %i\n", err);
			break;
		}
	} while(pending);
	
	
	/* Send the initial data to 0x02 pipe */
	
	memset(p54u->mgmt_tx.buf[0], 0, p54u->mgmt_tx.size[0]);
	memset(p54u->mgmt_tx.buf[1], 0, p54u->mgmt_tx.size[1]);
	
	tx1 = p54u->mgmt_tx.buf[0];
	tx2 = p54u->mgmt_tx.buf[1];
	
	tx1->magic1 = 0x0002066c;
	tx1->magic2 = 0x0002040c;
	tx1->magic3 = 0;
	tx1->pos = 0;
	tx1->len = 0;

	tx2->magic1 = 0x04008000;
	tx2->magic2 = 0x813376b0;
	tx2->magic3 = 0x0000000c;
	tx2->pos = 0;
	tx2->len = P54U_INIT_BLOCK;
	
	for(i = 0; i < 1; i++) {
		p54u_bulk_msg(netdev, P54U_PIPE_MGMT, tx1, 16);
		p54u_dev_writel(netdev, ISL38XX_DEV_INT_REG, 0x80);
		p54u_bulk_msg(netdev, P54U_PIPE_MGMT, tx2, P54U_INIT_BLOCK + 16);
//		p54u_bulk_msg(netdev, P54U_PIPE_MGMT, tx2, 512);
		tx2->pos += P54U_INIT_BLOCK;
		p54u_mdelay(200);
	}
	
	return p54u->err;
}

int p54u_boot(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	int err;
	
	p54u_info("%s: Boot the device.\n", netdev->name);

	p54u_dbg("%s: Request firmware.\n", netdev->name);
	
	err = request_firmware(&p54u->fw_entry, P54U_IMAGE_FILE, netdev->class_dev.dev);
	if(err) {
		p54u_err("%s: Request firmware failed: %i\n", netdev->name, err);
		return err;
	}
		
	p54u_dbg("%s: Request firmware done: len = %i.\n", netdev->name, p54u->fw_entry->size);
	
	p54u->time = get_jiffies_64();
	
	err = p54u_reset_usb(netdev);
	
	if(!err)
		err = p54u_alloc_buffers(netdev);
	if(!err)
		err = p54u_reset_dev(netdev);
	if(!err)
		err = p54u_load_firmware(netdev);
	if(!err)
		err = p54u_setup_dev(netdev);
	
	release_firmware(p54u->fw_entry);
	memcpy(netdev->dev_addr, dummy_mac, ETH_ALEN);
	p54u->running = 1;
	
	if(err) {
		p54u_err("%s: Boot the device failed: %i\n", netdev->name, err);
		return err;
	}
	
	p54u_info("%s: Boot the device done.\n", netdev->name);
	return 0;
}

int p54u_shutdown(struct net_device *netdev)
{
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;
	u32 reg;

	p54u_dbg("%s: Shutting down the device.\n", netdev->name);
	
	p54u_free_buffers(netdev);
	
//	reg = p54u_brg_readl(netdev, P54U_BRG_CTRL_REG);
//	p54u_brg_writel(netdev, P54U_BRG_CTRL_REG, (reg | P54U_BRG_POWER_DOWN) & ~P54U_BRG_POWER_UP);
//	p54u_brg_writel(netdev, P54U_BRG_CTRL_REG, 0x3e);
	p54u->running = 0;
	
	p54u_dbg("%s: Shutdown complete.\n", netdev->name);
	
	return 0;
}

static int p54u_validate_device(struct usb_interface *interface, const struct usb_device_id *id)
{
	/* See if the device offered us matches what we can accept */

	return 0;
}

static int p54u_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
	struct usb_device *usbdev = interface_to_usbdev(interface);
	struct net_device *netdev;
	struct p54u *p54u;
	
	
	p54u_dbg("Prism54 USB Device Probe (Device number:%d): 0x%4.4x:0x%4.4x:0x%4.4x", usbdev->devnum, (int) usbdev->descriptor.idVendor, (int) usbdev->descriptor.idProduct, (int) usbdev->descriptor.bcdDevice);
	p54u_dbg("Device at %p", usbdev);
	p54u_dbg("Descriptor length: %x type: %x", (int) usbdev->descriptor.bLength, (int) usbdev->descriptor.bDescriptorType);
	
	if(p54u_validate_device(interface, id))
		return -ENODEV;
	
	netdev = alloc_etherdev(sizeof(*p54u));
	if (!netdev) {
		p54u_err("Failed to allocate netdevice.");
		return -ENOMEM;
	}
	
	SET_MODULE_OWNER(netdev);
	SET_NETDEV_DEV(netdev, &interface->dev);
	
	usb_set_intfdata(interface, netdev);
	
	p54u = netdev_priv(netdev);
	p54u->interface = interface;
	p54u->usbdev = usbdev;
	p54u->netdev = netdev;
	
	init_MUTEX (&p54u->disconnect_sem);
	memcpy(netdev->dev_addr, dummy_mac, ETH_ALEN);

	if(load_fw)
		/* Dont error if it fails, give it chance on open. */
		p54u_boot(netdev);

	if(p54u_setup_net(netdev)) {
		p54u_err("Failed to setup netdevice.");
		goto do_cleanup;
	}
	
	if(register_netdev(netdev)) {
		p54u_err("Failed to register netdevice.");
		goto do_cleanup;
	}
	
	p54u_info("Prism54 USB device now attached to %s", netdev->name);
	return 0;
	
    do_cleanup:
	p54u_free_buffers(netdev);
	usb_set_intfdata(interface, NULL);
	free_netdev(netdev);

	return -EIO;
}

static void p54u_disconnect(struct usb_interface *interface)
{
	struct net_device *netdev = usb_get_intfdata(interface);
	struct p54u *p54u = netdev_priv(netdev);
	struct usb_device *usbdev = p54u->usbdev;

	p54u_info("Prism54 USB device %s disconnecing\n", netdev->name);
	
	p54u_dbg("sem down\n");
	down(&p54u->disconnect_sem);
	p54u_dbg("sem down done\n");
	
	p54u->running = 0;
	
	p54u_dbg("Running = 0.\n");
	
	usb_set_intfdata(interface, NULL);
	
	p54u_dbg("Cleared interface data.\n");
	
	unregister_netdev(netdev);
	
	p54u_dbg("Unregistered netdevice.\n");
	
	p54u_free_buffers(netdev);
	
	p54u_dbg("Freed buffers.\n");
	
	free_netdev(netdev);
	
	p54u_dbg("Freed netdevice.\n");
		
	p54u_info("Disconnect complete.\n");

	p54u_dbg("sem up\n");
	up(&p54u->disconnect_sem);
	p54u_dbg("sem up done\n");
}

static struct usb_driver p54u_driver = {
	.owner = THIS_MODULE,
	.name = driver_name,
	.probe = p54u_probe,
	.disconnect = p54u_disconnect,
	.id_table = p54u_table,
};

static int __init p54u_init(void)
{
	int result;

	/* register this driver with the USB subsystem */
	result = usb_register(&p54u_driver);
	if (result) {
		p54u_err("usb_register failed. Error number %d", result);
		return result;
	}

	return 0;
}

static void __exit p54u_exit(void)
{
	/* deregister this driver with the USB subsystem */
	usb_deregister(&p54u_driver);
}

module_init(p54u_init);
module_exit(p54u_exit);


--Boundary-00=_p1AvACddcg0Aur4--