linux内核空间向用户空间传递信息之输入子系统和内核定时器的使用

一、是什么?

输入子系统
linux内核的输入子系统为鼠标键盘触摸屏和游戏杆等输入设备提供了驱动框架。
输入事件如何处理,用户接口如果实现,都由输入子系统完成。
(1)设备驱动
设备信息:
(1)设备总线类型,厂商,产品,版本号,名称等身份信息;
(2)设备可产生的事件类型.
(3)各事件类型的分量
当输入设备发生输入事件时,驱动程序要把输入事件向输入子系统报告.
事件管理器
事件类设备驱动(evdev事件管理器)
鼠标类设备驱动(mousedev事件管理器)
游戏杆类设备驱动(joydev事件管理器)

evdev
通用的输入事件接口
drivers/input/evdev.c 
event0 event1 event2 event3 

设备驱动
设备驱动由input_dev的数据结构描述
struct input_dev
{
  const char*name;
  const char*phys;
  const char*uniq;
  struct input_id id;
  unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
  unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];  //
  unsigned long relbit[BITS_TO_LONGS(REL_CNT)];
  unsigned long absbit[BITS_TO_LONGS(ABS_CNT)]unsigned long mscbit[BITS_TO_LONGS(LED_CNT)];
  unsigned long ledbit[BITS_TO_LONGS(SND_CNT)];
  unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
  unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
  unsigned int keycodemax;
  unsigned int keycodesize;
  void*keycode;
  int(*setkeycode)(struct input_dev*dev,unsigned int scancode,unsigned
  int keycode);
  int(*getkeycode)(struct input_dev*dev,unsigned int scancode,unsigned int*keycode);
  struct ff_device*ff;
  unsigned int repeat_key;
  struct timer_list timer;
};

name:该成员表示设备驱动的名字  
id:表面输入设备身份,描述了输入设备总线类型,产商号,版本信息.使用evdev事件管理器
,而evdev事件管理器则不需要设备驱动初始化id成员.
evbit:该成员描述输入设备会产生的输入事件类型
按键 用来接收外部事件 

#define EV_SYN  0x00   
#define EV_KEY  0x01 
#define EV_REL  0x02 
#define EV_ABS  0x03 
#define EV_MSC  0x04 
#define EV_SW   0x05
#define EV_LED  0x11 
#define EV_SND  0x12 
#define EV_REP  0x14 
#define EV_FF   0x15 
#define EV_PWR  0x16 
#define EV_FF_STATUS  0x17 

set_bit(nr,addr);
将指定位置1:
set_bit(EV_KEY,input_dev->evbit);
keybit:支持按键的键值
#define KEY_RESERVED 0 
#define KEY_ESC  1 
#define KEY_1 2 
#define KEY_2 3 
#define KEY_3 4 
#define KEY_4 5 
...
#define KEY_MINUS 12 
#define KEY_EQUAL 13 
#define KEY_BACKSPACE 14 
#define KEY_TAB 15 
#define KEY_Q 16 
#define KEY_W 17 
#define KEY_E 18
#define KEY_R 19 
#define KEY_T 20 
..
注册设备驱动/注销设备驱动
int input_register_device(struct intput_dev*);
void input_unregister_device(struct input_dev*);

输入事件的提交:
(1)提交任何类型的事件
void intput_event(struct intput_dev*dev,unsigned int type,
unsigned int code,int value);
dev参数为设备驱动 type为输入事件的类型 code为具体类型的分量  
value为事件的值  input_event()函数可以提交任何类型的事件,但实践使用中用事件提交数  
(2)提交按键事件
void  intput_report_key(struct intput_dev*dev,unsigned int code,int value);
code 为按键的键值 value(1为按下 0 为谈起)
intput_report_key(dev,KEY_A,1);
(3)提交绝对坐标事件
void intput_report_abs(struct intput_dev*dev unsigned int code,int value);
intput_report_abs(dev,ABS_X,value);//报告X轴坐标值
(4)提交相对坐标值
void input_report_rel(struct intput_dev*dev,unsigned int code,int value);
intput_report_rel(dev,REL_X,1);
intput_report_rel(dev,REL_X,-1);  X轴坐标减小
(5)输入同步事件
任何一个输入事件都是以同步事件结束,提交同步事件可以如下
void intput_sync(struct intput_dev*dev);本质是间接唤醒了等待队列evdev->wait的功能.


struct intput_event
{
  struct timeval time;
  __u16 type;
  __u16 code;
  __s32 value;
};

内核定时器:
软件意义上的定时器最终依赖硬件定时器实现,内核在时钟中断发生后检测各定时器
是否到期,到期后定时器处理函数将作为软中断在底半步执行.实质上,时钟中断处理
程序会唤起TIMER_SOFTIRQ软中断,运行当前处理器到期的所有定时器.
在linux设备驱动编程中,可以用linux内核中提供一组函数和数据结构来完成定时触发
工作或者某周期的事务.
(1)timer_list 
struct timer_list
{
  struct list_head entry;
  unsigned long expires;  //到期的时间
  struct tvec_base*base;
  void(*function)(unsigned long long);  //定时器满   
  unsigned long long data;
  int slack;
  
};

定义一个定时器
struct timer_list my_timer;
初始化定时器
void init_timer(struct timer_list*timer);
增加定时器
void add_timer(struct timer_list*timer);   //只有注册这个定时器 定时器才操作
删除定时器
int del_timer(struct timer_list*timer);
修改定时器expire
int mod_timer(struct timer_list*timer,unsigned long expires);

dev->xx_timer.data = (unsigned long)dev;
dev->xx_timer.expires = jiffies+delay; // 表示1s jiffies

setup_timer() 用于初始化定时器并赋值其成员
#define __setup_timer(_timer,_fn,_data,_flags)
   do
   {
      __init_timer((_timer),(__flags));
	  (timer)->function = (_fn),
	  (timer)->data = (_data),
   }while(0);   

延时2s :
unsigned long delay = jiffies +2*HZ 
while(time_before(jiffies,delay));
#include<linux/input.h>
#include <linux/timer.h>


cat /proc/bus/input/devices
struct input_id {
	//总线类型
	__u16 bustype;
	//与厂商相关ID
	__u16 vendor;
	//与产品相关ID
	__u16 product;
	//版本ID
	__u16 version;
};
KEY表示:此设备具有按钮
MSC表示:此设备支持其他的事件
hexdump /dev/input/event5 
通过加载/卸载也可只是是哪个event接口


二、使用步骤

1.引入库

代码如下(示例):

#include<linux/module.h>
#include<linux/fs.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/uaccess.h>
#include<linux/device.h>
#include<linux/wait.h>
#include<linux/poll.h>
#include<linux/platform_device.h>
#include<linux/input.h>
#include <linux/timer.h>


#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/wait.h>
static int debug = 1;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debuging infomation");
#define dprintk(args...)\
        if(debug){ \
			printk(KERN_DEBUG args);\
		}
#define DEVICE_NUM  1
struct key_struct
{
  int key_code;  // 按键产生的值
  int gpio;  
};
static struct input_dev*key_input_dev;
static struct timer_list my_timer;
static int key_flag = 0;
struct key_struct  keys_list[] =
{
	{.key_code = KEY_A,0},
	{.key_code = KEY_TAB,1},
	{.key_code = KEY_C,2},
	{.key_code = KEY_C,3},
};


static void timer_work(unsigned long data)
{
	//priv->edid_delay_active = true;
	//printk("timer mer 100ms ms \n");
	if(key_flag)
	{
		input_report_key(key_input_dev,KEY_TAB,1);
		key_flag = 0;
	}
	else
	{
		input_report_key(key_input_dev,KEY_TAB,0);
		key_flag = 1;
	}
	input_sync(key_input_dev);
	mod_timer(&my_timer, jiffies+ 5*HZ);     // 一秒钟(1*HZ) 
	//printk("input_report_key \n");
}


static int global_mem_probe(struct platform_device*pdev)
{
	int i = 0;
	unsigned int code;
	key_input_dev = input_allocate_device(); //为输入设备驱动对象申请内存空间
	if(!key_input_dev)
	{
		return -ENOMEM;
	}
	key_input_dev->name = "key_name";
	key_input_dev->id.bustype = BUS_HOST;
	key_input_dev->phys = "keys/input0";
    __set_bit(EV_KEY,key_input_dev->evbit); //设置输入设备支持按键事件
	__set_bit(EV_REP,key_input_dev->evbit);
    for(i=0;i<sizeof(keys_list)/sizeof(keys_list[0]);i++) 
    {
		code = keys_list[i].key_code;
		__set_bit(code,key_input_dev->keybit);
		//__set_bit(KEY_POWER, key_input_dev->keybit);
		//__set_bit(KEY_HP, key_input_dev->keybit);
		//__set_bit(KEY_B,key_input_dev->keybit);  //设置输入设备支持的键值 
	}
	input_register_device(key_input_dev); //注册设备驱动
	setup_timer(&my_timer,timer_work,0);
	my_timer.expires = jiffies + 2*HZ;  // 不写这个定时器很久才工作 
	add_timer(&my_timer);
	printk("input register device\n");
	return 0;
}

static int  global_mem_remove(struct platform_device*dev)
{
	printk("global_mem_remove\n");
	input_unregister_device(key_input_dev);
	input_free_device(key_input_dev);
	del_timer(&my_timer);
	return 0;
}

//定义和初始化平台驱动
static struct platform_driver global_mem_driver = 
{
	.probe = global_mem_probe,
	.remove = (global_mem_remove),
	.driver = 
	{
		.name = "globalmem",  //该名称和platform_device.name一致
		.owner = THIS_MODULE,
	},
};

static int __init globalmem_init(void)
{
	int ret;
	
	ret = platform_driver_register(&global_mem_driver);
	return ret;
}

static void __exit globalmem_exit(void)
{
	platform_driver_unregister(&global_mem_driver);
}

module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_LICENSE("GPL");
#include<linux/init.h>
#include<linux/module.h>
#include<linux/device.h>
#include<linux/platform_device.h>

#define GPIO_LED_PIN_NUM   55 

//定义LED资源
static struct resource globalmem_resourec[] = 
{
  [0] = 
  {
     .start = GPIO_LED_PIN_NUM,
	 .end   = GPIO_LED_PIN_NUM,
	 .flags = IORESOURCE_IO,
  },
};

static void globalmem_platform_release(struct device*dev)
{
  return ;
}

//定义平台设备
static struct platform_device globalmem_platform_device = 
{
   .name = "globalmem",
   .id = -1,
 //  .num_resources = ARRAY_SIZE(globalmem_resourec),
   .dev = 
   {
     .release = globalmem_platform_release,
	 .platform_data = NULL,
   },
};

static int __init globalmem_platform_init(void)
{
   int ret;
   ret = platform_device_register(&globalmem_platform_device);
   if(ret<0)
   {
	  printk("platform device register failure\n");
      platform_device_put(&globalmem_platform_device);
	  return ret;
   }
    printk("platform device register success\n");
   return 0;  
}

static void __exit globalmem_platform_exit(void)
{  
   platform_device_unregister(&globalmem_platform_device);
}

module_init(globalmem_platform_init);
module_exit(globalmem_platform_exit);
MODULE_LICENSE("GPL");

#include<stdio.h>
#include<linux/input.h>
#include<sys/types.h>
#include<fcntl.h>
int main()
{
   int ret = 0;
   int fd = -1;
   struct input_event inputevent;
   fd = open("/dev/input/event5",O_RDWR);
   if(fd<0)
   {
	   perror("open file failure\n");
	   return -1;
   }
   while(1)
   {
	   ret = read(fd,&inputevent,sizeof(struct input_event));
	   if(ret)
	   {
		  switch(inputevent.type)
		  {
			 case EV_KEY:
			      printf("%s(%d):EV_KEY event triggered.\n",
							__FILE__, __LINE__);
				  break;
			 case EV_SYN:
			      printf("%s(%d):EV_SYN event triggered.\n",
							__FILE__, __LINE__);
				  break;
			 default:
				  break;
		  }
	   }
	   else 
	   {
			printf("read file failure.\n");
	   }
	}
	close(fd);
   return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天6点半起床10点半睡觉和今天早晚运动

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值