杂项设备的主设备号是10,在任何Linux 系统中它都是固定的。
这样杂项设备的引入即解决了设备号数量少的问题,又降低了使用难度,还能防止碎片化,
一举多得。
杂项设备的头文件在“include/linux/miscdevice.h”
extern int misc_register(struct miscdevice * misc);
杂项设备注册函数;一般在probe 中调用,参数是miscdevice
extern int misc_deregister(struct miscdevice *misc);
杂项设备卸载函数;一般是在hello_remove 中用于卸载驱动。
结构体miscdevice 中参数很多,下面几个是常用的。
int .minor;设备号,赋值为MISC_DYNAMIC_MINOR,这个宏定义可以查到为255
const char *name;设备名称
const struct file_operations *fops;file_operations 结构。
file_operations 结构体在头文件“include/linux/fs.h”中
struct module *owner;一般是THIS_MODULE。
int (*open) (struct inode *, struct file *);对应上层的open 函数,打开文件。
int (*release) (struct inode *, struct file *);对应上层的close 函数,打开文件操作之后一
般需要关闭。
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);读函数,上层应用从底层读取
函数。
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);写函数,上层应用向底
层传输数据。
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);这个函数功能和写
函数稍微有点重合,但是这个函数占用的内存非常小,主要针对IO 口的控制。
实现实例源码:
#include <linux/init.h>
#include <linux/module.h>
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>
#define DRIVER_NAME "hello_ctl"
#define DEVICE_NAME "hello_ctl123"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("SKYFALL");
//long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
//驱动实际操作的实现
static long hello_ioctl( struct file *file, unsigned int cmd, unsigned long arg){
printk("%s,%d\n",__func__,__LINE__);
printk("cmd is %d,arg is %d\n",cmd,arg);
return 0;
}
//int (*release) (struct inode *, struct file *);
//关闭该驱动的实现
static int hello_release(struct inode *inode, struct file *file){
printk("%s,%d\n",__func__,__LINE__);
printk(KERN_EMERG "hello release\n");
return 0;
}
//int (*open) (struct inode *, struct file *);
//打开该驱动的实现
static int hello_open(struct inode *inode, struct file *file){
printk("%s,%d\n",__func__,__LINE__);
printk(KERN_EMERG "hello open\n");
return 0;
}
//定义一个字符设备操作集合
static struct file_operations hello_ops = {
.owner = THIS_MODULE,
.open = hello_open,
.release = hello_release,
.unlocked_ioctl = hello_ioctl,
};
//定义一个杂项设备结构体
static struct miscdevice hello_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
//字符设备操作集合,需要提前定义一个,然后取地址
.fops = &hello_ops,
};
static int hello_probe(struct platform_device *pdv){
printk(KERN_EMERG "\tinitialized\n");
printk("%s,%d\n",__func__,__LINE__);
//注册杂项设备,需要一个传一个杂项设备的结构体,所以需要提前定义一个,然后取地址
misc_register(&hello_dev);
return 0;
}
static int hello_remove(struct platform_device *pdv){
printk(KERN_EMERG "\tremove\n");
//注销杂项设备
misc_deregister(&hello_dev);
return 0;
}
static void hello_shutdown(struct platform_device *pdv){
;
}
static int hello_suspend(struct platform_device *pdv,pm_message_t pmt){
return 0;
}
static int hello_resume(struct platform_device *pdv){
return 0;
}
struct platform_driver hello_driver = {
.probe = hello_probe,
.remove = hello_remove,
.shutdown = hello_shutdown,
.suspend = hello_suspend,
.resume = hello_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
static int hello_init(void)
{
int DriverState;//定义驱动状态,判断驱动是否注册成功
printk(KERN_EMERG "HELLO WORLD enter!\n");
printk("%s,%d\n",__func__,__LINE__);
DriverState = platform_driver_register(&hello_driver);
/*
驱动一旦注册成功,会与设备进行匹配,匹配成功是由
platform_match函数进行匹配,驱动调用probe初始化函数
*/
printk(KERN_EMERG "\tDriverState is %d\n",DriverState);
return 0;
}
static void hello_exit(void)
{
printk(KERN_EMERG "HELLO WORLD exit!\n");
platform_driver_unregister(&hello_driver);
}
module_init(hello_init);
module_exit(hello_exit);
相应的Makefile文件参考生成字符设备的。
加载驱动进行测试:
加载之后, ls /dev, 可以看到新生成了设备节点hello_ctl123,也就是设备节点和驱动名以及设备名没有一关系,不过最好设备节点的命名便于识别。