DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一
Posted 刁海威
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一相关的知识,希望对你有一定的参考价值。
关于dm9000的驱动移植分为两篇,第一篇在mini2440上实现,基于linux2.6.29,也成功在在6410上移植了一遍,和2440非常类似,第二篇在fs4412(Cortex A9)上实现,基于linux3.14.78,用设备树匹配,移植过程中调试和整体理解很重要,一路上幸有良师益友指点,下面详细介绍:
1.物理时序分析相关
DM9000芯片是DAVICOM公司生产的一款以太网处理芯片,提供一个通用的处理器接口、一个10/100M自适应的PHY芯片和4K双字的SRAM.内部框架如下,涉及到4个基本概念:
1、TCP/IP参考模型包含应用层、传输层、网络层和网络接口层,其中的网络接口层包含有数据链路层和网络层;
2、MAC:网卡数据链路层的芯片成为MAC控制器,数据链路层则提供寻址机构、数据帧的构建、数据差错检查、传送控制、向网络层提供标准的数据接口等功能;
3、PHY:网卡物理芯片,物理定义了数据传送和接收所需的电和光信号、线路状态、时钟基准、数据编码和电路等,并向数据链路层设备提供标准接口;
4、MII:介质无关接口,它是IEEE802.3定义的以太网行业标准,包含一个数据接口,以及一个MAC和PHY之间的管理接口,数据接口包括分别用于发送器和接收器的两条独立通道,管理接口用来监视和控制PHY,介质无关表明不对MAC硬件重新设计或替换的情况下,任何类型的PHY设备都可以正常工作;
图1 DM9000内部结构框架
图2 DM9000读时序
图3 DM9000写时序
图4 s3c2440的BANK4的读写时序
图5 s3c2440的存储BANK
图6 mini2440的DM9000相关原理图
1、物理连接和理解:S3c2440有27根地址线:2^27=128MB,所以一个bank最大可寻址128M, 8个bank说明s3c2440最大可寻址1G,Mini2440采用的是dm9000直接连接CPU(s3c2440)上。就像是nandflash一样直接被挂在CUP上,被挂在s3c2440的bank4上,s3c2440芯片把存储系统分为了8个Bank,由nGCS0[0]~nGCS[7] 这8根引脚决定当前访问的是哪一个Bank对应的存储器。其中,前6个Bank用于连接ROM或者SRAM,第7个Bank地址作为SDRAM的起始地址(即0x30000000)。DM9000通过CMD端口控制写命令和读写数据,mini2440开发板上的DM9000和S3c2440的连接方式如图6所示,数据信号SD0-SD15对应 DATA0-DATA15 ,CMD ADDR2 识别为地址还是数据,INT EINT7 中断,IOR# nOE 读命令使能,IOW# nWE 写命令使能,AEN nGCS4 片选使能,连接了16条数据线,1条地址线,而这唯一的一条地址线用于判断数据线传输的是地址还是数据,所以这16条数据线为数据和地址复用。
2、详尽时序分析:要使挂接在BANK4上的DM9000正常工作,需要配置存储器控制器的BWSCON和BANKCON4两个寄存器,前者可以设置DM9000的总线宽度,后者可以设置DM9000的访问时序,DM9000的寄存器读写时序分别如图2和图3所示,T1s3C2440的BANK4读写时序如图4,内存控制器使用HCLK作为时钟,在HCLK为100MHz时,1个clock大约为10ms,通过对比:Tcos对应T1,呢么最少应该为5ns,也就是1个clock Tacc对应T2,呢么最少应该为22ns,呢么我们这里最少也要选3个clock,也就是30ns, Toch对应T5,Toch最少应该为5ns,也就是1个clock。从DM9000的读写时序图中可以看出,T2+T6实际上构成了DM9000的一个访问周期,因此还需要满足:Tacs + Tcos + Tacc + Tcoh + Tcah>= T2+T6,最终使用下面的表达式来表达:
(Tacs >= 0 && Tacs <= 4) && (Tcos >= 1 && Tcos <= 4) && (Tacc >= 3 && Tacc <= 14 ) && (Tcoh >=1 && Tcoh <= 4 ), 故使用下值进行设置:
在dm9000_init()函数中添加如下代码:
1 #if defined(CONFIG_ARCH_S3C2410) 2 unsigned int oldval_bwscon; //用来保存BWSCON寄存器的值 3 unsigned int oldval_bankcon4; //用来保存S3C2410_BANKCON4寄存器的值 4 #endif 5 … 6 #if defined (CONFIG_ARCH_S3C2410) 7 //设置BANK4:总线宽度为16,使能nWAIT 8 oldval_bwscon = *((volatile unsigned int *)S3C2410_BWSCON); 9 *((volatile unsigned int *)S3C2410_BWSCON) = (oldval_bwscon & ~(3<<16)) \\ 10 | S3C2410_BWSCON_DW4_16 | S3C2410_BWSCON_WS4 | S3C2410_BWSCON_ST4; //使能总线宽度为16位,使能nWAIT 11 //设置BANK4的时间参数 12 oldval_bankcon4 = *((volatile unsigned int *)S3C2410_BANKCON4); 13 *((volatile unsigned int *)S3C2410_BANKCON4) = 0x1f7c; // BANK4控制寄存器,用于控制BANK4外接设备的访问时序 14 #endif 15 … 16 if (!is_valid_ether_addr(ndev->dev_addr)) 17 dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " 18 "set using ifconfig\\n", ndev->name); 19 20 #if defined(CONFIG_ARCH_S3C2410) 21 printk("Now use the default MAC address:08:90:90:90:90:90\\n"); 22 ndev->dev_addr[0] = 0x08; 23 ndev->dev_addr[1] = 0x90; 24 ndev->dev_addr[2] = 0x90; 25 ndev->dev_addr[3] = 0x90; 26 ndev->dev_addr[4] = 0x90; 27 ndev->dev_addr[5] = 0x90; 28 #endif
2.核心数据结构和网络子系统分析
网络体系结构由5个部分组成,分别如下:
系统调用接口:为应用程序提供访问内核网络子系统的方法,主要指socket系统调用;协议无关接口:实现一组基于socket的通用函数来访问各种不同的协议;网络协议:网络协议层用于实现各种具体的网络协议,如TCP、UDP;设备无关接口:设备物管接口层将协议和各种网络设备驱动连接在一起,这一层提供一组通用函数供底层网络设备驱动程序使用,使他们可以操作高层协议栈;设备驱动:网络体系结构的最底部是负责管理物理网络设备的设备驱动程序层。
将上面概念搞清楚后,紧接着分析核心数据结构net_device和sk_buff,分别用来描述一个网络设备和用来接收和发送的数据包,以不变应万变,实现多种硬件在软件层次上的统一:
int (*init)(struct net_device*dev); //初始化函数,该函数在register_netdev时被调用来完成对net_device结构的初始化
int (*hard_start_xmit)(structsk_buf*skb,struct net_device *dev); //数据发送函数
int (*hard_header)(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len); //该方法根据先前检索到的源和目的硬件地址建立硬件头
int (*rebuild_header)(struct sk_buff *skb); //以太网的mac地址是固定的,为了高效,第一个包去询问mac地址,得到对应的mac地址后就会作为cache把mac地址保存起来。以后每次发包不用询问了,直接把包的地址拷贝出来
void (*tx_timeout)(struct net_device *dev); //如果数据包发送在超时时间内失败,这时该方法被调用,这个方法应该解决失败的问题,并重新开始发送数据
struct net_device_stats *(*get_stats)(struct net_device *dev); //当应用程序需要获得接口的统计信息时,这个方法被调用
int (*set_config)(struct net_device *dev, struct ifmap *map); //改变接口的配置,比如改变I/O端口和中断号等,现在的驱动程序通常无需该方法
int (*do_ioctl)(struct net_device *dev, struct ifmap *map); //该函数用来处理特定网络命令的ioctl命令
void (*set_multicast_list)(struct net_device *dev); //当设备的组播列表改变或设备标志改变时,该方法被调用
int (*set_mac_address)(struct net_device *dev, void *addr); //该函数用来设置网络设备的MAC地址
传输包的所有信息都保存在sk_buff结构中,这一结构被所有网络层使用,也被称为“套接字缓冲区”,用于在网络子系统各层之间传递数据。内核中,所有的sk_buff结构都被组织在一个双向链表中,见下图:
3.驱动代码详尽分析
从网上下载下来的Linux2.6.29中有完整的DM9000网卡驱动,该驱动对应源文件drivers/net/dm9000.c,在移植bootloader的过程中可知,DM9000可通过S3C2440地址总线访问,因此有专属它的IO内存。驱动将DM9000的IO内存和IRQ资源以平台设备资源的方法进行管理,并在dm9000.c中实现并注册了相应的平台驱动,这样,只要向内核中注册板级相关的DM9000平台设备,网卡就能正常工作,按照面向对象设计的思想,任何一个网卡设备都可以由net_device结构描述,这是因为net_device包含了所有网卡设备的共性,而任何一个具体网卡设备的表述结构应该由net_device派生而来。其中的board_info可视为对net_device结构的扩展,以描述一个具体的网卡设备,dm9000的平台驱动定义和board_info如下:
step1、紧接着在注册平台驱动时,内核遍历平台总线上的所有平台设备,linux3.0之后有四种方式进行匹配,典型的就是通过基于设备树风格的匹配,现在移植的为2.6.29时通过匹配platform_device设备名和驱动名的名称匹配,并在找到匹配的设备后,调用平台驱动中的probe函数。平台驱动通常利用probe()函数从匹配上的平台设备中获取平台资源,并根据这些资源申请和映射IO内存、获取并注册IRQ中断,dm9000_probe()最终调用register_netdev()注册网卡设备,下面详尽分析dm9000_probe()函数:
1 static int __devinit dm9000_probe(struct platform_device *pdev) 2 { 3 struct dm9000_plat_data *pdata = pdev->dev.platform_data;//驱动程序中获得系统定义的网卡板级信息 4 //定义在/arch/arm/mach-s3c64xx/mach-smdk6410.c中。 5 struct board_info *db; /* Point a board information structure */ 6 struct net_device *ndev; //定义设备结构体 7 const unsigned char *mac_src; 8 int ret = 0; 9 int iosize; 10 int i; 11 u32 id_val; 12 13 /* Init network device */ 14 //分配生成net_device结构体 alloc_etherdev是alloc_netdev()针对以太网的快捷操作函数 15 ndev = alloc_etherdev(sizeof(struct board_info)); 16 17 //判断是否分配正确 18 if (!ndev) { 19 dev_err(&pdev->dev, "could not allocate device.\\n"); 20 return -ENOMEM; 21 } 22 //建立net_device到device的连接 23 SET_NETDEV_DEV(ndev, &pdev->dev); 24 //内核输出信息 25 dev_dbg(&pdev->dev, "dm9000_probe()\\n"); 26 //函数netdev_priv直接返回了net_device结构末端地址,也就是网卡私有数据结构的起始地址。 27 /* setup board info structure */ 28 db = netdev_priv(ndev); 29 30 db->dev = &pdev->dev; 31 db->ndev = ndev; 32 33 spin_lock_init(&db->lock);//初始化自旋锁 34 mutex_init(&db->addr_lock); 35 36 INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work); //?? 37 //获取平台设备资源 resource 地址空间、数据空间、中断信号 7号中断 38 db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 39 db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 40 db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 41 //判断资源是否获取成功 42 if (db->addr_res == NULL || db->data_res == NULL || 43 db->irq_res == NULL) { 44 dev_err(db->dev, "insufficient resources\\n"); 45 ret = -ENOENT; 46 goto out; 47 } 48 //platform_get_resource的变体 同 platform_get_resource(pdev, IORESOURCE_IRQ, 1) 49 db->irq_wake = platform_get_irq(pdev, 1); 50 if (db->irq_wake >= 0) { 51 dev_dbg(db->dev, "wakeup irq %d\\n", db->irq_wake); 52 //前面获得中断号 申请中断 53 ret = request_irq(db->irq_wake, dm9000_wol_interrupt, 54 IRQF_SHARED, dev_name(db->dev), ndev); 55 if (ret) { 56 dev_err(db->dev, "cannot get wakeup irq (%d)\\n", ret); 57 } else { 58 /* test to see if irq is really wakeup capable */ 59 ret = set_irq_wake(db->irq_wake, 1); 60 if (ret) { 61 dev_err(db->dev, "irq %d cannot set wakeup (%d)\\n", 62 db->irq_wake, ret); 63 ret = 0; 64 } else { 65 set_irq_wake(db->irq_wake, 0); 66 db->wake_supported = 1; 67 } 68 } 69 } 70 //IO资源分配大小 地址 为resource分配内存 71 iosize = resource_size(db->addr_res); 72 db->addr_req = request_mem_region(db->addr_res->start, iosize, 73 pdev->name); //内存申请 74 75 if (db->addr_req == NULL) { 76 dev_err(db->dev, "cannot claim address reg area\\n"); 77 ret = -EIO; 78 goto out; 79 } 80 81 db->io_addr = ioremap(db->addr_res->start, iosize); 82 83 if (db->io_addr == NULL) { 84 dev_err(db->dev, "failed to ioremap address reg\\n"); 85 ret = -EINVAL; 86 goto out; 87 } 88 89 iosize = resource_size(db->data_res); 90 db->data_req = request_mem_region(db->data_res->start, iosize, 91 pdev->name); 92 93 if (db->data_req == NULL) { 94 dev_err(db->dev, "cannot claim data reg area\\n"); 95 ret = -EIO; 96 goto out; 97 } 98 99 db->io_data = ioremap(db->data_res->start, iosize); 100 101 if (db->io_data == NULL) { 102 dev_err(db->dev, "failed to ioremap data reg\\n"); 103 ret = -EINVAL; 104 goto out; 105 } 106 //初始化net_device中的成员 107 /* fill in parameters for net-dev structure */ 108 ndev->base_addr = (unsigned long)db->io_addr; //网络接口的IO基地址 109 ndev->irq = db->irq_res->start;//中断号 ifconfig时会打印出这个值 也可通过这个修改 110 111 /* ensure at least we have a default set of IO routines */ 112 dm9000_set_io(db, iosize); 113 114 /* check to see if anything is being over-ridden ,pdata 是在dm9000_probe函数开头获取的平台数据,其flags成员记录了DM9000的访问位宽,下面的代码根据flags的值,调用dm9000_set_io()函数来确定board_info结构中关于DM9000块读写操作的具体函数*/ 115 if (pdata != NULL) { 116 /* check to see if the driver wants to over-ride the 117 * default IO width */ 118 //检测与板级信息是否相同 119 if (pdata->flags & DM9000_PLATF_8BITONLY) 120 dm9000_set_io(db, 1); 121 122 if (pdata->flags & DM9000_PLATF_16BITONLY) 123 dm9000_set_io(db, 2); 124 125 if (pdata->flags & DM9000_PLATF_32BITONLY) 126 dm9000_set_io(db, 4); 127 128 /* check to see if there are any IO routine 129 * over-rides */ 130 131 if (pdata->inblk != NULL) 132 db->inblk = pdata->inblk; 133 134 if (pdata->outblk != NULL) 135 db->outblk = pdata->outblk; 136 137 if (pdata->dumpblk != NULL) 138 db->dumpblk = pdata->dumpblk; 139 140 db->flags = pdata->flags; 141 } 142 143 #ifdef CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL 144 db->flags |= DM9000_PLATF_SIMPLE_PHY; 145 #endif 146 147 dm9000_reset(db); 148 149 /* try multiple times, DM9000 sometimes gets the read wrong */ 150 for (i = 0; i < 8; i++) { //宏定义在dm9000.h中 151 id_val = ior(db, DM9000_VIDL); 152 id_val |= (u32)ior(db, DM9000_VIDH) << 8; 153 id_val |= (u32)ior(db, DM9000_PIDL) << 16; 154 id_val |= (u32)ior(db, DM9000_PIDH) << 24; 155 156 if (id_val == DM9000_ID) 157 break; 158 dev_err(db->dev, "read wrong id 0x%08x\\n", id_val); 159 } 160 161 if (id_val != DM9000_ID) { 162 dev_err(db->dev, "wrong id: 0x%08x\\n", id_val); 163 ret = -ENODEV; 164 goto out; 165 } 166 167 /* Identify what type of DM9000 we are working on */ 168 169 id_val = ior(db, DM9000_CHIPR); 170 dev_dbg(db->dev, "dm9000 revision 0x%02x\\n", id_val); 171 172 switch (id_val) { 173 case CHIPR_DM9000A: 174 db->type = TYPE_DM9000A; 175 break; 176 case CHIPR_DM9000B: 177 db->type = TYPE_DM9000B; 178 break; 179 default: 180 dev_dbg(db->dev, "ID %02x => defaulting to DM9000E\\n", id_val); 181 db->type = TYPE_DM9000E; 182 } 183 184 /* dm9000a/b are capable of hardware checksum offload */ 185 if (db->type == TYPE_DM9000A || db->type == TYPE_DM9000B) { 186 db->can_csum = 1; 187 db->rx_csum = 1; 188 ndev->features |= NETIF_F_IP_CSUM; 189 } 190 191 /* from this point we assume that we have found a DM9000 */ 192 193 /* driver system function */ //初始化以太网设备的公有成员 194 ether_setup(ndev);//在调用register_netdev之前必须初始化完全。该函数中为net_device设置了很多默认值 195 196 ndev->netdev_ops = &dm9000_netdev_ops; 197 ndev->watchdog_timeo = msecs_to_jiffies(watchdog); 198 ndev->ethtool_ops = &dm9000_ethtool_ops; 199 200 db->msg_enable = NETIF_MSG_LINK; 201 db->mii.phy_id_mask = 0x1f; 202 db->mii.reg_num_mask = 0x1f; 203 db->mii.force_media = 0; 204 db->mii.full_duplex = 0; 205 db->mii.dev = ndev; 206 db->mii.mdio_read = dm9000_phy_read; 207 db->mii.mdio_write = dm9000_phy_write; 208 209 mac_src = "eeprom"; 210 211 /* try reading the node address from the attached EEPROM */ 212 for (i = 0; i < 6; i += 2) 213 dm9000_read_eeprom(db, i / 2, ndev->dev_addr+i); 214 215 if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { 216 mac_src = "platform data"; 217 memcpy(ndev->dev_addr, pdata->dev_addr, 6); 218 } 219 220 if (!is_valid_ether_addr(ndev->dev_addr)) { 221 /* try reading from mac */ 222 223 mac_src = "chip"; 224 for (i = 0; i < 6; i++) 225 ndev->dev_addr[i] = ior(db, i+DM9000_PAR); 226 } 227 228 if (!is_valid_ether_addr(ndev->dev_addr)) 229 dev_warn(db->dev, "%s: Invalid ethernet MAC address. Please " 230 "set using ifconfig\\n", ndev->name); 232 platform_set_drvdata(pdev, ndev); //将ndev保存成平台总线设备的私有数据,以后再要使用时可以通过调用platform_get_drvdata(),从响应平台设备中获取 233 ret = register_netdev(ndev); //注册net_device结构体 234 235 if (ret == 0) 236 printk(KERN_INFO "%s: dm9000%c at %p,%p IRQ %d MAC: %pM (%s)\\n", 237 ndev->name, dm9000_type_to_char(db->type), 238 db->io_addr, db->io_data, ndev->irq, 239 ndev->dev_addr, mac_src); 240 return 0; 241 242 out: 243 dev_err(db->dev, "not found (%d).\\n", ret); 244 245 dm9000_release_board(pdev, db); 246 free_netdev(ndev); //分配错误则释放net_device结构 247 248 return ret; 249 }
step2、接下来向内核注册中断、复位并初始化dm9000、检查MII接口,由dm9000_open()函数来实现:
1 m9000_open(struct net_device *dev) 2 { 3 board_info_t *db = netdev_priv(dev);//获取设备私有数据 返回board_info_t的地址 4 unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; 5 6 if (netif_msg_ifup(db)) 7 dev_dbg(db->dev, "enabling %s\\n", dev->Linux驱动TQ2440 DM9000E网卡驱动移植(Linux-2.6.30.4)