summaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/Kconfig23
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/gpio_keys.c49
-rw-r--r--drivers/input/imx_keypad.c20
-rw-r--r--drivers/input/input.c27
-rw-r--r--drivers/input/keymap.c1
-rw-r--r--drivers/input/matrix-keymap.c32
-rw-r--r--drivers/input/qt1070.c11
-rw-r--r--drivers/input/specialkeys.c2
-rw-r--r--drivers/input/twl6030_pwrbtn.c18
-rw-r--r--drivers/input/usb_kbd.c17
-rw-r--r--drivers/input/virtio_input.c287
12 files changed, 385 insertions, 104 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index be061683fb..01e0b722c2 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Input device configuration
#
@@ -11,6 +12,17 @@ config INPUT
config INPUT_MATRIXKMAP
bool
+config INPUT_EVBUG
+ bool "Event debugging"
+ help
+ Say Y here if you have a problem with the input subsystem and
+ want all events (keypresses), to be output to
+ the barebox log. While this is useful for debugging, it's also
+ a security threat - your keypresses include your passwords, of
+ course and could be visible to userspace if pstore is configured.
+
+ If unsure, say N.
+
config KEYBOARD_GPIO
bool "GPIO Buttons"
depends on GENERIC_GPIO
@@ -27,7 +39,7 @@ config KEYBOARD_GPIO
config KEYBOARD_IMX_KEYPAD
bool "IMX Keypad"
- depends on ARCH_IMX
+ depends on ARCH_IMX || COMPILE_TEST
select INPUT_MATRIXKMAP
select POLLER
select INPUT
@@ -66,8 +78,17 @@ config KEYBOARD_USB
config INPUT_SPECIALKEYS
bool "Special keys handler"
+ select POLLER
select INPUT
help
Say Y here to handle key events like KEY_RESTART and KEY_POWER.
+config VIRTIO_INPUT
+ bool "Virtio input driver"
+ depends on VIRTIO
+ select POLLER
+ select INPUT
+ help
+ This driver supports virtio keyboard input devices.
+
endmenu
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 36a4204d53..703b78b972 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_INPUT) += input.o keymap.o
obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
obj-$(CONFIG_KEYBOARD_USB) += usb_kbd.o
@@ -6,3 +7,4 @@ obj-$(CONFIG_KEYBOARD_TWL6030) += twl6030_pwrbtn.o
obj-$(CONFIG_KEYBOARD_IMX_KEYPAD) += imx_keypad.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_INPUT_SPECIALKEYS) += specialkeys.o
+obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
diff --git a/drivers/input/gpio_keys.c b/drivers/input/gpio_keys.c
index 38c0f11535..c897acf3bd 100644
--- a/drivers/input/gpio_keys.c
+++ b/drivers/input/gpio_keys.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * Under GPLv2
*/
#include <common.h>
@@ -30,24 +29,24 @@ struct gpio_keys {
struct gpio_key *buttons;
int nbuttons;
- struct poller_struct poller;
+ struct poller_async poller;
struct input_device input;
- struct device_d *dev;
};
-static inline struct gpio_keys *
-poller_to_gk_pdata(struct poller_struct *poller)
+static void gpio_key_poller(void *data)
{
- return container_of(poller, struct gpio_keys, poller);
-}
-
-static void gpio_key_poller(struct poller_struct *poller)
-{
- struct gpio_keys *gk = poller_to_gk_pdata(poller);
+ struct gpio_keys *gk = data;
struct gpio_key *gb;
int i, val;
for (i = 0; i < gk->nbuttons; i++) {
+ gb = &gk->buttons[i];
+
+ if (gpio_slice_acquired(gb->gpio))
+ goto out;
+ }
+
+ for (i = 0; i < gk->nbuttons; i++) {
gb = &gk->buttons[i];
val = gpio_get_value(gb->gpio);
@@ -60,14 +59,16 @@ static void gpio_key_poller(struct poller_struct *poller)
gb->debounce_start = get_time_ns();
input_report_key_event(&gk->input, gb->code, pressed);
- dev_dbg(gk->dev, "%s gpio(%d) as %d\n",
+ dev_dbg(gk->input.parent, "%s gpio(%d) as %d\n",
pressed ? "pressed" : "released", gb->gpio, gb->code);
gb->previous_state = val;
}
}
+out:
+ poller_call_async(&gk->poller, 10 * MSECOND, gpio_key_poller, gk);
}
-static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device_d *dev)
+static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device *dev)
{
struct gpio_keys_platform_data *pdata;
int i;
@@ -93,9 +94,9 @@ static int gpio_keys_probe_pdata(struct gpio_keys *gk, struct device_d *dev)
return 0;
}
-static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device_d *dev)
+static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device *dev)
{
- struct device_node *npkey, *np = dev->device_node;
+ struct device_node *npkey, *np = dev->of_node;
int i = 0, ret;
if (!IS_ENABLED(CONFIG_OFDEVICE) || !IS_ENABLED(CONFIG_OF_GPIO))
@@ -132,16 +133,14 @@ static int gpio_keys_probe_dt(struct gpio_keys *gk, struct device_d *dev)
return 0;
}
-static int __init gpio_keys_probe(struct device_d *dev)
+static int __init gpio_keys_probe(struct device *dev)
{
int ret, i, gpio;
struct gpio_keys *gk;
gk = xzalloc(sizeof(*gk));
- gk->dev = dev;
-
- if (dev->device_node)
+ if (dev->of_node)
ret = gpio_keys_probe_dt(gk, dev);
else
ret = gpio_keys_probe_pdata(gk, dev);
@@ -160,16 +159,17 @@ static int __init gpio_keys_probe(struct device_d *dev)
gk->buttons[i].previous_state = gk->buttons[i].active_low;
}
- gk->poller.func = gpio_key_poller;
-
+ gk->input.parent = dev;
ret = input_device_register(&gk->input);
if (ret)
return ret;
- ret = poller_register(&gk->poller);
+ ret = poller_async_register(&gk->poller, dev_name(dev));
if (ret)
return ret;
+ poller_call_async(&gk->poller, 10 * MSECOND, gpio_key_poller, gk);
+
return 0;
}
@@ -177,8 +177,9 @@ static struct of_device_id key_gpio_of_ids[] = {
{ .compatible = "gpio-keys", },
{ }
};
+MODULE_DEVICE_TABLE(of, key_gpio_of_ids);
-static struct driver_d gpio_keys_driver = {
+static struct driver gpio_keys_driver = {
.name = "gpio_keys",
.probe = gpio_keys_probe,
.of_compatible = DRV_OF_COMPAT(key_gpio_of_ids),
diff --git a/drivers/input/imx_keypad.c b/drivers/input/imx_keypad.c
index 44ff9b7856..e57a884630 100644
--- a/drivers/input/imx_keypad.c
+++ b/drivers/input/imx_keypad.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the IMX keypad port.
* Copyright (C) 2009 Alberto Panizzo <maramaopercheseimorto@gmail.com>
* Copyright (C) 2012 Christian Kapeller <christian.kapeller@cmotion.eu>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
*/
/*
@@ -76,7 +72,6 @@
struct imx_keypad {
struct input_device input;
struct clk *clk;
- struct device_d *dev;
void __iomem *mmio_base;
struct poller_struct poller;
@@ -203,10 +198,6 @@ static void imx_keypad_fire_events(struct imx_keypad *keypad,
input_report_key_event(&keypad->input, keypad->keycodes[code],
matrix_volatile_state[col] & (1 << row));
-
- dev_dbg(keypad->dev, "Event code: %d, val: %d",
- keypad->keycodes[code],
- matrix_volatile_state[col] & (1 << row));
}
}
}
@@ -362,7 +353,7 @@ static void imx_keypad_inhibit(struct imx_keypad *keypad)
writew(0xff00, keypad->mmio_base + KPCR);
}
-static int __init imx_keypad_probe(struct device_d *dev)
+static int __init imx_keypad_probe(struct device *dev)
{
struct resource *iores;
struct imx_keypad *keypad;
@@ -371,7 +362,6 @@ static int __init imx_keypad_probe(struct device_d *dev)
keypad = xzalloc(sizeof(struct imx_keypad));
- keypad->dev = dev;
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -410,10 +400,11 @@ static int __init imx_keypad_probe(struct device_d *dev)
keypad->poller.func = imx_keypad_check_for_events;
- ret = poller_register(&keypad->poller);
+ ret = poller_register(&keypad->poller, dev_name(dev));
if (ret)
return ret;
+ keypad->input.parent = dev;
ret = input_device_register(&keypad->input);
if (ret)
return ret;
@@ -425,8 +416,9 @@ static __maybe_unused struct of_device_id imx_keypad_dt_ids[] = {
{ .compatible = "fsl,imx21-kpp", },
{ }
};
+MODULE_DEVICE_TABLE(of, imx_keypad_dt_ids);
-static struct driver_d imx_keypad_driver = {
+static struct driver imx_keypad_driver = {
.name = "imx-kpp",
.probe = imx_keypad_probe,
.of_compatible = DRV_OF_COMPAT(imx_keypad_dt_ids),
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 1e8f6e178e..1a47929351 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1,13 +1,6 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "input: " fmt
#include <common.h>
#include <init.h>
@@ -42,6 +35,15 @@ void input_report_key_event(struct input_device *idev, unsigned int code, int va
if (code > KEY_MAX)
return;
+ /*
+ * We don't use pr_debug here as we want to output the message
+ * to the log, even if CONFIG_COMPILE_LOGLEVEL < MSG_DEBUG and
+ * the DEBUG mcro wasn't defined for the file.
+ */
+ if (IS_ENABLED(CONFIG_INPUT_EVBUG))
+ pr_print(MSG_DEBUG, "Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
+ dev_name(idev->parent), EV_KEY, code, value);
+
if (value)
set_bit(code, idev->keys);
else
@@ -165,6 +167,9 @@ static void input_console_notify(struct input_notifier *in,
if (ic->modstate[4] || ic->modstate[5])
modstate |= 1 << 2;
+ if (ev->code >= NR_KEYS)
+ return;
+
if (modstate & (1 << 1)) {
ascii = keycode_bb_keys[ev->code];
ascii = ascii >= 'a' ? CTL_CH(ascii) : 0;
@@ -201,7 +206,7 @@ static int input_init(void)
ic->fifo = kfifo_alloc(32);
ic->notifier.notify = input_console_notify;
input_register_notfier(&ic->notifier);
- poller_async_register(&ic->poller);
+ poller_async_register(&ic->poller, "input");
return console_register(&ic->console);
}
diff --git a/drivers/input/keymap.c b/drivers/input/keymap.c
index fbe396b11f..a16e537a51 100644
--- a/drivers/input/keymap.c
+++ b/drivers/input/keymap.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/types.h>
#include <input/keyboard.h>
diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c
index 288b6a4b53..70551d4a2b 100644
--- a/drivers/input/matrix-keymap.c
+++ b/drivers/input/matrix-keymap.c
@@ -1,36 +1,27 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <input/matrix_keypad.h>
-static int matrix_keypad_parse_of_keymap(struct device_d *dev,
+static int matrix_keypad_parse_of_keymap(struct device *dev,
unsigned int row_shift,
unsigned short *keymap)
{
unsigned int proplen, i, size;
const __be32 *prop;
- struct device_node *np = dev->device_node;
+ struct device_node *np = dev->of_node;
const char *propname = "linux,keymap";
prop = of_get_property(np, propname, &proplen);
if (!prop) {
- dev_err(dev, "OF: %s property not defined in %s\n",
- propname, np->full_name);
+ dev_err(dev, "OF: %s property not defined in %pOF\n",
+ propname, np);
return -ENOENT;
}
if (proplen % sizeof(u32)) {
- dev_err(dev, "OF: Malformed keycode property %s in %s\n",
- propname, np->full_name);
+ dev_err(dev, "OF: Malformed keycode property %s in %pOF\n",
+ propname, np);
return -EINVAL;
}
@@ -64,13 +55,14 @@ static int matrix_keypad_parse_of_keymap(struct device_d *dev,
* an array of keycodes that is suitable for using in a standard matrix
* keyboard driver that uses row and col as indices.
*/
-int matrix_keypad_build_keymap(struct device_d *dev, const struct matrix_keymap_data *keymap_data,
- unsigned int row_shift,
- unsigned short *keymap)
+int matrix_keypad_build_keymap(struct device *dev,
+ const struct matrix_keymap_data *keymap_data,
+ unsigned int row_shift,
+ unsigned short *keymap)
{
int i;
- if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node)
+ if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node)
return matrix_keypad_parse_of_keymap(dev, row_shift, keymap);
if (!keymap_data)
diff --git a/drivers/input/qt1070.c b/drivers/input/qt1070.c
index 59acee5c39..c0fd85b03d 100644
--- a/drivers/input/qt1070.c
+++ b/drivers/input/qt1070.c
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
- *
- * Under GPLv2
*/
#include <common.h>
@@ -178,7 +177,7 @@ static int qt1070_getc(struct console_device *cdev)
return code;
}
-static int qt1070_pdata_init(struct device_d *dev, struct qt1070_data *data)
+static int qt1070_pdata_init(struct device *dev, struct qt1070_data *data)
{
struct qt1070_platform_data *pdata = dev->platform_data;
int ret;
@@ -208,7 +207,7 @@ err:
return ret;
}
-static int qt1070_probe(struct device_d *dev)
+static int qt1070_probe(struct device *dev)
{
struct console_device *cdev;
struct qt1070_data *data;
@@ -269,7 +268,7 @@ static int qt1070_probe(struct device_d *dev)
console_register(&data->cdev);
- ret = poller_register(&data->poller);
+ ret = poller_register(&data->poller, dev_name(dev));
if (ret)
goto err;
@@ -279,7 +278,7 @@ err:
return ret;
}
-static struct driver_d qt1070_driver = {
+static struct driver qt1070_driver = {
.name = "qt1070",
.probe = qt1070_probe,
};
diff --git a/drivers/input/specialkeys.c b/drivers/input/specialkeys.c
index a3f2bf4e4f..f9693dc383 100644
--- a/drivers/input/specialkeys.c
+++ b/drivers/input/specialkeys.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2019 Ahmad Fatoum, Pengutronix
#include <common.h>
diff --git a/drivers/input/twl6030_pwrbtn.c b/drivers/input/twl6030_pwrbtn.c
index fc4c728778..3e9e01f38b 100644
--- a/drivers/input/twl6030_pwrbtn.c
+++ b/drivers/input/twl6030_pwrbtn.c
@@ -1,14 +1,4 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-or-later
#include <common.h>
#include <init.h>
@@ -67,7 +57,7 @@ static int twl6030_pwrbtn_getc(struct console_device *cdev)
return code;
}
-static int __init twl6030_pwrbtn_probe(struct device_d *dev)
+static int __init twl6030_pwrbtn_probe(struct device *dev)
{
struct twl6030_pwrbtn_internal_data *idata;
struct twl6030_pwrbtn_platform_data *pdata;
@@ -97,10 +87,10 @@ static int __init twl6030_pwrbtn_probe(struct device_d *dev)
idata->cdev.getc = twl6030_pwrbtn_getc;
console_register(&idata->cdev);
- return poller_register(&idata->poller);
+ return poller_register(&idata->poller, dev_name(dev));
}
-static struct driver_d twl6030_pwrbtn_driver = {
+static struct driver twl6030_pwrbtn_driver = {
.name = "twl6030_pwrbtn",
.probe = twl6030_pwrbtn_probe,
};
diff --git a/drivers/input/usb_kbd.c b/drivers/input/usb_kbd.c
index a2b92c2856..2e75aabf3d 100644
--- a/drivers/input/usb_kbd.c
+++ b/drivers/input/usb_kbd.c
@@ -1,26 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* USB keyboard driver for barebox
*
* (C) Copyright 2001 Denis Peter, MPL AG Switzerland
* (C) Copyright 2015 Peter Mamonov
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
#include <init.h>
#include <clock.h>
#include <poller.h>
-#include <usb/usb.h>
+#include <linux/usb/usb.h>
#include <string.h>
#include <dma.h>
#include <input/input.h>
@@ -198,13 +188,14 @@ static int usb_kbd_probe(struct usb_device *usbdev,
} else
dev_dbg(&usbdev->dev, "poll keyboard via int ep\n");
+ data->input.parent = &usbdev->dev;
ret = input_device_register(&data->input);
if (ret) {
dev_err(&usbdev->dev, "can't register input\n");
return ret;
}
- ret = poller_async_register(&data->poller);
+ ret = poller_async_register(&data->poller, "usb-kbd");
if (ret) {
dev_err(&usbdev->dev, "can't setup poller\n");
return ret;
diff --git a/drivers/input/virtio_input.c b/drivers/input/virtio_input.c
new file mode 100644
index 0000000000..655a905172
--- /dev/null
+++ b/drivers/input/virtio_input.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <poller.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+#include <input/input.h>
+#include <sound.h>
+#include <dt-bindings/input/linux-event-codes.h>
+
+#include <uapi/linux/virtio_ids.h>
+#include <uapi/linux/virtio_input.h>
+
+struct virtio_input {
+ struct input_device idev;
+ struct virtio_device *vdev;
+ struct virtqueue *evt, *sts;
+ struct virtio_input_event evts[64];
+ struct poller_struct poller;
+ struct sound_card beeper;
+ unsigned long sndbit[BITS_TO_LONGS(SND_CNT)];
+};
+
+static void virtinput_queue_evtbuf(struct virtio_input *vi,
+ struct virtio_input_event *evtbuf)
+{
+ struct virtio_sg sg[1];
+ virtio_sg_init_one(sg, evtbuf, sizeof(*evtbuf));
+ virtqueue_add_inbuf(vi->evt, sg, 1);
+}
+
+static int virtinput_recv_events(struct virtio_input *vi)
+{
+ struct device *dev = &vi->vdev->dev;
+ struct virtio_input_event *event;
+ unsigned int len;
+ int i = 0;
+
+ while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
+ if (le16_to_cpu(event->type) == EV_KEY)
+ input_report_key_event(&vi->idev, le16_to_cpu(event->code),
+ le32_to_cpu(event->value));
+
+ pr_debug("\n%s: input event #%td received (type=%u, code=%u, value=%u)\n",
+ dev_name(dev),
+ event - &vi->evts[0],
+ le16_to_cpu(event->type), le16_to_cpu(event->code),
+ le32_to_cpu(event->value));
+
+ virtinput_queue_evtbuf(vi, event);
+ i++;
+ }
+
+ return i;
+}
+
+/*
+ * On error we are losing the status update, which isn't critical as
+ * this is used for the bell.
+ */
+static int virtinput_send_status(struct sound_card *beeper, unsigned freq, unsigned duration)
+{
+ struct virtio_input *vi = container_of(beeper, struct virtio_input, beeper);
+ struct virtio_input_event *stsbuf;
+ struct virtio_sg sg[1];
+ u16 code;
+ int rc;
+
+ stsbuf = kzalloc(sizeof(*stsbuf), 0);
+ if (!stsbuf)
+ return -ENOMEM;
+
+ code = vi->sndbit[0] & BIT_MASK(SND_TONE) ? SND_TONE : SND_BELL;
+
+ stsbuf->type = cpu_to_le16(EV_SND);
+ stsbuf->code = cpu_to_le16(code);
+ stsbuf->value = cpu_to_le32(freq);
+ virtio_sg_init_one(sg, stsbuf, sizeof(*stsbuf));
+
+ rc = virtqueue_add_outbuf(vi->sts, sg, 1);
+ virtqueue_kick(vi->sts);
+
+ if (rc != 0)
+ kfree(stsbuf);
+ return rc;
+}
+
+static int virtinput_recv_status(struct virtio_input *vi)
+{
+ struct virtio_input_event *stsbuf;
+ unsigned int len;
+ int i = 0;
+
+ while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL) {
+ kfree(stsbuf);
+ i++;
+ }
+
+ return i;
+}
+
+static void virtinput_poll_vqs(struct poller_struct *poller)
+{
+ struct virtio_input *vi = container_of(poller, struct virtio_input, poller);
+
+ int bufs = 0;
+
+ bufs += virtinput_recv_events(vi);
+ bufs += virtinput_recv_status(vi);
+
+ if (bufs)
+ virtqueue_kick(vi->evt);
+}
+
+static u8 virtinput_cfg_select(struct virtio_input *vi,
+ u8 select, u8 subsel)
+{
+ u8 size;
+
+ virtio_cwrite_le(vi->vdev, struct virtio_input_config, select, &select);
+ virtio_cwrite_le(vi->vdev, struct virtio_input_config, subsel, &subsel);
+ virtio_cread_le(vi->vdev, struct virtio_input_config, size, &size);
+ return size;
+}
+
+static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
+ unsigned long *bits, unsigned int bitcount)
+{
+ unsigned int bit;
+ u8 *virtio_bits;
+ u8 bytes;
+
+ bytes = virtinput_cfg_select(vi, select, subsel);
+ if (!bytes)
+ return;
+ if (bitcount > bytes * 8)
+ bitcount = bytes * 8;
+
+ /*
+ * Bitmap in virtio config space is a simple stream of bytes,
+ * with the first byte carrying bits 0-7, second bits 8-15 and
+ * so on.
+ */
+ virtio_bits = kzalloc(bytes, GFP_KERNEL);
+ if (!virtio_bits)
+ return;
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config,
+ u.bitmap),
+ virtio_bits, bytes);
+ for (bit = 0; bit < bitcount; bit++) {
+ if (virtio_bits[bit / 8] & (1 << (bit % 8)))
+ __set_bit(bit, bits);
+ }
+ kfree(virtio_bits);
+}
+
+static void virtinput_fill_evt(struct virtio_input *vi)
+{
+ int i, size;
+
+ size = virtqueue_get_vring_size(vi->evt);
+ if (size > ARRAY_SIZE(vi->evts))
+ size = ARRAY_SIZE(vi->evts);
+ for (i = 0; i < size; i++)
+ virtinput_queue_evtbuf(vi, &vi->evts[i]);
+ virtqueue_kick(vi->evt);
+}
+
+static int virtinput_init_vqs(struct virtio_input *vi)
+{
+ struct virtqueue *vqs[2];
+ int err;
+
+
+ err = virtio_find_vqs(vi->vdev, 2, vqs);
+ if (err)
+ return err;
+
+ vi->evt = vqs[0];
+ vi->sts = vqs[1];
+
+ return 0;
+}
+
+static int virtinput_probe(struct virtio_device *vdev)
+{
+ struct virtio_input *vi;
+ char name[64];
+ size_t size;
+ int err;
+
+ if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
+ return -ENODEV;
+
+ vi = kzalloc(sizeof(*vi), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vdev->priv = vi;
+ vi->vdev = vdev;
+
+ err = virtinput_init_vqs(vi);
+ if (err)
+ goto err_init_vq;
+
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u.string),
+ name, min(size, sizeof(name)));
+ name[size] = '\0';
+
+ dev_info(&vdev->dev, "'%s' detected\n", name);
+
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
+ vi->sndbit, SND_CNT);
+
+ virtio_device_ready(vdev);
+
+ vi->idev.parent = &vdev->dev;
+ err = input_device_register(&vi->idev);
+ if (err)
+ goto err_input_register;
+
+ virtinput_fill_evt(vi);
+
+ vi->poller.func = virtinput_poll_vqs;
+ snprintf(name, sizeof(name), "%s/input0", dev_name(&vdev->dev));
+
+ err = poller_register(&vi->poller, name);
+ if (err)
+ goto err_poller_register;
+
+ if (IS_ENABLED(CONFIG_SOUND) &&
+ (vi->sndbit[0] & (BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE)))) {
+ struct sound_card *beeper;
+
+ beeper = &vi->beeper;
+ beeper->name = basprintf("%s/beeper0", dev_name(&vdev->dev));
+ beeper->beep = virtinput_send_status;
+
+ err = sound_card_register(&vi->beeper);
+ if (err)
+ dev_warn(&vdev->dev, "bell registration failed: %pe\n", ERR_PTR(err));
+ else
+ dev_info(&vdev->dev, "bell registered\n");
+ }
+
+ return 0;
+
+err_poller_register:
+ input_device_unregister(&vi->idev);
+err_input_register:
+ vdev->config->del_vqs(vdev);
+err_init_vq:
+ kfree(vi);
+ return err;
+}
+
+static void virtinput_remove(struct virtio_device *vdev)
+{
+ struct virtio_input *vi = vdev->priv;
+
+ vdev->config->reset(vdev);
+ poller_unregister(&vi->poller);
+ input_device_unregister(&vi->idev);
+ vdev->config->del_vqs(vdev);
+
+ kfree(vi);
+}
+
+static const struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver virtio_input_driver = {
+ .driver.name = "virtio_input",
+ .id_table = id_table,
+ .probe = virtinput_probe,
+ .remove = virtinput_remove,
+};
+device_virtio_driver(virtio_input_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtio input device driver");
+MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
+MODULE_AUTHOR("Ahmad Fatoum <a.fatoum@pengutronix.de>");