Linux驱动之触摸屏(4)-多点触控

Linux驱动之触摸屏(4)-多点触控

4.1 概述

         此驱动支持it7260触摸屏控制器,最多支持三点触摸,已在CPU: s5pc110、linux-2.6.32.9、android-2.2上测试通过。原本以为三天就能搞定,最后还是用了一个礼拜才弄完。水平有限,可能存在一些bug,请及时反馈给我(cjok.liao@gmail.com)。

         触摸屏驱动主要分为两个部分:

         I2C驱动部分:主要负责将设备挂接到I2C总线上,实现数据传输;

         输入子系统部分:负责把获取到的数据上报到用户空间。

          中断下半部采用延迟的工作队列,完成数据的解析和上报工作。

         其他都要参考控制器的数据手册来完成,比如像数据包的解析,数据传输协议(标准的I2C协议)。


4.2 驱动解析

/*
* multi touch screen driver for it7260
* base on multi-touch protocol A
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/timer.h>

/* buffer address */
#define CMD_BUF		0x20	/* command buffer (write only) */
#define SYS_CMD_BUF	0x40	/* systerm command buffer (write only) */
#define QUERY_BUF	0x80	/* query buffer (read only) */
#define CMD_RSP_BUF	0xA0	/* command response buffer (read only) */
#define SYS_CMD_RSP_BUF	0xC0	/* systerm command response buffer (read only) */
#define POINT_INFO_BUF	0xE0	/* point information buffer (read only) */

/* 构造一个触摸屏设备结构体 */
struct it7260_ts_priv {
	struct i2c_client *client; /* I2C 设备 */
	struct input_dev *input;   /* 输入设备结构体 */
	struct delayed_work work;  /* 延迟工作队列 */
	struct mutex mutex;        /* 互斥体 */
	int irq;                   /* 中断 */
};

/**
 * 发送和接受函数,虽然内核中提供了i2c_master_recv和i2c_master_send,
 * 但是这两个函数只适合单个msg的情况
*/
/**
* i2c_master_read_it7260 - issue two I2C message in master receive mode
* @client: handler to slave device
* @buf_index: buffer address
* @buf_data: where to store data read from slave
* @len_data: the bytes of buf_data to read 
*
* returns negative errno, or else the number of bytes read
*/
static int i2c_master_read_it7260(struct i2c_client *client, 
		unsigned char buf_index, unsigned char *buf_data, 
		unsigned short len_data)
{
	int ret;
	struct i2c_msg msgs[2] = {
		{
			.addr = client->addr,
			.flags = I2C_M_NOSTART,
			.len = 1,
			.buf = &buf_index,
		},
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = len_data,
			.buf = buf_data,
		}
	};

	ret = i2c_transfer(client->adapter, msgs, 2);

	return (ret == 2) ? len_data : ret;
}

/**
* i2c_master_write_it7260 - issue a single I2C message in master transmit mode
* @client: handler to slave device
* @buf_index: buffer address
* @buf_data: data that wile be write to the slave
* @len_data: the bytes of buf_data to write
*
* returns negative errno, or else the number of bytes written
*/
static int i2c_master_write_it7260(struct i2c_client *client, 
		unsigned char buf_index, unsigned char const *buf_data,
		unsigned short len_data)
{
	unsigned char buf[2];
	int ret;

	struct i2c_msg msgs[1] = {
		{
			.addr = client->addr,
			.flags = 0, /* default write flag */
			.len = len_data + 1, 
			.buf = buf,
		}
	};

	buf[0] = buf_index;
	memcpy(&buf[1], buf_data, len_data);

	ret = i2c_transfer(client->adapter, msgs, 1);
	
	return (ret == 1) ? sizeof(buf) : ret;
}

/**
* 延迟工作,当产生中断时调用,负责从I2C总线上读取数据,然后按照数
* 据手册上的进行解析,然后进行上报。
*/
static void it7260_ts_poscheck(struct work_struct *work)
{
	struct it7260_ts_priv *priv = container_of(work, 
	 			struct it7260_ts_priv, work.work);
	unsigned char buf[14];
	unsigned short xpos[3] = {0}, ypos[3] = {0};
	unsigned char event[3] = {0};
	unsigned char query = 0;
	int ret, i;

	mutex_lock(&priv->mutex);

	i2c_master_read_it7260(priv->client, QUERY_BUF, &query, 1);
	if (!(query & 0x80)) {
		dev_err(&priv->client->dev, "no finger touch\n");
		goto out;
	}

	memset(&buf, 0, sizeof(buf));

	ret = i2c_master_read_it7260(priv->client, POINT_INFO_BUF, buf, 14);
	if (ret != 14) {
		dev_err(&priv->client->dev, "failed to read point info buffer\n");
		goto out;
	}

	/* touch key */
	if (buf[0] == 0x41) {
		dev_info(&priv->client->dev, "the key number %d\n", buf[1]);
		if (buf[1] == 0x04)
			input_report_key(priv->input, KEY_HOME, !!buf[2]);
		else if (buf[1] == 0x03)
			input_report_key(priv->input, KEY_MENU, !!buf[2]);
		else if (buf[1] == 0x02)
			input_report_key(priv->input, KEY_BACK, !!buf[2]);
		else if (buf[1] == 0x01)
			input_report_key(priv->input, KEY_POWER, !!buf[2]);
		else
			goto out;

		input_sync(priv->input);
		goto out;
	}

	/* finger 0 */
	if (buf[0] & 0x01) {
		xpos[0] = ((buf[3] & 0x0F) << 8) | buf[2];
		ypos[0] = ((buf[3] & 0xF0) << 4) | buf[4];
		event[0] = buf[5] & 0x0F;
	}

	/* finger 1 */
	if (buf[0] & 0x02) {
		xpos[1] = ((buf[7] & 0x0F) << 8) | buf[6];
		ypos[1] = ((buf[7] & 0xF0) << 4) | buf[8];
		event[1] = buf[9] & 0x0F;
	}

	/* finger 2 */
	if (buf[0] & 0x04) {
		xpos[2] = ((buf[11] & 0x0F) << 8) | buf[10];
		ypos[2] = ((buf[11] & 0xF0) << 4) | buf[12];
		event[2] = buf[13] & 0x0F;
	}

	for (i = 0; i < 3; i++) {
		input_report_abs(priv->input, ABS_MT_POSITION_X, ypos[i]);
		input_report_abs(priv->input, ABS_MT_POSITION_Y, xpos[i]);
		input_report_abs(priv->input, ABS_MT_TOUCH_MAJOR, !!event[i]);
		input_report_abs(priv->input, ABS_MT_WIDTH_MAJOR, 0);
		input_mt_sync(priv->input);
		dev_info(&priv->client->dev, "finger %d > xpos = %d, \
		ypos = %d, event = %d\n", i, ypos[i], xpos[i], event[i]);
	}
	input_sync(priv->input);

out:
	mutex_unlock(&priv->mutex);
	enable_irq(priv->irq);
}

/* 中断服务子程序,产生中断后,延迟(HZ/20)个tick后调度工作 */
static irqreturn_t it7260_ts_isr(int irq, void *dev_id)
{
	struct it7260_ts_priv *priv = dev_id;

	disable_irq_nosync(irq);
	schedule_delayed_work(&priv->work, HZ / 20);

	return IRQ_HANDLED;
}

/**
* it7260_identify_capsensor - identify capacitance sensor model
*
* returns error -1, or else suc 0
*/
static int it7260_identify_capsensor(struct i2c_client *client)
{
	unsigned char buf[10];
	unsigned char query = 0;

	do {
		i2c_master_read_it7260(client, QUERY_BUF, &query, 1);
	} while (query & 0x01);

	/* 0x00: the command of identify cap sensor */
	buf[0] = 0x00;	
	i2c_master_write_it7260(client, CMD_BUF, buf, 1);

	do {
		i2c_master_read_it7260(client, QUERY_BUF, &query, 1);
	} while (query & 0x01);

	memset(&buf, 0, sizeof(buf));
	i2c_master_read_it7260(client, CMD_RSP_BUF, buf, 10);
	dev_err(&client->dev, "len = %d, %c%c%c\n", buf[0], buf[1], buf[2], buf[3]);
	if (buf[1] != 'I' || buf[2] != 'T' || buf[3] != 'E') 
		return -1;

	return 0;
}

/* probe函数,在i2c设备和i2c驱动匹配时会调用此函数来完成相应的工作 */
static int it7260_ts_probe(struct i2c_client *client, 
			const struct i2c_device_id *idp)
{
	struct it7260_ts_priv *priv;
	struct input_dev *input;
	int error;

	/* 识别此设备型号是否为it7260 */
	error = it7260_identify_capsensor(client);
	if (error) {
		dev_err(&client->dev, "cannot identify the touch screen\n");
		goto err0;
	}

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		dev_err(&client->dev, "failed to allocate driver data\n");
		error = -ENOMEM;
		goto err0;
	}
	
	/* 初始化mutex */
	mutex_init(&priv->mutex);

	dev_set_drvdata(&client->dev, priv);

	/* 分配一个input设备 */
	input = input_allocate_device();
	if (!input) {
		dev_err(&client->dev, "failed to allocate input device\n");
		error = -ENOMEM;
		goto err1;
	}

	/* 设置input设备所支持的事件类型 */
	input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);

	input_set_capability(input, EV_KEY, KEY_MENU);
	input_set_capability(input, EV_KEY, KEY_BACK);
	input_set_capability(input, EV_KEY, KEY_HOME);
	input_set_capability(input, EV_KEY, KEY_POWER);

	input_set_abs_params(input, ABS_MT_POSITION_X, 0, 600, 0, 0);
	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 1024, 0, 0);
	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 15, 0, 0);
	input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 2, 0, 0);

	input->name = "it7260 touch screen";
	input->phys = "I2C";
	input->id.bustype = BUS_I2C;

	input_set_drvdata(input, priv);

	priv->client = client;
	priv->input = input;
	/* 初始化延迟工作队列 */
	INIT_DELAYED_WORK(&priv->work, it7260_ts_poscheck);
	priv->irq = client->irq;

	/* 向输入子系统注册此input设备 */
	error = input_register_device(input);
	if (error) {
		dev_err(&client->dev, "failed to register input device\n");
		goto err1;
	}

	/* 注册中断,低电平触发 */
	error = request_irq(priv->irq, it7260_ts_isr, IRQF_TRIGGER_LOW,
						client->name, priv);
	if (error) {
		dev_err(&client->dev, "unable to request touchscreen IRQ\n");
		goto err2;
	}

	device_init_wakeup(&client->dev, 1);
	return 0;
	
err2:
	input_unregister_device(input);
	input = NULL;
err1:
	input_free_device(input);
	kfree(priv);
err0:
	dev_set_drvdata(&client->dev, NULL);
	return error;
}

/* 当没有使用此设备时调用移除函数进行注销 */
static int __devexit it7260_ts_remove(struct i2c_client *client)
{
	struct it7260_ts_priv *priv = dev_get_drvdata(&client->dev);

	free_irq(priv->irq, priv);
	input_unregister_device(priv->input);
	kfree(priv);

	dev_set_drvdata(&client->dev, NULL);

	return 0;
}

/* 电源管理函数 */
static int it7260_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
	int ret = -1;
	u8 suspend_cmd[] = {0x04, 0x00, 0x02};
	struct it7260_ts_priv *priv = i2c_get_clientdata(client);

	if (device_may_wakeup(&client->dev)) {
		enable_irq_wake(priv->irq);
		if (sizeof(suspend_cmd) == i2c_master_write_it7260(client, 
					CMD_BUF, suspend_cmd, 3)) 
			ret = 0;
	} 

	return ret;
}

static int it7260_ts_resume(struct i2c_client *client)
{
	int ret = -1;
	unsigned char query;
	struct it7260_ts_priv *priv = i2c_get_clientdata(client);

	if (device_may_wakeup(&client->dev)) {
		i2c_master_read_it7260(client, QUERY_BUF, &query, 1);
		disable_irq_wake(priv->irq);
		ret = 0;
	}

	return ret;
}

/* 驱动支持的设备列表,用来匹配 */
static const struct i2c_device_id it7260_ts_id[] = {
	{"IT7260", 0},
	{}			/* should not omitted */
};
MODULE_DEVICE_TABLE(i2c, it7260_ts_id);

static struct i2c_driver it7260_ts_driver = {
	.driver = {
		.name = "IT7260-ts",
	},
	.probe = it7260_ts_probe,
	.remove = __devexit_p(it7260_ts_remove),
	.suspend = it7260_ts_suspend,
	.resume = it7260_ts_resume,
	.id_table = it7260_ts_id,
};

/* 模块加载函数 */
static int __init it7260_ts_init(void)
{
	return i2c_add_driver(&it7260_ts_driver);	
}

/* 模块卸载函数 */
static void __exit it7260_ts_exit(void)
{
	i2c_del_driver(&it7260_ts_driver);
}

module_init(it7260_ts_init);
module_exit(it7260_ts_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CJOK <cjok.liao@gmail.com>");
MODULE_DESCRIPTION("it7260 touchscreen driver");

完整的源码可以通过git来下载:git clone git://github.com/cjok/it7260.git

 


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值