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");
/* * 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