linux input子系统实例分析 -- 1,adc 按键

实际项目开发中,如果需要使用多个按键,很多时候都是一个gpio控制一个按键,但是有时候当gpio资源不够的时候,会使用adc的方式,通过按下不同的按键分压不同用来区别按键。

下面就以这种adc的方式去分析input子系统。

在分析之前,想想之前的方式。之前都是按键按下触发中断,然后中断中上报键值或者中断中启动工作队列,工作队列中的工作进行数据上报。

那么如果有使用adc的方式,是没有中断的,那么什么时候上报事件呢?

先看看如下的原理图:

 对应dts如下

adc-keys {
		compatible = "adc-keys";
		io-channels = <&saradc 0>;
		io-channel-names = "buttons";
		poll-interval = <100>;
		keyup-threshold-microvolt = <1800000>;

		esc-key {
			label = "esc";
			linux,code = <KEY_ESC>;
			press-threshold-microvolt = <0>;
		};

		right-key {
			label = "right";
			linux,code = <KEY_RIGHT>;
			press-threshold-microvolt = <400781>;
		};

		left-key {
			label = "left";
			linux,code = <KEY_LEFT>;
			press-threshold-microvolt = <801562>;
		};

		menu-key {
			label = "menu";
			linux,code = <KEY_MENU>;
			press-threshold-microvolt = <1198828>;
		};
	};

对应的驱动代码为  drivers/input/keyboard/adc-keys.c

adc_keys_probe()

static int adc_keys_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
	struct input_polled_dev *poll_dev;
	struct input_dev *input;

    ...

    poll_dev = devm_input_allocate_polled_device(dev);
	if (!poll_dev) {
		dev_err(dev, "failed to allocate input device\n");
		return -ENOMEM;
	}

	if (!device_property_read_u32(dev, "poll-interval", &value))
		poll_dev->poll_interval = value;

	poll_dev->poll = adc_keys_poll;

	input = poll_dev->input;

	input->name = pdev->name;
	input->phys = "adc-keys/input0";

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;

	__set_bit(EV_KEY, input->evbit);
	for (i = 0; i < st->num_keys; i++)
		__set_bit(st->map[i].keycode, input->keybit);

	if (device_property_read_bool(dev, "autorepeat"))
		__set_bit(EV_REP, input->evbit);

	error = input_register_polled_device(poll_dev);
	if (error) {
		dev_err(dev, "Unable to register input device: %d\n", error);
		return error;
	}

	return 0;
}

上面的代码忽略了IIO相关的东西,另外忽略了dts的解析

这里的input设备是 input_polled_devinput_polled_dev中包含了 input_dev

其实在这里就能看出来,adc方式的input事件上报用的是轮询的方式,poll函数是 adc_key_poll

且轮询间隔的时间 dts中通过poll-interval指定,之后的操作input和正常的input设备没有什么区别

如果dts没有制定poll-interval,那么input_register_polled_device中会默认设置为500

int input_register_polled_device(struct input_polled_dev *dev)
{
	struct input_polled_devres *devres = NULL;
	struct input_dev *input = dev->input;
	int error;

	if (dev->devres_managed) {
		devres = devres_alloc(devm_input_polldev_unregister,
				      sizeof(*devres), GFP_KERNEL);
		if (!devres)
			return -ENOMEM;

		devres->polldev = dev;
	}

	input_set_drvdata(input, dev);
	INIT_DELAYED_WORK(&dev->work, input_polled_device_work);

	if (!dev->poll_interval)
		dev->poll_interval = 500;
	if (!dev->poll_interval_max)
		dev->poll_interval_max = dev->poll_interval;

	input->open = input_open_polled_device;
	input->close = input_close_polled_device;

	input->dev.groups = input_polldev_attribute_groups;

	error = input_register_device(input);
	if (error) {
		devres_free(devres);
		return error;
	}

	/*
	 * Take extra reference to the underlying input device so
	 * that it survives call to input_unregister_polled_device()
	 * and is deleted only after input_free_polled_device()
	 * has been invoked. This is needed to ease task of freeing
	 * sparse keymaps.
	 */
	input_get_device(input);

	if (dev->devres_managed) {
		dev_dbg(input->dev.parent, "%s: registering %s with devres.\n",
			__func__, dev_name(&input->dev));
		devres_add(input->dev.parent, devres);
	}

	return 0;
}

那么poll函数什么时候被调用?

注意看上面函数中的 input的open和close

static int input_open_polled_device(struct input_dev *input)
{
	struct input_polled_dev *dev = input_get_drvdata(input);

	if (dev->open)
		dev->open(dev);

	/* Only start polling if polling is enabled */
	if (dev->poll_interval > 0) {
		dev->poll(dev);
		input_polldev_queue_work(dev);
	}

	return 0;
}

可以看到在open(/dev/input/eventX)的时候,就会启动工作队列

static void input_polldev_queue_work(struct input_polled_dev *dev)
{
	unsigned long delay;

	delay = msecs_to_jiffies(dev->poll_interval);
	if (delay >= HZ)
		delay = round_jiffies_relative(delay);

	queue_delayed_work(system_freezable_wq, &dev->work, delay);
}

work函数为input_polled_device_work

static void input_polled_device_work(struct work_struct *work)
{
	struct input_polled_dev *dev =
		container_of(work, struct input_polled_dev, work.work);

	dev->poll(dev);
	input_polldev_queue_work(dev);
}

可以看到poll函数被调用。

综上看来,poll方式的input子系统事件上报很容易理解,下一篇文章,我们使用这种方式自己去写一个poll方式的驱动代码。

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dianlong_lee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值