一、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
当应用程序使用open打开杂项设备时,将根据设备节点的次设备号得到设备真正的操作函数集合
该函数供驱动人员调用。主要的功能有:给设备分配次设备号;根据设备号在/dev目录下新建设备节点;将杂项设备加入misc_list链表
该函数供驱动人员调用。主要功能和misc_register相反:将设备从misc_list中删除;删除/dev目录下的设备
三、杂项驱动实例
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
- static int __init misc_init(void)
- {
- int err;
- #ifdef CONFIG_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))
- goto fail_remove;
- err = -EIO;
- /*注册设备,其中设备的主设备号为MISC_MAJOR,为10。设备名为misc,misc_fops是操作函数的集合*/
- if (register_chrdev(MISC_MAJOR,"misc",&misc_fops))
- goto fail_printk;
- return 0;
- fail_printk:
- printk("unable to get major %d for misc devices/n", MISC_MAJOR);
- class_destroy(misc_class);
- fail_remove:
- remove_proc_entry("misc", NULL);
- return err;
- }
- /*misc作为一个子系统被注册到linux内核中,系统开机后会自动调用misc_init*/
- subsys_initcall(misc_init);
- static int misc_open(struct inode * inode, struct file * file)
- {
- int minor = iminor(inode);
- struct miscdevice *c;
- int err = -ENODEV;
- const struct file_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)
- goto fail;
- }
- 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();
- return err;
- }
- int misc_register(struct miscdevice * misc)
- {
- struct miscdevice *c;
- dev_t dev;
- int err = 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;
- }
- }
- /*
- *#define DYNAMIC_MINORS 64
- *static unsigned char misc_minors[DYNAMIC_MINORS / 8];
- *这里存在一个次设备号的位图,一共64位,下边是遍历每一位;
- *如果这位为0,表示没有被占有,可以使用,为1表示被占用。
- */
- if (misc->minor == MISC_DYNAMIC_MINOR) {
- int i = 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);//设置位图中相应为1
- dev = 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);
- goto out;
- }
- /*将这个miscdevice添加到misc_list链表中*/
- list_add(&misc->list, &misc_list);
- out:
- mutex_unlock(&misc_mtx);
- return err;
- }
- int misc_deregister(struct miscdevice *misc)
- {
- int i = 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);
- return 0;
- }
- #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>
- #define DEVICE_NAME "led_test"/*注册驱动时自动建立的设备名称*/
- #define IOCTL_GPIO_ON 1
- #define IOCTL_GPIO_OFF 0
- /* 用来指定LED所用的GPIO引脚 */
- static unsigned long gpio_table [] =
- {
- S3C2410_GPB5,
- S3C2410_GPB6,
- S3C2410_GPB7,
- S3C2410_GPB8,
- };
- /* 用来指定GPIO引脚的功能:输出 */
- static unsigned int gpio_cfg_table [] =
- {
- S3C2410_GPB5_OUTP,
- S3C2410_GPB6_OUTP,
- S3C2410_GPB7_OUTP,
- S3C2410_GPB8_OUTP,
- };
- static int s3c2440_leds_open(struct inode *inode,struct file *filp)/*open函数实现的是led的灯的配置:输出*/
- {
- int i;
- for(i=0;i<4;i++)
- {
- s3c2410_gpio_cfgpin(gpio_table[i],gpio_cfg_table[i]);
- }
- return 0;
- }
- static int tq2440_gpio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)/*实现的是led灯的控制*/
- {
- if (arg > 4)
- {
- return -EINVAL;
- }
- switch(cmd)
- {
- case IOCTL_GPIO_ON:
- s3c2410_gpio_setpin(gpio_table[arg], 0); // 设置指定引脚的输出电平为0
- return 0;
- case IOCTL_GPIO_OFF:
- s3c2410_gpio_setpin(gpio_table[arg], 1);// 设置指定引脚的输出电平为1
- return 0;
- default:
- return -EINVAL;
- }
- }
- /*驱动框架必备,file_operations 是包含了对这个设备所能进行的操作*/
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE,
- .ioctl = tq2440_gpio_ioctl,
- .open = s3c2440_leds_open,
- };
- /*驱动框架必备,驱动信息的打包,用于该驱动程序的注册*/
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- /*驱动框架必备,驱动初始化函数*/
- static int __init dev_init(void)
- {
- int ret;
- ret = misc_register(&misc);
- printk (DEVICE_NAME" initialized\n");
- return ret;
- }
- /*驱动框架必备,驱动退出函数*/
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc);
- printk (DEVICE_NAME" is over!\n");
- }
- /*驱动框架必备,模块初始化入口函数*/
- module_init(dev_init);
- /*驱动框架必备,模块结束入口函数*/
- module_exit(dev_exit);
- /*驱动框架必备,通用公共许可*/
- MODULE_LICENSE("GPL");