维汝堂集团:网卡驱动(CS8900)在ARM上的移植和测试

来源:百度文库 编辑:中财网 时间:2024/04/19 16:02:52

网卡驱动(CS8900)在ARM上的移植和测试  

2010-05-04 19:21:26|  分类: 嵌入式学习 |字号 订阅

1.先以模块的方式
    驱动源码如下cs8900new.c(自己由源代码修改的,只保留)
#define VERSION_STRING "Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410//SMDK2440)"

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include
#include
#include
#include
#include
//#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_Data));
}

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_Data);
}

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->data,(skb->len + 1) / 2);
}

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  协议栈使用的
    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_CODE) {//判断网卡是否存在 0x630e
        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));//这个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内容如下
#ifndef CS8900_H
#define CS8900_H

/*
 * linux/drivers/net/cs8900.h
 *
 * Author: Abraham van der Merwe
 *
 * A Cirrus Logic CS8900A driver for Linux
 * based on the cs89x0 driver written by Russell Nelson,
 * Donald Becker, and others.
 *
 * This source code is free software; you can redistribute it and/or
 * 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_Data            0x0c    /* PacketPage Data Port (Section 4.10.10) */

/*
 * Registers
 */

#define PP_ProductID        0x0000    /* Section 4.3.1   Product Identification Code */
#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 Data */
#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_CODE    0x630e
#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 Onecoll            0x0200
#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 内容如下:
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机上LINUX源代码路径,这样编译出来的是在PC上运行的(其他驱动一样),要编译成在ARM上运行的模块,SRC改为/xxx/xxx,代表的是编译成能在ARM上运行的kernel后的内核源代码目录,如我的是/home/akaedu/linux-2.6.27,这样再执行make编译驱动模块,生成可以插入ARM上内核的模块cs8900.ko
 (2)安装文件包lrzsz,  sudo apt-get install lrzsz
配置minicom,启动开发板,从nand 启动,首先要保证文件nand上的文件系统是可写的,如yaffs,而cramfs文件系统是只读文件系统。把cs8900.ko复制到家目录下(我的是/home/akaedu)

输入命令:rx cs8900.ko
此时minicom等待文件的输入,按ctrl+a,然后按z,再按s,选择xmodem,此时打开的是家目录,光标移到cs8900.ko,然后按空格键选中文件,再回车键,文件开始传输。传输结束后,在minicon上ls下,看到了文件cs8900.ko。
在minicom上执行命令 insmod cs8900.ko,打印了驱动程序第一行定义的宏,说明插入成功。

启动网卡并为它设置个IP,输入ifconfig eth0 192.168.0.210,此时网卡被激活,可输入ifconfig 查看是否正确。

测试能否ping通主机,
ping 192.168.0.5(主机IP)

(3)挂载主机上的目录到开发板
主机上安装nfs网络服务,sudo aot-get install nfs-kernel-server
重启nfs服务 ,sudo /etc/init.d/nfs-kernel-server restart

编辑主机上的 /etc/exports文件,加入主机上被挂载的目录,如/home/akaedu/root,格式如下:
/home/akaedu/root   *(rw,sync,no_root_squash)
*表示允许所有IP访问,括号里内容表示拥有读写,同步,和root权限。
然后在minicom上输入命令 :mount -t nfs -o nolock  192.168.0.5:/home/akaedu/root  /mnt
                                                                                      (主机IP)
这样挂载到了目标板上的mnt目录下,可以进入mnt目录查看是否成功。

(2)将驱动加入内核源代码并静态编译
将cs8900new.c和cs8900.h(由于cs8900.h就来自这个目录,可以不用拷贝)放入内核源代码目录,如/home/akaedu/linux-2.6.27/drivers/net/arm,编辑这个目录下的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
      as well as
      .

      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