首页 » 技术分享 » 输入设备驱动

输入设备驱动

 
文章目录

输入设备是典型的字符驱动。

工作机理

输入设备驱动工作机理:

  1. 底层在按键发生时,产生一个中断(或者驱动通过timer定时查询), 
  2. 然后CPU通过总线(SPI, I2C)读取键值,并将它们放入到一个缓冲区
  3. 字符设备驱动管理该缓冲区。驱动的read() API让用户可以读取键值

只有中断,键值是具体设备相关,其他是输入设备通用。

因此,内核设计了输入子系统

输入子系统框架

引用链接中架构图,点击打开链接

按键驱动分析

那么以GPIO按键驱动做个分析。

GPIO driver

drivers/input/keyboard/gpio_keys.c

源码链接

probe()

对照输入子系统架构图, GPIO按键驱动调用了“Input Core"中提供的通用API, 见下面代码中的

input = devm_input_allocate_device(dev);
error = input_register_device(input);

 /dev/input目录下的事件都是在驱动中调用input_register_device(struct input_dev *dev)产生的。


static int gpio_keys_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
	struct fwnode_handle *child = NULL;
	struct gpio_keys_drvdata *ddata;
	struct input_dev *input;
	size_t size;
	int i, error;
	int wakeup = 0;
	if (!pdata) {
		pdata = gpio_keys_get_devtree_pdata(dev);
		if (IS_ERR(pdata))
			return PTR_ERR(pdata);
	}
	size = sizeof(struct gpio_keys_drvdata) +
			pdata->nbuttons * sizeof(struct gpio_button_data);
	ddata = devm_kzalloc(dev, size, GFP_KERNEL);
	if (!ddata) {
		dev_err(dev, "failed to allocate state\n");
		return -ENOMEM;
	}
	ddata->keymap = devm_kcalloc(dev,
				     pdata->nbuttons, sizeof(ddata->keymap[0]),
				     GFP_KERNEL);
	if (!ddata->keymap)
		return -ENOMEM;
	input = devm_input_allocate_device(dev);
	if (!input) {
		dev_err(dev, "failed to allocate input device\n");
		return -ENOMEM;
	}
	ddata->pdata = pdata;
	ddata->input = input;
	mutex_init(&ddata->disable_lock);
	platform_set_drvdata(pdev, ddata);
	input_set_drvdata(input, ddata);
	input->name = pdata->name ? : pdev->name;
	input->phys = "gpio-keys/input0";
	input->dev.parent = dev;
	input->open = gpio_keys_open;
	input->close = gpio_keys_close;
	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;
	input->keycode = ddata->keymap;
	input->keycodesize = sizeof(ddata->keymap[0]);
	input->keycodemax = pdata->nbuttons;
	/* Enable auto repeat feature of Linux input subsystem */
	if (pdata->rep)
		__set_bit(EV_REP, input->evbit);
	for (i = 0; i < pdata->nbuttons; i++) {
		const struct gpio_keys_button *button = &pdata->buttons[i];
		if (!dev_get_platdata(dev)) {
			child = device_get_next_child_node(dev, child);
			if (!child) {
				dev_err(dev,
					"missing child device node for entry %d\n",
					i);
				return -EINVAL;
			}
		}
		error = gpio_keys_setup_key(pdev, input, ddata,
					    button, i, child);
		if (error) {
			fwnode_handle_put(child);
			return error;
		}
		if (button->wakeup)
			wakeup = 1;
	}
	fwnode_handle_put(child);
	error = devm_device_add_group(dev, &gpio_keys_attr_group);
	if (error) {
		dev_err(dev, "Unable to export keys/switches, error: %d\n",
			error);
		return error;
	}
	error = input_register_device(input);
	if (error) {
		dev_err(dev, "Unable to register input device, error: %d\n",
			error);
		return error;
	}
	device_init_wakeup(dev, wakeup);
	return 0;
}

ISR

GPIO按键通过 input core提供的API, input_event()和input_sync(),来汇报按键事件

static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
{
	struct gpio_button_data *bdata = dev_id;
	struct input_dev *input = bdata->input;
	unsigned long flags;
	BUG_ON(irq != bdata->irq);
	spin_lock_irqsave(&bdata->lock, flags);
	if (!bdata->key_pressed) {
		if (bdata->button->wakeup)
			pm_wakeup_event(bdata->input->dev.parent, 0);
		input_event(input, EV_KEY, *bdata->code, 1);
		input_sync(input);
		if (!bdata->release_delay) {
			input_event(input, EV_KEY, *bdata->code, 0);
			input_sync(input);
			goto out;
		}
		bdata->key_pressed = true;
	}
	if (bdata->release_delay)
		mod_timer(&bdata->release_timer,
			jiffies + msecs_to_jiffies(bdata->release_delay));
out:
	spin_unlock_irqrestore(&bdata->lock, flags);
	return IRQ_HANDLED;
}

Input Core

设备注册,事件,同步等相关API, drivers/input/input.c , 点击打开链接

linux VFS相关API (file_operations), drivers/input/evdev.c, 点击打开链接

platform device

一般的,在板级的初始化c文件里面。比如:arch\arm\mach-mx6\board-mx6q-sabresd.c 

#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
#define GPIO_BUTTON(gpio_num, ev_code, act_low, descr, wake, debounce)    \
{                                \
    .gpio        = gpio_num,                \
    .type        = EV_KEY,                \
    .code        = ev_code,                \
    .active_low    = act_low,                \
    .desc        = "btn " descr,                \
    .wakeup        = wake,                    \
    .debounce_interval = debounce,                \
}

static struct gpio_keys_button imx6q_buttons[] = {
    GPIO_BUTTON(SABRESD_KEY_USER1, KEY_VOLUMEUP, 1, "user-key-1", 0, 1),
    GPIO_BUTTON(SABRESD_KEY_USER2, KEY_VOLUMEDOWN, 1, "user-key-2", 0, 1),
    GPIO_BUTTON(SABRESD_KEY_WHIBUSB, KEY_F1, 1, "whibusb", 0, 1),
    GPIO_BUTTON(SABRESD_KEY_WHIBUSL, KEY_F2, 1, "whibusl", 0, 1),
    GPIO_BUTTON(SABRESD_KEY_WHIBUSR, KEY_F3, 1, "whibusr", 0, 1),
};

static struct gpio_keys_platform_data imx6q_button_data = {
    .buttons    = imx6q_buttons,
    .nbuttons    = ARRAY_SIZE(imx6q_buttons),
};

static struct platform_device imx6q_button_device = {
    .name        = "gpio-keys",
    .id        = -1,
    .num_resources  = 0,
    .dev        = {
        .platform_data = &imx6q_button_data,
    }
};

static void __init imx6q_add_device_buttons(void)
{
    platform_device_register(&imx6q_button_device);
}
#else
static void __init imx6q_add_device_buttons(void) {}
#endif

上面注册了5个按键设备。然后在board_init()初始化函数里面,添加imx6q_add_device_buttons()。我们就可以通过应用层操作了。比如:按下某个按键的时候,在read()函数中获取哪个键被按下。


利用proc文件系统。cat /proc/bus/input/devices  查看一下按键是哪个event。

I: Bus=0011 Vendor=0002 Product=0006 Version=0000
N: Name="ImExPS/2 Generic Explorer Mouse"
P: Phys=isa0060/serio1/input0
S: Sysfs=/devices/platform/i8042/serio1/input/input4
U: Uniq=
H: Handlers=mouse0 event3 
B: PROP=1
B: EV=7
B: KEY=1f0000 0 0 0 0 0 0 0 0
B: REL=143


platform driver

drivers/input/keyboard/gpio_keys.c

static struct platform_driver gpio_keys_device_driver = {
	.probe		= gpio_keys_probe,
	.driver		= {
		.name	= "gpio-keys",
		.pm	= &gpio_keys_pm_ops,
		.of_match_table = gpio_keys_of_match,
	}
};

转载自原文链接, 如需删除请联系管理员。

原文链接:输入设备驱动,转载请注明来源!

0