From fe4f304815b1c9283aac94138535ffd2047c089d Mon Sep 17 00:00:00 2001 From: Sebastian Sumpf Date: Thu, 2 Nov 2017 18:21:51 +0100 Subject: [PATCH] usb: LED suppport --- repos/dde_linux/run/usb_hid.run | 55 +++++- repos/dde_linux/src/lib/usb/input/evdev.cc | 182 ++++++++++++++++++ repos/dde_linux/src/lib/usb/input/led_state.h | 66 +++++++ repos/dde_linux/src/lib/usb/main.cc | 2 + 4 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 repos/dde_linux/src/lib/usb/input/led_state.h diff --git a/repos/dde_linux/run/usb_hid.run b/repos/dde_linux/run/usb_hid.run index a12658d90d..3f87583721 100644 --- a/repos/dde_linux/run/usb_hid.run +++ b/repos/dde_linux/run/usb_hid.run @@ -62,6 +62,7 @@ set build_components { drivers/timer drivers/usb test/input + server/dynamic_rom } lappend_if [have_spec gpio] build_components drivers/gpio @@ -112,13 +113,63 @@ append config { + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -132,7 +183,7 @@ install_config $config # generic modules set boot_modules { - core ld.lib.so init timer usb_drv test-input + core ld.lib.so init timer usb_drv test-input dynamic_rom } lappend_if [have_spec gpio] boot_modules [gpio_drv] diff --git a/repos/dde_linux/src/lib/usb/input/evdev.cc b/repos/dde_linux/src/lib/usb/input/evdev.cc index d13bac0227..f4fde54058 100644 --- a/repos/dde_linux/src/lib/usb/input/evdev.cc +++ b/repos/dde_linux/src/lib/usb/input/evdev.cc @@ -25,12 +25,24 @@ /* Genode includes */ #include +#include +#include +#include + +/* LX kit */ +#include +#include + +/* local */ +#include "led_state.h" /* Linux includes */ #include #include +#include #include #include +#include #include @@ -300,3 +312,173 @@ void genode_input_register(genode_input_event_cb h, unsigned long res_x, screen_y = res_y; multi_touch = multitouch; } + + +/*************************** + ** Keyboard LED handling ** + ***************************/ + +class Keyboard_led +{ + private: + + Genode::Registry::Element _reg_elem; + input_dev * const _input_dev; + + usb_interface *_interface() { + return container_of(_input_dev->dev.parent->parent, usb_interface, dev); } + + usb_device *_usb_device() { + return interface_to_usbdev(_interface()); } + + public: + + Keyboard_led(Genode::Registry ®istry, input_dev *dev) + : _reg_elem(registry, *this), _input_dev(dev) { } + + bool match(input_dev const *other) const { return _input_dev == other; } + + void update(unsigned leds) + { + unsigned *buf = (unsigned *)kmalloc(4, GFP_LX_DMA); + *buf = leds; + usb_control_msg(_usb_device(), usb_sndctrlpipe(_usb_device(), 0), + 0x9, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x200, + _interface()->cur_altsetting->desc.bInterfaceNumber, + buf, 1, 0); + kfree(buf); + } +}; + + +static Genode::Registry _registry; + + +namespace Usb { class Led; } + +class Usb::Led +{ + private: + + Lx::Task _task { _run, this, "led_worker", Lx::Task::PRIORITY_2, + Lx::scheduler() }; + + completion _config_update; + + Led_state _capslock { Lx_kit::env().env(), "capslock" }, + _numlock { Lx_kit::env().env(), "numlock" }, + _scrlock { Lx_kit::env().env(), "scrlock" }; + + Genode::Signal_handler _config_handler { + Lx_kit::env().env().ep(), *this, &Led::_handle_config }; + + void _handle_config() + { + Lx_kit::env().config_rom().update(); + Genode::Xml_node config = Lx_kit::env().config_rom().xml(); + + _capslock.update(config, _config_handler); + _numlock .update(config, _config_handler); + _scrlock .update(config, _config_handler); + + complete(&_config_update); + Lx::scheduler().schedule(); + } + + static void _run(void *l) + { + Led *led = (Led *)l; + + while (true) { + wait_for_completion(&led->_config_update); + _registry.for_each([&] (Keyboard_led &keyboard) { + led->update(keyboard); }); + } + } + + public: + + Led() + { + init_completion(&_config_update); + Genode::Signal_transmitter(_config_handler).submit(); + } + + void update(Keyboard_led &keyboard) + { + unsigned leds = 0; + + leds |= _capslock.enabled() ? 1u << LED_CAPSL : 0; + leds |= _numlock.enabled() ? 1u << LED_NUML : 0; + leds |= _scrlock.enabled() ? 1u << LED_SCROLLL : 0; + + keyboard.update(leds); + } +}; + + +static Genode::Constructible _led; + + +static int led_connect(struct input_handler *handler, struct input_dev *dev, + const struct input_device_id *id) +{ + Keyboard_led *keyboard = new (Lx_kit::env().heap()) Keyboard_led(_registry, dev); + _led->update(*keyboard); + return 0; +} + + +static void led_disconnect(struct input_handle *handle) +{ + input_dev *dev = handle->dev; + + _registry.for_each([&] (Keyboard_led &keyboard) { + if (keyboard.match(dev)) + destroy(Lx_kit::env().heap(), &keyboard); + }); +} + + +static bool led_match(struct input_handler *handler, struct input_dev *dev) +{ + hid_device *hid = (hid_device *)input_get_drvdata(dev); + hid_report *report; + + /* search report for keyboard entries */ + list_for_each_entry(report, &hid->report_enum[0].report_list, list) { + + for (unsigned i = 0; i < report->maxfield; i++) + for (unsigned j = 0; j < report->field[i]->maxusage; j++) { + hid_usage *usage = report->field[i]->usage + j; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) { + return true; + } + } + } + + return false; +} + + +static struct input_handler led_handler; +static struct input_device_id led_ids[2]; + +static int led_init(void) +{ + led_ids[0].driver_info = 1; /* match all */ + led_ids[1] = {}; + + led_handler.name = "led"; + led_handler.connect = led_connect; + led_handler.disconnect = led_disconnect; + led_handler.id_table = led_ids; + led_handler.match = led_match; + + _led.construct(); + + return input_register_handler(&led_handler); +} + + +extern "C" { module_init(led_init); } diff --git a/repos/dde_linux/src/lib/usb/input/led_state.h b/repos/dde_linux/src/lib/usb/input/led_state.h new file mode 100644 index 0000000000..9cd3362038 --- /dev/null +++ b/repos/dde_linux/src/lib/usb/input/led_state.h @@ -0,0 +1,66 @@ +/* + * \brief Configuration of keyboard mode indicators + * \author Norman Feske + * \date 2017-10-25 + */ + +/* + * Copyright (C) 2017 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU Affero General Public License version 3. + */ + +#ifndef _INPUT__LED_STATE_H_ +#define _INPUT__LED_STATE_H_ + +#include +#include +#include + +namespace Usb { struct Led_state; } + + +struct Usb::Led_state +{ + Genode::Env &_env; + + typedef Genode::String<32> Name; + + Name const _name; + + Genode::Constructible _rom; + + bool _enabled = false; + + Led_state(Genode::Env &env, Name const &name) : _env(env), _name(name) { } + + void update(Genode::Xml_node config, Genode::Signal_context_capability sigh) + { + typedef Genode::String<32> Attr; + typedef Genode::String<16> Value; + + Attr const attr(_name, "_led"); + Value const value = config.attribute_value(attr.string(), Value()); + + bool const rom_configured = (value == "rom"); + + if (rom_configured && !_rom.constructed()) { + _rom.construct(_env, _name.string()); + _rom->sigh(sigh); + } + + if (!rom_configured && _rom.constructed()) + _rom.destruct(); + + if (_rom.constructed()) + _rom->update(); + + _enabled = _rom.constructed() ? _rom->xml().attribute_value("enabled", false) + : config.attribute_value(attr.string(), false); + } + + bool enabled() const { return _enabled; } +}; + +#endif /* _INPUT__LED_STATE_H_ */ diff --git a/repos/dde_linux/src/lib/usb/main.cc b/repos/dde_linux/src/lib/usb/main.cc index d9827b057a..049c41542c 100644 --- a/repos/dde_linux/src/lib/usb/main.cc +++ b/repos/dde_linux/src/lib/usb/main.cc @@ -46,6 +46,7 @@ extern "C" void module_ch_driver_init(); extern "C" void module_ms_driver_init(); extern "C" void module_mt_driver_init(); extern "C" void module_raw_driver_init(); +extern "C" void module_led_init(); extern "C" void start_input_service(void *ep, void *services); @@ -80,6 +81,7 @@ static void run_linux(void *s) if (services->hid) { subsys_input_init(); module_evdev_init(); + module_led_init(); /* HID */ module_hid_init_core();