usb: LED suppport

This commit is contained in:
Sebastian Sumpf 2017-11-02 18:21:51 +01:00 committed by Christian Helmuth
parent 12461291b8
commit fe4f304815
4 changed files with 303 additions and 2 deletions

View File

@ -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 {
<resource name="RAM" quantum="1M"/>
<provides><service name="Timer"/></provides>
</start>
<start name="usb_drv" caps="120">
<resource name="RAM" quantum="16M"/>
<provides><service name="Input"/></provides>
<config uhci="yes" ohci="yes" ehci="yes" xhci="yes">
<config uhci="yes" ohci="yes" ehci="yes" xhci="yes"
capslock_led="rom" numlock_led="rom" scrlock_led="rom"
bios_handoff="no">
<hid/>
</config>
<route>
<service name="ROM" label="capslock"> <child name="dynamic_rom"/> </service>
<service name="ROM" label="numlock"> <child name="dynamic_rom"/> </service>
<service name="ROM" label="scrlock"> <child name="dynamic_rom"/> </service>
<service name="ROM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="IO_PORT"> <parent/> </service>
<service name="IO_MEM"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="RM"> <parent/> </service>
<service name="Platform"> <any-child/> </service>
<service name="Timer"> <child name="timer"/> </service>
</route>
</start>
<start name="dynamic_rom">
<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>
<sleep milliseconds="500" />
<inline> <numlock enabled="yes"/> </inline>
<sleep milliseconds="500" />
</rom>
<rom name="scrlock">
<inline> <scrlock enabled="no"/> </inline>
<sleep milliseconds="1000" />
<inline> <scrlock enabled="yes"/> </inline>
<sleep milliseconds="1000" />
</rom>
</config>
<route>
<service name="ROM"> <parent/> </service>
<service name="CPU"> <parent/> </service>
<service name="PD"> <parent/> </service>
<service name="LOG"> <parent/> </service>
<service name="Timer"> <child name="timer"/> </service>
</route>
</start>
<start name="test-input">
<resource name="RAM" quantum="1M"/>
</start>
@ -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]

View File

@ -25,12 +25,24 @@
/* Genode includes */
#include <base/log.h>
#include <base/debug.h>
#include <base/registry.h>
#include <util/reconstructible.h>
/* LX kit */
#include <lx_kit/env.h>
#include <lx_kit/scheduler.h>
/* local */
#include "led_state.h"
/* Linux includes */
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
@ -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<Keyboard_led>::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<Keyboard_led> &registry, 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<Keyboard_led> _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<Led> _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<Usb::Led> _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); }

View File

@ -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 <util/xml_node.h>
#include <util/reconstructible.h>
#include <base/component.h>
namespace Usb { struct Led_state; }
struct Usb::Led_state
{
Genode::Env &_env;
typedef Genode::String<32> Name;
Name const _name;
Genode::Constructible<Genode::Attached_rom_dataspace> _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_ */

View File

@ -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();