| /*--------------------------------------------------------------------------- |
| 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #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/io.h> |
| #include <linux/uaccess.h> |
| #include <linux/vmalloc.h> |
| |
| #include "ft1000.h" |
| #include "boot.h" |
| |
| #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. */ |
| } __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; |
| } __packed; |
| |
| void card_bootload(struct net_device *dev) |
| { |
| struct ft1000_info *info = netdev_priv(dev); |
| unsigned long flags; |
| u32 *pdata; |
| u32 size; |
| u32 i; |
| u32 templong; |
| |
| netdev_dbg(dev, "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 = 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; |
| } |
| loopcnt++; |
| mdelay(DSP_WAIT_SLEEP_TIME); |
| |
| } |
| |
| return HANDSHAKE_TIMEOUT_VALUE; |
| |
| } |
| |
| void put_handshake(struct net_device *dev, u16 handshake_value) |
| { |
| struct ft1000_info *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 = 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 = 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 = 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 = 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 = false; |
| 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) { |
| pr_err("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: |
| netdev_dbg(dev, |
| "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; |
| netdev_dbg(dev, |
| "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 { |
| netdev_dbg(dev, |
| "Download error: Bad Port IDs in Pseudo Record\n"); |
| netdev_dbg(dev, "\t Port Source = 0x%2.2x\n", |
| pHdr->portsrc); |
| netdev_dbg(dev, "\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, 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; |
| |
| } |