linux输入子系统

Linux输入子系统:

前面章节讲解按键设备驱动,实际上,在Linux系统中,一种更值得推荐的实现这类设备驱动的方法是利用input子系统。Linux系统提供了input子系统,按键、触摸屏、鼠标等都可以利用input接口函数来实现设备驱动。



在Linux内核中,input设备用input_dev结构体描述,使用input子系统实现输入设备驱动的时候,驱动的核心工作是向系统报告按键、触摸屏、键盘、鼠标等输入事件(event,通过input_event结构体描述),不再需要关心文件操作接口,因为input子系统已经完成了文件操作接口。驱动报告的事件经过InputCore和 Eventhandler最终到达用户空间。

设备注册/注销:

注册输入设备的函数为:
int input_register_device(struct input_dev *dev)

注销输入设备的函数为:
void input_unregister_device(struct input_dev *dev)

驱动实现-事件支持
设备驱动通过set_bit()告诉input子系统它支持哪些事件,
如下所示:
set_bit(EV_KEY, button_dev.evbit)
struct input_dev中有两个成员,一个是evbit;一个是keybit。分别用来表示设备所支持的事件类型和按键类型。
事件类型:
EV_RST Reset、EV_KEY 按键、EV_REL 相对坐标、EV_ABS 绝对坐标、EV_MSC 其它、EV_LED LED、EV_SND 声音、EV_REP Repeat、EV_FF 力反馈
驱动实现-报告事件
用于报告EV_KEY、EV_REL和EV_ABS事件的函数分别为:
void input_report_key(struct input_dev *dev, unsigned int code,int value)
void input_report_rel(struct input_dev *dev, unsigned int code,int value)
void input_report_abs(struct input_dev *dev, unsigned int code,int value)
code:
事件的代码。如果事件的类型是EV_KEY,该代码code为设备键盘代码。代码值0~127为键盘上的按键代码,0x110~0x116 为鼠标上按键代码,其中0x110(BTN_LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_ MIDDLE)为鼠标中键。其它代码含义请参看include/linux/input.h文件
value:
事件的值。如果事件的类型是EV_KEY,当按键按下时值为1,松开时值为0。
驱动实现-报告事件
input_sync()用于事件同步,它告知事件的接收者:驱动已经发出了一个完整的报告。例如,在触摸屏设备驱动中,一次坐标及按下状态的整个报告过程如下:
input_report_abs(input_dev, ABS_X, x); //X坐标
input_report_abs(input_dev, ABS_Y, y);//Y坐标
input_report_abs(input_dev, ABS_PRESSURE, pres); //压力
input_sync(input_dev);//同步
实例分析

/*在按键中断中报告事件*/
static void button_interrupt(int irq, void *dummy , struct pt_regs *fp)
{
	input_report_key(&button_dev, BTN_0, inb(BUTTON_PORT0));
	input_report_key(&button_dev, BTN_0, inb(BUTTON_PORT1));
	input_sync(&button_dev);
}
staticint _ _init button_init(void){
/*申请中断*/
	if (request_irq(BUTTON_IRQ, button_interrupt, 0, "button", NULL))
		return  - EBUSY;
	set_bit(EV_KEY, button_dev.evbit)//支持EV_KEY事件
	set_bit(BTN_0,  button_dev.keybit);//设备支持两个键
	set_bit(BTN_1,  button_dev.keybit);
	input_register_device(&button_dev);   //注册input设备
}
应用程序
struct input_event
{
	struct timeval time;//按键时间
	__u16 type;//类型,在下面有定义
	__u16 code;//要模拟成什么按键
	__s32 value;//是按下还是释放
}
struct input_event ev_mouse[2];
fd = open ("/dev/input/event3",O_RDWR);
while(1)
{
	count=read(fd, ev_mouse, sizeof(struct input_event));
	for(i=0;i<(int)count/sizeof(struct input_event);i++)
	{
		if(EV_REL==ev_mouse[i].type)
		{
			printf(“time:%ld.%d”,ev_mouse[i].time.tv_sec,ev_mouse[i].time.tv_usec);
			printf(“ type:%d code:%d value:%d\n",ev_mouse[i].type,ev_mouse[i].code,ev_mouse[i].value);
		}
		if(EV_KEY==ev_mouse[i].type)
		{
			printf("time:%ld. %d",ev_mouse[i].time.tv_sec,ev_mouse[i].time.tv_usec);
			printf(" type:%d code:%d value:%d\n",ev_mouse[i].type,ev_mouse[i].code,ev_mouse[i].value);
		}
	}
}

关于输入子系统详细

输入子系统按键实例:

开发环境:

Cent OS 6.4、TQ2440开发板、内核版本linux-2.30.4

硬件描述:

四个按键所在端口分别为EINT1/GPF1、EINT4/GPF4、EINT2/GPF2、EINT0/GPF0

驱动程序:

#include <linux/input.h>
#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/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
//#include <linux/init.h>
//#include <asm/irq.h>
//#include <asm/io.h>

struct input_dev *button_dev;
struct button_irq_desc {
	int irq;
	int pin;
	int pin_setting;
	int number;
	char *name;	
};

static struct button_irq_desc button_irqs [] = {
	{IRQ_EINT1, S3C2410_GPF1 ,  S3C2410_GPF1_EINT1 , 0, "KEY1"},  
	{IRQ_EINT4, S3C2410_GPF4 ,  S3C2410_GPF4_EINT4 , 1, "KEY2"},
	{IRQ_EINT2, S3C2410_GPF2 ,  S3C2410_GPF2_EINT2 , 2, "KEY3"},
	{IRQ_EINT0, S3C2410_GPF0 ,  S3C2410_GPF0_EINT0 , 3, "KEY4"},
};

static int key_values = 0;

static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
	int down;
	udelay(0);
	down = !s3c2410_gpio_getpin(button_irqs->pin);   //down: 1(按下),0(弹起)
	if (!down) { 
		/*报告事件*/
		key_values = button_irqs->number;
		//printk("====>rising key_values=%d\n",key_values);
		if(key_values==0)
			input_report_key(button_dev, KEY_1, 0);
		if(key_values==1)
			input_report_key(button_dev, KEY_2, 0);
		if(key_values==2)
			input_report_key(button_dev, KEY_3, 0);
		if(key_values==3)
			input_report_key(button_dev, KEY_4, 0);
		input_sync(button_dev);      
	}
	else {
		key_values = button_irqs->number;
		//printk("====>falling key_values=%d\n",key_values);
		if(key_values==0)
			input_report_key(button_dev, KEY_1, 1);
		if(key_values==1)
			input_report_key(button_dev, KEY_2, 1);
		if(key_values==2)
			input_report_key(button_dev, KEY_3, 1);
		if(key_values==3)
			input_report_key(button_dev, KEY_4, 1);
		input_sync(button_dev);      
	}
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int s3c24xx_request_irq(void)
{
	int i;
	int err = 0;
	
	for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if (button_irqs[i].irq < 0) {
			continue;
		}
		/* IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH */
		err = request_irq(button_irqs[i].irq,buttons_interrupt,IRQ_TYPE_EDGE_BOTH,button_irqs[i].name, (void *)&button_irqs[i]);
		if (err)
			break;
	}
	if (err) {
		i--;
		for (; i >= 0; i--) {
			if (button_irqs[i].irq < 0) {
				continue;
			}
			disable_irq(button_irqs[i].irq);
			free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
		}
	return -EBUSY;
	}
	return 0;
}
static int __init dev_init(void)
{
	 int error;
	/*request irq*/
	s3c24xx_request_irq();
	/* Initialise input stuff */
	button_dev = input_allocate_device();//得到一个struct input_dev的空间
	if (!button_dev) {
		printk(KERN_ERR "Unable to allocate the input device !!\n");
		return -ENOMEM;
	}
	button_dev->name = "s3c2440_button";
	/*button_dev->id.bustype = BUS_RS232;
  	button_dev->id.vendor = 0xDEAD;
  	button_dev->id.product = 0xBEEF;
  	button_dev->id.version = 0x0100;*/
	set_bit(EV_KEY, button_dev->evbit);//支持EV_KEY事件或者用button_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT(EV_SYN);
	set_bit(KEY_1,   button_dev->keybit);
	set_bit(KEY_2,   button_dev->keybit);
	set_bit(KEY_3,   button_dev->keybit);
	set_bit(KEY_4,   button_dev->keybit);
 
	error=input_register_device(button_dev);
	if(error){
	  printk(KERN_ERR "button.c: Failed to register device\n");
	  //input_free_device(button_dev);
	  return error;
	}
	return 0;
}

static void __exit dev_exit(void)
{
	int i;

	for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if (button_irqs[i].irq < 0) {
			continue;
		}
		//disable_irq(button_irqs[i].irq);
		free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
	}
	input_unregister_device(button_dev);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zbffff");
应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <linux/input.h>

int main(void)
{
	int buttons_fd=-1;
	int key_value,i=0,count;
	struct input_event ev_key;
	buttons_fd = open("/dev/event0", O_RDWR);//|O_NONBLOCK可以防止阻塞
	if (buttons_fd < 0) {
		perror("open device buttons\n");
		exit(1);
	}
	for (;;) {
		count = read(buttons_fd,&ev_key,sizeof(struct input_event));//如果没有信号会在此阻塞
	  //printf("readed\n");
	  if(count>0)
		for(i=0; i<(int)count/sizeof(struct input_event); i++)
	    if(EV_KEY==ev_key.type)
				printf("type:%d,keynum:%d,value:%d\n", ev_key.type,ev_key.code-1,ev_key.value);
			else if(EV_SYN==ev_key.type)
				printf("syn event\n");//每次按键一次EV_KEY的信息之后会再有一次EV_SYN类型的信号,分两次
	}
	close(buttons_fd);
	return 0;
}

触摸屏驱动设计:

触摸屏分为电阻式、电容式、声表面波式和红外线扫描式等类型,使用得最多的是4线电阻式触摸屏。 S3C2440触摸屏由横向电阻比和纵向电阻线组成,由nYPON、YMON、nXPON、XMON四个控制信号控制4个MOS管(S1、S2、S3、S4)的通断。

S3C2440触摸屏控制器有2种处理模式:
①X/Y位置分别转换模式。触摸屏控制器包括两个控制阶段,X坐标转换阶段和Y坐标转换阶段。
②X/Y位置自动转换模式。触摸屏控制器将自动转换X和Y坐标。

1. Select Separate X/Y Position Conversion Mode or Auto (Sequential) X/Y Position ConversionModeto get X/Y position.
2. Set Touch ScreenInterfaceto Waiting Interrupt Mode,
3. If interrupt occurs, then appropriate conversion(Separate X/Y Position Conversion Mode or Auto (Sequential) X/Y Position Conversion Mode) is activated.
4. After get the proper value about X/Y position, return to Waiting for Interrupt Mode.

When Touch Screen Controller is in Waiting for Interrupt Mode, it waits for Stylus down. The controller, generates Interrupt(INT_TC) signals when the Stylus is down on Touch Screen Panel. After an interruptoccurs, X and Yposition can be read by the proper conversion mode (SeparateX/Y position conversion Mode or Auto X/Y Position conversion Mode).












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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值