MISC 驱动其实就是最简单的字符设备驱动,通常嵌套在 platform 总线驱动中,实现复杂的驱动,他的作用就是解决Linux系统设备号紧缺的问题。
所有的MISC设备驱动的主设备号都为10,不同的设备使用不同的从设备号!
一、为什么使用MISC
MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设
备驱动可以简化字符设备驱动的编写,可以省去以下函数:
alloc_chrdev_region() /*申请设备号*/
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */
① 要使用MISC设备驱动,我们需要往Linux系统中注册一个miscdevice设备。miscdevice
是一个结构体定义如下:
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;
};
要想Linux系统注册一个miscdevice设备,至少需要配置minor、name和fops三个参数。其中minor表示从设备号,name表示设备名字,fops是字符设备操作函数集合。
②注册和注销MISC设备
注册函数为:
int misc_register(struct miscdevice * misc)
函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。
注销函数为:
int misc_deregister(struct miscdevice *misc)
函数参数和返回值含义如下:
misc:要注销的 MISC 设备。
返回值: 负数,失败; 0,成功
通常在platform的probe函数中注册,remove函数中注销 。
驱动代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MISCBEEP_MINOR 144 /*子设备号*/
#define MISCBEEP_NAME "misbeep" /*设备名*/
#define BEEPON 1 /*开蜂鸣器*/
#define BEEPOFF 0 /*关蜂鸣器*/
struct miscbeep_dev{
dev_t devid; /*设备号*/
struct cdev cdev; /*字符设备*/
struct class *class; /*定义类*/
struct device *device; /*定义设备*/
struct device_node *node; /*添加节点*/
int beep_gpio; /*beep所用的gpio号*/
struct miscdevice *beep_miscdev;
};
struct miscbeep_dev miscbeep;
static int miscbeep_open(struct inode *inode, struct file *filp){
filp->private_data = &miscbeep; /* 设置私有数据 */
return 0;
}
static ssize_t miscbeep_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
int ret = 0;
unsigned char databuf[1];
ret = copy_from_user(databuf, buf, cnt);
if(ret < 0){
return -EINVAL;
}
if(databuf[0] == BEEPON){
gpio_set_value(miscbeep.beep_gpio, 0);
}
else if(databuf[0] == BEEPOFF){
gpio_set_value(miscbeep.beep_gpio, 1);
}
return 0;
}
/*定义字符操作集*/
static const struct file_operations miscbeep_fops = {
.owner = THIS_MODULE,
.write = miscbeep_write,
.open = miscbeep_open,
};
static struct miscdevice beep_miscdev = {
.minor = MISCBEEP_MINOR,
.name = MISCBEEP_NAME,
.fops = &miscbeep_fops,
};
static int miscbeep_probe(struct platform_device *dev){
int ret = 0;
printk("miscbeep_init!\r\n");
miscbeep.beep_miscdev = &beep_miscdev;
/*设置Beep所用GPIO*/
miscbeep.node = of_find_node_by_path("/beep"); /*获取设备节点*/
if(miscbeep.node == NULL){
printk("can't find beep node\r\n");
ret = -EINVAL;
goto failed_findnode;
}
/*获取设备树gpio属性,beep的gpio号*/
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.node, "beep-gpios", 0);
if(miscbeep.beep_gpio < 0){
printk("can't find beep gpio \r\n");
ret = -EINVAL;
goto failed_findnode;
}
printk("beep gpio num = %d \r\n",miscbeep.beep_gpio);
/*设置 GPIO5_IO01 为输出,并且输出高电平,默认关闭 BEEP */
ret = gpio_direction_output(miscbeep.beep_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
/* 一般情况下会注册对应的字符设备,但是这里我们使用 MISC 设备
* 所以我们不需要自己注册字符设备驱动,只需要注册 misc 设备驱动即可
*/
ret = misc_register(miscbeep.beep_miscdev);
if(ret < 0){
printk("misc device register failed!\r\n");
return -EFAULT;
}
return 0;
failed_findnode:
return ret;
}
static int miscbeep_remove(struct platform_device *dev){
printk("miscbeep_exit!\r\n");
/* 注销设备的时候关闭蜂鸣器 */
gpio_set_value(miscbeep.beep_gpio, 1);
/* 注销 misc 设备驱动 */
misc_deregister(miscbeep.beep_miscdev);
return 0;
}
struct of_device_id miscbeep_of_match[] = {
{.compatible = "alientek,beep"},
{/* Sentinel */},
};
struct platform_driver miscbeep_driver = {
.driver ={
.name = "imx6ul-beep", /*无设备树的时候,会和设备里面platform_device->driver->name匹配*/
.of_match_table = miscbeep_of_match, /*设备树匹配表*/
},
.probe = miscbeep_probe,
.remove = miscbeep_remove,
};
/*驱动加载函数*/
static int __init miscbeep_init(void){
/*注册platform驱动*/
platform_driver_register(&miscbeep_driver);
return 0;
}
/*驱动卸载函数*/
static void __exit misbeep_exit(void){
platform_driver_unregister(&miscbeep_driver);
}
/*驱动入口和出口*/
module_init(miscbeep_init);
module_exit(misbeep_exit);
/*协议*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZYC");
应用代码:
#include
#include
#include
#include
#include
#include
/*
*argc:应用程序参数个数
*argv[]文件参数名,字符串形式
*./beepApp /dev/led 0 关更名器
*./beepApp /dev/led 1 开蜂鸣器
*/
#define LEDOFF 0
#define LEDON 1
int main(int argc, int *argv[])
{
int fd, retval;
char *filename;
unsigned char databuf[1];
if(argc != 3){
printf("Error use\r\n");
return -1;
}
/*保存设备文件名*/
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0){
printf("open file failed \r\n");
return -1;
}
/*将字符转化为数字*/
databuf[0] = atoi(argv[2]);
retval = write(fd, databuf, sizeof(databuf));
if(retval < 0){
printf("led write failed\r\n");
close(fd);
return -1;
}
close(fd);
return 0;
}