Misc 是英文单词 miscellaneous 的缩写,意为杂乱项。在 linux 下不好明确定义种类的驱动都能定义为杂乱项,比如 led,按键,adc 些设备。这些设备有着相似的特性,所以在 Linux 中可以统一被封装成 MISC 设备来提高驱动 的可读性。misc 驱动其实是一种字符驱动设备,这个驱动会代替用户完成设备号申请,字符驱动注册等一系列的流程。
简单来说就是当想开发按键,led灯等驱动时,可以使用misc驱动来简化开发流程。
按下按键button,触发中断,引起led灯状态的改变(0变1,1变0)
驱动代码 misc_drv.c
//添加头文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#define ZYNQMP_GPIO_NR_GPIOS 174
#define MIO_PIN_45 (ARCH_NR_GPIOS - ZYNQMP_GPIO_NR_GPIOS + 45)
#define MIO_PIN_17 (ARCH_NR_GPIOS - ZYNQMP_GPIO_NR_GPIOS + 17)
#define MIO_PIN_23 (ARCH_NR_GPIOS - ZYNQMP_GPIO_NR_GPIOS + 23)
static int led_value=0;
//设置一个设备全局变量
static struct misc_device
{
//dev_t devno; //设备号
//struct cdev cdev; //字符设备
//struct class *class; //设备类
//struct device *device; //设备
atomic64_t state; //原子变量,此处用作按键状态
unsigned int irq; //中断
wait_queue_head_t waitqueue; //等待队列
} misc_dev; //定义一个全局的设备结构体
//中断回调函数,此处会当按键检测到上升沿触发
static irqreturn_t key_handler(int irq, void *dev)
{
char value;
//读取按键状态,按下为1,松开为0
value = gpio_get_value(MIO_PIN_45);
if (value == 1) //按下的情况
{
//设置原子变量为1
atomic_set(&misc_dev.state, 1);
//唤醒等待队列
wake_up_interruptible(&misc_dev.waitqueue);
}
return 0;
}
static int misc_open(struct inode *inode, struct file *filp)
{
printk("-misc_open-\n");
return 0;
}
static ssize_t misc_read(struct file *filp, char __user *buf, size_t count, loff_t *fops)
{
int ret = 0, key = 0;
//添加进阻塞队列,当状态为0时休眠,为1时唤醒,由第二个参数控制
ret = wait_event_interruptible(misc_dev.waitqueue, atomic64_read(&misc_dev.state));
if (ret)
return ret;
//上面的队列被唤醒后,读取当前按键状态
key = atomic64_read(&misc_dev.state);
if(key){
led_value=1-led_value;
}
gpio_set_value(MIO_PIN_17, led_value);
gpio_set_value(MIO_PIN_23, led_value);
//发送到用户空间
ret = copy_to_user(buf, &led_value, sizeof(led_value));
if (ret)
return ret;
//将状态置0,等待下一次中断
atomic_set(&misc_dev.state, 0);
return 0;
}
static int misc_close(struct inode *inode, struct file *filp)
{
printk("-misc_close-\n");
return 0;
}
static struct file_operations misc_fops = {
.open = misc_open,
.read = misc_read,
.release = misc_close,
};
static struct miscdevice miscdev = {
.minor = 11, //次设备号
.name = "misc_device", //设备名称
.fops = &misc_fops, //文件操作描述
};
//实现装载入口函数和卸载入口函数
static __init int misc_drv_init(void)
{
int ret = 0;
printk("-misc_drv_init start-\n");
/* 注册 misc 设备 */
ret = misc_register(&miscdev);
if (ret < 0)
{
printk("misc_register failed.\n");
return ret;
}
//MIO_PIN_45申请GPIO口
ret = gpio_request(MIO_PIN_45, "key");
if (ret < 0)
{
printk("gpio request key error!\n");
return ret;
}
//GPIO口方向设置成输入
ret = gpio_direction_input(MIO_PIN_45);
if (ret != 0)
{
printk("gpio direction input MIO_PIN_45 fail!\n");
}
//MIO_PIN_17申请GPIO口
ret = gpio_request(MIO_PIN_17, "led0");
if (ret < 0)
{
printk("gpio request led0 error!\n");
return ret;
}
//GPIO口方向设置成输出
ret = gpio_direction_output(MIO_PIN_17,0);
if (ret != 0)
{
printk("gpio direction input MIO_PIN_45 fail!\n");
}
//MIO_PIN_23申请GPIO口
ret = gpio_request(MIO_PIN_23, "led1");
if (ret < 0)
{
printk("gpio request led1 error!\n");
return ret;
}
//GPIO口方向设置成输出
ret = gpio_direction_output(MIO_PIN_23,0);
if (ret != 0)
{
printk("gpio direction input MIO_PIN_45 fail!\n");
}
//将原子变量置0,相当于初始化
atomic64_set(&misc_dev.state, 0);
//申请中断号
misc_dev.irq = gpio_to_irq(MIO_PIN_45);
//设置中断方式
ret = request_irq(misc_dev.irq, key_handler, IRQF_TRIGGER_RISING, "key", NULL);
if (ret < 0)
{
printk("request_irq error!\n");
return ret;
}
//初始化等待队列头
init_waitqueue_head(&misc_dev.waitqueue);
printk("-misc_drv_init finish-\n");
return 0;
}
static __exit void misc_drv_exit(void)
{
printk("-misc_drv_exit start-\n");
//释放中断
free_irq(misc_dev.irq, NULL);
//释放按键GPIO
gpio_free(MIO_PIN_45);\
//释放led
gpio_free(MIO_PIN_17);
gpio_free(MIO_PIN_23);
misc_deregister(&miscdev);
printk("-misc_drv_exit finish-\n");
}
//申明装载入口函数和卸载入口函数
module_init(misc_drv_init);
module_exit(misc_drv_exit);
//添加GPL协议
MODULE_LICENSE("GPL");
MODULE_AUTHOR("msxbo");
应用代码 misc_app.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd = 0;
int i = 0;
int a = 0;
fd = open("/dev/misc_device", O_RDWR);
if (fd < 0)
{
perror("open fail!\n");
exit(1);
}
while (1)
{
//持续打印led灯状态
read(fd, &a, sizeof(a));
printf("led value %d\n", a);
}
close(fd);
return 0;
}
Makefile
#已经编译过的内核源码路径
KERNEL_DIR = /home/uisrc/uisrc-lab-xlnx/sources/kernel
export ARCH=arm64
export CROSS_COMPILE=aarch64-linux-gnu-
#当前路径
CURRENT_DIR = $(shell pwd)
MODULE = misc_drv
APP = misc_app
all :
#进入并调用内核源码目录中Makefile的规则, 将当前的目录中的源码编译成模块
make -C $(KERNEL_DIR) M=$(CURRENT_DIR) modules
rm -rf *.mod.c *.mod.o *.symvers *.order *.o
ifneq ($(APP), )
$(CROSS_COMPILE)gcc $(APP).c -o $(APP)
endif
clean :
make -C $(KERNEL_DIR) M=$(CURRENT_DIR) clean
rm $(APP)
#指定编译哪个文件
obj-m += $(MODULE).o
交叉编译上板执行,按下按键会使led灯由熄灭变为点亮,再次按下会从点亮变为熄灭,如此反复。
本文章使用limfx的vscode插件快速发布