您好,欢迎访问三七文档
网卡设备驱动1.8139too网卡设备简介一个PCI设备,总共有三个地址空间:内存,端口和配置。内存和端口其实是同一个内容的不同访问路径而已。PCI设备的配置空间是标准化的,每个PCI设备的配置空间的前64个字节的含义都是一样的。但各个设备的内存和端口空间是完全不一样的。在硬件概念上,设备的I/O内存和I/O端口空间没有区别,其实都是设备拥用的一些读写寄存器。8139too网卡拥有256字节的读写寄存器空间。它的整个布局如下:/*Symbolicoffsetstoregisters.*/enumRTL8139_registers{MAC0=0,/*Ethernethardwareaddress.*/MAR0=8,/*Multicastfilter.*/TxStatus0=0x10,/*Transmitstatus(Four32bitregisters).*/TxAddr0=0x20,/*Txdescriptors(alsofour32bit).*/RxBuf=0x30,ChipCmd=0x37,RxBufPtr=0x38,RxBufAddr=0x3A,IntrMask=0x3C,IntrStatus=0x3E,TxConfig=0x40,RxConfig=0x44,Timer=0x48,/*Ageneral-purposecounter.*/RxMissed=0x4C,/*24bitsvalid,writeclears.*/Cfg9346=0x50,Config0=0x51,Config1=0x52,FlashReg=0x54,MediaStatus=0x58,Config3=0x59,Config4=0x5A,/*absentonRTL-8139A*/HltClk=0x5B,MultiIntr=0x5C,TxSummary=0x60,BasicModeCtrl=0x62,BasicModeStatus=0x64,NWayAdvert=0x66,NWayLPAR=0x68,NWayExpansion=0x6A,/*Undocumentedregisters,butrequiredforproperoperation.*/FIFOTMS=0x70,/*FIFOControlandtest.*/CSCR=0x74,/*ChipStatusandConfigurationRegister.*/PARA78=0x78,PARA7c=0x7c,/*Magictransceiverparameterregister.*/Config5=0xD8,/*absentonRTL-8139A*/};每个寄存器都有它特殊的含义和用途。举个例子(我们假设使用I/O内存的方式,ioaddr为设备内存映射在CPU内存地址空间的起始地址,下述代码能读取板卡的版本号):#defineHW_REVID(b30,b29,b28,b27,b26,b23,b22)\(b3030|b2929|b2828|b2727|b2626|b2323|b2222)#defineHW_REVID_MASKHW_REVID(1,1,1,1,1,1,1)/*identifychipattachedtoboard*/version=ioread32(ioaddr+TxConfig)&HW_REVID_MASK;在我的电脑上,version读出来的值的0x74400000,经过比对,板卡名称为:RTL-8100B/8139D。下面是整个系例版本号的表格:enumchip_flags{HasHltClk=(10),HasLWake=(11),};/*directlyindexedbychip_t,above*/conststaticstruct{constchar*name;u32version;/*fromRTL8139C/RTL8139Ddocs*/u32flags;}rtl_chip_info[]={{RTL-8139,HW_REVID(1,0,0,0,0,0,0),HasHltClk,},{RTL-8139revK,HW_REVID(1,1,0,0,0,0,0),HasHltClk,},{RTL-8139A,HW_REVID(1,1,1,0,0,0,0),HasHltClk,},{RTL-8139ArevG,HW_REVID(1,1,1,0,0,1,0),HasHltClk,},{RTL-8139B,HW_REVID(1,1,1,1,0,0,0),HasLWake,},{RTL-8130,HW_REVID(1,1,1,1,1,0,0),HasLWake,},{RTL-8139C,HW_REVID(1,1,1,0,1,0,0),HasLWake,},{RTL-8100,HW_REVID(1,1,1,1,0,1,0),HasLWake,},{RTL-8100B/8139D,HW_REVID(1,1,1,0,1,0,1),HasLWake,},{RTL-8101,HW_REVID(1,1,1,0,1,1,1),HasLWake,},};在这个表格中,RTL_8139B以下(包括RTL_8139B)的板卡都属于较新的版本。新老版本之间有不同的唤醒方式。先看新版本的:#defineLWAKE0x10#defineCfg1_PM_Enable0x01u8new_tmp8,tmp8;enumConfig4Bits{LWPTN=(12),/*noton8139,8139A*/};enumCfg9346Bits{Cfg9346_Lock=0x00,Cfg9346_Unlock=0xC0,};new_tmp8=tmp8=ioread8(ioaddr+Config1);if((rtl_chip_info[tp-chipset].flags&HasLWake)&&(tmp8&LWAKE))new_tmp8&=~LWAKE;new_tmp8|=Cfg1_PM_Enable;if(new_tmp8!=tmp8){iowrite8(Cfg9346_Unlock,ioaddr+Cfg9346);iowrite8(tmp8,ioaddr+Config1);iowrite8(Cfg9346_Lock,ioaddr+Cfg9346);}if(rtl_chip_info[tp-chipset].flags&HasLWake){tmp8=ioread8(ioaddr+Config4);if(tmp8&LWPTN){iowrite8(Cfg9346_Unlock,ioaddr+Cfg9346);iowrite8(tmp8&~LWPTN,ioaddr+Config4);iowrite8(Cfg9346_Lock,ioaddr+Cfg9346);}}基本的一个流程是:如果板卡版本本身支持了HasLWake,而Config1中读出的值带有LWAKE,把Config1的值写回,并把Config4中的LWPTN去除。而我的网卡中从Config1,Config4读取的值分别为0x8d,0x88,所以,无需做任何操作。下面是旧版本的唤醒方式:enumConfig1Bits{Cfg1_PM_Enable=0x01,Cfg1_VPD_Enable=0x02,Cfg1_PIO=0x04,Cfg1_MMIO=0x08,LWAKE=0x10,/*noton8139,8139A*/Cfg1_Driver_Load=0x20,Cfg1_LED0=0x40,Cfg1_LED1=0x80,SLEEP=(11),/*onlyon8139,8139A*/PWRDN=(10),/*onlyon8139,8139A*/};tmp8=ioread8(ioaddr+Config1);tmp8&=~(SLEEP|PWRDN);iowrite8(tmp8,ioaddr+Config1);下面是一个板卡的复位操作:enumChipCmdBits{CmdReset=0x10,CmdRxEnb=0x08,CmdTxEnb=0x04,RxBufEmpty=0x01,};staticvoidrtl8139_chip_reset(void__iomem*ioaddr){inti;/*Softresetthechip.*/iowrite8(CmdReset,ioaddr+ChipCmd);/*Checkthatthechiphasfinishedthereset.*/for(i=1000;i0;i--){barrier();if((ioread8(ioaddr+ChipCmd)&CmdReset)==0)break;udelay(10);}}写一个CmdReset命令到ChipCmd位置,等待该命令消失,即可。关于网卡的寄存器操作,还有一些,再进行过程中遇到时再介绍。2.网络设备的初始化网络接口是字符设备,块设备之后的第三类标准Linux设备。网络驱动程序和其它内核模块一样,当被装载到正在运行的内核中时,它要请求资源并提供一些功能设施。网络驱动程序对每个新检测到的接口,会向全局的网络设备链表中插入一个数据结构。每个接口由一个net_device结构描述,这是一个很庞大的结构,在下面的描述中,我们会看到一些这个结构的成员。在8139too网卡的驱动程序中,我们看到alloc_etherdev动态分配了该结构,这是为以太网接口封装的一个分配函数:structnet_device*alloc_etherdev(intsizeof_priv){returnalloc_netdev(sizeof_priv,eth%d,ether_setup);}EXPORT_SYMBOL(alloc_etherdev);真正用来实现网络设备接口分配的函数是alloc_netdevalloc_ehterdev的封装为以太网设备接口分配了形如“eth%d”的名字,同时,指定了一个ether_setup的初始化函数:voidether_setup(structnet_device*dev){dev-change_mtu=eth_change_mtu;dev-hard_header=eth_header;dev-rebuild_header=eth_rebuild_header;dev-set_mac_address=eth_mac_addr;dev-hard_header_cache=eth_header_cache;dev-header_cache_update=eth_header_cache_update;dev-hard_header_parse=eth_header_parse;dev-type=ARPHRD_ETHER;dev-hard_header_len=ETH_HLEN;dev-mtu=1500;/*eth_mtu*/dev-addr_len=ETH_ALEN;dev-tx_queue_len=1000;/*Ethernetwantsgoodqueues*/dev-flags=IFF_BROADCAST|IFF_MULTICAST;memset(dev-broadcast,0xFF,ETH_ALEN);}EXPORT_SYMBOL(ether_setup);它为表示以太网设备接口的net_device结构进行了部分初始化,因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成。alloc_netdev分配sizeof(dev)+sizeof_priv的内核页,并调用初始化函数。这些都是在PCI的探测函数中做的事情,在完成了网络设备接口的分配后,我们要访问PCI设备的一些资源。在能够访问PCI设备的任何资源之前,我们必须激活PCI设备:/*enabledevice(incl.PCIPMwakeupandhotplugsetup)*/rc=pci_enable_device(pdev);if(rc)
本文标题:网卡设备驱动
链接地址:https://www.777doc.com/doc-1315043 .html