網(wǎng)卡驅(qū)動(CS8900)在ARM上的移植和測試2010-05-04 19:21:26| 分類: 嵌入式學(xué)習(xí) |字號 訂閱 1.先以模塊的方式
驅(qū)動源碼如下cs8900new.c(自己由源代碼修改的,只保留) #define VERSION_STRING "Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410//SMDK2440)" #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/version.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/ioport.h> #include <linux/pm.h> #include <linux/irq.h> #include <linux/fs.h> #include <asm/irq.h> #include <mach/hardware.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <asm/mach-types.h> //#include "asm/arch/smdk2410.h" /* ++ */ #include "cs8900.h" static struct net_device *dev_cs8900; spinlock_t mylock; #define MAX_EEPROM_SIZE 256 static inline u16 cs8900_read (struct net_device *dev,u16 reg) { outw (reg,dev->base_addr + PP_Address); return (inw (dev->base_addr + PP_Da } static inline void cs8900_write (struct net_device *dev,u16 reg,u16 value) { outw (reg,dev->base_addr + PP_Address); outw (value,dev->base_addr + PP_Da } static inline void cs8900_set (struct net_device *dev,u16 reg,u16 value) { cs8900_write (dev,reg,cs8900_read (dev,reg) | value); } static inline void cs8900_clear (struct net_device *dev,u16 reg,u16 value) { cs8900_write (dev,reg,cs8900_read (dev,reg) & ~value); } static inline void cs8900_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length) { insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2); } static inline void cs8900_frame_write (struct net_device *dev,struct sk_buff *skb) { outsw (dev->base_addr,skb->da } static void cs8900_receive (struct net_device *dev) { u16 status,length; struct sk_buff *skb; status = cs8900_read (dev,PP_RxStatus); length = cs8900_read (dev,PP_RxLength); if (!(status & RxOK)) return; if ((skb = dev_alloc_skb (length + 2)) == NULL) return; skb->dev = dev; skb_reserve (skb,2); cs8900_frame_read (dev,skb,length); skb->protocol = eth_type_trans (skb,dev); netif_rx (skb); } static int cs8900_send_start (struct sk_buff *skb,struct net_device *dev) { u16 status; spin_lock_irq(&mylock); netif_stop_queue (dev); cs8900_write (dev,PP_TxCMD,TxStart (After5)); cs8900_write (dev,PP_TxLength,skb->len); status = cs8900_read (dev,PP_BusST); if ((status & TxBidErr)) { spin_unlock_irq(&mylock); printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len); return (1); } if (!(status & Rdy4TxNOW)) { spin_unlock_irq(&mylock); printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name); return (1); } cs8900_frame_write (dev,skb); spin_unlock_irq(&mylock); dev_kfree_skb (skb); return (0); } static irqreturn_t cs8900_interrupt (int irq,void *id) /* ++ */ { volatile u16 status; struct net_device *dev = (struct net_device *) id; while ((status = cs8900_read (dev, PP_ISQ))) { switch (RegNum (status)) { case RxEvent: cs8900_receive (dev); break; case TxEvent: netif_wake_queue (dev); break; case BufEvent: if ((RegContent (status) & TxUnderrun)) { netif_wake_queue (dev); } break; } } return IRQ_HANDLED; } static int cs8900_start (struct net_device *dev) { int result; set_irq_type(dev->irq, IRQF_TRIGGER_RISING); cs8900_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA); cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE); cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE); cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON); cs8900_set (dev,PP_BusCTL,EnableRQ); udelay(200); if ((result = request_irq (dev->irq, &cs8900_interrupt, 0, dev->name, dev)) < 0) { printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name, dev->irq); return (result); } netif_start_queue (dev); return (0); } static int cs8900_stop (struct net_device *dev) { /* disable ethernet controller */ cs8900_write (dev,PP_BusCTL,0); cs8900_write (dev,PP_TestCTL,0); cs8900_write (dev,PP_SelfCTL,0); cs8900_write (dev,PP_LineCTL,0); cs8900_write (dev,PP_BufCFG,0); cs8900_write (dev,PP_TxCFG,0); cs8900_write (dev,PP_RxCTL,0); cs8900_write (dev,PP_RxCFG,0); /* uninstall interrupt handler */ free_irq (dev->irq,dev); /* stop the queue */ netif_stop_queue (dev); return (0); } int __init cs8900_probe(struct net_device *dev) { int i, value; printk(VERSION_STRING); ether_setup (dev); dev->open = cs8900_start; dev->stop = cs8900_stop; dev->hard_start_xmit = cs8900_send_start; //define mac_addr 協(xié)議棧使用的 dev->dev_addr[0] = 0x08; dev->dev_addr[1] = 0x00; dev->dev_addr[2] = 0x3e; dev->dev_addr[3] = 0x26; dev->dev_addr[4] = 0x0a; dev->dev_addr[5] = 0x5b; dev->if_port = IF_PORT_10BASET; dev->base_addr = 0xe0000000 + 0x300; dev->irq = IRQ_EINT9; spin_lock_init(&mylock); dev_cs8900 = dev; if (request_region(dev->base_addr,16,dev->name) == NULL) { printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr); return (-EIO); } if ((value = cs8900_read (dev,PP_ProductID)) != EISA_REG_CO printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value); return (-ENXIO); } cs8900_write (dev,PP_IntNum,0);//選擇CS8900的0號中斷有效 for (i = 0; i < ETH_ALEN; i += 2) cs8900_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));//這個(gè)MAC地址寫到了PP_IA寄存器中,作用是地址過濾。 return (0); } static int __init cs8900_init (void) { struct net_device *dev; dev = alloc_etherdev(0); if(!dev) { printk("unable to alloc new ethernet\n"); return -1; } strcpy(dev->name, "eth%d"); dev->init = cs8900_probe; return(register_netdev(dev)); } static void __exit cs8900_cleanup (void) { unregister_netdev(dev_cs8900); release_region(dev_cs8900->base_addr, 16); free_netdev(dev_cs8900); } MODULE_AUTHOR ("zhuwensheng"); MODULE_DESCRIPTION (VERSION_STRING); MODULE_LICENSE ("GPL"); module_init (cs8900_init); module_exit (cs8900_cleanup); cs8900.h內(nèi)容如下 #ifndef CS8900_H #define CS8900_H /* * linux/drivers/net/cs8900.h * * Author: Abraham van der Merwe <abraham at 2d3d.co.za> * * A Cirrus Logic CS8900A driver for Linux * based on the cs89x0 driver written by Russell Nelson, * Donald Becker, and others. * * This source co * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. */ /* * Ports */ #define PP_Address 0x0a /* PacketPage Pointer Port (Section 4.10.10) */ #define PP_Da /* * Registers */ #define PP_ProductID 0x0000 /* Section 4.3.1 Product Identification Co #define PP_MemBase 0x002c /* Section 4.9.2 Memory Base Address Register */ #define PP_IntNum 0x0022 /* Section 3.2.3 Interrupt Number */ #define PP_EEPROMCommand 0x0040 /* Section 4.3.11 EEPROM Command */ #define PP_EEPROMData 0x0042 /* Section 4.3.12 EEPROM Da #define PP_RxCFG 0x0102 /* Section 4.4.6 Receiver Configuration */ #define PP_RxCTL 0x0104 /* Section 4.4.8 Receiver Control */ #define PP_TxCFG 0x0106 /* Section 4.4.9 Transmit Configuration */ #define PP_BufCFG 0x010a /* Section 4.4.12 Buffer Configuration */ #define PP_LineCTL 0x0112 /* Section 4.4.16 Line Control */ #define PP_SelfCTL 0x0114 /* Section 4.4.18 Self Control */ #define PP_BusCTL 0x0116 /* Section 4.4.20 Bus Control */ #define PP_TestCTL 0x0118 /* Section 4.4.22 Test Control */ #define PP_ISQ 0x0120 /* Section 4.4.5 Interrupt Status Queue */ #define PP_TxEvent 0x0128 /* Section 4.4.10 Transmitter Event */ #define PP_BufEvent 0x012c /* Section 4.4.13 Buffer Event */ #define PP_RxMISS 0x0130 /* Section 4.4.14 Receiver Miss Counter */ #define PP_TxCOL 0x0132 /* Section 4.4.15 Transmit Collision Counter */ #define PP_SelfST 0x0136 /* Section 4.4.19 Self Status */ #define PP_BusST 0x0138 /* Section 4.4.21 Bus Status */ #define PP_TxCMD 0x0144 /* Section 4.4.11 Transmit Command */ #define PP_TxLength 0x0146 /* Section 4.5.2 Transmit Length */ #define PP_IA 0x0158 /* Section 4.6.2 Individual Address (IEEE Address) */ #define PP_RxStatus 0x0400 /* Section 4.7.1 Receive Status */ #define PP_RxLength 0x0402 /* Section 4.7.1 Receive Length (in bytes) */ #define PP_RxFrame 0x0404 /* Section 4.7.2 Receive Frame Location */ #define PP_TxFrame 0x0a00 /* Section 4.7.2 Transmit Frame Location */ /* * Values */ /* PP_IntNum */ #define INTRQ0 0x0000 #define INTRQ1 0x0001 #define INTRQ2 0x0002 #define INTRQ3 0x0003 /* PP_ProductID */ #define EISA_REG_CO #define REVISION(x) (((x) & 0x1f00) >> 8) #define VERSION(x) ((x) & ~0x1f00) #define CS8900A 0x0000 #define REV_B 7 #define REV_C 8 #define REV_D 9 /* PP_RxCFG */ #define Skip_1 0x0040 #define StreamE 0x0080 #define RxOKiE 0x0100 #define RxDMAonly 0x0200 #define AutoRxDMAE 0x0400 #define BufferCRC 0x0800 #define CRCerroriE 0x1000 #define RuntiE 0x2000 #define ExtradataiE 0x4000 /* PP_RxCTL */ #define IAHashA 0x0040 #define PromiscuousA 0x0080 #define RxOKA 0x0100 #define MulticastA 0x0200 #define IndividualA 0x0400 #define BroadcastA 0x0800 #define CRCerrorA 0x1000 #define RuntA 0x2000 #define ExtradataA 0x4000 /* PP_TxCFG */ #define Loss_of_CRSiE 0x0040 #define SQErroriE 0x0080 #define TxOKiE 0x0100 #define Out_of_windowiE 0x0200 #define JabberiE 0x0400 #define AnycolliE 0x0800 #define T16colliE 0x8000 /* PP_BufCFG */ #define SWint_X 0x0040 #define RxDMAiE 0x0080 #define Rdy4TxiE 0x0100 #define TxUnderruniE 0x0200 #define RxMissiE 0x0400 #define Rx128iE 0x0800 #define TxColOvfiE 0x1000 #define MissOvfloiE 0x2000 #define RxDestiE 0x8000 /* PP_LineCTL */ #define SerRxON 0x0040 #define SerTxON 0x0080 #define AUIonly 0x0100 #define AutoAUI_10BT 0x0200 #define ModBackoffE 0x0800 #define PolarityDis 0x1000 #define L2_partDefDis 0x2000 #define LoRxSquelch 0x4000 /* PP_SelfCTL */ #define RESET 0x0040 #define SWSuspend 0x0100 #define HWSleepE 0x0200 #define HWStandbyE 0x0400 #define HC0E 0x1000 #define HC1E 0x2000 #define HCB0 0x4000 #define HCB1 0x8000 /* PP_BusCTL */ #define ResetRxDMA 0x0040 #define DMAextend 0x0100 #define UseSA 0x0200 #define MemoryE 0x0400 #define DMABurst 0x0800 #define IOCHRDYE 0x1000 #define RxDMAsize 0x2000 #define EnableRQ 0x8000 /* PP_TestCTL */ #define DisableLT 0x0080 #define ENDECloop 0x0200 #define AUIloop 0x0400 #define DisableBackoff 0x0800 #define FDX 0x4000 /* PP_ISQ */ #define RegNum(x) ((x) & 0x3f) #define RegContent(x) ((x) & ~0x3d) #define RxEvent 0x0004 #define TxEvent 0x0008 #define BufEvent 0x000c #define RxMISS 0x0010 #define TxCOL 0x0012 /* PP_RxStatus */ #define IAHash 0x0040 #define Dribblebits 0x0080 #define RxOK 0x0100 #define Hashed 0x0200 #define IndividualAdr 0x0400 #define Broadcast 0x0800 #define CRCerror 0x1000 #define Runt 0x2000 #define Extradata 0x4000 #define HashTableIndex(x) ((x) >> 0xa) /* PP_TxCMD */ #define After5 0 #define After381 1 #define After1021 2 #define AfterAll 3 #define TxStart(x) ((x) << 6) #define Force 0x0100 #define On #define InhibitCRC 0x1000 #define TxPadDis 0x2000 /* PP_BusST */ #define TxBidErr 0x0080 #define Rdy4TxNOW 0x0100 /* PP_TxEvent */ #define Loss_of_CRS 0x0040 #define SQEerror 0x0080 #define TxOK 0x0100 #define Out_of_window 0x0200 #define Jabber 0x0400 #define T16coll 0x8000 #define TX_collisions(x) (((x) >> 0xb) & ~0x8000) /* PP_BufEvent */ #define SWint 0x0040 #define RxDMAFrame 0x0080 #define Rdy4Tx 0x0100 #define TxUnderrun 0x0200 #define RxMiss 0x0400 #define Rx128 0x0800 #define RxDest 0x8000 /* PP_RxMISS */ #define MissCount(x) ((x) >> 6) /* PP_TxCOL */ #define ColCount(x) ((x) >> 6) /* PP_SelfST */ #define T3VActive 0x0040 #define INITD 0x0080 #define SIBUSY 0x0100 #define EEPROMpresent 0x0200 #define EEPROMOK 0x0400 #define ELpresent 0x0800 #define EEsize 0x1000 /* PP_EEPROMCommand */ #define EEWriteEnable 0x00F0 #define EEWriteDisable 0x0000 #define EEWriteRegister 0x0100 #define EEReadRegister 0x0200 #define EEEraseRegister 0x0300 #define ELSEL 0x0400 #endif /* #ifndef CS8900_H */ Makefile 內(nèi)容如下: ifneq ($(KERNELRELEASE),) obj-m :=drive.o else KERNEL SRC := /usr/src/linux-headers-2.6.28-18-generic modules: make -C $(KERNEL SRC) SUBDIRS=$(PWD) $@ clean: rm -f *.o *.ko *.mod.c .*.cmd *~ *.order *.symvers *.markers endif SRC路徑說明:以上的路徑是PC機(jī)上LINUX源代碼路徑,這樣編譯出來的是在PC上運(yùn)行的(其他驅(qū)動一樣),要編譯成在ARM上運(yùn)行的模塊,SRC改為/xxx/xxx,代表的是編譯成能在ARM上運(yùn)行的kernel后的內(nèi)核源代碼目錄,,如我的是/home/akaedu/linux-2.6.27,這樣再執(zhí)行make編譯驅(qū)動模塊,,生成可以插入ARM上內(nèi)核的模塊cs8900.ko (2)安裝文件包lrzsz, sudo apt-get install lrzsz 配置minicom,,啟動開發(fā)板,從nand 啟動,,首先要保證文件nand上的文件系統(tǒng)是可寫的,,如yaffs,而cramfs文件系統(tǒng)是只讀文件系統(tǒng),。把cs8900.ko復(fù)制到家目錄下(我的是/home/akaedu) 輸入命令:rx cs8900.ko 此時(shí)minicom等待文件的輸入,,按ctrl+a,然后按z,再按s,,選擇xmodem,此時(shí)打開的是家目錄,,光標(biāo)移到cs8900.ko,然后按空格鍵選中文件,,再回車鍵,,文件開始傳輸。傳輸結(jié)束后,,在minicon上ls下,,看到了文件cs8900.ko,。 在minicom上執(zhí)行命令 insmod cs8900.ko,打印了驅(qū)動程序第一行定義的宏,,說明插入成功,。 啟動網(wǎng)卡并為它設(shè)置個(gè)IP,輸入ifconfig eth0 192.168.0.210,此時(shí)網(wǎng)卡被激活,,可輸入ifconfig 查看是否正確,。 測試能否ping通主機(jī), ping 192.168.0.5(主機(jī)IP) (3)掛載主機(jī)上的目錄到開發(fā)板 主機(jī)上安裝nfs網(wǎng)絡(luò)服務(wù),,sudo aot-get install nfs-kernel-server 重啟nfs服務(wù) ,sudo /etc/init.d/nfs-kernel-server restart 編輯主機(jī)上的 /etc/exports文件,加入主機(jī)上被掛載的目錄,,如/home/akaedu/root,格式如下: /home/akaedu/root *(rw,sync,no_root_squash) *表示允許所有IP訪問,,括號里內(nèi)容表示擁有讀寫,同步,,和root權(quán)限,。 然后在minicom上輸入命令 :mount -t nfs -o nolock 192.168.0.5:/home/akaedu/root /mnt (主機(jī)IP) 這樣掛載到了目標(biāo)板上的mnt目錄下,可以進(jìn)入mnt目錄查看是否成功,。 (2)將驅(qū)動加入內(nèi)核源代碼并靜態(tài)編譯 將cs8900new.c和cs8900.h(由于cs8900.h就來自這個(gè)目錄,,可以不用拷貝)放入內(nèi)核源代碼目錄,如/home/akaedu/linux-2.6.27/drivers/net/arm,,編輯這個(gè)目錄下的Kconfig 和Makefile,參照原有的cs8900配置,,在Kconfig 加入 config MY_OPTION tristate "my option" config CS8900 tristate "CS8900A support" depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_AKAE2440) ---help--- Support for CS8900A chipset based Ethernet cards. If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from <http://www./docs.html#howto> as well as <file:Documentation/networking/cs89x0.txt>. To compile this driver as a module, choose M here. The module will be called cs89x0. config NEW_CS8900 tristate "my CS8900 support" depends on NET_PCI && (ISA || MACH_IXDP2351 || ARCH_IXDP2X01 || ARCH_PNX010X || MACH_AKAE2440)&&MY_OPTION |
|