01模块驱动实验基本原理Word文档下载推荐.docx

上传人:b****2 文档编号:5721692 上传时间:2023-05-05 格式:DOCX 页数:22 大小:28.06KB
下载 相关 举报
01模块驱动实验基本原理Word文档下载推荐.docx_第1页
第1页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第2页
第2页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第3页
第3页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第4页
第4页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第5页
第5页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第6页
第6页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第7页
第7页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第8页
第8页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第9页
第9页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第10页
第10页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第11页
第11页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第12页
第12页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第13页
第13页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第14页
第14页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第15页
第15页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第16页
第16页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第17页
第17页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第18页
第18页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第19页
第19页 / 共22页
01模块驱动实验基本原理Word文档下载推荐.docx_第20页
第20页 / 共22页
亲,该文档总共22页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

01模块驱动实验基本原理Word文档下载推荐.docx

《01模块驱动实验基本原理Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《01模块驱动实验基本原理Word文档下载推荐.docx(22页珍藏版)》请在冰点文库上搜索。

01模块驱动实验基本原理Word文档下载推荐.docx

模块方式调试效率很高,它使用insmod工具将编译的模块直接插入内核,如果出现故障,可以使用rmmod从内核中卸载模块。

不需要重新启动内核,这使驱动调试效率大大提高。

我们的实验在PC机和UP-NETARM2410-S上都可以运行,编译时使用不同的编译器就可以了。

1.驱动程序与应用程序的区别

应用程序一般有一个main函数,从头到尾执行一个任务;

驱动程序却不同,它没有main函数,通过使用宏module_init(初始化函数名);

将初始化函数加入内核全局初始化函数列表中,在内核初始化时执行驱动的初始化函数,从而完成驱动的初始化和注册,之后驱动便停止等待被应用软件调用。

驱动程序中有一个宏moudule_exit(退出处理函数名)注册退出处理函数。

它在驱动退出时被调用。

应用程序可以和GLIBC库连接,因此可以包含标准的头文件,比如<

stdio.h>

<

stdlib.h>

在驱动程序中是不能使用标准C库的,因此不能调用所有的C库函数,比如输出打印函数只能使用内核的printk函数,包含的头文件只能是内核的头文件,比如<

linux/module.h>

2.内核版本与编译器的版本依赖

当模块与内核链接时,insmod会检查模块和当前内核版本是否匹配,每个模块都定义了版本符号__module_kernel_version,这个符号位于模块文件的ELF头的.modinfo段中。

只要在模块中包含<

,编译器就会自动定义这个符号。

每个内核版本都需要特定版本的编译器的支持,高版本的编译器并不适合低版本的内核,比如UP-NETARM2410-S实验仪中的LINUX-2.4.19的内核需要2.95.3的GCC版本编译器。

Linux-2.4版本的insmod命令装载模块时,首先从/lib/modules目录和内核相关的子目录中查找模块文件,如果需要从当前目录装载,使用insmodmodule.o。

3.主设备号和次设备号

传统方式中的设备管理中,除了设备类型外,内核还需要一对称作主次设备号的参数,才能唯一标识一个设备。

主设备号相同的设备使用相同的驱动程序,次设备号用于区分具体设备的实例。

比如PC机中的IDE设备,一般主设备号使用3,WINDOWS下进行的分区,一般将主分区的次设备号为1,扩展分区的次设备号为2、3、4,逻辑分区使用5、6….。

设备操作宏MAJOR()和MINOR()可分别用于获取主次设备号,宏MKDEV()用于将主设备号和次设备号合并为设备号,这些宏定义在include/linux/kdev_t.h中。

对于LINUX中对设备号的分配原则可以参考Documentation/devices.txt。

对于查看/dev目录下的设备的主次设备号可以使用如下命令:

[/mnt/yaffs]ls/dev-l

crw-------1rootroot5,1Jan100:

00console

crw-------1rootroot5,64Jan100:

00cua0

crw-------1rootroot5,65Jan100:

00cua1

crw-rw-rw-1rootroot1,7Jan100:

00full

drwxr-xr-x1rootroot0Jan100:

00keyboard

crw-r-----1rootroot1,2Jan100:

00kmem

crw-r-----1rootroot1,1Jan100:

00mem

00mtd

00mtdblock

crw-rw-rw-1rootroot1,3Jan100:

00null

crw-r-----1rootroot1,4Jan100:

00port

crw-------1rootroot108,0Jan100:

00ppp

crw-rw-rw-1rootroot5,2Jan100:

00ptmx

crw-r--r--1rootroot1,8Jan100:

00random

lr-xr-xr-x1rootroot4Jan100:

00root->

rd/0

crw-rw-rw-1rootroot5,0Jan100:

00tty

crw-------1rootroot4,64Jan100:

11ttyS0

crw-------1rootroot4,65Jan100:

00ttyS1

crw-r--r--1rootroot1,9Jan100:

00urandom

crw-rw-rw-1rootroot1,5Jan100:

00zero

4.设备文件

设备类型、主次设备号是内核与设备驱动程序通信时所使用的,但是对于开发应用程序的用户来说比较难于理解和记忆,所以Linux使用了设备文件的概念来统一对设备的访问接口,在引入设备文件系统(devfs)之前Linux将设备文件放在/dev目录下,设备的命名一般为设备文件名+数字或字母表示的子类,例如/dev/hda1、/dev/hda2等。

在Linux-2.4内核中引入了设备文件系统(devfs),所有的设备文件作为一个可以挂装的文件系统,这样就可以被文件系统进行统一管理,从而设备文件就可以挂装到任何需要的地方。

命名规则也发生了变化,一般将主设备建立一个目录,再将具体的子设备文件建立在此目录下。

比如在UP-NETARM2410-S中的MTD设备为:

/dev/mtdblock/0。

5.设备驱动程序接口

通常所说的设备驱动程序接口是指结构file_operations{},它定义在include/linux/fs.h中。

file_operations数据结构说明

structfile_operations{

structmodule*owner;

loff_t(*llseek)(structfile*,loff_t,int);

ssize_t(*read)(structfile*,char*,size_t,loff_t*);

ssize_t(*write)(structfile*,constchar*,size_t,loff_t*);

int(*readdir)(structfile*,void*,filldir_t);

unsignedint(*poll)(structfile*,structpoll_table_struct*);

int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);

int(*mmap)(structfile*,structvm_area_struct*);

int(*open)(structinode*,structfile*);

int(*flush)(structfile*);

int(*release)(structinode*,structfile*);

int(*fsync)(structfile*,structdentry*,intdatasync);

int(*fasync)(int,structfile*,int);

int(*lock)(structfile*,int,structfile_lock*);

ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);

ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);

ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);

unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);

#ifdefMAGIC_ROM_PTR

int(*romptr)(structfile*,structvm_area_struct*);

#endif/*MAGIC_ROM_PTR*/

};

file_operations结构是整个Linux内核的重要数据结构,它也是file{}、inode{}结构的重要成员,表5.1.1中分别说明结构中主要的成员:

表5.1.1file_operations结构

Owner

module的拥有者。

Llseek

重新定位读写位置。

Read

从设备中读取数据。

Write

向字符设备中写入数据。

Readdir

只用于文件系统,对设备无用。

Ioctl

控制设备,除读写操作外的其他控制命令。

Mmap

将设备内存映射到进程地址空间,通常只用于块设备。

Open

打开设备并初始化设备。

Flush

清除内容,一般只用于网络文件系统中。

Release

关闭设备并释放资源。

Fsync

实现内存与设备的同步,如将内存数据写入硬盘。

Fasync

实现内存与设备之间的异步通讯。

Lock

文件锁定,用于文件共享时的互斥访问。

Readv

在进行读操作前要验证地址是否可读。

Writev

在进行写操作前要验证地址是否可写。

在嵌入式系统的开发中,我们一般仅仅实现其中几个接口函数:

read、write、ioctl、open、release,就可以完成应用系统需要的功能。

6.file数据结构说明

structfile{

structlist_headf_list;

structdentry*f_dentry;

structvfsmount*f_vfsmnt;

structfile_operations*f_op;

atomic_tf_count;

unsignedintf_flags;

mode_tf_mode;

loff_tf_pos;

unsignedlongf_reada,f_ramax,f_raend,f_ralen,f_rawin;

structfown_structf_owner;

unsignedintf_uid,f_gid;

intf_error;

unsignedlongf_version;

/*neededforttydriver,andmaybeothers*/

void*private_data;

/*preallocatedhelperkiobuftospeedupO_DIRECT*/

structkiobuf*f_iobuf;

longf_iobuf_lock;

file结构中与驱动相关的重要成员说明

我们将structfile结构指针定义为flip,以便于下面说明。

表5.1.2file结构中与驱动相关的成员

f_mode

标识文件的读写权限

f_pos

当前读写位置,类型为loff_t是64位的数,只能读不能写

f_flag

文件标志,主要用于进行阻塞/非阻塞型操作时检查

f_op

文件操作的结构指针,内核在OPEN操作时对此指针赋值。

private_data

Open系统调用在调用驱动程序的open方法前,将此指针值NULL,驱动程序可以将这个字段用于任何目的,一般用它指向已经分配的数据,但在内核销毁file结构前要在release方法中释放内存。

f_dentry

文件对应的目录项结构,一般在驱动中用filp->

f_dentry->

d_inode访问索引节点时用到它。

7.驱动接口的实现过程

我们先看看实验代码框架

#defineDEVICE_NAME"

demo"

staticssize_tdemo_write(structfile*filp,constchar*buffer,size_tcount)

{chardrv_buf[];

copy_from_user(drv_buf,buffer,count);

}

staticssize_tdemo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos)

{

chardrv_buf[];

copy_to_user(buffer,drv_buf,count);

….

staticintdemo_ioctl(structinode*inode,structfile*file,

unsignedintcmd,unsignedlongarg)

staticintdemo_open(structinode*inode,structfile*file)

staticintdemo_release(structinode*inode,structfile*filp)

MOD_DEC_USE_COUNT;

DPRINTK("

devicerelease\n"

);

return0;

staticstructfile_operationsdemo_fops={

owner:

THIS_MODULE,

write:

demo_write,

read:

demo_read,

ioctl:

demo_ioctl,

open:

demo_open,

release:

demo_release,

#ifdefCONFIG_DEVFS_FS

staticdevfs_handle_tdevfs_demo_dir,devfs_demoraw;

#endif

staticint__initdemo_init(void)

intresult;

devfs_demo_dir=devfs_mk_dir(NULL,"

NULL);

devfs_demoraw=devfs_register(devfs_demo_dir,"

0"

DEVFS_FL_DEFAULT,

demo_Major,demo_MINOR,S_IFCHR|S_IRUSR|S_IWUSR,

&

demo_fops,NULL);

#else

SET_MODULE_OWNER(&

demo_fops);

result=register_chrdev(demo_Major,"

scullc"

&

if(result<

0)returnresult;

if(demo_Major==0)demo_Major=result;

/*dynamic*/

printk(DEVICE_NAME"

initialized\n"

staticvoid__exitdemo_exit(void)

unregister_chrdev(demo_major,"

kfree(demo_devices);

unloaded\n"

module_init(demo_init);

module_exit(demo_exit);

其中,staticstructfile_operationsdemo_fops={…}完成了将驱动函数映射为标准接口,devfs_registe()和register_chrdev()函数完成将驱动向内核注册。

上面的这种特殊表示方法不是标准C的语法,这是GNU编译器的一种特殊扩展,它使用名字对进行结构字段的初始化,它的好处体现在结构清晰,易于理解,并且避免了结构发生变化带来的许多问题。

Open方法

Open方法提供给驱动程序初始化设备的能力,从而为以后的设备操作做好准备,此外open操作一般还会递增使用计数,用以防止文件关闭前模块被卸载出内核。

在大多数驱动程序中Open方法应完成如下工作:

1.递增使用计数

2.检查特定设备错误。

3.如果设备是首次打开,则对其进行初始化。

4.识别次设备号,如有必要修改f_op指针。

5.分配并填写filp->

private_data中的数据。

Release方法

与open方法相反,release方法应完成如下功能:

1.释放由open分配的filp->

private_data中的所有内容

2.在最后一次关闭操作时关闭设备

3.使用计数减一

Read和Write方法

ssize_tdemo_write(structfile*filp,constchar*buffer,size_tcount,loff_t*ppos)

ssize_tdemo_read(structfile*filp,char*buffer,size_tcount,loff_t*ppos)

read方法完成将数据从内核拷贝到应用程序空间,write方法相反,将数据从应用程序空间拷贝到内核。

对于者两个方法,参数filp是文件指针,count是请求传输数据的长度,buffer是用户空间的数据缓冲区,ppos是文件中进行操作的偏移量,类型为64位数。

由于用户空间和内核空间的内存映射方式完全不同,所以不能使用象memcpy之类的函数,必须使用如下函数:

unsignedlongcopy_to_user(void*to,constvoid*from,unsignedlongcount);

unsignedlongcopy_from_user(void*to,constvoid*from,unsignedlongcount);

Read的返回值

1.返回值等于传递给read系统调用的count参数,表明请求的数据传输成功。

2.返回值大于0,但小于传递给read系统调用的count参数,表明部分数据传输成功,根据设备的不同,导致这个问题的原因也不同,一般采取再次读取的方法。

3.返回值=0,表示到达文件的末尾。

4.返回值为负数,表示出现错误,并且指明是何种错误。

5.在阻塞型io中,read调用会出现阻塞。

Write的返回值

1.返回值等于传递给write系统调用的count参数,表明请求的数据传输成功。

2.返回值大于0,但小于传递给write系统调用的count参数,表明部分数据传输成功,根据设备的不同,导致这个问题的原因也不同,一般采取再次读取的方法。

3.返回值=0,表示没有写入任何数据。

标准库在调用write时,出现这种情况会重复调用write。

错误号的定义参见<

linux/errno.h>

5.在阻塞型io中,write调用会出现阻塞。

ioctl方法

ioctl方法主要用于对设备进行读写之外的其他控制,比如配置设备、进入或退出某种操作模式,这些操作一般都无法通过read/write文件操作来完成,比如在UP-NETARM2410-S中的SPI设备通道的选择操作,无法通过write操作控制,这就是ioctl操作的功能。

用户空间的ioctl函数的原型为:

intioctl(inffd,intcmd,…)

其中的…代表可变数目的参数表,实际中是一个可选参数,一般定义为:

intioctl(inffd,intcmd,char*argp)

驱动程序中定义的ioctl方法原型为:

int(*io

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2