blob: 4dd456fbab9b96233a3d8793a019386c4c08685a [file] [log] [blame]
//=====================================================
// CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
//
//
// This file is part of Express Card USB Driver
//
// $Id:
//====================================================
// 20090926; aelias; removed compiler warnings; ubuntu 9.04; 2.6.28-15-generic
#include <linux/init.h>
#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;
};
//---------------------------------------------------------------------------
// Function: check_usb_db
//
// Parameters: struct ft1000_device - device structure
//
// Returns: 0 - success
//
// Description: This function checks if the doorbell register is cleared
//
// Notes:
//
//---------------------------------------------------------------------------
static ULONG check_usb_db (struct ft1000_device *ft1000dev)
{
int loopcnt;
USHORT temp;
ULONG status;
loopcnt = 0;
while (loopcnt < 10)
{
status = ft1000_read_register (ft1000dev, &temp, FT1000_REG_DOORBELL);
DEBUG("check_usb_db: read FT1000_REG_DOORBELL value is %x\n", temp);
if (temp & 0x0080)
{
DEBUG("FT1000: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;
}
else
{
loopcnt++;
msleep (10);
}
} //end of while
loopcnt = 0;
while (loopcnt < 20)
{
status = ft1000_read_register (ft1000dev, &temp, FT1000_REG_DOORBELL);
DEBUG("FT1000:check_usb_db:Doorbell = 0x%x\n", temp);
if (temp & 0x8000)
{
loopcnt++;
msleep (10);
}
else
{
DEBUG("check_usb_db: door bell is cleared, return 0\n");
return 0;
}
#if 0
// Check if Card is present
status = ft1000_read_register (ft1000dev, &temp, FT1000_REG_SUP_IMASK);
if (temp == 0x0000) {
break;
}
status = ft1000_read_register (ft1000dev, &temp, FT1000_REG_ASIC_ID);
if (temp == 0xffff) {
break;
}
#endif
}
return HANDSHAKE_MAG_TIMEOUT_VALUE;
}
//---------------------------------------------------------------------------
// Function: get_handshake
//
// Parameters: struct ft1000_device - device structure
// USHORT expected_value - the handshake value expected
//
// Returns: handshakevalue - success
// HANDSHAKE_TIMEOUT_VALUE - failure
//
// Description: This function gets the handshake and compare with the expected value
//
// Notes:
//
//---------------------------------------------------------------------------
static USHORT get_handshake(struct ft1000_device *ft1000dev, USHORT expected_value)
{
USHORT handshake;
int loopcnt;
ULONG status=0;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
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);
//DEBUG("FT1000:get_handshake:doorbell = 0x%x\n", temp);
if (pft1000info->fcodeldr)
{
DEBUG(" get_handshake: fcodeldr is %d\n", pft1000info->fcodeldr);
pft1000info->fcodeldr = 0;
status = check_usb_db(ft1000dev);
if (status != STATUS_SUCCESS)
{
DEBUG("get_handshake: check_usb_db failed\n");
status = STATUS_FAILURE;
break;
}
status = ft1000_write_register (ft1000dev, FT1000_DB_DNLD_RX, FT1000_REG_DOORBELL);
}
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, (PUCHAR)&handshake, 1);
//DEBUG("get_handshake: handshake is %x\n", tempx);
handshake = ntohs(handshake);
//DEBUG("get_handshake: after swap, handshake is %x\n", handshake);
if (status)
return HANDSHAKE_TIMEOUT_VALUE;
//DEBUG("get_handshake: handshake= %x\n", handshake);
if ((handshake == expected_value) || (handshake == HANDSHAKE_RESET_VALUE_USB))
{
//DEBUG("get_handshake: return handshake %x\n", handshake);
return handshake;
}
else
{
loopcnt++;
msleep (10);
}
//DEBUG("HANDSHKE LOOP: %d\n", loopcnt);
}
//DEBUG("get_handshake: return handshake time out\n");
return HANDSHAKE_TIMEOUT_VALUE;
}
//---------------------------------------------------------------------------
// Function: put_handshake
//
// Parameters: struct ft1000_device - device structure
// USHORT handshake_value - handshake to be written
//
// Returns: none
//
// Description: This function write the handshake value to the handshake location
// in DPRAM
//
// Notes:
//
//---------------------------------------------------------------------------
static void put_handshake(struct ft1000_device *ft1000dev,USHORT handshake_value)
{
ULONG tempx;
USHORT tempword;
ULONG status;
tempx = (ULONG)handshake_value;
tempx = ntohl(tempx);
tempword = (USHORT)(tempx & 0xffff);
status = ft1000_write_dpram16 (ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, tempword, 0);
tempword = (USHORT)(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 USHORT get_handshake_usb(struct ft1000_device *ft1000dev, USHORT expected_value)
{
USHORT handshake;
int loopcnt;
USHORT temp;
ULONG status=0;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
loopcnt = 0;
handshake = 0;
while (loopcnt < 100)
{
if (pft1000info->usbboot == 2) {
status = ft1000_read_dpram32 (ft1000dev, 0, (PUCHAR)&(pft1000info->tempbuf[0]), 64);
for (temp=0; temp<16; temp++)
DEBUG("tempbuf %d = 0x%x\n", temp, pft1000info->tempbuf[temp]);
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, (PUCHAR)&handshake, 1);
DEBUG("handshake from read_dpram16 = 0x%x\n", handshake);
if (pft1000info->dspalive == pft1000info->tempbuf[6])
handshake = 0;
else {
handshake = pft1000info->tempbuf[1];
pft1000info->dspalive = pft1000info->tempbuf[6];
}
}
else {
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, (PUCHAR)&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_device *ft1000dev,USHORT handshake_value)
{
int i;
for (i=0; i<1000; i++);
}
//---------------------------------------------------------------------------
// Function: get_request_type
//
// Parameters: struct ft1000_device - device structure
//
// Returns: request type - success
//
// Description: This function returns the request type
//
// Notes:
//
//---------------------------------------------------------------------------
static USHORT get_request_type(struct ft1000_device *ft1000dev)
{
USHORT request_type;
ULONG status;
USHORT tempword;
ULONG tempx;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
if ( pft1000info->bootmode == 1)
{
status = fix_ft1000_read_dpram32 (ft1000dev, DWNLD_MAG1_TYPE_LOC, (PUCHAR)&tempx);
tempx = ntohl(tempx);
}
else
{
tempx = 0;
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_TYPE_LOC, (PUCHAR)&tempword, 1);
tempx |= (tempword << 16);
tempx = ntohl(tempx);
}
request_type = (USHORT)tempx;
//DEBUG("get_request_type: request_type is %x\n", request_type);
return request_type;
}
static USHORT get_request_type_usb(struct ft1000_device *ft1000dev)
{
USHORT request_type;
ULONG status;
USHORT tempword;
ULONG tempx;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
if ( pft1000info->bootmode == 1)
{
status = fix_ft1000_read_dpram32 (ft1000dev, DWNLD_MAG1_TYPE_LOC, (PUCHAR)&tempx);
tempx = ntohl(tempx);
}
else
{
if (pft1000info->usbboot == 2) {
tempx = pft1000info->tempbuf[2];
tempword = pft1000info->tempbuf[3];
}
else {
tempx = 0;
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_TYPE_LOC, (PUCHAR)&tempword, 1);
}
tempx |= (tempword << 16);
tempx = ntohl(tempx);
}
request_type = (USHORT)tempx;
//DEBUG("get_request_type: request_type is %x\n", request_type);
return request_type;
}
//---------------------------------------------------------------------------
// Function: get_request_value
//
// Parameters: struct ft1000_device - device structure
//
// Returns: request value - success
//
// Description: This function returns the request value
//
// Notes:
//
//---------------------------------------------------------------------------
static long get_request_value(struct ft1000_device *ft1000dev)
{
ULONG value;
USHORT tempword;
ULONG status;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
if ( pft1000info->bootmode == 1)
{
status = fix_ft1000_read_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC, (PUCHAR)&value);
value = ntohl(value);
}
else
{
status = ft1000_read_dpram16(ft1000dev, DWNLD_MAG1_SIZE_LOC, (PUCHAR)&tempword, 0);
value = tempword;
status = ft1000_read_dpram16(ft1000dev, DWNLD_MAG1_SIZE_LOC, (PUCHAR)&tempword, 1);
value |= (tempword << 16);
value = ntohl(value);
}
//DEBUG("get_request_value: value is %x\n", value);
return value;
}
#if 0
static long get_request_value_usb(struct ft1000_device *ft1000dev)
{
ULONG value;
USHORT tempword;
ULONG status;
struct ft1000_info * pft1000info = netdev_priv(ft1000dev->net);
if (pft1000info->usbboot == 2) {
value = pft1000info->tempbuf[4];
tempword = pft1000info->tempbuf[5];
}
else {
value = 0;
status = ft1000_read_dpram16(ft1000dev, DWNLD_MAG1_SIZE_LOC, (PUCHAR)&tempword, 1);
}
value |= (tempword << 16);
value = ntohl(value);
if (pft1000info->usbboot == 1)
pft1000info->usbboot = 2;
//DEBUG("get_request_value_usb: value is %x\n", value);
return value;
}
#endif
//---------------------------------------------------------------------------
// Function: put_request_value
//
// Parameters: struct ft1000_device - device structure
// long lvalue - value to be put into DPRAM location DWNLD_MAG1_SIZE_LOC
//
// Returns: none
//
// Description: This function writes a value to DWNLD_MAG1_SIZE_LOC
//
// Notes:
//
//---------------------------------------------------------------------------
static void put_request_value(struct ft1000_device *ft1000dev, long lvalue)
{
ULONG tempx;
ULONG status;
tempx = ntohl(lvalue);
status = fix_ft1000_write_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC, (PUCHAR)&tempx);
//DEBUG("put_request_value: value is %x\n", lvalue);
}
//---------------------------------------------------------------------------
// Function: hdr_checksum
//
// Parameters: struct pseudo_hdr *pHdr - Pseudo header pointer
//
// Returns: checksum - success
//
// Description: This function returns the checksum of the pseudo header
//
// Notes:
//
//---------------------------------------------------------------------------
static USHORT hdr_checksum(struct pseudo_hdr *pHdr)
{
USHORT *usPtr = (USHORT *)pHdr;
USHORT chksum;
chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
usPtr[4]) ^ usPtr[5]) ^ usPtr[6]);
return chksum;
}
//---------------------------------------------------------------------------
// Function: write_blk
//
// Parameters: struct ft1000_device - device structure
// USHORT **pUsFile - DSP image file pointer in USHORT
// UCHAR **pUcFile - DSP image file pointer in UCHAR
// long word_length - lenght of the buffer to be written
// to DPRAM
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function writes a block of DSP image to DPRAM
//
// Notes:
//
//---------------------------------------------------------------------------
static ULONG write_blk (struct ft1000_device *ft1000dev, USHORT **pUsFile, UCHAR **pUcFile, long word_length)
{
ULONG Status = STATUS_SUCCESS;
USHORT dpram;
long temp_word_length;
int loopcnt, i, j;
USHORT *pTempFile;
USHORT tempword;
USHORT tempbuffer[64];
USHORT resultbuffer[64];
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
//DEBUG("FT1000:download:start word_length = %d\n",(int)word_length);
dpram = (USHORT)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 = (USHORT)word_length;
word_length = (word_length / 16) + 1;
pTempFile = *pUsFile;
temp_word_length = word_length;
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;
}
}
//DEBUG("write_blk: loopcnt is %d\n", loopcnt);
//DEBUG("write_blk: bootmode = %d\n", bootmode);
//DEBUG("write_blk: dpram = %x\n", dpram);
if (pft1000info->bootmode == 0)
{
if (dpram >= 0x3F4)
Status = ft1000_write_dpram32 (ft1000dev, dpram, (PUCHAR)&tempbuffer[0], 8);
else
Status = ft1000_write_dpram32 (ft1000dev, dpram, (PUCHAR)&tempbuffer[0], 64);
}
else
{
for (j=0; j<10; j++)
{
Status = ft1000_write_dpram32 (ft1000dev, dpram, (PUCHAR)&tempbuffer[0], 64);
if (Status == STATUS_SUCCESS)
{
// Work around for ASIC bit stuffing problem.
if ( (tempbuffer[31] & 0xfe00) == 0xfe00)
{
Status = ft1000_write_dpram32(ft1000dev, dpram+12, (PUCHAR)&tempbuffer[24], 64);
}
// Let's check the data written
Status = ft1000_read_dpram32 (ft1000dev, dpram, (PUCHAR)&resultbuffer[0], 64);
if ( (tempbuffer[31] & 0xfe00) == 0xfe00)
{
for (i=0; i<28; i++)
{
if (resultbuffer[i] != tempbuffer[i])
{
//NdisMSleep (100);
DEBUG("FT1000:download:DPRAM write failed 1 during bootloading\n");
msleep(10);
Status = STATUS_FAILURE;
break;
}
}
Status = ft1000_read_dpram32 (ft1000dev, dpram+12, (PUCHAR)&resultbuffer[0], 64);
for (i=0; i<16; i++)
{
if (resultbuffer[i] != tempbuffer[i+24])
{
//NdisMSleep (100);
DEBUG("FT1000:download:DPRAM write failed 2 during bootloading\n");
msleep(10);
Status = STATUS_FAILURE;
break;
}
}
}
else
{
for (i=0; i<32; i++)
{
if (resultbuffer[i] != tempbuffer[i])
{
//NdisMSleep (100);
DEBUG("FT1000:download:DPRAM write failed 3 during bootloading\n");
msleep(10);
Status = STATUS_FAILURE;
break;
}
}
}
if (Status == STATUS_SUCCESS)
break;
}
}
if (Status != STATUS_SUCCESS)
{
DEBUG("FT1000:download:Write failed tempbuffer[31] = 0x%x\n", tempbuffer[31]);
break;
}
}
dpram = dpram + loopcnt;
}
return Status;
}
static void usb_dnld_complete (struct urb *urb)
{
//DEBUG("****** usb_dnld_complete\n");
}
//---------------------------------------------------------------------------
// Function: write_blk_fifo
//
// Parameters: struct ft1000_device - device structure
// USHORT **pUsFile - DSP image file pointer in USHORT
// UCHAR **pUcFile - DSP image file pointer in UCHAR
// long word_length - lenght of the buffer to be written
// to DPRAM
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function writes a block of DSP image to DPRAM
//
// Notes:
//
//---------------------------------------------------------------------------
static ULONG write_blk_fifo (struct ft1000_device *ft1000dev, USHORT **pUsFile, UCHAR **pUcFile, long word_length)
{
ULONG Status = STATUS_SUCCESS;
int byte_length;
long aligncnt;
byte_length = word_length * 4;
if (byte_length % 4)
aligncnt = 4 - (byte_length % 4);
else
aligncnt = 0;
byte_length += aligncnt;
if (byte_length && ((byte_length % 64) == 0)) {
byte_length += 4;
}
if (byte_length < 64)
byte_length = 68;
#if 0
pblk = kzalloc(byte_length, GFP_KERNEL);
memcpy (pblk, *pUcFile, byte_length);
pipe = usb_sndbulkpipe (ft1000dev->dev, ft1000dev->bulk_out_endpointAddr);
Status = usb_bulk_msg (ft1000dev->dev,
pipe,
pblk,
byte_length,
&cnt,
10);
DEBUG("write_blk_fifo Status = 0x%8x Bytes Transfer = %d Data = 0x%x\n", Status, cnt, *pblk);
kfree(pblk);
#else
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,
(void*)ft1000dev);
usb_submit_urb(ft1000dev->tx_urb, GFP_ATOMIC);
#endif
*pUsFile = *pUsFile + (word_length << 1);
*pUcFile = *pUcFile + (word_length << 2);
return Status;
}
//---------------------------------------------------------------------------
//
// Function: scram_dnldr
//
// Synopsis: Scramble downloader for Harley based ASIC via USB interface
//
// Arguments: pFileStart - pointer to start of file
// FileLength - file length
//
// Returns: status - return code
//---------------------------------------------------------------------------
u16 scram_dnldr(struct ft1000_device *ft1000dev, void *pFileStart, ULONG FileLength)
{
u16 Status = STATUS_SUCCESS;
UINT uiState;
USHORT handshake;
struct pseudo_hdr *pHdr;
USHORT usHdrLength;
long word_length;
USHORT request;
USHORT temp;
USHORT tempword;
struct dsp_file_hdr *pFileHdr5;
struct dsp_image_info *pDspImageInfoV6 = NULL;
long requested_version;
BOOLEAN bGoodVersion;
struct drv_msg *pMailBoxData;
USHORT *pUsData = NULL;
USHORT *pUsFile = NULL;
UCHAR *pUcFile = NULL;
UCHAR *pBootEnd = NULL, *pCodeEnd= NULL;
int imageN;
long loader_code_address, loader_code_size = 0;
long run_address = 0, run_size = 0;
ULONG templong;
ULONG image_chksum = 0;
USHORT dpram = 0;
PUCHAR pbuffer;
struct prov_record *pprov_record;
struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net);
DEBUG("Entered scram_dnldr...\n");
pft1000info->fcodeldr = 0;
pft1000info->usbboot = 0;
pft1000info->dspalive = 0xffff;
//
// Get version id of file, at first 4 bytes of file, for newer files.
//
uiState = STATE_START_DWNLD;
pFileHdr5 = (struct dsp_file_hdr *)pFileStart;
ft1000_write_register (ft1000dev, 0x800, FT1000_REG_MAG_WATERMARK);
pUsFile = (USHORT *)(pFileStart + pFileHdr5->loader_offset);
pUcFile = (UCHAR *)(pFileStart + pFileHdr5->loader_offset);
pBootEnd = (UCHAR *)(pFileStart + pFileHdr5->loader_code_end);
loader_code_address = pFileHdr5->loader_code_address;
loader_code_size = pFileHdr5->loader_code_size;
bGoodVersion = FALSE;
while ((Status == STATUS_SUCCESS) && (uiState != STATE_DONE_FILE))
{
switch (uiState)
{
case STATE_START_DWNLD:
DEBUG("FT1000:STATE_START_DWNLD\n");
if (pft1000info->usbboot)
handshake = get_handshake_usb(ft1000dev, HANDSHAKE_DSP_BL_READY);
else
handshake = get_handshake(ft1000dev, HANDSHAKE_DSP_BL_READY);
if (handshake == HANDSHAKE_DSP_BL_READY)
{
DEBUG("scram_dnldr: handshake is HANDSHAKE_DSP_BL_READY, call put_handshake(HANDSHAKE_DRIVER_READY)\n");
put_handshake(ft1000dev, HANDSHAKE_DRIVER_READY);
}
else
{
DEBUG("FT1000:download:Download error: Handshake failed\n");
Status = STATUS_FAILURE;
}
uiState = STATE_BOOT_DWNLD;
break;
case STATE_BOOT_DWNLD:
DEBUG("FT1000:STATE_BOOT_DWNLD\n");
pft1000info->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:
DEBUG("FT1000:REQUEST_RUN_ADDRESS\n");
put_request_value(ft1000dev, loader_code_address);
break;
case REQUEST_CODE_LENGTH:
DEBUG("FT1000:REQUEST_CODE_LENGTH\n");
put_request_value(ft1000dev, loader_code_size);
break;
case REQUEST_DONE_BL:
DEBUG("FT1000:REQUEST_DONE_BL\n");
/* Reposition ptrs to beginning of code section */
pUsFile = (USHORT *)(pBootEnd);
pUcFile = (UCHAR *)(pBootEnd);
//DEBUG("FT1000:download:pUsFile = 0x%8x\n", (int)pUsFile);
//DEBUG("FT1000:download:pUcFile = 0x%8x\n", (int)pUcFile);
uiState = STATE_CODE_DWNLD;
pft1000info->fcodeldr = 1;
break;
case REQUEST_CODE_SEGMENT:
//DEBUG("FT1000:REQUEST_CODE_SEGMENT\n");
word_length = get_request_value(ft1000dev);
//DEBUG("FT1000:word_length = 0x%x\n", (int)word_length);
//NdisMSleep (100);
if (word_length > MAX_LENGTH)
{
DEBUG("FT1000:download:Download error: Max length exceeded\n");
Status = STATUS_FAILURE;
break;
}
if ( (word_length*2 + pUcFile) > pBootEnd)
{
/*
* Error, beyond boot code range.
*/
DEBUG("FT1000:download:Download error: Requested len=%d exceeds BOOT code boundry.\n",
(int)word_length);
Status = STATUS_FAILURE;
break;
}
/*
* Position ASIC DPRAM auto-increment pointer.
*/
dpram = (USHORT)DWNLD_MAG1_PS_HDR_LOC;
if (word_length & 0x1)
word_length++;
word_length = word_length / 2;
Status = write_blk(ft1000dev, &pUsFile, &pUcFile, word_length);
//DEBUG("write_blk returned %d\n", Status);
break;
default:
DEBUG("FT1000:download:Download error: Bad request type=%d in BOOT download state.\n",request);
Status = STATUS_FAILURE;
break;
}
if (pft1000info->usbboot)
put_handshake_usb(ft1000dev, HANDSHAKE_RESPONSE);
else
put_handshake(ft1000dev, HANDSHAKE_RESPONSE);
}
else
{
DEBUG("FT1000:download:Download error: Handshake failed\n");
Status = STATUS_FAILURE;
}
break;
case STATE_CODE_DWNLD:
//DEBUG("FT1000:STATE_CODE_DWNLD\n");
pft1000info->bootmode = 0;
if (pft1000info->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 (pft1000info->usbboot)
request = get_request_type_usb(ft1000dev);
else
request = get_request_type(ft1000dev);
switch (request)
{
case REQUEST_FILE_CHECKSUM:
DEBUG("FT1000:download:image_chksum = 0x%8x\n", image_chksum);
put_request_value(ft1000dev, image_chksum);
break;
case REQUEST_RUN_ADDRESS:
DEBUG("FT1000:download: REQUEST_RUN_ADDRESS\n");
if (bGoodVersion)
{
DEBUG("FT1000:download:run_address = 0x%8x\n", (int)run_address);
put_request_value(ft1000dev, run_address);
}
else
{
DEBUG("FT1000:download:Download error: Got Run address request before image offset request.\n");
Status = STATUS_FAILURE;
break;
}
break;
case REQUEST_CODE_LENGTH:
DEBUG("FT1000:download:REQUEST_CODE_LENGTH\n");
if (bGoodVersion)
{
DEBUG("FT1000:download:run_size = 0x%8x\n", (int)run_size);
put_request_value(ft1000dev, run_size);
}
else
{
DEBUG("FT1000:download:Download error: Got Size request before image offset request.\n");
Status = STATUS_FAILURE;
break;
}
break;
case REQUEST_DONE_CL:
pft1000info->usbboot = 3;
/* Reposition ptrs to beginning of provisioning section */
pUsFile = (USHORT *)(pFileStart + pFileHdr5->commands_offset);
pUcFile = (UCHAR *)(pFileStart + pFileHdr5->commands_offset);
uiState = STATE_DONE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
//DEBUG("FT1000:download: REQUEST_CODE_SEGMENT - CODELOADER\n");
if (!bGoodVersion)
{
DEBUG("FT1000:download:Download error: Got Code Segment request before image offset request.\n");
Status = STATUS_FAILURE;
break;
}
#if 0
word_length = get_request_value_usb(ft1000dev);
//DEBUG("FT1000:download:word_length = %d\n", (int)word_length);
if (word_length > MAX_LENGTH/2)
#else
word_length = get_request_value(ft1000dev);
//DEBUG("FT1000:download:word_length = %d\n", (int)word_length);
if (word_length > MAX_LENGTH)
#endif
{
DEBUG("FT1000:download:Download error: Max length exceeded\n");
Status = STATUS_FAILURE;
break;
}
if ( (word_length*2 + pUcFile) > pCodeEnd)
{
/*
* Error, beyond boot code range.
*/
DEBUG("FT1000:download:Download error: Requested len=%d exceeds DSP code boundry.\n",
(int)word_length);
Status = STATUS_FAILURE;
break;
}
/*
* Position ASIC DPRAM auto-increment pointer.
*/
dpram = (USHORT)DWNLD_MAG1_PS_HDR_LOC;
if (word_length & 0x1)
word_length++;
word_length = word_length / 2;
write_blk_fifo (ft1000dev, &pUsFile, &pUcFile, word_length);
if (pft1000info->usbboot == 0)
pft1000info->usbboot++;
if (pft1000info->usbboot == 1) {
tempword = 0;
ft1000_write_dpram16 (ft1000dev, DWNLD_MAG1_PS_HDR_LOC, tempword, 0);
}
break;
case REQUEST_MAILBOX_DATA:
DEBUG("FT1000:download: 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);
pMailBoxData = (struct drv_msg *)&(pft1000info->DSPInfoBlk[0]);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
pUsData = (USHORT *)&pMailBoxData->data[0];
dpram = (USHORT)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 = *pUsData++;
templong |= (*pUsData++ << 16);
Status = fix_ft1000_write_dpram32 (ft1000dev, dpram++, (PUCHAR)&templong);
}
break;
case REQUEST_VERSION_INFO:
DEBUG("FT1000:download:REQUEST_VERSION_INFO\n");
word_length = pFileHdr5->version_data_size;
put_request_value(ft1000dev, word_length);
/*
* Position ASIC DPRAM auto-increment pointer.
*/
pUsFile = (USHORT *)(pFileStart + pFileHdr5->version_data_offset);
dpram = (USHORT)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(*pUsFile++);
temp = ntohs(*pUsFile++);
templong |= (temp << 16);
Status = fix_ft1000_write_dpram32 (ft1000dev, dpram++, (PUCHAR)&templong);
}
break;
case REQUEST_CODE_BY_VERSION:
DEBUG("FT1000:download:REQUEST_CODE_BY_VERSION\n");
bGoodVersion = FALSE;
requested_version = get_request_value(ft1000dev);
pDspImageInfoV6 = (struct dsp_image_info *)(pFileStart + sizeof(struct dsp_file_hdr ));
for (imageN = 0; imageN < pFileHdr5->nDspImages; imageN++)
{
temp = (USHORT)(pDspImageInfoV6->version);
templong = temp;
temp = (USHORT)(pDspImageInfoV6->version >> 16);
templong |= (temp << 16);
if (templong == (ULONG)requested_version)
{
bGoodVersion = TRUE;
DEBUG("FT1000:download: bGoodVersion is TRUE\n");
pUsFile = (USHORT *)(pFileStart + pDspImageInfoV6->begin_offset);
pUcFile = (UCHAR *)(pFileStart + pDspImageInfoV6->begin_offset);
pCodeEnd = (UCHAR *)(pFileStart + pDspImageInfoV6->end_offset);
run_address = pDspImageInfoV6->run_address;
run_size = pDspImageInfoV6->image_size;
image_chksum = (ULONG)pDspImageInfoV6->checksum;
break;
}
pDspImageInfoV6++;
} //end of for
if (!bGoodVersion)
{
/*
* Error, beyond boot code range.
*/
DEBUG("FT1000:download:Download error: Bad Version Request = 0x%x.\n",(int)requested_version);
Status = STATUS_FAILURE;
break;
}
break;
default:
DEBUG("FT1000:download:Download error: Bad request type=%d in CODE download state.\n",request);
Status = STATUS_FAILURE;
break;
}
if (pft1000info->usbboot)
put_handshake_usb(ft1000dev, HANDSHAKE_RESPONSE);
else
put_handshake(ft1000dev, HANDSHAKE_RESPONSE);
}
else
{
DEBUG("FT1000:download:Download error: Handshake failed\n");
Status = STATUS_FAILURE;
}
break;
case STATE_DONE_DWNLD:
DEBUG("FT1000:download:Code loader is done...\n");
uiState = STATE_SECTION_PROV;
break;
case STATE_SECTION_PROV:
DEBUG("FT1000:download:STATE_SECTION_PROV\n");
pHdr = (struct pseudo_hdr *)pUcFile;
if (pHdr->checksum == hdr_checksum(pHdr))
{
if (pHdr->portdest != 0x80 /* Dsp OAM */)
{
uiState = STATE_DONE_PROV;
break;
}
usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
// Get buffer for provisioning data
pbuffer = kmalloc((usHdrLength + sizeof(struct pseudo_hdr)), GFP_ATOMIC);
if (pbuffer) {
memcpy(pbuffer, (void *)pUcFile, (UINT)(usHdrLength + 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
pUcFile = (UCHAR *)((unsigned long)pUcFile + (UINT)((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
if ( (unsigned long)(pUcFile) - (unsigned long)(pFileStart) >= (unsigned long)FileLength) {
uiState = STATE_DONE_FILE;
}
}
else {
kfree(pbuffer);
Status = STATUS_FAILURE;
}
}
else {
Status = STATUS_FAILURE;
}
}
else
{
/* Checksum did not compute */
Status = STATUS_FAILURE;
}
DEBUG("ft1000:download: after STATE_SECTION_PROV, uiState = %d, Status= %d\n", uiState, Status);
break;
case STATE_DONE_PROV:
DEBUG("FT1000:download:STATE_DONE_PROV\n");
uiState = STATE_DONE_FILE;
break;
default:
Status = STATUS_FAILURE;
break;
} /* End Switch */
if (Status != STATUS_SUCCESS) {
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 */
DEBUG("Download exiting with status = 0x%8x\n", Status);
ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX, FT1000_REG_DOORBELL);
return Status;
}