arm linux网卡驱动


Linux-3.6.6很好的支持了DM9000,因此对于S3C2440芯片来说无需进行任何修改,甚至连menuconfig都已经默认配置了网卡驱动。 但我们还需要设置网卡的MAC和IP等信息。有许多方法可以实现网卡的设置,在这里我们选择一种比较简单的方法——修改根文件系统的启动脚本文件rcS。 在根文件etc/init.d/rcS文件中添加下列语句:

              /sbin/ifconfig lo 127.0.0.1

              /sbin/ifconfig eth0 hw ether 5e:f7:90:82:66:28

              /sbin/ifconfig eth0 192.168.1.234 up

              route add default gw 192.168.1.1
              
这样就完成了网卡的移植。我们测试一下网卡。在启动开发板时,系统会打印出类似下列信息:
              dm9000 dm9000: eth0: link down

              dm9000 dm9000: eth0: link up, 100Mbps,full-duplex, lpa 0xCDE1
              
如果在提示符下输入ifconfig命令,则:
              [root@zhaocj /]#ifconfig

              eth0 Link encap:Ethernet HWaddr5E:F7:90:82:66:28

              inet addr:192.168.1.234 Bcast:192.168.1.255 Mask:255.255.255.0

              UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1

              RX packets:51 errors:0 dropped:0 overruns:0 frame:0

              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

              collisions:0 txqueuelen:1000

              RX bytes:5324 (5.1 KiB) TXbytes:0 (0.0 B)

              Interrupt:51 Base address:0x2300



              lo Link encap:Local Loopback

              inet addr:127.0.0.1 Mask:255.0.0.0

              UP LOOPBACK RUNNING MTU:16436 Metric:1

              RX packets:0 errors:0 dropped:0 overruns:0 frame:0

              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0

              collisions:0 txqueuelen:0

              RX bytes:0 (0.0 B) TX bytes:0(0.0 B)
              
另外主机和开发板都可以互相ping通对方。
下面我们就简单介绍一下DM9000网卡驱动。
在arch/arm/mach-s3c24xx/mach-zhaocj2440.c文件内定义了DM9000网卡平台设备:
              static struct resource zhaocj2440_dm9k_resource[]= {

              [0]= DEFINE_RES_MEM(MACH_ZHAOCJ2440_DM9K_BASE, 4),

              [1]= DEFINE_RES_MEM(MACH_ZHAOCJ2440_DM9K_BASE + 4, 4),

              [2]= DEFINE_RES_NAMED(IRQ_EINT7, 1, NULL, IORESOURCE_IRQ \

              |IORESOURCE_IRQ_HIGHEDGE),

              };



              /*

              *The DM9000 has no eeprom, and it's MAC address is set by

              *the bootloader before starting the kernel.

              */

              static struct dm9000_plat_data zhaocj2440_dm9k_pdata= {

              .flags = (DM9000_PLATF_16BITONLY |DM9000_PLATF_NO_EEPROM),

              };



              static struct platform_device zhaocj2440_device_eth= {

              .name = "dm9000",

              .id = -1,

              .num_resources = ARRAY_SIZE(zhaocj2440_dm9k_resource),

              .resource =zhaocj2440_dm9k_resource,

              .dev ={

              .platform_data = &zhaocj2440_dm9k_pdata,

              },

              };
              
并把网卡平台设备添加进平台设备数组内:
              static structplatform_device *zhaocj2440_devices[] __initdata = {

              ……

              &zhaocj2440_device_eth,

              ……

              };
              
在drivers/net/ethernet/davicom/dm9000.c文件内定义了DM9000网卡平台驱动:
              static struct platform_driver dm9000_driver= {

              .driver = {

              .name = "dm9000",

              .owner = THIS_MODULE,

              .pm = &dm9000_drv_pm_ops,

              },

              .probe = dm9000_probe,

              .remove = __devexit_p(dm9000_drv_remove),

              };
              
下面分析一下探测函数dm9000_probe:
              static int__devinit

              dm9000_probe(structplatform_device *pdev)

              {

              struct dm9000_plat_data *pdata =pdev->dev.platform_data;

              struct board_info*db; /* Point a board informationstructure */

              structnet_device *ndev;

              constunsigned char *mac_src;

              intret = 0;

              intiosize;

              inti;

              u32id_val;



              /*Init network device */

              //初始化网络设备结构体net_device

              ndev= alloc_etherdev(sizeof(struct board_info));

              if(!ndev)

              return-ENOMEM;



              SET_NETDEV_DEV(ndev,&pdev->dev);



              dev_dbg(&pdev->dev,"dm9000_probe()\n");



              /*setup board info structure */

              //设置板块信息结构体——db

              db= netdev_priv(ndev);



              db->dev= &pdev->dev;

              db->ndev= ndev;

              //自旋锁

              spin_lock_init(&db->lock);

              mutex_init(&db->addr_lock);

              //初始化工作队列

              INIT_DELAYED_WORK(&db->phy_poll,dm9000_poll_work);

              //提取出DM9000网卡资源,即zhaocj2440_dm9k_resource

              db->addr_res= platform_get_resource(pdev, IORESOURCE_MEM, 0); //地址

              db->data_res= platform_get_resource(pdev, IORESOURCE_MEM, 1); //数据

              db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ,0); //中断



              if(db->addr_res == NULL || db->data_res == NULL ||

              db->irq_res == NULL) {

              dev_err(db->dev,"insufficient resources\n");

              ret =-ENOENT;

              gotoout;

              }

              //获取网卡中断资源

              db->irq_wake= platform_get_irq(pdev, 1);

              if(db->irq_wake >= 0) {

              dev_dbg(db->dev,"wakeup irq %d\n", db->irq_wake);

              //申请网卡中断,中断函数为dm9000_wol_interrupt

              ret= request_irq(db->irq_wake, dm9000_wol_interrupt,

              IRQF_SHARED, dev_name(db->dev), ndev);

              if(ret) {

              dev_err(db->dev,"cannot get wakeup irq (%d)\n", ret);

              }else {



              /*test to see if irq is really wakeup capable */

              ret= irq_set_irq_wake(db->irq_wake, 1);

              if(ret) {

              dev_err(db->dev,"irq %d cannot set wakeup (%d)\n",

              db->irq_wake, ret);

              ret = 0;

              } else {

              irq_set_irq_wake(db->irq_wake,0);

              db->wake_supported = 1;

              }

              }

              }

              //申请网卡地址所需的内存空间

              iosize= resource_size(db->addr_res);

              db->addr_req= request_mem_region(db->addr_res->start, iosize,

              pdev->name);



              if(db->addr_req == NULL) {

              dev_err(db->dev,"cannot claim address reg area\n");

              ret= -EIO;

              gotoout;

              }

              //地址内存空间映射

              db->io_addr= ioremap(db->addr_res->start, iosize);



              if(db->io_addr == NULL) {

              dev_err(db->dev,"failed to ioremap address reg\n");

              ret= -EINVAL;

              gotoout;

              }

              //申请网卡数据所需的内存空间

              iosize= resource_size(db->data_res);

              db->data_req= request_mem_region(db->data_res->start, iosize,

              pdev->name);



              if(db->data_req == NULL) {

              dev_err(db->dev,"cannot claim data reg area\n");

              ret= -EIO;

              gotoout;

              }

              //数据内存空间映射

              db->io_data= ioremap(db->data_res->start, iosize);



              if (db->io_data == NULL) {

              dev_err(db->dev,"failed to ioremap data reg\n");

              ret= -EINVAL;

              gotoout;

              }



              /*fill in parameters for net-dev structure */

              //设置网络设备结构体——ndev

              ndev->base_addr= (unsigned long)db->io_addr;

              ndev->irq = db->irq_res->start;



              /*ensure at least we have a default set of IO routines */

              //确保至少有一种IO路由方式,即先设置一种缺省的IO路由

              dm9000_set_io(db,iosize);



              /*check to see if anything is being over-ridden */

              /*检查是否可以替代刚才设置的IO路由,由于我们在zhaocj2440_dm9k_pdata结构体内的flags元素定义了DM9000_PLATF_16BITONLY,
              所以缺省的IO路由方式会被替代,即执行dm9000_set_io(db, 2);*/

              if(pdata != NULL) {

              /*check to see if the driver wants to over-ride the

              * default IO width */



              if(pdata->flags & DM9000_PLATF_8BITONLY)

              dm9000_set_io(db,1);



              if(pdata->flags & DM9000_PLATF_16BITONLY)

              dm9000_set_io(db,2);



              if(pdata->flags & DM9000_PLATF_32BITONLY)

              dm9000_set_io(db,4);



              /*check to see if there are any IO routine

              * over-rides */



              if(pdata->inblk != NULL)

              db->inblk= pdata->inblk;



              if(pdata->outblk != NULL)

              db->outblk= pdata->outblk;



              if(pdata->dumpblk != NULL)

              db->dumpblk= pdata->dumpblk;



              db->flags= pdata->flags;

              }



              #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL

              db->flags|= DM9000_PLATF_SIMPLE_PHY;

              #endif

              //网卡复位

              dm9000_reset(db);



              /*try multiple times, DM9000 sometimes gets the read wrong */

              //多次读取DM9000的ID,以确保能够正确识别DM9000

              for (i = 0; i < 8; i++) { id_val=ior(db, DM9000_VIDL); id_val |=(u32)ior(db,DM9000_VIDH) << 8; id_val
                |=(u32)ior(db, DM9000_PIDL)<< 16; id_val |=(u32)ior(db, DM9000_PIDH) << 24; //判断读取的值是否为0x90000A46
                if(id_val==DM9000_ID) break; dev_err(db->dev,"read wrong id 0x%08x\n", id_val);

                }



                if(id_val != DM9000_ID) {

                dev_err(db->dev,"wrong id: 0x%08x\n", id_val);

                ret= -ENODEV;

                gotoout;

                }



                /*Identify what type of DM9000 we are working on */

                //读取CHIPR寄存器,获得type信息

                id_val= ior(db, DM9000_CHIPR);

                dev_dbg(db->dev,"dm9000 revision 0x%02x\n", id_val);



                switch(id_val) {

                caseCHIPR_DM9000A:

                db->type= TYPE_DM9000A;

                break;

                caseCHIPR_DM9000B:

                db->type= TYPE_DM9000B;

                break;

                default:

                dev_dbg(db->dev,"ID %02x => defaulting to DM9000E\n", id_val);

                db->type= TYPE_DM9000E;

                }



                /*dm9000a/b arecapable of hardware checksum offload */

                if(db->type == TYPE_DM9000A|| db->type == TYPE_DM9000B) {

                ndev->hw_features= NETIF_F_RXCSUM | NETIF_F_IP_CSUM;

                ndev->features|= ndev->hw_features;

                }



                /*from this point we assume that we have found a DM9000 */



                /*driver system function */

                //设置以太网卡设备,即把有关以太网设备的数据赋值给ndev

                ether_setup(ndev);



                ndev->netdev_ops = &dm9000_netdev_ops; //网卡设备操作集

                ndev->watchdog_timeo = msecs_to_jiffies(watchdog);

                ndev->ethtool_ops = &dm9000_ethtool_ops; //以太网卡操作集



                db->msg_enable = NETIF_MSG_LINK;

                db->mii.phy_id_mask = 0x1f;

                db->mii.reg_num_mask= 0x1f;

                db->mii.force_media = 0;

                db->mii.full_duplex = 0;

                db->mii.dev =ndev;

                db->mii.mdio_read = dm9000_phy_read;

                db->mii.mdio_write = dm9000_phy_write;



                //通过不同方式读取网卡的MAC

                mac_src= "eeprom";



                /*try reading the node address from the attached EEPROM */

                //从eeprom中读取MAC

                for(i = 0; i < 6; i +=2) dm9000_read_eeprom(db,i / 2, ndev->dev_addr+i);

                  /*如果从eeprom中读取MAC失败,则从平台设备中读取MAC,即提取出zhaocj2440_dm9k_pdata结构中的dev_addr数组元素*/

                  if(!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) {

                  mac_src= "platform data";

                  memcpy(ndev->dev_addr,pdata->dev_addr, 6);

                  }

                  /*如果到目前为止,还没有读取到正确的MAC,则从DM9000芯片的PAR寄存器内提取出MAC*/

                  if(!is_valid_ether_addr(ndev->dev_addr)) {

                  /*try reading from mac */



                  mac_src= "chip";

                  for (i = 0; i < 6; i++) ndev->dev_addr[i] =ior(db, i+DM9000_PAR);

                    }

                    /*如果上述方法都不行,则MAC值最终是要通过ifconfig来确定。但由于现在还没有执行ifconfig命令,因此系统在启动初始化的过程中,要随机分配MAC。*/

                    if(!is_valid_ether_addr(ndev->dev_addr)) {

                    dev_warn(db->dev,"%s: Invalid ethernet MAC address. Please "

                    "set using ifconfig\n",ndev->name);



                    eth_hw_addr_random(ndev);

                    mac_src= "random";

                    }



                    //把ndev保存为平台设备pdev的私有数据,以后可以用platform_get_drvdata获取该数据

                    platform_set_drvdata(pdev,ndev);

                    //注册网络设备

                    ret= register_netdev(ndev);

                    //在系统启动过程中,打印网卡相关数据

                    if(ret == 0)

                    printk(KERN_INFO"%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\n",

                    ndev->name,dm9000_type_to_char(db->type),

                    db->io_addr, db->io_data, ndev->irq,

                    ndev->dev_addr,mac_src);

                    return0;



                    out:

                    dev_err(db->dev,"not found (%d).\n", ret);



                    dm9000_release_board(pdev,db);

                    free_netdev(ndev);



                    returnret;

                    }



                    在dm9000_probe函数内定义了dm9000_netdev_ops:

                    static const struct net_device_opsdm9000_netdev_ops = {

                    .ndo_open = dm9000_open, //打开设备

                    .ndo_stop = dm9000_stop, //关闭设备

                    .ndo_start_xmit = dm9000_start_xmit, //开始发送数据

                    .ndo_tx_timeout = dm9000_timeout, //发送超时

                    .ndo_set_rx_mode = dm9000_hash_table, //设定多播列表

                    .ndo_do_ioctl = dm9000_ioctl, //io操作函数

                    .ndo_change_mtu = eth_change_mtu, //改变mtu

                    .ndo_set_features = dm9000_set_features,

                    .ndo_validate_addr = eth_validate_addr,

                    .ndo_set_mac_address = eth_mac_addr,

                    #ifdef CONFIG_NET_POLL_CONTROLLER

                    .ndo_poll_controller = dm9000_poll_controller,

                    #endif

                    };



                    我们分析几个重要的回调函数。

                    当执行ifconfig命令时,会调用dm9000_open函数,以打开网络接口

                    static int

                    dm9000_open(struct net_device *dev)

                    {

                    board_info_t*db = netdev_priv(dev);

                    unsignedlong irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;



                    if(netif_msg_ifup(db))

                    dev_dbg(db->dev,"enabling %s\n", dev->name);



                    /*If there is no IRQ type specified, default to something that

                    * may work, and tell the user that this is aproblem */



                    if(irqflags == IRQF_TRIGGER_NONE)

                    dev_warn(db->dev,"WARNING: no IRQ resource flags set.\n");



                    irqflags|= IRQF_SHARED;



                    /*GPIO0 on pre-activate PHY, Reg 1Fis not set by reset */

                    //将GPR寄存器的第0位清零,激活内部PHY

                    iow(db,DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */

                    mdelay(1);/* delay needs by DM9000B */



                    /*Initialize DM9000 board */

                    dm9000_reset(db); //复位DM9000

                    //初始化DM9000,主要就是配置DM9000的内部寄存器

                    dm9000_init_dm9000(dev);

                    //注册中断,中断函数为dm9000_interrupt,该中断负责收发数据

                    if(request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))

                    return-EAGAIN;



                    /*Init driver variable */

                    db->dbug_cnt= 0;

                    //检测mii接口的状态

                    mii_check_media(&db->mii,netif_msg_link(db), 1);

                    //允许上层协议使用该设备,开启发送队列

                    netif_start_queue(dev);

                    /*在dm9000_probe函数中,使用INIT_DELAYED_WORK初始化了一个工作队列,现在就调用它,即执行dm9000_poll_work函数,用以检测并显示网络信息*/

                    dm9000_schedule_poll(db);



                    return 0;

                    }



                    关闭网卡设备函数dm9000_stop:

                    static int

                    dm9000_stop(struct net_device *ndev)

                    {

                    board_info_t*db = netdev_priv(ndev);



                    if(netif_msg_ifdown(db))

                    dev_dbg(db->dev,"shutting down %s\n", ndev->name);

                    //终止phy_poll队列中被延迟的任务

                    cancel_delayed_work_sync(&db->phy_poll);

                    //关闭发送队列

                    netif_stop_queue(ndev);

                    //载波丢失

                    netif_carrier_off(ndev);



                    /*free interrupt */

                    free_irq(ndev->irq,ndev); //释放中断



                    dm9000_shutdown(ndev); //关闭网卡



                    return0;

                    }



                    发送数据函数dm9000_start_xmit:

                    static int

                    dm9000_start_xmit(struct sk_buff *skb,struct net_device *dev)

                    {

                    unsignedlong flags;

                    board_info_t*db = netdev_priv(dev);



                    dm9000_dbg(db,3, "%s:\n", __func__);



                    if(db->tx_pkt_cnt > 1)

                    returnNETDEV_TX_BUSY;

                    //获得自旋锁

                    spin_lock_irqsave(&db->lock,flags);



                    /*Move data to DM9000 TX RAM */

                    //根据io操作数据宽度,来设置MWCMD,即填充数据完毕后指针加1还是加2

                    writeb(DM9000_MWCMD,db->io_addr);

                    //将数据从sk_buff中复制到DM9000内部发送RAM(TX_Buffer)中

                    (db->outblk)(db->io_data,skb->data, skb->len);

                    dev->stats.tx_bytes +=skb->len; //统计发送的字节数



                    db->tx_pkt_cnt++; //发送计数

                    /*TX control: First packet immediately send, second packet queue */

                    if(db->tx_pkt_cnt == 1) {

                    //计数值为1,立即发送

                    dm9000_send_packet(dev,skb->ip_summed, skb->len);

                    } else {

                    /* Secondpacket */

                    //如果是第2个数据包,加入队列以后,停止发送

                    db->queue_pkt_len= skb->len;

                    db->queue_ip_summed= skb->ip_summed;

                    netif_stop_queue(dev);

                    }

                    //解自旋锁

                    spin_unlock_irqrestore(&db->lock,flags);



                    /*free this SKB */

                    dev_kfree_skb(skb);



                    returnNETDEV_TX_OK;

                    }



                    dm9000_start_xmit函数只负责发送一个数据包,那么如何发送第二个数据包呢?网卡又如何接收数据呢?
                    还记得在dm9000_open函数内,申请了一个中断,中断函数为dm9000_interrupt,该中断就是负责接收数据和发送多个数据包的任务:

                    static irqreturn_t dm9000_interrupt(intirq, void *dev_id)

                    {

                    struct net_device *dev = dev_id;

                    board_info_t *db = netdev_priv(dev);

                    int int_status;

                    unsigned long flags;

                    u8 reg_save;



                    dm9000_dbg(db, 3, "entering%s\n", __func__);



                    /* A realinterrupt coming */



                    /*holders of db->lock must always block IRQs */

                    //获取自旋锁

                    spin_lock_irqsave(&db->lock,flags);



                    /*Save previous register address */

                    //保存以前的寄存器地址

                    reg_save= readb(db->io_addr);



                    /*Disable all interrupts */

                    //禁止所有中断

                    iow(db,DM9000_IMR, IMR_PAR);



                    /*Got DM9000 interrupt status */

                    //得到中断状态,即是何种原因引起的中断

                    int_status= ior(db, DM9000_ISR); /* Got ISR */

                    iow(db,DM9000_ISR, int_status); /* ClearISR status */



                    if(netif_msg_intr(db))

                    dev_dbg(db->dev,"interrupt status %02x\n", int_status);



                    /*Received the coming packet */

                    //该中断为接收数据中断

                    if(int_status & ISR_PRS)

                    dm9000_rx(dev);



                    /*Trnasmit Interrupt check */

                    //该中断为发送数据结束中断

                    if(int_status & ISR_PTS)

                    dm9000_tx_done(dev,db);



                    if(db->type != TYPE_DM9000E) {

                    if(int_status & ISR_LNKCHNG) {

                    /*fire a link-change request */

                    schedule_delayed_work(&db->phy_poll,1);

                    }

                    }



                    /*Re-enable interrupt mask */

                    //开启中断

                    iow(db,DM9000_IMR, db->imr_all);



                    /*Restore previous register address */

                    writeb(reg_save,db->io_addr);



                    spin_unlock_irqrestore(&db->lock,flags);



                    returnIRQ_HANDLED;

                    }

                    
从上面函数的分析可以看出,当是接收数据触发的中断时,会调用dm9000_rx函数来接收数据,然后调用netif_rx函数把数据传输到上层网络协议中; 而当一个数据包发送完毕后也会触发该中断,并调用dm9000_tx_done函数。在该函数内,如果判断还有待发送的数据则调用执行dm9000_send_packet函数再次发送数据。 例如我们要发送两个数据包,则首先调用dm9000_start_xmit函数发送第一个数据包,当发送完毕后触发中断dm9000_interrupt, 并调用dm9000_tx_done函数发送第二个数据包,至此两个数据包都发送了出去。
————————————————
版权声明:本文为CSDN博主「zhaocj」的编辑整理,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaocj/article/details/21972375

硬件平台:FL2440
内核版本:2.6.39
主机平台:Ubuntu 11.04
内核版本:2.6.35
交叉编译器:arm-linux-gcc 4.3.2
原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6641579
1、DM9000网卡驱动的分析请见http://blog.csdn.net/yming0221/article/details/6609742
2、如果想自己调试DM9000网卡驱动,那么在编译内核之前将网卡驱动不要编译进内核,启动后自己编译并加载内核
由于一般的驱动或者程序是通过NFS挂载到开发板上的,所以,如果没有网卡驱动可以
(1)使用U盘作为中介来传输自己编译的DM9000网卡驱动(FL2440开发板U盘挂载)
(2)首先将正常可用的DM9000网卡驱动拷贝到开发板的目录下,每次想要调试自己的驱动之前,首先
insmod 正常的DM9000驱动
挂载NFS(mount -t nfs -o nolock ..................)
将主机上的自己编译的驱动通过NFS拷贝到开发板
卸载NFS(umount ...............)
rmmod 正常的DM9000驱动
insmod 自己编译的DM9000网卡驱动
网络测试
————————————————
版权声明:本文为CSDN博主「YongXMan」的编辑整理,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/geekcome/article/details/6641579

相关阅读:
linux基础
linux怎么学
linux和GNU
GNU Free Documentation License
最受欢迎的linux发行版
initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读