一、MISC驱动简介
MISC 驱动叫杂项驱动,当外设无法分类时,可以使用 MISC 驱动。
所有的 MISC 驱动的主设备号都是 10,不同的设备使用不同的次设备号。MISC设备会自动创建 cdev。只需要注册 MISC 结构体就能完成驱动的设备号处理到自动创建设备节点。
1、miscdevice结构体
struct miscdevice {
int minor; /* 子设备号 */
const char *name; /* 设备名字,在/dev目录下的设备名字 */
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 这三个成员。
linux 系统预设了一些 MISC 设备的次设备号,定义在 include/linux/miscdevice.h文件。
2、misc_register函数
设置好 miscdevice 结构体以后就要使用 misc_register 函数来注册 MISC 设备,函数原型如下:
int misc_register(struct miscdevice * misc)
misc:要注册的 miscdevice 结构体。
返回值:负值,失败;0,成功。
3、misc_deregister函数
卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:
int misc_deregister(struct miscdevice *misc)
misc:要注销的 miscdevice 结构体。
返回值:负值,失败;0,成功。
二、MISC驱动实验
1、添加设备树节点
dtsbeep {
compatible = "beep_test";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&beep>;
gpios = <&gpio5 1 GPIO_ACTIVE_HIGH>;
};
beep: beepgrp {
fsl,pins = <
MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01 0x10b0
>;
};
2、添加设备结构体
/* 设备结构体 */
typedef struct {
int beep_gpio; //gpio编号
struct device_node *nd; //设备树节点
}beep_dev;
beep_dev beep;
3、编写加载和卸载注册函数
/* 模块注册函数 */
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LZK");
入口函数:
/* 入口函数 */
static int __init beep_init(void)
{
int ret = 0;
ret = platform_driver_register(&platform_beep);
return 0;
}
出口函数:
/* 出口函数 */
static void __exit beep_exit(void)
{
platform_driver_unregister(&platform_beep);
}
4、编写操作函数
/* open函数 */
static int beep_open(struct inode *inode, struct file *filp)
{
return 0;
}
/* write函数 */
static ssize_t beep_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int ret = 0;
int beep_state = BEEP_OFF;
ret = copy_from_user(&beep_state, buf, count);
if(beep_state == BEEP_ON)
beep_switch(BEEP_ON);
if(beep_state == BEEP_OFF)
beep_switch(BEEP_OFF);
return 0;
}
/* 操作函数集合 */
static const struct file_operations beep_fops = {
.owner = THIS_MODULE,
.open = beep_open,
.write = beep_write,
};
5、添加头文件
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/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/of_platform.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#define DEVICE_NAME "beep"
#define BEEP_ON 1
#define BEEP_OFF 0
6、编写BEEP切换函数
/* beep状态切换函数 */
void beep_switch(int state)
{
if(state == BEEP_ON)
gpio_set_value(beep.beep_gpio, 0);
if(state == BEEP_OFF)
gpio_set_value(beep.beep_gpio, 1);
}
7、添加MISC驱动结构体
/* misc结构体 */
static struct miscdevice misc_beep = {
.name = DEVICE_NAME,
.minor = 144,
.fops = &beep_fops,
};
8、编写probe函数
/* probe函数 */
static int beep_probe(struct platform_device *dev)
{
int ret = 0;
printk("device and driver match\r\n");
/* 注册misc驱动 */
ret = misc_register(&misc_beep);
/* 查找设备树节点 */
beep.nd = of_find_node_by_name(NULL, "dtsbeep");
/* 读取gpio编号 */
beep.beep_gpio = of_get_named_gpio(beep.nd, "gpios", 0);
/* 注册gpio */
ret = gpio_request(beep.beep_gpio, "beep_gpio");
/* 设置gpio输出 */
ret = gpio_direction_output(beep.beep_gpio, 1);
beep_switch(BEEP_OFF);
return 0;
}
9、编写remove函数
/* remove函数 */
static int beep_remove(struct platform_device *dev)
{
int ret = 0;
beep_switch(BEEP_OFF);
gpio_free(beep.beep_gpio);
/* 注销misc驱动 */
ret = misc_deregister(&misc_beep);
return 0;
}
10、编写匹配列表
/* 匹配列表 */
const struct of_device_id beep_match_table[] = {
{ .compatible = "beep_test" },
{ /* 保留 */ },
};
11、添加platform驱动结构体
/* platform驱动结构体 */
static struct platform_driver platform_beep = {
.driver = {
.name = "beep",
.of_match_table = beep_match_table,
},
.probe = beep_probe,
.remove = beep_remove,
};
12、编写测试应用
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include "linux/ioctl.h"
#include "poll.h"
#include "sys/select.h"
#include "signal.h"
#include "fcntl.h"
static int fd = 0;
int main(int argc, char *argv[])
{
int flags = 0;
char *filename;
int beep_state = 0;
if(argc != 3){
printf("missing parameter!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR); //阻塞方式打开驱动
if(fd < 0){
printf("open file %s failed\r\n", filename);
return -1;
}
if(atoi(argv[2]) == 1){
beep_state = 1;
write(fd, &beep_state, sizeof(beep_state));
}else{
beep_state = 0;
write(fd, &beep_state, sizeof(beep_state));
}
close(fd);
return 0;
}