| /*--------------------------------------------------------------------------- |
| 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 |
| |
| 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(struct pseudo_hdr *pHdr); |
| |
| struct dsp_file_hdr { |
| u32 build_date; |
| u32 dsp_coff_date; |
| u32 loader_code_address; |
| u32 loader_code_size; |
| u32 loader_code_end; |
| u32 dsp_code_address; |
| u32 dsp_code_size; |
| u32 dsp_code_end; |
| u32 reserved[8]; |
| } __attribute__ ((packed)); |
| |
| struct dsp_file_hdr_5 { |
| 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. |
| } __attribute__ ((packed)); |
| |
| struct dsp_image_info_v6 { |
| 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; |
| 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) |
| { |
| struct ft1000_info *info = (struct ft1000_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) |
| { |
| struct ft1000_info *info = (struct ft1000_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) |
| { |
| struct ft1000_info *info = (struct ft1000_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) |
| { |
| struct ft1000_info *info = (struct ft1000_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) |
| { |
| struct ft1000_info *info = (struct ft1000_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(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; |
| } |
| |
| int card_download(struct net_device *dev, const u8 *pFileStart, UINT FileLength) |
| { |
| struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
| int Status = SUCCESS; |
| USHORT DspWordCnt = 0; |
| UINT uiState; |
| USHORT handshake; |
| struct pseudo_hdr *pHdr; |
| USHORT usHdrLength; |
| struct dsp_file_hdr *pFileHdr; |
| long word_length; |
| USHORT request; |
| USHORT temp; |
| struct prov_record *pprov_record; |
| PUCHAR pbuffer; |
| struct dsp_file_hdr_5 *pFileHdr5; |
| struct dsp_image_info *pDspImageInfo = NULL; |
| struct dsp_image_info_v6 *pDspImageInfoV6 = NULL; |
| long requested_version; |
| BOOLEAN bGoodVersion = 0; |
| struct drv_msg *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 = (struct dsp_file_hdr *) pFileStart; |
| pFileHdr5 = (struct dsp_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 = |
| (struct drv_msg *) & 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 = |
| (struct dsp_image_info *) ((long) |
| pFileStart |
| + |
| sizeof |
| (struct 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 = |
| (struct dsp_image_info_v6 *) ((long) |
| pFileStart |
| + |
| sizeof |
| (struct 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 (((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, |
| (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, |
| &info->prov_list); |
| // Move to next entry if available |
| pUcFile = |
| (UCHAR *) ((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; |
| |
| } |