uclinux内核与 驱动开发 -...

73
uClinux内核与 驱动开发 刘淼 [email protected]

Upload: others

Post on 29-Jul-2020

14 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux内核与驱动开发

刘淼

[email protected]

Page 2: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux内核简介• uCLinux 是Linux 2.0核心的分支,是针对没有MMU管理单元的微控制器

• 继承了Linux的大多数特性• 多数的Linux下应用程序和驱动程序都可以在uClinux下运行

• 内核精简• Kernel < 512KB

• Kernel+root < 900KB

• 并入linux 2.6内核中

Page 3: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux与linux的区别(1)内存管理

• uClinux运行于没有MMU的处理器上

• 使用平坦式(flat)内存管理模式,虚拟内存到物理内存是一对一的映射关系

• 对于应用程序,使用固定的栈空间。加载应用程序的时候需要重新定位

• 可用内存空间的大小受到物理内存的限制

Page 4: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux与linux的区别(2)

fork与vfork

• uClinux 的多进程管理通过vfork 来实现,uClinux只能支持vfork

• vfork使父进程锁定直到子进程exec() 或exit()

注:uClinux的应用程序的多线程可以依赖于标准C库来实现(uClibc)

Page 5: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux与uCOS-II(1)• uClinux源于linux,是一个很完整的系统,包括:

• 多任务调度

• 内存管理

• 文件系统(及接口)

• 设备驱动程序

• 完整的TCP/IP的支持

• 源码开放,支持广泛(GUI、FS、驱动程序等等)

Page 6: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux与uCOS-II(2)uCOS-II

• 源码开放,内核简单,易于学习和移植

• 占先式内核,实时性好

• 只有多任务调度的简单内核

• 内存管理过于简单,几乎没有动态内存管理功能

• 文件系统和图形界面需要外挂

• 对于设备驱动程序没有专门统一的接口

Page 7: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux的实时性问题• uClinux内核不关心实时性问题

• 可以和RTLinux配合来实现实时

• RTLinux处理实时任务,非实时任务由linux完成

• RTLinux是为linux提供实时性的方法,同样也适用于uClinux。通过RTLinux的patch,可以满足uClinux对实时性的需求

Page 8: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux的移植版本• Motorola DragonBall、68K

• Motorola Coldfire

• ARM7TDMI、ARM9TDMI(ARM940T)

• Altera Nios

• …

www.uClinux.org

Page 9: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux 的内核组成• 初始化程序段(init) 32k 左右

• 数据段(data) 50~100k 左右

• 未初始化数据段(bss) 100k~150k 左右

• 代码段(text) 300k 左右

• (init、data、bss 和text 的地址是由编译链接时的定位文件vmlinux.lds 决定的)

• 文件系统(romfs) > 80k 左右

Page 10: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

移植uClinux的主要工作• 选择处理器对应的交叉编译器

• 选择并修改Bootloader

• 修改链接文件(vmlinux-armv.lds.in),定位各个数据段

• 定义系统定时器、控制台(Console)

• 编写中断的控制函数

• 定义根文件系统

• 编写其他系统设备驱动…

Page 11: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

编译器的选择

uClinux内核在ARM上的编译

• 交叉编译器

• arm-elf-gcc、arm-linux-gcc

• 标准C库——应用程序

• uC-libc、uClibc

Page 12: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

(uC)linux的bootloader• 系统配置、中断接管、引导

• 装载内核、根文件系统、参数传递、内核调试、内核和根文件系统的下载等等

• 常见的uClinux(Linux)的Bootloader:• Redboot

• Blob

• Vivi

• Uboot

• armBoot…

Page 13: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux发行版的目录结构uClinux-distuClinux-dist

/NetSilicon/NetSilicon/Samsung/Samsung

/Arcturus/Arcturus

Romfs的相关工具

根文件系统

新版的标准C库 uClibc

应用程序

各制造商的配置文件

for NET+40for S3C4510

for uCdimm and uCsimm

configconfigDocumentationDocumentation

vendorsvendors

liblib

linux-2.0.xlinux-2.0.x

linux-2.4.xlinux-2.4.x

toolstools

uClibcuClibc

useruser

romfsromfs

uClinux-2.4内核uClinux-2.0内核

旧版的标准C库uC-libc配置脚本

相关的文档

Page 14: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux的内核目录结构

Linux2.4.xLinux2.4.x

/arch/arch

/drivers/drivers

/fs/fs

/include/include

/init/init

/ipc/ipc

/kernel/kernel

/lib/lib

/mm/mm

/mmnommu/mmnommu

/net/net

/scripts/scripts

/armnommu/armnommu /i196/i196 /m68knommu/m68knommu

/boot/boot /mach-s3c44b0/mach-s3c44b0 /kernel/kernel /lib/lib /mm/mm

/asm-armnommu/asm-armnommu /linux/linux /net/net

/arch-netarm/arch-netarm /proc-netarm/proc-netarm

Page 15: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

读懂uClinux内核源码• (uC)linux内核庞大,结构复杂

• 对uClinux内核的统计:接近1万个文件,4百万行代码

• 内核编程习惯(技巧)不同于应用程序

Page 16: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

(uC)linux内核的C代码• Linux内核的主体使用GNU C,在ANSI C上进行了扩充

• Linux内核必须由gcc编译编译

• gcc和linux内核版本并行发展,对于版本的依赖性强

• 内核代码中使用的一些编程技巧,在通常的应用程序中很少遇到

Page 17: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

GNU C的扩充举例• 从C++中吸收了inline和const关键字

• ANSI C代码与GNU C中的保留关键字冲突的问题可以通过双下划线(_ _)解决

• 例如:inline 等价于 __inline__、asm等价于__asm__

• 结构体(struct)的初始化

Page 18: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

结构体初始化

struct sample {

int member_int;

char *member_str;

void (*member_fun)(void);

};

Page 19: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

ANSI C中的实现struct sample inst_c={

100, //member_int

NULL, //*member_str;

myfunc //void (*member_fun)(void);

};

C99中的实现

struct sample inst_c99 = {

.member_int = 100,

.member_fun = myfun,

};

Page 20: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

GCC中的实现struct sample inst_gcc = {

member_fun: myfun,

member_int: 100,

};

与C99中的用法类似,不必关心struct定义的中的实际的顺序和其他未定义的数据,在复杂的结构体初始化的时候很有优势。

Page 21: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

宏定义的灵活使用(1)• 虽然GCC中定义了inline关键字,但是,宏操作(#define)仍然在系统中大量使用

• 举例:

#define DUMP_WRITE(addr,nr) do\

{ memcpy(bufp,addr,nr); bufp += nr; } while(0)

Page 22: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

应用DUMP_WRITE,就像使用C的函数一样:

if(addr)

DUMP_WRITE(addr, nr);

else…

但是,如果如通过下的定义,都不能满足上述的情况

定义1:

#define DUMP_WRITE(addr,nr) memcpy(bufp,addr,nr);\bufp += nr ;

定义2:

#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr);\bufp += nr;}

Page 23: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

宏定义的灵活使用(2)#define OFFSETOF(strct, elem) \

((long)&(((struct strct *)0)->elem))

• 1、((struct strct *)0) 结构体strct的指针

• 2、&((struct strct *)0)->elem)成员的地址,也就是相对于0的偏移

• 3、结果:OFFSETOF(strct,elem)返回的是,结构体strct中成员elem的偏移量

Page 24: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

C语言中goto的使用• 在应用程序的C编程中,为了保证程序的模块化,建议不使用goto

• 内核代码需要兼顾到效率,所以,大量使用goto

• 整个内核的比例大概是每260行一个goto语句——速度优先

• 短距离的goto——在函数中

Page 25: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

(uC)linux的驱动程序开发

Page 26: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

(uC)linux的驱动程序• Linux下对外设的访问只能通过驱动程序• Linux对于驱动程序有统一的接口,以文件的形式定义系统的驱动程序:• Open、Release、read、write、ioctl…

• 驱动程序是内核的一部分,可以使用中断、DMA等操作

• 驱动程序需要在用户态和内核态之间传递数据• uClinux下可以在应用层直接访问外设,操作寄存器口,但是无法处理中断——不推荐使用

• uClinux不支持模块加载

Page 27: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

内核功能的划分

• 进程管理(进程之间的通讯与同步)

• 内存管理(malloc/free)

• 文件系统

• 设备控制

• 网络功能(网络通讯协议等)

Page 28: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Linux下设备和模块的分类按照上述系统内核的功能,Linux中把系统

的设备定义成如下三类:

• 字符设备

• 块设备

• 网络设备

Page 29: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Linux下的设备• Linux的设备以文件的形式存在于/dev目录下

• 设备文件是特殊文件,使用ls /dev -l命令可以看到:

crw------- 1 root root 10, 7 Aug 31 2002 amigamouse1

crw------- 1 root root 10, 134 Aug 31 2002 apm_bios

brw-rw---- 1 root disk 29, 0 Aug 31 2002 aztcd

Page 30: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

主设备号和次设备号

• 主设备号标识设备对应的驱动程序

• 一个驱动程序可以控制若干个设备,次设备号提供了一种区分它们的方法

• 系统增加一个驱动程序就要赋予它一个主设备号。这一赋值过程在驱动程序的初始化过程中

int register_chrdev(unsigned int major, const char*name,struct file_operations *fops);

Page 31: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

创建设备节点

• 设备已经注册到内核表中,对于设备的访问通过设备文件(设备文件与设备驱动程序的主设备号匹配),内核会调用驱动程序中的正确函数

• 给程序一个它们可以请求设备驱动程序的名字。这个名字必须插入到/dev目录中,并与驱动程序的主设备号和次设备号相连

• 使用mknod在文件系统上创建一个设备节点

mknod /dev/mydevice c 254 0

Page 32: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

动态分配设备号

• 在Documentation/device.txt文件中可以找到已经静态分配给大部分设备的列表

• 由于许多数字已经分配了,为新设备选择一个唯一的号码是很困难的

• 如果调用register_chrdev时的major为零,函数就会选择一个空闲号码并做为返回值返回

Page 33: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

动态分配的问题

动态分配的主设备号不能保证总是一样的,无法事先创建设备节点

• 可以从/proc/devices读取

cat /proc/devices

• 利用脚本动态创建设备文件节点

Page 34: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

设备管理的问题

如今,Linux 支持很多不同种类的硬件。这意味着/dev中都有数百个特殊文件来表示所有这些设备。而且,这些特殊文件中大多数甚至不会映射到系统中存在的设备上

Page 35: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

使用devfs• 在Linux 2.4的内核里引入了devfs来解决linux下设备文件管理的问题

• 在驱动程序中通过devfs_register()函数创建设备文件系统的节点

• 系统启动的时候mount设备文件系统

• 所有需要的设备节点都由内核自动管理。/dev目录下只有挂载的设备

Page 36: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

uClinux设备驱动程序剖析• 驱动程序的移植

• RTL8019AS——NE2000兼容的网卡驱动程序

• Nand Flash上的MTD设备驱动与文件系统

• 编写驱动程序

• CAN总线设备驱动程序

Page 37: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

UP-NETARM3000上的RTL8019• 利用16位外部总线模式连接S3C44B0和RTL8019AS

• 使用外部中断1,高电平有效

• 没有配置EEPROM,使用跳线模式。通过IO口上电配置,通过驱动程序配置MAC地址等信息

Page 38: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

原理框图

Page 39: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

阅读Linux源码的工具• 在windows平台下

• SourceInsight(读C代码)

• UltraEdit(读汇编)

Page 40: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

RTL8019网卡驱动程序的移植• Ne2000网卡的驱动就是drivers/net/ne.c,和它相关的还有8390.h和8390.c

• __init关键字:• 将该函数置于内核的特定区域

• 在内核完成自身初始化之后,就试图释放这个特定区域

• 在Ne.c中函数ne_probe是网卡的检测函数

• 在ne_probe1函数里的检测并初始化网卡

Page 41: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

• 网卡的检测

• 内核调试信息输出(printk)

• 网卡复位

• 初始化配置(MAC地址等信息)

• dev->irq < 2 的问题

• 注册中断

• 网卡的检测是./drivers/net/Space.c的ethif_probe函数中实现的

• 定义struct devprobe arm_probes

Page 42: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

•NS8390_init函数,配置网卡

•Ne2000兼容网卡的寄存器定义

• drivers/net/ne.c中的定义

• drivers/net/8390.h中的EI_SHIFT宏

这时候,系统启动的时候就会报告找到网卡。注意:

• 中断模式——高电平触发

• 关闭网卡空间的缓冲区

Page 43: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

ARMv3与ARMv4• 在drivers/net/ne.c 中,ne_block_output函数里,针对16位总线有outsw函数的调用

• outsw函数定义在arch/armnommu/lib/ 包含io-writesw-armv3.S和io-writesw-armv4.S两个汇编文件 ,应该使用ARMv4的汇编

• 在arch/armnommu/Config.in中,定义CONFIG_ARCH_S3C44B0的时候,应该定义CONFIG_CPU_32v4

Page 44: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

ARM家族的更新

Page 45: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

网卡驱动程序移植需要注意的问题

• 确定网卡的基地址、中断无误

• 注意网卡的数据总线宽度,地址是否连续,如果不连续,如何映射

• 注意网卡中断的模式和对应的外部中断是不是一致

• 对于IO和RAM统一编址的处理器,注意缓冲区范围的设置

• 注意ARMv3和ARMv4等一些和处理器结构相关的底层函数库带来的问题

• 用抓包软件可以帮助分析定位问题所在

Page 46: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Nand Flash上的MTD设备驱动

• MTD(memory technology device)是用于访问memory设备(ROM、flash)的Linux子系统

• 在硬件和上层之间提供了一个抽象的接口 ,MTD把文件系统和Flash设备相隔离

Page 47: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

MTD与文件系统

Page 48: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

• Flash硬件驱动层• 在init时驱动Flash硬件,NAND型Flash的驱动程序则位于

/drivers/mtd/nand子目录下

• MTD原始设备有两部分组成• MTD原始设备的通用代码,

• 分是各个特定的Flash的数据,例如分区。

• mtd_info、mtd_table(mtdcore.c)、mtd_part(mtd_part.c)

• MTD设备层• linux系统定义出MTD的块设备(主设备号31)和字符设备(设备

号90)。设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。

• 根文件系统• 在Bootloader中将文件系统映像烧录到flash的某一个分区中,在

启动的时候,将该分区作为根文件系统挂载。

• 文件系统:• 内核启动后,mount

Page 49: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

NAND和NOR——性能比较NOR和NAND是现在市场上两种主要的非易失

闪存技术

• NOR的读速度比NAND稍快一些

• NAND的写入速度比NOR快很多

• NAND的擦除速度远比NOR的快

• 大多数写入操作需要先进行擦除操作

• NAND的擦除单元更小,相应的擦除电路更少

Page 50: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

接口差别

• NOR flash带有SRAM接口,线性寻址,可以很容易地存取其内部的每一个字节

• NAND flash使用复用接口和控制IO多次寻址存取数据

• NAND读和写操作采用512字节的块,这一点有点像硬盘管理此类操作易于取代硬盘等类似的块设备

Page 51: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

容量和成本

• NAND flash生产过程更为简单,成本低

• 常见的NOR flash为128KB~16MB,而NANDflash通常有8~128MB

• NOR主要应用在代码存储介质中,NAND适合于数据存储

• NAND在CompactFlash、Secure Digital、PC Cards和MMC存储卡市场上所占份额最大

Page 52: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

可靠性和耐用性

• 在NAND中每块的最大擦写次数是100万次,而NOR的擦写次数是10万次

• 位交换的问题NAND flash中更突出,需要ECC纠错

• NAND flash中坏块随机分布,需要通过软件标定——产品量产的问题

Page 53: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

MTD设备驱动程序的移植• 建立在Nand Flash上

• MTD的补丁

• http://www.linux-mtd.infradead.org/

• MTD设备工具在uClinux发行版的应用程序中——eraseall等

参考:The Linux MTD, JFFS HOWTO

Page 54: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Nand Flash连接原理

D[15..0]

CLE16

ALE17

WE18

WP19

I/O0 29I/O1 30I/O2 31I/O3 32

VSS13 VCC 12

I/O4 41I/O5 42I/O6 43I/O7 44

VCC 37

R/B7

RE8

CE9

VSS36

SE6

U204K29F2808U

nSMCWEnSMCOE

R21710K

R21610K

D0D1D2D3D4D5D6D7

VDD33

VDD33

C205

104

C204

104

GPC10GPC11

GPE0

GPC15

Page 55: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

移植代码

• 建议先给内核打补丁

• 有的linux内核带的MTD驱动程序有严重的问题

• nand flash相关代码在/drivers/mtd/nand/目录下

• 添加自己的驱动程序,可以从/drivers/mtd/nand/spia.c派生(arm_nand.c)

• 修改drivers/mtd/nand/config.in配置菜单

• 修改/drivers/mtd/nand/Makefile添加obj-$(CONFIG_MTD_NAND_ARM) +=arm_nand.o

Page 56: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

解读arm_nand.c• 模块入口:armnand_init函数

• 初始化Nand flash所用端口

• 新版本的MTD设备驱动程序的改进

• module_init宏定义了linux加载的模块——启动的时候加载或者通过模块加载

• nand_scan确定设备及其类型,挂载相应的驱动程序

• add_mtd_partitions函数注册MTD分区

Page 57: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

测试MTD设备(1)

• 正确加载了设备,使用命令cat /proc/mtd,可以显示MTD设备信息:dev: size erasesize name

mtd0: 00e00000 00004000 "Nand flash partition“

• 创建节点(如果不是用devfs)

• 在host linux上的romfs/dev目录创建@mtd0,c,90,0、@mtdblock0,b,31,0文件

• 用mkfs.jffs2(或者mkfs.yaffs)生成文件系统映象(比如jffs2.img)

Page 58: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

测试MTD设备(2)• 目标板启动以后

cp jffs2.img /dev/mtd0

注意:这里使用的/dev/mtd0是字符设备

• mount –t jffs2 /dev/mtdblock0 /var/jffs2

• 需要在编译内核的时候包含jffs2文件系统

• 使用块设备

• 在文件系统上进行其他测试:

• [/var/jffs2]cp /root/hello .

Page 59: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

嵌入式linux下常见的文件系统

• RomFS:只读文件系统,可以放在ROM空间,也可以在系统的RAM中,嵌入式linux中常用来作根文件系统

• RamFS:利用VFS自身结构而形成的内存文件系统,使用系统的RAM空间

• JFFS/JFFS2:为Flash设计的日志文件系统

• Yaffs:专门为Nand Flash设计

• proc:为内核和内核模块将信息发送给进程提供一种机制,可以查看系统模块装载的信息

• devFS:设备文件系统

Page 60: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Linux上的Ext2fs

• 支持 4 TB 存储、文件名称最长1012 字符

• 可选择逻辑块

• 快速符号链接

• Ext2不适合flash设备

• 是为象 IDE 设备那样的块设备设计的,逻辑块大小必须是 512 byte、1 KB、2KB等

• 没有提供对基于扇区的擦除/写操作的良好管理• 如果在一个扇区中擦除单个字节,必须将整个扇区复制到

RAM,然后擦除,再重写入

• 在出现电源故障时,Ext2fs 是不能防止崩溃的

• 文件系统不支持损耗平衡,缩短了flash的寿命

Page 61: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

jffs/jffs2文件系统的优缺点• 日志文件系统

• 提供了更好的崩溃、掉电安全保护

• jffs2支持对flash的均匀磨损

• 在扇区级别上执行闪存擦除/写/读操作要比 Ext2文件系统好

• 文件系统接近满时,JFFS2 会大大放慢运行速度——垃圾收集

Page 62: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

Nand上yaffs文件系统的优势• 专门为Nand flash设计的日志文件系统

• jffs/jffs2不适合大容量的Nand flash

• jffs的日志通过jffs_node建立在RAM中,占用RAM空间:对于128MB的Nand大概需要4MB的空间来维护节点

• 启动的时候需要扫描日志节点,不适合大容量的Nand flash

• FAT系统没有日志

Page 63: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

编译yaffs文件系统

• mtd的最新补丁升级?

• 接口更新,适合与yaffs

• 与原有的mtd驱动程序不兼容,需要重写

• 如果使用旧mtd驱动需要定义Makefile中

MTD_OLD = -DCONFIG_YAFFS_USE_OLD_MTD

• 参考文档:yaffs-rootfs-howto

• 最新版的yaffs网站:http://www.aleph1.co.uk/armlinux/projects/yaffs

Page 64: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

使用yaffs文件系统• 通过cat /proc/yaffs命令可以看到yaffs系统的相关信息

• mount -t yaffs /dev/mtdblock/0 /mnt/yaffs

Page 65: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

CAN总线设备驱动程序• 编写自己的驱动程序——字符设备

• 驱动程序与应用程序之间的交互

Page 66: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

硬件连接

TXCAN 1RXCAN 2

CLKOUT 3

TX0RTS4TX1RTS5TX2RTS6

OSC2 7OSC1 8

VSS9

RX1BUF10RX0BUF11 INT 12

SCK13SI14SO15

CS16RESET17

VD

D18

U502

MCP2510SO

nRESET

SIOTXDSIORXD

SIOCLK

VDD33

EXINT6

TXD1

GND 2

VCC 3

RXD4Vref5 CANL 6CANH 7Rs8

U503

TJA1050

SR140021K

VCC

SR14003

120CANHCANLEXIO2

D12CLKOUT

Page 67: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

CAN总线驱动程序的结构

Page 68: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

添加驱动程序

• 在./driver/char/Config.in中添加配置菜单

• 创建自己的驱动程序

./driver/char/s3c44b0-mcp2510

• 修改./driver/char/Makefile

• 定义驱动程序的加载入口

• module_init(s3c44b0_mcp2510_init);

• module_exit(s3c44b0_mcp2510_exit);

Page 69: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

• 在s3c44b0_mcp2510_init中实现硬件的初始化:设置中断模式、初始化芯片等

• 注册字符设备register_chrdev,定义file_operations结构

• 申请中断request_irq

• 允许启动devfs,创建devfs节点• devfs_mk_dir

• devfs_register

初始化配置过程

Page 70: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

设备的读写与等待

• copy_from_user和copy_to_user

• 对非堵塞标志O_NONBLOCK的支持

• 进程的同步:interruptible_sleep_on

• signal_pending(current)

• current是一个宏,指向代表当前正在执行的进程的struct task_struct结构

• 中断服务程序

• wake_up_interruptible

Page 71: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

应用程序和设备文件的对话

• read和write功能有限

• 其他的控制需要使用ioctl

• 定义:int (*ioctl) (struct inode *, structfile *, unsigned int, unsigned long);

• ioctrl的分配,参考Documentation/ioctl-number.txt

• CAN总线驱动程序中的ioctl定义s3c44b0_mcp2510_ioctl

Page 72: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

应用程序接口

exp-CAN的例子:

• 使用open以O_RDWR方式打开CAN设备

• ioctl的使用

• pthread_create创建接收线程

Page 73: uClinux内核与 驱动开发 - read.pudn.comread.pudn.com/downloads75/ebook/275705/uClinux.pdf移植uClinux的主要工作 • 选择处理器对应的交叉编译器 • 选择并修改Bootloader

谢谢!