linux 电容触摸屏驱动-4

linux 输入子系统

input子系统在内核中的实现,包括输入子系统(Input Core),事件处理层(Event Handler)和设备驱动层.

input


###linux 输入子系统重要函数和结构体

####struct input_dev

/**
 * struct input_dev - represents an input device
 * @name: name of the device
 * @phys: physical path to the device in the system hierarchy
 * @uniq: unique identification code for the device (if device has it)
 * @id: id of the device (struct input_id)
 * @propbit: bitmap of device properties and quirks
 * @evbit: bitmap of types of events supported by the device (EV_KEY,
 *	EV_REL, etc.)
 * @keybit: bitmap of keys/buttons this device has
 * @relbit: bitmap of relative axes for the device
 * @absbit: bitmap of absolute axes for the device
 * @mscbit: bitmap of miscellaneous events supported by the device
 * @ledbit: bitmap of leds present on the device
 * @sndbit: bitmap of sound effects supported by the device
 * @ffbit: bitmap of force feedback effects supported by the device
 * @swbit: bitmap of switches present on the device
 * @hint_events_per_packet: average number of events generated by the
 *	device in a packet (between EV_SYN/SYN_REPORT events). Used by
 *	event handlers to estimate size of the buffer needed to hold
 *	events.
 * @keycodemax: size of keycode table
 * @keycodesize: size of elements in keycode table
 * @keycode: map of scancodes to keycodes for this device
 * @getkeycode: optional legacy method to retrieve current keymap.
 * @setkeycode: optional method to alter current keymap, used to implement
 *	sparse keymaps. If not supplied default mechanism will be used.
 *	The method is being called while holding event_lock and thus must
 *	not sleep
 * @ff: force feedback structure associated with the device if device
 *	supports force feedback effects
 * @repeat_key: stores key code of the last key pressed; used to implement
 *	software autorepeat
 * @timer: timer for software autorepeat
 * @rep: current values for autorepeat parameters (delay, rate)
 * @mt: pointer to multitouch state
 * @absinfo: array of &struct input_absinfo elements holding information
 *	about absolute axes (current value, min, max, flat, fuzz,
 *	resolution)
 * @key: reflects current state of device's keys/buttons
 * @led: reflects current state of device's LEDs
 * @snd: reflects current state of sound effects
 * @sw: reflects current state of device's switches
 * @open: this method is called when the very first user calls
 *	input_open_device(). The driver must prepare the device
 *	to start generating events (start polling thread,
 *	request an IRQ, submit URB, etc.)
 * @close: this method is called when the very last user calls
 *	input_close_device().
 * @flush: purges the device. Most commonly used to get rid of force
 *	feedback effects loaded into the device when disconnecting
 *	from it
 * @event: event handler for events sent _to_ the device, like EV_LED
 *	or EV_SND. The device is expected to carry out the requested
 *	action (turn on a LED, play sound, etc.) The call is protected
 *	by @event_lock and must not sleep
 * @grab: input handle that currently has the device grabbed (via
 *	EVIOCGRAB ioctl). When a handle grabs a device it becomes sole
 *	recipient for all input events coming from the device
 * @event_lock: this spinlock is is taken when input core receives
 *	and processes a new event for the device (in input_event()).
 *	Code that accesses and/or modifies parameters of a device
 *	(such as keymap or absmin, absmax, absfuzz, etc.) after device
 *	has been registered with input core must take this lock.
 * @mutex: serializes calls to open(), close() and flush() methods
 * @users: stores number of users (input handlers) that opened this
 *	device. It is used by input_open_device() and input_close_device()
 *	to make sure that dev->open() is only called when the first
 *	user opens device and dev->close() is called when the very
 *	last user closes the device
 * @going_away: marks devices that are in a middle of unregistering and
 *	causes input_open_device*() fail with -ENODEV.
 * @dev: driver model's view of this device
 * @h_list: list of input handles associated with the device. When
 *	accessing the list dev->mutex must be held
 * @node: used to place the device onto input_dev_list
 * @num_vals: number of values queued in the current frame
 * @max_vals: maximum number of values queued in a frame
 * @vals: array of values queued in the current frame
 * @devres_managed: indicates that devices is managed with devres framework
 *	and needs not be explicitly unregistered or freed.
 */
struct input_dev {
	const char *name;
	const char *phys;
	const char *uniq;
	struct input_id id;

	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 *mt;

	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;

	struct device dev;

	struct list_head	h_list;
	struct list_head	node;

	unsigned int num_vals;
	unsigned int max_vals;
	struct input_value *vals;

	bool devres_managed;
};

input_allocate_device()

input_allocate_device()给input device分配内存,并做一些默认初始化

/**
 * input_allocate_device - allocate memory for new input device
 *
 * Returns prepared struct input_dev or %NULL.
 *
 * NOTE: Use input_free_device() to free devices that have not been
 * registered; input_unregister_device() should be used for already
 * registered devices.
 */
struct input_dev *input_allocate_device(void)
{
	struct input_dev *dev;
//动态申请内存,使用GFP_KERNEL方式,注意GFP_KERNEL可能导致睡眠,不能在中断中调用这个函数
	dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
	if (dev) {
		dev->dev.type = &input_dev_type;	//支持热插拔的结构体
		dev->dev.class = &input_class;		//描述设备的硬件信息和支持的事件类型
		device_initialize(&dev->dev); 		//类设备初始化,添加进input类设备模型中
		mutex_init(&dev->mutex);    	 	//初始化互斥锁
		spin_lock_init(&dev->event_lock); 	
		INIT_LIST_HEAD(&dev->h_list);		//初始化handle链表
		INIT_LIST_HEAD(&dev->node);		//初始化输入设备链表

		__module_get(THIS_MODULE);
	}

	return dev;
}

input_register_device

input_register_device()注册input device,主要做下面几件事:

  • 进一步初始化输入设备,例如连击事件;
  • 注册输入设备到input类中;
  • 把输入设备挂到输入设备链表input_dev_list中;
  • 查找并匹配输入设备对应的事件处理层,通过input_handler_list链表

/**
 * input_register_device - register device with input core
 * @dev: device to be registered
 *
 * This function registers device with input core. The device must be
 * allocated with input_allocate_device() and all it's capabilities
 * set up before registering.
 * If function fails the device must be freed with input_free_device().
 * Once device has been successfully registered it can be unregistered
 * with input_unregister_device(); input_free_device() should not be
 * called in this case.
 *
 * Note that this function is also used to register managed input devices
 * (ones allocated with devm_input_allocate_device()). Such managed input
 * devices need not be explicitly unregistered or freed, their tear down
 * is controlled by the devres infrastructure. It is also worth noting
 * that tear down of managed input devices is internally a 2-step process:
 * registered managed input device is first unregistered, but stays in
 * memory and can still handle input_event() calls (although events will
 * not be delivered anywhere). The freeing of managed input device will
 * happen later, when devres stack is unwound to the point where device
 * allocation was made.
 */
int input_register_device(struct input_dev *dev)
{
	/* 用于记录输入设备名称的索引值 */
	static atomic_t input_no = ATOMIC_INIT(0);
	struct input_devres *devres = NULL;
/* 输入事件的处理接口指针,用于和设备的事件类型进行匹配 */
	struct input_handler *handler;
	unsigned int packet_size;
	const char *path;
	int error;

	if (dev->devres_managed) {
		devres = devres_alloc(devm_input_device_unregister,
				      sizeof(struct input_devres), GFP_KERNEL);
		if (!devres)
			return -ENOMEM;

		devres->input = dev;
	}

	/* 默认所有的输入设备都支持EV_SYN/SYN_REPORT同步事件 */
	__set_bit(EV_SYN, dev->evbit);

	/* KEY_RESERVED 不支持数据传输到应用层 */
	__clear_bit(KEY_RESERVED, dev->keybit);

	/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
	input_cleanse_bitmasks(dev);

	packet_size = input_estimate_events_per_packet(dev);
	if (dev->hint_events_per_packet < packet_size)
		dev->hint_events_per_packet = packet_size;

	dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
	dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
	if (!dev->vals) {
		error = -ENOMEM;
		goto err_devres_free;
	}


	/*
	 * 如果设备驱动没有指定重复按键(连击),系统默认提供以下的支持
	 * 其中init_timer为连击产生的定时器,时间到调用input_repeat_key函数
	 * 上报,REP_DELAY用于设置重复按键的键值,REP_PERIOD设置延时时间
	 */
	init_timer(&dev->timer);
	if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
		dev->timer.data = (long) dev;
		dev->timer.function = input_repeat_key;
		dev->rep[REP_DELAY] = 250;
		dev->rep[REP_PERIOD] = 33;
	}

	/* 如果设备驱动没有设置自己的获取键值的函数,系统默认 */

	if (!dev->getkeycode)
		dev->getkeycode = input_default_getkeycode;

	/* 如果设备驱动没有指定按键重置函数,系统默认 */
	if (!dev->setkeycode)
		dev->setkeycode = input_default_setkeycode;
	/*设置设备的名字*/
	dev_set_name(&dev->dev, "input%ld",
		     (unsigned long) atomic_inc_return(&input_no) - 1);

	/*添加设备*/
	error = device_add(&dev->dev);
	if (error)
		goto err_free_vals;

	/* 获取并打印设备的绝对路径名称 */
	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
	pr_info("%s as %s\n",
		dev->name ? dev->name : "Unspecified device",
		path ? path : "N/A");
	kfree(path);

	error = mutex_lock_interruptible(&input_mutex);
	if (error)
		goto err_device_del;

	/* 重要,把设备挂到全局的input子系统设备链表input_dev_list上 */
	list_add_tail(&dev->node, &input_dev_list);

	/* 核心重点,input设备在增加到input_dev_list链表上之后,会查找
	 * input_handler_list事件处理链表上的handler进行匹配,这里的匹配
	 * 方式与设备模型的device和driver匹配过程很相似,所有的input
	 * 都挂在input_dev_list上,所有类型的事件都挂在input_handler_list
	 * 上,进行“匹配相亲”*/
	list_for_each_entry(handler, &input_handler_list, node)
		input_attach_handler(dev, handler);

	input_wakeup_procfs_readers();

	mutex_unlock(&input_mutex);

	/* 如果这个值没有设置,系统把输入设备挂入设备链表 */
	if (dev->devres_managed) {
		dev_dbg(dev->dev.parent, "%s: registering %s with devres.\n",
			__func__, dev_name(&dev->dev));
		devres_add(dev->dev.parent, devres);
	}
	return 0;

err_device_del:
	device_del(&dev->dev);
err_free_vals:
	kfree(dev->vals);
	dev->vals = NULL;
err_devres_free:
	devres_free(devres);
	return error;
}

input 事件上报相关

EV_SYN     0x00    同步事件

EV_KEY     0x01    按键事件

EV_REL     0x02    相对坐标(如:鼠标移动,报告相对最后一次位置的偏移)

EV_ABS     0x03    绝对坐标(如:触摸屏或操作杆,报告绝对的坐标位置)

EV_MSC     0x04    其它

EV_SW      0x05    开关

EV_LED     0x11    按键/设备灯

EV_SND     0x12    声音/警报

EV_REP     0x14    重复

EV_FF      0x15    力反馈

EV_PWR    0x16    电源

EV_FF_STATUS    0x17   力反馈状态

EV_MAX    0x1f    事件类型最大个数和提供位掩码支持

Linux输入子系统提供了设备驱动层上报输入事件的函数

//上报按键事件
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);              
//如果你觉得麻烦,你也可以只记住1个函数(因为上述函数都是通过它实现的)
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)

linux触摸屏i2c设备驱动

接下来我们来看这个linux触摸屏驱动的最后一个代码,drv.c。他整合了i2c设备驱动和input设备驱动的整合. <pre class="prettyprint" id="c"> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/i2c.h> #include <linux/err.h> #include <linux/slab.h> #include <linux/fs.h> #include <asm/uaccess.h>

include <linux/errno.h>

#include <linux/platform_device.h> #include <linux/clk.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/init.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/interrupt.h>

define DOWN 1

#define UP 0

define SCREEN_MAX_X 800

#define SCREEN_MAX_Y 480 #define PRESS_MAX 255 #define FT5X0X_NAME “ft5x06”

static struct i2c_client ft5x06_client; static unsigned long count = 1; static struct timer_list timer_ts; /设置定时器:为了支持滑动*/ static struct input_dev *ts_test_dev; unsigned long *gph1con; unsigned int isdetect = 0;

struct work_struct ft5x06_wq; static unsigned int ts_status = DOWN;

/获取x坐标/ static unsigned int get_x_coordinate(void) { unsigned int xh,xl; xh = i2c_smbus_read_byte_data(ft5x06_client, 0x3); xl = i2c_smbus_read_byte_data(ft5x06_client, 0x4); return (((xh&0xf)«8)|xl); } /获得Y坐标/ static unsigned int get_y_coordinate(void) { unsigned int yh,yl; yh = i2c_smbus_read_byte_data(ft5x06_client, 0x5); yl = i2c_smbus_read_byte_data(ft5x06_client, 0x6); return (((yh&0xf)«8)|yl);

} /*up or down *up : 1 down : 0 */ static unsigned int get_ts_status(void) { unsigned int status; status = i2c_smbus_read_byte_data(ft5x06_client, 0x3); status = (status»6)&3; return status; } static void webee210_ft5x06_init(void) { i2c_smbus_write_byte_data(ft5x06_client, 0, 0); i2c_smbus_write_byte_data(ft5x06_client, 0xa4, 0); i2c_smbus_write_byte_data(ft5x06_client, 0xa0, 0); printk(“ft50x6 init …\n”); }

static void touch_detect(void) { unsigned int data_x, data_y; unsigned int x_data[5], y_data[5]; unsigned int i; while(1) { if(ts_status == UP) { printk(“\n up!!!!!!!!!!!!!!!!!!!!!\n”); input_report_abs(ts_test_dev, ABS_PRESSURE, 0); input_report_key(ts_test_dev, BTN_TOUCH, 0); input_sync(ts_test_dev);

		return;
	}
	if(ts_status == DOWN )
	{
		printk("\n down !!!!!!!!!!!!!!!!!!!!!\n");
		for(i = 0; i < 2; i++)
		{	
			x_data[i] = get_x_coordinate();
			y_data[i] = get_y_coordinate();
				
		}

		data_x = (x_data[0] + x_data[1])/2;
		data_y = (y_data[0] + y_data[1])/2;
		
		if(250< data_x && data_x <400)
			data_x = data_x-40;
		if(400< data_x && data_x <800)
			data_x = data_x+30;

		printk("x = %d\n",data_x);
		printk("y = %d\n",data_y);
		
		input_report_abs(ts_test_dev, ABS_X, data_x);
		input_report_abs(ts_test_dev, ABS_Y, data_y);
		input_report_abs(ts_test_dev, ABS_PRESSURE, PRESS_MAX);
		input_report_key(ts_test_dev, BTN_TOUCH, 1);
		input_sync(ts_test_dev);

	}

	printk("count %d\n",count);
	if(count == 65535)
		count = 1;
}
//printk("tasklet\n");	

} /中断上半部分的工作/ static irqreturn_t pen_up_down_handler(int irq, void *dev_id) { if((count++)%2 == DOWN) { ts_status = DOWN; schedule_work(&ft5x06_wq);

}
else
{
	ts_status = UP;
	
}
return IRQ_HANDLED; }

static int ft5x06_probe(struct i2c_client client, const struct i2c_device_id *id) { unsigned int ret; /探测到client,将其赋值给ft5x06_client*/ ft5x06_client = client;

/*1. 分配一个input_dev 结构体*/
ts_test_dev = input_allocate_device();
/*2. 设置支持哪些事件*/
set_bit(EV_KEY, ts_test_dev->evbit);
set_bit(EV_ABS, ts_test_dev->evbit);

/*3. 设置支持事件中的那种事件*/
set_bit(BTN_TOUCH, ts_test_dev->keybit);

 /*设置触摸屏坐标的长宽*/
input_set_abs_params(ts_test_dev, ABS_X, 0, SCREEN_MAX_X, 0, 0);
input_set_abs_params(ts_test_dev, ABS_Y, 0, SCREEN_MAX_Y, 0, 0);
input_set_abs_params(ts_test_dev, ABS_PRESSURE, 0, PRESS_MAX, 0, 0);

/*注册*/
ret = input_register_device(ts_test_dev);

/*注册一个中断, 设为IRQ_TYPE_EDGE_BOTH,按下和放松都会触发中断*/
ret = request_irq(IRQ_EINT(14), pen_up_down_handler, IRQ_TYPE_EDGE_BOTH, "ts_test", NULL);
printk("ret_eint14_irq = %d\n", ret);

printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
  
  /*将中断的下半部分添加到一个工作队列中*/
INIT_WORK(&ft5x06_wq, touch_detect);
webee210_ft5x06_init();	

return 0; }

static int ft5x06_remove(struct i2c_client *client) { printk(“%s %s %d\n”, FILE, FUNCTION, LINE); free_irq(IRQ_EINT(14),NULL); return 0; }

static const struct i2c_device_id ft5x06_id_table[] = { { FT5X0X_NAME, 0 }, {} };

/* 1. 分配/设置i2c_driver */ static struct i2c_driver ft5x06_driver = { .driver = { .name = FT5X0X_NAME, .owner = THIS_MODULE, }, .probe = ft5x06_probe, .remove = ft5x06_remove,

.id_table	= ft5x06_id_table, };

static int ft5x06_drv_init(void) { /* 2. 注册i2c_driver */ i2c_add_driver(&ft5x06_driver);

return 0; }

static void ft5x06_drv_exit(void) { i2c_del_driver(&ft5x06_driver); }

module_init(ft5x06_drv_init); module_exit(ft5x06_drv_exit); MODULE_LICENSE(“GPL”);

</pre>

关于i2c_driver的注册,关键在于实现.probe = ft5x06_probe,ft5x06_probe函数在探测到设备时,将被调用。