>> Read No. 45424 article  
랜카드 어떻게 붙여요 ?

등록 2002-03-23 15:58:00     조회 2
이름 헛발질    

		리눅스 설치 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:
 */
이름
암호
Register [ localhost 목록보기 윗글 아랫글
글쓰기
답장쓰기 수정 삭제
정규표현식 [ 상세 검색 ]
페이지로딩: [ 0.86 초 ] 작업시간: [ 0.29 초 ]

Copyleft 1999-2026 by JSBoard Open Project
Theme Designed by IDOO All right reserved
[TOP]

적수네 동네
+
| 적수네 동네
| 공부방
| 리눅스 잡지 서고
| LSN 소스
| 링크 모음
+---+
게시판
+
| 떠들어보세!
| 질문과 답변
| 새소식과 정보
| 1원짜리 팁?
| 대화방
+---+
칼럼?
+
| 세하 훔쳐보기
| Welcome2nite
| 혜진의 염장판
+---+
리눅스 상표권
+
| 반대 서명란
| 토론 게시판
+---+
GNU
+
| GNU 선언문
| GNU GPL
| GNU 미러 목록
+---+
프로젝트?
+
| 리눅스카운터
| RC5DES
| 실질헌법 제작
+---+
커널 소식
+
| 안정 버젼: 2.4.14
+---+
테마 선택
+
LSN 방송국?
+
| OFF AIR
+---+
회원
+
| 로그인
+---+
[ 적수네 동네 ] [ 리눅스 상표권 독점 반대 ] [ 한글 리눅스 문서 프로젝트 ] [ KrLine ] [ 사랑넷 ] [ Valid HTML 4.0! ] [ SlashDot ] [ Freshmeat ]
Copyleft (C) 1998-2001 Byeong-Chan Kim . License
TIME: 1791652515
System by WYZsoft, HDD by I.O.Linux, Mizi Research, Embryo, WOWLINUX, Domain by SarangNet, Network by KrLine.