Linux驅動之觸控螢幕(4)-多點觸控

來源:互聯網
上載者:User
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_BUF0x20/* command buffer (write only) */#define SYS_CMD_BUF0x40/* systerm command buffer (write only) */#define QUERY_BUF0x80/* query buffer (read only) */#define CMD_RSP_BUF0xA0/* command response buffer (read only) */#define SYS_CMD_RSP_BUF0xC0/* systerm command response buffer (read only) */#define POINT_INFO_BUF0xE0/* 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]);elsegoto 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

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.