s3c2410的字符型设备驱动程序设计Word格式文档下载.docx
《s3c2410的字符型设备驱动程序设计Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《s3c2410的字符型设备驱动程序设计Word格式文档下载.docx(10页珍藏版)》请在冰豆网上搜索。
![s3c2410的字符型设备驱动程序设计Word格式文档下载.docx](https://file1.bdocx.com/fileroot1/2023-1/2/636ff713-49cf-4612-95f9-c49f8dda3c49/636ff713-49cf-4612-95f9-c49f8dda3c491.gif)
设备驱动在卸载时需要回收相应的资源,将设备的响应寄存器值复位并从系统中注销该设备。
系统调用部分则是对设备的操作过程,比如open、read、write、ioctl等操作。
设备驱动程序可以分成以下3个主要部分:
(1)自动配置和初始化子程序。
负责检测所需驱动的硬件设备是否存在以及是否能正常工作,这部分驱动程序仅在初始化时被调用一次。
(2)服务I/O就是请求子程序,是驱动程序的上半部分,这部分是系统调用的结果。
(3)中断服务程序又称驱动程序的下半部分,设备在I/O请求结束或其他状态改变时产生中断。
因为设备驱动程序一般支持同一类型的若干个设备,所以调用中断服务子程序时都带有一个或多个参数以唯一标识请求服务的设备。
3 字符设备驱动程序中重要的数据结构和函数
对于每个系统调用,驱动程序中都有一个与之对应的函数。
对于字符设备驱动程序,这些函数集合在一个file_operations类型的数据结构中,它定义了常见文件I/O函数的入口.编写字符设备驱动程序就是为具体硬件的file_operations结构编写各个函数,大多数的驱动程序只是利用了其中的一部分对于驱动程序中不提供的功能,把相应位置的值设为NULL),对于字符设备来说,要提供的主要入口有:
open()、release()、read()、write()、lseek()、ioctl()等。
本课程设计中用到的主要有open()、read()、write()、release()函数。
int(*open)(structinode*,structfile*);
该操作用来打开设备文件。
int(*release)(structinode*,structfile*);
该操作用来释放文件结构。
ssize_t(*read)(structfile*,char_user*,size_t,loff_t*);
该操作用来从设备中读取数据。
该操作发送数据给设备。
4 驱动程序的注册和卸载
驱动程序有一个初始化函数,在安装驱动程序时会调用它。
在初始化函数中会将驱动程序的file_operations与主设备号一起向内核进行注册。
对字符设备使用如下函数进行注册:
intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);
其中,major是为设备驱动程序向系统申请的主设备号,如果为0则系统动态地分配l个主设备号,name是设备名。
fops是file_operations对各个调用入口点的说明。
此函数返回0表示成功;
返回-1是表示出错;
返回-EINVAL表示申请的主设备号非法;
返回-EBUSY表示所申请的主设备号正在被其他设备驱动程序使用。
模块在调用rmmod函数时被卸载,此时的入口点是cleanup_module函数或宏module_exit,并在其中完成对设备的注销。
类似的,字符设备的卸载函数定义为:
intunregister_chrdev(unsignedintmajor,constchar*name);
2、实验目的:
了解、掌握字符型驱动程序的设计过程、编译、加载以及测试过程,包括了解file_operation数据结构、驱动程序的注册与注销、makefile文件的编写。
3、实验步骤:
①写入驱动程序源程序fakedev.c,本程序设计思维为设备输入一串字符,再输出同样的字符。
设备驱动程序的主体流程为:
module_init->
fakedev_init_module-->
register_chrdev-->
fakedev_fops-->
fakkdev_open/release/read/write.
源代码fakedev.c
#include<
linux/config.h>
/*预定义和必要的头文件*/
linux/module.h>
linux/moduleparam.h>
linux/init.h>
linux/kernel.h>
linux/slab.h>
linux/fs.h>
linux/errno.h>
linux/types.h>
linux/proc_fs.h>
linux/fcntl.h>
linux/seq_file.h>
linux/cdev.h>
asm/uaccess.h>
#defineDEVICE_NAME"
fakedevice"
MODULE_LICENSE("
DualBSD/GPL"
);
/*用宏来声明该模块的许可协议*/
structfake_device{/*该设备其他的私有数据和信号量的信息的定义*/
intusage;
char*data;
intnew_msg;
}
structfake_devicefakedev;
intfakedev_major=200;
/*指定设备主设备号为200*/
/*以下为file_operation数据结构*/
staticintfakedev_open(structinode*inode,structfile*file)
/*定义了fakedev_open即打开操作函数,这里函数定义为只打印一行消息*/
{
printk("
fakedev_device_open(%p,%p)\n"
inode,file);
return0;
staticintfakedev_release(structinode*inode,structfile*file)/*定义了{fakedev_release即释放文件结构函数。
*/
staticvoidfakedev_device_release(%p,%p)\n"
staticssize_tfakedev_read(structfile*f,char*buf,intsize,loff_toff)
/*定义了fakedev_read即从设备中读取数据的函数*/
intlength;
intcount=size;
if(count<
0)
return-EINVAL;
if(fakedev.usage)
return-EBUSY;
fakedev.usage=1;
if(fakedev.data==0)
length=strlen(fakedev.data);
if(length<
count)
count=length;
copy_to_user(buf,fakedev.data,count+1);
fakedev.new_msg=0;
fakedev.usage=0;
returncount;
staticssize_tfakedev_write(structfile*f,constchar*buf,intsize,loff_toff)/*定义fakedev_write函数即发送数据给设备函数*/
intcount=size;
if(fakedev.usage|fakedev.new_msg)
fakedev.usage=1;
kfree(fakedev.data);
fakedev.data=kmalloc(sizeof(char)*(count+1),GFP_KERNEL);
if(!
fakedev.data)
return-ENOMEM;
copy_from_user(fakedev.data,buf,count+1);
fakedev.usage=0;
fakedev.new_msg=1;
structfile_operationsfakedev_fops={
/*定义该字符设备的具体文件操作,包括read、write、open、release*/
.read=fakedev_read,
.write=fakedev_write,
.open=fakedev_open,
.release=fakedev_release,
};
staticintfakedev_init_module(void)
/*定义fakedev_init_module,即字符设备向系统注册函数*/
intresult;
result=register_chrdev(fakedev(fakedev_major,DEVICE_NAME,&
fakedev_fops);
if(result<
0)
returnresult;
if(fakedev_major==0)
fakedev_major=result;
printk(KERN_INFO"
RegisterFAKEDEV.major-number=%d\n"
result);
staticvoidfakedev_cleanup_module(void)
/*定义fake_exit_module,即字符设备向系统注销函数*/
{
UnregisterFAKEDEV\n"
unregister_chrdev(fakedev_major,DEVICE_NAME);
module_init(fakedev_init_module);
/*包含了注册、注销两个函数的指针*/
module_exit(fakedev_cleanup_module);
②Makefile文件:
在linux2.6内核中,模块的编译需要配置过的内核代码,编译过程首先会到内核源码目录下,读取顶层的makefile文件,然后再返回模块代码所在目录进行编译.可根据网络上公开的针对字符设备驱动程序的makefile模板进行该模块makefile文件设计。
Makefile文件
#Makefile2.6
ifneq($(KERNELRELEASE),)
#kbuildsyntax.dependencyrelationshsipoffilesandtargetmodulesarelistedhere.
obj-m:
=fakedev.o/*指定模块源文件*/
else
PWD:
=$(shellpwd)
KVER?
=$(shelluname-r)
KDIR:
=/lib/modules/$(KVER)/build/*指定内核源码的路径*/
all:
$(MAKE)-C$(KDIR)M=$(PWD)
clean:
rm-rf.*.cmd*.o*.mod.c*.ko.tmp_versions
endif
通过执行makefile来编译驱动程序的源文件fakedev.c,以得到可执行目标文件。
截图如下:
由截图可见:
编译之后得到可加载模块fakedev.ko。
③fakedev源程序的测试程序fakedev_test.c。
测试的例程:
运行之前需要使用mkond命令来创建设备文件,然后执行不带参数的insmod,将上面编译得到的可加载模块fakedev.ko装载到内核。
测试代码fakedev_test.c
#include<
stdio.h>
/*所需头文件与预定义*/
stdlib.h>
unistd.h>
sys/types.h>
sys/stat.h>
fcntl.h>
#defineMAX_LENGTH100
intmian(intargc,char**argv)
{
charstr[MAX_LENGTH];
charp;
printf("
pleaseenterthewords(maxcharnum:
%d):
\n"
MAX_LENGTH-1);
p=fgets(str,MAX_LENGTH,stdin);
/*接收控制台输入的一组字符串*/
intfakedev_fd=open("
/dev/fakedev"
O_RDWR);
/*通过open系统调用打开虚拟设if(fakedev_fd==-1)备并获得文件描述符fakedev_fd*/
perror("
open"
exit
(1);
write(fakedev_fd,p,strlen(p));
/*通过write系统调用将字符串缓存到该设备中*/
charbuf[MAX_LENGTH];
/*将字符取出来打印到标准输出*/
read(fakedev_fd,buf,MAX_LENGTH-1);
Getstringsfromfakedev:
\n%s\n"
buf);
close(fakedev_fd);
由上图可看到:
mknod命令指定了主设备号200,其余的参数:
-m600设置为设备文件的读写权限;
字符c代表这是一个字符设备;
200之后的1为次设备号。
使用insmod命令将fakedev.ko加载之后,就可以从/proc/devices看到设备文件fakedevice(在源代码中已经被定义)
④测试运行程序:
运行test,就可在终端中摸拟本设计程序的功能。
如若能够在输入一行字符串之后,终端能自行输出同样的字符串,则说明该程序设计正确。
即输入:
#./test
测试结果达到了我们需要的效果:
输入字符22之后,通过write系统调用将其写入/dev/fakedev设备中,再通过read系统调用从该设备中得到了该字符串。
四实验总结:
本文结合简单字符设备驱动程序的开发,详细讨论了嵌入式Linux系统中字符设备驱动程序的设计方法和关键技术,对类似的其他字符设备驱动程序开发过程可以起到一定的启发作用。
在编译过程中会遇到找不到头文件的问题,这时可以通过寻找添加头文件以求编译通过。