目录
misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某
些外设无法进行分类的时候就可以使用 MISC 驱动。
一、MISC 设备驱动简介
所有的 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 文件中,在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口
name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。 fops 就是字符设备操作集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合
misc_deregister 函数
以前我们需要自己调用一堆的函数去创建和卸载设备,比如在以前的字符设备驱动中我们会使用如下几个函数完成设备创建和卸载过程:
创建
1 alloc_chrdev_region();/* 申请设备号*/
2 cdev_init(); /* 初始化 cdev */
3 cdev_add(); /* 添加 cdev */
4 class_create(); /* 创建类 */
5 device_create(); /* 创建设备 */卸载
1 cdev_del(); /* 删除 cdev */
2 unregister_chrdev_region(); /* 注销设备号 */
3 device_destroy(); /* 删除设备 */
4 class_destroy(); /* 删除类 */
现在只需要一个 misc_deregister 函数即可完成上面的操作,设置好 miscdevice结构体 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:
int misc_register(struct miscdevice * misc)
misc:要注册的 MISC 设备。返回值: 负数,失败; 0,成功
卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:
int misc_deregister(struct miscdevice *misc)
misc:要注销的 MISC 设备。返回值: 负数,失败; 0,成功。
下面开始编写
二、MISC驱动编写
在“蜂鸣器驱动”实验中已经创建好了设备树节点,可以直接使用
创建文件miscbeep.c文件和APP,修改makefile
添加头文件 #include <linux/miscdevice.h>
1、编写框架
45行,因为这里使用设备树,所以.name字段无需作匹配功能 ,如果不使用设备树,就要在编写设备的时候保证设备和驱动的name字段要一致
2、platform结构体对应的函数
39行,compatible字段的值一定要与使用的设备树节点compatible值的要一致
2、宏定义和miscbeep设备结构体
29行,设置次设备号为144
3、定义miscdevice结构体
65行,次设备号,使用宏定义
66行,设备名字,使用宏定义
67行,字符设备操作集,对应的函数需要实现
字符设备操作集
4、probe函数
75行,因为已经使用设备树匹配设备节点,所以不需要再通过路径进行搜索,直接利用
dev->dev.of_node 即可获取
76行,获取设备树中的gpio属性,得到BEEP所使用的BEEP编号
81行,申请一个 GPIO 管脚,gpio 设置名字为beep-gpio
88行,设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP
94行, 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备, 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
5、remove函数
退出的时候要把资源释放
验证
编译后把模块加载到开发板,如下
可以看到,主设备号为10,次设备号为57(设置的自动分配)
6、添加开关
添加开关宏定义,在miscbeep_write函数里面获取APP传来的数据,然后根据数据去开关beep
三、总代码
APP
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
/*
argc:应用程序参数个数(argv数组元素个数)
argv:具体参数,也可以写作char **argv
./miscbeepAPP <filename> <0:1> 0表示关,1表示开
./miscbeepAPP /dev/miscbeep 0 关
./miscbeepAPP /dev/miscbeep 1 开
*/
int main(int argc, char *argv[])
{
int fd,retvalue;
char *filename;
unsigned char databuf[1];
/*判断命令行输入参数是否正确*/
if(argc != 3){
printf("error usage!\r\n");
return -1;
}
/*用指针指向文件*/
filename = argv[1];
/*打开文件*/
fd = open(filename , O_RDWR);
if(fd < 0){
printf("file open failed\r\n",filename);
return -1;
}
/*获取控制开关的数字*/
databuf[0] = atoi(argv[2]);/*将字符转换为数字*/
/*如果输入的控制命令不是1或者0直接退出*/
if(((int)databuf[0]) < 0 || ((int)databuf[0]) >1)
{
printf("control parameter error\r\n");
close(fd);
return -1;
}
/*写入操作*/
retvalue = write(fd,databuf,sizeof(databuf));
if(retvalue < 0){
printf("LED control failed\r\n");
close(fd);
return -1;
}
/*关闭文件*/
close(fd);
return 0;
}
miscbeep.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/string.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ide.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#define MISCBEEP_NAME "miscbeep"
#define MISCBEEP_MINOR 144
#define BEEP_OFF 0
#define BEEP_ON 1
/*miscbeep设备结构体*/
struct miscbeep_dev{
struct device_node *nd;/*设备节点*/
int beep_gpio;/* beep所使用的GPIO编号*/
};
struct miscbeep_dev miscbeep;/* beep设备 */
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 count, loff_t *ppos)
{
int ret = 0;
unsigned char databuf[1];
struct miscbeep_dev *dev = filp->private_data;
ret = copy_from_user(databuf,buf,count);
if(ret < 0){
return -EINVAL;
}
if(databuf[0] == BEEP_ON){
gpio_set_value(dev->beep_gpio, 0);/*打开*/
}else if(databuf[0] == BEEP_OFF){
gpio_set_value(dev->beep_gpio,1);/*关闭*/
}
return 0;
}
static int miscbeep_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*字符设备操作集*/
struct file_operations miscbeep_fops = {
.owner = THIS_MODULE,
.open = miscbeep_open,
.write = miscbeep_write,
.release= miscbeep_release,
};
/*miscdevice结构体*/
static struct miscdevice beep_miscdev = {
.minor = MISCBEEP_MINOR,
.name = MISCBEEP_NAME,
.fops = &miscbeep_fops,
};
/*probe函数*/
static int miscbeep_probe(struct platform_device *dev)
{
int ret =0;
/*1、初始化beep的io*/
miscbeep.nd = dev->dev.of_node;
miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd,"beep-gpios",0);
if(miscbeep.beep_gpio < 0){
ret = -EINVAL;
goto fail_findgpio;
}
ret = gpio_request(miscbeep.beep_gpio,"beep-gpio");
if(ret){
printk("can't request %d gpio\r\n",miscbeep.beep_gpio);
ret = -EINVAL;
goto fail_findgpio;
}
/*输出默认为高电平*/
ret = gpio_direction_output(miscbeep.beep_gpio,1);
if(ret < 0){
ret = -EINVAL;
goto fail_setoutput;
}
/*2、misc驱动注册*/
ret = misc_register(&beep_miscdev);
if(ret < 0){
ret = -EINVAL;
goto fail_setoutput;
}
return 0;
fail_setoutput:
gpio_free(miscbeep.beep_gpio);
fail_findgpio:
return ret;
}
/*remove函数*/
static int miscbeep_remove(struct platform_device *dev)
{
/* 注销misc设备 */
misc_deregister(&beep_miscdev);
/*拉高关闭beep*/
gpio_set_value(miscbeep.beep_gpio , 1);
/*释放GPIO*/
gpio_free(miscbeep.beep_gpio);
return 0;
}
/*platform匹配表*/
static const struct of_device_id beep_of_math[] = {
{.compatible = "my,beep"},
{/* sentinel */},
};
/*platfrom*/
static struct platform_driver miscbeep_driver = {
.driver = {
.name = "imx6ull-beep",
.of_match_table = beep_of_math,/*设备树匹配表*/
},
.probe = miscbeep_probe,
.remove = miscbeep_remove,
};
/*驱动入口函数*/
static int __init miscbeep_init(void)
{
return platform_driver_register(&miscbeep_driver);
}
/*驱动出口函数*/
static void __exit miscbeep_exit(void)
{
platform_driver_unregister(&miscbeep_driver);
}
module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");
验证
主设备号为10,次设备为144,发送0就是关,1就是开;开着卸载驱动也会关