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