700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > Linux杂项设备驱动

Linux杂项设备驱动

时间:2022-09-16 21:46:11

相关推荐

Linux杂项设备驱动

一、Linux杂项设备驱动简介

Linux杂项驱动出现的意义在于:有很多简单的外围字符设备,它们功能相对简单,一个设备占用一个主设备号对于内核资源来说太浪费。

所以对于这些简单的字符设备它们共用一个主设备号,不同的设备使用不同的次设备号.

杂项驱动特点:

主设备号相同,次设备号不同

在文件系统中自动生成设备节点

杂项设备描述结构体

struct miscdevice {

int minor;//次设备号

const char *name;//设备的名字

const struct file_operations *fops;//设备的操作函数集

struct list_head list;//将miscdevice串成链表使用

struct device *parent;//父设备(设备模型有关)

struct device *this_device;//代表自己的设备(设备模型有关)

};

杂项设备驱动基本原理:

/devices/char/misc.c

static LIST_HEAD(misc_list);//保存所有注册的杂项设备的链表头

static DEFINE_MUTEX(misc_mtx);

#define DYNAMIC_MINORS 64 //最大可注册的杂项设备数目

static unsigned char misc_minors[DYNAMIC_MINORS/8];//杂项设备位图,每位代表该对应的次设备号是否使用

杂项设备注册过程:

(1)在misc_minors[DYNAMIC_MINORS/8]找一个未使用的次设备号

(2)将新的杂项设备挂在misc_list链表中

(3)根据设备号(主设备10+次设备号)新建设备

杂项设备调用过程:

(1)应用程序打开杂项设备节点

(2)根据设备的主设备号得出是杂项设备,调用杂项设备的misc_open

(3)在misc_open中根据次设备号在misc_list链表中找出设备

(4)将设备的fops赋值给文件结构体struct file * file

二、杂项设备原理分析

Linux杂项设备注册的使用了主设备号10,提供只有open函数,设备操作函数集的初始化就在该函数中完成。它主要是根据open函数

传递的struct inode * inode得到设备的次设备号,然后根据次设备号在misc_list链表中查询得到具体设备的操作函数集。

/devices/char/misc.c

static const struct file_operations misc_fops = {

.owner = THIS_MODULE,

.open = misc_open,

};

将杂项设备注册为字符驱动,主设备号为10,设备操作函数为misc_open

[cpp]view plaincopystaticint__initmisc_init(void){interr;#ifdefCONFIG_PROC_FS/*创建一个proc入口项*/proc_create("misc",0,NULL,&misc_proc_fops);#endif/*在/sys/class/目录下创建一个名为misc的类*/misc_class=class_create(THIS_MODULE,"misc");err=PTR_ERR(misc_class);if(IS_ERR(misc_class))gotofail_remove;err=-EIO;/*注册设备,其中设备的主设备号为MISC_MAJOR,为10。设备名为misc,misc_fops是操作函数的集合*/if(register_chrdev(MISC_MAJOR,"misc",&misc_fops))gotofail_printk;return0;fail_printk:printk("unabletogetmajor%dformiscdevices/n",MISC_MAJOR);class_destroy(misc_class);fail_remove:remove_proc_entry("misc",NULL);returnerr;}/*misc作为一个子系统被注册到linux内核中,系统开机后会自动调用misc_init*/subsys_initcall(misc_init);当应用程序使用open打开杂项设备时,将根据设备节点的次设备号得到设备真正的操作函数集合

[cpp]view plaincopystaticintmisc_open(structinode*inode,structfile*file){intminor=iminor(inode);structmiscdevice*c;interr=-ENODEV;conststructfile_operations*old_fops,*new_fops=NULL;lock_kernel();mutex_lock(&misc_mtx);list_for_each_entry(c,&misc_list,list){//取出misc_list链表中次设备号为minor的设备if(c->minor==minor){new_fops=fops_get(c->fops);//获得该设备的操作函数集合break;}}if(!new_fops){mutex_unlock(&misc_mtx);request_module("char-major-%d-%d",MISC_MAJOR,minor);mutex_lock(&misc_mtx);list_for_each_entry(c,&misc_list,list){if(c->minor==minor){new_fops=fops_get(c->fops);break;}}if(!new_fops)gotofail;}err=0;old_fops=file->f_op;/*保存旧打开函数的地址*/file->f_op=new_fops;//让设备的文件结构体中的操作函数指针指向设备的操作函数指针if(file->f_op->open){err=file->f_op->open(inode,file);//调用设备的open函数if(err){fops_put(file->f_op);file->f_op=fops_get(old_fops);}}fops_put(old_fops);fail:mutex_unlock(&misc_mtx);unlock_kernel();returnerr;}该函数供驱动人员调用。主要的功能有:给设备分配次设备号;根据设备号在/dev目录下新建设备节点;将杂项设备加入misc_list链表

[cpp]view plaincopyintmisc_register(structmiscdevice*misc){structmiscdevice*c;dev_tdev;interr=0;INIT_LIST_HEAD(&misc->list);/*初始化misc_list链表*/mutex_lock(&misc_mtx);/*遍历misc_list链表,看这个次设备号以前有没有被用过,如果次设备号已被占有则退出*/list_for_each_entry(c,&misc_list,list){if(c->minor==misc->minor){mutex_unlock(&misc_mtx);return-EBUSY;}}/**#defineDYNAMIC_MINORS64*staticunsignedcharmisc_minors[DYNAMIC_MINORS/8];*这里存在一个次设备号的位图,一共64位,下边是遍历每一位;*如果这位为0,表示没有被占有,可以使用,为1表示被占用。*/if(misc->minor==MISC_DYNAMIC_MINOR){inti=DYNAMIC_MINORS;while(--i>=0)if((misc_minors[i>>3]&(1<<(i&7)))==0)break;if(i<0){mutex_unlock(&misc_mtx);return-EBUSY;}misc->minor=i;//得到可用的次设备号}if(misc->minor<DYNAMIC_MINORS)misc_minors[misc->minor>>3]|=1<<(misc->minor&7);//设置位图中相应为1dev=MKDEV(MISC_MAJOR,misc->minor);//计算出设备号/*在/dev下创建设备节点,这就是有些驱动程序没有显式调用device_create,却出现了设备节点的原因*/misc->this_device=device_create(misc_class,misc->parent,dev,NULL,"%s",misc->name);if(IS_ERR(misc->this_device)){err=PTR_ERR(misc->this_device);gotoout;}/*将这个miscdevice添加到misc_list链表中*/list_add(&misc->list,&misc_list);out:mutex_unlock(&misc_mtx);returnerr;}该函数供驱动人员调用。主要功能和misc_register相反:将设备从misc_list中删除;删除/dev目录下的设备

[cpp]view plaincopyintmisc_deregister(structmiscdevice*misc){inti=misc->minor;//取得设备此设备号if(list_empty(&misc->list))return-EINVAL;mutex_lock(&misc_mtx);list_del(&misc->list);//将misc从misc_list链表中取出device_destroy(misc_class,MKDEV(MISC_MAJOR,misc->minor));//将/dev目录下的相应设备清除if(i<DYNAMIC_MINORS&&i>0){misc_minors[i>>3]&=~(1<<(misc->minor&7));//位图相应为置0}mutex_unlock(&misc_mtx);return0;}三、杂项驱动实例

[cpp]view plaincopy#include<linux/miscdevice.h>#include<linux/delay.h>#include<asm/irq.h>#include<mach/regs-gpio.h>#include<mach/hardware.h>#include<linux/kernel.h>#include<linux/module.h>#include<linux/init.h>#include<linux/mm.h>#include<linux/fs.h>#include<linux/types.h>#include<linux/slab.h>#include<linux/errno.h>#include<linux/ioctl.h>#include<linux/cdev.h>#include<linux/string.h>#include<linux/list.h>#include<asm/uaccess.h>#include<asm/atomic.h>#include<asm/unistd.h>#defineDEVICE_NAME"led_test"/*注册驱动时自动建立的设备名称*/#defineIOCTL_GPIO_ON1#defineIOCTL_GPIO_OFF0/*用来指定LED所用的GPIO引脚*/staticunsignedlonggpio_table[]={S3C2410_GPB5,S3C2410_GPB6,S3C2410_GPB7,S3C2410_GPB8,};/*用来指定GPIO引脚的功能:输出*/staticunsignedintgpio_cfg_table[]={S3C2410_GPB5_OUTP,S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,S3C2410_GPB8_OUTP,};staticints3c2440_leds_open(structinode*inode,structfile*filp)/*open函数实现的是led的灯的配置:输出*/{inti;for(i=0;i<4;i++){s3c2410_gpio_cfgpin(gpio_table[i],gpio_cfg_table[i]);}return0;}staticinttq2440_gpio_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)/*实现的是led灯的控制*/{if(arg>4){return-EINVAL;}switch(cmd){caseIOCTL_GPIO_ON:s3c2410_gpio_setpin(gpio_table[arg],0);//设置指定引脚的输出电平为0return0;caseIOCTL_GPIO_OFF:s3c2410_gpio_setpin(gpio_table[arg],1);//设置指定引脚的输出电平为1return0;default:return-EINVAL;}}/*驱动框架必备,file_operations是包含了对这个设备所能进行的操作*/staticstructfile_operationsdev_fops={.owner=THIS_MODULE,.ioctl=tq2440_gpio_ioctl,.open=s3c2440_leds_open,};/*驱动框架必备,驱动信息的打包,用于该驱动程序的注册*/staticstructmiscdevicemisc={.minor=MISC_DYNAMIC_MINOR,.name=DEVICE_NAME,.fops=&dev_fops,};/*驱动框架必备,驱动初始化函数*/staticint__initdev_init(void){intret;ret=misc_register(&misc);printk(DEVICE_NAME"initialized\n");returnret;}/*驱动框架必备,驱动退出函数*/staticvoid__exitdev_exit(void){misc_deregister(&misc);printk(DEVICE_NAME"isover!\n");}/*驱动框架必备,模块初始化入口函数*/module_init(dev_init);/*驱动框架必备,模块结束入口函数*/module_exit(dev_exit);/*驱动框架必备,通用公共许可*/MODULE_LICENSE("GPL");

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。