Linux内核与驱动开发教材.docx
- 文档编号:11585536
- 上传时间:2023-03-19
- 格式:DOCX
- 页数:47
- 大小:214.25KB
Linux内核与驱动开发教材.docx
《Linux内核与驱动开发教材.docx》由会员分享,可在线阅读,更多相关《Linux内核与驱动开发教材.docx(47页珍藏版)》请在冰豆网上搜索。
Linux内核与驱动开发教材
Linux内核与驱动开发实验教材
中程在线
实验一嵌入式开发环境的建立
1实验目的
掌握嵌入式开发环境的构建,熟悉课程实验的开发板
掌握安装交叉编译工具的安装方法
掌握Bootloader,Kernel,Rootfs的烧写方法
掌握Bootloader,Kernel,Rootfs的编译方法
2实验内容
安装arm-linux-gcc交叉编译工具
编译Kernel
烧写Kernel
生成Rootfs映像
3基础知识
3.1交叉编译工具
嵌入式系统的开发中,开发环境被称为主机。
因为嵌入式目标系统的资源局限性,不可能完成构建系统的任务,所以需要主机使用交叉编译工具来构建目标系统。
实验使用ARM交叉编译器,与x86桌面系统采用的编译器是不同,因为实验开发板采用的是ARM处理器。
编译器将使用下列GNU工具
GNUgcccompilersforC,C++
GNUbinutil
GNUCLibrary
GNUCheader
与通常在x86平台上使用的GNU工具不同,ARM交叉编译工具编译处理的执行文件只能够在ARM平台上运行。
3.2嵌入式系统构建
一个嵌入式Linux系统从软件的角度看通常可以分为四个层次:
1.引导加载程序(Bootloader)。
引导加载程序是系统加电后运行的第一段软件代码。
2.Linux内核。
特定于嵌入式板子的定制内核以及内核的启动参数。
3.文件系统。
包括根文件系统和建立于Flash内存设备之上文件系统。
通常用ramdisk来作为rootfs。
4.用户应用程序。
特定于用户的应用程序。
3.3BOOTLOADER
bootloader主要的功能有:
1初始化硬件,初始化CPUclock,Memorytiming,interrupt,UART,andGPIO。
2启动Linux,这是bootloader最重要的功能,保存内核映像到DSRAM中,并跳转到内核起始地址。
3映像下载,下载内核映像和文件系统到SDRAM,下载只能通过以太网进行。
如命令tftp完成tftp文件下载。
4Flash内存控制,如fl命令可以烧写Flash。
PC机中的引导加载程序由BIOS(其本质就是一段固件程序)和位于硬盘MBR中的OSBootLoader(比如,LILO和GRUB等)一起组成。
BIOS在完成硬件检测和资源分配后,将硬盘MBR中的BootLoader读到系统的RAM中,然后将控制权交给OSBootLoader。
BootLoader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点去运行,也即开始启动操作系统。
而在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。
在实验开发板(基于S3C2440)的嵌入式系统中,系统在上电或复位时都从地址0x00000000处开始执行,而在这个地址处安排的通常就是系统的BootLoader程序。
简单地说,BootLoader就是在操作系统内核运行之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
通常,BootLoader是严重地依赖于硬件而实现的,特别是在嵌入式世界。
因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。
尽管如此,我们仍然可以对BootLoader归纳出一些通用的概念来,以指导用户特定的BootLoader设计与实现。
3.3KERNEL
内核是所有linux系统的中心软件组件。
整个系统的能力完全受内核本身能力的限制。
由于linux内核支持多个CPU架构,由于CPU架构的差异性,每种架构都有不同的团队在维护,所以必须根据CPU架构来选择供应内核的网站。
见下表:
CPU架构
最合适的内核网站
下载方式
X86
http:
//www.kernel.org/
ftp,http,sync
ARM
http:
//www.arm.linux.uk/
ftp,rsync
PowerPC
http:
//penguinppc.org/
ftp,http,rsync等
MIPS
http:
//www.linux-mips.org/
cvs
M68k
http:
//www.linux-m68k.org
ftp,http
Linux内核源代码目录树结构说明如下:
arch:
包含和硬件体系结构相关的代码,每种平台占一个相应的目录。
和32位PC相关的代码存放在i386目录下,其中比较重要的包括kernel(内核核心部分)、mm(内存管理)、math-emu(浮点单元仿真)、lib(硬件相关工具函数)、boot(引导程序)、pci(PCI总线)和power(CPU相关状态)。
crypto:
常用加密和散列算法(如AES、SHA等),还有一些压缩和CRC校验算法。
Documentation:
关于内核各部分的通用解释和注释。
drivers:
设备驱动程序,每个不同的驱动占用一个子目录。
fs:
各种支持的文件系统,如ext、fat、ntfs等。
include:
头文件。
其中,和系统相关的头文件被放置在linux子目录下。
init:
内核初始化代码(注意不是系统引导代码)。
ipc:
进程间通信的代码。
kernel:
内核的最核心部分,包括进程调度、定时器等,和平台相关的一部分代码放在arch/*/kernel目录下。
lib:
库文件代码。
mm:
内存管理代码,和平台相关的一部分代码放在arch/*/mm目录下。
net:
网络相关代码,实现了各种常见的网络协议。
scripts:
用于配置内核文件的脚本文件。
security:
主要是一个SELinux的模块。
sound:
常用音频设备的驱动程序等。
usr:
实现了一个cpio。
3.4ROOTFS
linux内核在系统启动期间进行的最后操作之一就是安装根文件系统。
根文件系统一直都是所有Unix系统不可缺少的组件。
根文件系统的顶层目录说明见下表。
其中/bin,/dev,/etc,/lib,/proc,/sbin和/usr,都是不可缺少的目录。
目录
内容
bin
必要的用户命令(二进制文件)
dev
设备文件和其它特殊文件
etc
系统配置文件,包括启动文件
home
用户主目录
lib
必要的链接库,包括C链接库、内核模块
mnt
安装点,用于暂时安装文件系统
proc
用来提供内核与进程信息的虚拟文件系统
root
Root用户的主目录
sbin
必要的系统管理员命令(二进制文件)
usr
在第二层包含对大多数用户都有用的应用程序和文件,包括X服务器等。
tmp
暂时性的文件
/bin和/sbin目录下存放了很多常见的linux操作命令。
这些命令参数多、功能强,同样每个命令文件都比较大,这对嵌入式系统来说是不合适的。
因此嵌入式系统中,通常采用busybox工具来实现linux的常见操作。
busybox是一个集成了一百多个最常用linux命令和工具的软件,它甚至还集成了http服务器和telnet服务器,而busybox软件大小仅1M左右。
常见的文件系统类型有:
Romfs:
只读文件系统,未压缩。
多用于NORFlash和较低速的CPU(如无MMUCPU)
Cramfs:
只读文件系统,压缩,可用于较高速的CPU。
JFFS2:
可读写文件系统,可支持压缩或不压缩,较适用于NorFlash和容量较小的NandFlash
Yaffs:
可读写文件系统,未压缩。
较适用于NandFlash
4实验步骤:
4.1安装arm-linux-gcc
把光盘目录linux\中的arm-linux-gcc-3.4.1.tgz拷贝到虚拟机的linux系统中。
在文件所在目录进行解压缩,命令如下:
#tarxvzfarm-linux-gcc-3.4.1.tgz–C/
更改虚拟机系统的路径,把arm-linux-gcc工具链加入到搜索路径中。
#cdroot
#vi.bash_profile
在.bash_profile文件中加入下面语句:
PATH=$PATH:
/usr/loca/arm/3.4.1/bin
最后执行source命令,使设置的路径信息生效。
命令如下:
#source.bash_profile
检查是否安装成功。
输入如下命令,如果安装成功,将打印出正确的版本信息。
#arm-linux-gcc-v
4.2编译linux内核代码
把光盘目录linux\中的linux-2.6.13-qq2440-20070908.tgz拷贝到虚拟机的linux系统中。
注意,具体文件名可能会根据版本的更新而发生变化。
在文件所在目录进行解压缩,命令如下:
#tarxvzflinux-2.6.13-qq2440-20070908.tgz–C/opt/FriendlyARM/QQ2440
内核配置,具体内核选项请参考光盘资料《QQ2440V3用户手册》8.2小节。
#makemenuconfig
内核编译
#makezImage
生成映像文件zImage,在目录arch/arm/boot/中。
可以将该文件烧写到开发板中。
4.3写linux内核代码
开机进入BIOS模式。
Supervivi在出厂的时候已经预装入板子的NorFlash中,设置跳线J1为NorFlash启动,即可进入BIOS模式,
安装USB驱动。
使用USB连接线连接开发板与系统主机,如果您是第一次使用,WindowsXP系统会提示您发现了新的USB设备,此时需要安装USB驱动。
具体安装步骤请参考光盘资料《QQ2440V3用户手册》2.2.2小节。
启动光盘中的工具DNW,在BIOS主菜单中选择功能号[k],开始下载linux内核zImage。
具体步骤请参考光盘资料《QQ2440V3用户手册》3.1.3小节。
点击“USBPort->Transmit”选项,并选择相应的内核文件。
4.4生成yaffs文件系统映象
安装mkyaffsimage工具程序,把光盘中文件mkyaffsimage.tgz拷贝到虚拟机系统中。
在运行解压命令进行安装。
#tarxvzfmkyaffsimage.tgz-C/usr/sbin
安装根文件系统目录树,把光盘中文件root_default.tgz拷贝到虚拟机系统中。
在运行解压命令进行安装。
#tarxvzfroot_default.tgz-C/opt/FriendlyARM/QQ2440
生成映像文件,使用mkyaffsimage命令制作yaffs文件系统映象。
映像的文件名为:
root_default.img。
mkyaffsimageroot_defaultroot_default.img
5独立实验(可选做)
在完成上述实验后,学员可以根据刚才的实验内容,举一反三,独立完成如下实验。
独立完成烧写根文件系统映像的实验。
实验方法请参考光盘资料《QQ2440V3用户手册》3.1.4小节。
独立完成Bootloader的编译,Bootloader的烧写实验。
实验方法请参考光盘资料《QQ2440V3用户手册》第七章。
实验二编写内核模块程序
1实验目的
掌握内核模块程序的框架
掌握2.6内核版本中内核模块的编译方法
2实验内容
2.1编写内核模块程序
内核模块的程序的实验步骤:
编写helloworld.c文件。
拷贝helloworld.c文件到开发板内核代码树的driver/char目录下。
修改开发板内核代码树的driver/char目录下的Makefile文件。
添加
obj-m+=helloworld.o
执行命令:
makemodules
回到kernel目录下运行:
makemodules
在开发板内核代码树的driver/char目录下生成文件helloworld.ko。
下载helloworld.ko到开发板上。
并加载模块:
insmodhelloworld.ko
卸载模块:
rmmodhelloworld
3相关知识
3.1Linux2.6版本内核模块的编译方法
linux2.6版本的内核编译需要Linux内核代码树。
通用的Makefile文件为:
#没有定义KERNELRELEASE,则说明是直接从命令行调用的,
#因此要调用内核构造系统。
ifeq($(KERNELRELEASE),)
KERNELDIR?
=/lib/modules/$(shelluname-r)/build#当前内核树所在目录
#Thecurrentdirectoryispassedtosub-makesasargument
PWD:
=$(shellpwd)
modules:
$(MAKE)-C$(KERNELDIR)M=$(PWD)modules
modules_install:
$(MAKE)-C$(KERNELDIR)M=$(PWD)modules_install
clean:
rm-rf*.o*~core.depend.*.cmd*.ko*.mod.c.tmp_versions
.PHONY:
modulesmodules_installclean
#如果已定义KERNELRELEASE,则说明是从内核构造系统调用的,
#因此可利用其内建语句。
else
#calledfromkernelbuildsystem:
justdeclarewhatourmodulesare
obj-m:
=hello.ohellop.o
endif
4实验说明
4.1编写内核模块程序
#include
#include
MODULE_LICENSE("DualBSD/GPL");
staticinthello_init(void)
{
printk(KERN_ALERT"Hello,world\n");
return0;
}
staticvoidhello_exit(void)
{
printk(KERN_ALERT"Goodbye,cruelworld\n");
}
module_init(hello_init);
module_exit(hello_exit);
5独立实验(可选做)
5.1PC环境下编译内核模块程序
在虚拟机的linux系统中编写完成简单的Makefile文件,使helloworld模块在虚拟机的linux系统中进行实验。
5.2模块参数传递实验
在内核模块程序中加入模块参数,通过加载命令insmod传递模块参数到模块程序中。
实验三编写字符设备驱动程序
1实验目的
掌握字符设备驱动的编写
掌握阻塞型I/O驱动程序的编写
掌握GPIO引脚的控制方法
2实验内容
2.1编写LED字符设备驱动
编写LED设备的字符驱动,主设备号为:
230。
LED字符驱动只支持ioctl方法,不提供open,close,read,write等方法。
用户使用命令ioctl(cmd参数可以选0或1,arg参数值用来指示第几个LED灯),可以控制LED灯的亮与灭。
2.2编写阻塞型I/O驱动程序
实现字符设备的读写方法,并实现阻塞型I/O。
使用一个静态字符数组buffer做为循环数组,并设置两个静态全局变量head和tail用来指示循环数组buffer的使用情况。
在字符设备的读方法中,判断buffer中是否有数据,如果有,就把数据读出。
并修改指示值head和tail。
如果没有数据,即阻塞住当前进程。
在字符设备的写方法中,把数据写如buffer中,并修改指示值head和tail。
如果buffer写满,即覆盖前面的数据。
3相关知识
3.1驱动程序如何访问硬件。
如何操作硬件,需要查看开发板的原理图及所涉及的芯片的数据手册。
对于S3C2440,在linux驱动程序中操作其寄存器时,不能使用物理地址,而要使用虚拟地址。
它们的转换关系非常简单:
#defineio_p2v(x)((x)|0xa0000000)//物理地址转换为虚拟地址
#defineio_v2p(x)((x)&~0xa0000000)//虚拟地址转换为物理地址
这些宏在内核include/asm-arm/arch-s3c2440/hardware.h中定义。
也可以使用内核
include/asm-arm/arch/s3c2440.h中定义的宏,比如要访问GPIOA的控制寄存器器(物理地址为0x56000000),可以使用宏GPCON(0)。
4.2GPIO引脚介绍
开发板带有4个用户可编程I/O方式LED,如图所示LED硬件原理图,下表为LED对应的I/O口。
序号
名称
GPIO端口
1
LED1
GPB5
2
LED2
GPB6
3
LED3
GPB7
4
LED4
GPB8
S3C2440的I/OPORTS含GPA、GPB、……、GPH八个端口。
八个端口的控制寄存器基本一致。
下面列出其控制寄存器。
GPxCON:
用于选择引脚功能。
GPxDAT:
用于读/写引脚数据。
GpxUP:
用于确定是否使用内部上拉电阻(x为A、B、……、H,没有GPAUP寄存器)。
PORTA与PORTB-H在功能选择方面有所不同,GPACON中每一位对应一根引脚(共23根引脚)。
当某位设为0时,相应引脚为输出引脚,此时我们可以在GPADAT中相应位写入0或1让此引脚输出低电平或高电平;当某位设为1时,相应引脚为地址线或用于地址控制,此时GPADAT无用。
一般而言GPACON通常设为全1,以便访问外部存储器件。
PORTA我们暂时不必理会。
PORTB-H在寄存器操作方面完全相同。
GPxCON中每两位控制一根引脚:
00表示输入、01表示输出、10表示特殊功能、11保留不用。
GPxDAT用于读/写引脚:
当引脚设为输入时,读此寄存器可知相应引脚的状态是高是低;当引脚设为输出时,写此寄存器相应位可令此引脚输出低电平或高电平。
GpxUP:
某位为0时,相应引脚无内部上拉;为1时,相应引脚使用内部上拉。
PORTB的引脚5、6、7、8对应LED1-4,在驱动程序中按照上述说明设置GPBCON、GPBDAT这两个个寄存器,即可控制LED的亮灭。
内核给我们提供了两个函数来设置GPIO的控制寄存器和数据寄存器,非常方便,它们的代码在include/asm-arm/arch-s3c2440/s3c2440.h中:
s3c2410_gpio_cfgpin设置GPIO引脚的功能(如输入,输出,中断等。
)
s3c2410_gpio_setpin设置GPIO引脚电平
set_irq_type设置GPIO引脚中断属性
5实验步骤
5.1LED设备驱动程序
LED设备驱动程序提供了应用程序控制访问LED灯的接口。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineDEVICE_NAME"leds"
#defineLED_MAJOR231
staticunsignedlongled_table[]={
S3C2410_GPB5,
S3C2410_GPB6,
S3C2410_GPB7,
S3C2410_GPB8,
};
staticunsignedintled_cfg_table[]={
S3C2410_GPB5_OUTP,
S3C2410_GPB6_OUTP,
S3C2410_GPB7_OUTP,
S3C2410_GPB8_OUTP,
};
staticintsbc2440_leds_ioctl(
structinode*inode,
structfile*file,
unsignedintcmd,
unsignedlongarg)
{
switch(cmd){
case0:
case1:
if(arg>4){
return-EINVAL;
}
s3c2410_gpio_setpin(led_table[arg],!
cmd);
return0;
default:
return-EINVAL;
}
}
staticstructfile_operationssbc2440_leds_fops={
.owner=THIS_MODULE,
.ioctl=sbc2440_leds_ioctl,
};
staticint__initsbc2440_leds_init(void)
{
intret;
inti;
ret=register_chrdev(LED_MAJOR,DEVICE_NAME,&sbc2440_leds_fops);
if(ret<0){
printk(DEVICE_NAME"can'tregistermajornumber\n");
returnret;
}
devfs_mk_cdev(MKDEV(LED_MAJOR,0),S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP,DEVICE_NAME);
for(i=0;i<4;i++){
s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i],1);
}
printk(DEVICE_NAME"initialized\n");
return0;
}
staticvoid__exitsbc2440_leds_exit(void)
{
devfs_remove(DEVICE_NAME);
unregister_chrdev(LED_MAJOR,DEVICE_NAME);
}
m
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 内核 驱动 开发 教材