linux input: TTP229触摸键盘驱动

1. 前言

限于作者能力水平,本文可能存在的谬误,因此而给读者带来的损失,作者不做任何承诺。

2. 背景

门禁设备加入一款 TTP229 芯片,作为输入键盘,没有驱动代码提供,需为该设备编写Linux input驱动。设备相关原理图如下:
在这里插入图片描述

3. 实现

3.1 定义设备数据结构

struct ttp229_device {
	struct platform_device *pdev;
	
	struct input_dev *input_dev;
#if (SAMPLE_FACILITY == 0)
	struct timer_list input_timer; /* 使用 timer 轮询方式 */
#elif (SAMPLE_FACILITY == 1)
	struct delayed_work input_work; /* 使用 work 方式 */
#endif
	__u16 state;
};

3.2 设备初始化

static int ttp229_probe(struct platform_device *pdev)
{
	struct ttp229_device *ttp229;
	int result;

	/* 创建设备对象 */
	ttp229 = kzalloc(sizeof(*ttp229), GFP_KERNEL);
	if (!ttp229) {
		result = -ENOMEM;
		dev_err(&pdev->dev, "out of memory\n");
		goto err_out;
	}

	ttp229->pdev = pdev;
	platform_set_drvdata(pdev, ttp229);

	/* TTP229 是 I2C 设备,但硬件设计上,是通过 GPIO 模拟 I2C bus */
	result = ttp229_timing_init(ttp229);
	if (result)
		goto err_init;

	/* 创建input设备,注册到内核inut子系统 */
	result = ttp229_input_init(ttp229);
	if (result)
		goto err_init;

    dev_info(&pdev->dev, "%s successful.\n", __func__);
	return 0;

err_init:
	kfree(ttp229);
err_out:
	dev_err(&pdev->dev, "%s failed, result = %d\n", __func__, result);
	return result;
}

用两个 GPIO 模拟 I2C 的 SCL,SDO:

static int ttp229_timing_init(struct ttp229_device *ttp229)
{
	int result;

	if (!ttp229)
		return -EINVAL;

	result = ttp229_gpio_request(TTP229_SCL, "ttp229-scl");
	if (result)
		goto scl_req_err;
	gpio_direction_output(TTP229_SCL, !SAMPLE_LEVEL);
	
	result = ttp229_gpio_request(TTP229_SDO, "ttp229-sdo");
	if (result)
		goto sdo_req_err;
	gpio_direction_output(TTP229_SDO, !SAMPLE_LEVEL);

	ttp229->state = 0xFFFF;

	return 0;

sdo_req_err:
	gpio_free(TTP229_SCL);
scl_req_err:
	return result;
}

注册设备到内核input子系统:

static int ttp229_input_init(struct ttp229_device *ttp229)
{
	struct input_dev *input_dev;
	int i, result;

	input_dev = input_allocate_device();
	if (!input_dev) {
		result = -ENOMEM;
		dev_err(&ttp229->pdev->dev, "input_allocate_device error\n");
		goto input_alloc_err;
	}
	input_dev->name = "ttp229-keypad";
	input_dev->dev.parent = &ttp229->pdev->dev;
	for(i = 0; i < ARRAY_SIZE(key_hash_tb); i++)
		input_set_capability(input_dev, EV_KEY, key_hash_tb[i].code);
	__set_bit(EV_REP, input_dev->evbit);

	ttp229->input_dev = input_dev;
	result = input_register_device(ttp229->input_dev);
	if (result) {
		dev_err(&ttp229->pdev->dev, "input_register_device error\n");
		goto input_register_err;
	}

	/* 无中断信号,只能用 timer 或者 work 方式轮训输入 */
#if (SAMPLE_FACILITY == 0)
	setup_timer(&ttp229->input_timer, ttp229_input_timer_fn, (unsigned long)ttp229);
	mod_timer(&ttp229->input_timer, jiffies + msecs_to_jiffies(5));
#elif (SAMPLE_FACILITY == 1)
	INIT_DELAYED_WORK(&ttp229->input_work, ttp229_input_work);
	schedule_delayed_work(&ttp229->input_work, msecs_to_jiffies(5));
#endif

	return 0;

input_register_err:
	input_free_device(ttp229->input_dev);
input_alloc_err:
	return result;
}

3.3 按键采样和上报

#if (SAMPLE_FACILITY == 0)
static void ttp229_input_timer_fn(unsigned long data)
{
	struct ttp229_device *ttp229 = (struct ttp229_device *)data;

	ttp229_key_report(ttp229, ttp229_sample());
	mod_timer(&ttp229->input_timer, jiffies + msecs_to_jiffies(SAMPLE_PERIOD));
}
#elif (SAMPLE_FACILITY == 1)
static void ttp229_input_work(struct work_struct *work)
{
	struct delayed_work *input_work = to_delayed_work(work);
	struct ttp229_device *ttp229 = 
			container_of(input_work, struct ttp229_device, input_work);

	ttp229_key_report(ttp229, ttp229_sample());
	schedule_delayed_work(&ttp229->input_work, msecs_to_jiffies(SAMPLE_PERIOD));
}
#endif

3.3.1 按键采样

static const struct ttp229_key_map {
	__u16 data;
	unsigned int code;
} key_hash_tb[] = {
	{ 0xFDFD, KEY_1  },  
	{ 0xFBFB, KEY_2  },  
	{ 0xF7F7, KEY_3  }, 
	{ 0xEFEF, KEY_4  }, 
	{ 0xDFDF, KEY_5  }, 
	{ 0xBFBF, KEY_6  }, 
	{ 0x7F7F, KEY_7  }, 
	{ 0xFEFF, KEY_8  },
#if 0	
	/* TODO: remain 8 keys */
	{ 0xFFFF, KEY_9  },  
	{ 0xFFFF, KEY_0  },  
	{ 0xFFFF, KEY_F1 }, 
	{ 0xFFFF, KEY_F2 }, 
	{ 0xFFFF, KEY_F3 }, 
	{ 0xFFFF, KEY_F4 }, 
	{ 0xFFFF, KEY_F5 }, 
	{ 0xFFFF, KEY_F6 },
#endif
};

static unsigned int ttp229_key_hash(__u16 data)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(key_hash_tb); i++)
		if (data == key_hash_tb[i].data)
			return key_hash_tb[i].code;
	return 0xFFFF;
}

static __u16 ttp229_sample(void)
{
	__u16 data = 0x0000;
	int i;

	gpio_direction_output(TTP229_SDO, SAMPLE_LEVEL);
	udelay(93);
	gpio_set_value(TTP229_SDO, !SAMPLE_LEVEL);
	udelay(10);

	gpio_direction_input(TTP229_SDO);
	for (i = 0; i < 16; ++i) {
		gpio_set_value(TTP229_SCL, SAMPLE_LEVEL);
		data |= (gpio_get_value(TTP229_SDO) << i);
		udelay(SAMPLE_CLK_T_US - 20);

		gpio_set_value(TTP229_SCL, !SAMPLE_LEVEL);;
		udelay(SAMPLE_CLK_T_US);
	}

	gpio_direction_output(TTP229_SDO, !SAMPLE_LEVEL);

	return data;
}

3.3.2 按键上报

static void ttp229_key_report(struct ttp229_device *ttp229, __u16 new_state)
{
	if (ttp229->state == new_state) /* long tap not support now!!! */
		return;

	input_report_key(ttp229->input_dev, 
				ttp229_key_hash(new_state == 0xFFFF ? ttp229->state : new_state), 
				new_state == 0xFFFF ? 0 : 1);
	input_sync(ttp229->input_dev);

	ttp229->state = new_state;
}

完整的驱动代码见TTP229驱动

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值