Linux 驱动——Button8(输入子系统)

输入子系统由驱动层、输入子系统核心、事件处理层三部分组成。一个输入事件,如鼠标移动、键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序。

其中核心层提供一些设备层与事件层公用的函数,比如说注册函数、反注册函数、事件到来的处理函数等等;事件层其实在Linux内核里面已经帮我们写好了很多有关的事件;而设备层就跟我们新添加到输入系统的具体设备相关了。这里以JZ2440开发板上的4个按键作为输入子系统的按键:它定义的功能分别为:KEY_L、KEY_S、KEY_ENTER、KEY_LEFTSHIFT。这几个值是在include\linux\input.h中被定义的。

button_drv.c文件:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <asm/io.h> 
#include <asm/uaccess.h>
#include <linux/device.h>/
#include <asm/arch/regs-gpio.h>
#include <linux/irq.h>
#include <asm-arm/irq.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm-generic/errno-base.h>
#include <linux/input.h>

struct pin_desc
{
  char * name;            //名称
  unsigned int pin;       //管脚定义
  unsigned int irq;       //中断号
  unsigned int key_val;   //按键值
};

static struct pin_desc pins_desc[4] = 
{
  {"S1",S3C2410_GPF0,IRQ_EINT0,KEY_L},
  {"S2",S3C2410_GPF2,IRQ_EINT2,KEY_S},
  {"S3",S3C2410_GPG3,IRQ_EINT11,KEY_ENTER},
  {"S4",S3C2410_GPG11,IRQ_EINT19,KEY_LEFTSHIFT}
};

static struct pin_desc *pinss_desc=NULL;

static struct timer_list timer_button;                //新建一个定时器

static struct input_dev *buttons_input;               //新建一个输入子系统的设备层结构

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
  pinss_desc = (struct pin_desc *)dev_id;            //取得哪个按键被按下的状态
  mod_timer(&inputbuttons_timer, jiffies+HZ/100);    //10ms之后调用定时器处理函数
  return IRQ_HANDLED;
}

static void timer_button_timeout(unsigned long a)
{
  unsigned int pin_val;

  if(pinss_desc==NULL)
    return;
  else
  {
    pin_val = s3c2410_gpio_getpin(pinss_desc->pin);

    if(pin_val)
    {
      input_event(buttons_input,EV_KEY, pinss_desc->key_val, 0);
    }
    else
    {
      input_event(buttons_input,EV_KEY, pinss_desc->key_val, 1);
    }
  }
}

static int button_drv_init(void)
{
  unsigned char i;
  int ret;
  buttons_input = input_allocate_device();        //分配input_dev结构体
  if (!buttons_input)
    return -ENOMEM;

  set_bit(EV_KEY, buttons_input->evbit);          //按键事件
  set_bit(EV_REP, buttons_input->evbit);          //重复事件类型
  set_bit(KEY_L, buttons_input->keybit);
  set_bit(KEY_S, buttons_input->keybit);
  set_bit(KEY_ENTER, buttons_input->keybit);
  set_bit(KEY_LEFTSHIFT, buttons_input->keybit);

  input_register_device(buttons_input);           //注册设备驱动

  init_timer(&timer_button);
  inputbuttons_timer.expires = 0;
  inputbuttons_timer.function = timer_button_timeout;
  add_timer(&timer_button);
                                                                                                                                     //注意这里没有创建设备类和设备文件(设备节点)

                                                              //因为输入子系统的设备文件本来就存在为/dev/event0
  for(i=0;i<4;i++)
  {
    ret = request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, (void * )&pins_desc[i]);
    if(ret)
    {
      printk("open failed %d\n",i);
      return -(i+1);
    }
  }
  return 0;
}

static void button_drv_exit(void)
{
  unsigned char i;

  input_unregister_device(buttons_input);
  input_free_device(buttons_input);
  del_timer(&timer_button);

  for(i=0;i<4;i++)
  {
    free_irq(pins_desc[i].irq, (void * )&pins_desc[i]);
  }
}

module_init(seven_drv_init);
module_exit(seven_drv_exit);

MODULE_LICENSE("GPL");

Makefile文件:

obj-m += button_drv.o

KERN_DIR = /work/system/linux-2.6.22.6

all:
make -C $(KERN_DIR) M=`pwd` modules 
clean:
rm -rf *.o *.ko *.order *.symvers *.mod.c

button_app.c文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>

static int fd;

int main(int argc, char **argv)
{
  char* filename = argv[1];
  int oflags, ret=0;
  unsigned char key_val[16];

  fd = open(filename, O_RDWR);//打开设备文件,阻塞方式打开
  if (fd < 0)
  {
    printf("error, can‘t open %s\n", filename);
    return 0;
  }
  while(1)
  {
    ret = read(fd, key_val, 16);
    printf("ret: %d, code: %02d, value: %d \n", ret, key_val[10], key_val[12]);
  }
  return 0;
}

app_read->evdev_read->kbtab_irq->input_report_key->input_event->evdev_event->evdev_read

应用层      事件层        设备层        核心层               核心层          事件层          事件层

应用函数最终会通过事件层的evdev_event调用evdev_read

而在linux中evdev_event结构体共16字节,所以app中的read函数也应该读取16字节

编译生成button_drv.ko和button_app文件,运行./button_app /dev/event0

原文地址:https://www.cnblogs.com/lian-meng/p/10592124.html

时间: 2024-11-07 22:34:06

Linux 驱动——Button8(输入子系统)的相关文章

Linux驱动之输入子系统框架

    好记性不如烂笔头,整理一下笔记~ Linux驱动之输入子系统框架 输入子系统将该类驱动划分为3部分 1.核心层 input.c 2.设备层 Gpio_keys.c ... 3.事件处理层 Evdev.c 事件处理层为纯软件的东西,设备层涉及底层硬件,它们通过核心层建立联系,对外提供open write等接口. 1.我们首先来看,核心层 input.c如何向外界提供接口 在 input_init 中注册了字符设备驱动 register_chrdev(INPUT_MAJOR, "input&

Linux驱动之输入子系统简析

输入子系统由驱动层.输入子系统核心.事件处理层三部分组成.一个输入事件,如鼠标移动.键盘按下等通过Driver->Inputcore->Event handler->userspace的顺序到达用户控件的应用程序. 系统框图 假设打开一个字符设备驱动程序/dev/event0,event代表的是输入子系统的设备文件,当应用程序调用C库的open函数后,open函数会进入系统调用,最后定位到drivers\input\input.c文件下(这个文件就是核心层)的.这个函数的功能主要是根据设

关于linux input device输入子系统架构及android中的框架

关于linux input device输入子系统架构及android中的框架 应用app和windowmanagerservice的input event通信方式 在Native层的InputChannel就是一个通道,仅仅是一个通道,仅仅具有通信功能,不包含其他的.至于从数据流动方向,与InputChannel无关.数据流向是由InputPublisher和InputConsumer在组合了InputChannel后决定的.把InputChannel由应用程序传递到WindowManageS

Linux学习:输入子系统 input

一.输入子系统 针对输入设备设计:触摸屏.键盘.按键.传感器.鼠标...... 二.每种设备都属于字符设备驱动,程序的写法步骤也相同 1.实现入口函数 xxx_init() 和卸载函数 xxx_exit() 2.申请设备号 register_chrdev() --- 与内核相关 3.创建设备文件(节点) class_create() 和 device_create() --- 与内核相关 4.硬件初始化 GPIO操作 --- 与硬件相关 注册中断 --- 与硬件相关 初始化等待队列 --- 与内

linux驱动之i2c子系统device注册driver注册简单分析

Linux 驱动设计主要是根据分层分离思想,i2c子系统分为i2cocre.adapter.及device_driver层,其实adapter也是个device,只不过是我们主控芯片的I2C控制接口而已,我们的主控芯片有几个I2C接口就有几个adapter; i2ccore这一层linux已经帮我们实现,主要的工做是类似platform总线的作用,负责drvier及设备的注册,相比platform多了个adapter的注册管理工作,以及i2c的数据发送接收等等算法,说算法有点夸大,其实就是按照i

linux驱动之i2c子系统mpu6050设备驱动

以下是mpu6050简单的驱动实现,mpu6050是I2C接口的6轴传感器,可以作为字符设备注册到内核,本代码运行环境是3.4.2内核,4.3.2版本的编译链,12.04版本的Ubuntu,硬件环境是jz2440开发板: 按照之前分析的I2C驱动框架,mpu6050驱动主要是实现外设端的驱动,主要是注册外设到I2C总线,而外设端注册到I2C总线包括device及driver两个部分注册到I2C总线,采用分离的设计思想,详情见代码: device注册到I2C总线: #include <linux/

Linux驱动之Input子系统要点分析

① Input_device与Input_handler的匹配过程 当Input_device与Input_handler->id中的位图信息全部一致,则匹配成功,然后调用Input_handler->connect函数,在该函数中创建相对应的设备访问节点 ② 事件报告过程 底层驱动程序通过调用report_xxx函数向上层报告输入事件,经Input_core层对输入事件进行分类,并且分发到相应的Input_handle层(evdev.c tsdev.c consoledev.c moused

Linux驱动之触摸屏程序编写

本篇博客分以下几部分讲解 1.介绍电阻式触摸屏的原理 2.介绍触摸屏驱动的框架(输入子系统) 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍电阻式触摸屏的原理 所谓的电阻式触摸屏,只不过是在LCD屏幕上贴了一层膜,这层膜的大小与LCD的尺寸刚好相同,它分为上下两层膜(假设上层为X膜,下层为Y膜),按下膜的不同位置,会产生不同的电压值,这样根据不同的电压值可以确定触点的位置,这就是触摸屏的基本原理.其实是利用了最简单的电阻分压原理. 下面的图是四线式电阻触摸

Linux输入子系统框架分析(1)

在Linux下的输入设备键盘.触摸屏.鼠标等都可以用输入子系统来实现驱动.输入子系统分为三层,核心层和设备驱动层,事件层.核心层和事件层由Linux输入子系统本身实现,设备驱动层由我们实现.我们在设备驱动层将输入事件上报给核心层input.c,核心层找到匹配的事件层,将事件交给事件层处理,事件层处理完后传递到用户空间. 我们最终要搞清楚的是在用户空间调用open和read最终在内核中是怎样处理的,向内核上报的事件又是谁处理的,处理完后是怎样传递到用户空间的? 上面两个图是输入子系统的框架. 下面