Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / ft1000 / ft1000-usb / ft1000_hw.c
diff --git a/kernel/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c b/kernel/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
new file mode 100644 (file)
index 0000000..e6fb066
--- /dev/null
@@ -0,0 +1,1587 @@
+/* CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
+ *
+ *
+ * This file is part of Express Card USB Driver
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include "ft1000_usb.h"
+#include <linux/types.h>
+
+#define HARLEY_READ_REGISTER     0x0
+#define HARLEY_WRITE_REGISTER    0x01
+#define HARLEY_READ_DPRAM_32     0x02
+#define HARLEY_READ_DPRAM_LOW    0x03
+#define HARLEY_READ_DPRAM_HIGH   0x04
+#define HARLEY_WRITE_DPRAM_32    0x05
+#define HARLEY_WRITE_DPRAM_LOW   0x06
+#define HARLEY_WRITE_DPRAM_HIGH  0x07
+
+#define HARLEY_READ_OPERATION    0xc1
+#define HARLEY_WRITE_OPERATION   0x41
+
+#if 0
+#define JDEBUG
+#endif
+
+static int ft1000_submit_rx_urb(struct ft1000_info *info);
+
+static u8 tempbuffer[1600];
+
+#define MAX_RCV_LOOP   100
+
+/* send a control message via USB interface synchronously
+ *  Parameters:  ft1000_usb  - device structure
+ *               pipe - usb control message pipe
+ *               request - control request
+ *               requesttype - control message request type
+ *               value - value to be written or 0
+ *               index - register index
+ *               data - data buffer to hold the read/write values
+ *               size - data size
+ *               timeout - control message time out value
+ */
+static int ft1000_control(struct ft1000_usb *ft1000dev, unsigned int pipe,
+                         u8 request, u8 requesttype, u16 value, u16 index,
+                         void *data, u16 size, int timeout)
+{
+       int ret;
+
+       if ((ft1000dev == NULL) || (ft1000dev->dev == NULL)) {
+               pr_debug("ft1000dev or ft1000dev->dev == NULL, failure\n");
+               return -ENODEV;
+       }
+
+       ret = usb_control_msg(ft1000dev->dev, pipe, request, requesttype,
+                             value, index, data, size, timeout);
+
+       if (ret > 0)
+               ret = 0;
+
+       return ret;
+}
+
+/* returns the value in a register */
+int ft1000_read_register(struct ft1000_usb *ft1000dev, u16 *Data,
+                        u16 nRegIndx)
+{
+       int ret = 0;
+
+       ret = ft1000_control(ft1000dev,
+                            usb_rcvctrlpipe(ft1000dev->dev, 0),
+                            HARLEY_READ_REGISTER,
+                            HARLEY_READ_OPERATION,
+                            0,
+                            nRegIndx,
+                            Data,
+                            2,
+                            USB_CTRL_GET_TIMEOUT);
+
+       return ret;
+}
+
+/* writes the value in a register */
+int ft1000_write_register(struct ft1000_usb *ft1000dev, u16 value,
+                         u16 nRegIndx)
+{
+       int ret = 0;
+
+       ret = ft1000_control(ft1000dev,
+                            usb_sndctrlpipe(ft1000dev->dev, 0),
+                            HARLEY_WRITE_REGISTER,
+                            HARLEY_WRITE_OPERATION,
+                            value,
+                            nRegIndx,
+                            NULL,
+                            0,
+                            USB_CTRL_SET_TIMEOUT);
+
+       return ret;
+}
+
+/* read a number of bytes from DPRAM */
+int ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+                       u16 cnt)
+{
+       int ret = 0;
+
+       ret = ft1000_control(ft1000dev,
+                            usb_rcvctrlpipe(ft1000dev->dev, 0),
+                            HARLEY_READ_DPRAM_32,
+                            HARLEY_READ_OPERATION,
+                            0,
+                            indx,
+                            buffer,
+                            cnt,
+                            USB_CTRL_GET_TIMEOUT);
+
+       return ret;
+}
+
+/* writes into DPRAM a number of bytes */
+int ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+                        u16 cnt)
+{
+       int ret = 0;
+
+       if (cnt % 4)
+               cnt += cnt - (cnt % 4);
+
+       ret = ft1000_control(ft1000dev,
+                            usb_sndctrlpipe(ft1000dev->dev, 0),
+                            HARLEY_WRITE_DPRAM_32,
+                            HARLEY_WRITE_OPERATION,
+                            0,
+                            indx,
+                            buffer,
+                            cnt,
+                            USB_CTRL_SET_TIMEOUT);
+
+       return ret;
+}
+
+/* read 16 bits from DPRAM */
+int ft1000_read_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer,
+                       u8 highlow)
+{
+       int ret = 0;
+       u8 request;
+
+       if (highlow == 0)
+               request = HARLEY_READ_DPRAM_LOW;
+       else
+               request = HARLEY_READ_DPRAM_HIGH;
+
+       ret = ft1000_control(ft1000dev,
+                            usb_rcvctrlpipe(ft1000dev->dev, 0),
+                            request,
+                            HARLEY_READ_OPERATION,
+                            0,
+                            indx,
+                            buffer,
+                            2,
+                            USB_CTRL_GET_TIMEOUT);
+
+       return ret;
+}
+
+/* write into DPRAM a number of bytes */
+int ft1000_write_dpram16(struct ft1000_usb *ft1000dev, u16 indx, u16 value,
+                        u8 highlow)
+{
+       int ret = 0;
+       u8 request;
+
+       if (highlow == 0)
+               request = HARLEY_WRITE_DPRAM_LOW;
+       else
+               request = HARLEY_WRITE_DPRAM_HIGH;
+
+       ret = ft1000_control(ft1000dev,
+                            usb_sndctrlpipe(ft1000dev->dev, 0),
+                            request,
+                            HARLEY_WRITE_OPERATION,
+                            value,
+                            indx,
+                            NULL,
+                            0,
+                            USB_CTRL_SET_TIMEOUT);
+
+       return ret;
+}
+
+/* read DPRAM 4 words at a time */
+int fix_ft1000_read_dpram32(struct ft1000_usb *ft1000dev, u16 indx,
+                           u8 *buffer)
+{
+       u8 buf[16];
+       u16 pos;
+       int ret = 0;
+
+       pos = (indx / 4) * 4;
+       ret = ft1000_read_dpram32(ft1000dev, pos, buf, 16);
+
+       if (ret == 0) {
+               pos = (indx % 4) * 4;
+               *buffer++ = buf[pos++];
+               *buffer++ = buf[pos++];
+               *buffer++ = buf[pos++];
+               *buffer++ = buf[pos++];
+       } else {
+               pr_debug("DPRAM32 Read failed\n");
+               *buffer++ = 0;
+               *buffer++ = 0;
+               *buffer++ = 0;
+               *buffer++ = 0;
+       }
+
+       return ret;
+}
+
+
+/* Description: This function write to DPRAM 4 words at a time */
+int fix_ft1000_write_dpram32(struct ft1000_usb *ft1000dev, u16 indx, u8 *buffer)
+{
+       u16 pos1;
+       u16 pos2;
+       u16 i;
+       u8 buf[32];
+       u8 resultbuffer[32];
+       u8 *pdata;
+       int ret  = 0;
+
+       pos1 = (indx / 4) * 4;
+       pdata = buffer;
+       ret = ft1000_read_dpram32(ft1000dev, pos1, buf, 16);
+
+       if (ret == 0) {
+               pos2 = (indx % 4)*4;
+               buf[pos2++] = *buffer++;
+               buf[pos2++] = *buffer++;
+               buf[pos2++] = *buffer++;
+               buf[pos2++] = *buffer++;
+               ret = ft1000_write_dpram32(ft1000dev, pos1, buf, 16);
+       } else {
+               pr_debug("DPRAM32 Read failed\n");
+               return ret;
+       }
+
+       ret = ft1000_read_dpram32(ft1000dev, pos1, (u8 *)&resultbuffer[0], 16);
+
+       if (ret == 0) {
+               buffer = pdata;
+               for (i = 0; i < 16; i++) {
+                       if (buf[i] != resultbuffer[i])
+                               ret = -1;
+               }
+       }
+
+       if (ret == -1) {
+               ret = ft1000_write_dpram32(ft1000dev, pos1,
+                                          (u8 *)&tempbuffer[0], 16);
+               ret = ft1000_read_dpram32(ft1000dev, pos1,
+                                         (u8 *)&resultbuffer[0], 16);
+               if (ret == 0) {
+                       buffer = pdata;
+                       for (i = 0; i < 16; i++) {
+                               if (tempbuffer[i] != resultbuffer[i]) {
+                                       ret = -1;
+                                       pr_debug("Failed to write\n");
+                               }
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/* reset or activate the DSP */
+static void card_reset_dsp(struct ft1000_usb *ft1000dev, bool value)
+{
+       int status = 0;
+       u16 tempword;
+
+       status = ft1000_write_register(ft1000dev, HOST_INTF_BE,
+                                      FT1000_REG_SUP_CTRL);
+       status = ft1000_read_register(ft1000dev, &tempword,
+                                     FT1000_REG_SUP_CTRL);
+
+       if (value) {
+               pr_debug("Reset DSP\n");
+               status = ft1000_read_register(ft1000dev, &tempword,
+                                             FT1000_REG_RESET);
+               tempword |= DSP_RESET_BIT;
+               status = ft1000_write_register(ft1000dev, tempword,
+                                              FT1000_REG_RESET);
+       } else {
+               pr_debug("Activate DSP\n");
+               status = ft1000_read_register(ft1000dev, &tempword,
+                                             FT1000_REG_RESET);
+               tempword |= DSP_ENCRYPTED;
+               tempword &= ~DSP_UNENCRYPTED;
+               status = ft1000_write_register(ft1000dev, tempword,
+                                              FT1000_REG_RESET);
+               status = ft1000_read_register(ft1000dev, &tempword,
+                                             FT1000_REG_RESET);
+               tempword &= ~EFUSE_MEM_DISABLE;
+               tempword &= ~DSP_RESET_BIT;
+               status = ft1000_write_register(ft1000dev, tempword,
+                                              FT1000_REG_RESET);
+               status = ft1000_read_register(ft1000dev, &tempword,
+                                             FT1000_REG_RESET);
+       }
+}
+
+/* send a command to ASIC
+ *  Parameters:  ft1000_usb  - device structure
+ *               ptempbuffer - command buffer
+ *               size - command buffer size
+ */
+int card_send_command(struct ft1000_usb *ft1000dev, void *ptempbuffer,
+                     int size)
+{
+       int ret;
+       unsigned short temp;
+       unsigned char *commandbuf;
+
+       pr_debug("enter card_send_command... size=%d\n", size);
+
+       ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
+       if (ret)
+               return ret;
+
+       commandbuf = kmalloc(size + 2, GFP_KERNEL);
+       if (!commandbuf)
+               return -ENOMEM;
+       memcpy((void *)commandbuf + 2, ptempbuffer, size);
+
+       if (temp & 0x0100)
+               usleep_range(900, 1100);
+
+       /* check for odd word */
+       size = size + 2;
+
+       /* Must force to be 32 bit aligned */
+       if (size % 4)
+               size += 4 - (size % 4);
+
+       ret = ft1000_write_dpram32(ft1000dev, 0, commandbuf, size);
+       if (ret)
+               return ret;
+       usleep_range(900, 1100);
+       ret = ft1000_write_register(ft1000dev, FT1000_DB_DPRAM_TX,
+                                   FT1000_REG_DOORBELL);
+       if (ret)
+               return ret;
+       usleep_range(900, 1100);
+
+       ret = ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
+
+#if 0
+       if ((temp & 0x0100) == 0)
+               pr_debug("Message sent\n");
+#endif
+       return ret;
+}
+
+/* load or reload the DSP */
+int dsp_reload(struct ft1000_usb *ft1000dev)
+{
+       int status;
+       u16 tempword;
+       u32 templong;
+
+       struct ft1000_info *pft1000info;
+
+       pft1000info = netdev_priv(ft1000dev->net);
+
+       pft1000info->CardReady = 0;
+
+       /* Program Interrupt Mask register */
+       status = ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_SUP_IMASK);
+
+       status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
+       tempword |= ASIC_RESET_BIT;
+       status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
+       msleep(1000);
+       status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
+       pr_debug("Reset Register = 0x%x\n", tempword);
+
+       /* Toggle DSP reset */
+       card_reset_dsp(ft1000dev, 1);
+       msleep(1000);
+       card_reset_dsp(ft1000dev, 0);
+       msleep(1000);
+
+       status =
+               ft1000_write_register(ft1000dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
+
+       /* Let's check for FEFE */
+       status =
+               ft1000_read_dpram32(ft1000dev, FT1000_MAG_DPRAM_FEFE_INDX,
+                                   (u8 *)&templong, 4);
+       pr_debug("templong (fefe) = 0x%8x\n", templong);
+
+       /* call codeloader */
+       status = scram_dnldr(ft1000dev, pFileStart, FileLength);
+
+       if (status != 0)
+               return -EIO;
+
+       msleep(1000);
+
+       return 0;
+}
+
+/* call the Card Service function to reset the ASIC. */
+static void ft1000_reset_asic(struct net_device *dev)
+{
+       struct ft1000_info *info = netdev_priv(dev);
+       struct ft1000_usb *ft1000dev = info->priv;
+       u16 tempword;
+
+       /* Let's use the register provided by the Magnemite ASIC to reset the
+        * ASIC and DSP.
+        */
+       ft1000_write_register(ft1000dev, DSP_RESET_BIT | ASIC_RESET_BIT,
+                             FT1000_REG_RESET);
+
+       mdelay(1);
+
+       /* set watermark to -1 in order to not generate an interrupt */
+       ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_MAG_WATERMARK);
+
+       /* clear interrupts */
+       ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
+       pr_debug("interrupt status register = 0x%x\n", tempword);
+       ft1000_write_register(ft1000dev, tempword, FT1000_REG_SUP_ISR);
+       ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_ISR);
+       pr_debug("interrupt status register = 0x%x\n", tempword);
+}
+
+static int ft1000_reset_card(struct net_device *dev)
+{
+       struct ft1000_info *info = netdev_priv(dev);
+       struct ft1000_usb *ft1000dev = info->priv;
+       u16 tempword;
+       struct prov_record *ptr;
+       struct prov_record *tmp;
+
+       ft1000dev->fCondResetPend = true;
+       info->CardReady = 0;
+       ft1000dev->fProvComplete = false;
+
+       /* Make sure we free any memory reserve for provisioning */
+       list_for_each_entry_safe(ptr, tmp, &info->prov_list, list) {
+               pr_debug("deleting provisioning record\n");
+               list_del(&ptr->list);
+               kfree(ptr->pprov_data);
+               kfree(ptr);
+       }
+
+       pr_debug("reset asic\n");
+       ft1000_reset_asic(dev);
+
+       pr_debug("call dsp_reload\n");
+       dsp_reload(ft1000dev);
+
+       pr_debug("dsp reload successful\n");
+
+       mdelay(10);
+
+       /* Initialize DSP heartbeat area */
+       ft1000_write_dpram16(ft1000dev, FT1000_MAG_HI_HO, ho_mag,
+                            FT1000_MAG_HI_HO_INDX);
+       ft1000_read_dpram16(ft1000dev, FT1000_MAG_HI_HO, (u8 *)&tempword,
+                           FT1000_MAG_HI_HO_INDX);
+       pr_debug("hi_ho value = 0x%x\n", tempword);
+
+       info->CardReady = 1;
+
+       ft1000dev->fCondResetPend = false;
+
+       return TRUE;
+}
+
+/* callback function when a urb is transmitted */
+static void ft1000_usb_transmit_complete(struct urb *urb)
+{
+
+       struct ft1000_usb *ft1000dev = urb->context;
+
+       if (urb->status)
+               pr_err("%s: TX status %d\n", ft1000dev->net->name, urb->status);
+
+       netif_wake_queue(ft1000dev->net);
+}
+
+/* take an ethernet packet and convert it to a Flarion
+ *  packet prior to sending it to the ASIC Downlink FIFO.
+ */
+static int ft1000_copy_down_pkt(struct net_device *netdev, u8 *packet, u16 len)
+{
+       struct ft1000_info *pInfo = netdev_priv(netdev);
+       struct ft1000_usb *pFt1000Dev = pInfo->priv;
+
+       int count, ret;
+       u8 *t;
+       struct pseudo_hdr hdr;
+
+       if (!pInfo->CardReady) {
+               pr_debug("Card Not Ready\n");
+               return -ENODEV;
+       }
+
+       count = sizeof(struct pseudo_hdr) + len;
+       if (count > MAX_BUF_SIZE) {
+               pr_debug("Message Size Overflow! size = %d\n", count);
+               return -EINVAL;
+       }
+
+       if (count % 4)
+               count = count + (4 - (count % 4));
+
+       memset(&hdr, 0, sizeof(struct pseudo_hdr));
+
+       hdr.length = ntohs(count);
+       hdr.source = 0x10;
+       hdr.destination = 0x20;
+       hdr.portdest = 0x20;
+       hdr.portsrc = 0x10;
+       hdr.sh_str_id = 0x91;
+       hdr.control = 0x00;
+
+       hdr.checksum = hdr.length ^ hdr.source ^ hdr.destination ^
+               hdr.portdest ^ hdr.portsrc ^ hdr.sh_str_id ^ hdr.control;
+
+       memcpy(&pFt1000Dev->tx_buf[0], &hdr, sizeof(hdr));
+       memcpy(&pFt1000Dev->tx_buf[sizeof(struct pseudo_hdr)], packet, len);
+
+       netif_stop_queue(netdev);
+
+       usb_fill_bulk_urb(pFt1000Dev->tx_urb,
+                         pFt1000Dev->dev,
+                         usb_sndbulkpipe(pFt1000Dev->dev,
+                                         pFt1000Dev->bulk_out_endpointAddr),
+                         pFt1000Dev->tx_buf, count,
+                         ft1000_usb_transmit_complete, pFt1000Dev);
+
+       t = (u8 *)pFt1000Dev->tx_urb->transfer_buffer;
+
+       ret = usb_submit_urb(pFt1000Dev->tx_urb, GFP_ATOMIC);
+
+       if (ret) {
+               pr_debug("failed tx_urb %d\n", ret);
+               return ret;
+       }
+       pInfo->stats.tx_packets++;
+       pInfo->stats.tx_bytes += (len + 14);
+
+       return 0;
+}
+
+/* transmit an ethernet packet
+ *  Parameters:  skb - socket buffer to be sent
+ *               dev - network device
+ */
+static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ft1000_info *pInfo = netdev_priv(dev);
+       struct ft1000_usb *pFt1000Dev = pInfo->priv;
+       u8 *pdata;
+       int maxlen, pipe;
+
+       if (skb == NULL) {
+               pr_debug("skb == NULL!!!\n");
+               return NETDEV_TX_OK;
+       }
+
+       if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
+               pr_debug("network driver is closed, return\n");
+               goto err;
+       }
+
+       pipe =
+               usb_sndbulkpipe(pFt1000Dev->dev, pFt1000Dev->bulk_out_endpointAddr);
+       maxlen = usb_maxpacket(pFt1000Dev->dev, pipe, usb_pipeout(pipe));
+
+       pdata = (u8 *)skb->data;
+
+       if (pInfo->mediastate == 0) {
+               /* Drop packet is mediastate is down */
+               pr_debug("mediastate is down\n");
+               goto err;
+       }
+
+       if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
+               /* Drop packet which has invalid size */
+               pr_debug("invalid ethernet length\n");
+               goto err;
+       }
+
+       ft1000_copy_down_pkt(dev, pdata + ENET_HEADER_SIZE - 2,
+                            skb->len - ENET_HEADER_SIZE + 2);
+
+err:
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+/* open the network driver */
+static int ft1000_open(struct net_device *dev)
+{
+       struct ft1000_info *pInfo = netdev_priv(dev);
+       struct ft1000_usb *pFt1000Dev = pInfo->priv;
+       struct timeval tv;
+
+       pr_debug("ft1000_open is called for card %d\n", pFt1000Dev->CardNumber);
+
+       pInfo->stats.rx_bytes = 0;
+       pInfo->stats.tx_bytes = 0;
+       pInfo->stats.rx_packets = 0;
+       pInfo->stats.tx_packets = 0;
+       do_gettimeofday(&tv);
+       pInfo->ConTm = tv.tv_sec;
+       pInfo->ProgConStat = 0;
+
+       netif_start_queue(dev);
+
+       netif_carrier_on(dev);
+
+       return ft1000_submit_rx_urb(pInfo);
+}
+
+static struct net_device_stats *ft1000_netdev_stats(struct net_device *dev)
+{
+       struct ft1000_info *info = netdev_priv(dev);
+
+       return &(info->stats);
+}
+
+static const struct net_device_ops ftnet_ops = {
+       .ndo_open = &ft1000_open,
+       .ndo_stop = &ft1000_close,
+       .ndo_start_xmit = &ft1000_start_xmit,
+       .ndo_get_stats = &ft1000_netdev_stats,
+};
+
+/* initialize the network device */
+static int ft1000_reset(void *dev)
+{
+       ft1000_reset_card(dev);
+       return 0;
+}
+
+int init_ft1000_netdev(struct ft1000_usb *ft1000dev)
+{
+       struct net_device *netdev;
+       struct ft1000_info *pInfo = NULL;
+       struct dpram_blk *pdpram_blk;
+       int i, ret_val;
+       struct list_head *cur, *tmp;
+       char card_nr[2];
+       u8 gCardIndex = 0;
+
+       netdev = alloc_etherdev(sizeof(struct ft1000_info));
+       if (!netdev) {
+               pr_debug("can not allocate network device\n");
+               return -ENOMEM;
+       }
+
+       pInfo = netdev_priv(netdev);
+
+       memset(pInfo, 0, sizeof(struct ft1000_info));
+
+       dev_alloc_name(netdev, netdev->name);
+
+       pr_debug("network device name is %s\n", netdev->name);
+
+       if (strncmp(netdev->name, "eth", 3) == 0) {
+               card_nr[0] = netdev->name[3];
+               card_nr[1] = '\0';
+               ret_val = kstrtou8(card_nr, 10, &gCardIndex);
+               if (ret_val) {
+                       netdev_err(ft1000dev->net, "Can't parse netdev\n");
+                       goto err_net;
+               }
+
+               ft1000dev->CardNumber = gCardIndex;
+               pr_debug("card number = %d\n", ft1000dev->CardNumber);
+       } else {
+               netdev_err(ft1000dev->net, "ft1000: Invalid device name\n");
+               ret_val = -ENXIO;
+               goto err_net;
+       }
+
+       memset(&pInfo->stats, 0, sizeof(struct net_device_stats));
+
+       spin_lock_init(&pInfo->dpram_lock);
+       pInfo->priv = ft1000dev;
+       pInfo->DrvErrNum = 0;
+       pInfo->registered = 1;
+       pInfo->ft1000_reset = ft1000_reset;
+       pInfo->mediastate = 0;
+       pInfo->fifo_cnt = 0;
+       ft1000dev->DeviceCreated = FALSE;
+       pInfo->CardReady = 0;
+       pInfo->DSP_TIME[0] = 0;
+       pInfo->DSP_TIME[1] = 0;
+       pInfo->DSP_TIME[2] = 0;
+       pInfo->DSP_TIME[3] = 0;
+       ft1000dev->fAppMsgPend = false;
+       ft1000dev->fCondResetPend = false;
+       ft1000dev->usbboot = 0;
+       ft1000dev->dspalive = 0;
+       memset(&ft1000dev->tempbuf[0], 0, sizeof(ft1000dev->tempbuf));
+
+       INIT_LIST_HEAD(&pInfo->prov_list);
+
+       INIT_LIST_HEAD(&ft1000dev->nodes.list);
+
+       netdev->netdev_ops = &ftnet_ops;
+
+       ft1000dev->net = netdev;
+
+       pr_debug("Initialize free_buff_lock and freercvpool\n");
+       spin_lock_init(&free_buff_lock);
+
+       /* initialize a list of buffers to be use for queuing
+        * up receive command data
+        */
+       INIT_LIST_HEAD(&freercvpool);
+
+       /* create list of free buffers */
+       for (i = 0; i < NUM_OF_FREE_BUFFERS; i++) {
+               /* Get memory for DPRAM_DATA link list */
+               pdpram_blk = kmalloc(sizeof(struct dpram_blk), GFP_KERNEL);
+               if (pdpram_blk == NULL) {
+                       ret_val = -ENOMEM;
+                       goto err_free;
+               }
+               /* Get a block of memory to store command data */
+               pdpram_blk->pbuffer = kmalloc(MAX_CMD_SQSIZE, GFP_KERNEL);
+               if (pdpram_blk->pbuffer == NULL) {
+                       ret_val = -ENOMEM;
+                       kfree(pdpram_blk);
+                       goto err_free;
+               }
+               /* link provisioning data */
+               list_add_tail(&pdpram_blk->list, &freercvpool);
+       }
+       numofmsgbuf = NUM_OF_FREE_BUFFERS;
+
+       return 0;
+
+err_free:
+       list_for_each_safe(cur, tmp, &freercvpool) {
+               pdpram_blk = list_entry(cur, struct dpram_blk, list);
+               list_del(&pdpram_blk->list);
+               kfree(pdpram_blk->pbuffer);
+               kfree(pdpram_blk);
+       }
+err_net:
+       free_netdev(netdev);
+       return ret_val;
+}
+
+/* register the network driver */
+int reg_ft1000_netdev(struct ft1000_usb *ft1000dev,
+                     struct usb_interface *intf)
+{
+       struct net_device *netdev;
+       struct ft1000_info *pInfo;
+       int rc;
+
+       netdev = ft1000dev->net;
+       pInfo = netdev_priv(ft1000dev->net);
+
+       ft1000_read_register(ft1000dev, &pInfo->AsicID, FT1000_REG_ASIC_ID);
+
+       usb_set_intfdata(intf, pInfo);
+       SET_NETDEV_DEV(netdev, &intf->dev);
+
+       rc = register_netdev(netdev);
+       if (rc) {
+               pr_debug("could not register network device\n");
+               free_netdev(netdev);
+               return rc;
+       }
+
+       ft1000_create_dev(ft1000dev);
+
+       pInfo->CardReady = 1;
+
+       return 0;
+}
+
+/* take a packet from the FIFO up link and
+ *  convert it into an ethernet packet and deliver it to the IP stack
+ */
+static int ft1000_copy_up_pkt(struct urb *urb)
+{
+       struct ft1000_info *info = urb->context;
+       struct ft1000_usb *ft1000dev = info->priv;
+       struct net_device *net = ft1000dev->net;
+
+       u16 tempword;
+       u16 len;
+       u16 lena;
+       struct sk_buff *skb;
+       u16 i;
+       u8 *pbuffer = NULL;
+       u8 *ptemp = NULL;
+       u16 *chksum;
+
+       if (ft1000dev->status & FT1000_STATUS_CLOSING) {
+               pr_debug("network driver is closed, return\n");
+               return 0;
+       }
+       /* Read length */
+       len = urb->transfer_buffer_length;
+       lena = urb->actual_length;
+
+       chksum = (u16 *)ft1000dev->rx_buf;
+
+       tempword = *chksum++;
+       for (i = 1; i < 7; i++)
+               tempword ^= *chksum++;
+
+       if (tempword != *chksum) {
+               info->stats.rx_errors++;
+               ft1000_submit_rx_urb(info);
+               return -1;
+       }
+
+       skb = dev_alloc_skb(len + 12 + 2);
+
+       if (skb == NULL) {
+               pr_debug("No Network buffers available\n");
+               info->stats.rx_errors++;
+               ft1000_submit_rx_urb(info);
+               return -1;
+       }
+
+       pbuffer = (u8 *)skb_put(skb, len + 12);
+
+       /* subtract the number of bytes read already */
+       ptemp = pbuffer;
+
+       /* fake MAC address */
+       *pbuffer++ = net->dev_addr[0];
+       *pbuffer++ = net->dev_addr[1];
+       *pbuffer++ = net->dev_addr[2];
+       *pbuffer++ = net->dev_addr[3];
+       *pbuffer++ = net->dev_addr[4];
+       *pbuffer++ = net->dev_addr[5];
+       *pbuffer++ = 0x00;
+       *pbuffer++ = 0x07;
+       *pbuffer++ = 0x35;
+       *pbuffer++ = 0xff;
+       *pbuffer++ = 0xff;
+       *pbuffer++ = 0xfe;
+
+       memcpy(pbuffer, ft1000dev->rx_buf + sizeof(struct pseudo_hdr),
+              len - sizeof(struct pseudo_hdr));
+
+       skb->dev = net;
+
+       skb->protocol = eth_type_trans(skb, net);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       netif_rx(skb);
+
+       info->stats.rx_packets++;
+       /* Add on 12 bytes for MAC address which was removed */
+       info->stats.rx_bytes += (lena + 12);
+
+       ft1000_submit_rx_urb(info);
+
+       return 0;
+}
+
+
+/* the receiving function of the network driver */
+static int ft1000_submit_rx_urb(struct ft1000_info *info)
+{
+       int result;
+       struct ft1000_usb *pFt1000Dev = info->priv;
+
+       if (pFt1000Dev->status & FT1000_STATUS_CLOSING) {
+               pr_debug("network driver is closed, return\n");
+               return -ENODEV;
+       }
+
+       usb_fill_bulk_urb(pFt1000Dev->rx_urb,
+                         pFt1000Dev->dev,
+                         usb_rcvbulkpipe(pFt1000Dev->dev,
+                                         pFt1000Dev->bulk_in_endpointAddr),
+                         pFt1000Dev->rx_buf, MAX_BUF_SIZE,
+                         (usb_complete_t)ft1000_copy_up_pkt, info);
+
+       result = usb_submit_urb(pFt1000Dev->rx_urb, GFP_ATOMIC);
+
+       if (result) {
+               pr_err("submitting rx_urb %d failed\n", result);
+               return result;
+       }
+
+       return 0;
+}
+
+/* close the network driver */
+int ft1000_close(struct net_device *net)
+{
+       struct ft1000_info *pInfo = netdev_priv(net);
+       struct ft1000_usb *ft1000dev = pInfo->priv;
+
+       ft1000dev->status |= FT1000_STATUS_CLOSING;
+
+       pr_debug("pInfo=%p, ft1000dev=%p\n", pInfo, ft1000dev);
+       netif_carrier_off(net);
+       netif_stop_queue(net);
+       ft1000dev->status &= ~FT1000_STATUS_CLOSING;
+
+       pInfo->ProgConStat = 0xff;
+
+       return 0;
+}
+
+/* check if the device is presently available on the system. */
+static int ft1000_chkcard(struct ft1000_usb *dev)
+{
+       u16 tempword;
+       int status;
+
+       if (dev->fCondResetPend) {
+               pr_debug("Card is being reset, return FALSE\n");
+               return TRUE;
+       }
+       /* Mask register is used to check for device presence since it is never
+        * set to zero.
+        */
+       status = ft1000_read_register(dev, &tempword, FT1000_REG_SUP_IMASK);
+       if (tempword == 0) {
+               pr_debug("IMASK = 0 Card not detected\n");
+               return FALSE;
+       }
+       /* The system will return the value of 0xffff for the version register
+        * if the device is not present.
+        */
+       status = ft1000_read_register(dev, &tempword, FT1000_REG_ASIC_ID);
+       if (tempword != 0x1b01) {
+               dev->status |= FT1000_STATUS_CLOSING;
+               pr_debug("Version = 0xffff Card not detected\n");
+               return FALSE;
+       }
+       return TRUE;
+}
+
+/* read a message from the dpram area.
+ *  Input:
+ *    dev - network device structure
+ *    pbuffer - caller supply address to buffer
+ */
+static bool ft1000_receive_cmd(struct ft1000_usb *dev, u16 *pbuffer,
+                              int maxsz)
+{
+       u16 size;
+       int ret;
+       u16 *ppseudohdr;
+       int i;
+       u16 tempword;
+
+       ret =
+               ft1000_read_dpram16(dev, FT1000_MAG_PH_LEN, (u8 *)&size,
+                                   FT1000_MAG_PH_LEN_INDX);
+       size = ntohs(size) + PSEUDOSZ;
+       if (size > maxsz) {
+               pr_debug("Invalid command length = %d\n", size);
+               return FALSE;
+       }
+       ppseudohdr = (u16 *)pbuffer;
+       ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE,
+                             FT1000_REG_DPRAM_ADDR);
+       ret =
+               ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
+       pbuffer++;
+       ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE + 1,
+                             FT1000_REG_DPRAM_ADDR);
+       for (i = 0; i <= (size >> 2); i++) {
+               ret =
+                       ft1000_read_register(dev, pbuffer,
+                                            FT1000_REG_MAG_DPDATAL);
+               pbuffer++;
+               ret =
+                       ft1000_read_register(dev, pbuffer,
+                                            FT1000_REG_MAG_DPDATAH);
+               pbuffer++;
+       }
+       /* copy odd aligned word */
+       ret =
+               ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAL);
+
+       pbuffer++;
+       ret =
+               ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
+
+       pbuffer++;
+       if (size & 0x0001) {
+               /* copy odd byte from fifo */
+               ret =
+                       ft1000_read_register(dev, &tempword,
+                                            FT1000_REG_DPRAM_DATA);
+               *pbuffer = ntohs(tempword);
+       }
+       /* Check if pseudo header checksum is good
+        * Calculate pseudo header checksum
+        */
+       tempword = *ppseudohdr++;
+       for (i = 1; i < 7; i++)
+               tempword ^= *ppseudohdr++;
+
+       if (tempword != *ppseudohdr)
+               return FALSE;
+
+       return TRUE;
+}
+
+static int ft1000_dsp_prov(void *arg)
+{
+       struct ft1000_usb *dev = (struct ft1000_usb *)arg;
+       struct ft1000_info *info = netdev_priv(dev->net);
+       u16 tempword;
+       u16 len;
+       u16 i = 0;
+       struct prov_record *ptr;
+       struct pseudo_hdr *ppseudo_hdr;
+       u16 *pmsg;
+       int status;
+       u16 TempShortBuf[256];
+
+       while (list_empty(&info->prov_list) == 0) {
+               pr_debug("DSP Provisioning List Entry\n");
+
+               /* Check if doorbell is available */
+               pr_debug("check if doorbell is cleared\n");
+               status =
+                       ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
+               if (status) {
+                       pr_debug("ft1000_read_register error\n");
+                       break;
+               }
+
+               while (tempword & FT1000_DB_DPRAM_TX) {
+                       mdelay(10);
+                       i++;
+                       if (i == 10) {
+                               pr_debug("message drop\n");
+                               return -1;
+                       }
+                       ft1000_read_register(dev, &tempword,
+                                            FT1000_REG_DOORBELL);
+               }
+
+               if (!(tempword & FT1000_DB_DPRAM_TX)) {
+                       pr_debug("*** Provision Data Sent to DSP\n");
+
+                       /* Send provisioning data */
+                       ptr =
+                               list_entry(info->prov_list.next, struct prov_record,
+                                          list);
+                       len = *(u16 *)ptr->pprov_data;
+                       len = htons(len);
+                       len += PSEUDOSZ;
+
+                       pmsg = (u16 *)ptr->pprov_data;
+                       ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+                       /* Insert slow queue sequence number */
+                       ppseudo_hdr->seq_num = info->squeseqnum++;
+                       ppseudo_hdr->portsrc = 0;
+                       /* Calculate new checksum */
+                       ppseudo_hdr->checksum = *pmsg++;
+                       for (i = 1; i < 7; i++)
+                               ppseudo_hdr->checksum ^= *pmsg++;
+
+                       TempShortBuf[0] = 0;
+                       TempShortBuf[1] = htons(len);
+                       memcpy(&TempShortBuf[2], ppseudo_hdr, len);
+
+                       status =
+                               ft1000_write_dpram32(dev, 0,
+                                                    (u8 *)&TempShortBuf[0],
+                                                    (unsigned short)(len + 2));
+                       status =
+                               ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
+                                                     FT1000_REG_DOORBELL);
+
+                       list_del(&ptr->list);
+                       kfree(ptr->pprov_data);
+                       kfree(ptr);
+               }
+               usleep_range(9000, 11000);
+       }
+
+       pr_debug("DSP Provisioning List Entry finished\n");
+
+       msleep(100);
+
+       dev->fProvComplete = true;
+       info->CardReady = 1;
+
+       return 0;
+}
+
+static int ft1000_proc_drvmsg(struct ft1000_usb *dev, u16 size)
+{
+       struct ft1000_info *info = netdev_priv(dev->net);
+       u16 msgtype;
+       u16 tempword;
+       struct media_msg *pmediamsg;
+       struct dsp_init_msg *pdspinitmsg;
+       struct drv_msg *pdrvmsg;
+       u16 i;
+       struct pseudo_hdr *ppseudo_hdr;
+       u16 *pmsg;
+       int status;
+       union {
+               u8 byte[2];
+               u16 wrd;
+       } convert;
+
+       char *cmdbuffer = kmalloc(1600, GFP_KERNEL);
+
+       if (!cmdbuffer)
+               return -ENOMEM;
+
+       status = ft1000_read_dpram32(dev, 0x200, cmdbuffer, size);
+
+#ifdef JDEBUG
+       print_hex_dump_debug("cmdbuffer: ", HEX_DUMP_OFFSET, 16, 1,
+                            cmdbuffer, size, true);
+#endif
+       pdrvmsg = (struct drv_msg *)&cmdbuffer[2];
+       msgtype = ntohs(pdrvmsg->type);
+       pr_debug("Command message type = 0x%x\n", msgtype);
+       switch (msgtype) {
+       case MEDIA_STATE:{
+               pr_debug("Command message type = MEDIA_STATE\n");
+               pmediamsg = (struct media_msg *)&cmdbuffer[0];
+               if (info->ProgConStat != 0xFF) {
+                       if (pmediamsg->state) {
+                               pr_debug("Media is up\n");
+                               if (info->mediastate == 0) {
+                                       if (dev->NetDevRegDone)
+                                               netif_wake_queue(dev->net);
+                                       info->mediastate = 1;
+                               }
+                       } else {
+                               pr_debug("Media is down\n");
+                               if (info->mediastate == 1) {
+                                       info->mediastate = 0;
+                                       if (dev->NetDevRegDone)
+                                               info->ConTm = 0;
+                               }
+                       }
+               } else {
+                       pr_debug("Media is down\n");
+                       if (info->mediastate == 1) {
+                               info->mediastate = 0;
+                               info->ConTm = 0;
+                       }
+               }
+               break;
+       }
+       case DSP_INIT_MSG:{
+               pr_debug("Command message type = DSP_INIT_MSG\n");
+               pdspinitmsg = (struct dsp_init_msg *)&cmdbuffer[2];
+               memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
+               pr_debug("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
+                        info->DspVer[0], info->DspVer[1], info->DspVer[2],
+                        info->DspVer[3]);
+               memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
+                      HWSERNUMSZ);
+               memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
+               memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
+               pr_debug("EUI64=%2x.%2x.%2x.%2x.%2x.%2x.%2x.%2x\n",
+                        info->eui64[0], info->eui64[1], info->eui64[2],
+                        info->eui64[3], info->eui64[4], info->eui64[5],
+                        info->eui64[6], info->eui64[7]);
+               dev->net->dev_addr[0] = info->eui64[0];
+               dev->net->dev_addr[1] = info->eui64[1];
+               dev->net->dev_addr[2] = info->eui64[2];
+               dev->net->dev_addr[3] = info->eui64[5];
+               dev->net->dev_addr[4] = info->eui64[6];
+               dev->net->dev_addr[5] = info->eui64[7];
+
+               if (ntohs(pdspinitmsg->length) ==
+                   (sizeof(struct dsp_init_msg) - 20)) {
+                       memcpy(info->ProductMode, pdspinitmsg->ProductMode,
+                              MODESZ);
+                       memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, CALVERSZ);
+                       memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
+                              CALDATESZ);
+                       pr_debug("RFCalVer = 0x%2x 0x%2x\n",
+                                info->RfCalVer[0], info->RfCalVer[1]);
+               }
+               break;
+       }
+       case DSP_PROVISION:{
+               pr_debug("Command message type = DSP_PROVISION\n");
+
+               /* kick off dspprov routine to start provisioning
+                * Send provisioning data to DSP
+                */
+               if (list_empty(&info->prov_list) == 0) {
+                       dev->fProvComplete = false;
+                       status = ft1000_dsp_prov(dev);
+                       if (status != 0)
+                               goto out;
+               } else {
+                       dev->fProvComplete = true;
+                       status = ft1000_write_register(dev, FT1000_DB_HB,
+                                                      FT1000_REG_DOORBELL);
+                       pr_debug("No more DSP provisioning data in dsp image\n");
+               }
+               pr_debug("DSP PROVISION is done\n");
+               break;
+       }
+       case DSP_STORE_INFO:{
+               pr_debug("Command message type = DSP_STORE_INFO");
+               tempword = ntohs(pdrvmsg->length);
+               info->DSPInfoBlklen = tempword;
+               if (tempword < (MAX_DSP_SESS_REC - 4)) {
+                       pmsg = (u16 *)&pdrvmsg->data[0];
+                       for (i = 0; i < ((tempword + 1) / 2); i++) {
+                               pr_debug("dsp info data = 0x%x\n", *pmsg);
+                               info->DSPInfoBlk[i + 10] = *pmsg++;
+                       }
+               } else {
+                       info->DSPInfoBlklen = 0;
+               }
+               break;
+       }
+       case DSP_GET_INFO:{
+               pr_debug("Got DSP_GET_INFO\n");
+               /* copy dsp info block to dsp */
+               dev->DrvMsgPend = 1;
+               /* allow any outstanding ioctl to finish */
+               mdelay(10);
+               status = ft1000_read_register(dev, &tempword,
+                                             FT1000_REG_DOORBELL);
+               if (tempword & FT1000_DB_DPRAM_TX) {
+                       mdelay(10);
+                       status = ft1000_read_register(dev, &tempword,
+                                                     FT1000_REG_DOORBELL);
+                       if (tempword & FT1000_DB_DPRAM_TX) {
+                               mdelay(10);
+                               status = ft1000_read_register(dev, &tempword,
+                                                             FT1000_REG_DOORBELL);
+                               if (tempword & FT1000_DB_DPRAM_TX)
+                                       break;
+                       }
+               }
+               /* Put message into Slow Queue Form Pseudo header */
+               pmsg = (u16 *)info->DSPInfoBlk;
+               *pmsg++ = 0;
+               *pmsg++ = htons(info->DSPInfoBlklen + 20 + info->DSPInfoBlklen);
+               ppseudo_hdr =
+                       (struct pseudo_hdr *)(u16 *)&info->DSPInfoBlk[2];
+               ppseudo_hdr->length = htons(info->DSPInfoBlklen + 4
+                                           + info->DSPInfoBlklen);
+               ppseudo_hdr->source = 0x10;
+               ppseudo_hdr->destination = 0x20;
+               ppseudo_hdr->portdest = 0;
+               ppseudo_hdr->portsrc = 0;
+               ppseudo_hdr->sh_str_id = 0;
+               ppseudo_hdr->control = 0;
+               ppseudo_hdr->rsvd1 = 0;
+               ppseudo_hdr->rsvd2 = 0;
+               ppseudo_hdr->qos_class = 0;
+               /* Insert slow queue sequence number */
+               ppseudo_hdr->seq_num = info->squeseqnum++;
+               /* Insert application id */
+               ppseudo_hdr->portsrc = 0;
+               /* Calculate new checksum */
+               ppseudo_hdr->checksum = *pmsg++;
+               for (i = 1; i < 7; i++)
+                       ppseudo_hdr->checksum ^= *pmsg++;
+
+               info->DSPInfoBlk[10] = 0x7200;
+               info->DSPInfoBlk[11] = htons(info->DSPInfoBlklen);
+               status = ft1000_write_dpram32(dev, 0,
+                                             (u8 *)&info->DSPInfoBlk[0],
+                                             (unsigned short)(info->DSPInfoBlklen + 22));
+               status = ft1000_write_register(dev, FT1000_DB_DPRAM_TX,
+                                              FT1000_REG_DOORBELL);
+               dev->DrvMsgPend = 0;
+               break;
+       }
+       case GET_DRV_ERR_RPT_MSG:{
+               pr_debug("Got GET_DRV_ERR_RPT_MSG\n");
+               /* copy driver error message to dsp */
+               dev->DrvMsgPend = 1;
+               /* allow any outstanding ioctl to finish */
+               mdelay(10);
+               status = ft1000_read_register(dev, &tempword,
+                                             FT1000_REG_DOORBELL);
+               if (tempword & FT1000_DB_DPRAM_TX) {
+                       mdelay(10);
+                       status = ft1000_read_register(dev, &tempword,
+                                                     FT1000_REG_DOORBELL);
+                       if (tempword & FT1000_DB_DPRAM_TX)
+                               mdelay(10);
+               }
+               if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
+                       /* Put message into Slow Queue Form Pseudo header */
+                       pmsg = (u16 *)&tempbuffer[0];
+                       ppseudo_hdr = (struct pseudo_hdr *)pmsg;
+                       ppseudo_hdr->length = htons(0x0012);
+                       ppseudo_hdr->source = 0x10;
+                       ppseudo_hdr->destination = 0x20;
+                       ppseudo_hdr->portdest = 0;
+                       ppseudo_hdr->portsrc = 0;
+                       ppseudo_hdr->sh_str_id = 0;
+                       ppseudo_hdr->control = 0;
+                       ppseudo_hdr->rsvd1 = 0;
+                       ppseudo_hdr->rsvd2 = 0;
+                       ppseudo_hdr->qos_class = 0;
+                       /* Insert slow queue sequence number */
+                       ppseudo_hdr->seq_num = info->squeseqnum++;
+                       /* Insert application id */
+                       ppseudo_hdr->portsrc = 0;
+                       /* Calculate new checksum */
+                       ppseudo_hdr->checksum = *pmsg++;
+                       for (i = 1; i < 7; i++)
+                               ppseudo_hdr->checksum ^= *pmsg++;
+
+                       pmsg = (u16 *)&tempbuffer[16];
+                       *pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
+                       *pmsg++ = htons(0x000e);
+                       *pmsg++ = htons(info->DSP_TIME[0]);
+                       *pmsg++ = htons(info->DSP_TIME[1]);
+                       *pmsg++ = htons(info->DSP_TIME[2]);
+                       *pmsg++ = htons(info->DSP_TIME[3]);
+                       convert.byte[0] = info->DspVer[0];
+                       convert.byte[1] = info->DspVer[1];
+                       *pmsg++ = convert.wrd;
+                       convert.byte[0] = info->DspVer[2];
+                       convert.byte[1] = info->DspVer[3];
+                       *pmsg++ = convert.wrd;
+                       *pmsg++ = htons(info->DrvErrNum);
+
+                       status = card_send_command(dev, (unsigned char *)&tempbuffer[0],
+                                                  (u16)(0x0012 + PSEUDOSZ));
+                       if (status)
+                               goto out;
+                       info->DrvErrNum = 0;
+               }
+               dev->DrvMsgPend = 0;
+               break;
+       }
+       default:
+               break;
+       }
+
+       status = 0;
+out:
+       kfree(cmdbuffer);
+       return status;
+}
+
+/* Check which application has registered for dsp broadcast messages */
+static int dsp_broadcast_msg_id(struct ft1000_usb *dev)
+{
+       struct dpram_blk *pdpram_blk;
+       unsigned long flags;
+       int i;
+
+       for (i = 0; i < MAX_NUM_APP; i++) {
+               if ((dev->app_info[i].DspBCMsgFlag)
+                   && (dev->app_info[i].fileobject)
+                   && (dev->app_info[i].NumOfMsg
+                       < MAX_MSG_LIMIT)) {
+                       pdpram_blk = ft1000_get_buffer(&freercvpool);
+                       if (pdpram_blk == NULL) {
+                               pr_debug("Out of memory in free receive command pool\n");
+                               dev->app_info[i].nRxMsgMiss++;
+                               return -1;
+                       }
+                       if (ft1000_receive_cmd(dev, pdpram_blk->pbuffer,
+                                              MAX_CMD_SQSIZE)) {
+                               /* Put message into the
+                                * appropriate application block
+                                */
+                               dev->app_info[i].nRxMsg++;
+                               spin_lock_irqsave(&free_buff_lock, flags);
+                               list_add_tail(&pdpram_blk->list,
+                                             &dev->app_info[i] .app_sqlist);
+                               dev->app_info[i].NumOfMsg++;
+                               spin_unlock_irqrestore(&free_buff_lock, flags);
+                               wake_up_interruptible(&dev->app_info[i]
+                                                     .wait_dpram_msg);
+                       } else {
+                               dev->app_info[i].nRxMsgMiss++;
+                               ft1000_free_buffer(pdpram_blk, &freercvpool);
+                               pr_debug("ft1000_get_buffer NULL\n");
+                               return -1;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int handle_misc_portid(struct ft1000_usb *dev)
+{
+       struct dpram_blk *pdpram_blk;
+       int i;
+
+       pdpram_blk = ft1000_get_buffer(&freercvpool);
+       if (pdpram_blk == NULL) {
+               pr_debug("Out of memory in free receive command pool\n");
+               return -1;
+       }
+       if (!ft1000_receive_cmd(dev, pdpram_blk->pbuffer, MAX_CMD_SQSIZE))
+               goto exit_failure;
+
+       /* Search for correct application block */
+       for (i = 0; i < MAX_NUM_APP; i++) {
+               if (dev->app_info[i].app_id == ((struct pseudo_hdr *)
+                                               pdpram_blk->pbuffer)->portdest)
+                       break;
+       }
+       if (i == MAX_NUM_APP) {
+               pr_debug("No application matching id = %d\n",
+                        ((struct pseudo_hdr *)pdpram_blk->pbuffer)->portdest);
+               goto exit_failure;
+       } else if (dev->app_info[i].NumOfMsg > MAX_MSG_LIMIT) {
+               goto exit_failure;
+       } else {
+               dev->app_info[i].nRxMsg++;
+               /* Put message into the appropriate application block */
+               list_add_tail(&pdpram_blk->list, &dev->app_info[i].app_sqlist);
+               dev->app_info[i].NumOfMsg++;
+       }
+       return 0;
+
+exit_failure:
+       ft1000_free_buffer(pdpram_blk, &freercvpool);
+       return -1;
+}
+
+int ft1000_poll(void *dev_id)
+{
+       struct ft1000_usb *dev = (struct ft1000_usb *)dev_id;
+       struct ft1000_info *info = netdev_priv(dev->net);
+       u16 tempword;
+       int status;
+       u16 size;
+       int i;
+       u16 data;
+       u16 modulo;
+       u16 portid;
+
+       if (ft1000_chkcard(dev) == FALSE) {
+               pr_debug("failed\n");
+               return -1;
+       }
+       status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
+       if (!status) {
+               if (tempword & FT1000_DB_DPRAM_RX) {
+                       status = ft1000_read_dpram16(dev,
+                                                    0x200, (u8 *)&data, 0);
+                       size = ntohs(data) + 16 + 2;
+                       if (size % 4) {
+                               modulo = 4 - (size % 4);
+                               size = size + modulo;
+                       }
+                       status = ft1000_read_dpram16(dev, 0x201,
+                                                    (u8 *)&portid, 1);
+                       portid &= 0xff;
+                       if (size < MAX_CMD_SQSIZE) {
+                               switch (portid) {
+                               case DRIVERID:
+                                       pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid DRIVERID\n");
+                                       status = ft1000_proc_drvmsg(dev, size);
+                                       if (status != 0)
+                                               return status;
+                                       break;
+                               case DSPBCMSGID:
+                                       status = dsp_broadcast_msg_id(dev);
+                                       break;
+                               default:
+                                       status = handle_misc_portid(dev);
+                                       break;
+                               }
+                       } else
+                               pr_debug("Invalid total length for SlowQ = %d\n",
+                                        size);
+                       status = ft1000_write_register(dev,
+                                                      FT1000_DB_DPRAM_RX,
+                                                      FT1000_REG_DOORBELL);
+               } else if (tempword & FT1000_DSP_ASIC_RESET) {
+                       /* Let's reset the ASIC from the Host side as well */
+                       status = ft1000_write_register(dev, ASIC_RESET_BIT,
+                                                      FT1000_REG_RESET);
+                       status = ft1000_read_register(dev, &tempword,
+                                                     FT1000_REG_RESET);
+                       i = 0;
+                       while (tempword & ASIC_RESET_BIT) {
+                               status = ft1000_read_register(dev, &tempword,
+                                                             FT1000_REG_RESET);
+                               usleep_range(9000, 11000);
+                               i++;
+                               if (i == 100)
+                                       break;
+                       }
+                       if (i == 100) {
+                               pr_debug("Unable to reset ASIC\n");
+                               return 0;
+                       }
+                       usleep_range(9000, 11000);
+                       /* Program WMARK register */
+                       status = ft1000_write_register(dev, 0x600,
+                                                      FT1000_REG_MAG_WATERMARK);
+                       /* clear ASIC reset doorbell */
+                       status = ft1000_write_register(dev,
+                                                      FT1000_DSP_ASIC_RESET,
+                                                      FT1000_REG_DOORBELL);
+                       usleep_range(9000, 11000);
+               } else if (tempword & FT1000_ASIC_RESET_REQ) {
+                       pr_debug("FT1000_REG_DOORBELL message type: FT1000_ASIC_RESET_REQ\n");
+                       /* clear ASIC reset request from DSP */
+                       status = ft1000_write_register(dev,
+                                                      FT1000_ASIC_RESET_REQ,
+                                                      FT1000_REG_DOORBELL);
+                       status = ft1000_write_register(dev, HOST_INTF_BE,
+                                                      FT1000_REG_SUP_CTRL);
+                       /* copy dsp session record from Adapter block */
+                       status = ft1000_write_dpram32(dev, 0,
+                                                     (u8 *)&info->DSPSess.Rec[0], 1024);
+                       status = ft1000_write_register(dev, 0x600,
+                                                      FT1000_REG_MAG_WATERMARK);
+                       /* ring doorbell to tell DSP that
+                        * ASIC is out of reset
+                        * */
+                       status = ft1000_write_register(dev,
+                                                      FT1000_ASIC_RESET_DSP,
+                                                      FT1000_REG_DOORBELL);
+               } else if (tempword & FT1000_DB_COND_RESET) {
+                       pr_debug("FT1000_REG_DOORBELL message type: FT1000_DB_COND_RESET\n");
+                       if (!dev->fAppMsgPend) {
+                               /* Reset ASIC and DSP */
+                               status = ft1000_read_dpram16(dev,
+                                                            FT1000_MAG_DSP_TIMER0,
+                                                            (u8 *)&info->DSP_TIME[0],
+                                                            FT1000_MAG_DSP_TIMER0_INDX);
+                               status = ft1000_read_dpram16(dev,
+                                                            FT1000_MAG_DSP_TIMER1,
+                                                            (u8 *)&info->DSP_TIME[1],
+                                                            FT1000_MAG_DSP_TIMER1_INDX);
+                               status = ft1000_read_dpram16(dev,
+                                                            FT1000_MAG_DSP_TIMER2,
+                                                            (u8 *)&info->DSP_TIME[2],
+                                                            FT1000_MAG_DSP_TIMER2_INDX);
+                               status = ft1000_read_dpram16(dev,
+                                                            FT1000_MAG_DSP_TIMER3,
+                                                            (u8 *)&info->DSP_TIME[3],
+                                                            FT1000_MAG_DSP_TIMER3_INDX);
+                               info->CardReady = 0;
+                               info->DrvErrNum = DSP_CONDRESET_INFO;
+                               pr_debug("DSP conditional reset requested\n");
+                               info->ft1000_reset(dev->net);
+                       } else {
+                               dev->fProvComplete = false;
+                               dev->fCondResetPend = true;
+                       }
+                       ft1000_write_register(dev, FT1000_DB_COND_RESET,
+                                             FT1000_REG_DOORBELL);
+               }
+       }
+       return 0;
+}