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;
}

效果:

相关推荐
dlz08367 小时前
POE驱动开发流程
驱动开发
嵌入式-老费9 小时前
Linux camera驱动开发(DVP接口的camera sensor)
驱动开发
VernonJsn1 天前
visual studio 2022的windows驱动开发
ide·驱动开发·visual studio
嵌入式郑工1 天前
RK3566 LubanCat 开发板 USB Gadget 配置完整复盘
linux·驱动开发·ubuntu
雾削木3 天前
树莓派 ESPHome 固件编译与烧录全攻略(解决超时与串口识别问题)
驱动开发
春日见3 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
DarkAthena4 天前
【GaussDB】手动编译不同python版本的psycopg2驱动以适配airflow
驱动开发·python·gaussdb
松涛和鸣4 天前
DAY66 SPI Driver for ADXL345 Accelerometer
linux·网络·arm开发·数据库·驱动开发
嵌入式郑工5 天前
# RK3576 平台 RTC 时钟调试全过程
linux·驱动开发·ubuntu
GS8FG5 天前
针对Linux,RK3568平台下,I2C驱动的一点小小的领悟
linux·驱动开发