最近在做Linux内核移植,整体的感觉是这样的,想要完全的阅览Linux内核代码几乎是不或许的,至少这还不是嵌入式学期初期的重要任务。内核代码解压后有250M左右,据统计,有400多万行,并且触及到了软件和硬件两方面的许多常识,凭一人之力在短时间内阅览Linux内核代码是底子不或许的,强行阅览或许会消除咱们嵌入式学习的积极性,最终乃至或许抛弃嵌入式学习,假如真的想阅览内核代码来进步自己水平的话能够等熟练掌握嵌入式今后再回过头来阅览,这样了解也会更深入,更透彻。
我以为Linux内核移植的初期阶段应该将要点放在剖析内核设备驱动上。实践上,Linux内核的移植便是设备驱动的移植,内核自身不会直接拜访硬件,是经过驱动程序来直接操控硬件的,而其他的高档功用如内存办理,进程办理等是通用的,无需做其他装备,所以咱们只需求装备相关的驱动即可完成Linux内核移植。驱动移植的关键在于了解在驱动的结构,本文将以Nand驱动为例,剖析Linux内核的驱动结构。
在剖析驱动结构之前,还需求了解下内核辨认设备的办法,内核经过驱动程序辨认设备的办法有两种,一种是驱动程序自身带有设备信息,比方开端地址、中止号等,加载驱动时就能够依据驱动中的信息来辨认设备;另一种是驱动程序自身没有设备信息,可是内核中现已依据其他办法确认了许多设备信息,加载驱动时将驱动程序与这些设备逐一比较,确认两者是否匹配,假如匹配就能够运用该驱动来辨认设备了。内核常选用的是第二种办法,这样办法可将各种设备会集在一个文件中办理,当开发板的装备改动时便于修正代码。对应的,内核文件include/linux/platform_device.h中界说了两个结构,一个是platform_device,用来描绘设备信息,一个是platform_driver,用来描绘驱动信息,内核发动后首要结构链表将plartfrom_device结构组织起来得到一个设备链表,当加载某个驱动时依据platform_driver供给的信息与设备链表逐个进行匹配,这便是内核设备辨认的大体进程,详细的进程比这杂乱许多,这儿不做过多研讨。下面咱们开端剖析Linux内核的Nand驱动。
这儿以Linux内核的3.5.3中默许的mini2440开发板为例,首要定位到arm/arm/mach-s3c24xx/mach-mini2440.c,然后找到如下结构:
- staticstructplatform_device*mini2440_devices[]__initdata={
- &s3c_device_ohci,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_rtc,
- &s3c_device_usbgadget,
- &mini2440_device_eth,
- &mini2440_led1,
- &mini2440_led2,
- &mini2440_led3,
- &mini2440_led4,
- &mini2440_button_device,
- &s3c_device_nand,
- &s3c_device_sdi,
- &s3c_device_iis,
- &uda1340_codec,
- &mini2440_audio,
- &samsung_asoc_dma,
- };
显着,这儿便是内核需求的设备列表,经过后边的mini2440_init函数中的
- platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
注册到内核,然后由内核进行办理,显着,跟咱们剖析的Nand相关的便是s3c_device_nand,这就代表咱们开发版上的Nand flash,咱们先定位到它的界说,在arch/arm/plat-samsung/devs.c中有如下代码
- staticstructresources3c_nand_resource[]={
- [0]=DEFINE_RES_MEM(S3C_PA_NAND,SZ_1M),
- };
- structplatform_devices3c_device_nand={
- .name=”s3c2410-nand”,
- .id=-1,
- .num_resources=ARRAY_SIZE(s3c_nand_resource),
- .resource=s3c_nand_resource,
- };
第二个 结构便是s3c_device_nand的界说,之所以带上第一个结构是因为界说s3c_device_nand时用到了s3c_nand_resource,咱们先看一下s3c_device_nand的界说,s3c_device_nand只清晰认义了Nand设备的称号和设备ID,并没有给出详细的寄存器信息,加上s3c_nand_resource的姓名带有资源的意思,因而咱们判定,寄存器信息应该在s3c_nand_resource中,从s3c_nand_resource的界说中咱们只能看到很少的信息,要想了解详细信息需求看一下struct resource和宏DEFINE_RES_MEM的界说及
- structresource{
- resource_size_tstart;
- resource_size_tend;
- constchar*name;
- unsignedlongflags;
- structresource*parent,*sibling,*child;
- };
这儿 能够看到,struct resource中界说了开始,完毕,姓名等信息,咱们再来看一下DEFINE_RES_MEM的界说
- #defineDEFINE_RES_NAMED(_start,_size,_name,_flags)
- {
- .start=(_start),
- .end=(_start)+(_size)-1,
- .name=(_name),
- .flags=(_flags),
- }
- #defineDEFINE_RES_MEM_NAMED(_start,_size,_name)
- DEFINE_RES_NAMED((_start),(_size),(_name),IORESOURCE_MEM)
- #defineDEFINE_RES_MEM(_start,_size)
- DEFINE_RES_MEM_NAMED((_start),(_size),NULL)
我这儿整合了一下上面的信息,将相关的宏都做了一下追寻,因而,s3c_nand_resource的实践界说为
- {
- .start=(S3C_PA_NAND),
- .end=(S3C_PA_NAND)+(SZ_1M)-1,
- .name=(NULL),
- .flags=(IORESOURCE_MEM),
- }
追寻可知,S3C_PA_NAND界说如下
- #defineS3C2410_PA_NAND(0x4E000000)
- #defineS3C24XX_PA_NANDS3C2410_PA_NAND
- #defineS3C_PA_NANDS3C24XX_PA_NAND
也便是说,S3C_PA_NAND是Nand flash寄存器首地址,而SZ_1M显着是个长度,因而,这儿的resource实践上是Nand flash寄存器首地址跟接下来的1M空间,可是,Nand的寄存器并没有那么多,这又是为什么呢?这些信息有什么用又在哪里用到了呢?答案很简单,这肯定是给驱动程序运用的了,带着这个疑问咱们持续剖析代码。定位到/drivers/mtd/nand/s3c2410.c,阅读代码能够看到驱动结构界说
- staticstructplatform_drivers3c24xx_nand_driver={
- .probe=s3c24xx_nand_probe,
- .remove=s3c24xx_nand_remove,
- .suspend=s3c24xx_nand_suspend,
- .resume=s3c24xx_nand_resume,
- .id_table=s3c24xx_driver_ids,
- .driver={
- .name=”s3c24xx-nand”,
- .owner=THIS_MODULE,
- },
- };
能够看到,这儿指定了结构中的各种操作的函数指针,从姓名上能够看出probe是加载驱动程序后履行的第一个函数,remove是移除驱动前最终履行的函数,suspend是挂起操作,等等。先不着急剖析这些函数,先来看看内核是怎么加载驱动的,s3c24xx_nand_driver又是怎么注册到内核的。往下阅读代码能够看到
- staticint__inits3c2410_nand_init(void)
- {
- printk(“S3C24XXNANDDriver,(c)2004SimtecElectronics”);
- returnplatform_driver_register(&s3c24xx_nand_driver);
- }
- staticvoid__exits3c2410_nand_exit(void)
- {
- platform_driver_unregister(&s3c24xx_nand_driver);
- }
- module_init(s3c2410_nand_init);
- module_exit(s3c2410_nand_exit);
- MODULE_LICENSE(“GPL”);
- MODULE_AUTHOR(“BenDooks
“); - MODULE_DESCRIPTION(“S3C24XXMTDNANDdriver”);
显着,加载该驱动时s3c2410_nand_init函数将s3c24xx_nand_driver注册到了内核,卸载该驱动时s3c2410_nand_exit将s3c24xx_nand_driver刊出,可是这两个函数也不过是两个一般函数,内核怎么知道加载驱动时运转s3c2410_nand_init,卸载驱动时运转s3c2410_nand_exit呢?下面的module_init和module_exit处理了这个问题,它们别离告知内核驱动程序的进口和出口。至于下面的MODULE_LICENSE指定了内核的权限协议,这儿指定内核为GPL协议的,只要契合这个协议才干调用这个协议内的函数,因而是驱动程序有必要的部分,剩余的两行是驱动的作者和描绘,无关紧要,能够没有。现在咱们理解了内核怎么加载驱动了,咱们再去剖析probe函数,往上阅读代码能够找到