转载:http://www.eefocus.com/lishutong/blog/13-06/295363_c502a.html
虽然此文名为移植,但实际的工作量很小,只需简单修改即可完成tiny210板上的以太网驱动程序移植。
tiny210开发板上板载DM9000网络芯片,集成了网络控制器和PHY,外接一个网络变压器。uboot源码目录driver/net提供了驱动程序dm9000x.c/dm9000x.h。由于DM9000通过SRAM接口挂接在s5pv210的SROM端口1上,16位总线模式;所以从思路上来说,只需要将dm9000驱动程序加入Uboot编译中,并且在uboot初始化时打开SROM端口1,并注册dm9000驱动程序,s5pv210即可访问dm9000内部寄存器。
以太网设备抽像结构及其注册
uboot对以太网设备的抽像和对串口设备的抽像方法很类似(串口设备抽像见UBoot的Serial驱动分析),每个以太网设备用struct eth_device结构表示。struct eth_device定义如下:
struct eth_device {
char name[16];
unsigned char enetaddr[6];
int iobase;
int state;int (*init) (struct eth_device *, bd_t *);
int (*send) (struct eth_device *, void *packet, int length);
int (*recv) (struct eth_device *);
void (*halt) (struct eth_device *);
#ifdef CONFIG_MCAST_TFTP
int (*mcast) (struct eth_device *, u32 ip, u8 set);
#endif
int (*write_hwaddr) (struct eth_device *);
struct eth_device *next;
int index;
void *priv;
};
如果要支持多个以太网设备,则需定义多个struct eth_device,然后通过调用eth_register(device)向uboot注册该结构。所有注册的设备被加入到eth_devices所指向的链表中,eth_current指向当前的设备,这种组织方式和serial驱动类似。
static struct eth_device *eth_devices; // 位于net/eth.c
struct eth_device *eth_current;
DM9000x.c中包含完成了定义结构和注册工作的初始化函数,我们只需要调用:
int dm9000_initialize(bd_t *bis)
{
struct eth_device *dev = &(dm9000_info.netdev);/* Load MAC address from EEPROM */
dm9000_get_enetaddr(dev);dev->init = dm9000_init;
dev->halt = dm9000_halt;
dev->send = dm9000_send;
dev->recv = dm9000_rx;
sprintf(dev->name, "dm9000");eth_register(dev);
return 0;
}
由于uboot的工作机制比较简单,不需要过于考虑效率,因而DM9000的驱动函数实现也比较简单。主要完成初始化、停止、发送帧、接收帧等操作。
s5pv210上DM9000驱动程序移植
移植步骤很简单,除了将DM9000x.c加入到uboot源码编译中外,还需要在某个地方初始化DM9000所在的SROM端口1以及调用dm9000_initialize()。
我认为这些初始化调用不能随便加入到源码中的某个位置,uboot应该有它自己的流程。所以,分析了下net/eth.c源码,因为这部分与硬件最为接近,发现int eth_initialize(bd_t *bis)完成整个以太网驱动硬件底层初始化。而其中就有调用board_eth_init(),从该函数的字面意思来看是完成与目标板相关的以太网驱动初始化。而该函数的缺省实现是直接返回错误,即初始化失败。
static int __def_eth_init(bd_t *bis)
{
return -1;
}int cpu_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
int board_eth_init(bd_t *bis) __attribute__((weak, alias("__def_eth_init")));
依照uboot的源码风格,board_eth_init是可以被重向定义的。于是我重新定义了该函数,并将相关的初始化操作放在里面。
int board_eth_init(bd_t *bis)
{
int ret = 0;SROM_BW_REG &= ~(0xf << 4);
SROM_BW_REG |= (0<<7) | (0<<6) | (1<<5) | (1<<4); // SROM_ADDR[22:0] <= HADDR[23:1],16位总线
SROM_BC1_REG = ((0<<28)|(1<<24)|(5<<16)|(1<<12)|(4<<8)|(6<<4)|(0<<0));
ret = dm9000_initialize(bis);
return ret;
}
通过上面的两步,即完成了以太网驱动程序的移植。
UDP/IP协议栈概述
UBoot提供了一个简单的网络协议栈,所有源码位于net目录下。实现的协议有ARP、IP、UDP、ICMP、bootp、nfs、rarp、tftp,没有实现tcp协议。所以说,这个协议只能称作是UDP/IP协议栈,而不能称作TCP/IP协议栈。
类似的协议栈我在大学里实现过一个,当时能实现ping操作、tftp文件传输,实现比较简单,跟uboot的实现类似。所以,这里不做详细分析。
一般而言,在初始完成移植后,常使用ping来完成基本通信测试。ping命令基于IP/ICMP协议,首先会向目标IP地址的主机发送一个ICMP数据包,目标主机收到响应后会将该数据包原样返回,如果uboot接收到该数据包,即认为和主机已经连通。
如果不能ping通,有可能是驱动程序根本就没有把ICMP数据包发出去,或者是发送了错误的数据包,也有可能是发出去了但主机没有收到或者没有发送响应。这里就需要借助以太网抓包工具来抓取网络上的整个数据流,才能进一步分析是何种原因。抓包工具推荐使用wireshark,软件截图如下:
uboot驱动程序移植的总结
移植完后,使用tftp传输文件,大概250KB/s,速度还行。这个驱动程序的移植看起来没有太多技术含量。源码都有,要做的只不过是修改后能在特定的目标板上运行。
移植本身并不复杂,uboot的doc目录下有相关的说明文档;外加源码开放,如有不理解的,可参考其它目标板的移植。这样通过参考、修改就可完成这个移植过程。
无论是移植何种驱动,移植的工作均可以分成两块,先是熟悉硬件、实现驱动接口函数;而后才是按照uboot的源码格式要求编写。前者需要通用的技术基础,跟任何bootloader、os都无关,取决于自己是否对硬件能有深入地理解;而后者只不过是适应一种软件框架。
所以,从我目前学习和理解uboot的目的出发,接下来的USB驱动、NAND驱动程序移植中,以熟悉uboot驱动框架为主,不深究硬件细节。