Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / staging / ft1000 / ft1000-usb / ft1000_download.c
diff --git a/kernel/drivers/staging/ft1000/ft1000-usb/ft1000_download.c b/kernel/drivers/staging/ft1000/ft1000-usb/ft1000_download.c
new file mode 100644 (file)
index 0000000..5def347
--- /dev/null
@@ -0,0 +1,1052 @@
+/*
+ * 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 <linux/vmalloc.h>
+#include "ft1000_usb.h"
+
+
+#define  DWNLD_HANDSHAKE_LOC     0x02
+#define  DWNLD_TYPE_LOC          0x04
+#define  DWNLD_SIZE_MSW_LOC      0x06
+#define  DWNLD_SIZE_LSW_LOC      0x08
+#define  DWNLD_PS_HDR_LOC        0x0A
+
+#define  MAX_DSP_WAIT_LOOPS      40
+#define  DSP_WAIT_SLEEP_TIME     1000       /* 1 millisecond */
+#define  DSP_WAIT_DISPATCH_LVL   50         /* 50 usec */
+
+#define  HANDSHAKE_TIMEOUT_VALUE 0xF1F1
+#define  HANDSHAKE_RESET_VALUE   0xFEFE   /* When DSP requests startover */
+#define  HANDSHAKE_RESET_VALUE_USB   0xFE7E   /* When DSP requests startover */
+#define  HANDSHAKE_DSP_BL_READY  0xFEFE   /* At start DSP writes this when bootloader ready */
+#define  HANDSHAKE_DSP_BL_READY_USB  0xFE7E   /* At start DSP writes this when bootloader ready */
+#define  HANDSHAKE_DRIVER_READY  0xFFFF   /* Driver writes after receiving 0xFEFE */
+#define  HANDSHAKE_SEND_DATA     0x0000   /* DSP writes this when ready for more data */
+
+#define  HANDSHAKE_REQUEST       0x0001   /* Request from DSP */
+#define  HANDSHAKE_RESPONSE      0x0000   /* Satisfied DSP request */
+
+#define  REQUEST_CODE_LENGTH     0x0000
+#define  REQUEST_RUN_ADDRESS     0x0001
+#define  REQUEST_CODE_SEGMENT    0x0002   /* In WORD count */
+#define  REQUEST_DONE_BL         0x0003
+#define  REQUEST_DONE_CL         0x0004
+#define  REQUEST_VERSION_INFO    0x0005
+#define  REQUEST_CODE_BY_VERSION 0x0006
+#define  REQUEST_MAILBOX_DATA    0x0007
+#define  REQUEST_FILE_CHECKSUM   0x0008
+
+#define  STATE_START_DWNLD       0x01
+#define  STATE_BOOT_DWNLD        0x02
+#define  STATE_CODE_DWNLD        0x03
+#define  STATE_DONE_DWNLD        0x04
+#define  STATE_SECTION_PROV      0x05
+#define  STATE_DONE_PROV         0x06
+#define  STATE_DONE_FILE         0x07
+
+#define  MAX_LENGTH              0x7f0
+
+/* Temporary download mechanism for Magnemite */
+#define  DWNLD_MAG_TYPE_LOC          0x00
+#define  DWNLD_MAG_LEN_LOC           0x01
+#define  DWNLD_MAG_ADDR_LOC          0x02
+#define  DWNLD_MAG_CHKSUM_LOC        0x03
+#define  DWNLD_MAG_VAL_LOC           0x04
+
+#define  HANDSHAKE_MAG_DSP_BL_READY  0xFEFE0000   /* At start DSP writes this when bootloader ready */
+#define  HANDSHAKE_MAG_DSP_ENTRY     0x01000000   /* Dsp writes this to request for entry address */
+#define  HANDSHAKE_MAG_DSP_DATA      0x02000000   /* Dsp writes this to request for data block */
+#define  HANDSHAKE_MAG_DSP_DONE      0x03000000   /* Dsp writes this to indicate download done */
+
+#define  HANDSHAKE_MAG_DRV_READY     0xFFFF0000   /* Driver writes this to indicate ready to download */
+#define  HANDSHAKE_MAG_DRV_DATA      0x02FECDAB   /* Driver writes this to indicate data available to DSP */
+#define  HANDSHAKE_MAG_DRV_ENTRY     0x01FECDAB   /* Driver writes this to indicate entry point to DSP */
+
+#define  HANDSHAKE_MAG_TIMEOUT_VALUE 0xF1F1
+
+
+/* New Magnemite downloader */
+#define  DWNLD_MAG1_HANDSHAKE_LOC     0x00
+#define  DWNLD_MAG1_TYPE_LOC          0x01
+#define  DWNLD_MAG1_SIZE_LOC          0x02
+#define  DWNLD_MAG1_PS_HDR_LOC        0x03
+
+struct dsp_file_hdr {
+       long              version_id;          /* Version ID of this image format. */
+       long              package_id;          /* Package ID of code release. */
+       long              build_date;          /* Date/time stamp when file was built. */
+       long              commands_offset;     /* Offset to attached commands in Pseudo Hdr format. */
+       long              loader_offset;       /* Offset to bootloader code. */
+       long              loader_code_address; /* Start address of bootloader. */
+       long              loader_code_end;     /* Where bootloader code ends. */
+       long              loader_code_size;
+       long              version_data_offset; /* Offset were scrambled version data begins. */
+       long              version_data_size;   /* Size, in words, of scrambled version data. */
+       long              nDspImages;          /* Number of DSP images in file. */
+};
+
+#pragma pack(1)
+struct dsp_image_info {
+       long              coff_date;           /* Date/time when DSP Coff image was built. */
+       long              begin_offset;        /* Offset in file where image begins. */
+       long              end_offset;          /* Offset in file where image begins. */
+       long              run_address;         /* On chip Start address of DSP code. */
+       long              image_size;          /* Size of image. */
+       long              version;             /* Embedded version # of DSP code. */
+       unsigned short    checksum;            /* DSP File checksum */
+       unsigned short    pad1;
+};
+
+
+/* checks if the doorbell register is cleared */
+static int check_usb_db(struct ft1000_usb *ft1000dev)
+{
+       int loopcnt;
+       u16 temp;
+       int status;
+
+       loopcnt = 0;
+
+       while (loopcnt < 10) {
+               status = ft1000_read_register(ft1000dev, &temp,
+                                             FT1000_REG_DOORBELL);
+               pr_debug("read FT1000_REG_DOORBELL value is %x\n", temp);
+               if (temp & 0x0080) {
+                       pr_debug("Got checkusb doorbell\n");
+                       status = ft1000_write_register(ft1000dev, 0x0080,
+                                                      FT1000_REG_DOORBELL);
+                       status = ft1000_write_register(ft1000dev, 0x0100,
+                                                      FT1000_REG_DOORBELL);
+                       status = ft1000_write_register(ft1000dev,  0x8000,
+                                                      FT1000_REG_DOORBELL);
+                       break;
+               }
+               loopcnt++;
+               msleep(10);
+
+       }
+
+       loopcnt = 0;
+       while (loopcnt < 20) {
+               status = ft1000_read_register(ft1000dev, &temp,
+                                             FT1000_REG_DOORBELL);
+               pr_debug("Doorbell = 0x%x\n", temp);
+               if (temp & 0x8000) {
+                       loopcnt++;
+                       msleep(10);
+               } else  {
+                       pr_debug("door bell is cleared, return 0\n");
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+/* gets the handshake and compares it with the expected value */
+static u16 get_handshake(struct ft1000_usb *ft1000dev, u16 expected_value)
+{
+       u16 handshake;
+       int loopcnt;
+       int status = 0;
+
+       loopcnt = 0;
+
+       while (loopcnt < 100) {
+               /* Need to clear downloader doorbell if Hartley ASIC */
+               status = ft1000_write_register(ft1000dev,  FT1000_DB_DNLD_RX,
+                                              FT1000_REG_DOORBELL);
+               if (ft1000dev->fcodeldr) {
+                       pr_debug("fcodeldr is %d\n", ft1000dev->fcodeldr);
+                       ft1000dev->fcodeldr = 0;
+                       status = check_usb_db(ft1000dev);
+                       if (status != 0) {
+                               pr_debug("check_usb_db failed\n");
+                               break;
+                       }
+                       status = ft1000_write_register(ft1000dev,
+                                                      FT1000_DB_DNLD_RX,
+                                                      FT1000_REG_DOORBELL);
+               }
+
+               status = ft1000_read_dpram16(ft1000dev,
+                                            DWNLD_MAG1_HANDSHAKE_LOC, (u8 *)&handshake, 1);
+               handshake = ntohs(handshake);
+
+               if (status)
+                       return HANDSHAKE_TIMEOUT_VALUE;
+
+               if ((handshake == expected_value) ||
+                   (handshake == HANDSHAKE_RESET_VALUE_USB)) {
+                       return handshake;
+               }
+               loopcnt++;
+               msleep(10);
+       }
+
+       return HANDSHAKE_TIMEOUT_VALUE;
+}
+
+/* write the handshake value to the handshake location */
+static void put_handshake(struct ft1000_usb *ft1000dev, u16 handshake_value)
+{
+       u32 tempx;
+       u16 tempword;
+       int status;
+
+       tempx = (u32)handshake_value;
+       tempx = ntohl(tempx);
+
+       tempword = (u16)(tempx & 0xffff);
+       status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
+                                     tempword, 0);
+       tempword = (u16)(tempx >> 16);
+       status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC,
+                                     tempword, 1);
+       status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
+                                      FT1000_REG_DOORBELL);
+}
+
+static u16 get_handshake_usb(struct ft1000_usb *ft1000dev, u16 expected_value)
+{
+       u16 handshake;
+       int loopcnt;
+       u16 temp;
+       int status = 0;
+
+       loopcnt = 0;
+       handshake = 0;
+
+       while (loopcnt < 100) {
+               if (ft1000dev->usbboot == 2) {
+                       status = ft1000_read_dpram32(ft1000dev, 0,
+                                                    (u8 *)&ft1000dev->tempbuf[0], 64);
+                       for (temp = 0; temp < 16; temp++) {
+                               pr_debug("tempbuf %d = 0x%x\n",
+                                        temp, ft1000dev->tempbuf[temp]);
+                       }
+                       status = ft1000_read_dpram16(ft1000dev,
+                                                    DWNLD_MAG1_HANDSHAKE_LOC,
+                                                    (u8 *)&handshake, 1);
+                       pr_debug("handshake from read_dpram16 = 0x%x\n",
+                                handshake);
+                       if (ft1000dev->dspalive == ft1000dev->tempbuf[6]) {
+                               handshake = 0;
+                       } else {
+                               handshake = ft1000dev->tempbuf[1];
+                               ft1000dev->dspalive =
+                                       ft1000dev->tempbuf[6];
+                       }
+               } else {
+                       status = ft1000_read_dpram16(ft1000dev,
+                                                    DWNLD_MAG1_HANDSHAKE_LOC,
+                                                    (u8 *)&handshake, 1);
+               }
+
+               loopcnt++;
+               msleep(10);
+               handshake = ntohs(handshake);
+               if ((handshake == expected_value) ||
+                   (handshake == HANDSHAKE_RESET_VALUE_USB))
+                       return handshake;
+       }
+
+       return HANDSHAKE_TIMEOUT_VALUE;
+}
+
+static void put_handshake_usb(struct ft1000_usb *ft1000dev, u16 handshake_value)
+{
+       int i;
+
+       for (i = 0; i < 1000; i++)
+               ;
+}
+
+static u16 get_request_type(struct ft1000_usb *ft1000dev)
+{
+       u16 request_type;
+       int status;
+       u16 tempword;
+       u32 tempx;
+
+       if (ft1000dev->bootmode == 1) {
+               status = fix_ft1000_read_dpram32(ft1000dev,
+                                                DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx);
+               tempx = ntohl(tempx);
+       } else {
+               tempx = 0;
+               status = ft1000_read_dpram16(ft1000dev,
+                                            DWNLD_MAG1_TYPE_LOC, (u8 *)&tempword, 1);
+               tempx |= (tempword << 16);
+               tempx = ntohl(tempx);
+       }
+       request_type = (u16)tempx;
+
+       return request_type;
+}
+
+static u16 get_request_type_usb(struct ft1000_usb *ft1000dev)
+{
+       u16 request_type;
+       int status;
+       u16 tempword;
+       u32 tempx;
+
+       if (ft1000dev->bootmode == 1) {
+               status = fix_ft1000_read_dpram32(ft1000dev,
+                                                DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx);
+               tempx = ntohl(tempx);
+       } else {
+               if (ft1000dev->usbboot == 2) {
+                       tempx = ft1000dev->tempbuf[2];
+                       tempword = ft1000dev->tempbuf[3];
+               } else {
+                       tempx = 0;
+                       status = ft1000_read_dpram16(ft1000dev,
+                                                    DWNLD_MAG1_TYPE_LOC,
+                                                    (u8 *)&tempword, 1);
+               }
+               tempx |= (tempword << 16);
+               tempx = ntohl(tempx);
+       }
+       request_type = (u16)tempx;
+
+       return request_type;
+}
+
+static long get_request_value(struct ft1000_usb *ft1000dev)
+{
+       u32 value;
+       u16 tempword;
+       int status;
+
+       if (ft1000dev->bootmode == 1) {
+               status = fix_ft1000_read_dpram32(ft1000dev,
+                                                DWNLD_MAG1_SIZE_LOC, (u8 *)&value);
+               value = ntohl(value);
+       } else  {
+               status = ft1000_read_dpram16(ft1000dev,
+                                            DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 0);
+               value = tempword;
+               status = ft1000_read_dpram16(ft1000dev,
+                                            DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 1);
+               value |= (tempword << 16);
+               value = ntohl(value);
+       }
+
+       return value;
+}
+
+
+/* writes a value to DWNLD_MAG1_SIZE_LOC */
+static void put_request_value(struct ft1000_usb *ft1000dev, long lvalue)
+{
+       u32    tempx;
+       int    status;
+
+       tempx = ntohl(lvalue);
+       status = fix_ft1000_write_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC,
+                                         (u8 *)&tempx);
+}
+
+
+
+/* returns the checksum of the pseudo header */
+static u16 hdr_checksum(struct pseudo_hdr *pHdr)
+{
+       u16   *usPtr = (u16 *)pHdr;
+       u16   chksum;
+
+
+       chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
+                   usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
+
+       return chksum;
+}
+
+static int check_buffers(u16 *buff_w, u16 *buff_r, int len, int offset)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (buff_w[i] != buff_r[i + offset])
+                       return -EREMOTEIO;
+       }
+
+       return 0;
+}
+
+static int write_dpram32_and_check(struct ft1000_usb *ft1000dev,
+                                  u16 tempbuffer[], u16 dpram)
+{
+       int status;
+       u16 resultbuffer[64];
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               status = ft1000_write_dpram32(ft1000dev, dpram,
+                                             (u8 *)&tempbuffer[0], 64);
+               if (status == 0) {
+                       /* Work around for ASIC bit stuffing problem. */
+                       if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
+                               status = ft1000_write_dpram32(ft1000dev,
+                                                             dpram+12, (u8 *)&tempbuffer[24],
+                                                             64);
+                       }
+                       /* Let's check the data written */
+                       status = ft1000_read_dpram32(ft1000dev, dpram,
+                                                    (u8 *)&resultbuffer[0], 64);
+                       if ((tempbuffer[31] & 0xfe00) == 0xfe00) {
+                               if (check_buffers(tempbuffer, resultbuffer, 28,
+                                                 0)) {
+                                       pr_debug("DPRAM write failed 1 during bootloading\n");
+                                       usleep_range(9000, 11000);
+                                       break;
+                               }
+                               status = ft1000_read_dpram32(ft1000dev,
+                                                            dpram+12,
+                                                            (u8 *)&resultbuffer[0], 64);
+
+                               if (check_buffers(tempbuffer, resultbuffer, 16,
+                                                 24)) {
+                                       pr_debug("DPRAM write failed 2 during bootloading\n");
+                                       usleep_range(9000, 11000);
+                                       break;
+                               }
+                       } else {
+                               if (check_buffers(tempbuffer, resultbuffer, 32,
+                                                 0)) {
+                                       pr_debug("DPRAM write failed 3 during bootloading\n");
+                                       usleep_range(9000, 11000);
+                                       break;
+                               }
+                       }
+                       if (status == 0)
+                               break;
+               }
+       }
+       return status;
+}
+
+/* writes a block of DSP image to DPRAM
+ * Parameters:  struct ft1000_usb  - device structure
+ *              u16 **pUsFile - DSP image file pointer in u16
+ *              u8  **pUcFile - DSP image file pointer in u8
+ *              long word_length - length of the buffer to be written to DPRAM
+ */
+static int write_blk(struct ft1000_usb *ft1000dev, u16 **pUsFile, u8 **pUcFile,
+                    long word_length)
+{
+       int status = 0;
+       u16 dpram;
+       int loopcnt, i;
+       u16 tempword;
+       u16 tempbuffer[64];
+
+       /*pr_debug("start word_length = %d\n",(int)word_length); */
+       dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+       tempword = *(*pUsFile);
+       (*pUsFile)++;
+       status = ft1000_write_dpram16(ft1000dev, dpram, tempword, 0);
+       tempword = *(*pUsFile);
+       (*pUsFile)++;
+       status = ft1000_write_dpram16(ft1000dev, dpram++, tempword, 1);
+
+       *pUcFile = *pUcFile + 4;
+       word_length--;
+       tempword = (u16)word_length;
+       word_length = (word_length / 16) + 1;
+       for (; word_length > 0; word_length--) { /* In words */
+               loopcnt = 0;
+               for (i = 0; i < 32; i++) {
+                       if (tempword != 0) {
+                               tempbuffer[i++] = *(*pUsFile);
+                               (*pUsFile)++;
+                               tempbuffer[i] = *(*pUsFile);
+                               (*pUsFile)++;
+                               *pUcFile = *pUcFile + 4;
+                               loopcnt++;
+                               tempword--;
+                       } else {
+                               tempbuffer[i++] = 0;
+                               tempbuffer[i] = 0;
+                       }
+               }
+
+               /*pr_debug("loopcnt is %d\n", loopcnt); */
+               /*pr_debug("write_blk: bootmode = %d\n", bootmode); */
+               /*pr_debug("write_blk: dpram = %x\n", dpram); */
+               if (ft1000dev->bootmode == 0) {
+                       if (dpram >= 0x3F4)
+                               status = ft1000_write_dpram32(ft1000dev, dpram,
+                                                             (u8 *)&tempbuffer[0], 8);
+                       else
+                               status = ft1000_write_dpram32(ft1000dev, dpram,
+                                                             (u8 *)&tempbuffer[0], 64);
+               } else {
+                       status = write_dpram32_and_check(ft1000dev, tempbuffer,
+                                                        dpram);
+                       if (status != 0) {
+                               pr_debug("Write failed tempbuffer[31] = 0x%x\n",
+                                        tempbuffer[31]);
+                               break;
+                       }
+               }
+               dpram = dpram + loopcnt;
+       }
+       return status;
+}
+
+static void usb_dnld_complete(struct urb *urb)
+{
+       /* pr_debug("****** usb_dnld_complete\n"); */
+}
+
+/* writes a block of DSP image to DPRAM
+ * Parameters:  struct ft1000_usb  - device structure
+ *              u16 **pUsFile - DSP image file pointer in u16
+ *              u8  **pUcFile - DSP image file pointer in u8
+ *              long word_length - length of the buffer to be written to DPRAM
+ */
+static int write_blk_fifo(struct ft1000_usb *ft1000dev, u16 **pUsFile,
+                         u8 **pUcFile, long word_length)
+{
+       int byte_length;
+
+       byte_length = word_length * 4;
+
+       if (byte_length && ((byte_length % 64) == 0))
+               byte_length += 4;
+
+       if (byte_length < 64)
+               byte_length = 68;
+
+       usb_init_urb(ft1000dev->tx_urb);
+       memcpy(ft1000dev->tx_buf, *pUcFile, byte_length);
+       usb_fill_bulk_urb(ft1000dev->tx_urb,
+                         ft1000dev->dev,
+                         usb_sndbulkpipe(ft1000dev->dev,
+                                         ft1000dev->bulk_out_endpointAddr),
+                         ft1000dev->tx_buf, byte_length, usb_dnld_complete,
+                         ft1000dev);
+
+       usb_submit_urb(ft1000dev->tx_urb, GFP_ATOMIC);
+
+       *pUsFile = *pUsFile + (word_length << 1);
+       *pUcFile = *pUcFile + (word_length << 2);
+
+       return 0;
+}
+
+static int scram_start_dwnld(struct ft1000_usb *ft1000dev, u16 *hshake,
+                            u32 *state)
+{
+       int status = 0;
+
+       if (ft1000dev->usbboot)
+               *hshake = get_handshake_usb(ft1000dev, HANDSHAKE_DSP_BL_READY);
+       else
+               *hshake = get_handshake(ft1000dev, HANDSHAKE_DSP_BL_READY);
+       if (*hshake == HANDSHAKE_DSP_BL_READY) {
+               pr_debug("handshake is HANDSHAKE_DSP_BL_READY, call put_handshake(HANDSHAKE_DRIVER_READY)\n");
+               put_handshake(ft1000dev, HANDSHAKE_DRIVER_READY);
+       } else if (*hshake == HANDSHAKE_TIMEOUT_VALUE) {
+               status = -ETIMEDOUT;
+       } else {
+               pr_debug("Download error: Handshake failed\n");
+               status = -ENETRESET;
+       }
+       *state = STATE_BOOT_DWNLD;
+       return status;
+}
+
+static int request_code_segment(struct ft1000_usb *ft1000dev, u16 **s_file,
+                               u8 **c_file, const u8 *endpoint, bool boot_case)
+{
+       long word_length;
+       int status = 0;
+
+       word_length = get_request_value(ft1000dev);
+       /*pr_debug("word_length = 0x%x\n", (int)word_length); */
+       /*NdisMSleep (100); */
+       if (word_length > MAX_LENGTH) {
+               pr_debug("Download error: Max length exceeded\n");
+               return -1;
+       }
+       if ((word_length * 2 + (long)c_file) > (long)endpoint) {
+               /* Error, beyond boot code range.*/
+               pr_debug("Download error: Requested len=%d exceeds BOOT code boundary\n",
+                        (int)word_length);
+               return -1;
+       }
+       if (word_length & 0x1)
+               word_length++;
+       word_length = word_length / 2;
+
+       if (boot_case) {
+               status = write_blk(ft1000dev, s_file, c_file, word_length);
+               /*pr_debug("write_blk returned %d\n", status); */
+       } else {
+               status = write_blk_fifo(ft1000dev, s_file, c_file, word_length);
+               if (ft1000dev->usbboot == 0)
+                       ft1000dev->usbboot++;
+               if (ft1000dev->usbboot == 1)
+                       status |= ft1000_write_dpram16(ft1000dev,
+                                                      DWNLD_MAG1_PS_HDR_LOC, 0, 0);
+       }
+       return status;
+}
+
+/* Scramble downloader for Harley based ASIC via USB interface */
+int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart,
+               u32 FileLength)
+{
+       int status = 0;
+       u32 state;
+       u16 handshake;
+       struct pseudo_hdr *pseudo_header;
+       u16 pseudo_header_len;
+       long word_length;
+       u16 request;
+       u16 temp;
+
+       struct dsp_file_hdr *file_hdr;
+       struct dsp_image_info *dsp_img_info = NULL;
+       long requested_version;
+       bool correct_version;
+       struct drv_msg *mailbox_data;
+       u16 *data = NULL;
+       u16 *s_file = NULL;
+       u8 *c_file = NULL;
+       u8 *boot_end = NULL, *code_end = NULL;
+       int image;
+       long loader_code_address, loader_code_size = 0;
+       long run_address = 0, run_size = 0;
+
+       u32 templong;
+       u32 image_chksum = 0;
+
+       u16 dpram = 0;
+       u8 *pbuffer;
+       struct prov_record *pprov_record;
+       struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
+
+       ft1000dev->fcodeldr = 0;
+       ft1000dev->usbboot = 0;
+       ft1000dev->dspalive = 0xffff;
+
+       /*
+        * Get version id of file, at first 4 bytes of file, for newer files.
+        */
+
+       state = STATE_START_DWNLD;
+
+       file_hdr = pFileStart;
+
+       ft1000_write_register(ft1000dev, 0x800, FT1000_REG_MAG_WATERMARK);
+
+       s_file = (u16 *) (pFileStart + file_hdr->loader_offset);
+       c_file = (u8 *) (pFileStart + file_hdr->loader_offset);
+
+       boot_end = (u8 *) (pFileStart + file_hdr->loader_code_end);
+
+       loader_code_address = file_hdr->loader_code_address;
+       loader_code_size = file_hdr->loader_code_size;
+       correct_version = false;
+
+       while ((status == 0) && (state != STATE_DONE_FILE)) {
+               switch (state) {
+               case STATE_START_DWNLD:
+                       status = scram_start_dwnld(ft1000dev, &handshake,
+                                                  &state);
+                       break;
+
+               case STATE_BOOT_DWNLD:
+                       pr_debug("STATE_BOOT_DWNLD\n");
+                       ft1000dev->bootmode = 1;
+                       handshake = get_handshake(ft1000dev, HANDSHAKE_REQUEST);
+                       if (handshake == HANDSHAKE_REQUEST) {
+                               /*
+                                * Get type associated with the request.
+                                */
+                               request = get_request_type(ft1000dev);
+                               switch (request) {
+                               case REQUEST_RUN_ADDRESS:
+                                       pr_debug("REQUEST_RUN_ADDRESS\n");
+                                       put_request_value(ft1000dev,
+                                                         loader_code_address);
+                                       break;
+                               case REQUEST_CODE_LENGTH:
+                                       pr_debug("REQUEST_CODE_LENGTH\n");
+                                       put_request_value(ft1000dev,
+                                                         loader_code_size);
+                                       break;
+                               case REQUEST_DONE_BL:
+                                       pr_debug("REQUEST_DONE_BL\n");
+                                       /* Reposition ptrs to beginning of code section */
+                                       s_file = (u16 *) (boot_end);
+                                       c_file = (u8 *) (boot_end);
+                                       /* pr_debug("download:s_file = 0x%8x\n", (int)s_file); */
+                                       /* pr_debug("FT1000:download:c_file = 0x%8x\n", (int)c_file); */
+                                       state = STATE_CODE_DWNLD;
+                                       ft1000dev->fcodeldr = 1;
+                                       break;
+                               case REQUEST_CODE_SEGMENT:
+                                       status = request_code_segment(ft1000dev,
+                                                                     &s_file, &c_file,
+                                                                     boot_end,
+                                                                     true);
+                                       break;
+                               default:
+                                       pr_debug("Download error: Bad request type=%d in BOOT download state\n",
+                                                request);
+                                       status = -1;
+                                       break;
+                               }
+                               if (ft1000dev->usbboot)
+                                       put_handshake_usb(ft1000dev,
+                                                         HANDSHAKE_RESPONSE);
+                               else
+                                       put_handshake(ft1000dev,
+                                                     HANDSHAKE_RESPONSE);
+                       } else {
+                               pr_debug("Download error: Handshake failed\n");
+                               status = -1;
+                       }
+
+                       break;
+
+               case STATE_CODE_DWNLD:
+                       /* pr_debug("STATE_CODE_DWNLD\n"); */
+                       ft1000dev->bootmode = 0;
+                       if (ft1000dev->usbboot)
+                               handshake =
+                                       get_handshake_usb(ft1000dev,
+                                                         HANDSHAKE_REQUEST);
+                       else
+                               handshake =
+                                       get_handshake(ft1000dev, HANDSHAKE_REQUEST);
+                       if (handshake == HANDSHAKE_REQUEST) {
+                               /*
+                                * Get type associated with the request.
+                                */
+                               if (ft1000dev->usbboot)
+                                       request =
+                                               get_request_type_usb(ft1000dev);
+                               else
+                                       request = get_request_type(ft1000dev);
+                               switch (request) {
+                               case REQUEST_FILE_CHECKSUM:
+                                       pr_debug("image_chksum = 0x%8x\n",
+                                                image_chksum);
+                                       put_request_value(ft1000dev,
+                                                         image_chksum);
+                                       break;
+                               case REQUEST_RUN_ADDRESS:
+                                       pr_debug("REQUEST_RUN_ADDRESS\n");
+                                       if (correct_version) {
+                                               pr_debug("run_address = 0x%8x\n",
+                                                        (int)run_address);
+                                               put_request_value(ft1000dev,
+                                                                 run_address);
+                                       } else {
+                                               pr_debug("Download error: Got Run address request before image offset request\n");
+                                               status = -1;
+                                               break;
+                                       }
+                                       break;
+                               case REQUEST_CODE_LENGTH:
+                                       pr_debug("REQUEST_CODE_LENGTH\n");
+                                       if (correct_version) {
+                                               pr_debug("run_size = 0x%8x\n",
+                                                        (int)run_size);
+                                               put_request_value(ft1000dev,
+                                                                 run_size);
+                                       } else {
+                                               pr_debug("Download error: Got Size request before image offset request\n");
+                                               status = -1;
+                                               break;
+                                       }
+                                       break;
+                               case REQUEST_DONE_CL:
+                                       ft1000dev->usbboot = 3;
+                                       /* Reposition ptrs to beginning of provisioning section */
+                                       s_file =
+                                               (u16 *) (pFileStart +
+                                                        file_hdr->commands_offset);
+                                       c_file =
+                                               (u8 *) (pFileStart +
+                                                       file_hdr->commands_offset);
+                                       state = STATE_DONE_DWNLD;
+                                       break;
+                               case REQUEST_CODE_SEGMENT:
+                                       /* pr_debug("REQUEST_CODE_SEGMENT - CODELOADER\n"); */
+                                       if (!correct_version) {
+                                               pr_debug("Download error: Got Code Segment request before image offset request\n");
+                                               status = -1;
+                                               break;
+                                       }
+
+                                       status = request_code_segment(ft1000dev,
+                                                                     &s_file, &c_file,
+                                                                     code_end,
+                                                                     false);
+
+                                       break;
+
+                               case REQUEST_MAILBOX_DATA:
+                                       pr_debug("REQUEST_MAILBOX_DATA\n");
+                                       /* Convert length from byte count to word count. Make sure we round up. */
+                                       word_length =
+                                               (long)(pft1000info->DSPInfoBlklen +
+                                                      1) / 2;
+                                       put_request_value(ft1000dev,
+                                                         word_length);
+                                       mailbox_data =
+                                               (struct drv_msg *)&(pft1000info->
+                                                                   DSPInfoBlk[0]);
+                                       /*
+                                        * Position ASIC DPRAM auto-increment pointer.
+                                        */
+
+                                       data = (u16 *)&mailbox_data->data[0];
+                                       dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+                                       if (word_length & 0x1)
+                                               word_length++;
+
+                                       word_length = word_length / 2;
+
+                                       for (; word_length > 0; word_length--) {        /* In words */
+
+                                               templong = *data++;
+                                               templong |= (*data++ << 16);
+                                               status =
+                                                       fix_ft1000_write_dpram32
+                                                       (ft1000dev, dpram++,
+                                                        (u8 *)&templong);
+
+                                       }
+                                       break;
+
+                               case REQUEST_VERSION_INFO:
+                                       pr_debug("REQUEST_VERSION_INFO\n");
+                                       word_length =
+                                               file_hdr->version_data_size;
+                                       put_request_value(ft1000dev,
+                                                         word_length);
+                                       /*
+                                        * Position ASIC DPRAM auto-increment pointer.
+                                        */
+
+                                       s_file =
+                                               (u16 *) (pFileStart +
+                                                        file_hdr->
+                                                        version_data_offset);
+
+                                       dpram = (u16)DWNLD_MAG1_PS_HDR_LOC;
+                                       if (word_length & 0x1)
+                                               word_length++;
+
+                                       word_length = word_length / 2;
+
+                                       for (; word_length > 0; word_length--) {        /* In words */
+
+                                               templong = ntohs(*s_file++);
+                                               temp = ntohs(*s_file++);
+                                               templong |= (temp << 16);
+                                               status =
+                                                       fix_ft1000_write_dpram32
+                                                       (ft1000dev, dpram++,
+                                                        (u8 *)&templong);
+
+                                       }
+                                       break;
+
+                               case REQUEST_CODE_BY_VERSION:
+                                       pr_debug("REQUEST_CODE_BY_VERSION\n");
+                                       correct_version = false;
+                                       requested_version =
+                                               get_request_value(ft1000dev);
+
+                                       dsp_img_info =
+                                               (struct dsp_image_info *)(pFileStart
+                                                                         +
+                                                                         sizeof
+                                                                         (struct
+                                                                          dsp_file_hdr));
+
+                                       for (image = 0;
+                                            image < file_hdr->nDspImages;
+                                            image++) {
+
+                                               if (dsp_img_info->version ==
+                                                   requested_version) {
+                                                       correct_version = true;
+                                                       pr_debug("correct_version is TRUE\n");
+                                                       s_file =
+                                                               (u16 *) (pFileStart
+                                                                        +
+                                                                        dsp_img_info->
+                                                                        begin_offset);
+                                                       c_file =
+                                                               (u8 *) (pFileStart +
+                                                                       dsp_img_info->
+                                                                       begin_offset);
+                                                       code_end =
+                                                               (u8 *) (pFileStart +
+                                                                       dsp_img_info->
+                                                                       end_offset);
+                                                       run_address =
+                                                               dsp_img_info->
+                                                               run_address;
+                                                       run_size =
+                                                               dsp_img_info->
+                                                               image_size;
+                                                       image_chksum =
+                                                               (u32)dsp_img_info->
+                                                               checksum;
+                                                       break;
+                                               }
+                                               dsp_img_info++;
+
+                                       }       /* end of for */
+
+                                       if (!correct_version) {
+                                               /*
+                                                * Error, beyond boot code range.
+                                                */
+                                               pr_debug("Download error: Bad Version Request = 0x%x.\n",
+                                                        (int)requested_version);
+                                               status = -1;
+                                               break;
+                                       }
+                                       break;
+
+                               default:
+                                       pr_debug("Download error: Bad request type=%d in CODE download state.\n",
+                                                request);
+                                       status = -1;
+                                       break;
+                               }
+                               if (ft1000dev->usbboot)
+                                       put_handshake_usb(ft1000dev,
+                                                         HANDSHAKE_RESPONSE);
+                               else
+                                       put_handshake(ft1000dev,
+                                                     HANDSHAKE_RESPONSE);
+                       } else {
+                               pr_debug("Download error: Handshake failed\n");
+                               status = -1;
+                       }
+
+                       break;
+
+               case STATE_DONE_DWNLD:
+                       pr_debug("Code loader is done...\n");
+                       state = STATE_SECTION_PROV;
+                       break;
+
+               case STATE_SECTION_PROV:
+                       pr_debug("STATE_SECTION_PROV\n");
+                       pseudo_header = (struct pseudo_hdr *)c_file;
+
+                       if (pseudo_header->checksum ==
+                           hdr_checksum(pseudo_header)) {
+                               if (pseudo_header->portdest !=
+                                   0x80 /* Dsp OAM */) {
+                                       state = STATE_DONE_PROV;
+                                       break;
+                               }
+                               pseudo_header_len = ntohs(pseudo_header->length);       /* Byte length for PROV records */
+
+                               /* Get buffer for provisioning data */
+                               pbuffer =
+                                       kmalloc(pseudo_header_len +
+                                                sizeof(struct pseudo_hdr),
+                                               GFP_ATOMIC);
+                               if (pbuffer) {
+                                       memcpy(pbuffer, c_file,
+                                              (u32) (pseudo_header_len +
+                                                     sizeof(struct
+                                                            pseudo_hdr)));
+                                       /* link provisioning data */
+                                       pprov_record =
+                                               kmalloc(sizeof(struct prov_record),
+                                                       GFP_ATOMIC);
+                                       if (pprov_record) {
+                                               pprov_record->pprov_data =
+                                                       pbuffer;
+                                               list_add_tail(&pprov_record->
+                                                             list,
+                                                             &pft1000info->
+                                                             prov_list);
+                                               /* Move to next entry if available */
+                                               c_file =
+                                                       (u8 *) ((unsigned long)
+                                                               c_file +
+                                                               (u32) ((pseudo_header_len + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
+                                               if ((unsigned long)(c_file) -
+                                                   (unsigned long)(pFileStart)
+                                                   >=
+                                                   (unsigned long)FileLength) {
+                                                       state = STATE_DONE_FILE;
+                                               }
+                                       } else {
+                                               kfree(pbuffer);
+                                               status = -1;
+                                       }
+                               } else {
+                                       status = -1;
+                               }
+                       } else {
+                               /* Checksum did not compute */
+                               status = -1;
+                       }
+                       pr_debug("after STATE_SECTION_PROV, state = %d, status= %d\n",
+                                state, status);
+                       break;
+
+               case STATE_DONE_PROV:
+                       pr_debug("STATE_DONE_PROV\n");
+                       state = STATE_DONE_FILE;
+                       break;
+
+               default:
+                       status = -1;
+                       break;
+               }               /* End Switch */
+
+               if (status != 0)
+                       break;
+
+/****
+ // Check if Card is present
+ status = Harley_Read_Register(&temp, FT1000_REG_SUP_IMASK);
+ if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0x0000) ) {
+ break;
+ }
+
+ status = Harley_Read_Register(&temp, FT1000_REG_ASIC_ID);
+ if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0xffff) ) {
+ break;
+ }
+****/
+
+       }                       /* End while */
+
+       pr_debug("Download exiting with status = 0x%8x\n", status);
+       ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX,
+                             FT1000_REG_DOORBELL);
+
+       return status;
+}