基于Tiny4412的Linux按键输入子系统驱动的实现(一)

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 03:38   11   0

本文主要包含的章节:
一、前期的准备工作
二、Linux输入子系统的简单介绍
三、基于输入子系统的按键驱动的实现


一、前期的准备工作
1、基本的开发环境

交叉开发环境 : Ubuntu12.04
Linux内核版本 : Linux-3.0.86
GUI系统 : Qtopia2.2.0
开发板 : 友善之臂的Tiny4412(Cortex-A9)
2、内核的配置
由于默认的内核在初始化时已经将按键对应的GPIO口添加到平台设备当中,每当内核启动的时候这几个按键都会自动去请求中断,导致中断被占用。所以在编写自己的按键输入子系统驱动之前,要注释掉这个平台设备。
这个平台设备对应的代码在内核中位置是:arch\arm\mach-exynos\mach-tiny4412.c,对应的行数为 :2968,代码如下所示:

static struct platform_device tiny4412_input_device = {
 .name = GPIO_EVENT_DEV_NAME,
 .id  = 0,
 .dev = {
  .platform_data = &tiny4412_input_data,
 },
};
对这个文件进行修改,将 mach-tiny4412.c的3315行注释掉即可。


二、Linux输入子系统的简单介绍
1、输入子系统

常见的输入设备有按键、键盘、鼠标、触摸屏等,它们的工作原理虽然都各不相同,但是工作机制却是相似的。一般来讲都是通过触发中断,然后让CPU来读取键值、坐标值等。显然,在这些工作中,只有中断、读键值、坐标值等是设备相关的,而输入事件的缓冲区管理以及字符设备驱动的VFS(Virtual File System)接口对输入设备是通用的。所以,内核把这些输入事件相似的地方给抽象了出来,设计出了输入子系统。
2、核心函数
核心函数在内核的位置:drivers\input\input.c 和 include\linux\input.h
2.1 分配、释放一个输入设备

struct input_dev *input_allocate_device(void)
void input_free_device(struct input_dev *dev)
2.2 注册、注销输入设备

int input_register_device(struct input_dev *dev)
void input_unregister_device(struct input_dev *dev)
2.3报告输入事件的接口

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
/* 报告键值 */
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value)
/* 报告相对坐标 */
static inline void input_report_rel(struct input_dev *dev, unsigned int code, int value)
/* 报告绝对坐标 */
static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
/* 报告同步事件 */
static inline void input_sync(struct input_dev *dev)
3、输入子系统驱动的一般编写步骤
a、分配一个input_dev结构体
b、设置input_dev结构体 : 支持哪类事件, 支持该类事件中的那些事件
c、注册input_dev结构体
d、硬件相关的操作 : 中断申请,定时器的设置等

三、基于输入子系统的按键驱动的实现
3.1 Tiny4412开发板上的按键电路原理图


通过对原理图进行分析,可以看出当按键松开的时候IO口对应的是高电平,当按键按下的时候对应的是低电平。按键对应的GPIO是 :
* XEINT26----GPX3_2

* XEINT27----GPX3_3

* XEINT28----GPX3_4

* XEINT29----GPX3_5
如下图所示:

3.2 按键输入子系统驱动的编写
3.2.1 分配一个名为buttons_dev的input_dev结构体

buttons_dev = input_allocate_device();
if(!buttons_dev)
{
 printk("input_allocate_device error!\n");
 return -ENOMEM;
}
3.2.2 设置buttons_dev结构体

/* 2.1、支持哪类事件 */
set_bit(EV_KEY, buttons_dev->evbit);
set_bit(EV_REP, buttons_dev->evbit);

/* 2.2、支持该类事件中的那些事件 */
for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
{
 set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
}
3.2.3 注册这个结构体

input_register_device(buttons_dev);
3.2.4 硬件相关的操作

/* 4、硬件相关的操作 
*   为每个按键申请一个中断,共用中断处理函数
*   按键触发方式为双边沿触发
*/
for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
{
 irq = gpio_to_irq(buttons_desc[i].gpio);
 request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]); 
}
在硬件操作的核心是请求中断,中断的核心是 yl_buttons_irq 这个函数,这个函数的代码如下所示:

/* 按键中断处理程序 */
static irqreturn_t yl_buttons_irq(int irq, void *devid)
{
 struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
 int pinval = gpio_get_value(buttons_desc->gpio);

 if(pinval == 1) /* 判断按键是按下还是松开 */
 {
  /* 松开 */
  input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
  input_sync(buttons_dev);
 }
 else
 {
  /* 按下 */
  input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
  input_sync(buttons_dev);
 }
 
 return IRQ_HANDLED;
}
当发生按键中断时将触发按键中断程序,根据获得的对应按键的值来确定按键是按下还是松开,然后分别进行事件上报和事件同步。


附录:本节实现的按键输入子系统的完整代码如下所示。
/* 包含的头文件 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>

#include <mach/map.h>
#include <mach/gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

/* 输入子系统需要的头文件 */
#include <linux/input.h>

/** 按键映射:
  *  XEINT26  ---- GPX3_2
  *  XEINT27  ---- GPX3_3
  *  XEINT28  ---- GPX3_4
  *  XEINT29  ---- GPX3_5
  */

/* 定义一个结构体用来对输入按键进行描述 */
struct yl_buttons_desc{
 int  gpio;  // 表示对于的按键的引脚
 char *name;  // 表示对应的按键请求中断时的中断名
 int  key_code; // 表示按键在输入子系统中对应的键值
};

/* 定义一个描述按键的数组 */
static struct yl_buttons_desc buttons_desc[] = {
  {EXYNOS4_GPX3(2), "yl_buttons_L",     KEY_L},
  {EXYNOS4_GPX3(3), "yl_buttons_S",    KEY_S},
  {EXYNOS4_GPX3(4), "yl_buttons_ENTER",    KEY_ENTER},
  {EXYNOS4_GPX3(5), "yl_buttons_LEFTSHIFT", KEY_LEFTSHIFT},
};

/* 定义一个输入子系统的结构体指针变量 */
static struct input_dev *buttons_dev;

/* 按键中断处理程序 */
static irqreturn_t yl_buttons_irq(int irq, void *devid)
{
 struct yl_buttons_desc *buttons_desc = (struct yl_buttons_desc *)devid;
 int pinval = gpio_get_value(buttons_desc->gpio);

 if(pinval == 1) /* 判断按键是按下还是松开 */
 {
  /* 松开 */
  input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 0);
  input_sync(buttons_dev);
 }
 else
 {
  /* 按下 */
  input_event(buttons_dev, EV_KEY, buttons_desc->key_code, 1);
  input_sync(buttons_dev);
 }
 
 return IRQ_HANDLED;
}

/* 入口函数 */
static int __init yl_buttons_init(void)
{
 int irq;
 int i;

 /* 1、分配一个input_dev结构体 */
 buttons_dev = input_allocate_device();
 if(!buttons_dev)
 {
  printk("input_allocate_device error!\n");
  return -ENOMEM;
 }

 /* 2、设置input_dev结构体 */
 /* 2.1、支持哪类事件 */
 set_bit(EV_KEY, buttons_dev->evbit);
 set_bit(EV_REP, buttons_dev->evbit);

 /* 2.2、支持该类事件中的那些事件 */
 for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
 {
  set_bit(buttons_desc[i].key_code, buttons_dev->keybit);
 }

 /* 3、注册input_dev结构体 */
 input_register_device(buttons_dev);

 /* 4、硬件相关的操作 
  *   为每个按键申请一个中断,共用中断处理函数
  *   按键触发方式为双边沿触发
  */
 for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
 {
  irq = gpio_to_irq(buttons_desc[i].gpio);
  request_irq(irq, yl_buttons_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, buttons_desc[i].name, (void*)&buttons_desc[i]); 
 }
 
 return 0;
}

/* 出口函数 */
static void __exit yl_buttons_exit(void)
{
 int irq;
 int i;

 /* 释放申请的按键中断 */
 for(i = 0; i < sizeof(buttons_desc)/sizeof(buttons_desc[0]); i++)
 {
  irq = gpio_to_irq(buttons_desc[i].gpio);
  free_irq(irq, (void*)&buttons_desc[i]); 
 }

 input_unregister_device(buttons_dev);
 input_free_device(buttons_dev);
}

module_init(yl_buttons_init);
module_exit(yl_buttons_exit);

MODULE_LICENSE("GPL");








分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP