4.1 概述
此驱动支持it7260触摸屏控制器,最多支持三点触摸,已在CPU: s5pc110、linux-2.6.32.9、android-2.2上测试通过。原本以为三天就能搞定,最后还是用了一个礼拜才弄完。水平有限,可能存在一些bug,请及时反馈给我([email protected])。
触摸屏驱动主要分为两个部分:
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 <[email protected]>"); MODULE_DESCRIPTION("it7260 touchscreen driver");
完整的源码可以通过git来下载:git clone git://github.com/cjok/it7260.git