设备树
给gpio添加中断功能
驱动代码
c
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
//#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#define KEY_CNT 1 /* 设备号个数 */
#define KEY_NAME "irqkey" /* 名字 */
enum key_status{
KEY_PRESS = 0, // 按下
KEY_RELEASE, // 松开
KEY_KEEP, // 保持
};
struct key_dev{
dev_t devid; // 设备号
struct cdev cdev; // cdev
struct class* class; // 类
struct device* device; // 设备
struct device_node* nd; // 设备节点
int key_gpio; // gpio编号
struct timer_list timer; //
int irq_num; // 中断号
spinlock_t spinlock; //自旋锁
};
static struct key_dev key; // 设备
static int status = KEY_KEEP; // 状态
static void key_timer_function(struct timer_list* arg){ // 定时器处理函数
static int last_val = 0;
unsigned long flags;
int current_val;
spin_lock_irqsave(&key.spinlock, flags); // 上锁
current_val = gpio_get_value(key.key_gpio); // 读取按键值
if(1 == current_val && !last_val) // 按下
status = KEY_PRESS;
else if(0 == current_val && last_val)
status = KEY_RELEASE;
else
status = KEY_KEEP;
last_val = current_val;
spin_unlock_irqrestore(&key.spinlock, flags); // 解锁
}
/*解析设备树*/
static int key_parse_dt(void){
int ret;
const char* str;
key.nd = of_find_node_by_path("/key");
if(key.nd == NULL){
printk("key node not find \n");
return -1;
}
ret = of_property_read_string(key.nd, "status", &str);
if(ret < 0)
return -1;
if(strcmp(str, "okay"))
return -1;
ret = of_property_read_string(key.nd, "compatible", &str);
if(ret < 0){
printk("key: failed to get compatible property\n");
return -1;
}
if(strcmp(str, "alientek,key")){
printk("key: compatible match failed\n");
return -1;
}
key.key_gpio = of_get_named_gpio(key.nd, "key-gpio", 0);
if(key.key_gpio < 0){
printk("cant get key-gpio");
return -1;
}
key.irq_num = irq_of_parse_and_map(key.nd, 0); // 获取gpio对应中断号
if(!key.irq_num){
return -1;
}
printk("key-gpio num: %d\n", key.key_gpio);
return 0;
}
static irqreturn_t key_interrupt(int irq, void* dev_id){ // 中断处理函数
mod_timer(&key.timer, jiffies + msecs_to_jiffies(15));
return IRQ_HANDLED;
}
static int key_gpio_init(void){
int ret;
unsigned long irq_flags;
ret = gpio_request(key.key_gpio, "KEY0");
if(ret){
printk("failed to request key-gpio\n");
return ret;
}
gpio_direction_input(key.key_gpio); // 输入模式
irq_flags = irq_get_trigger_type(key.irq_num);
if(irq_flags == IRQF_TRIGGER_NONE)
irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
ret = request_irq(key.irq_num, key_interrupt, irq_flags, "key0_IRQ", NULL); // 申请中断
if(ret){
gpio_free(key.key_gpio);
return ret;
}
return 0;
}
static int key_open(struct inode* inode, struct file* filp){
return 0;
}
static ssize_t key_read(struct file* filp, char __user* buf, size_t cnt, loff_t* offt){
unsigned long flags;
int ret;
spin_lock_irqsave(&key.spinlock, flags);
ret = copy_to_user(buf, &status, sizeof(int));
status = KEY_KEEP;
spin_unlock_irqrestore(&key.spinlock, flags);
return ret;
}
static ssize_t key_write(struct file* filp, const char __user* buf, size_t cnt, loff_t* offt){
return 0;
}
static int key_release(struct inode* inode, struct file* filp){
return 0;
}
static struct file_operations key_fops = {
.owner = THIS_MODULE,
.open = key_open,
.read = key_read,
.write = key_write,
.release = key_release,
};
static int __init mykey_init(void){
int ret;
spin_lock_init(&key.spinlock); // 初始化自旋锁
timer_setup(&key.timer, key_timer_function, 0); // 初始化timer
ret = key_parse_dt(); // 设备树解析
if(ret) return ret;
ret = key_gpio_init(); // 中断初始化
if(ret) return ret;
/*注册字符设备驱动*/
// 1、创建设备号
ret = alloc_chrdev_region(&key.devid, 0, KEY_CNT, KEY_NAME); // 申请设备号
if(ret < 0){
printk("alloc_chrdev_region failed");
goto free_gpio;
}
// 2.初始化cdev
key.cdev.owner = THIS_MODULE;
cdev_init(&key.cdev, &key_fops);
ret = cdev_add(&key.cdev, key.devid, KEY_CNT);
if(ret < 0) goto del_unregister;
// 创建类
key.class = class_create(THIS_MODULE, KEY_NAME);
if(IS_ERR(key.class)){
goto del_cdev;
}
// 创建设备
key.device = device_create(key.class, NULL, key.devid, NULL, KEY_NAME);
if(IS_ERR(key.device)){
goto destory_class;
}
return 0;
destory_class:
class_destroy(key.class);
del_cdev:
cdev_del(&key.cdev);
del_unregister:
unregister_chrdev_region(key.devid, KEY_CNT);
free_gpio:
free_irq(key.irq_num, NULL);
gpio_free(key.key_gpio);
return -1;
}
static void __exit mykey_exit(void){
// 注销字符设备驱动
cdev_del(&key.cdev); // 删除cdev
unregister_chrdev_region(key.devid, KEY_CNT); // 注销设备号
del_timer_sync(&key.timer); // 删除timer
device_destroy(key.class, key.devid); // 注销设备
class_destroy(key.class); // 注销类
free_irq(key.irq_num, NULL); // 释放中断
gpio_free(key.key_gpio); // 释放io
}
module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Narnat");
设备树中给gpio口配置的是上升沿和下降沿都会触发中断,驱动中key_interrupt为中断处理函数,中断产生后会进入这个函数做延时操作mod_timer是定时器操作,定时结束后会进入,驱动中注册好的定时器处理函数key_timer_function 在这里读取按键值key_read 将值发送给用户
应用层代码
c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]){
int fd, ret;
int key_val;
if(argc != 2){
printf("input number error!\n");
return -1;
}
fd = open(argv[1], O_RDONLY);
if(fd < 0){
printf("open file error\n");
return -1;
}
printf("fd: %d \n", fd);
while(1){
read(fd, &key_val, sizeof(int));
if(key_val == 0){
printf("key press\n");
}else if(key_val == 1){
printf("key release\n");
}
}
close(fd);
return 0;
}
效果