Rk3568驱动开发_阻塞IO_15

需求

驱动中应用层循环读取按键状态耗费太多cpu资源,期望在按键未被操作时以阻塞形式让程序休眠,待按键被操作时,唤醒读取数据给应用层。

驱动代码:

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		"blockirqkey"	/* 名字 		*/

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;             // 中断号
  atomic_t status;         // 按键状态
  wait_queue_head_t r_wait; // 读等待队列头
};

static struct key_dev key;  // 设备

static void key_timer_function(struct timer_list* arg){   // 定时器处理函数
  static int last_val = 0;
  int current_val;

  current_val = gpio_get_value(key.key_gpio); // 读取按键值
  if(1 == current_val && !last_val){  // 按下
    atomic_set(&key.status, KEY_PRESS);
    wake_up_interruptible(&key.r_wait);  // 唤醒r_wait队列头中的所有队列
  }
  else if(0 == current_val && last_val){ // 松开
    atomic_set(&key.status, KEY_RELEASE);
    wake_up_interruptible(&key.r_wait); // 唤醒
  }
  else 
    atomic_set(&key.status, KEY_KEEP); // 状态保持

  last_val = current_val;

}

/*解析设备树*/
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){
  
  int ret;
  // 加入队列,当有按键按下或松开动作发生才会被唤醒
  printk("read wait\n");
  ret = wait_event_interruptible(key.r_wait, KEY_KEEP != atomic_read(&key.status));
  printk("read release\n");
  if(ret) 
    return ret;
  // 发送数据给应用层序
  ret = copy_to_user(buf, &key.status, sizeof(int));
  atomic_set(&key.status, KEY_KEEP);
  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;
  
  // 初始化等待队列头
  init_waitqueue_head(&key.r_wait);
  timer_setup(&key.timer, key_timer_function, 0); // 初始化timer
  // 初始化按键状态
  atomic_set(&key.status, KEY_KEEP);

  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");

按键未被操作ret = wait_event_interruptible(key.r_wait, KEY_KEEP != atomic_read(&key.status));会休眠程序,按键被操作后触发上升沿或下降沿,进入定时器、进入中断后唤醒程序,将键值发送给用户

应用层:

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));
    printf("keyApp key_value: %d\n", key_val);
    if(key_val == 0){
      printf("key press\n");
    }else if(key_val == 1){
      printf("key release\n");
    }
  }
  close(fd);
  return 0;
}

效果:

相关推荐
sukalot14 小时前
window显示驱动开发—BGRA 扫描输出支持
驱动开发
专一的咸鱼哥15 小时前
Linux驱动开发(platform 设备驱动)
linux·运维·驱动开发
牧以南歌〆3 天前
在Ubuntu主机中修改ARM Linux开发板的根文件系统
linux·arm开发·驱动开发·ubuntu
Narnat3 天前
Rk3568驱动开发_中断_14
驱动开发
mmoyula4 天前
【RK3568 驱动开发:实现一个最基础的网络设备】
android·linux·驱动开发
站在巨人肩膀上的码农5 天前
全志T507 音频ALSA核心层注册流程分析
驱动开发·音视频·安卓·全志·alsa·声卡
车载操作系统---攻城狮5 天前
[驱动开发篇] Can通信快速入门手册 - 应用篇
驱动开发
Natsume17107 天前
嵌入式开发:GPIO、UART、SPI、I2C 驱动开发详解与实战案例
c语言·驱动开发·stm32·嵌入式硬件·mcu·架构·github
S,D7 天前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程