第12章工程中的 Linux设备驱动之misc设备驱动

12.5 misc设备驱动

    Linux 包含许多的设备驱动类型,不管分类有多细,总会有些漏网的,这就是经常说到的“其他的”、“等等”。在 Linux 里面,把无法归类的五花八门的设备定义为混杂设备(用miscdevice 结构体描述)。Linux 内核所提供的 miscdevice 有很强的包容性,如 NVRAM(非易失随机存取存储器)、看门狗、DS1286 等实时时钟、字符 LCD、AMD 768 随机数发生器等,体现了大杂烩的本意。

    miscdevice 共享一个主设备号 MISC_MAJOR(10),但次设备号不同。所有的 miscdevice设备形成一个链表,对设备访问时内核根据次设备号查找对应的 miscdevice 设备,然后调用其file_operations 结构体中注册的文件操作接口进行操作。在内核中,用 struct miscdevice 结构体表征 miscdevice 设备,这个结构体的定义如代码清单12.20 所示。

代码清单 12.20 miscdevice 结构体

include/linux/miscdevice.h

struct miscdevice  {
        int minor; /* 次设备号 */
        const char *name; /* 名称 */
        const struct file_operations *fops; /* 文件操作函数集的指针*/
        struct list_head list;
        struct device *parent;
        struct device *this_device;
        const struct attribute_group **groups;
        const char *nodename;
        umode_t mode;

};

    miscdevice 在本质上仍然属于字符设备,只是被增加了一层封装,因此其驱动的主体工作还是 file_operations 的成员函数。代码清单 12.21 则给出了源代码 drivers/char/nvram.c 所实现NVRAM 驱动的 miscdevice 和 file_operations 实例。

代码清单 12.21 NVRAM(非易失随机存取存储器) 设备结构体

static const struct file_operations nvram_fops = {
.owner = THIS_MODULE,
.llseek = nvram_llseek,
.read = nvram_read,
.write = nvram_write,
.unlocked_ioctl = nvram_ioctl,
.open = nvram_open,
.release = nvram_release,

};

static struct miscdevice nvram_dev = {
NVRAM_MINOR,
"nvram",
&nvram_fops

};

对misddevice 的注册和注销分别通过如下两个API 完成:

include/linux/miscdevice.h

int misc_register(struct miscdevice * misc);

drivers/char/misc.c 

/**
 *      misc_register   -       register a miscellaneous device
 *      @misc: device structure
 *      
 *      Register a miscellaneous device with the kernel. If the minor
 *      number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
 *      and placed in the minor field of the structure. For other cases
 *      the minor number requested is used.
 *
 *      The structure passed is linked into the kernel and may not be
 *      destroyed until it has been unregistered.
 *
 *      A zero is returned on success and a negative errno code for
 *      failure.
 */

int misc_register(struct miscdevice * misc)
{
        dev_t dev;
        int err = 0;

        INIT_LIST_HEAD(&misc->list);

        mutex_lock(&misc_mtx);

        if (misc->minor == MISC_DYNAMIC_MINOR) {
                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
                if (i >= DYNAMIC_MINORS) {
                        err = -EBUSY;
                        goto out;
                }
                misc->minor = DYNAMIC_MINORS - i - 1;
                set_bit(i, misc_minors);
        } else {
                struct miscdevice *c;

                list_for_each_entry(c, &misc_list, list) {
                        if (c->minor == misc->minor) {
                                err = -EBUSY;
                                goto out;
                        }
                }
        }

        dev = MKDEV(MISC_MAJOR, misc->minor);

        misc->this_device = device_create(misc_class, misc->parent, dev,
                                          misc, "%s", misc->name);
        if (IS_ERR(misc->this_device)) {
                int i = DYNAMIC_MINORS - misc->minor - 1;
                if (i < DYNAMIC_MINORS && i >= 0)
                        clear_bit(i, misc_minors);
                err = PTR_ERR(misc->this_device);
                goto out;
        }

        /*
         * Add it to the front, so that later devices can "override"
         * earlier defaults
         */
        list_add(&misc->list, &misc_list);
 out:
        mutex_unlock(&misc_mtx);
        return err;
}

void misc_deregister(struct miscdevice *misc);

/**
 *      misc_deregister - unregister a miscellaneous device
 *      @misc: device to unregister
 *
 *      Unregister a miscellaneous device that was previously
 *      successfully registered with misc_register(). Success
 *      is indicated by a zero return, a negative errno code
 *      indicates an error.
 */

int misc_deregister(struct miscdevice *misc)
{
        int i = DYNAMIC_MINORS - misc->minor - 1;

        if (WARN_ON(list_empty(&misc->list)))
                return -EINVAL;

        mutex_lock(&misc_mtx);
        list_del(&misc->list);
        device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
        if (i < DYNAMIC_MINORS && i >= 0)
                clear_bit(i, misc_minors);
        mutex_unlock(&misc_mtx);
        return 0;
}

查看 drivers/char/nvram.c 的模块加载和卸载函数可知,其在加载的时候调用了“misc_register(&nvram_dev);”,而在模块卸载时调用了“misc_deregister(&nvram_dev);”。

混杂设备驱动实例

1、驱动源代码

#include <linux/init.h>
#include <linux/module.h>  
#include <linux/miscdevice.h>
#include <linux/fs.h>

static int test_miscdev_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "----test_miscdev_open!----\n");

return 0;
}


static int test_miscdev_release(struct inode *inode, struct file *file)
{
printk(KERN_INFO "----test_miscdev_release!----\n");

return 0;
}

static const struct file_operations test_miscdev_fops = {
.owner = THIS_MODULE,
.open = test_miscdev_open,
.release = test_miscdev_release,
};

static struct miscdevice test_miscdev = {
MISC_DYNAMIC_MINOR,
"misc_test",
&test_miscdev_fops
};

static int __init test_misdev_init(void)
{
int ret;

ret = misc_register(&test_miscdev);
if (ret) {
printk(KERN_ERR "test_misdev: can't misc_register on minor=%d\n", 
MISC_DYNAMIC_MINOR);
goto out;
}
printk(KERN_INFO "----test_misdev_init!----\n");

return 0;

out:
return ret;
}


static void __exit test_misdev_exit(void)
{
misc_deregister(&test_miscdev);
        printk(KERN_INFO "----test_misdev_exit!----\n");
}

module_init(test_misdev_init);
module_exit(test_misdev_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("[email protected]");

MODULE_DESCRIPTION("a miscdev demo");

2、Makefile

ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:                               
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:                                             
        $(MAKE) -C $(KERNELDIR) M=$(PWD) clean
else
    obj-m := misc_dev_test.o

endif

3、测试

1)ls -l /dev/misc_test

crw------- 1 root root 10, 55 Jun 13 09:40 /dev/misc_test

2)lsmod | grep misc_dev_test

misc_dev_test   16384  0

3)ls -l /sys/class/misc/ | grep misc_test  

lrwxrwxrwx 1 root root 0 Jun 13 09:41 misc_test -> ../../devices/virtual/misc/misc_test

4)cat /proc/misc | grep misc_test 
 55 misc_test


猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80673713
今日推荐