misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,其实就是最简单的字符设备驱
动,通常嵌套在 platform 总线驱动中,实现复杂的驱动。
所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux 字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。 MISC 设备会自动创建 cdev,不需要手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。我们需要向 Linux 注册一个 miscdevice 设备, 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;
};
定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、 name 和 fops 这三个成员变量。 minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在 include/linux/miscdevice.h 文件中,如下所示:
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
......
#define MISC_DYNAMIC_MINOR 255
我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name 的设备文件。 fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops 操作集合。当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:
int misc_register(struct miscdevice * misc)
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。
ps:之前创建设备的过程:
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
杂项设备之需要调用 misc_register 即可完成上述繁杂的操作,misc_register 函数原型如下:
int misc_register(struct miscdevice *misc)
misc:杂项设备
返回值:负数,失败; 0,成功。
ps:同样的注销设备驱动的时候,我们需要调用一堆的函数去删除此前创建的 cdev、设备等等内容,如下所示:
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */
杂项只需要调用 misc_deregister 即可,函数原型如下:
int misc_deregister(struct miscdevice *misc)
misc:要注销的 MISC 设备。
返回值: 负数,失败; 0,成功。
一般杂项设备和 platform 配合使用,利用 platform 完成设备和驱动的分离,杂项设备简化字符设备的各种操作,一般用于一些简单驱动的编写。
example:
struct miscxxx_dev misctest_dev; /* misc设备 */
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int miscxxx_open(struct inode *inode, struct file *filp)
{
filp->private_data = &miscxxx; /* 设置私有数据 */
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t miscxxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
......
return 0;
}
/* 设备操作函数 */
static struct file_operations miscxxx_fops = {
.owner = THIS_MODULE,
.open = miscxxx_open,
.write = miscxxx_write,
};
/* MISC设备结构体 */
static struct miscdevice xxx_miscdev = {
.minor = MISCBEEP_MINOR,
.name = MISCBEEP_NAME,
.fops = &miscxxx_fops,
};
/*
* @description : flatform驱动的probe函数,当驱动与
* 设备匹配以后此函数就会执行
* @param - dev : platform设备
* @return : 0,成功;其他负值,失败
*/
static int miscxxx_probe(struct platform_device *dev)
{
......
/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
* 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
*/
ret = misc_register(&xxx_miscdev);
if(ret < 0){
printk("misc device register failed!\r\n");
return -EFAULT;
}
return 0;
}
/*
* @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行
* @param - dev : platform设备
* @return : 0,成功;其他负值,失败
*/
static int miscxxx_remove(struct platform_device *dev)
{
/* 注销misc设备 */
misc_deregister(&xxx_miscdev);
return 0;
}
/* 匹配列表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxxx" },
{ /* Sentinel */ }
};
/* platform驱动结构体 */
static struct platform_driver xxx_driver = {
.driver = {
.name = "xxxx", /* 驱动名字,用于和设备匹配 */
.of_match_table = xxx_of_match, /* 设备树匹配表 */
},
.probe = miscxxx_probe,
.remove = miscxxx_remove,
};
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static int __init xxx_init(void)
{
return platform_driver_register(&xxx_driver);
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit xxx_exit(void)
{
platform_driver_unregister(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxx");
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于