From b32a3cd4d9e231af8b1eed816a62b7447cc2bc2a Mon Sep 17 00:00:00 2001 From: Stefan Kalkowski Date: Tue, 19 Jun 2018 15:09:56 +0200 Subject: [PATCH] dde_linux: USB-client driver for HID version 4.16 --- .../lib/import/import-usb_hid_include.mk | 33 + repos/dde_linux/lib/mk/usb_hid_include.mk | 37 + repos/dde_linux/patches/usb_hid_evdev.patch | 22 + repos/dde_linux/patches/usb_hid_usbhid.patch | 31 + .../dde_linux/patches/usb_hid_wacom_sys.patch | 12 + repos/dde_linux/ports/dde_linux.hash | 2 +- repos/dde_linux/ports/dde_linux.port | 17 +- repos/dde_linux/run/usb_hid.run | 60 +- repos/dde_linux/src/drivers/usb_hid/README | 49 ++ repos/dde_linux/src/drivers/usb_hid/driver.h | 121 ++++ repos/dde_linux/src/drivers/usb_hid/dummies.c | 662 ++++++++++++++++++ repos/dde_linux/src/drivers/usb_hid/evdev.cc | 496 +++++++++++++ .../dde_linux/src/drivers/usb_hid/led_state.h | 66 ++ .../dde_linux/src/drivers/usb_hid/lx_emul.cc | 310 ++++++++ repos/dde_linux/src/drivers/usb_hid/lx_emul.h | 622 ++++++++++++++++ repos/dde_linux/src/drivers/usb_hid/main.cc | 323 +++++++++ repos/dde_linux/src/drivers/usb_hid/target.mk | 34 + .../dde_linux/src/include/lx_emul/impl/usb.h | 72 ++ repos/dde_linux/src/include/lx_kit/usb.h | 133 ++++ repos/dde_linux/usb_hid.list | 43 ++ 20 files changed, 3120 insertions(+), 25 deletions(-) create mode 100644 repos/dde_linux/lib/import/import-usb_hid_include.mk create mode 100644 repos/dde_linux/lib/mk/usb_hid_include.mk create mode 100644 repos/dde_linux/patches/usb_hid_evdev.patch create mode 100644 repos/dde_linux/patches/usb_hid_usbhid.patch create mode 100644 repos/dde_linux/patches/usb_hid_wacom_sys.patch create mode 100644 repos/dde_linux/src/drivers/usb_hid/README create mode 100644 repos/dde_linux/src/drivers/usb_hid/driver.h create mode 100644 repos/dde_linux/src/drivers/usb_hid/dummies.c create mode 100644 repos/dde_linux/src/drivers/usb_hid/evdev.cc create mode 100644 repos/dde_linux/src/drivers/usb_hid/led_state.h create mode 100644 repos/dde_linux/src/drivers/usb_hid/lx_emul.cc create mode 100644 repos/dde_linux/src/drivers/usb_hid/lx_emul.h create mode 100644 repos/dde_linux/src/drivers/usb_hid/main.cc create mode 100644 repos/dde_linux/src/drivers/usb_hid/target.mk create mode 100644 repos/dde_linux/src/include/lx_emul/impl/usb.h create mode 100644 repos/dde_linux/src/include/lx_kit/usb.h create mode 100644 repos/dde_linux/usb_hid.list diff --git a/repos/dde_linux/lib/import/import-usb_hid_include.mk b/repos/dde_linux/lib/import/import-usb_hid_include.mk new file mode 100644 index 000000000..75476a3be --- /dev/null +++ b/repos/dde_linux/lib/import/import-usb_hid_include.mk @@ -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 diff --git a/repos/dde_linux/lib/mk/usb_hid_include.mk b/repos/dde_linux/lib/mk/usb_hid_include.mk new file mode 100644 index 000000000..61cfbc43f --- /dev/null +++ b/repos/dde_linux/lib/mk/usb_hid_include.mk @@ -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 = diff --git a/repos/dde_linux/patches/usb_hid_evdev.patch b/repos/dde_linux/patches/usb_hid_evdev.patch new file mode 100644 index 000000000..d9c6c2b49 --- /dev/null +++ b/repos/dde_linux/patches/usb_hid_evdev.patch @@ -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, diff --git a/repos/dde_linux/patches/usb_hid_usbhid.patch b/repos/dde_linux/patches/usb_hid_usbhid.patch new file mode 100644 index 000000000..5784fc1e3 --- /dev/null +++ b/repos/dde_linux/patches/usb_hid_usbhid.patch @@ -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"); diff --git a/repos/dde_linux/patches/usb_hid_wacom_sys.patch b/repos/dde_linux/patches/usb_hid_wacom_sys.patch new file mode 100644 index 000000000..67a3be737 --- /dev/null +++ b/repos/dde_linux/patches/usb_hid_wacom_sys.patch @@ -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 ++#include + + #define WAC_MSG_RETRIES 5 + #define WAC_CMD_RETRIES 10 diff --git a/repos/dde_linux/ports/dde_linux.hash b/repos/dde_linux/ports/dde_linux.hash index d5216bd5e..5fdcbd6ec 100644 --- a/repos/dde_linux/ports/dde_linux.hash +++ b/repos/dde_linux/ports/dde_linux.hash @@ -1 +1 @@ -d0b8fe75d35d994df3bc98161d5d7558513a5d4a +66e54dcbcd22c597db5794f080d92df114b88985 diff --git a/repos/dde_linux/ports/dde_linux.port b/repos/dde_linux/ports/dde_linux.port index 5b9f04efe..34a56501f 100644 --- a/repos/dde_linux/ports/dde_linux.port +++ b/repos/dde_linux/ports/dde_linux.port @@ -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) diff --git a/repos/dde_linux/run/usb_hid.run b/repos/dde_linux/run/usb_hid.run index 9718367f4..e4076aa25 100644 --- a/repos/dde_linux/run/usb_hid.run +++ b/repos/dde_linux/run/usb_hid.run @@ -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 { - - - - - + + + + + + + + } +append config "" +append config { + + + + + + + + + + + + + + + + - - - - - - - - - + + @@ -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 diff --git a/repos/dde_linux/src/drivers/usb_hid/README b/repos/dde_linux/src/drivers/usb_hid/README new file mode 100644 index 000000000..27a657a6e --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/README @@ -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: + +! +! +! +! +! + +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: + +!... +! +!... + +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: + +!... +! +!... diff --git a/repos/dde_linux/src/drivers/usb_hid/driver.h b/repos/dde_linux/src/drivers/usb_hid/driver.h new file mode 100644 index 000000000..a05172236 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/driver.h @@ -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 +#include +#include +#include +#include +#include + +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 handler; + + void handle_signal() + { + task.unblock(); + Lx::scheduler().schedule(); + } + + template + Task(Genode::Entrypoint & ep, ARGS &&... args) + : task(args...), handler(ep, *this, &Task::handle_signal) {} + }; + + struct Device + { + using Le = Genode::List_element; + + 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 + { + template + 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 main_task; + Genode::Constructible 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_ */ diff --git a/repos/dde_linux/src/drivers/usb_hid/dummies.c b/repos/dde_linux/src/drivers/usb_hid/dummies.c new file mode 100644 index 000000000..f5f395328 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/dummies.c @@ -0,0 +1,662 @@ +#include + +#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; +} diff --git a/repos/dde_linux/src/drivers/usb_hid/evdev.cc b/repos/dde_linux/src/drivers/usb_hid/evdev.cc new file mode 100644 index 000000000..621462a3f --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/evdev.cc @@ -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 +#include +#include + +/* LX kit */ +#include +#include + +/* local */ +#include "led_state.h" + +/* Linux includes */ +#include +#include +#include +#include +#include +#include +#include +#include + + +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::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, 500); + 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; + 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 _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 _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); } diff --git a/repos/dde_linux/src/drivers/usb_hid/led_state.h b/repos/dde_linux/src/drivers/usb_hid/led_state.h new file mode 100644 index 000000000..9cd336203 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/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/drivers/usb_hid/lx_emul.cc b/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc new file mode 100644 index 000000000..2cb7649c7 --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/lx_emul.cc @@ -0,0 +1,310 @@ + +#include +#include + +#include +#include + +#include +#include +#include + +#define TRACE do { ; } while (0) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct Lx_driver +{ + using Element = Genode::List_element; + using List = Genode::List; + + 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; + 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); +} diff --git a/repos/dde_linux/src/drivers/usb_hid/lx_emul.h b/repos/dde_linux/src/drivers/usb_hid/lx_emul.h new file mode 100644 index 000000000..2f778d95a --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/lx_emul.h @@ -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 +#include + +#include + +#define __KERNEL__ 1 + +#include +#include +#include +#include + +enum { HZ = 100UL }; + +#include +#include +#include + +typedef int clockid_t; + +#include +#include +#include + +typedef __u16 __le16; +typedef __u32 __le32; +typedef __u64 __le64; +typedef __u64 __be64; + +#include +#include +#include +#include +#include +#include +#include + +static inline void barrier() { asm volatile ("": : :"memory"); } + +#define READ_ONCE(x) x + +#include +#include +#include +#include + +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 + +#endif /* _SRC__DRIVERS__USB_HID__LX_EMUL_H_ */ diff --git a/repos/dde_linux/src/drivers/usb_hid/main.cc b/repos/dde_linux/src/drivers/usb_hid/main.cc new file mode 100644 index 000000000..3927fcd4c --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/main.cc @@ -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 + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +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(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(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(this), + "usb_state", Lx::Task::PRIORITY_0, Lx::scheduler()), + urb_task(env.ep(), urb_task_entry, reinterpret_cast(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(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(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); +} diff --git a/repos/dde_linux/src/drivers/usb_hid/target.mk b/repos/dde_linux/src/drivers/usb_hid/target.mk new file mode 100644 index 000000000..f79a202fb --- /dev/null +++ b/repos/dde_linux/src/drivers/usb_hid/target.mk @@ -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 diff --git a/repos/dde_linux/src/include/lx_emul/impl/usb.h b/repos/dde_linux/src/include/lx_emul/impl/usb.h new file mode 100644 index 000000000..a2f205d3e --- /dev/null +++ b/repos/dde_linux/src/include/lx_emul/impl/usb.h @@ -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 +#include + +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); +} diff --git a/repos/dde_linux/src/include/lx_kit/usb.h b/repos/dde_linux/src/include/lx_kit/usb.h new file mode 100644 index 000000000..73d2d1cb9 --- /dev/null +++ b/repos/dde_linux/src/include/lx_kit/usb.h @@ -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 +#include + + +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_ */ diff --git a/repos/dde_linux/usb_hid.list b/repos/dde_linux/usb_hid.list new file mode 100644 index 000000000..cc05c00e6 --- /dev/null +++ b/repos/dde_linux/usb_hid.list @@ -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