From 12ad6ebd615f57b7b1032953ab9ea9b29ca1bc87 Mon Sep 17 00:00:00 2001 From: Nicolas James Date: Sun, 13 Apr 2025 20:42:31 +1000 Subject: Add ability to write userspace tests, update cmake build process --- src/CMakeLists.txt | 37 + src/fastmouse.h | 5 + src/fastmouse_test.cc | 6 + src/hid-input.c | 2477 -------------------------------------- src/linux/hid-core.c | 3130 +++++++++++++++++++++++++++++++++++++++++++++++++ src/linux/hid-input.c | 2540 +++++++++++++++++++++++++++++++++++++++ src/make_kernel.sh | 9 + src/make_patch.sh | 9 + 8 files changed, 5736 insertions(+), 2477 deletions(-) create mode 100644 src/CMakeLists.txt create mode 100644 src/fastmouse.h create mode 100644 src/fastmouse_test.cc delete mode 100644 src/hid-input.c create mode 100644 src/linux/hid-core.c create mode 100644 src/linux/hid-input.c create mode 100755 src/make_kernel.sh create mode 100755 src/make_patch.sh (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..27d5504 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.14) + +project(test_fastmouse) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +cmake_policy(SET CMP0135 NEW) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.16.0.zip +) + +FetchContent_MakeAvailable(googletest) + +file (GLOB SOURCE_FILES CONFIGURE_DEPENDS + "*.cc" +) +add_executable(${PROJECT_NAME} + ${SOURCE_FILES} +) + +target_compile_options(${PROJECT_NAME} PRIVATE + -Wall -Wextra -Wshadow -Wdouble-promotion -Wformat=2 -Wundef -fno-common + -Wconversion -Wpedantic -std=c++20 -O2 + -Wno-exceptions + -Wno-missing-field-initializers -Wno-unknown-pragmas +) +target_link_libraries(${PROJECT_NAME} PRIVATE + GTest::gtest_main +) + +enable_testing() +include(GoogleTest) +gtest_discover_tests(test_fastmouse) diff --git a/src/fastmouse.h b/src/fastmouse.h new file mode 100644 index 0000000..f1ee96a --- /dev/null +++ b/src/fastmouse.h @@ -0,0 +1,5 @@ +#ifndef FASTMOUSE_H_ +#define FASTMOUSE_H_ + + +#endif \ No newline at end of file diff --git a/src/fastmouse_test.cc b/src/fastmouse_test.cc new file mode 100644 index 0000000..9cfa3f2 --- /dev/null +++ b/src/fastmouse_test.cc @@ -0,0 +1,6 @@ +#include "fastmouse.h" +#include "gtest/gtest.h" + +TEST(TestTest, basictesttest) { + EXPECT_EQ(5 * 1761, 8805); +} diff --git a/src/hid-input.c b/src/hid-input.c deleted file mode 100644 index 8c8a748..0000000 --- a/src/hid-input.c +++ /dev/null @@ -1,2477 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2006-2010 Jiri Kosina - * - * HID to Linux Input mapping - */ - -/* - * - * Should you need to contact me, the author, you can do so either by - * e-mail - mail your message to , or by paper mail: - * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic - */ - -#include -#include -#include -#include - -#include -#include - -#include "hid-ids.h" - -#define unk KEY_UNKNOWN - -static const unsigned char hid_keyboard[256] = { - 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, - 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, - 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, - 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, - 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, - 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, - 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, - 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, - 115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk, - 122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk, - unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, - unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk, - unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, - unk,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,unk,unk,unk,unk, - 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, - 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk -}; - -static const struct { - __s32 x; - __s32 y; -} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; - -struct usage_priority { - __u32 usage; /* the HID usage associated */ - bool global; /* we assume all usages to be slotted, - * unless global - */ - unsigned int slot_overwrite; /* for globals: allows to set the usage - * before or after the slots - */ -}; - -/* - * hid-input will convert this list into priorities: - * the first element will have the highest priority - * (the length of the following array) and the last - * element the lowest (1). - * - * hid-input will then shift the priority by 8 bits to leave some space - * in case drivers want to interleave other fields. - * - * To accommodate slotted devices, the slot priority is - * defined in the next 8 bits (defined by 0xff - slot). - * - * If drivers want to add fields before those, hid-input will - * leave out the first 8 bits of the priority value. - * - * This still leaves us 65535 individual priority values. - */ -static const struct usage_priority hidinput_usages_priorities[] = { - { /* Eraser (eraser touching) must always come before tipswitch */ - .usage = HID_DG_ERASER, - }, - { /* Invert must always come before In Range */ - .usage = HID_DG_INVERT, - }, - { /* Is the tip of the tool touching? */ - .usage = HID_DG_TIPSWITCH, - }, - { /* Tip Pressure might emulate tip switch */ - .usage = HID_DG_TIPPRESSURE, - }, - { /* In Range needs to come after the other tool states */ - .usage = HID_DG_INRANGE, - }, -}; - -#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c)) -#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c)) -#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c)) -#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c)) -#define map_msc(c) hid_map_usage(hidinput, usage, &bit, &max, EV_MSC, (c)) - -#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ - &max, EV_ABS, (c)) -#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ - &max, EV_KEY, (c)) - -static bool match_scancode(struct hid_usage *usage, - unsigned int cur_idx, unsigned int scancode) -{ - return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode; -} - -static bool match_keycode(struct hid_usage *usage, - unsigned int cur_idx, unsigned int keycode) -{ - /* - * We should exclude unmapped usages when doing lookup by keycode. - */ - return (usage->type == EV_KEY && usage->code == keycode); -} - -static bool match_index(struct hid_usage *usage, - unsigned int cur_idx, unsigned int idx) -{ - return cur_idx == idx; -} - -typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage, - unsigned int cur_idx, unsigned int val); - -static struct hid_usage *hidinput_find_key(struct hid_device *hid, - hid_usage_cmp_t match, - unsigned int value, - unsigned int *usage_idx) -{ - unsigned int i, j, k, cur_idx = 0; - struct hid_report *report; - struct hid_usage *usage; - - for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { - list_for_each_entry(report, &hid->report_enum[k].report_list, list) { - for (i = 0; i < report->maxfield; i++) { - for (j = 0; j < report->field[i]->maxusage; j++) { - usage = report->field[i]->usage + j; - if (usage->type == EV_KEY || usage->type == 0) { - if (match(usage, cur_idx, value)) { - if (usage_idx) - *usage_idx = cur_idx; - return usage; - } - cur_idx++; - } - } - } - } - } - return NULL; -} - -static struct hid_usage *hidinput_locate_usage(struct hid_device *hid, - const struct input_keymap_entry *ke, - unsigned int *index) -{ - struct hid_usage *usage; - unsigned int scancode; - - if (ke->flags & INPUT_KEYMAP_BY_INDEX) - usage = hidinput_find_key(hid, match_index, ke->index, index); - else if (input_scancode_to_scalar(ke, &scancode) == 0) - usage = hidinput_find_key(hid, match_scancode, scancode, index); - else - usage = NULL; - - return usage; -} - -static int hidinput_getkeycode(struct input_dev *dev, - struct input_keymap_entry *ke) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hid_usage *usage; - unsigned int scancode, index; - - usage = hidinput_locate_usage(hid, ke, &index); - if (usage) { - ke->keycode = usage->type == EV_KEY ? - usage->code : KEY_RESERVED; - ke->index = index; - scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE); - ke->len = sizeof(scancode); - memcpy(ke->scancode, &scancode, sizeof(scancode)); - return 0; - } - - return -EINVAL; -} - -static int hidinput_setkeycode(struct input_dev *dev, - const struct input_keymap_entry *ke, - unsigned int *old_keycode) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hid_usage *usage; - - usage = hidinput_locate_usage(hid, ke, NULL); - if (usage) { - *old_keycode = usage->type == EV_KEY ? - usage->code : KEY_RESERVED; - usage->type = EV_KEY; - usage->code = ke->keycode; - - clear_bit(*old_keycode, dev->keybit); - set_bit(usage->code, dev->keybit); - dbg_hid("Assigned keycode %d to HID usage code %x\n", - usage->code, usage->hid); - - /* - * Set the keybit for the old keycode if the old keycode is used - * by another key - */ - if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL)) - set_bit(*old_keycode, dev->keybit); - - return 0; - } - - return -EINVAL; -} - - -/** - * hidinput_calc_abs_res - calculate an absolute axis resolution - * @field: the HID report field to calculate resolution for - * @code: axis code - * - * The formula is: - * (logical_maximum - logical_minimum) - * resolution = ---------------------------------------------------------- - * (physical_maximum - physical_minimum) * 10 ^ unit_exponent - * - * as seen in the HID specification v1.11 6.2.2.7 Global Items. - * - * Only exponent 1 length units are processed. Centimeters and inches are - * converted to millimeters. Degrees are converted to radians. - */ -__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) -{ - __s32 unit_exponent = field->unit_exponent; - __s32 logical_extents = field->logical_maximum - - field->logical_minimum; - __s32 physical_extents = field->physical_maximum - - field->physical_minimum; - __s32 prev; - - /* Check if the extents are sane */ - if (logical_extents <= 0 || physical_extents <= 0) - return 0; - - /* - * Verify and convert units. - * See HID specification v1.11 6.2.2.7 Global Items for unit decoding - */ - switch (code) { - case ABS_X: - case ABS_Y: - case ABS_Z: - case ABS_MT_POSITION_X: - case ABS_MT_POSITION_Y: - case ABS_MT_TOOL_X: - case ABS_MT_TOOL_Y: - case ABS_MT_TOUCH_MAJOR: - case ABS_MT_TOUCH_MINOR: - if (field->unit == 0x11) { /* If centimeters */ - /* Convert to millimeters */ - unit_exponent += 1; - } else if (field->unit == 0x13) { /* If inches */ - /* Convert to millimeters */ - prev = physical_extents; - physical_extents *= 254; - if (physical_extents < prev) - return 0; - unit_exponent -= 1; - } else { - return 0; - } - break; - - case ABS_RX: - case ABS_RY: - case ABS_RZ: - case ABS_WHEEL: - case ABS_TILT_X: - case ABS_TILT_Y: - if (field->unit == 0x14) { /* If degrees */ - /* Convert to radians */ - prev = logical_extents; - logical_extents *= 573; - if (logical_extents < prev) - return 0; - unit_exponent += 1; - } else if (field->unit != 0x12) { /* If not radians */ - return 0; - } - break; - - default: - return 0; - } - - /* Apply negative unit exponent */ - for (; unit_exponent < 0; unit_exponent++) { - prev = logical_extents; - logical_extents *= 10; - if (logical_extents < prev) - return 0; - } - /* Apply positive unit exponent */ - for (; unit_exponent > 0; unit_exponent--) { - prev = physical_extents; - physical_extents *= 10; - if (physical_extents < prev) - return 0; - } - - /* Calculate resolution */ - return DIV_ROUND_CLOSEST(logical_extents, physical_extents); -} -EXPORT_SYMBOL_GPL(hidinput_calc_abs_res); - -#ifdef CONFIG_HID_BATTERY_STRENGTH -static enum power_supply_property hidinput_battery_props[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_CAPACITY, - POWER_SUPPLY_PROP_MODEL_NAME, - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_SCOPE, -}; - -#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */ -#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */ -#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */ -#define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */ - -static const struct hid_device_id hid_battery_quirks[] = { - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_MAGICTRACKPAD), - HID_BATTERY_QUIRK_IGNORE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, - USB_DEVICE_ID_ELECOM_BM084), - HID_BATTERY_QUIRK_IGNORE }, - { HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL, - USB_DEVICE_ID_SYMBOL_SCANNER_3), - HID_BATTERY_QUIRK_IGNORE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, - USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), - HID_BATTERY_QUIRK_IGNORE }, - { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, - USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD), - HID_BATTERY_QUIRK_IGNORE }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), - HID_BATTERY_QUIRK_IGNORE }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L), - HID_BATTERY_QUIRK_AVOID_QUERY }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), - HID_BATTERY_QUIRK_AVOID_QUERY }, - { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW), - HID_BATTERY_QUIRK_AVOID_QUERY }, - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM), - HID_BATTERY_QUIRK_AVOID_QUERY }, - /* - * Elan I2C-HID touchscreens seem to all report a non present battery, - * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices. - */ - { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, - {} -}; - -static unsigned find_battery_quirk(struct hid_device *hdev) -{ - unsigned quirks = 0; - const struct hid_device_id *match; - - match = hid_match_id(hdev, hid_battery_quirks); - if (match != NULL) - quirks = match->driver_data; - - return quirks; -} - -static int hidinput_scale_battery_capacity(struct hid_device *dev, - int value) -{ - if (dev->battery_min < dev->battery_max && - value >= dev->battery_min && value <= dev->battery_max) - value = ((value - dev->battery_min) * 100) / - (dev->battery_max - dev->battery_min); - - return value; -} - -static int hidinput_query_battery_capacity(struct hid_device *dev) -{ - u8 *buf; - int ret; - - buf = kmalloc(4, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4, - dev->battery_report_type, HID_REQ_GET_REPORT); - if (ret < 2) { - kfree(buf); - return -ENODATA; - } - - ret = hidinput_scale_battery_capacity(dev, buf[1]); - kfree(buf); - return ret; -} - -static int hidinput_get_battery_property(struct power_supply *psy, - enum power_supply_property prop, - union power_supply_propval *val) -{ - struct hid_device *dev = power_supply_get_drvdata(psy); - int value; - int ret = 0; - - switch (prop) { - case POWER_SUPPLY_PROP_PRESENT: - case POWER_SUPPLY_PROP_ONLINE: - val->intval = 1; - break; - - case POWER_SUPPLY_PROP_CAPACITY: - if (dev->battery_status != HID_BATTERY_REPORTED && - !dev->battery_avoid_query) { - value = hidinput_query_battery_capacity(dev); - if (value < 0) - return value; - } else { - value = dev->battery_capacity; - } - - val->intval = value; - break; - - case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = dev->name; - break; - - case POWER_SUPPLY_PROP_STATUS: - if (dev->battery_status != HID_BATTERY_REPORTED && - !dev->battery_avoid_query) { - value = hidinput_query_battery_capacity(dev); - if (value < 0) - return value; - - dev->battery_capacity = value; - dev->battery_status = HID_BATTERY_QUERIED; - } - - if (dev->battery_status == HID_BATTERY_UNKNOWN) - val->intval = POWER_SUPPLY_STATUS_UNKNOWN; - else - val->intval = dev->battery_charge_status; - break; - - case POWER_SUPPLY_PROP_SCOPE: - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - break; - - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, - struct hid_field *field, bool is_percentage) -{ - struct power_supply_desc *psy_desc; - struct power_supply_config psy_cfg = { .drv_data = dev, }; - unsigned quirks; - s32 min, max; - int error; - - if (dev->battery) - return 0; /* already initialized? */ - - quirks = find_battery_quirk(dev); - - hid_dbg(dev, "device %x:%x:%x %d quirks %d\n", - dev->bus, dev->vendor, dev->product, dev->version, quirks); - - if (quirks & HID_BATTERY_QUIRK_IGNORE) - return 0; - - psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); - if (!psy_desc) - return -ENOMEM; - - psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", - strlen(dev->uniq) ? - dev->uniq : dev_name(&dev->dev)); - if (!psy_desc->name) { - error = -ENOMEM; - goto err_free_mem; - } - - psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; - psy_desc->properties = hidinput_battery_props; - psy_desc->num_properties = ARRAY_SIZE(hidinput_battery_props); - psy_desc->use_for_apm = 0; - psy_desc->get_property = hidinput_get_battery_property; - - min = field->logical_minimum; - max = field->logical_maximum; - - if (is_percentage || (quirks & HID_BATTERY_QUIRK_PERCENT)) { - min = 0; - max = 100; - } - - if (quirks & HID_BATTERY_QUIRK_FEATURE) - report_type = HID_FEATURE_REPORT; - - dev->battery_min = min; - dev->battery_max = max; - dev->battery_report_type = report_type; - dev->battery_report_id = field->report->id; - dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; - - /* - * Stylus is normally not connected to the device and thus we - * can't query the device and get meaningful battery strength. - * We have to wait for the device to report it on its own. - */ - dev->battery_avoid_query = report_type == HID_INPUT_REPORT && - field->physical == HID_DG_STYLUS; - - if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY) - dev->battery_avoid_query = true; - - dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); - if (IS_ERR(dev->battery)) { - error = PTR_ERR(dev->battery); - hid_warn(dev, "can't register power supply: %d\n", error); - goto err_free_name; - } - - power_supply_powers(dev->battery, &dev->dev); - return 0; - -err_free_name: - kfree(psy_desc->name); -err_free_mem: - kfree(psy_desc); - dev->battery = NULL; - return error; -} - -static void hidinput_cleanup_battery(struct hid_device *dev) -{ - const struct power_supply_desc *psy_desc; - - if (!dev->battery) - return; - - psy_desc = dev->battery->desc; - power_supply_unregister(dev->battery); - kfree(psy_desc->name); - kfree(psy_desc); - dev->battery = NULL; -} - -static void hidinput_update_battery(struct hid_device *dev, int value) -{ - int capacity; - - if (!dev->battery) - return; - - if (value == 0 || value < dev->battery_min || value > dev->battery_max) - return; - - capacity = hidinput_scale_battery_capacity(dev, value); - - if (dev->battery_status != HID_BATTERY_REPORTED || - capacity != dev->battery_capacity || - ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { - dev->battery_capacity = capacity; - dev->battery_status = HID_BATTERY_REPORTED; - dev->battery_ratelimit_time = - ktime_add_ms(ktime_get_coarse(), 30 * 1000); - power_supply_changed(dev->battery); - } -} - -static bool hidinput_set_battery_charge_status(struct hid_device *dev, - unsigned int usage, int value) -{ - switch (usage) { - case HID_BAT_CHARGING: - dev->battery_charge_status = value ? - POWER_SUPPLY_STATUS_CHARGING : - POWER_SUPPLY_STATUS_DISCHARGING; - return true; - } - - return false; -} -#else /* !CONFIG_HID_BATTERY_STRENGTH */ -static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, - struct hid_field *field, bool is_percentage) -{ - return 0; -} - -static void hidinput_cleanup_battery(struct hid_device *dev) -{ -} - -static void hidinput_update_battery(struct hid_device *dev, int value) -{ -} - -static bool hidinput_set_battery_charge_status(struct hid_device *dev, - unsigned int usage, int value) -{ - return false; -} -#endif /* CONFIG_HID_BATTERY_STRENGTH */ - -static bool hidinput_field_in_collection(struct hid_device *device, struct hid_field *field, - unsigned int type, unsigned int usage) -{ - struct hid_collection *collection; - - collection = &device->collection[field->usage->collection_index]; - - return collection->type == type && collection->usage == usage; -} - -static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, - struct hid_usage *usage, unsigned int usage_index) -{ - struct input_dev *input = hidinput->input; - struct hid_device *device = input_get_drvdata(input); - const struct usage_priority *usage_priority = NULL; - int max = 0, code; - unsigned int i = 0; - unsigned long *bit = NULL; - - field->hidinput = hidinput; - - if (field->flags & HID_MAIN_ITEM_CONSTANT) - goto ignore; - - /* Ignore if report count is out of bounds. */ - if (field->report_count < 1) - goto ignore; - - /* only LED usages are supported in output fields */ - if (field->report_type == HID_OUTPUT_REPORT && - (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { - goto ignore; - } - - /* assign a priority based on the static list declared here */ - for (i = 0; i < ARRAY_SIZE(hidinput_usages_priorities); i++) { - if (usage->hid == hidinput_usages_priorities[i].usage) { - usage_priority = &hidinput_usages_priorities[i]; - - field->usages_priorities[usage_index] = - (ARRAY_SIZE(hidinput_usages_priorities) - i) << 8; - break; - } - } - - /* - * For slotted devices, we need to also add the slot index - * in the priority. - */ - if (usage_priority && usage_priority->global) - field->usages_priorities[usage_index] |= - usage_priority->slot_overwrite; - else - field->usages_priorities[usage_index] |= - (0xff - field->slot_idx) << 16; - - if (device->driver->input_mapping) { - int ret = device->driver->input_mapping(device, hidinput, field, - usage, &bit, &max); - if (ret > 0) - goto mapped; - if (ret < 0) - goto ignore; - } - - switch (usage->hid & HID_USAGE_PAGE) { - case HID_UP_UNDEFINED: - goto ignore; - - case HID_UP_KEYBOARD: - set_bit(EV_REP, input->evbit); - - if ((usage->hid & HID_USAGE) < 256) { - if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore; - map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); - } else - map_key(KEY_UNKNOWN); - - break; - - case HID_UP_BUTTON: - code = ((usage->hid - 1) & HID_USAGE); - - switch (field->application) { - case HID_GD_MOUSE: - case HID_GD_POINTER: code += BTN_MOUSE; break; - case HID_GD_JOYSTICK: - if (code <= 0xf) - code += BTN_JOYSTICK; - else - code += BTN_TRIGGER_HAPPY - 0x10; - break; - case HID_GD_GAMEPAD: - if (code <= 0xf) - code += BTN_GAMEPAD; - else - code += BTN_TRIGGER_HAPPY - 0x10; - break; - case HID_CP_CONSUMER_CONTROL: - if (hidinput_field_in_collection(device, field, - HID_COLLECTION_NAMED_ARRAY, - HID_CP_PROGRAMMABLEBUTTONS)) { - if (code <= 0x1d) - code += KEY_MACRO1; - else - code += BTN_TRIGGER_HAPPY - 0x1e; - break; - } - fallthrough; - default: - switch (field->physical) { - case HID_GD_MOUSE: - case HID_GD_POINTER: code += BTN_MOUSE; break; - case HID_GD_JOYSTICK: code += BTN_JOYSTICK; break; - case HID_GD_GAMEPAD: code += BTN_GAMEPAD; break; - default: code += BTN_MISC; - } - } - - map_key(code); - break; - - case HID_UP_SIMULATION: - switch (usage->hid & 0xffff) { - case 0xba: map_abs(ABS_RUDDER); break; - case 0xbb: map_abs(ABS_THROTTLE); break; - case 0xc4: map_abs(ABS_GAS); break; - case 0xc5: map_abs(ABS_BRAKE); break; - case 0xc8: map_abs(ABS_WHEEL); break; - default: goto ignore; - } - break; - - case HID_UP_GENDESK: - if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ - switch (usage->hid & 0xf) { - case 0x1: map_key_clear(KEY_POWER); break; - case 0x2: map_key_clear(KEY_SLEEP); break; - case 0x3: map_key_clear(KEY_WAKEUP); break; - case 0x4: map_key_clear(KEY_CONTEXT_MENU); break; - case 0x5: map_key_clear(KEY_MENU); break; - case 0x6: map_key_clear(KEY_PROG1); break; - case 0x7: map_key_clear(KEY_HELP); break; - case 0x8: map_key_clear(KEY_EXIT); break; - case 0x9: map_key_clear(KEY_SELECT); break; - case 0xa: map_key_clear(KEY_RIGHT); break; - case 0xb: map_key_clear(KEY_LEFT); break; - case 0xc: map_key_clear(KEY_UP); break; - case 0xd: map_key_clear(KEY_DOWN); break; - case 0xe: map_key_clear(KEY_POWER2); break; - case 0xf: map_key_clear(KEY_RESTART); break; - default: goto unknown; - } - break; - } - - if ((usage->hid & 0xf0) == 0x90) { /* SystemControl & D-pad */ - switch (usage->hid) { - case HID_GD_UP: usage->hat_dir = 1; break; - case HID_GD_DOWN: usage->hat_dir = 5; break; - case HID_GD_RIGHT: usage->hat_dir = 3; break; - case HID_GD_LEFT: usage->hat_dir = 7; break; - case HID_GD_DO_NOT_DISTURB: - map_key_clear(KEY_DO_NOT_DISTURB); break; - default: goto unknown; - } - - if (usage->hid <= HID_GD_LEFT) { - if (field->dpad) { - map_abs(field->dpad); - goto ignore; - } - map_abs(ABS_HAT0X); - } - break; - } - - if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ - switch (usage->hid & 0xf) { - case 0x9: map_key_clear(KEY_MICMUTE); break; - case 0xa: map_key_clear(KEY_ACCESSIBILITY); break; - default: goto ignore; - } - break; - } - - if ((usage->hid & 0xf0) == 0xb0) { /* SC - Display */ - switch (usage->hid & 0xf) { - case 0x05: map_key_clear(KEY_SWITCHVIDEOMODE); break; - default: goto ignore; - } - break; - } - - /* - * Some lazy vendors declare 255 usages for System Control, - * leading to the creation of ABS_X|Y axis and too many others. - * It wouldn't be a problem if joydev doesn't consider the - * device as a joystick then. - */ - if (field->application == HID_GD_SYSTEM_CONTROL) - goto ignore; - - switch (usage->hid) { - /* These usage IDs map directly to the usage codes. */ - case HID_GD_X: case HID_GD_Y: case HID_GD_Z: - case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: - if (field->flags & HID_MAIN_ITEM_RELATIVE) - map_rel(usage->hid & 0xf); - else - map_abs_clear(usage->hid & 0xf); - break; - - case HID_GD_WHEEL: - if (field->flags & HID_MAIN_ITEM_RELATIVE) { - set_bit(REL_WHEEL, input->relbit); - map_rel(REL_WHEEL_HI_RES); - } else { - map_abs(usage->hid & 0xf); - } - break; - case HID_GD_SLIDER: case HID_GD_DIAL: - if (field->flags & HID_MAIN_ITEM_RELATIVE) - map_rel(usage->hid & 0xf); - else - map_abs(usage->hid & 0xf); - break; - - case HID_GD_HATSWITCH: - usage->hat_min = field->logical_minimum; - usage->hat_max = field->logical_maximum; - map_abs(ABS_HAT0X); - break; - - case HID_GD_START: map_key_clear(BTN_START); break; - case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; - - case HID_GD_RFKILL_BTN: - /* MS wireless radio ctl extension, also check CA */ - if (field->application == HID_GD_WIRELESS_RADIO_CTLS) { - map_key_clear(KEY_RFKILL); - /* We need to simulate the btn release */ - field->flags |= HID_MAIN_ITEM_RELATIVE; - break; - } - goto unknown; - - default: goto unknown; - } - - break; - - case HID_UP_LED: - switch (usage->hid & 0xffff) { /* HID-Value: */ - case 0x01: map_led (LED_NUML); break; /* "Num Lock" */ - case 0x02: map_led (LED_CAPSL); break; /* "Caps Lock" */ - case 0x03: map_led (LED_SCROLLL); break; /* "Scroll Lock" */ - case 0x04: map_led (LED_COMPOSE); break; /* "Compose" */ - case 0x05: map_led (LED_KANA); break; /* "Kana" */ - case 0x27: map_led (LED_SLEEP); break; /* "Stand-By" */ - case 0x4c: map_led (LED_SUSPEND); break; /* "System Suspend" */ - case 0x09: map_led (LED_MUTE); break; /* "Mute" */ - case 0x4b: map_led (LED_MISC); break; /* "Generic Indicator" */ - case 0x19: map_led (LED_MAIL); break; /* "Message Waiting" */ - case 0x4d: map_led (LED_CHARGING); break; /* "External Power Connected" */ - - default: goto ignore; - } - break; - - case HID_UP_DIGITIZER: - if ((field->application & 0xff) == 0x01) /* Digitizer */ - __set_bit(INPUT_PROP_POINTER, input->propbit); - else if ((field->application & 0xff) == 0x02) /* Pen */ - __set_bit(INPUT_PROP_DIRECT, input->propbit); - - switch (usage->hid & 0xff) { - case 0x00: /* Undefined */ - goto ignore; - - case 0x30: /* TipPressure */ - if (!test_bit(BTN_TOUCH, input->keybit)) { - device->quirks |= HID_QUIRK_NOTOUCH; - set_bit(EV_KEY, input->evbit); - set_bit(BTN_TOUCH, input->keybit); - } - map_abs_clear(ABS_PRESSURE); - break; - - case 0x32: /* InRange */ - switch (field->physical) { - case HID_DG_PUCK: - map_key(BTN_TOOL_MOUSE); - break; - case HID_DG_FINGER: - map_key(BTN_TOOL_FINGER); - break; - default: - /* - * If the physical is not given, - * rely on the application. - */ - if (!field->physical) { - switch (field->application) { - case HID_DG_TOUCHSCREEN: - case HID_DG_TOUCHPAD: - map_key_clear(BTN_TOOL_FINGER); - break; - default: - map_key_clear(BTN_TOOL_PEN); - } - } else { - map_key(BTN_TOOL_PEN); - } - break; - } - break; - - case 0x3b: /* Battery Strength */ - hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); - usage->type = EV_PWR; - return; - - case 0x3c: /* Invert */ - device->quirks &= ~HID_QUIRK_NOINVERT; - map_key_clear(BTN_TOOL_RUBBER); - break; - - case 0x3d: /* X Tilt */ - map_abs_clear(ABS_TILT_X); - break; - - case 0x3e: /* Y Tilt */ - map_abs_clear(ABS_TILT_Y); - break; - - case 0x33: /* Touch */ - case 0x42: /* TipSwitch */ - case 0x43: /* TipSwitch2 */ - device->quirks &= ~HID_QUIRK_NOTOUCH; - map_key_clear(BTN_TOUCH); - break; - - case 0x44: /* BarrelSwitch */ - map_key_clear(BTN_STYLUS); - break; - - case 0x45: /* ERASER */ - /* - * This event is reported when eraser tip touches the surface. - * Actual eraser (BTN_TOOL_RUBBER) is set and released either - * by Invert if tool reports proximity or by Eraser directly. - */ - if (!test_bit(BTN_TOOL_RUBBER, input->keybit)) { - device->quirks |= HID_QUIRK_NOINVERT; - set_bit(BTN_TOOL_RUBBER, input->keybit); - } - map_key_clear(BTN_TOUCH); - break; - - case 0x46: /* TabletPick */ - case 0x5a: /* SecondaryBarrelSwitch */ - map_key_clear(BTN_STYLUS2); - break; - - case 0x5b: /* TransducerSerialNumber */ - case 0x6e: /* TransducerSerialNumber2 */ - map_msc(MSC_SERIAL); - break; - - default: goto unknown; - } - break; - - case HID_UP_TELEPHONY: - switch (usage->hid & HID_USAGE) { - case 0x2f: map_key_clear(KEY_MICMUTE); break; - case 0xb0: map_key_clear(KEY_NUMERIC_0); break; - case 0xb1: map_key_clear(KEY_NUMERIC_1); break; - case 0xb2: map_key_clear(KEY_NUMERIC_2); break; - case 0xb3: map_key_clear(KEY_NUMERIC_3); break; - case 0xb4: map_key_clear(KEY_NUMERIC_4); break; - case 0xb5: map_key_clear(KEY_NUMERIC_5); break; - case 0xb6: map_key_clear(KEY_NUMERIC_6); break; - case 0xb7: map_key_clear(KEY_NUMERIC_7); break; - case 0xb8: map_key_clear(KEY_NUMERIC_8); break; - case 0xb9: map_key_clear(KEY_NUMERIC_9); break; - case 0xba: map_key_clear(KEY_NUMERIC_STAR); break; - case 0xbb: map_key_clear(KEY_NUMERIC_POUND); break; - case 0xbc: map_key_clear(KEY_NUMERIC_A); break; - case 0xbd: map_key_clear(KEY_NUMERIC_B); break; - case 0xbe: map_key_clear(KEY_NUMERIC_C); break; - case 0xbf: map_key_clear(KEY_NUMERIC_D); break; - default: goto ignore; - } - break; - - case HID_UP_CONSUMER: /* USB HUT v1.12, pages 75-84 */ - switch (usage->hid & HID_USAGE) { - case 0x000: goto ignore; - case 0x030: map_key_clear(KEY_POWER); break; - case 0x031: map_key_clear(KEY_RESTART); break; - case 0x032: map_key_clear(KEY_SLEEP); break; - case 0x034: map_key_clear(KEY_SLEEP); break; - case 0x035: map_key_clear(KEY_KBDILLUMTOGGLE); break; - case 0x036: map_key_clear(BTN_MISC); break; - - case 0x040: map_key_clear(KEY_MENU); break; /* Menu */ - case 0x041: map_key_clear(KEY_SELECT); break; /* Menu Pick */ - case 0x042: map_key_clear(KEY_UP); break; /* Menu Up */ - case 0x043: map_key_clear(KEY_DOWN); break; /* Menu Down */ - case 0x044: map_key_clear(KEY_LEFT); break; /* Menu Left */ - case 0x045: map_key_clear(KEY_RIGHT); break; /* Menu Right */ - case 0x046: map_key_clear(KEY_ESC); break; /* Menu Escape */ - case 0x047: map_key_clear(KEY_KPPLUS); break; /* Menu Value Increase */ - case 0x048: map_key_clear(KEY_KPMINUS); break; /* Menu Value Decrease */ - - case 0x060: map_key_clear(KEY_INFO); break; /* Data On Screen */ - case 0x061: map_key_clear(KEY_SUBTITLE); break; /* Closed Caption */ - case 0x063: map_key_clear(KEY_VCR); break; /* VCR/TV */ - case 0x065: map_key_clear(KEY_CAMERA); break; /* Snapshot */ - case 0x069: map_key_clear(KEY_RED); break; - case 0x06a: map_key_clear(KEY_GREEN); break; - case 0x06b: map_key_clear(KEY_BLUE); break; - case 0x06c: map_key_clear(KEY_YELLOW); break; - case 0x06d: map_key_clear(KEY_ASPECT_RATIO); break; - - case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break; - case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break; - case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break; - case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break; - case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; - case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; - - case 0x076: map_key_clear(KEY_CAMERA_ACCESS_ENABLE); break; - case 0x077: map_key_clear(KEY_CAMERA_ACCESS_DISABLE); break; - case 0x078: map_key_clear(KEY_CAMERA_ACCESS_TOGGLE); break; - - case 0x079: map_key_clear(KEY_KBDILLUMUP); break; - case 0x07a: map_key_clear(KEY_KBDILLUMDOWN); break; - case 0x07c: map_key_clear(KEY_KBDILLUMTOGGLE); break; - - case 0x082: map_key_clear(KEY_VIDEO_NEXT); break; - case 0x083: map_key_clear(KEY_LAST); break; - case 0x084: map_key_clear(KEY_ENTER); break; - case 0x088: map_key_clear(KEY_PC); break; - case 0x089: map_key_clear(KEY_TV); break; - case 0x08a: map_key_clear(KEY_WWW); break; - case 0x08b: map_key_clear(KEY_DVD); break; - case 0x08c: map_key_clear(KEY_PHONE); break; - case 0x08d: map_key_clear(KEY_PROGRAM); break; - case 0x08e: map_key_clear(KEY_VIDEOPHONE); break; - case 0x08f: map_key_clear(KEY_GAMES); break; - case 0x090: map_key_clear(KEY_MEMO); break; - case 0x091: map_key_clear(KEY_CD); break; - case 0x092: map_key_clear(KEY_VCR); break; - case 0x093: map_key_clear(KEY_TUNER); break; - case 0x094: map_key_clear(KEY_EXIT); break; - case 0x095: map_key_clear(KEY_HELP); break; - case 0x096: map_key_clear(KEY_TAPE); break; - case 0x097: map_key_clear(KEY_TV2); break; - case 0x098: map_key_clear(KEY_SAT); break; - case 0x09a: map_key_clear(KEY_PVR); break; - - case 0x09c: map_key_clear(KEY_CHANNELUP); break; - case 0x09d: map_key_clear(KEY_CHANNELDOWN); break; - case 0x0a0: map_key_clear(KEY_VCR2); break; - - case 0x0b0: map_key_clear(KEY_PLAY); break; - case 0x0b1: map_key_clear(KEY_PAUSE); break; - case 0x0b2: map_key_clear(KEY_RECORD); break; - case 0x0b3: map_key_clear(KEY_FASTFORWARD); break; - case 0x0b4: map_key_clear(KEY_REWIND); break; - case 0x0b5: map_key_clear(KEY_NEXTSONG); break; - case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; - case 0x0b7: map_key_clear(KEY_STOPCD); break; - case 0x0b8: map_key_clear(KEY_EJECTCD); break; - case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; - case 0x0b9: map_key_clear(KEY_SHUFFLE); break; - case 0x0bf: map_key_clear(KEY_SLOW); break; - - case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; - case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break; - - case 0x0d8: map_key_clear(KEY_DICTATE); break; - case 0x0d9: map_key_clear(KEY_EMOJI_PICKER); break; - - case 0x0e0: map_abs_clear(ABS_VOLUME); break; - case 0x0e2: map_key_clear(KEY_MUTE); break; - case 0x0e5: map_key_clear(KEY_BASSBOOST); break; - case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; - case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; - case 0x0f5: map_key_clear(KEY_SLOW); break; - - case 0x181: map_key_clear(KEY_BUTTONCONFIG); break; - case 0x182: map_key_clear(KEY_BOOKMARKS); break; - case 0x183: map_key_clear(KEY_CONFIG); break; - case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; - case 0x185: map_key_clear(KEY_EDITOR); break; - case 0x186: map_key_clear(KEY_SPREADSHEET); break; - case 0x187: map_key_clear(KEY_GRAPHICSEDITOR); break; - case 0x188: map_key_clear(KEY_PRESENTATION); break; - case 0x189: map_key_clear(KEY_DATABASE); break; - case 0x18a: map_key_clear(KEY_MAIL); break; - case 0x18b: map_key_clear(KEY_NEWS); break; - case 0x18c: map_key_clear(KEY_VOICEMAIL); break; - case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; - case 0x18e: map_key_clear(KEY_CALENDAR); break; - case 0x18f: map_key_clear(KEY_TASKMANAGER); break; - case 0x190: map_key_clear(KEY_JOURNAL); break; - case 0x191: map_key_clear(KEY_FINANCE); break; - case 0x192: map_key_clear(KEY_CALC); break; - case 0x193: map_key_clear(KEY_PLAYER); break; - case 0x194: map_key_clear(KEY_FILE); break; - case 0x196: map_key_clear(KEY_WWW); break; - case 0x199: map_key_clear(KEY_CHAT); break; - case 0x19c: map_key_clear(KEY_LOGOFF); break; - case 0x19e: map_key_clear(KEY_COFFEE); break; - case 0x19f: map_key_clear(KEY_CONTROLPANEL); break; - case 0x1a2: map_key_clear(KEY_APPSELECT); break; - case 0x1a3: map_key_clear(KEY_NEXT); break; - case 0x1a4: map_key_clear(KEY_PREVIOUS); break; - case 0x1a6: map_key_clear(KEY_HELP); break; - case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; - case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; - case 0x1ae: map_key_clear(KEY_KEYBOARD); break; - case 0x1b1: map_key_clear(KEY_SCREENSAVER); break; - case 0x1b4: map_key_clear(KEY_FILE); break; - case 0x1b6: map_key_clear(KEY_IMAGES); break; - case 0x1b7: map_key_clear(KEY_AUDIO); break; - case 0x1b8: map_key_clear(KEY_VIDEO); break; - case 0x1bc: map_key_clear(KEY_MESSENGER); break; - case 0x1bd: map_key_clear(KEY_INFO); break; - case 0x1cb: map_key_clear(KEY_ASSISTANT); break; - case 0x201: map_key_clear(KEY_NEW); break; - case 0x202: map_key_clear(KEY_OPEN); break; - case 0x203: map_key_clear(KEY_CLOSE); break; - case 0x204: map_key_clear(KEY_EXIT); break; - case 0x207: map_key_clear(KEY_SAVE); break; - case 0x208: map_key_clear(KEY_PRINT); break; - case 0x209: map_key_clear(KEY_PROPS); break; - case 0x21a: map_key_clear(KEY_UNDO); break; - case 0x21b: map_key_clear(KEY_COPY); break; - case 0x21c: map_key_clear(KEY_CUT); break; - case 0x21d: map_key_clear(KEY_PASTE); break; - case 0x21f: map_key_clear(KEY_FIND); break; - case 0x221: map_key_clear(KEY_SEARCH); break; - case 0x222: map_key_clear(KEY_GOTO); break; - case 0x223: map_key_clear(KEY_HOMEPAGE); break; - case 0x224: map_key_clear(KEY_BACK); break; - case 0x225: map_key_clear(KEY_FORWARD); break; - case 0x226: map_key_clear(KEY_STOP); break; - case 0x227: map_key_clear(KEY_REFRESH); break; - case 0x22a: map_key_clear(KEY_BOOKMARKS); break; - case 0x22d: map_key_clear(KEY_ZOOMIN); break; - case 0x22e: map_key_clear(KEY_ZOOMOUT); break; - case 0x22f: map_key_clear(KEY_ZOOMRESET); break; - case 0x232: map_key_clear(KEY_FULL_SCREEN); break; - case 0x233: map_key_clear(KEY_SCROLLUP); break; - case 0x234: map_key_clear(KEY_SCROLLDOWN); break; - case 0x238: /* AC Pan */ - set_bit(REL_HWHEEL, input->relbit); - map_rel(REL_HWHEEL_HI_RES); - break; - case 0x23d: map_key_clear(KEY_EDIT); break; - case 0x25f: map_key_clear(KEY_CANCEL); break; - case 0x269: map_key_clear(KEY_INSERT); break; - case 0x26a: map_key_clear(KEY_DELETE); break; - case 0x279: map_key_clear(KEY_REDO); break; - - case 0x289: map_key_clear(KEY_REPLY); break; - case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; - case 0x28c: map_key_clear(KEY_SEND); break; - - case 0x29d: map_key_clear(KEY_KBD_LAYOUT_NEXT); break; - - case 0x2a2: map_key_clear(KEY_ALL_APPLICATIONS); break; - - case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV); break; - case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT); break; - case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP); break; - case 0x2ca: map_key_clear(KEY_KBDINPUTASSIST_NEXTGROUP); break; - case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT); break; - case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL); break; - - case 0x29f: map_key_clear(KEY_SCALE); break; - - default: map_key_clear(KEY_UNKNOWN); - } - break; - - case HID_UP_GENDEVCTRLS: - switch (usage->hid) { - case HID_DC_BATTERYSTRENGTH: - hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); - usage->type = EV_PWR; - return; - } - goto unknown; - - case HID_UP_BATTERY: - switch (usage->hid) { - case HID_BAT_ABSOLUTESTATEOFCHARGE: - hidinput_setup_battery(device, HID_INPUT_REPORT, field, true); - usage->type = EV_PWR; - return; - case HID_BAT_CHARGING: - usage->type = EV_PWR; - return; - } - goto unknown; - case HID_UP_CAMERA: - switch (usage->hid & HID_USAGE) { - case 0x020: - map_key_clear(KEY_CAMERA_FOCUS); break; - case 0x021: - map_key_clear(KEY_CAMERA); break; - default: - goto ignore; - } - break; - - case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ - set_bit(EV_REP, input->evbit); - switch (usage->hid & HID_USAGE) { - case 0x021: map_key_clear(KEY_PRINT); break; - case 0x070: map_key_clear(KEY_HP); break; - case 0x071: map_key_clear(KEY_CAMERA); break; - case 0x072: map_key_clear(KEY_SOUND); break; - case 0x073: map_key_clear(KEY_QUESTION); break; - case 0x080: map_key_clear(KEY_EMAIL); break; - case 0x081: map_key_clear(KEY_CHAT); break; - case 0x082: map_key_clear(KEY_SEARCH); break; - case 0x083: map_key_clear(KEY_CONNECT); break; - case 0x084: map_key_clear(KEY_FINANCE); break; - case 0x085: map_key_clear(KEY_SPORT); break; - case 0x086: map_key_clear(KEY_SHOP); break; - default: goto ignore; - } - break; - - case HID_UP_HPVENDOR2: - set_bit(EV_REP, input->evbit); - switch (usage->hid & HID_USAGE) { - case 0x001: map_key_clear(KEY_MICMUTE); break; - case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; - case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; - default: goto ignore; - } - break; - - case HID_UP_MSVENDOR: - goto ignore; - - case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ - set_bit(EV_REP, input->evbit); - goto ignore; - - case HID_UP_LOGIVENDOR: - /* intentional fallback */ - case HID_UP_LOGIVENDOR2: - /* intentional fallback */ - case HID_UP_LOGIVENDOR3: - goto ignore; - - case HID_UP_PID: - switch (usage->hid & HID_USAGE) { - case 0xa4: map_key_clear(BTN_DEAD); break; - default: goto ignore; - } - break; - - default: - unknown: - if (field->report_size == 1) { - if (field->report->type == HID_OUTPUT_REPORT) { - map_led(LED_MISC); - break; - } - map_key(BTN_MISC); - break; - } - if (field->flags & HID_MAIN_ITEM_RELATIVE) { - map_rel(REL_MISC); - break; - } - map_abs(ABS_MISC); - break; - } - -mapped: - /* Mapping failed, bail out */ - if (!bit) - return; - - if (device->driver->input_mapped && - device->driver->input_mapped(device, hidinput, field, usage, - &bit, &max) < 0) { - /* - * The driver indicated that no further generic handling - * of the usage is desired. - */ - return; - } - - set_bit(usage->type, input->evbit); - - /* - * This part is *really* controversial: - * - HID aims at being generic so we should do our best to export - * all incoming events - * - HID describes what events are, so there is no reason for ABS_X - * to be mapped to ABS_Y - * - HID is using *_MISC+N as a default value, but nothing prevents - * *_MISC+N to overwrite a legitimate even, which confuses userspace - * (for instance ABS_MISC + 7 is ABS_MT_SLOT, which has a different - * processing) - * - * If devices still want to use this (at their own risk), they will - * have to use the quirk HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE, but - * the default should be a reliable mapping. - */ - while (usage->code <= max && test_and_set_bit(usage->code, bit)) { - if (device->quirks & HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE) { - usage->code = find_next_zero_bit(bit, - max + 1, - usage->code); - } else { - device->status |= HID_STAT_DUP_DETECTED; - goto ignore; - } - } - - if (usage->code > max) - goto ignore; - - if (usage->type == EV_ABS) { - - int a = field->logical_minimum; - int b = field->logical_maximum; - - if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) { - a = field->logical_minimum = 0; - b = field->logical_maximum = 255; - } - - if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) - input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); - else input_set_abs_params(input, usage->code, a, b, 0, 0); - - input_abs_set_res(input, usage->code, - hidinput_calc_abs_res(field, usage->code)); - - /* use a larger default input buffer for MT devices */ - if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0) - input_set_events_per_packet(input, 60); - } - - if (usage->type == EV_ABS && - (usage->hat_min < usage->hat_max || usage->hat_dir)) { - int i; - for (i = usage->code; i < usage->code + 2 && i <= max; i++) { - input_set_abs_params(input, i, -1, 1, 0, 0); - set_bit(i, input->absbit); - } - if (usage->hat_dir && !field->dpad) - field->dpad = usage->code; - } - - /* for those devices which produce Consumer volume usage as relative, - * we emulate pressing volumeup/volumedown appropriate number of times - * in hidinput_hid_event() - */ - if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && - (usage->code == ABS_VOLUME)) { - set_bit(KEY_VOLUMEUP, input->keybit); - set_bit(KEY_VOLUMEDOWN, input->keybit); - } - - if (usage->type == EV_KEY) { - set_bit(EV_MSC, input->evbit); - set_bit(MSC_SCAN, input->mscbit); - } - - return; - -ignore: - usage->type = 0; - usage->code = 0; -} - -static void hidinput_handle_scroll(struct hid_usage *usage, - struct input_dev *input, - __s32 value) -{ - int code; - int hi_res, lo_res; - - if (value == 0) - return; - - if (usage->code == REL_WHEEL_HI_RES) - code = REL_WHEEL; - else - code = REL_HWHEEL; - - /* - * Windows reports one wheel click as value 120. Where a high-res - * scroll wheel is present, a fraction of 120 is reported instead. - * Our REL_WHEEL_HI_RES axis does the same because all HW must - * adhere to the 120 expectation. - */ - hi_res = value * 120/usage->resolution_multiplier; - - usage->wheel_accumulated += hi_res; - lo_res = usage->wheel_accumulated/120; - if (lo_res) - usage->wheel_accumulated -= lo_res * 120; - - input_event(input, EV_REL, code, lo_res); - input_event(input, EV_REL, usage->code, hi_res); -} - -static void hid_report_release_tool(struct hid_report *report, struct input_dev *input, - unsigned int tool) -{ - /* if the given tool is not currently reported, ignore */ - if (!test_bit(tool, input->key)) - return; - - /* - * if the given tool was previously set, release it, - * release any TOUCH and send an EV_SYN - */ - input_event(input, EV_KEY, BTN_TOUCH, 0); - input_event(input, EV_KEY, tool, 0); - input_event(input, EV_SYN, SYN_REPORT, 0); - - report->tool = 0; -} - -static void hid_report_set_tool(struct hid_report *report, struct input_dev *input, - unsigned int new_tool) -{ - if (report->tool != new_tool) - hid_report_release_tool(report, input, report->tool); - - input_event(input, EV_KEY, new_tool, 1); - report->tool = new_tool; -} - -struct fastmouse_state { - int division; - long x, y; - int rise, run; -}; -static struct fastmouse_state fastmouse = { - .division = 1, - .x = 0, - .y = 0, - .rise = 0, - .run = INT_MAX, -}; - -static int set_division(const char *val, const struct kernel_param *kp) { - const int ret = kstrtoint(val, 0, &fastmouse.division); - if (ret != 0 || fastmouse.division <= 0) { - return -EINVAL; - } - return ret; -} -static const struct kernel_param_ops division_ops = { - .set = set_division, - .get = param_get_int, -}; -module_param_cb(division, &division_ops, &fastmouse.division, 0664); -MODULE_PARM_DESC(division, "Mouse movement division amount (default: 1)"); - -static int set_rise(const char *val, const struct kernel_param *kp) { - const int ret = kstrtoint(val, 0, &fastmouse.rise); - if (ret != 0) { - return -EINVAL; - } - return ret; -} -static const struct kernel_param_ops rise_ops = { - .set = set_rise, - .get = param_get_int, -}; -module_param_cb(rise, &rise_ops, &fastmouse.rise, 0664); -MODULE_PARM_DESC(rise, "Mouse movement rise amount (default: 0)"); - -static int set_run(const char *val, const struct kernel_param *kp) { - const int ret = kstrtoint(val, 0, &fastmouse.run); - if (ret != 0) { - return -EINVAL; - } - return ret; -} -static const struct kernel_param_ops run_ops = { - .set = set_run, - .get = param_get_int, -}; -module_param_cb(run, &run_ops, &fastmouse.run, 0664); -MODULE_PARM_DESC(run, "Mouse movement run amount (default: INT_MAX)"); - -static void fastmouse_handle_motion(const long in_x, const long in_y) { - const long rise = fastmouse.rise / fastmouse.division; - const long run = fastmouse.run / fastmouse.division; - fastmouse.x += in_x * run - in_y * rise; - fastmouse.y += in_x * rise + in_y * run; -} - -static void input_event_fastmouse(struct input_dev *input, const unsigned int type, const unsigned int code, __s32 value) { - const long in_x = (code == REL_X) * value; - const long in_y = (code == REL_Y) * value; - - fastmouse_handle_motion(in_x, in_y); - - const long emit_x = fastmouse.x / INT_MAX; - if (emit_x != 0) { - input_event(input, type, REL_X, emit_x); - } - const long emit_y = fastmouse.y / INT_MAX; - if (emit_y != 0) { - input_event(input, type, REL_Y, emit_y); - } - - fastmouse.x %= INT_MAX; - fastmouse.y %= INT_MAX; -} - -void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) -{ - struct input_dev *input; - struct hid_report *report = field->report; - unsigned *quirks = &hid->quirks; - - if (!usage->type) - return; - - if (usage->type == EV_PWR) { - bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value); - - if (!handled) - hidinput_update_battery(hid, value); - - return; - } - - if (!field->hidinput) - return; - - input = field->hidinput->input; - - if (usage->hat_min < usage->hat_max || usage->hat_dir) { - int hat_dir = usage->hat_dir; - if (!hat_dir) - hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; - if (hat_dir < 0 || hat_dir > 8) hat_dir = 0; - input_event(input, usage->type, usage->code , hid_hat_to_axis[hat_dir].x); - input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y); - return; - } - - /* - * Ignore out-of-range values as per HID specification, - * section 5.10 and 6.2.25, when NULL state bit is present. - * When it's not, clamp the value to match Microsoft's input - * driver as mentioned in "Required HID usages for digitizers": - * https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp - * - * The logical_minimum < logical_maximum check is done so that we - * don't unintentionally discard values sent by devices which - * don't specify logical min and max. - */ - if ((field->flags & HID_MAIN_ITEM_VARIABLE) && - field->logical_minimum < field->logical_maximum) { - if (field->flags & HID_MAIN_ITEM_NULL_STATE && - (value < field->logical_minimum || - value > field->logical_maximum)) { - dbg_hid("Ignoring out-of-range value %x\n", value); - return; - } - value = clamp(value, - field->logical_minimum, - field->logical_maximum); - } - - switch (usage->hid) { - case HID_DG_ERASER: - report->tool_active |= !!value; - - /* - * if eraser is set, we must enforce BTN_TOOL_RUBBER - * to accommodate for devices not following the spec. - */ - if (value) - hid_report_set_tool(report, input, BTN_TOOL_RUBBER); - else if (report->tool != BTN_TOOL_RUBBER) - /* value is off, tool is not rubber, ignore */ - return; - else if (*quirks & HID_QUIRK_NOINVERT && - !test_bit(BTN_TOUCH, input->key)) { - /* - * There is no invert to release the tool, let hid_input - * send BTN_TOUCH with scancode and release the tool after. - */ - hid_report_release_tool(report, input, BTN_TOOL_RUBBER); - return; - } - - /* let hid-input set BTN_TOUCH */ - break; - - case HID_DG_INVERT: - report->tool_active |= !!value; - - /* - * If invert is set, we store BTN_TOOL_RUBBER. - */ - if (value) - hid_report_set_tool(report, input, BTN_TOOL_RUBBER); - else if (!report->tool_active) - /* tool_active not set means Invert and Eraser are not set */ - hid_report_release_tool(report, input, BTN_TOOL_RUBBER); - - /* no further processing */ - return; - - case HID_DG_INRANGE: - report->tool_active |= !!value; - - if (report->tool_active) { - /* - * if tool is not set but is marked as active, - * assume ours - */ - if (!report->tool) - report->tool = usage->code; - - /* drivers may have changed the value behind our back, resend it */ - hid_report_set_tool(report, input, report->tool); - } else { - hid_report_release_tool(report, input, usage->code); - } - - /* reset tool_active for the next event */ - report->tool_active = false; - - /* no further processing */ - return; - - case HID_DG_TIPSWITCH: - report->tool_active |= !!value; - - /* if tool is set to RUBBER we should ignore the current value */ - if (report->tool == BTN_TOOL_RUBBER) - return; - - break; - - case HID_DG_TIPPRESSURE: - if (*quirks & HID_QUIRK_NOTOUCH) { - int a = field->logical_minimum; - int b = field->logical_maximum; - - if (value > a + ((b - a) >> 3)) { - input_event(input, EV_KEY, BTN_TOUCH, 1); - report->tool_active = true; - } - } - break; - - case HID_UP_PID | 0x83UL: /* Simultaneous Effects Max */ - dbg_hid("Maximum Effects - %d\n",value); - return; - - case HID_UP_PID | 0x7fUL: - dbg_hid("PID Pool Report\n"); - return; - } - - switch (usage->type) { - case EV_KEY: - if (usage->code == 0) /* Key 0 is "unassigned", not KEY_UNKNOWN */ - return; - break; - - case EV_REL: - if (usage->code == REL_WHEEL_HI_RES || - usage->code == REL_HWHEEL_HI_RES) { - hidinput_handle_scroll(usage, input, value); - return; - } - break; - - case EV_ABS: - if ((field->flags & HID_MAIN_ITEM_RELATIVE) && - usage->code == ABS_VOLUME) { - int count = abs(value); - int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; - int i; - - for (i = 0; i < count; i++) { - input_event(input, EV_KEY, direction, 1); - input_sync(input); - input_event(input, EV_KEY, direction, 0); - input_sync(input); - } - return; - - } else if (((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) || - ((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y)) - value = field->logical_maximum - value; - break; - } - - /* - * Ignore reports for absolute data if the data didn't change. This is - * not only an optimization but also fixes 'dead' key reports. Some - * RollOver implementations for localized keys (like BACKSLASH/PIPE; HID - * 0x31 and 0x32) report multiple keys, even though a localized keyboard - * can only have one of them physically available. The 'dead' keys - * report constant 0. As all map to the same keycode, they'd confuse - * the input layer. If we filter the 'dead' keys on the HID level, we - * skip the keycode translation and only forward real events. - */ - if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | - HID_MAIN_ITEM_BUFFERED_BYTE)) && - (field->flags & HID_MAIN_ITEM_VARIABLE) && - usage->usage_index < field->maxusage && - value == field->value[usage->usage_index]) - return; - - /* report the usage code as scancode if the key status has changed */ - if (usage->type == EV_KEY && - (!test_bit(usage->code, input->key)) == value) - input_event(input, EV_MSC, MSC_SCAN, usage->hid); - - if (hid->type == HID_TYPE_USBMOUSE && usage->type == EV_REL) { - input_event_fastmouse(input, usage->type, usage->code, value); - } else { - input_event(input, usage->type, usage->code, value); - } - - - if ((field->flags & HID_MAIN_ITEM_RELATIVE) && - usage->type == EV_KEY && value) { - input_sync(input); - input_event(input, usage->type, usage->code, 0); - } -} - -void hidinput_report_event(struct hid_device *hid, struct hid_report *report) -{ - struct hid_input *hidinput; - - if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC) - return; - - list_for_each_entry(hidinput, &hid->inputs, list) - input_sync(hidinput->input); -} -EXPORT_SYMBOL_GPL(hidinput_report_event); - -static int hidinput_find_field(struct hid_device *hid, unsigned int type, - unsigned int code, struct hid_field **field) -{ - struct hid_report *report; - int i, j; - - list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) { - for (i = 0; i < report->maxfield; i++) { - *field = report->field[i]; - for (j = 0; j < (*field)->maxusage; j++) - if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) - return j; - } - } - return -1; -} - -struct hid_field *hidinput_get_led_field(struct hid_device *hid) -{ - struct hid_report *report; - struct hid_field *field; - int i, j; - - list_for_each_entry(report, - &hid->report_enum[HID_OUTPUT_REPORT].report_list, - list) { - for (i = 0; i < report->maxfield; i++) { - field = report->field[i]; - for (j = 0; j < field->maxusage; j++) - if (field->usage[j].type == EV_LED) - return field; - } - } - return NULL; -} -EXPORT_SYMBOL_GPL(hidinput_get_led_field); - -unsigned int hidinput_count_leds(struct hid_device *hid) -{ - struct hid_report *report; - struct hid_field *field; - int i, j; - unsigned int count = 0; - - list_for_each_entry(report, - &hid->report_enum[HID_OUTPUT_REPORT].report_list, - list) { - for (i = 0; i < report->maxfield; i++) { - field = report->field[i]; - for (j = 0; j < field->maxusage; j++) - if (field->usage[j].type == EV_LED && - field->value[j]) - count += 1; - } - } - return count; -} -EXPORT_SYMBOL_GPL(hidinput_count_leds); - -static void hidinput_led_worker(struct work_struct *work) -{ - struct hid_device *hid = container_of(work, struct hid_device, - led_work); - struct hid_field *field; - struct hid_report *report; - int ret; - u32 len; - __u8 *buf; - - field = hidinput_get_led_field(hid); - if (!field) - return; - - /* - * field->report is accessed unlocked regarding HID core. So there might - * be another incoming SET-LED request from user-space, which changes - * the LED state while we assemble our outgoing buffer. However, this - * doesn't matter as hid_output_report() correctly converts it into a - * boolean value no matter what information is currently set on the LED - * field (even garbage). So the remote device will always get a valid - * request. - * And in case we send a wrong value, a next led worker is spawned - * for every SET-LED request so the following worker will send the - * correct value, guaranteed! - */ - - report = field->report; - - /* use custom SET_REPORT request if possible (asynchronous) */ - if (hid->ll_driver->request) - return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); - - /* fall back to generic raw-output-report */ - len = hid_report_len(report); - buf = hid_alloc_report_buf(report, GFP_KERNEL); - if (!buf) - return; - - hid_output_report(report, buf); - /* synchronous output report */ - ret = hid_hw_output_report(hid, buf, len); - if (ret == -ENOSYS) - hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT, - HID_REQ_SET_REPORT); - kfree(buf); -} - -static int hidinput_input_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hid_field *field; - int offset; - - if (type == EV_FF) - return input_ff_event(dev, type, code, value); - - if (type != EV_LED) - return -1; - - if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - hid_set_field(field, offset, value); - - schedule_work(&hid->led_work); - return 0; -} - -static int hidinput_open(struct input_dev *dev) -{ - struct hid_device *hid = input_get_drvdata(dev); - - return hid_hw_open(hid); -} - -static void hidinput_close(struct input_dev *dev) -{ - struct hid_device *hid = input_get_drvdata(dev); - - hid_hw_close(hid); -} - -static bool __hidinput_change_resolution_multipliers(struct hid_device *hid, - struct hid_report *report, bool use_logical_max) -{ - struct hid_usage *usage; - bool update_needed = false; - bool get_report_completed = false; - int i, j; - - if (report->maxfield == 0) - return false; - - for (i = 0; i < report->maxfield; i++) { - __s32 value = use_logical_max ? - report->field[i]->logical_maximum : - report->field[i]->logical_minimum; - - /* There is no good reason for a Resolution - * Multiplier to have a count other than 1. - * Ignore that case. - */ - if (report->field[i]->report_count != 1) - continue; - - for (j = 0; j < report->field[i]->maxusage; j++) { - usage = &report->field[i]->usage[j]; - - if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) - continue; - - /* - * If we have more than one feature within this - * report we need to fill in the bits from the - * others before we can overwrite the ones for the - * Resolution Multiplier. - * - * But if we're not allowed to read from the device, - * we just bail. Such a device should not exist - * anyway. - */ - if (!get_report_completed && report->maxfield > 1) { - if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) - return update_needed; - - hid_hw_request(hid, report, HID_REQ_GET_REPORT); - hid_hw_wait(hid); - get_report_completed = true; - } - - report->field[i]->value[j] = value; - update_needed = true; - } - } - - return update_needed; -} - -static void hidinput_change_resolution_multipliers(struct hid_device *hid) -{ - struct hid_report_enum *rep_enum; - struct hid_report *rep; - int ret; - - rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; - list_for_each_entry(rep, &rep_enum->report_list, list) { - bool update_needed = __hidinput_change_resolution_multipliers(hid, - rep, true); - - if (update_needed) { - ret = __hid_request(hid, rep, HID_REQ_SET_REPORT); - if (ret) { - __hidinput_change_resolution_multipliers(hid, - rep, false); - return; - } - } - } - - /* refresh our structs */ - hid_setup_resolution_multiplier(hid); -} - -static void report_features(struct hid_device *hid) -{ - struct hid_driver *drv = hid->driver; - struct hid_report_enum *rep_enum; - struct hid_report *rep; - struct hid_usage *usage; - int i, j; - - rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; - list_for_each_entry(rep, &rep_enum->report_list, list) - for (i = 0; i < rep->maxfield; i++) { - /* Ignore if report count is out of bounds. */ - if (rep->field[i]->report_count < 1) - continue; - - for (j = 0; j < rep->field[i]->maxusage; j++) { - usage = &rep->field[i]->usage[j]; - - /* Verify if Battery Strength feature is available */ - if (usage->hid == HID_DC_BATTERYSTRENGTH) - hidinput_setup_battery(hid, HID_FEATURE_REPORT, - rep->field[i], false); - - if (drv->feature_mapping) - drv->feature_mapping(hid, rep->field[i], usage); - } - } -} - -static struct hid_input *hidinput_allocate(struct hid_device *hid, - unsigned int application) -{ - struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); - struct input_dev *input_dev = input_allocate_device(); - const char *suffix = NULL; - size_t suffix_len, name_len; - - if (!hidinput || !input_dev) - goto fail; - - if ((hid->quirks & HID_QUIRK_INPUT_PER_APP) && - hid->maxapplication > 1) { - switch (application) { - case HID_GD_KEYBOARD: - suffix = "Keyboard"; - break; - case HID_GD_KEYPAD: - suffix = "Keypad"; - break; - case HID_GD_MOUSE: - suffix = "Mouse"; - break; - case HID_DG_PEN: - /* - * yes, there is an issue here: - * DG_PEN -> "Stylus" - * DG_STYLUS -> "Pen" - * But changing this now means users with config snippets - * will have to change it and the test suite will not be happy. - */ - suffix = "Stylus"; - break; - case HID_DG_STYLUS: - suffix = "Pen"; - break; - case HID_DG_TOUCHSCREEN: - suffix = "Touchscreen"; - break; - case HID_DG_TOUCHPAD: - suffix = "Touchpad"; - break; - case HID_GD_SYSTEM_CONTROL: - suffix = "System Control"; - break; - case HID_CP_CONSUMER_CONTROL: - suffix = "Consumer Control"; - break; - case HID_GD_WIRELESS_RADIO_CTLS: - suffix = "Wireless Radio Control"; - break; - case HID_GD_SYSTEM_MULTIAXIS: - suffix = "System Multi Axis"; - break; - default: - break; - } - } - - if (suffix) { - name_len = strlen(hid->name); - suffix_len = strlen(suffix); - if ((name_len < suffix_len) || - strcmp(hid->name + name_len - suffix_len, suffix)) { - hidinput->name = kasprintf(GFP_KERNEL, "%s %s", - hid->name, suffix); - if (!hidinput->name) - goto fail; - } - } - - input_set_drvdata(input_dev, hid); - input_dev->event = hidinput_input_event; - input_dev->open = hidinput_open; - input_dev->close = hidinput_close; - input_dev->setkeycode = hidinput_setkeycode; - input_dev->getkeycode = hidinput_getkeycode; - - input_dev->name = hidinput->name ? hidinput->name : hid->name; - input_dev->phys = hid->phys; - input_dev->uniq = hid->uniq; - input_dev->id.bustype = hid->bus; - input_dev->id.vendor = hid->vendor; - input_dev->id.product = hid->product; - input_dev->id.version = hid->version; - input_dev->dev.parent = &hid->dev; - - hidinput->input = input_dev; - hidinput->application = application; - list_add_tail(&hidinput->list, &hid->inputs); - - INIT_LIST_HEAD(&hidinput->reports); - - return hidinput; - -fail: - kfree(hidinput); - input_free_device(input_dev); - hid_err(hid, "Out of memory during hid input probe\n"); - return NULL; -} - -static bool hidinput_has_been_populated(struct hid_input *hidinput) -{ - int i; - unsigned long r = 0; - - for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++) - r |= hidinput->input->evbit[i]; - - for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) - r |= hidinput->input->keybit[i]; - - for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++) - r |= hidinput->input->relbit[i]; - - for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++) - r |= hidinput->input->absbit[i]; - - for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++) - r |= hidinput->input->mscbit[i]; - - for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++) - r |= hidinput->input->ledbit[i]; - - for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++) - r |= hidinput->input->sndbit[i]; - - for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++) - r |= hidinput->input->ffbit[i]; - - for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++) - r |= hidinput->input->swbit[i]; - - return !!r; -} - -static void hidinput_cleanup_hidinput(struct hid_device *hid, - struct hid_input *hidinput) -{ - struct hid_report *report; - int i, k; - - list_del(&hidinput->list); - input_free_device(hidinput->input); - kfree(hidinput->name); - - for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { - if (k == HID_OUTPUT_REPORT && - hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) - continue; - - list_for_each_entry(report, &hid->report_enum[k].report_list, - list) { - - for (i = 0; i < report->maxfield; i++) - if (report->field[i]->hidinput == hidinput) - report->field[i]->hidinput = NULL; - } - } - - kfree(hidinput); -} - -static struct hid_input *hidinput_match(struct hid_report *report) -{ - struct hid_device *hid = report->device; - struct hid_input *hidinput; - - list_for_each_entry(hidinput, &hid->inputs, list) { - if (hidinput->report && - hidinput->report->id == report->id) - return hidinput; - } - - return NULL; -} - -static struct hid_input *hidinput_match_application(struct hid_report *report) -{ - struct hid_device *hid = report->device; - struct hid_input *hidinput; - - list_for_each_entry(hidinput, &hid->inputs, list) { - if (hidinput->application == report->application) - return hidinput; - - /* - * Keep SystemControl and ConsumerControl applications together - * with the main keyboard, if present. - */ - if ((report->application == HID_GD_SYSTEM_CONTROL || - report->application == HID_CP_CONSUMER_CONTROL) && - hidinput->application == HID_GD_KEYBOARD) { - return hidinput; - } - } - - return NULL; -} - -static inline void hidinput_configure_usages(struct hid_input *hidinput, - struct hid_report *report) -{ - int i, j, k; - int first_field_index = 0; - int slot_collection_index = -1; - int prev_collection_index = -1; - unsigned int slot_idx = 0; - struct hid_field *field; - - /* - * First tag all the fields that are part of a slot, - * a slot needs to have one Contact ID in the collection - */ - for (i = 0; i < report->maxfield; i++) { - field = report->field[i]; - - /* ignore fields without usage */ - if (field->maxusage < 1) - continue; - - /* - * janitoring when collection_index changes - */ - if (prev_collection_index != field->usage->collection_index) { - prev_collection_index = field->usage->collection_index; - first_field_index = i; - } - - /* - * if we already found a Contact ID in the collection, - * tag and continue to the next. - */ - if (slot_collection_index == field->usage->collection_index) { - field->slot_idx = slot_idx; - continue; - } - - /* check if the current field has Contact ID */ - for (j = 0; j < field->maxusage; j++) { - if (field->usage[j].hid == HID_DG_CONTACTID) { - slot_collection_index = field->usage->collection_index; - slot_idx++; - - /* - * mark all previous fields and this one in the - * current collection to be slotted. - */ - for (k = first_field_index; k <= i; k++) - report->field[k]->slot_idx = slot_idx; - break; - } - } - } - - for (i = 0; i < report->maxfield; i++) - for (j = 0; j < report->field[i]->maxusage; j++) - hidinput_configure_usage(hidinput, report->field[i], - report->field[i]->usage + j, - j); -} - -/* - * Register the input device; print a message. - * Configure the input layer interface - * Read all reports and initialize the absolute field values. - */ - -int hidinput_connect(struct hid_device *hid, unsigned int force) -{ - struct hid_driver *drv = hid->driver; - struct hid_report *report; - struct hid_input *next, *hidinput = NULL; - unsigned int application; - int i, k; - - INIT_LIST_HEAD(&hid->inputs); - INIT_WORK(&hid->led_work, hidinput_led_worker); - - hid->status &= ~HID_STAT_DUP_DETECTED; - - if (!force) { - for (i = 0; i < hid->maxcollection; i++) { - struct hid_collection *col = &hid->collection[i]; - if (col->type == HID_COLLECTION_APPLICATION || - col->type == HID_COLLECTION_PHYSICAL) - if (IS_INPUT_APPLICATION(col->usage)) - break; - } - - if (i == hid->maxcollection) - return -1; - } - - report_features(hid); - - for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { - if (k == HID_OUTPUT_REPORT && - hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) - continue; - - list_for_each_entry(report, &hid->report_enum[k].report_list, list) { - - if (!report->maxfield) - continue; - - application = report->application; - - /* - * Find the previous hidinput report attached - * to this report id. - */ - if (hid->quirks & HID_QUIRK_MULTI_INPUT) - hidinput = hidinput_match(report); - else if (hid->maxapplication > 1 && - (hid->quirks & HID_QUIRK_INPUT_PER_APP)) - hidinput = hidinput_match_application(report); - - if (!hidinput) { - hidinput = hidinput_allocate(hid, application); - if (!hidinput) - goto out_unwind; - } - - hidinput_configure_usages(hidinput, report); - - if (hid->quirks & HID_QUIRK_MULTI_INPUT) - hidinput->report = report; - - list_add_tail(&report->hidinput_list, - &hidinput->reports); - } - } - - hidinput_change_resolution_multipliers(hid); - - list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { - if (drv->input_configured && - drv->input_configured(hid, hidinput)) - goto out_unwind; - - if (!hidinput_has_been_populated(hidinput)) { - /* no need to register an input device not populated */ - hidinput_cleanup_hidinput(hid, hidinput); - continue; - } - - if (input_register_device(hidinput->input)) - goto out_unwind; - hidinput->registered = true; - } - - if (list_empty(&hid->inputs)) { - hid_err(hid, "No inputs registered, leaving\n"); - goto out_unwind; - } - - if (hid->status & HID_STAT_DUP_DETECTED) - hid_dbg(hid, - "Some usages could not be mapped, please use HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE if this is legitimate.\n"); - - return 0; - -out_unwind: - /* unwind the ones we already registered */ - hidinput_disconnect(hid); - - return -1; -} -EXPORT_SYMBOL_GPL(hidinput_connect); - -void hidinput_disconnect(struct hid_device *hid) -{ - struct hid_input *hidinput, *next; - - hidinput_cleanup_battery(hid); - - list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { - list_del(&hidinput->list); - if (hidinput->registered) - input_unregister_device(hidinput->input); - else - input_free_device(hidinput->input); - kfree(hidinput->name); - kfree(hidinput); - } - - /* led_work is spawned by input_dev callbacks, but doesn't access the - * parent input_dev at all. Once all input devices are removed, we - * know that led_work will never get restarted, so we can cancel it - * synchronously and are safe. */ - cancel_work_sync(&hid->led_work); -} -EXPORT_SYMBOL_GPL(hidinput_disconnect); - -#ifdef CONFIG_HID_KUNIT_TEST -#include "hid-input-test.c" -#endif diff --git a/src/linux/hid-core.c b/src/linux/hid-core.c new file mode 100644 index 0000000..5a64a10 --- /dev/null +++ b/src/linux/hid-core.c @@ -0,0 +1,3130 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik + * Copyright (c) 2005 Michael Haboustak for Concept2, Inc + * Copyright (c) 2006-2012 Jiri Kosina + */ + +/* + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* + * Version Information + */ + +#define DRIVER_DESC "HID core driver" + +static int hid_ignore_special_drivers = 0; +module_param_named(ignore_special_drivers, hid_ignore_special_drivers, int, 0600); +MODULE_PARM_DESC(ignore_special_drivers, "Ignore any special drivers and handle all devices by generic driver"); + +/* + * Convert a signed n-bit integer to signed 32-bit integer. + */ + +static s32 snto32(__u32 value, unsigned int n) +{ + if (!value || !n) + return 0; + + if (n > 32) + n = 32; + + return sign_extend32(value, n - 1); +} + +/* + * Convert a signed 32-bit integer to a signed n-bit integer. + */ + +static u32 s32ton(__s32 value, unsigned int n) +{ + s32 a = value >> (n - 1); + + if (a && a != -1) + return value < 0 ? 1 << (n - 1) : (1 << (n - 1)) - 1; + return value & ((1 << n) - 1); +} + +/* + * Register a new report for a device. + */ + +struct hid_report *hid_register_report(struct hid_device *device, + enum hid_report_type type, unsigned int id, + unsigned int application) +{ + struct hid_report_enum *report_enum = device->report_enum + type; + struct hid_report *report; + + if (id >= HID_MAX_IDS) + return NULL; + if (report_enum->report_id_hash[id]) + return report_enum->report_id_hash[id]; + + report = kzalloc(sizeof(struct hid_report), GFP_KERNEL); + if (!report) + return NULL; + + if (id != 0) + report_enum->numbered = 1; + + report->id = id; + report->type = type; + report->size = 0; + report->device = device; + report->application = application; + report_enum->report_id_hash[id] = report; + + list_add_tail(&report->list, &report_enum->report_list); + INIT_LIST_HEAD(&report->field_entry_list); + + return report; +} +EXPORT_SYMBOL_GPL(hid_register_report); + +/* + * Register a new field for this report. + */ + +static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages) +{ + struct hid_field *field; + + if (report->maxfield == HID_MAX_FIELDS) { + hid_err(report->device, "too many fields in report\n"); + return NULL; + } + + field = kvzalloc((sizeof(struct hid_field) + + usages * sizeof(struct hid_usage) + + 3 * usages * sizeof(unsigned int)), GFP_KERNEL); + if (!field) + return NULL; + + field->index = report->maxfield++; + report->field[field->index] = field; + field->usage = (struct hid_usage *)(field + 1); + field->value = (s32 *)(field->usage + usages); + field->new_value = (s32 *)(field->value + usages); + field->usages_priorities = (s32 *)(field->new_value + usages); + field->report = report; + + return field; +} + +/* + * Open a collection. The type/usage is pushed on the stack. + */ + +static int open_collection(struct hid_parser *parser, unsigned type) +{ + struct hid_collection *collection; + unsigned usage; + int collection_index; + + usage = parser->local.usage[0]; + + if (parser->collection_stack_ptr == parser->collection_stack_size) { + unsigned int *collection_stack; + unsigned int new_size = parser->collection_stack_size + + HID_COLLECTION_STACK_SIZE; + + collection_stack = krealloc(parser->collection_stack, + new_size * sizeof(unsigned int), + GFP_KERNEL); + if (!collection_stack) + return -ENOMEM; + + parser->collection_stack = collection_stack; + parser->collection_stack_size = new_size; + } + + if (parser->device->maxcollection == parser->device->collection_size) { + collection = kmalloc( + array3_size(sizeof(struct hid_collection), + parser->device->collection_size, + 2), + GFP_KERNEL); + if (collection == NULL) { + hid_err(parser->device, "failed to reallocate collection array\n"); + return -ENOMEM; + } + memcpy(collection, parser->device->collection, + sizeof(struct hid_collection) * + parser->device->collection_size); + memset(collection + parser->device->collection_size, 0, + sizeof(struct hid_collection) * + parser->device->collection_size); + kfree(parser->device->collection); + parser->device->collection = collection; + parser->device->collection_size *= 2; + } + + parser->collection_stack[parser->collection_stack_ptr++] = + parser->device->maxcollection; + + collection_index = parser->device->maxcollection++; + collection = parser->device->collection + collection_index; + collection->type = type; + collection->usage = usage; + collection->level = parser->collection_stack_ptr - 1; + collection->parent_idx = (collection->level == 0) ? -1 : + parser->collection_stack[collection->level - 1]; + + if (type == HID_COLLECTION_APPLICATION) + parser->device->maxapplication++; + + return 0; +} + +/* + * Close a collection. + */ + +static int close_collection(struct hid_parser *parser) +{ + if (!parser->collection_stack_ptr) { + hid_err(parser->device, "collection stack underflow\n"); + return -EINVAL; + } + parser->collection_stack_ptr--; + return 0; +} + +/* + * Climb up the stack, search for the specified collection type + * and return the usage. + */ + +static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) +{ + struct hid_collection *collection = parser->device->collection; + int n; + + for (n = parser->collection_stack_ptr - 1; n >= 0; n--) { + unsigned index = parser->collection_stack[n]; + if (collection[index].type == type) + return collection[index].usage; + } + return 0; /* we know nothing about this usage type */ +} + +/* + * Concatenate usage which defines 16 bits or less with the + * currently defined usage page to form a 32 bit usage + */ + +static void complete_usage(struct hid_parser *parser, unsigned int index) +{ + parser->local.usage[index] &= 0xFFFF; + parser->local.usage[index] |= + (parser->global.usage_page & 0xFFFF) << 16; +} + +/* + * Add a usage to the temporary parser table. + */ + +static int hid_add_usage(struct hid_parser *parser, unsigned usage, u8 size) +{ + if (parser->local.usage_index >= HID_MAX_USAGES) { + hid_err(parser->device, "usage index exceeded\n"); + return -1; + } + parser->local.usage[parser->local.usage_index] = usage; + + /* + * If Usage item only includes usage id, concatenate it with + * currently defined usage page + */ + if (size <= 2) + complete_usage(parser, parser->local.usage_index); + + parser->local.usage_size[parser->local.usage_index] = size; + parser->local.collection_index[parser->local.usage_index] = + parser->collection_stack_ptr ? + parser->collection_stack[parser->collection_stack_ptr - 1] : 0; + parser->local.usage_index++; + return 0; +} + +/* + * Register a new field for this report. + */ + +static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags) +{ + struct hid_report *report; + struct hid_field *field; + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + unsigned int usages; + unsigned int offset; + unsigned int i; + unsigned int application; + + application = hid_lookup_collection(parser, HID_COLLECTION_APPLICATION); + + report = hid_register_report(parser->device, report_type, + parser->global.report_id, application); + if (!report) { + hid_err(parser->device, "hid_register_report failed\n"); + return -1; + } + + /* Handle both signed and unsigned cases properly */ + if ((parser->global.logical_minimum < 0 && + parser->global.logical_maximum < + parser->global.logical_minimum) || + (parser->global.logical_minimum >= 0 && + (__u32)parser->global.logical_maximum < + (__u32)parser->global.logical_minimum)) { + dbg_hid("logical range invalid 0x%x 0x%x\n", + parser->global.logical_minimum, + parser->global.logical_maximum); + return -1; + } + + offset = report->size; + report->size += parser->global.report_size * parser->global.report_count; + + if (parser->device->ll_driver->max_buffer_size) + max_buffer_size = parser->device->ll_driver->max_buffer_size; + + /* Total size check: Allow for possible report index byte */ + if (report->size > (max_buffer_size - 1) << 3) { + hid_err(parser->device, "report is too long\n"); + return -1; + } + + if (!parser->local.usage_index) /* Ignore padding fields */ + return 0; + + usages = max_t(unsigned, parser->local.usage_index, + parser->global.report_count); + + field = hid_register_field(report, usages); + if (!field) + return 0; + + field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); + field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); + field->application = application; + + for (i = 0; i < usages; i++) { + unsigned j = i; + /* Duplicate the last usage we parsed if we have excess values */ + if (i >= parser->local.usage_index) + j = parser->local.usage_index - 1; + field->usage[i].hid = parser->local.usage[j]; + field->usage[i].collection_index = + parser->local.collection_index[j]; + field->usage[i].usage_index = i; + field->usage[i].resolution_multiplier = 1; + } + + field->maxusage = usages; + field->flags = flags; + field->report_offset = offset; + field->report_type = report_type; + field->report_size = parser->global.report_size; + field->report_count = parser->global.report_count; + field->logical_minimum = parser->global.logical_minimum; + field->logical_maximum = parser->global.logical_maximum; + field->physical_minimum = parser->global.physical_minimum; + field->physical_maximum = parser->global.physical_maximum; + field->unit_exponent = parser->global.unit_exponent; + field->unit = parser->global.unit; + + return 0; +} + +/* + * Read data value from item. + */ + +static u32 item_udata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.u8; + case 2: return item->data.u16; + case 4: return item->data.u32; + } + return 0; +} + +static s32 item_sdata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.s8; + case 2: return item->data.s16; + case 4: return item->data.s32; + } + return 0; +} + +/* + * Process a global item. + */ + +static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) +{ + __s32 raw_value; + switch (item->tag) { + case HID_GLOBAL_ITEM_TAG_PUSH: + + if (parser->global_stack_ptr == HID_GLOBAL_STACK_SIZE) { + hid_err(parser->device, "global environment stack overflow\n"); + return -1; + } + + memcpy(parser->global_stack + parser->global_stack_ptr++, + &parser->global, sizeof(struct hid_global)); + return 0; + + case HID_GLOBAL_ITEM_TAG_POP: + + if (!parser->global_stack_ptr) { + hid_err(parser->device, "global environment stack underflow\n"); + return -1; + } + + memcpy(&parser->global, parser->global_stack + + --parser->global_stack_ptr, sizeof(struct hid_global)); + return 0; + + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + parser->global.usage_page = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + parser->global.logical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + if (parser->global.logical_minimum < 0) + parser->global.logical_maximum = item_sdata(item); + else + parser->global.logical_maximum = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: + parser->global.physical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + if (parser->global.physical_minimum < 0) + parser->global.physical_maximum = item_sdata(item); + else + parser->global.physical_maximum = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: + /* Many devices provide unit exponent as a two's complement + * nibble due to the common misunderstanding of HID + * specification 1.11, 6.2.2.7 Global Items. Attempt to handle + * both this and the standard encoding. */ + raw_value = item_sdata(item); + if (!(raw_value & 0xfffffff0)) + parser->global.unit_exponent = snto32(raw_value, 4); + else + parser->global.unit_exponent = raw_value; + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT: + parser->global.unit = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + parser->global.report_size = item_udata(item); + if (parser->global.report_size > 256) { + hid_err(parser->device, "invalid report_size %d\n", + parser->global.report_size); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + parser->global.report_count = item_udata(item); + if (parser->global.report_count > HID_MAX_USAGES) { + hid_err(parser->device, "invalid report_count %d\n", + parser->global.report_count); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + parser->global.report_id = item_udata(item); + if (parser->global.report_id == 0 || + parser->global.report_id >= HID_MAX_IDS) { + hid_err(parser->device, "report_id %u is invalid\n", + parser->global.report_id); + return -1; + } + return 0; + + default: + hid_err(parser->device, "unknown global tag 0x%x\n", item->tag); + return -1; + } +} + +/* + * Process a local item. + */ + +static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + unsigned n; + __u32 count; + + data = item_udata(item); + + switch (item->tag) { + case HID_LOCAL_ITEM_TAG_DELIMITER: + + if (data) { + /* + * We treat items before the first delimiter + * as global to all usage sets (branch 0). + * In the moment we process only these global + * items and the first delimiter set. + */ + if (parser->local.delimiter_depth != 0) { + hid_err(parser->device, "nested delimiters\n"); + return -1; + } + parser->local.delimiter_depth++; + parser->local.delimiter_branch++; + } else { + if (parser->local.delimiter_depth < 1) { + hid_err(parser->device, "bogus close delimiter\n"); + return -1; + } + parser->local.delimiter_depth--; + } + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE: + + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); + return 0; + } + + return hid_add_usage(parser, data, item->size); + + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); + return 0; + } + + parser->local.usage_minimum = data; + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + + if (parser->local.delimiter_branch > 1) { + dbg_hid("alternative usage ignored\n"); + return 0; + } + + count = data - parser->local.usage_minimum; + if (count + parser->local.usage_index >= HID_MAX_USAGES) { + /* + * We do not warn if the name is not set, we are + * actually pre-scanning the device. + */ + if (dev_name(&parser->device->dev)) + hid_warn(parser->device, + "ignoring exceeding usage max\n"); + data = HID_MAX_USAGES - parser->local.usage_index + + parser->local.usage_minimum - 1; + if (data <= 0) { + hid_err(parser->device, + "no more usage index available\n"); + return -1; + } + } + + for (n = parser->local.usage_minimum; n <= data; n++) + if (hid_add_usage(parser, n, item->size)) { + dbg_hid("hid_add_usage failed\n"); + return -1; + } + return 0; + + default: + + dbg_hid("unknown local item tag 0x%x\n", item->tag); + return 0; + } + return 0; +} + +/* + * Concatenate Usage Pages into Usages where relevant: + * As per specification, 6.2.2.8: "When the parser encounters a main item it + * concatenates the last declared Usage Page with a Usage to form a complete + * usage value." + */ + +static void hid_concatenate_last_usage_page(struct hid_parser *parser) +{ + int i; + unsigned int usage_page; + unsigned int current_page; + + if (!parser->local.usage_index) + return; + + usage_page = parser->global.usage_page; + + /* + * Concatenate usage page again only if last declared Usage Page + * has not been already used in previous usages concatenation + */ + for (i = parser->local.usage_index - 1; i >= 0; i--) { + if (parser->local.usage_size[i] > 2) + /* Ignore extended usages */ + continue; + + current_page = parser->local.usage[i] >> 16; + if (current_page == usage_page) + break; + + complete_usage(parser, i); + } +} + +/* + * Process a main item. + */ + +static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int ret; + + hid_concatenate_last_usage_page(parser); + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + ret = open_collection(parser, data & 0xff); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + ret = close_collection(parser); + break; + case HID_MAIN_ITEM_TAG_INPUT: + ret = hid_add_field(parser, HID_INPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_FEATURE: + ret = hid_add_field(parser, HID_FEATURE_REPORT, data); + break; + default: + hid_warn(parser->device, "unknown main item tag 0x%x\n", item->tag); + ret = 0; + } + + memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ + + return ret; +} + +/* + * Process a reserved item. + */ + +static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) +{ + dbg_hid("reserved item type, tag 0x%x\n", item->tag); + return 0; +} + +/* + * Free a report and all registered fields. The field->usage and + * field->value table's are allocated behind the field, so we need + * only to free(field) itself. + */ + +static void hid_free_report(struct hid_report *report) +{ + unsigned n; + + kfree(report->field_entries); + + for (n = 0; n < report->maxfield; n++) + kvfree(report->field[n]); + kfree(report); +} + +/* + * Close report. This function returns the device + * state to the point prior to hid_open_report(). + */ +static void hid_close_report(struct hid_device *device) +{ + unsigned i, j; + + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = device->report_enum + i; + + for (j = 0; j < HID_MAX_IDS; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + if (report) + hid_free_report(report); + } + memset(report_enum, 0, sizeof(*report_enum)); + INIT_LIST_HEAD(&report_enum->report_list); + } + + /* + * If the HID driver had a rdesc_fixup() callback, dev->rdesc + * will be allocated by hid-core and needs to be freed. + * Otherwise, it is either equal to dev_rdesc or bpf_rdesc, in + * which cases it'll be freed later on device removal or destroy. + */ + if (device->rdesc != device->dev_rdesc && device->rdesc != device->bpf_rdesc) + kfree(device->rdesc); + device->rdesc = NULL; + device->rsize = 0; + + kfree(device->collection); + device->collection = NULL; + device->collection_size = 0; + device->maxcollection = 0; + device->maxapplication = 0; + + device->status &= ~HID_STAT_PARSED; +} + +static inline void hid_free_bpf_rdesc(struct hid_device *hdev) +{ + /* bpf_rdesc is either equal to dev_rdesc or allocated by call_hid_bpf_rdesc_fixup() */ + if (hdev->bpf_rdesc != hdev->dev_rdesc) + kfree(hdev->bpf_rdesc); + hdev->bpf_rdesc = NULL; +} + +/* + * Free a device structure, all reports, and all fields. + */ + +void hiddev_free(struct kref *ref) +{ + struct hid_device *hid = container_of(ref, struct hid_device, ref); + + hid_close_report(hid); + hid_free_bpf_rdesc(hid); + kfree(hid->dev_rdesc); + kfree(hid); +} + +static void hid_device_release(struct device *dev) +{ + struct hid_device *hid = to_hid_device(dev); + + kref_put(&hid->ref, hiddev_free); +} + +/* + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. + */ + +static const u8 *fetch_item(const __u8 *start, const __u8 *end, struct hid_item *item) +{ + u8 b; + + if ((end - start) <= 0) + return NULL; + + b = *start++; + + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; + + if (item->tag == HID_ITEM_TAG_LONG) { + + item->format = HID_ITEM_FORMAT_LONG; + + if ((end - start) < 2) + return NULL; + + item->size = *start++; + item->tag = *start++; + + if ((end - start) < item->size) + return NULL; + + item->data.longdata = start; + start += item->size; + return start; + } + + item->format = HID_ITEM_FORMAT_SHORT; + item->size = BIT(b & 3) >> 1; /* 0, 1, 2, 3 -> 0, 1, 2, 4 */ + + if (end - start < item->size) + return NULL; + + switch (item->size) { + case 0: + break; + + case 1: + item->data.u8 = *start; + break; + + case 2: + item->data.u16 = get_unaligned_le16(start); + break; + + case 4: + item->data.u32 = get_unaligned_le32(start); + break; + } + + return start + item->size; +} + +static void hid_scan_input_usage(struct hid_parser *parser, u32 usage) +{ + struct hid_device *hid = parser->device; + + if (usage == HID_DG_CONTACTID) + hid->group = HID_GROUP_MULTITOUCH; +} + +static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) +{ + if (usage == 0xff0000c5 && parser->global.report_count == 256 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; + + if (usage == 0xff0000c6 && parser->global.report_count == 1 && + parser->global.report_size == 8) + parser->scan_flags |= HID_SCAN_FLAG_MT_WIN_8; +} + +static void hid_scan_collection(struct hid_parser *parser, unsigned type) +{ + struct hid_device *hid = parser->device; + int i; + + if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && + (type == HID_COLLECTION_PHYSICAL || + type == HID_COLLECTION_APPLICATION)) + hid->group = HID_GROUP_SENSOR_HUB; + + if (hid->vendor == USB_VENDOR_ID_MICROSOFT && + hid->product == USB_DEVICE_ID_MS_POWER_COVER && + hid->group == HID_GROUP_MULTITOUCH) + hid->group = HID_GROUP_GENERIC; + + if ((parser->global.usage_page << 16) == HID_UP_GENDESK) + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == HID_GD_POINTER) + parser->scan_flags |= HID_SCAN_FLAG_GD_POINTER; + + if ((parser->global.usage_page << 16) >= HID_UP_MSVENDOR) + parser->scan_flags |= HID_SCAN_FLAG_VENDOR_SPECIFIC; + + if ((parser->global.usage_page << 16) == HID_UP_GOOGLEVENDOR) + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == + (HID_UP_GOOGLEVENDOR | 0x0001)) + parser->device->group = + HID_GROUP_VIVALDI; +} + +static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int i; + + hid_concatenate_last_usage_page(parser); + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + hid_scan_collection(parser, data & 0xff); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + break; + case HID_MAIN_ITEM_TAG_INPUT: + /* ignore constant inputs, they will be ignored by hid-input */ + if (data & HID_MAIN_ITEM_CONSTANT) + break; + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_input_usage(parser, parser->local.usage[i]); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + break; + case HID_MAIN_ITEM_TAG_FEATURE: + for (i = 0; i < parser->local.usage_index; i++) + hid_scan_feature_usage(parser, parser->local.usage[i]); + break; + } + + /* Reset the local parser environment */ + memset(&parser->local, 0, sizeof(parser->local)); + + return 0; +} + +/* + * Scan a report descriptor before the device is added to the bus. + * Sets device groups and other properties that determine what driver + * to load. + */ +static int hid_scan_report(struct hid_device *hid) +{ + struct hid_parser *parser; + struct hid_item item; + const __u8 *start = hid->dev_rdesc; + const __u8 *end = start + hid->dev_rsize; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_scan_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + parser = vzalloc(sizeof(struct hid_parser)); + if (!parser) + return -ENOMEM; + + parser->device = hid; + hid->group = HID_GROUP_GENERIC; + + /* + * The parsing is simpler than the one in hid_open_report() as we should + * be robust against hid errors. Those errors will be raised by + * hid_open_report() anyway. + */ + while ((start = fetch_item(start, end, &item)) != NULL) + dispatch_type[item.type](parser, &item); + + /* + * Handle special flags set during scanning. + */ + if ((parser->scan_flags & HID_SCAN_FLAG_MT_WIN_8) && + (hid->group == HID_GROUP_MULTITOUCH)) + hid->group = HID_GROUP_MULTITOUCH_WIN_8; + + /* + * Vendor specific handlings + */ + switch (hid->vendor) { + case USB_VENDOR_ID_WACOM: + hid->group = HID_GROUP_WACOM; + break; + case USB_VENDOR_ID_SYNAPTICS: + if (hid->group == HID_GROUP_GENERIC) + if ((parser->scan_flags & HID_SCAN_FLAG_VENDOR_SPECIFIC) + && (parser->scan_flags & HID_SCAN_FLAG_GD_POINTER)) + /* + * hid-rmi should take care of them, + * not hid-generic + */ + hid->group = HID_GROUP_RMI; + break; + } + + kfree(parser->collection_stack); + vfree(parser); + return 0; +} + +/** + * hid_parse_report - parse device report + * + * @hid: hid device + * @start: report start + * @size: report size + * + * Allocate the device report as read by the bus driver. This function should + * only be called from parse() in ll drivers. + */ +int hid_parse_report(struct hid_device *hid, const __u8 *start, unsigned size) +{ + hid->dev_rdesc = kmemdup(start, size, GFP_KERNEL); + if (!hid->dev_rdesc) + return -ENOMEM; + hid->dev_rsize = size; + return 0; +} +EXPORT_SYMBOL_GPL(hid_parse_report); + +static const char * const hid_report_names[] = { + "HID_INPUT_REPORT", + "HID_OUTPUT_REPORT", + "HID_FEATURE_REPORT", +}; +/** + * hid_validate_values - validate existing device report's value indexes + * + * @hid: hid device + * @type: which report type to examine + * @id: which report ID to examine (0 for first) + * @field_index: which report field to examine + * @report_counts: expected number of values + * + * Validate the number of values in a given field of a given report, after + * parsing. + */ +struct hid_report *hid_validate_values(struct hid_device *hid, + enum hid_report_type type, unsigned int id, + unsigned int field_index, + unsigned int report_counts) +{ + struct hid_report *report; + + if (type > HID_FEATURE_REPORT) { + hid_err(hid, "invalid HID report type %u\n", type); + return NULL; + } + + if (id >= HID_MAX_IDS) { + hid_err(hid, "invalid HID report id %u\n", id); + return NULL; + } + + /* + * Explicitly not using hid_get_report() here since it depends on + * ->numbered being checked, which may not always be the case when + * drivers go to access report values. + */ + if (id == 0) { + /* + * Validating on id 0 means we should examine the first + * report in the list. + */ + report = list_first_entry_or_null( + &hid->report_enum[type].report_list, + struct hid_report, list); + } else { + report = hid->report_enum[type].report_id_hash[id]; + } + if (!report) { + hid_err(hid, "missing %s %u\n", hid_report_names[type], id); + return NULL; + } + if (report->maxfield <= field_index) { + hid_err(hid, "not enough fields in %s %u\n", + hid_report_names[type], id); + return NULL; + } + if (report->field[field_index]->report_count < report_counts) { + hid_err(hid, "not enough values in %s %u field %u\n", + hid_report_names[type], id, field_index); + return NULL; + } + return report; +} +EXPORT_SYMBOL_GPL(hid_validate_values); + +static int hid_calculate_multiplier(struct hid_device *hid, + struct hid_field *multiplier) +{ + int m; + __s32 v = *multiplier->value; + __s32 lmin = multiplier->logical_minimum; + __s32 lmax = multiplier->logical_maximum; + __s32 pmin = multiplier->physical_minimum; + __s32 pmax = multiplier->physical_maximum; + + /* + * "Because OS implementations will generally divide the control's + * reported count by the Effective Resolution Multiplier, designers + * should take care not to establish a potential Effective + * Resolution Multiplier of zero." + * HID Usage Table, v1.12, Section 4.3.1, p31 + */ + if (lmax - lmin == 0) + return 1; + /* + * Handling the unit exponent is left as an exercise to whoever + * finds a device where that exponent is not 0. + */ + m = ((v - lmin)/(lmax - lmin) * (pmax - pmin) + pmin); + if (unlikely(multiplier->unit_exponent != 0)) { + hid_warn(hid, + "unsupported Resolution Multiplier unit exponent %d\n", + multiplier->unit_exponent); + } + + /* There are no devices with an effective multiplier > 255 */ + if (unlikely(m == 0 || m > 255 || m < -255)) { + hid_warn(hid, "unsupported Resolution Multiplier %d\n", m); + m = 1; + } + + return m; +} + +static void hid_apply_multiplier_to_field(struct hid_device *hid, + struct hid_field *field, + struct hid_collection *multiplier_collection, + int effective_multiplier) +{ + struct hid_collection *collection; + struct hid_usage *usage; + int i; + + /* + * If multiplier_collection is NULL, the multiplier applies + * to all fields in the report. + * Otherwise, it is the Logical Collection the multiplier applies to + * but our field may be in a subcollection of that collection. + */ + for (i = 0; i < field->maxusage; i++) { + usage = &field->usage[i]; + + collection = &hid->collection[usage->collection_index]; + while (collection->parent_idx != -1 && + collection != multiplier_collection) + collection = &hid->collection[collection->parent_idx]; + + if (collection->parent_idx != -1 || + multiplier_collection == NULL) + usage->resolution_multiplier = effective_multiplier; + + } +} + +static void hid_apply_multiplier(struct hid_device *hid, + struct hid_field *multiplier) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_field *field; + struct hid_collection *multiplier_collection; + int effective_multiplier; + int i; + + /* + * "The Resolution Multiplier control must be contained in the same + * Logical Collection as the control(s) to which it is to be applied. + * If no Resolution Multiplier is defined, then the Resolution + * Multiplier defaults to 1. If more than one control exists in a + * Logical Collection, the Resolution Multiplier is associated with + * all controls in the collection. If no Logical Collection is + * defined, the Resolution Multiplier is associated with all + * controls in the report." + * HID Usage Table, v1.12, Section 4.3.1, p30 + * + * Thus, search from the current collection upwards until we find a + * logical collection. Then search all fields for that same parent + * collection. Those are the fields the multiplier applies to. + * + * If we have more than one multiplier, it will overwrite the + * applicable fields later. + */ + multiplier_collection = &hid->collection[multiplier->usage->collection_index]; + while (multiplier_collection->parent_idx != -1 && + multiplier_collection->type != HID_COLLECTION_LOGICAL) + multiplier_collection = &hid->collection[multiplier_collection->parent_idx]; + if (multiplier_collection->type != HID_COLLECTION_LOGICAL) + multiplier_collection = NULL; + + effective_multiplier = hid_calculate_multiplier(hid, multiplier); + + rep_enum = &hid->report_enum[HID_INPUT_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + field = rep->field[i]; + hid_apply_multiplier_to_field(hid, field, + multiplier_collection, + effective_multiplier); + } + } +} + +/* + * hid_setup_resolution_multiplier - set up all resolution multipliers + * + * @device: hid device + * + * Search for all Resolution Multiplier Feature Reports and apply their + * value to all matching Input items. This only updates the internal struct + * fields. + * + * The Resolution Multiplier is applied by the hardware. If the multiplier + * is anything other than 1, the hardware will send pre-multiplied events + * so that the same physical interaction generates an accumulated + * accumulated_value = value * * multiplier + * This may be achieved by sending + * - "value * multiplier" for each event, or + * - "value" but "multiplier" times as frequently, or + * - a combination of the above + * The only guarantee is that the same physical interaction always generates + * an accumulated 'value * multiplier'. + * + * This function must be called before any event processing and after + * any SetRequest to the Resolution Multiplier. + */ +void hid_setup_resolution_multiplier(struct hid_device *hid) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_usage *usage; + int i, j; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + if (usage->hid == HID_GD_RESOLUTION_MULTIPLIER) + hid_apply_multiplier(hid, + rep->field[i]); + } + } + } +} +EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier); + +/** + * hid_open_report - open a driver-specific device report + * + * @device: hid device + * + * Parse a report description into a hid_device structure. Reports are + * enumerated, fields are attached to these reports. + * 0 returned on success, otherwise nonzero error value. + * + * This function (or the equivalent hid_parse() macro) should only be + * called from probe() in drivers, before starting the device. + */ +int hid_open_report(struct hid_device *device) +{ + struct hid_parser *parser; + struct hid_item item; + unsigned int size; + const __u8 *start; + const __u8 *end; + const __u8 *next; + int ret; + int i; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_parser_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + if (WARN_ON(device->status & HID_STAT_PARSED)) + return -EBUSY; + + start = device->bpf_rdesc; + if (WARN_ON(!start)) + return -ENODEV; + size = device->bpf_rsize; + + if (device->driver->report_fixup) { + /* + * device->driver->report_fixup() needs to work + * on a copy of our report descriptor so it can + * change it. + */ + __u8 *buf = kmemdup(start, size, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + start = device->driver->report_fixup(device, buf, &size); + + /* + * The second kmemdup is required in case report_fixup() returns + * a static read-only memory, but we have no idea if that memory + * needs to be cleaned up or not at the end. + */ + start = kmemdup(start, size, GFP_KERNEL); + kfree(buf); + if (start == NULL) + return -ENOMEM; + } + + device->rdesc = start; + device->rsize = size; + + parser = vzalloc(sizeof(struct hid_parser)); + if (!parser) { + ret = -ENOMEM; + goto alloc_err; + } + + parser->device = device; + + end = start + size; + + device->collection = kcalloc(HID_DEFAULT_NUM_COLLECTIONS, + sizeof(struct hid_collection), GFP_KERNEL); + if (!device->collection) { + ret = -ENOMEM; + goto err; + } + device->collection_size = HID_DEFAULT_NUM_COLLECTIONS; + for (i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++) + device->collection[i].parent_idx = -1; + + ret = -EINVAL; + while ((next = fetch_item(start, end, &item)) != NULL) { + start = next; + + if (item.format != HID_ITEM_FORMAT_SHORT) { + hid_err(device, "unexpected long global item\n"); + goto err; + } + + if (dispatch_type[item.type](parser, &item)) { + hid_err(device, "item %u %u %u %u parsing failed\n", + item.format, (unsigned)item.size, + (unsigned)item.type, (unsigned)item.tag); + goto err; + } + + if (start == end) { + if (parser->collection_stack_ptr) { + hid_err(device, "unbalanced collection at end of report description\n"); + goto err; + } + if (parser->local.delimiter_depth) { + hid_err(device, "unbalanced delimiter at end of report description\n"); + goto err; + } + + /* + * fetch initial values in case the device's + * default multiplier isn't the recommended 1 + */ + hid_setup_resolution_multiplier(device); + + kfree(parser->collection_stack); + vfree(parser); + device->status |= HID_STAT_PARSED; + + return 0; + } + } + + hid_err(device, "item fetching failed at offset %u/%u\n", + size - (unsigned int)(end - start), size); +err: + kfree(parser->collection_stack); +alloc_err: + vfree(parser); + hid_close_report(device); + return ret; +} +EXPORT_SYMBOL_GPL(hid_open_report); + +/* + * Extract/implement a data field from/to a little endian report (bit array). + * + * Code sort-of follows HID spec: + * http://www.usb.org/developers/hidpage/HID1_11.pdf + * + * While the USB HID spec allows unlimited length bit fields in "report + * descriptors", most devices never use more than 16 bits. + * One model of UPS is claimed to report "LINEV" as a 32-bit field. + * Search linux-kernel and linux-usb-devel archives for "hid-core extract". + */ + +static u32 __extract(u8 *report, unsigned offset, int n) +{ + unsigned int idx = offset / 8; + unsigned int bit_nr = 0; + unsigned int bit_shift = offset % 8; + int bits_to_copy = 8 - bit_shift; + u32 value = 0; + u32 mask = n < 32 ? (1U << n) - 1 : ~0U; + + while (n > 0) { + value |= ((u32)report[idx] >> bit_shift) << bit_nr; + n -= bits_to_copy; + bit_nr += bits_to_copy; + bits_to_copy = 8; + bit_shift = 0; + idx++; + } + + return value & mask; +} + +u32 hid_field_extract(const struct hid_device *hid, u8 *report, + unsigned offset, unsigned n) +{ + if (n > 32) { + hid_warn_once(hid, "%s() called with n (%d) > 32! (%s)\n", + __func__, n, current->comm); + n = 32; + } + + return __extract(report, offset, n); +} +EXPORT_SYMBOL_GPL(hid_field_extract); + +/* + * "implement" : set bits in a little endian bit stream. + * Same concepts as "extract" (see comments above). + * The data mangled in the bit stream remains in little endian + * order the whole time. It make more sense to talk about + * endianness of register values by considering a register + * a "cached" copy of the little endian bit stream. + */ + +static void __implement(u8 *report, unsigned offset, int n, u32 value) +{ + unsigned int idx = offset / 8; + unsigned int bit_shift = offset % 8; + int bits_to_set = 8 - bit_shift; + + while (n - bits_to_set >= 0) { + report[idx] &= ~(0xff << bit_shift); + report[idx] |= value << bit_shift; + value >>= bits_to_set; + n -= bits_to_set; + bits_to_set = 8; + bit_shift = 0; + idx++; + } + + /* last nibble */ + if (n) { + u8 bit_mask = ((1U << n) - 1); + report[idx] &= ~(bit_mask << bit_shift); + report[idx] |= value << bit_shift; + } +} + +static void implement(const struct hid_device *hid, u8 *report, + unsigned offset, unsigned n, u32 value) +{ + if (unlikely(n > 32)) { + hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n", + __func__, n, current->comm); + n = 32; + } else if (n < 32) { + u32 m = (1U << n) - 1; + + if (unlikely(value > m)) { + hid_warn(hid, + "%s() called with too large value %d (n: %d)! (%s)\n", + __func__, value, n, current->comm); + value &= m; + } + } + + __implement(report, offset, n, value); +} + +/* + * Search an array for a value. + */ + +static int search(__s32 *array, __s32 value, unsigned n) +{ + while (n--) { + if (*array++ == value) + return 0; + } + return -1; +} + +/** + * hid_match_report - check if driver's raw_event should be called + * + * @hid: hid device + * @report: hid report to match against + * + * compare hid->driver->report_table->report_type to report->type + */ +static int hid_match_report(struct hid_device *hid, struct hid_report *report) +{ + const struct hid_report_id *id = hid->driver->report_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->report_type != HID_TERMINATOR; id++) + if (id->report_type == HID_ANY_ID || + id->report_type == report->type) + return 1; + return 0; +} + +/** + * hid_match_usage - check if driver's event should be called + * + * @hid: hid device + * @usage: usage to match against + * + * compare hid->driver->usage_table->usage_{type,code} to + * usage->usage_{type,code} + */ +static int hid_match_usage(struct hid_device *hid, struct hid_usage *usage) +{ + const struct hid_usage_id *id = hid->driver->usage_table; + + if (!id) /* NULL means all */ + return 1; + + for (; id->usage_type != HID_ANY_ID - 1; id++) + if ((id->usage_hid == HID_ANY_ID || + id->usage_hid == usage->hid) && + (id->usage_type == HID_ANY_ID || + id->usage_type == usage->type) && + (id->usage_code == HID_ANY_ID || + id->usage_code == usage->code)) + return 1; + return 0; +} + +static void hid_process_event(struct hid_device *hid, struct hid_field *field, + struct hid_usage *usage, __s32 value, int interrupt) +{ + struct hid_driver *hdrv = hid->driver; + int ret; + + if (!list_empty(&hid->debug_list)) + hid_dump_input(hid, usage, value); + + if (hdrv && hdrv->event && hid_match_usage(hid, usage)) { + ret = hdrv->event(hid, field, usage, value); + if (ret != 0) { + if (ret < 0) + hid_err(hid, "%s's event failed with %d\n", + hdrv->name, ret); + return; + } + } + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_hid_event(hid, field, usage, value); + if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event) + hid->hiddev_hid_event(hid, field, usage, value); +} + +/* + * Checks if the given value is valid within this field + */ +static inline int hid_array_value_is_valid(struct hid_field *field, + __s32 value) +{ + __s32 min = field->logical_minimum; + + /* + * Value needs to be between logical min and max, and + * (value - min) is used as an index in the usage array. + * This array is of size field->maxusage + */ + return value >= min && + value <= field->logical_maximum && + value - min < field->maxusage; +} + +/* + * Fetch the field from the data. The field content is stored for next + * report processing (we do differential reporting to the layer). + */ +static void hid_input_fetch_field(struct hid_device *hid, + struct hid_field *field, + __u8 *data) +{ + unsigned n; + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + __s32 min = field->logical_minimum; + __s32 *value; + + value = field->new_value; + memset(value, 0, count * sizeof(__s32)); + field->ignored = false; + + for (n = 0; n < count; n++) { + + value[n] = min < 0 ? + snto32(hid_field_extract(hid, data, offset + n * size, + size), size) : + hid_field_extract(hid, data, offset + n * size, size); + + /* Ignore report if ErrorRollOver */ + if (!(field->flags & HID_MAIN_ITEM_VARIABLE) && + hid_array_value_is_valid(field, value[n]) && + field->usage[value[n] - min].hid == HID_UP_KEYBOARD + 1) { + field->ignored = true; + return; + } + } +} + +/* + * Process a received variable field. + */ + +static void hid_input_var_field(struct hid_device *hid, + struct hid_field *field, + int interrupt) +{ + unsigned int count = field->report_count; + __s32 *value = field->new_value; + unsigned int n; + + for (n = 0; n < count; n++) + hid_process_event(hid, + field, + &field->usage[n], + value[n], + interrupt); + + memcpy(field->value, value, count * sizeof(__s32)); +} + +/* + * Process a received array field. The field content is stored for + * next report processing (we do differential reporting to the layer). + */ + +static void hid_input_array_field(struct hid_device *hid, + struct hid_field *field, + int interrupt) +{ + unsigned int n; + unsigned int count = field->report_count; + __s32 min = field->logical_minimum; + __s32 *value; + + value = field->new_value; + + /* ErrorRollOver */ + if (field->ignored) + return; + + for (n = 0; n < count; n++) { + if (hid_array_value_is_valid(field, field->value[n]) && + search(value, field->value[n], count)) + hid_process_event(hid, + field, + &field->usage[field->value[n] - min], + 0, + interrupt); + + if (hid_array_value_is_valid(field, value[n]) && + search(field->value, value[n], count)) + hid_process_event(hid, + field, + &field->usage[value[n] - min], + 1, + interrupt); + } + + memcpy(field->value, value, count * sizeof(__s32)); +} + +/* + * Analyse a received report, and fetch the data from it. The field + * content is stored for next report processing (we do differential + * reporting to the layer). + */ +static void hid_process_report(struct hid_device *hid, + struct hid_report *report, + __u8 *data, + int interrupt) +{ + unsigned int a; + struct hid_field_entry *entry; + struct hid_field *field; + struct input_dev *fastmouse_dev = NULL; + + + /* first retrieve all incoming values in data */ + for (a = 0; a < report->maxfield; a++) + hid_input_fetch_field(hid, report->field[a], data); + + if (!list_empty(&report->field_entry_list)) { + /* INPUT_REPORT, we have a priority list of fields */ + list_for_each_entry(entry, + &report->field_entry_list, + list) { + field = entry->field; + + if (hid->type == HID_TYPE_USBMOUSE && field->hidinput) { + fastmouse_dev = field->hidinput->input; + } + + if (field->flags & HID_MAIN_ITEM_VARIABLE) + hid_process_event(hid, + field, + &field->usage[entry->index], + field->new_value[entry->index], + interrupt); + else + hid_input_array_field(hid, field, interrupt); + } + + /* we need to do the memcpy at the end for var items */ + for (a = 0; a < report->maxfield; a++) { + field = report->field[a]; + + if (field->flags & HID_MAIN_ITEM_VARIABLE) + memcpy(field->value, field->new_value, + field->report_count * sizeof(__s32)); + } + } else { + /* FEATURE_REPORT, regular processing */ + for (a = 0; a < report->maxfield; a++) { + field = report->field[a]; + + if (field->flags & HID_MAIN_ITEM_VARIABLE) + hid_input_var_field(hid, field, interrupt); + else + hid_input_array_field(hid, field, interrupt); + } + } + + + if (fastmouse_dev) { + fastmouse_input_emit(fastmouse_dev); + } + + +} + +/* + * Insert a given usage_index in a field in the list + * of processed usages in the report. + * + * The elements of lower priority score are processed + * first. + */ +static void __hid_insert_field_entry(struct hid_device *hid, + struct hid_report *report, + struct hid_field_entry *entry, + struct hid_field *field, + unsigned int usage_index) +{ + struct hid_field_entry *next; + + entry->field = field; + entry->index = usage_index; + entry->priority = field->usages_priorities[usage_index]; + + /* insert the element at the correct position */ + list_for_each_entry(next, + &report->field_entry_list, + list) { + /* + * the priority of our element is strictly higher + * than the next one, insert it before + */ + if (entry->priority > next->priority) { + list_add_tail(&entry->list, &next->list); + return; + } + } + + /* lowest priority score: insert at the end */ + list_add_tail(&entry->list, &report->field_entry_list); +} + +static void hid_report_process_ordering(struct hid_device *hid, + struct hid_report *report) +{ + struct hid_field *field; + struct hid_field_entry *entries; + unsigned int a, u, usages; + unsigned int count = 0; + + /* count the number of individual fields in the report */ + for (a = 0; a < report->maxfield; a++) { + field = report->field[a]; + + if (field->flags & HID_MAIN_ITEM_VARIABLE) + count += field->report_count; + else + count++; + } + + /* allocate the memory to process the fields */ + entries = kcalloc(count, sizeof(*entries), GFP_KERNEL); + if (!entries) + return; + + report->field_entries = entries; + + /* + * walk through all fields in the report and + * store them by priority order in report->field_entry_list + * + * - Var elements are individualized (field + usage_index) + * - Arrays are taken as one, we can not chose an order for them + */ + usages = 0; + for (a = 0; a < report->maxfield; a++) { + field = report->field[a]; + + if (field->flags & HID_MAIN_ITEM_VARIABLE) { + for (u = 0; u < field->report_count; u++) { + __hid_insert_field_entry(hid, report, + &entries[usages], + field, u); + usages++; + } + } else { + __hid_insert_field_entry(hid, report, &entries[usages], + field, 0); + usages++; + } + } +} + +static void hid_process_ordering(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_report_enum *report_enum = &hid->report_enum[HID_INPUT_REPORT]; + + list_for_each_entry(report, &report_enum->report_list, list) + hid_report_process_ordering(hid, report); +} + +/* + * Output the field into the report. + */ + +static void hid_output_field(const struct hid_device *hid, + struct hid_field *field, __u8 *data) +{ + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + unsigned n; + + for (n = 0; n < count; n++) { + if (field->logical_minimum < 0) /* signed values */ + implement(hid, data, offset + n * size, size, + s32ton(field->value[n], size)); + else /* unsigned values */ + implement(hid, data, offset + n * size, size, + field->value[n]); + } +} + +/* + * Compute the size of a report. + */ +static size_t hid_compute_report_size(struct hid_report *report) +{ + if (report->size) + return ((report->size - 1) >> 3) + 1; + + return 0; +} + +/* + * Create a report. 'data' has to be allocated using + * hid_alloc_report_buf() so that it has proper size. + */ + +void hid_output_report(struct hid_report *report, __u8 *data) +{ + unsigned n; + + if (report->id > 0) + *data++ = report->id; + + memset(data, 0, hid_compute_report_size(report)); + for (n = 0; n < report->maxfield; n++) + hid_output_field(report->device, report->field[n], data); +} +EXPORT_SYMBOL_GPL(hid_output_report); + +/* + * Allocator for buffer that is going to be passed to hid_output_report() + */ +u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) +{ + /* + * 7 extra bytes are necessary to achieve proper functionality + * of implement() working on 8 byte chunks + */ + + u32 len = hid_report_len(report) + 7; + + return kzalloc(len, flags); +} +EXPORT_SYMBOL_GPL(hid_alloc_report_buf); + +/* + * Set a field value. The report this field belongs to has to be + * created and transferred to the device, to set this value in the + * device. + */ + +int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) +{ + unsigned size; + + if (!field) + return -1; + + size = field->report_size; + + hid_dump_input(field->report->device, field->usage + offset, value); + + if (offset >= field->report_count) { + hid_err(field->report->device, "offset (%d) exceeds report_count (%d)\n", + offset, field->report_count); + return -1; + } + if (field->logical_minimum < 0) { + if (value != snto32(s32ton(value, size), size)) { + hid_err(field->report->device, "value %d is out of range\n", value); + return -1; + } + } + field->value[offset] = value; + return 0; +} +EXPORT_SYMBOL_GPL(hid_set_field); + +struct hid_field *hid_find_field(struct hid_device *hdev, unsigned int report_type, + unsigned int application, unsigned int usage) +{ + struct list_head *report_list = &hdev->report_enum[report_type].report_list; + struct hid_report *report; + int i, j; + + list_for_each_entry(report, report_list, list) { + if (report->application != application) + continue; + + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == usage) + return field; + } + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(hid_find_field); + +static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, + const u8 *data) +{ + struct hid_report *report; + unsigned int n = 0; /* Normally report number is 0 */ + + /* Device uses numbered reports, data[0] is report number */ + if (report_enum->numbered) + n = *data; + + report = report_enum->report_id_hash[n]; + if (report == NULL) + dbg_hid("undefined report_id %u received\n", n); + + return report; +} + +/* + * Implement a generic .request() callback, using .raw_request() + * DO NOT USE in hid drivers directly, but through hid_hw_request instead. + */ +int __hid_request(struct hid_device *hid, struct hid_report *report, + enum hid_class_request reqtype) +{ + char *buf; + int ret; + u32 len; + + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len = hid_report_len(report); + + if (reqtype == HID_REQ_SET_REPORT) + hid_output_report(report, buf); + + ret = hid->ll_driver->raw_request(hid, report->id, buf, len, + report->type, reqtype); + if (ret < 0) { + dbg_hid("unable to complete request: %d\n", ret); + goto out; + } + + if (reqtype == HID_REQ_GET_REPORT) + hid_input_report(hid, report->type, buf, ret, 0); + + ret = 0; + +out: + kfree(buf); + return ret; +} +EXPORT_SYMBOL_GPL(__hid_request); + +int hid_report_raw_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +{ + struct hid_report_enum *report_enum = hid->report_enum + type; + struct hid_report *report; + struct hid_driver *hdrv; + int max_buffer_size = HID_MAX_BUFFER_SIZE; + u32 rsize, csize = size; + u8 *cdata = data; + int ret = 0; + + report = hid_get_report(report_enum, data); + if (!report) + goto out; + + if (report_enum->numbered) { + cdata++; + csize--; + } + + rsize = hid_compute_report_size(report); + + if (hid->ll_driver->max_buffer_size) + max_buffer_size = hid->ll_driver->max_buffer_size; + + if (report_enum->numbered && rsize >= max_buffer_size) + rsize = max_buffer_size - 1; + else if (rsize > max_buffer_size) + rsize = max_buffer_size; + + if (csize < rsize) { + dbg_hid("report %d is too short, (%d < %d)\n", report->id, + csize, rsize); + memset(cdata + csize, 0, rsize - csize); + } + + if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) + hid->hiddev_report_event(hid, report); + if (hid->claimed & HID_CLAIMED_HIDRAW) { + ret = hidraw_report_event(hid, data, size); + if (ret) + goto out; + } + + if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) { + hid_process_report(hid, report, cdata, interrupt); + hdrv = hid->driver; + if (hdrv && hdrv->report) + hdrv->report(hid, report); + } + + if (hid->claimed & HID_CLAIMED_INPUT) + hidinput_report_event(hid, report); +out: + return ret; +} +EXPORT_SYMBOL_GPL(hid_report_raw_event); + + +static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, + bool lock_already_taken) +{ + struct hid_report_enum *report_enum; + struct hid_driver *hdrv; + struct hid_report *report; + int ret = 0; + + if (!hid) + return -ENODEV; + + ret = down_trylock(&hid->driver_input_lock); + if (lock_already_taken && !ret) { + up(&hid->driver_input_lock); + return -EINVAL; + } else if (!lock_already_taken && ret) { + return -EBUSY; + } + + if (!hid->driver) { + ret = -ENODEV; + goto unlock; + } + report_enum = hid->report_enum + type; + hdrv = hid->driver; + + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + goto unlock; + } + + if (!size) { + dbg_hid("empty report\n"); + ret = -1; + goto unlock; + } + + /* Avoid unnecessary overhead if debugfs is disabled */ + if (!list_empty(&hid->debug_list)) + hid_dump_report(hid, type, data, size); + + report = hid_get_report(report_enum, data); + + if (!report) { + ret = -1; + goto unlock; + } + + if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) { + ret = hdrv->raw_event(hid, report, data, size); + if (ret < 0) + goto unlock; + } + + ret = hid_report_raw_event(hid, type, data, size, interrupt); + +unlock: + if (!lock_already_taken) + up(&hid->driver_input_lock); + return ret; +} + +/** + * hid_input_report - report data from lower layer (usb, bt...) + * + * @hid: hid device + * @type: HID report type (HID_*_REPORT) + * @data: report contents + * @size: size of data parameter + * @interrupt: distinguish between interrupt and control transfers + * + * This is data entry for lower layers. + */ +int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size, + int interrupt) +{ + return __hid_input_report(hid, type, data, size, interrupt, 0, + false, /* from_bpf */ + false /* lock_already_taken */); +} +EXPORT_SYMBOL_GPL(hid_input_report); + +bool hid_match_one_id(const struct hid_device *hdev, + const struct hid_device_id *id) +{ + return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) && + (id->group == HID_GROUP_ANY || id->group == hdev->group) && + (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) && + (id->product == HID_ANY_ID || id->product == hdev->product); +} + +const struct hid_device_id *hid_match_id(const struct hid_device *hdev, + const struct hid_device_id *id) +{ + for (; id->bus; id++) + if (hid_match_one_id(hdev, id)) + return id; + + return NULL; +} +EXPORT_SYMBOL_GPL(hid_match_id); + +static const struct hid_device_id hid_hiddev_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1) }, + { } +}; + +static bool hid_hiddev(struct hid_device *hdev) +{ + return !!hid_match_id(hdev, hid_hiddev_list); +} + + +static ssize_t +report_descriptor_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct hid_device *hdev = to_hid_device(dev); + + if (off >= hdev->rsize) + return 0; + + if (off + count > hdev->rsize) + count = hdev->rsize - off; + + memcpy(buf, hdev->rdesc + off, count); + + return count; +} + +static ssize_t +country_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + + return sprintf(buf, "%02x\n", hdev->country & 0xff); +} + +static const BIN_ATTR_RO(report_descriptor, HID_MAX_DESCRIPTOR_SIZE); + +static const DEVICE_ATTR_RO(country); + +int hid_connect(struct hid_device *hdev, unsigned int connect_mask) +{ + static const char *types[] = { "Device", "Pointer", "Mouse", "Device", + "Joystick", "Gamepad", "Keyboard", "Keypad", + "Multi-Axis Controller" + }; + const char *type, *bus; + char buf[64] = ""; + unsigned int i; + int len; + int ret; + + ret = hid_bpf_connect_device(hdev); + if (ret) + return ret; + + if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) + connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); + if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) + connect_mask |= HID_CONNECT_HIDINPUT_FORCE; + if (hdev->bus != BUS_USB) + connect_mask &= ~HID_CONNECT_HIDDEV; + if (hid_hiddev(hdev)) + connect_mask |= HID_CONNECT_HIDDEV_FORCE; + + if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, + connect_mask & HID_CONNECT_HIDINPUT_FORCE)) + hdev->claimed |= HID_CLAIMED_INPUT; + + if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && + !hdev->hiddev_connect(hdev, + connect_mask & HID_CONNECT_HIDDEV_FORCE)) + hdev->claimed |= HID_CLAIMED_HIDDEV; + if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) + hdev->claimed |= HID_CLAIMED_HIDRAW; + + if (connect_mask & HID_CONNECT_DRIVER) + hdev->claimed |= HID_CLAIMED_DRIVER; + + /* Drivers with the ->raw_event callback set are not required to connect + * to any other listener. */ + if (!hdev->claimed && !hdev->driver->raw_event) { + hid_err(hdev, "device has no listeners, quitting\n"); + return -ENODEV; + } + + hid_process_ordering(hdev); + + if ((hdev->claimed & HID_CLAIMED_INPUT) && + (connect_mask & HID_CONNECT_FF) && hdev->ff_init) + hdev->ff_init(hdev); + + len = 0; + if (hdev->claimed & HID_CLAIMED_INPUT) + len += sprintf(buf + len, "input"); + if (hdev->claimed & HID_CLAIMED_HIDDEV) + len += sprintf(buf + len, "%shiddev%d", len ? "," : "", + ((struct hiddev *)hdev->hiddev)->minor); + if (hdev->claimed & HID_CLAIMED_HIDRAW) + len += sprintf(buf + len, "%shidraw%d", len ? "," : "", + ((struct hidraw *)hdev->hidraw)->minor); + + type = "Device"; + for (i = 0; i < hdev->maxcollection; i++) { + struct hid_collection *col = &hdev->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION && + (col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK && + (col->usage & 0xffff) < ARRAY_SIZE(types)) { + type = types[col->usage & 0xffff]; + break; + } + } + + switch (hdev->bus) { + case BUS_USB: + bus = "USB"; + break; + case BUS_BLUETOOTH: + bus = "BLUETOOTH"; + break; + case BUS_I2C: + bus = "I2C"; + break; + case BUS_VIRTUAL: + bus = "VIRTUAL"; + break; + case BUS_INTEL_ISHTP: + case BUS_AMD_SFH: + bus = "SENSOR HUB"; + break; + default: + bus = ""; + } + + ret = device_create_file(&hdev->dev, &dev_attr_country); + if (ret) + hid_warn(hdev, + "can't create sysfs country code attribute err: %d\n", ret); + + hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n", + buf, bus, hdev->version >> 8, hdev->version & 0xff, + type, hdev->name, hdev->phys); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_connect); + +void hid_disconnect(struct hid_device *hdev) +{ + device_remove_file(&hdev->dev, &dev_attr_country); + if (hdev->claimed & HID_CLAIMED_INPUT) + hidinput_disconnect(hdev); + if (hdev->claimed & HID_CLAIMED_HIDDEV) + hdev->hiddev_disconnect(hdev); + if (hdev->claimed & HID_CLAIMED_HIDRAW) + hidraw_disconnect(hdev); + hdev->claimed = 0; + + hid_bpf_disconnect_device(hdev); +} +EXPORT_SYMBOL_GPL(hid_disconnect); + +/** + * hid_hw_start - start underlying HW + * @hdev: hid device + * @connect_mask: which outputs to connect, see HID_CONNECT_* + * + * Call this in probe function *after* hid_parse. This will setup HW + * buffers and start the device (if not defeirred to device open). + * hid_hw_stop must be called if this was successful. + */ +int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask) +{ + int error; + + error = hdev->ll_driver->start(hdev); + if (error) + return error; + + if (connect_mask) { + error = hid_connect(hdev, connect_mask); + if (error) { + hdev->ll_driver->stop(hdev); + return error; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(hid_hw_start); + +/** + * hid_hw_stop - stop underlying HW + * @hdev: hid device + * + * This is usually called from remove function or from probe when something + * failed and hid_hw_start was called already. + */ +void hid_hw_stop(struct hid_device *hdev) +{ + hid_disconnect(hdev); + hdev->ll_driver->stop(hdev); +} +EXPORT_SYMBOL_GPL(hid_hw_stop); + +/** + * hid_hw_open - signal underlying HW to start delivering events + * @hdev: hid device + * + * Tell underlying HW to start delivering events from the device. + * This function should be called sometime after successful call + * to hid_hw_start(). + */ +int hid_hw_open(struct hid_device *hdev) +{ + int ret; + + ret = mutex_lock_killable(&hdev->ll_open_lock); + if (ret) + return ret; + + if (!hdev->ll_open_count++) { + ret = hdev->ll_driver->open(hdev); + if (ret) + hdev->ll_open_count--; + } + + mutex_unlock(&hdev->ll_open_lock); + return ret; +} +EXPORT_SYMBOL_GPL(hid_hw_open); + +/** + * hid_hw_close - signal underlaying HW to stop delivering events + * + * @hdev: hid device + * + * This function indicates that we are not interested in the events + * from this device anymore. Delivery of events may or may not stop, + * depending on the number of users still outstanding. + */ +void hid_hw_close(struct hid_device *hdev) +{ + mutex_lock(&hdev->ll_open_lock); + if (!--hdev->ll_open_count) + hdev->ll_driver->close(hdev); + mutex_unlock(&hdev->ll_open_lock); +} +EXPORT_SYMBOL_GPL(hid_hw_close); + +/** + * hid_hw_request - send report request to device + * + * @hdev: hid device + * @report: report to send + * @reqtype: hid request type + */ +void hid_hw_request(struct hid_device *hdev, + struct hid_report *report, enum hid_class_request reqtype) +{ + if (hdev->ll_driver->request) + return hdev->ll_driver->request(hdev, report, reqtype); + + __hid_request(hdev, report, reqtype); +} +EXPORT_SYMBOL_GPL(hid_hw_request); + +int __hid_hw_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, enum hid_report_type rtype, + enum hid_class_request reqtype, + u64 source, bool from_bpf) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) + return -EINVAL; + + ret = dispatch_hid_bpf_raw_requests(hdev, reportnum, buf, len, rtype, + reqtype, source, from_bpf); + if (ret) + return ret; + + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, + rtype, reqtype); +} + +/** + * hid_hw_raw_request - send report request to device + * + * @hdev: hid device + * @reportnum: report ID + * @buf: in/out data to transfer + * @len: length of buf + * @rtype: HID report type + * @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT + * + * Return: count of data transferred, negative if error + * + * Same behavior as hid_hw_request, but with raw buffers instead. + */ +int hid_hw_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, enum hid_report_type rtype, enum hid_class_request reqtype) +{ + return __hid_hw_raw_request(hdev, reportnum, buf, len, rtype, reqtype, 0, false); +} +EXPORT_SYMBOL_GPL(hid_hw_raw_request); + +int __hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len, u64 source, + bool from_bpf) +{ + unsigned int max_buffer_size = HID_MAX_BUFFER_SIZE; + int ret; + + if (hdev->ll_driver->max_buffer_size) + max_buffer_size = hdev->ll_driver->max_buffer_size; + + if (len < 1 || len > max_buffer_size || !buf) + return -EINVAL; + + ret = dispatch_hid_bpf_output_report(hdev, buf, len, source, from_bpf); + if (ret) + return ret; + + if (hdev->ll_driver->output_report) + return hdev->ll_driver->output_report(hdev, buf, len); + + return -ENOSYS; +} + +/** + * hid_hw_output_report - send output report to device + * + * @hdev: hid device + * @buf: raw data to transfer + * @len: length of buf + * + * Return: count of data transferred, negative if error + */ +int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) +{ + return __hid_hw_output_report(hdev, buf, len, 0, false); +} +EXPORT_SYMBOL_GPL(hid_hw_output_report); + +#ifdef CONFIG_PM +int hid_driver_suspend(struct hid_device *hdev, pm_message_t state) +{ + if (hdev->driver && hdev->driver->suspend) + return hdev->driver->suspend(hdev, state); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_driver_suspend); + +int hid_driver_reset_resume(struct hid_device *hdev) +{ + if (hdev->driver && hdev->driver->reset_resume) + return hdev->driver->reset_resume(hdev); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_driver_reset_resume); + +int hid_driver_resume(struct hid_device *hdev) +{ + if (hdev->driver && hdev->driver->resume) + return hdev->driver->resume(hdev); + + return 0; +} +EXPORT_SYMBOL_GPL(hid_driver_resume); +#endif /* CONFIG_PM */ + +struct hid_dynid { + struct list_head list; + struct hid_device_id id; +}; + +/** + * new_id_store - add a new HID device ID to this driver and re-probe devices + * @drv: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Adds a new dynamic hid device ID to this driver, + * and causes the driver to probe for all devices again. + */ +static ssize_t new_id_store(struct device_driver *drv, const char *buf, + size_t count) +{ + struct hid_driver *hdrv = to_hid_driver(drv); + struct hid_dynid *dynid; + __u32 bus, vendor, product; + unsigned long driver_data = 0; + int ret; + + ret = sscanf(buf, "%x %x %x %lx", + &bus, &vendor, &product, &driver_data); + if (ret < 3) + return -EINVAL; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.bus = bus; + dynid->id.group = HID_GROUP_ANY; + dynid->id.vendor = vendor; + dynid->id.product = product; + dynid->id.driver_data = driver_data; + + spin_lock(&hdrv->dyn_lock); + list_add_tail(&dynid->list, &hdrv->dyn_list); + spin_unlock(&hdrv->dyn_lock); + + ret = driver_attach(&hdrv->driver); + + return ret ? : count; +} +static DRIVER_ATTR_WO(new_id); + +static struct attribute *hid_drv_attrs[] = { + &driver_attr_new_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hid_drv); + +static void hid_free_dynids(struct hid_driver *hdrv) +{ + struct hid_dynid *dynid, *n; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) { + list_del(&dynid->list); + kfree(dynid); + } + spin_unlock(&hdrv->dyn_lock); +} + +const struct hid_device_id *hid_match_device(struct hid_device *hdev, + struct hid_driver *hdrv) +{ + struct hid_dynid *dynid; + + spin_lock(&hdrv->dyn_lock); + list_for_each_entry(dynid, &hdrv->dyn_list, list) { + if (hid_match_one_id(hdev, &dynid->id)) { + spin_unlock(&hdrv->dyn_lock); + return &dynid->id; + } + } + spin_unlock(&hdrv->dyn_lock); + + return hid_match_id(hdev, hdrv->id_table); +} +EXPORT_SYMBOL_GPL(hid_match_device); + +static int hid_bus_match(struct device *dev, const struct device_driver *drv) +{ + struct hid_driver *hdrv = to_hid_driver(drv); + struct hid_device *hdev = to_hid_device(dev); + + return hid_match_device(hdev, hdrv) != NULL; +} + +/** + * hid_compare_device_paths - check if both devices share the same path + * @hdev_a: hid device + * @hdev_b: hid device + * @separator: char to use as separator + * + * Check if two devices share the same path up to the last occurrence of + * the separator char. Both paths must exist (i.e., zero-length paths + * don't match). + */ +bool hid_compare_device_paths(struct hid_device *hdev_a, + struct hid_device *hdev_b, char separator) +{ + int n1 = strrchr(hdev_a->phys, separator) - hdev_a->phys; + int n2 = strrchr(hdev_b->phys, separator) - hdev_b->phys; + + if (n1 != n2 || n1 <= 0 || n2 <= 0) + return false; + + return !strncmp(hdev_a->phys, hdev_b->phys, n1); +} +EXPORT_SYMBOL_GPL(hid_compare_device_paths); + +static bool hid_check_device_match(struct hid_device *hdev, + struct hid_driver *hdrv, + const struct hid_device_id **id) +{ + *id = hid_match_device(hdev, hdrv); + if (!*id) + return false; + + if (hdrv->match) + return hdrv->match(hdev, hid_ignore_special_drivers); + + /* + * hid-generic implements .match(), so we must be dealing with a + * different HID driver here, and can simply check if + * hid_ignore_special_drivers or HID_QUIRK_IGNORE_SPECIAL_DRIVER + * are set or not. + */ + return !hid_ignore_special_drivers && !(hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIVER); +} + +static int __hid_device_probe(struct hid_device *hdev, struct hid_driver *hdrv) +{ + const struct hid_device_id *id; + int ret; + + if (!hdev->bpf_rsize) { + /* in case a bpf program gets detached, we need to free the old one */ + hid_free_bpf_rdesc(hdev); + + /* keep this around so we know we called it once */ + hdev->bpf_rsize = hdev->dev_rsize; + + /* call_hid_bpf_rdesc_fixup will always return a valid pointer */ + hdev->bpf_rdesc = call_hid_bpf_rdesc_fixup(hdev, hdev->dev_rdesc, + &hdev->bpf_rsize); + } + + if (!hid_check_device_match(hdev, hdrv, &id)) + return -ENODEV; + + hdev->devres_group_id = devres_open_group(&hdev->dev, NULL, GFP_KERNEL); + if (!hdev->devres_group_id) + return -ENOMEM; + + /* reset the quirks that has been previously set */ + hdev->quirks = hid_lookup_quirk(hdev); + hdev->driver = hdrv; + + if (hdrv->probe) { + ret = hdrv->probe(hdev, id); + } else { /* default probe */ + ret = hid_open_report(hdev); + if (!ret) + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + } + + /* + * Note that we are not closing the devres group opened above so + * even resources that were attached to the device after probe is + * run are released when hid_device_remove() is executed. This is + * needed as some drivers would allocate additional resources, + * for example when updating firmware. + */ + + if (ret) { + devres_release_group(&hdev->dev, hdev->devres_group_id); + hid_close_report(hdev); + hdev->driver = NULL; + } + + return ret; +} + +static int hid_device_probe(struct device *dev) +{ + struct hid_device *hdev = to_hid_device(dev); + struct hid_driver *hdrv = to_hid_driver(dev->driver); + int ret = 0; + + if (down_interruptible(&hdev->driver_input_lock)) + return -EINTR; + + hdev->io_started = false; + clear_bit(ffs(HID_STAT_REPROBED), &hdev->status); + + if (!hdev->driver) + ret = __hid_device_probe(hdev, hdrv); + + if (!hdev->io_started) + up(&hdev->driver_input_lock); + + return ret; +} + +static void hid_device_remove(struct device *dev) +{ + struct hid_device *hdev = to_hid_device(dev); + struct hid_driver *hdrv; + + down(&hdev->driver_input_lock); + hdev->io_started = false; + + hdrv = hdev->driver; + if (hdrv) { + if (hdrv->remove) + hdrv->remove(hdev); + else /* default remove */ + hid_hw_stop(hdev); + + /* Release all devres resources allocated by the driver */ + devres_release_group(&hdev->dev, hdev->devres_group_id); + + hid_close_report(hdev); + hdev->driver = NULL; + } + + if (!hdev->io_started) + up(&hdev->driver_input_lock); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + + return scnprintf(buf, PAGE_SIZE, "hid:b%04Xg%04Xv%08Xp%08X\n", + hdev->bus, hdev->group, hdev->vendor, hdev->product); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *hid_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL, +}; +static const struct bin_attribute *hid_dev_bin_attrs[] = { + &bin_attr_report_descriptor, + NULL +}; +static const struct attribute_group hid_dev_group = { + .attrs = hid_dev_attrs, + .bin_attrs_new = hid_dev_bin_attrs, +}; +__ATTRIBUTE_GROUPS(hid_dev); + +static int hid_uevent(const struct device *dev, struct kobj_uevent_env *env) +{ + const struct hid_device *hdev = to_hid_device(dev); + + if (add_uevent_var(env, "HID_ID=%04X:%08X:%08X", + hdev->bus, hdev->vendor, hdev->product)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_NAME=%s", hdev->name)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_PHYS=%s", hdev->phys)) + return -ENOMEM; + + if (add_uevent_var(env, "HID_UNIQ=%s", hdev->uniq)) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=hid:b%04Xg%04Xv%08Xp%08X", + hdev->bus, hdev->group, hdev->vendor, hdev->product)) + return -ENOMEM; + + return 0; +} + +const struct bus_type hid_bus_type = { + .name = "hid", + .dev_groups = hid_dev_groups, + .drv_groups = hid_drv_groups, + .match = hid_bus_match, + .probe = hid_device_probe, + .remove = hid_device_remove, + .uevent = hid_uevent, +}; +EXPORT_SYMBOL(hid_bus_type); + +int hid_add_device(struct hid_device *hdev) +{ + static atomic_t id = ATOMIC_INIT(0); + int ret; + + if (WARN_ON(hdev->status & HID_STAT_ADDED)) + return -EBUSY; + + hdev->quirks = hid_lookup_quirk(hdev); + + /* we need to kill them here, otherwise they will stay allocated to + * wait for coming driver */ + if (hid_ignore(hdev)) + return -ENODEV; + + /* + * Check for the mandatory transport channel. + */ + if (!hdev->ll_driver->raw_request) { + hid_err(hdev, "transport driver missing .raw_request()\n"); + return -EINVAL; + } + + /* + * Read the device report descriptor once and use as template + * for the driver-specific modifications. + */ + ret = hdev->ll_driver->parse(hdev); + if (ret) + return ret; + if (!hdev->dev_rdesc) + return -ENODEV; + + /* + * Scan generic devices for group information + */ + if (hid_ignore_special_drivers) { + hdev->group = HID_GROUP_GENERIC; + } else if (!hdev->group && + !(hdev->quirks & HID_QUIRK_HAVE_SPECIAL_DRIVER)) { + ret = hid_scan_report(hdev); + if (ret) + hid_warn(hdev, "bad device descriptor (%d)\n", ret); + } + + hdev->id = atomic_inc_return(&id); + + /* XXX hack, any other cleaner solution after the driver core + * is converted to allow more than 20 bytes as the device name? */ + dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus, + hdev->vendor, hdev->product, hdev->id); + + hid_debug_register(hdev, dev_name(&hdev->dev)); + ret = device_add(&hdev->dev); + if (!ret) + hdev->status |= HID_STAT_ADDED; + else + hid_debug_unregister(hdev); + + return ret; +} +EXPORT_SYMBOL_GPL(hid_add_device); + +/** + * hid_allocate_device - allocate new hid device descriptor + * + * Allocate and initialize hid device, so that hid_destroy_device might be + * used to free it. + * + * New hid_device pointer is returned on success, otherwise ERR_PTR encoded + * error value. + */ +struct hid_device *hid_allocate_device(void) +{ + struct hid_device *hdev; + int ret = -ENOMEM; + + hdev = kzalloc(sizeof(*hdev), GFP_KERNEL); + if (hdev == NULL) + return ERR_PTR(ret); + + device_initialize(&hdev->dev); + hdev->dev.release = hid_device_release; + hdev->dev.bus = &hid_bus_type; + device_enable_async_suspend(&hdev->dev); + + hid_close_report(hdev); + + init_waitqueue_head(&hdev->debug_wait); + INIT_LIST_HEAD(&hdev->debug_list); + spin_lock_init(&hdev->debug_list_lock); + sema_init(&hdev->driver_input_lock, 1); + mutex_init(&hdev->ll_open_lock); + kref_init(&hdev->ref); + + ret = hid_bpf_device_init(hdev); + if (ret) + goto out_err; + + return hdev; + +out_err: + hid_destroy_device(hdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(hid_allocate_device); + +static void hid_remove_device(struct hid_device *hdev) +{ + if (hdev->status & HID_STAT_ADDED) { + device_del(&hdev->dev); + hid_debug_unregister(hdev); + hdev->status &= ~HID_STAT_ADDED; + } + hid_free_bpf_rdesc(hdev); + kfree(hdev->dev_rdesc); + hdev->dev_rdesc = NULL; + hdev->dev_rsize = 0; + hdev->bpf_rsize = 0; +} + +/** + * hid_destroy_device - free previously allocated device + * + * @hdev: hid device + * + * If you allocate hid_device through hid_allocate_device, you should ever + * free by this function. + */ +void hid_destroy_device(struct hid_device *hdev) +{ + hid_bpf_destroy_device(hdev); + hid_remove_device(hdev); + put_device(&hdev->dev); +} +EXPORT_SYMBOL_GPL(hid_destroy_device); + + +static int __hid_bus_reprobe_drivers(struct device *dev, void *data) +{ + struct hid_driver *hdrv = data; + struct hid_device *hdev = to_hid_device(dev); + + if (hdev->driver == hdrv && + !hdrv->match(hdev, hid_ignore_special_drivers) && + !test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + return device_reprobe(dev); + + return 0; +} + +static int __hid_bus_driver_added(struct device_driver *drv, void *data) +{ + struct hid_driver *hdrv = to_hid_driver(drv); + + if (hdrv->match) { + bus_for_each_dev(&hid_bus_type, NULL, hdrv, + __hid_bus_reprobe_drivers); + } + + return 0; +} + +static int __bus_removed_driver(struct device_driver *drv, void *data) +{ + return bus_rescan_devices(&hid_bus_type); +} + +int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, + const char *mod_name) +{ + int ret; + + hdrv->driver.name = hdrv->name; + hdrv->driver.bus = &hid_bus_type; + hdrv->driver.owner = owner; + hdrv->driver.mod_name = mod_name; + + INIT_LIST_HEAD(&hdrv->dyn_list); + spin_lock_init(&hdrv->dyn_lock); + + ret = driver_register(&hdrv->driver); + + if (ret == 0) + bus_for_each_drv(&hid_bus_type, NULL, NULL, + __hid_bus_driver_added); + + return ret; +} +EXPORT_SYMBOL_GPL(__hid_register_driver); + +void hid_unregister_driver(struct hid_driver *hdrv) +{ + driver_unregister(&hdrv->driver); + hid_free_dynids(hdrv); + + bus_for_each_drv(&hid_bus_type, NULL, hdrv, __bus_removed_driver); +} +EXPORT_SYMBOL_GPL(hid_unregister_driver); + +int hid_check_keys_pressed(struct hid_device *hid) +{ + struct hid_input *hidinput; + int i; + + if (!(hid->claimed & HID_CLAIMED_INPUT)) + return 0; + + list_for_each_entry(hidinput, &hid->inputs, list) { + for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++) + if (hidinput->input->key[i]) + return 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(hid_check_keys_pressed); + +#ifdef CONFIG_HID_BPF +static const struct hid_ops __hid_ops = { + .hid_get_report = hid_get_report, + .hid_hw_raw_request = __hid_hw_raw_request, + .hid_hw_output_report = __hid_hw_output_report, + .hid_input_report = __hid_input_report, + .owner = THIS_MODULE, + .bus_type = &hid_bus_type, +}; +#endif + +static int __init hid_init(void) +{ + int ret; + + ret = bus_register(&hid_bus_type); + if (ret) { + pr_err("can't register hid bus\n"); + goto err; + } + +#ifdef CONFIG_HID_BPF + hid_ops = &__hid_ops; +#endif + + ret = hidraw_init(); + if (ret) + goto err_bus; + + hid_debug_init(); + + return 0; +err_bus: + bus_unregister(&hid_bus_type); +err: + return ret; +} + +static void __exit hid_exit(void) +{ +#ifdef CONFIG_HID_BPF + hid_ops = NULL; +#endif + hid_debug_exit(); + hidraw_exit(); + bus_unregister(&hid_bus_type); + hid_quirks_exit(HID_BUS_ANY); +} + +module_init(hid_init); +module_exit(hid_exit); + +MODULE_AUTHOR("Andreas Gal"); +MODULE_AUTHOR("Vojtech Pavlik"); +MODULE_AUTHOR("Jiri Kosina"); +MODULE_DESCRIPTION("HID support for Linux"); +MODULE_LICENSE("GPL"); diff --git a/src/linux/hid-input.c b/src/linux/hid-input.c new file mode 100644 index 0000000..56e1bb7 --- /dev/null +++ b/src/linux/hid-input.c @@ -0,0 +1,2540 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2006-2010 Jiri Kosina + * + * HID to Linux Input mapping + */ + +/* + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include + +#include +#include + +#include "hid-ids.h" + +#define unk KEY_UNKNOWN + +static const unsigned char hid_keyboard[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190, + 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114,unk,unk,unk,121,unk, 89, 93,124, 92, 94, 95,unk,unk,unk, + 122,123, 90, 91, 85,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,179,180,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,111,unk,unk,unk,unk,unk,unk,unk, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk +}; + +static const struct { + __s32 x; + __s32 y; +} hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +struct usage_priority { + __u32 usage; /* the HID usage associated */ + bool global; /* we assume all usages to be slotted, + * unless global + */ + unsigned int slot_overwrite; /* for globals: allows to set the usage + * before or after the slots + */ +}; + +/* + * hid-input will convert this list into priorities: + * the first element will have the highest priority + * (the length of the following array) and the last + * element the lowest (1). + * + * hid-input will then shift the priority by 8 bits to leave some space + * in case drivers want to interleave other fields. + * + * To accommodate slotted devices, the slot priority is + * defined in the next 8 bits (defined by 0xff - slot). + * + * If drivers want to add fields before those, hid-input will + * leave out the first 8 bits of the priority value. + * + * This still leaves us 65535 individual priority values. + */ +static const struct usage_priority hidinput_usages_priorities[] = { + { /* Eraser (eraser touching) must always come before tipswitch */ + .usage = HID_DG_ERASER, + }, + { /* Invert must always come before In Range */ + .usage = HID_DG_INVERT, + }, + { /* Is the tip of the tool touching? */ + .usage = HID_DG_TIPSWITCH, + }, + { /* Tip Pressure might emulate tip switch */ + .usage = HID_DG_TIPPRESSURE, + }, + { /* In Range needs to come after the other tool states */ + .usage = HID_DG_INRANGE, + }, +}; + +#define map_abs(c) hid_map_usage(hidinput, usage, &bit, &max, EV_ABS, (c)) +#define map_rel(c) hid_map_usage(hidinput, usage, &bit, &max, EV_REL, (c)) +#define map_key(c) hid_map_usage(hidinput, usage, &bit, &max, EV_KEY, (c)) +#define map_led(c) hid_map_usage(hidinput, usage, &bit, &max, EV_LED, (c)) +#define map_msc(c) hid_map_usage(hidinput, usage, &bit, &max, EV_MSC, (c)) + +#define map_abs_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_ABS, (c)) +#define map_key_clear(c) hid_map_usage_clear(hidinput, usage, &bit, \ + &max, EV_KEY, (c)) + +static bool match_scancode(struct hid_usage *usage, + unsigned int cur_idx, unsigned int scancode) +{ + return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode; +} + +static bool match_keycode(struct hid_usage *usage, + unsigned int cur_idx, unsigned int keycode) +{ + /* + * We should exclude unmapped usages when doing lookup by keycode. + */ + return (usage->type == EV_KEY && usage->code == keycode); +} + +static bool match_index(struct hid_usage *usage, + unsigned int cur_idx, unsigned int idx) +{ + return cur_idx == idx; +} + +typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage, + unsigned int cur_idx, unsigned int val); + +static struct hid_usage *hidinput_find_key(struct hid_device *hid, + hid_usage_cmp_t match, + unsigned int value, + unsigned int *usage_idx) +{ + unsigned int i, j, k, cur_idx = 0; + struct hid_report *report; + struct hid_usage *usage; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + for (j = 0; j < report->field[i]->maxusage; j++) { + usage = report->field[i]->usage + j; + if (usage->type == EV_KEY || usage->type == 0) { + if (match(usage, cur_idx, value)) { + if (usage_idx) + *usage_idx = cur_idx; + return usage; + } + cur_idx++; + } + } + } + } + } + return NULL; +} + +static struct hid_usage *hidinput_locate_usage(struct hid_device *hid, + const struct input_keymap_entry *ke, + unsigned int *index) +{ + struct hid_usage *usage; + unsigned int scancode; + + if (ke->flags & INPUT_KEYMAP_BY_INDEX) + usage = hidinput_find_key(hid, match_index, ke->index, index); + else if (input_scancode_to_scalar(ke, &scancode) == 0) + usage = hidinput_find_key(hid, match_scancode, scancode, index); + else + usage = NULL; + + return usage; +} + +static int hidinput_getkeycode(struct input_dev *dev, + struct input_keymap_entry *ke) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_usage *usage; + unsigned int scancode, index; + + usage = hidinput_locate_usage(hid, ke, &index); + if (usage) { + ke->keycode = usage->type == EV_KEY ? + usage->code : KEY_RESERVED; + ke->index = index; + scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE); + ke->len = sizeof(scancode); + memcpy(ke->scancode, &scancode, sizeof(scancode)); + return 0; + } + + return -EINVAL; +} + +static int hidinput_setkeycode(struct input_dev *dev, + const struct input_keymap_entry *ke, + unsigned int *old_keycode) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_usage *usage; + + usage = hidinput_locate_usage(hid, ke, NULL); + if (usage) { + *old_keycode = usage->type == EV_KEY ? + usage->code : KEY_RESERVED; + usage->type = EV_KEY; + usage->code = ke->keycode; + + clear_bit(*old_keycode, dev->keybit); + set_bit(usage->code, dev->keybit); + dbg_hid("Assigned keycode %d to HID usage code %x\n", + usage->code, usage->hid); + + /* + * Set the keybit for the old keycode if the old keycode is used + * by another key + */ + if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL)) + set_bit(*old_keycode, dev->keybit); + + return 0; + } + + return -EINVAL; +} + + +/** + * hidinput_calc_abs_res - calculate an absolute axis resolution + * @field: the HID report field to calculate resolution for + * @code: axis code + * + * The formula is: + * (logical_maximum - logical_minimum) + * resolution = ---------------------------------------------------------- + * (physical_maximum - physical_minimum) * 10 ^ unit_exponent + * + * as seen in the HID specification v1.11 6.2.2.7 Global Items. + * + * Only exponent 1 length units are processed. Centimeters and inches are + * converted to millimeters. Degrees are converted to radians. + */ +__s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code) +{ + __s32 unit_exponent = field->unit_exponent; + __s32 logical_extents = field->logical_maximum - + field->logical_minimum; + __s32 physical_extents = field->physical_maximum - + field->physical_minimum; + __s32 prev; + + /* Check if the extents are sane */ + if (logical_extents <= 0 || physical_extents <= 0) + return 0; + + /* + * Verify and convert units. + * See HID specification v1.11 6.2.2.7 Global Items for unit decoding + */ + switch (code) { + case ABS_X: + case ABS_Y: + case ABS_Z: + case ABS_MT_POSITION_X: + case ABS_MT_POSITION_Y: + case ABS_MT_TOOL_X: + case ABS_MT_TOOL_Y: + case ABS_MT_TOUCH_MAJOR: + case ABS_MT_TOUCH_MINOR: + if (field->unit == 0x11) { /* If centimeters */ + /* Convert to millimeters */ + unit_exponent += 1; + } else if (field->unit == 0x13) { /* If inches */ + /* Convert to millimeters */ + prev = physical_extents; + physical_extents *= 254; + if (physical_extents < prev) + return 0; + unit_exponent -= 1; + } else { + return 0; + } + break; + + case ABS_RX: + case ABS_RY: + case ABS_RZ: + case ABS_WHEEL: + case ABS_TILT_X: + case ABS_TILT_Y: + if (field->unit == 0x14) { /* If degrees */ + /* Convert to radians */ + prev = logical_extents; + logical_extents *= 573; + if (logical_extents < prev) + return 0; + unit_exponent += 1; + } else if (field->unit != 0x12) { /* If not radians */ + return 0; + } + break; + + default: + return 0; + } + + /* Apply negative unit exponent */ + for (; unit_exponent < 0; unit_exponent++) { + prev = logical_extents; + logical_extents *= 10; + if (logical_extents < prev) + return 0; + } + /* Apply positive unit exponent */ + for (; unit_exponent > 0; unit_exponent--) { + prev = physical_extents; + physical_extents *= 10; + if (physical_extents < prev) + return 0; + } + + /* Calculate resolution */ + return DIV_ROUND_CLOSEST(logical_extents, physical_extents); +} +EXPORT_SYMBOL_GPL(hidinput_calc_abs_res); + +#ifdef CONFIG_HID_BATTERY_STRENGTH +static enum power_supply_property hidinput_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_SCOPE, +}; + +#define HID_BATTERY_QUIRK_PERCENT (1 << 0) /* always reports percent */ +#define HID_BATTERY_QUIRK_FEATURE (1 << 1) /* ask for feature report */ +#define HID_BATTERY_QUIRK_IGNORE (1 << 2) /* completely ignore the battery */ +#define HID_BATTERY_QUIRK_AVOID_QUERY (1 << 3) /* do not query the battery */ + +static const struct hid_device_id hid_battery_quirks[] = { + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD), + HID_BATTERY_QUIRK_IGNORE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, + USB_DEVICE_ID_ELECOM_BM084), + HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL, + USB_DEVICE_ID_SYMBOL_SCANNER_3), + HID_BATTERY_QUIRK_IGNORE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), + HID_BATTERY_QUIRK_IGNORE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, + USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD), + HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L), + HID_BATTERY_QUIRK_AVOID_QUERY }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_MW), + HID_BATTERY_QUIRK_AVOID_QUERY }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW), + HID_BATTERY_QUIRK_AVOID_QUERY }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM), + HID_BATTERY_QUIRK_AVOID_QUERY }, + /* + * Elan I2C-HID touchscreens seem to all report a non present battery, + * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices. + */ + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE }, + {} +}; + +static unsigned find_battery_quirk(struct hid_device *hdev) +{ + unsigned quirks = 0; + const struct hid_device_id *match; + + match = hid_match_id(hdev, hid_battery_quirks); + if (match != NULL) + quirks = match->driver_data; + + return quirks; +} + +static int hidinput_scale_battery_capacity(struct hid_device *dev, + int value) +{ + if (dev->battery_min < dev->battery_max && + value >= dev->battery_min && value <= dev->battery_max) + value = ((value - dev->battery_min) * 100) / + (dev->battery_max - dev->battery_min); + + return value; +} + +static int hidinput_query_battery_capacity(struct hid_device *dev) +{ + u8 *buf; + int ret; + + buf = kmalloc(4, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4, + dev->battery_report_type, HID_REQ_GET_REPORT); + if (ret < 2) { + kfree(buf); + return -ENODATA; + } + + ret = hidinput_scale_battery_capacity(dev, buf[1]); + kfree(buf); + return ret; +} + +static int hidinput_get_battery_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct hid_device *dev = power_supply_get_drvdata(psy); + int value; + int ret = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 1; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + if (dev->battery_status != HID_BATTERY_REPORTED && + !dev->battery_avoid_query) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + } else { + value = dev->battery_capacity; + } + + val->intval = value; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = dev->name; + break; + + case POWER_SUPPLY_PROP_STATUS: + if (dev->battery_status != HID_BATTERY_REPORTED && + !dev->battery_avoid_query) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + + dev->battery_capacity = value; + dev->battery_status = HID_BATTERY_QUERIED; + } + + if (dev->battery_status == HID_BATTERY_UNKNOWN) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else + val->intval = dev->battery_charge_status; + break; + + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field, bool is_percentage) +{ + struct power_supply_desc *psy_desc; + struct power_supply_config psy_cfg = { .drv_data = dev, }; + unsigned quirks; + s32 min, max; + int error; + + if (dev->battery) + return 0; /* already initialized? */ + + quirks = find_battery_quirk(dev); + + hid_dbg(dev, "device %x:%x:%x %d quirks %d\n", + dev->bus, dev->vendor, dev->product, dev->version, quirks); + + if (quirks & HID_BATTERY_QUIRK_IGNORE) + return 0; + + psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); + if (!psy_desc) + return -ENOMEM; + + psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", + strlen(dev->uniq) ? + dev->uniq : dev_name(&dev->dev)); + if (!psy_desc->name) { + error = -ENOMEM; + goto err_free_mem; + } + + psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; + psy_desc->properties = hidinput_battery_props; + psy_desc->num_properties = ARRAY_SIZE(hidinput_battery_props); + psy_desc->use_for_apm = 0; + psy_desc->get_property = hidinput_get_battery_property; + + min = field->logical_minimum; + max = field->logical_maximum; + + if (is_percentage || (quirks & HID_BATTERY_QUIRK_PERCENT)) { + min = 0; + max = 100; + } + + if (quirks & HID_BATTERY_QUIRK_FEATURE) + report_type = HID_FEATURE_REPORT; + + dev->battery_min = min; + dev->battery_max = max; + dev->battery_report_type = report_type; + dev->battery_report_id = field->report->id; + dev->battery_charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + + /* + * Stylus is normally not connected to the device and thus we + * can't query the device and get meaningful battery strength. + * We have to wait for the device to report it on its own. + */ + dev->battery_avoid_query = report_type == HID_INPUT_REPORT && + field->physical == HID_DG_STYLUS; + + if (quirks & HID_BATTERY_QUIRK_AVOID_QUERY) + dev->battery_avoid_query = true; + + dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); + if (IS_ERR(dev->battery)) { + error = PTR_ERR(dev->battery); + hid_warn(dev, "can't register power supply: %d\n", error); + goto err_free_name; + } + + power_supply_powers(dev->battery, &dev->dev); + return 0; + +err_free_name: + kfree(psy_desc->name); +err_free_mem: + kfree(psy_desc); + dev->battery = NULL; + return error; +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ + const struct power_supply_desc *psy_desc; + + if (!dev->battery) + return; + + psy_desc = dev->battery->desc; + power_supply_unregister(dev->battery); + kfree(psy_desc->name); + kfree(psy_desc); + dev->battery = NULL; +} + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ + int capacity; + + if (!dev->battery) + return; + + if (value == 0 || value < dev->battery_min || value > dev->battery_max) + return; + + capacity = hidinput_scale_battery_capacity(dev, value); + + if (dev->battery_status != HID_BATTERY_REPORTED || + capacity != dev->battery_capacity || + ktime_after(ktime_get_coarse(), dev->battery_ratelimit_time)) { + dev->battery_capacity = capacity; + dev->battery_status = HID_BATTERY_REPORTED; + dev->battery_ratelimit_time = + ktime_add_ms(ktime_get_coarse(), 30 * 1000); + power_supply_changed(dev->battery); + } +} + +static bool hidinput_set_battery_charge_status(struct hid_device *dev, + unsigned int usage, int value) +{ + switch (usage) { + case HID_BAT_CHARGING: + dev->battery_charge_status = value ? + POWER_SUPPLY_STATUS_CHARGING : + POWER_SUPPLY_STATUS_DISCHARGING; + return true; + } + + return false; +} +#else /* !CONFIG_HID_BATTERY_STRENGTH */ +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field, bool is_percentage) +{ + return 0; +} + +static void hidinput_cleanup_battery(struct hid_device *dev) +{ +} + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ +} + +static bool hidinput_set_battery_charge_status(struct hid_device *dev, + unsigned int usage, int value) +{ + return false; +} +#endif /* CONFIG_HID_BATTERY_STRENGTH */ + +static bool hidinput_field_in_collection(struct hid_device *device, struct hid_field *field, + unsigned int type, unsigned int usage) +{ + struct hid_collection *collection; + + collection = &device->collection[field->usage->collection_index]; + + return collection->type == type && collection->usage == usage; +} + +static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, + struct hid_usage *usage, unsigned int usage_index) +{ + struct input_dev *input = hidinput->input; + struct hid_device *device = input_get_drvdata(input); + const struct usage_priority *usage_priority = NULL; + int max = 0, code; + unsigned int i = 0; + unsigned long *bit = NULL; + + field->hidinput = hidinput; + + if (field->flags & HID_MAIN_ITEM_CONSTANT) + goto ignore; + + /* Ignore if report count is out of bounds. */ + if (field->report_count < 1) + goto ignore; + + /* only LED usages are supported in output fields */ + if (field->report_type == HID_OUTPUT_REPORT && + (usage->hid & HID_USAGE_PAGE) != HID_UP_LED) { + goto ignore; + } + + /* assign a priority based on the static list declared here */ + for (i = 0; i < ARRAY_SIZE(hidinput_usages_priorities); i++) { + if (usage->hid == hidinput_usages_priorities[i].usage) { + usage_priority = &hidinput_usages_priorities[i]; + + field->usages_priorities[usage_index] = + (ARRAY_SIZE(hidinput_usages_priorities) - i) << 8; + break; + } + } + + /* + * For slotted devices, we need to also add the slot index + * in the priority. + */ + if (usage_priority && usage_priority->global) + field->usages_priorities[usage_index] |= + usage_priority->slot_overwrite; + else + field->usages_priorities[usage_index] |= + (0xff - field->slot_idx) << 16; + + if (device->driver->input_mapping) { + int ret = device->driver->input_mapping(device, hidinput, field, + usage, &bit, &max); + if (ret > 0) + goto mapped; + if (ret < 0) + goto ignore; + } + + switch (usage->hid & HID_USAGE_PAGE) { + case HID_UP_UNDEFINED: + goto ignore; + + case HID_UP_KEYBOARD: + set_bit(EV_REP, input->evbit); + + if ((usage->hid & HID_USAGE) < 256) { + if (!hid_keyboard[usage->hid & HID_USAGE]) goto ignore; + map_key_clear(hid_keyboard[usage->hid & HID_USAGE]); + } else + map_key(KEY_UNKNOWN); + + break; + + case HID_UP_BUTTON: + code = ((usage->hid - 1) & HID_USAGE); + + switch (field->application) { + case HID_GD_MOUSE: + case HID_GD_POINTER: code += BTN_MOUSE; break; + case HID_GD_JOYSTICK: + if (code <= 0xf) + code += BTN_JOYSTICK; + else + code += BTN_TRIGGER_HAPPY - 0x10; + break; + case HID_GD_GAMEPAD: + if (code <= 0xf) + code += BTN_GAMEPAD; + else + code += BTN_TRIGGER_HAPPY - 0x10; + break; + case HID_CP_CONSUMER_CONTROL: + if (hidinput_field_in_collection(device, field, + HID_COLLECTION_NAMED_ARRAY, + HID_CP_PROGRAMMABLEBUTTONS)) { + if (code <= 0x1d) + code += KEY_MACRO1; + else + code += BTN_TRIGGER_HAPPY - 0x1e; + break; + } + fallthrough; + default: + switch (field->physical) { + case HID_GD_MOUSE: + case HID_GD_POINTER: code += BTN_MOUSE; break; + case HID_GD_JOYSTICK: code += BTN_JOYSTICK; break; + case HID_GD_GAMEPAD: code += BTN_GAMEPAD; break; + default: code += BTN_MISC; + } + } + + map_key(code); + break; + + case HID_UP_SIMULATION: + switch (usage->hid & 0xffff) { + case 0xba: map_abs(ABS_RUDDER); break; + case 0xbb: map_abs(ABS_THROTTLE); break; + case 0xc4: map_abs(ABS_GAS); break; + case 0xc5: map_abs(ABS_BRAKE); break; + case 0xc8: map_abs(ABS_WHEEL); break; + default: goto ignore; + } + break; + + case HID_UP_GENDESK: + if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x1: map_key_clear(KEY_POWER); break; + case 0x2: map_key_clear(KEY_SLEEP); break; + case 0x3: map_key_clear(KEY_WAKEUP); break; + case 0x4: map_key_clear(KEY_CONTEXT_MENU); break; + case 0x5: map_key_clear(KEY_MENU); break; + case 0x6: map_key_clear(KEY_PROG1); break; + case 0x7: map_key_clear(KEY_HELP); break; + case 0x8: map_key_clear(KEY_EXIT); break; + case 0x9: map_key_clear(KEY_SELECT); break; + case 0xa: map_key_clear(KEY_RIGHT); break; + case 0xb: map_key_clear(KEY_LEFT); break; + case 0xc: map_key_clear(KEY_UP); break; + case 0xd: map_key_clear(KEY_DOWN); break; + case 0xe: map_key_clear(KEY_POWER2); break; + case 0xf: map_key_clear(KEY_RESTART); break; + default: goto unknown; + } + break; + } + + if ((usage->hid & 0xf0) == 0x90) { /* SystemControl & D-pad */ + switch (usage->hid) { + case HID_GD_UP: usage->hat_dir = 1; break; + case HID_GD_DOWN: usage->hat_dir = 5; break; + case HID_GD_RIGHT: usage->hat_dir = 3; break; + case HID_GD_LEFT: usage->hat_dir = 7; break; + case HID_GD_DO_NOT_DISTURB: + map_key_clear(KEY_DO_NOT_DISTURB); break; + default: goto unknown; + } + + if (usage->hid <= HID_GD_LEFT) { + if (field->dpad) { + map_abs(field->dpad); + goto ignore; + } + map_abs(ABS_HAT0X); + } + break; + } + + if ((usage->hid & 0xf0) == 0xa0) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x9: map_key_clear(KEY_MICMUTE); break; + case 0xa: map_key_clear(KEY_ACCESSIBILITY); break; + default: goto ignore; + } + break; + } + + if ((usage->hid & 0xf0) == 0xb0) { /* SC - Display */ + switch (usage->hid & 0xf) { + case 0x05: map_key_clear(KEY_SWITCHVIDEOMODE); break; + default: goto ignore; + } + break; + } + + /* + * Some lazy vendors declare 255 usages for System Control, + * leading to the creation of ABS_X|Y axis and too many others. + * It wouldn't be a problem if joydev doesn't consider the + * device as a joystick then. + */ + if (field->application == HID_GD_SYSTEM_CONTROL) + goto ignore; + + switch (usage->hid) { + /* These usage IDs map directly to the usage codes. */ + case HID_GD_X: case HID_GD_Y: case HID_GD_Z: + case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: + if (field->flags & HID_MAIN_ITEM_RELATIVE) + map_rel(usage->hid & 0xf); + else + map_abs_clear(usage->hid & 0xf); + break; + + case HID_GD_WHEEL: + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + set_bit(REL_WHEEL, input->relbit); + map_rel(REL_WHEEL_HI_RES); + } else { + map_abs(usage->hid & 0xf); + } + break; + case HID_GD_SLIDER: case HID_GD_DIAL: + if (field->flags & HID_MAIN_ITEM_RELATIVE) + map_rel(usage->hid & 0xf); + else + map_abs(usage->hid & 0xf); + break; + + case HID_GD_HATSWITCH: + usage->hat_min = field->logical_minimum; + usage->hat_max = field->logical_maximum; + map_abs(ABS_HAT0X); + break; + + case HID_GD_START: map_key_clear(BTN_START); break; + case HID_GD_SELECT: map_key_clear(BTN_SELECT); break; + + case HID_GD_RFKILL_BTN: + /* MS wireless radio ctl extension, also check CA */ + if (field->application == HID_GD_WIRELESS_RADIO_CTLS) { + map_key_clear(KEY_RFKILL); + /* We need to simulate the btn release */ + field->flags |= HID_MAIN_ITEM_RELATIVE; + break; + } + goto unknown; + + default: goto unknown; + } + + break; + + case HID_UP_LED: + switch (usage->hid & 0xffff) { /* HID-Value: */ + case 0x01: map_led (LED_NUML); break; /* "Num Lock" */ + case 0x02: map_led (LED_CAPSL); break; /* "Caps Lock" */ + case 0x03: map_led (LED_SCROLLL); break; /* "Scroll Lock" */ + case 0x04: map_led (LED_COMPOSE); break; /* "Compose" */ + case 0x05: map_led (LED_KANA); break; /* "Kana" */ + case 0x27: map_led (LED_SLEEP); break; /* "Stand-By" */ + case 0x4c: map_led (LED_SUSPEND); break; /* "System Suspend" */ + case 0x09: map_led (LED_MUTE); break; /* "Mute" */ + case 0x4b: map_led (LED_MISC); break; /* "Generic Indicator" */ + case 0x19: map_led (LED_MAIL); break; /* "Message Waiting" */ + case 0x4d: map_led (LED_CHARGING); break; /* "External Power Connected" */ + + default: goto ignore; + } + break; + + case HID_UP_DIGITIZER: + if ((field->application & 0xff) == 0x01) /* Digitizer */ + __set_bit(INPUT_PROP_POINTER, input->propbit); + else if ((field->application & 0xff) == 0x02) /* Pen */ + __set_bit(INPUT_PROP_DIRECT, input->propbit); + + switch (usage->hid & 0xff) { + case 0x00: /* Undefined */ + goto ignore; + + case 0x30: /* TipPressure */ + if (!test_bit(BTN_TOUCH, input->keybit)) { + device->quirks |= HID_QUIRK_NOTOUCH; + set_bit(EV_KEY, input->evbit); + set_bit(BTN_TOUCH, input->keybit); + } + map_abs_clear(ABS_PRESSURE); + break; + + case 0x32: /* InRange */ + switch (field->physical) { + case HID_DG_PUCK: + map_key(BTN_TOOL_MOUSE); + break; + case HID_DG_FINGER: + map_key(BTN_TOOL_FINGER); + break; + default: + /* + * If the physical is not given, + * rely on the application. + */ + if (!field->physical) { + switch (field->application) { + case HID_DG_TOUCHSCREEN: + case HID_DG_TOUCHPAD: + map_key_clear(BTN_TOOL_FINGER); + break; + default: + map_key_clear(BTN_TOOL_PEN); + } + } else { + map_key(BTN_TOOL_PEN); + } + break; + } + break; + + case 0x3b: /* Battery Strength */ + hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); + usage->type = EV_PWR; + return; + + case 0x3c: /* Invert */ + device->quirks &= ~HID_QUIRK_NOINVERT; + map_key_clear(BTN_TOOL_RUBBER); + break; + + case 0x3d: /* X Tilt */ + map_abs_clear(ABS_TILT_X); + break; + + case 0x3e: /* Y Tilt */ + map_abs_clear(ABS_TILT_Y); + break; + + case 0x33: /* Touch */ + case 0x42: /* TipSwitch */ + case 0x43: /* TipSwitch2 */ + device->quirks &= ~HID_QUIRK_NOTOUCH; + map_key_clear(BTN_TOUCH); + break; + + case 0x44: /* BarrelSwitch */ + map_key_clear(BTN_STYLUS); + break; + + case 0x45: /* ERASER */ + /* + * This event is reported when eraser tip touches the surface. + * Actual eraser (BTN_TOOL_RUBBER) is set and released either + * by Invert if tool reports proximity or by Eraser directly. + */ + if (!test_bit(BTN_TOOL_RUBBER, input->keybit)) { + device->quirks |= HID_QUIRK_NOINVERT; + set_bit(BTN_TOOL_RUBBER, input->keybit); + } + map_key_clear(BTN_TOUCH); + break; + + case 0x46: /* TabletPick */ + case 0x5a: /* SecondaryBarrelSwitch */ + map_key_clear(BTN_STYLUS2); + break; + + case 0x5b: /* TransducerSerialNumber */ + case 0x6e: /* TransducerSerialNumber2 */ + map_msc(MSC_SERIAL); + break; + + default: goto unknown; + } + break; + + case HID_UP_TELEPHONY: + switch (usage->hid & HID_USAGE) { + case 0x2f: map_key_clear(KEY_MICMUTE); break; + case 0xb0: map_key_clear(KEY_NUMERIC_0); break; + case 0xb1: map_key_clear(KEY_NUMERIC_1); break; + case 0xb2: map_key_clear(KEY_NUMERIC_2); break; + case 0xb3: map_key_clear(KEY_NUMERIC_3); break; + case 0xb4: map_key_clear(KEY_NUMERIC_4); break; + case 0xb5: map_key_clear(KEY_NUMERIC_5); break; + case 0xb6: map_key_clear(KEY_NUMERIC_6); break; + case 0xb7: map_key_clear(KEY_NUMERIC_7); break; + case 0xb8: map_key_clear(KEY_NUMERIC_8); break; + case 0xb9: map_key_clear(KEY_NUMERIC_9); break; + case 0xba: map_key_clear(KEY_NUMERIC_STAR); break; + case 0xbb: map_key_clear(KEY_NUMERIC_POUND); break; + case 0xbc: map_key_clear(KEY_NUMERIC_A); break; + case 0xbd: map_key_clear(KEY_NUMERIC_B); break; + case 0xbe: map_key_clear(KEY_NUMERIC_C); break; + case 0xbf: map_key_clear(KEY_NUMERIC_D); break; + default: goto ignore; + } + break; + + case HID_UP_CONSUMER: /* USB HUT v1.12, pages 75-84 */ + switch (usage->hid & HID_USAGE) { + case 0x000: goto ignore; + case 0x030: map_key_clear(KEY_POWER); break; + case 0x031: map_key_clear(KEY_RESTART); break; + case 0x032: map_key_clear(KEY_SLEEP); break; + case 0x034: map_key_clear(KEY_SLEEP); break; + case 0x035: map_key_clear(KEY_KBDILLUMTOGGLE); break; + case 0x036: map_key_clear(BTN_MISC); break; + + case 0x040: map_key_clear(KEY_MENU); break; /* Menu */ + case 0x041: map_key_clear(KEY_SELECT); break; /* Menu Pick */ + case 0x042: map_key_clear(KEY_UP); break; /* Menu Up */ + case 0x043: map_key_clear(KEY_DOWN); break; /* Menu Down */ + case 0x044: map_key_clear(KEY_LEFT); break; /* Menu Left */ + case 0x045: map_key_clear(KEY_RIGHT); break; /* Menu Right */ + case 0x046: map_key_clear(KEY_ESC); break; /* Menu Escape */ + case 0x047: map_key_clear(KEY_KPPLUS); break; /* Menu Value Increase */ + case 0x048: map_key_clear(KEY_KPMINUS); break; /* Menu Value Decrease */ + + case 0x060: map_key_clear(KEY_INFO); break; /* Data On Screen */ + case 0x061: map_key_clear(KEY_SUBTITLE); break; /* Closed Caption */ + case 0x063: map_key_clear(KEY_VCR); break; /* VCR/TV */ + case 0x065: map_key_clear(KEY_CAMERA); break; /* Snapshot */ + case 0x069: map_key_clear(KEY_RED); break; + case 0x06a: map_key_clear(KEY_GREEN); break; + case 0x06b: map_key_clear(KEY_BLUE); break; + case 0x06c: map_key_clear(KEY_YELLOW); break; + case 0x06d: map_key_clear(KEY_ASPECT_RATIO); break; + + case 0x06f: map_key_clear(KEY_BRIGHTNESSUP); break; + case 0x070: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x072: map_key_clear(KEY_BRIGHTNESS_TOGGLE); break; + case 0x073: map_key_clear(KEY_BRIGHTNESS_MIN); break; + case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; + case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; + + case 0x076: map_key_clear(KEY_CAMERA_ACCESS_ENABLE); break; + case 0x077: map_key_clear(KEY_CAMERA_ACCESS_DISABLE); break; + case 0x078: map_key_clear(KEY_CAMERA_ACCESS_TOGGLE); break; + + case 0x079: map_key_clear(KEY_KBDILLUMUP); break; + case 0x07a: map_key_clear(KEY_KBDILLUMDOWN); break; + case 0x07c: map_key_clear(KEY_KBDILLUMTOGGLE); break; + + case 0x082: map_key_clear(KEY_VIDEO_NEXT); break; + case 0x083: map_key_clear(KEY_LAST); break; + case 0x084: map_key_clear(KEY_ENTER); break; + case 0x088: map_key_clear(KEY_PC); break; + case 0x089: map_key_clear(KEY_TV); break; + case 0x08a: map_key_clear(KEY_WWW); break; + case 0x08b: map_key_clear(KEY_DVD); break; + case 0x08c: map_key_clear(KEY_PHONE); break; + case 0x08d: map_key_clear(KEY_PROGRAM); break; + case 0x08e: map_key_clear(KEY_VIDEOPHONE); break; + case 0x08f: map_key_clear(KEY_GAMES); break; + case 0x090: map_key_clear(KEY_MEMO); break; + case 0x091: map_key_clear(KEY_CD); break; + case 0x092: map_key_clear(KEY_VCR); break; + case 0x093: map_key_clear(KEY_TUNER); break; + case 0x094: map_key_clear(KEY_EXIT); break; + case 0x095: map_key_clear(KEY_HELP); break; + case 0x096: map_key_clear(KEY_TAPE); break; + case 0x097: map_key_clear(KEY_TV2); break; + case 0x098: map_key_clear(KEY_SAT); break; + case 0x09a: map_key_clear(KEY_PVR); break; + + case 0x09c: map_key_clear(KEY_CHANNELUP); break; + case 0x09d: map_key_clear(KEY_CHANNELDOWN); break; + case 0x0a0: map_key_clear(KEY_VCR2); break; + + case 0x0b0: map_key_clear(KEY_PLAY); break; + case 0x0b1: map_key_clear(KEY_PAUSE); break; + case 0x0b2: map_key_clear(KEY_RECORD); break; + case 0x0b3: map_key_clear(KEY_FASTFORWARD); break; + case 0x0b4: map_key_clear(KEY_REWIND); break; + case 0x0b5: map_key_clear(KEY_NEXTSONG); break; + case 0x0b6: map_key_clear(KEY_PREVIOUSSONG); break; + case 0x0b7: map_key_clear(KEY_STOPCD); break; + case 0x0b8: map_key_clear(KEY_EJECTCD); break; + case 0x0bc: map_key_clear(KEY_MEDIA_REPEAT); break; + case 0x0b9: map_key_clear(KEY_SHUFFLE); break; + case 0x0bf: map_key_clear(KEY_SLOW); break; + + case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; + case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break; + + case 0x0d8: map_key_clear(KEY_DICTATE); break; + case 0x0d9: map_key_clear(KEY_EMOJI_PICKER); break; + + case 0x0e0: map_abs_clear(ABS_VOLUME); break; + case 0x0e2: map_key_clear(KEY_MUTE); break; + case 0x0e5: map_key_clear(KEY_BASSBOOST); break; + case 0x0e9: map_key_clear(KEY_VOLUMEUP); break; + case 0x0ea: map_key_clear(KEY_VOLUMEDOWN); break; + case 0x0f5: map_key_clear(KEY_SLOW); break; + + case 0x181: map_key_clear(KEY_BUTTONCONFIG); break; + case 0x182: map_key_clear(KEY_BOOKMARKS); break; + case 0x183: map_key_clear(KEY_CONFIG); break; + case 0x184: map_key_clear(KEY_WORDPROCESSOR); break; + case 0x185: map_key_clear(KEY_EDITOR); break; + case 0x186: map_key_clear(KEY_SPREADSHEET); break; + case 0x187: map_key_clear(KEY_GRAPHICSEDITOR); break; + case 0x188: map_key_clear(KEY_PRESENTATION); break; + case 0x189: map_key_clear(KEY_DATABASE); break; + case 0x18a: map_key_clear(KEY_MAIL); break; + case 0x18b: map_key_clear(KEY_NEWS); break; + case 0x18c: map_key_clear(KEY_VOICEMAIL); break; + case 0x18d: map_key_clear(KEY_ADDRESSBOOK); break; + case 0x18e: map_key_clear(KEY_CALENDAR); break; + case 0x18f: map_key_clear(KEY_TASKMANAGER); break; + case 0x190: map_key_clear(KEY_JOURNAL); break; + case 0x191: map_key_clear(KEY_FINANCE); break; + case 0x192: map_key_clear(KEY_CALC); break; + case 0x193: map_key_clear(KEY_PLAYER); break; + case 0x194: map_key_clear(KEY_FILE); break; + case 0x196: map_key_clear(KEY_WWW); break; + case 0x199: map_key_clear(KEY_CHAT); break; + case 0x19c: map_key_clear(KEY_LOGOFF); break; + case 0x19e: map_key_clear(KEY_COFFEE); break; + case 0x19f: map_key_clear(KEY_CONTROLPANEL); break; + case 0x1a2: map_key_clear(KEY_APPSELECT); break; + case 0x1a3: map_key_clear(KEY_NEXT); break; + case 0x1a4: map_key_clear(KEY_PREVIOUS); break; + case 0x1a6: map_key_clear(KEY_HELP); break; + case 0x1a7: map_key_clear(KEY_DOCUMENTS); break; + case 0x1ab: map_key_clear(KEY_SPELLCHECK); break; + case 0x1ae: map_key_clear(KEY_KEYBOARD); break; + case 0x1b1: map_key_clear(KEY_SCREENSAVER); break; + case 0x1b4: map_key_clear(KEY_FILE); break; + case 0x1b6: map_key_clear(KEY_IMAGES); break; + case 0x1b7: map_key_clear(KEY_AUDIO); break; + case 0x1b8: map_key_clear(KEY_VIDEO); break; + case 0x1bc: map_key_clear(KEY_MESSENGER); break; + case 0x1bd: map_key_clear(KEY_INFO); break; + case 0x1cb: map_key_clear(KEY_ASSISTANT); break; + case 0x201: map_key_clear(KEY_NEW); break; + case 0x202: map_key_clear(KEY_OPEN); break; + case 0x203: map_key_clear(KEY_CLOSE); break; + case 0x204: map_key_clear(KEY_EXIT); break; + case 0x207: map_key_clear(KEY_SAVE); break; + case 0x208: map_key_clear(KEY_PRINT); break; + case 0x209: map_key_clear(KEY_PROPS); break; + case 0x21a: map_key_clear(KEY_UNDO); break; + case 0x21b: map_key_clear(KEY_COPY); break; + case 0x21c: map_key_clear(KEY_CUT); break; + case 0x21d: map_key_clear(KEY_PASTE); break; + case 0x21f: map_key_clear(KEY_FIND); break; + case 0x221: map_key_clear(KEY_SEARCH); break; + case 0x222: map_key_clear(KEY_GOTO); break; + case 0x223: map_key_clear(KEY_HOMEPAGE); break; + case 0x224: map_key_clear(KEY_BACK); break; + case 0x225: map_key_clear(KEY_FORWARD); break; + case 0x226: map_key_clear(KEY_STOP); break; + case 0x227: map_key_clear(KEY_REFRESH); break; + case 0x22a: map_key_clear(KEY_BOOKMARKS); break; + case 0x22d: map_key_clear(KEY_ZOOMIN); break; + case 0x22e: map_key_clear(KEY_ZOOMOUT); break; + case 0x22f: map_key_clear(KEY_ZOOMRESET); break; + case 0x232: map_key_clear(KEY_FULL_SCREEN); break; + case 0x233: map_key_clear(KEY_SCROLLUP); break; + case 0x234: map_key_clear(KEY_SCROLLDOWN); break; + case 0x238: /* AC Pan */ + set_bit(REL_HWHEEL, input->relbit); + map_rel(REL_HWHEEL_HI_RES); + break; + case 0x23d: map_key_clear(KEY_EDIT); break; + case 0x25f: map_key_clear(KEY_CANCEL); break; + case 0x269: map_key_clear(KEY_INSERT); break; + case 0x26a: map_key_clear(KEY_DELETE); break; + case 0x279: map_key_clear(KEY_REDO); break; + + case 0x289: map_key_clear(KEY_REPLY); break; + case 0x28b: map_key_clear(KEY_FORWARDMAIL); break; + case 0x28c: map_key_clear(KEY_SEND); break; + + case 0x29d: map_key_clear(KEY_KBD_LAYOUT_NEXT); break; + + case 0x2a2: map_key_clear(KEY_ALL_APPLICATIONS); break; + + case 0x2c7: map_key_clear(KEY_KBDINPUTASSIST_PREV); break; + case 0x2c8: map_key_clear(KEY_KBDINPUTASSIST_NEXT); break; + case 0x2c9: map_key_clear(KEY_KBDINPUTASSIST_PREVGROUP); break; + case 0x2ca: map_key_clear(KEY_KBDINPUTASSIST_NEXTGROUP); break; + case 0x2cb: map_key_clear(KEY_KBDINPUTASSIST_ACCEPT); break; + case 0x2cc: map_key_clear(KEY_KBDINPUTASSIST_CANCEL); break; + + case 0x29f: map_key_clear(KEY_SCALE); break; + + default: map_key_clear(KEY_UNKNOWN); + } + break; + + case HID_UP_GENDEVCTRLS: + switch (usage->hid) { + case HID_DC_BATTERYSTRENGTH: + hidinput_setup_battery(device, HID_INPUT_REPORT, field, false); + usage->type = EV_PWR; + return; + } + goto unknown; + + case HID_UP_BATTERY: + switch (usage->hid) { + case HID_BAT_ABSOLUTESTATEOFCHARGE: + hidinput_setup_battery(device, HID_INPUT_REPORT, field, true); + usage->type = EV_PWR; + return; + case HID_BAT_CHARGING: + usage->type = EV_PWR; + return; + } + goto unknown; + case HID_UP_CAMERA: + switch (usage->hid & HID_USAGE) { + case 0x020: + map_key_clear(KEY_CAMERA_FOCUS); break; + case 0x021: + map_key_clear(KEY_CAMERA); break; + default: + goto ignore; + } + break; + + case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x021: map_key_clear(KEY_PRINT); break; + case 0x070: map_key_clear(KEY_HP); break; + case 0x071: map_key_clear(KEY_CAMERA); break; + case 0x072: map_key_clear(KEY_SOUND); break; + case 0x073: map_key_clear(KEY_QUESTION); break; + case 0x080: map_key_clear(KEY_EMAIL); break; + case 0x081: map_key_clear(KEY_CHAT); break; + case 0x082: map_key_clear(KEY_SEARCH); break; + case 0x083: map_key_clear(KEY_CONNECT); break; + case 0x084: map_key_clear(KEY_FINANCE); break; + case 0x085: map_key_clear(KEY_SPORT); break; + case 0x086: map_key_clear(KEY_SHOP); break; + default: goto ignore; + } + break; + + case HID_UP_HPVENDOR2: + set_bit(EV_REP, input->evbit); + switch (usage->hid & HID_USAGE) { + case 0x001: map_key_clear(KEY_MICMUTE); break; + case 0x003: map_key_clear(KEY_BRIGHTNESSDOWN); break; + case 0x004: map_key_clear(KEY_BRIGHTNESSUP); break; + default: goto ignore; + } + break; + + case HID_UP_MSVENDOR: + goto ignore; + + case HID_UP_CUSTOM: /* Reported on Logitech and Apple USB keyboards */ + set_bit(EV_REP, input->evbit); + goto ignore; + + case HID_UP_LOGIVENDOR: + /* intentional fallback */ + case HID_UP_LOGIVENDOR2: + /* intentional fallback */ + case HID_UP_LOGIVENDOR3: + goto ignore; + + case HID_UP_PID: + switch (usage->hid & HID_USAGE) { + case 0xa4: map_key_clear(BTN_DEAD); break; + default: goto ignore; + } + break; + + default: + unknown: + if (field->report_size == 1) { + if (field->report->type == HID_OUTPUT_REPORT) { + map_led(LED_MISC); + break; + } + map_key(BTN_MISC); + break; + } + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + map_rel(REL_MISC); + break; + } + map_abs(ABS_MISC); + break; + } + +mapped: + /* Mapping failed, bail out */ + if (!bit) + return; + + if (device->driver->input_mapped && + device->driver->input_mapped(device, hidinput, field, usage, + &bit, &max) < 0) { + /* + * The driver indicated that no further generic handling + * of the usage is desired. + */ + return; + } + + set_bit(usage->type, input->evbit); + + /* + * This part is *really* controversial: + * - HID aims at being generic so we should do our best to export + * all incoming events + * - HID describes what events are, so there is no reason for ABS_X + * to be mapped to ABS_Y + * - HID is using *_MISC+N as a default value, but nothing prevents + * *_MISC+N to overwrite a legitimate even, which confuses userspace + * (for instance ABS_MISC + 7 is ABS_MT_SLOT, which has a different + * processing) + * + * If devices still want to use this (at their own risk), they will + * have to use the quirk HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE, but + * the default should be a reliable mapping. + */ + while (usage->code <= max && test_and_set_bit(usage->code, bit)) { + if (device->quirks & HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE) { + usage->code = find_next_zero_bit(bit, + max + 1, + usage->code); + } else { + device->status |= HID_STAT_DUP_DETECTED; + goto ignore; + } + } + + if (usage->code > max) + goto ignore; + + if (usage->type == EV_ABS) { + + int a = field->logical_minimum; + int b = field->logical_maximum; + + if ((device->quirks & HID_QUIRK_BADPAD) && (usage->code == ABS_X || usage->code == ABS_Y)) { + a = field->logical_minimum = 0; + b = field->logical_maximum = 255; + } + + if (field->application == HID_GD_GAMEPAD || field->application == HID_GD_JOYSTICK) + input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); + else input_set_abs_params(input, usage->code, a, b, 0, 0); + + input_abs_set_res(input, usage->code, + hidinput_calc_abs_res(field, usage->code)); + + /* use a larger default input buffer for MT devices */ + if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0) + input_set_events_per_packet(input, 60); + } + + if (usage->type == EV_ABS && + (usage->hat_min < usage->hat_max || usage->hat_dir)) { + int i; + for (i = usage->code; i < usage->code + 2 && i <= max; i++) { + input_set_abs_params(input, i, -1, 1, 0, 0); + set_bit(i, input->absbit); + } + if (usage->hat_dir && !field->dpad) + field->dpad = usage->code; + } + + /* for those devices which produce Consumer volume usage as relative, + * we emulate pressing volumeup/volumedown appropriate number of times + * in hidinput_hid_event() + */ + if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) && + (usage->code == ABS_VOLUME)) { + set_bit(KEY_VOLUMEUP, input->keybit); + set_bit(KEY_VOLUMEDOWN, input->keybit); + } + + if (usage->type == EV_KEY) { + set_bit(EV_MSC, input->evbit); + set_bit(MSC_SCAN, input->mscbit); + } + + return; + +ignore: + usage->type = 0; + usage->code = 0; +} + +static void hidinput_handle_scroll(struct hid_usage *usage, + struct input_dev *input, + __s32 value) +{ + int code; + int hi_res, lo_res; + + if (value == 0) + return; + + if (usage->code == REL_WHEEL_HI_RES) + code = REL_WHEEL; + else + code = REL_HWHEEL; + + /* + * Windows reports one wheel click as value 120. Where a high-res + * scroll wheel is present, a fraction of 120 is reported instead. + * Our REL_WHEEL_HI_RES axis does the same because all HW must + * adhere to the 120 expectation. + */ + hi_res = value * 120/usage->resolution_multiplier; + + usage->wheel_accumulated += hi_res; + lo_res = usage->wheel_accumulated/120; + if (lo_res) + usage->wheel_accumulated -= lo_res * 120; + + input_event(input, EV_REL, code, lo_res); + input_event(input, EV_REL, usage->code, hi_res); +} + +static void hid_report_release_tool(struct hid_report *report, struct input_dev *input, + unsigned int tool) +{ + /* if the given tool is not currently reported, ignore */ + if (!test_bit(tool, input->key)) + return; + + /* + * if the given tool was previously set, release it, + * release any TOUCH and send an EV_SYN + */ + input_event(input, EV_KEY, BTN_TOUCH, 0); + input_event(input, EV_KEY, tool, 0); + input_event(input, EV_SYN, SYN_REPORT, 0); + + report->tool = 0; +} + +static void hid_report_set_tool(struct hid_report *report, struct input_dev *input, + unsigned int new_tool) +{ + if (report->tool != new_tool) + hid_report_release_tool(report, input, report->tool); + + input_event(input, EV_KEY, new_tool, 1); + report->tool = new_tool; +} + +struct fastmouse_state { + int division; + long frame_x, frame_y; + long accum_x, accum_y; + int rise, run; + /* TODO + int polling_rate; + long accel; + */ +}; +static struct fastmouse_state fastmouse = { + .division = 1, + .frame_x = 0, + .frame_y = 0, + .accum_x = 0, + .accum_y = 0, + .rise = 0, + .run = INT_MAX, + /* TODO + .polling_rate = 4000, + .accel = 1, + */ +}; + +static int set_division(const char *val, const struct kernel_param *kp) { + const int ret = kstrtoint(val, 0, &fastmouse.division); + if (ret != 0 || fastmouse.division <= 0) { + return -EINVAL; + } + return ret; +} +static const struct kernel_param_ops division_ops = { + .set = set_division, + .get = param_get_int, +}; +module_param_cb(division, &division_ops, &fastmouse.division, 0664); +MODULE_PARM_DESC(division, "Mouse movement division amount (default: 1)"); + +static int set_rise(const char *val, const struct kernel_param *kp) { + const int ret = kstrtoint(val, 0, &fastmouse.rise); + if (ret != 0) { + return -EINVAL; + } + return ret; +} +static const struct kernel_param_ops rise_ops = { + .set = set_rise, + .get = param_get_int, +}; +module_param_cb(rise, &rise_ops, &fastmouse.rise, 0664); +MODULE_PARM_DESC(rise, "Mouse movement rise amount (default: 0)"); + +static int set_run(const char *val, const struct kernel_param *kp) { + const int ret = kstrtoint(val, 0, &fastmouse.run); + if (ret != 0) { + return -EINVAL; + } + return ret; +} +static const struct kernel_param_ops run_ops = { + .set = set_run, + .get = param_get_int, +}; +module_param_cb(run, &run_ops, &fastmouse.run, 0664); +MODULE_PARM_DESC(run, "Mouse movement run amount (default: INT_MAX)"); + +/* TODO +static int set_polling_rate(const char *val, const struct kernel_param *kp) { + const int ret = kstrtoint(val, 0, &fastmouse.polling_rate); + if (ret != 0) { + return -EINVAL; + } + return ret; +} +static const struct kernel_param_ops poll_ops = { + .set = set_polling_rate, + .get = param_get_int, +}; +module_param_cb(polling_rate, &poll_ops, &fastmouse.polling_rate, 0664); +MODULE_PARM_DESC(polling_rate, "Mouse polling rate (default: 4000)"); + +static int set_accel(const char *val, const struct kernel_param *kp) { + const int ret = kstrtol(val, 0, &fastmouse.accel); + if (ret != 0) { + return -EINVAL; + } + return ret; +} +static const struct kernel_param_ops accel_ops = { + .set = set_accel, + .get = param_get_int, +}; +module_param_cb(acceleration, &accel_ops, &fastmouse.accel, 0664); +MODULE_PARM_DESC(acceleration, "Mouse acceleration (default: 1)"); +*/ + +static void input_event_fastmouse_log( + struct input_dev *input, + const unsigned int type, + const unsigned int code, + __s32 value) { + fastmouse.frame_x += (code == REL_X) * value; + fastmouse.frame_y += (code == REL_Y) * value; +} + +/* TODO (for accel) +static unsigned __int128 i128_sqrt(const unsigned __int128 v) { + unsigned __int128 left = 0; + unsigned __int128 right = v; + while (left < right) { + const unsigned __int128 mid = left + ((right - left) / 2); + if (mid >= ~(u64)0 || mid * mid > v) { + right = mid; + } else if (mid * mid == v) { + return mid; + } else { + left = mid + 1; + } + } + return left != 0 ? left - 1 : 0; +} +*/ + +void fastmouse_input_emit(struct input_dev *input) { + const long rise = fastmouse.rise / fastmouse.division; + const long run = fastmouse.run / fastmouse.division; + + fastmouse.accum_x += fastmouse.frame_x * run - fastmouse.frame_y * rise; + fastmouse.accum_y += fastmouse.frame_x * rise + fastmouse.frame_y * run; + + const long emit_x = fastmouse.accum_x / INT_MAX; + if (emit_x != 0) { + input_event(input, EV_REL, REL_X, emit_x); + } + const long emit_y = fastmouse.accum_y / INT_MAX; + if (emit_y != 0) { + input_event(input, EV_REL, REL_Y, emit_y); + } + fastmouse.accum_x %= INT_MAX; + fastmouse.accum_y %= INT_MAX; + + fastmouse.frame_x = fastmouse.frame_y = 0; +} + +void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) +{ + struct input_dev *input; + struct hid_report *report = field->report; + unsigned *quirks = &hid->quirks; + + if (!usage->type) + return; + + if (usage->type == EV_PWR) { + bool handled = hidinput_set_battery_charge_status(hid, usage->hid, value); + + if (!handled) + hidinput_update_battery(hid, value); + + return; + } + + if (!field->hidinput) + return; + + input = field->hidinput->input; + + if (usage->hat_min < usage->hat_max || usage->hat_dir) { + int hat_dir = usage->hat_dir; + if (!hat_dir) + hat_dir = (value - usage->hat_min) * 8 / (usage->hat_max - usage->hat_min + 1) + 1; + if (hat_dir < 0 || hat_dir > 8) hat_dir = 0; + input_event(input, usage->type, usage->code , hid_hat_to_axis[hat_dir].x); + input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[hat_dir].y); + return; + } + + /* + * Ignore out-of-range values as per HID specification, + * section 5.10 and 6.2.25, when NULL state bit is present. + * When it's not, clamp the value to match Microsoft's input + * driver as mentioned in "Required HID usages for digitizers": + * https://msdn.microsoft.com/en-us/library/windows/hardware/dn672278(v=vs.85).asp + * + * The logical_minimum < logical_maximum check is done so that we + * don't unintentionally discard values sent by devices which + * don't specify logical min and max. + */ + if ((field->flags & HID_MAIN_ITEM_VARIABLE) && + field->logical_minimum < field->logical_maximum) { + if (field->flags & HID_MAIN_ITEM_NULL_STATE && + (value < field->logical_minimum || + value > field->logical_maximum)) { + dbg_hid("Ignoring out-of-range value %x\n", value); + return; + } + value = clamp(value, + field->logical_minimum, + field->logical_maximum); + } + + switch (usage->hid) { + case HID_DG_ERASER: + report->tool_active |= !!value; + + /* + * if eraser is set, we must enforce BTN_TOOL_RUBBER + * to accommodate for devices not following the spec. + */ + if (value) + hid_report_set_tool(report, input, BTN_TOOL_RUBBER); + else if (report->tool != BTN_TOOL_RUBBER) + /* value is off, tool is not rubber, ignore */ + return; + else if (*quirks & HID_QUIRK_NOINVERT && + !test_bit(BTN_TOUCH, input->key)) { + /* + * There is no invert to release the tool, let hid_input + * send BTN_TOUCH with scancode and release the tool after. + */ + hid_report_release_tool(report, input, BTN_TOOL_RUBBER); + return; + } + + /* let hid-input set BTN_TOUCH */ + break; + + case HID_DG_INVERT: + report->tool_active |= !!value; + + /* + * If invert is set, we store BTN_TOOL_RUBBER. + */ + if (value) + hid_report_set_tool(report, input, BTN_TOOL_RUBBER); + else if (!report->tool_active) + /* tool_active not set means Invert and Eraser are not set */ + hid_report_release_tool(report, input, BTN_TOOL_RUBBER); + + /* no further processing */ + return; + + case HID_DG_INRANGE: + report->tool_active |= !!value; + + if (report->tool_active) { + /* + * if tool is not set but is marked as active, + * assume ours + */ + if (!report->tool) + report->tool = usage->code; + + /* drivers may have changed the value behind our back, resend it */ + hid_report_set_tool(report, input, report->tool); + } else { + hid_report_release_tool(report, input, usage->code); + } + + /* reset tool_active for the next event */ + report->tool_active = false; + + /* no further processing */ + return; + + case HID_DG_TIPSWITCH: + report->tool_active |= !!value; + + /* if tool is set to RUBBER we should ignore the current value */ + if (report->tool == BTN_TOOL_RUBBER) + return; + + break; + + case HID_DG_TIPPRESSURE: + if (*quirks & HID_QUIRK_NOTOUCH) { + int a = field->logical_minimum; + int b = field->logical_maximum; + + if (value > a + ((b - a) >> 3)) { + input_event(input, EV_KEY, BTN_TOUCH, 1); + report->tool_active = true; + } + } + break; + + case HID_UP_PID | 0x83UL: /* Simultaneous Effects Max */ + dbg_hid("Maximum Effects - %d\n",value); + return; + + case HID_UP_PID | 0x7fUL: + dbg_hid("PID Pool Report\n"); + return; + } + + switch (usage->type) { + case EV_KEY: + if (usage->code == 0) /* Key 0 is "unassigned", not KEY_UNKNOWN */ + return; + break; + + case EV_REL: + if (usage->code == REL_WHEEL_HI_RES || + usage->code == REL_HWHEEL_HI_RES) { + hidinput_handle_scroll(usage, input, value); + return; + } + break; + + case EV_ABS: + if ((field->flags & HID_MAIN_ITEM_RELATIVE) && + usage->code == ABS_VOLUME) { + int count = abs(value); + int direction = value > 0 ? KEY_VOLUMEUP : KEY_VOLUMEDOWN; + int i; + + for (i = 0; i < count; i++) { + input_event(input, EV_KEY, direction, 1); + input_sync(input); + input_event(input, EV_KEY, direction, 0); + input_sync(input); + } + return; + + } else if (((*quirks & HID_QUIRK_X_INVERT) && usage->code == ABS_X) || + ((*quirks & HID_QUIRK_Y_INVERT) && usage->code == ABS_Y)) + value = field->logical_maximum - value; + break; + } + + /* + * Ignore reports for absolute data if the data didn't change. This is + * not only an optimization but also fixes 'dead' key reports. Some + * RollOver implementations for localized keys (like BACKSLASH/PIPE; HID + * 0x31 and 0x32) report multiple keys, even though a localized keyboard + * can only have one of them physically available. The 'dead' keys + * report constant 0. As all map to the same keycode, they'd confuse + * the input layer. If we filter the 'dead' keys on the HID level, we + * skip the keycode translation and only forward real events. + */ + if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | + HID_MAIN_ITEM_BUFFERED_BYTE)) && + (field->flags & HID_MAIN_ITEM_VARIABLE) && + usage->usage_index < field->maxusage && + value == field->value[usage->usage_index]) + return; + + /* report the usage code as scancode if the key status has changed */ + if (usage->type == EV_KEY && + (!test_bit(usage->code, input->key)) == value) + input_event(input, EV_MSC, MSC_SCAN, usage->hid); + + if (hid->type == HID_TYPE_USBMOUSE && usage->type == EV_REL) { + input_event_fastmouse_log(input, usage->type, usage->code, value); + } else { + input_event(input, usage->type, usage->code, value); + } + + + if ((field->flags & HID_MAIN_ITEM_RELATIVE) && + usage->type == EV_KEY && value) { + input_sync(input); + input_event(input, usage->type, usage->code, 0); + } +} + +void hidinput_report_event(struct hid_device *hid, struct hid_report *report) +{ + struct hid_input *hidinput; + + if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC) + return; + + list_for_each_entry(hidinput, &hid->inputs, list) + input_sync(hidinput->input); +} +EXPORT_SYMBOL_GPL(hidinput_report_event); + +static int hidinput_find_field(struct hid_device *hid, unsigned int type, + unsigned int code, struct hid_field **field) +{ + struct hid_report *report; + int i, j; + + list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + *field = report->field[i]; + for (j = 0; j < (*field)->maxusage; j++) + if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) + return j; + } + } + return -1; +} + +struct hid_field *hidinput_get_led_field(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED) + return field; + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(hidinput_get_led_field); + +unsigned int hidinput_count_leds(struct hid_device *hid) +{ + struct hid_report *report; + struct hid_field *field; + int i, j; + unsigned int count = 0; + + list_for_each_entry(report, + &hid->report_enum[HID_OUTPUT_REPORT].report_list, + list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) + if (field->usage[j].type == EV_LED && + field->value[j]) + count += 1; + } + } + return count; +} +EXPORT_SYMBOL_GPL(hidinput_count_leds); + +static void hidinput_led_worker(struct work_struct *work) +{ + struct hid_device *hid = container_of(work, struct hid_device, + led_work); + struct hid_field *field; + struct hid_report *report; + int ret; + u32 len; + __u8 *buf; + + field = hidinput_get_led_field(hid); + if (!field) + return; + + /* + * field->report is accessed unlocked regarding HID core. So there might + * be another incoming SET-LED request from user-space, which changes + * the LED state while we assemble our outgoing buffer. However, this + * doesn't matter as hid_output_report() correctly converts it into a + * boolean value no matter what information is currently set on the LED + * field (even garbage). So the remote device will always get a valid + * request. + * And in case we send a wrong value, a next led worker is spawned + * for every SET-LED request so the following worker will send the + * correct value, guaranteed! + */ + + report = field->report; + + /* use custom SET_REPORT request if possible (asynchronous) */ + if (hid->ll_driver->request) + return hid->ll_driver->request(hid, report, HID_REQ_SET_REPORT); + + /* fall back to generic raw-output-report */ + len = hid_report_len(report); + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + hid_output_report(report, buf); + /* synchronous output report */ + ret = hid_hw_output_report(hid, buf, len); + if (ret == -ENOSYS) + hid_hw_raw_request(hid, report->id, buf, len, HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); + kfree(buf); +} + +static int hidinput_input_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct hid_field *field; + int offset; + + if (type == EV_FF) + return input_ff_event(dev, type, code, value); + + if (type != EV_LED) + return -1; + + if ((offset = hidinput_find_field(hid, type, code, &field)) == -1) { + hid_warn(dev, "event field not found\n"); + return -1; + } + + hid_set_field(field, offset, value); + + schedule_work(&hid->led_work); + return 0; +} + +static int hidinput_open(struct input_dev *dev) +{ + struct hid_device *hid = input_get_drvdata(dev); + + return hid_hw_open(hid); +} + +static void hidinput_close(struct input_dev *dev) +{ + struct hid_device *hid = input_get_drvdata(dev); + + hid_hw_close(hid); +} + +static bool __hidinput_change_resolution_multipliers(struct hid_device *hid, + struct hid_report *report, bool use_logical_max) +{ + struct hid_usage *usage; + bool update_needed = false; + bool get_report_completed = false; + int i, j; + + if (report->maxfield == 0) + return false; + + for (i = 0; i < report->maxfield; i++) { + __s32 value = use_logical_max ? + report->field[i]->logical_maximum : + report->field[i]->logical_minimum; + + /* There is no good reason for a Resolution + * Multiplier to have a count other than 1. + * Ignore that case. + */ + if (report->field[i]->report_count != 1) + continue; + + for (j = 0; j < report->field[i]->maxusage; j++) { + usage = &report->field[i]->usage[j]; + + if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER) + continue; + + /* + * If we have more than one feature within this + * report we need to fill in the bits from the + * others before we can overwrite the ones for the + * Resolution Multiplier. + * + * But if we're not allowed to read from the device, + * we just bail. Such a device should not exist + * anyway. + */ + if (!get_report_completed && report->maxfield > 1) { + if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) + return update_needed; + + hid_hw_request(hid, report, HID_REQ_GET_REPORT); + hid_hw_wait(hid); + get_report_completed = true; + } + + report->field[i]->value[j] = value; + update_needed = true; + } + } + + return update_needed; +} + +static void hidinput_change_resolution_multipliers(struct hid_device *hid) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + int ret; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + bool update_needed = __hidinput_change_resolution_multipliers(hid, + rep, true); + + if (update_needed) { + ret = __hid_request(hid, rep, HID_REQ_SET_REPORT); + if (ret) { + __hidinput_change_resolution_multipliers(hid, + rep, false); + return; + } + } + } + + /* refresh our structs */ + hid_setup_resolution_multiplier(hid); +} + +static void report_features(struct hid_device *hid) +{ + struct hid_driver *drv = hid->driver; + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_usage *usage; + int i, j; + + rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) + for (i = 0; i < rep->maxfield; i++) { + /* Ignore if report count is out of bounds. */ + if (rep->field[i]->report_count < 1) + continue; + + for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + + /* Verify if Battery Strength feature is available */ + if (usage->hid == HID_DC_BATTERYSTRENGTH) + hidinput_setup_battery(hid, HID_FEATURE_REPORT, + rep->field[i], false); + + if (drv->feature_mapping) + drv->feature_mapping(hid, rep->field[i], usage); + } + } +} + +static struct hid_input *hidinput_allocate(struct hid_device *hid, + unsigned int application) +{ + struct hid_input *hidinput = kzalloc(sizeof(*hidinput), GFP_KERNEL); + struct input_dev *input_dev = input_allocate_device(); + const char *suffix = NULL; + size_t suffix_len, name_len; + + if (!hidinput || !input_dev) + goto fail; + + if ((hid->quirks & HID_QUIRK_INPUT_PER_APP) && + hid->maxapplication > 1) { + switch (application) { + case HID_GD_KEYBOARD: + suffix = "Keyboard"; + break; + case HID_GD_KEYPAD: + suffix = "Keypad"; + break; + case HID_GD_MOUSE: + suffix = "Mouse"; + break; + case HID_DG_PEN: + /* + * yes, there is an issue here: + * DG_PEN -> "Stylus" + * DG_STYLUS -> "Pen" + * But changing this now means users with config snippets + * will have to change it and the test suite will not be happy. + */ + suffix = "Stylus"; + break; + case HID_DG_STYLUS: + suffix = "Pen"; + break; + case HID_DG_TOUCHSCREEN: + suffix = "Touchscreen"; + break; + case HID_DG_TOUCHPAD: + suffix = "Touchpad"; + break; + case HID_GD_SYSTEM_CONTROL: + suffix = "System Control"; + break; + case HID_CP_CONSUMER_CONTROL: + suffix = "Consumer Control"; + break; + case HID_GD_WIRELESS_RADIO_CTLS: + suffix = "Wireless Radio Control"; + break; + case HID_GD_SYSTEM_MULTIAXIS: + suffix = "System Multi Axis"; + break; + default: + break; + } + } + + if (suffix) { + name_len = strlen(hid->name); + suffix_len = strlen(suffix); + if ((name_len < suffix_len) || + strcmp(hid->name + name_len - suffix_len, suffix)) { + hidinput->name = kasprintf(GFP_KERNEL, "%s %s", + hid->name, suffix); + if (!hidinput->name) + goto fail; + } + } + + input_set_drvdata(input_dev, hid); + input_dev->event = hidinput_input_event; + input_dev->open = hidinput_open; + input_dev->close = hidinput_close; + input_dev->setkeycode = hidinput_setkeycode; + input_dev->getkeycode = hidinput_getkeycode; + + input_dev->name = hidinput->name ? hidinput->name : hid->name; + input_dev->phys = hid->phys; + input_dev->uniq = hid->uniq; + input_dev->id.bustype = hid->bus; + input_dev->id.vendor = hid->vendor; + input_dev->id.product = hid->product; + input_dev->id.version = hid->version; + input_dev->dev.parent = &hid->dev; + + hidinput->input = input_dev; + hidinput->application = application; + list_add_tail(&hidinput->list, &hid->inputs); + + INIT_LIST_HEAD(&hidinput->reports); + + return hidinput; + +fail: + kfree(hidinput); + input_free_device(input_dev); + hid_err(hid, "Out of memory during hid input probe\n"); + return NULL; +} + +static bool hidinput_has_been_populated(struct hid_input *hidinput) +{ + int i; + unsigned long r = 0; + + for (i = 0; i < BITS_TO_LONGS(EV_CNT); i++) + r |= hidinput->input->evbit[i]; + + for (i = 0; i < BITS_TO_LONGS(KEY_CNT); i++) + r |= hidinput->input->keybit[i]; + + for (i = 0; i < BITS_TO_LONGS(REL_CNT); i++) + r |= hidinput->input->relbit[i]; + + for (i = 0; i < BITS_TO_LONGS(ABS_CNT); i++) + r |= hidinput->input->absbit[i]; + + for (i = 0; i < BITS_TO_LONGS(MSC_CNT); i++) + r |= hidinput->input->mscbit[i]; + + for (i = 0; i < BITS_TO_LONGS(LED_CNT); i++) + r |= hidinput->input->ledbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SND_CNT); i++) + r |= hidinput->input->sndbit[i]; + + for (i = 0; i < BITS_TO_LONGS(FF_CNT); i++) + r |= hidinput->input->ffbit[i]; + + for (i = 0; i < BITS_TO_LONGS(SW_CNT); i++) + r |= hidinput->input->swbit[i]; + + return !!r; +} + +static void hidinput_cleanup_hidinput(struct hid_device *hid, + struct hid_input *hidinput) +{ + struct hid_report *report; + int i, k; + + list_del(&hidinput->list); + input_free_device(hidinput->input); + kfree(hidinput->name); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + if (k == HID_OUTPUT_REPORT && + hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) + continue; + + list_for_each_entry(report, &hid->report_enum[k].report_list, + list) { + + for (i = 0; i < report->maxfield; i++) + if (report->field[i]->hidinput == hidinput) + report->field[i]->hidinput = NULL; + } + } + + kfree(hidinput); +} + +static struct hid_input *hidinput_match(struct hid_report *report) +{ + struct hid_device *hid = report->device; + struct hid_input *hidinput; + + list_for_each_entry(hidinput, &hid->inputs, list) { + if (hidinput->report && + hidinput->report->id == report->id) + return hidinput; + } + + return NULL; +} + +static struct hid_input *hidinput_match_application(struct hid_report *report) +{ + struct hid_device *hid = report->device; + struct hid_input *hidinput; + + list_for_each_entry(hidinput, &hid->inputs, list) { + if (hidinput->application == report->application) + return hidinput; + + /* + * Keep SystemControl and ConsumerControl applications together + * with the main keyboard, if present. + */ + if ((report->application == HID_GD_SYSTEM_CONTROL || + report->application == HID_CP_CONSUMER_CONTROL) && + hidinput->application == HID_GD_KEYBOARD) { + return hidinput; + } + } + + return NULL; +} + +static inline void hidinput_configure_usages(struct hid_input *hidinput, + struct hid_report *report) +{ + int i, j, k; + int first_field_index = 0; + int slot_collection_index = -1; + int prev_collection_index = -1; + unsigned int slot_idx = 0; + struct hid_field *field; + + /* + * First tag all the fields that are part of a slot, + * a slot needs to have one Contact ID in the collection + */ + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + + /* ignore fields without usage */ + if (field->maxusage < 1) + continue; + + /* + * janitoring when collection_index changes + */ + if (prev_collection_index != field->usage->collection_index) { + prev_collection_index = field->usage->collection_index; + first_field_index = i; + } + + /* + * if we already found a Contact ID in the collection, + * tag and continue to the next. + */ + if (slot_collection_index == field->usage->collection_index) { + field->slot_idx = slot_idx; + continue; + } + + /* check if the current field has Contact ID */ + for (j = 0; j < field->maxusage; j++) { + if (field->usage[j].hid == HID_DG_CONTACTID) { + slot_collection_index = field->usage->collection_index; + slot_idx++; + + /* + * mark all previous fields and this one in the + * current collection to be slotted. + */ + for (k = first_field_index; k <= i; k++) + report->field[k]->slot_idx = slot_idx; + break; + } + } + } + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hidinput_configure_usage(hidinput, report->field[i], + report->field[i]->usage + j, + j); +} + +/* + * Register the input device; print a message. + * Configure the input layer interface + * Read all reports and initialize the absolute field values. + */ + +int hidinput_connect(struct hid_device *hid, unsigned int force) +{ + struct hid_driver *drv = hid->driver; + struct hid_report *report; + struct hid_input *next, *hidinput = NULL; + unsigned int application; + int i, k; + + INIT_LIST_HEAD(&hid->inputs); + INIT_WORK(&hid->led_work, hidinput_led_worker); + + hid->status &= ~HID_STAT_DUP_DETECTED; + + if (!force) { + for (i = 0; i < hid->maxcollection; i++) { + struct hid_collection *col = &hid->collection[i]; + if (col->type == HID_COLLECTION_APPLICATION || + col->type == HID_COLLECTION_PHYSICAL) + if (IS_INPUT_APPLICATION(col->usage)) + break; + } + + if (i == hid->maxcollection) + return -1; + } + + report_features(hid); + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + if (k == HID_OUTPUT_REPORT && + hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS) + continue; + + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + + if (!report->maxfield) + continue; + + application = report->application; + + /* + * Find the previous hidinput report attached + * to this report id. + */ + if (hid->quirks & HID_QUIRK_MULTI_INPUT) + hidinput = hidinput_match(report); + else if (hid->maxapplication > 1 && + (hid->quirks & HID_QUIRK_INPUT_PER_APP)) + hidinput = hidinput_match_application(report); + + if (!hidinput) { + hidinput = hidinput_allocate(hid, application); + if (!hidinput) + goto out_unwind; + } + + hidinput_configure_usages(hidinput, report); + + if (hid->quirks & HID_QUIRK_MULTI_INPUT) + hidinput->report = report; + + list_add_tail(&report->hidinput_list, + &hidinput->reports); + } + } + + hidinput_change_resolution_multipliers(hid); + + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + if (drv->input_configured && + drv->input_configured(hid, hidinput)) + goto out_unwind; + + if (!hidinput_has_been_populated(hidinput)) { + /* no need to register an input device not populated */ + hidinput_cleanup_hidinput(hid, hidinput); + continue; + } + + if (input_register_device(hidinput->input)) + goto out_unwind; + hidinput->registered = true; + } + + if (list_empty(&hid->inputs)) { + hid_err(hid, "No inputs registered, leaving\n"); + goto out_unwind; + } + + if (hid->status & HID_STAT_DUP_DETECTED) + hid_dbg(hid, + "Some usages could not be mapped, please use HID_QUIRK_INCREMENT_USAGE_ON_DUPLICATE if this is legitimate.\n"); + + return 0; + +out_unwind: + /* unwind the ones we already registered */ + hidinput_disconnect(hid); + + return -1; +} +EXPORT_SYMBOL_GPL(hidinput_connect); + +void hidinput_disconnect(struct hid_device *hid) +{ + struct hid_input *hidinput, *next; + + hidinput_cleanup_battery(hid); + + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + list_del(&hidinput->list); + if (hidinput->registered) + input_unregister_device(hidinput->input); + else + input_free_device(hidinput->input); + kfree(hidinput->name); + kfree(hidinput); + } + + /* led_work is spawned by input_dev callbacks, but doesn't access the + * parent input_dev at all. Once all input devices are removed, we + * know that led_work will never get restarted, so we can cancel it + * synchronously and are safe. */ + cancel_work_sync(&hid->led_work); +} +EXPORT_SYMBOL_GPL(hidinput_disconnect); + +#ifdef CONFIG_HID_KUNIT_TEST +#include "hid-input-test.c" +#endif diff --git a/src/make_kernel.sh b/src/make_kernel.sh new file mode 100755 index 0000000..8700351 --- /dev/null +++ b/src/make_kernel.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -xe + +cp ./src/linux/* ./linux/drivers/hid/ +cd ./linux +make -j$(nproc) + +exit 0 diff --git a/src/make_patch.sh b/src/make_patch.sh new file mode 100755 index 0000000..58c946e --- /dev/null +++ b/src/make_patch.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -xe + +cp ./src/linux/* ./linux/drivers/hid/ +cd ./linux +git diff > ../fastmouse.patch + +exit 0 \ No newline at end of file -- cgit v1.2.3