The input subsystem reports touch screen coordinates under Linux

The input subsystem reports touch screen coordinates under Linux

1. Introduction to input subsystem

  In Linux, the input subsystem is composed of the input subsystem device driver layer , the input subsystem core layer (Input Core) and the input subsystem event processing layer (Event Handler) .
insert image description here

  • Device driver layer

      The device driver layer implements access to each register of the hardware device, converts the response data of the underlying hardware to the user layer into standard input events, and then submits them to the event processing layer through the core layer.
  • Core layer

      The core layer is the connection bridge between the device driver layer and the event processing layer, and provides a programming interface for the device driver layer and the event processing layer.
  • Event processing layer

      The event processing layer provides a unified access interface for the user space and processes the data submitted by the driver layer, so that the driver part of our input device does not need to care about the operation of the device file, but only needs to care about the operation of each hardware register and Submitted input events.

2. Input subsystem benefits

  1. Unifies the processing functions of similar input devices with different physical forms. For example, all kinds of mice, whether PS/2, USB, or Bluetooth, are treated the same. The common event types of the input subsystem are: key event (such as keyboard), relative coordinate event (such as mouse), and absolute coordinate event (such as touch screen).
  2. Provides a simple event interface for dispatching input reports to user applications. Your driver does not have to create and manage /dev nodes and related access methods. So it can easily call input API to send mouse movement, keyboard key press, or touch event to user space.
  3. Extracts common parts of input drivers, simplifies drivers, and provides consistency. For example, the input subsystem provides a collection of low-level drivers (called serios) that support access to hardware inputs such as serial ports and keyboard controllers.

3. Input subsystem-related interface functions

  • struct input_dev structure
      The structure input_dev represents the underlying hardware device and is the abstraction of all input devices. The driver layer needs to fill the input_dev structure.
struct input_dev {
    
    
	const char *name; //设备名字--比如:键盘的名字
	const char *phys; //设备在系统中的路径。比如:input/key0
	const char *uniq; //唯一ID号
	struct input_id id; //用于匹配事件处理层 handler

	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)]; 

	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(MSC_CNT)];
	unsigned long ledbit[BITS_TO_LONGS(LED_CNT)];
	unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
	unsigned long ffbit[BITS_TO_LONGS(FF_CNT)];
	unsigned long swbit[BITS_TO_LONGS(SW_CNT)];

	unsigned int hint_events_per_packet;

	unsigned int keycodemax;
	unsigned int keycodesize;
	void *keycode;

	int (*setkeycode)(struct input_dev *dev,
			  const struct input_keymap_entry *ke,
			  unsigned int *old_keycode);
	int (*getkeycode)(struct input_dev *dev,
			  struct input_keymap_entry *ke);

	struct ff_device *ff;

	unsigned int repeat_key;
	struct timer_list timer;

	int rep[REP_CNT];

	struct input_mt_slot *mt;
	int mtsize;
	int slot;
	int trkid;

	struct input_absinfo *absinfo;

	unsigned long key[BITS_TO_LONGS(KEY_CNT)];
	unsigned long led[BITS_TO_LONGS(LED_CNT)];
	unsigned long snd[BITS_TO_LONGS(SND_CNT)];
	unsigned long sw[BITS_TO_LONGS(SW_CNT)];
	//文件操作函数 ,可以自行实现
	int (*open)(struct input_dev *dev);
	void (*close)(struct input_dev *dev);
	int (*flush)(struct input_dev *dev, struct file *file);
	int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

	struct input_handle __rcu *grab;

	spinlock_t event_lock;
	struct mutex mutex;

	unsigned int users;
	bool going_away;

	bool sync;//最后一次同步后没有新的事件置 1

	struct device dev;

	struct list_head	h_list;
	struct list_head	node;
};
  • struct input_event structure
      This structure is generally called at the application layer, and the user receives the data content reported by the event layer.
struct input_event {
    
    
	struct timeval time; //时间戳
	__u16 type;//事件类型EV_KEY、EV_REL、EV_ABS
	__u16 code;//事件数据值,若按键事件,则保证按键键值;若坐标信息,则表明为x,y
	__s32 value;//标志值,若按键,则表示按下还是松开;若坐标,则表示位具体的坐标值
};
  • Dynamically allocate and release inptu_dev structure function

//Dynamic allocation of input_dev structure
struct input_dev *input_allocate_device(void)
//Release input_dev structure
void input_free_device(struct input_dev *dev)

  • Registering and Unregistering the Input Subsystem

//Register input subsystem
int input_register_device(struct input_dev *dev)
//Cancel input subsystem
void input_free_device(struct input_dev *dev)
Parameters: input_dev --Input device structure
return value: registration succeeds and returns 0, failure returns other values

  • Set the reported data content input_set_capability

      The input_set_capability function is used to fill the input_dev structure and set the data type and data information to be reported.

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
parameter: dev --input_dev structure
   type --event type EV_KEY, EV_REL, EV_ABS
   code --specific value to be reported
Example: input_set_capability(dev,EV_KEY ,KEY_A);//Report the key event, the reported key value is 'A'

  • Set the reported data content __set_bit

  The inptu_dev structure is filled by the function of setting the bit, and the interior of the input_set_capability function is realized by calling the __set_bit function.

inline void __set_bit(int nr, volatile unsigned long *addr)
Formal parameter: nr – the specific value to be reported
   addr -- the address to set
to report the button event Example:
  __set_bit(EV_KEY,dev->evbit);//Set the event attribute as Key event
  __set_bit(KEY_A,dev->keybit);//Set the reported key value

and set repeated reporting Example: __set_bit(EV_REP,dev->evbit);

  • Set the range of the reported value input_set_abs_params

      The input_set_abs_params function is used to set the value range of the reported value.

void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)
formal parameters: dev --input_dev structure
   axis --reported value
   min --minimum value
   max --maximum
    fuzz --Data deviation value
    flat --Smooth position
Set the x-coordinate range of the touch screen:
 input_set_abs_params(touch_dev,ABS_X,0,800,0,0);//Set the x-coordinate range
Set the x-coordinate range of the touch screen:
 input_set_abs_params(touch_dev,ABS_PRESSURE,0,1 ,0,0);//Set pressure value range

  • Report data to the event processing layer

//Report key event key value, such as keyboard
inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
//Report relative event coordinate value, such as mouse
inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
//Report the absolute event coordinate value, such as the touch screen
inline void input_report_abs(struct input_dev *dev, unsigned int code, int value);
formal parameters: dev --input_dev structure
    code --event data value, if the key event , then ensure the key value of the button; if the coordinate information, it is indicated as x,y
    value -- flag value, if the button is pressed, it indicates whether it is pressed or released; if it is the coordinate, it indicates the specific coordinate value

  These functions complete the data reporting internally by the input_event function.

  • Event synchronization input_mt_sync

void input_mt_sync(struct input_dev *dev)
parameter: dev --input_dev structure

  Be sure to call the event synchronization function after completing the data reporting.

4. Example of input subsystem reporting touch screen coordinates

Hardware platform: tiny4412
Development platform: ubuntu18.04
Cross compiler: arm-linux-gcc
Kernel: linux3.5
Touch screen driver IC: ft5X06

ft5x06 driver example reference: IIC subsystem and touch screen driver under Linux

  • Input subsystem registration report data example
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
static struct work_struct touch_work;
static struct i2c_client *touch_client;
static struct input_dev *touch_dev=NULL;
/*工作处理函数*/
static void touch_work_func(struct work_struct *work)
{
    
    
	u8 touch_buff[7];
	int x,y;
	int num;
	i2c_smbus_read_i2c_block_data(touch_client,0, 7,touch_buff);
	num=touch_buff[2]&0xf;//触控点个数
	x=((touch_buff[3]&0xf)<<8)|touch_buff[4];
	y=((touch_buff[5]&0xf)<<8)|touch_buff[6];
	//printk("(x,y)=%d,%d\tnum=%d\n",x,y,num);
	if(num)
	{
    
    
		 input_report_abs(touch_dev,ABS_X,x);//上报x坐标
		 input_report_abs(touch_dev,ABS_Y,y);//上报x坐标
		 input_report_abs(touch_dev,ABS_PRESSURE,1);//压力值,1表示按下
		 input_report_key(touch_dev,BTN_TOUCH,1);//按下
	}
	else
	{
    
    
		input_report_abs(touch_dev,ABS_PRESSURE,0);//压力值,0表示松开
		input_report_key(touch_dev,BTN_TOUCH,0);//释放
	}
	input_sync(touch_dev);//同步
}
/*中断处理函数*/
static irqreturn_t touch_irq_work(int irq, void *dev)
{
    
    
	schedule_work(&touch_work);//调度工作
	return IRQ_HANDLED;
}

static int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)//资源匹配函数
{
    
    
	int ret; 
	printk("资源匹配成功\n");
	printk("name=%s\taddr=%#x\tirq=%d\n",client->name,client->addr,client->irq);
	touch_client=client;

	/*动态分配input_dev结构体*/
	touch_dev=input_allocate_device();
	if(!touch_dev)return -1;//动态分配失败

	/*设置要上报的数据内容*/
	input_set_capability(touch_dev,EV_ABS,ABS_X);//上报x坐标
	input_set_capability(touch_dev,EV_ABS,ABS_Y);//上报x坐标
	input_set_capability(touch_dev,EV_ABS,ABS_PRESSURE);//压力值
	input_set_capability(touch_dev,EV_KEY,BTN_TOUCH);//触摸屏点击事件
	/*设置xy取值范围*/
	input_set_abs_params(touch_dev,ABS_X,0,800,0,0);//设置x坐标范围
	input_set_abs_params(touch_dev,ABS_Y,0,480,0,0);//设置y坐标范围
	input_set_abs_params(touch_dev,ABS_PRESSURE,0,1,0,0);//设置压力值

	/*注册输入子系统*/
	ret=input_register_device(touch_dev);
	if(ret)return ret;//注册输入子系统设备失败
	/*1.初始化工作*/
	INIT_WORK(&touch_work, touch_work_func);
	/*注册中断*/
	ret=request_irq(client->irq,touch_irq_work,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"ft5x06",NULL);
	if(ret)
	{
    
    
		printk("中断注册失败\n");
		return -1;
	}

	return 0;
}
static int ft5x06_remove(struct i2c_client *client)//资源释放函数
{
    
    
	printk("IIC驱动程资源释放成功\n");
	free_irq(client->irq,NULL);//注销中断
	/*注销输入子系统设备*/
	input_unregister_device(touch_dev);
	/*释放input_dev结构体*/
	input_free_device(touch_dev);
	return 0;
}
//资源匹配结构体
static struct i2c_device_id id_table[]=
{
    
    
		{
    
    "touch_ft5x06",0},
			{
    
    },
};
static struct i2c_driver ft5x06_drv=
{
    
    
	.probe=ft5x06_probe,
	.remove=ft5x06_remove,
	.driver=
	{
    
    
		.name="touch_drv",
	},
	.id_table=id_table,//资源匹配结构体
};

static int __init wbyq_ft5x06_drv_init(void)
{
    
    
	i2c_add_driver(&ft5x06_drv); 
	return 0;
	
}
/*驱动释放*/
static void __exit wbyq_ft5x06_drv_cleanup(void)
{
    
    
	i2c_del_driver(&ft5x06_drv);
    printk("IIC驱动层注销成功\n");
}
module_init(wbyq_ft5x06_drv_init);//驱动入口函数
module_exit(wbyq_ft5x06_drv_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 ft5x06_drv Driver");
  • Example of reading touch screen coordinates at the application layer
#include <stdio.h>
#include <linux/fb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <linux/input.h>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
static unsigned char *lcd_p=NULL;//屏幕缓存地址
static unsigned char *gbk_addr=NULL;//屏幕缓存地址
static struct fb_fix_screeninfo fb_fix;//固定参数结构体
static struct fb_var_screeninfo fb_var;//可变参数结构体
extern const unsigned char ascii_32_16[][32*16/8];//逐列式,高位在前

/*LCD画点函数*/
static inline void LCD_DrawPoint(int x,int y,int c)
{
    
    
	//获取要绘制的点的地址
	unsigned int *p= (unsigned int *)(lcd_p+y*fb_fix.line_length+x*fb_var.bits_per_pixel/8);
	*p=c;//写入颜色值
}

/*
显示汉字
x,y  --要显示的位置
size  --字体大小
font --要显示的汉字
c -- 颜色值
*/
static void LCD_DisplayFont(int x,int y,int size,char *font,int c)
{
    
    
	u8 *p=NULL;
	u8 H,L;
	u32 addr=0;//汉字偏移地址
	u16 font_size=size*size/8;//汉字点阵大小(宽度保证为8的倍数)
	H=*font;//汉字高字节
	L=*(font+1);//汉字的低字节
	if(L<0x7F)L-=0x40;
	else L-=0x41;
	H-=0x81;
	addr=(190*H+L)*font_size;//汉字所在点阵中的偏移地址
	p=malloc(font_size);
	if(p==NULL)
	{
    
    
		printf("申请空间失败\r\n");
		return ;
	}
	memcpy(p,gbk_addr+addr,font_size);//读取点阵码数据	
	int i,j;
	int x0=x;
	unsigned char tmep;
	for(i=0;i<size*size/8;i++)
	{
    
    
		tmep=p[i];//取出每个字节数据
		for(j=0;j<8;j++)
		{
    
    
			if(tmep&0x80)
			{
    
    
				 LCD_DrawPoint(x0,y,c);
			}
			x0++;
			c+=60;//修改颜色值
			tmep<<=1;//继续下一个像素点
		}
		//换行
		if(x0-x>=size)
		{
    
    
			x0=x;
			y++;
		}
	}
}
/*
显示字符
x,y  --要显示的位置
h,w -- 字符高和宽
cha --要显示的字符
c -- 颜色值
取模走向:逐列式,高位在前
*/
static void LCD_DisplayCha(int x,int y,int h,int w,char cha,int c)
{
    
    
	int i,j;
	int y0=y;
	u8 temp;
	for(i=0;i<w*h/8;i++)
	{
    
    
		temp=ascii_32_16[cha-' '][i];
		for(j=0;j<8;j++)
		{
    
    
			if(temp&0x80)LCD_DrawPoint(x,y0,c);
			y0++;
			c+=100;//修改颜色值
			temp<<=1;
		}
		if(y0-y==h)//换到下一列
		{
    
    
			y0=y;
			x++;
		}
	}
}
/*
显示字符串
x,y  --要显示的位置
size -- 字符高度
str --要显示的字符串
c -- 颜色值
*/
static void LCD_DisplayStr(int x,int y,int size,char *str,int c)
{
    
    
	int x0=x;
	while(*str)
	{
    
    
		if(*str>=0x80)//汉字
		{
    
    
			LCD_DisplayFont(x0,y,size,str,c);
			str+=2;
			x0+=size;
		}
		else if(*str>=' ' && *str<='~')//字符显示
		{
    
    
			LCD_DisplayCha(x0,y,size,size/2,*str,c);
			str++;
			x0+=size/2;
		}
		else str++;
	}
	
}
int main()
{
    
    
	/*1.打开设备*/
	int fd=open("/dev/fb0", 2);
	if(fd<0)
	{
    
    
		printf("打开设备失败\n");
	}
	/*2.获取固定参数*/
	memset(&fb_fix,0, sizeof(fb_fix));
 	ioctl(fd,FBIOGET_FSCREENINFO,&fb_fix);
	printf("屏幕缓存大小:%d\n",fb_fix.smem_len);
	printf("一行的字节数:%d\n",fb_fix.line_length);
	/*3.获取屏幕可变参数*/
	memset(&fb_var,0, sizeof(fb_var));
	ioctl(fd,FBIOGET_VSCREENINFO,&fb_var);
	printf("屏幕尺寸:%d*%d\n",fb_var.xres,fb_var.yres);
	printf("颜色位数:%d\n",fb_var.bits_per_pixel);
	/*4.将屏幕缓冲区映射到进程空间*/
	lcd_p=mmap(NULL,fb_fix.smem_len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
	close(fd);
	if(lcd_p==(void *)-1)
	{
    
    
		printf("内存映射失败\n");
		return 0;
	}
	/*打开字库文件*/
	int fontfd=open("GBK_32.DZK",2);
	if(fontfd<0)
	{
    
    
		printf("字库文件打开失败\n");
		return 0;
	}
	struct stat statbuf;
	fstat(fontfd,&statbuf);
	if(statbuf.st_size<=0)goto AA;
	gbk_addr=mmap(NULL,statbuf.st_size,PROT_READ|PROT_WRITE,MAP_SHARED,fontfd,0);
	close(fontfd);
	if(gbk_addr==(void *)-1)goto AA;
	memset(lcd_p,0xff,fb_fix.smem_len);//将屏幕清空为白色
	LCD_DisplayStr(300,50,32,"触摸屏驱动测试",0x45ff);
	
	/*打开触摸屏设备*/
	fd=open("/dev/input/event1",2);
	if(fd<0)
	{
    
    
		printf("触摸屏驱动打开失败\n");
		return 0;
	}
	struct pollfd fds=
	{
    
    
		.fd=fd,
		.events=POLLIN,
	};
	/*
		struct input_event {
		struct timeval time;
		__u16 type;
		__u16 code;
		__s32 value;
		};
	*/
	struct input_event touchxy;
	while(1)
	{
    
    
		poll(&fds,1,-1);
		read(fd,&touchxy,sizeof(touchxy));
		switch(touchxy.type)
		{
    
    
			case EV_KEY://按键值
				printf("key=%d\tstat=%d\n",touchxy.code,touchxy.value);
				break;
			case EV_ABS://绝对坐标
				if(touchxy.code == ABS_X)//x坐标
				{
    
    
					printf("x=%d\n",touchxy.value);
				}
				else if(touchxy.code == ABS_Y)//Y坐标
				{
    
    
					printf("y=%d\n",touchxy.value);
				}
				else if(touchxy.code == ABS_PRESSURE)//压力值
				{
    
    
					printf("press=%d\n",touchxy.value);
				}
				break;
		}
	}
	munmap(gbk_addr,statbuf.st_size);
AA:	
	//取消映射
	munmap(lcd_p,fb_fix.smem_len);
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_44453694/article/details/126906896