




已阅读5页,还剩8页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
CS8900A网卡设备驱动程序分析基于ARM平台(上) 推荐给好友 打印 加入收藏 更新于2009-04-29 18:17:14 ARM device 网络驱动程序主要完成系统的初始化、数据包的发送和接收。在以前的内核版本中,网络设备的初始化主要由net_device数据结构中的init函数指针所指向的初始化函数来完成。在现在较新的2.6内核中,网络设备的初始化主要由device_driver数据结构中的probe函数指针所指向的函数来完成。数据包的发送和接收是实现Linux网络驱动程序中两个最关键的过程,对这两个过程处理的好坏将直接影响到驱动程序的整体运行质量。首先来分析CS8900A网卡设备驱动的初始化。 1初始化 CS8900A网卡设备驱动的初始化主要由device_driver数据结构中的probe函数指针所指向的初始化函数来完成,当内核启动或加载网络驱动模块的时候,就会调用这个初始化函数。该模块加载函数实现如下: 1 static int _init cirrus_init(void) 2 3 return driver_register(&cirrus_driver); 4 模块加载函数cirrus_init通过调用内核函数driver_register来注册CS8900A网卡设备驱动,driver_register函数的实现在内核文件中。对设备驱动程序进行注册和初始化是两件不同的事情。设备驱动程序应当尽快被注册,以便用户应用程序通过相应的设备文件使用它。通常设备驱动程序在最后可能的时刻才被初始化。事实上,初始化驱动程序意味着分配系统宝贵的资源,这些被分配的资源因此就对其他驱动程序不能用。关于注册的网络设备驱动结构cirrus_driver的定义如下: 1 static struct device_driver cirrus_driver = 2 .name = cirrus-cs89x0, 3 .bus = &platform_bus_type, 4 .probe = cirrus_drv_probe, 5 .remove = cirrus_remove, 6 .suspend = cirrus_suspend, 7 .resume = cirrus_resume, 8 ; 第1行,定义变量cirrus_driver为device_driver结构类型,关于device_driver结构的定义在文件中。第2行,定义设备驱动名称为cirrus-cs89x0。第3行,定义bus类型为platform_bus_type。第4行,定义probe函数为cirrus_drv_probe,也就是说该网络设备的初始化是由cirrus_drv_probe函数来完成的,下面会具体讲述这个函数。第5行,定义remove函数为cirrus_remove,该函数主要完成网络设备的退出功能。第6行,定义suspend函数为cirrus_suspend,用来实现设备驱动的挂起操作,一般不用实现。第7行,定义resume函数为cirrus_resume,该函数用来实现从挂起状态返回到继续执行状态,一般也不用实现。 现在来分析一下初始化函数cirrus_drv_probe的具体实现。在初始化函数中通过检测物理设备的硬件特征来侦测网络物理设备是否存在,然后再对设备进行资源配置,以及内存映射,接下来构造设备的net_device数据结构,并用检测到的数据对net_device中的变量初始化,最后向Linux内核注册该设备并申请内存空间。 1 int _init cirrus_drv_probe (struct device *dev) 2 3 struct platform_device *pdev = to_platform_device(dev); 4 struct resource *res; 5 unsigned int *addr; 6 int ret; 7 8 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 9 if (!res) 10 ret = -ENODEV; 11 goto out; 12 13 14 /* Request the regions. */ 15 if (!request_mem_region(res-start, 16, cirrus-cs89x0) 16 ret = -EBUSY; 17 goto out; 18 19 20 /* remap it. */ 21 addr = ioremap(res-start, res-end - res-start); 22 if (!addr) 23 ret = -ENOMEM; 24 goto release_1; 25 26 27 ndev = alloc_etherdev(sizeof(cirrus_t); 28 if (!ndev) 29 printk(cirrus-cs89x0: could not allocate device.n); 30 ret = -ENOMEM; 31 goto release_2; 32 33 34 SET_NETDEV_DEV(ndev, dev); 35 36 ndev-irq = platform_get_irq(pdev, 0); 37 printk(KERN_DEBUG cirrus: irq:%dn,ndev-irq); 38 39 dev_set_drvdata(dev, ndev); 40 41 ret = cirrus_probe(ndev, (unsigned long)addr); 42 if (ret != 0) 43 goto release_3; 44 return 0; 45 46 release_3: 47 dev_set_drvdata(dev, NULL); 48 free_netdev(ndev); 49 release_2: 50 iounmap(addr); 51 release_1: 52 release_mem_region(res-start, res-end - res-start); 53 out: 54 printk(cirrus-cs89x0: not found (%d).n, ret); 55 return ret; 56 现在来分析上述代码。第3行,调用to_platform_device宏将device结构转化为platform_device结构的指针。第4-6行,定义一些该函数内部将用到的局部变量。第8-12行,调用platform_get_resource内核函数来为该平台设备申请内存资源,当该函数返回值为0时,表示申请内存资源失败,否则表示成功,返回申请的资源地址。第15-18行,调用request_mem_region宏来请求分配指定的I/O内存资源,如果返回值为0表示设备或资源被占用。第21-25行,调用ioremap函数将该设备的物理地址转化为内核地址,如果返回值为0,表示失败,此时需要释放指定的I/O内存资源。第27-32行,调用alloc_etherdev函数分配和设置一个以太网设备,如果返回值为0,表示分配失败,然后调用iounmap函数取消之前的内存映射。第34行,调用SET_NETDEV_DEV宏实现为系统文件系统中物理设备创建一个与网络类逻辑设备的链接,也就是说将物理设备与网络设备联系起来。第36行,调用platform_get_irq函数获得一个设备IRQ,并将获得结果传递给ndev-irq。第39行,调用dev_set_drvdata函数将网络设备与驱动具体的数据关联起来。第41-44行,调用cirrus_probe函数实现对网络设备的初始化工作,如果返回0,表示初始化失败,然后调用dev_set_drvdata将驱动设备的具体数据设为空,最后调用free_netdev内核函数释放之前注册的网络设备。关于cirrus_probe函数的具体实现,下面将具体介绍。在这里为止,对cirrus_drv_probe函数的介绍基本完毕。 cirrus_probe函数主要用来初始化网络设备,包括数据包发送、接收函数的定义,网络设备的注册等。下面将具体分析该函数的实现: 1 int _init cirrus_probe (struct net_device *dev, unsigned long ioaddr) 2 3 cirrus_t *priv = netdev_priv(dev); 4 int i; 5 u16 value; 6 7 ether_setup (dev); 8 9 dev-open = cirrus_start; 10 dev-stop = cirrus_stop; 11 dev-hard_start_xmit = cirrus_send_start; 12 dev-get_stats = cirrus_get_stats; 13 dev-set_multicast_list = cirrus_set_receive_mode; 14 dev-set_mac_address = cirrus_set_mac_address; 15 dev-tx_timeout = cirrus_transmit_timeout; 16 dev-watchdog_timeo = HZ; 17 18 dev-if_port = IF_PORT_10BASET; 19 dev-priv = (void *)priv; 20 21 spin_lock_init(&priv-lock); 22 23 SET_MODULE_OWNER (dev); 24 25 dev-base_addr = ioaddr; 26 27 /* if an EEPROM is present, use its MAC address */ 28 if (!cirrus_eeprom(dev,&priv-eeprom) 29 for (i = 0; i dev_addri = priv-eeprom.maci; 31 else 32 cirrus_parse_mac(cirrus_mac, dev-dev_addr); 33 34 /* verify EISA registration number for Cirrus Logic */ 35 if (value = cirrus_read (dev,PP_ProductID) != EISA_REG_CODE) 36 printk (KERN_ERR %s: incorrect signature 0x%.4xn,dev-name,value); 37 return (-ENXIO); 38 39 40 /* verify chip version */ 41 value = cirrus_read (dev,PP_ProductID + 2); 42 if (VERSION (value) != CS8900A) 43 printk (KERN_ERR %s: unknown chip version 0x%08xn,dev-name,VERSION (value); 44 return (-ENXIO); 45 46 printk (KERN_INFO %s: CS8900A rev %c detectedn,dev-name,B + REVISION (value) - REV_B); 47 48 /* setup interrupt number */ 49 cirrus_write (dev,PP_IntNum,0); 50 51 /* configure MAC address */ 52 for (i = 0; i dev_addri | (dev-dev_addri + 1 dev_addr) 7 printk(KERN_ERR %s: invalid ethernet MAC addressn,dev-name); 8 return (-EINVAL); 9 10 11 /* install interrupt handler */ 12 if (result = request_irq (dev-irq,&cirrus_interrupt,0,dev-name,dev) name,dev-irq); 14 return (result); 15 16 17 set_irq_type(dev-irq, IRQT_RISING); 18 19 /* enable the ethernet controller */ 20 cirrus_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE); 21 cirrus_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA); 22 cirrus_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE); 23 cirrus_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE); 24 cirrus_set (dev,PP_LineCTL,SerRxON | SerTxON); 25 cirrus_set (dev,PP_BusCTL,EnableRQ); 26 27 #ifdef FULL_DUPLEX 28 cirrus_set (dev,PP_TestCTL,FDX); 29 #endif /* #ifdef FULL_DUPLEX */ 30 31 /* start the queue */ 32 netif_start_queue (dev); 33 34 return (0); 35 现在来分析上述代码。第6-9行,调用is_valid_ether_addr函数来确认已知的以太网地址是否有效,如果不是返回无效错误标志。第12-15行,调用request_irq函数来申请一个IRQ资源,并且定义该中断处理函数为cirrus_interrupt,这个函数在后面会专门介绍。第17行,调用set_irq_type函数来设置刚才申请的IRQ类型,设置类型为上升沿触发(IRQT_RISING)。第20-25行,通过调用cirrus_set函数来设置CS8900A芯片的相关寄存器,来启动CS8900A以太网控制器。关于需要设置哪些寄存器需要参考CS8900A芯片的用户手册。第27-29行,如果支持全双工传输模式,需要调用cirrus_set函数来设置相应的寄存器使该功能启动。第32行,调用netif_start_queue函数用来告诉上层网络协议该驱动程序有空的缓冲区可用,请开始送数据包。第34行,返回0。 关闭方法用于停止网络设备,它的作用与open方法相反,CS8900A网卡设备的关闭方法由cirrus_stop函数实现,具体实现如下: 1 static int cirrus_stop (struct net_device *dev) 2 3 /* disable ethernet controller */ 4 cirrus_write (dev,PP_BusCTL,0); 5 cirrus_write (dev,PP_TestCTL,0); 6 cirrus_write (dev,PP_SelfCTL,0); 7 cirrus_write (dev,PP_LineCTL,0); 8 cirrus_write (dev,PP_BufCFG,0); 9 cirrus_write (dev,PP_TxCFG,0); 10 cirrus_write (dev,PP_RxCTL,0); 11 cirrus_write (dev,PP_RxCFG,0); 12 13 /* uninstall interrupt handler */ 14 free_irq (dev-irq,dev); 15 16 /* stop the queue */ 17 netif_stop_queue (dev); 18 19 return (0); 20 现在来分析上述代码。第4-11行,调用cirrus_write函数写CS8900A芯片相应的寄存器,从而关闭CS8900A以太网控制器。第14行,调用free_irq函数释放之前申请的IRQ资源。第17行,调用netif_stop_queue函数告诉上层网络请不要再送数据包。 关于CS8900A网络设备的打开和关闭方法已经分析完毕,相对来说这两个方法的实现比较简单,但是非常重要。下面将介绍网卡驱动的中断处理函数。 3中断处理 CS8900A网卡设备驱动的中断处理函数是对接收帧(Frame,即帧。数据在网络上是以很小的称为帧的单位传输的)事件,发送帧事件,Buffer事件,传输出现冲突事件和获取丢失帧事件等进行处理。关于该中断处理函数由cirrus_interrupt函数实现,具体实现如下: 1 static irqreturn_t cirrus_interrupt (int irq,void *id) 2 3 struct net_device *dev = (struct net_device *) id; 4 cirrus_t *priv = netdev_priv(dev); 5 u16 status; 6 7 if (dev-priv = NULL) 8 printk (KERN_WARNING %s: irq %d for unknown device.n,dev-name,irq); 9 return IRQ_RETVAL(IRQ_NONE); 10 11 12 spin_lock(&priv-lock); 13 14 while (status = cirrus_read (dev,PP_ISQ) 15 switch (RegNum (status) 16 case RxEvent: 17 cirrus_receive (dev); 18 break; 19 20 case TxEvent: 21 priv-stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL); 22 if (!(RegContent (status) & TxOK) 23 priv-stats.tx_errors+; 24 if (RegContent (status) & Out_of_window) priv-stats.tx_window_errors+; 25 if (RegContent (status) & Jabber) priv-stats.tx_aborted_errors+; 26 break; 27 else if (priv-txlen) 28 priv-stats.tx_packets+; 29 priv-stats.tx_bytes += priv-txlen; 30 31 priv-txlen = 0; 32 netif_wake_queue (dev); 33 break; 34 35 case BufEvent: 36 if (RegContent (status) & RxMiss) 37 u16 missed = MissCount (cirrus_read (dev,PP_RxMISS); 38 priv-stats.rx_errors += missed; 39 priv-stats.rx_missed_errors += missed; 40 41 if (RegContent (status) & TxUnderrun) 42 priv-stats.tx_errors+; 43 /* Shift start tx, if underruns come too often */ 44 switch (priv-stats.tx_fifo_errors+) 45 case 3: priv-txafter = After381; break; 46 case 6: priv-txafter = After1021; break; 47 case 9: priv-txafter = AfterAll; break; 48 default: break; 49 50 51 /* Wakeup only for tx events ! */ 52 if (RegContent (status) & (TxUnderrun | Rdy4Tx) 53 priv-txlen = 0; 54 netif_wake_queue (dev); 55 56 break; 57 58 case TxCOL: 59 priv-stats.collisions += ColCount (cirrus_read (dev,PP_TxCOL); 60 break; 61 62 case RxMISS: 63 status = MissCount (cirrus_read (dev,PP_RxMISS); 64 priv-stats.rx_errors += status; 65 priv-stats.rx_missed_errors += status; 66 break; 67 68 default: 69 break; 70 71 72 73 spin_unlock(&priv-lock); 74 return IRQ_RETVAL(IRQ_HANDLED); 75 现在来分析上述代码。第3行,将参数id强制转化为net_device结构的指针然后赋值给dev变量。第4行,调用netdev_priv函数来获得网卡私有数据结构的起始地址赋值给变量priv。第7-10行,如果网络设备的私有数据为空,直接返回错误。第12行,调用spin_lock宏来获得网络设备私有数据的自旋锁。第14行,调用cirrus_read函数读CS8900A的中断状态队列寄存器来获得当前的中断状态。第15行,调用RegNum宏将得出具体的中断类型。第16-17行,此时为接收帧事件,当网卡接收到数据就会触发一次该中断,然后调用cirrus_receive函数来处理数据包的接收,关于该函数的实现在下面会具体介绍。第20-33行,如果网卡将数据发送成功,则会触发一次该中断,在中断处理例程中将net_device_stats结构体中的tx_packets(表示发送的包的个数)元素递增并通知上层可继续送包下来。第35-56行,Buffer事件当RxMiss置位表示由于数据从缓冲区中搬移到主机速度较慢而丢失了一些接收的帧,读寄存器获取丢失的包的数目。当TxUnderrun置位表示在帧结束前网卡运行已过时。改变网络状态结构体中对应元素的值,接着通知上层可往下发送包数据。第58-60行,当传输出现冲突错误时,通过读寄存器值得到当前冲突的个数,加到统计结构体中的对应元素collisions上。第62-66行,读寄存器获取丢失帧的个数,并将其状态结果加到统计结构体对应的元素值上。第73行,调用spin_unlock宏来解锁。第74行,返回1表示中断处理完成。 关于CS8900A网卡设备的中断处理过程已经分析完毕,下面将介绍网卡驱动的发送数据函数。 4发送数据 网络设备数据发送是由net_device结构中的hard_start_xmit方法实现的,所有的网络设备驱动程序都必须实现该方法。CS8900A网卡设备驱动的数据发送具体是由cirrus_send_start函数实现,其实现代码如下: 1 static int cirrus_send_start (struct sk_buff *skb,struct net_device *dev) 2 3 cirrus_t *priv = netdev_priv(dev); 4 u16 status; 5 6 /* Tx start must be done with irq disabled 7 * else status can be wrong */ 8 spin_lock_irq(&priv-lock); 9 10 netif_stop_queue (dev); 11 12 cirrus_write (dev,PP_TxCMD,TxStart (priv-txafter); 13 cirrus_write (dev,PP_TxLength,skb-len); 14 15 status = cirrus_read (dev,PP_BusST); 16 17 if (status & TxBidErr) 18 printk (KERN_WARNING %s: Invalid frame size %d!n,dev-name,skb-len); 19 priv-stats.tx_errors+; 20 priv-stats.tx_aborted_errors+; 21 priv-txlen = 0; 22 spin_unlock_irq(&priv-lock); 23 return (1); 24 25 26 if (!(status & Rdy4TxNOW) 27 printk (KERN_WARNING %s: Transmit buffer not free!n,dev-name); 28 priv-stats.tx_errors+; 29 priv-txlen = 0; 30 spin_unlock_irq(&priv-lock); 31 return (1); 32 33 34 cirrus_frame_write (dev,skb); 35 36 #ifdef DEBUG 37 dump_packet (dev,skb,send); 38 #endif /* #ifdef DEBUG */ 39 40 dev-trans_start = jiffies; 41 spin_unlock_irq(&priv-lock); 42 43 dev_kfree_skb (skb); 44 priv-txlen = skb-len; 45 46 return (0); 47 现在来分析上述代码。第3行,调用netdev_priv函数来获得网卡私有数据结构的起始地址赋值给变量priv。第8行,调用spin_lock_irq宏获得自旋锁,该宏首先会关闭当前CPU的中断,然后获得锁,因为在处理发送数据之前,必须先关闭中断,否则会引起错误。第10行,调用netif_stop_queue函数告诉上层网络请不要再送数据包。第12-13行,调用cirrus_write函数写数据发送命令寄存器开始发送数据,并且给发送数据长度寄存器中写要发送数据包的长度。第15行,调用cirrus_read函数读Bus状态寄存器。第16-24行,当数据帧的大小不在规定的大小范围之内时产生Bid错误。当Bus状态寄存器中的TxBidErr置位时,表明已经产生了Bid错误,此时在统计数据结构中增加相应的错误计数值,然后调用spin_unlock_irq宏解锁,最后退出发送函数。第26-32行,当Bus状态寄存器中的Rdy4TxNOW置0时,表明Bus状态还没有准备就绪发送数据,此时在统计数据结构中增加相应的错误计数值,然后调用spin_unlock_irq宏解锁,最后退出发送函数。第34行,调用cirrus_frame_write函数将sk_buff结构的data值写进设备I/O地址中,该函数的实质就是将数据发送到硬件层。第36-38行,如果打开了调试选项,将调用dump_packet函数来打印发送数据包的一些数据信息。第40行,将jiffies值赋给设备结构的trans_start项,jiffies用来统计系统自运行以来的时钟中断的次数,每次时钟中断都会使jiffies的加1。第41行,调用spin_unlock_irq宏解锁。第43行,调用dev_kfree_skb函数来释放缓冲区,并把它返回给缓冲池(缓存)。第46行,退出发送数据函数。 关于CS8900A网卡设备的发送数据过程已经分析完毕,下面将介绍网卡驱动的接收数据函数。 5接收数据 当有数据到达时产生中断信号,网络设备驱动功能层调用中断处理程序来处理数据包的接收,在中断处理程序中调用cirrus_receive函数具体完成数据包的接收,关于cirrus_receive函数的具体实现如下: 1 static void cirrus_receive (struct net_device *dev) 2 3 cirrus_t *priv = netdev_priv(dev); 4 struct sk_buff *skb; 5 u16 status,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 美的集团工作流程体系
- 2025年事业单位工勤技能-湖北-湖北水生产处理工三级(高级工)历年参考题库含答案解析
- 文化场馆扩建工程2025年社会稳定风险评估研究
- 2025年事业单位工勤技能-浙江-浙江土建施工人员一级(高级技师)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-河南-河南舞台技术工一级(高级技师)历年参考题库含答案解析
- 2024版房产证抵押合同样本
- 2025年事业单位工勤技能-河北-河北理疗技术员三级(高级工)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-江西-江西药剂员三级(高级工)历年参考题库含答案解析(5套)
- 2025年事业单位工勤技能-广西-广西计算机信息处理员二级技师历年参考题库含答案解析
- 2025年事业单位工勤技能-广西-广西放射技术员四级(中级工)历年参考题库典型考点含答案解析
- 2025年安徽国控集团所属企业招聘7人笔试备考题库及答案解析
- 2025年海南省警务辅助人员招聘考试(公共基础知识)历年参考题库含答案详解(5套)
- 城市道路清扫保洁协议
- 人教版二年级上册数学全册教学设计(配2025年秋新版教材)
- 2025年医学检验在编考试题库
- 特色食品卖场建设方案(3篇)
- 2025年书法级考试题及答案
- 子宫癌肉瘤护理查房
- 乡村产业融合发展路径与振兴策略研究
- 夫妻离婚协议书(2025版)
- 消费券提振机制-洞察及研究
评论
0/150
提交评论