免费预览已结束,剩余23页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux DM9000网卡驱动程序完全分析说明1:本文分析基于内核源码版本为linux-2.6.31说明2:本文在理解了linux中总线、设备和驱动模型的基础上加以分析代码虽然Linux驱动程序应该是和具体的硬件平台分离的,但是为了更好的理解DM9000的驱动程序,这里还是结合一下Mini2440开发板,这样也可以更好的体会如何实现驱动和平台分离。本文分成以下几个部分:一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系。二、两个重要的结构体介绍:sk_buff和net_device三、具体代码分析一、Mini2440开发板上DM9000的电气连接和Mach-mini2440.c文件的关系Mini2440开发板上DM9000与S3C2440的连接关系如下:其中片选信号AEN使用了nGCS4,所以网卡的内存区域在BANK4,也就是从地址0x20000000开始。DM9000的TXD2:0作为strap pin在电路图中是空接的,所以IO base是300H。中断使用了EINT7。这些内容在Mach文件中有如下体现:1. #defineS3C2410_CS4(0x20000000)2. #defineMACH_MINI2440_DM9K_BASE(S3C2410_CS4+0x300)3. staticstructresourcemini2440_dm9k_resource_initdata= 4. 0= 5. .start=MACH_MINI2440_DM9K_BASE, 6. .end=MACH_MINI2440_DM9K_BASE+3, 7. .flags=IORESOURCE_MEM 8. , 9. 1= 10. .start=MACH_MINI2440_DM9K_BASE+4, 11. .end=MACH_MINI2440_DM9K_BASE+7, 12. .flags=IORESOURCE_MEM 13. , 14. 2= 15. .start=IRQ_EINT7, 16. .end=IRQ_EINT7, 17. .flags=IORESOURCE_IRQ|IORESOURCE_IRQ_HIGHEDGE, 18. 19. ;另外在Mach文件中还定义了DM9000平台设备,设备名称为“dm9000”,设备资源就是上面定义的IO和中断资源。代码清单如下:1. staticstructdm9000_plat_datamini2440_dm9k_pdata_initdata= 2. .flags=(DM9000_PLATF_16BITONLY|DM9000_PLATF_NO_EEPROM), 3. ; 4. 5. staticstructplatform_devicemini2440_device_eth_initdata= 6. .name=dm9000, 7. .id=-1, 8. .num_resources=ARRAY_SIZE(mini2440_dm9k_resource), 9. .resource=mini2440_dm9k_resource, 10. .dev= 11. .platform_data=&mini2440_dm9k_pdata, 12. , 13. ;这个DM9000平台设备作为众多平台设备中的一个在扳子初始化的时候就被添加到了总线上。代码清单如下:1. MACHINE_START(MINI2440,MINI2440) 2. /*Maintainer:MichelPollet*/3. .phys_io=S3C2410_PA_UART, 4. .io_pg_offst=(u32)S3C24XX_VA_UART)18)&0xfffc, 5. .boot_params=S3C2410_SDRAM_PA+0x100, 6. .map_io=mini2440_map_io, 7. .init_machine=mini2440_init,/*初始化函数*/8. .init_irq=s3c24xx_init_irq, 9. .timer=&s3c24xx_timer, 10. MACHINE_END1. staticvoid_initmini2440_init(void) 2. 3. . 4. . 5. platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices); 6. 7. . 8. . 9. 1. staticstructplatform_device*mini2440_devices_initdata= 2. &s3c_device_usb, 3. &s3c_device_wdt, 4. /*&s3c_device_adc,*/*ADCdoesntlikelivingwithtouchscreen!*/5. &s3c_device_i2c0, 6. &s3c_device_rtc, 7. &s3c_device_usbgadget, 8. &mini2440_device_eth,/*dm9000是众多平台设备中的一个*/9. &mini2440_led1, 10. &mini2440_led2, 11. &mini2440_led3, 12. &mini2440_led4, 13. &mini2440_button_device, 14. &s3c_device_nand, 15. &s3c_device_sdi, 16. &s3c_device_iis, 17. &mini2440_audio, 18. /*&s3c_device_timer0,*/*buzzerpwm,noAPIforit*/19. /*remainingdevicesareoptional*/20. ;二、两个重要的结构体简单介绍:sk_buff和net_device *sk_buff 如果把网络传输看成是运送货物的话,那么sk_buff就是这个“货物”了,所有经手这个货物的人都要干点什么事儿,要么加个包装,要么印个戳儿等等。收货的时候就要拆掉这些包装,得到我们需要的货物(payload data)。没有货物你还运输什么呢?由此可见sk_buff的重要性了。关于sk_buff的详细介绍和几个操作它的函数,参考:“linux内核sk_buff的结构分析”/Linux/2011-07/39163.htm,写得非常明白了。赞一个 *net_device 又是一个庞大的结构体。好吧,我承认我从来就没有看全过这个结构体。它在内核中就是指代了一个网络设备。驱动程序需要在探测的时候分配并初始化这个结构体,然后使用register_netdev来注册它,这样就可以把操作硬件的函数与内核挂接在一起。三、具体代码的分析在顺序分析之前先看三个结构体变量和一个自定义的结构体。 *dm9000_driver变量。是platform_driver结构体变量,其中包含了重要的:驱动的名字(用来match)和几个重要操作函数。1. staticstructplatform_driverdm9000_driver= 2. .driver= 3. .name=dm9000, 4. .owner=THIS_MODULE, 5. , 6. .probe=dm9000_probe, 7. .remove=_devexit_p(dm9000_drv_remove), 8. .suspend=dm9000_drv_suspend, 9. .resume=dm9000_drv_resume, 10. ; *dm9000_netdev_ops变量。是net_device_ops结构体变量,其中定义了操作net_device的重要函数,我们在驱动程序中根据需要的操作要填充这些函数。代码清单如下:1. staticconststructnet_device_opsdm9000_netdev_ops= 2. .ndo_open=dm9000_open, 3. .ndo_stop=dm9000_stop, 4. .ndo_start_xmit=dm9000_start_xmit, 5. .ndo_tx_timeout=dm9000_timeout, 6. .ndo_set_multicast_list=dm9000_hash_table, 7. .ndo_do_ioctl=dm9000_ioctl, 8. .ndo_change_mtu=eth_change_mtu, 9. .ndo_validate_addr=eth_validate_addr, 10. .ndo_set_mac_address=eth_mac_addr,11. #ifdefCONFIG_NET_POLL_CONTROLLER12. .ndo_poll_controller=dm9000_poll_controller,13. #endif14. ; *dm9000_ethtool_ops变量。是ethtool_ops结构体变量,为了支持ethtool,其中的函数主要是用于查询和设置网卡参数(当然也有的驱动程序可能不支持ethtool)。代码清单如下:1. staticconststructethtool_opsdm9000_ethtool_ops= 2. .get_drvinfo=dm9000_get_drvinfo, 3. .get_settings=dm9000_get_settings, 4. .set_settings=dm9000_set_settings, 5. .get_msglevel=dm9000_get_msglevel, 6. .set_msglevel=dm9000_set_msglevel, 7. .nway_reset=dm9000_nway_reset, 8. .get_link=dm9000_get_link, 9. .get_eeprom_len=dm9000_get_eeprom_len, 10. .get_eeprom=dm9000_get_eeprom, 11. .set_eeprom=dm9000_set_eeprom, 12. ; *board_info结构体。用来保存芯片相关的一些私有信息。具体在代码中分析。下面是这个结构体的清单。1. /*Structure/enumdeclaration-*/2. typedefstructboard_info 3. 4. void_iomem*io_addr;/*RegisterI/Obaseaddress*/5. void_iomem*io_data;/*DataI/Oaddress*/6. u16irq;/*IRQ*/7. 8. u16tx_pkt_cnt; 9. u16queue_pkt_len; 10. u16queue_start_addr; 11. u16dbug_cnt; 12. u8io_mode;/*0:word,2:byte*/13. u8phy_addr; 14. u8imr_all; 15. 16. unsignedintflags; 17. unsignedintin_suspend:1; 18. intdebug_level; 19. 20. enumdm9000_typetype; 21. 22. void(*inblk)(void_iomem*port,void*data,intlength); 23. void(*outblk)(void_iomem*port,void*data,intlength); 24. void(*dumpblk)(void_iomem*port,intlength); 25. 26. structdevice*dev;/*parentdevice*/27. 28. structresource*addr_res;/*resourcesfound*/29. structresource*data_res; 30. structresource*addr_req;/*resourcesrequested*/31. structresource*data_req; 32. structresource*irq_res; 33. 34. structmutexaddr_lock;/*phyandeepromaccesslock*/35. 36. structdelayed_workphy_poll; 37. structnet_device*ndev; 38. 39. spinlock_tlock; 40. 41. structmii_if_infomii; 42. u32msg_enable; 43. board_info_t;下面看一下具体代码。分析代码还是从init顺序开始。1. 注册平台驱动。主要完成的任务是:将驱动添加到总线上,完成驱动和设备的match,并执行驱动的probe函数。代码清单如下:1. staticstructplatform_driverdm9000_driver= 2. .driver= 3. .name=dm9000,/*用这个名字完成驱动和设备的match*/4. .owner=THIS_MODULE, 5. , 6. .probe=dm9000_probe, 7. .remove=_devexit_p(dm9000_drv_remove), 8. .suspend=dm9000_drv_suspend, 9. .resume=dm9000_drv_resume, 10. ; 11. 12. staticint_init 13. dm9000_init(void) 14. 15. printk(KERN_INFO%sEthernetDriver,V%sn,CARDNAME,DRV_VERSION); 16. 17. returnplatform_driver_register(&dm9000_driver); 18. 2. probe函数。主要完成的任务是:探测设备获得并保存资源信息,根据这些信息申请内存和中断,最后调用register_netdev注册这个网络设备。以下是代码清单,可以分成几个部分来看: 1) 首先定义了几个局部变量: struct dm9000_plat_data *pdata = pdev-dev.platform_data; struct board_info *db;/* Point a board information structure */ struct net_device *ndev; 2) 初始化一个网络设备。关键系统函数:alloc_etherdev() 3) 获得资源信息并将其保存在board_info变量db中。关键系统函数:netdev_priv(), platform_get_resource() 4) 根据资源信息分配内存,申请中断等等, 并将申请后的资源信息也保存到db中,并且填充ndev中的参数。 关键系统函数:request_mem_region(), ioremap()。 自定义函数:dm9000_set_io()5) 完成了第4步以后,回顾一下db和ndev中都有了什么: struct board_info *db:addr_res - 地址资源 data_res - 数据资源 irq_res - 中断资源 addr_req- 分配的地址内存资源io_addr - 寄存器I/O基地址 data_req - 分配的数据内存资源 io_data - 数据I/O基地址 dumpblk - IO模式 outblk - IO模式 inblk - IO模式 lock - 自旋锁(已经被初始化) addr_lock - 互斥锁(已经被初始化) struct net_device *ndev: base_addr - 设备IO地址 irq - 设备IRQ号 6) 设备复位。硬件操作函数dm9000_reset() 7) 读一下生产商和制造商的ID,应该是0x9000 0A46。 关键函数:ior() 8) 读一下芯片类型。 =以上步骤结束后我们可以认为已经找到了DM9000=9) 借助ether_setup()函数来部分初始化ndev。因为对以太网设备来讲,很多操作与属性是固定的,内核可以帮助完成。 10) 手动初始化ndev的ops和db的mii部分。 11) (如果有的话)从EEPROM中读取节点地址。这里可以看到mini2440这个板子上没有为DM9000外挂EEPROM,所以读取出来的全部是0xff。见函数dm9000_read_eeprom。 关于外挂EEPROM,可以参考datasheet上的7.EEPROM Format一节。 12) 很显然ndev是我们在probe函数中定义的局部变量,如果我想在其他地方使用它怎么办呢? 这就需要把它保存起来。内核提供了这个方法,使用函数platform_set_drvdata()可以将ndev保存成平台总线设备的私有数据。以后再要使用它时只需调用platform_get_drvdata()就可以了。13) 使用register_netdev()注册ndev。下面是代码清单:1. staticint_devinit 2. dm9000_probe(structplatform_device*pdev) 3. 4. structdm9000_plat_data*pdata=pdev-dev.platform_data; 5. structboard_info*db;/*Pointaboardinformationstructure*/6. structnet_device*ndev; 7. constunsignedchar*mac_src; 8. intret=0; 9. intiosize; 10. inti; 11. u32id_val; 12. 13. /*Initnetworkdevice*/14. /*使用alloc_etherdev()来生成一个net_device结构体,并对其公有成员赋值*/15. ndev=alloc_etherdev(sizeof(structboard_info); 16. if(!ndev) 17. dev_err(&pdev-dev,couldnotallocatedevice.n); 18. return-ENOMEM; 19. 20. 21. SET_NETDEV_DEV(ndev,&pdev-dev); 22. 23. dev_dbg(&pdev-dev,dm9000_probe()n); 24. 25. /*setupboardinfostructure*/26. db=netdev_priv(ndev); 27. memset(db,0,sizeof(*db); 28. 29. db-dev=&pdev-dev; 30. db-ndev=ndev; 31. 32. spin_lock_init(&db-lock); 33. mutex_init(&db-addr_lock); 34. 35. INIT_DELAYED_WORK(&db-phy_poll,dm9000_poll_work); 36. 37. db-addr_res=platform_get_resource(pdev,IORESOURCE_MEM,0); 38. db-data_res=platform_get_resource(pdev,IORESOURCE_MEM,1); 39. db-irq_res=platform_get_resource(pdev,IORESOURCE_IRQ,0); 40. 41. if(db-addr_res=NULL|db-data_res=NULL| 42. db-irq_res=NULL) 43. dev_err(db-dev,insufficientresourcesn); 44. ret=-ENOENT; 45. gotoout; 46. 47. 48. iosize=res_size(db-addr_res); 49. db-addr_req=request_mem_region(db-addr_res-start,iosize, 50. pdev-name); 51. 52. if(db-addr_req=NULL) 53. dev_err(db-dev,cannotclaimaddressregarean); 54. ret=-EIO; 55. gotoout; 56. 57. 58. db-io_addr=ioremap(db-addr_res-start,iosize); 59. 60. if(db-io_addr=NULL) 61. dev_err(db-dev,failedtoioremapaddressregn); 62. ret=-EINVAL; 63. gotoout; 64. 65. 66. iosize=res_size(db-data_res); 67. db-data_req=request_mem_region(db-data_res-start,iosize, 68. pdev-name); 69. 70. if(db-data_req=NULL) 71. dev_err(db-dev,cannotclaimdataregarean); 72. ret=-EIO; 73. gotoout; 74. 75. 76. db-io_data=ioremap(db-data_res-start,iosize); 77. 78. if(db-io_data=NULL) 79. dev_err(db-dev,failedtoioremapdataregn); 80. ret=-EINVAL; 81. gotoout; 82. 83. 84. /*fillinparametersfornet-devstructure*/85. ndev-base_addr=(unsignedlong)db-io_addr; 86. ndev-irq=db-irq_res-start; 87. 88. /*ensureatleastwehaveadefaultsetofIOroutines*/89. dm9000_set_io(db,iosize); 90. 91. /*checktoseeifanythingisbeingover-ridden*/92. if(pdata!=NULL) 93. /*checktoseeifthedriverwantstoover-ridethe94. *defaultIOwidth*/95. 96. if(pdata-flags&DM9000_PLATF_8BITONLY) 97. dm9000_set_io(db,1); 98. 99. if(pdata-flags&DM9000_PLATF_16BITONLY) 100. dm9000_set_io(db,2); 101. 102. if(pdata-flags&DM9000_PLATF_32BITONLY) 103. dm9000_set_io(db,4); 104. 105. /*checktoseeifthereareanyIOroutine106. *over-rides*/107. 108. if(pdata-inblk!=NULL) 109. db-inblk=pdata-inblk; 110. 111. if(pdata-outblk!=NULL) 112. db-outblk=pdata-outblk; 113. 114. if(pdata-dumpblk!=NULL) 115. db-dumpblk=pdata-dumpblk; 116. 117. db-flags=pdata-flags; 118. 119. 120. #ifdefCONFIG_DM9000_FORCE_SIMPLE_PHY_POLL121. db-flags|=DM9000_PLATF_SIMPLE_PHY;122. #endif123. 124. dm9000_reset(db); 125. 126. /*trymultipletimes,DM9000sometimesgetsthereadwrong*/127. for(i=0;i8;i+) 128. id_val=ior(db,DM9000_VIDL); 129. id_val|=(u32)ior(db,DM9000_VIDH)8; 130. id_val|=(u32)ior(db,DM9000_PIDL)16; 131. id_val|=(u32)ior(db,DM9000_PIDH)dev,readwrongid0x%08xn,id_val); 136. 137. 138. if(id_val!=DM9000_ID) 139. dev_err(db-dev,wrongid:0x%08xn,id_val); 140. ret=-ENODEV; 141. gotoout; 142. 143. 144. /*IdentifywhattypeofDM9000weareworkingon*/145. 146. id_val=ior(db,DM9000_CHIPR); 147. dev_dbg(db-dev,dm9000revision0x%02xn,id_val); 148. 149. switch(id_val) 150. caseCHIPR_DM9000A: 151. db-type=TYPE_DM9000A; 152. break; 153. caseCHIPR_DM9000B: 154. db-type=TYPE_DM9000B; 155. break; 156. default: 157. dev_dbg(db-dev,ID%02x=defaultingtoDM9000En,id_val); 158. db-type=TYPE_DM9000E; 159. 160. 161. /*fromthispointweassumethatwehavefoundaDM9000*/162. 163. /*driversystemfunction*/164. ether_setup(ndev); 165. 166. ndev-netdev_ops=&dm9000_netdev_ops; 167. ndev-watchdog_timeo=msecs_to_jiffies(watchdog); 168. ndev-ethtool_ops=&dm9000_ethtool_ops; 169. 170. db-msg_enable=NETIF_MSG_LINK; 171. db-mii.phy_id_mask=0x1f; 172. db-mii.reg_num_mask=0x1f; 173. db-mii.force_media=0; 174. db-mii.full_duplex=0; 175. db-mii.dev=ndev; 176. db-mii.mdio_read=dm9000_phy_read; 177. db-mii.mdio_write=dm9000_phy_write; 178. 179. mac_src=eeprom; 180. 181. /*tryreadingthenodeaddressfromtheattachedEEPROM*/182. for(i=0;idev_addr+i); 184. 185. if(!is_valid_ether_addr(ndev-dev_addr)&pdata!=NULL) 186. mac_src=platformdata; 187. memcpy(ndev-dev_addr,pdata-dev_addr,6); 188. 189. 190. if(!is_valid_ether_addr(ndev-dev_addr) 191. /*tryreadingfrommac*/192. 193. mac_src=chip; 194. for(i=0;idev_addri=ior(db,i+DM9000_PAR); 196. 197. 198. if(!is_valid_ether_addr(ndev-dev_addr) 199. dev_warn(db-dev,%s:InvalidethernetMACaddress.Please200. setusingifconfign,ndev-name); 201. 202. platform_set_drvdata(pdev,ndev); 203. ret=register_netdev(ndev); 204. 205. if(ret=0) 206. printk(KERN_INFO%s:dm9000%cat%p,%pIRQ%dMAC:%pM(%s)n, 207. ndev-name,dm9000_type_to_char(db-type), 208. db-io_addr,db-io_data,ndev-irq, 209. ndev-dev_addr,mac_src); 210. return0; 211. 212. out: 213. dev_err(db-dev,notfound(%d).n,ret); 214. 215. dm9000_release_board(pdev,db); 216. free_netdev(ndev); 217. 218. returnret; 219. 3. platform_driver的remove, suspend和resume的实现 remove函数的功能是把设备从内核中移除,释放内存区域。该函数在卸载模块时被调用。代码清单如下:1. staticint_devexit 2. dm9000_drv_remove(structplatform_device*pdev) 3. 4. structnet_device*ndev=platform_get_drvdata(pdev); 5. 6. platform_set_drvdata(pdev,NULL); 7. 8. unregister_net
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年二级建造师考试试题含答案详解(培优a卷)
- 2025年安全教育培训试题含答案【巩固】
- 2025年幼儿教师资格证综合素质考试题库及答案1
- 安全员之B证(项目负责人)题库完整答案
- 安全员a证考试题库
- 劳务作业人员普法维权培训考试试卷答案
- 2025年中级会计考试《财务管理》试题及答案
- 二建资料题库及答案内部题库
- 低压电工考试题库(含答案)
- 变态反应试题库及答案(副高)
- 2025年安全信息考试试题及答案
- 2025衢州市市级机关事业单位第三期编外招聘39人笔试考试参考试题及答案解析
- 2025标准网签购房合同范本下载
- 云南人力资源开发有限责任公司招聘笔试题库2025
- 人教版八年级上册生物第五单元第一章综合实践项目 设计并制作生态瓶
- 山西某污水处理厂投资估算编制分析
- 2025全国医疗应急能力培训系列课程参考答案
- 江西体彩中心笔试题库及答案
- 理性看待分数用心守护成长+2025-2026学年高二上学期期中家长会主题班会
- 网络安全技术课件下载
- 上海安保考试题目及答案
评论
0/150
提交评论