linux设备驱动学习1.docx
- 文档编号:29437799
- 上传时间:2023-07-23
- 格式:DOCX
- 页数:10
- 大小:42.35KB
linux设备驱动学习1.docx
《linux设备驱动学习1.docx》由会员分享,可在线阅读,更多相关《linux设备驱动学习1.docx(10页珍藏版)》请在冰豆网上搜索。
linux设备驱动学习1
Linux设备驱动程序学习
(1)
-字符设备驱动程序
今天进入《Linux设备驱动程序(第3版)》第三章字符设备驱动程序的学习。
这一章主要通过介绍字符设备scull(SimpleCharacterUtilityforLoadingLocalities,区域装载的简单字符工具)的驱动程序编写,来学习Linux设备驱动的基本知识。
scull可以为真正的设备驱动程序提供样板。
一、主设备号和此设备号
主设备号表示设备对应的驱动程序;次设备号由内核使用,用于正确确定设备文件所指的设备。
内核用dev_t类型(
在实际使用中,是通过
(dev_t)-->主设备号、次设备号
MAJOR(dev_tdev)
MINOR(dev_tdev)
主设备号、次设备号-->(dev_t)
MKDEV(intmajor,intminor)
建立一个字符设备之前,驱动程序首先要做的事情就是获得设备编号。
其这主要函数在
intregister_chrdev_region(dev_tfirst,unsignedintcount,
char*name); //指定设备编号
intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,
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");
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》
URL:
二、一些重要的数据结构
大部分基本的驱动程序操作涉及及到三个重要的内核数据结构,分别是file_operations、file和inode,它们的定义都在
三、字符设备的注册
内核内部使用structcdev结构来表示字符设备。
在内核调用设备的操作之前,必须分配并注册一个或多个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的信息(在执行这步之前必须确定你对structcdev的以上设置已经完成!
)
intcdev_add(structcdev*p,dev_tdev,unsignedcount)
从系统中移除一个字符设备:
voidcdev_del(structcdev*p)
以下是scull中的初始化代码(之前已经为structscull_dev分配了空间):
/*
*Setupthechar_devstructureforthisdevice.
*/
staticvoidscull_setup_cdev(structscull_dev*dev,intindex)
{
interr,devno=MKDEV(scull_major,scull_minor+index);
cdev_init(&dev->cdev,&scull_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&scull_fops; //这句可以省略,在cdev_init中已经做过
err=cdev_add(&dev->cdev,devno,1);
/*Failgracefullyifneedbe这步值得注意*/
if(err)
printk(KERN_NOTICE"Error%daddingscull%d",err,index);
}
四、scull模型的内存使用
以下是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内核中用于内存管理的核心函数,它们的定义都在
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 kfree(dptr->data[i]);/*释放其中一个量子的空间*/ kfree(dptr->data);/*释放当前的scull_set的量子集的空间*/ dptr->data=NULL;/*释放一个scull_set中的void**data指针*/ } next=dptr->next;/*准备下个scull_set的指针*/ kfree(dptr);/*释放当前的scull_set*/ } dev->size=0;/*当前的scull_device所存的数据为0字节*/ dev->quantum=scull_quantum;/*初始化一个量子的大小*/ dev->qset=scull_qset;/*初始化一个量子集中量子的个数*/ dev->data=NULL;/*释放当前的scull_device的structscull_qset*data指针*/ return0; } 以下是scull模块中的一个沿链表前行得到正确scull_set指针的函数,将在read和write方法中被调用: /*Followthelist*/ structscull_qset*scull_follow(structscull_dev*dev,intn) { structscull_qset*qs=dev->data; /*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--){ if(! qs->next){ qs->next=kmalloc(sizeof(structscull_qset),GFP_KERNEL); if(qs->next==NULL) returnNULL;/*Nevermind*/ memset(qs->next,0,sizeof(structscull_qset)); } qs=qs->next; continue; } returnqs; } 其实这个函数的实质是: 如果已经存在这个scull_set,就返回这个scull_set的指针。 如果不存在这个scull_set,一边沿链表为scull_set分配空间一边沿链表前行,直到所需要的scull_set被分配到空间并初始化为止,就返回这个scull_set的指针。 五、open和release open方法提供给驱动程序以初始化的能力,为以后的操作作准备。 应完成的工作如下: (1)检查设备特定的错误(如设备未就绪或硬件问题); (2)如果设备是首次打开,则对其进行初始化; (3)如有必要,更新f_op指针; (4)分配并填写置于filp->private_data里的数据结构。 而根据scull的实际情况,他的open函数只要完成第四步(将初始化过的structscull_devdev的指针传递到filp->private_data里,以备后用)就好了,所以open函数很简单。 但是其中用到了定义在 #definecontainer_of(ptr,type,member)({ \ consttypeof(((type*)0)->member)*__mptr=(ptr); \ (type*)((char*)__mptr-offsetof(type,member));}) 其实从源码可以看出,其作用就是: 通过指针ptr,获得包含ptr所指向数据(是member结构体)的type结构体的指针。 即是用指针得到另外一个指针。 release方法提供释放内存,关闭设备的功能。 应完成的工作如下: (1)释放由open分配的、保存在file->private_data中的所有内容; (2)在最后一次关闭操作时关闭设备。 由于前面定义了scull是一个全局且持久的内存区,所以他的release什么都不做。 六、read和write 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) 之间的关系: 通过源码可知,前者调用后者,但前者在调用前对用户空间指针进行了检查。 至于read和write的具体函数比较简单,就在实验中验证好了。 七、模块实验 这次模块实验的使用是友善之臂SBC2440V4,使用Linux2.6.22.2内核。 模块程序链接: scull模块源程序 模块测试程序链接: 模块测试程序 测试结果: 量子大小为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 writeerror! code=6 writeerror! code=6 writeok! code=2 readerror! code=6 readerror! code=6 readerror! code=6 readok! code=2 [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 writeok! code=20 readok! 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 [15]=15[16]=16[17]=17[18]=18[19]=19 [Tekkaman2440@SBC2440V4]# 改变量子大小为6,量子集大小为2: [Tekkaman2440@SBC2440V4]#cd/lib/modules/ [Tekkaman2440@SBC2440V4]#rmmodscull [Tekkaman2440@SBC2440V4]#insmodscull.koscull_quantum=6scull_qset=2 启动测试程序 [Tekkaman2440@SBC2440V4]#./scull_test writeerror! code=6 writeerror! code=6 writeerror! code=6 writeok! code=2 readerror! code=6 readerror! code=6 readerror! code=6 readok! code=2 [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
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 设备 驱动 学习