Commit f7c1be0c authored by Marek Belisko's avatar Marek Belisko Committed by Greg Kroah-Hartman

Staging: Add support for Flarion OFDM usb and pcmcia devices.

This drivers add support for following devices:

(usb)-> Qleadtek FLASH-OFDM USB Modem [LR7F04]
     -> Qleadtek Express Card
     -> Leadtek Multi-band modem HSDPA

Sources for usb:
https://sourceforge.net/projects/ft1000/files/ft1000_usb/ft1000_usb_v01.04.tar.gz/download

(pcmcia) -> Multimedia Net Card

Sources for pcmcia :
https://sourceforge.net/projects/ft1000/files/ft1000_pcmcia_2.6.30-2.6.31.tgz/download

More informations (in Slovak language):
	http://ft1000.qintec.sk/home.htmlSigned-off-by: default avatarMarek Belisko <marek.belisko@gmail.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 027360c5
...@@ -165,5 +165,7 @@ source "drivers/staging/keucr/Kconfig" ...@@ -165,5 +165,7 @@ source "drivers/staging/keucr/Kconfig"
source "drivers/staging/bcm/Kconfig" source "drivers/staging/bcm/Kconfig"
source "drivers/staging/ft1000/Kconfig"
endif # !STAGING_EXCLUDE_BUILD endif # !STAGING_EXCLUDE_BUILD
endif # STAGING endif # STAGING
...@@ -63,3 +63,4 @@ obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/ ...@@ -63,3 +63,4 @@ obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/
obj-$(CONFIG_ATH6K_LEGACY) += ath6kl/ obj-$(CONFIG_ATH6K_LEGACY) += ath6kl/
obj-$(CONFIG_USB_ENESTORAGE) += keucr/ obj-$(CONFIG_USB_ENESTORAGE) += keucr/
obj-$(CONFIG_BCM_WIMAX) += bcm/ obj-$(CONFIG_BCM_WIMAX) += bcm/
obj-$(CONFIG_FT1000) += ft1000/
config FT1000
tristate "Drivers for Flarion ft1000 devices"
if FT1000
config FT1000_USB
tristate "Driver for ft1000 usb devices."
depends on USB
depends on NET
help
Say Y if you want to have support for Qleadtek FLASH-OFDM USB Modem [LR7F04],
Qleadtek Express Card or Leadtek Multi-band modem HSDPA.
config FT1000_PCMCIA
tristate "Driver for ft1000 pcmcia device."
depends on PCMCIA
depends on NET
help
Say Y if you want to have support for Flarion card also called
Multimedia Net Card.
endif
obj-$(CONFIG_FT1000_USB) += ft1000-usb/
obj-$(CONFIG_FT1000_PCMCIA) += ft1000-pcmcia/
TODO:
- checkpatch.pl cleanups
- coding style
- sparse fixes
- adapt to latest usb and pcmcia api changes
- change firmware loading for usb driver to proper kernel method (request_firmware)
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and
Cc: Marek Belisko <marek.belisko@gmail.com>
obj-$(CONFIG_FT1000_PCMCIA) = ft1000_pcmcia.o
ft1000_pcmcia-objs := ft1000_hw.o ft1000_dnld.o ft1000_proc.o ft1000_cs.o
//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// File: boot.h
//
// Description: boatloader
//
// History:
// 1/11/05 Whc Ported to Linux.
//
//---------------------------------------------------------------------------
#ifndef _BOOTH_
#define _BOOTH_
// Official bootloader
unsigned char bootimage [] = {
0x00,0x00,0x01,0x5E,0x00,0x00
,0x00,0x00,0x00,0x00,0x02,0xD7
,0x00,0x00,0x01,0x5E,0x46,0xB3
,0xE6,0x02,0x00,0x98,0xE6,0x8C
,0x00,0x98,0xFB,0x92,0xFF,0xFF
,0x98,0xFB,0x94,0xFF,0xFF,0x98
,0xFB,0x06,0x08,0x00,0x98,0xFB
,0x96,0x84,0x00,0x98,0xFB,0x08
,0x1C,0x00,0x98,0xFB,0x51,0x25
,0x10,0x1C,0x00,0xE6,0x51,0x01
,0x07,0xFD,0x4C,0xFF,0x20,0xF5
,0x51,0x02,0x20,0x08,0x00,0x4C
,0xFF,0x20,0x3C,0x00,0xC0,0x64
,0x98,0xC0,0x66,0x98,0xC0,0x68
,0x98,0xC0,0x6A,0x98,0xC0,0x6C
,0x98,0x90,0x08,0x90,0x09,0x90
,0x0A,0x90,0x0B,0x90,0x0C,0x90
,0x0D,0x90,0x0E,0x90,0x0F,0x90
,0x04,0x90,0x06,0xFB,0x51,0x22
,0x16,0x08,0x03,0xFB,0x51,0x52
,0x16,0x08,0x04,0xFB,0x51,0x24
,0x2B,0x08,0x06,0xFB,0x51,0x54
,0x2B,0x08,0x07,0xFB,0x51,0x24
,0x2B,0x08,0x09,0xFB,0x51,0x54
,0x2B,0x08,0x0A,0xFB,0x51,0x12
,0x16,0x08,0x0C,0xFB,0x51,0x52
,0x16,0x08,0x0D,0x78,0x00,0x00
,0x00,0x16,0x00,0x00,0xEC,0x31
,0xAE,0x00,0x00,0x81,0x4C,0x0F
,0xE6,0x43,0xFF,0xEC,0x31,0x4E
,0x00,0x00,0x91,0xEC,0x31,0xAE
,0x00,0x00,0x91,0x4C,0x0F,0xE6
,0x43,0xFF,0xEC,0x31,0x5E,0x00
,0x00,0xA1,0xEB,0x31,0x08,0x00
,0x00,0xA6,0xEB,0x31,0x08,0x00
,0x00,0xAC,0x3C,0x00,0xEB,0x31
,0x08,0x00,0x00,0xA8,0x76,0xFE
,0xFE,0x08,0xEB,0x31,0x08,0x20
,0x00,0x00,0x76,0xFF,0xFF,0x18
,0xED,0x31,0x08,0x20,0x00,0x00
,0x26,0x10,0x04,0x10,0xF5,0x3C
,0x01,0x3C,0x00,0x08,0x01,0x12
,0x3C,0x11,0x3C,0x00,0x08,0x01
,0x0B,0x08,0x00,0x6D,0xEC,0x31
,0xAE,0x20,0x00,0x06,0xED,0x4D
,0x08,0x00,0x00,0x67,0x80,0x6F
,0x00,0x01,0x0B,0x6F,0x00,0x02
,0x2E,0x76,0xEE,0x01,0x48,0x06
,0x01,0x39,0xED,0x4D,0x18,0x00
,0x02,0xED,0x4D,0x08,0x00,0x04
,0x14,0x06,0xA4,0xED,0x31,0x22
,0x00,0x00,0xAC,0x76,0xEE,0x07
,0x48,0x6D,0x22,0x01,0x1E,0x08
,0x01,0x58,0xEB,0x31,0x08,0x00
,0x00,0xAC,0x06,0xFF,0xBA,0x3C
,0x00,0xEB,0x31,0x08,0x20,0x00
,0x04,0x3C,0x30,0xEB,0x31,0x08
,0x20,0x00,0x02,0x3C,0x10,0xEB
,0x31,0x08,0x20,0x00,0x00,0xED
,0x31,0x08,0x20,0x00,0x00,0x04
,0x10,0xF7,0xED,0x31,0x08,0x00
,0x00,0xA2,0x91,0x00,0x9C,0x3C
,0x80,0xEB,0x31,0x08,0x20,0x00
,0x04,0x3C,0x20,0xEB,0x31,0x08
,0x20,0x00,0x02,0x3C,0x10,0xEB
,0x31,0x08,0x20,0x00,0x00,0xED
,0x31,0x08,0x20,0x00,0x00,0x04
,0x10,0xF7,0xED,0x31,0x08,0x20
,0x00,0x04,0x42,0x10,0x90,0x08
,0xEC,0x31,0xAE,0x20,0x00,0x06
,0xA4,0x41,0x08,0x00,0xB6,0xED
,0x41,0x28,0x7D,0xFF,0xFF,0x22
,0xB3,0x40,0x98,0x2A,0x32,0xEB
,0x41,0x28,0xB4,0x43,0xFC,0x05
,0xFF,0xE6,0xA0,0x31,0x20,0x00
,0x06,0xEB,0x31,0x08,0x20,0x00
,0x04,0x3C,0x20,0xEB,0x31,0x08
,0x20,0x00,0x02,0x3C,0x10,0xEB
,0x31,0x08,0x20,0x00,0x00,0xED
,0x31,0x08,0x20,0x00,0x00,0x04
,0x10,0xF7,0xED,0x31,0x08,0x20
,0x00,0x04,0x42,0x10,0x90,0x08
,0xEC,0x31,0xAE,0x20,0x00,0x06
,0xA4,0x41,0x08,0x00,0x68,0xED
,0x41,0x28,0x7D,0xFF,0xFF,0x22
,0xB3,0x40,0x98,0x2A,0x32,0xEB
,0x41,0x28,0xB4,0x43,0xFC,0x05
,0xFF,0xE6,0x48,0x04,0xEB,0x31
,0x08,0x20,0x00,0x04,0xEB,0x31
,0x18,0x20,0x00,0x02,0x3C,0x11
,0xEB,0x31,0x18,0x20,0x00,0x00
,0xED,0x31,0x08,0x20,0x00,0x00
,0x04,0x10,0xF7,0xED,0x31,0x08
,0x20,0x00,0x02,0x66,0x00,0x6F
,0x00,0x01,0x16,0x76,0xEE,0x06
,0x48,0x4A,0x1E,0x48,0x04,0xED
,0x31,0x08,0x20,0x00,0x04,0xEB
,0x31,0x08,0x00,0x00,0xA4,0x48
,0x04,0xED,0x31,0x08,0x20,0x00
,0x04,0xEB,0x31,0x08,0x00,0x00
,0xA2,0x48,0x04,0x20,0x20,0x4A
,0x7C,0x46,0x82,0x50,0x05,0x50
,0x15,0xB5,0x1E,0x98,0xED,0x31
,0x08,0x00,0x00,0xA8,0x10,0x47
,0x3B,0x2C,0x01,0xDB,0x40,0x11
,0x98,0xC1,0x1E,0x98,0x10,0x07
,0x30,0xF9,0x40,0x07,0x18,0x98
,0x2A,0x10,0xEB,0x31,0x08,0x00
,0x00,0xA8,0xA4,0x1E,0x98,0xBB
,0x1E,0x98,0x50,0x14,0x50,0x04
,0x46,0x83,0x48,0x04,0x02,0x01
,0x00,0x50,0x05,0x50,0x15,0x10
,0x87,0x3F,0x90,0x2B,0x18,0x01
,0x00,0xC0,0x31,0x00,0x00,0xAE
,0xDF,0x41,0x00,0x08,0x00,0x1A
,0x42,0x11,0x67,0x01,0xDF,0x41
,0x02,0x08,0x00,0x10,0x42,0x11
,0x62,0x01,0xB4,0x43,0x4A,0x68
,0x50,0x14,0x50,0x04,0x24,0x10
,0x48,0x04,0xF2,0x31,0x00,0x01
,0x00,0x00,0xAE,0xF6,0x31,0x00
,0x01,0x00,0x00,0xAE,0x62,0xE4
,0xE5,0x61,0x04,0x48,0x04,0xE5
,0x63,0x05,0x48,0x04,0x20,0x20
,0x00,0x00,0x00,0x00
};
#endif
device "ft1000_cs"
class "network" module "ft1000","ft1000_cs"
card "flarion FT1000"
manfid 0x02cc, 0x0100
bind "ft1000_cs"
card "flarion FT1000"
manfid 0x02cc, 0x1000
bind "ft1000_cs"
card "flarion FT1000"
manfid 0x02cc, 0x1300
bind "ft1000_cs"
//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// File: ft1000.h
//
// Description: Common structures and defines
//
// History:
// 8/29/02 Whc Ported to Linux.
// 7/19/04 Whc Drop packet and cmd msg with pseudo header
// checksum
// 10/27/04 Whc Added dynamic downloading of test image.
// 01/11/04 Whc Added support for Magnemite ASIC
//
//---------------------------------------------------------------------------
#ifndef _FT1000H_
#define _FT1000H_
#define FT1000_DRV_VER 0x01010300
#define DSPVERSZ 4
#define HWSERNUMSZ 16
#define SKUSZ 20
#define EUISZ 8
#define MODESZ 2
#define CALVERSZ 2
#define CALDATESZ 6
// Pseudo Header structure
typedef struct _PSEUDO_HDR
{
unsigned short length; // length of msg body
unsigned char source; // hardware source id
// Host = 0x10
// Dsp = 0x20
unsigned char destination; // hardware destination id (refer to source)
unsigned char portdest; // software destination port id
// Host = 0x00
// Applicaton Broadcast = 0x10
// Network Stack = 0x20
// Dsp OAM = 0x80
// Dsp Airlink = 0x90
// Dsp Loader = 0xa0
// Dsp MIP = 0xb0
unsigned char portsrc; // software source port id (refer to portdest)
unsigned short sh_str_id; // not used
unsigned char control; // not used
unsigned char rsvd1;
unsigned char seq_num; // message sequence number
unsigned char rsvd2;
unsigned short qos_class; // not used
unsigned short checksum; // pseudo header checksum
} __attribute__ ((packed)) PSEUDO_HDR, *PPSEUDO_HDR;
// Definitions to maintain compatibility between other platforms
#define UCHAR u8
#define USHORT u16
#define ULONG u32
#define BOOLEAN u8
#define PULONG u32 *
#define PUSHORT u16 *
#define PUCHAR u8 *
#define PCHAR u8 *
#define UINT u32
#define ELECTRABUZZ_ID 0 // ASIC ID for Electrabuzz
#define MAGNEMITE_ID 0x1a01 // ASIC ID for Magnemite
// MEMORY MAP common to both ELECTRABUZZ and MAGNEMITE
#define FT1000_REG_DPRAM_ADDR 0x000E // DPADR - Dual Port Ram Indirect Address Register
#define FT1000_REG_SUP_CTRL 0x0020 // HCTR - Host Control Register
#define FT1000_REG_SUP_STAT 0x0022 // HSTAT - Host Status Register
#define FT1000_REG_RESET 0x0024 // HCTR - Host Control Register
#define FT1000_REG_SUP_ISR 0x0026 // HISR - Host Interrupt Status Register
#define FT1000_REG_SUP_IMASK 0x0028 // HIMASK - Host Interrupt Mask
#define FT1000_REG_DOORBELL 0x002a // DBELL - Door Bell Register
#define FT1000_REG_ASIC_ID 0x002e // ASICID - ASIC Identification Number
// (Electrabuzz=0 Magnemite=0x1A01)
// MEMORY MAP FOR ELECTRABUZZ ASIC
#define FT1000_REG_UFIFO_STAT 0x0000 // UFSR - Uplink FIFO status register
#define FT1000_REG_UFIFO_BEG 0x0002 // UFBR - Uplink FIFO beginning register
#define FT1000_REG_UFIFO_MID 0x0004 // UFMR - Uplink FIFO middle register
#define FT1000_REG_UFIFO_END 0x0006 // UFER - Uplink FIFO end register
#define FT1000_REG_DFIFO_STAT 0x0008 // DFSR - Downlink FIFO status register
#define FT1000_REG_DFIFO 0x000A // DFR - Downlink FIFO Register
#define FT1000_REG_DPRAM_DATA 0x000C // DPRAM - Dual Port Indirect Data Register
#define FT1000_REG_WATERMARK 0x0010 // WMARK - Watermark Register
// MEMORY MAP FOR MAGNEMITE
#define FT1000_REG_MAG_UFDR 0x0000 // UFDR - Uplink FIFO Data Register (32-bits)
#define FT1000_REG_MAG_UFDRL 0x0000 // UFDRL - Uplink FIFO Data Register low-word (16-bits)
#define FT1000_REG_MAG_UFDRH 0x0002 // UFDRH - Uplink FIFO Data Register high-word (16-bits)
#define FT1000_REG_MAG_UFER 0x0004 // UFER - Uplink FIFO End Register
#define FT1000_REG_MAG_UFSR 0x0006 // UFSR - Uplink FIFO Status Register
#define FT1000_REG_MAG_DFR 0x0008 // DFR - Downlink FIFO Register (32-bits)
#define FT1000_REG_MAG_DFRL 0x0008 // DFRL - Downlink FIFO Register low-word (16-bits)
#define FT1000_REG_MAG_DFRH 0x000a // DFRH - Downlink FIFO Register high-word (16-bits)
#define FT1000_REG_MAG_DFSR 0x000c // DFSR - Downlink FIFO Status Register
#define FT1000_REG_MAG_DPDATA 0x0010 // DPDATA - Dual Port RAM Indirect Data Register (32-bits)
#define FT1000_REG_MAG_DPDATAL 0x0010 // DPDATAL - Dual Port RAM Indirect Data Register low-word (16-bits)
#define FT1000_REG_MAG_DPDATAH 0x0012 // DPDATAH - Dual Port RAM Indirect Data Register high-word (16-bits)
#define FT1000_REG_MAG_WATERMARK 0x002c // WMARK - Watermark Register
// Reserved Dual Port RAM offsets for Electrabuzz
#define FT1000_DPRAM_TX_BASE 0x0002 // Host to PC Card Messaging Area
#define FT1000_DPRAM_RX_BASE 0x0800 // PC Card to Host Messaging Area
#define FT1000_FIFO_LEN 0x7FC // total length for DSP FIFO tracking
#define FT1000_HI_HO 0x7FE // heartbeat with HI/HO
#define FT1000_DSP_STATUS 0xFFE // dsp status - non-zero is a request to reset dsp
#define FT1000_DSP_LED 0xFFA // dsp led status for PAD device
#define FT1000_DSP_CON_STATE 0xFF8 // DSP Connection Status Info
#define FT1000_DPRAM_FEFE 0x002 // location for dsp ready indicator
#define FT1000_DSP_TIMER0 0x1FF0 // Timer Field from Basestation
#define FT1000_DSP_TIMER1 0x1FF2 // Timer Field from Basestation
#define FT1000_DSP_TIMER2 0x1FF4 // Timer Field from Basestation
#define FT1000_DSP_TIMER3 0x1FF6 // Timer Field from Basestation
// Reserved Dual Port RAM offsets for Magnemite
#define FT1000_DPRAM_MAG_TX_BASE 0x0000 // Host to PC Card Messaging Area
#define FT1000_DPRAM_MAG_RX_BASE 0x0200 // PC Card to Host Messaging Area
#define FT1000_MAG_FIFO_LEN 0x1FF // total length for DSP FIFO tracking
#define FT1000_MAG_FIFO_LEN_INDX 0x1 // low-word index
#define FT1000_MAG_HI_HO 0x1FF // heartbeat with HI/HO
#define FT1000_MAG_HI_HO_INDX 0x0 // high-word index
#define FT1000_MAG_DSP_LED 0x3FE // dsp led status for PAD device
#define FT1000_MAG_DSP_LED_INDX 0x0 // dsp led status for PAD device
#define FT1000_MAG_DSP_CON_STATE 0x3FE // DSP Connection Status Info
#define FT1000_MAG_DSP_CON_STATE_INDX 0x1 // DSP Connection Status Info
#define FT1000_MAG_DPRAM_FEFE 0x000 // location for dsp ready indicator
#define FT1000_MAG_DPRAM_FEFE_INDX 0x0 // location for dsp ready indicator
#define FT1000_MAG_DSP_TIMER0 0x3FC // Timer Field from Basestation
#define FT1000_MAG_DSP_TIMER0_INDX 0x1
#define FT1000_MAG_DSP_TIMER1 0x3FC // Timer Field from Basestation
#define FT1000_MAG_DSP_TIMER1_INDX 0x0
#define FT1000_MAG_DSP_TIMER2 0x3FD // Timer Field from Basestation
#define FT1000_MAG_DSP_TIMER2_INDX 0x1
#define FT1000_MAG_DSP_TIMER3 0x3FD // Timer Field from Basestation
#define FT1000_MAG_DSP_TIMER3_INDX 0x0
#define FT1000_MAG_TOTAL_LEN 0x200
#define FT1000_MAG_TOTAL_LEN_INDX 0x1
#define FT1000_MAG_PH_LEN 0x200
#define FT1000_MAG_PH_LEN_INDX 0x0
#define FT1000_MAG_PORT_ID 0x201
#define FT1000_MAG_PORT_ID_INDX 0x0
#define HOST_INTF_LE 0x0 // Host interface little endian mode
#define HOST_INTF_BE 0x1 // Host interface big endian mode
// PC Card to Host Doorbell assignments
#define FT1000_DB_DPRAM_RX 0x0001 // this value indicates that DSP has
// data for host in DPRAM
#define FT1000_ASIC_RESET_REQ 0x0004 // DSP requesting host to reset the ASIC
#define FT1000_DSP_ASIC_RESET 0x0008 // DSP indicating host that it will reset the ASIC
#define FT1000_DB_COND_RESET 0x0010 // DSP request for a card reset.
// Host to PC Card Doorbell assignments
#define FT1000_DB_DPRAM_TX 0x0100 // this value indicates that host has
// data for DSP in DPRAM.
#define FT1000_ASIC_RESET_DSP 0x0400 // Responds to FT1000_ASIC_RESET_REQ
#define FT1000_DB_HB 0x1000 // Indicates that supervisor
// has a heartbeat message for DSP.
#define FT1000_DPRAM_BASE 0x0000 // Dual Port RAM starting offset
#define hi 0x6869 // PC Card heartbeat values
#define ho 0x686f // PC Card heartbeat values
// Magnemite specific defines
#define hi_mag 0x6968 // Byte swap hi to avoid additional system call
#define ho_mag 0x6f68 // Byte swap ho to avoid additional system call
//
// Bit field definitions for Host Interrupt Status Register
//
// Indicate the cause of an interrupt.
//
#define ISR_EMPTY 0x00 // no bits set
#define ISR_DOORBELL_ACK 0x01 // Doorbell acknowledge from DSP
#define ISR_DOORBELL_PEND 0x02 // Doorbell pending from DSP
#define ISR_RCV 0x04 // Packet available in Downlink FIFO
#define ISR_WATERMARK 0x08 // Watermark requirements satisfied
// Bit field definition for Host Interrupt Mask
#define ISR_MASK_NONE 0x0000 // no bits set
#define ISR_MASK_DOORBELL_ACK 0x0001 // Doorbell acknowledge mask
#define ISR_MASK_DOORBELL_PEND 0x0002 // Doorbell pending mask
#define ISR_MASK_RCV 0x0004 // Downlink Packet available mask
#define ISR_MASK_WATERMARK 0x0008 // Watermark interrupt mask
#define ISR_MASK_ALL 0xffff // Mask all interrupts
// Bit field definition for Host Control Register
#define DSP_RESET_BIT 0x0001 // Bit field to control dsp reset state
// (0 = out of reset 1 = reset)
#define ASIC_RESET_BIT 0x0002 // Bit field to control ASIC reset state
// (0 = out of reset 1 = reset)
// Default interrupt mask (Enable Doorbell pending and Packet available interrupts)
#define ISR_DEFAULT_MASK 0x7ff9
// Application specific IDs
#define DSPID 0x20
#define HOSTID 0x10
#define DSPAIRID 0x90
#define DRIVERID 0x00
#define NETWORKID 0x20
// Size of DPRAM Message
#define MAX_CMD_SQSIZE 1780
#define ENET_MAX_SIZE 1514
#define ENET_HEADER_SIZE 14
#define SLOWQ_TYPE 0
#define FASTQ_TYPE 1
#define MAX_DSP_SESS_REC 1024
#define DSP_QID_OFFSET 4
#define PSEUDOSZ 16
#define PSEUDOSZWRD 8
// Maximum number of occurrence of pseudo header errors before resetting PC Card.
#define MAX_PH_ERR 300
// Driver message types
#define MEDIA_STATE 0x0010
#define TIME_UPDATE 0x0020
#define DSP_PROVISION 0x0030
#define DSP_INIT_MSG 0x0050
#define DSP_HIBERNATE 0x0060
#define DSP_STORE_INFO 0x0070
#define DSP_GET_INFO 0x0071
#define GET_DRV_ERR_RPT_MSG 0x0073
#define RSP_DRV_ERR_RPT_MSG 0x0074
// Driver Error Messages for DSP
#define DSP_HB_INFO 0x7ef0
#define DSP_FIFO_INFO 0x7ef1
#define DSP_CONDRESET_INFO 0x7ef2
#define DSP_CMDLEN_INFO 0x7ef3
#define DSP_CMDPHCKSUM_INFO 0x7ef4
#define DSP_PKTPHCKSUM_INFO 0x7ef5
#define DSP_PKTLEN_INFO 0x7ef6
#define DSP_USER_RESET 0x7ef7
#define FIFO_FLUSH_MAXLIMIT 0x7ef8
#define FIFO_FLUSH_BADCNT 0x7ef9
#define FIFO_ZERO_LEN 0x7efa
#define HOST_QID_OFFSET 5
#define QTYPE_OFFSET 13
#define SUCCESS 0x00
#define FAILURE 0x01
#define TRUE 0x1
#define FALSE 0x0
#define MAX_NUM_APP 6
#define MAXIMUM_ASIC_HB_CNT 15
typedef struct _DRVMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u8 data[0];
} __attribute__ ((packed)) DRVMSG, *PDRVMSG;
typedef struct _MEDIAMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u16 state;
u32 ip_addr;
u32 net_mask;
u32 gateway;
u32 dns_1;
u32 dns_2;
} __attribute__ ((packed)) MEDIAMSG, *PMEDIAMSG;
typedef struct _TIMEMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u8 timeval[8];
} __attribute__ ((packed)) TIMEMSG, *PTIMEMSG;
typedef struct _DSPINITMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u8 DspVer[DSPVERSZ]; // DSP version number
u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number
u8 Sku[SKUSZ]; // SKU
u8 eui64[EUISZ]; // EUI64
u8 ProductMode[MODESZ]; // Product Mode (Market/Production)
u8 RfCalVer[CALVERSZ]; // Rf Calibration version
u8 RfCalDate[CALDATESZ]; // Rf Calibration date
} __attribute__ ((packed)) DSPINITMSG, *PDSPINITMSG;
typedef struct _DSPHIBERNATE {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u32 timeout;
u16 sess_info[0];
} DSPHIBERNATE, *PDSPHIBERNATE;
typedef struct _APP_INFO_BLOCK
{
u32 fileobject; // Application's file object
u16 app_id; // Application id
} APP_INFO_BLOCK, *PAPP_INFO_BLOCK;
typedef struct _PROV_RECORD {
struct list_head list;
u8 *pprov_data;
} PROV_RECORD, *PPROV_RECORD;
typedef struct _FT1000_INFO {
struct net_device_stats stats;
u16 DrvErrNum;
u16 AsicID;
int ASICResetNum;
int DspAsicReset;
int PktIntfErr;
int DSPResetNum;
int NumIOCTLBufs;
int IOCTLBufLvl;
int DeviceCreated;
int CardReady;
int DspHibernateFlag;
int DSPReady;
u8 DeviceName[15];
int DeviceMajor;
int registered;
int mediastate;
u16 packetseqnum;
u8 squeseqnum; // sequence number on slow queue
spinlock_t dpram_lock;
u16 CurrentInterruptEnableMask;
int InterruptsEnabled;
u16 fifo_cnt;
u8 DspVer[DSPVERSZ]; // DSP version number
u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number
u8 Sku[SKUSZ]; // SKU
u8 eui64[EUISZ]; // EUI64
time_t ConTm; // Connection Time
u16 LedStat;
u16 ConStat;
u16 ProgConStat;
u8 ProductMode[MODESZ];
u8 RfCalVer[CALVERSZ];
u8 RfCalDate[CALDATESZ];
u16 DSP_TIME[4];
struct list_head prov_list;
int appcnt;
APP_INFO_BLOCK app_info[MAX_NUM_APP];
u16 DSPInfoBlklen;
u16 DrvMsgPend;
int (*ft1000_reset)(void *);
void *link;
u16 DSPInfoBlk[MAX_DSP_SESS_REC];
union {
u16 Rec[MAX_DSP_SESS_REC];
u32 MagRec[MAX_DSP_SESS_REC/2];
} DSPSess;
struct proc_dir_entry *proc_ft1000;
char netdevname[IFNAMSIZ];
} FT1000_INFO, *PFT1000_INFO;
typedef struct _DPRAM_BLK {
struct list_head list;
u16 *pbuffer;
} __attribute__ ((packed)) DPRAM_BLK, *PDPRAM_BLK;
extern u16 ft1000_read_dpram (struct net_device *dev, int offset);
extern void card_bootload(struct net_device *dev);
extern u16 ft1000_read_dpram_mag_16 (struct net_device *dev, int offset, int Index);
extern u32 ft1000_read_dpram_mag_32 (struct net_device *dev, int offset);
void ft1000_write_dpram_mag_32 (struct net_device *dev, int offset, u32 value);
#endif // _FT1000H_
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 1999 David A. Hinds. All Rights Reserved.
Copyright (C) 2002 Flarion Technologies, All rights reserved.
Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
Copyright (C) 2006 ProWeb Consulting, a.s, All rights reserved.
The initial developer of the original code is David A. Hinds
<dahinds@users.sourceforge.net>. Portions created by David A. Hinds.
This file was modified to support the Flarion Flash OFDM NIC Device
by Wai Chan (w.chan@flarion.com).
Port for kernel 2.6 created by Patrik Ostrihon (patrik.ostrihon@pwc.sk)
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.
-----------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
//#include <pcmcia/version.h> // Slavius 21.10.2009 removed from kernel
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/byteorder.h>
#include <asm/uaccess.h>
#include "ft1000_cs.h" // Slavius 21.10.2009 because CS_SUCCESS constant is missing due to removed pcmcia/version.h
/*====================================================================*/
/* Module parameters */
#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
MODULE_AUTHOR("Wai Chan");
MODULE_DESCRIPTION("FT1000 PCMCIA driver");
MODULE_LICENSE("GPL");
/* Newer, simpler way of listing specific interrupts */
/* The old way: bit map of interrupts to choose from */
/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
/*
All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
you do not define PCMCIA_DEBUG at all, all the debug code will be
left out. If you compile with PCMCIA_DEBUG=0, the debug code will
be present but disabled.
*/
#ifdef FT_DEBUG
#define DEBUG(n, args...) printk(KERN_DEBUG args)
#else
#define DEBUG(n, args...)
#endif
/*====================================================================*/
struct net_device *init_ft1000_card(int, int, unsigned char *,
void *ft1000_reset, struct pcmcia_device * link,
struct device *fdev);
void stop_ft1000_card(struct net_device *);
static int ft1000_config(struct pcmcia_device *link);
static void ft1000_release(struct pcmcia_device *link);
/*
The attach() and detach() entry points are used to create and destroy
"instances" of the driver, where each instance represents everything
needed to manage one actual PCMCIA card.
*/
static void ft1000_detach(struct pcmcia_device *link);
static int ft1000_attach(struct pcmcia_device *link);
typedef struct local_info_t {
struct pcmcia_device *link;
struct net_device *dev;
} local_info_t;
#define MAX_ASIC_RESET_CNT 10
#define COR_DEFAULT 0x55
/*====================================================================*/
static void ft1000_reset(struct pcmcia_device * link)
{
conf_reg_t reg;
DEBUG(0, "ft1000_cs:ft1000_reset is called................\n");
/* Soft-Reset card */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = COR_SOFT_RESET;
pcmcia_access_configuration_register(link, &reg);
/* Wait until the card has acknowledged our reset */
udelay(2);
/* Restore original COR configuration index */
/* Need at least 2 write to respond */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = COR_DEFAULT;
pcmcia_access_configuration_register(link, &reg);
/* Wait until the card has finished restarting */
udelay(1);
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = COR_DEFAULT;
pcmcia_access_configuration_register(link, &reg);
/* Wait until the card has finished restarting */
udelay(1);
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = COR_DEFAULT;
pcmcia_access_configuration_register(link, &reg);
/* Wait until the card has finished restarting */
udelay(1);
}
/*====================================================================*/
static int get_tuple_first(struct pcmcia_device *link, tuple_t * tuple,
cisparse_t * parse)
{
int i;
i = pcmcia_get_first_tuple(link, tuple);
if (i != CS_SUCCESS)
return i;
i = pcmcia_get_tuple_data(link, tuple);
if (i != CS_SUCCESS)
return i;
return pcmcia_parse_tuple(tuple, parse); // Slavius 21.10.2009 removed unused link parameter
}
static int get_tuple_next(struct pcmcia_device *link, tuple_t * tuple,
cisparse_t * parse)
{
int i;
i = pcmcia_get_next_tuple(link, tuple);
if (i != CS_SUCCESS)
return i;
i = pcmcia_get_tuple_data(link, tuple);
if (i != CS_SUCCESS)
return i;
return pcmcia_parse_tuple(tuple, parse); // Slavius 21.10.2009 removed unused link parameter
}
/*======================================================================
======================================================================*/
static int ft1000_attach(struct pcmcia_device *link)
{
local_info_t *local;
DEBUG(0, "ft1000_cs: ft1000_attach()\n");
local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
if (!local) {
return -ENOMEM;
}
memset(local, 0, sizeof(local_info_t));
local->link = link;
link->priv = local;
local->dev = NULL;
link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
link->irq.Handler = NULL;
return ft1000_config(link);
} /* ft1000_attach */
/*======================================================================
This deletes a driver "instance". The device is de-registered
with Card Services. If it has been released, all local data
structures are freed. Otherwise, the structures will be freed
when the device is released.
======================================================================*/
static void ft1000_detach(struct pcmcia_device *link)
{
struct net_device *dev = ((local_info_t *) link->priv)->dev;
DEBUG(0, "ft1000_cs: ft1000_detach(0x%p)\n", link);
if (link == NULL) {
DEBUG(0,"ft1000_cs:ft1000_detach: Got a NULL pointer\n");
return;
}
if (dev) {
stop_ft1000_card(dev);
}
ft1000_release(link);
/* This points to the parent local_info_t struct */
free_netdev(dev);
} /* ft1000_detach */
/*======================================================================
ft1000_config() is scheduled to run after a CARD_INSERTION event
is received, to configure the PCMCIA socket, and to make the
device available to the system.
======================================================================*/
#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
#define CFG_CHECK(fn, ret) \
last_fn = (fn); if ((last_ret = (ret)) != 0) goto next_entry
static int ft1000_config(struct pcmcia_device * link)
{
tuple_t tuple;
cisparse_t parse;
int last_fn, last_ret, i;
u_char buf[64];
cistpl_lan_node_id_t *node_id;
cistpl_cftable_entry_t dflt = { 0 };
cistpl_cftable_entry_t *cfg;
unsigned char mac_address[6];
DEBUG(0, "ft1000_cs: ft1000_config(0x%p)\n", link);
/*
This reads the card's CONFIG tuple to find its configuration
registers.
*/
// tuple.DesiredTuple = CISTPL_CONFIG;
// tuple.Attributes = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
tuple.TupleOffset = 0;
// CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
// CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
// CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse));
// link->conf.ConfigBase = parse.config.base;
// link->conf.Present = parse.config.rmask[0];
/*
In this loop, we scan the CIS for configuration table entries,
each of which describes a valid card configuration, including
voltage, IO window, memory window, and interrupt settings.
We make no assumptions about the card to be configured: we use
just the information available in the CIS. In an ideal world,
this would work for any PCMCIA card, but it requires a complete
and accurate CIS. In practice, a driver usually "knows" most of
these things without consulting the CIS, and most client drivers
will only use the CIS to fill in implementation-defined details.
*/
tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
tuple.Attributes = 0;
CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
while (1) {
cfg = &(parse.cftable_entry);
CFG_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple));
CFG_CHECK(ParseTuple,
pcmcia_parse_tuple(&tuple, &parse)); // Slavius 21.10.2009 removed unused link parameter
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
dflt = *cfg;
if (cfg->index == 0)
goto next_entry;
link->conf.ConfigIndex = cfg->index;
/* Do we need to allocate an interrupt? */
if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
link->conf.Attributes |= CONF_ENABLE_IRQ;
/* IO window settings */
link->io.NumPorts1 = link->io.NumPorts2 = 0;
if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) {
cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io;
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
if (!(io->flags & CISTPL_IO_8BIT)) {
DEBUG(0, "ft1000_cs: IO_DATA_PATH_WIDTH_16\n");
link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
}
if (!(io->flags & CISTPL_IO_16BIT)) {
DEBUG(0, "ft1000_cs: IO_DATA_PATH_WIDTH_8\n");
link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
}
link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
link->io.BasePort1 = io->win[0].base;
link->io.NumPorts1 = io->win[0].len;
if (io->nwin > 1) {
link->io.Attributes2 = link->io.Attributes1;
link->io.BasePort2 = io->win[1].base;
link->io.NumPorts2 = io->win[1].len;
}
/* This reserves IO space but doesn't actually enable it */
pcmcia_request_io(link, &link->io);
}
break;
next_entry:
last_ret = pcmcia_get_next_tuple(link, &tuple);
}
if (last_ret != CS_SUCCESS) {
cs_error(link, RequestIO, last_ret);
goto failed;
}
/*
Allocate an interrupt line. Note that this does not assign a
handler to the interrupt, unless the 'Handler' member of the
irq structure is initialized.
*/
CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
/*
This actually configures the PCMCIA socket -- setting up
the I/O windows and the interrupt mapping, and putting the
card and host interface into "Memory and IO" mode.
*/
CS_CHECK(RequestConfiguration,
pcmcia_request_configuration(link, &link->conf));
/* Get MAC address from tuples */
tuple.Attributes = tuple.TupleOffset = 0;
tuple.TupleData = buf;
tuple.TupleDataMax = sizeof(buf);
/* Check for a LAN function extension tuple */
tuple.DesiredTuple = CISTPL_FUNCE;
i = get_tuple_first(link, &tuple, &parse);
while (i == CS_SUCCESS) {
if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID)
break;
i = get_tuple_next(link, &tuple, &parse);
}
if (i == CS_SUCCESS) {
node_id = (cistpl_lan_node_id_t *) parse.funce.data;
if (node_id->nb == 6) {
for (i = 0; i < 6; i++)
mac_address[i] = node_id->id[i];
}
}
((local_info_t *) link->priv)->dev =
init_ft1000_card(link->irq.AssignedIRQ, link->io.BasePort1,
&mac_address[0], ft1000_reset, link,
&handle_to_dev(link));
/*
At this point, the dev_node_t structure(s) need to be
initialized and arranged in a linked list at link->dev.
*/
/* Finally, report what we've done */
return 0;
cs_failed:
cs_error(link, last_fn, last_ret);
failed:
ft1000_release(link);
return -ENODEV;
} /* ft1000_config */
/*======================================================================
After a card is removed, ft1000_release() will unregister the
device, and release the PCMCIA configuration. If the device is
still open, this will be postponed until it is closed.
======================================================================*/
static void ft1000_release(struct pcmcia_device * link)
{
DEBUG(0, "ft1000_cs: ft1000_release(0x%p)\n", link);
/*
If the device is currently in use, we won't release until it
is actually closed, because until then, we can't be sure that
no one will try to access the device or its data structures.
*/
/* Unlink the device chain */
link->dev_node = NULL;
/*
In a normal driver, additional code may be needed to release
other kernel data structures associated with this device.
*/
/* Don't bother checking to see if these succeed or not */
pcmcia_disable_device(link);
} /* ft1000_release */
/*======================================================================
The card status event handler. Mostly, this schedules other
stuff to run after an event is received.
When a CARD_REMOVAL event is received, we immediately set a
private flag to block future accesses to this device. All the
functions that actually access the device should check this flag
to make sure the card is still present.
======================================================================*/
static int ft1000_suspend(struct pcmcia_device *link)
{
struct net_device *dev = ((local_info_t *) link->priv)->dev;
DEBUG(1, "ft1000_cs: ft1000_event(0x%06x)\n", event);
if (link->open)
netif_device_detach(dev);
return 0;
}
static int ft1000_resume(struct pcmcia_device *link)
{
/* struct net_device *dev = link->priv;
*/
return 0;
}
/*====================================================================*/
static struct pcmcia_device_id ft1000_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x0100),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1000),
PCMCIA_DEVICE_MANF_CARD(0x02cc, 0x1300),
PCMCIA_DEVICE_NULL,
};
MODULE_DEVICE_TABLE(pcmcia, ft1000_ids);
static struct pcmcia_driver ft1000_cs_driver = {
.owner = THIS_MODULE,
.drv = {
.name = "ft1000_cs",
},
.probe = ft1000_attach,
.remove = ft1000_detach,
.id_table = ft1000_ids,
.suspend = ft1000_suspend,
.resume = ft1000_resume,
};
static int __init init_ft1000_cs(void)
{
DEBUG(0, "ft1000_cs: loading\n");
return pcmcia_register_driver(&ft1000_cs_driver);
}
static void __exit exit_ft1000_cs(void)
{
DEBUG(0, "ft1000_cs: unloading\n");
pcmcia_unregister_driver(&ft1000_cs_driver);
}
module_init(init_ft1000_cs);
module_exit(exit_ft1000_cs);
//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// File: ft1000_dev.h
//
// Description: Register definitions and bit masks for the FT1000 NIC
//
// History:
// 2/5/02 Ivan Bohannon Written.
// 8/29/02 Whc Ported to Linux.
//
//---------------------------------------------------------------------------
#ifndef _FT1000_DEVH_
#define _FT1000_DEVH_
//---------------------------------------------------------------------------
//
// Function: ft1000_read_reg
// Descripton: This function will read the value of a given ASIC register.
// Input:
// dev - device structure
// offset - ASIC register offset
// Output:
// data - ASIC register value
//
//---------------------------------------------------------------------------
static inline u16 ft1000_read_reg (struct net_device *dev, u16 offset) {
u16 data = 0;
data = inw(dev->base_addr + offset);
return (data);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_write_reg
// Descripton: This function will set the value for a given ASIC register.
// Input:
// dev - device structure
// offset - ASIC register offset
// value - value to write
// Output:
// None.
//
//---------------------------------------------------------------------------
static inline void ft1000_write_reg (struct net_device *dev, u16 offset, u16 value) {
outw (value, dev->base_addr + offset);
}
#endif // _FT1000_DEVH_
/*---------------------------------------------------------------------------
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;
}
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2002 Flarion Technologies, All rights reserved.
Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
Copyright (C) 2006 ProWeb Consulting, a.s, 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.
-----------------------------------------------------------------------------*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/bitops.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/wait.h>
#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/ethtool.h>
#ifdef FT_DEBUG
#define DEBUG(n, args...) printk(KERN_DEBUG args);
#else
#define DEBUG(n, args...)
#endif
#include <linux/delay.h>
#include "ft1000_dev.h"
#include "ft1000.h"
int card_download(struct net_device *dev, void *pFileStart, UINT FileLength);
void ft1000InitProc(struct net_device *dev);
void ft1000CleanupProc(struct net_device *dev);
const struct firmware *fw_entry;
static void ft1000_hbchk(u_long data);
static struct timer_list poll_timer = {
function:ft1000_hbchk
};
static u16 cmdbuffer[1024];
static u8 tempbuffer[1600];
static u8 ft1000_card_present = 0;
static u8 flarion_ft1000_cnt = 0;
static irqreturn_t ft1000_interrupt(int irq, void *dev_id);
static void ft1000_enable_interrupts(struct net_device *dev);
static void ft1000_disable_interrupts(struct net_device *dev);
/* new kernel */
MODULE_AUTHOR("");
MODULE_DESCRIPTION
("Support for Flarion Flash OFDM NIC Device. Support for PCMCIA when used with ft1000_cs.");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("FT1000");
#define MAX_RCV_LOOP 100
//---------------------------------------------------------------------------
//
// Function: ft1000_asic_read
// Descripton: This function will retrieve the value of a specific ASIC
// register.
// Input:
// dev - network device structure
// offset - ASIC register to read
// Output:
// value - value of ASIC register
//
//---------------------------------------------------------------------------
inline u16 ft1000_asic_read(struct net_device *dev, u16 offset)
{
return (ft1000_read_reg(dev, offset));
}
//---------------------------------------------------------------------------
//
// Function: ft1000_asic_write
// Descripton: This function will set the value of a specific ASIC
// register.
// Input:
// dev - network device structure
// value - value to set ASIC register
// Output:
// none
//
//---------------------------------------------------------------------------
inline void ft1000_asic_write(struct net_device *dev, u16 offset, u16 value)
{
ft1000_write_reg(dev, offset, value);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_read_fifo_len
// Descripton: This function will read the ASIC Uplink FIFO status register
// which will return the number of bytes remaining in the Uplink FIFO.
// Sixteen bytes are subtracted to make sure that the ASIC does not
// reach its threshold.
// Input:
// dev - network device structure
// Output:
// value - number of bytes available in the ASIC Uplink FIFO.
//
//---------------------------------------------------------------------------
static inline u16 ft1000_read_fifo_len(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
if (info->AsicID == ELECTRABUZZ_ID) {
return (ft1000_read_reg(dev, FT1000_REG_UFIFO_STAT) - 16);
} else {
return (ft1000_read_reg(dev, FT1000_REG_MAG_UFSR) - 16);
}
}
//---------------------------------------------------------------------------
//
// Function: ft1000_read_dpram
// Descripton: This function will read the specific area of dpram
// (Electrabuzz ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// Output:
// value - value of dpram
//
//---------------------------------------------------------------------------
u16 ft1000_read_dpram(struct net_device * dev, int offset)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
u16 data;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
data = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
spin_unlock_irqrestore(&info->dpram_lock, flags);
return (data);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_write_dpram
// Descripton: This function will write to a specific area of dpram
// (Electrabuzz ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// value - value to write
// Output:
// none.
//
//---------------------------------------------------------------------------
static inline void ft1000_write_dpram(struct net_device *dev,
int offset, u16 value)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, value);
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_read_dpram_mag_16
// Descripton: This function will read the specific area of dpram
// (Magnemite ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// Output:
// value - value of dpram
//
//---------------------------------------------------------------------------
u16 ft1000_read_dpram_mag_16(struct net_device *dev, int offset, int Index)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
u16 data;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
// check if we want to read upper or lower 32-bit word
if (Index) {
data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAL);
} else {
data = ft1000_read_reg(dev, FT1000_REG_MAG_DPDATAH);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
return (data);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_write_dpram_mag_16
// Descripton: This function will write to a specific area of dpram
// (Magnemite ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// value - value to write
// Output:
// none.
//
//---------------------------------------------------------------------------
static inline void ft1000_write_dpram_mag_16(struct net_device *dev,
int offset, u16 value, int Index)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
if (Index) {
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAL, value);
} else {
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, value);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_read_dpram_mag_32
// Descripton: This function will read the specific area of dpram
// (Magnemite ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// Output:
// value - value of dpram
//
//---------------------------------------------------------------------------
u32 ft1000_read_dpram_mag_32(struct net_device *dev, int offset)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
u32 data;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
data = inl(dev->base_addr + FT1000_REG_MAG_DPDATAL);
spin_unlock_irqrestore(&info->dpram_lock, flags);
return (data);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_write_dpram_mag_32
// Descripton: This function will write to a specific area of dpram
// (Magnemite ASIC only)
// Input:
// dev - device structure
// offset - index of dpram
// value - value to write
// Output:
// none.
//
//---------------------------------------------------------------------------
void ft1000_write_dpram_mag_32(struct net_device *dev, int offset, u32 value)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
unsigned long flags;
// Provide mutual exclusive access while reading ASIC registers.
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, offset);
outl(value, dev->base_addr + FT1000_REG_MAG_DPDATAL);
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_enable_interrupts
// Descripton: This function will enable interrupts base on the current interrupt mask.
// Input:
// dev - device structure
// Output:
// None.
//
//---------------------------------------------------------------------------
static void ft1000_enable_interrupts(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 tempword;
DEBUG(1, "ft1000_hw:ft1000_enable_interrupts()\n");
ft1000_write_reg(dev, FT1000_REG_SUP_IMASK,
info->CurrentInterruptEnableMask);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
DEBUG(1,
"ft1000_hw:ft1000_enable_interrupts:current interrupt enable mask = 0x%x\n",
tempword);
info->InterruptsEnabled = TRUE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_disable_interrupts
// Descripton: This function will disable all interrupts.
// Input:
// dev - device structure
// Output:
// None.
//
//---------------------------------------------------------------------------
static void ft1000_disable_interrupts(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 tempword;
DEBUG(1, "ft1000_hw: ft1000_disable_interrupts()\n");
ft1000_write_reg(dev, FT1000_REG_SUP_IMASK, ISR_MASK_ALL);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
DEBUG(1,
"ft1000_hw:ft1000_disable_interrupts:current interrupt enable mask = 0x%x\n",
tempword);
info->InterruptsEnabled = FALSE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_reset_asic
// Descripton: This function will call the Card Service function to reset the
// ASIC.
// Input:
// dev - device structure
// Output:
// none
//
//---------------------------------------------------------------------------
static void ft1000_reset_asic(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 tempword;
DEBUG(1, "ft1000_hw:ft1000_reset_asic called\n");
(*info->ft1000_reset) (info->link);
info->ASICResetNum++;
// Let's use the register provided by the Magnemite ASIC to reset the
// ASIC and DSP.
if (info->AsicID == MAGNEMITE_ID) {
ft1000_write_reg(dev, FT1000_REG_RESET,
(DSP_RESET_BIT | ASIC_RESET_BIT));
}
mdelay(1);
if (info->AsicID == ELECTRABUZZ_ID) {
// set watermark to -1 in order to not generate an interrrupt
ft1000_write_reg(dev, FT1000_REG_WATERMARK, 0xffff);
} else {
// set watermark to -1 in order to not generate an interrrupt
ft1000_write_reg(dev, FT1000_REG_MAG_WATERMARK, 0xffff);
}
// clear interrupts
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword);
ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_reset_card
// Descripton: This function will reset the card
// Input:
// dev - device structure
// Output:
// status - FALSE (card reset fail)
// TRUE (card reset successful)
//
//---------------------------------------------------------------------------
static int ft1000_reset_card(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 tempword;
int i;
unsigned long flags;
PPROV_RECORD ptr;
DEBUG(1, "ft1000_hw:ft1000_reset_card called.....\n");
info->CardReady = 0;
info->ProgConStat = 0;
info->squeseqnum = 0;
ft1000_disable_interrupts(dev);
// del_timer(&poll_timer);
// Make sure we free any memory reserve for provisioning
while (list_empty(&info->prov_list) == 0) {
DEBUG(0,
"ft1000_hw:ft1000_reset_card:deleting provisioning record\n");
ptr = list_entry(info->prov_list.next, PROV_RECORD, list);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
if (info->AsicID == ELECTRABUZZ_ID) {
DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting DSP\n");
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
} else {
DEBUG(1,
"ft1000_hw:ft1000_reset_card:resetting ASIC and DSP\n");
ft1000_write_reg(dev, FT1000_REG_RESET,
(DSP_RESET_BIT | ASIC_RESET_BIT));
}
// Copy DSP session record into info block if this is not a coldstart
if (ft1000_card_present == 1) {
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
if (info->DspHibernateFlag == 0) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC; i++) {
info->DSPSess.Rec[i] =
ft1000_read_reg(dev,
FT1000_REG_DPRAM_DATA);
}
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
info->DSPSess.MagRec[i] =
inl(dev->base_addr + FT1000_REG_MAG_DPDATA);
}
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
}
DEBUG(1, "ft1000_hw:ft1000_reset_card:resetting ASIC\n");
mdelay(10);
//reset ASIC
ft1000_reset_asic(dev);
info->DSPResetNum++;
DEBUG(1, "ft1000_hw:ft1000_reset_card:downloading dsp image\n");
if (info->AsicID == MAGNEMITE_ID) {
// Put dsp in reset and take ASIC out of reset
DEBUG(0,
"ft1000_hw:ft1000_reset_card:Put DSP in reset and take ASIC out of reset\n");
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
// Setting MAGNEMITE ASIC to big endian mode
ft1000_write_reg(dev, FT1000_REG_SUP_CTRL, HOST_INTF_BE);
// Download bootloader
card_bootload(dev);
// Take DSP out of reset
ft1000_write_reg(dev, FT1000_REG_RESET, 0);
// FLARION_DSP_ACTIVE;
mdelay(10);
DEBUG(0, "ft1000_hw:ft1000_reset_card:Take DSP out of reset\n");
// Wait for 0xfefe indicating dsp ready before starting download
for (i = 0; i < 50; i++) {
tempword =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DPRAM_FEFE,
FT1000_MAG_DPRAM_FEFE_INDX);
if (tempword == 0xfefe) {
break;
}
mdelay(20);
}
if (i == 50) {
DEBUG(0,
"ft1000_hw:ft1000_reset_card:No FEFE detected from DSP\n");
return FALSE;
}
} else {
// Take DSP out of reset
ft1000_write_reg(dev, FT1000_REG_RESET, ~DSP_RESET_BIT);
mdelay(10);
}
if (card_download(dev, fw_entry->data, fw_entry->size)) {
DEBUG(1, "card download unsuccessful\n");
return FALSE;
} else {
DEBUG(1, "card download successful\n");
}
mdelay(10);
if (info->AsicID == ELECTRABUZZ_ID) {
// Need to initialize the FIFO length counter to zero in order to sync up
// with the DSP
info->fifo_cnt = 0;
ft1000_write_dpram(dev, FT1000_FIFO_LEN, info->fifo_cnt);
// Initialize DSP heartbeat area to ho
ft1000_write_dpram(dev, FT1000_HI_HO, ho);
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
DEBUG(1, "ft1000_hw:ft1000_reset_asic:hi_ho value = 0x%x\n",
tempword);
} else {
// Initialize DSP heartbeat area to ho
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, ho_mag,
FT1000_MAG_HI_HO_INDX);
tempword =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX);
DEBUG(1, "ft1000_hw:ft1000_reset_card:hi_ho value = 0x%x\n",
tempword);
}
info->CardReady = 1;
ft1000_enable_interrupts(dev);
/* Schedule heartbeat process to run every 2 seconds */
// poll_timer.expires = jiffies + (2*HZ);
// poll_timer.data = (u_long)dev;
// add_timer(&poll_timer);
return TRUE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_chkcard
// Descripton: This function will check if the device is presently available on
// the system.
// Input:
// dev - device structure
// Output:
// status - FALSE (device is not present)
// TRUE (device is present)
//
//---------------------------------------------------------------------------
static int ft1000_chkcard(struct net_device *dev)
{
u16 tempword;
// Mask register is used to check for device presence since it is never
// set to zero.
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_IMASK);
if (tempword == 0) {
DEBUG(1,
"ft1000_hw:ft1000_chkcard: IMASK = 0 Card not detected\n");
return FALSE;
}
// The system will return the value of 0xffff for the version register
// if the device is not present.
tempword = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
if (tempword == 0xffff) {
DEBUG(1,
"ft1000_hw:ft1000_chkcard: Version = 0xffff Card not detected\n");
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_hbchk
// Descripton: This function will perform the heart beat check of the DSP as
// well as the ASIC.
// Input:
// dev - device structure
// Output:
// none
//
//---------------------------------------------------------------------------
static void ft1000_hbchk(u_long data)
{
struct net_device *dev = (struct net_device *)data;
FT1000_INFO *info;
USHORT tempword;
info = (FT1000_INFO *) netdev_priv(dev);
if (info->CardReady == 1) {
// Perform dsp heartbeat check
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
} else {
tempword =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
DEBUG(1, "ft1000_hw:ft1000_hbchk:hi_ho value = 0x%x\n",
tempword);
// Let's perform another check if ho is not detected
if (tempword != ho) {
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
}
else {
tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX));
}
}
if (tempword != ho) {
printk(KERN_INFO
"ft1000: heartbeat failed - no ho detected\n");
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
printk(KERN_INFO
"ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
// Let's check doorbell again if fail
if (tempword & FT1000_DB_HB) {
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
}
if (tempword & FT1000_DB_HB) {
printk(KERN_INFO
"ft1000: heartbeat doorbell not clear by firmware\n");
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
printk(KERN_INFO
"ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
// Set dedicated area to hi and ring appropriate doorbell according
// to hi/ho heartbeat protocol
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_dpram(dev, FT1000_HI_HO, hi);
} else {
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag,
FT1000_MAG_HI_HO_INDX);
}
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
} else {
tempword =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_HI_HO,
FT1000_MAG_HI_HO_INDX));
}
// Let's write hi again if fail
if (tempword != hi) {
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_dpram(dev, FT1000_HI_HO, hi);
}
else {
ft1000_write_dpram_mag_16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX);
}
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_dpram(dev, FT1000_HI_HO);
}
else {
tempword = ntohs(ft1000_read_dpram_mag_16(dev, FT1000_MAG_HI_HO, FT1000_MAG_HI_HO_INDX));
}
}
if (tempword != hi) {
printk(KERN_INFO
"ft1000: heartbeat failed - cannot write hi into DPRAM\n");
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev) == 0) {
printk(KERN_INFO
"ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2*HZ);
poll_timer.data = (u_long)dev;
add_timer(&poll_timer);
return;
}
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_HB);
}
/* Schedule this module to run every 2 seconds */
poll_timer.expires = jiffies + (2 * HZ);
poll_timer.data = (u_long) dev;
add_timer(&poll_timer);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_send_cmd
// Descripton:
// Input:
// Output:
//
//---------------------------------------------------------------------------
void ft1000_send_cmd (struct net_device *dev, u16 *ptempbuffer, int size, u16 qtype)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
int i;
u16 tempword;
unsigned long flags;
size += PSEUDOSZ;
// check for odd byte and increment to 16-bit word align value
if ((size & 0x0001)) {
size++;
}
DEBUG(1, "FT1000:ft1000_send_cmd:total length = %d\n", size);
DEBUG(1, "FT1000:ft1000_send_cmd:length = %d\n", ntohs(*ptempbuffer));
// put message into slow queue area
// All messages are in the form total_len + pseudo header + message body
spin_lock_irqsave(&info->dpram_lock, flags);
// Make sure SLOWQ doorbell is clear
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
i=0;
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
i++;
if (i==10) {
spin_unlock_irqrestore(&info->dpram_lock, flags);
return;
}
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
}
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_TX_BASE);
// Write total length to dpram
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
// Write pseudo header and messgae body
for (i = 0; i < (size >> 1); i++) {
DEBUG(1, "FT1000:ft1000_send_cmd:data %d = 0x%x\n", i,
*ptempbuffer);
tempword = htons(*ptempbuffer++);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, tempword);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_TX_BASE);
// Write total length to dpram
ft1000_write_reg(dev, FT1000_REG_MAG_DPDATAH, htons(size));
// Write pseudo header and messgae body
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_TX_BASE + 1);
for (i = 0; i < (size >> 2); i++) {
DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n",
*ptempbuffer);
outw(*ptempbuffer++,
dev->base_addr + FT1000_REG_MAG_DPDATAL);
DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n",
*ptempbuffer);
outw(*ptempbuffer++,
dev->base_addr + FT1000_REG_MAG_DPDATAH);
}
DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAL);
DEBUG(1, "FT1000:ft1000_send_cmd:data = 0x%x\n", *ptempbuffer);
outw(*ptempbuffer++, dev->base_addr + FT1000_REG_MAG_DPDATAH);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
// ring doorbell to notify DSP that we have a message ready
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_TX);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_receive_cmd
// Descripton: This function will read a message from the dpram area.
// Input:
// dev - network device structure
// pbuffer - caller supply address to buffer
// pnxtph - pointer to next pseudo header
// Output:
// Status = 0 (unsuccessful)
// = 1 (successful)
//
//---------------------------------------------------------------------------
BOOLEAN ft1000_receive_cmd(struct net_device *dev, u16 * pbuffer, int maxsz, u16 *pnxtph)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 size;
u16 *ppseudohdr;
int i;
u16 tempword;
unsigned long flags;
if (info->AsicID == ELECTRABUZZ_ID) {
size = ( ft1000_read_dpram(dev, *pnxtph) ) + PSEUDOSZ;
} else {
size =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_PH_LEN,
FT1000_MAG_PH_LEN_INDX)) + PSEUDOSZ;
}
if (size > maxsz) {
DEBUG(1,
"FT1000:ft1000_receive_cmd:Invalid command length = %d\n",
size);
return FALSE;
} else {
ppseudohdr = (u16 *) pbuffer;
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE + 2);
for (i = 0; i <= (size >> 1); i++) {
tempword =
ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
*pbuffer++ = ntohs(tempword);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE + 1);
for (i = 0; i <= (size >> 2); i++) {
*pbuffer =
inw(dev->base_addr +
FT1000_REG_MAG_DPDATAL);
pbuffer++;
*pbuffer =
inw(dev->base_addr +
FT1000_REG_MAG_DPDATAH);
pbuffer++;
}
//copy odd aligned word
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAL);
DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
*pbuffer = inw(dev->base_addr + FT1000_REG_MAG_DPDATAH);
DEBUG(1, "ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
}
if (size & 0x0001) {
//copy odd byte from fifo
tempword = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
*pbuffer = ntohs(tempword);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
// Check if pseudo header checksum is good
// Calculate pseudo header checksum
tempword = *ppseudohdr++;
for (i = 1; i < 7; i++) {
tempword ^= *ppseudohdr++;
}
if ((tempword != *ppseudohdr)) {
DEBUG(1,
"FT1000:ft1000_receive_cmd:Pseudo header checksum mismatch\n");
// Drop this message
return FALSE;
}
return TRUE;
}
}
//---------------------------------------------------------------------------
//
// Function: ft1000_proc_drvmsg
// Descripton: This function will process the various driver messages.
// Input:
// dev - device structure
// pnxtph - pointer to next pseudo header
// Output:
// none
//
//---------------------------------------------------------------------------
void ft1000_proc_drvmsg(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 msgtype;
u16 tempword;
PMEDIAMSG pmediamsg;
PDSPINITMSG pdspinitmsg;
PDRVMSG pdrvmsg;
u16 len;
u16 i;
PPROV_RECORD ptr;
PPSEUDO_HDR ppseudo_hdr;
PUSHORT pmsg;
struct timeval tv;
union {
u8 byte[2];
u16 wrd;
} convert;
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = FT1000_DPRAM_RX_BASE+2;
}
else {
tempword = FT1000_DPRAM_MAG_RX_BASE;
}
if ( ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword) ) {
// Get the message type which is total_len + PSEUDO header + msgtype + message body
pdrvmsg = (PDRVMSG) & cmdbuffer[0];
msgtype = ntohs(pdrvmsg->type);
DEBUG(1, "Command message type = 0x%x\n", msgtype);
switch (msgtype) {
case DSP_PROVISION:
DEBUG(0,
"Got a provisioning request message from DSP\n");
mdelay(25);
while (list_empty(&info->prov_list) == 0) {
DEBUG(0, "Sending a provisioning message\n");
// Make sure SLOWQ doorbell is clear
tempword =
ft1000_read_reg(dev, FT1000_REG_DOORBELL);
i = 0;
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(5);
i++;
if (i == 10) {
break;
}
}
ptr =
list_entry(info->prov_list.next,
PROV_RECORD, list);
len = *(u16 *) ptr->pprov_data;
len = htons(len);
pmsg = (PUSHORT) ptr->pprov_data;
ppseudo_hdr = (PPSEUDO_HDR) pmsg;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
DEBUG(1, "checksum = 0x%x\n",
ppseudo_hdr->checksum);
for (i = 1; i < 7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
DEBUG(1, "checksum = 0x%x\n",
ppseudo_hdr->checksum);
}
ft1000_send_cmd (dev, (u16 *)ptr->pprov_data, len, SLOWQ_TYPE);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
// Indicate adapter is ready to take application messages after all
// provisioning messages are sent
info->CardReady = 1;
break;
case MEDIA_STATE:
pmediamsg = (PMEDIAMSG) & cmdbuffer[0];
if (info->ProgConStat != 0xFF) {
if (pmediamsg->state) {
DEBUG(1, "Media is up\n");
if (info->mediastate == 0) {
netif_carrier_on(dev);
netif_wake_queue(dev);
info->mediastate = 1;
do_gettimeofday(&tv);
info->ConTm = tv.tv_sec;
}
} else {
DEBUG(1, "Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
netif_carrier_off(dev);
netif_stop_queue(dev);
info->ConTm = 0;
}
}
}
else {
DEBUG(1,"Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
netif_carrier_off(dev);
netif_stop_queue(dev);
info->ConTm = 0;
}
}
break;
case DSP_INIT_MSG:
pdspinitmsg = (PDSPINITMSG) & cmdbuffer[0];
memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
DEBUG(1, "DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n",
info->DspVer[0], info->DspVer[1], info->DspVer[2],
info->DspVer[3]);
memcpy(info->HwSerNum, pdspinitmsg->HwSerNum,
HWSERNUMSZ);
memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
dev->dev_addr[0] = info->eui64[0];
dev->dev_addr[1] = info->eui64[1];
dev->dev_addr[2] = info->eui64[2];
dev->dev_addr[3] = info->eui64[5];
dev->dev_addr[4] = info->eui64[6];
dev->dev_addr[5] = info->eui64[7];
if (ntohs(pdspinitmsg->length) ==
(sizeof(DSPINITMSG) - 20)) {
memcpy(info->ProductMode,
pdspinitmsg->ProductMode, MODESZ);
memcpy(info->RfCalVer, pdspinitmsg->RfCalVer,
CALVERSZ);
memcpy(info->RfCalDate, pdspinitmsg->RfCalDate,
CALDATESZ);
DEBUG(1, "RFCalVer = 0x%2x 0x%2x\n",
info->RfCalVer[0], info->RfCalVer[1]);
}
break ;
case DSP_STORE_INFO:
DEBUG(1, "FT1000:drivermsg:Got DSP_STORE_INFO\n");
tempword = ntohs(pdrvmsg->length);
info->DSPInfoBlklen = tempword;
if (tempword < (MAX_DSP_SESS_REC - 4)) {
pmsg = (PUSHORT) & pdrvmsg->data[0];
for (i = 0; i < ((tempword + 1) / 2); i++) {
DEBUG(1,
"FT1000:drivermsg:dsp info data = 0x%x\n",
*pmsg);
info->DSPInfoBlk[i + 10] = *pmsg++;
}
}
break;
case DSP_GET_INFO:
DEBUG(1, "FT1000:drivermsg:Got DSP_GET_INFO\n");
// copy dsp info block to dsp
info->DrvMsgPend = 1;
// allow any outstanding ioctl to finish
mdelay(10);
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
tempword =
ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
}
}
if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
// Put message into Slow Queue
// Form Pseudo header
pmsg = (PUSHORT) info->DSPInfoBlk;
ppseudo_hdr = (PPSEUDO_HDR) pmsg;
ppseudo_hdr->length =
htons(info->DSPInfoBlklen + 4);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
// Insert application id
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
for (i = 1; i < 7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
}
info->DSPInfoBlk[8] = 0x7200;
info->DSPInfoBlk[9] =
htons(info->DSPInfoBlklen);
ft1000_send_cmd (dev, (PUSHORT)info->DSPInfoBlk, (USHORT)(info->DSPInfoBlklen+4), 0);
}
info->DrvMsgPend = 0;
break;
case GET_DRV_ERR_RPT_MSG:
DEBUG(1, "FT1000:drivermsg:Got GET_DRV_ERR_RPT_MSG\n");
// copy driver error message to dsp
info->DrvMsgPend = 1;
// allow any outstanding ioctl to finish
mdelay(10);
tempword = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
tempword =
ft1000_read_reg(dev, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
}
}
if ((tempword & FT1000_DB_DPRAM_TX) == 0) {
// Put message into Slow Queue
// Form Pseudo header
pmsg = (PUSHORT) & tempbuffer[0];
ppseudo_hdr = (PPSEUDO_HDR) pmsg;
ppseudo_hdr->length = htons(0x0012);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
// Insert application id
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
for (i=1; i<7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
}
pmsg = (PUSHORT) & tempbuffer[16];
*pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
*pmsg++ = htons(0x000e);
*pmsg++ = htons(info->DSP_TIME[0]);
*pmsg++ = htons(info->DSP_TIME[1]);
*pmsg++ = htons(info->DSP_TIME[2]);
*pmsg++ = htons(info->DSP_TIME[3]);
convert.byte[0] = info->DspVer[0];
convert.byte[1] = info->DspVer[1];
*pmsg++ = convert.wrd;
convert.byte[0] = info->DspVer[2];
convert.byte[1] = info->DspVer[3];
*pmsg++ = convert.wrd;
*pmsg++ = htons(info->DrvErrNum);
ft1000_send_cmd (dev, (PUSHORT)&tempbuffer[0], (USHORT)(0x0012), 0);
info->DrvErrNum = 0;
}
info->DrvMsgPend = 0;
break;
default:
break;
}
}
}
//---------------------------------------------------------------------------
//
// Function: ft1000_parse_dpram_msg
// Descripton: This function will parse the message received from the DSP
// via the DPRAM interface.
// Input:
// dev - device structure
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_parse_dpram_msg(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 doorbell;
u16 portid;
u16 nxtph;
u16 total_len;
int i = 0;
int cnt;
unsigned long flags;
doorbell = ft1000_read_reg(dev, FT1000_REG_DOORBELL);
DEBUG(1, "Doorbell = 0x%x\n", doorbell);
if (doorbell & FT1000_ASIC_RESET_REQ) {
// Copy DSP session record from info block
spin_lock_irqsave(&info->dpram_lock, flags);
if (info->AsicID == ELECTRABUZZ_ID) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC; i++) {
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA,
info->DSPSess.Rec[i]);
}
} else {
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
FT1000_DPRAM_MAG_RX_BASE);
for (i = 0; i < MAX_DSP_SESS_REC / 2; i++) {
outl(info->DSPSess.MagRec[i],
dev->base_addr + FT1000_REG_MAG_DPDATA);
}
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
// clear ASIC RESET request
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_ASIC_RESET_REQ);
DEBUG(1, "Got an ASIC RESET Request\n");
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_ASIC_RESET_DSP);
if (info->AsicID == MAGNEMITE_ID) {
// Setting MAGNEMITE ASIC to big endian mode
ft1000_write_reg(dev, FT1000_REG_SUP_CTRL,
HOST_INTF_BE);
}
info->DspAsicReset = 0;
}
if (doorbell & FT1000_DSP_ASIC_RESET) {
DEBUG(0,
"FT1000:ft1000_parse_dpram_msg: Got a dsp ASIC reset message\n");
info->DspAsicReset = 1;
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_DSP_ASIC_RESET);
udelay(200);
return SUCCESS;
}
if (doorbell & FT1000_DB_DPRAM_RX) {
DEBUG(1,
"FT1000:ft1000_parse_dpram_msg: Got a slow queue message\n");
nxtph = FT1000_DPRAM_RX_BASE + 2;
if (info->AsicID == ELECTRABUZZ_ID) {
total_len =
ft1000_read_dpram(dev, FT1000_DPRAM_RX_BASE);
} else {
total_len =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_TOTAL_LEN,
FT1000_MAG_TOTAL_LEN_INDX));
}
DEBUG(1, "FT1000:ft1000_parse_dpram_msg:total length = %d\n",
total_len);
if ((total_len < MAX_CMD_SQSIZE) && (total_len > PSEUDOSZ)) {
total_len += nxtph;
cnt = 0;
// ft1000_read_reg will return a value that needs to be byteswap
// in order to get DSP_QID_OFFSET.
if (info->AsicID == ELECTRABUZZ_ID) {
portid =
(ft1000_read_dpram
(dev,
DSP_QID_OFFSET + FT1000_DPRAM_RX_BASE +
2) >> 8) & 0xff;
} else {
portid =
(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_PORT_ID,
FT1000_MAG_PORT_ID_INDX) & 0xff);
}
DEBUG(1, "DSP_QID = 0x%x\n", portid);
if (portid == DRIVERID) {
// We are assumming one driver message from the DSP at a time.
ft1000_proc_drvmsg(dev);
}
}
ft1000_write_reg(dev, FT1000_REG_DOORBELL, FT1000_DB_DPRAM_RX);
}
if (doorbell & FT1000_DB_COND_RESET) {
// Reset ASIC and DSP
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
info->DrvErrNum = DSP_CONDRESET_INFO;
DEBUG(1, "ft1000_hw:DSP conditional reset requested\n");
ft1000_reset_card(dev);
ft1000_write_reg(dev, FT1000_REG_DOORBELL,
FT1000_DB_COND_RESET);
}
// let's clear any unexpected doorbells from DSP
doorbell =
doorbell & ~(FT1000_DB_DPRAM_RX | FT1000_ASIC_RESET_REQ |
FT1000_DB_COND_RESET | 0xff00);
if (doorbell) {
DEBUG(1, "Clearing unexpected doorbell = 0x%x\n", doorbell);
ft1000_write_reg(dev, FT1000_REG_DOORBELL, doorbell);
}
return SUCCESS;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_flush_fifo
// Descripton: This function will flush one packet from the downlink
// FIFO.
// Input:
// dev - device structure
// drv_err - driver error causing the flush fifo
// Output:
// None.
//
//---------------------------------------------------------------------------
static void ft1000_flush_fifo(struct net_device *dev, u16 DrvErrNum)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 i;
u32 templong;
u16 tempword;
DEBUG(1, "ft1000:ft1000_hw:ft1000_flush_fifo called\n");
if (info->PktIntfErr > MAX_PH_ERR) {
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev, FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev, FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
info->DrvErrNum = DrvErrNum;
ft1000_reset_card(dev);
return;
} else {
// Flush corrupted pkt from FIFO
i = 0;
do {
if (info->AsicID == ELECTRABUZZ_ID) {
tempword =
ft1000_read_reg(dev, FT1000_REG_DFIFO);
tempword =
ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT);
} else {
templong =
inl(dev->base_addr + FT1000_REG_MAG_DFR);
tempword =
inw(dev->base_addr + FT1000_REG_MAG_DFSR);
}
i++;
// This should never happen unless the ASIC is broken.
// We must reset to recover.
if ((i > 2048) || (tempword == 0)) {
if (info->AsicID == ELECTRABUZZ_ID) {
info->DSP_TIME[0] =
ft1000_read_dpram(dev,
FT1000_DSP_TIMER0);
info->DSP_TIME[1] =
ft1000_read_dpram(dev,
FT1000_DSP_TIMER1);
info->DSP_TIME[2] =
ft1000_read_dpram(dev,
FT1000_DSP_TIMER2);
info->DSP_TIME[3] =
ft1000_read_dpram(dev,
FT1000_DSP_TIMER3);
} else {
info->DSP_TIME[0] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER0,
FT1000_MAG_DSP_TIMER0_INDX);
info->DSP_TIME[1] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER1,
FT1000_MAG_DSP_TIMER1_INDX);
info->DSP_TIME[2] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER2,
FT1000_MAG_DSP_TIMER2_INDX);
info->DSP_TIME[3] =
ft1000_read_dpram_mag_16(dev,
FT1000_MAG_DSP_TIMER3,
FT1000_MAG_DSP_TIMER3_INDX);
}
if (tempword == 0) {
// Let's check if ASIC reads are still ok by reading the Mask register
// which is never zero at this point of the code.
tempword =
inw(dev->base_addr +
FT1000_REG_SUP_IMASK);
if (tempword == 0) {
// This indicates that we can not communicate with the ASIC
info->DrvErrNum =
FIFO_FLUSH_BADCNT;
} else {
// Let's assume that we really flush the FIFO
info->PktIntfErr++;
return;
}
} else {
info->DrvErrNum = FIFO_FLUSH_MAXLIMIT;
}
return;
}
tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
} while ((tempword & 0x03) != 0x03);
if (info->AsicID == ELECTRABUZZ_ID) {
i++;
DEBUG(0, "Flushing FIFO complete = %x\n", tempword);
// Flush last word in FIFO.
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
// Update FIFO counter for DSP
i = i * 2;
DEBUG(0, "Flush Data byte count to dsp = %d\n", i);
info->fifo_cnt += i;
ft1000_write_dpram(dev, FT1000_FIFO_LEN,
info->fifo_cnt);
} else {
DEBUG(0, "Flushing FIFO complete = %x\n", tempword);
// Flush last word in FIFO
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
tempword = inw(dev->base_addr + FT1000_REG_SUP_STAT);
DEBUG(0, "FT1000_REG_SUP_STAT = 0x%x\n", tempword);
tempword = inw(dev->base_addr + FT1000_REG_MAG_DFSR);
DEBUG(0, "FT1000_REG_MAG_DFSR = 0x%x\n", tempword);
}
if (DrvErrNum) {
info->PktIntfErr++;
}
}
}
//---------------------------------------------------------------------------
//
// Function: ft1000_copy_up_pkt
// Descripton: This function will pull Flarion packets out of the Downlink
// FIFO and convert it to an ethernet packet. The ethernet packet will
// then be deliver to the TCP/IP stack.
// Input:
// dev - device structure
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_copy_up_pkt(struct net_device *dev)
{
u16 tempword;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 len;
struct sk_buff *skb;
u16 i;
u8 *pbuffer = NULL;
u8 *ptemp = NULL;
u16 chksum;
u32 *ptemplong;
u32 templong;
DEBUG(1, "ft1000_copy_up_pkt\n");
// Read length
if (info->AsicID == ELECTRABUZZ_ID) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
len = tempword;
} else {
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
len = ntohs(tempword);
}
chksum = tempword;
DEBUG(1, "Number of Bytes in FIFO = %d\n", len);
if (len > ENET_MAX_SIZE) {
DEBUG(0, "size of ethernet packet invalid\n");
if (info->AsicID == MAGNEMITE_ID) {
// Read High word to complete 32 bit access
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
}
ft1000_flush_fifo(dev, DSP_PKTLEN_INFO);
info->stats.rx_errors++;
return FAILURE;
}
skb = dev_alloc_skb(len + 12 + 2);
if (skb == NULL) {
DEBUG(0, "No Network buffers available\n");
// Read High word to complete 32 bit access
if (info->AsicID == MAGNEMITE_ID) {
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
}
ft1000_flush_fifo(dev, 0);
info->stats.rx_errors++;
return FAILURE;
}
pbuffer = (u8 *) skb_put(skb, len + 12);
// Pseudo header
if (info->AsicID == ELECTRABUZZ_ID) {
for (i = 1; i < 7; i++) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
chksum ^= tempword;
}
// read checksum value
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
} else {
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRL);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
chksum ^= tempword;
// read checksum value
tempword = ft1000_read_reg(dev, FT1000_REG_MAG_DFRH);
DEBUG(1, "Pseudo = 0x%x\n", tempword);
}
if (chksum != tempword) {
DEBUG(0, "Packet checksum mismatch 0x%x 0x%x\n", chksum,
tempword);
ft1000_flush_fifo(dev, DSP_PKTPHCKSUM_INFO);
info->stats.rx_errors++;
return FAILURE;
}
//subtract the number of bytes read already
ptemp = pbuffer;
// fake MAC address
*pbuffer++ = dev->dev_addr[0];
*pbuffer++ = dev->dev_addr[1];
*pbuffer++ = dev->dev_addr[2];
*pbuffer++ = dev->dev_addr[3];
*pbuffer++ = dev->dev_addr[4];
*pbuffer++ = dev->dev_addr[5];
*pbuffer++ = 0x00;
*pbuffer++ = 0x07;
*pbuffer++ = 0x35;
*pbuffer++ = 0xff;
*pbuffer++ = 0xff;
*pbuffer++ = 0xfe;
if (info->AsicID == ELECTRABUZZ_ID) {
for (i = 0; i < len / 2; i++) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
*pbuffer++ = (u8) (tempword >> 8);
*pbuffer++ = (u8) tempword;
if (ft1000_chkcard(dev) == FALSE) {
return FAILURE;
}
}
// Need to read one more word if odd byte
if (len & 0x0001) {
tempword = ft1000_read_reg(dev, FT1000_REG_DFIFO);
*pbuffer++ = (u8) (tempword >> 8);
}
} else {
ptemplong = (u32 *) pbuffer;
for (i = 0; i < len / 4; i++) {
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
DEBUG(1, "Data = 0x%8x\n", templong);
*ptemplong++ = templong;
}
// Need to read one more word if odd align.
if (len & 0x0003) {
templong = inl(dev->base_addr + FT1000_REG_MAG_DFR);
DEBUG(1, "Data = 0x%8x\n", templong);
*ptemplong++ = templong;
}
}
DEBUG(1, "Data passed to Protocol layer:\n");
for (i = 0; i < len + 12; i++) {
DEBUG(1, "Protocol Data: 0x%x\n ", *ptemp++);
}
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
info->stats.rx_packets++;
// Add on 12 bytes for MAC address which was removed
info->stats.rx_bytes += (len + 12);
if (info->AsicID == ELECTRABUZZ_ID) {
// track how many bytes have been read from FIFO - round up to 16 bit word
tempword = len + 16;
if (tempword & 0x01)
tempword++;
info->fifo_cnt += tempword;
ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_FIFO_LEN);
ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, info->fifo_cnt);
}
return SUCCESS;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_copy_down_pkt
// Descripton: This function will take an ethernet packet and convert it to
// a Flarion packet prior to sending it to the ASIC Downlink
// FIFO.
// Input:
// dev - device structure
// packet - address of ethernet packet
// len - length of IP packet
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_copy_down_pkt(struct net_device *dev, u16 * packet, u16 len)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
union {
PSEUDO_HDR blk;
u16 buff[sizeof(PSEUDO_HDR) >> 1];
u8 buffc[sizeof(PSEUDO_HDR)];
} pseudo;
int i;
u32 *plong;
DEBUG(1, "ft1000_hw: copy_down_pkt()\n");
// Check if there is room on the FIFO
if (len > ft1000_read_fifo_len(dev)) {
udelay(10);
if (len > ft1000_read_fifo_len(dev)) {
udelay(20);
}
if (len > ft1000_read_fifo_len(dev)) {
udelay(20);
}
if (len > ft1000_read_fifo_len(dev)) {
udelay(20);
}
if (len > ft1000_read_fifo_len(dev)) {
udelay(20);
}
if (len > ft1000_read_fifo_len(dev)) {
udelay(20);
}
if (len > ft1000_read_fifo_len(dev)) {
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:Transmit FIFO is fulli - pkt drop\n");
info->stats.tx_errors++;
return SUCCESS;
}
}
// Create pseudo header and send pseudo/ip to hardware
if (info->AsicID == ELECTRABUZZ_ID) {
pseudo.blk.length = len;
} else {
pseudo.blk.length = ntohs(len);
}
pseudo.blk.source = DSPID; // Need to swap to get in correct order
pseudo.blk.destination = HOSTID;
pseudo.blk.portdest = NETWORKID; // Need to swap to get in correct order
pseudo.blk.portsrc = DSPAIRID;
pseudo.blk.sh_str_id = 0;
pseudo.blk.control = 0;
pseudo.blk.rsvd1 = 0;
pseudo.blk.seq_num = 0;
pseudo.blk.rsvd2 = info->packetseqnum++;
pseudo.blk.qos_class = 0;
/* Calculate pseudo header checksum */
pseudo.blk.checksum = pseudo.buff[0];
for (i = 1; i < 7; i++) {
pseudo.blk.checksum ^= pseudo.buff[i];
}
// Production Mode
if (info->AsicID == ELECTRABUZZ_ID) {
// copy first word to UFIFO_BEG reg
ft1000_write_reg(dev, FT1000_REG_UFIFO_BEG, pseudo.buff[0]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 0 BEG = 0x%04x\n",
pseudo.buff[0]);
// copy subsequent words to UFIFO_MID reg
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[1]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 1 MID = 0x%04x\n",
pseudo.buff[1]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[2]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 2 MID = 0x%04x\n",
pseudo.buff[2]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[3]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 3 MID = 0x%04x\n",
pseudo.buff[3]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[4]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 4 MID = 0x%04x\n",
pseudo.buff[4]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[5]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 5 MID = 0x%04x\n",
pseudo.buff[5]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[6]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 6 MID = 0x%04x\n",
pseudo.buff[6]);
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID, pseudo.buff[7]);
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:data 7 MID = 0x%04x\n",
pseudo.buff[7]);
// Write PPP type + IP Packet into Downlink FIFO
for (i = 0; i < (len >> 1) - 1; i++) {
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
htons(*packet));
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n",
i + 8, htons(*packet));
packet++;
}
// Check for odd byte
if (len & 0x0001) {
ft1000_write_reg(dev, FT1000_REG_UFIFO_MID,
htons(*packet));
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:data MID = 0x%04x\n",
htons(*packet));
packet++;
ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
htons(*packet));
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n",
i + 8, htons(*packet));
} else {
ft1000_write_reg(dev, FT1000_REG_UFIFO_END,
htons(*packet));
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:data %d MID = 0x%04x\n",
i + 8, htons(*packet));
}
} else {
outl(*(u32 *) & pseudo.buff[0],
dev->base_addr + FT1000_REG_MAG_UFDR);
DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n",
*(u32 *) & pseudo.buff[0]);
outl(*(u32 *) & pseudo.buff[2],
dev->base_addr + FT1000_REG_MAG_UFDR);
DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n",
*(u32 *) & pseudo.buff[2]);
outl(*(u32 *) & pseudo.buff[4],
dev->base_addr + FT1000_REG_MAG_UFDR);
DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n",
*(u32 *) & pseudo.buff[4]);
outl(*(u32 *) & pseudo.buff[6],
dev->base_addr + FT1000_REG_MAG_UFDR);
DEBUG(1, "ft1000_copy_down_pkt: Pseudo = 0x%8x\n",
*(u32 *) & pseudo.buff[6]);
plong = (u32 *) packet;
// Write PPP type + IP Packet into Downlink FIFO
for (i = 0; i < (len >> 2); i++) {
outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
}
// Check for odd alignment
if (len & 0x0003) {
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:data = 0x%8x\n",
*plong);
outl(*plong++, dev->base_addr + FT1000_REG_MAG_UFDR);
}
outl(1, dev->base_addr + FT1000_REG_MAG_UFER);
}
info->stats.tx_packets++;
// Add 14 bytes for MAC adddress plus ethernet type
info->stats.tx_bytes += (len + 14);
return SUCCESS;
}
static struct net_device_stats *ft1000_stats(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
return (&info->stats);
}
static int ft1000_open(struct net_device *dev)
{
DEBUG(0, "ft1000_hw: ft1000_open is called\n");
ft1000_reset_card(dev);
DEBUG(0, "ft1000_hw: ft1000_open is ended\n");
/* schedule ft1000_hbchk to perform periodic heartbeat checks on DSP and ASIC */
init_timer(&poll_timer);
poll_timer.expires = jiffies + (2 * HZ);
poll_timer.data = (u_long) dev;
add_timer(&poll_timer);
DEBUG(0, "ft1000_hw: ft1000_open is ended2\n");
return 0;
}
static int ft1000_close(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
DEBUG(0, "ft1000_hw: ft1000_close()\n");
info->CardReady = 0;
del_timer(&poll_timer);
if (ft1000_card_present == 1) {
DEBUG(0, "Media is down\n");
netif_stop_queue(dev);
ft1000_disable_interrupts(dev);
ft1000_write_reg(dev, FT1000_REG_RESET, DSP_RESET_BIT);
//reset ASIC
ft1000_reset_asic(dev);
}
return 0;
}
static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u8 *pdata;
DEBUG(1, "ft1000_hw: ft1000_start_xmit()\n");
if (skb == NULL) {
DEBUG(1, "ft1000_hw: ft1000_start_xmit:skb == NULL!!!\n");
return 0;
}
DEBUG(1, "ft1000_hw: ft1000_start_xmit:length of packet = %d\n",
skb->len);
pdata = (u8 *) skb->data;
if (info->mediastate == 0) {
/* Drop packet is mediastate is down */
DEBUG(1, "ft1000_hw:ft1000_copy_down_pkt:mediastate is down\n");
return SUCCESS;
}
if ((skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE)) {
/* Drop packet which has invalid size */
DEBUG(1,
"ft1000_hw:ft1000_copy_down_pkt:invalid ethernet length\n");
return SUCCESS;
}
ft1000_copy_down_pkt(dev, (u16 *) (pdata + ENET_HEADER_SIZE - 2),
skb->len - ENET_HEADER_SIZE + 2);
dev_kfree_skb(skb);
return 0;
}
static irqreturn_t ft1000_interrupt(int irq, void *dev_id)
{
struct net_device *dev = (struct net_device *)dev_id;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
u16 tempword;
u16 inttype;
int cnt;
DEBUG(1, "ft1000_hw: ft1000_interrupt()\n");
if (info->CardReady == 0) {
ft1000_disable_interrupts(dev);
return IRQ_HANDLED;
}
if (ft1000_chkcard(dev) == FALSE) {
ft1000_disable_interrupts(dev);
return IRQ_HANDLED;
}
ft1000_disable_interrupts(dev);
// Read interrupt type
inttype = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
// Make sure we process all interrupt before leaving the ISR due to the edge trigger interrupt type
while (inttype) {
if (inttype & ISR_DOORBELL_PEND) {
ft1000_parse_dpram_msg(dev);
}
if (inttype & ISR_RCV) {
DEBUG(1, "Data in FIFO\n");
cnt = 0;
do {
// Check if we have packets in the Downlink FIFO
if (info->AsicID == ELECTRABUZZ_ID) {
tempword =
ft1000_read_reg(dev, FT1000_REG_DFIFO_STAT);
} else {
tempword =
ft1000_read_reg(dev, FT1000_REG_MAG_DFSR);
}
if (tempword & 0x1f) {
ft1000_copy_up_pkt(dev);
} else {
break;
}
cnt++;
} while (cnt < MAX_RCV_LOOP);
}
// clear interrupts
tempword = ft1000_read_reg(dev, FT1000_REG_SUP_ISR);
DEBUG(1, "ft1000_hw: interrupt status register = 0x%x\n", tempword);
ft1000_write_reg(dev, FT1000_REG_SUP_ISR, tempword);
// Read interrupt type
inttype = ft1000_read_reg (dev, FT1000_REG_SUP_ISR);
DEBUG(1,"ft1000_hw: interrupt status register after clear = 0x%x\n",inttype);
}
ft1000_enable_interrupts(dev);
return IRQ_HANDLED;
}
void stop_ft1000_card(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv(dev);
PPROV_RECORD ptr;
// int cnt;
DEBUG(0, "ft1000_hw: stop_ft1000_card()\n");
info->CardReady = 0;
ft1000_card_present = 0;
netif_stop_queue(dev);
ft1000_disable_interrupts(dev);
// Make sure we free any memory reserve for provisioning
while (list_empty(&info->prov_list) == 0) {
ptr = list_entry(info->prov_list.next, PROV_RECORD, list);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
if (info->registered) {
unregister_netdev(dev);
info->registered = 0;
}
free_irq(dev->irq, dev);
release_region(dev->base_addr,256);
release_firmware(fw_entry);
flarion_ft1000_cnt--;
ft1000CleanupProc(dev);
}
static void ft1000_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
FT1000_INFO *ft_info;
ft_info = (FT1000_INFO *) netdev_priv(dev);
snprintf(info->driver, 32, "ft1000");
snprintf(info->bus_info, ETHTOOL_BUSINFO_LEN, "PCMCIA 0x%lx",
dev->base_addr);
snprintf(info->fw_version, 32, "%d.%d.%d.%d", ft_info->DspVer[0],
ft_info->DspVer[1], ft_info->DspVer[2], ft_info->DspVer[3]);
}
static u32 ft1000_get_link(struct net_device *dev)
{
FT1000_INFO *info;
info = (FT1000_INFO *) netdev_priv(dev);
return info->mediastate;
}
static const struct ethtool_ops ops = {
.get_drvinfo = ft1000_get_drvinfo,
.get_link = ft1000_get_link
};
struct net_device *init_ft1000_card(unsigned short irq, int port,
unsigned char *mac_addr, void *ft1000_reset,
void *link, struct device *fdev)
{
FT1000_INFO *info;
struct net_device *dev;
int i;
static const struct net_device_ops ft1000ops = // Slavius 21.10.2009 due to kernel changes
{
.ndo_open = &ft1000_open,
.ndo_stop = &ft1000_close,
.ndo_start_xmit = &ft1000_start_xmit,
.ndo_get_stats = &ft1000_stats,
};
DEBUG(1, "ft1000_hw: init_ft1000_card()\n");
DEBUG(1, "ft1000_hw: irq = %d\n", irq);
DEBUG(1, "ft1000_hw: port = 0x%04x\n", port);
flarion_ft1000_cnt++;
if (flarion_ft1000_cnt > 1) {
flarion_ft1000_cnt--;
printk(KERN_INFO
"ft1000: This driver can not support more than one instance\n");
return NULL;
}
dev = alloc_etherdev(sizeof(FT1000_INFO));
if (!dev) {
printk(KERN_ERR "ft1000: failed to allocate etherdev\n");
return NULL;
}
SET_NETDEV_DEV(dev, fdev);
info = (FT1000_INFO *) netdev_priv(dev);
memset(info, 0, sizeof(FT1000_INFO));
DEBUG(1, "address of dev = 0x%8x\n", (u32) dev);
DEBUG(1, "address of dev info = 0x%8x\n", (u32) info);
DEBUG(0, "device name = %s\n", dev->name);
memset(&info->stats, 0, sizeof(struct net_device_stats));
spin_lock_init(&info->dpram_lock);
info->DrvErrNum = 0;
info->ASICResetNum = 0;
info->registered = 1;
info->link = link;
info->ft1000_reset = ft1000_reset;
info->mediastate = 0;
info->fifo_cnt = 0;
info->DeviceCreated = FALSE;
info->DeviceMajor = 0;
info->CurrentInterruptEnableMask = ISR_DEFAULT_MASK;
info->InterruptsEnabled = FALSE;
info->CardReady = 0;
info->DSP_TIME[0] = 0;
info->DSP_TIME[1] = 0;
info->DSP_TIME[2] = 0;
info->DSP_TIME[3] = 0;
flarion_ft1000_cnt = 0;
INIT_LIST_HEAD(&info->prov_list);
info->squeseqnum = 0;
// dev->hard_start_xmit = &ft1000_start_xmit;
// dev->get_stats = &ft1000_stats;
// dev->open = &ft1000_open;
// dev->stop = &ft1000_close;
dev->netdev_ops = &ft1000ops; // Slavius 21.10.2009 due to kernel changes
DEBUG(0, "device name = %s\n", dev->name);
for (i = 0; i < 6; i++) {
dev->dev_addr[i] = mac_addr[i];
DEBUG(1, "ft1000_hw: mac_addr %d = 0x%02x\n", i, mac_addr[i]);
}
netif_stop_queue(dev);
dev->irq = irq;
dev->base_addr = port;
if (request_irq(dev->irq, ft1000_interrupt, IRQF_SHARED, dev->name, dev)) {
printk(KERN_ERR "ft1000: Could not request_irq\n");
kfree(dev);
return (NULL);
}
request_region(dev->base_addr, 256, dev->name);
if (register_netdev(dev) != 0) {
DEBUG(0, "ft1000: Could not register netdev");
return NULL;
}
info->AsicID = ft1000_read_reg(dev, FT1000_REG_ASIC_ID);
if (info->AsicID == ELECTRABUZZ_ID) {
DEBUG(0, "ft1000_hw: ELECTRABUZZ ASIC\n");
if (request_firmware(&fw_entry, "ft1000.img", fdev) != 0) {
printk(KERN_INFO "ft1000: Could not open ft1000.img\n");
unregister_netdev(dev);
free_irq(dev->irq, dev);
kfree(dev);
return NULL;
}
} else {
DEBUG(0, "ft1000_hw: MAGNEMITE ASIC\n");
if (request_firmware(&fw_entry, "ft2000.img", fdev) != 0) {
printk(KERN_INFO "ft1000: Could not open ft2000.img\n");
unregister_netdev(dev);
free_irq(dev->irq, dev);
kfree(dev);
return NULL;
}
}
ft1000_enable_interrupts(dev);
ft1000InitProc(dev);
ft1000_card_present = 1;
SET_ETHTOOL_OPS(dev, &ops);
printk(KERN_INFO
"ft1000: %s: addr 0x%04lx irq %d, MAC addr %02x:%02x:%02x:%02x:%02x:%02x\n",
dev->name, dev->base_addr, dev->irq, dev->dev_addr[0],
dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3],
dev->dev_addr[4], dev->dev_addr[5]);
return dev;
}
EXPORT_SYMBOL(init_ft1000_card);
EXPORT_SYMBOL(stop_ft1000_card);
EXPORT_SYMBOL(flarion_ft1000_cnt);
/*---------------------------------------------------------------------------
FT1000 driver for Flarion Flash OFDM NIC Device
Copyright (C) 2006 Patrik Ostrihon, All rights reserved.
Copyright (C) 2006 ProWeb Consulting, a.s, 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.
-----------------------------------------------------------------------------*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include "ft1000.h"
#define FT1000_PROC "ft1000"
#define MAX_FILE_LEN 255
#define PUTM_TO_PAGE(len,page,args...) \
len += snprintf(page+len, PAGE_SIZE - len, args)
#define PUTX_TO_PAGE(len,page,message,size,var) \
len += snprintf(page+len, PAGE_SIZE - len, message); \
for(i = 0; i < (size - 1); i++) \
{ \
len += snprintf(page+len, PAGE_SIZE - len, "%02x:", var[i]); \
} \
len += snprintf(page+len, PAGE_SIZE - len, "%02x\n", var[i])
#define PUTD_TO_PAGE(len,page,message,size,var) \
len += snprintf(page+len, PAGE_SIZE - len, message); \
for(i = 0; i < (size - 1); i++) \
{ \
len += snprintf(page+len, PAGE_SIZE - len, "%d.", var[i]); \
} \
len += snprintf(page+len, PAGE_SIZE - len, "%d\n", var[i])
int ft1000ReadProc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
struct net_device *dev;
int len;
int i;
FT1000_INFO *info;
char *status[] =
{ "Idle (Disconnect)", "Searching", "Active (Connected)",
"Waiting for L2", "Sleep", "No Coverage", "", ""
};
char *signal[] = { "", "*", "**", "***", "****" };
int strength;
int quality;
struct timeval tv;
time_t delta;
dev = (struct net_device *)data;
info = (FT1000_INFO *) netdev_priv(dev);
if (off > 0) {
*eof = 1;
return 0;
}
/* Wrap-around */
if (info->AsicID == ELECTRABUZZ_ID) {
if (info->DspHibernateFlag == 0) {
if (info->ProgConStat != 0xFF) {
info->LedStat =
ft1000_read_dpram(dev, FT1000_DSP_LED);
info->ConStat =
ft1000_read_dpram(dev,
FT1000_DSP_CON_STATE);
} else {
info->ConStat = 0xf;
}
}
} else {
if (info->ProgConStat != 0xFF) {
info->LedStat =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_DSP_LED,
FT1000_MAG_DSP_LED_INDX));
info->ConStat =
ntohs(ft1000_read_dpram_mag_16
(dev, FT1000_MAG_DSP_CON_STATE,
FT1000_MAG_DSP_CON_STATE_INDX));
} else {
info->ConStat = 0xf;
}
}
i = (info->LedStat) & 0xf;
switch (i) {
case 0x1:
strength = 1;
break;
case 0x3:
strength = 2;
break;
case 0x7:
strength = 3;
break;
case 0xf:
strength = 4;
break;
default:
strength = 0;
}
i = (info->LedStat >> 8) & 0xf;
switch (i) {
case 0x1:
quality = 1;
break;
case 0x3:
quality = 2;
break;
case 0x7:
quality = 3;
break;
case 0xf:
quality = 4;
break;
default:
quality = 0;
}
do_gettimeofday(&tv);
delta = (tv.tv_sec - info->ConTm);
len = 0;
PUTM_TO_PAGE(len, page, "Connection Time: %02ld:%02ld:%02ld\n",
((delta / 3600) % 24), ((delta / 60) % 60), (delta % 60));
PUTM_TO_PAGE(len, page, "Connection Time[s]: %ld\n", delta);
PUTM_TO_PAGE(len, page, "Asic ID: %s\n",
(info->AsicID) ==
ELECTRABUZZ_ID ? "ELECTRABUZZ ASIC" : "MAGNEMITE ASIC");
PUTX_TO_PAGE(len, page, "SKU: ", SKUSZ, info->Sku);
PUTX_TO_PAGE(len, page, "EUI64: ", EUISZ, info->eui64);
PUTD_TO_PAGE(len, page, "DSP version number: ", DSPVERSZ, info->DspVer);
PUTX_TO_PAGE(len, page, "Hardware Serial Number: ", HWSERNUMSZ,
info->HwSerNum);
PUTX_TO_PAGE(len, page, "Caliberation Version: ", CALVERSZ,
info->RfCalVer);
PUTD_TO_PAGE(len, page, "Caliberation Date: ", CALDATESZ,
info->RfCalDate);
PUTM_TO_PAGE(len, page, "Media State: %s\n",
(info->mediastate) ? "link" : "no link");
PUTM_TO_PAGE(len, page, "Connection Status: %s\n",
status[((info->ConStat) & 0x7)]);
PUTM_TO_PAGE(len, page, "RX packets: %ld\n", info->stats.rx_packets);
PUTM_TO_PAGE(len, page, "TX packets: %ld\n", info->stats.tx_packets);
PUTM_TO_PAGE(len, page, "RX bytes: %ld\n", info->stats.rx_bytes);
PUTM_TO_PAGE(len, page, "TX bytes: %ld\n", info->stats.tx_bytes);
PUTM_TO_PAGE(len, page, "Signal Strength: %s\n", signal[strength]);
PUTM_TO_PAGE(len, page, "Signal Quality: %s\n", signal[quality]);
return len;
}
static int ft1000NotifyProc(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = ptr;
FT1000_INFO *info;
info = (FT1000_INFO *) netdev_priv(dev);
switch (event) {
case NETDEV_CHANGENAME:
remove_proc_entry(info->netdevname, info->proc_ft1000);
create_proc_read_entry(dev->name, 0644, info->proc_ft1000,
ft1000ReadProc, dev);
snprintf(info->netdevname, IFNAMSIZ, "%s", dev->name);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block ft1000_netdev_notifier = {
.notifier_call = ft1000NotifyProc
};
void ft1000InitProc(struct net_device *dev)
{
FT1000_INFO *info;
info = (FT1000_INFO *) netdev_priv(dev);
info->proc_ft1000 = proc_mkdir(FT1000_PROC, init_net.proc_net);
create_proc_read_entry(dev->name, 0644, info->proc_ft1000,
ft1000ReadProc, dev);
snprintf(info->netdevname, IFNAMSIZ, "%s", dev->name);
register_netdevice_notifier(&ft1000_netdev_notifier);
}
void ft1000CleanupProc(struct net_device *dev)
{
FT1000_INFO *info;
info = (FT1000_INFO *) netdev_priv(dev);
remove_proc_entry(dev->name, info->proc_ft1000);
remove_proc_entry(FT1000_PROC, init_net.proc_net);
unregister_netdevice_notifier(&ft1000_netdev_notifier);
}
EXPORT_SYMBOL(ft1000InitProc);
EXPORT_SYMBOL(ft1000CleanupProc);
obj-$(CONFIG_FT1000_USB) += ft1000.o
ft1000-objs := ft1000_chdev.o ft1000_download.o ft1000_hw.o ft1000_proc.o ft1000_usb.o
//---------------------------------------------------------------------------
// FT1000 driver for Flarion Flash OFDM NIC Device
//
// Copyright (C) 2006 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.
//---------------------------------------------------------------------------
//
// File: ft1000_chdev.c
//
// Description: Custom character device dispatch routines.
//
// History:
// 8/29/02 Whc Ported to Linux.
// 6/05/06 Whc Porting to Linux 2.6.9
//
//---------------------------------------------------------------------------
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/poll.h>
#include <linux/netdevice.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/kmod.h>
#include <linux/ioctl.h>
#include <linux/unistd.h>
#include "ft1000_usb.h"
//#include "ft1000_ioctl.h"
void ft1000_DestroyDevice(struct net_device *dev);
u16 ft1000_read_dpram16(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, u8 highlow);
u16 ft1000_read_register(struct ft1000_device *ft1000dev, short* Data, u16 nRegIndx);
extern inline u16 ft1000_asic_read (struct net_device *dev, u16 offset);
extern inline void ft1000_asic_write (struct net_device *dev, u16 offset, u16 value);
extern void CardSendCommand(struct ft1000_device *ft1000dev, unsigned short *ptempbuffer, int size);
static int ft1000_ChOpen (struct inode *Inode, struct file *File);
static unsigned int ft1000_ChPoll(struct file *file, poll_table *wait);
static int ft1000_ChIoctl (struct inode *Inode, struct file *File, unsigned int Command,
unsigned long Argument);
static int ft1000_ChRelease (struct inode *Inode, struct file *File);
static int ft1000_flarion_cnt = 0;
//need to looking usage of ft1000Handle
// Global pointer to device object
static struct ft1000_device *pdevobj[MAX_NUM_CARDS + 2];
//static devfs_handle_t ft1000Handle[MAX_NUM_CARDS];
// List to free receive command buffer pool
struct list_head freercvpool;
// lock to arbitrate free buffer list for receive command data
spinlock_t free_buff_lock;
int numofmsgbuf = 0;
// Global variable to indicate that all provisioning data is sent to DSP
//BOOLEAN fProvComplete;
//
// Table of entry-point routines for char device
//
static struct file_operations ft1000fops =
{
ioctl: ft1000_ChIoctl,
poll: ft1000_ChPoll,
open: ft1000_ChOpen,
release: ft1000_ChRelease
};
//---------------------------------------------------------------------------
// Function: exec_mknod
//
// Parameters:
//
// Returns:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
static int exec_mknod (void *pdata)
{
PFT1000_INFO info;
char mjnum[4];
char minornum[4];
char temp[32];
int retcode;
// int i; //aelias [-] reason : unused variable
char *envp[] = { "HOME=/", "PATH=/usr/bin:/bin", NULL };
char *argv[]={"-m 666",temp,"c",mjnum,minornum,NULL};
info = pdata;
DEBUG("ft1000_chdev:exec_mknod is called with major number = %d\n", info->DeviceMajor);
sprintf(temp, "%s%s", "/dev/", info->DeviceName) ;
sprintf(mjnum, "%d", info->DeviceMajor);
sprintf(minornum, "%d", info->CardNumber);
//char *argv[]={"mknod","-m 666",temp,"c",mjnum,minornum,NULL};
// char *argv[]={"-m 666",temp,"c",mjnum,minornum,NULL};
//for (i=0; i<7;i++)
// DEBUG("argv[%d]=%s\n", i, argv[i]);
retcode = call_usermodehelper ("/bin/mknod", argv, envp, 1);
if (retcode) {
DEBUG("ft1000_chdev:exec_mknod failed to make the node: retcode = %d\n", retcode);
}
return retcode;
}
//---------------------------------------------------------------------------
// Function: rm_mknod
//
// Description: This module removes the FT1000 device file
//
//---------------------------------------------------------------------------
static int rm_mknod (void *pdata)
{
PFT1000_INFO info;
//char *argv[4]={"rm", "-f", "/dev/FT1000", NULL};
int retcode;
char temp[32];
char *argv[]={"rm", "-f", temp, NULL};
info = (PFT1000_INFO)pdata;
DEBUG("ft1000_chdev:rm_mknod is called for device %s\n", info->DeviceName);
sprintf(temp, "%s%s", "/dev/", info->DeviceName) ;
// char *argv[]={"rm", "-f", temp, NULL};
retcode = call_usermodehelper ("/bin/rm", argv, NULL, 1);
if (retcode) {
DEBUG("ft1000_chdev:rm_mknod failed to remove the node: retcode = %d\n", retcode);
}
else
DEBUG("ft1000_chdev:rm_mknod done!\n");
return retcode;
}
//---------------------------------------------------------------------------
// Function: ft1000_get_buffer
//
// Parameters:
//
// Returns:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
PDPRAM_BLK ft1000_get_buffer (struct list_head *bufflist)
{
unsigned long flags;
PDPRAM_BLK ptr;
spin_lock_irqsave(&free_buff_lock, flags);
// Check if buffer is available
if ( list_empty(bufflist) ) {
DEBUG("ft1000_get_buffer: No more buffer - %d\n", numofmsgbuf);
ptr = NULL;
}
else {
numofmsgbuf--;
ptr = list_entry(bufflist->next, DPRAM_BLK, list);
list_del(&ptr->list);
//DEBUG("ft1000_get_buffer: number of free msg buffers = %d\n", numofmsgbuf);
}
spin_unlock_irqrestore(&free_buff_lock, flags);
return ptr;
}
//---------------------------------------------------------------------------
// Function: ft1000_free_buffer
//
// Parameters:
//
// Returns:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
void ft1000_free_buffer (PDPRAM_BLK pdpram_blk, struct list_head *plist)
{
unsigned long flags;
spin_lock_irqsave(&free_buff_lock, flags);
// Put memory back to list
list_add_tail(&pdpram_blk->list, plist);
numofmsgbuf++;
//DEBUG("ft1000_free_buffer: number of free msg buffers = %d\n", numofmsgbuf);
spin_unlock_irqrestore(&free_buff_lock, flags);
}
//---------------------------------------------------------------------------
// Function: ft1000_CreateDevice
//
// Parameters: dev - pointer to adapter object
//
// Returns: 0 if successful
//
// Description: Creates a private char device.
//
// Notes: Only called by init_module().
//
//---------------------------------------------------------------------------
int ft1000_CreateDevice(struct ft1000_device *dev)
{
PFT1000_INFO info = netdev_priv(dev->net);
int result;
int i;
pid_t pid;
// make a new device name
sprintf(info->DeviceName, "%s%d", "FT100", info->CardNumber);
// Delete any existing FT1000 node
pid = kernel_thread (rm_mknod,(void *)info, 0);
msleep(1000);
DEBUG("ft1000_CreateDevice: number of instance = %d\n", ft1000_flarion_cnt);
DEBUG("DeviceCreated = %x\n", info->DeviceCreated);
//save the device info to global array
pdevobj[info->CardNumber] = dev;
DEBUG("ft1000_CreateDevice: ******SAVED pdevobj[%d]=%x\n", info->CardNumber, (unsigned int)pdevobj[info->CardNumber]); //aelias [+] reason:up
if (info->DeviceCreated)
{
DEBUG("ft1000_CreateDevice: \"%s\" already registered\n", info->DeviceName);
return -EIO;
}
// register the device
DEBUG("ft1000_CreateDevice: \"%s\" device registration\n", info->DeviceName);
info->DeviceMajor = 0;
result = register_chrdev(info->DeviceMajor, info->DeviceName, &ft1000fops);
if (result < 0)
{
DEBUG("ft1000_CreateDevice: unable to get major %d\n", info->DeviceMajor);
return result;
}
DEBUG("ft1000_CreateDevice: registered char device \"%s\"\n", info->DeviceName);
// save a dynamic device major number
if (info->DeviceMajor == 0)
{
info->DeviceMajor = result;
DEBUG("ft1000_PcdCreateDevice: device major = %d\n", info->DeviceMajor);
}
// Create a thread to call user mode app to mknod
pid = kernel_thread (exec_mknod, (void *)info, 0);
// initialize application information
info->appcnt = 0;
// if (ft1000_flarion_cnt == 0) {
//
// DEBUG("Initialize free_buff_lock and freercvpool\n");
// spin_lock_init(&free_buff_lock);
//
// // initialize a list of buffers to be use for queuing up receive command data
// INIT_LIST_HEAD (&freercvpool);
//
// // create list of free buffers
// for (i=0; i<NUM_OF_FREE_BUFFERS; i++) {
// // Get memory for DPRAM_DATA link list
// pdpram_blk = kmalloc ( sizeof(DPRAM_BLK), GFP_KERNEL );
// // Get a block of memory to store command data
// pdpram_blk->pbuffer = kmalloc ( MAX_CMD_SQSIZE, GFP_KERNEL );
// // link provisioning data
// list_add_tail (&pdpram_blk->list, &freercvpool);
// }
// numofmsgbuf = NUM_OF_FREE_BUFFERS;
// }
// initialize application information
info->appcnt = 0;
for (i=0; i<MAX_NUM_APP; i++) {
info->app_info[i].nTxMsg = 0;
info->app_info[i].nRxMsg = 0;
info->app_info[i].nTxMsgReject = 0;
info->app_info[i].nRxMsgMiss = 0;
info->app_info[i].fileobject = 0;
info->app_info[i].app_id = i+1;
info->app_info[i].DspBCMsgFlag = 0;
info->app_info[i].NumOfMsg = 0;
init_waitqueue_head(&info->app_info[i].wait_dpram_msg);
INIT_LIST_HEAD (&info->app_info[i].app_sqlist);
}
// ft1000Handle[info->CardNumber] = devfs_register(NULL, info->DeviceName, DEVFS_FL_AUTO_DEVNUM, 0, 0,
// S_IFCHR | S_IRUGO | S_IWUGO, &ft1000fops, NULL);
info->DeviceCreated = TRUE;
ft1000_flarion_cnt++;
return result;
}
//---------------------------------------------------------------------------
// Function: ft1000_DestroyDeviceDEBUG
//
// Parameters: dev - pointer to adapter object
//
// Description: Destroys a private char device.
//
// Notes: Only called by cleanup_module().
//
//---------------------------------------------------------------------------
void ft1000_DestroyDevice(struct net_device *dev)
{
PFT1000_INFO info = netdev_priv(dev);
int result = 0;
pid_t pid;
int i;
PDPRAM_BLK pdpram_blk;
DPRAM_BLK *ptr;
DEBUG("ft1000_chdev:ft1000_DestroyDevice called\n");
if (info->DeviceCreated)
{
ft1000_flarion_cnt--;
unregister_chrdev(info->DeviceMajor, info->DeviceName);
DEBUG("ft1000_DestroyDevice: unregistered device \"%s\", result = %d\n",
info->DeviceName, result);
pid = kernel_thread (rm_mknod, (void *)info, 0);
// Make sure we free any memory reserve for slow Queue
for (i=0; i<MAX_NUM_APP; i++) {
while (list_empty(&info->app_info[i].app_sqlist) == 0) {
pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, DPRAM_BLK, list);
list_del(&pdpram_blk->list);
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
wake_up_interruptible(&info->app_info[i].wait_dpram_msg);
}
// Remove buffer allocated for receive command data
if (ft1000_flarion_cnt == 0) {
while (list_empty(&freercvpool) == 0) {
ptr = list_entry(freercvpool.next, DPRAM_BLK, list);
list_del(&ptr->list);
kfree(ptr->pbuffer);
kfree(ptr);
}
}
// devfs_unregister(ft1000Handle[info->CardNumber]);
info->DeviceCreated = FALSE;
pdevobj[info->CardNumber] = NULL;
}
}
//---------------------------------------------------------------------------
// Function: ft1000_ChOpen
//
// Parameters:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_ChOpen (struct inode *Inode, struct file *File)
{
PFT1000_INFO info;
int i,num;
DEBUG("ft1000_ChOpen called\n");
num = (MINOR(Inode->i_rdev) & 0xf);
DEBUG("ft1000_ChOpen: minor number=%d\n", num);
for (i=0; i<5; i++)
DEBUG("pdevobj[%d]=%x\n", i, (unsigned int)pdevobj[i]); //aelias [+] reason: down
if ( pdevobj[num] != NULL )
//info = (PFT1000_INFO)(pdevobj[num]->net->priv);
info = (FT1000_INFO *) netdev_priv (pdevobj[num]->net);
else
{
DEBUG("ft1000_ChOpen: can not find device object %d\n", num);
return -1;
}
DEBUG("f_owner = 0x%8x number of application = %d\n", (u32)(&File->f_owner), info->appcnt );
// Check if maximum number of application exceeded
if (info->appcnt > MAX_NUM_APP) {
DEBUG("Maximum number of application exceeded\n");
return -EACCES;
}
// Search for available application info block
for (i=0; i<MAX_NUM_APP; i++) {
if ( (info->app_info[i].fileobject == 0) ) {
break;
}
}
// Fail due to lack of application info block
if (i == MAX_NUM_APP) {
DEBUG("Could not find an application info block\n");
return -EACCES;
}
info->appcnt++;
info->app_info[i].fileobject = (u32)(&File->f_owner);
info->app_info[i].nTxMsg = 0;
info->app_info[i].nRxMsg = 0;
info->app_info[i].nTxMsgReject = 0;
info->app_info[i].nRxMsgMiss = 0;
File->private_data = pdevobj[num]->net;
return 0;
}
//---------------------------------------------------------------------------
// Function: ft1000_ChPoll
//
// Parameters:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
static unsigned int ft1000_ChPoll(struct file *file, poll_table *wait)
{
struct net_device *dev = file->private_data;
PFT1000_INFO info;
int i;
//DEBUG("ft1000_ChPoll called\n");
if (ft1000_flarion_cnt == 0) {
DEBUG("FT1000:ft1000_ChPoll called when ft1000_flarion_cnt is zero\n");
return (-EBADF);
}
info = (FT1000_INFO *) netdev_priv (dev);
// Search for matching file object
for (i=0; i<MAX_NUM_APP; i++) {
if ( info->app_info[i].fileobject == (u32)(&file->f_owner) ) {
//DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id);
break;
}
}
// Could not find application info block
if (i == MAX_NUM_APP) {
DEBUG("FT1000:ft1000_ChIoctl:Could not find application info block\n");
return ( -EACCES );
}
if (list_empty(&info->app_info[i].app_sqlist) == 0) {
DEBUG("FT1000:ft1000_ChPoll:Message detected in slow queue\n");
return(POLLIN | POLLRDNORM | POLLPRI);
}
poll_wait (file, &info->app_info[i].wait_dpram_msg, wait);
//DEBUG("FT1000:ft1000_ChPoll:Polling for data from DSP\n");
return (0);
}
//---------------------------------------------------------------------------
// Function: ft1000_ChIoctl
//
// Parameters:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_ChIoctl (struct inode *Inode, struct file *File, unsigned int Command,
unsigned long Argument)
{
struct net_device *dev;
PFT1000_INFO info;
struct ft1000_device *ft1000dev;
int result=0;
int cmd;
int i;
u16 tempword;
unsigned long flags;
struct timeval tv;
IOCTL_GET_VER get_ver_data;
IOCTL_GET_DSP_STAT get_stat_data;
u8 ConnectionMsg[] = {0x00,0x44,0x10,0x20,0x80,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x93,0x64,
0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0a,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x37,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x01,0x00,0x01,0x7f,0x00,
0x00,0x01,0x00,0x00};
unsigned short ledStat=0;
unsigned short conStat=0;
//DEBUG("ft1000_ChIoctl called\n");
if (ft1000_flarion_cnt == 0) {
DEBUG("FT1000:ft1000_ChIoctl called when ft1000_flarion_cnt is zero\n");
return (-EBADF);
}
//DEBUG("FT1000:ft1000_ChIoctl:Command = 0x%x Argument = 0x%8x\n", Command, (u32)Argument);
dev = File->private_data;
info = (FT1000_INFO *) netdev_priv (dev);
ft1000dev = info->pFt1000Dev;
cmd = _IOC_NR(Command);
//DEBUG("FT1000:ft1000_ChIoctl:cmd = 0x%x\n", cmd);
// process the command
switch (cmd) {
case IOCTL_REGISTER_CMD:
DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_REGISTER called\n");
result = get_user(tempword, (unsigned short *)Argument);
if (result) {
DEBUG("result = %d failed to get_user\n", result);
break;
}
if (tempword == DSPBCMSGID) {
// Search for matching file object
for (i=0; i<MAX_NUM_APP; i++) {
if ( info->app_info[i].fileobject == (u32)(&File->f_owner) ) {
info->app_info[i].DspBCMsgFlag = 1;
DEBUG("FT1000:ft1000_ChIoctl:Registered for broadcast messages\n");
break;
}
}
}
break;
case IOCTL_GET_VER_CMD:
DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_VER called\n");
get_ver_data.drv_ver = FT1000_DRV_VER;
if (copy_to_user((PIOCTL_GET_VER)Argument, &get_ver_data, sizeof(get_ver_data)) ) {
DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n");
result = -EFAULT;
break;
}
DEBUG("FT1000:ft1000_ChIoctl:driver version = 0x%x\n",(unsigned int)get_ver_data.drv_ver);
break;
case IOCTL_CONNECT:
// Connect Message
DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_CONNECT\n");
ConnectionMsg[79] = 0xfc;
CardSendCommand(ft1000dev, (unsigned short *)ConnectionMsg, 0x4c);
break;
case IOCTL_DISCONNECT:
// Disconnect Message
DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_DISCONNECT\n");
ConnectionMsg[79] = 0xfd;
CardSendCommand(ft1000dev, (unsigned short *)ConnectionMsg, 0x4c);
break;
case IOCTL_GET_DSP_STAT_CMD:
//DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DSP_STAT called\n");
memcpy(get_stat_data.DspVer, info->DspVer, DSPVERSZ);
memcpy(get_stat_data.HwSerNum, info->HwSerNum, HWSERNUMSZ);
memcpy(get_stat_data.Sku, info->Sku, SKUSZ);
memcpy(get_stat_data.eui64, info->eui64, EUISZ);
if (info->ProgConStat != 0xFF) {
ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_LED, (PUCHAR)&ledStat, FT1000_MAG_DSP_LED_INDX);
get_stat_data.LedStat = ntohs(ledStat);
DEBUG("FT1000:ft1000_ChIoctl: LedStat = 0x%x\n", get_stat_data.LedStat);
ft1000_read_dpram16(ft1000dev, FT1000_MAG_DSP_CON_STATE, (PUCHAR)&conStat, FT1000_MAG_DSP_CON_STATE_INDX);
get_stat_data.ConStat = ntohs(conStat);
DEBUG("FT1000:ft1000_ChIoctl: ConStat = 0x%x\n", get_stat_data.ConStat);
}
else {
get_stat_data.ConStat = 0x0f;
}
get_stat_data.nTxPkts = info->stats.tx_packets;
get_stat_data.nRxPkts = info->stats.rx_packets;
get_stat_data.nTxBytes = info->stats.tx_bytes;
get_stat_data.nRxBytes = info->stats.rx_bytes;
do_gettimeofday ( &tv );
get_stat_data.ConTm = (u32)(tv.tv_sec - info->ConTm);
DEBUG("Connection Time = %d\n", (int)get_stat_data.ConTm);
if (copy_to_user((PIOCTL_GET_DSP_STAT)Argument, &get_stat_data, sizeof(get_stat_data)) ) {
DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n");
result = -EFAULT;
break;
}
DEBUG("ft1000_chioctl: GET_DSP_STAT succeed\n");
break;
case IOCTL_SET_DPRAM_CMD:
{
IOCTL_DPRAM_BLK dpram_data;
//IOCTL_DPRAM_COMMAND dpram_command;
USHORT qtype;
USHORT msgsz;
PPSEUDO_HDR ppseudo_hdr;
PUSHORT pmsg;
USHORT total_len;
USHORT app_index;
u16 status;
//DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_SET_DPRAM called\n");
if (ft1000_flarion_cnt == 0) {
return (-EBADF);
}
if (info->DrvMsgPend) {
return (-ENOTTY);
}
if ( (info->DspAsicReset) || (info->fProvComplete == 0) ) {
return (-EACCES);
}
info->fAppMsgPend = 1;
if (info->CardReady) {
//DEBUG("FT1000:ft1000_ChIoctl: try to SET_DPRAM \n");
// Get the length field to see how many bytes to copy
result = get_user(msgsz, (unsigned short *)Argument);
msgsz = ntohs (msgsz);
//DEBUG("FT1000:ft1000_ChIoctl: length of message = %d\n", msgsz);
if (msgsz > MAX_CMD_SQSIZE) {
DEBUG("FT1000:ft1000_ChIoctl: bad message length = %d\n", msgsz);
result = -EINVAL;
break;
}
//if ( copy_from_user(&(dpram_command.dpram_blk), (PIOCTL_DPRAM_BLK)Argument, msgsz+2) ) {
if ( copy_from_user(&dpram_data, (PIOCTL_DPRAM_BLK)Argument, msgsz+2) ) {
DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n");
result = -EFAULT;
}
else {
#if 0
// whc - for debugging only
ptr = (char *)&dpram_data;
for (i=0; i<msgsz; i++) {
DEBUG(1,"FT1000:ft1000_ChIoctl: data %d = 0x%x\n", i, *ptr++);
}
#endif
// Check if this message came from a registered application
for (i=0; i<MAX_NUM_APP; i++) {
if ( info->app_info[i].fileobject == (u32)(&File->f_owner) ) {
break;
}
}
if (i==MAX_NUM_APP) {
DEBUG("FT1000:No matching application fileobject\n");
result = -EINVAL;
break;
}
app_index = i;
// Check message qtype type which is the lower byte within qos_class
//qtype = ntohs(dpram_command.dpram_blk.pseudohdr.qos_class) & 0xff;
qtype = ntohs(dpram_data.pseudohdr.qos_class) & 0xff;
//DEBUG("FT1000_ft1000_ChIoctl: qtype = %d\n", qtype);
if (qtype) {
}
else {
// Put message into Slow Queue
// Only put a message into the DPRAM if msg doorbell is available
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
//DEBUG("FT1000_ft1000_ChIoctl: READ REGISTER tempword=%x\n", tempword);
if (tempword & FT1000_DB_DPRAM_TX) {
// Suspend for 2ms and try again due to DSP doorbell busy
mdelay(2);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
// Suspend for 1ms and try again due to DSP doorbell busy
mdelay(1);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
// Suspend for 3ms and try again due to DSP doorbell busy
mdelay(3);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
DEBUG("FT1000:ft1000_ChIoctl:Doorbell not available\n");
result = -ENOTTY;
break;
}
}
}
}
}
//DEBUG("FT1000_ft1000_ChIoctl: finished reading register\n");
// Make sure we are within the limits of the slow queue memory limitation
if ( (msgsz < MAX_CMD_SQSIZE) && (msgsz > PSEUDOSZ) ) {
// Need to put sequence number plus new checksum for message
//pmsg = (PUSHORT)&dpram_command.dpram_blk.pseudohdr;
pmsg = (PUSHORT)&dpram_data.pseudohdr;
ppseudo_hdr = (PPSEUDO_HDR)pmsg;
total_len = msgsz+2;
if (total_len & 0x1) {
total_len++;
}
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = info->app_info[app_index].app_id;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
//DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum);
for (i=1; i<7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
//DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum);
}
pmsg++;
ppseudo_hdr = (PPSEUDO_HDR)pmsg;
#if 0
ptr = (char *)&dpram_data;
DEBUG("FT1000:ft1000_ChIoctl: Command Send\n");
for (i=0; i<total_len; i++) {
DEBUG("FT1000:ft1000_ChIoctl: data %d = 0x%x\n", i, *ptr++);
}
#endif
//dpram_command.extra = 0;
//CardSendCommand(ft1000dev,(unsigned char*)&dpram_command,total_len+2);
CardSendCommand(ft1000dev,(unsigned short*)&dpram_data,total_len+2);
info->app_info[app_index].nTxMsg++;
break;
}
else {
result = -EINVAL;
break;
}
}
}
}
else {
DEBUG("FT1000:ft1000_ChIoctl: Card not ready take messages\n");
result = -EACCES;
}
}
break;
case IOCTL_GET_DPRAM_CMD:
{
PDPRAM_BLK pdpram_blk;
PIOCTL_DPRAM_BLK pioctl_dpram;
int msglen;
//DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DPRAM called\n");
if (ft1000_flarion_cnt == 0) {
return (-EBADF);
}
// Search for matching file object
for (i=0; i<MAX_NUM_APP; i++) {
if ( info->app_info[i].fileobject == (u32)(&File->f_owner) ) {
//DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id);
break;
}
}
// Could not find application info block
if (i == MAX_NUM_APP) {
DEBUG("FT1000:ft1000_ChIoctl:Could not find application info block\n");
result = -EBADF;
break;
}
result = 0;
pioctl_dpram = (PIOCTL_DPRAM_BLK)Argument;
if (list_empty(&info->app_info[i].app_sqlist) == 0) {
//DEBUG("FT1000:ft1000_ChIoctl:Message detected in slow queue\n");
spin_lock_irqsave(&free_buff_lock, flags);
pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, DPRAM_BLK, list);
list_del(&pdpram_blk->list);
info->app_info[i].NumOfMsg--;
//DEBUG("FT1000:ft1000_ChIoctl:NumOfMsg for app %d = %d\n", i, info->app_info[i].NumOfMsg);
spin_unlock_irqrestore(&free_buff_lock, flags);
msglen = ntohs(*(u16 *)pdpram_blk->pbuffer) + PSEUDOSZ;
pioctl_dpram->total_len = htons(msglen);
//DEBUG("FT1000:ft1000_ChIoctl:msg length = %x\n", msglen);
if(copy_to_user (&pioctl_dpram->pseudohdr, pdpram_blk->pbuffer, msglen))
{
DEBUG("FT1000:ft1000_ChIoctl: copy fault occurred\n");
result = -EFAULT;
break;
}
ft1000_free_buffer(pdpram_blk, &freercvpool);
result = msglen;
}
//DEBUG("FT1000:ft1000_ChIoctl: IOCTL_FT1000_GET_DPRAM no message\n");
}
break;
default:
DEBUG("FT1000:ft1000_ChIoctl:unknown command: 0x%x\n", Command);
result = -ENOTTY;
break;
}
info->fAppMsgPend = 0;
return result;
}
//---------------------------------------------------------------------------
// Function: ft1000_ChRelease
//
// Parameters:
//
// Description:
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_ChRelease (struct inode *Inode, struct file *File)
{
PFT1000_INFO info;
struct net_device *dev;
int i;
PDPRAM_BLK pdpram_blk;
DEBUG("ft1000_ChRelease called\n");
dev = File->private_data;
info = (FT1000_INFO *) netdev_priv (dev);
if (ft1000_flarion_cnt == 0) {
info->appcnt--;
return (-EBADF);
}
// Search for matching file object
for (i=0; i<MAX_NUM_APP; i++) {
if ( info->app_info[i].fileobject == (u32)(&File->f_owner) ) {
//DEBUG("FT1000:ft1000_ChIoctl: Message is for AppId = %d\n", info->app_info[i].app_id);
break;
}
}
if (i==MAX_NUM_APP)
return 0;
while (list_empty(&info->app_info[i].app_sqlist) == 0) {
DEBUG("Remove and free memory queue up on slow queue\n");
pdpram_blk = list_entry(info->app_info[i].app_sqlist.next, DPRAM_BLK, list);
list_del(&pdpram_blk->list);
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
// initialize application information
info->appcnt--;
DEBUG("ft1000_chdev:%s:appcnt = %d\n", __FUNCTION__, info->appcnt);
info->app_info[i].fileobject = 0;
return 0;
}
//=====================================================
// 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 FIFO_DNLD 1
#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
#pragma pack (push, pack_save, 1)
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];
} 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.
} 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.
} 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;
} DSP_IMAGE_INFO_V6, *PDSP_IMAGE_INFO_V6;
u16 ft1000_read_register(struct ft1000_device *ft1000dev, short* Data, u16 nRegIndx);
u16 ft1000_write_register(struct ft1000_device *ft1000dev, USHORT value, u16 nRegIndx);
u16 ft1000_read_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, USHORT cnt);
u16 ft1000_write_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, USHORT cnt);
u16 ft1000_read_dpram16(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, u8 highlow);
u16 ft1000_write_dpram16(struct ft1000_device *ft1000dev, USHORT indx, USHORT value, u8 highlow);
u16 fix_ft1000_read_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer);
u16 fix_ft1000_write_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer);
//---------------------------------------------------------------------------
// Function: getfw
//
// Parameters: char *fn - input DSP image file name
// int *pimgsz - output DSP image file size
// Returns: DSP image buffer
//
// Description: Read the DSP image file into a char buffer
//
// Notes:
//
//---------------------------------------------------------------------------
char *getfw (char *fn, int *pimgsz)
{
struct file *fd;
mm_segment_t fs = get_fs();
loff_t pos;
char *pfwimg;
int fwimgsz;
set_fs(get_ds());
fd = filp_open(fn, 0, 0);
if ( IS_ERR(fd) )
{
DEBUG("FT1000:%s:can not open dsp image\n", __FUNCTION__);
set_fs(fs);
return NULL;
}
fwimgsz = i_size_read(fd->f_dentry->d_inode);
*pimgsz = fwimgsz;
if (fwimgsz <= 0)
{
DEBUG("FT1000:%s:invalid file size\n", __FUNCTION__);
filp_close(fd, current->files);
set_fs(fs);
return NULL;
}
pfwimg = (char*)vmalloc ( fwimgsz );
if (pfwimg == NULL) {
DEBUG("FT1000:%s:can not allocate memory for dsp image\n", __FUNCTION__);
filp_close(fd, current->files);
set_fs(fs);
return NULL;
}
pos = 0;
if (vfs_read(fd, pfwimg, fwimgsz, &pos) != fwimgsz) {
vfree(pfwimg);
DEBUG("FT1000:%s:failed to read firmware image\n",__FUNCTION__);
filp_close(fd, current->files);
set_fs(fs);
return NULL;
}
filp_close(fd, current->files);
set_fs(fs);
return pfwimg;
}
//---------------------------------------------------------------------------
// Function: check_usb_db
//
// Parameters: struct ft1000_device - device structure
//
// Returns: 0 - success
//
// Description: This function checks if the doorbell register is cleared
//
// Notes:
//
//---------------------------------------------------------------------------
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);
#if FIFO_DNLD
status = ft1000_write_register (ft1000dev, 0x0100, FT1000_REG_DOORBELL);
#endif
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:
//
//---------------------------------------------------------------------------
USHORT get_handshake(struct ft1000_device *ft1000dev, USHORT expected_value)
{
USHORT handshake;
int loopcnt;
ULONG status=0;
PFT1000_INFO pft1000info = netdev_priv(ft1000dev->net);
loopcnt = 0;
while (loopcnt < 100)
{
#if FIFO_DNLD
// 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);
#else
// Need to clear downloader doorbell if Hartley ASIC
status = ft1000_read_register (ft1000dev, &temp, FT1000_REG_DOORBELL);
//DEBUG("FT1000:get_handshake:doorbell = 0x%x\n", temp);
if (temp)
{
if (temp & FT1000_DB_DNLD_RX)
{
//DEBUG("get_handshake: write FT1000_DB_DNLD_RX to doorbell register\n");
status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_RX, FT1000_REG_DOORBELL);
}
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_read_register (ft1000dev, &temp, FT1000_REG_DOORBELL);
//DEBUG("FT1000:get_handshake:doorbell = 0x%x\n", temp);
if (temp)
{
if (temp & FT1000_DB_DNLD_RX)
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);
} //end of if temp
#endif
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:
//
//---------------------------------------------------------------------------
void put_handshake(struct ft1000_device *ft1000dev,USHORT handshake_value)
{
ULONG tempx;
USHORT tempword;
int i;
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);
#if FIFO_DNLD
for (i=0; i<1000; i++);
#else
for (i=0; i<10; i++)
{
status = ft1000_read_register (ft1000dev, &tempword, FT1000_REG_DOORBELL);
if ((tempword & FT1000_DB_DNLD_TX) == 0)
break;
}
if (i==10)
{
DEBUG("FT1000:put_handshake could not clear Tx doorbell\n");
status = ft1000_read_register (ft1000dev, &tempword, FT1000_REG_DOORBELL);
DEBUG("FT1000:put_handshake:doorbell = 0x%x\n",tempword);
}
#endif
}
USHORT get_handshake_usb(struct ft1000_device *ft1000dev, USHORT expected_value)
{
USHORT handshake;
int loopcnt;
USHORT temp;
ULONG status=0;
PFT1000_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;
}
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:
//
//---------------------------------------------------------------------------
USHORT get_request_type(struct ft1000_device *ft1000dev)
{
USHORT request_type;
ULONG status;
USHORT tempword;
ULONG tempx;
PFT1000_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 FIFO_DNLD
tempx = 0;
#else
status = ft1000_read_dpram16 (ft1000dev, DWNLD_MAG1_TYPE_LOC, (PUCHAR)&tempword, 0);
tempx = tempword;
#endif
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;
}
USHORT get_request_type_usb(struct ft1000_device *ft1000dev)
{
USHORT request_type;
ULONG status;
USHORT tempword;
ULONG tempx;
PFT1000_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:
//
//---------------------------------------------------------------------------
long get_request_value(struct ft1000_device *ft1000dev)
{
ULONG value;
USHORT tempword;
ULONG status;
PFT1000_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;
}
long get_request_value_usb(struct ft1000_device *ft1000dev)
{
ULONG value;
USHORT tempword;
ULONG status;
PFT1000_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 FIFO_DNLD
if (pft1000info->usbboot == 1)
pft1000info->usbboot = 2;
#endif
//DEBUG("get_request_value_usb: value is %x\n", value);
return value;
}
//---------------------------------------------------------------------------
// 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:
//
//---------------------------------------------------------------------------
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: PPSEUDO_HDR pHdr - Pseudo header pointer
//
// Returns: checksum - success
//
// Description: This function returns the checksum of the pseudo header
//
// Notes:
//
//---------------------------------------------------------------------------
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;
}
//---------------------------------------------------------------------------
// 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:
//
//---------------------------------------------------------------------------
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];
PFT1000_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:
//
//---------------------------------------------------------------------------
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;
PPSEUDO_HDR pHdr;
USHORT usHdrLength;
//PPROV_RECORD pProvRecord;
PDSP_FILE_HDR pFileHdr;
long word_length;
USHORT request;
USHORT temp;
USHORT tempword;
PDSP_FILE_HDR_5 pFileHdr5;
PDSP_IMAGE_INFO_V6 pDspImageInfoV6 = NULL;
long requested_version;
BOOLEAN bGoodVersion;
PDRVMSG 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;
PPROV_RECORD pprov_record;
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;
pFileHdr = (PDSP_FILE_HDR)pFileStart;
pFileHdr5 = (PDSP_FILE_HDR_5)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:
#if FIFO_DNLD
pft1000info->usbboot = 3;
#endif
/* 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;
#if FIFO_DNLD
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);
}
#else
write_blk (ft1000dev, &pUsFile, &pUcFile, word_length);
//ft1000_write_dpram32 (ft1000dev, dpram, (PUCHAR)pUcFile, word_length);
#endif
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 = (PDRVMSG)&(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 = (PDSP_IMAGE_INFO_V6)(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 == (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 = (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, &pft1000info->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 = 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;
}
//=====================================================
// CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
//
//
// This file is part of Express Card USB Driver
//
// $Id:
//====================================================
// 20090926; aelias; removed compiler warnings & errors; 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 "ft1000_usb.h"
#include <linux/types.h>
//#include <asm/semaphore.h> //aelias [-] reason : file moved
//#include <linux/semaphore.h> //aelias [+] reason : file moved
//#include <asm/io.h>
//#include <linux/kthread.h>
#define HARLEY_READ_REGISTER 0x0
#define HARLEY_WRITE_REGISTER 0x01
#define HARLEY_READ_DPRAM_32 0x02
#define HARLEY_READ_DPRAM_LOW 0x03
#define HARLEY_READ_DPRAM_HIGH 0x04
#define HARLEY_WRITE_DPRAM_32 0x05
#define HARLEY_WRITE_DPRAM_LOW 0x06
#define HARLEY_WRITE_DPRAM_HIGH 0x07
#define HARLEY_READ_OPERATION 0xc1
#define HARLEY_WRITE_OPERATION 0x41
//#define JDEBUG
extern void *pFileStart;
extern ULONG FileLength;
extern int numofmsgbuf;
int ft1000_poll_thread(void *arg);
static void ft1000_hbchk(u_long data);
int ft1000_reset(struct net_device *ft1000dev);
static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev);
static int ft1000_open (struct net_device *dev);
int ft1000_close (struct net_device *dev);
static struct net_device_stats *ft1000_netdev_stats(struct net_device *dev);
u16 scram_dnldr(struct ft1000_device *ft1000dev, void *pFileStart, ULONG FileLength);
int ft1000_submit_rx_urb(PFT1000_INFO info);
static struct timer_list poll_timer[MAX_NUM_CARDS];
static int ft1000_chkcard (struct ft1000_device *dev);
/*
static const struct net_device_ops ft1000net_ops = {
.ndo_start_xmit = ft1000_start_xmit,
.ndo_get_stats = ft1000_netdev_stats,
.ndo_open = ft1000_open,
.ndo_stop = ft1000_close,
};
*/
//Jim
static u8 tempbuffer[1600];
int gCardIndex;
#define MAX_RCV_LOOP 100
extern struct list_head freercvpool;
extern spinlock_t free_buff_lock; // lock to arbitrate free buffer list for receive command data
//end of Jim
extern int ft1000_CreateDevice(struct ft1000_device *dev);
extern PDPRAM_BLK ft1000_get_buffer (struct list_head *bufflist);
extern void ft1000_free_buffer (PDPRAM_BLK pdpram_blk, struct list_head *plist);
static int atoi(const char *s)
{
int k = 0;
int cnt;
k = 0;
cnt = 0;
while (*s != '\0' && *s >= '0' && *s <= '9') {
k = 10 * k + (*s - '0');
s++;
// Let's put a limit on this while loop to avoid deadlock scenario
if (cnt > 100)
break;
cnt++;
}
return k;
}
/****************************************************************
* ft1000_control_complete
****************************************************************/
static void ft1000_control_complete(struct urb *urb)
{
struct ft1000_device *ft1000dev = (struct ft1000_device *)urb->context;
//DEBUG("FT1000_CONTROL_COMPLETE ENTERED\n");
if (ft1000dev == NULL )
{
DEBUG("NULL ft1000dev, failure\n");
return ;
}
else if ( ft1000dev->dev == NULL )
{
DEBUG("NULL ft1000dev->dev, failure\n");
return ;
}
//spin_lock(&ft1000dev->device_lock);
if(waitqueue_active(&ft1000dev->control_wait))
{
wake_up(&ft1000dev->control_wait);
}
//DEBUG("FT1000_CONTROL_COMPLETE RETURNED\n");
//spin_unlock(&ft1000dev->device_lock);
}
//---------------------------------------------------------------------------
// Function: ft1000_control
//
// Parameters: ft1000_device - device structure
// pipe - usb control message pipe
// request - control request
// requesttype - control message request type
// value - value to be written or 0
// index - register index
// data - data buffer to hold the read/write values
// size - data size
// timeout - control message time out value
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function sends a control message via USB interface synchronously
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_control(struct ft1000_device *ft1000dev,unsigned int pipe,
u8 request,
u8 requesttype,
u16 value,
u16 index,
void *data,
u16 size,
int timeout)
{
u16 ret;
if (ft1000dev == NULL )
{
DEBUG("NULL ft1000dev, failure\n");
return STATUS_FAILURE;
}
else if ( ft1000dev->dev == NULL )
{
DEBUG("NULL ft1000dev->dev, failure\n");
return STATUS_FAILURE;
}
ret = usb_control_msg(ft1000dev->dev,
pipe,
request,
requesttype,
value,
index,
data,
size,
LARGE_TIMEOUT);
if (ret>0)
ret = STATUS_SUCCESS;
else
ret = STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_read_register
//
// Parameters: ft1000_device - device structure
// Data - data buffer to hold the value read
// nRegIndex - register index
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function returns the value in a register
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_read_register(struct ft1000_device *ft1000dev, short* Data, u16 nRegIndx)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_read_register: reg index is %d\n", nRegIndx);
//DEBUG("ft1000_read_register: spin_lock locked\n");
ret = ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev,0),
HARLEY_READ_REGISTER, //request --READ_REGISTER
HARLEY_READ_OPERATION, //requestType
0, //value
nRegIndx, //index
Data, //data
2, //data size
LARGE_TIMEOUT ); //timeout
//DEBUG("ft1000_read_register: ret is %d \n", ret);
//DEBUG("ft1000_read_register: data is %x \n", *Data);
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_write_register
//
// Parameters: ft1000_device - device structure
// value - value to write into a register
// nRegIndex - register index
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function writes the value in a register
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_write_register(struct ft1000_device *ft1000dev, USHORT value, u16 nRegIndx)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_write_register: value is: %d, reg index is: %d\n", value, nRegIndx);
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
HARLEY_WRITE_REGISTER, //request -- WRITE_REGISTER
HARLEY_WRITE_OPERATION, //requestType
value,
nRegIndx,
NULL,
0,
LARGE_TIMEOUT );
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_read_dpram32
//
// Parameters: ft1000_device - device structure
// indx - starting address to read
// buffer - data buffer to hold the data read
// cnt - number of byte read from DPRAM
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function read a number of bytes from DPRAM
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_read_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, USHORT cnt)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_read_dpram32: indx: %d cnt: %d\n", indx, cnt);
ret =ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev,0),
HARLEY_READ_DPRAM_32, //request --READ_DPRAM_32
HARLEY_READ_OPERATION, //requestType
0, //value
indx, //index
buffer, //data
cnt, //data size
LARGE_TIMEOUT ); //timeout
//DEBUG("ft1000_read_dpram32: ret is %d \n", ret);
//DEBUG("ft1000_read_dpram32: ret=%d \n", ret);
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_write_dpram32
//
// Parameters: ft1000_device - device structure
// indx - starting address to write the data
// buffer - data buffer to write into DPRAM
// cnt - number of bytes to write
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function writes into DPRAM a number of bytes
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_write_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, USHORT cnt)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_write_dpram32: indx: %d buffer: %x cnt: %d\n", indx, buffer, cnt);
if ( cnt % 4)
cnt += cnt - (cnt % 4);
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
HARLEY_WRITE_DPRAM_32, //request -- WRITE_DPRAM_32
HARLEY_WRITE_OPERATION, //requestType
0, //value
indx, //index
buffer, //buffer
cnt, //buffer size
LARGE_TIMEOUT );
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_read_dpram16
//
// Parameters: ft1000_device - device structure
// indx - starting address to read
// buffer - data buffer to hold the data read
// hightlow - high or low 16 bit word
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function read 16 bits from DPRAM
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_read_dpram16(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, u8 highlow)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_read_dpram16: indx: %d hightlow: %d\n", indx, highlow);
u8 request;
if (highlow == 0 )
request = HARLEY_READ_DPRAM_LOW;
else
request = HARLEY_READ_DPRAM_HIGH;
ret = ft1000_control(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev,0),
request, //request --READ_DPRAM_H/L
HARLEY_READ_OPERATION, //requestType
0, //value
indx, //index
buffer, //data
2, //data size
LARGE_TIMEOUT ); //timeout
//DEBUG("ft1000_read_dpram16: ret is %d \n", ret);
//DEBUG("ft1000_read_dpram16: data is %x \n", *buffer);
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_write_dpram16
//
// Parameters: ft1000_device - device structure
// indx - starting address to write the data
// value - 16bits value to write
// hightlow - high or low 16 bit word
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function writes into DPRAM a number of bytes
//
// Notes:
//
//---------------------------------------------------------------------------
u16 ft1000_write_dpram16(struct ft1000_device *ft1000dev, USHORT indx, USHORT value, u8 highlow)
{
u16 ret = STATUS_SUCCESS;
//DEBUG("ft1000_write_dpram16: indx: %d value: %d highlow: %d\n", indx, value, highlow);
u8 request;
if ( highlow == 0 )
request = HARLEY_WRITE_DPRAM_LOW;
else
request = HARLEY_WRITE_DPRAM_HIGH;
ret = ft1000_control(ft1000dev,
usb_sndctrlpipe(ft1000dev->dev, 0),
request, //request -- WRITE_DPRAM_H/L
HARLEY_WRITE_OPERATION, //requestType
value, //value
indx, //index
NULL, //buffer
0, //buffer size
LARGE_TIMEOUT );
if ( ret != STATUS_SUCCESS )
return STATUS_FAILURE;
return ret;
}
//---------------------------------------------------------------------------
// Function: fix_ft1000_read_dpram32
//
// Parameters: ft1000_device - device structure
// indx - starting address to read
// buffer - data buffer to hold the data read
//
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function read DPRAM 4 words at a time
//
// Notes:
//
//---------------------------------------------------------------------------
u16 fix_ft1000_read_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer)
{
UCHAR tempbuffer[16];
USHORT pos;
u16 ret = STATUS_SUCCESS;
//DEBUG("fix_ft1000_read_dpram32: indx: %d \n", indx);
pos = (indx / 4)*4;
ret = ft1000_read_dpram32(ft1000dev, pos, (PUCHAR)&tempbuffer[0], 16);
if (ret == STATUS_SUCCESS)
{
pos = (indx % 4)*4;
*buffer++ = tempbuffer[pos++];
*buffer++ = tempbuffer[pos++];
*buffer++ = tempbuffer[pos++];
*buffer++ = tempbuffer[pos++];
}
else
{
DEBUG("fix_ft1000_read_dpram32: DPRAM32 Read failed\n");
*buffer++ = 0;
*buffer++ = 0;
*buffer++ = 0;
*buffer++ = 0;
}
//DEBUG("fix_ft1000_read_dpram32: data is %x \n", *buffer);
return ret;
}
//---------------------------------------------------------------------------
// Function: fix_ft1000_write_dpram32
//
// Parameters: ft1000_device - device structure
// indx - starting address to write
// buffer - data buffer to write
//
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function write to DPRAM 4 words at a time
//
// Notes:
//
//---------------------------------------------------------------------------
u16 fix_ft1000_write_dpram32(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer)
{
USHORT pos1;
USHORT pos2;
USHORT i;
UCHAR tempbuffer[32];
UCHAR resultbuffer[32];
PUCHAR pdata;
u16 ret = STATUS_SUCCESS;
//DEBUG("fix_ft1000_write_dpram32: Entered:\n");
pos1 = (indx / 4)*4;
pdata = buffer;
ret = ft1000_read_dpram32(ft1000dev, pos1, (PUCHAR)&tempbuffer[0], 16);
if (ret == STATUS_SUCCESS)
{
pos2 = (indx % 4)*4;
tempbuffer[pos2++] = *buffer++;
tempbuffer[pos2++] = *buffer++;
tempbuffer[pos2++] = *buffer++;
tempbuffer[pos2++] = *buffer++;
ret = ft1000_write_dpram32(ft1000dev, pos1, (PUCHAR)&tempbuffer[0], 16);
}
else
{
DEBUG("fix_ft1000_write_dpram32: DPRAM32 Read failed\n");
return ret;
}
ret = ft1000_read_dpram32(ft1000dev, pos1, (PUCHAR)&resultbuffer[0], 16);
if (ret == STATUS_SUCCESS)
{
buffer = pdata;
for (i=0; i<16; i++)
{
if (tempbuffer[i] != resultbuffer[i]){
ret = STATUS_FAILURE;
}
}
}
if (ret == STATUS_FAILURE)
{
ret = ft1000_write_dpram32(ft1000dev, pos1, (PUCHAR)&tempbuffer[0], 16);
ret = ft1000_read_dpram32(ft1000dev, pos1, (PUCHAR)&resultbuffer[0], 16);
if (ret == STATUS_SUCCESS)
{
buffer = pdata;
for (i=0; i<16; i++)
{
if (tempbuffer[i] != resultbuffer[i])
{
ret = STATUS_FAILURE;
DEBUG("fix_ft1000_write_dpram32 Failed to write\n");
}
}
}
}
return ret;
}
//------------------------------------------------------------------------
//
// Function: card_reset_dsp
//
// Synopsis: This function is called to reset or activate the DSP
//
// Arguments: value - reset or activate
//
// Returns: None
//-----------------------------------------------------------------------
void card_reset_dsp (struct ft1000_device *ft1000dev, BOOLEAN value)
{
u16 status = STATUS_SUCCESS;
USHORT tempword;
status = ft1000_write_register (ft1000dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_SUP_CTRL);
if (value)
{
DEBUG("Reset DSP\n");
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
tempword |= DSP_RESET_BIT;
status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
}
else
{
DEBUG("Activate DSP\n");
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
#if 1
tempword |= DSP_ENCRYPTED;
tempword &= ~DSP_UNENCRYPTED;
#else
tempword |= DSP_UNENCRYPTED;
tempword &= ~DSP_ENCRYPTED;
#endif
status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
tempword &= ~EFUSE_MEM_DISABLE;
tempword &= ~DSP_RESET_BIT;
status = ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
status = ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
}
}
//---------------------------------------------------------------------------
// Function: CardSendCommand
//
// Parameters: ft1000_device - device structure
// ptempbuffer - command buffer
// size - command buffer size
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function sends a command to ASIC
//
// Notes:
//
//---------------------------------------------------------------------------
void CardSendCommand(struct ft1000_device *ft1000dev, unsigned char *ptempbuffer, int size)
{
unsigned short temp;
unsigned char *commandbuf;
DEBUG("CardSendCommand: enter CardSendCommand... size=%d\n", size);
commandbuf =(unsigned char*) kmalloc(size+2, GFP_KERNEL);
//memset((void*)commandbuf, 0, size+2);
memcpy((void*)commandbuf+2, (void*)ptempbuffer, size);
//DEBUG("CardSendCommand: Command Send\n");
/***
for (i=0; i<size+2; i++)
{
DEBUG("FT1000:ft1000_ChIoctl: data %d = 0x%x\n", i, *ptr++);
}
***/
ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
if (temp & 0x0100)
{
msleep(10);
}
// check for odd word
size = size + 2;
if (size % 4)
{
// Must force to be 32 bit aligned
size += 4 - (size % 4);
}
//DEBUG("CardSendCommand: write dpram ... size=%d\n", size);
ft1000_write_dpram32(ft1000dev, 0,commandbuf, size);
msleep(1);
//DEBUG("CardSendCommand: write into doorbell ...\n");
ft1000_write_register(ft1000dev, FT1000_DB_DPRAM_TX ,FT1000_REG_DOORBELL) ;
msleep(1);
ft1000_read_register(ft1000dev, &temp, FT1000_REG_DOORBELL);
//DEBUG("CardSendCommand: read doorbell ...temp=%x\n", temp);
if ( (temp & 0x0100) == 0)
{
//DEBUG("CardSendCommand: Message sent\n");
}
}
//--------------------------------------------------------------------------
//
// Function: dsp_reload
//
// Synopsis: This function is called to load or reload the DSP
//
// Arguments: ft1000dev - device structure
//
// Returns: None
//-----------------------------------------------------------------------
void dsp_reload (struct ft1000_device *ft1000dev)
{
u16 status;
USHORT tempword;
ULONG templong;
PFT1000_INFO pft1000info;
pft1000info = netdev_priv(ft1000dev->net);
pft1000info->CardReady = 0;
pft1000info->DSP_loading= 1;
// Program Interrupt Mask register
status = ft1000_write_register (ft1000dev, 0xffff, FT1000_REG_SUP_IMASK);
status = ft1000_read_register (ft1000dev, &tempword, FT1000_REG_RESET);
tempword |= ASIC_RESET_BIT;
status = ft1000_write_register (ft1000dev, tempword, FT1000_REG_RESET);
msleep(1000);
status = ft1000_read_register (ft1000dev, &tempword, FT1000_REG_RESET);
DEBUG("Reset Register = 0x%x\n", tempword);
// Toggle DSP reset
card_reset_dsp (ft1000dev, 1);
msleep(1000);
card_reset_dsp (ft1000dev, 0);
msleep(1000);
status = ft1000_write_register (ft1000dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
// Let's check for FEFE
status = ft1000_read_dpram32 (ft1000dev, FT1000_MAG_DPRAM_FEFE_INDX, (PUCHAR)&templong, 4);
DEBUG("templong (fefe) = 0x%8x\n", templong);
// call codeloader
status = scram_dnldr(ft1000dev, pFileStart, FileLength);
if ( status != STATUS_SUCCESS)
return;
msleep(1000);
pft1000info->DSP_loading= 0;
DEBUG("dsp_reload returned\n");
}
//---------------------------------------------------------------------------
//
// Function: ft1000_reset_asic
// Descripton: This function will call the Card Service function to reset the
// ASIC.
// Input:
// dev - device structure
// Output:
// none
//
//---------------------------------------------------------------------------
static void ft1000_reset_asic (struct net_device *dev)
{
FT1000_INFO *info = netdev_priv(dev);
struct ft1000_device *ft1000dev = info->pFt1000Dev;
u16 tempword;
DEBUG("ft1000_hw:ft1000_reset_asic called\n");
info->ASICResetNum++;
// Let's use the register provided by the Magnemite ASIC to reset the
// ASIC and DSP.
ft1000_write_register(ft1000dev, (DSP_RESET_BIT | ASIC_RESET_BIT), FT1000_REG_RESET );
mdelay(1);
// set watermark to -1 in order to not generate an interrrupt
ft1000_write_register(ft1000dev, 0xffff, FT1000_REG_MAG_WATERMARK);
// clear interrupts
ft1000_read_register (ft1000dev, &tempword, FT1000_REG_SUP_ISR);
DEBUG("ft1000_hw: interrupt status register = 0x%x\n",tempword);
ft1000_write_register (ft1000dev, tempword, FT1000_REG_SUP_ISR);
ft1000_read_register (ft1000dev, &tempword, FT1000_REG_SUP_ISR);
DEBUG("ft1000_hw: interrupt status register = 0x%x\n",tempword);
}
/*
//---------------------------------------------------------------------------
//
// Function: ft1000_disable_interrupts
// Descripton: This function will disable all interrupts.
// Input:
// dev - device structure
// Output:
// None.
//
//---------------------------------------------------------------------------
static void ft1000_disable_interrupts(struct net_device *dev) {
FT1000_INFO *info = netdev_priv(dev);
struct ft1000_device *ft1000dev = info->pFt1000Dev;
u16 tempword;
DEBUG("ft1000_hw: ft1000_disable_interrupts()\n");
ft1000_write_register (ft1000dev, ISR_MASK_ALL, FT1000_REG_SUP_IMASK);
ft1000_read_register (ft1000dev, &tempword, FT1000_REG_SUP_IMASK);
DEBUG("ft1000_hw:ft1000_disable_interrupts:current interrupt enable mask = 0x%x\n", tempword);
info->InterruptsEnabled = FALSE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_enable_interrupts
// Descripton: This function will enable interrupts base on the current interrupt mask.
// Input:
// dev - device structure
// Output:
// None.
//
//---------------------------------------------------------------------------
static void ft1000_enable_interrupts(struct net_device *dev) {
FT1000_INFO *info = netdev_priv(dev);
struct ft1000_device *ft1000dev = info->pFt1000Dev;
u16 tempword;
DEBUG("ft1000_hw:ft1000_enable_interrupts()\n");
ft1000_write_register (ft1000dev, info->CurrentInterruptEnableMask, FT1000_REG_SUP_IMASK);
ft1000_read_register (ft1000dev, &tempword, FT1000_REG_SUP_IMASK);
DEBUG("ft1000_hw:ft1000_enable_interrupts:current interrupt enable mask = 0x%x\n", tempword);
info->InterruptsEnabled = TRUE;
}
*/
//---------------------------------------------------------------------------
//
// Function: ft1000_reset_card
// Descripton: This function will reset the card
// Input:
// dev - device structure
// Output:
// status - FALSE (card reset fail)
// TRUE (card reset successful)
//
//---------------------------------------------------------------------------
static int ft1000_reset_card (struct net_device *dev)
{
FT1000_INFO *info = netdev_priv(dev);
struct ft1000_device *ft1000dev = info->pFt1000Dev;
u16 tempword;
PPROV_RECORD ptr;
DEBUG("ft1000_hw:ft1000_reset_card called.....\n");
info->fCondResetPend = 1;
info->CardReady = 0;
info->fProvComplete = 0;
//ft1000_disable_interrupts(dev);
// Cancel heartbeat task since we are reloading the dsp
//del_timer(&poll_timer[info->CardNumber]);
// Make sure we free any memory reserve for provisioning
while (list_empty(&info->prov_list) == 0) {
DEBUG("ft1000_hw:ft1000_reset_card:deleting provisioning record\n");
ptr = list_entry(info->prov_list.next, PROV_RECORD, list);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
DEBUG("ft1000_hw:ft1000_reset_card: reset asic\n");
//reset ASIC
ft1000_reset_asic(dev);
info->DSPResetNum++;
#if 0
DEBUG("ft1000_hw:ft1000_reset_card:resetting ASIC and DSP\n");
ft1000_write_register (ft1000dev, (DSP_RESET_BIT | ASIC_RESET_BIT), FT1000_REG_RESET );
// Copy DSP session record into info block if this is not a coldstart
//if (ft1000_card_present == 1) {
spin_lock_irqsave(&info->dpram_lock, flags);
ft1000_write_register(ft1000dev, FT1000_DPRAM_MAG_RX_BASE, FT1000_REG_DPRAM_ADDR);
for (i=0;i<MAX_DSP_SESS_REC/2; i++) {
//info->DSPSess.MagRec[i] = inl(dev->base_addr+FT1000_REG_MAG_DPDATA);
ft1000_read_dpram32(ft1000dev, FT1000_REG_MAG_DPDATA, (PCHAR)&(info->DSPSess.MagRec[i]), 4);
}
spin_unlock_irqrestore(&info->dpram_lock, flags);
//}
info->squeseqnum = 0;
DEBUG("ft1000_hw:ft1000_reset_card:resetting ASIC\n");
mdelay(10);
//reset ASIC
ft1000_reset_asic(dev);
info->DSPResetNum++;
DEBUG("ft1000_hw:ft1000_reset_card:downloading dsp image\n");
// Put dsp in reset and take ASIC out of reset
DEBUG("ft1000_hw:ft1000_reset_card:Put DSP in reset and take ASIC out of reset\n");
ft1000_write_register (ft1000dev, DSP_RESET_BIT, FT1000_REG_RESET);
// Setting MAGNEMITE ASIC to big endian mode
ft1000_write_register (ft1000dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
// Take DSP out of reset
ft1000_read_register(ft1000dev, &tempword, FT1000_REG_RESET);
tempword |= DSP_ENCRYPTED;
tempword &= ~DSP_UNENCRYPTED;
ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
tempword &= ~EFUSE_MEM_DISABLE;
ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
tempword &= ~DSP_RESET_BIT;
ft1000_write_register(ft1000dev, tempword, FT1000_REG_RESET);
// FLARION_DSP_ACTIVE;
mdelay(10);
DEBUG("ft1000_hw:ft1000_reset_card:Take DSP out of reset\n");
// Wait for 0xfefe indicating dsp ready before starting download
for (i=0; i<50; i++) {
//tempword = ft1000_read_dpram_mag_16(dev, FT1000_MAG_DPRAM_FEFE, FT1000_MAG_DPRAM_FEFE_INDX);
ft1000_read_dpram32 (ft1000dev, FT1000_MAG_DPRAM_FEFE_INDX, (PUCHAR)&templong, 4);
if (tempword == 0xfefe) {
break;
}
mdelay(20);
}
if (i==50) {
DEBUG("ft1000_hw:ft1000_reset_card:No FEFE detected from DSP\n");
return FALSE;
}
#endif
DEBUG("ft1000_hw:ft1000_reset_card: call dsp_reload\n");
dsp_reload(ft1000dev);
DEBUG("dsp reload successful\n");
mdelay(10);
// Initialize DSP heartbeat area to ho
ft1000_write_dpram16(ft1000dev, FT1000_MAG_HI_HO, ho_mag, FT1000_MAG_HI_HO_INDX);
ft1000_read_dpram16(ft1000dev, FT1000_MAG_HI_HO, (PCHAR)&tempword, FT1000_MAG_HI_HO_INDX);
DEBUG("ft1000_hw:ft1000_reset_card:hi_ho value = 0x%x\n", tempword);
info->CardReady = 1;
//ft1000_enable_interrupts(dev);
/* Schedule heartbeat process to run every 2 seconds */
//poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
//poll_timer[info->CardNumber].data = (u_long)dev;
//add_timer(&poll_timer[info->CardNumber]);
info->fCondResetPend = 0;
return TRUE;
}
//mbelian
#ifdef HAVE_NET_DEVICE_OPS
static const struct net_device_ops ftnet_ops =
{
.ndo_open = &ft1000_open,
.ndo_stop = &ft1000_close,
.ndo_start_xmit = &ft1000_start_xmit,
.ndo_get_stats = &ft1000_netdev_stats,
};
#endif
//---------------------------------------------------------------------------
// Function: init_ft1000_netdev
//
// Parameters: ft1000dev - device structure
//
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function initialize the network device
//
// Notes:
//
//---------------------------------------------------------------------------
u16 init_ft1000_netdev(struct ft1000_device *ft1000dev)
{
struct net_device *netdev;
FT1000_INFO *pInfo = NULL;
PDPRAM_BLK pdpram_blk;
int i;
gCardIndex=0; //mbelian
DEBUG("Enter init_ft1000_netdev...\n");
netdev = alloc_etherdev( sizeof(FT1000_INFO));
if (!netdev )
{
DEBUG("init_ft1000_netdev: can not allocate network device\n");
return STATUS_FAILURE;
}
//pInfo = (PFT1000_INFO)netdev->priv;
pInfo = (FT1000_INFO *) netdev_priv (netdev);
//DEBUG("init_ft1000_netdev: gFt1000Info=%x, netdev=%x, ft1000dev=%x\n", gFt1000Info, netdev, ft1000dev);
memset (pInfo, 0, sizeof(FT1000_INFO));
dev_alloc_name(netdev, netdev->name);
//for the first inserted card, decide the card index beginning number, in case there are existing network interfaces
if ( gCardIndex == 0 )
{
DEBUG("init_ft1000_netdev: network device name is %s\n", netdev->name);
if ( strncmp(netdev->name,"eth", 3) == 0) {
//pInfo->CardNumber = atoi(&netdev->name[3]);
gCardIndex = atoi(&netdev->name[3]);
pInfo->CardNumber = gCardIndex;
DEBUG("card number = %d\n", pInfo->CardNumber);
}
else {
printk(KERN_ERR "ft1000: Invalid device name\n");
kfree(netdev);
return STATUS_FAILURE;
}
}
else
{
//not the first inserted card, increase card number by 1
/*gCardIndex ++;*/
pInfo->CardNumber = gCardIndex;
/*DEBUG("card number = %d\n", pInfo->CardNumber);*/ //mbelian
}
memset(&pInfo->stats, 0, sizeof(struct net_device_stats) );
spin_lock_init(&pInfo->dpram_lock);
pInfo->pFt1000Dev = ft1000dev;
pInfo->DrvErrNum = 0;
pInfo->ASICResetNum = 0;
pInfo->registered = 1;
pInfo->ft1000_reset = ft1000_reset;
pInfo->mediastate = 0;
pInfo->fifo_cnt = 0;
pInfo->DeviceCreated = FALSE;
pInfo->DeviceMajor = 0;
pInfo->CurrentInterruptEnableMask = ISR_DEFAULT_MASK;
pInfo->InterruptsEnabled = FALSE;
pInfo->CardReady = 0;
pInfo->DSP_loading = 0;
pInfo->DSP_TIME[0] = 0;
pInfo->DSP_TIME[1] = 0;
pInfo->DSP_TIME[2] = 0;
pInfo->DSP_TIME[3] = 0;
pInfo->fAppMsgPend = 0;
pInfo->fCondResetPend = 0;
pInfo->usbboot = 0;
pInfo->dspalive = 0;
for (i=0;i<32 ;i++ )
{
pInfo->tempbuf[i] = 0;
}
INIT_LIST_HEAD(&pInfo->prov_list);
//mbelian
#ifdef HAVE_NET_DEVICE_OPS
netdev->netdev_ops = &ftnet_ops;
#else
netdev->hard_start_xmit = &ft1000_start_xmit;
netdev->get_stats = &ft1000_netdev_stats;
netdev->open = &ft1000_open;
netdev->stop = &ft1000_close;
#endif
//netif_stop_queue(netdev); //mbelian
ft1000dev->net = netdev;
//init free_buff_lock, freercvpool, numofmsgbuf, pdpram_blk
//only init once per card
//Jim
DEBUG("Initialize free_buff_lock and freercvpool\n");
spin_lock_init(&free_buff_lock);
// initialize a list of buffers to be use for queuing up receive command data
INIT_LIST_HEAD (&freercvpool);
// create list of free buffers
for (i=0; i<NUM_OF_FREE_BUFFERS; i++) {
// Get memory for DPRAM_DATA link list
pdpram_blk = kmalloc ( sizeof(DPRAM_BLK), GFP_KERNEL );
// Get a block of memory to store command data
pdpram_blk->pbuffer = kmalloc ( MAX_CMD_SQSIZE, GFP_KERNEL );
// link provisioning data
list_add_tail (&pdpram_blk->list, &freercvpool);
}
numofmsgbuf = NUM_OF_FREE_BUFFERS;
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
// Function: reg_ft1000_netdev
//
// Parameters: ft1000dev - device structure
//
//
// Returns: STATUS_SUCCESS - success
// STATUS_FAILURE - failure
//
// Description: This function register the network driver
//
// Notes:
//
//---------------------------------------------------------------------------
u16 reg_ft1000_netdev(struct ft1000_device *ft1000dev, struct usb_interface *intf)
{
struct net_device *netdev;
FT1000_INFO *pInfo;
int i, rc;
netdev = ft1000dev->net;
pInfo = netdev_priv(ft1000dev->net);
DEBUG("Enter reg_ft1000_netdev...\n");
ft1000_read_register(ft1000dev, &pInfo->AsicID, FT1000_REG_ASIC_ID);
usb_set_intfdata(intf, pInfo);
SET_NETDEV_DEV(netdev, &intf->dev);
rc = register_netdev(netdev);
if (rc)
{
DEBUG("reg_ft1000_netdev: could not register network device\n");
free_netdev(netdev);
return STATUS_FAILURE;
}
//Create character device, implemented by Jim
ft1000_CreateDevice(ft1000dev);
//INIT_LIST_HEAD(&pInfo->prov_list);
for (i=0; i<MAX_NUM_CARDS; i++) {
poll_timer[i].function = ft1000_hbchk;
}
//hard code MAC address for now
/**
netdev->dev_addr[0] = 0;
netdev->dev_addr[1] = 7;
netdev->dev_addr[2] = 0x35;
netdev->dev_addr[3] = 0x84;
netdev->dev_addr[4] = 0;
netdev->dev_addr[5] = 0x20 + pInfo->CardNumber;
**/
DEBUG ("reg_ft1000_netdev returned\n");
pInfo->CardReady = 1;
return STATUS_SUCCESS;
}
int ft1000_reset(struct net_device *dev)
{
ft1000_reset_card(dev);
return 0;
}
//---------------------------------------------------------------------------
// Function: ft1000_usb_transmit_complete
//
// Parameters: urb - transmitted usb urb
//
//
// Returns: none
//
// Description: This is the callback function when a urb is transmitted
//
// Notes:
//
//---------------------------------------------------------------------------
static void ft1000_usb_transmit_complete(struct urb *urb)
{
struct ft1000_device *ft1000dev = urb->context;
//DEBUG("ft1000_usb_transmit_complete entered\n");
// Jim spin_lock(&ft1000dev->device_lock);
if (urb->status)
printk("%s: TX status %d\n", ft1000dev->net->name, urb->status);
netif_wake_queue(ft1000dev->net);
//Jim spin_unlock(&ft1000dev->device_lock);
//DEBUG("Return from ft1000_usb_transmit_complete\n");
}
/****************************************************************
* ft1000_control
****************************************************************/
static int ft1000_read_fifo_reg(struct ft1000_device *ft1000dev,unsigned int pipe,
u8 request,
u8 requesttype,
u16 value,
u16 index,
void *data,
u16 size,
int timeout)
{
u16 ret;
DECLARE_WAITQUEUE(wait, current);
struct urb *urb;
struct usb_ctrlrequest *dr;
int status;
if (ft1000dev == NULL )
{
DEBUG("NULL ft1000dev, failure\n");
return STATUS_FAILURE;
}
else if ( ft1000dev->dev == NULL )
{
DEBUG("NULL ft1000dev->dev, failure\n");
return STATUS_FAILURE;
}
spin_lock(&ft1000dev->device_lock);
/*DECLARE_WAITQUEUE(wait, current);
struct urb *urb;
struct usb_ctrlrequest *dr;
int status;*/
if(in_interrupt())
{
spin_unlock(&ft1000dev->device_lock);
return -EBUSY;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
dr = kmalloc(sizeof(struct usb_ctrlrequest), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
if(!urb || !dr)
{
if(urb) kfree(urb);
spin_unlock(&ft1000dev->device_lock);
return -ENOMEM;
}
dr->bRequestType = requesttype;
dr->bRequest = request;
dr->wValue = value;
dr->wIndex = index;
dr->wLength = size;
usb_fill_control_urb(urb, ft1000dev->dev, pipe, (char*)dr, (void*)data, size, (void *)ft1000_control_complete, (void*)ft1000dev);
init_waitqueue_head(&ft1000dev->control_wait);
//current->state = TASK_INTERRUPTIBLE; //mbelian
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&ft1000dev->control_wait, &wait);
status = usb_submit_urb(urb, GFP_KERNEL);
if(status)
{
usb_free_urb(urb);
kfree(dr);
remove_wait_queue(&ft1000dev->control_wait, &wait);
spin_unlock(&ft1000dev->device_lock);
return status;
}
if(urb->status == -EINPROGRESS)
{
while(timeout && urb->status == -EINPROGRESS)
{
status = timeout = schedule_timeout(timeout);
}
}
else
{
status = 1;
}
remove_wait_queue(&ft1000dev->control_wait, &wait);
if(!status)
{
usb_unlink_urb(urb);
printk("ft1000 timeout\n");
status = -ETIMEDOUT;
}
else
{
status = urb->status;
if(urb->status)
{
printk("ft1000 control message failed (urb addr: %x) with error number: %i\n", (int)urb, (int)status);
usb_clear_halt(ft1000dev->dev, usb_rcvctrlpipe(ft1000dev->dev, 0));
usb_clear_halt(ft1000dev->dev, usb_sndctrlpipe(ft1000dev->dev, 0));
usb_unlink_urb(urb);
}
}
usb_free_urb(urb);
kfree(dr);
spin_unlock(&ft1000dev->device_lock);
return ret;
}
//---------------------------------------------------------------------------
// Function: ft1000_read_fifo_len
//
// Parameters: ft1000dev - device structure
//
//
// Returns: none
//
// Description: read the fifo length register content
//
// Notes:
//
//---------------------------------------------------------------------------
static inline u16 ft1000_read_fifo_len (struct net_device *dev)
{
u16 temp;
u16 ret;
//FT1000_INFO *info = (PFT1000_INFO)dev->priv;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev);
struct ft1000_device *ft1000dev = info->pFt1000Dev;
// DEBUG("ft1000_read_fifo_len: enter ft1000dev %x\n", ft1000dev); //aelias [-] reason: warning: format ???%x??? expects type ???unsigned int???, but argument 2 has type ???struct ft1000_device *???
DEBUG("ft1000_read_fifo_len: enter ft1000dev %x\n", (unsigned int)ft1000dev); //aelias [+] reason: up
//ft1000_read_register(ft1000dev, &temp, FT1000_REG_MAG_UFSR);
ret = STATUS_SUCCESS;
ret = ft1000_read_fifo_reg(ft1000dev,
usb_rcvctrlpipe(ft1000dev->dev,0),
HARLEY_READ_REGISTER,
HARLEY_READ_OPERATION,
0,
FT1000_REG_MAG_UFSR,
&temp,
2,
LARGE_TIMEOUT);
if (ret>0)
ret = STATUS_SUCCESS;
else
ret = STATUS_FAILURE;
DEBUG("ft1000_read_fifo_len: returned %d\n", temp);
return (temp- 16);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_copy_down_pkt
// Descripton: This function will take an ethernet packet and convert it to
// a Flarion packet prior to sending it to the ASIC Downlink
// FIFO.
// Input:
// dev - device structure
// packet - address of ethernet packet
// len - length of IP packet
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_copy_down_pkt (struct net_device *netdev, u8 *packet, u16 len)
{
FT1000_INFO *pInfo = netdev_priv(netdev);
struct ft1000_device *pFt1000Dev = pInfo->pFt1000Dev;
int i, count, ret;
USHORT *pTemp;
USHORT checksum;
u8 *t;
if (!pInfo->CardReady)
{
DEBUG("ft1000_copy_down_pkt::Card Not Ready\n");
return STATUS_FAILURE;
}
//DEBUG("ft1000_copy_down_pkt() entered, len = %d\n", len);
#if 0
// Check if there is room on the FIFO
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(10);
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(20);
}
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(20);
}
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(20);
}
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(20);
}
if ( len > ft1000_read_fifo_len (netdev) )
{
udelay(20);
}
if ( len > ft1000_read_fifo_len (netdev) )
{
DEBUG("ft1000_hw:ft1000_copy_down_pkt:Transmit FIFO is fulli - pkt drop\n");
pInfo->stats.tx_errors++;
return STATUS_SUCCESS;
}
}
#endif
count = sizeof (PSEUDO_HDR) + len;
if(count > MAX_BUF_SIZE)
{
DEBUG("Error:ft1000_copy_down_pkt:Message Size Overflow!\n");
DEBUG("size = %d\n", count);
return STATUS_FAILURE;
}
if ( count % 4)
count = count + (4- (count %4) );
pTemp = (PUSHORT)&(pFt1000Dev->tx_buf[0]);
*pTemp ++ = ntohs(count);
*pTemp ++ = 0x1020;
*pTemp ++ = 0x2010;
*pTemp ++ = 0x9100;
*pTemp ++ = 0;
*pTemp ++ = 0;
*pTemp ++ = 0;
pTemp = (PUSHORT)&(pFt1000Dev->tx_buf[0]);
checksum = *pTemp ++;
for (i=1; i<7; i++)
{
checksum ^= *pTemp ++;
}
*pTemp++ = checksum;
memcpy (&(pFt1000Dev->tx_buf[sizeof(PSEUDO_HDR)]), packet, len);
//usb_init_urb(pFt1000Dev->tx_urb); //mbelian
netif_stop_queue(netdev);
//DEBUG ("ft1000_copy_down_pkt: count = %d\n", count);
usb_fill_bulk_urb(pFt1000Dev->tx_urb,
pFt1000Dev->dev,
usb_sndbulkpipe(pFt1000Dev->dev, pFt1000Dev->bulk_out_endpointAddr),
pFt1000Dev->tx_buf,
count,
ft1000_usb_transmit_complete,
(void*)pFt1000Dev);
t = (u8 *)pFt1000Dev->tx_urb->transfer_buffer;
//DEBUG("transfer_length=%d\n", pFt1000Dev->tx_urb->transfer_buffer_length);
/*for (i=0; i<count; i++ )
{
DEBUG("%x ", *t++ );
}*/
ret = usb_submit_urb(pFt1000Dev->tx_urb, GFP_ATOMIC);
if(ret)
{
DEBUG("ft1000 failed tx_urb %d\n", ret);
/* pInfo->stats.tx_errors++;
netif_start_queue(netdev); */ //mbelian
return STATUS_FAILURE;
}
else
{
//DEBUG("ft1000 sucess tx_urb %d\n", ret);
pInfo->stats.tx_packets++;
pInfo->stats.tx_bytes += (len+14);
}
//DEBUG("ft1000_copy_down_pkt() exit\n");
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
// Function: ft1000_start_xmit
//
// Parameters: skb - socket buffer to be sent
// dev - network device
//
//
// Returns: none
//
// Description: transmit a ethernet packet
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
FT1000_INFO *pInfo = netdev_priv(dev);
struct ft1000_device *pFt1000Dev= pInfo->pFt1000Dev;
u8 *pdata;
int maxlen, pipe;
//DEBUG(" ft1000_start_xmit() entered\n");
if ( skb == NULL )
{
DEBUG ("ft1000_hw: ft1000_start_xmit:skb == NULL!!!\n" );
return STATUS_FAILURE;
}
if ( pFt1000Dev->status & FT1000_STATUS_CLOSING)
{
DEBUG("network driver is closed, return\n");
dev_kfree_skb(skb);
//usb_kill_urb(pFt1000Dev->tx_urb); //mbelian
return STATUS_SUCCESS;
}
//DEBUG("ft1000_start_xmit 1:length of packet = %d\n", skb->len);
pipe = usb_sndbulkpipe(pFt1000Dev->dev, pFt1000Dev->bulk_out_endpointAddr);
maxlen = usb_maxpacket(pFt1000Dev->dev, pipe, usb_pipeout(pipe));
//DEBUG("ft1000_start_xmit 2: pipe=%d dev->maxpacket = %d\n", pipe, maxlen);
pdata = (u8 *)skb->data;
/*for (i=0; i<skb->len; i++)
DEBUG("skb->data[%d]=%x ", i, *(skb->data+i));
DEBUG("\n");*/
if (pInfo->mediastate == 0)
{
/* Drop packet is mediastate is down */
DEBUG("ft1000_hw:ft1000_start_xmit:mediastate is down\n");
dev_kfree_skb(skb);
return STATUS_SUCCESS;
}
if ( (skb->len < ENET_HEADER_SIZE) || (skb->len > ENET_MAX_SIZE) )
{
/* Drop packet which has invalid size */
DEBUG("ft1000_hw:ft1000_start_xmit:invalid ethernet length\n");
dev_kfree_skb(skb);
return STATUS_SUCCESS;
}
//mbelian
if(ft1000_copy_down_pkt (dev, (pdata+ENET_HEADER_SIZE-2), skb->len - ENET_HEADER_SIZE + 2) == STATUS_FAILURE)
{
dev_kfree_skb(skb);
return STATUS_SUCCESS;
}
dev_kfree_skb(skb);
//DEBUG(" ft1000_start_xmit() exit\n");
return 0;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_copy_up_pkt
// Descripton: This function will take a packet from the FIFO up link and
// convert it into an ethernet packet and deliver it to the IP stack
// Input:
// urb - the receving usb urb
//
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_copy_up_pkt (struct urb *urb)
{
PFT1000_INFO info = urb->context;
struct ft1000_device *ft1000dev = info->pFt1000Dev;
struct net_device *net = ft1000dev->net;
u16 tempword;
u16 len;
u16 lena; //mbelian
struct sk_buff *skb;
u16 i;
u8 *pbuffer=NULL;
u8 *ptemp=NULL;
u16 *chksum;
//DEBUG("ft1000_copy_up_pkt entered\n");
if ( ft1000dev->status & FT1000_STATUS_CLOSING)
{
DEBUG("network driver is closed, return\n");
return STATUS_SUCCESS;
}
// Read length
len = urb->transfer_buffer_length;
lena = urb->actual_length; //mbelian
//DEBUG("ft1000_copy_up_pkt: transfer_buffer_length=%d, actual_buffer_len=%d\n",
// urb->transfer_buffer_length, urb->actual_length);
chksum = (PUSHORT)ft1000dev->rx_buf;
tempword = *chksum++;
for (i=1; i<7; i++)
{
tempword ^= *chksum++;
}
if (tempword != *chksum)
{
info->stats.rx_errors ++;
ft1000_submit_rx_urb(info);
return STATUS_FAILURE;
}
//DEBUG("ft1000_copy_up_pkt: checksum is correct %x\n", *chksum);
skb = dev_alloc_skb(len+12+2);
if (skb == NULL)
{
DEBUG("ft1000_copy_up_pkt: No Network buffers available\n");
info->stats.rx_errors++;
ft1000_submit_rx_urb(info);
return STATUS_FAILURE;
}
pbuffer = (u8 *)skb_put(skb, len+12);
//subtract the number of bytes read already
ptemp = pbuffer;
// fake MAC address
*pbuffer++ = net->dev_addr[0];
*pbuffer++ = net->dev_addr[1];
*pbuffer++ = net->dev_addr[2];
*pbuffer++ = net->dev_addr[3];
*pbuffer++ = net->dev_addr[4];
*pbuffer++ = net->dev_addr[5];
*pbuffer++ = 0x00;
*pbuffer++ = 0x07;
*pbuffer++ = 0x35;
*pbuffer++ = 0xff;
*pbuffer++ = 0xff;
*pbuffer++ = 0xfe;
memcpy(pbuffer, ft1000dev->rx_buf+sizeof(PSEUDO_HDR), len-sizeof(PSEUDO_HDR));
//DEBUG("ft1000_copy_up_pkt: Data passed to Protocol layer\n");
/*for (i=0; i<len+12; i++)
{
DEBUG("ft1000_copy_up_pkt: Protocol Data: 0x%x\n ", *ptemp++);
}*/
skb->dev = net;
skb->protocol = eth_type_trans(skb, net);
skb->ip_summed = CHECKSUM_UNNECESSARY;
netif_rx(skb);
info->stats.rx_packets++;
// Add on 12 bytes for MAC address which was removed
info->stats.rx_bytes += (lena+12); //mbelian
ft1000_submit_rx_urb(info);
//DEBUG("ft1000_copy_up_pkt exited\n");
return SUCCESS;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_submit_rx_urb
// Descripton: the receiving function of the network driver
//
// Input:
// info - a private structure contains the device information
//
// Output:
// status - FAILURE
// SUCCESS
//
//---------------------------------------------------------------------------
int ft1000_submit_rx_urb(PFT1000_INFO info)
{
int result;
struct ft1000_device *pFt1000Dev = info->pFt1000Dev;
//netif_carrier_on(pFt1000Dev->net);
//DEBUG ("ft1000_submit_rx_urb entered: sizeof rx_urb is %d\n", sizeof(*pFt1000Dev->rx_urb));
if ( pFt1000Dev->status & FT1000_STATUS_CLOSING)
{
DEBUG("network driver is closed, return\n");
//usb_kill_urb(pFt1000Dev->rx_urb); //mbelian
return STATUS_SUCCESS;
}
//memset(pFt1000Dev->rx_urb, 0, sizeof(*pFt1000Dev->rx_urb));
//usb_init_urb(pFt1000Dev->rx_urb);//mbelian
//spin_lock_init(&pFt1000Dev->rx_urb->lock);
usb_fill_bulk_urb(pFt1000Dev->rx_urb,
pFt1000Dev->dev,
usb_rcvbulkpipe(pFt1000Dev->dev, pFt1000Dev->bulk_in_endpointAddr),
pFt1000Dev->rx_buf,
MAX_BUF_SIZE,
(usb_complete_t)ft1000_copy_up_pkt,
info);
if((result = usb_submit_urb(pFt1000Dev->rx_urb, GFP_ATOMIC)))
{
printk("ft1000_submit_rx_urb: submitting rx_urb %d failed\n", result);
return STATUS_FAILURE;
}
//DEBUG("ft1000_submit_rx_urb exit: result=%d\n", result);
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
// Function: ft1000_open
//
// Parameters:
// dev - network device
//
//
// Returns: none
//
// Description: open the network driver
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_open (struct net_device *dev)
{
FT1000_INFO *pInfo = (FT1000_INFO *)netdev_priv(dev);
struct timeval tv; //mbelian
DEBUG("ft1000_open is called for card %d\n", pInfo->CardNumber);
//DEBUG("ft1000_open: dev->addr=%x, dev->addr_len=%d\n", dev->addr, dev->addr_len);
pInfo->stats.rx_bytes = 0; //mbelian
pInfo->stats.tx_bytes = 0; //mbelian
pInfo->stats.rx_packets = 0; //mbelian
pInfo->stats.tx_packets = 0; //mbelian
do_gettimeofday(&tv);
pInfo->ConTm = tv.tv_sec;
pInfo->ProgConStat = 0; //mbelian
netif_start_queue(dev);
//netif_device_attach(dev);
netif_carrier_on(dev); //mbelian
ft1000_submit_rx_urb(pInfo);
return 0;
}
//---------------------------------------------------------------------------
// Function: ft1000_close
//
// Parameters:
// net - network device
//
//
// Returns: none
//
// Description: close the network driver
//
// Notes:
//
//---------------------------------------------------------------------------
int ft1000_close(struct net_device *net)
{
FT1000_INFO *pInfo = (FT1000_INFO *) netdev_priv (net);
struct ft1000_device *ft1000dev = pInfo->pFt1000Dev;
//DEBUG ("ft1000_close: netdev->refcnt=%d\n", net->refcnt);
ft1000dev->status |= FT1000_STATUS_CLOSING;
//DEBUG("ft1000_close: calling usb_kill_urb \n");
//usb_kill_urb(ft1000dev->rx_urb);
//usb_kill_urb(ft1000dev->tx_urb);
DEBUG("ft1000_close: pInfo=%x, ft1000dev=%x\n", (int)pInfo, (int)ft1000dev);
netif_carrier_off(net);//mbelian
netif_stop_queue(net);
//DEBUG("ft1000_close: netif_stop_queue called\n");
ft1000dev->status &= ~FT1000_STATUS_CLOSING;
pInfo->ProgConStat = 0xff; //mbelian
return 0;
}
static struct net_device_stats *ft1000_netdev_stats(struct net_device *dev)
{
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev);
//struct ft1000_device *ft1000dev = info->pFt1000Dev;
//return &(ft1000dev->stats);//mbelian
return &(info->stats); //mbelian
}
/*********************************************************************************
Jim
*/
//---------------------------------------------------------------------------
//
// Function: ft1000_chkcard
// Descripton: This function will check if the device is presently available on
// the system.
// Input:
// dev - device structure
// Output:
// status - FALSE (device is not present)
// TRUE (device is present)
//
//---------------------------------------------------------------------------
static int ft1000_chkcard (struct ft1000_device *dev) {
u16 tempword;
u16 status;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev->net);
if (info->fCondResetPend)
{
DEBUG("ft1000_hw:ft1000_chkcard:Card is being reset, return FALSE\n");
return TRUE;
}
// Mask register is used to check for device presence since it is never
// set to zero.
status = ft1000_read_register(dev, &tempword, FT1000_REG_SUP_IMASK);
//DEBUG("ft1000_hw:ft1000_chkcard: read FT1000_REG_SUP_IMASK = %x\n", tempword);
if (tempword == 0) {
DEBUG("ft1000_hw:ft1000_chkcard: IMASK = 0 Card not detected\n");
return FALSE;
}
// The system will return the value of 0xffff for the version register
// if the device is not present.
status = ft1000_read_register(dev, &tempword, FT1000_REG_ASIC_ID);
//DEBUG("ft1000_hw:ft1000_chkcard: read FT1000_REG_ASIC_ID = %x\n", tempword);
//pxu if (tempword == 0xffff) {
if (tempword != 0x1b01 ){
dev->status |= FT1000_STATUS_CLOSING; //mbelian
DEBUG("ft1000_hw:ft1000_chkcard: Version = 0xffff Card not detected\n");
return FALSE;
}
return TRUE;
}
//---------------------------------------------------------------------------
//
// Function: ft1000_hbchk
// Descripton: This function will perform the heart beat check of the DSP as
// well as the ASIC.
// Input:
// dev - device structure
// Output:
// none
//
//---------------------------------------------------------------------------
static void ft1000_hbchk(u_long data)
{
struct ft1000_device *dev = (struct ft1000_device *)data;
FT1000_INFO *info;
USHORT tempword;
u16 status;
info = (FT1000_INFO *) netdev_priv (dev->net);
DEBUG("ft1000_hbchk called for CardNumber = %d CardReady = %d\n", info->CardNumber, info->CardReady);
if (info->fCondResetPend == 1) {
// Reset ASIC and DSP
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER0, (PUCHAR)&(info->DSP_TIME[0]), FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER1, (PUCHAR)&(info->DSP_TIME[1]), FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER2, (PUCHAR)&(info->DSP_TIME[2]), FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER3, (PUCHAR)&(info->DSP_TIME[3]), FT1000_MAG_DSP_TIMER3_INDX);
info->DrvErrNum = DSP_CONDRESET_INFO;
DEBUG("ft1000_hw:DSP conditional reset requested\n");
ft1000_reset_card(dev->net);
info->fCondResetPend = 0;
/* Schedule this module to run every 2 seconds */
poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
poll_timer[info->CardNumber].data = (u_long)dev;
add_timer(&poll_timer[info->CardNumber]);
return;
}
if (info->CardReady == 1) {
// Perform dsp heartbeat check
status = ntohs(ft1000_read_dpram16(dev, FT1000_MAG_HI_HO, (PUCHAR)&tempword, FT1000_MAG_HI_HO_INDX));
DEBUG("ft1000_hw:ft1000_hbchk:hi_ho value = 0x%x\n", tempword);
// Let's perform another check if ho is not detected
if (tempword != ho) {
status = ntohs(ft1000_read_dpram16(dev, FT1000_MAG_HI_HO, (PUCHAR)&tempword,FT1000_MAG_HI_HO_INDX));
}
if (tempword != ho) {
printk(KERN_INFO "ft1000: heartbeat failed - no ho detected\n");
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER0, (PUCHAR)&(info->DSP_TIME[0]), FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER1, (PUCHAR)&(info->DSP_TIME[1]), FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER2, (PUCHAR)&(info->DSP_TIME[2]), FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER3, (PUCHAR)&(info->DSP_TIME[3]), FT1000_MAG_DSP_TIMER3_INDX);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev->net) == 0) {
printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
poll_timer[info->CardNumber].data = (u_long)dev;
add_timer(&poll_timer[info->CardNumber]);
return;
}
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
// Let's check doorbell again if fail
if (tempword & FT1000_DB_HB) {
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
}
if (tempword & FT1000_DB_HB) {
printk(KERN_INFO "ft1000: heartbeat doorbell not clear by firmware\n");
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER0, (PUCHAR)&(info->DSP_TIME[0]), FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER1, (PUCHAR)&(info->DSP_TIME[1]), FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER2, (PUCHAR)&(info->DSP_TIME[2]), FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER3, (PUCHAR)&(info->DSP_TIME[3]), FT1000_MAG_DSP_TIMER3_INDX);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev->net) == 0) {
printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
poll_timer[info->CardNumber].data = (u_long)dev;
add_timer(&poll_timer[info->CardNumber]);
return;
}
// Set dedicated area to hi and ring appropriate doorbell according
// to hi/ho heartbeat protocol
ft1000_write_dpram16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX);
status = ntohs(ft1000_read_dpram16(dev, FT1000_MAG_HI_HO, (PUCHAR)&tempword, FT1000_MAG_HI_HO_INDX));
// Let's write hi again if fail
if (tempword != hi) {
ft1000_write_dpram16(dev, FT1000_MAG_HI_HO, hi_mag, FT1000_MAG_HI_HO_INDX);
status = ntohs(ft1000_read_dpram16(dev, FT1000_MAG_HI_HO, (PUCHAR)&tempword, FT1000_MAG_HI_HO_INDX));
}
if (tempword != hi) {
printk(KERN_INFO "ft1000: heartbeat failed - cannot write hi into DPRAM\n");
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER0, (PUCHAR)&(info->DSP_TIME[0]), FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER1, (PUCHAR)&(info->DSP_TIME[1]), FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER2, (PUCHAR)&(info->DSP_TIME[2]), FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER3, (PUCHAR)&(info->DSP_TIME[3]), FT1000_MAG_DSP_TIMER3_INDX);
info->DrvErrNum = DSP_HB_INFO;
if (ft1000_reset_card(dev->net) == 0) {
printk(KERN_INFO "ft1000: Hardware Failure Detected - PC Card disabled\n");
info->ProgConStat = 0xff;
return;
}
/* Schedule this module to run every 2 seconds */
poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
poll_timer[info->CardNumber].data = (u_long)dev;
add_timer(&poll_timer[info->CardNumber]);
return;
}
ft1000_write_register(dev, FT1000_DB_HB, FT1000_REG_DOORBELL);
}
/* Schedule this module to run every 2 seconds */
poll_timer[info->CardNumber].expires = jiffies + (2*HZ);
poll_timer[info->CardNumber].data = (u_long)dev;
add_timer(&poll_timer[info->CardNumber]);
}
//---------------------------------------------------------------------------
//
// Function: ft1000_receive_cmd
// Descripton: This function will read a message from the dpram area.
// Input:
// dev - network device structure
// pbuffer - caller supply address to buffer
// pnxtph - pointer to next pseudo header
// Output:
// Status = 0 (unsuccessful)
// = 1 (successful)
//
//---------------------------------------------------------------------------
BOOLEAN ft1000_receive_cmd (struct ft1000_device *dev, u16 *pbuffer, int maxsz, u16 *pnxtph) {
u16 size, ret;
u16 *ppseudohdr;
int i;
u16 tempword;
ret = ft1000_read_dpram16(dev, FT1000_MAG_PH_LEN, (PUCHAR)&size, FT1000_MAG_PH_LEN_INDX);
size = ntohs(size) + PSEUDOSZ;
if (size > maxsz) {
DEBUG("FT1000:ft1000_receive_cmd:Invalid command length = %d\n", size);
return FALSE;
}
else {
ppseudohdr = (u16 *)pbuffer;
//spin_lock_irqsave (&info->dpram_lock, flags);
ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE, FT1000_REG_DPRAM_ADDR);
ret = ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
//DEBUG("ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
ft1000_write_register(dev, FT1000_DPRAM_MAG_RX_BASE+1, FT1000_REG_DPRAM_ADDR);
for (i=0; i<=(size>>2); i++) {
ret = ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAL);
pbuffer++;
ret = ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
pbuffer++;
}
//copy odd aligned word
ret = ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAL);
//DEBUG("ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
ret = ft1000_read_register(dev, pbuffer, FT1000_REG_MAG_DPDATAH);
//DEBUG("ft1000_hw:received data = 0x%x\n", *pbuffer);
pbuffer++;
if (size & 0x0001) {
//copy odd byte from fifo
ret = ft1000_read_register(dev, &tempword, FT1000_REG_DPRAM_DATA);
*pbuffer = ntohs(tempword);
}
//spin_unlock_irqrestore(&info->dpram_lock, flags);
// Check if pseudo header checksum is good
// Calculate pseudo header checksum
tempword = *ppseudohdr++;
for (i=1; i<7; i++) {
tempword ^= *ppseudohdr++;
}
if ( (tempword != *ppseudohdr) ) {
return FALSE;
}
#if 0
DEBUG("ft1000_receive_cmd:pbuffer\n");
for(i = 0; i < size; i+=5)
{
if( (i + 5) < size )
DEBUG("0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", tempbuffer[i], tempbuffer[i+1], tempbuffer[i+2], tempbuffer[i+3], tempbuffer[i+4]);
else
{
for (j = i; j < size; j++)
DEBUG("0x%x ", tempbuffer[j]);
DEBUG("\n");
break;
}
}
#endif
return TRUE;
}
}
int ft1000_dsp_prov(void *arg)
{
struct ft1000_device *dev = (struct ft1000_device *)arg;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev->net);
u16 tempword;
u16 len;
u16 i=0;
PPROV_RECORD ptr;
PPSEUDO_HDR ppseudo_hdr;
PUSHORT pmsg;
u16 status;
USHORT TempShortBuf [256];
DEBUG("*** DspProv Entered\n");
while ( list_empty(&info->prov_list) == 0
/*&& !kthread_should_stop() */)
{
DEBUG("DSP Provisioning List Entry\n");
// Check if doorbell is available
DEBUG("check if doorbell is cleared\n");
status = ft1000_read_register (dev, &tempword, FT1000_REG_DOORBELL);
if (status)
{
DEBUG("ft1000_dsp_prov::ft1000_read_register error\n");
break;
}
while (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
i++;
if (i==10) {
DEBUG("FT1000:ft1000_dsp_prov:message drop\n");
return STATUS_FAILURE;
}
ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
}
if ( !(tempword & FT1000_DB_DPRAM_TX) ) {
DEBUG("*** Provision Data Sent to DSP\n");
// Send provisioning data
ptr = list_entry(info->prov_list.next, PROV_RECORD, list);
len = *(u16 *)ptr->pprov_data;
len = htons(len);
len += PSEUDOSZ;
//len = htons(len);
pmsg = (PUSHORT)ptr->pprov_data;
ppseudo_hdr = (PPSEUDO_HDR)pmsg;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
//DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum);
for (i=1; i<7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
//DEBUG("checksum = 0x%x\n", ppseudo_hdr->checksum);
}
TempShortBuf[0] = 0;
TempShortBuf[1] = htons (len);
memcpy(&TempShortBuf[2], ppseudo_hdr, len);
status = ft1000_write_dpram32 (dev, 0, (PUCHAR)&TempShortBuf[0], (unsigned short)(len+2));
status = ft1000_write_register (dev, FT1000_DB_DPRAM_TX, FT1000_REG_DOORBELL);
list_del(&ptr->list);
kfree(ptr->pprov_data);
kfree(ptr);
}
msleep(10);
}
DEBUG("DSP Provisioning List Entry finished\n");
msleep(100);
info->fProvComplete = 1;
info->CardReady = 1;
info->DSP_loading= 0;
return STATUS_SUCCESS;
}
int ft1000_proc_drvmsg (struct ft1000_device *dev, u16 size) {
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev->net);
u16 msgtype;
u16 tempword;
PMEDIAMSG pmediamsg;
PDSPINITMSG pdspinitmsg;
PDRVMSG pdrvmsg;
u16 i;
PPSEUDO_HDR ppseudo_hdr;
PUSHORT pmsg;
u16 status;
//struct timeval tv; //mbelian
union {
u8 byte[2];
u16 wrd;
} convert;
char cmdbuffer[1600];
status = ft1000_read_dpram32(dev, 0x200, (PUCHAR)&cmdbuffer[0], size);
//if (ft1000_receive_cmd(dev, &cmdbuffer[0], MAX_CMD_SQSIZE, &tempword))
{
#ifdef JDEBUG
DEBUG("ft1000_proc_drvmsg:cmdbuffer\n");
for(i = 0; i < size; i+=5)
{
if( (i + 5) < size )
DEBUG("0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", cmdbuffer[i], cmdbuffer[i+1], cmdbuffer[i+2], cmdbuffer[i+3], cmdbuffer[i+4]);
else
{
for (j = i; j < size; j++)
DEBUG("0x%x ", cmdbuffer[j]);
DEBUG("\n");
break;
}
}
#endif
pdrvmsg = (PDRVMSG)&cmdbuffer[2];
msgtype = ntohs(pdrvmsg->type);
DEBUG("ft1000_proc_drvmsg:Command message type = 0x%x\n", msgtype);
switch (msgtype) {
case MEDIA_STATE: {
DEBUG("ft1000_proc_drvmsg:Command message type = MEDIA_STATE");
pmediamsg = (PMEDIAMSG)&cmdbuffer[0];
if (info->ProgConStat != 0xFF) {
if (pmediamsg->state) {
DEBUG("Media is up\n");
if (info->mediastate == 0) {
if ( info->NetDevRegDone )
{
//netif_carrier_on(dev->net);//mbelian
netif_wake_queue(dev->net);
}
info->mediastate = 1;
/*do_gettimeofday(&tv);
info->ConTm = tv.tv_sec;*/ //mbelian
}
}
else {
DEBUG("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
if ( info->NetDevRegDone )
{
//netif_carrier_off(dev->net); mbelian
//netif_stop_queue(dev->net);
}
info->ConTm = 0;
}
}
}
else {
DEBUG("Media is down\n");
if (info->mediastate == 1) {
info->mediastate = 0;
if ( info->NetDevRegDone)
{
//netif_carrier_off(dev->net); //mbelian
//netif_stop_queue(dev->net);
}
info->ConTm = 0;
}
}
break;
}
case DSP_INIT_MSG: {
DEBUG("ft1000_proc_drvmsg:Command message type = DSP_INIT_MSG");
pdspinitmsg = (PDSPINITMSG)&cmdbuffer[2];
memcpy(info->DspVer, pdspinitmsg->DspVer, DSPVERSZ);
DEBUG("DSPVER = 0x%2x 0x%2x 0x%2x 0x%2x\n", info->DspVer[0], info->DspVer[1], info->DspVer[2], info->DspVer[3]);
memcpy(info->HwSerNum, pdspinitmsg->HwSerNum, HWSERNUMSZ);
memcpy(info->Sku, pdspinitmsg->Sku, SKUSZ);
memcpy(info->eui64, pdspinitmsg->eui64, EUISZ);
DEBUG("EUI64=%2x.%2x.%2x.%2x.%2x.%2x.%2x.%2x\n", info->eui64[0],info->eui64[1], info->eui64[2], info->eui64[3], info->eui64[4], info->eui64[5],info->eui64[6], info->eui64[7]);
dev->net->dev_addr[0] = info->eui64[0];
dev->net->dev_addr[1] = info->eui64[1];
dev->net->dev_addr[2] = info->eui64[2];
dev->net->dev_addr[3] = info->eui64[5];
dev->net->dev_addr[4] = info->eui64[6];
dev->net->dev_addr[5] = info->eui64[7];
if (ntohs(pdspinitmsg->length) == (sizeof(DSPINITMSG) - 20) ) {
memcpy(info->ProductMode, pdspinitmsg->ProductMode, MODESZ);
memcpy(info->RfCalVer, pdspinitmsg->RfCalVer, CALVERSZ);
memcpy(info->RfCalDate, pdspinitmsg->RfCalDate, CALDATESZ);
DEBUG("RFCalVer = 0x%2x 0x%2x\n", info->RfCalVer[0], info->RfCalVer[1]);
}
break;
}
case DSP_PROVISION: {
DEBUG("ft1000_proc_drvmsg:Command message type = DSP_PROVISION\n");
// kick off dspprov routine to start provisioning
// Send provisioning data to DSP
if (list_empty(&info->prov_list) == 0)
{
info->fProvComplete = 0;
status = ft1000_dsp_prov(dev);
if (status != STATUS_SUCCESS)
return status;
}
else {
info->fProvComplete = 1;
status = ft1000_write_register (dev, FT1000_DB_HB, FT1000_REG_DOORBELL);
DEBUG("FT1000:drivermsg:No more DSP provisioning data in dsp image\n");
}
DEBUG("ft1000_proc_drvmsg:DSP PROVISION is done\n");
break;
}
case DSP_STORE_INFO: {
DEBUG("ft1000_proc_drvmsg:Command message type = DSP_STORE_INFO");
DEBUG("FT1000:drivermsg:Got DSP_STORE_INFO\n");
tempword = ntohs(pdrvmsg->length);
info->DSPInfoBlklen = tempword;
if (tempword < (MAX_DSP_SESS_REC-4) ) {
pmsg = (PUSHORT)&pdrvmsg->data[0];
for (i=0; i<((tempword+1)/2); i++) {
DEBUG("FT1000:drivermsg:dsp info data = 0x%x\n", *pmsg);
info->DSPInfoBlk[i+10] = *pmsg++;
}
}
else {
info->DSPInfoBlklen = 0;
}
break;
}
case DSP_GET_INFO: {
DEBUG("FT1000:drivermsg:Got DSP_GET_INFO\n");
// copy dsp info block to dsp
info->DrvMsgPend = 1;
// allow any outstanding ioctl to finish
mdelay(10);
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
break;
}
}
}
// Put message into Slow Queue
// Form Pseudo header
pmsg = (PUSHORT)info->DSPInfoBlk;
*pmsg++ = 0;
*pmsg++ = htons(info->DSPInfoBlklen+20+info->DSPInfoBlklen);
ppseudo_hdr = (PPSEUDO_HDR)(PUSHORT)&info->DSPInfoBlk[2];
ppseudo_hdr->length = htons(info->DSPInfoBlklen+4+info->DSPInfoBlklen);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
// Insert application id
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
for (i=1; i<7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
}
info->DSPInfoBlk[10] = 0x7200;
info->DSPInfoBlk[11] = htons(info->DSPInfoBlklen);
status = ft1000_write_dpram32 (dev, 0, (PUCHAR)&info->DSPInfoBlk[0], (unsigned short)(info->DSPInfoBlklen+22));
status = ft1000_write_register (dev, FT1000_DB_DPRAM_TX, FT1000_REG_DOORBELL);
info->DrvMsgPend = 0;
break;
}
case GET_DRV_ERR_RPT_MSG: {
DEBUG("FT1000:drivermsg:Got GET_DRV_ERR_RPT_MSG\n");
// copy driver error message to dsp
info->DrvMsgPend = 1;
// allow any outstanding ioctl to finish
mdelay(10);
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
status = ft1000_read_register(dev, &tempword, FT1000_REG_DOORBELL);
if (tempword & FT1000_DB_DPRAM_TX) {
mdelay(10);
}
}
if ( (tempword & FT1000_DB_DPRAM_TX) == 0) {
// Put message into Slow Queue
// Form Pseudo header
pmsg = (PUSHORT)&tempbuffer[0];
ppseudo_hdr = (PPSEUDO_HDR)pmsg;
ppseudo_hdr->length = htons(0x0012);
ppseudo_hdr->source = 0x10;
ppseudo_hdr->destination = 0x20;
ppseudo_hdr->portdest = 0;
ppseudo_hdr->portsrc = 0;
ppseudo_hdr->sh_str_id = 0;
ppseudo_hdr->control = 0;
ppseudo_hdr->rsvd1 = 0;
ppseudo_hdr->rsvd2 = 0;
ppseudo_hdr->qos_class = 0;
// Insert slow queue sequence number
ppseudo_hdr->seq_num = info->squeseqnum++;
// Insert application id
ppseudo_hdr->portsrc = 0;
// Calculate new checksum
ppseudo_hdr->checksum = *pmsg++;
for (i=1; i<7; i++) {
ppseudo_hdr->checksum ^= *pmsg++;
}
pmsg = (PUSHORT)&tempbuffer[16];
*pmsg++ = htons(RSP_DRV_ERR_RPT_MSG);
*pmsg++ = htons(0x000e);
*pmsg++ = htons(info->DSP_TIME[0]);
*pmsg++ = htons(info->DSP_TIME[1]);
*pmsg++ = htons(info->DSP_TIME[2]);
*pmsg++ = htons(info->DSP_TIME[3]);
convert.byte[0] = info->DspVer[0];
convert.byte[1] = info->DspVer[1];
*pmsg++ = convert.wrd;
convert.byte[0] = info->DspVer[2];
convert.byte[1] = info->DspVer[3];
*pmsg++ = convert.wrd;
*pmsg++ = htons(info->DrvErrNum);
CardSendCommand (dev, (unsigned char*)&tempbuffer[0], (USHORT)(0x0012 + PSEUDOSZ));
info->DrvErrNum = 0;
}
info->DrvMsgPend = 0;
break;
}
default:
break;
}
}
DEBUG("return from ft1000_proc_drvmsg\n");
return STATUS_SUCCESS;
}
int ft1000_poll(void* dev_id) {
//FT1000_INFO *info = (PFT1000_INFO)((struct net_device *)dev_id)->priv;
//struct ft1000_device *dev = (struct ft1000_device *)info->pFt1000Dev;
struct ft1000_device *dev = (struct ft1000_device *)dev_id;
FT1000_INFO *info = (FT1000_INFO *) netdev_priv (dev->net);
u16 tempword;
u16 status;
u16 size;
int i;
USHORT data;
USHORT modulo;
USHORT portid;
u16 nxtph;
PDPRAM_BLK pdpram_blk;
PPSEUDO_HDR ppseudo_hdr;
unsigned long flags;
//DEBUG("Enter ft1000_poll...\n");
if (ft1000_chkcard(dev) == FALSE) {
DEBUG("ft1000_poll::ft1000_chkcard: failed\n");
return STATUS_FAILURE;
}
status = ft1000_read_register (dev, &tempword, FT1000_REG_DOORBELL);
// DEBUG("ft1000_poll: read FT1000_REG_DOORBELL message 0x%x\n", tempword);
//while ( (tempword) && (!status) ) {
if ( !status )
{
if (tempword & FT1000_DB_DPRAM_RX) {
//DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX\n");
status = ft1000_read_dpram16(dev, 0x200, (PUCHAR)&data, 0);
//DEBUG("ft1000_poll:FT1000_DB_DPRAM_RX:ft1000_read_dpram16:size = 0x%x\n", data);
size = ntohs(data) + 16 + 2; //wai
if (size % 4) {
modulo = 4 - (size % 4);
size = size + modulo;
}
status = ft1000_read_dpram16(dev, 0x201, (PUCHAR)&portid, 1);
portid &= 0xff;
//DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid 0x%x\n", portid);
if (size < MAX_CMD_SQSIZE) {
switch (portid)
{
case DRIVERID:
DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid DRIVERID\n");
status = ft1000_proc_drvmsg (dev, size);
if (status != STATUS_SUCCESS )
return status;
break;
case DSPBCMSGID:
// This is a dsp broadcast message
// Check which application has registered for dsp broadcast messages
//DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DB_DPRAM_RX : portid DSPBCMSGID\n");
for (i=0; i<MAX_NUM_APP; i++) {
if ( (info->app_info[i].DspBCMsgFlag) && (info->app_info[i].fileobject) &&
(info->app_info[i].NumOfMsg < MAX_MSG_LIMIT) )
{
//DEBUG("Dsp broadcast message detected for app id %d\n", i);
nxtph = FT1000_DPRAM_RX_BASE + 2;
pdpram_blk = ft1000_get_buffer (&freercvpool);
if (pdpram_blk != NULL) {
if ( ft1000_receive_cmd(dev, pdpram_blk->pbuffer, MAX_CMD_SQSIZE, &nxtph) ) {
ppseudo_hdr = (PPSEUDO_HDR)pdpram_blk->pbuffer;
// Put message into the appropriate application block
info->app_info[i].nRxMsg++;
spin_lock_irqsave(&free_buff_lock, flags);
list_add_tail(&pdpram_blk->list, &info->app_info[i].app_sqlist);
info->app_info[i].NumOfMsg++;
spin_unlock_irqrestore(&free_buff_lock, flags);
wake_up_interruptible(&info->app_info[i].wait_dpram_msg);
}
else {
info->app_info[i].nRxMsgMiss++;
// Put memory back to free pool
ft1000_free_buffer(pdpram_blk, &freercvpool);
DEBUG("pdpram_blk::ft1000_get_buffer NULL\n");
}
}
else {
DEBUG("Out of memory in free receive command pool\n");
info->app_info[i].nRxMsgMiss++;
}//endof if (pdpram_blk != NULL)
}//endof if
//else
// DEBUG("app_info mismatch\n");
}// endof for
break;
default:
pdpram_blk = ft1000_get_buffer (&freercvpool);
//DEBUG("Memory allocated = 0x%8x\n", (u32)pdpram_blk);
if (pdpram_blk != NULL) {
if ( ft1000_receive_cmd(dev, pdpram_blk->pbuffer, MAX_CMD_SQSIZE, &nxtph) ) {
ppseudo_hdr = (PPSEUDO_HDR)pdpram_blk->pbuffer;
// Search for correct application block
for (i=0; i<MAX_NUM_APP; i++) {
if (info->app_info[i].app_id == ppseudo_hdr->portdest) {
break;
}
}
if (i==(MAX_NUM_APP-1)) { // aelias [+] reason: was out of array boundary
info->app_info[i].nRxMsgMiss++;
DEBUG("FT1000:ft1000_parse_dpram_msg: No application matching id = %d\n", ppseudo_hdr->portdest);
// Put memory back to free pool
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
else {
if (info->app_info[i].NumOfMsg > MAX_MSG_LIMIT) {
// Put memory back to free pool
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
else {
info->app_info[i].nRxMsg++;
// Put message into the appropriate application block
//pxu spin_lock_irqsave(&free_buff_lock, flags);
list_add_tail(&pdpram_blk->list, &info->app_info[i].app_sqlist);
info->app_info[i].NumOfMsg++;
//pxu spin_unlock_irqrestore(&free_buff_lock, flags);
//pxu wake_up_interruptible(&info->app_info[i].wait_dpram_msg);
}
}
}
else {
// Put memory back to free pool
ft1000_free_buffer(pdpram_blk, &freercvpool);
}
}
else {
DEBUG("Out of memory in free receive command pool\n");
}
break;
} //end of switch
} //endof if (size < MAX_CMD_SQSIZE)
else {
DEBUG("FT1000:dpc:Invalid total length for SlowQ = %d\n", size);
}
status = ft1000_write_register (dev, FT1000_DB_DPRAM_RX, FT1000_REG_DOORBELL);
}
else if (tempword & FT1000_DSP_ASIC_RESET) {
//DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DSP_ASIC_RESET\n");
// Let's reset the ASIC from the Host side as well
status = ft1000_write_register (dev, ASIC_RESET_BIT, FT1000_REG_RESET);
status = ft1000_read_register (dev, &tempword, FT1000_REG_RESET);
i = 0;
while (tempword & ASIC_RESET_BIT) {
status = ft1000_read_register (dev, &tempword, FT1000_REG_RESET);
msleep(10);
i++;
if (i==100)
break;
}
if (i==100) {
DEBUG("Unable to reset ASIC\n");
return STATUS_SUCCESS;
}
msleep(10);
// Program WMARK register
status = ft1000_write_register (dev, 0x600, FT1000_REG_MAG_WATERMARK);
// clear ASIC reset doorbell
status = ft1000_write_register (dev, FT1000_DSP_ASIC_RESET, FT1000_REG_DOORBELL);
msleep(10);
}
else if (tempword & FT1000_ASIC_RESET_REQ) {
DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_ASIC_RESET_REQ\n");
// clear ASIC reset request from DSP
status = ft1000_write_register (dev, FT1000_ASIC_RESET_REQ, FT1000_REG_DOORBELL);
status = ft1000_write_register (dev, HOST_INTF_BE, FT1000_REG_SUP_CTRL);
// copy dsp session record from Adapter block
status = ft1000_write_dpram32 (dev, 0, (PUCHAR)&info->DSPSess.Rec[0], 1024);
// Program WMARK register
status = ft1000_write_register (dev, 0x600, FT1000_REG_MAG_WATERMARK);
// ring doorbell to tell DSP that ASIC is out of reset
status = ft1000_write_register (dev, FT1000_ASIC_RESET_DSP, FT1000_REG_DOORBELL);
}
else if (tempword & FT1000_DB_COND_RESET) {
DEBUG("ft1000_poll: FT1000_REG_DOORBELL message type: FT1000_DB_COND_RESET\n");
//By Jim
// Reset ASIC and DSP
//MAG
if (info->fAppMsgPend == 0) {
// Reset ASIC and DSP
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER0, (PUCHAR)&(info->DSP_TIME[0]), FT1000_MAG_DSP_TIMER0_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER1, (PUCHAR)&(info->DSP_TIME[1]), FT1000_MAG_DSP_TIMER1_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER2, (PUCHAR)&(info->DSP_TIME[2]), FT1000_MAG_DSP_TIMER2_INDX);
status = ft1000_read_dpram16(dev, FT1000_MAG_DSP_TIMER3, (PUCHAR)&(info->DSP_TIME[3]), FT1000_MAG_DSP_TIMER3_INDX);
info->CardReady = 0;
info->DrvErrNum = DSP_CONDRESET_INFO;
DEBUG("ft1000_hw:DSP conditional reset requested\n");
info->ft1000_reset(dev->net);
}
else {
info->fProvComplete = 0;
info->fCondResetPend = 1;
}
ft1000_write_register(dev, FT1000_DB_COND_RESET, FT1000_REG_DOORBELL);
}
}//endof if ( !status )
//DEBUG("return from ft1000_poll.\n");
return STATUS_SUCCESS;
}
/*end of Jim*/
#ifndef _FT1000_HW_H_
#define _FT1000_HW_H_
#include "ft1000_usb.h"
extern u16 ft1000_read_register(struct usb_device *dev, PUSHORT Data, u8 nRegIndx);
extern u16 ft1000_write_register(struct usb_device *dev, USHORT value, u8 nRegIndx);
#endif
//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// File: ft1000_ioctl.h
//
// Description: Common structures and defines relating to IOCTL
//
// History:
// 11/5/02 Whc Created.
//
//---------------------------------------------------------------------------//---------------------------------------------------------------------------
#ifndef _FT1000IOCTLH_
#define _FT1000IOCTLH_
#define DSPVERSZ 4
#define HWSERNUMSZ 16
#define SKUSZ 20
#define EUISZ 8
#define CALVERSZ 2
#define CALDATESZ 6
#define MAX_DNLD_BLKSZ 1024
// Standard Flarion Pseudo header
typedef struct _PSEUDO_HDR
{
unsigned short length; //length of msg body
unsigned char source; //source address (0x10=Host 0x20=DSP)
unsigned char destination; //destination address (refer to source address)
unsigned char portdest; //destination port id
// 0x00=Driver
// 0x10=Application Broadcast
// 0x20=Network Stack
// 0x80=Dsp OAM
// 0x90=Dsp Airlink
// 0xa0=Dsp Loader
// 0xb0=Dsp MIP
unsigned char portsrc; //source port id (refer to portdest)
unsigned short sh_str_id; //stream id (Not applicable on Mobile)
unsigned char control; //stream id (Not applicable on Mobile)
unsigned char rsvd1; //reserved
unsigned char seq_num; //sequence number
unsigned char rsvd2; //reserved
unsigned short qos_class; //Quality of Service class (Not applicable on Mobile)
unsigned short checksum; //Psuedo header checksum
} __attribute__ ((packed)) PSEUDO_HDR, *PPSEUDO_HDR;
typedef struct _IOCTL_GET_VER
{
unsigned long drv_ver;
} __attribute__ ((packed)) IOCTL_GET_VER, *PIOCTL_GET_VER;
//Data structure for Dsp statistics
typedef struct _IOCTL_GET_DSP_STAT
{
unsigned char DspVer[DSPVERSZ]; // DSP version number
unsigned char HwSerNum[HWSERNUMSZ]; // Hardware Serial Number
unsigned char Sku[SKUSZ]; // SKU
unsigned char eui64[EUISZ]; // EUI64
unsigned short ConStat; // Connection Status
// Bits 0-3 = Connection Status Field
// 0000=Idle (Disconnect)
// 0001=Searching
// 0010=Active (Connected)
// 0011=Waiting for L2 down
// 0100=Sleep
unsigned short LedStat; // Led Status
// Bits 0-3 = Signal Strength Field
// 0000 = -105dBm to -92dBm
// 0001 = -92dBm to -85dBm
// 0011 = -85dBm to -75dBm
// 0111 = -75dBm to -50dBm
// 1111 = -50dBm to 0dBm
// Bits 4-7 = Reserved
// Bits 8-11 = SNR Field
// 0000 = <2dB
// 0001 = 2dB to 8dB
// 0011 = 8dB to 15dB
// 0111 = 15dB to 22dB
// 1111 = >22dB
// Bits 12-15 = Reserved
unsigned long nTxPkts; // Number of packets transmitted from host to dsp
unsigned long nRxPkts; // Number of packets received from dsp to host
unsigned long nTxBytes; // Number of bytes transmitted from host to dsp
unsigned long nRxBytes; // Number of bytes received from dsp to host
unsigned long ConTm; // Current session connection time in seconds
unsigned char CalVer[CALVERSZ]; // Proprietary Calibration Version
unsigned char CalDate[CALDATESZ]; // Proprietary Calibration Date
} __attribute__ ((packed)) IOCTL_GET_DSP_STAT, *PIOCTL_GET_DSP_STAT;
//Data structure for Dual Ported RAM messaging between Host and Dsp
typedef struct _IOCTL_DPRAM_BLK
{
unsigned short total_len;
PSEUDO_HDR pseudohdr;
unsigned char buffer[1780];
} __attribute__ ((packed)) IOCTL_DPRAM_BLK, *PIOCTL_DPRAM_BLK;
typedef struct _IOCTL_DPRAM_COMMAND
{
unsigned short extra;
IOCTL_DPRAM_BLK dpram_blk;
} __attribute__ ((packed)) IOCTL_DPRAM_COMMAND, *PIOCTL_DPRAM_COMMAND;
//
// Custom IOCTL command codes
//
#define FT1000_MAGIC_CODE 'F'
#define IOCTL_REGISTER_CMD 0
#define IOCTL_SET_DPRAM_CMD 3
#define IOCTL_GET_DPRAM_CMD 4
#define IOCTL_GET_DSP_STAT_CMD 6
#define IOCTL_GET_VER_CMD 7
#define IOCTL_CONNECT 10
#define IOCTL_DISCONNECT 11
#define IOCTL_FT1000_GET_DSP_STAT _IOR (FT1000_MAGIC_CODE, IOCTL_GET_DSP_STAT_CMD, sizeof(IOCTL_GET_DSP_STAT) )
#define IOCTL_FT1000_GET_VER _IOR (FT1000_MAGIC_CODE, IOCTL_GET_VER_CMD, sizeof(IOCTL_GET_VER) )
#define IOCTL_FT1000_CONNECT _IOW (FT1000_MAGIC_CODE, IOCTL_CONNECT, 0 )
#define IOCTL_FT1000_DISCONNECT _IOW (FT1000_MAGIC_CODE, IOCTL_DISCONNECT, 0 )
#define IOCTL_FT1000_SET_DPRAM _IOW (FT1000_MAGIC_CODE, IOCTL_SET_DPRAM_CMD, sizeof(IOCTL_DPRAM_BLK) )
#define IOCTL_FT1000_GET_DPRAM _IOR (FT1000_MAGIC_CODE, IOCTL_GET_DPRAM_CMD, sizeof(IOCTL_DPRAM_BLK) )
#define IOCTL_FT1000_REGISTER _IOW (FT1000_MAGIC_CODE, IOCTL_REGISTER_CMD, sizeof(unsigned short *) )
#endif // _FT1000IOCTLH_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include "ft1000_usb.h"
#define FT1000_PROC_DIR "ft1000"
#define PUTM_TO_PAGE(len,page,args...) \
len += snprintf(page+len, PAGE_SIZE - len, args)
#define PUTX_TO_PAGE(len,page,message,size,var) \
len += snprintf(page+len, PAGE_SIZE - len, message); \
for(i = 0; i < (size - 1); i++) \
{ \
len += snprintf(page+len, PAGE_SIZE - len, "%02x:", var[i]); \
} \
len += snprintf(page+len, PAGE_SIZE - len, "%02x\n", var[i])
#define PUTD_TO_PAGE(len,page,message,size,var) \
len += snprintf(page+len, PAGE_SIZE - len, message); \
for(i = 0; i < (size - 1); i++) \
{ \
len += snprintf(page+len, PAGE_SIZE - len, "%d.", var[i]); \
} \
len += snprintf(page+len, PAGE_SIZE - len, "%d\n", var[i])
#ifdef INIT_NET_NS
#define FTNET_PROC init_net.proc_net
#else
#define FTNET_PROC proc_net
#endif
u16 ft1000_read_dpram16 (struct ft1000_device *ft1000dev, USHORT indx,
PUCHAR buffer, u8 highlow);
int
ft1000ReadProc (char *page, char **start, off_t off, int count, int *eof,
void *data)
{
struct net_device *dev;
int len;
int i;
unsigned short ledStat;
unsigned short conStat;
FT1000_INFO *info;
char *status[] = { "Idle (Disconnect)", "Searching", "Active (Connected)",
"Waiting for L2", "Sleep", "No Coverage", "", ""
};
char *signal[] = { "", "*", "**", "***", "****" };
int strength;
int quality;
struct timeval tv;
time_t delta;
dev = (struct net_device *) data;
info = (FT1000_INFO *) netdev_priv (dev);
if (off > 0)
{
*eof = 1;
return 0;
}
if (info->ProgConStat != 0xFF)
{
ft1000_read_dpram16 (info->pFt1000Dev, FT1000_MAG_DSP_LED,
(PUCHAR) & ledStat, FT1000_MAG_DSP_LED_INDX);
info->LedStat = ntohs (ledStat);
ft1000_read_dpram16 (info->pFt1000Dev, FT1000_MAG_DSP_CON_STATE,
(PUCHAR) & conStat, FT1000_MAG_DSP_CON_STATE_INDX);
info->ConStat = ntohs (conStat);
do_gettimeofday (&tv);
delta = (tv.tv_sec - info->ConTm);
}
else
{
info->ConStat = 0xf;
delta = 0;
}
i = (info->LedStat) & 0xf;
switch (i)
{
case 0x1:
strength = 1;
break;
case 0x3:
strength = 2;
break;
case 0x7:
strength = 3;
break;
case 0xf:
strength = 4;
break;
default:
strength = 0;
}
i = (info->LedStat >> 8) & 0xf;
switch (i)
{
case 0x1:
quality = 1;
break;
case 0x3:
quality = 2;
break;
case 0x7:
quality = 3;
break;
case 0xf:
quality = 4;
break;
default:
quality = 0;
}
len = 0;
PUTM_TO_PAGE (len, page, "Connection Time: %02ld:%02ld:%02ld\n",
((delta / 3600) % 24), ((delta / 60) % 60), (delta % 60));
PUTM_TO_PAGE (len, page, "Connection Time[s]: %ld\n", delta);
PUTM_TO_PAGE (len, page, "Asic ID: %s\n",
(info->AsicID) ==
ELECTRABUZZ_ID ? "ELECTRABUZZ ASIC" : "MAGNEMITE ASIC");
PUTX_TO_PAGE (len, page, "SKU: ", SKUSZ, info->Sku);
PUTX_TO_PAGE (len, page, "EUI64: ", EUISZ, info->eui64);
PUTD_TO_PAGE (len, page, "DSP version number: ", DSPVERSZ, info->DspVer);
PUTX_TO_PAGE (len, page, "Hardware Serial Number: ", HWSERNUMSZ,
info->HwSerNum);
PUTX_TO_PAGE (len, page, "Caliberation Version: ", CALVERSZ,
info->RfCalVer);
PUTD_TO_PAGE (len, page, "Caliberation Date: ", CALDATESZ, info->RfCalDate);
PUTM_TO_PAGE (len, page, "Media State: %s\n",
(info->mediastate) ? "link" : "no link");
PUTM_TO_PAGE (len, page, "Connection Status: %s\n",
status[((info->ConStat) & 0x7)]);
PUTM_TO_PAGE (len, page, "RX packets: %ld\n", info->stats.rx_packets);
PUTM_TO_PAGE (len, page, "TX packets: %ld\n", info->stats.tx_packets);
PUTM_TO_PAGE (len, page, "RX bytes: %ld\n", info->stats.rx_bytes);
PUTM_TO_PAGE (len, page, "TX bytes: %ld\n", info->stats.tx_bytes);
PUTM_TO_PAGE (len, page, "Signal Strength: %s\n", signal[strength]);
PUTM_TO_PAGE (len, page, "Signal Quality: %s\n", signal[quality]);
return len;
}
static int
ft1000NotifyProc (struct notifier_block *this, unsigned long event, void *ptr)
{
struct net_device *dev = ptr;
FT1000_INFO *info;
struct proc_dir_entry *ft1000_proc_file;
info = (FT1000_INFO *) netdev_priv (dev);
switch (event)
{
case NETDEV_CHANGENAME:
remove_proc_entry (info->netdevname, info->ft1000_proc_dir);
ft1000_proc_file = create_proc_read_entry (dev->name, 0644,
info->ft1000_proc_dir,
ft1000ReadProc, dev);
snprintf (info->netdevname, IFNAMSIZ, "%s", dev->name);
break;
}
return NOTIFY_DONE;
}
static struct notifier_block ft1000_netdev_notifier = {
.notifier_call = ft1000NotifyProc
};
void
ft1000InitProc (struct net_device *dev)
{
FT1000_INFO *info;
struct proc_dir_entry *ft1000_proc_file;
info = (FT1000_INFO *) netdev_priv (dev);
info->ft1000_proc_dir = proc_mkdir (FT1000_PROC_DIR, FTNET_PROC);
if (info->ft1000_proc_dir == NULL)
{
remove_proc_entry (FT1000_PROC_DIR, FTNET_PROC);
}
ft1000_proc_file =
create_proc_read_entry (dev->name, 0644, info->ft1000_proc_dir,
ft1000ReadProc, dev);
if (ft1000_proc_file == NULL)
{
remove_proc_entry (info->netdevname, info->ft1000_proc_dir);
}
snprintf (info->netdevname, IFNAMSIZ, "%s", dev->name);
register_netdevice_notifier (&ft1000_netdev_notifier);
return;
}
void
ft1000CleanupProc (FT1000_INFO * info)
{
remove_proc_entry (info->netdevname, info->ft1000_proc_dir);
remove_proc_entry (FT1000_PROC_DIR, FTNET_PROC);
unregister_netdevice_notifier (&ft1000_netdev_notifier);
return;
}
//=====================================================
// CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved.
//
//
// This file is part of Express Card USB Driver
//
// $Id:
//====================================================
// 20090926; aelias; removed all compiler warnings; ubuntu 9.04; 2.6.28-15-generic
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include "ft1000_usb.h"
//#include <linux/sched.h>
//#include <linux/ptrace.h>
//#include <linux/slab.h>
//#include <linux/string.h>
//#include <linux/timer.h>
//#include <linux/netdevice.h>
//#include <linux/ioport.h>
//#include <linux/delay.h>
//#include <asm/io.h>
//#include <asm/system.h>
#include <linux/kthread.h>
MODULE_DESCRIPTION("FT1000 EXPRESS CARD DRIVER");
MODULE_LICENSE("Dual MPL/GPL");
MODULE_SUPPORTED_DEVICE("QFT FT1000 Express Cards");
void *pFileStart;
ULONG FileLength;
#define VENDOR_ID 0x1291 /* Qualcomm vendor id */
#define PRODUCT_ID 0x11 /* fake product id */
/* table of devices that work with this driver */
static struct usb_device_id id_table[] = {
{USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
{ },
};
MODULE_DEVICE_TABLE (usb, id_table);
extern struct ft1000_device *pdevobj[MAX_NUM_CARDS+2];
char *getfw (char *fn, int *pimgsz);
int ft1000_close(struct net_device *net);
void dsp_reload (struct ft1000_device *ft1000dev);
u16 init_ft1000_netdev(struct ft1000_device *ft1000dev);
u16 reg_ft1000_netdev(struct ft1000_device *ft1000dev, struct usb_interface *intf);
int ft1000_poll(void* dev_id);
void ft1000_DestroyDevice(struct net_device *dev);
u16 ft1000_read_dpram16(struct ft1000_device *ft1000dev, USHORT indx, PUCHAR buffer, u8 highlow);
u16 ft1000_read_register(struct ft1000_device *ft1000dev, short* Data, u16 nRegIndx);
BOOLEAN gPollingfailed = FALSE;
void ft1000InitProc(struct net_device *dev);
void ft1000CleanupProc(FT1000_INFO *info);
int ft1000_poll_thread(void *arg);
int ft1000_poll_thread(void *arg)
{
int ret = STATUS_SUCCESS;
while(!kthread_should_stop() )
{
msleep(10);
if ( ! gPollingfailed )
{
ret = ft1000_poll(arg);
if ( ret != STATUS_SUCCESS )
{
DEBUG("ft1000_poll_thread: polling failed\n");
gPollingfailed = TRUE;
}
}
}
//DEBUG("returned from polling thread\n");
return STATUS_SUCCESS;
}
//---------------------------------------------------------------------------
// Function: ft1000_probe
//
// Parameters: struct usb_interface *interface - passed by USB core
// struct usb_device_id *id - passed by USB core
// Returns: 0 - success
//
// Description: This function is invoked when the express card is plugged in
//
// Notes:
//
//---------------------------------------------------------------------------
static int ft1000_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *dev;
unsigned numaltsetting;
int i;
struct ft1000_device *ft1000dev;
FT1000_INFO *pft1000info;
if(!(ft1000dev = kmalloc(sizeof(struct ft1000_device), GFP_KERNEL)))
{
printk("out of memory allocating device structure\n");
return 0;
}
memset(ft1000dev, 0, sizeof(*ft1000dev));
//get usb device
dev = interface_to_usbdev(interface);
DEBUG("ft1000_probe: usb device descriptor info:\n");
DEBUG("ft1000_probe: number of configuration is %d\n", dev->descriptor.bNumConfigurations);
ft1000dev->dev = dev;
ft1000dev->status = 0;
ft1000dev->net = NULL;
//ft1000dev->device_lock = SPIN_LOCK_UNLOCKED;
spin_lock_init(&ft1000dev->device_lock);
ft1000dev->tx_urb = usb_alloc_urb(0, GFP_ATOMIC);
ft1000dev->rx_urb = usb_alloc_urb(0, GFP_ATOMIC);
DEBUG("ft1000_probe is called\n");
numaltsetting = interface->num_altsetting;
DEBUG("ft1000_probe: number of alt settings is :%d\n",numaltsetting);
iface_desc = interface->cur_altsetting;
DEBUG("ft1000_probe: number of endpoints is %d\n", iface_desc->desc.bNumEndpoints);
DEBUG("ft1000_probe: descriptor type is %d\n", iface_desc->desc.bDescriptorType);
DEBUG("ft1000_probe: interface number is %d\n", iface_desc->desc.bInterfaceNumber);
DEBUG("ft1000_probe: alternatesetting is %d\n", iface_desc->desc.bAlternateSetting);
DEBUG("ft1000_probe: interface class is %d\n", iface_desc->desc.bInterfaceClass);
DEBUG("ft1000_probe: control endpoint info:\n");
DEBUG("ft1000_probe: descriptor0 type -- %d\n", iface_desc->endpoint[0].desc.bmAttributes);
DEBUG("ft1000_probe: descriptor1 type -- %d\n", iface_desc->endpoint[1].desc.bmAttributes);
DEBUG("ft1000_probe: descriptor2 type -- %d\n", iface_desc->endpoint[2].desc.bmAttributes);
for (i=0; i< iface_desc->desc.bNumEndpoints;i++ )
{
endpoint = (struct usb_endpoint_descriptor *)&iface_desc->endpoint[i].desc;
DEBUG("endpoint %d\n", i);
DEBUG("bEndpointAddress=%x, bmAttributes=%x\n", endpoint->bEndpointAddress, endpoint->bmAttributes);
if ( (endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
{
ft1000dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
DEBUG("ft1000_probe: in: %d\n", endpoint->bEndpointAddress);
}
if (!(endpoint->bEndpointAddress & USB_DIR_IN) && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
{
ft1000dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
DEBUG("ft1000_probe: out: %d\n", endpoint->bEndpointAddress);
}
}
DEBUG("bulk_in=%d, bulk_out=%d\n", ft1000dev->bulk_in_endpointAddr, ft1000dev->bulk_out_endpointAddr);
//read DSP image
pFileStart = (void*)getfw("/etc/flarion/ft3000.img", &FileLength);
if (pFileStart == NULL )
{
DEBUG ("ft1000_probe: Read DSP image failed\n");
return 0;
}
//for ( i=0; i< MAX_NUM_CARDS+2; i++)
// pdevobj[i] = NULL;
//download dsp image
DEBUG("ft1000_probe: start downloading dsp image...\n");
init_ft1000_netdev(ft1000dev);
pft1000info = (FT1000_INFO *) netdev_priv (ft1000dev->net);
// DEBUG("In probe: pft1000info=%x\n", pft1000info); // aelias [-] reason: warning: format ???%x??? expects type ???unsigned int???, but argument 2 has type ???struct FT1000_INFO *???
DEBUG("In probe: pft1000info=%x\n", (unsigned int)pft1000info); // aelias [+] reason: up
dsp_reload(ft1000dev);
gPollingfailed = FALSE; //mbelian
pft1000info->pPollThread = kthread_run(ft1000_poll_thread, ft1000dev, "ft1000_poll");
msleep(500); //mbelian
if ( pft1000info->DSP_loading )
{
DEBUG("ERROR!!!! RETURN FROM ft1000_probe **********************\n");
return 0;
}
while (!pft1000info->CardReady)
{
if ( gPollingfailed )
{
if ( pft1000info->pPollThread )
{
kthread_stop(pft1000info->pPollThread );
}
return 0;
}
msleep(100);
DEBUG("ft1000_probe::Waiting for Card Ready\n");
}
//initialize network device
DEBUG("ft1000_probe::Card Ready!!!! Registering network device\n");
reg_ft1000_netdev(ft1000dev, interface);
pft1000info->NetDevRegDone = 1;
ft1000InitProc(ft1000dev->net);// +mbelian
return 0;
}
//---------------------------------------------------------------------------
// Function: ft1000_disconnect
//
// Parameters: struct usb_interface *interface - passed by USB core
//
// Returns: 0 - success
//
// Description: This function is invoked when the express card is plugged out
//
// Notes:
//
//---------------------------------------------------------------------------
static void ft1000_disconnect(struct usb_interface *interface)
{
FT1000_INFO *pft1000info;
DEBUG("ft1000_disconnect is called\n");
pft1000info = (PFT1000_INFO)usb_get_intfdata(interface);
// DEBUG("In disconnect pft1000info=%x\n", pft1000info); // aelias [-] reason: warning: format ???%x??? expects type ???unsigned int???, but argument 2 has type ???struct FT1000_INFO *???
DEBUG("In disconnect pft1000info=%x\n", (unsigned int) pft1000info); // aelias [+] reason: up
if (pft1000info)
{
ft1000CleanupProc(pft1000info); //+mbelian
if ( pft1000info->pPollThread )
{
kthread_stop(pft1000info->pPollThread );
}
DEBUG("ft1000_disconnect: threads are terminated\n");
if (pft1000info->pFt1000Dev->net)
{
DEBUG("ft1000_disconnect: destroy char driver\n");
ft1000_DestroyDevice(pft1000info->pFt1000Dev->net);
//DEBUG("ft1000_disconnect: calling ft1000_close\n");
//ft1000_close(pft1000info->pFt1000Dev->net);
//DEBUG("ft1000_disconnect: ft1000_close is called\n");
unregister_netdev(pft1000info->pFt1000Dev->net);
DEBUG("ft1000_disconnect: network device unregisterd\n");
free_netdev(pft1000info->pFt1000Dev->net);
}
usb_free_urb(pft1000info->pFt1000Dev->rx_urb);
usb_free_urb(pft1000info->pFt1000Dev->tx_urb);
DEBUG("ft1000_disconnect: urb freed\n");
kfree(pft1000info->pFt1000Dev); //+mbelian
}
//terminate other kernel threads
//in multiple instances case, first find the device
//in the link list
/**if (pPollThread)
{
kthread_stop(pPollThread);
DEBUG("Polling thread is killed \n");
}**/
return;
}
static struct usb_driver ft1000_usb_driver = {
//.owner = THIS_MODULE,
.name = "ft1000usb",
.probe = ft1000_probe,
.disconnect = ft1000_disconnect,
.id_table = id_table,
};
//---------------------------------------------------------------------------
// Function: usb_ft1000_init
//
// Parameters: none
//
// Returns: 0 - success
//
// Description: The entry point of the module, register the usb driver
//
// Notes:
//
//---------------------------------------------------------------------------
static int __init usb_ft1000_init(void)
{
int ret = 0;
DEBUG("Initialize and register the driver\n");
ret = usb_register(&ft1000_usb_driver);
if (ret)
err("usb_register failed. Error number %d", ret);
return ret;
}
//---------------------------------------------------------------------------
// Function: usb_ft1000_exit
//
// Parameters:
//
// Returns:
//
// Description: Moudle unload function, deregister usb driver
//
// Notes:
//
//---------------------------------------------------------------------------
static void __exit usb_ft1000_exit(void)
{
DEBUG("Deregister the driver\n");
usb_deregister(&ft1000_usb_driver);
}
module_init (usb_ft1000_init);
module_exit (usb_ft1000_exit);
#ifndef _FT1000_USB_H_
#define _FT1000_USB_H_
/*Jim*/
#include "ft1000_ioctl.h"
#define FT1000_DRV_VER 0x01010403
#define MODESZ 2
#define MAX_NUM_APP 6
#define MAX_MSG_LIMIT 200
#define NUM_OF_FREE_BUFFERS 1500
// Driver message types
#define MEDIA_STATE 0x0010
#define DSP_PROVISION 0x0030
#define DSP_INIT_MSG 0x0050
#define DSP_STORE_INFO 0x0070
#define DSP_GET_INFO 0x0071
#define GET_DRV_ERR_RPT_MSG 0x0073
#define RSP_DRV_ERR_RPT_MSG 0x0074
// Size of DPRAM Command
#define MAX_CMD_SQSIZE 1780
#define SLOWQ_TYPE 0
#define PSEUDOSZ 16
#define DSP_QID_OFFSET 4
// MEMORY MAP FOR ELECTRABUZZ ASIC
#define FT1000_REG_DFIFO_STAT 0x0008 // Downlink FIFO status register
#define FT1000_REG_DPRAM_DATA 0x000C // DPRAM VALUE in DPRAM ADDR
#define FT1000_DSP_LED 0xFFA // dsp led status for PAD device
#define FT1000_MAG_DSP_LED 0x3FE // dsp led status for PAD device
#define FT1000_MAG_DSP_LED_INDX 0x1 // dsp led status for PAD device
#define SUCCESS 0x00
#define DRIVERID 0x00
// Driver Error Messages for DSP
#define DSP_CONDRESET_INFO 0x7ef2
#define DSP_HB_INFO 0x7ef0
// Magnemite specific defines
#define hi_mag 0x6968 // Byte swap hi to avoid additional system call
#define ho_mag 0x6f68 // Byte swap ho to avoid additional system call
typedef struct _MEDIAMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u16 state;
u32 ip_addr;
u32 net_mask;
u32 gateway;
u32 dns_1;
u32 dns_2;
} __attribute__ ((packed)) MEDIAMSG, *PMEDIAMSG;
typedef struct _DSPINITMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u8 DspVer[DSPVERSZ]; // DSP version number
u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number
u8 Sku[SKUSZ]; // SKU
u8 eui64[EUISZ]; // EUI64
u8 ProductMode[MODESZ]; // Product Mode (Market/Production)
u8 RfCalVer[CALVERSZ]; // Rf Calibration version
u8 RfCalDate[CALDATESZ]; // Rf Calibration date
} __attribute__ ((packed)) DSPINITMSG, *PDSPINITMSG;
typedef struct _APP_INFO_BLOCK
{
u32 nTxMsg; // DPRAM msg sent to DSP with app_id
u32 nRxMsg; // DPRAM msg rcv from dsp with app_id
u32 nTxMsgReject; // DPRAM msg rejected due to DSP doorbell set
u32 nRxMsgMiss; // DPRAM msg dropped due to overflow
u32 fileobject; // Application's file object
u16 app_id; // Application id
int DspBCMsgFlag;
int NumOfMsg; // number of messages queued up
wait_queue_head_t wait_dpram_msg;
struct list_head app_sqlist; // link list of msgs for applicaton on slow queue
} APP_INFO_BLOCK, *PAPP_INFO_BLOCK;
typedef struct _PROV_RECORD {
struct list_head list;
u8 *pprov_data;
} PROV_RECORD, *PPROV_RECORD;
/*end of Jim*/
#define DEBUG(args...) printk(KERN_INFO args)
#define UCHAR u8
#define USHORT u16
#define ULONG u32
#define BOOLEAN u8
#define PULONG u32 *
#define PUSHORT u16 *
#define PUCHAR u8 *
#define PCHAR u8 *
#define UINT u32
#define FALSE 0
#define TRUE 1
#define STATUS_SUCCESS 0
#define STATUS_FAILURE 0x1001
#define FT1000_STATUS_CLOSING 0x01
#define LARGE_TIMEOUT 5000
#define MAX_DSP_SESS_REC 1024
#define MAX_NUM_CARDS 32
#define DSPVERSZ 4
#define HWSERNUMSZ 16
#define SKUSZ 20
#define EUISZ 8
#define CALVERSZ 2
#define CALDATESZ 6
#define MODESZ 2
#define DSPID 0x20
#define HOSTID 0x10
#define DSPOAM 0x80
#define DSPAIRID 0x90
#define DRIVERID 0x00
#define FMM 0x10
#define NETWORKID 0x20
#define AUTOLNCHID 0x30
#define DSPLPBKID 0x40
#define DSPBCMSGID 0x10
#define ENET_MAX_SIZE 1514
#define ENET_HEADER_SIZE 14
#define CIS_NET_ADDR_OFFSET 0xff0
#define MEM_TAG 'FLRN'
// MAGNEMITE specific
#define FT1000_REG_MAG_UFDR 0x0000 // Uplink FIFO Data Register.
#define FT1000_REG_MAG_UFDRL 0x0000 // Uplink FIFO Data Register low-word.
#define FT1000_REG_MAG_UFDRH 0x0002 // Uplink FIFO Data Register high-word.
#define FT1000_REG_MAG_UFER 0x0004 // Uplink FIFO End Register
#define FT1000_REG_MAG_UFSR 0x0006 // Uplink FIFO Status Register
#define FT1000_REG_MAG_DFR 0x0008 // Downlink FIFO Register
#define FT1000_REG_MAG_DFRL 0x0008 // Downlink FIFO Register low-word
#define FT1000_REG_MAG_DFRH 0x000a // Downlink FIFO Register high-word
#define FT1000_REG_MAG_DFSR 0x000c // Downlink FIFO Status Register
#define FT1000_REG_MAG_DPDATA 0x0010 // Dual Port RAM Indirect Data Register
#define FT1000_REG_MAG_DPDATAL 0x0010 // Dual Port RAM Indirect Data Register low-word
#define FT1000_REG_MAG_DPDATAH 0x0012 // Dual Port RAM Indirect Data Register high-word
#define FT1000_REG_MAG_WATERMARK 0x002c // Supv. Control Reg. LLC register
#define FT1000_REG_MAG_VERSION 0x0030 // LLC Version LLC register
// Common
#define FT1000_REG_DPRAM_ADDR 0x000E // DPRAM ADDRESS when card in IO mode
#define FT1000_REG_SUP_CTRL 0x0020 // Supv. Control Reg. LLC register
#define FT1000_REG_SUP_STAT 0x0022 // Supv. Status Reg LLC register
#define FT1000_REG_RESET 0x0024 // Reset Reg LLC register
#define FT1000_REG_SUP_ISR 0x0026 // Supv ISR LLC register
#define FT1000_REG_SUP_IMASK 0x0028 // Supervisor Interrupt Mask LLC register
#define FT1000_REG_DOORBELL 0x002a // Door Bell Reg LLC register
#define FT1000_REG_ASIC_ID 0x002e // ASIC Identification Number
// (Electrabuzz=0 Magnemite=TBD)
// DSP doorbells
#define FT1000_DB_DPRAM_RX 0x0001 // this value indicates that DSP has
// data for host in DPRAM SlowQ
#define FT1000_DB_DNLD_RX 0x0002 // Downloader handshake doorbell
#define FT1000_ASIC_RESET_REQ 0x0004
#define FT1000_DSP_ASIC_RESET 0x0008
#define FT1000_DB_COND_RESET 0x0010
// Host doorbells
#define FT1000_DB_DPRAM_TX 0x0100 // this value indicates that host has
// data for DSP in DPRAM.
#define FT1000_DB_DNLD_TX 0x0200 // Downloader handshake doorbell
#define FT1000_ASIC_RESET_DSP 0x0400
#define FT1000_DB_HB 0x1000 // this value indicates that supervisor
// Electrabuzz specific DPRAM mapping // has a heartbeat message for DSP.
#define FT1000_DPRAM_BASE 0x1000 // 0x0000 to 0x07FF DPRAM 2Kx16 - R/W from PCMCIA or DSP
#define FT1000_DPRAM_TX_BASE 0x1002 // TX AREA (SlowQ)
#define FT1000_DPRAM_RX_BASE 0x1800 // RX AREA (SlowQ)
#define FT1000_DPRAM_SIZE 0x1000 // 4K bytes
#define FT1000_DRV_DEBUG 0x17E0 // Debug area for driver
#define FT1000_FIFO_LEN 0x17FC // total length for DSP FIFO tracking
#define FT1000_HI_HO 0x17FE // heartbeat with HI/HO
#define FT1000_DSP_STATUS 0x1FFE // dsp status - non-zero is a request to reset dsp
#define FT1000_DSP_CON_STATE 0x1FF8 // DSP Connection Status Info
#define FT1000_DSP_LEDS 0x1FFA // DSP LEDS for rcv pwr strength, Rx data, Tx data
#define DSP_TIMESTAMP 0x1FFC // dsp timestamp
#define DSP_TIMESTAMP_DIFF 0x1FFA // difference of dsp timestamp in DPRAM and Pseudo header.
#define FT1000_DPRAM_FEFE 0x1002 // Dsp Downloader handshake location
#define FT1000_DSP_TIMER0 0x1FF0
#define FT1000_DSP_TIMER1 0x1FF2
#define FT1000_DSP_TIMER2 0x1FF4
#define FT1000_DSP_TIMER3 0x1FF6
// MEMORY MAP FOR MAGNEMITE
#define FT1000_DPRAM_MAG_TX_BASE 0x0000 // TX AREA (SlowQ)
#define FT1000_DPRAM_MAG_RX_BASE 0x0200 // RX AREA (SlowQ)
#define FT1000_MAG_FIFO_LEN 0x1FF // total length for DSP FIFO tracking
#define FT1000_MAG_FIFO_LEN_INDX 0x1 // low-word index
#define FT1000_MAG_HI_HO 0x1FF // heartbeat with HI/HO
#define FT1000_MAG_HI_HO_INDX 0x0 // high-word index
#define FT1000_MAG_DSP_LEDS 0x3FE // dsp led status for PAD device
#define FT1000_MAG_DSP_LEDS_INDX 0x1 // dsp led status for PAD device
#define FT1000_MAG_DSP_CON_STATE 0x3FE // DSP Connection Status Info
#define FT1000_MAG_DSP_CON_STATE_INDX 0x0 // DSP Connection Status Info
#define FT1000_MAG_DPRAM_FEFE 0x000 // location for dsp ready indicator
#define FT1000_MAG_DPRAM_FEFE_INDX 0x0 // location for dsp ready indicator
#define FT1000_MAG_DSP_TIMER0 0x3FC
#define FT1000_MAG_DSP_TIMER0_INDX 0x1
#define FT1000_MAG_DSP_TIMER1 0x3FC
#define FT1000_MAG_DSP_TIMER1_INDX 0x0
#define FT1000_MAG_DSP_TIMER2 0x3FD
#define FT1000_MAG_DSP_TIMER2_INDX 0x1
#define FT1000_MAG_DSP_TIMER3 0x3FD
#define FT1000_MAG_DSP_TIMER3_INDX 0x0
#define FT1000_MAG_TOTAL_LEN 0x200
#define FT1000_MAG_TOTAL_LEN_INDX 0x1
#define FT1000_MAG_PH_LEN 0x200
#define FT1000_MAG_PH_LEN_INDX 0x0
#define FT1000_MAG_PORT_ID 0x201
#define FT1000_MAG_PORT_ID_INDX 0x0
//
// Constants for the FT1000_REG_SUP_ISR
//
// Indicate the cause of an interrupt.
//
// SUPERVISOR ISR BIT MAPS
#define ISR_EMPTY (UCHAR)0x00 // no bits set in ISR
#define ISR_DOORBELL_ACK (UCHAR)0x01 // the doorbell i sent has been recieved.
#define ISR_DOORBELL_PEND (UCHAR)0x02 // doorbell for me
#define ISR_RCV (UCHAR)0x04 // packet received with no errors
#define ISR_WATERMARK (UCHAR)0x08 //
// Interrupt mask register defines
// note these are different from the ISR BIT MAPS.
#define ISR_MASK_NONE 0x0000
#define ISR_MASK_DOORBELL_ACK 0x0001
#define ISR_MASK_DOORBELL_PEND 0x0002
#define ISR_MASK_RCV 0x0004
#define ISR_MASK_WATERMARK 0x0008 // Normally we will only mask the watermark interrupt when we want to enable interrupts.
#define ISR_MASK_ALL 0xffff
#define HOST_INTF_LE 0x0000 // Host interface little endian
#define HOST_INTF_BE 0x0001 // Host interface big endian
#define ISR_DEFAULT_MASK 0x7ff9
#define hi 0x6869
#define ho 0x686f
#define FT1000_ASIC_RESET 0x80 // COR value for soft reset to PCMCIA core
#define FT1000_ASIC_BITS 0x51 // Bits set in COR register under normal operation
#define FT1000_ASIC_MAG_BITS 0x55 // Bits set in COR register under normal operation
#define FT1000_COR_OFFSET 0x100
#define ELECTRABUZZ_ID 0 // ASIC ID for ELECTRABUZZ
#define MAGNEMITE_ID 0x1a01 // ASIC ID for MAGNEMITE
// Maximum times trying to get ASIC out of reset
#define MAX_ASIC_RESET_CNT 20
#define DSP_RESET_BIT 0x1
#define ASIC_RESET_BIT 0x2
#define DSP_UNENCRYPTED 0x4
#define DSP_ENCRYPTED 0x8
#define EFUSE_MEM_DISABLE 0x0040
#define MAX_BUF_SIZE 4096
#if 0 //Removed by Jim
typedef struct _PSEUDO_HDR
{
unsigned short length;
unsigned char source;
unsigned char destination;
unsigned char portdest;
unsigned char portsrc;
unsigned short sh_str_id;
unsigned char control;
unsigned char rsvd1;
unsigned char seq_num;
unsigned char rsvd2;
unsigned short qos_class;
unsigned short checksum;
} PSEUDO_HDR, *PPSEUDO_HDR;
#endif //end of Jim
typedef struct _DRVMSG {
PSEUDO_HDR pseudo;
u16 type;
u16 length;
u8 data[0];
} __attribute__ ((packed)) DRVMSG, *PDRVMSG;
struct ft1000_device
{
struct usb_device *dev;
struct net_device *net;
spinlock_t device_lock;
u32 status;
wait_queue_head_t control_wait;
struct urb *rx_urb;
struct urb *tx_urb;
u8 tx_buf[MAX_BUF_SIZE];
u8 rx_buf[MAX_BUF_SIZE];
u8 bulk_in_endpointAddr;
u8 bulk_out_endpointAddr;
//struct ft1000_ethernet_configuration configuration;
// struct net_device_stats stats; //mbelian
} __attribute__ ((packed));
typedef struct _FT1000_INFO {
struct ft1000_device *pFt1000Dev;
struct net_device_stats stats;
struct task_struct *pPollThread;
unsigned char fcodeldr;
unsigned char bootmode;
unsigned char usbboot;
unsigned short dspalive;
u16 ASIC_ID;
BOOLEAN fProvComplete;
BOOLEAN fCondResetPend;
BOOLEAN fAppMsgPend;
char *pfwimg;
int fwimgsz;
u16 DrvErrNum;
u8 *pTestImage;
u16 AsicID;
unsigned long TestImageIndx;
unsigned long TestImageSz;
u8 TestImageEnable;
u8 TestImageReady;
int ASICResetNum;
int DspAsicReset;
int PktIntfErr;
int DSPResetNum;
int NumIOCTLBufs;
int IOCTLBufLvl;
int DeviceCreated;
int CardReady;
int DSP_loading;
int NetDevRegDone;
u8 CardNumber;
u8 DeviceName[15];
int DeviceMajor;
int registered;
int mediastate;
int dhcpflg;
u16 packetseqnum;
u8 squeseqnum; // sequence number on slow queue
spinlock_t dpram_lock;
spinlock_t fifo_lock;
u16 CurrentInterruptEnableMask;
int InterruptsEnabled;
u16 fifo_cnt;
u8 DspVer[DSPVERSZ]; // DSP version number
u8 HwSerNum[HWSERNUMSZ]; // Hardware Serial Number
u8 Sku[SKUSZ]; // SKU
u8 eui64[EUISZ]; // EUI64
time_t ConTm; // Connection Time
u8 ProductMode[MODESZ];
u8 RfCalVer[CALVERSZ];
u8 RfCalDate[CALDATESZ];
u16 DSP_TIME[4];
u16 ProgSnr;
u16 LedStat; //mbelian
u16 ConStat; //mbelian
u16 ProgConStat;
struct list_head prov_list;
int appcnt;
APP_INFO_BLOCK app_info[MAX_NUM_APP]; //Added by Jim
u16 DSPInfoBlklen;
u16 DrvMsgPend;
int (*ft1000_reset)(struct net_device *dev);
u16 DSPInfoBlk[MAX_DSP_SESS_REC];
union {
u16 Rec[MAX_DSP_SESS_REC];
u32 MagRec[MAX_DSP_SESS_REC/2];
} DSPSess;
unsigned short tempbuf[32];
char netdevname[IFNAMSIZ];
struct proc_dir_entry *ft1000_proc_dir; //mbelian
} FT1000_INFO, *PFT1000_INFO;
typedef struct _DPRAM_BLK {
struct list_head list;
u16 *pbuffer;
} __attribute__ ((packed)) DPRAM_BLK, *PDPRAM_BLK;
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment