以下程式碼為整點原子程式碼:
多點觸控相關API函式:
int input_mt_init_slots( struct input_dev *dev, unsigned int num_slots, unsigned int flags) // 初始化slots // 函式引數和返回值含義如下: dev: MT 裝置對應的 input_dev,因為 MT 裝置隸屬於 input_dev。 num_slots:裝置要使用的 SLOT 數量,也就是觸控點的數量。 flags:其他一些 flags 資訊,可設定的 flags 如下所示: void input_mt_slot(struct input_dev *dev, int slot) // 告訴核心層當前上報的是哪一個觸控點的座標 // 函式引數和返回值含義如下: dev: MT 裝置對應的 input_dev。 slot:當前傳送的是哪個 slot 的座標資訊,也就是哪個觸控點。 返回值:無。 void input_mt_report_slot_state( struct input_dev *dev, unsigned int tool_type, bool active) // 關聯ABS_MT_TRACKING_ID // 函式引數和返回值含義如下: dev: MT 裝置對應的 input_dev。 tool_type:觸控型別,可以選擇 MT_TOOL_FINGER(手指)、MT_TOOL_PEN(筆)或 MT_TOOL_PALM(手掌),對於多點電容觸控式螢幕來說一般都是手指。 active:true,連續觸控,input 子系統核心會自動分配一個 ABS_MT_TRACKING_ID 給 slot。 false,觸控點抬起,表示某個觸控點無效了,input 子系統核心會分配一個-1 給 slot,表示觸控點溢位。 void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count) // 上報上層觸控點多了 // 函式引數和返回值含義如下: dev: MT 裝置對應的 input_dev。 use_count:true,有效的觸控點數量;false,追蹤到的觸控點數量多於當前上報的數量。 返回值:無。
驅動檔案ft5x06.c:
#include <linux/module.h> #include <linux/ratelimit.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/i2c.h> #include <linux/uaccess.h> #include <linux/delay.h> #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/input/mt.h> #include <linux/input/touchscreen.h> #include <linux/input/edt-ft5x06.h> #include <linux/i2c.h> #define MAX_SUPPORT_POINTS 5 /* 5點觸控 */ #define TOUCH_EVENT_DOWN 0x00 /* 按下 */ #define TOUCH_EVENT_UP 0x01 /* 抬起 */ #define TOUCH_EVENT_ON 0x02 /* 接觸 */ #define TOUCH_EVENT_RESERVED 0x03 /* 保留 */ /* FT5X06暫存器相關宏定義 */ #define FT5X06_TD_STATUS_REG 0X02 /* 狀態暫存器地址 */ #define FT5x06_DEVICE_MODE_REG 0X00 /* 模式暫存器 */ #define FT5426_IDG_MODE_REG 0XA4 /* 中斷模式 */ #define FT5X06_READLEN 29 /* 要讀取的暫存器個數 */ struct ft5x06_dev { struct device_node *nd; /* 裝置節點 */ int irq_pin,reset_pin; /* 中斷和復位IO */ int irqnum; /* 中斷號 */ void *private_data; /* 私有資料 */ struct input_dev *input; /* input結構體 */ struct i2c_client *client; /* I2C客戶端 */ }; static struct ft5x06_dev ft5x06; /* * @description : 復位FT5X06 * @param - client : 要操作的i2c * @param - multidev: 自定義的multitouch裝置 * @return : 0,成功;其他負值,失敗 */ static int ft5x06_ts_reset(struct i2c_client *client, struct ft5x06_dev *dev) { int ret = 0; if (gpio_is_valid(dev->reset_pin)) { /* 檢查IO是否有效 */ /* 申請復位IO,並且預設輸出低電平 */ ret = devm_gpio_request_one(&client->dev, dev->reset_pin, GPIOF_OUT_INIT_LOW, "edt-ft5x06 reset"); if (ret) { return ret; } msleep(5); gpio_set_value(dev->reset_pin, 1); /* 輸出高電平,停止復位 */ msleep(300); } return 0; } /* * @description : 從FT5X06讀取多個暫存器資料 * @param - dev: ft5x06裝置 * @param - reg: 要讀取的暫存器首地址 * @param - val: 讀取到的資料 * @param - len: 要讀取的資料長度 * @return : 操作結果 */ static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len) { int ret; struct i2c_msg msg[2]; struct i2c_client *client = (struct i2c_client *)dev->client; /* msg[0]為傳送要讀取的首地址 */ msg[0].addr = client->addr; /* ft5x06地址 */ msg[0].flags = 0; /* 標記為傳送資料 */ msg[0].buf = ® /* 讀取的首地址 */ msg[0].len = 1; /* reg長度*/ /* msg[1]讀取資料 */ msg[1].addr = client->addr; /* ft5x06地址 */ msg[1].flags = I2C_M_RD; /* 標記為讀取資料*/ msg[1].buf = val; /* 讀取資料緩衝區 */ msg[1].len = len; /* 要讀取的資料長度*/ ret = i2c_transfer(client->adapter, msg, 2); if(ret == 2) { ret = 0; } else { ret = -EREMOTEIO; } return ret; } /* * @description : 向ft5x06多個暫存器寫入資料 * @param - dev: ft5x06裝置 * @param - reg: 要寫入的暫存器首地址 * @param - val: 要寫入的資料緩衝區 * @param - len: 要寫入的資料長度 * @return : 操作結果 */ static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg, u8 *buf, u8 len) { u8 b[256]; struct i2c_msg msg; struct i2c_client *client = (struct i2c_client *)dev->client; b[0] = reg; /* 暫存器首地址 */ memcpy(&b[1],buf,len); /* 將要寫入的資料複製到陣列b裡面 */ msg.addr = client->addr; /* ft5x06地址 */ msg.flags = 0; /* 標記為寫資料 */ msg.buf = b; /* 要寫入的資料緩衝區 */ msg.len = len + 1; /* 要寫入的資料長度 */ return i2c_transfer(client->adapter, &msg, 1); } /* * @description : 向ft5x06指定暫存器寫入指定的值,寫一個暫存器 * @param - dev: ft5x06裝置 * @param - reg: 要寫的暫存器 * @param - data: 要寫入的值 * @return : 無 */ static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data) { u8 buf = 0; buf = data; ft5x06_write_regs(dev, reg, &buf, 1); } /* * @description : FT5X06中斷服務函式 * @param - irq : 中斷號 * @param - dev_id : 裝置結構。 * @return : 中斷執行結果 */ static irqreturn_t ft5x06_handler(int irq, void *dev_id) { struct ft5x06_dev *multidata = dev_id; u8 rdbuf[29]; int i, type, x, y, id; int offset, tplen; int ret; bool down; offset = 1; /* 偏移1,也就是0X02+1=0x03,從0X03開始是觸控值 */ tplen = 6; /* 一個觸控點有6個暫存器來儲存觸控值 */ memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */ /* 讀取FT5X06觸控點座標從0X02暫存器開始,連續讀取29個暫存器 */ ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN); if (ret) { goto fail; } /* 上報每一個觸控點座標 */ for (i = 0; i < MAX_SUPPORT_POINTS; i++) { u8 *buf = &rdbuf[i * tplen + offset]; /* 以第一個觸控點為例,暫存器TOUCH1_XH(地址0X03),各位描述如下: * bit7:6 Event flag 0:按下 1:釋放 2:接觸 3:沒有事件 * bit5:4 保留 * bit3:0 X軸觸控點的11~8位。 */ type = buf[0] >> 6; /* 獲取觸控型別 */ if (type == TOUCH_EVENT_RESERVED) continue; /* 我們所使用的觸控式螢幕和FT5X06是反過來的 */ x = ((buf[2] << 8) | buf[3]) & 0x0fff; y = ((buf[0] << 8) | buf[1]) & 0x0fff; /* 以第一個觸控點為例,暫存器TOUCH1_YH(地址0X05),各位描述如下: * bit7:4 Touch ID 觸控ID,表示是哪個觸控點 * bit3:0 Y軸觸控點的11~8位。 */ id = (buf[2] >> 4) & 0x0f; down = type != TOUCH_EVENT_UP; input_mt_slot(multidata->input, id); input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down); if (!down) continue; input_report_abs(multidata->input, ABS_MT_POSITION_X, x); input_report_abs(multidata->input, ABS_MT_POSITION_Y, y); } input_mt_report_pointer_emulation(multidata->input, true); input_sync(multidata->input); fail: return IRQ_HANDLED; } /* * @description : FT5x06中斷初始化 * @param - client : 要操作的i2c * @param - multidev: 自定義的multitouch裝置 * @return : 0,成功;其他負值,失敗 */ static int ft5x06_ts_irq(struct i2c_client *client, struct ft5x06_dev *dev) { int ret = 0; /* 1,申請中斷GPIO */ if (gpio_is_valid(dev->irq_pin)) { ret = devm_gpio_request_one(&client->dev, dev->irq_pin, GPIOF_IN, "edt-ft5x06 irq"); if (ret) { dev_err(&client->dev, "Failed to request GPIO %d, error %d\n", dev->irq_pin, ret); return ret; } } /* 2,申請中斷,client->irq就是IO中斷, */ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, &ft5x06); if (ret) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); return ret; } return 0; } /* * @description : i2c驅動的probe函式,當驅動與 * 裝置匹配以後此函式就會執行 * @param - client : i2c裝置 * @param - id : i2c裝置ID * @return : 0,成功;其他負值,失敗 */ static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; ft5x06.client = client; /* 1,獲取裝置樹中的中斷和復位引腳 */ ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0); ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0); /* 2,復位FT5x06 */ ret = ft5x06_ts_reset(client, &ft5x06); if(ret < 0) { goto fail; } /* 3,初始化中斷 */ ret = ft5x06_ts_irq(client, &ft5x06); if(ret < 0) { goto fail; } /* 4,初始化FT5X06 */ ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 進入正常模式 */ ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426中斷模式 */ /* 5,input裝置註冊 */ ft5x06.input = devm_input_allocate_device(&client->dev); if (!ft5x06.input) { ret = -ENOMEM; goto fail; } ft5x06.input->name = client->name; ft5x06.input->id.bustype = BUS_I2C; ft5x06.input->dev.parent = &client->dev; __set_bit(EV_KEY, ft5x06.input->evbit); __set_bit(EV_ABS, ft5x06.input->evbit); __set_bit(BTN_TOUCH, ft5x06.input->keybit); input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0); input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0); input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0); input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0); ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0); if (ret) { goto fail; } ret = input_register_device(ft5x06.input); if (ret) goto fail; return 0; fail: return ret; } /* * @description : i2c驅動的remove函式,移除i2c驅動的時候此函式會執行 * @param - client : i2c裝置 * @return : 0,成功;其他負值,失敗 */ static int ft5x06_ts_remove(struct i2c_client *client) { /* 釋放input_dev */ input_unregister_device(ft5x06.input); return 0; } /* * 傳統驅動匹配表 */ static const struct i2c_device_id ft5x06_ts_id[] = { { "edt-ft5206", 0, }, { "edt-ft5426", 0, }, { /* sentinel */ } }; /* * 裝置樹匹配表 */ static const struct of_device_id ft5x06_of_match[] = { { .compatible = "edt,edt-ft5206", }, { .compatible = "edt,edt-ft5426", }, { /* sentinel */ } }; /* i2c驅動結構體 */ static struct i2c_driver ft5x06_ts_driver = { .driver = { .owner = THIS_MODULE, .name = "edt_ft5x06", .of_match_table = of_match_ptr(ft5x06_of_match), }, .id_table = ft5x06_ts_id, .probe = ft5x06_ts_probe, .remove = ft5x06_ts_remove, }; /* * @description : 驅動入口函式 * @param : 無 * @return : 無 */ static int __init ft5x06_init(void) { int ret = 0; ret = i2c_add_driver(&ft5x06_ts_driver); return ret; } /* * @description : 驅動出口函式 * @param : 無 * @return : 無 */ static void __exit ft5x06_exit(void) { i2c_del_driver(&ft5x06_ts_driver); } module_init(ft5x06_init); module_exit(ft5x06_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("lethe1203");