blob: 0bf398d570dcaa1c814e62e742f8f547992d5b9d [file] [log] [blame]
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option) any
later version. This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details. You should have received a copy of the GNU General Public
License along with this program; if not, write to the
Free Software Foundation, Inc., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.
--------------------------------------------------------------------------
Description: This module will handshake with the DSP bootloader to
download the DSP runtime image.
---------------------------------------------------------------------------*/
#define __KERNEL_SYSCALLS__
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/unistd.h>
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/vmalloc.h>
#include "ft1000_dev.h"
#include "ft1000.h"
#include "boot.h"
#ifdef FT_DEBUG
#define DEBUG(n, args...) printk(KERN_DEBUG args);
#else
#define DEBUG(n, args...)
#endif
#define MAX_DSP_WAIT_LOOPS 100
#define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
#define MAX_LENGTH 0x7f0
#define DWNLD_MAG_HANDSHAKE_LOC 0x00
#define DWNLD_MAG_TYPE_LOC 0x01
#define DWNLD_MAG_SIZE_LOC 0x02
#define DWNLD_MAG_PS_HDR_LOC 0x03
#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 HANDSHAKE_TIMEOUT_VALUE 0xF1F1
#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
#define HANDSHAKE_DSP_BL_READY 0xFEFE /* 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
USHORT get_handshake(struct net_device *dev, USHORT expected_value);
void put_handshake(struct net_device *dev, USHORT handshake_value);
USHORT get_request_type(struct net_device *dev);
long get_request_value(struct net_device *dev);
void put_request_value(struct net_device *dev, long lvalue);
USHORT hdr_checksum(PPSEUDO_HDR pHdr);
typedef struct _DSP_FILE_HDR {
long build_date;
long dsp_coff_date;
long loader_code_address;
long loader_code_size;
long loader_code_end;
long dsp_code_address;
long dsp_code_size;
long dsp_code_end;
long reserved[8];
} __attribute__ ((packed)) DSP_FILE_HDR, *PDSP_FILE_HDR;
typedef struct _DSP_FILE_HDR_5 {
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.
} __attribute__ ((packed)) DSP_FILE_HDR_5, *PDSP_FILE_HDR_5;
typedef 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.
} __attribute__ ((packed)) DSP_IMAGE_INFO, *PDSP_IMAGE_INFO;
typedef struct _DSP_IMAGE_INFO_V6 {
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;
} __attribute__ ((packed)) DSP_IMAGE_INFO_V6, *PDSP_IMAGE_INFO_V6;
void card_bootload(struct net_device *dev)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
unsigned long flags;
PULONG pdata;
UINT size;
UINT i;
ULONG templong;
DEBUG(0, "card_bootload is called\n");
pdata = (PULONG) bootimage;
size = sizeof(bootimage);
// check for odd word
if (size & 0x0003) {
size += 4;
}
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
// need to set i/o base address initially and hardware will autoincrement
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
// write bytes
for (i = 0; i < (size >> 2); i++) {
templong = *pdata++;
outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
USHORT get_handshake(struct net_device *dev, USHORT expected_value)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
USHORT handshake;
ULONG tempx;
int loopcnt;
loopcnt = 0;
while (loopcnt < MAX_DSP_WAIT_LOOPS) {
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_HANDSHAKE_LOC);
handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
} else {
tempx =
ntohl(ft1000_read_dpram_mag_32
(dev, DWNLD_MAG_HANDSHAKE_LOC));
handshake = (USHORT) tempx;
}
if ((handshake == expected_value)
|| (handshake == HANDSHAKE_RESET_VALUE)) {
return handshake;
} else {
loopcnt++;
mdelay(DSP_WAIT_SLEEP_TIME);
}
}
return HANDSHAKE_TIMEOUT_VALUE;
}
void put_handshake(struct net_device *dev, USHORT handshake_value)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
ULONG tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_HANDSHAKE_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
} else {
tempx = (ULONG) handshake_value;
tempx = ntohl(tempx);
ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
}
}
USHORT get_request_type(struct net_device *dev)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
USHORT request_type;
ULONG tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
} else {
tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
tempx = ntohl(tempx);
request_type = (USHORT) tempx;
}
return request_type;
}
long get_request_value(struct net_device *dev)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
long value;
USHORT w_val;
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_MSW_LOC);
w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
value = (long)(w_val << 16);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_LSW_LOC);
w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
value = (long)(value | w_val);
} else {
value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
value = ntohl(value);
}
return value;
}
void put_request_value(struct net_device *dev, long lvalue)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
USHORT size;
ULONG tempx;
if (info->AsicID == ELECTRABUZZ_ID) {
size = (USHORT) (lvalue >> 16);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_MSW_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
size = (USHORT) (lvalue);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
DWNLD_SIZE_LSW_LOC);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
} else {
tempx = ntohl(lvalue);
ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
}
}
USHORT hdr_checksum(PPSEUDO_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;
}
int card_download(struct net_device *dev, void *pFileStart, UINT FileLength)
{
FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
int Status = SUCCESS;
USHORT DspWordCnt = 0;
UINT uiState;
USHORT handshake;
PPSEUDO_HDR pHdr;
USHORT usHdrLength;
PDSP_FILE_HDR pFileHdr;
long word_length;
USHORT request;
USHORT temp;
PPROV_RECORD pprov_record;
PUCHAR pbuffer;
PDSP_FILE_HDR_5 pFileHdr5;
PDSP_IMAGE_INFO pDspImageInfo = NULL;
PDSP_IMAGE_INFO_V6 pDspImageInfoV6 = NULL;
long requested_version;
BOOLEAN bGoodVersion = 0;
PDRVMSG pMailBoxData;
USHORT *pUsData = NULL;
USHORT *pUsFile = NULL;
UCHAR *pUcFile = NULL;
UCHAR *pBootEnd = NULL;
UCHAR *pCodeEnd = NULL;
int imageN;
long file_version;
long loader_code_address = 0;
long loader_code_size = 0;
long run_address = 0;
long run_size = 0;
unsigned long flags;
unsigned long templong;
unsigned long image_chksum = 0;
//
// Get version id of file, at first 4 bytes of file, for newer files.
//
file_version = *(long *)pFileStart;
uiState = STATE_START_DWNLD;
pFileHdr = (PDSP_FILE_HDR) pFileStart;
pFileHdr5 = (PDSP_FILE_HDR_5) pFileStart;
switch (file_version) {
case 5:
case 6:
pUsFile =
(USHORT *) ((long)pFileStart + pFileHdr5->loader_offset);
pUcFile =
(UCHAR *) ((long)pFileStart + pFileHdr5->loader_offset);
pBootEnd =
(UCHAR *) ((long)pFileStart + pFileHdr5->loader_code_end);
loader_code_address = pFileHdr5->loader_code_address;
loader_code_size = pFileHdr5->loader_code_size;
bGoodVersion = FALSE;
break;
default:
Status = FAILURE;
break;
}
while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
switch (uiState) {
case STATE_START_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
if (handshake == HANDSHAKE_DSP_BL_READY) {
put_handshake(dev, HANDSHAKE_DRIVER_READY);
} else {
Status = FAILURE;
}
uiState = STATE_BOOT_DWNLD;
break;
case STATE_BOOT_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
request = get_request_type(dev);
switch (request) {
case REQUEST_RUN_ADDRESS:
put_request_value(dev,
loader_code_address);
break;
case REQUEST_CODE_LENGTH:
put_request_value(dev,
loader_code_size);
break;
case REQUEST_DONE_BL:
/* Reposition ptrs to beginning of code section */
pUsFile = (USHORT *) ((long)pBootEnd);
pUcFile = (UCHAR *) ((long)pBootEnd);
uiState = STATE_CODE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
word_length = get_request_value(dev);
if (word_length > MAX_LENGTH) {
Status = FAILURE;
break;
}
if ((word_length * 2 + (long)pUcFile) >
(long)pBootEnd) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock,
flags);
if (file_version == 5) {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
ft1000_write_reg(dev,
FT1000_REG_DPRAM_ADDR,
DWNLD_PS_HDR_LOC);
for (; word_length > 0; word_length--) { /* In words */
//temp = *pUsFile;
//temp = RtlUshortByteSwap(temp);
ft1000_write_reg(dev,
FT1000_REG_DPRAM_DATA,
*pUsFile);
pUsFile++;
pUcFile += 2;
DspWordCnt++;
}
} else {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01) {
word_length++;
}
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsFile++;
templong |=
(*pUsFile++ << 16);
pUcFile += 4;
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
default:
Status = FAILURE;
break;
}
put_handshake(dev, HANDSHAKE_RESPONSE);
} else {
Status = FAILURE;
}
break;
case STATE_CODE_DWNLD:
handshake = get_handshake(dev, HANDSHAKE_REQUEST);
if (handshake == HANDSHAKE_REQUEST) {
/*
* Get type associated with the request.
*/
request = get_request_type(dev);
switch (request) {
case REQUEST_FILE_CHECKSUM:
DEBUG(0,
"ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
put_request_value(dev, image_chksum);
break;
case REQUEST_RUN_ADDRESS:
if (bGoodVersion) {
put_request_value(dev,
run_address);
} else {
Status = FAILURE;
break;
}
break;
case REQUEST_CODE_LENGTH:
if (bGoodVersion) {
put_request_value(dev,
run_size);
} else {
Status = FAILURE;
break;
}
break;
case REQUEST_DONE_CL:
/* Reposition ptrs to beginning of provisioning section */
switch (file_version) {
case 5:
case 6:
pUsFile =
(USHORT *) ((long)pFileStart
+
pFileHdr5->
commands_offset);
pUcFile =
(UCHAR *) ((long)pFileStart
+
pFileHdr5->
commands_offset);
break;
default:
Status = FAILURE;
break;
}
uiState = STATE_DONE_DWNLD;
break;
case REQUEST_CODE_SEGMENT:
if (!bGoodVersion) {
Status = FAILURE;
break;
}
word_length = get_request_value(dev);
if (word_length > MAX_LENGTH) {
Status = FAILURE;
break;
}
if ((word_length * 2 + (long)pUcFile) >
(long)pCodeEnd) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
if (file_version == 5) {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
ft1000_write_reg(dev,
FT1000_REG_DPRAM_ADDR,
DWNLD_PS_HDR_LOC);
for (; word_length > 0; word_length--) { /* In words */
//temp = *pUsFile;
//temp = RtlUshortByteSwap(temp);
ft1000_write_reg(dev,
FT1000_REG_DPRAM_DATA,
*pUsFile);
pUsFile++;
pUcFile += 2;
DspWordCnt++;
}
} else {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01) {
word_length++;
}
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsFile++;
templong |=
(*pUsFile++ << 16);
pUcFile += 4;
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
}
break;
case REQUEST_MAILBOX_DATA:
// Convert length from byte count to word count. Make sure we round up.
word_length =
(long)(info->DSPInfoBlklen + 1) / 2;
put_request_value(dev, word_length);
pMailBoxData =
(PDRVMSG) & info->DSPInfoBlk[0];
pUsData =
(USHORT *) & pMailBoxData->data[0];
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock,
flags);
if (file_version == 5) {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
ft1000_write_reg(dev,
FT1000_REG_DPRAM_ADDR,
DWNLD_PS_HDR_LOC);
for (; word_length > 0; word_length--) { /* In words */
temp = ntohs(*pUsData);
ft1000_write_reg(dev,
FT1000_REG_DPRAM_DATA,
temp);
pUsData++;
}
} else {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01) {
word_length++;
}
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong = *pUsData++;
templong |=
(*pUsData++ << 16);
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
case REQUEST_VERSION_INFO:
word_length =
pFileHdr5->version_data_size;
put_request_value(dev, word_length);
pUsFile =
(USHORT *) ((long)pFileStart +
pFileHdr5->
version_data_offset);
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock,
flags);
if (file_version == 5) {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
ft1000_write_reg(dev,
FT1000_REG_DPRAM_ADDR,
DWNLD_PS_HDR_LOC);
for (; word_length > 0; word_length--) { /* In words */
ft1000_write_reg(dev,
FT1000_REG_DPRAM_DATA,
*pUsFile
/*temp */
);
pUsFile++;
}
} else {
/*
* Position ASIC DPRAM auto-increment pointer.
*/
outw(DWNLD_MAG_PS_HDR_LOC,
dev->base_addr +
FT1000_REG_DPRAM_ADDR);
if (word_length & 0x01) {
word_length++;
}
word_length = word_length / 2;
for (; word_length > 0; word_length--) { /* In words */
templong =
ntohs(*pUsFile++);
temp =
ntohs(*pUsFile++);
templong |=
(temp << 16);
outl(templong,
dev->base_addr +
FT1000_REG_MAG_DPDATAL);
}
}
spin_unlock_irqrestore(&info->
dpram_lock,
flags);
break;
case REQUEST_CODE_BY_VERSION:
bGoodVersion = FALSE;
requested_version =
get_request_value(dev);
if (file_version == 5) {
pDspImageInfo =
(PDSP_IMAGE_INFO) ((long)
pFileStart
+
sizeof
(DSP_FILE_HDR_5));
for (imageN = 0;
imageN <
pFileHdr5->nDspImages;
imageN++) {
if (pDspImageInfo->
version ==
requested_version) {
bGoodVersion =
TRUE;
pUsFile =
(USHORT
*) ((long)
pFileStart
+
pDspImageInfo->
begin_offset);
pUcFile =
(UCHAR
*) ((long)
pFileStart
+
pDspImageInfo->
begin_offset);
pCodeEnd =
(UCHAR
*) ((long)
pFileStart
+
pDspImageInfo->
end_offset);
run_address =
pDspImageInfo->
run_address;
run_size =
pDspImageInfo->
image_size;
break;
}
pDspImageInfo++;
}
} else {
pDspImageInfoV6 =
(PDSP_IMAGE_INFO_V6) ((long)
pFileStart
+
sizeof
(DSP_FILE_HDR_5));
for (imageN = 0;
imageN <
pFileHdr5->nDspImages;
imageN++) {
temp = (USHORT)
(pDspImageInfoV6->
version);
templong = temp;
temp = (USHORT)
(pDspImageInfoV6->
version >> 16);
templong |=
(temp << 16);
if (templong ==
requested_version) {
bGoodVersion =
TRUE;
pUsFile =
(USHORT
*) ((long)
pFileStart
+
pDspImageInfoV6->
begin_offset);
pUcFile =
(UCHAR
*) ((long)
pFileStart
+
pDspImageInfoV6->
begin_offset);
pCodeEnd =
(UCHAR
*) ((long)
pFileStart
+
pDspImageInfoV6->
end_offset);
run_address =
pDspImageInfoV6->
run_address;
run_size =
pDspImageInfoV6->
image_size;
image_chksum =
(ULONG)
pDspImageInfoV6->
checksum;
DEBUG(0,
"ft1000_dnld: image_chksum = 0x%8x\n",
(unsigned
int)
image_chksum);
break;
}
pDspImageInfoV6++;
}
}
if (!bGoodVersion) {
/*
* Error, beyond boot code range.
*/
Status = FAILURE;
break;
}
break;
default:
Status = FAILURE;
break;
}
put_handshake(dev, HANDSHAKE_RESPONSE);
} else {
Status = FAILURE;
}
break;
case STATE_DONE_DWNLD:
if (((UINT) (pUcFile) - (UINT) pFileStart) >=
(UINT) FileLength) {
uiState = STATE_DONE_FILE;
break;
}
pHdr = (PPSEUDO_HDR) pUsFile;
if (pHdr->portdest == 0x80 /* DspOAM */
&& (pHdr->portsrc == 0x00 /* Driver */
|| pHdr->portsrc == 0x10 /* FMM */ )) {
uiState = STATE_SECTION_PROV;
} else {
DEBUG(1,
"FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
DEBUG(1, "\t Port Source = 0x%2.2x\n",
pHdr->portsrc);
DEBUG(1, "\t Port Destination = 0x%2.2x\n",
pHdr->portdest);
Status = FAILURE;
}
break;
case STATE_SECTION_PROV:
pHdr = (PPSEUDO_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(PSEUDO_HDR)),
GFP_ATOMIC);
if (pbuffer) {
memcpy(pbuffer, (void *)pUcFile,
(UINT) (usHdrLength +
sizeof(PSEUDO_HDR)));
// link provisioning data
pprov_record =
kmalloc(sizeof(PROV_RECORD),
GFP_ATOMIC);
if (pprov_record) {
pprov_record->pprov_data =
pbuffer;
list_add_tail(&pprov_record->
list,
&info->prov_list);
// Move to next entry if available
pUcFile =
(UCHAR *) ((UINT) pUcFile +
(UINT) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(PSEUDO_HDR));
if ((UINT) (pUcFile) -
(UINT) (pFileStart) >=
(UINT) FileLength) {
uiState =
STATE_DONE_FILE;
}
} else {
kfree(pbuffer);
Status = FAILURE;
}
} else {
Status = FAILURE;
}
} else {
/* Checksum did not compute */
Status = FAILURE;
}
break;
case STATE_DONE_PROV:
uiState = STATE_DONE_FILE;
break;
default:
Status = FAILURE;
break;
} /* End Switch */
} /* End while */
return Status;
}