Linux设备驱动程序学习Word文档格式.docx
- 文档编号:19673038
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:32
- 大小:60.55KB
Linux设备驱动程序学习Word文档格式.docx
《Linux设备驱动程序学习Word文档格式.docx》由会员分享,可在线阅读,更多相关《Linux设备驱动程序学习Word文档格式.docx(32页珍藏版)》请在冰豆网上搜索。
unsignedintcount,char*name);
//动态生成设备编号
voidunregister_chrdev_region(dev_tfirst,unsignedintcount);
//释放设备编号
分配之设备号的最佳方式是:
默认采用动态分配,同时保留在加载甚至是编译时指定主设备号的余地。
以下是在scull.c中用来获取主设备好的代码:
if(scull_major){
dev=MKDEV(scull_major,scull_minor);
result=register_chrdev_region(dev,scull_nr_devs,"
scull"
);
}else{
result=alloc_chrdev_region(&
dev,scull_minor,scull_nr_devs,"
scull_major=MAJOR(dev);
}
if(result<
0){
printk(KERN_WARNING"
scull:
can'
tgetmajor%d\n"
scull_major);
returnresult;
在这部分中,比较重要的是在用函数获取设备编号后,其中的参数name是和该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中。
看到这里,就可以理解为什么mdev和udev可以动态、自动地生成当前系统需要的设备文件。
udev就是通过读取sysfs理解和认识udev
二、一些重要的数据结构
大部分基本的驱动程序操作涉及及到三个重要的内核数据结构,分别是file_operations、file和inode,它们的定义都在
三、字符设备的注册
内核内部使用structcdev结构来表示字符设备。
在内核调用设备的操作之前,必须分配并注册一个或多个structcdev。
代码应包含<
linux/cdev.h>
,它定义了structcdev以及与其相关的一些辅助函数。
注册一个独立的cdev设备的基本过程如下:
1、为structcdev分配空间(如果已经将structcdev嵌入到自己的设备的特定结构体中,并分配了空间,这步略过!
)
structcdev*my_cdev=cdev_alloc();
2、初始化structcdev
voidcdev_init(structcdev*cdev,conststructfile_operations*fops)
3、初始化cdev.owner
cdev.owner=THIS_MODULE;
4、cdev设置完成,通知内核structcdev的信息(
intcdev_add(structcdev*p,dev_tdev,unsignedcount)
/*
*Setupthechar_devstructureforthisdevice.
*/
staticvoidscull_setup_cdev(structscull_dev*dev,intindex)
{
interr,devno=MKDEV(scull_major,scull_minor+index);
cdev_init(&
dev->
cdev,&
scull_fops);
cdev.ops=&
scull_fops;
err=cdev_add(&
cdev,devno,1);
/*Failgracefullyifneedbe这步值得注意*/
if(err)
printk(KERN_NOTICE"
Error%daddingscull%d"
err,index);
}
以下是scull模型的结构体:
*Representationofscullquantumsets.
structscull_qset{
void**data;
structscull_qset*next;
};
structscull_dev{
structscull_qset*data;
/*Pointertofirstquantumset*/
intquantum;
/*thecurrentquantumsize*/
intqset;
/*thecurrentarraysize*/
unsignedlongsize;
/*amountofdatastoredhere*/
unsignedintaccess_key;
/*usedbysculluidandscullpriv*/
structsemaphoresem;
/*mutualexclusionsemaphore*/
structcdevcdev;
/*Chardevicestructure
scull驱动程序引入了两个Linux内核中用于内存管理的核心函数,它们的定义都在<
linux/slab.h>
:
void*kmalloc(size_tsize,intflags);
voidkfree(void*ptr);
以下是scull模块中的一个释放整个数据区的函数(类似清零),将在scull以写方式打开和scull_cleanup_module中被调用:
intscull_trim(structscull_dev*dev)
structscull_qset*next,*dptr;
intqset=dev->
qset;
inti;
for(dptr=dev->
data;
dptr;
dptr=next){/*循环scull_set个数次,直到dptr为NULL为止。
if(dptr->
data){
for(i=0;
i<
qset;
i++
kfree(dptr->
data[i])
data)
dptr->
data=NULL
}
next=dptr->
next
kfree(dptr)
dev->
size=0
quantum=scull_quantum
qset=scull_qset
return0;
/*Followthelist*/
structscull_qset*scull_follow(structscull_dev*dev,intn)
structscull_qset*qs=dev->
/*Allocatefirstqsetexplicitlyifneedbe*/
if(!
qs){
qs=dev->
data=kmalloc(sizeof(structscull_qset),GFP_KERNEL);
if(qs==NULL)
returnNULL;
/*Nevermind*/
memset(qs,0,sizeof(structscull_qset));
/*Thenfollowthelist*/
while(n--){
qs->
next){
next=kmalloc(sizeof(structscull_qset),GFP_KERNEL);
if(qs->
next==NULL)
memset(qs->
next,0,sizeof(structscull_qset));
qs=qs->
next;
continue;
returnqs;
其实这个函数的实质是:
如果已经存在这个scull_set,就返回这个scull_set的指针。
如果不存在这个scull_set,一边沿链表为scull_set分配空间一边沿链表前行,直到所需要的
五、open和release
open方法提供给驱动程序以初始化的能力,为以后的操作作准备。
应完成的工作如下:
(1)检查设备特定的错误(如设备未就绪或硬件问题);
(2)如果设备是首次打开,则对其进行初始化;
(3)如有必要,更新f_op指针;
(4)分配并填写置于filp->
private_data里的数据结构。
而根据scull的实际情况,他的open函数只要完成第四步(将初始化过的传递到filp->
private_data里,以备后用)就好了,所以open函数很简单。
但是其中用到了
#definecontainer_of(ptr,type,member)({
\
consttypeof(((type*)0)->
member)*__mptr=(ptr);
(type*)((char*)__mptr-offsetof(type,member));
})
其实从源码可以看出,其作用就是:
通过指针
release方法提供释放内存,关闭设备的功能。
(1)释放由open分配的、保存在file->
private_data中的所有内容;
(2)在最后一次关闭操作时关闭设备。
由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做。
read和write方法的主要作用就是实现内核与用户空间之间的数据拷贝。
因为Linux的内核空间和用户空间隔离的,所以要实现数据拷贝就必须使用在:
unsignedlongcopy_to_user(void__user*to,
constvoid*from,
unsignedlongcount);
unsignedlongcopy_from_user(void*to,
constvoid__user*from,
unsignedlongcount);
而值得一提的是以上两个函数和
#define__copy_from_user(to,from,n)(memcpy(to,(void__force*)from,n),0)
#define__copy_to_user(to,from,n)(memcpy((void__force*)to,from,n),0)
之间的关系:
通过源码可知,前者调用后者,但前者在调用前对用户空间指针进行了检查。
七、模块实验
这次模块实验的使用是友善之臂SBC2440V4,使用Linux2.6.22.2内核。
量子大小为6:
[Tekkaman2440@SBC2440V4]#cd/lib/modules/[Tekkaman2440@SBC2440V4]#insmodscull.koscull_quantum=6
[Tekkaman2440@SBC2440V4]#cat/proc/devices
Characterdevices:
1mem
2pty
3ttyp
4/dev/vc/0
4tty
4ttyS
5/dev/tty
5/dev/console
5/dev/ptmx
7vcs
10misc
13input
14sound
81video4linux
89i2c
90mtd
116alsa
128ptm
136pts
180usb
189usb_device
204s3c2410_serial
252scull
253usb_endpoint
254rtc
Blockdevices:
1ramdisk
256rfd
7loop
31mtdblock
93nftl
96inftl
179mmc
[Tekkaman2440@SBC2440V4]#mknod-m666scull0c
2520
[Tekkaman2440@SBC2440V4]#mknod-m666scull1c
2521
[Tekkaman2440@SBC2440V4]#mknod-m666scull2c
2522
[Tekkaman2440@SBC2440V4]#mknod-m666scull3c
2523
启动测试程序
[Tekkaman2440@SBC2440V4]#./scull_test
writeerror!
code=6
writeok!
code=2
readerror!
readok!
[0]=0[1]=1[2]=2[3]=3[4]=4
[5]=5[6]=6[7]=7[8]=8[9]=9
[10]=10[11]=11[12]=12[13]=13[14]=14
[15]=15[16]=16[17]=17[18]=18[19]=19
改变量子大小为默认值4000:
[Tekkaman2440@SBC2440V4]#cd/lib/modules/
[Tekkaman2440@SBC2440V4]#rmmodscull
[Tekkaman2440@SBC2440V4]#insmodscull.ko
[Tekkaman2440@SBC2440V4]#./scull_test
code=20
[0]=0[1]=1[2]=2[3]=3[4]=4
[5]=5[6]=6[7]=7[8]=8[9]=9
[10]=10[11]=11[12]=12[13]=13[14]=14
[Tekkaman2440@SBC2440V4]#
改变量子大小为6,量子集大小为2:
[Tekkaman2440@SBC2440V4]#insmodscull.koscull_quantum=6scull_qset=2
code=6
code=2
实验不仅测试了模块的读写能力,还测试了量子读写是否有效。
发表于:
2007-10-20,修改于:
2007-10-2514:
12,已浏览9664次,有评论0条推荐投诉
网友评论
好文章,我是从你的第一章开始看的,今天9号看到你又有新内容,学不少东西,请坚持下去,加油啊
很不错啊,我几乎每天都来学习你的经验,感觉收益非浅,加油啊!
我觉得最好的方式就是有人交流^_^我很想与你交流,我的QQ32313055
我学arm一年多了,可是水平比阁下差远了。
看完文章,收获良多,非常感谢!
向Tekkaman
Ninja致敬,你的学习态度和学习方式都值得我们借鉴!
圣诞快乐!
让我学到了很多东西,
很好的文章啊,我正在学习驱动开发,刚刚入门,谢谢啊
write
error!
code=-1
ok!
code=21
read
code=20
[0]=0
[1]=0
[2]=1
[3]=2
[4]=3
[5]=4
[6]=5
[7]=6
[8]=7
[9]=8
[10]=9
[11]=10
[12]=11
[13]=12
[14]=13
[15]=14
[16]=15
[17]=16
[18]=17
[19]=18
我改了一下makefile在pc上运行是这个结果
所有的源码都在友善之臂SBC2440V4上反复测试过(内核为2.6.22.2)。
所以基本上只要改Makefile就好(除了硬件相关的部分)。
如果有问题可以将详细的情况发邮件给我,有空我会回复的。
写的真的很好
我们老师就让写一个字符设备驱动程序,我都是通过在你的网页上学会的所有的东西。
赞一个,总结的很好
曾经研究过ldd3,没有认真写下读书笔记,以后还得好好再看下了
请问怎么使用drivers/spi/atmel_spi.c提供的驱动呢?
在/dev下没有相应的设备节点,怎么才能在用户空间访问设备?
如果自己写一个驱动注册一个spi驱动可以使用其中哪些函数接口?
诚请帮忙
今天看了你的文章很感慨,希望能共同学习。
有个问题想问,
请问当insmod
scull.ko
scull_quantum=6
scull_qset=2
数据已经溢出了,为什么20个数据还能写进去。
谢谢了
这个和scull的数据结构有关,你认真看看《Linux设备驱动程序学习
(1)-字符设备驱动程序》,中的图,你可能对scull的数据结构还没有理解透,scull_quantum代表了一个quantum的字节数,scull_qset代表一个qset包含几个quantum,但是还有qset没有限制,也就是说我写20个字节进去,一个scull中有2个qset。
总结的太好了。
有一个问题想请教一下。
看模块中定义了一个char
*whom
=
"
world"
这是一个字符串常量,如果按照标准C的话,指针指向的是一个常量字符串才对,其中的内容不能修改。
比如whom[2]='
a'
;
这样会有段错误,为什么在内核中这样操作是合法的呢?
我觉得你这样是定义一个字符串变量,所以可以修改。
我学硬件的,对c研究还不深,不知见解是否正确?
lz的经验和态度值得我们学习,向你致敬!
很不错,尤其是有源码和测试程序。
你好!
刚接触linux驱动,看到你这里的设备模型分析,很不错
帮了我的大忙!
我有一个问题,对于nand
flash,作为一个块设备,应用程序如何去访问它,对它进行擦除、读写等操作?
因为它不象字符设备那样有可供调用的系统调用
块设备我暂时还没看,见谅
我也想知道块设备的一些知识,老是来你这儿看,希望能得到一些帮助,呵呵
块设备我还没看啊,苦于没时间和没板做实验
问一个问题:
你上文提到的测试程序是不是相当于linux系统的应用程序,实现对驱动的系统调用?
如果是这样,那么测试程序应该放在什么路径下,用什么编译器进行编译,因为我看到你有写测试程序的makefile,是不是也要用交叉编译器对它进行编译阿,可是这样的话,生成的目标代码是arm格式的,不能在pc上直接执行。
麻烦你帮我解答一下这个疑问。
刚刚接触linux,问
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 设备 驱动程序 学习