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 函数的实现在内核<drivers/base/driver.c>文件中。对设备驱动程序进行注册和初始化是两件不同的事情。设备驱动程序应 当尽快被注册,以便用户应用程序通过相应的设备文件使用它。通常设备驱动程序在最后可能的时刻才被初始化。事实上,初始化驱动程序意味着分配系统宝贵的资 源,这些被分配的资源因此就对其他驱动程序不能用。关于注册的网络设备驱动结构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结构的定义 在<include/linux/device.h>文件中。第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 15 if (!request_mem_region(res->start, 16, "cirrus-cs89x0")) { 16 ret = -EBUSY; 17 goto out; 18 } 19 20 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:%d\n",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 } 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 28 if (!cirrus_eeprom(dev,&priv->eeprom)) 29 for (i = 0; i < 6; i++) 30 dev->dev_addr[i] = priv->eeprom.mac[i]; 31 else 32 cirrus_parse_mac(cirrus_mac, dev->dev_addr); 33 34 35 if ((value = cirrus_read (dev,PP_ProductID)) != EISA_REG_CODE) { 36 printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value); 37 return (-ENXIO); 38 } 39 40 41 value = cirrus_read (dev,PP_ProductID + 2); 42 if (VERSION (value) != CS8900A) { 43 printk (KERN_ERR "%s: unknown chip version 0xx\n",dev->name,VERSION (value)); 44 return (-ENXIO); 45 } 46 printk (KERN_INFO "%s: CS8900A rev %c detected\n",dev->name,'B' + REVISION (value) - REV_B); 47 48 49 cirrus_write (dev,PP_IntNum,0); 50 51 52 for (i = 0; i < ETH_ALEN; i += 2) 53 cirrus_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8)); 54 55 return register_netdev(dev); 56 } 现在来分析上述代码。第3行,调用netdev_priv函数获得net_device结构末端地址,也就是网卡私有数据结构的起始地址。关于netdev_priv函数的内核实现如下: static inline void *netdev_priv(struct net_device *dev) { return (char *)dev + ((sizeof(struct net_device) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST); } 第4-5行,定义两个局部变量供函数内部使用。第7行,调用ether_setup函数设置以太网相关的网络设备属性。第9-10行,定义网络设备的 open方法和stop方法分别由cirrus_start和cirrus_stop函数实现,这两个函数会在后面具体讲解。第11行,定义网络设备的数 据包发送函数hard_start_xmit由cirrus_send_start函数具体实现。该函数是网络设备驱动中非常重要的一个函数,后面将会具 体介绍该函数的实现。第12行,定义网络设备的get_stats方法由cirrus_get_stats函数实现,主要用来获得网络设备的状态信息。第 13行,设备网络设备的set_multicast_list方法由cirrus_set_receive_mode函数完成,主要实现设置接收数据包的 类型。第14行,定义网络设备的set_mac_address方法由cirrus_set_mac_address函数实现,该函数用来设备网络设备的 MAC地址。第15行,定义网络设备的tx_timeout方法由cirrus_transmit_timeout函数完成,该函数的用于当传输数据包超 时时告诉内核来调度网络处理事件队列。第16行,初始化网络设备watchdog_timeo的值为HZ。第18行,初始化网络设备的if_port属性 为IF_PORT_10BASET,即选择10M以太网。第19行,初始化网络设备的私有数据。第21行,调用spin_lock_init宏来初始化网 络设备的自旋锁。第23行,调用SET_MODULE_OWNER宏设置网络设备的模块拥有属性。第25行,初始化网络设备的base_addr为 ioremap映射后的地址。第28-32行,检查是否有EEPROM(Electrically Erasable Programmable Read-Only Memory,电可擦写可编程只读存储器),如果有的话,设置网络设备的MAC地址为EEPROM的MAC地址,否则,调用 cirrus_parse_mac函数来获得网络设备的MAC地址。第35-38行,通过读CS8900A的产品标识寄存器来检查是否是已定义的产品,如 果不是,返回无此设备的错误标志。第41-45行,验证芯片的版本是否与定义的版本相同,如果不同,返回无此设备的错误标志。第49行,调用 cirrus_write函数来设置网络设备中断的个数在相应的寄存器中。第52-53行,调用cirrus_write函数将MAC地址写到相应的寄存 器。第55行,调用register_netdev函数来注册网络设备到内核中去,根据该函数的返回值可以判断是否注册成功,返回值为0表示成功,否则表 示错误。 关于CS8900A网卡设备驱动的初始化过程已经介绍完毕,下面将接着介绍网络设备的打开和关闭函数的实现。 2打开和关闭 网络设备的打开(open)和关闭(stop)是非常重要的网络设备驱动的两个实现函数。打开方法的作用就是激活网络接口,使它能接收来自网络的数据并且 传递到网络协议栈的上层,也可以将数据发送到网络上。先来分析CS8900A网卡设备的打开实现函数cirrus_start: 1 static int cirrus_start (struct net_device *dev) 2 { 3 int result; 4 5 6 if (!is_valid_ether_addr(dev->dev_addr)) { 7 printk(KERN_ERR "%s: invalid ethernet MAC address\n",dev->name); 8 return (-EINVAL); 9 } 10 11 12 if ((result = request_irq (dev->irq,&cirrus_interrupt,0,dev->name,dev)) < 0) { 13 printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq); 14 return (result); 15 } 16 17 set_irq_type(dev->irq, IRQT_RISING); 18 19 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 30 31 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 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 14 free_irq (dev->irq,dev); 15 16 17 netif_stop_queue (dev); 18 19 return (0); 20 } |
|
来自: WUCANADA > 《ethernet》