最近需要在Freescale i.MX6上移植Ethernet AVB的内核patch,Ethernet AVB的Wiki:
http://en.wikipedia.org/wiki/Audio_Video_Bridging,而Freescale原来已经在kernel
3.0.35 LTIB 4.0.0的基础上提供了patch:https://community.freescale.com/docs/DOC-95578,现在需要做的是把kernel 3.0.35上的patch移植到yocto kernel-3.10.17_ga上,第一次听上去就感觉像是代码的复制粘贴,不过首要的问题是Ethernet
Driver都没有看过,还谈何移植,所以索性把Ethernet Driver也学习一遍,Google了一段时间,找到了几份不错的学习资料,特此共享之:
http://free-electrons.com/doc/network-drivers-lab.pdf
《Linux network driver development Training lab book》个人认为最经典的学习资料,从无到有地讲述了网卡驱动的编写过程,基于Atmel的MPU的Ethernet Controller。
http://lwn.net/images/pdf/LDD3/ch17.pdf
《LDD3》的第17章节专门讲述了network driver,不过这里面着重介绍了一些技术细节,适合当工具书来看。
http://free-electrons.com/training/kernel/
这个页面是偶然找到的,不过对free-electrons印象一直不错,所以强烈推荐,这是专门讲内核与驱动开发的材料。
那几天我硬着头皮把
《Linux network driver development Training lab book》看完了,后来又看了两遍,对Linux Ethernet Driver有了比较整体的认识,接下来的工作就是理解i.MX6 3.0.35内核的驱动源码。
这里不得不称赞的是,Freescale的驱动有相应的《i.MX 6Dual/6Quad Linux Reference
Manual》文档可以从官网上下到,它会给你一些简要的介绍。这对于很多初学者来说还是非常友好的。首先找到源码的位置:
drviers/net/fec.c对应还有一个fec.h作头文件,同时现在大多数的MPU还集成了支持FEC1588的硬件控制器,从硬件上支持一些对时间有要求的应用,比如Ethernet AVB等等。这里以3.0.35内核目前最新release的LTIB 4.1.0环境的内核为参考。
首先是驱动程序入口:
static int __initfec_enet_module_init(void){ printk(KERN_INFO "FEC Ethernet Driver
");
return platform_driver_register(&fec_driver);}
static void __exitfec_enet_cleanup(void){ platform_driver_unregister(&fec_driver);}
module_exit(fec_enet_cleanup);module_init(fec_enet_module_init);
MODULE_LICENSE("GPL");
现在看到printk后面那个KERN_INFO一点都不觉得别扭,当初还被坑了。
printk有8个loglevel,定义在
中,其中数值范围从0到7,数值越小,优先级越高。
#define KERN_EMERG "<0>"
#define KERN_ALERT "<1>"
#define KERN_CRIT "<2>"
#define KERN_ERR "<3>"
#define KERN_WARNING "<4>"
#define KERN_NOTICE "<5>"
#define KERN_INFO "<6>"
#define KERN_DEBUG "<7>"
默认在Ubuntu rootfs下,优先级大于4才会显示出来,没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认情况下是4,具体的解释见http://blog.sina.com.cn/s/blog_636a55070101i6sr.html)所以如果不设置优先级的情况下,在中断上是看不到printk打印的语句的,不过在dmesg中可以看到,当然也可以echo
8 > /proc/sys/kernel/printk来修改默认输出的最低优先级。
static int fec_mac_addr_setup(char *mac_addr){ char *ptr, *p = mac_addr; unsigned long tmp; int i = 0, ret = 0;
while (p && (*p) && i < 6) { ptr = strchr(p, ':'); if (ptr) *ptr++ = '