776 lines
20 KiB
C
776 lines
20 KiB
C
/*---------------------------------------------------------------------------
|
|
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 <asm/io.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/vmalloc.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
|
|
|
|
u16 get_handshake(struct net_device *dev, u16 expected_value);
|
|
void put_handshake(struct net_device *dev, u16 handshake_value);
|
|
u16 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);
|
|
u16 hdr_checksum(struct pseudo_hdr *pHdr);
|
|
|
|
struct dsp_file_hdr {
|
|
u32 version_id; /* Version ID of this image format. */
|
|
u32 package_id; /* Package ID of code release. */
|
|
u32 build_date; /* Date/time stamp when file was built. */
|
|
u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
|
|
u32 loader_offset; /* Offset to bootloader code. */
|
|
u32 loader_code_address; /* Start address of bootloader. */
|
|
u32 loader_code_end; /* Where bootloader code ends. */
|
|
u32 loader_code_size;
|
|
u32 version_data_offset; /* Offset were scrambled version data begins. */
|
|
u32 version_data_size; /* Size, in words, of scrambled version data. */
|
|
u32 nDspImages; /* Number of DSP images in file. */
|
|
} __attribute__ ((packed));
|
|
|
|
struct dsp_image_info {
|
|
u32 coff_date; /* Date/time when DSP Coff image was built. */
|
|
u32 begin_offset; /* Offset in file where image begins. */
|
|
u32 end_offset; /* Offset in file where image begins. */
|
|
u32 run_address; /* On chip Start address of DSP code. */
|
|
u32 image_size; /* Size of image. */
|
|
u32 version; /* Embedded version # of DSP code. */
|
|
unsigned short checksum; /* Dsp File checksum */
|
|
unsigned short pad1;
|
|
} __attribute__ ((packed));
|
|
|
|
void card_bootload(struct net_device *dev)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
unsigned long flags;
|
|
u32 *pdata;
|
|
u32 size;
|
|
u32 i;
|
|
u32 templong;
|
|
|
|
DEBUG(0, "card_bootload is called\n");
|
|
|
|
pdata = (u32 *) 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);
|
|
}
|
|
|
|
u16 get_handshake(struct net_device *dev, u16 expected_value)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
u16 handshake;
|
|
u32 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 = (u16) 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, u16 handshake_value)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
u32 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 = (u32) handshake_value;
|
|
tempx = ntohl(tempx);
|
|
ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
|
|
}
|
|
}
|
|
|
|
u16 get_request_type(struct net_device *dev)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
u16 request_type;
|
|
u32 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 = (u16) tempx;
|
|
}
|
|
|
|
return request_type;
|
|
|
|
}
|
|
|
|
long get_request_value(struct net_device *dev)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
long value;
|
|
u16 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)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
u16 size;
|
|
u32 tempx;
|
|
|
|
if (info->AsicID == ELECTRABUZZ_ID) {
|
|
size = (u16) (lvalue >> 16);
|
|
|
|
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
|
|
DWNLD_SIZE_MSW_LOC);
|
|
|
|
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
|
|
|
|
size = (u16) (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 */
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int card_download(struct net_device *dev, const u8 *pFileStart,
|
|
size_t FileLength)
|
|
{
|
|
struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
|
|
int Status = SUCCESS;
|
|
u32 uiState;
|
|
u16 handshake;
|
|
struct pseudo_hdr *pHdr;
|
|
u16 usHdrLength;
|
|
long word_length;
|
|
u16 request;
|
|
u16 temp;
|
|
struct prov_record *pprov_record;
|
|
u8 *pbuffer;
|
|
struct dsp_file_hdr *pFileHdr5;
|
|
struct dsp_image_info *pDspImageInfoV6 = NULL;
|
|
long requested_version;
|
|
bool bGoodVersion = 0;
|
|
struct drv_msg *pMailBoxData;
|
|
u16 *pUsData = NULL;
|
|
u16 *pUsFile = NULL;
|
|
u8 *pUcFile = NULL;
|
|
u8 *pBootEnd = NULL;
|
|
u8 *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;
|
|
|
|
file_version = *(long *)pFileStart;
|
|
if (file_version != 6) {
|
|
printk(KERN_ERR "ft1000: unsupported firmware version %ld\n", file_version);
|
|
Status = FAILURE;
|
|
}
|
|
|
|
uiState = STATE_START_DWNLD;
|
|
|
|
pFileHdr5 = (struct dsp_file_hdr *) pFileStart;
|
|
|
|
pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset);
|
|
pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset);
|
|
pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end);
|
|
loader_code_address = pFileHdr5->loader_code_address;
|
|
loader_code_size = pFileHdr5->loader_code_size;
|
|
bGoodVersion = false;
|
|
|
|
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 = (u16 *) ((long)pBootEnd);
|
|
pUcFile = (u8 *) ((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);
|
|
/*
|
|
* 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 */
|
|
pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
|
|
pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
|
|
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;
|
|
}
|
|
/*
|
|
* 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 =
|
|
(struct drv_msg *) & info->DSPInfoBlk[0];
|
|
pUsData =
|
|
(u16 *) & 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 =
|
|
(u16 *) ((long)pFileStart +
|
|
pFileHdr5->
|
|
version_data_offset);
|
|
// Provide mutual exclusive access while reading ASIC registers.
|
|
spin_lock_irqsave(&info->dpram_lock,
|
|
flags);
|
|
/*
|
|
* 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);
|
|
pDspImageInfoV6 =
|
|
(struct dsp_image_info *) ((long)
|
|
pFileStart
|
|
+
|
|
sizeof
|
|
(struct dsp_file_hdr));
|
|
for (imageN = 0;
|
|
imageN <
|
|
pFileHdr5->nDspImages;
|
|
imageN++) {
|
|
temp = (u16)
|
|
(pDspImageInfoV6->
|
|
version);
|
|
templong = temp;
|
|
temp = (u16)
|
|
(pDspImageInfoV6->
|
|
version >> 16);
|
|
templong |=
|
|
(temp << 16);
|
|
if (templong ==
|
|
requested_version) {
|
|
bGoodVersion =
|
|
true;
|
|
pUsFile =
|
|
(u16
|
|
*) ((long)
|
|
pFileStart
|
|
+
|
|
pDspImageInfoV6->
|
|
begin_offset);
|
|
pUcFile =
|
|
(u8
|
|
*) ((long)
|
|
pFileStart
|
|
+
|
|
pDspImageInfoV6->
|
|
begin_offset);
|
|
pCodeEnd =
|
|
(u8
|
|
*) ((long)
|
|
pFileStart
|
|
+
|
|
pDspImageInfoV6->
|
|
end_offset);
|
|
run_address =
|
|
pDspImageInfoV6->
|
|
run_address;
|
|
run_size =
|
|
pDspImageInfoV6->
|
|
image_size;
|
|
image_chksum =
|
|
(u32)
|
|
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 (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >=
|
|
(unsigned long) FileLength) {
|
|
uiState = STATE_DONE_FILE;
|
|
break;
|
|
}
|
|
|
|
pHdr = (struct pseudo_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 = (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,
|
|
(u32) (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,
|
|
&info->prov_list);
|
|
// Move to next entry if available
|
|
pUcFile =
|
|
(u8 *) ((unsigned long) pUcFile +
|
|
(unsigned long) ((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 = 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;
|
|
|
|
}
|