usb_hid: mode led handling to shadow input-leds.c

The former implementation relied on input drvdata always pointing to
struct hid_device, which is not true for Wacom touch devices (at least).
Now, we implement the input handler for devices providing LEDs
(evbit[EV_LED] set) only and use input_inject_event() to set the LED
states.
This commit is contained in:
Christian Helmuth 2024-01-19 14:46:59 +01:00
parent d6d1b8e025
commit 092e4a001f
13 changed files with 244 additions and 241 deletions

View File

@ -113,7 +113,7 @@ install_config {
</route>
</start>
<start name="usb_hid_drv" caps="140">
<start name="usb_hid_drv" caps="180">
<resource name="RAM" quantum="11M"/>
<config use_report="yes" capslock_led="rom" numlock_led="rom" scrlock_led="rom"/>
<route>
@ -132,23 +132,41 @@ install_config {
<resource name="RAM" quantum="4M"/>
<provides> <service name="ROM"/> </provides>
<config verbose="no">
<rom name="capslock">
<inline> <capslock enabled="no"/> </inline>
<sleep milliseconds="250" />
<inline> <capslock enabled="yes"/> </inline>
<sleep milliseconds="250" />
</rom>
<rom name="numlock">
<inline> <numlock enabled="no"/> </inline>
<inline> <numlock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <numlock enabled="yes"/> </inline>
<sleep milliseconds="500" />
<inline> <numlock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <numlock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <numlock enabled="yes"/> </inline>
<sleep milliseconds="500" />
</rom>
<rom name="capslock">
<inline> <capslock enabled="no"/> </inline>
<sleep milliseconds="500"/>
<inline> <capslock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <capslock enabled="yes"/> </inline>
<sleep milliseconds="500" />
<inline> <capslock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <capslock enabled="yes"/> </inline>
<sleep milliseconds="500" />
</rom>
<rom name="scrlock">
<inline> <scrlock enabled="no"/> </inline>
<sleep milliseconds="1000" />
<inline> <scrlock enabled="no"/> </inline>
<sleep milliseconds="500"/>
<inline> <scrlock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <scrlock enabled="no"/> </inline>
<sleep milliseconds="500" />
<inline> <scrlock enabled="yes"/> </inline>
<sleep milliseconds="1000" />
<sleep milliseconds="500" />
<inline> <scrlock enabled="yes"/> </inline>
<sleep milliseconds="500" />
</rom>
</config>
<route>

View File

@ -68,7 +68,7 @@ install_config {
</route>
</start>
<start name="usb_hid_drv" caps="140">
<start name="usb_hid_drv" caps="180">
<resource name="RAM" quantum="11M"/>
<config use_report="yes" capslock_led="rom" numlock_led="rom" scrlock_led="rom"/>
<route>

View File

@ -1,206 +0,0 @@
/*
* \brief Keyboard LED handling
* \author Sebastian Sumpf
* \date 2023-06-29
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/usb.h>
struct keyboard
{
struct input_dev *input_dev;
struct usb_interface *intf;
struct usb_device *udev;
struct list_head list;
};
enum Update_state { NONE, UPDATE, BLOCKED };
struct led_update
{
enum Update_state state;
struct completion update;
unsigned leds;
};
static LIST_HEAD(_keyboards);
static struct led_update _led_update;
static bool keyboard_match(struct keyboard *kbd, struct input_dev *input_dev)
{
return kbd->input_dev == input_dev;
}
static void keyboard_update(struct keyboard *kbd, unsigned leds)
{
usb_control_msg(kbd->udev, usb_sndctrlpipe(kbd->udev, 0),
0x9, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x200,
kbd->intf->cur_altsetting->desc.bInterfaceNumber,
&leds, 1, 500);
}
void lx_led_state_update(bool capslock, bool numlock, bool scrlock)
{
struct keyboard *kbd;
unsigned leds = 0;
leds |= capslock ? 1u << LED_CAPSL : 0;
leds |= numlock ? 1u << LED_NUML : 0;
leds |= scrlock ? 1u << LED_SCROLLL : 0;
_led_update.leds = leds;
_led_update.state = UPDATE;
/* udpdate keyboards */
list_for_each_entry(kbd, &_keyboards, list) {
keyboard_update(kbd, leds);
}
if (_led_update.state == BLOCKED)
complete(&_led_update.update);
_led_update.state = NONE;
}
static void wait_for_update(void)
{
if (_led_update.state == UPDATE) {
_led_update.state = BLOCKED;
wait_for_completion(&_led_update.update);
}
}
static int led_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct keyboard *kbd;
struct input_handle *handle;
wait_for_update();
handle = (struct input_handle *)kzalloc(sizeof(*handle), 0);
if (!handle) return -ENOMEM;
handle->dev = input_get_device(dev);
handle->handler = handler;
kbd = (struct keyboard *)kzalloc(sizeof(*kbd), GFP_KERNEL);
if (!kbd) goto err;
kbd->input_dev = input_get_device(dev);
kbd->intf = container_of(kbd->input_dev->dev.parent->parent, struct usb_interface, dev);
kbd->udev = interface_to_usbdev(kbd->intf);
INIT_LIST_HEAD(&kbd->list);
list_add_tail(&kbd->list, &_keyboards);
keyboard_update(kbd, _led_update.leds);
input_register_handle(handle);
return 0;
err:
kfree(handle);
return -ENOMEM;
}
static void led_disconnect(struct input_handle *handle)
{
struct input_dev *dev = handle->dev;
struct keyboard *kbd, *temp;
wait_for_update();
list_for_each_entry_safe(kbd, temp, &_keyboards, list) {
if (keyboard_match(kbd, dev)) {
list_del(&kbd->list);
kfree(kbd);
}
}
input_unregister_handle(handle);
input_put_device(dev);
kfree(handle);
}
static bool led_match(struct input_handler *handler, struct input_dev *dev)
{
struct hid_device *hid = (struct hid_device *)input_get_drvdata(dev);
struct hid_report *report;
struct hid_usage *usage;
unsigned i, j;
/* search report for keyboard entries */
list_for_each_entry(report, &hid->report_enum[0].report_list, list) {
for (i = 0; i < report->maxfield; i++)
for (j = 0; j < report->field[i]->maxusage; j++) {
usage = report->field[i]->usage + j;
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) {
return true;
}
}
}
return false;
}
static struct input_device_id led_ids[] = {
{ .driver_info = 1 }, /* match all */
{ },
};
MODULE_DEVICE_TABLE(input, led_ids);
static struct input_handler led_handler = {
.name = "keyboard_led",
.connect = led_connect,
.disconnect = led_disconnect,
.id_table = led_ids,
.match = led_match,
.id_table = led_ids,
};
static int __init input_leds_init(void)
{
_led_update.state = NONE;
init_completion(&_led_update.update);
return input_register_handler(&led_handler);
}
static void __exit input_leds_exit(void)
{
input_unregister_handler(&led_handler);
}
/**
* Let's hook into the input_leds initcall, so we do not need to register
* an additional one
*/
module_init(input_leds_init);
module_exit(input_leds_exit);

View File

@ -18,6 +18,7 @@
#include <lx_emul/init.h>
#include <lx_emul/task.h>
#include <lx_emul/input_leds.h>
#include <lx_kit/env.h>
#include <genode_c_api/event.h>
@ -127,9 +128,9 @@ struct Leds
while (true) {
led.handle_config();
lx_led_state_update(led.capslock.enabled(),
led.numlock.enabled(),
led.scrlock.enabled());
lx_emul_input_leds_update(led.capslock.enabled(),
led.numlock.enabled(),
led.scrlock.enabled());
led.led_task_handler.block_and_schedule();
}

View File

@ -6,11 +6,11 @@ arch/arm/lib/strchr.S
arch/arm/lib/testclearbit.S
arch/arm/lib/testsetbit.S
drivers/base/bus.c
drivers/base/core.c
drivers/base/class.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/devres.c
drivers/base/driver.c
drivers/base/dd.c
drivers/base/property.c
drivers/hid/hid-apple.c
drivers/hid/hid-cherry.c
@ -41,8 +41,8 @@ kernel/time/clocksource.c
kernel/time/hrtimer.c
kernel/time/jiffies.c
kernel/time/ntp.c
kernel/time/tick-broadcast.c
kernel/time/tick-broadcast-hrtimer.c
kernel/time/tick-broadcast.c
kernel/time/tick-common.c
kernel/time/tick-oneshot.c
kernel/time/tick-sched.c
@ -53,7 +53,6 @@ kernel/time/timekeeping.c
kernel/time/timer.c
kernel/time/timer_list.c
kernel/workqueue.c
mm/util.c
lib/bitmap.c
lib/ctype.c
lib/find_bit.c
@ -65,8 +64,9 @@ lib/klist.c
lib/kobject.c
lib/kstrtox.c
lib/radix-tree.c
lib/xarray.c
lib/siphash.c
lib/string.c
lib/string_helpers.c
lib/vsprintf.c
lib/xarray.c
mm/util.c

View File

@ -4,11 +4,11 @@ arch/arm64/lib/strcmp.S
arch/arm64/lib/strlen.S
arch/arm64/lib/strncmp.S
drivers/base/bus.c
drivers/base/core.c
drivers/base/class.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/devres.c
drivers/base/driver.c
drivers/base/dd.c
drivers/base/property.c
drivers/hid/hid-apple.c
drivers/hid/hid-cherry.c
@ -39,8 +39,8 @@ kernel/time/clocksource.c
kernel/time/hrtimer.c
kernel/time/jiffies.c
kernel/time/ntp.c
kernel/time/tick-broadcast.c
kernel/time/tick-broadcast-hrtimer.c
kernel/time/tick-broadcast.c
kernel/time/tick-common.c
kernel/time/tick-oneshot.c
kernel/time/tick-sched.c
@ -51,7 +51,6 @@ kernel/time/timekeeping.c
kernel/time/timer.c
kernel/time/timer_list.c
kernel/workqueue.c
mm/util.c
lib/bitmap.c
lib/ctype.c
lib/find_bit.c
@ -63,8 +62,9 @@ lib/klist.c
lib/kobject.c
lib/kstrtox.c
lib/radix-tree.c
lib/xarray.c
lib/siphash.c
lib/string.c
lib/string_helpers.c
lib/vsprintf.c
lib/xarray.c
mm/util.c

View File

@ -1,10 +1,10 @@
arch/x86/lib/hweight.S
drivers/base/bus.c
drivers/base/core.c
drivers/base/class.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/devres.c
drivers/base/driver.c
drivers/base/dd.c
drivers/base/property.c
drivers/hid/hid-apple.c
drivers/hid/hid-cherry.c
@ -46,7 +46,6 @@ kernel/time/timekeeping.c
kernel/time/timer.c
kernel/time/timer_list.c
kernel/workqueue.c
mm/util.c
lib/bitmap.c
lib/ctype.c
lib/find_bit.c
@ -57,8 +56,9 @@ lib/klist.c
lib/kobject.c
lib/kstrtox.c
lib/radix-tree.c
lib/xarray.c
lib/siphash.c
lib/string.c
lib/string_helpers.c
lib/vsprintf.c
lib/xarray.c
mm/util.c

View File

@ -1,10 +1,10 @@
arch/x86/lib/hweight.S
drivers/base/bus.c
drivers/base/core.c
drivers/base/class.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/devres.c
drivers/base/driver.c
drivers/base/dd.c
drivers/base/property.c
drivers/hid/hid-apple.c
drivers/hid/hid-cherry.c
@ -46,7 +46,6 @@ kernel/time/timekeeping.c
kernel/time/timer.c
kernel/time/timer_list.c
kernel/workqueue.c
mm/util.c
lib/bitmap.c
lib/ctype.c
lib/find_bit.c
@ -57,8 +56,9 @@ lib/klist.c
lib/kobject.c
lib/kstrtox.c
lib/radix-tree.c
lib/xarray.c
lib/siphash.c
lib/string.c
lib/string_helpers.c
lib/vsprintf.c
lib/xarray.c
mm/util.c

View File

@ -5,11 +5,11 @@ INC_DIR = $(PRG_DIR)/../.. $(REP_DIR)/src/lib/usb_client
SRC_C += dummies.c \
generated_dummies.c \
led.c \
lx_emul.c \
lx_user.c
SRC_C += lx_emul/shadow/drivers/input/evdev.c \
lx_emul/shadow/drivers/input/input-leds.c \
lx_emul/virt/shadow/drivers/usb/core/message.c \
lx_emul/virt/shadow/drivers/usb/core/urb.c \
lx_emul/virt/usb_client.c

View File

@ -0,0 +1,27 @@
/*
* \brief Lx_emul support for input leds
* \author Christian Helmuth
* \date 2024-01-19
*/
/*
* Copyright (C) 2024 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__INPUT_LEDS_H_
#define _LX_EMUL__INPUT_LEDS_H_
#ifdef __cplusplus
extern "C" {
#endif
extern void lx_emul_input_leds_update(bool capslock, bool numlock, bool scrolllock);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__INPUT_LEDS_H_ */

View File

@ -311,6 +311,9 @@ static void evdev_event_generator(struct genode_event_generator_ctx *ctx,
bool processed = false;
/* filter injected EV_LED updates */
if (v->type == EV_LED) continue;
/* filter input_repeat_key() */
if ((v->type == EV_KEY) && (v->value > 1)) continue;

View File

@ -0,0 +1,160 @@
/*
* \brief Input LED handling
* \author Sebastian Sumpf
* \author Christian Helmuth
* \date 2023-06-29
*
* This implementation is derived from drivers/input/input-leds.c.
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/usb.h>
#include <lx_emul/input_leds.h>
struct led_handler
{
struct list_head list;
struct input_handle handle;
};
static LIST_HEAD(led_handlers);
enum Update_state { NONE, UPDATE, BLOCKED };
struct led_update
{
enum Update_state state;
struct completion update;
bool capsl, numl, scrolll;
};
static struct led_update led_update;
static void update_leds(struct led_handler *handler)
{
input_inject_event(&handler->handle, EV_LED, LED_CAPSL, led_update.capsl);
input_inject_event(&handler->handle, EV_LED, LED_NUML, led_update.numl);
input_inject_event(&handler->handle, EV_LED, LED_SCROLLL, led_update.scrolll);
}
void lx_emul_input_leds_update(bool capslock, bool numlock, bool scrolllock)
{
struct led_handler *handler;
led_update.state = UPDATE;
led_update.capsl = capslock;
led_update.numl = numlock;
led_update.scrolll = scrolllock;
list_for_each_entry(handler, &led_handlers, list) {
update_leds(handler);
}
if (led_update.state == BLOCKED)
complete(&led_update.update);
led_update.state = NONE;
}
static void wait_for_update(void)
{
if (led_update.state == UPDATE) {
led_update.state = BLOCKED;
wait_for_completion(&led_update.update);
}
}
static int input_leds_connect(struct input_handler *input_handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct led_handler *handler;
wait_for_update();
handler = (struct led_handler *)kzalloc(sizeof(*handler), GFP_KERNEL);
if (!handler)
return -ENOMEM;
handler->handle.dev = input_get_device(dev);
handler->handle.handler = input_handler;
handler->handle.name = "leds";
handler->handle.private = handler;
INIT_LIST_HEAD(&handler->list);
list_add_tail(&handler->list, &led_handlers);
update_leds(handler);
input_register_handle(&handler->handle);
return 0;
}
static void input_leds_disconnect(struct input_handle *handle)
{
struct led_handler *handler = (struct led_handler *)handle->private;
wait_for_update();
list_del(&handler->list);
input_unregister_handle(handle);
input_put_device(handle->dev);
kfree(handler);
}
static struct input_device_id input_leds_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT_MASK(EV_LED) },
},
};
MODULE_DEVICE_TABLE(input, led_ids);
static struct input_handler input_leds_handler = {
.name = "input-leds",
.connect = input_leds_connect,
.disconnect = input_leds_disconnect,
.id_table = input_leds_ids,
};
static int __init input_leds_init(void)
{
led_update.state = NONE;
init_completion(&led_update.update);
return input_register_handler(&input_leds_handler);
}
static void __exit input_leds_exit(void)
{
input_unregister_handler(&input_leds_handler);
}
module_init(input_leds_init);
module_exit(input_leds_exit);

View File

@ -138,7 +138,7 @@
</route>
</start>
<start name="usb_hid_drv" caps="140">
<start name="usb_hid_drv" caps="180">
<resource name="RAM" quantum="10M"/>
<config use_report="yes"/>
<route>