리눅스 설치 5일째를 맞이하고있읍니다.
래드헷7.2를 설치하였읍니다. 저도 드뎌 삽질이 시작되었나봅니다 --''
처음 설치시 랜카드설정부분에서 장착된 카드와 일치하는것이 없어서 그냥 건너뛰었읍니
다. (설치후 차근차근 붙일 생각이었읍니다.) /proc/pci를 cat해보니 다음과 같이 나오것으로
봐서 커널에서 무슨카드인지는 모르지만 일단 디텍트(?) 해두고 있나봅니다. Bus 0, device 15,
function 0: Ethernet controller: PCI device 10c3:8920 (Samsung Semiconductors, Inc.) (rev 1).
IRQ 10.
Master Capable. Latency=64.
I/O at 0x1400 [0x147f].
Non-prefetchable 32 bit memory at 0xf4000000 [0xf400007f].
그리고 /lib/modules/2.4.7-10에는 지극히 당연하게 해당 랜카드 모듈이 없었고.
/usr/src/driver에는 해당카드 소스파일이 없으며,
/dev/eth0역시 없읍니다.
어떻게든 /dev/eth0 만이라도 생성시키면 네트웍구성하는것은 해볼만 할텐데...
위에서 보는 바와 같이 삼성8290(사실 SC-1200A-TX 라는 모델로 팔고있읍니다)
랜카드의 리눅스용 모듈 소스를 삼성전자 홈페이지에서 다운 받아서 컴파일코자
하였읍니다만, 워낙 무식찬란한 문외한이다보니 실폐하고 말았읍니다.
삼성전자에서 제공하는 컴파일용 쉘스크립트를(trans) 사용하였는데, 래드헷 7.0에서
사용하던 것이어서(리드미 파일에 그렇게 되어있더군요) 옵션이 제대로 맞지 않았나
봅니다.
이 스크립트를 어떻게 고치면 될까요 ? 또 모듈 소스가 래드헷7.2에 붙이더라도 별
문제가 없을까요 ?
고수님들의 도움이 필요합니다. 도와주세요 --;;
**********************************************
************* shell script *********************
***********************************************
gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe
-fno-strength-reduce -m486 -malign-loops=2 -malign-jumps=2 -malign-functions=2 -DCPU=586 -DMODULE -DMODVERSIONS -includ
e /usr/src/linux/include/linux/modversions.h -c -o ks8920.o ks8920.c *************************************************
** ************* ReadMe File의 내용 ************************* *******************************************************
* 와우7.0 래드햇7.0
파일을 열어보면 trans, ks8920.c가 있습니다.
./trans을 눌러 ks8920.o 이미지파일을 만듭니다.
cd ks8920.o /lib/modules/2.2.XX/net에 카피합니다.
/lib/modules/2.2.**/에 modules.dep가 있습니다.
이곳에 /lib/modules/2.2.**/net/ks8920.o를 적어두면 됩니다.
방식은 아래와 같습니다.
이렇게 하고 modprobe ks8920을 하면 됩니다.
modules.dep을 읽어보면
/lib/modules/2.2.**/fs/fat.o
/lib/modules/2.2.**/fs/ntfs.o
.
.
/lib/modules/2.2.**/net/3c509x.o
/lib/modules/2.2.**/net/tlan.o
.
/lib/modules/2.2.**/net/ks8920.o <= 이렇게 적어줍니다.
.
.
/lib/modules/2.2.**/ipv4/ip_masq_ftp.o
/lib/modules/2.2.**/ipv4/ip_masq_cuseeme.o
******************************************************************
************** 모듈소스 ks8920.c *********************************
******************************************************************
/* ks8920.c: An SAMSUNG KS8920 fast ethernet driver for Linux. */
/*
NOTICE: this version tested with kernels 2.2.12, 2.2.15 and last kernel only!
Written 1998-1999 by Chang-Kyu Beck and Su-Ho,Kwon
Modified by tae-hyung kang (June 2000)
thkang at adams.kwangwoon.ac.kr or openfly at zaigen.co.kr This software may be used and distributed according to the
terms of the GNU Public License, incorporated herein by reference.
This driver is for the Samsung KS8920 Fast Ethernet boards.
To use a built-in driver, install as drivers/net/ks8920.c.
To use as a module, use the compile-command at the end of the file.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/processor.h> /* Processor type for cache alignment. */
#include <asm/bitops.h>
#include <asm/io.h>
#define RUN_AT(x) (jiffies + (x))
#include <linux/delay.h>
#define VERSION_CODE(vers,rel,seq) ((vers)<<16 | (rel)<<8 | (seq))
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,55)
#include <linux/bios32.h>
#define PCI_SUPPORT_VER1
#else
#define PCI_SUPPORT_VER2
#endif
#if LINUX_VERSION_CODE < VERSION_CODE(2,1,59)
#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
#else
#define dev_free_skb(skb) dev_kfree_skb(skb);
#endif
#define NODE_ID_START_ADDRESS 2
// define for Buffer List Hang error ( fix for chip bug )
#define BLIST_CQUEUE_MODE
#define RX_BL_NUMBER 2
// phy define
#define PHY_WRITE 0x0400
#define PHY_BUSY 0x0800
#define PHY_BMC_REG 0 // Basic Mode Control Register
#define PHY_AN_ENABLE 0x1000
#define PHY_RESTART_AN 0x200
#define PHY_SPEED_SELECT_100 0x2000
#define PHY_FULL_DUPLEX 0x100
#define PHY_ISOLATE 0x400
#define PHY_LOOPBACK 0x4000
#define PHY_BMS_REG 1 // Basic Mode Status Register
#define PHY_AN_COMPLETE 0x20
#define PHY_LINK_STATUS 4
#define PHY_ANLPA_REG 5 // Auto-Negotiation Link Partner Ability Reg.
#define PHY_100_BASE_T4 0x200
#define PHY_100_BASE_TX_FD 0x100
#define PHY_100_BASE_TX_HD 0x80
#define PHY_10_BASE_T_FD 0x40
#define PHY_10_BASE_T_HD 0x20
#define PHY_ID_REG1 2 // Identifier register 1
#define PHY_ANE_REG 6 // Auto-Negotiation Expansion Reg.
#define PHY_LP_AN_ABLE 1
#define PHY_INT_SOURCE_REG 29 // Interrupt Source Register
#define PHY_AN_COMPLETE_INT 0x40
#define PHY_INT_MASK_REG 30 // Interrupt Mask Register
#define PHY_INT_MODE 0x8000
// KS8920 register index
#define DMA_Ctl 0x00
#define TxFrmPtr 0x04
#define TxThrsh 0x08
#define TxPollCtr 0x0c
#define BLFrmPtr 0x10
#define RxFragSize 0x14
#define Int_En 0x18
#define FDA_Bas 0x1c
#define FDA_Lim 0x20
#define Int_Src 0x24
#define Reserved0 0x28
#define Reserved1 0x2c
#define PauseCnt 0x30
#define RemPauCnt 0x34
#define TxConFrmStat 0x38
#define MAC_Ctl 0x40
#define CAM_Ctl 0x44
#define Tx_Ctl 0x48
#define Tx_Stat 0x4c
#define Rx_Ctl 0x50
#define Rx_Stat 0x54
#define MD_Data 0x58
#define MD_CA 0x5c
#define CAM_Adr 0x60
#define CAM_Data 0x64
#define CAM_Ena 0x68
#define PROM_Ctl 0x6c
#define PROM_Data 0x70
#define Miss_Cnt 0x7c
//
/* Vendor ID & Device ID */
#define PCI_VENDOR_ID_SAMSUNG 0x10c3
#define PCI_DEVICE_ID_KS8920 0x8920
#define SUBVENDOR_ID0 0x10c3
#define SUBVENDOR_ID1 0x10c3
//
// DMA_ctl register
#define DMACtl_DmBurst 0x00000040 // burst size
#define DMACtl_PowrMgmnt 0x00001000
#define DMACtl_TestMode 0x00002000
#define DMACtl_TxBigE 0x00004000
#define DMACtl_RxBigE 0x00008000
#define DMACtl_TxWakeUp 0x00010000
#define DMACtl_SWIntReq 0x00020000
#define DMACtl_IntMask 0x00040000
// RxFragSize register
#define RxFragSize_EnPack 0x00008000
//Interrupt enable register
#define IntEn_FDAExE 0x00000001
#define IntEn_BLExEn 0x00000002
#define IntEn_STargAbtEn 0x00000004
#define IntEn_RTargAbtEn 0x00000008
#define IntEn_RMasAbtEn 0x00000010
#define IntEn_SSysErrEn 0x00000020
#define IntEn_DParErrEn 0x00000040
#define IntEn_EarNotEn 0x00000080
#define IntEn_DParDEn 0x00000100
#define IntEn_TxCtlCmpEn 0x00000400
#define IntEn_NRAbtEn 0x00000800
// interrupt source register
#define IntSrc_IntMacTx 0x00000001
#define IntSrc_IntMacRx 0x00000002
#define IntSrc_IntPCI 0x00000004
#define IntSrc_IntFDAEx 0x00000008
#define IntSrc_IntBLEx 0x00000010
#define IntSrc_SWInt 0x00000020
#define IntSrc_IntEarNot 0x00000040
#define IntSrc_IntExBD 0x00000100
#define IntSrc_IntTxCtlCmp 0x00000200
#define IntSrc_IntNRAbt 0x00000400
#define IntSrc_FDAEx 0x00000800
#define IntSrc_BLEx 0x00001000
#define IntSrc_NRAbt 0x00004000
// MAC control register
#define MACCtl_HaltReq 0x00000001
#define MACCtl_HaltImm 0x00000002
#define MACCtl_Reset 0x00000004
#define MACCtl_FullDup 0x00000008
#define MACCtl_MacLoop 0x00000010
#define MACCtl_Conn 0x00000020
//#define MACCtl_Loop10 0x000000c0
#define MACCtl_MissRoll 0x00000400
#define MACCtl_EnMissRoll 0x00002000
#define MACCtl_Link10 0x00008000
// CAM control register
#define CAMCtl_StationAcc 0x00000001
#define CAMCtl_GroupAcc 0x00000002
#define CAMCtl_BroadAcc 0x00000004
#define CAMCtl_NegCAM 0x00000008
#define CAMCtl_CompEn 0x00000010
//transmit control register
#define TxCtl_TxEn 0x00000001
#define TxCtl_TxHalt 0x00000002
#define TxCtl_NoPad 0x00000004
#define TxCtl_NoCRC 0x00000008
#define TxCtl_FBack 0x00000010
#define TxCtl_NoExDef 0x00000020
#define TxCtl_SdPause 0x00000040
#define TxCtl_MII10 0x00000080
#define TxCtl_EnUnder 0x00000100
#define TxCtl_EnExDefer 0x00000200
#define TxCtl_EnLCarr 0x00000400
#define TxCtl_EnExColl 0x00000800
#define TxCtl_EnLateColl 0x00001000
#define TxCtl_EnTxPar 0x00002000
#define TxCtl_EnComp 0x00004000
// Transmit status register
#define TxStat_TxColl 0x0000000f
#define TxStat_ExColl 0x00000010
#define TxStat_TxDefer 0x00000020
#define TxStat_Paused 0x00000040
#define TxStat_IntTx 0x00000080
#define TxStat_Under 0x00000100
#define TxStat_ExDefer 0x00000200
#define TxStat_LostCrs 0x00000400
#define TxStat_Tx10Stat 0x00000800
#define TxStat_LateColl 0x00001000
#define TxStat_TxPar 0x00002000
#define TxStat_Comp 0x00004000
#define TxStat_TxHalted 0x00008000
#define TxStat_SQErr 0x00010000
// receive control register
#define RxCtl_RxEn 0x00000001
#define RxCtl_RxHalt 0x00000002
#define RxCtl_LongEn 0x00000004
#define RxCtl_ShortEn 0x00000008
#define RxCtl_StripCRC 0x00000010
#define RxCtl_PassCtl 0x00000020
#define RxCtl_IgnoreCRC 0x00000040
#define RxCtl_EnAlign 0x00000100
#define RxCtl_EnCRCErr 0x00000200
#define RxCtl_EnOver 0x00000400
#define RxCtl_EnLongErr 0x00000800
#define RxCtl_EnRxPar 0x00002000
#define RxCtl_EnGood 0x00004000
// receive status register
#define RxStat_CtlRecd 0x00000020
#define RxStat_IntRx 0x00000040
#define RxStat_Rx10Stat 0x00000080
#define RxStat_AlignErr 0x00000100
#define RxStat_CRCErr 0x00000200
#define RxStat_OverFlow 0x00000400
#define RxStat_LongErr 0x00000800
#define RxStat_RxPar 0x00002000
#define RxStat_Good 0x00004000
#define RxStat_RxHalted 0x00008000
// station Management Data control and Address
#define MDCA_Wr 0x00000400
#define MDCA_Busy 0x00000800
#define MDCA_PreSup 0x00001000
// i/o macro define
#define ks_outl(x,y) outl( y, dev->base_addr+(x) )
#define ks_inl(x) inl( dev->base_addr+(x) )
// RrmOpt
#define FRMOPT_BIG_ENDIAN 0x10
#define FRMOPT_INT_TRANSMIT 0x08
#define FRMOPT_NO_CRC_APPEND 0x04
#define FRMOPT_NO_PAD 0x02
// COwner bit
#define EOL_BIT 1
#define BD_OWNER_CONTROLLER 1// 0x80
#define BD_OWNER_SYSTEM 0// 0x7f
#define FD_OWNER_CONTROLLER 1// 0x8000
#define FD_OWNER_SYSTEM 0// 0x7fff
/* tx and rx ring size */
#define TX_RING_SIZE 32
#define RX_RING_SIZE 32
#define RX_FDA_BD_NUMBER 2
#define TX_FD_BD_NUMBER 2
#define MAX_BUFFER_SIZE 1536 // 12*128
#define TX_POLL_CTL_SIZE 0xfff
#define TX_THRSH_SIZE 0x00000200
/* The total I/O port extent of the board. */
#define KS8920_TOTAL_SIZE 0x80
#define MAX_MULTICAST_LIMIT 21
/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
static const char *version=
"ks8920.c: Ver2.1 SAMSUNG SC1200-ATX 10/100 FAST ETHERNET Adapter";
/* options */
#define AUTO_SENSE 2
#ifdef KDEBUG
char *DMA_Name<> = {"DMA_Ctl", "TxFrmPtr", "TxThrsh", "TxPollCtr", "BLFrmPtr",
"RxFragSize", "int_En", "FDA_Bas", "FDA_Lim", "Int_Src"};
char *MAC_Name<> = {"MAC Control","CAM Control", "Transmit Control", "Transmit Status",
"Receive Control", "Receive Status"};
//
// KS8920 register content print debugging function
//
void print_register_map(struct device *dev)
{
int i;
int retval;
printk( "register value =====>");
for ( i = 0; i< 10; i ++){
retval = ks_inl( i*4 );
printk("%20s : 0x%08x", DMA_Name[i], retval);
}
for ( i = 0; i< 6; i++){
retval = ks_inl( i*4+ 0x40 );
printk("%20s : 0x%08x", MAC_Name[i], retval);
}
}
//
// CAM register content print debugging function
//
void print_cam_register(struct device *dev )
{
int i;
int reg_value;
printk("Cam register value =====>");
for( i=0;i<0x20;i+=4) {
ks_outl( CAM_Adr, i );
reg_value = ks_inl( CAM_Data );
printk( " CAM address %02x :0x%08x", i, reg_value );
}
printk( "CAM Enable regsiter %04x", ks_inl( CAM_Ena ) );
printk( "CAM Control regsiter %04x", ks_inl( CAM_Ctl ) );
}
#endif
// endian translate structure
// This function used to CAM setting function.
//
typedef union {
unsigned int data;
unsigned char tdata[4];
} EndianTran;
// Buffer Descriptor structure define
typedef struct {
unsigned int data; // data pointer
unsigned short length; // data length
unsigned char RxBDID; // Rx Buffer descriptor ID
union {
unsigned char bdctl;
struct {
unsigned char RxBDSeqN : 7; // rx Buffer descriptor sequence number
unsigned char COwnsFD : 1; // buffer descriptor owner bit( 1: controller, 0:system )
}s;
}u;
} BD;
/* frame descriptor structure define */
typedef struct { /* Transmit frame descriptor set. */
unsigned int next; /* FDNext */
struct sk_buff* tx_skbuff; // system field -> used by socket buffer address store
unsigned short stat; /* FDStat */
unsigned short reserved;
unsigned short length; // frame length
union {
unsigned short fdctl; // frame descriptor control
struct {
unsigned short BDCount : 5; // buffer descriptor count
unsigned short Reserved : 5; //
unsigned short FrmOpt : 5; // frame option
unsigned short COwnsFD : 1; // frame descriptor owner bit ( 1: controller, 0:system )
}s;
}u;
BD freebufflist[TX_FD_BD_NUMBER]; // 2
} TxFD;
/* The Rx buffer list frame descriptors. structure */
typedef struct { /* Receive frame descriptor. */
unsigned int next; /* FDNext */
unsigned int system;
unsigned short stat; /* FDStat */
unsigned short reserved;
unsigned short length;
union {
unsigned short fdctl;
struct {
unsigned short BDCount : 5;
unsigned short Reserved : 5;
unsigned short FrmOpt : 5;
unsigned short COwnsFD : 1;
}s;
}u;
#ifndef BLIST_CQUEUE_MODE
BD freebufflist[RX_RING_SIZE]; //
#else
BD freebufflist[RX_BL_NUMBER]; // 2
#endif
} RxBL;
/* The Rx FDA descriptors */
typedef struct {
unsigned int next; /* FDNext */
unsigned int system;
unsigned short stat; /* FDStat */
unsigned short reserved;
unsigned short length;
union {
unsigned short fdctl;
struct {
unsigned short BDCount : 5;
unsigned short Reserved : 5;
unsigned short FrmOpt : 5;
unsigned short COwnsFD : 1;
}s;
}u;
BD freebufflist[RX_FDA_BD_NUMBER]; // 2
} RxFDA;
struct ks8920_private {
char devname[8]; /* Used only for kernel debugging. */
struct device *next_module;
#if LINUX_VERSION_CODE < VERSION_CODE(2,2,14)
int dummy; // for 16byte aligned
#endif
/* Tx descriptor ring */
TxFD tx_ring[TX_RING_SIZE];
/* Rx descriptor ring & addresses of receive-in-place skbuffs. */
RxFDA rx_ring[RX_RING_SIZE];
/* safety buffer allocation for long packet(more 1514 byte) */
char safebuffer[16*14]; // 16byte align = 28*8
/* receive buffer list queue*/
#ifndef BLIST_CQUEUE_MODE
RxBL rx_bufflist;
#else
RxBL rx_bufflist[RX_RING_SIZE];
#endif
struct sk_buff* rx_skbuff[RX_RING_SIZE];
struct enet_statistics stats; // transmision statistic storage
int in_interrupt; // into interrupt routine
struct timer_list timer; /* Media selection timer. */
long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
unsigned int enq_rx, enq_tx; /* The next free ring entry */
unsigned int deq_rx, deq_tx; /* The ring entries to be free()ed. */
unsigned int tx_full:1; /* The Tx queue is full. */
unsigned int link_speed:2; /* link speed : 0 : 10M, 1:100M , 2:auto_sense */
unsigned int full_duplex:1; /* Full-duplex operation requested. */
unsigned int default_port:1; /* Last dev->if_port value. */
};
/* function prototype define */
static unsigned short read_eeprom(int ioaddr, int location);
static int ks8920_open(struct device *dev);
static int ks8920_start_xmit(struct sk_buff *skb, struct device *dev);
static int ks8920_rx_proc(struct device *dev);
static void ks8920_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
static int ks8920_close(struct device *dev);
static struct enet_statistics *ks8920_get_stats(struct device *dev);
static int ks8920_ioctl(struct device *dev, struct ifreq *rq, int cmd);
static int ks8920_tx_proc( struct device *dev );
static void ks8920_probe(struct device *dev, int ioaddr, int irq, int card_idx );
static int ks8920_initialize( struct device *dev );
static void ks8920_sw_reset( struct device *dev );
static void ks8920_timer(unsigned long data);
static void set_rx_mode(struct device *dev);
static struct device *root_ks8920_dev = NULL;
/*
PROM Control(PROM_Ctl) - 6CH
Bit 15 : Busy bit
14 - 13 : Opcode 10 read, 01 write, 00 enable/disable (5:4 - 11: enable/ 00 : disable)
12 - 6 : Reserved
5 - 0 : PROM_Addr
*/
#define EEPROM_BUSY 0x8000
#define EEPROM_READ 0x4000
#define EEPROM_WRITE 0x2000
#define EEPROM_ENABLE 0x0030
#define EEPROM_DISABLE 0x0000
#define EEPROM_ERASE 0x6000
/*++
This routine will read the EEPROM Space
to retrieve the MAC address and so forth.
--*/
static unsigned short read_eeprom(int ioaddr, int location)
{
unsigned short retval = 0;
int eeprom_ctrl_addr = ioaddr + PROM_Ctl;
int eeprom_data_addr = ioaddr + PROM_Data;
int timer;
/* Write EEPROM Control bit for reading */
outw( EEPROM_BUSY | EEPROM_READ | location, eeprom_ctrl_addr);
for(timer=10;timer>0;timer--) {
udelay(162);
if(( inw(eeprom_ctrl_addr) & 0x8000) == 0)
break;
}
/* read the EEPROM Data value */
retval = inw(eeprom_data_addr);
return retval;
}
// write the station management data register
static void md_out( struct device *dev, ushort ctl, ushort data )
{
int reg_value;
ks_outl(MD_Data, data);
ks_outl(MD_CA, ctl);
do {
reg_value = ks_inl( MD_CA );
} while(reg_value & 0x0800);
}
// read the station management data register
static int md_in( struct device *dev, ushort ctl )
{
int reg_value;
ks_outl( MD_CA, ctl );
do {
reg_value = ks_inl(MD_CA);
} while(reg_value & 0x0800);
reg_value = ks_inl(MD_Data);
return reg_value;
}
/*
This routine will detect the ks8920 pci h/w.
*/
int ks8920_init(struct device *dev)
{
int adapters_found = 0;
if( pcibios_present() ) { // pcibios present?
static int pci_index = 0;
for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_latency;
int ioaddr;
int irq;
unsigned short pci_command, new_command;
if (pcibios_find_device(PCI_VENDOR_ID_SAMSUNG, // 0x10c3
PCI_DEVICE_ID_KS8920, // 0x8920
pci_index, &pci_bus,
&pci_device_fn))
break;
/* Get and check the bus-master and latency values. */
pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); // read the pci command register
new_command = (pci_command & !PCI_COMMAND_MEMORY) | PCI_COMMAND_MASTER | PCI_COMMAND_IO; // set the master mode & i
/o mode if (pci_command != new_command) {
#ifdef KDEBUG
printk(" The PCI BIOS has not enabled this"
" device! Updating PCI command %4.4x->%4.4x.", pci_command, new_command);
#endif
pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, new_command); // set command register
}
#ifdef PCI_SUPPORT_VER2
{
struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
ioaddr = pdev->base_address[0] & ~3UL;
irq = pdev->irq;
}
#else
{
int pci_ioaddr;
u8 pci_irq_line;
pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_irq_line);
pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_ioaddr);
ioaddr = pci_ioaddr & ~3;
irq = pci_irq_line;
}
#endif
pcibios_read_config_byte(pci_bus, pci_device_fn,
PCI_LATENCY_TIMER, &pci_latency);
if (pci_latency < 32)
pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, 32);
ks8920_probe( dev, ioaddr, irq, adapters_found );
dev = NULL;
adapters_found++;
}
}
return adapters_found;
}
/* 1. check the ks8920 interface card and read a MAC address.
2. set the nic entry pointer at the device structure.
*/
static void ks8920_probe(struct device *dev, int ioaddr, int irq, int card_idx )
{
static int print_version = 0; /* Already printed version info. */
struct ks8920_private *sp;
int i, option;
unsigned short reg_value;
unsigned char subvendor_ID, subsystem_ID;
/* Print Version Information */
if( print_version++ == 0)
printk("%s", version);
dev = init_etherdev(dev, sizeof(struct ks8920_private)); // dev structure initialize
if (dev->mem_start > 0)
option = dev->mem_start;
else
option = 0;
/* Get the Subvendor ID, Subsystem ID and Hardware Address */
reg_value = read_eeprom( ioaddr,0 );
subvendor_ID = reg_value;
subsystem_ID = reg_value >> 8;
#ifdef KDEBUG
printk("ks8920_adapter_detect: SubVendor ID - 0x%x, SubSystem ID -0x%x", subvendor_ID, subsystem_ID);
#endif
// Get the network address from serial eeprom.
for (i=0;i<3;i++) {
reg_value = read_eeprom( ioaddr,i+2 );
dev->dev_addr[i*2] = reg_value;
dev->dev_addr[i*2+1] = reg_value >> 8;
}
/* Reset the chip */
outl(0x0 , ioaddr+MAC_Ctl);
/* We do a request_region() only to register /proc/ioports info. */
request_region(ioaddr, KS8920_TOTAL_SIZE, dev->name);
// store the io address and irq number at dev structure
dev->base_addr = ioaddr;
dev->irq = irq;
// allocate the private area
if (dev->priv == NULL)
sp = kmalloc(sizeof(*sp), GFP_KERNEL | GFP_DMA );
sp = dev->priv;
memset(sp, 0, sizeof(*sp));
sp->next_module = root_ks8920_dev;
root_ks8920_dev = dev;
if(option > 0) {
sp->link_speed = (option & 0x20) ? 1 : 0;
sp->link_speed = (option & 0x60) ? 2 : sp->link_speed;
sp->full_duplex =(option & 0x10) ? 1 : 0;
}
else
sp->link_speed = 2; // default : auto sense
/* The ks8920-specific entries in the device structure. */
dev->open = &ks8920_open;
dev->hard_start_xmit = &ks8920_start_xmit;
dev->stop = &ks8920_close;
dev->get_stats = &ks8920_get_stats;
dev->set_multicast_list = &set_rx_mode;
dev->do_ioctl = &ks8920_ioctl;
return;
}
/*
device open function
*/
static int ks8920_open(struct device *dev)
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
int ret;
// regist interrupt service routine
if( request_irq(dev->irq, &ks8920_interrupt, SA_SHIRQ, dev->name, dev))
return -EAGAIN;
MOD_INC_USE_COUNT;
sp->enq_tx = sp->deq_tx = sp->enq_rx = sp->deq_rx =0; // queue pointer initialize
dev->if_port = 0;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
sp->in_interrupt = 0;
sp->tx_full = 0;
ret = ks8920_initialize(dev); // call the initalize routine
/* Set the timer. The timer serves a dual purpose:
1) to monitor the media interface (e.g. link beat) and perhaps switch
to an alternate media type
2) to monitor Rx activity, and restart the Rx process if the receiver
hangs. */
init_timer(&sp->timer);
sp->timer.expires = RUN_AT((24 * HZ)/10); /* 2.4 sec. */
sp->timer.data = (unsigned long)dev;
sp->timer.function = &ks8920_timer; /* timer handler */
add_timer(&sp->timer);
return ret;
}
/*
1. auto sense the speed and duplex
2. rx and tx queue initialize
3. ks8920 register initialize
*/
static int ks8920_initialize( struct device *dev )
{
int reg_value;
int phy_sta_addr, i;
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
// Issue a eeprom read command.
reg_value = read_eeprom( dev->base_addr, 0 );
if( (ushort)reg_value == 0xffff || (ushort)reg_value == 0x1464)
reg_value = 0x10c3; // VENDOR_ID;
if((ushort)reg_value == SUBVENDOR_ID1 ) {
phy_sta_addr = 1;
}
else {
phy_sta_addr = 0;
while (1) {
ks_outl( MD_Data, 0x5555 );
reg_value = md_in( dev, (phy_sta_addr<<5) | 0x0801 );
if (reg_value == 0xffff) {
phy_sta_addr++;
if (phy_sta_addr > 31) {
printk("%s: unsupported configuration.", dev->name);
return 1;
}
else
continue;
}
else {
md_out( dev, (phy_sta_addr<<5) | 0x0C00, sp->link_speed ? 0x2000 : 0x0000 );
break;
}
} // while
} // else
if( sp->link_speed == AUTO_SENSE ) {
reg_value = md_in( dev, (((phy_sta_addr<<5) | 0x4 ) & ~0x400)| 0x800 );
// Ready to program auto_negotiation.
md_out( dev, (phy_sta_addr<<5) | 0x4 | 0x400 | 0x800, (reg_value | 0x1e0) );
// Enable auto_negotiation.
md_out( dev, (phy_sta_addr<<5) | 0x0c00 | PHY_BMC_REG, PHY_AN_ENABLE );
// Auto_negotiation complete.
// Read back current link speed.
reg_value = md_in( dev, (phy_sta_addr<<5) | 0x0800 | PHY_ANLPA_REG );
if ( reg_value & PHY_100_BASE_TX_FD ) { // 100M and Full duplex
sp->link_speed = 1;
sp->full_duplex = 1;
}
else if ( reg_value & PHY_100_BASE_TX_HD ) { // 100M and Half duplex
sp->link_speed = 1;
sp->full_duplex = 0;
}
else if ( reg_value & PHY_10_BASE_T_FD ) { // 10M and Full duplex
sp->link_speed = 0;
sp->full_duplex = 1;
}
else { // 10M and Half duplex
sp->link_speed = 0;
sp->full_duplex = 0;
}
#ifdef KDEBUG
printk( "%s: speed(%d), full_duplex(%d)", dev->name, sp->link_speed, sp->full_duplex );
#endif
} // if (sp->link_speed == AUTO_SENSE)
else {
// Set 100Mbps or 10Mbps.
ushort data;
data = sp->link_speed ? 0x2000 : 0; /* speed : 100M or 10 M */
data += sp->full_duplex ? 0x100 : 0; /* fdx? */
md_out( dev, (phy_sta_addr<<5) | 0x0c00 | PHY_BMC_REG, data );
} // if (sp->link_speed != AUTO_SENSE
// Reset ks8920
reg_value = ks_inl( MAC_Ctl );
ks_outl( MAC_Ctl, reg_value| MACCtl_Reset);
/* Initialize the descriptor ownership in the */
/* Transmit and Receive descriptor rings */
for (i = 0; i < TX_RING_SIZE; i++) {
sp->tx_ring[i].next = virt_to_bus( &sp->tx_ring[i==(TX_RING_SIZE-1) ? 0 : i+1] ); // link at the next FD
sp->tx_ring[i].length = 0;
sp->tx_ring[i].u.fdctl = 0;
sp->tx_ring[i].u.s.BDCount = 2;
sp->tx_ring[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_SYSTEM;
sp->tx_ring[i].freebufflist[1].u.s.COwnsFD = BD_OWNER_SYSTEM;
sp->tx_ring[i].u.s.COwnsFD = FD_OWNER_SYSTEM;
}
// initialize buffer list queue
#ifndef BLIST_CQUEUE_MODE
// 1 FD and more BD
sp->rx_bufflist.next = virt_to_bus( &sp->rx_bufflist );
sp->rx_bufflist.length = RX_RING_SIZE;
sp->rx_bufflist.u.s.COwnsFD = FD_OWNER_CONTROLLER;
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
skb = dev_alloc_skb(MAX_BUFFER_SIZE); // allocate the receive socket buffer
sp->rx_skbuff[i] = skb;
if (skb == NULL) {
printk("%s: dev_alloc_skb() error", dev->name);
break; // OK. Just initially short of Rx bufs.
}
skb->dev = dev; // Mark as being used by this device.
sp->rx_bufflist.freebufflist[i].data = virt_to_bus( skb->tail ); // data pointer
sp->rx_bufflist.freebufflist[i].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_bufflist.freebufflist[i].length = MAX_BUFFER_SIZE;
sp->rx_bufflist.freebufflist[i].RxBDID = i;
}
#else
// more FD
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
skb = alloc_skb(MAX_BUFFER_SIZE,GFP_ATOMIC); // allocate the receive socket buffer
sp->rx_skbuff[i] = skb;
if (skb == NULL) {
printk("%s: dev_alloc_skb() error", dev->name);
break; /* OK. Just initially short of Rx bufs. */
}
skb->dev = dev; /* Mark as being used by this device. */
sp->rx_bufflist[i].next = virt_to_bus( &sp->rx_bufflist[i==(RX_RING_SIZE-1) ? 0 : i+1] );
sp->rx_bufflist[i].length = 1;
sp->rx_bufflist[i].u.fdctl = 0;
sp->rx_bufflist[i].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_bufflist[i].u.s.BDCount = 2;
sp->rx_bufflist[i].freebufflist[0].data = virt_to_bus( skb->tail ); // data pointer
sp->rx_bufflist[i].freebufflist[0].length = MAX_BUFFER_SIZE;
sp->rx_bufflist[i].freebufflist[0].RxBDID = i;
sp->rx_bufflist[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
}
// for buffer list hang
sp->rx_bufflist[RX_RING_SIZE-1].u.s.COwnsFD = FD_OWNER_SYSTEM;
#endif
/* initialize FDA */
for (i = 0; i < RX_RING_SIZE; i++) {
sp->rx_ring[i].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
}
// Initialize the SEC CSRs
/* load the ethernet and broadcast address into CAM */
{
EndianTran TranD;
memset( &TranD, 0, sizeof(EndianTran));
TranD.tdata[0] = dev->dev_addr[3];
TranD.tdata[1] = dev->dev_addr[2];
TranD.tdata[2] = dev->dev_addr[1];
TranD.tdata[3] = dev->dev_addr[0];
ks_outl( CAM_Adr, 0 );
ks_outl( CAM_Data, TranD.data );
TranD.tdata[2] = dev->dev_addr[5];
TranD.tdata[3] = dev->dev_addr[4];
TranD.tdata[0] = TranD.tdata[1] = 0x00;
ks_outl( CAM_Adr, 4 );
ks_outl( CAM_Data, TranD.data );
}
ks_outl( DMA_Ctl, DMACtl_DmBurst );
ks_outl( TxFrmPtr, virt_to_bus( &sp->tx_ring[0] ) );
ks_outl( TxThrsh, TX_THRSH_SIZE );
ks_outl( TxPollCtr, TX_POLL_CTL_SIZE );
#ifndef BLIST_CQUEUE_MODE
ks_outl( BLFrmPtr, virt_to_bus( &sp->rx_bufflist ) );
#else
ks_outl( BLFrmPtr, virt_to_bus( &sp->rx_bufflist[0] ) );
#endif
ks_outl( RxFragSize, 0x00000100);
ks_outl( FDA_Bas, virt_to_bus( &sp->rx_ring[0]) );
ks_outl( FDA_Lim, sizeof(RxFDA) * (RX_RING_SIZE-1) );
// Interrupt Enable..
ks_outl( Int_En, IntEn_DParDEn | IntEn_DParErrEn | IntEn_SSysErrEn
| IntEn_RMasAbtEn | IntEn_RTargAbtEn | IntEn_STargAbtEn);
if( sp->full_duplex )
ks_outl( MAC_Ctl, MACCtl_EnMissRoll | MACCtl_FullDup );
else
ks_outl( MAC_Ctl, MACCtl_EnMissRoll );
ks_outl( CAM_Ctl, CAMCtl_CompEn | CAMCtl_BroadAcc );
ks_outl( Miss_Cnt, 0x00000000 );
// Rx_Ctl and Tx_Ctl enable
ks_outl( Rx_Ctl, RxCtl_RxEn | RxCtl_EnAlign | RxCtl_EnCRCErr | RxCtl_EnOver | RxCtl_EnLongErr | RxCtl_EnGood );
ks_outl( Tx_Ctl, TxCtl_EnComp | TxCtl_TxEn );
#ifdef KDEBUG
print_cam_register(dev);
print_register_map(dev);
#endif
return 0;
}
/*
set the multicast address at the CAM register.
*/
static void set_rx_mode(struct device *dev)
{
struct dev_mc_list *mc_ptr;
EndianTran TranD;
int i, offset,j;
int EnableBit;
if(dev->flags & IFF_PROMISC) { // Set promiscuous.
// StationACC | GroupAcc | BroadAcc
ks_outl( CAM_Ctl, CAMCtl_StationAcc | CAMCtl_GroupAcc | CAMCtl_BroadAcc );
return;
}
if((dev->flags & IFF_ALLMULTI) || dev->mc_count > (MAX_MULTICAST_LIMIT-1) ) { // get all multicast_packet
// GroupAcc
ks_outl( CAM_Ctl, CAMCtl_GroupAcc | CAMCtl_CompEn | CAMCtl_BroadAcc );
ks_outl( CAM_Ena, 1 );
return;
}
#ifdef KDEBUG
print_cam_register(dev);
#endif
TranD.data = 0;
ks_outl( CAM_Adr, 4 );
TranD.tdata[2] = dev->dev_addr[5];
TranD.tdata[3] = dev->dev_addr[4];
ks_outl( CAM_Data, TranD.data );
if( dev->mc_count == 0 ) { // get only own packets
ks_outl( CAM_Ctl, CAMCtl_CompEn | CAMCtl_BroadAcc );
ks_outl( CAM_Ena, 1 );
#ifdef KDEBUG
print_cam_register(dev);
#endif
return;
}
// set mc list
for( i=1,offset=4,mc_ptr=dev->mc_list, j = 0;
j < dev->mc_count ; mc_ptr=mc_ptr->next, offset+=4, i++, j++ )
{
if( i%2 ) {
TranD.tdata[0] = mc_ptr->dmi_addr[1];
TranD.tdata[1] = mc_ptr->dmi_addr[0];
ks_outl( CAM_Adr, offset );
ks_outl( CAM_Data, TranD.data );
TranD.tdata[0] = mc_ptr->dmi_addr[5];
TranD.tdata[1] = mc_ptr->dmi_addr[4];
TranD.tdata[2] = mc_ptr->dmi_addr[3];
TranD.tdata[3] = mc_ptr->dmi_addr[2];
offset += 4;
ks_outl( CAM_Adr, offset );
ks_outl( CAM_Data, TranD.data );
#ifdef KDEBUG
printk( "CAM address - %02x:%02x:%02x:%02x:%02x:%02x", TranD.tdata[1], TranD.tdata[0], TranD.tdata[3], TranD.tdata[2], T
ranD.tdata[1], TranD.tdata[0] ); #endif
}
else {
TranD.tdata[0] = mc_ptr->dmi_addr[3];
TranD.tdata[1] = mc_ptr->dmi_addr[2];
TranD.tdata[2] = mc_ptr->dmi_addr[1];
TranD.tdata[3] = mc_ptr->dmi_addr[0];
ks_outl( CAM_Adr, offset );
ks_outl( CAM_Data, TranD.data );
TranD.tdata[2] = mc_ptr->dmi_addr[5];
TranD.tdata[3] = mc_ptr->dmi_addr[4];
}
}
if( !(i%2) ) {
TranD.tdata[0] = TranD.tdata[1] = 0;
ks_outl( CAM_Adr, offset );
ks_outl( CAM_Data, TranD.data );
}
// enable bits of CAM_Ena register
for( j = EnableBit=0;j < i ; j++ )
EnableBit += (1 << j);
ks_outl( CAM_Ena, EnableBit );
// CompAen
ks_outl( CAM_Ctl, CAMCtl_CompEn | CAMCtl_BroadAcc );
#ifdef KDEBUG
print_cam_register(dev);
#endif
}
/*
packet transmit routine.
argument :
struct sk_buff *skb : transmit socket buffer pointer
*/
static int ks8920_start_xmit(struct sk_buff *skb, struct device *dev)
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
unsigned long flags;
int entry;
int reg_value;
if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
if (jiffies - dev->trans_start >= (4*HZ))
ks8920_sw_reset(dev);
return 1;
}
save_flags(flags);
cli(); // clear interrupt
/* Calculate the Tx descriptor entry. */
entry = sp->enq_tx % TX_RING_SIZE;
sp->tx_ring[entry].tx_skbuff = skb; // save the socket pointer
sp->tx_ring[entry].freebufflist[0].data = virt_to_bus(skb->data); // real packet data pointer
sp->tx_ring[entry].freebufflist[0].length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* minimum len */
// if tx data size >= DMACtl_DmBurst size, split it to 2 buffers : chip bug process
/* divide buffer descriptor for chip bug process */
if( sp->tx_ring[entry].freebufflist[0].length >= DMACtl_DmBurst ) {
sp->tx_ring[entry].freebufflist[1].data = virt_to_bus(skb->data) + sp->tx_ring[entry].freebufflist[0].length - 4;
sp->tx_ring[entry].freebufflist[1].length = 4; /* minimum len */
sp->tx_ring[entry].freebufflist[0].length -= 4;
sp->tx_ring[entry].u.s.BDCount = 2;
sp->tx_ring[entry].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
}
else
sp->tx_ring[entry].u.s.BDCount = 1;
sp->tx_ring[entry].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->tx_ring[entry].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->enq_tx = (sp->enq_tx+1)%TX_RING_SIZE;
if( sp->deq_tx == ((sp->enq_tx+2)%TX_RING_SIZE) ) // increment dequeue pointer
sp->tx_full = 1;
clear_bit(0, (void*)&dev->tbusy);
restore_flags(flags); // set the interrupt mask
dev->trans_start = jiffies; /* save the timestamp */
reg_value = ks_inl( DMA_Ctl );
ks_outl( DMA_Ctl, reg_value | DMACtl_TxWakeUp );
return 0;
}
/*
main interrupt service routine.
*/
static void ks8920_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
struct device *dev = dev_instance;
struct ks8920_private *sp;
unsigned short status;
int reg_value,ret=0;
// Diable Interrupt
reg_value = ks_inl(DMA_Ctl);
ks_outl(DMA_Ctl, reg_value | DMACtl_IntMask);
sp = (struct ks8920_private *)dev->priv;
if(test_and_set_bit(0, (void*)&sp->in_interrupt)) {
printk("%s: SMP simultaneous entry of an interrupt handler.", dev->name);
return;
}
dev->interrupt = 1;
status = ks_inl( Int_Src ); // read the interrupt source
if( status&IntSrc_IntMacRx ) {// rx interrupt?
ret = ks8920_rx_proc(dev); // call the rx process routine
}
if( status&IntSrc_IntMacTx ) { // tx interrupt?
ret = ks8920_tx_proc(dev); // call the tx process routine
mark_bh(NET_BH);
}
dev->interrupt = 0;
clear_bit(0, (void*)&sp->in_interrupt);
// clear interrupt register
ks_outl( Int_Src, IntSrc_IntMacTx | IntSrc_IntMacRx | IntSrc_IntEarNot | IntSrc_IntExBD | IntSrc_IntTxCtlCmp | IntSrc_F
DAEx | IntSrc_BLEx | IntSrc_NRAbt ); if(ret) // error( underrun or Rx_Stat=0 )
ks8920_sw_reset(dev);
// Enable Interrupt
ks_outl(DMA_Ctl, reg_value & (~DMACtl_IntMask));
return;
}
/*
tx interrupt process routine.
This routine is called by ks8920_interrupt();
*/
static int ks8920_tx_proc( struct device *dev )
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
int state, entry;
int ret=0;
while( sp->deq_tx != sp->enq_tx ) {
entry = sp->deq_tx;
if( sp->tx_ring[entry].u.s.COwnsFD == FD_OWNER_CONTROLLER )
break;
dev_free_skb( sp->tx_ring[entry].tx_skbuff); /* release the skb buffer */
sp->tx_ring[entry].tx_skbuff = 0;
state=sp->tx_ring[entry].stat;
sp->tx_ring[entry].length = 0;
sp->tx_ring[entry].u.fdctl = 0;
if( state & TxStat_Comp ) // transmit complete
sp->stats.tx_packets++;
if( state & TxStat_ExColl ) // excessive collision
sp->stats.tx_aborted_errors++;
if( state & TxStat_LateColl ) // late collision
sp->stats.tx_window_errors++;
if( state & TxStat_Under ) { // underrun error
sp->stats.tx_fifo_errors++;
}
if( state & TxStat_LostCrs ) // lost carrier sense
sp->stats.tx_fifo_errors++;
if( state & TxStat_TxColl ) // transmit collision count
sp->stats.collisions += (state & TxStat_TxColl);
sp->deq_tx = (sp->deq_tx+1)%TX_RING_SIZE; // increment dequeue pointer
sp->tx_full = 0;
}
return ret;
}
/*
rx interrupt process routine.
This routine is called by ks8920_interrupt();
*/
static int ks8920_rx_proc(struct device *dev)
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
int entry = sp->deq_rx % RX_RING_SIZE;
int state;
int ret=0;
while(1) {
struct sk_buff *skb;
if( sp->rx_ring[entry].u.s.COwnsFD == FD_OWNER_CONTROLLER ) {
break;
}
if( (state=sp->rx_ring[entry].stat) & RxStat_Good ) {
// receive good completion
int pkt_len = sp->rx_ring[entry].freebufflist[0].length;
struct sk_buff *skb = sp->rx_skbuff[entry];
if (skb == NULL) {
printk("%s: Inconsistent Rx descriptor chain.", dev->name);
break;
}
sp->rx_skbuff[entry] = NULL;
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
sp->stats.rx_packets++;
netif_rx(skb); // send the socket data to kernel
}
else {
// chip bug process ( rx state =0, long packet process )
if( !state || state&(RxStat_OverFlow|RxStat_CRCErr|RxStat_LongErr) ) {
ret = 1;
}
// The frame was received with errors:
// Update the statistics based on the Receive status.
if( state & RxStat_AlignErr ) //alignment Error
sp->stats.rx_frame_errors++;
if( state & RxStat_CRCErr ) // CRC Error
sp->stats.rx_crc_errors++;
if( state & RxStat_OverFlow ) // Overflow Error
sp->stats.rx_fifo_errors++;
if( state & RxStat_LongErr ) // long packet error ( longer than 1518 byte )
sp->stats.rx_length_errors++;
break;
}
// Initialize Buffer
skb = dev_alloc_skb(MAX_BUFFER_SIZE); // socket buffer allocate
sp->rx_skbuff[entry] = skb;
if(skb == NULL) {
printk("%s: dev_alloc_skb() error", dev->name);
break;
}
skb->dev = dev; /* Mark as being used by this device. */
/* receive ring and buffer list initialize */
#ifndef BLIST_CQUEUE_MODE
sp->rx_bufflist.freebufflist[entry].data = virt_to_bus( skb->tail ); // data?
sp->rx_bufflist.freebufflist[entry].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_bufflist.freebufflist[entry].length = MAX_BUFFER_SIZE;
sp->rx_bufflist.freebufflist[entry].RxBDID= entry;
sp->rx_ring[entry].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_ring[entry].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_ring[entry].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
#else
sp->rx_bufflist[entry].freebufflist[0].data = virt_to_bus( skb->tail ); // data?
sp->rx_bufflist[entry].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_bufflist[entry].freebufflist[0].length = MAX_BUFFER_SIZE;
sp->rx_bufflist[entry].freebufflist[0].RxBDID= entry;
sp->rx_ring[entry].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_ring[entry].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_ring[entry].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
// for buffer list hang
sp->rx_bufflist[entry ? entry-1 : RX_RING_SIZE-1 ].u.s.COwnsFD = FD_OWNER_CONTROLLER;
#endif
entry = sp->deq_rx = (sp->deq_rx+1) % RX_RING_SIZE;
}
sp->last_rx_time = jiffies;
return ret;
}
/*
generate the software reset
*/
static void ks8920_sw_reset( struct device *dev )
{
int reg_value;
int i;
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
#ifdef KDEBUG
printk("ks8920_sw_reset()");
print_register_map(dev);
#endif
// Reset ks8920
reg_value = ks_inl( MAC_Ctl );
ks_outl( MAC_Ctl, reg_value | MACCtl_Reset );
while( ks_inl( MAC_Ctl ) & MACCtl_Reset ); // check the s/w reset completion
// tx ring initialize
for(i=0;i<TX_RING_SIZE;i++) { // tx ring re-initialize
if( sp->tx_ring[i].tx_skbuff == 0 )
continue;
dev_free_skb( sp->tx_ring[i].tx_skbuff); //* release the skb buffer */
sp->tx_ring[i].tx_skbuff = 0;
sp->tx_ring[i].length = 0;
sp->tx_ring[i].u.fdctl = 0;
sp->tx_ring[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_SYSTEM;
sp->tx_ring[i].freebufflist[1].u.s.COwnsFD = BD_OWNER_SYSTEM;
sp->tx_ring[i].u.s.COwnsFD = FD_OWNER_SYSTEM;
}
// rx ring and buffer list initialize
#ifndef BLIST_CQUEUE_MODE
for(i=0;i<RX_RING_SIZE;i++) { // rx ring re-initialize
sp->rx_bufflist.freebufflist[i].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_bufflist.freebufflist[i].length = MAX_BUFFER_SIZE;
sp->rx_bufflist.freebufflist[i].RxBDID= i;
sp->rx_ring[i].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
}
#else
for(i=0;i<RX_RING_SIZE;i++) { // rx ring re-initialize
sp->rx_bufflist[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_bufflist[i].freebufflist[0].length = MAX_BUFFER_SIZE;
sp->rx_bufflist[i].freebufflist[0].RxBDID= i;
sp->rx_ring[i].u.s.COwnsFD = FD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[0].u.s.COwnsFD = BD_OWNER_CONTROLLER;
sp->rx_ring[i].freebufflist[1].u.s.COwnsFD = BD_OWNER_CONTROLLER;
}
// for buffer list hang
sp->rx_bufflist[RX_RING_SIZE-1].u.s.COwnsFD = FD_OWNER_SYSTEM;
#endif
// register initialize
ks_outl( TxFrmPtr, virt_to_bus( &sp->tx_ring[0] ) );
#ifndef BLIST_CQUEUE_MODE
ks_outl( BLFrmPtr, virt_to_bus( &sp->rx_bufflist) );
#else
ks_outl( BLFrmPtr, virt_to_bus( &sp->rx_bufflist[0]) );
#endif
ks_outl( Rx_Ctl, RxCtl_RxEn | RxCtl_EnAlign | RxCtl_EnCRCErr | RxCtl_EnOver | RxCtl_EnLongErr | RxCtl_EnGood );
ks_outl( Tx_Ctl, TxCtl_EnComp | TxCtl_TxEn );
sp->enq_tx = sp->deq_tx = sp->enq_rx = sp->deq_rx =0;
dev->tbusy = 0;
sp->tx_full = 0;
}
/* Media monitoring and control. */
static void ks8920_timer(unsigned long data)
{
struct device *dev = (struct device *)data;
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
int tickssofar = jiffies - sp->last_rx_time;
if (tickssofar > 10 * HZ ) {
/* We haven't received a packet in a Long Time. We might have been
bitten by the receiver hang bug. This can be cleared by software reset. */
ks8920_sw_reset(dev);
}
/* We must continue to monitor the media. */
sp->timer.expires = RUN_AT( 20 * HZ ); /* 10 sec. */
add_timer(&sp->timer);
}
/*
get statistics information
This routine is called by kernel.
*/
static struct enet_statistics *ks8920_get_stats(struct device *dev)
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
return &sp->stats;
}
/*
device ioctl function
This routine not used.
*/
static int ks8920_ioctl(struct device *dev, struct ifreq *rq, int cmd)
{
switch(cmd) {
default:
return -EOPNOTSUPP;
}
}
/*
device close function
*/
static int ks8920_close(struct device *dev)
{
struct ks8920_private *sp = (struct ks8920_private *)dev->priv;
int i;
int reg_value;
/* shutdown timer */
del_timer( &sp->timer );
dev->start = 0;
dev->tbusy = 1;
/* Disable interrupts */
reg_value = ks_inl( DMA_Ctl );
ks_outl( DMA_Ctl, reg_value | DMACtl_IntMask );
free_irq( dev->irq, dev );
/* Free all the skbuffs in the Rx*/
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb = sp->rx_skbuff[i];
sp->rx_skbuff[i] = 0;
/* Clear the Rx descriptors. */
if (skb)
dev_free_skb(skb);
}
MOD_DEC_USE_COUNT;
return 0;
}
#ifdef MODULE
/*
module initialize routine
This routine is called by insmod command.
*/
int init_module(void)
{
int found;
root_ks8920_dev = NULL;
found = ks8920_init(NULL);
return found ? 0 : -ENODEV;
}
/*
module down routine
This routine is called by rmmod command.
*/
void cleanup_module(void)
{
struct device *next_dev;
#ifdef KDEBUG
printk("samsung ethernet : cleanup_module()" );
#endif
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
while (root_ks8920_dev) {
next_dev = ((struct ks8920_private *)root_ks8920_dev->priv)->next_module;
unregister_netdev(root_ks8920_dev);
release_region(root_ks8920_dev->base_addr, KS8920_TOTAL_SIZE);
kfree(root_ks8920_dev);
root_ks8920_dev = next_dev;
}
}
#else /* not MODULE */
/*
kernel mode initialize routine
*/
int init_ks8920_probe(struct device *dev)
{
int adapters_found = 0;
adapters_found = ks8920_init(dev);
return adapters_found ? 0 : -ENODEV;
}
#endif /* MODULE */
/*
* Local variables:
compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c ks8920.c"
* SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c ks8920.c"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
* End:
*/