O subsistema de entrada informa as coordenadas da tela sensível ao toque no Linux

O subsistema de entrada informa as coordenadas da tela sensível ao toque no Linux

1. Introdução ao subsistema de entrada

  No Linux, o subsistema de entrada é composto pela camada do driver de dispositivo do subsistema de entrada , a camada principal do subsistema de entrada (Input Core) e a camada de processamento de eventos do subsistema de entrada (Event Handler) .
insira a descrição da imagem aqui

  • Camada de driver de dispositivo

      A camada de driver de dispositivo implementa o acesso a cada registro do dispositivo de hardware, converte os dados de resposta do hardware subjacente para a camada do usuário em eventos de entrada padrão e os envia para a camada de processamento de eventos por meio da camada central.
  • Camada de núcleo

      A camada de núcleo é a ponte de conexão entre a camada de driver de dispositivo e a camada de processamento de evento e fornece uma interface de programação para a camada de driver de dispositivo e a camada de processamento de evento.
  • Camada de processamento de eventos

      A camada de processamento de eventos fornece uma interface de acesso unificada para o espaço do usuário e processa os dados enviados pela camada do driver, para que a parte do driver do nosso dispositivo de entrada não precise se preocupar com a operação do arquivo do dispositivo, mas apenas precisa se preocupar com a operação de cada registro de hardware e evento de entrada enviado.

2. Benefícios do subsistema de entrada

  1. Unifica as funções de processamento de dispositivos de entrada semelhantes com diferentes formas físicas. Por exemplo, todos os tipos de mouse, sejam PS/2, USB ou Bluetooth, são tratados da mesma forma. Os tipos de evento comuns do subsistema de entrada são: evento de tecla (como teclado), evento de coordenada relativa (como mouse) e evento de coordenada absoluta (como tela sensível ao toque).
  2. Fornece uma interface de evento simples para despachar relatórios de entrada para aplicativos de usuário. Seu driver não precisa criar e gerenciar nós /dev e métodos de acesso relacionados. Assim, ele pode facilmente chamar a API de entrada para enviar movimento do mouse, pressionamento de tecla do teclado ou evento de toque para o espaço do usuário.
  3. Extrai partes comuns dos drivers de entrada, simplifica os drivers e fornece consistência. Por exemplo, o subsistema de entrada fornece uma coleção de drivers de baixo nível (chamados sérios) que suportam acesso a entradas de hardware, como portas seriais e controladores de teclado.

3. Funções de interface relacionadas ao subsistema de entrada

  • struct input_dev estrutura
      A estrutura input_dev representa o dispositivo de hardware subjacente e é a abstração de todos os dispositivos de entrada. A camada do driver precisa preencher a estrutura input_dev.
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
      Essa estrutura geralmente é chamada na camada de aplicativo e o usuário recebe o conteúdo dos dados informados pela camada de evento.
struct input_event {
    
    
	struct timeval time; //时间戳
	__u16 type;//事件类型EV_KEY、EV_REL、EV_ABS
	__u16 code;//事件数据值,若按键事件,则保证按键键值;若坐标信息,则表明为x,y
	__s32 value;//标志值,若按键,则表示按下还是松开;若坐标,则表示位具体的坐标值
};
  • Alocar e liberar dinamicamente a função de estrutura inptu_dev

//Alocação dinâmica da estrutura input_dev
struct input_dev *input_allocate_device(void)
//Liberação da estrutura input_dev
void input_free_device(struct input_dev *dev)

  • Registrando e cancelando o registro do subsistema de entrada

//Registra o subsistema de entrada
int input_register_device(struct input_dev *dev)
//Cancela o subsistema de entrada
void input_free_device(struct input_dev *dev)
Parâmetros: input_dev --Valor
de retorno da estrutura do dispositivo de entrada: o registro é bem-sucedido e retorna 0, a falha retorna outros valores

  • Definir o conteúdo de dados relatados input_set_capability

      A função input_set_capability é usada para preencher a estrutura input_dev e definir o tipo de dados e informações de dados a serem relatados.

void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
parâmetro: dev --input_dev tipo de estrutura
   --tipo de evento EV_KEY, EV_REL,
   código EV_ABS --valor específico a ser relatado
Exemplo: input_set_capability(dev,EV_KEY ,KEY_A );//Relata o evento de chave, o valor de chave relatado é 'A'

  • Defina o conteúdo de dados relatado __set_bit

  A estrutura inptu_dev é preenchida pela função de configuração do bit, e o interior da função input_set_capability é realizado chamando a função __set_bit.

inline void __set_bit(int nr, volátil unsigned long *addr)
Parâmetro formal: nr – o valor específico a ser relatado
   addr -- o endereço a ser definido
para relatar o evento do botão Exemplo:
  __set_bit(EV_KEY,dev->evbit);// Defina o atributo do evento como Key event
  __set_bit(KEY_A,dev->keybit);//Defina o valor da chave relatada

e defina o relatório repetido Exemplo: __set_bit(EV_REP,dev->evbit);

  • Definir o intervalo do valor relatado input_set_abs_params

      A função input_set_abs_params é usada para definir o intervalo de valores do valor relatado.

void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)
parâmetros formais: dev --input_dev structure
   axis --reported value
   min --minimum value
   max --maximum
    fuzz --Data valor de desvio
    plano --Posição suave
Defina o intervalo da coordenada x da tela sensível ao toque:
 input_set_abs_params(touch_dev,ABS_X,0,800,0,0);//Defina o intervalo da coordenada x
Defina o intervalo da coordenada x da tela sensível ao toque:
 input_set_abs_params(touch_dev,ABS_PRESSURE,0,1 ,0,0);//Definir faixa de valor de pressão

  • Reportar dados para a camada de processamento de eventos

//Relata o valor da chave do evento chave, como
void inline do teclado input_report_key(struct input_dev *dev, unsigned int code, int value);
//Relata o valor relativo da coordenada do evento, como mouse
inline void input_report_rel(struct input_dev *dev, unsigned int code, int value);
//Relata o valor absoluto da coordenada do evento, como a tela sensível ao toque
void inline input_report_abs(struct input_dev *dev, unsigned int code, int value);
parâmetros formais: dev --input_dev structure
    code --event data valor, se o evento de tecla , verifique o valor da chave do botão; se a informação da coordenada, é indicada como
    valor x,y -- valor do sinalizador, se o botão for pressionado, indica se foi pressionado ou liberado; se é a coordenada, indica o valor da coordenada específica

  Essas funções completam o relatório de dados internamente pela função input_event.

  • Sincronização de eventos input_mt_sync

void input_mt_sync(struct input_dev *dev)
parâmetro: dev --input_dev estrutura

  Certifique-se de chamar a função de sincronização de eventos após concluir o relatório de dados.

4. Exemplo de subsistema de entrada informando as coordenadas da tela sensível ao toque

Plataforma de hardware: tiny4412
Plataforma de desenvolvimento: ubuntu18.04
Compilador cruzado: arm-linux-gcc
Kernel: linux3.5
Touch screen driver IC: ft5X06

referência de exemplo de driver ft5x06: subsistema IIC e driver de tela sensível ao toque no Linux

  • Exemplo de dados do relatório de registro do subsistema de entrada
#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");
  • Exemplo de leitura de coordenadas da tela sensível ao toque na camada do aplicativo
#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;
}

Acho que você gosta

Origin blog.csdn.net/weixin_44453694/article/details/126906896
Recomendado
Clasificación