dde_linux: USB-client driver for HID version 4.16

This commit is contained in:
Stefan Kalkowski 2018-06-19 15:09:56 +02:00 committed by Christian Helmuth
parent b02f483841
commit b32a3cd4d9
20 changed files with 3120 additions and 25 deletions

View File

@ -0,0 +1,33 @@
USB_HID_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_hid
# architecture-dependent includes
ifeq ($(filter-out $(SPECS),x86),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/x86
ifeq ($(filter-out $(SPECS),32bit),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/x86_32
endif # 32bit
ifeq ($(filter-out $(SPECS),64bit),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/x86_64
endif # 64bit
endif # x86
ifeq ($(filter-out $(SPECS),arm),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/arm
ifeq ($(filter-out $(SPECS),arm_v6),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/arm_v6
endif # arm_v6
ifeq ($(filter-out $(SPECS),arm_v7),)
ARCH_SRC_INC_DIR += $(REP_DIR)/src/include/spec/arm_v7
endif # arm_v7
endif # arm
#
# The order of include-search directories is important, we need to look into
# 'contrib' before falling back to our custom 'lx_emul.h' header.
#
INC_DIR += $(ARCH_SRC_INC_DIR)
INC_DIR += $(USB_HID_CONTRIB_DIR)/include
INC_DIR += $(USB_HID_CONTRIB_DIR)/drivers/hid
INC_DIR += $(USB_HID_CONTRIB_DIR)/drivers/input
INC_DIR += $(LIB_CACHE_DIR)/usb_hid_include/include/include/include

View File

@ -0,0 +1,37 @@
ifeq ($(called_from_lib_mk),yes)
USB_HID_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_hid
LX_EMUL_H := $(REP_DIR)/src/drivers/usb_hid/lx_emul.h
#
# Determine the header files included by the contrib code. For each
# of these header files we create a symlink to 'lx_emul.h'.
#
SCAN_DIRS := $(addprefix $(USB_HID_CONTRIB_DIR)/include/, asm-generic linux uapi) \
$(addprefix $(USB_HID_CONTRIB_DIR)/, drivers)
GEN_INCLUDES := $(shell grep -rIh "^\#include .*\/" $(SCAN_DIRS) |\
sed "s/^\#include [^<\"]*[<\"]\([^>\"]*\)[>\"].*/\1/" |\
sort | uniq)
#
# Filter out original Linux headers that exist in the contrib directory
#
NO_GEN_INCLUDES := $(shell cd $(USB_HID_CONTRIB_DIR)/; find include -name "*.h" |\
sed "s/.\///" | sed "s/.*include\///")
GEN_INCLUDES := $(filter-out $(NO_GEN_INCLUDES),$(GEN_INCLUDES))
#
# Put Linux headers in 'GEN_INC' dir, since some include use "../../" paths use
# three level include hierarchy
#
GEN_INC := $(shell pwd)/include/include/include
GEN_INCLUDES := $(addprefix $(GEN_INC)/,$(GEN_INCLUDES))
all: $(GEN_INCLUDES)
$(GEN_INCLUDES):
$(VERBOSE)mkdir -p $(dir $@)
$(VERBOSE)ln -s $(LX_EMUL_H) $@
endif
CC_CXX_WARN_STRICT =

View File

@ -0,0 +1,22 @@
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index c81c79d..c75bd8d 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -1425,6 +1425,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
if (error)
goto err_cleanup_evdev;
+ evdev_open_device(evdev);
return 0;
err_cleanup_evdev:
@@ -1456,8 +1457,7 @@ static const struct input_device_id evdev_ids[] = {
MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
- .event = evdev_event,
- .events = evdev_events,
+ .event = genode_evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.legacy_minors = true,

View File

@ -0,0 +1,31 @@
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 77c50cd..55379da 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1636,7 +1636,7 @@ struct usb_interface *usbhid_find_interface(int minor)
return usb_find_interface(&hid_driver, minor);
}
-static int __init hid_init(void)
+static int __init usbhid_init(void)
{
int retval = -ENOMEM;
@@ -1655,14 +1655,14 @@ usbhid_quirks_init_fail:
return retval;
}
-static void __exit hid_exit(void)
+static void __exit usbhid_exit(void)
{
usb_deregister(&hid_driver);
hid_quirks_exit(BUS_USB);
}
-module_init(hid_init);
-module_exit(hid_exit);
+module_init(usbhid_init);
+module_exit(usbhid_exit);
MODULE_AUTHOR("Andreas Gal");
MODULE_AUTHOR("Vojtech Pavlik");

View File

@ -0,0 +1,12 @@
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 4095431..92ea1ab 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -14,6 +14,7 @@
#include "wacom_wac.h"
#include "wacom.h"
#include <linux/input/mt.h>
+#include <linux/usb.h>
#define WAC_MSG_RETRIES 5
#define WAC_CMD_RETRIES 10

View File

@ -1 +1 @@
d0b8fe75d35d994df3bc98161d5d7558513a5d4a
66e54dcbcd22c597db5794f080d92df114b88985

View File

@ -2,7 +2,7 @@ LICENSE := GPLv2
VERSION := 2
DOWNLOADS := dwc_otg.git usb.archive intel_fb.archive lxip.archive \
wifi.archive fec.archive libnl.archive wpa_supplicant.git \
fw.archive usb_host.archive dwc_otg_host.git
fw.archive usb_host.archive dwc_otg_host.git usb_hid.archive
#
# Tools
@ -43,6 +43,14 @@ DIR(usb_host) := $(SRC_DIR_USB_HOST)
TAR_OPT(usb_host) := --strip-components=1 --files-from - < <(sed 's/-x.x.x/-$(VERSION_USB_HOST)/g' $(REP_DIR)/usb_host.list)
HASH_INPUT += $(REP_DIR)/usb_host.list
SRC_DIR_USB_HID := src/drivers/usb_hid
VERSION_USB_HID := 4.16.3
URL(usb_hid) := https://www.kernel.org/pub/linux/kernel/v4.x/linux-$(VERSION_USB_HID).tar.xz
SHA(usb_hid) := 0d6971a81da97e38b974c5eba31a74803bfe41aabc46d406c3acda56306c81a3
DIR(usb_hid) := $(SRC_DIR_USB_HID)
TAR_OPT(usb_hid) := --strip-components=1 --files-from - < <(sed 's/-x.x.x/-$(VERSION_USB_HID)/g' $(REP_DIR)/usb_hid.list)
HASH_INPUT += $(REP_DIR)/usb_hid.list
#
# Raspberry Pi USB controller
#
@ -135,6 +143,7 @@ PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb_host*
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/intel*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/fec_*.patch)))
PATCHES += $(addprefix patches/,$(notdir $(wildcard $(REP_DIR)/patches/usb_hid*.patch)))
#IP stack
LXIP_OPT = -p1 -d$(SRC_DIR_LXIP)
@ -172,6 +181,12 @@ USB_HOST_OPT = -p1 -d$(SRC_DIR_USB_HOST)
PATCH_OPT(patches/usb_host_mem.patch) := $(USB_HOST_OPT)
PATCH_OPT(patches/usb_host_omap.patch) := $(USB_HOST_OPT)
# USB HID
USB_HID_OPT = -p1 -d$(SRC_DIR_USB_HID)
PATCH_OPT(patches/usb_hid_usbhid.patch) := $(USB_HID_OPT)
PATCH_OPT(patches/usb_hid_wacom_sys.patch) := $(USB_HID_OPT)
PATCH_OPT(patches/usb_hid_evdev.patch) := $(USB_HID_OPT)
# INTEL FB
PATCH_OPT(patches/intel_fb_backlight.patch) := -p1 -d$(SRC_DIR_INTEL_FB)
PATCH_OPT(patches/intel_fb_drm.patch) := -p1 -d$(SRC_DIR_INTEL_FB)

View File

@ -60,9 +60,11 @@ if { [get_cmd_switch --autopilot] && ![have_spec x86_64] } {
set build_components {
core init
drivers/timer
drivers/usb
drivers/usb_host
drivers/usb_hid
test/input
server/dynamic_rom
server/report_rom
}
lappend_if [have_spec gpio] build_components drivers/gpio
@ -114,27 +116,39 @@ append config {
<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"
capslock_led="rom" numlock_led="rom" scrlock_led="rom"
bios_handoff="no">
<hid/>
<start name="report_rom">
<resource name="RAM" quantum="1M"/>
<provides> <service name="Report"/> <service name="ROM"/> </provides>
<config verbose="no">
<default-policy report="usb_drv -> devices"/>
</config>
</start>
<start name="usb_drv" caps="120"> }
append config "<binary name=\"[usb_host_drv_binary]\"/>"
append config {
<resource name="RAM" quantum="10M"/>
<provides> <service name="Usb"/> </provides>
<config bios_handoff="no">
<report devices="yes"/>
<policy label_prefix="usb_hid_drv" class="0x3"/>
</config>
<route>
<service name="Report"> <child name="report_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
<start name="usb_hid_drv" caps="120">
<resource name="RAM" quantum="10M"/>
<provides><service name="Input"/></provides>
<config use_report="yes" capslock_led="rom" numlock_led="rom" scrlock_led="rom"/>
<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>
<service name="ROM" label="report"> <child name="report_rom"/> </service>
<any-service> <parent/> <any-child/> </any-service>
</route>
</start>
@ -183,15 +197,17 @@ install_config $config
# generic modules
set boot_modules {
core ld.lib.so init timer usb_drv test-input dynamic_rom
core ld.lib.so init timer usb_hid_drv test-input dynamic_rom report_rom
}
append boot_modules [usb_host_drv_binary]
lappend_if [have_spec gpio] boot_modules [gpio_drv]
append_platform_drv_boot_modules
build_boot_image $boot_modules
append qemu_args " -nographic"
append qemu_args " -usb -usbdevice mouse -usbdevice keyboard"
append qemu_args " -device usb-ehci,id=ehci"
append xen_args { usbdevice=\["mouse","keyboard"\] }
@ -211,7 +227,7 @@ run_genode_until {\[init -\> test-input\] Input event #11.*\n} 40 [output_spawn_
grep_output {^\[init }
unify_output { number [0-9]+} ""
unify_output {(?n)on usb-dummy.*$} ""
unify_output {(?n)on usb-usbbus.*$} ""
unify_output {(?n)using .*$} ""
unify_output {(?n)^.*__wait_event.*$} ""
unify_output {(?n)^.*Failed to submit URB.*$} ""
@ -228,10 +244,8 @@ compare_output_to {
[init -> test-input] Input event #5 RELEASE BTN_LEFT
[init -> usb_drv] dev_info: USB disconnect, device
[init -> usb_drv] dev_info: new full-speed USB device
[init -> usb_drv] dev_info: D L
[init -> usb_drv] dev_info: input: USB HID v1.11 Keyboard [D L]
[init -> usb_drv] dev_info: D L
[init -> usb_drv] dev_info: input: USB HID v1.11 Mouse [D L]
[init -> usb_hid_drv] dev_info: input: USB HID v1.11 Keyboard [HID 03eb:204d]
[init -> usb_hid_drv] dev_info: input: USB HID v1.11 Mouse [HID 03eb:204d]
[init -> test-input] Input event #6 PRESS KEY_X 0
[init -> test-input] Input event #7 RELEASE KEY_X
[init -> test-input] Input event #8 PRESS BTN_LEFT 0

View File

@ -0,0 +1,49 @@
USB HID driver
##############
Supports keyboard and mouse connected via USB.
It connects to one or multiple USB sessions. A run script can be found
under 'run/usb_hid.run'.
Configuration snippet:
!<start name="usb_hid_drv">
! <resource name="RAM" quantum="10M"/>
! <provides><service name="Input"/></provides>
! <config use_report="yes"/>
!</start>
When the use_report attribute is set, the HID driver will request a ROM called
"report" that is used to iterate over all devices provided by the USB host
controller driver (resp. a rom filter). In that mode the driver tries to
claim all HID devices (class 0x3) via dedicated USB sessions.
Note: It has been observed that certain 1.0 versions of Qemu do not generate
mouse interrupts. The mouse driver should work correctly on Qemu 1.0.93 and
above.
HID - Touchscreen support
~~~~~~~~~~~~~~~~~~~~~~~~~
Touchscreen absolute coordinates must be calibrated (e.g. re-calculated) to
screen absolute coordinates. The screen resolution is not determined
automatically by the HID driver, but can be configured:
!...
!<config touchscreen width="1024" height="768" multitouch="no"/>
!...
If a touchscreen is multi-touch-capable than the multitouch attribute gears
which type of Genode input events are generated. If set to 'no' (default)
than absolute events are generated and no multitouch events. If set to 'yes'
solely multitouch events are generated.
Keyboard LED handling
~~~~~~~~~~~~~~~~~~~~~
The state of the keyboard LEDs like capslock can be controlled by providing
a corresponding ROM to the driver and configure it appropriatedly:
!...
!<config capslock_led="rom" numlock_led="rom" scrlock_led="rom"/>
!...

View File

@ -0,0 +1,121 @@
/*
* \brief USB HID driver
* \author Stefan Kalkowski
* \date 2018-06-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _SRC__DRIVERS__USB_HID__DRIVER_H_
#define _SRC__DRIVERS__USB_HID__DRIVER_H_
#include <base/allocator_avl.h>
#include <base/attached_rom_dataspace.h>
#include <base/heap.h>
#include <input/root.h>
#include <usb_session/connection.h>
#include <lx_kit/scheduler.h>
struct usb_device_id;
struct usb_interface;
struct usb_device;
struct Driver
{
using Label = Genode::String<64>;
struct Task
{
Lx::Task task;
Genode::Signal_handler<Task> handler;
void handle_signal()
{
task.unblock();
Lx::scheduler().schedule();
}
template <typename... ARGS>
Task(Genode::Entrypoint & ep, ARGS &&... args)
: task(args...), handler(ep, *this, &Task::handle_signal) {}
};
struct Device
{
using Le = Genode::List_element<Device>;
Le le { this };
Label label;
Driver &driver;
Genode::Env &env;
Genode::Allocator_avl &alloc;
Task state_task;
Task urb_task;
Usb::Connection usb { env, &alloc, label.string(),
512 * 1024, state_task.handler };
usb_device * udev = nullptr;
bool updated = true;
Device(Driver & drv, Label label);
~Device();
static void state_task_entry(void *);
static void urb_task_entry(void *);
void register_device();
void unregister_device();
void scan_interfaces(unsigned iface_idx);
void scan_altsettings(usb_interface * iface,
unsigned iface_idx, unsigned alt_idx);
void probe_interface(usb_interface *, usb_device_id *);
void remove_interface(usb_interface *);
};
struct Devices : Genode::List<Device::Le>
{
template <typename FN>
void for_each(FN const & fn)
{
Device::Le * cur = first();
for (Device::Le * next = cur ? cur->next() : nullptr; cur;
cur = next, next = cur ? cur->next() : nullptr)
fn(*cur->object());
}
};
enum Input_event {
EVENT_TYPE_PRESS,
EVENT_TYPE_RELEASE,
EVENT_TYPE_MOTION,
EVENT_TYPE_WHEEL,
EVENT_TYPE_TOUCH
};
Devices devices;
Genode::Env &env;
Genode::Entrypoint &ep { env.ep() };
Genode::Heap heap { env.ram(), env.rm() };
Genode::Allocator_avl alloc { &heap };
Input::Session_component session { env, env.ram() };
Input::Root_component root { env.ep().rpc_ep(), session };
Genode::Constructible<Task> main_task;
Genode::Constructible<Genode::Attached_rom_dataspace> report_rom;
Driver(Genode::Env &env);
void scan_report();
static unsigned long screen_x;
static unsigned long screen_y;
static bool multi_touch;
static void main_task_entry(void *);
static void input_callback(Input_event type, unsigned code,
int ax, int ay, int rx, int ry);
};
#endif /* _SRC__DRIVERS__USB_HID__DRIVER_H_ */

View File

@ -0,0 +1,662 @@
#include <lx_emul.h>
#if 0
#define TRACE \
do { \
lx_printf("%s not implemented\n", __func__); \
} while (0)
#else
#define TRACE do { ; } while (0)
#endif
#define TRACE_AND_STOP \
do { \
lx_printf("%s not implemented\n", __func__); \
BUG(); \
} while (0)
int bitmap_subset(const unsigned long *src1, const unsigned long *src2, int nbits)
{
TRACE;
return 1;
}
int bitmap_weight(const unsigned long *src, unsigned int nbits)
{
TRACE;
return 0;
}
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *dev, void *data))
{
TRACE_AND_STOP;
return -1;
}
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *))
{
TRACE;
return 0;
}
int bus_register(struct bus_type *bus)
{
TRACE;
return 0;
}
void bus_unregister(struct bus_type *bus)
{
TRACE_AND_STOP;
}
int cdev_device_add(struct cdev *cdev, struct device *dev)
{
TRACE;
return 0;
}
void cdev_device_del(struct cdev *cdev, struct device *dev)
{
TRACE;
}
int class_register(struct class *cls)
{
TRACE;
return 0;
}
void class_unregister(struct class *cls)
{
TRACE_AND_STOP;
}
unsigned long clear_user(void *to, unsigned long n)
{
TRACE_AND_STOP;
return -1;
}
long copy_from_user(void *to, const void *from, unsigned long n)
{
TRACE_AND_STOP;
return -1;
}
size_t copy_to_user(void *dst, void const *src, size_t len)
{
TRACE_AND_STOP;
return -1;
}
int device_create_file(struct device *device, const struct device_attribute *entry)
{
TRACE;
return 0;
}
void device_enable_async_suspend(struct device *dev)
{
TRACE;
}
void device_initialize(struct device *dev)
{
TRACE;
}
void device_lock(struct device *dev)
{
TRACE_AND_STOP;
}
void device_release_driver(struct device *dev)
{
TRACE_AND_STOP;
}
void device_remove_file(struct device *dev, const struct device_attribute *attr)
{
TRACE;
}
int device_set_wakeup_enable(struct device *dev, bool enable)
{
TRACE;
return 0;
}
void device_unlock(struct device *dev)
{
TRACE_AND_STOP;
}
int devm_add_action(struct device *dev, void (*action)(void *), void *data)
{
TRACE_AND_STOP;
return -1;
}
int devm_add_action_or_reset(struct device *dev, void (*action)(void *), void *data)
{
TRACE_AND_STOP;
return -1;
}
char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...)
{
TRACE_AND_STOP;
return NULL;
}
int devm_led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
TRACE_AND_STOP;
return -1;
}
int devm_led_trigger_register(struct device *dev, struct led_trigger *trigger)
{
TRACE_AND_STOP;
return -1;
}
void devres_add(struct device *dev, void *res)
{
TRACE_AND_STOP;
}
void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
{
TRACE_AND_STOP;
return NULL;
}
void devres_close_group(struct device *dev, void *id)
{
TRACE_AND_STOP;
}
int devres_destroy(struct device *dev, dr_release_t release, dr_match_t match, void *match_data)
{
TRACE_AND_STOP;
return -1;
}
void * devres_open_group(struct device *dev, void *id, gfp_t gfp)
{
TRACE_AND_STOP;
return NULL;
}
int devres_release_group(struct device *dev, void *id)
{
TRACE_AND_STOP;
return -1;
}
int dev_set_name(struct device *dev, const char *name, ...)
{
TRACE;
return 0;
}
int down_interruptible(struct semaphore *sem)
{
TRACE;
return 0;
}
int down_trylock(struct semaphore *sem)
{
TRACE;
return 0;
}
int driver_attach(struct device_driver *drv)
{
TRACE_AND_STOP;
return -1;
}
int fasync_helper(int a1, struct file * f, int a2, struct fasync_struct ** fas)
{
TRACE_AND_STOP;
return -1;
}
u64 get_unaligned_le64(const void *p)
{
TRACE_AND_STOP;
return -1;
}
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, gfp_t gfp_mask)
{
TRACE;
return 0;
}
void ida_simple_remove(struct ida *ida, unsigned int id)
{
TRACE;
}
struct input_event;
int input_event_from_user(const char __user *buffer, struct input_event *event)
{
TRACE_AND_STOP;
return -1;
}
int input_event_to_user(char __user *buffer, const struct input_event *event)
{
TRACE_AND_STOP;
return -1;
}
struct input_dev;
void input_ff_destroy(struct input_dev *dev)
{
TRACE_AND_STOP;
}
struct ff_effect;
int input_ff_effect_from_user(const char __user *buffer, size_t size, struct ff_effect *effect)
{
TRACE_AND_STOP;
return -1;
}
int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
{
TRACE_AND_STOP;
return -1;
}
int input_ff_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
TRACE_AND_STOP;
return -1;
}
int input_ff_upload(struct input_dev *dev, struct ff_effect *effect, struct file *file)
{
TRACE_AND_STOP;
return -1;
}
unsigned long int_sqrt(unsigned long x)
{
TRACE_AND_STOP;
return -1;
}
unsigned int jiffies_to_usecs(const unsigned long j)
{
TRACE;
return 0;
}
void kill_fasync(struct fasync_struct **a0, int a1, int a2)
{
TRACE_AND_STOP;
}
struct kobject * kobject_create_and_add(const char * a0, struct kobject * a1)
{
TRACE_AND_STOP;
return NULL;
}
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
{
TRACE;
return NULL;
}
void kobject_put(struct kobject * a0)
{
TRACE_AND_STOP;
}
struct device *kobj_to_dev(struct kobject *kobj)
{
TRACE_AND_STOP;
return NULL;
}
void kref_get(struct kref *kref)
{
TRACE_AND_STOP;
}
void kref_init(struct kref *kref)
{
TRACE_AND_STOP;
}
int kref_put(struct kref *kref, void (*release) (struct kref *kref))
{
TRACE_AND_STOP;
return -1;
}
int kstrtou8(const char *s, unsigned int base, u8 *res)
{
TRACE_AND_STOP;
return -1;
}
int kstrtouint(const char *s, unsigned int base, unsigned int *res)
{
TRACE_AND_STOP;
return -1;
}
int kstrtoul(const char *s, unsigned int base, unsigned long *res)
{
TRACE_AND_STOP;
return -1;
}
ktime_t ktime_get_boottime(void)
{
TRACE_AND_STOP;
return -1;
}
ktime_t ktime_get_real(void)
{
TRACE_AND_STOP;
return -1;
}
ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs)
{
TRACE_AND_STOP;
return -1;
}
ktime_t ktime_mono_to_real(ktime_t mono)
{
TRACE_AND_STOP;
return -1;
}
struct timespec64 ktime_to_timespec64(const s64 nsec)
{
struct timespec64 ret;
TRACE_AND_STOP;
return ret;
}
void kvfree(const void *addr)
{
TRACE_AND_STOP;
}
int memcmp(const void * a0, const void * a1, size_t a2)
{
TRACE_AND_STOP;
return -1;
}
void __module_get(struct module *module)
{
TRACE;
}
void module_put(struct module * module)
{
TRACE_AND_STOP;
}
loff_t no_llseek(struct file *file, loff_t offset, int whence)
{
TRACE_AND_STOP;
return -1;
}
int nonseekable_open(struct inode *inode, struct file *filp)
{
TRACE_AND_STOP;
return -1;
}
void *power_supply_get_drvdata(struct power_supply *psy)
{
TRACE_AND_STOP;
return NULL;
}
int power_supply_powers(struct power_supply *psy, struct device *dev)
{
TRACE_AND_STOP;
return -1;
}
int register_chrdev_region(dev_t a0, unsigned a1, const char * a2)
{
TRACE;
return 0;
}
int scnprintf(char *buf, size_t size, const char *fmt, ...)
{
TRACE_AND_STOP;
return -1;
}
void sema_init(struct semaphore *sem, int val)
{
TRACE;
}
void spin_lock(spinlock_t *lock)
{
TRACE;
}
void spin_lock_irq(spinlock_t *lock)
{
TRACE;
}
void spin_unlock_irq(spinlock_t *lock)
{
TRACE;
}
int sscanf(const char * a0, const char * a1, ...)
{
TRACE_AND_STOP;
return -1;
}
int strcmp(const char *s1, const char *s2)
{
TRACE_AND_STOP;
return -1;
}
size_t strlcpy(char *dest, const char *src, size_t size)
{
TRACE_AND_STOP;
return -1;
}
int strncmp(const char *cs, const char *ct, size_t count)
{
TRACE_AND_STOP;
return -1;
}
char *strrchr(const char * a0, int a1)
{
TRACE_AND_STOP;
return NULL;
}
char *strstr(const char * a0, const char * a1)
{
TRACE_AND_STOP;
return NULL;
}
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
{
TRACE_AND_STOP;
return -1;
}
void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
{
TRACE_AND_STOP;
}
struct urb;
void usb_block_urb(struct urb *urb)
{
TRACE_AND_STOP;
}
struct usb_device;
int usb_clear_halt(struct usb_device *dev, int pipe)
{
TRACE_AND_STOP;
return -1;
}
int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
{
TRACE_AND_STOP;
return -1;
}
struct usb_interface;
void usb_queue_reset_device(struct usb_interface *dev)
{
TRACE_AND_STOP;
}
int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
{
TRACE;
return -1;
}
void usb_unpoison_urb(struct urb *urb)
{
TRACE_AND_STOP;
}
struct __kfifo;
int __kfifo_alloc(struct __kfifo *fifo, unsigned int size, size_t esize, gfp_t gfp_mask)
{
TRACE_AND_STOP;
return -1;
}
void __kfifo_free(struct __kfifo *fifo)
{
TRACE_AND_STOP;
}
unsigned int __kfifo_in(struct __kfifo *fifo, const void *buf, unsigned int len)
{
TRACE_AND_STOP;
return 0;
}
unsigned int __kfifo_in_r(struct __kfifo *fifo, const void *buf, unsigned int len, size_t recsize)
{
TRACE_AND_STOP;
return 0;
}
unsigned int __kfifo_out(struct __kfifo *fifo, void *buf, unsigned int len)
{
TRACE_AND_STOP;
return 0;
}
unsigned int __kfifo_out_r(struct __kfifo *fifo, void *buf, unsigned int len, size_t recsize)
{
TRACE_AND_STOP;
return 0;
}
void __kfifo_skip_r(struct __kfifo *fifo, size_t recsize)
{
TRACE_AND_STOP;
}
unsigned int __kfifo_max_r(unsigned int len, size_t recsize)
{
TRACE_AND_STOP;
}
void usb_kill_urb(struct urb *urb)
{
TRACE;
}
int usb_unlink_urb(struct urb *urb)
{
TRACE_AND_STOP;
return -1;
}
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
{
TRACE_AND_STOP;
return -1;
}
struct power_supply;
struct power_supply_desc;
struct power_supply_config;
struct power_supply * devm_power_supply_register(struct device *parent, const struct power_supply_desc *desc, const struct power_supply_config *cfg)
{
TRACE_AND_STOP;
return NULL;
}
void devres_free(void *res)
{
TRACE_AND_STOP;
}
char *kasprintf(gfp_t gfp, const char *fmt, ...)
{
TRACE_AND_STOP;
return NULL;
}
void poll_wait(struct file *a0, wait_queue_head_t *a1, poll_table *a2)
{
TRACE_AND_STOP;
}
void power_supply_changed(struct power_supply *psy)
{
TRACE_AND_STOP;
}
void put_device(struct device *dev)
{
TRACE;
}
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
{
TRACE;
return 0;
}
void up(struct semaphore *sem)
{
TRACE;
}

View File

@ -0,0 +1,496 @@
/*
* \brief Input service and event handler
* \author Christian Helmuth
* \author Dirk Vogt
* \author Sebastian Sumpf
* \author Christian Menard
* \author Alexander Boettcher
* \date 2009-04-20
*
* TODO make this a complete replacement for evdev.c from Linux
* TODO per-device slot handling
*
* The original implementation was in the L4Env from the TUD:OS group
* (l4/pkg/input/lib/src/l4evdev.c). This file was released under the terms of
* the GNU General Public License version 2.
*/
/*
* Copyright (C) 2009-2017 Genode Labs GmbH
* Copyright (C) 2014 Ksys Labs LLC
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.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 <driver.h>
#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>
static struct slot
{
int id = -1; /* current tracking id */
int x = -1; /* last reported x axis */
int y = -1; /* last reported y axis */
int event = -1; /* last reported ABS_MT_ event */
} slots[16];
static int slot = 0; /* store current input slot */
static bool transform(input_dev *dev, int &x, int &y)
{
if (!Driver::screen_x || !Driver::screen_y) return true;
int const min_x_dev = input_abs_get_min(dev, ABS_X);
int const min_y_dev = input_abs_get_min(dev, ABS_Y);
int const max_x_dev = input_abs_get_max(dev, ABS_X);
int const max_y_dev = input_abs_get_max(dev, ABS_Y);
int const max_y_norm = max_y_dev - min_y_dev;
int const max_x_norm = max_x_dev - min_x_dev;
if (!max_x_norm || !max_y_norm ||
x < min_x_dev || y < min_y_dev || x > max_x_dev || y > max_y_dev) {
Genode::warning("ignore input source with coordinates out of range");
return false;
}
x = Driver::screen_x * (x - min_x_dev) / (max_x_norm);
y = Driver::screen_y * (y - min_y_dev) / (max_y_norm);
return true;
}
static void handle_mt_tracking_id(input_dev *dev, int value)
{
if (value != -1) {
if (slots[slot].id != -1)
Genode::warning("old tracking id in use and got new one");
slots[slot].id = value;
return;
}
/* send end of slot usage event for clients */
int x = slots[slot].x < 0 ? 0 : slots[slot].x;
int y = slots[slot].y < 0 ? 0 : slots[slot].y;
if (!transform(dev, x, y)) return;
Driver::input_callback(Driver::EVENT_TYPE_TOUCH, slot, x, y, -1, -1);
slots[slot].event = slots[slot].x = slots[slot].y = -1;
slots[slot].id = value;
}
static void handle_mt_slot(int value)
{
if ((unsigned)value >= sizeof(slots) / sizeof(slots[0])) {
Genode::warning("drop multi-touch slot id ", value);
return;
}
slot = value;
}
enum Axis { AXIS_X, AXIS_Y };
static void handle_absolute_axis(input_dev *dev, unsigned code, int value, Axis axis)
{
slots[slot].event = code;
Driver::Input_event type = Driver::EVENT_TYPE_MOTION;
switch (axis) {
case AXIS_X:
type = code == ABS_X ? Driver::EVENT_TYPE_MOTION
: Driver::EVENT_TYPE_TOUCH;
slots[slot].x = value;
break;
case AXIS_Y:
type = code == ABS_Y ? Driver::EVENT_TYPE_MOTION
: Driver::EVENT_TYPE_TOUCH;
slots[slot].y = value;
break;
}
int x = slots[slot].x;
int y = slots[slot].y;
if (x == -1 || y == -1) return;
if (!transform(dev, x, y)) return;
Driver::input_callback(type, slot, x, y, 0, 0);
}
static void handle_absolute(input_dev *dev, unsigned code, int value)
{
switch (code) {
case ABS_WHEEL:
Driver::input_callback(Driver::EVENT_TYPE_WHEEL, 0, 0, 0, 0, value);
return;
case ABS_X:
if (dev->mt && Driver::multi_touch) return;
handle_absolute_axis(dev, code, value, AXIS_X);
return;
case ABS_MT_POSITION_X:
if (!Driver::multi_touch) return;
handle_absolute_axis(dev, code, value, AXIS_X);
return;
case ABS_Y:
if (dev->mt && Driver::multi_touch) return;
handle_absolute_axis(dev, code, value, AXIS_Y);
return;
case ABS_MT_POSITION_Y:
if (!Driver::multi_touch) return;
handle_absolute_axis(dev, code, value, AXIS_Y);
return;
case ABS_MT_TRACKING_ID:
if (!Driver::multi_touch) return;
handle_mt_tracking_id(dev, value);
return;
case ABS_MT_SLOT:
if (!Driver::multi_touch) return;
handle_mt_slot(value);
return;
case ABS_MT_TOUCH_MAJOR:
case ABS_MT_TOUCH_MINOR:
case ABS_MT_ORIENTATION:
case ABS_MT_TOOL_TYPE:
case ABS_MT_BLOB_ID:
case ABS_MT_PRESSURE:
case ABS_MT_DISTANCE:
case ABS_MT_TOOL_X:
case ABS_MT_TOOL_Y:
/* ignore unused multi-touch events */
return;
default:
Genode::warning("unknown absolute event code ", code, " not handled");
return;
}
}
static void handle_relative(unsigned code, int value)
{
Driver::Input_event type;
int x = 0, y = 0;
switch (code) {
case REL_X:
type = Driver::EVENT_TYPE_MOTION;
x = value;
break;
case REL_Y:
type = Driver::EVENT_TYPE_MOTION;
y = value;
break;
case REL_HWHEEL:
type = Driver::EVENT_TYPE_WHEEL;
x = value;
break;
case REL_WHEEL:
type = Driver::EVENT_TYPE_WHEEL;
y = value;
break;
default:
Genode::warning("unknown relative event code ", code, " not handled");
return;
}
Driver::input_callback(type, 0, 0, 0, x, y);
}
static void handle_key(input_dev *dev, unsigned code, int value)
{
/* no press/release events for multi-touch devices in multi-touch mode */
if (dev->mt && Driver::multi_touch) return;
/* map BTN_TOUCH to BTN_LEFT */
if (code == BTN_TOUCH) code = BTN_LEFT;
Driver::Input_event type;
switch (value) {
case 0: type = Driver::EVENT_TYPE_RELEASE; break;
case 1: type = Driver::EVENT_TYPE_PRESS; break;
default:
Genode::warning("unknown key event value ", value, " not handled");
return;
}
Driver::input_callback(type, code, 0, 0, 0, 0);
}
extern "C" void genode_evdev_event(input_handle *handle, unsigned int type,
unsigned int code, int value)
{
input_dev *dev = handle->dev;
/* filter sound events */
if (test_bit(EV_SND, handle->dev->evbit)) return;
/* filter input_repeat_key() */
if ((type == EV_KEY) && (value == 2)) return;
/* filter EV_SYN and EV_MSC */
if (type == EV_SYN || type == EV_MSC) return;
switch (type) {
case EV_KEY: handle_key(dev, code, value); return;
case EV_REL: handle_relative(code, value); return;
case EV_ABS: handle_absolute(dev, code, value); return;
default:
Genode::warning("unknown event type ", type, " not handled");
return;
}
}
/***************************
** 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, 500);
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;
completion _led_update;
enum Update_state { NONE, UPDATE, BLOCKED };
Update_state _update_state { NONE };
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) {
/* config update from EP */
wait_for_completion(&led->_config_update);
led->_update_state = UPDATE;
_registry.for_each([&] (Keyboard_led &keyboard) {
led->update(keyboard); });
/* wake up other task that waits for regestry access */
if (led->_update_state == BLOCKED)
complete(&led->_led_update);
led->_update_state = NONE;
}
}
public:
Led()
{
init_completion(&_config_update);
init_completion(&_led_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);
}
/*
* wait for completion of registry and led state updates
*/
void wait_for_registry()
{
/* task in _run function might receive multiple updates */
while ((_update_state == UPDATE)) {
_update_state = BLOCKED;
wait_for_completion(&_led_update);
}
}
};
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);
input_handle *handle = (input_handle *)kzalloc(sizeof(input_handle), 0);
handle->dev = input_get_device(dev);
handle->handler = handler;
input_register_handle(handle);
return 0;
}
static void led_disconnect(struct input_handle *handle)
{
input_dev *dev = handle->dev;
_led->wait_for_registry();
_registry.for_each([&] (Keyboard_led &keyboard) {
if (keyboard.match(dev))
destroy(Lx_kit::env().heap(), &keyboard);
});
input_unregister_handle(handle);
input_put_device(dev);
kfree(handle);
}
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

@ -0,0 +1,310 @@
#include <base/env.h>
#include <usb_session/client.h>
#include <driver.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
#define TRACE do { ; } while (0)
#include <lx_emul/impl/kernel.h>
#include <lx_emul/impl/delay.h>
#include <lx_emul/impl/slab.h>
#include <lx_emul/impl/work.h>
#include <lx_emul/impl/spinlock.h>
#include <lx_emul/impl/mutex.h>
#include <lx_emul/impl/sched.h>
#include <lx_emul/impl/timer.h>
#include <lx_emul/impl/completion.h>
#include <lx_emul/impl/wait.h>
#include <lx_emul/impl/usb.h>
#include <lx_kit/backend_alloc.h>
struct Lx_driver
{
using Element = Genode::List_element<Lx_driver>;
using List = Genode::List<Element>;
device_driver & dev_drv;
Element le { this };
Lx_driver(device_driver & drv) : dev_drv(drv) { list().insert(&le); }
bool match(struct device *dev) {
return dev_drv.bus->match ? dev_drv.bus->match(dev, &dev_drv)
: false; }
int probe(struct device *dev)
{
dev->driver = &dev_drv;
if (dev_drv.bus->probe) return dev_drv.bus->probe(dev);
return 0;
}
static List & list()
{
static List _list;
return _list;
}
};
struct task_struct *current;
struct workqueue_struct *system_wq;
unsigned long jiffies;
Genode::Ram_dataspace_capability Lx::backend_alloc(Genode::addr_t size, Genode::Cache_attribute cached) {
return Lx_kit::env().env().ram().alloc(size, cached); }
const char *dev_name(const struct device *dev) { return dev->name; }
size_t strlen(const char *s) { return Genode::strlen(s); }
int mutex_lock_interruptible(struct mutex *m)
{
mutex_lock(m);
return 0;
}
int driver_register(struct device_driver *drv)
{
if (drv) new (Lx::Malloc::mem()) Lx_driver(*drv);
return 0;
}
static struct usb_driver * hid_driver = nullptr;
int usb_register_driver(struct usb_driver * driver, struct module *, const char *)
{
hid_driver = driver;
return 0;
}
void Driver::Device::probe_interface(usb_interface * iface, usb_device_id * id)
{
hid_driver->probe(iface, id);
}
void Driver::Device::remove_interface(usb_interface * iface)
{
hid_driver->disconnect(iface);
for (unsigned i = 0; i < iface->num_altsetting; i++) {
if (iface->altsetting[i].extra)
kfree(iface->altsetting[i].extra);
kfree(iface->altsetting[i].endpoint);
kfree(iface->altsetting);
}
kfree(iface);
}
long __wait_completion(struct completion *work, unsigned long timeout)
{
Lx::timer_update_jiffies();
struct process_timer timer { *Lx::scheduler().current() };
unsigned long expire = timeout + jiffies;
if (timeout) {
timer_setup(&timer.timer, process_timeout, 0);
mod_timer(&timer.timer, expire);
}
while (!work->done) {
if (timeout && expire <= jiffies) return 0;
Lx::Task * task = Lx::scheduler().current();
work->task = (void *)task;
task->block_and_schedule();
}
if (timeout) del_timer(&timer.timer);
work->done = 0;
return (expire > jiffies) ? (expire - jiffies) : 1;
}
int dev_set_drvdata(struct device *dev, void *data)
{
dev->driver_data = data;
return 0;
}
void *dev_get_drvdata(const struct device *dev)
{
return dev->driver_data;
}
size_t strlcat(char *dest, const char *src, size_t dest_size)
{
size_t len_d = strlen(dest);
if (len_d > dest_size) return 0;
size_t len = dest_size - len_d - 1;
memcpy(dest + len_d, src, len);
dest[len_d + len] = 0;
return len;
}
int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr)
{
struct usb_descriptor_header *header;
while (size >= sizeof(struct usb_descriptor_header)) {
header = (struct usb_descriptor_header *)buffer;
if (header->bLength < 2) {
printk(KERN_ERR
"%s: bogus descriptor, type %d length %d\n",
usbcore_name,
header->bDescriptorType,
header->bLength);
return -1;
}
if (header->bDescriptorType == type) {
*ptr = header;
return 0;
}
buffer += header->bLength;
size -= header->bLength;
}
return -1;
}
void *vzalloc(unsigned long size)
{
size_t *addr;
try { addr = (size_t *)Lx::Malloc::mem().alloc_large(size); }
catch (...) { return 0; }
memset(addr, 0, size);
return addr;
}
void vfree(void *addr)
{
if (!addr) return;
Lx::Malloc::mem().free_large(addr);
}
int device_add(struct device *dev)
{
if (dev->driver) return 0;
/* foreach driver match and probe device */
using Le = Genode::List_element<Lx_driver>;
for (Le *le = Lx_driver::list().first(); le; le = le->next())
if (le->object()->match(dev)) {
int ret = le->object()->probe(dev);
if (!ret) return 0;
}
return 0;
}
void device_del(struct device *dev)
{
if (dev->bus && dev->bus->remove)
dev->bus->remove(dev);
}
void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma)
{
return kmalloc(size, GFP_KERNEL);
}
struct device *get_device(struct device *dev)
{
//dev->ref++;
return dev;
}
void cdev_init(struct cdev *c, const struct file_operations *fops)
{
c->ops = fops;
}
void usb_free_coherent(struct usb_device *dev, size_t size, void *addr, dma_addr_t dma)
{
//kfree(dev);
}
int mutex_lock_killable(struct mutex *lock)
{
mutex_lock(lock);
return 0;
}
u16 get_unaligned_le16(const void *p)
{
const struct __una_u16 *ptr = (const struct __una_u16 *)p;
return ptr->x;
}
unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset)
{
unsigned long i = offset / BITS_PER_LONG;
offset -= (i * BITS_PER_LONG);
for (; offset < size; offset++)
if (addr[i] & (1UL << offset))
return offset + (i * BITS_PER_LONG);
return size;
}
long find_next_zero_bit_le(const void *addr,
unsigned long size, unsigned long offset)
{
unsigned long max_size = sizeof(long) * 8;
if (offset >= max_size) {
Genode::warning("Offset greater max size");
return offset + size;
}
for (; offset < max_size; offset++)
if (!(*(unsigned long*)addr & (1L << offset)))
return offset;
return offset + size;
}
u32 get_unaligned_le32(const void *p)
{
const struct __una_u32 *ptr = (const struct __una_u32 *)p;
return ptr->x;
}
void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
return kzalloc(size, gfp);
}

View File

@ -0,0 +1,622 @@
/*
* \brief USB HID driver Linux emulation environment
* \author Stefan Kalkowski
* \date 2018-06-13
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _SRC__DRIVERS__USB_HID__LX_EMUL_H_
#define _SRC__DRIVERS__USB_HID__LX_EMUL_H_
#include <base/fixed_stdint.h>
#include <stdarg.h>
#include <lx_emul/extern_c_begin.h>
#define __KERNEL__ 1
#include <lx_emul/compiler.h>
#include <lx_emul/printf.h>
#include <lx_emul/types.h>
#include <lx_emul/kernel.h>
enum { HZ = 100UL };
#include <lx_emul/jiffies.h>
#include <lx_emul/time.h>
#include <lx_emul/bitops.h>
typedef int clockid_t;
#include <lx_emul/timer.h>
#include <lx_emul/spinlock.h>
#include <lx_emul/mutex.h>
typedef __u16 __le16;
typedef __u32 __le32;
typedef __u64 __le64;
typedef __u64 __be64;
#include <lx_emul/byteorder.h>
#include <lx_emul/atomic.h>
#include <lx_emul/work.h>
#include <lx_emul/bug.h>
#include <lx_emul/errno.h>
#include <lx_emul/module.h>
#include <lx_emul/gfp.h>
static inline void barrier() { asm volatile ("": : :"memory"); }
#define READ_ONCE(x) x
#include <lx_emul/list.h>
#include <lx_emul/string.h>
#include <lx_emul/kobject.h>
#include <lx_emul/completion.h>
struct file
{
unsigned int f_flags;
void * private_data;
};
struct device;
typedef unsigned long kernel_ulong_t;
typedef struct { __u8 b[16]; } uuid_le;
struct device * get_device(struct device *dev);
void put_device(struct device *);
void * dev_get_drvdata(const struct device *dev);
int dev_set_drvdata(struct device *dev, void *data);
#define dev_info(dev, format, arg...) lx_printf("dev_info: " format , ## arg)
#define dev_warn(dev, format, arg...) lx_printf("dev_warn: " format , ## arg)
#define dev_err( dev, format, arg...) lx_printf("dev_err: " format , ## arg)
#define dev_dbg( dev, format, arg...)
#define pr_info(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
#define pr_err(fmt, ...) printk(KERN_ERR fmt, ##__VA_ARGS__)
#define pr_warn(fmt, ...) printk(KERN_WARN fmt, ##__VA_ARGS__)
struct semaphore {};
struct device_driver
{
char const *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name;
};
typedef int devt;
struct class
{
const char *name;
char *(*devnode)(struct device *dev, mode_t *mode);
};
struct device
{
char const * name;
struct device * parent;
struct kobject * kobj;
struct device_driver * driver;
struct bus_type * bus;
dev_t devt;
struct class * class;
const struct device_type * type;
void (*release)(struct device *dev);
void * driver_data;
};
void down(struct semaphore *sem);
void up(struct semaphore *sem);
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);
void device_lock(struct device *dev);
void device_release_driver(struct device *dev);
#define KBUILD_MODNAME ""
struct attribute
{
const char * name;
mode_t mode;
};
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = #_name, .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
int sprintf(char *buf, const char *fmt, ...);
int kstrtoul(const char *s, unsigned int base, unsigned long *res);
void device_unlock(struct device *dev);
struct kobj_uevent_env;
struct bus_type {
const char * name;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
};
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *dev, void *data));
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *));
int driver_attach(struct device_driver *drv);
struct file_operations;
struct cdev { const struct file_operations * ops; };
typedef __s64 time64_t;
struct timespec64 {
time64_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
struct timespec64 ktime_to_timespec64(const s64 nsec);
enum {
S_IWGRP = 20,
S_IRGRP = 40,
S_IRUGO = 444,
S_IWUSR = 200,
S_IRUSR = 400,
};
struct attribute_group
{
const char *name;
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};
void kfree(const void *);
unsigned int jiffies_to_usecs(const unsigned long j);
void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp);
size_t strlen(const char *s);
#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp);
static inline void devm_kfree(struct device *dev, void *p) {}
#define module_param_array_named(name, array, type, nump, perm)
void *kmalloc(size_t size, gfp_t flags);
void *kzalloc(size_t size, gfp_t flags);
void *kcalloc(size_t n, size_t size, gfp_t flags);
int snprintf(char *buf, size_t size, const char *fmt, ...);
const char *dev_name(const struct device *dev);
u16 get_unaligned_le16(const void *p);
u32 get_unaligned_le32(const void *p);
void *vzalloc(unsigned long size);
void vfree(void *addr);
struct pm_message {};
typedef struct pm_message pm_message_t;
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)
struct task_struct
{
char comm[16];
};
extern struct task_struct *current;
struct completion
{
unsigned int done;
void * task;
};
long __wait_completion(struct completion *work, unsigned long timeout);
struct notifier_block;
enum {
EPOLLIN = 0x00000001,
EPOLLOUT = 0x00000004,
EPOLLERR = 0x00000008,
EPOLLHUP = 0x00000010,
EPOLLRDNORM = 0x00000040,
EPOLLWRNORM = 0x00000100,
EPOLLRDHUP = 0x00002000,
ESHUTDOWN = 58,
};
enum led_brightness { LED_OFF = 0, LED_FULL = 255 };
struct led_classdev
{
const char * name;
enum led_brightness max_brightness;
int flags;
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
int (*brightness_set_blocking)(struct led_classdev *led_cdev,
enum led_brightness brightness);
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
const char *default_trigger;
struct led_trigger *trigger;
};
struct led_trigger
{
const char * name;
};
int sscanf(const char *, const char *, ...);
#define hid_dump_input(a,b,c) do { } while (0)
#define hid_dump_report(a, b, c, d) do { } while (0)
#define hid_debug_register(a, b) do { } while (0)
#define hid_debug_unregister(a) do { } while (0)
#define hid_debug_init() do { } while (0)
#define hid_debug_exit() do { } while (0)
struct hid_device;
static inline int hidraw_report_event(struct hid_device *hid, u8 *data, int len) { return 0; }
int down_trylock(struct semaphore *sem);
struct bin_attribute {
struct attribute attr;
size_t size;
ssize_t (*read)(struct file *, struct kobject *,
struct bin_attribute *, char *, loff_t, size_t);
};
struct device *kobj_to_dev(struct kobject *kobj);
static inline int hidraw_connect(struct hid_device *hid) { return -1; }
static inline void hidraw_disconnect(struct hid_device *hid) { }
struct hidraw { u32 minor; };
int device_create_file(struct device *device, const struct device_attribute *entry);
void device_remove_file(struct device *dev, const struct device_attribute *attr);
int mutex_lock_killable(struct mutex *lock);
struct driver_attribute {
struct attribute attr;
};
#define DRIVER_ATTR_WO(_name) \
struct driver_attribute driver_attr_##_name = { .attr = { .name = NULL } }
void msleep(unsigned int);
long find_next_zero_bit_le(const void *addr, unsigned long size, unsigned long offset);
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
int down_interruptible(struct semaphore *sem);
int scnprintf(char *buf, size_t size, const char *fmt, ...);
enum { PAGE_SIZE = 4096, };
#define __ATTRIBUTE_GROUPS(_name) \
static const struct attribute_group *_name##_groups[] = { \
&_name##_group, \
NULL, \
}
#define ATTRIBUTE_GROUPS(_name) \
static const struct attribute_group _name##_group = { \
.attrs = _name##_attrs, \
}; \
__ATTRIBUTE_GROUPS(_name)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = { .attr = { .name = NULL } }
struct kobj_uevent_env;
int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);
int dev_set_name(struct device *dev, const char *name, ...);
int device_add(struct device *dev);
void device_initialize(struct device *dev);
void device_enable_async_suspend(struct device *dev);
void device_del(struct device *dev);
void sema_init(struct semaphore *sem, int val);
int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);
int bus_register(struct bus_type *bus);
void bus_unregister(struct bus_type *bus);
enum { CLOCK_BOOTTIME = 7, };
struct fasync_struct;
void kill_fasync(struct fasync_struct **, int, int);
enum { SIGIO = 29 };
enum {
POLL_IN = 1,
POLL_HUP = 6,
};
enum tk_offsets { TK_OFFS_BOOT = 1, };
ktime_t ktime_mono_to_real(ktime_t mono);
ktime_t ktime_mono_to_any(ktime_t tmono, enum tk_offsets offs);
static inline void rcu_read_lock(void) { }
static inline void rcu_read_unlock(void) { }
#define rcu_dereference(p) p
#define rcu_assign_pointer(p,v) p = v
#define rcu_dereference_protected(p, c) p
static inline int hidraw_init(void) { return 0; }
static inline int hidraw_exit(void) { return 0; }
static inline int hidraw_debug_init(void) { return 0; }
static inline int hidraw_debug_exit(void) { return 0; }
#define list_for_each_entry_rcu(pos, head, member) \
list_for_each_entry(pos, head, member)
int fasync_helper(int, struct file *, int, struct fasync_struct **);
typedef unsigned fl_owner_t;
struct scatterlist;
bool lockdep_is_held(void *);
struct power_supply;
void power_supply_changed(struct power_supply *psy);
static inline u16 get_unaligned_be16(const void *p) {
return be16_to_cpup((__be16 *)p); }
static inline void synchronize_rcu(void) { }
static inline void list_add_tail_rcu(struct list_head *n,
struct list_head *head) {
list_add_tail(n, head); }
static inline void list_del_rcu(struct list_head *entry) {
list_del(entry); }
int mutex_lock_interruptible(struct mutex *m);
void kvfree(const void *addr);
struct inode
{
struct cdev * i_cdev;
};
int nonseekable_open(struct inode *inode, struct file *filp);
enum { O_NONBLOCK = 0x4000 };
typedef unsigned __poll_t;
typedef struct poll_table_struct { } poll_table;
size_t copy_to_user(void *dst, void const *src, size_t len);
long copy_from_user(void *to, const void *from, unsigned long n);
#define put_user(x, ptr) ({ lx_printf("put_user not implemented"); (0);})
int devm_add_action(struct device *dev, void (*action)(void *), void *data);
void poll_wait(struct file *f, wait_queue_head_t *w, poll_table *p);
#define get_user(x, ptr) ({ x = *ptr; 0; })
unsigned long clear_user(void *to, unsigned long n);
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT + 8)
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT + 8)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT + 14)
#define _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC_SIZEMASK ((1 << 14) - 1)
#define _IOC_DIR(cmd) cmd
#define _IOC(dir,type,nr,size) (type+nr+sizeof(size))
#define _IOR(type,nr,size) (type+nr+sizeof(size)+10)
#define _IOW(type,nr,size) (type+nr+sizeof(size)+20)
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
#define _IOC_TYPE(cmd) cmd
#define _IOC_NR(cmd) cmd
signed long schedule_timeout(signed long timeout);
int kstrtouint(const char *s, unsigned int base, unsigned int *res);
#define find_next_zero_bit find_next_zero_bit_le
int device_set_wakeup_enable(struct device *dev, bool enable);
struct ida {};
#define DEFINE_IDA(name) struct ida name;
loff_t no_llseek(struct file *file, loff_t offset, int whence);
struct file_operations
{
struct module *owner;
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
loff_t (*llseek) (struct file *, loff_t, int);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*fasync) (int, struct file *, int);
};
#define MKDEV(major, minor) 0
unsigned long int_sqrt(unsigned long x);
u64 get_unaligned_le64(const void *p);
#define U8_MAX ((u8)~0U)
#define S8_MAX ((s8)(U8_MAX>>1))
#define S8_MIN ((s8)(-S8_MAX - 1))
#define U16_MAX ((u16)~0U)
#define S16_MAX ((s16)(U16_MAX>>1))
#define S16_MIN ((s16)(-S16_MAX - 1))
#define U32_MAX ((u32)~0U)
#define S32_MAX ((s32)(U32_MAX>>1))
#define S32_MIN ((s32)(-S32_MAX - 1))
typedef void (*dr_release_t)(struct device *dev, void *res);
void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
void devres_add(struct device *dev, void *res);
int devres_release_group(struct device *dev, void *id);
void * devres_open_group(struct device *dev, void *id, gfp_t gfp);
void devres_close_group(struct device *dev, void *id);
static inline void add_input_randomness(unsigned int type, unsigned int code, unsigned int value) { }
unsigned long find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset);
#define find_first_bit(addr, size) find_next_bit((addr), (size), 0)
char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...);
int devm_led_trigger_register(struct device *dev, struct led_trigger *trigger);
int devm_add_action_or_reset(struct device *dev, void (*action)(void *), void *data);
int devm_led_classdev_register(struct device *parent, struct led_classdev *led_cdev);
enum { LED_HW_PLUGGABLE = 1 << 19 };
int kstrtou8(const char *s, unsigned int base, u8 *res);
struct kobj_attribute
{
struct attribute attr;
void * show;
void * store;
};
int bitmap_subset(const unsigned long *src1, const unsigned long *src2, int nbits);
struct device_type {
const char *name;
const struct attribute_group **groups;
void (*release)(struct device *dev);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode, kuid_t *, kgid_t *);
};
char *kasprintf(gfp_t gfp, const char *fmt, ...);
void cdev_init(struct cdev *c, const struct file_operations *fops);
int cdev_device_add(struct cdev *cdev, struct device *dev);
void cdev_device_del(struct cdev *cdev, struct device *dev);
#define MINOR(dev) ((dev) & 0xff)
typedef int (*dr_match_t)(struct device *dev, void *res, void *match_data);
void devres_free(void *res);
int devres_destroy(struct device *dev, dr_release_t release, dr_match_t match, void *match_data);
static inline void dump_stack(void) { }
int bitmap_weight(const unsigned long *src, unsigned int nbits);
#define list_add_rcu list_add
int ida_simple_get(struct ida *ida, unsigned int start, unsigned int end, gfp_t gfp_mask);
void ida_simple_remove(struct ida *ida, unsigned int id);
int class_register(struct class *cls);
void class_unregister(struct class *cls);
int register_chrdev_region(dev_t, unsigned, const char *);
void unregister_chrdev_region(dev_t, unsigned);
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr);
static inline void led_trigger_event(struct led_trigger *trigger,
enum led_brightness event) {}
typedef unsigned slab_flags_t;
struct tasklet_struct
{
void (*func)(unsigned long);
unsigned long data;
};
struct __una_u16 { u16 x; } __attribute__((packed));
struct __una_u32 { u32 x; } __attribute__((packed));
void subsys_input_init();
int module_evdev_init();
int module_led_init();
int module_usbhid_init();
int module_hid_init();
int module_hid_generic_init();
int module_ch_driver_init();
int module_ms_driver_init();
int module_mt_driver_init();
int module_wacom_driver_init();
struct input_handle;
void genode_evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
#include <lx_emul/extern_c_end.h>
#endif /* _SRC__DRIVERS__USB_HID__LX_EMUL_H_ */

View File

@ -0,0 +1,323 @@
/*
* \brief USB HID driver
* \author Stefan Kalkowski
* \date 2018-06-07
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/component.h>
#include <lx_kit/env.h>
#include <lx_kit/malloc.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/timer.h>
#include <driver.h>
#include <lx_emul.h>
#include <lx_emul/extern_c_begin.h>
#include <linux/hid.h>
#include <linux/usb.h>
#include <lx_emul/extern_c_end.h>
void Driver::Device::scan_altsettings(usb_interface * iface,
unsigned iface_idx, unsigned alt_idx)
{
Usb::Interface_descriptor iface_desc;
usb.interface_descriptor(iface_idx, alt_idx, &iface_desc);
Genode::memcpy(&iface->altsetting[alt_idx].desc, &iface_desc,
sizeof(usb_interface_descriptor));
if (iface_desc.active)
iface->cur_altsetting = &iface->altsetting[alt_idx];
if (iface_desc.iclass == USB_CLASS_HID) {
hid_descriptor * hd = (hid_descriptor*)
kzalloc(sizeof(hid_descriptor), GFP_KERNEL);
if (usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
USB_REQ_GET_DESCRIPTOR,
USB_RECIP_INTERFACE | USB_DIR_IN,
(HID_DT_HID << 8), iface_idx, (void*)hd,
sizeof(hid_descriptor), USB_CTRL_GET_TIMEOUT) < 0) {
Genode::warning("could not get HID descriptor");
} else {
iface->altsetting[alt_idx].extra = (unsigned char*)hd;
iface->altsetting[alt_idx].extralen = sizeof(hid_descriptor);
}
}
iface->altsetting[alt_idx].endpoint = (usb_host_endpoint*)
kzalloc(sizeof(usb_host_endpoint)*iface->altsetting[alt_idx].desc.bNumEndpoints, GFP_KERNEL);
for (unsigned i = 0; i < iface->altsetting[alt_idx].desc.bNumEndpoints; i++) {
Usb::Endpoint_descriptor ep_desc;
usb.endpoint_descriptor(iface_idx, alt_idx, i, &ep_desc);
Genode::memcpy(&iface->altsetting[alt_idx].endpoint[i].desc,
&ep_desc, sizeof(usb_endpoint_descriptor));
int epnum = usb_endpoint_num(&iface->altsetting[alt_idx].endpoint[i].desc);
if (usb_endpoint_dir_out(&iface->altsetting[alt_idx].endpoint[i].desc))
udev->ep_out[epnum] = &iface->altsetting[alt_idx].endpoint[i];
else
udev->ep_in[epnum] = &iface->altsetting[alt_idx].endpoint[i];
}
}
void Driver::Device::scan_interfaces(unsigned iface_idx)
{
struct usb_interface * iface =
(usb_interface*) kzalloc(sizeof(usb_interface), GFP_KERNEL);
iface->num_altsetting = usb.alt_settings(iface_idx);
iface->altsetting = (usb_host_interface*)
kzalloc(sizeof(usb_host_interface)*iface->num_altsetting, GFP_KERNEL);
iface->dev.parent = &udev->dev;
iface->dev.bus = (bus_type*) 0xdeadbeef;
for (unsigned i = 0; i < iface->num_altsetting; i++)
scan_altsettings(iface, iface_idx, i);
struct usb_device_id id;
probe_interface(iface, &id);
udev->config->interface[iface_idx] = iface;
};
void Driver::Device::register_device()
{
if (udev) {
Genode::error("device already registered!");
return;
}
Usb::Device_descriptor dev_desc;
Usb::Config_descriptor config_desc;
usb.config_descriptor(&dev_desc, &config_desc);
udev = (usb_device*) kzalloc(sizeof(usb_device), GFP_KERNEL);
udev->bus = (usb_bus*) kzalloc(sizeof(usb_bus), GFP_KERNEL);
udev->config = (usb_host_config*) kzalloc(sizeof(usb_host_config), GFP_KERNEL);
udev->bus->bus_name = "usbbus";
udev->bus->controller = (device*) (&usb);
udev->descriptor.idVendor = dev_desc.vendor_id;
udev->descriptor.idProduct = dev_desc.product_id;
udev->descriptor.bcdDevice = dev_desc.device_release;
for (unsigned i = 0; i < config_desc.num_interfaces; i++)
scan_interfaces(i);
}
void Driver::Device::unregister_device()
{
for (unsigned i = 0; i < USB_MAXINTERFACES; i++) {
if (!udev->config->interface[i]) break;
else remove_interface(udev->config->interface[i]);
}
kfree(udev->bus);
kfree(udev->config);
kfree(udev);
udev = nullptr;
}
void Driver::Device::state_task_entry(void * arg)
{
Device & dev = *reinterpret_cast<Device*>(arg);
for (;;) {
if (dev.usb.plugged() && !dev.udev)
dev.register_device();
if (!dev.usb.plugged() && dev.udev)
dev.unregister_device();
Lx::scheduler().current()->block_and_schedule();
}
}
void Driver::Device::urb_task_entry(void * arg)
{
Device & dev = *reinterpret_cast<Device*>(arg);
for (;;) {
while (dev.udev && dev.usb.source()->ack_avail()) {
Usb::Packet_descriptor p = dev.usb.source()->get_acked_packet();
if (p.completion) p.completion->complete(p);
dev.usb.source()->release_packet(p);
}
Lx::scheduler().current()->block_and_schedule();
}
}
Driver::Device::Device(Driver & driver, Label label)
: label(label),
driver(driver),
env(driver.env),
alloc(driver.alloc),
state_task(env.ep(), state_task_entry, reinterpret_cast<void*>(this),
"usb_state", Lx::Task::PRIORITY_0, Lx::scheduler()),
urb_task(env.ep(), urb_task_entry, reinterpret_cast<void*>(this),
"usb_urb", Lx::Task::PRIORITY_0, Lx::scheduler())
{
usb.tx_channel()->sigh_ack_avail(urb_task.handler);
driver.devices.insert(&le);
}
Driver::Device::~Device()
{
driver.devices.remove(&le);
if (udev) unregister_device();
}
unsigned long Driver::screen_x = 0;
unsigned long Driver::screen_y = 0;
bool Driver::multi_touch = false;
static Driver * driver = nullptr;
void Driver::main_task_entry(void * arg)
{
driver = reinterpret_cast<Driver*>(arg);
subsys_input_init();
module_evdev_init();
module_led_init();
module_usbhid_init();
module_hid_init();
module_hid_generic_init();
module_ch_driver_init();
module_ms_driver_init();
module_mt_driver_init();
module_wacom_driver_init();
bool use_report = false;
try {
Genode::Xml_node config_node = Lx_kit::env().config_rom().xml();
use_report = config_node.attribute_value("use_report", false);
config_node.attribute("width").value(&screen_x);
config_node.attribute("height").value(&screen_y);
multi_touch = config_node.attribute_value("multitouch", false);
} catch(...) { }
if (use_report)
Genode::warning("use compatibility mode: ",
"will claim all HID devices from USB report");
Genode::log("Configured HID screen with ", screen_x, "x", screen_y,
" (multitouch=", multi_touch ? "true" : "false", ")");
driver->env.parent().announce(driver->ep.manage(driver->root));
for (;;) {
if (!use_report)
static Device dev(*driver, Label(""));
else
driver->scan_report();
Lx::scheduler().current()->block_and_schedule();
}
}
void Driver::scan_report()
{
if (!report_rom.constructed()) {
report_rom.construct(env, "report");
report_rom->sigh(main_task->handler);
} else
report_rom->update();
devices.for_each([&] (Device & d) { d.updated = false; });
try {
Genode::Xml_node report_node = report_rom->xml();
report_node.for_each_sub_node([&] (Genode::Xml_node & dev_node)
{
unsigned long c = 0;
dev_node.attribute("class").value(&c);
if (c != USB_CLASS_HID) return;
Label label;
dev_node.attribute("label").value(&label);
bool found = false;
devices.for_each([&] (Device & d) {
if (d.label == label) d.updated = found = true; });
if (!found)
new (heap) Device(*this, label);
});
} catch(...) {
Genode::error("Error parsing USB devices report");
};
devices.for_each([&] (Device & d) {
if (!d.updated) destroy(heap, &d); });
}
void Driver::input_callback(Input_event type, unsigned code,
int ax, int ay, int rx, int ry)
{
using namespace Input;
auto submit = [&] (Event const &ev) { driver->session.submit(ev); };
switch (type) {
case EVENT_TYPE_PRESS: submit(Press{Keycode(code)}); break;
case EVENT_TYPE_RELEASE: submit(Release{Keycode(code)}); break;
case EVENT_TYPE_MOTION:
if (rx == 0 && ry == 0)
submit(Absolute_motion{ax, ay});
else
submit(Relative_motion{rx, ry});
break;
case EVENT_TYPE_WHEEL: submit(Wheel{rx, ry}); break;
case EVENT_TYPE_TOUCH:
{
Touch_id const id { (int)code };
if (rx == -1 && ry == -1)
submit(Touch_release{id});
else
submit(Touch{id, (float)ax, (float)ay});
break;
}
}
}
Driver::Driver(Genode::Env &env) : env(env)
{
Genode::log("--- USB HID input driver ---");
Lx_kit::construct_env(env);
Lx::scheduler(&env);
Lx::malloc_init(env, heap);
Lx::timer(&env, &ep, &heap, &jiffies);
main_task.construct(env.ep(), main_task_entry, reinterpret_cast<void*>(this),
"main", Lx::Task::PRIORITY_0, Lx::scheduler());
/* give all task a first kick before returning */
Lx::scheduler().schedule();
}
void Component::construct(Genode::Env &env)
{
env.exec_static_constructors();
static Driver driver(env);
}

View File

@ -0,0 +1,34 @@
TARGET := usb_hid_drv
SRC_C := dummies.c
SRC_CC := main.cc lx_emul.cc evdev.cc
SRC_CC += printf.cc timer.cc scheduler.cc malloc.cc env.cc work.cc
LIBS := base usb_hid_include lx_kit_setjmp
USB_CONTRIB_DIR := $(call select_from_ports,dde_linux)/src/drivers/usb_hid
INC_DIR += $(PRG_DIR)
INC_DIR += $(REP_DIR)/src/include
SRC_C += drivers/hid/hid-cherry.c
SRC_C += drivers/hid/hid-core.c
SRC_C += drivers/hid/hid-generic.c
SRC_C += drivers/hid/hid-input.c
SRC_C += drivers/hid/hid-microsoft.c
SRC_C += drivers/hid/hid-multitouch.c
SRC_C += drivers/hid/hid-quirks.c
SRC_C += drivers/hid/wacom_sys.c
SRC_C += drivers/hid/wacom_wac.c
SRC_C += drivers/hid/usbhid/hid-core.c
SRC_C += drivers/input/evdev.c
SRC_C += drivers/input/input-mt.c
SRC_C += drivers/input/input.c
CC_C_OPT += -Wno-unused-but-set-variable -Wno-pointer-sign \
-Wno-incompatible-pointer-types -Wno-unused-variable \
-Wno-unused-function -Wno-uninitialized -Wno-maybe-uninitialized
CC_CXX_WARN_STRICT =
vpath %.c $(USB_CONTRIB_DIR)
vpath %.cc $(REP_DIR)/src/lx_kit

View File

@ -0,0 +1,72 @@
/*
* \brief Implementation of linux/usb.h
* \author Stefan Kalkowski
* \date 2018-08-25
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Linux kit includes */
#include <lx_kit/malloc.h>
#include <lx_kit/usb.h>
int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value,
__u16 index, void *data, __u16 size, int timeout)
{
usb_ctrlrequest *dr = (usb_ctrlrequest*)
kmalloc(sizeof(usb_ctrlrequest), GFP_KERNEL);
if (!dr) return -ENOMEM;
dr->bRequestType = requesttype;
dr->bRequest = request;
dr->wValue = cpu_to_le16(value);
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);
urb * u = (urb*) usb_alloc_urb(0, GFP_KERNEL);
if (!u) return -ENOMEM;
usb_fill_control_urb(u, dev, pipe, (unsigned char *)dr, data,
size, nullptr, nullptr);
Sync_ctrl_urb * scu = new (Lx::Malloc::mem())
Sync_ctrl_urb(*(Usb::Connection*)(dev->bus->controller), *u);
scu->send(timeout);
int ret = u->actual_length;
usb_free_urb(u);
return ret;
}
struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb = (struct urb*)
kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
GFP_KERNEL);
if (!urb) return NULL;
Genode::memset(urb, 0, sizeof(*urb));
INIT_LIST_HEAD(&urb->anchor_list);
return urb;
}
int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
Urb * u = new (Lx::Malloc::mem())
Urb(*(Usb::Connection*)(urb->dev->bus->controller), *urb);
u->send();
return 0;
}
void usb_free_urb(struct urb *urb)
{
kfree(urb);
}

View File

@ -0,0 +1,133 @@
/*
* \brief USB URB implementation
* \author Stefan Kalkowski
* \date 2018-06-13
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__USB_H_
#define _LX_KIT__USB_H_
/* Genode includes */
#include <usb_session/client.h>
#include <util/string.h>
class Urb : public Usb::Completion
{
protected:
Usb::Session_client & _usb;
urb & _urb;
Usb::Packet_descriptor _packet {
_usb.source()->alloc_packet(_urb.transfer_buffer_length) };
public:
Urb(Usb::Session_client &usb, urb & urb) : _usb(usb), _urb(urb)
{
_packet.completion = this;
switch(usb_pipetype(_urb.pipe)) {
case PIPE_INTERRUPT:
{
struct usb_host_endpoint *ep =
usb_pipe_endpoint(_urb.dev, _urb.pipe);
_packet.type = Usb::Packet_descriptor::IRQ;
_packet.transfer.polling_interval = _urb.interval;
_packet.transfer.ep = ep->desc.bEndpointAddress;
return;
}
case PIPE_CONTROL:
{
usb_ctrlrequest * ctrl = (usb_ctrlrequest *)
_urb.setup_packet;
_packet.type = Usb::Packet_descriptor::CTRL;
_packet.control.request = ctrl->bRequest;
_packet.control.request_type = ctrl->bRequestType;
_packet.control.value = ctrl->wValue;
_packet.control.index = ctrl->wIndex;
if (!(ctrl->bRequestType & USB_DIR_IN))
Genode::memcpy(_usb.source()->packet_content(_packet),
_urb.transfer_buffer, _urb.transfer_buffer_length);
return;
}
case PIPE_BULK:
{
struct usb_host_endpoint *ep =
usb_pipe_endpoint(_urb.dev, _urb.pipe);
_packet.type = Usb::Packet_descriptor::BULK;
_packet.transfer.ep = ep->desc.bEndpointAddress;
if (usb_pipeout(_urb.pipe))
Genode::memcpy(_usb.source()->packet_content(_packet),
_urb.transfer_buffer, _urb.transfer_buffer_length);
return;
}
default:
Genode::error("unknown URB requested");
};
}
void send()
{
_usb.source()->submit_packet(_packet);
}
void complete(Usb::Packet_descriptor & packet) override
{
if (packet.succeded) {
bool ctrl = (usb_pipetype(_urb.pipe) == PIPE_CONTROL);
_urb.actual_length = ctrl ? packet.control.actual_size
: packet.transfer.actual_size;
if (_urb.actual_length && _urb.transfer_buffer &&
(_urb.transfer_buffer_length >= _urb.actual_length))
Genode::memcpy(_urb.transfer_buffer,
_usb.source()->packet_content(packet),
_urb.actual_length);
}
if (_urb.complete) _urb.complete(&_urb);
kfree(_packet.completion);
}
};
class Sync_ctrl_urb : public Urb
{
private:
completion _comp;
public:
Sync_ctrl_urb(Usb::Session_client & usb, urb & urb) : Urb(usb, urb) {
init_completion(&_comp); }
void complete(Usb::Packet_descriptor &p) override
{
Urb::complete(p);
::complete(&_comp);
}
void send(int timeout)
{
_packet.completion = this;
_packet.control.timeout = timeout;
Urb::send();
wait_for_completion(&_comp);
}
};
#endif /* _LX_KIT__USB_H_ */

View File

@ -0,0 +1,43 @@
linux-x.x.x/drivers/hid/hid-cherry.c
linux-x.x.x/drivers/hid/hid-core.c
linux-x.x.x/drivers/hid/hid-generic.c
linux-x.x.x/drivers/hid/hid-ids.h
linux-x.x.x/drivers/hid/hid-input.c
linux-x.x.x/drivers/hid/hid-microsoft.c
linux-x.x.x/drivers/hid/hid-multitouch.c
linux-x.x.x/drivers/hid/hid-quirks.c
linux-x.x.x/drivers/hid/wacom.h
linux-x.x.x/drivers/hid/wacom_sys.c
linux-x.x.x/drivers/hid/wacom_wac.c
linux-x.x.x/drivers/hid/wacom_wac.h
linux-x.x.x/drivers/hid/usbhid/hid-core.c
linux-x.x.x/drivers/hid/usbhid/usbhid.h
linux-x.x.x/drivers/input/evdev.c
linux-x.x.x/drivers/input/input-compat.h
linux-x.x.x/drivers/input/input-mt.c
linux-x.x.x/drivers/input/input.c
linux-x.x.x/include/asm-generic/atomic64.h
linux-x.x.x/include/asm-generic/bitops/__ffs.h
linux-x.x.x/include/asm-generic/bitops/__fls.h
linux-x.x.x/include/asm-generic/bitops/ffs.h
linux-x.x.x/include/asm-generic/bitops/fls.h
linux-x.x.x/include/asm-generic/bitops/fls64.h
linux-x.x.x/include/asm-generic/bitops/non-atomic.h
linux-x.x.x/include/linux/hiddev.h
linux-x.x.x/include/linux/hid.h
linux-x.x.x/include/linux/input.h
linux-x.x.x/include/linux/input/mt.h
linux-x.x.x/include/linux/kfifo.h
linux-x.x.x/include/linux/list.h
linux-x.x.x/include/linux/log2.h
linux-x.x.x/include/linux/mod_devicetable.h
linux-x.x.x/include/linux/power_supply.h
linux-x.x.x/include/linux/swab.h
linux-x.x.x/include/linux/usb.h
linux-x.x.x/include/linux/usb/ch9.h
linux-x.x.x/include/uapi/linux/byteorder/little_endian.h
linux-x.x.x/include/uapi/linux/hid.h
linux-x.x.x/include/uapi/linux/input.h
linux-x.x.x/include/uapi/linux/input-event-codes.h
linux-x.x.x/include/uapi/linux/swab.h
linux-x.x.x/include/uapi/linux/usb/ch9.h