---
# System prepended metadata

title: MultiTouchPanel(MTP) driver 框架

---

# MultiTouchPanel(MTP) driver 框架


```
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/regmap.h>
#include <linux/slab.h>

#define MTP_ADDR 0x70
#define MTP_MAX_X  800
#define MTP_MAX_Y  480

#define MTP_IRQ  123

#define MTP_NAME "xxxx"
#define MTP_MAX_ID 1000 /* 由硬體決定 */

struct input_dev *ts_dev;
static struct work_struct mtp_work;


static irqreturn_t mtp_interrupt(int irq, void *dev_id) {

	/* 本該:
	 * 獲取觸點資料, 並上報
	 * 但是I2C是慢速裝置, 不該放在中斷服務程式中操作
	 */

	/* 使用工作佇列, 讓核心執行緒來操作 */
	schedule_work(&mtp_work);

	return IRQ_HANDLED;
}

static void mtp_work_func(struct work_struct *work)
{
	/* 讀取I2C裝置, 獲得觸點資料, 並上報 */
	/* 讀取 */

	/* 上報 */
	if (/* 沒有觸點 */) {
		input_mt_sync(ts_dev);
		input_sync(ts_dev);
		return;
	}
	
	for (/* 每一個點 */) {
	    input_report_abs(ts_dev, ABS_MT_POSITION_X, x);
	    input_report_abs(ts_dev, ABS_MT_POSITION_Y, y);
	    input_report_abs(ts_dev, ABS_MT_TRACKING_ID, id);
	    input_mt_sync(ts_dev);	
	}
	input_sync(ts_dev);
}

static int __devinit mtp_probe(struct i2c_client *client,
				  const struct i2c_device_id *id)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	/* 終於進入了輸入子系統 */

	/* 分配input_dev */
	ts_dev = input_allocate_device();

	/* 設定 */
	/* 2.1 能產生哪類事件 */
	set_bit(EV_SYN, ts_dev->evbit);
	set_bit(EV_ABS, ts_dev->evbit);

	/* 2.2 能產生這類事件中的哪些 */
	set_bit(ABS_MT_TRACKING_ID, ts_dev->absbit);
	set_bit(ABS_MT_POSITION_X,  ts_dev->absbit);
	set_bit(ABS_MT_POSITION_Y,  ts_dev->absbit);

	/* 2.3 這些事件的範圍 */
	input_set_abs_params(ts_dev, ABS_MT_TRACKING_ID, 0, MTP_MAX_ID, 0, 0);
	input_set_abs_params(ts_dev, ABS_MT_POSITION_X, 0, MTP_MAX_X, 0, 0);
	input_set_abs_params(ts_dev, ABS_MT_POSITION_Y, 0, MTP_MAX_Y, 0, 0);	

	ts_dev->name = MTP_NAME; /* android會根據它找到配置檔案 */

	/* 註冊 */
	input_register_device(ts_dev);

	/* 硬體相關操作 */
	INIT_WORK(&mtp_work, mtp_work_func);
	
	request_irq(MTP_IRQ, mtp_interrupt,
			IRQ_TYPE_EDGE_FALLING /*IRQF_TRIGGER_FALLING*/, "my_mtp", ts_dev);
	
	return 0;
}

static int __devexit mtp_remove(struct i2c_client *client)
{
	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);

	free_irq(MTP_IRQ, ts_dev);
	cancel_work_sync(&mtp_work);

	input_unregister_device(ts_dev);
	input_free_device(ts_dev);
	
	return 0;
}

static const struct i2c_device_id mtp_id_table[] = {
	{ "my_mtp", 0 },
	{}
};

static int mtp_detect(struct i2c_client *client,
		       struct i2c_board_info *info)
{
	/* 能執行到這裡, 表示該addr的裝置是存在的
	 * 但是有些裝置單憑地址無法分辨(A晶片的地址是0x50, B晶片的地址也是0x50)
	 * 還需要進一步讀寫I2C裝置來分辨是哪款晶片
	 * detect就是用來進一步分辨這個晶片是哪一款，並且設定info->type
	 */
	
	printk("mtp_detect : addr = 0x%x\n", client->addr);

	/* 進一步判斷是哪一款 */
	
	strlcpy(info->type, "my_mtp", I2C_NAME_SIZE);
	return 0;

	/* 返回0之後, 會建立一個新的I2C裝置
	 * i2c_new_device(adapter, &info), 其中的info->type = "my_mtp"
	 */
}

static const unsigned short addr_list[] = { MTP_ADDR, I2C_CLIENT_END };

/* 1. 分配/設定i2c_driver */
static struct i2c_driver mtp_driver = {
	.class  = I2C_CLASS_HWMON, /* 表示去哪些介面卡上找裝置 */
	.driver	= {
		.name	= "myMTP",
		.owner	= THIS_MODULE,
	},
	.probe		= mtp_probe,
	.remove		= __devexit_p(mtp_remove),
	.id_table	= mtp_id_table,
	.detect     = mtp_detect,  /* 用這個函式來檢測裝置確實存在 */
	.address_list	= addr_list,   /* 這些裝置的地址 */
};

static int mtp_drv_init(void)
{
	/* 2. 註冊i2c_driver */
	i2c_add_driver(&mtp_driver);
	
	return 0;
}

static void mtp_drv_exit(void)
{
	i2c_del_driver(&mtp_driver);
}


module_init(mtp_drv_init);
module_exit(mtp_drv_exit);
MODULE_LICENSE("GPL");



```