diff options
-rw-r--r-- | Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff | 15 | ||||
-rw-r--r-- | Documentation/ABI/testing/sysfs-driver-wacom | 5 | ||||
-rw-r--r-- | drivers/hid/Kconfig | 24 | ||||
-rw-r--r-- | drivers/hid/hid-alps.c | 26 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 12 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 12 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 10 | ||||
-rw-r--r-- | drivers/hid/hid-kye.c | 123 | ||||
-rw-r--r-- | drivers/hid/hid-lg.c | 86 | ||||
-rw-r--r-- | drivers/hid/hid-lg4ff.c | 109 | ||||
-rw-r--r-- | drivers/hid/hid-lg4ff.h | 4 | ||||
-rw-r--r-- | drivers/hid/hid-microsoft.c | 12 | ||||
-rw-r--r-- | drivers/hid/hid-saitek.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-sony.c | 88 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic.c | 187 | ||||
-rw-r--r-- | drivers/hid/hid-waltop.c | 36 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 4 | ||||
-rw-r--r-- | drivers/hid/wacom.h | 96 | ||||
-rw-r--r-- | drivers/hid/wacom_sys.c | 1187 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 435 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.h | 22 | ||||
-rw-r--r-- | include/linux/hid.h | 4 |
22 files changed, 1827 insertions, 672 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff index db197a879580..305dffd229a8 100644 --- a/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff +++ b/Documentation/ABI/testing/sysfs-driver-hid-logitech-lg4ff @@ -35,6 +35,12 @@ Description: Displays a set of alternate modes supported by a wheel. Each DF-EX <*--------> G25 <-> G27 DF-EX <*----------------> G27 + G29: + DF-EX <*> DFP <-> G25 <-> G27 <-> G29 + DF-EX <*--------> G25 <-> G27 <-> G29 + DF-EX <*----------------> G27 <-> G29 + DF-EX <*------------------------> G29 + DFGT: DF-EX <*> DFP <-> DFGT DF-EX <*--------> DFGT @@ -50,3 +56,12 @@ Description: Displays the real model of the wheel regardless of any alternate mode the wheel might be switched to. It is a read-only value. This entry is not created for devices that have only one mode. + +What: /sys/bus/hid/drivers/logitech/<dev>/combine_pedals +Date: Sep 2016 +KernelVersion: 4.9 +Contact: Simon Wood <simon@mungewell.org> +Description: Controls whether a combined value of accelerator and brake is + reported on the Y axis of the controller. Useful for older games + which can do not work with separate accelerator/brake axis. + Off ('0') by default, enabled by setting '1'. diff --git a/Documentation/ABI/testing/sysfs-driver-wacom b/Documentation/ABI/testing/sysfs-driver-wacom index dca429340772..2aa5503ee200 100644 --- a/Documentation/ABI/testing/sysfs-driver-wacom +++ b/Documentation/ABI/testing/sysfs-driver-wacom @@ -24,6 +24,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance Date: August 2014 Contact: linux-input@vger.kernel.org Description: + <obsoleted by the LED class API now exported by the driver> Writing to this file sets the status LED luminance (1..127) when the stylus does not touch the tablet surface, and no button is pressed on the stylus. This luminance level is @@ -33,6 +34,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance Date: August 2014 Contact: linux-input@vger.kernel.org Description: + <obsoleted by the LED class API now exported by the driver> Writing to this file sets the status LED luminance (1..127) when the stylus touches the tablet surface, or any button is pressed on the stylus. @@ -41,6 +43,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select Date: August 2014 Contact: linux-input@vger.kernel.org Description: + <obsoleted by the LED class API now exported by the driver> Writing to this file sets which one of the four (for Intuos 4 and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on the @@ -50,6 +53,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select Date: August 2014 Contact: linux-input@vger.kernel.org Description: + <obsoleted by the LED class API now exported by the driver> Writing to this file sets which one of the left four (for Cintiq 21UX2 and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on the left are always inactive. @@ -91,6 +95,7 @@ What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_remote/<serial_number>/r Date: July 2015 Contact: linux-input@vger.kernel.org Description: + <obsoleted by the LED class API now exported by the driver> Reading from this file reports the mode status of the remote as indicated by the LED lights on the device. If no reports have been received from the paired device, reading diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f590e16e41b..cd4599c0523b 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -457,8 +457,6 @@ config LOGITECH_FF - Logitech WingMan Cordless RumblePad - Logitech WingMan Cordless RumblePad 2 - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech WingMan Formula Force GP and if you want to enable force feedback for them. Note: if you say N here, this device will still be supported, but without @@ -488,15 +486,22 @@ config LOGIWHEELS_FF select INPUT_FF_MEMLESS default LOGITECH_FF help - Say Y here if you want to enable force feedback and range setting + Say Y here if you want to enable force feedback and range setting(*) support for following Logitech wheels: + - Logitech G25 (*) + - Logitech G27 (*) + - Logitech G29 (*) - Logitech Driving Force - - Logitech Driving Force Pro - - Logitech Driving Force GT - - Logitech G25 - - Logitech G27 - - Logitech MOMO/MOMO 2 - - Logitech Formula Force EX + - Logitech Driving Force Pro (*) + - Logitech Driving Force GT (*) + - Logitech Driving Force EX/RX + - Logitech Driving Force Wireless + - Logitech Speed Force Wireless + - Logitech MOMO Force + - Logitech MOMO Racing Force + - Logitech Formula Force GP + - Logitech Formula Force EX/RX + - Logitech Wingman Formula Force GP config HID_MAGICMOUSE tristate "Apple Magic Mouse/Trackpad multi-touch support" @@ -862,6 +867,7 @@ config HID_WACOM select POWER_SUPPLY select NEW_LEDS select LEDS_CLASS + select LEDS_TRIGGERS help Say Y here if you want to use the USB or BT version of the Wacom Intuos or Graphire tablet. diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 048befde295a..ed9c0ea5b026 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -139,8 +139,8 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address, if (read_flag) { readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL); if (!readbuf) { - kfree(input); - return -ENOMEM; + ret = -ENOMEM; + goto exit; } ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf, @@ -149,6 +149,7 @@ static int u1_read_write_register(struct hid_device *hdev, u32 address, if (ret < 0) { dev_err(&hdev->dev, "failed read register (%d)\n", ret); + kfree(readbuf); goto exit; } @@ -190,16 +191,16 @@ static int alps_raw_event(struct hid_device *hdev, if (z != 0) { input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, 1); + input_report_abs(hdata->input, + ABS_MT_POSITION_X, x); + input_report_abs(hdata->input, + ABS_MT_POSITION_Y, y); + input_report_abs(hdata->input, + ABS_MT_PRESSURE, z); } else { input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER, 0); - break; } - - input_report_abs(hdata->input, ABS_MT_POSITION_X, x); - input_report_abs(hdata->input, ABS_MT_POSITION_Y, y); - input_report_abs(hdata->input, ABS_MT_PRESSURE, z); - } input_mt_sync_frame(hdata->input); @@ -244,13 +245,13 @@ static int alps_raw_event(struct hid_device *hdev, static int alps_post_reset(struct hid_device *hdev) { return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, - NULL, U1_TP_ABS_MODE, false); + NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false); } static int alps_post_resume(struct hid_device *hdev) { return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1, - NULL, U1_TP_ABS_MODE, false); + NULL, U1_TP_ABS_MODE | U1_SP_ABS_MODE, false); } #endif /* CONFIG_PM */ @@ -383,7 +384,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) input2 = input_allocate_device(); if (!input2) { - input_free_device(input2); + ret = -ENOMEM; goto exit; } @@ -425,7 +426,8 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) __set_bit(INPUT_PROP_POINTER, input2->propbit); __set_bit(INPUT_PROP_POINTING_STICK, input2->propbit); - if (input_register_device(data->input2)) { + ret = input_register_device(data->input2); + if (ret) { input_free_device(input2); goto exit; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 08f53c7fd513..2b89c701076f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -727,6 +727,7 @@ static void hid_scan_collection(struct hid_parser *parser, unsigned type) (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP || hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || hid->product == USB_DEVICE_ID_MS_POWER_COVER) && hid->group == HID_GROUP_MULTITOUCH) @@ -1916,7 +1917,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, - { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, @@ -1982,6 +1983,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600) }, @@ -2037,6 +2039,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9) }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5) }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9) }, @@ -2083,6 +2086,11 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) }, @@ -2480,7 +2488,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) }, { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) }, { HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) }, -#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE) +#if IS_ENABLED(CONFIG_MOUSE_SYNAPTICS_USB) { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) }, { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 4ed9a4fdfea7..cd59c79eebdd 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -268,6 +268,7 @@ #define USB_DEVICE_ID_CORSAIR_K95RGB 0x1b11 #define USB_DEVICE_ID_CORSAIR_M65RGB 0x1b12 #define USB_DEVICE_ID_CORSAIR_K70RGB 0x1b13 +#define USB_DEVICE_ID_CORSAIR_STRAFE 0x1b15 #define USB_DEVICE_ID_CORSAIR_K65RGB 0x1b17 #define USB_VENDOR_ID_CREATIVELABS 0x041e @@ -565,7 +566,7 @@ #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 -#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a +#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2 0x501a #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 #define USB_DEVICE_ID_KYE_PENSKETCH_M912 0x5015 @@ -713,6 +714,7 @@ #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3 0x07dc #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2 0x07e2 #define USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP 0x07dd +#define USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP 0x07e9 #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07de #define USB_DEVICE_ID_MS_POWER_COVER 0x07da @@ -859,6 +861,7 @@ #define USB_DEVICE_ID_SAITEK_PS1000 0x0621 #define USB_DEVICE_ID_SAITEK_RAT7_OLD 0x0ccb #define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7 +#define USB_DEVICE_ID_SAITEK_RAT9 0x0cfa #define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0 #define USB_VENDOR_ID_SAMSUNG 0x0419 @@ -996,6 +999,10 @@ #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064 #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522 #define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781 +#define USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3 0x3031 +#define USB_DEVICE_ID_UGEE_TABLET_81 0x0081 +#define USB_DEVICE_ID_UGEE_TABLET_45 0x0045 +#define USB_DEVICE_ID_YIYNOVA_TABLET 0x004d #define USB_VENDOR_ID_UNITEC 0x227d #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709 @@ -1085,4 +1092,7 @@ #define USB_DEVICE_ID_RAPHNET_2NES2SNES 0x0002 #define USB_DEVICE_ID_RAPHNET_4NES4SNES 0x0003 +#define USB_VENDOR_ID_UGTIZER 0x2179 +#define USB_DEVICE_ID_UGTIZER_TABLET_GP0610 0x0053 + #endif diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index bcfaf32d9e5e..fb9ace1cef8b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -604,6 +604,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel 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; + if ((usage->hid & 0xf0) == 0x90) { /* D-pad */ switch (usage->hid) { case HID_GD_UP: usage->hat_dir = 1; break; @@ -953,6 +962,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel 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; diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index 32e6d8d9ded0..0dd1167b2c9b 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -19,11 +19,6 @@ #include "hid-ids.h" -/* - * See EasyPen i405X description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_i405X - */ - /* Original EasyPen i405X report descriptor size */ #define EASYPEN_I405X_RDESC_ORIG_SIZE 476 @@ -82,11 +77,6 @@ static __u8 easypen_i405x_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See MousePen i608X description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=KYE_MousePen_i608X - */ - /* Original MousePen i608X report descriptor size */ #define MOUSEPEN_I608X_RDESC_ORIG_SIZE 476 @@ -186,10 +176,104 @@ static __u8 mousepen_i608x_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See EasyPen M610X description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=KYE_EasyPen_M610X - */ +/* Original MousePen i608X v2 report descriptor size */ +#define MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE 482 + +/* Fixed MousePen i608X v2 report descriptor */ +static __u8 mousepen_i608x_v2_rdesc_fixed[] = { + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0x01, /* Usage (01h), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x05, /* Report ID (5), */ + 0x09, 0x01, /* Usage (01h), */ + 0x15, 0x80, /* Logical Minimum (-128), */ + 0x25, 0x7F, /* Logical Maximum (127), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x07, /* Report Count (7), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x02, /* Usage (Pen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x10, /* Report ID (16), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x04, /* Report Count (4), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x34, /* Physical Minimum (0), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ + 0x26, 0x00, 0x78, /* Logical Maximum (30720), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x26, 0xFF, 0x07, /* Logical Maximum (2047), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection, */ + 0xC0, /* End Collection, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x02, /* Usage (Mouse), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x11, /* Report ID (17), */ + 0x09, 0x01, /* Usage (Pointer), */ + 0xA0, /* Collection (Physical), */ + 0x14, /* Logical Minimum (0), */ + 0xA4, /* Push, */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x03, /* Usage Maximum (03h), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x05, /* Report Count (5), */ + 0x81, 0x01, /* Input (Constant), */ + 0xB4, /* Pop, */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x55, 0xFD, /* Unit Exponent (-3), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x34, /* Physical Minimum (0), */ + 0x75, 0x10, /* Report Size (16), */ + 0x09, 0x30, /* Usage (X), */ + 0x46, 0x40, 0x1F, /* Physical Maximum (8000), */ + 0x27, 0x00, 0xA0, 0x00, 0x00, /* Logical Maximum (40960), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x70, 0x17, /* Physical Maximum (6000), */ + 0x26, 0x00, 0x78, /* Logical Maximum (30720), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0x75, 0x08, /* Report Size (8), */ + 0x09, 0x38, /* Usage (Wheel), */ + 0x15, 0xFF, /* Logical Minimum (-1), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x06, /* Input (Variable, Relative), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; /* Original EasyPen M610X report descriptor size */ #define EASYPEN_M610X_RDESC_ORIG_SIZE 476 @@ -454,12 +538,17 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, } break; case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: - case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) { rdesc = mousepen_i608x_rdesc_fixed; *rsize = sizeof(mousepen_i608x_rdesc_fixed); } break; + case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2: + if (*rsize == MOUSEPEN_I608X_V2_RDESC_ORIG_SIZE) { + rdesc = mousepen_i608x_v2_rdesc_fixed; + *rsize = sizeof(mousepen_i608x_v2_rdesc_fixed); + } + break; case USB_DEVICE_ID_KYE_EASYPEN_M610X: if (*rsize == EASYPEN_M610X_RDESC_ORIG_SIZE) { rdesc = easypen_m610x_rdesc_fixed; @@ -553,7 +642,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) switch (id->product) { case USB_DEVICE_ID_KYE_EASYPEN_I405X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: - case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: + case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2: case USB_DEVICE_ID_KYE_EASYPEN_M610X: case USB_DEVICE_ID_KYE_PENSKETCH_M912: ret = kye_tablet_enable(hdev); @@ -586,7 +675,7 @@ static const struct hid_device_id kye_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, - USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, + USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index feb2be71f77c..76f644deb0a7 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -49,6 +49,7 @@ #define FV_RDESC_ORIG_SIZE 130 #define MOMO_RDESC_ORIG_SIZE 87 #define MOMO2_RDESC_ORIG_SIZE 87 +#define FFG_RDESC_ORIG_SIZE 85 /* Fixed report descriptors for Logitech Driving Force (and Pro) * wheel controllers @@ -334,6 +335,52 @@ static __u8 momo2_rdesc_fixed[] = { 0xC0 /* End Collection */ }; +static __u8 ffg_rdesc_fixed[] = { +0x05, 0x01, /* Usage Page (Desktop), */ +0x09, 0x04, /* Usage (Joystik), */ +0xA1, 0x01, /* Collection (Application), */ +0xA1, 0x02, /* Collection (Logical), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x0A, /* Report Size (10), */ +0x15, 0x00, /* Logical Minimum (0), */ +0x26, 0xFF, 0x03, /* Logical Maximum (1023), */ +0x35, 0x00, /* Physical Minimum (0), */ +0x46, 0xFF, 0x03, /* Physical Maximum (1023), */ +0x09, 0x30, /* Usage (X), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x06, /* Report Count (6), */ +0x75, 0x01, /* Report Size (1), */ +0x25, 0x01, /* Logical Maximum (1), */ +0x45, 0x01, /* Physical Maximum (1), */ +0x05, 0x09, /* Usage Page (Button), */ +0x19, 0x01, /* Usage Minimum (01h), */ +0x29, 0x06, /* Usage Maximum (06h), */ +0x81, 0x02, /* Input (Variable), */ +0x95, 0x01, /* Report Count (1), */ +0x75, 0x08, /* Report Size (8), */ +0x26, 0xFF, 0x00, /* Logical Maximum (255), */ +0x46, 0xFF, 0x00, /* Physical Maximum (255), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +0x81, 0x02, /* Input (Variable), */ +0x05, 0x01, /* Usage Page (Desktop), */ +0x81, 0x01, /* Input (Constant), */ +0x09, 0x31, /* Usage (Y), */ +0x81, 0x02, /* Input (Variable), */ +0x09, 0x32, /* Usage (Z), */ +0x81, 0x02, /* Input (Variable), */ +0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ +0x09, 0x01, /* Usage (01h), */ +0x81, 0x02, /* Input (Variable), */ +0xC0, /* End Collection, */ +0xA1, 0x02, /* Collection (Logical), */ +0x09, 0x02, /* Usage (02h), */ +0x95, 0x07, /* Report Count (7), */ +0x91, 0x02, /* Output (Variable), */ +0xC0, /* End Collection, */ +0xC0 /* End Collection */ +}; + /* * Certain Logitech keyboards send in report #3 keys which are far * above the logical maximum described in descriptor. This extends @@ -343,8 +390,6 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { struct lg_drv_data *drv_data = hid_get_drvdata(hdev); - struct usb_device_descriptor *udesc; - __u16 bcdDevice, rev_maj, rev_min; if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 && rdesc[84] == 0x8c && rdesc[85] == 0x02) { @@ -363,20 +408,18 @@ static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc, switch (hdev->product) { - /* Several wheels report as this id when operating in emulation mode. */ - case USB_DEVICE_ID_LOGITECH_WHEEL: - udesc = &(hid_to_usb_dev(hdev)->descriptor); - if (!udesc) { - hid_err(hdev, "NULL USB device descriptor\n"); - break; + case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: + if (*rsize == FFG_RDESC_ORIG_SIZE) { + hid_info(hdev, + "fixing up Logitech Wingman Formula Force GP report descriptor\n"); + rdesc = ffg_rdesc_fixed; + *rsize = sizeof(ffg_rdesc_fixed); } - bcdDevice = le16_to_cpu(udesc->bcdDevice); - rev_maj = bcdDevice >> 8; - rev_min = bcdDevice & 0xff; + break; - /* Update the report descriptor for only the Driving Force wheel */ - if (rev_maj == 1 && rev_min == 2 && - *rsize == DF_RDESC_ORIG_SIZE) { + /* Several wheels report as this id when operating in emulation mode. */ + case USB_DEVICE_ID_LOGITECH_WHEEL: + if (*rsize == DF_RDESC_ORIG_SIZE) { hid_info(hdev, "fixing up Logitech Driving Force report descriptor\n"); rdesc = df_rdesc_fixed; @@ -621,6 +664,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi, usage->code == ABS_RZ)) { switch (hdev->product) { case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: case USB_DEVICE_ID_LOGITECH_WHEEL: case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: @@ -657,6 +701,17 @@ static int lg_event(struct hid_device *hdev, struct hid_field *field, return 0; } +static int lg_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *rd, int size) +{ + struct lg_drv_data *drv_data = hid_get_drvdata(hdev); + + if (drv_data->quirks & LG_FF4) + return lg4ff_raw_event(hdev, report, rd, size, drv_data); + + return 0; +} + static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct usb_interface *iface = to_usb_interface(hdev->dev.parent); @@ -809,7 +864,7 @@ static const struct hid_device_id lg_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL), .driver_data = LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG), - .driver_data = LG_FF }, + .driver_data = LG_NOGET | LG_FF4 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2), .driver_data = LG_FF2 }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940), @@ -830,6 +885,7 @@ static struct hid_driver lg_driver = { .input_mapping = lg_input_mapping, .input_mapped = lg_input_mapped, .event = lg_event, + .raw_event = lg_raw_event, .probe = lg_probe, .remove = lg_remove, }; diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index af3a8ec8a746..1fc12e357035 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -75,6 +75,7 @@ static void lg4ff_set_range_g25(struct hid_device *hid, u16 range); struct lg4ff_wheel_data { const u32 product_id; + u16 combine; u16 range; const u16 min_range; const u16 max_range; @@ -136,6 +137,7 @@ struct lg4ff_alternate_mode { }; static const struct lg4ff_wheel lg4ff_devices[] = { + {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL}, {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}, {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp}, @@ -328,6 +330,56 @@ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, } } +int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *rd, int size, struct lg_drv_data *drv_data) +{ + int offset; + struct lg4ff_device_entry *entry = drv_data->device_props; + + if (!entry) + return 0; + + /* adjust HID report present combined pedals data */ + if (entry->wdata.combine) { + switch (entry->wdata.product_id) { + case USB_DEVICE_ID_LOGITECH_WHEEL: + rd[5] = rd[3]; + rd[6] = 0x7F; + return 1; + case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG: + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL: + case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2: + rd[4] = rd[3]; + rd[5] = 0x7F; + return 1; + case USB_DEVICE_ID_LOGITECH_DFP_WHEEL: + rd[5] = rd[4]; + rd[6] = 0x7F; + return 1; + case USB_DEVICE_ID_LOGITECH_G25_WHEEL: + case USB_DEVICE_ID_LOGITECH_G27_WHEEL: + offset = 5; + break; + case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL: + case USB_DEVICE_ID_LOGITECH_G29_WHEEL: + offset = 6; + break; + case USB_DEVICE_ID_LOGITECH_WII_WHEEL: + offset = 3; + break; + default: + return 0; + } + + /* Compute a combined axis when wheel does not supply it */ + rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1; + rd[offset+1] = 0x7F; + return 1; + } + + return 0; +} + static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel, const struct lg4ff_multimode_wheel *mmode_wheel, const u16 real_product_id) @@ -345,6 +397,7 @@ static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const s { struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id, .real_product_id = real_product_id, + .combine = 0, .min_range = wheel->min_range, .max_range = wheel->max_range, .set_range = wheel->set_range, @@ -885,6 +938,58 @@ static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_att } static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store); +static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + size_t count; + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return 0; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return 0; + } + + count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine); + return count; +} + +static ssize_t lg4ff_combine_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hid = to_hid_device(dev); + struct lg4ff_device_entry *entry; + struct lg_drv_data *drv_data; + u16 combine = simple_strtoul(buf, NULL, 10); + + drv_data = hid_get_drvdata(hid); + if (!drv_data) { + hid_err(hid, "Private driver data not found!\n"); + return -EINVAL; + } + + entry = drv_data->device_props; + if (!entry) { + hid_err(hid, "Device properties not found!\n"); + return -EINVAL; + } + + if (combine > 1) + combine = 1; + + entry->wdata.combine = combine; + return count; +} +static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store); + /* Export the currently set range of the wheel */ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1259,6 +1364,9 @@ int lg4ff_init(struct hid_device *hid) } /* Create sysfs interface */ + error = device_create_file(&hid->dev, &dev_attr_combine_pedals); + if (error) + hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error); error = device_create_file(&hid->dev, &dev_attr_range); if (error) hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error); @@ -1358,6 +1466,7 @@ int lg4ff_deinit(struct hid_device *hid) device_remove_file(&hid->dev, &dev_attr_alternate_modes); } + device_remove_file(&hid->dev, &dev_attr_combine_pedals); device_remove_file(&hid->dev, &dev_attr_range); #ifdef CONFIG_LEDS_CLASS { diff --git a/drivers/hid/hid-lg4ff.h b/drivers/hid/hid-lg4ff.h index 66201af44da3..de1f350e0bd3 100644 --- a/drivers/hid/hid-lg4ff.h +++ b/drivers/hid/hid-lg4ff.h @@ -6,11 +6,15 @@ extern int lg4ff_no_autoswitch; /* From hid-lg.c */ int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data); +int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *rd, int size, struct lg_drv_data *drv_data); int lg4ff_init(struct hid_device *hdev); int lg4ff_deinit(struct hid_device *hdev); #else static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data) { return 0; } +static inline int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report, + u8 *rd, int size, struct lg_drv_data *drv_data) { return 0; } static inline int lg4ff_init(struct hid_device *hdev) { return -1; } static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; } #endif diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index e924d555536c..c6cd392e9f99 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -28,7 +28,6 @@ #define MS_RDESC 0x08 #define MS_NOGET 0x10 #define MS_DUPLICATE_USAGES 0x20 -#define MS_RDESC_3K 0x40 static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) @@ -45,13 +44,6 @@ static __u8 *ms_report_fixup(struct hid_device *hdev, __u8 *rdesc, rdesc[557] = 0x35; rdesc[559] = 0x45; } - /* the same as above (s/usage/physical/) */ - if ((quirks & MS_RDESC_3K) && *rsize == 106 && rdesc[94] == 0x19 && - rdesc[95] == 0x00 && rdesc[96] == 0x29 && - rdesc[97] == 0xff) { - rdesc[94] = 0x35; - rdesc[96] = 0x45; - } return rdesc; } @@ -271,7 +263,7 @@ static const struct hid_device_id ms_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_USB), .driver_data = MS_PRESENTER }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_3K), - .driver_data = MS_ERGONOMY | MS_RDESC_3K }, + .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_7K), .driver_data = MS_ERGONOMY }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_DIGITAL_MEDIA_600), @@ -288,6 +280,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP), + .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), .driver_data = MS_HIDINPUT }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c index 2f84b26f1167..39e642686ff0 100644 --- a/drivers/hid/hid-saitek.c +++ b/drivers/hid/hid-saitek.c @@ -183,6 +183,8 @@ static const struct hid_device_id saitek_devices[] = { .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, + { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9), + .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), .driver_data = SAITEK_RELEASE_MODE_RAT7 }, { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 310436a54a3f..b0bb99a821bd 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -8,7 +8,7 @@ * Copyright (c) 2012 David Dillow <dave@thedillows.org> * Copyright (c) 2006-2013 Jiri Kosina * Copyright (c) 2013 Colin Leitner <colin.leitner@gmail.com> - * Copyright (c) 2014 Frank Praznik <frank.praznik@gmail.com> + * Copyright (c) 2014-2016 Frank Praznik <frank.praznik@gmail.com> */ /* @@ -51,6 +51,7 @@ #define NAVIGATION_CONTROLLER_USB BIT(9) #define NAVIGATION_CONTROLLER_BT BIT(10) #define SINO_LITE_CONTROLLER BIT(11) +#define FUTUREMAX_DANCE_MAT BIT(12) #define SIXAXIS_CONTROLLER (SIXAXIS_CONTROLLER_USB | SIXAXIS_CONTROLLER_BT) #define MOTION_CONTROLLER (MOTION_CONTROLLER_USB | MOTION_CONTROLLER_BT) @@ -65,6 +66,8 @@ MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER) #define SONY_FF_SUPPORT (SIXAXIS_CONTROLLER | DUALSHOCK4_CONTROLLER |\ MOTION_CONTROLLER) +#define SONY_BT_DEVICE (SIXAXIS_CONTROLLER_BT | DUALSHOCK4_CONTROLLER_BT |\ + MOTION_CONTROLLER_BT | NAVIGATION_CONTROLLER_BT) #define MAX_LEDS 4 @@ -1048,6 +1051,7 @@ struct sony_sc { u8 mac_address[6]; u8 worker_initialized; + u8 defer_initialization; u8 cable_state; u8 battery_charging; u8 battery_capacity; @@ -1058,6 +1062,12 @@ struct sony_sc { u8 led_count; }; +static inline void sony_schedule_work(struct sony_sc *sc) +{ + if (!sc->defer_initialization) + schedule_work(&sc->state_worker); +} + static u8 *sixaxis_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *rsize) { @@ -1125,7 +1135,7 @@ static u8 *sony_report_fixup(struct hid_device *hdev, u8 *rdesc, { struct sony_sc *sc = hid_get_drvdata(hdev); - if (sc->quirks & SINO_LITE_CONTROLLER) + if (sc->quirks & (SINO_LITE_CONTROLLER | FUTUREMAX_DANCE_MAT)) return rdesc; /* @@ -1317,6 +1327,11 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, dualshock4_parse_report(sc, rd, size); } + if (sc->defer_initialization) { + sc->defer_initialization = 0; + sony_schedule_work(sc); + } + return 0; } @@ -1554,7 +1569,7 @@ static void buzz_set_leds(struct sony_sc *sc) static void sony_set_leds(struct sony_sc *sc) { if (!(sc->quirks & BUZZ_CONTROLLER)) - schedule_work(&sc->state_worker); + sony_schedule_work(sc); else buzz_set_leds(sc); } @@ -1665,7 +1680,7 @@ static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on, new_off != drv_data->led_delay_off[n]) { drv_data->led_delay_on[n] = new_on; drv_data->led_delay_off[n] = new_off; - schedule_work(&drv_data->state_worker); + sony_schedule_work(drv_data); } return 0; @@ -1865,6 +1880,17 @@ static void dualshock4_send_output_report(struct sony_sc *sc) u8 *buf = sc->output_report_dmabuf; int offset; + /* + * NOTE: The buf[1] field of the Bluetooth report controls + * the Dualshock 4 reporting rate. + * + * Known values include: + * + * 0x80 - 1000hz (full speed) + * 0xA0 - 31hz + * 0xB0 - 20hz + * 0xD0 - 66hz + */ if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { memset(buf, 0, DS4_REPORT_0x05_SIZE); buf[0] = 0x05; @@ -1976,7 +2002,7 @@ static int sony_play_effect(struct input_dev *dev, void *data, sc->left = effect->u.rumble.strong_magnitude / 256; sc->right = effect->u.rumble.weak_magnitude / 256; - schedule_work(&sc->state_worker); + sony_schedule_work(sc); return 0; } @@ -2039,8 +2065,11 @@ static int sony_battery_get_property(struct power_supply *psy, return ret; } -static int sony_battery_probe(struct sony_sc *sc) +static int sony_battery_probe(struct sony_sc *sc, int append_dev_id) { + const char *battery_str_fmt = append_dev_id ? + "sony_controller_battery_%pMR_%i" : + "sony_controller_battery_%pMR"; struct power_supply_config psy_cfg = { .drv_data = sc, }; struct hid_device *hdev = sc->hdev; int ret; @@ -2056,9 +2085,8 @@ static int sony_battery_probe(struct sony_sc *sc) sc->battery_desc.get_property = sony_battery_get_property; sc->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; sc->battery_desc.use_for_apm = 0; - sc->battery_desc.name = kasprintf(GFP_KERNEL, - "sony_controller_battery_%pMR", - sc->mac_address); + sc->battery_desc.name = kasprintf(GFP_KERNEL, battery_str_fmt, + sc->mac_address, sc->device_id); if (!sc->battery_desc.name) return -ENOMEM; @@ -2094,7 +2122,21 @@ static void sony_battery_remove(struct sony_sc *sc) * it will show up as two devices. A global list of connected controllers and * their MAC addresses is maintained to ensure that a device is only connected * once. + * + * Some USB-only devices masquerade as Sixaxis controllers and all have the + * same dummy Bluetooth address, so a comparison of the connection type is + * required. Devices are only rejected in the case where two devices have + * matching Bluetooth addresses on different bus types. */ +static inline int sony_compare_connection_type(struct sony_sc *sc0, + struct sony_sc *sc1) +{ + const int sc0_not_bt = !(sc0->quirks & SONY_BT_DEVICE); + const int sc1_not_bt = !(sc1->quirks & SONY_BT_DEVICE); + + return sc0_not_bt == sc1_not_bt; +} + static int sony_check_add_dev_list(struct sony_sc *sc) { struct sony_sc *entry; @@ -2107,9 +2149,14 @@ static int sony_check_add_dev_list(struct sony_sc *sc) ret = memcmp(sc->mac_address, entry->mac_address, sizeof(sc->mac_address)); if (!ret) { - ret = -EEXIST; - hid_info(sc->hdev, "controller with MAC address %pMR already connected\n", + if (sony_compare_connection_type(sc, entry)) { + ret = 1; + } else { + ret = -EEXIST; + hid_info(sc->hdev, + "controller with MAC address %pMR already connected\n", sc->mac_address); + } goto unlock; } } @@ -2285,10 +2332,14 @@ static inline void sony_cancel_work_sync(struct sony_sc *sc) static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret; + int append_dev_id; unsigned long quirks = id->driver_data; struct sony_sc *sc; unsigned int connect_mask = HID_CONNECT_DEFAULT; + if (!strcmp(hdev->name, "FutureMax Dance Mat")) + quirks |= FUTUREMAX_DANCE_MAT; + sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); if (sc == NULL) { hid_err(hdev, "can't alloc sony descriptor\n"); @@ -2341,9 +2392,16 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) * the Sixaxis does not want the report_id as part of the data * packet, so we have to discard buf[0] when sending the actual * control message, even for numbered reports, humpf! + * + * Additionally, the Sixaxis on USB isn't properly initialized + * until the PS logo button is pressed and as such won't retain + * any state set by an output report, so the initial + * configuration report is deferred until the first input + * report arrives. */ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; + sc->defer_initialization = 1; ret = sixaxis_set_operational_usb(hdev); sony_init_output_report(sc, sixaxis_send_output_report); } else if ((sc->quirks & SIXAXIS_CONTROLLER_BT) || @@ -2379,7 +2437,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) if (ret < 0) goto err_stop; - ret = sony_check_add(sc); + ret = append_dev_id = sony_check_add(sc); if (ret < 0) goto err_stop; @@ -2390,7 +2448,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) } if (sc->quirks & SONY_BATTERY_SUPPORT) { - ret = sony_battery_probe(sc); + ret = sony_battery_probe(sc, append_dev_id); if (ret < 0) goto err_stop; @@ -2486,8 +2544,10 @@ static int sony_resume(struct hid_device *hdev) * reinitialized on resume or they won't behave properly. */ if ((sc->quirks & SIXAXIS_CONTROLLER_USB) || - (sc->quirks & NAVIGATION_CONTROLLER_USB)) + (sc->quirks & NAVIGATION_CONTROLLER_USB)) { sixaxis_set_operational_usb(sc->hdev); + sc->defer_initialization = 1; + } sony_set_leds(sc); } diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 85ac43517e3f..1509d7287ff3 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -21,13 +21,6 @@ #include "hid-ids.h" -/* - * See WPXXXXU model descriptions, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP4030U - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP5540U - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP8060U - */ - /* Size of the original descriptor of WPXXXXU tablets */ #define WPXXXXU_RDESC_ORIG_SIZE 212 @@ -221,11 +214,6 @@ static __u8 wp8060u_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See WP1062 description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP1062 - */ - /* Size of the original descriptor of WP1062 tablet */ #define WP1062_RDESC_ORIG_SIZE 254 @@ -274,11 +262,6 @@ static __u8 wp1062_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See PF1209 description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_PF1209 - */ - /* Size of the original descriptor of PF1209 tablet */ #define PF1209_RDESC_ORIG_SIZE 234 @@ -356,11 +339,6 @@ static __u8 pf1209_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See TWHL850 description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850 - */ - /* Size of the original descriptors of TWHL850 tablet */ #define TWHL850_RDESC_ORIG_SIZE0 182 #define TWHL850_RDESC_ORIG_SIZE1 161 @@ -469,11 +447,6 @@ static __u8 twhl850_rdesc_fixed2[] = { 0xC0 /* End Collection */ }; -/* - * See TWHA60 description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60 - */ - /* Size of the original descriptors of TWHA60 tablet */ #define TWHA60_RDESC_ORIG_SIZE0 254 #define TWHA60_RDESC_ORIG_SIZE1 139 @@ -613,6 +586,27 @@ static const __u8 uclogic_tablet_rdesc_template[] = { 0xC0 /* End Collection */ }; +/* Fixed virtual pad report descriptor */ +static const __u8 uclogic_buttonpad_rdesc[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0xF7, /* Report ID (247), */ + 0x05, 0x0D, /* Usage Page (Digitizers), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x18, /* Report Count (24), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + 0x29, 0x08, /* Usage Maximum (08h), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x02, /* Input (Variable), */ + 0xC0, /* End Collection */ + 0xC0 /* End Collection */ +}; + /* Parameter indices */ enum uclogic_prm { UCLOGIC_PRM_X_LM = 1, @@ -628,6 +622,7 @@ struct uclogic_drvdata { unsigned int rsize; bool invert_pen_inrange; bool ignore_pen_usage; + bool has_virtual_pad_interface; }; static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, @@ -637,6 +632,12 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + if (drvdata->rdesc != NULL) { + rdesc = drvdata->rdesc; + *rsize = drvdata->rsize; + return rdesc; + } + switch (hdev->product) { case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: if (*rsize == PF1209_RDESC_ORIG_SIZE) { @@ -706,11 +707,6 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, break; } break; - default: - if (drvdata->rdesc != NULL) { - rdesc = drvdata->rdesc; - *rsize = drvdata->rsize; - } } return rdesc; @@ -804,7 +800,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) len = UCLOGIC_PRM_NUM * sizeof(*buf); buf = kmalloc(len, GFP_KERNEL); if (buf == NULL) { - hid_err(hdev, "failed to allocate parameter buffer\n"); rc = -ENOMEM; goto cleanup; } @@ -848,7 +843,6 @@ static int uclogic_tablet_enable(struct hid_device *hdev) sizeof(uclogic_tablet_rdesc_template), GFP_KERNEL); if (drvdata->rdesc == NULL) { - hid_err(hdev, "failed to allocate fixed rdesc\n"); rc = -ENOMEM; goto cleanup; } @@ -876,11 +870,75 @@ cleanup: return rc; } +/** + * Enable actual button mode. + * + * @hdev: HID device + */ +static int uclogic_button_enable(struct hid_device *hdev) +{ + int rc; + struct usb_device *usb_dev = hid_to_usb_dev(hdev); + struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); + char *str_buf; + size_t str_len = 16; + unsigned char *rdesc; + size_t rdesc_len; + + str_buf = kzalloc(str_len, GFP_KERNEL); + if (str_buf == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + /* Enable abstract keyboard mode */ + rc = usb_string(usb_dev, 0x7b, str_buf, str_len); + if (rc == -EPIPE) { + hid_info(hdev, "button mode setting not found\n"); + rc = 0; + goto cleanup; + } else if (rc < 0) { + hid_err(hdev, "failed to enable abstract keyboard\n"); + goto cleanup; + } else if (strncmp(str_buf, "HK On", rc)) { + hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", + str_buf); + rc = -EINVAL; + goto cleanup; + } + + /* Re-allocate fixed report descriptor */ + rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc); + rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); + if (!rdesc) { + rc = -ENOMEM; + goto cleanup; + } + + memcpy(rdesc, drvdata->rdesc, drvdata->rsize); + + /* Append the buttonpad descriptor */ + memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc, + sizeof(uclogic_buttonpad_rdesc)); + + /* clean up old rdesc and use the new one */ + drvdata->rsize = rdesc_len; + devm_kfree(&hdev->dev, drvdata->rdesc); + drvdata->rdesc = rdesc; + + rc = 0; + +cleanup: + kfree(str_buf); + return rc; +} + static int uclogic_probe(struct hid_device *hdev, const struct hid_device_id *id) { int rc; struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + struct usb_device *udev = hid_to_usb_dev(hdev); struct uclogic_drvdata *drvdata; /* @@ -899,6 +957,10 @@ static int uclogic_probe(struct hid_device *hdev, switch (id->product) { case USB_DEVICE_ID_HUION_TABLET: + case USB_DEVICE_ID_YIYNOVA_TABLET: + case USB_DEVICE_ID_UGEE_TABLET_81: + case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: + case USB_DEVICE_ID_UGEE_TABLET_45: /* If this is the pen interface */ if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { rc = uclogic_tablet_enable(hdev); @@ -907,10 +969,48 @@ static int uclogic_probe(struct hid_device *hdev, return rc; } drvdata->invert_pen_inrange = true; + + rc = uclogic_button_enable(hdev); + drvdata->has_virtual_pad_interface = !rc; } else { drvdata->ignore_pen_usage = true; } break; + case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: + /* If this is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + } else { + drvdata->ignore_pen_usage = true; + } + break; + case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: + /* + * If it is the three-interface version, which is known to + * respond to initialization. + */ + if (udev->config->desc.bNumInterfaces == 3) { + /* If it is the pen interface */ + if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { + rc = uclogic_tablet_enable(hdev); + if (rc) { + hid_err(hdev, "tablet enabling failed\n"); + return rc; + } + drvdata->invert_pen_inrange = true; + + rc = uclogic_button_enable(hdev); + drvdata->has_virtual_pad_interface = !rc; + } else { + drvdata->ignore_pen_usage = true; + } + } + break; } rc = hid_parse(hdev); @@ -933,12 +1033,16 @@ static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, { struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); - if ((drvdata->invert_pen_inrange) && - (report->type == HID_INPUT_REPORT) && + if ((report->type == HID_INPUT_REPORT) && (report->id == UCLOGIC_PEN_REPORT_ID) && - (size >= 2)) - /* Invert the in-range bit */ - data[1] ^= 0x40; + (size >= 2)) { + if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) + /* Change to virtual frame button report ID */ + data[0] = 0xf7; + else if (drvdata->invert_pen_inrange) + /* Invert the in-range bit */ + data[1] ^= 0x40; + } return 0; } @@ -960,6 +1064,11 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, { } }; MODULE_DEVICE_TABLE(hid, uclogic_devices); diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c index 059931d7b392..a91aabe4a70a 100644 --- a/drivers/hid/hid-waltop.c +++ b/drivers/hid/hid-waltop.c @@ -42,11 +42,6 @@ * 02 16 02 ink */ -/* - * See Slim Tablet 5.8 inch description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_5.8%22 - */ - /* Size of the original report descriptor of Slim Tablet 5.8 inch */ #define SLIM_TABLET_5_8_INCH_RDESC_ORIG_SIZE 222 @@ -98,11 +93,6 @@ static __u8 slim_tablet_5_8_inch_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See Slim Tablet 12.1 inch description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Slim_Tablet_12.1%22 - */ - /* Size of the original report descriptor of Slim Tablet 12.1 inch */ #define SLIM_TABLET_12_1_INCH_RDESC_ORIG_SIZE 269 @@ -154,11 +144,6 @@ static __u8 slim_tablet_12_1_inch_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See Q Pad description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Q_Pad - */ - /* Size of the original report descriptor of Q Pad */ #define Q_PAD_RDESC_ORIG_SIZE 241 @@ -210,11 +195,6 @@ static __u8 q_pad_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See description, device and HID report descriptors of tablet with PID 0038 at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_PID_0038 - */ - /* Size of the original report descriptor of tablet with PID 0038 */ #define PID_0038_RDESC_ORIG_SIZE 241 @@ -268,11 +248,6 @@ static __u8 pid_0038_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See Media Tablet 10.6 inch description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_10.6%22 - */ - /* Size of the original report descriptor of Media Tablet 10.6 inch */ #define MEDIA_TABLET_10_6_INCH_RDESC_ORIG_SIZE 300 @@ -386,11 +361,6 @@ static __u8 media_tablet_10_6_inch_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See Media Tablet 14.1 inch description, device and HID report descriptors at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Media_Tablet_14.1%22 - */ - /* Size of the original report descriptor of Media Tablet 14.1 inch */ #define MEDIA_TABLET_14_1_INCH_RDESC_ORIG_SIZE 309 @@ -502,12 +472,6 @@ static __u8 media_tablet_14_1_inch_rdesc_fixed[] = { 0xC0 /* End Collection */ }; -/* - * See Sirius Battery Free Tablet description, device and HID report descriptors - * at - * http://sf.net/apps/mediawiki/digimend/?title=Waltop_Sirius_Battery_Free_Tablet - */ - /* Size of the original report descriptor of Sirius Battery Free Tablet */ #define SIRIUS_BATTERY_FREE_TABLET_RDESC_ORIG_SIZE 335 diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index b4b8c6abb03e..0a0eca5da47d 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -76,6 +76,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K95RGB, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_STRAFE, HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_CREATIVELABS, USB_DEVICE_ID_CREATIVE_SB_OMNI_SURROUND_51, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET }, { USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_WIIU, HID_QUIRK_MULTI_INPUT }, @@ -98,6 +99,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_2, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_3_JP, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_PRO_4_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, @@ -143,7 +145,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_V2, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_PENSKETCH_M912, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 4681a65a4579..b4800ea891cb 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -90,6 +90,8 @@ #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/hid.h> +#include <linux/kfifo.h> +#include <linux/leds.h> #include <linux/usb/input.h> #include <linux/power_supply.h> #include <asm/unaligned.h> @@ -105,32 +107,95 @@ #define USB_VENDOR_ID_WACOM 0x056a #define USB_VENDOR_ID_LENOVO 0x17ef +enum wacom_worker { + WACOM_WORKER_WIRELESS, + WACOM_WORKER_BATTERY, + WACOM_WORKER_REMOTE, +}; + +struct wacom; + +struct wacom_led { + struct led_classdev cdev; + struct led_trigger trigger; + struct wacom *wacom; + unsigned int group; + unsigned int id; + u8 llv; + u8 hlv; + bool held; +}; + +struct wacom_group_leds { + u8 select; /* status led selector (0..3) */ + struct wacom_led *leds; + unsigned int count; + struct device *dev; +}; + +struct wacom_battery { + struct wacom *wacom; + struct power_supply_desc bat_desc; + struct power_supply *battery; + char bat_name[WACOM_NAME_MAX]; + int battery_capacity; + int bat_charging; + int bat_connected; + int ps_connected; +}; + +struct wacom_remote { + spinlock_t remote_lock; + struct kfifo remote_fifo; + struct kobject *remote_dir; + struct { + struct attribute_group group; + u32 serial; + struct input_dev *input; + bool registered; + struct wacom_battery battery; + } remotes[WACOM_MAX_REMOTES]; +}; + struct wacom { struct usb_device *usbdev; struct usb_interface *intf; struct wacom_wac wacom_wac; struct hid_device *hdev; struct mutex lock; - struct work_struct work; - struct wacom_led { - u8 select[5]; /* status led selector (0..3) */ + struct work_struct wireless_work; + struct work_struct battery_work; + struct work_struct remote_work; + struct wacom_remote *remote; + struct wacom_leds { + struct wacom_group_leds *groups; + unsigned int count; u8 llv; /* status led brightness no button (1..127) */ u8 hlv; /* status led brightness button pressed (1..127) */ u8 img_lum; /* OLED matrix display brightness */ + u8 max_llv; /* maximum brightness of LED (llv) */ + u8 max_hlv; /* maximum brightness of LED (hlv) */ } led; - bool led_initialized; - struct power_supply *battery; - struct power_supply *ac; - struct power_supply_desc battery_desc; - struct power_supply_desc ac_desc; - struct kobject *remote_dir; - struct attribute_group remote_group[5]; + struct wacom_battery battery; + bool resources; }; -static inline void wacom_schedule_work(struct wacom_wac *wacom_wac) +static inline void wacom_schedule_work(struct wacom_wac *wacom_wac, + enum wacom_worker which) { struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - schedule_work(&wacom->work); + + switch (which) { + case WACOM_WORKER_WIRELESS: + schedule_work(&wacom->wireless_work); + break; + case WACOM_WORKER_BATTERY: + schedule_work(&wacom->battery_work); + break; + case WACOM_WORKER_REMOTE: + schedule_work(&wacom->remote_work); + break; + } } extern const struct hid_device_id wacom_ids[]; @@ -149,7 +214,8 @@ int wacom_wac_event(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage, __s32 value); void wacom_wac_report(struct hid_device *hdev, struct hid_report *report); void wacom_battery_work(struct work_struct *work); -int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, - int index); -void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial); +enum led_brightness wacom_leds_brightness_get(struct wacom_led *led); +struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group, + unsigned int id); +struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur); #endif diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 499cc8213cfe..5e7a5648e708 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -25,7 +25,6 @@ #define WAC_CMD_RETRIES 10 #define WAC_CMD_DELETE_PAIRING 0x20 #define WAC_CMD_UNPAIR_ALL 0xFF -#define WAC_REMOTE_SERIAL_MAX_STRLEN 9 #define DEV_ATTR_RW_PERM (S_IRUGO | S_IWUSR | S_IWGRP) #define DEV_ATTR_WO_PERM (S_IWUSR | S_IWGRP) @@ -91,7 +90,12 @@ static void wacom_close(struct input_dev *dev) { struct wacom *wacom = input_get_drvdata(dev); - hid_hw_close(wacom->hdev); + /* + * wacom->hdev should never be null, but surprisingly, I had the case + * once while unplugging the Wacom Wireless Receiver. + */ + if (wacom->hdev) + hid_hw_close(wacom->hdev); } /* @@ -523,36 +527,95 @@ struct wacom_hdev_data { static LIST_HEAD(wacom_udev_list); static DEFINE_MUTEX(wacom_udev_list_lock); +static bool 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); +} + static bool wacom_are_sibling(struct hid_device *hdev, struct hid_device *sibling) { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - int vid = features->oVid; - int pid = features->oPid; - int n1,n2; + struct wacom *sibling_wacom = hid_get_drvdata(sibling); + struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features; + __u32 oVid = features->oVid ? features->oVid : hdev->vendor; + __u32 oPid = features->oPid ? features->oPid : hdev->product; + + /* The defined oVid/oPid must match that of the sibling */ + if (features->oVid != HID_ANY_ID && sibling->vendor != oVid) + return false; + if (features->oPid != HID_ANY_ID && sibling->product != oPid) + return false; - if (vid == 0 && pid == 0) { - vid = hdev->vendor; - pid = hdev->product; + /* + * Devices with the same VID/PID must share the same physical + * device path, while those with different VID/PID must share + * the same physical parent device path. + */ + if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) { + if (!compare_device_paths(hdev, sibling, '/')) + return false; + } else { + if (!compare_device_paths(hdev, sibling, '.')) + return false; } - if (vid != sibling->vendor || pid != sibling->product) + /* Skip the remaining heuristics unless you are a HID_GENERIC device */ + if (features->type != HID_GENERIC) + return true; + + /* + * Direct-input devices may not be siblings of indirect-input + * devices. + */ + if ((features->device_type & WACOM_DEVICETYPE_DIRECT) && + !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) return false; - /* Compare the physical path. */ - n1 = strrchr(hdev->phys, '.') - hdev->phys; - n2 = strrchr(sibling->phys, '.') - sibling->phys; - if (n1 != n2 || n1 <= 0 || n2 <= 0) + /* + * Indirect-input devices may not be siblings of direct-input + * devices. + */ + if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) && + (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT)) + return false; + + /* Pen devices may only be siblings of touch devices */ + if ((features->device_type & WACOM_DEVICETYPE_PEN) && + !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH)) return false; - return !strncmp(hdev->phys, sibling->phys, n1); + /* Touch devices may only be siblings of pen devices */ + if ((features->device_type & WACOM_DEVICETYPE_TOUCH) && + !(sibling_features->device_type & WACOM_DEVICETYPE_PEN)) + return false; + + /* + * No reason could be found for these two devices to NOT be + * siblings, so there's a good chance they ARE siblings + */ + return true; } static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) { struct wacom_hdev_data *data; + /* Try to find an already-probed interface from the same device */ + list_for_each_entry(data, &wacom_udev_list, list) { + if (compare_device_paths(hdev, data->dev, '/')) + return data; + } + + /* Fallback to finding devices that appear to be "siblings" */ list_for_each_entry(data, &wacom_udev_list, list) { if (wacom_are_sibling(hdev, data->dev)) { kref_get(&data->kref); @@ -563,6 +626,38 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev) return NULL; } +static void wacom_release_shared_data(struct kref *kref) +{ + struct wacom_hdev_data *data = + container_of(kref, struct wacom_hdev_data, kref); + + mutex_lock(&wacom_udev_list_lock); + list_del(&data->list); + mutex_unlock(&wacom_udev_list_lock); + + kfree(data); +} + +static void wacom_remove_shared_data(void *res) +{ + struct wacom *wacom = res; + struct wacom_hdev_data *data; + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + if (wacom_wac->shared) { + data = container_of(wacom_wac->shared, struct wacom_hdev_data, + shared); + + if (wacom_wac->shared->touch == wacom->hdev) + wacom_wac->shared->touch = NULL; + else if (wacom_wac->shared->pen == wacom->hdev) + wacom_wac->shared->pen = NULL; + + kref_put(&data->kref, wacom_release_shared_data); + wacom_wac->shared = NULL; + } +} + static int wacom_add_shared_data(struct hid_device *hdev) { struct wacom *wacom = hid_get_drvdata(hdev); @@ -587,6 +682,13 @@ static int wacom_add_shared_data(struct hid_device *hdev) wacom_wac->shared = &data->shared; + retval = devm_add_action(&hdev->dev, wacom_remove_shared_data, wacom); + if (retval) { + mutex_unlock(&wacom_udev_list_lock); + wacom_remove_shared_data(wacom); + return retval; + } + if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) wacom_wac->shared->touch = hdev; else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) @@ -597,37 +699,6 @@ out: return retval; } -static void wacom_release_shared_data(struct kref *kref) -{ - struct wacom_hdev_data *data = - container_of(kref, struct wacom_hdev_data, kref); - - mutex_lock(&wacom_udev_list_lock); - list_del(&data->list); - mutex_unlock(&wacom_udev_list_lock); - - kfree(data); -} - -static void wacom_remove_shared_data(struct wacom *wacom) -{ - struct wacom_hdev_data *data; - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - - if (wacom_wac->shared) { - data = container_of(wacom_wac->shared, struct wacom_hdev_data, - shared); - - if (wacom_wac->shared->touch == wacom->hdev) - wacom_wac->shared->touch = NULL; - else if (wacom_wac->shared->pen == wacom->hdev) - wacom_wac->shared->pen = NULL; - - kref_put(&data->kref, wacom_release_shared_data); - wacom_wac->shared = NULL; - } -} - static int wacom_led_control(struct wacom *wacom) { unsigned char *buf; @@ -635,6 +706,12 @@ static int wacom_led_control(struct wacom *wacom) unsigned char report_id = WAC_CMD_LED_CONTROL; int buf_size = 9; + if (!hid_get_drvdata(wacom->hdev)) + return -ENODEV; + + if (!wacom->led.groups) + return -ENOTSUPP; + if (wacom->wacom_wac.pid) { /* wireless connected */ report_id = WAC_CMD_WL_LED_CONTROL; buf_size = 13; @@ -650,7 +727,7 @@ static int wacom_led_control(struct wacom *wacom) * one of four values: * 0 = Low; 1 = Medium; 2 = High; 3 = Off */ - int ring_led = wacom->led.select[0] & 0x03; + int ring_led = wacom->led.groups[0].select & 0x03; int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03; int crop_lum = 0; unsigned char led_bits = (crop_lum << 4) | (ring_lum << 2) | (ring_led); @@ -665,11 +742,11 @@ static int wacom_led_control(struct wacom *wacom) buf[1] = led_bits; } else { - int led = wacom->led.select[0] | 0x4; + int led = wacom->led.groups[0].select | 0x4; if (wacom->wacom_wac.features.type == WACOM_21UX2 || wacom->wacom_wac.features.type == WACOM_24HD) - led |= (wacom->led.select[1] << 4) | 0x40; + led |= (wacom->led.groups[1].select << 4) | 0x40; buf[0] = report_id; buf[1] = led; @@ -741,7 +818,7 @@ static ssize_t wacom_led_select_store(struct device *dev, int set_id, mutex_lock(&wacom->lock); - wacom->led.select[set_id] = id & 0x3; + wacom->led.groups[set_id].select = id & 0x3; err = wacom_led_control(wacom); mutex_unlock(&wacom->lock); @@ -761,7 +838,7 @@ static ssize_t wacom_led##SET_ID##_select_show(struct device *dev, \ struct hid_device *hdev = to_hid_device(dev);\ struct wacom *wacom = hid_get_drvdata(hdev); \ return scnprintf(buf, PAGE_SIZE, "%d\n", \ - wacom->led.select[SET_ID]); \ + wacom->led.groups[SET_ID].select); \ } \ static DEVICE_ATTR(status_led##SET_ID##_select, DEV_ATTR_RW_PERM, \ wacom_led##SET_ID##_select_show, \ @@ -904,6 +981,327 @@ static struct attribute_group intuos5_led_attr_group = { .attrs = intuos5_led_attrs, }; +struct wacom_sysfs_group_devres { + struct attribute_group *group; + struct kobject *root; +}; + +static void wacom_devm_sysfs_group_release(struct device *dev, void *res) +{ + struct wacom_sysfs_group_devres *devres = res; + struct kobject *kobj = devres->root; + + dev_dbg(dev, "%s: dropping reference to %s\n", + __func__, devres->group->name); + sysfs_remove_group(kobj, devres->group); +} + +static int __wacom_devm_sysfs_create_group(struct wacom *wacom, + struct kobject *root, + struct attribute_group *group) +{ + struct wacom_sysfs_group_devres *devres; + int error; + + devres = devres_alloc(wacom_devm_sysfs_group_release, + sizeof(struct wacom_sysfs_group_devres), + GFP_KERNEL); + if (!devres) + return -ENOMEM; + + devres->group = group; + devres->root = root; + + error = sysfs_create_group(devres->root, group); + if (error) + return error; + + devres_add(&wacom->hdev->dev, devres); + + return 0; +} + +static int wacom_devm_sysfs_create_group(struct wacom *wacom, + struct attribute_group *group) +{ + return __wacom_devm_sysfs_create_group(wacom, &wacom->hdev->dev.kobj, + group); +} + +enum led_brightness wacom_leds_brightness_get(struct wacom_led *led) +{ + struct wacom *wacom = led->wacom; + + if (wacom->led.max_hlv) + return led->hlv * LED_FULL / wacom->led.max_hlv; + + if (wacom->led.max_llv) + return led->llv * LED_FULL / wacom->led.max_llv; + + /* device doesn't support brightness tuning */ + return LED_FULL; +} + +static enum led_brightness __wacom_led_brightness_get(struct led_classdev *cdev) +{ + struct wacom_led *led = container_of(cdev, struct wacom_led, cdev); + struct wacom *wacom = led->wacom; + + if (wacom->led.groups[led->group].select != led->id) + return LED_OFF; + + return wacom_leds_brightness_get(led); +} + +static int wacom_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct wacom_led *led = container_of(cdev, struct wacom_led, cdev); + struct wacom *wacom = led->wacom; + int error; + + mutex_lock(&wacom->lock); + + if (!wacom->led.groups || (brightness == LED_OFF && + wacom->led.groups[led->group].select != led->id)) { + error = 0; + goto out; + } + + led->llv = wacom->led.llv = wacom->led.max_llv * brightness / LED_FULL; + led->hlv = wacom->led.hlv = wacom->led.max_hlv * brightness / LED_FULL; + + wacom->led.groups[led->group].select = led->id; + + error = wacom_led_control(wacom); + +out: + mutex_unlock(&wacom->lock); + + return error; +} + +static void wacom_led_readonly_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ +} + +static int wacom_led_register_one(struct device *dev, struct wacom *wacom, + struct wacom_led *led, unsigned int group, + unsigned int id, bool read_only) +{ + int error; + char *name; + + name = devm_kasprintf(dev, GFP_KERNEL, + "%s::wacom-%d.%d", + dev_name(dev), + group, + id); + if (!name) + return -ENOMEM; + + if (!read_only) { + led->trigger.name = name; + error = devm_led_trigger_register(dev, &led->trigger); + if (error) { + hid_err(wacom->hdev, + "failed to register LED trigger %s: %d\n", + led->cdev.name, error); + return error; + } + } + + led->group = group; + led->id = id; + led->wacom = wacom; + led->llv = wacom->led.llv; + led->hlv = wacom->led.hlv; + led->cdev.name = name; + led->cdev.max_brightness = LED_FULL; + led->cdev.flags = LED_HW_PLUGGABLE; + led->cdev.brightness_get = __wacom_led_brightness_get; + if (!read_only) { + led->cdev.brightness_set_blocking = wacom_led_brightness_set; + led->cdev.default_trigger = led->cdev.name; + } else { + led->cdev.brightness_set = wacom_led_readonly_brightness_set; + } + + error = devm_led_classdev_register(dev, &led->cdev); + if (error) { + hid_err(wacom->hdev, + "failed to register LED %s: %d\n", + led->cdev.name, error); + led->cdev.name = NULL; + return error; + } + + return 0; +} + +static void wacom_led_groups_release_one(void *data) +{ + struct wacom_group_leds *group = data; + + devres_release_group(group->dev, group); +} + +static int wacom_led_groups_alloc_and_register_one(struct device *dev, + struct wacom *wacom, + int group_id, int count, + bool read_only) +{ + struct wacom_led *leds; + int i, error; + + if (group_id >= wacom->led.count || count <= 0) + return -EINVAL; + + if (!devres_open_group(dev, &wacom->led.groups[group_id], GFP_KERNEL)) + return -ENOMEM; + + leds = devm_kzalloc(dev, sizeof(struct wacom_led) * count, GFP_KERNEL); + if (!leds) { + error = -ENOMEM; + goto err; + } + + wacom->led.groups[group_id].leds = leds; + wacom->led.groups[group_id].count = count; + + for (i = 0; i < count; i++) { + error = wacom_led_register_one(dev, wacom, &leds[i], + group_id, i, read_only); + if (error) + goto err; + } + + wacom->led.groups[group_id].dev = dev; + + devres_close_group(dev, &wacom->led.groups[group_id]); + + /* + * There is a bug (?) in devm_led_classdev_register() in which its + * increments the refcount of the parent. If the parent is an input + * device, that means the ref count never reaches 0 when + * devm_input_device_release() gets called. + * This means that the LEDs are still there after disconnect. + * Manually force the release of the group so that the leds are released + * once we are done using them. + */ + error = devm_add_action_or_reset(&wacom->hdev->dev, + wacom_led_groups_release_one, + &wacom->led.groups[group_id]); + if (error) + return error; + + return 0; + +err: + devres_release_group(dev, &wacom->led.groups[group_id]); + return error; +} + +struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group_id, + unsigned int id) +{ + struct wacom_group_leds *group; + + if (group_id >= wacom->led.count) + return NULL; + + group = &wacom->led.groups[group_id]; + + if (!group->leds) + return NULL; + + id %= group->count; + + return &group->leds[id]; +} + +/** + * wacom_led_next: gives the next available led with a wacom trigger. + * + * returns the next available struct wacom_led which has its default trigger + * or the current one if none is available. + */ +struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur) +{ + struct wacom_led *next_led; + int group, next; + + if (!wacom || !cur) + return NULL; + + group = cur->group; + next = cur->id; + + do { + next_led = wacom_led_find(wacom, group, ++next); + if (!next_led || next_led == cur) + return next_led; + } while (next_led->cdev.trigger != &next_led->trigger); + + return next_led; +} + +static void wacom_led_groups_release(void *data) +{ + struct wacom *wacom = data; + + wacom->led.groups = NULL; + wacom->led.count = 0; +} + +static int wacom_led_groups_allocate(struct wacom *wacom, int count) +{ + struct device *dev = &wacom->hdev->dev; + struct wacom_group_leds *groups; + int error; + + groups = devm_kzalloc(dev, sizeof(struct wacom_group_leds) * count, + GFP_KERNEL); + if (!groups) + return -ENOMEM; + + error = devm_add_action_or_reset(dev, wacom_led_groups_release, wacom); + if (error) + return error; + + wacom->led.groups = groups; + wacom->led.count = count; + + return 0; +} + +static int wacom_leds_alloc_and_register(struct wacom *wacom, int group_count, + int led_per_group, bool read_only) +{ + struct device *dev; + int i, error; + + if (!wacom->wacom_wac.pad_input) + return -EINVAL; + + dev = &wacom->wacom_wac.pad_input->dev; + + error = wacom_led_groups_allocate(wacom, group_count); + if (error) + return error; + + for (i = 0; i < group_count; i++) { + error = wacom_led_groups_alloc_and_register_one(dev, wacom, i, + led_per_group, + read_only); + if (error) + return error; + } + + return 0; +} + static int wacom_initialize_leds(struct wacom *wacom) { int error; @@ -917,25 +1315,38 @@ static int wacom_initialize_leds(struct wacom *wacom) case INTUOS4: case INTUOS4WL: case INTUOS4L: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; wacom->led.llv = 10; wacom->led.hlv = 20; + wacom->led.max_llv = 127; + wacom->led.max_hlv = 127; wacom->led.img_lum = 10; - error = sysfs_create_group(&wacom->hdev->dev.kobj, - &intuos4_led_attr_group); + + error = wacom_leds_alloc_and_register(wacom, 1, 4, false); + if (error) { + hid_err(wacom->hdev, + "cannot create leds err: %d\n", error); + return error; + } + + error = wacom_devm_sysfs_create_group(wacom, + &intuos4_led_attr_group); break; case WACOM_24HD: case WACOM_21UX2: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; wacom->led.llv = 0; wacom->led.hlv = 0; wacom->led.img_lum = 0; - error = sysfs_create_group(&wacom->hdev->dev.kobj, - &cintiq_led_attr_group); + error = wacom_leds_alloc_and_register(wacom, 2, 4, false); + if (error) { + hid_err(wacom->hdev, + "cannot create leds err: %d\n", error); + return error; + } + + error = wacom_devm_sysfs_create_group(wacom, + &cintiq_led_attr_group); break; case INTUOS5S: @@ -944,16 +1355,31 @@ static int wacom_initialize_leds(struct wacom *wacom) case INTUOSPS: case INTUOSPM: case INTUOSPL: - wacom->led.select[0] = 0; - wacom->led.select[1] = 0; wacom->led.llv = 32; - wacom->led.hlv = 0; - wacom->led.img_lum = 0; + wacom->led.max_llv = 96; + + error = wacom_leds_alloc_and_register(wacom, 1, 4, false); + if (error) { + hid_err(wacom->hdev, + "cannot create leds err: %d\n", error); + return error; + } - error = sysfs_create_group(&wacom->hdev->dev.kobj, - &intuos5_led_attr_group); + error = wacom_devm_sysfs_create_group(wacom, + &intuos5_led_attr_group); break; + case REMOTE: + wacom->led.llv = 255; + wacom->led.max_llv = 255; + error = wacom_led_groups_allocate(wacom, 5); + if (error) { + hid_err(wacom->hdev, + "cannot create leds err: %d\n", error); + return error; + } + return 0; + default: return 0; } @@ -964,86 +1390,45 @@ static int wacom_initialize_leds(struct wacom *wacom) return error; } wacom_led_control(wacom); - wacom->led_initialized = true; return 0; } -static void wacom_destroy_leds(struct wacom *wacom) -{ - if (!wacom->led_initialized) - return; - - if (!(wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD)) - return; - - wacom->led_initialized = false; - - switch (wacom->wacom_wac.features.type) { - case INTUOS4S: - case INTUOS4: - case INTUOS4WL: - case INTUOS4L: - sysfs_remove_group(&wacom->hdev->dev.kobj, - &intuos4_led_attr_group); - break; - - case WACOM_24HD: - case WACOM_21UX2: - sysfs_remove_group(&wacom->hdev->dev.kobj, - &cintiq_led_attr_group); - break; - - case INTUOS5S: - case INTUOS5: - case INTUOS5L: - case INTUOSPS: - case INTUOSPM: - case INTUOSPL: - sysfs_remove_group(&wacom->hdev->dev.kobj, - &intuos5_led_attr_group); - break; - } -} - static enum power_supply_property wacom_battery_props[] = { + POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_CAPACITY }; -static enum power_supply_property wacom_ac_props[] = { - POWER_SUPPLY_PROP_PRESENT, - POWER_SUPPLY_PROP_ONLINE, - POWER_SUPPLY_PROP_SCOPE, -}; - static int wacom_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct wacom *wacom = power_supply_get_drvdata(psy); + struct wacom_battery *battery = power_supply_get_drvdata(psy); int ret = 0; switch (psp) { + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = battery->wacom->wacom_wac.name; + break; case POWER_SUPPLY_PROP_PRESENT: - val->intval = wacom->wacom_wac.bat_connected; + val->intval = battery->bat_connected; break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = - wacom->wacom_wac.battery_capacity; + val->intval = battery->battery_capacity; break; case POWER_SUPPLY_PROP_STATUS: - if (wacom->wacom_wac.bat_charging) + if (battery->bat_charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; - else if (wacom->wacom_wac.battery_capacity == 100 && - wacom->wacom_wac.ps_connected) + else if (battery->battery_capacity == 100 && + battery->ps_connected) val->intval = POWER_SUPPLY_STATUS_FULL; - else if (wacom->wacom_wac.ps_connected) + else if (battery->ps_connected) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; else val->intval = POWER_SUPPLY_STATUS_DISCHARGING; @@ -1056,84 +1441,64 @@ static int wacom_battery_get_property(struct power_supply *psy, return ret; } -static int wacom_ac_get_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) +static int __wacom_initialize_battery(struct wacom *wacom, + struct wacom_battery *battery) { - struct wacom *wacom = power_supply_get_drvdata(psy); - int ret = 0; + static atomic_t battery_no = ATOMIC_INIT(0); + struct device *dev = &wacom->hdev->dev; + struct power_supply_config psy_cfg = { .drv_data = battery, }; + struct power_supply *ps_bat; + struct power_supply_desc *bat_desc = &battery->bat_desc; + unsigned long n; + int error; - switch (psp) { - case POWER_SUPPLY_PROP_PRESENT: - /* fall through */ - case POWER_SUPPLY_PROP_ONLINE: - val->intval = wacom->wacom_wac.ps_connected; - break; - case POWER_SUPPLY_PROP_SCOPE: - val->intval = POWER_SUPPLY_SCOPE_DEVICE; - break; - default: - ret = -EINVAL; - break; + if (!devres_open_group(dev, bat_desc, GFP_KERNEL)) + return -ENOMEM; + + battery->wacom = wacom; + + n = atomic_inc_return(&battery_no) - 1; + + bat_desc->properties = wacom_battery_props; + bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props); + bat_desc->get_property = wacom_battery_get_property; + sprintf(battery->bat_name, "wacom_battery_%ld", n); + bat_desc->name = battery->bat_name; + bat_desc->type = POWER_SUPPLY_TYPE_USB; + bat_desc->use_for_apm = 0; + + ps_bat = devm_power_supply_register(dev, bat_desc, &psy_cfg); + if (IS_ERR(ps_bat)) { + error = PTR_ERR(ps_bat); + goto err; } - return ret; + + power_supply_powers(ps_bat, &wacom->hdev->dev); + + battery->battery = ps_bat; + + devres_close_group(dev, bat_desc); + return 0; + +err: + devres_release_group(dev, bat_desc); + return error; } static int wacom_initialize_battery(struct wacom *wacom) { - static atomic_t battery_no = ATOMIC_INIT(0); - struct power_supply_config psy_cfg = { .drv_data = wacom, }; - unsigned long n; - - if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) { - struct power_supply_desc *bat_desc = &wacom->battery_desc; - struct power_supply_desc *ac_desc = &wacom->ac_desc; - n = atomic_inc_return(&battery_no) - 1; - - bat_desc->properties = wacom_battery_props; - bat_desc->num_properties = ARRAY_SIZE(wacom_battery_props); - bat_desc->get_property = wacom_battery_get_property; - sprintf(wacom->wacom_wac.bat_name, "wacom_battery_%ld", n); - bat_desc->name = wacom->wacom_wac.bat_name; - bat_desc->type = POWER_SUPPLY_TYPE_BATTERY; - bat_desc->use_for_apm = 0; - - ac_desc->properties = wacom_ac_props; - ac_desc->num_properties = ARRAY_SIZE(wacom_ac_props); - ac_desc->get_property = wacom_ac_get_property; - sprintf(wacom->wacom_wac.ac_name, "wacom_ac_%ld", n); - ac_desc->name = wacom->wacom_wac.ac_name; - ac_desc->type = POWER_SUPPLY_TYPE_MAINS; - ac_desc->use_for_apm = 0; - - wacom->battery = power_supply_register(&wacom->hdev->dev, - &wacom->battery_desc, &psy_cfg); - if (IS_ERR(wacom->battery)) - return PTR_ERR(wacom->battery); - - power_supply_powers(wacom->battery, &wacom->hdev->dev); - - wacom->ac = power_supply_register(&wacom->hdev->dev, - &wacom->ac_desc, - &psy_cfg); - if (IS_ERR(wacom->ac)) { - power_supply_unregister(wacom->battery); - return PTR_ERR(wacom->ac); - } - - power_supply_powers(wacom->ac, &wacom->hdev->dev); - } + if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) + return __wacom_initialize_battery(wacom, &wacom->battery); return 0; } static void wacom_destroy_battery(struct wacom *wacom) { - if (wacom->battery) { - power_supply_unregister(wacom->battery); - wacom->battery = NULL; - power_supply_unregister(wacom->ac); - wacom->ac = NULL; + if (wacom->battery.battery) { + devres_release_group(&wacom->hdev->dev, + &wacom->battery.bat_desc); + wacom->battery.battery = NULL; } } @@ -1179,7 +1544,7 @@ static ssize_t wacom_show_remote_mode(struct kobject *kobj, struct wacom *wacom = hid_get_drvdata(hdev); u8 mode; - mode = wacom->led.select[index]; + mode = wacom->led.groups[index].select; if (mode >= 0 && mode < 3) return snprintf(buf, PAGE_SIZE, "%d\n", mode); else @@ -1212,54 +1577,30 @@ DEVICE_EKR_ATTR_GROUP(2); DEVICE_EKR_ATTR_GROUP(3); DEVICE_EKR_ATTR_GROUP(4); -int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, int index) +static int wacom_remote_create_attr_group(struct wacom *wacom, __u32 serial, + int index) { int error = 0; - char *buf; - struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_remote *remote = wacom->remote; - wacom_wac->serial[index] = serial; - - buf = kzalloc(WAC_REMOTE_SERIAL_MAX_STRLEN, GFP_KERNEL); - if (!buf) + remote->remotes[index].group.name = devm_kasprintf(&wacom->hdev->dev, + GFP_KERNEL, + "%d", serial); + if (!remote->remotes[index].group.name) return -ENOMEM; - snprintf(buf, WAC_REMOTE_SERIAL_MAX_STRLEN, "%d", serial); - wacom->remote_group[index].name = buf; - error = sysfs_create_group(wacom->remote_dir, - &wacom->remote_group[index]); + error = __wacom_devm_sysfs_create_group(wacom, remote->remote_dir, + &remote->remotes[index].group); if (error) { + remote->remotes[index].group.name = NULL; hid_err(wacom->hdev, "cannot create sysfs group err: %d\n", error); - kobject_put(wacom->remote_dir); return error; } return 0; } -void wacom_remote_destroy_attr_group(struct wacom *wacom, __u32 serial) -{ - struct wacom_wac *wacom_wac = &wacom->wacom_wac; - int i; - - if (!serial) - return; - - for (i = 0; i < WACOM_MAX_REMOTES; i++) { - if (wacom_wac->serial[i] == serial) { - wacom_wac->serial[i] = 0; - wacom->led.select[i] = WACOM_STATUS_UNKNOWN; - if (wacom->remote_group[i].name) { - sysfs_remove_group(wacom->remote_dir, - &wacom->remote_group[i]); - kfree(wacom->remote_group[i].name); - wacom->remote_group[i].name = NULL; - } - } - } -} - static int wacom_cmd_unpair_remote(struct wacom *wacom, unsigned char selector) { const size_t buf_size = 2; @@ -1316,27 +1657,57 @@ static const struct attribute *remote_unpair_attrs[] = { NULL }; -static int wacom_initialize_remote(struct wacom *wacom) +static void wacom_remotes_destroy(void *data) +{ + struct wacom *wacom = data; + struct wacom_remote *remote = wacom->remote; + + if (!remote) + return; + + kobject_put(remote->remote_dir); + kfifo_free(&remote->remote_fifo); + wacom->remote = NULL; +} + +static int wacom_initialize_remotes(struct wacom *wacom) { int error = 0; - struct wacom_wac *wacom_wac = &(wacom->wacom_wac); + struct wacom_remote *remote; int i; if (wacom->wacom_wac.features.type != REMOTE) return 0; - wacom->remote_group[0] = remote0_serial_group; - wacom->remote_group[1] = remote1_serial_group; - wacom->remote_group[2] = remote2_serial_group; - wacom->remote_group[3] = remote3_serial_group; - wacom->remote_group[4] = remote4_serial_group; + remote = devm_kzalloc(&wacom->hdev->dev, sizeof(*wacom->remote), + GFP_KERNEL); + if (!remote) + return -ENOMEM; + + wacom->remote = remote; - wacom->remote_dir = kobject_create_and_add("wacom_remote", - &wacom->hdev->dev.kobj); - if (!wacom->remote_dir) + spin_lock_init(&remote->remote_lock); + + error = kfifo_alloc(&remote->remote_fifo, + 5 * sizeof(struct wacom_remote_data), + GFP_KERNEL); + if (error) { + hid_err(wacom->hdev, "failed allocating remote_fifo\n"); return -ENOMEM; + } - error = sysfs_create_files(wacom->remote_dir, remote_unpair_attrs); + remote->remotes[0].group = remote0_serial_group; + remote->remotes[1].group = remote1_serial_group; + remote->remotes[2].group = remote2_serial_group; + remote->remotes[3].group = remote3_serial_group; + remote->remotes[4].group = remote4_serial_group; + + remote->remote_dir = kobject_create_and_add("wacom_remote", + &wacom->hdev->dev.kobj); + if (!remote->remote_dir) + return -ENOMEM; + + error = sysfs_create_files(remote->remote_dir, remote_unpair_attrs); if (error) { hid_err(wacom->hdev, @@ -1345,10 +1716,15 @@ static int wacom_initialize_remote(struct wacom *wacom) } for (i = 0; i < WACOM_MAX_REMOTES; i++) { - wacom->led.select[i] = WACOM_STATUS_UNKNOWN; - wacom_wac->serial[i] = 0; + wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN; + remote->remotes[i].serial = 0; } + error = devm_add_action_or_reset(&wacom->hdev->dev, + wacom_remotes_destroy, wacom); + if (error) + return error; + return 0; } @@ -1358,7 +1734,7 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom) struct hid_device *hdev = wacom->hdev; struct wacom_wac *wacom_wac = &(wacom->wacom_wac); - input_dev = input_allocate_device(); + input_dev = devm_input_allocate_device(&hdev->dev); if (!input_dev) return NULL; @@ -1377,36 +1753,6 @@ static struct input_dev *wacom_allocate_input(struct wacom *wacom) return input_dev; } -static void wacom_clean_inputs(struct wacom *wacom) -{ - if (wacom->wacom_wac.pen_input) { - if (wacom->wacom_wac.pen_registered) - input_unregister_device(wacom->wacom_wac.pen_input); - else - input_free_device(wacom->wacom_wac.pen_input); - } - if (wacom->wacom_wac.touch_input) { - if (wacom->wacom_wac.touch_registered) - input_unregister_device(wacom->wacom_wac.touch_input); - else - input_free_device(wacom->wacom_wac.touch_input); - } - if (wacom->wacom_wac.pad_input) { - if (wacom->wacom_wac.pad_registered) - input_unregister_device(wacom->wacom_wac.pad_input); - else - input_free_device(wacom->wacom_wac.pad_input); - } - kobject_put(wacom->remote_dir); - wacom->wacom_wac.pen_input = NULL; - wacom->wacom_wac.touch_input = NULL; - wacom->wacom_wac.pad_input = NULL; - wacom->wacom_wac.pen_registered = false; - wacom->wacom_wac.touch_registered = false; - wacom->wacom_wac.pad_registered = false; - wacom_destroy_leds(wacom); -} - static int wacom_allocate_inputs(struct wacom *wacom) { struct wacom_wac *wacom_wac = &(wacom->wacom_wac); @@ -1414,10 +1760,10 @@ static int wacom_allocate_inputs(struct wacom *wacom) wacom_wac->pen_input = wacom_allocate_input(wacom); wacom_wac->touch_input = wacom_allocate_input(wacom); wacom_wac->pad_input = wacom_allocate_input(wacom); - if (!wacom_wac->pen_input || !wacom_wac->touch_input || !wacom_wac->pad_input) { - wacom_clean_inputs(wacom); + if (!wacom_wac->pen_input || + !wacom_wac->touch_input || + !wacom_wac->pad_input) return -ENOMEM; - } wacom_wac->pen_input->name = wacom_wac->pen_name; wacom_wac->touch_input->name = wacom_wac->touch_name; @@ -1448,8 +1794,7 @@ static int wacom_register_inputs(struct wacom *wacom) } else { error = input_register_device(pen_input_dev); if (error) - goto fail_register_pen_input; - wacom_wac->pen_registered = true; + goto fail; } error = wacom_setup_touch_input_capabilities(touch_input_dev, wacom_wac); @@ -1461,8 +1806,7 @@ static int wacom_register_inputs(struct wacom *wacom) } else { error = input_register_device(touch_input_dev); if (error) - goto fail_register_touch_input; - wacom_wac->touch_registered = true; + goto fail; } error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); @@ -1474,37 +1818,15 @@ static int wacom_register_inputs(struct wacom *wacom) } else { error = input_register_device(pad_input_dev); if (error) - goto fail_register_pad_input; - wacom_wac->pad_registered = true; - - error = wacom_initialize_leds(wacom); - if (error) - goto fail_leds; - - error = wacom_initialize_remote(wacom); - if (error) - goto fail_remote; + goto fail; } return 0; -fail_remote: - wacom_destroy_leds(wacom); -fail_leds: - input_unregister_device(pad_input_dev); - pad_input_dev = NULL; - wacom_wac->pad_registered = false; -fail_register_pad_input: - if (touch_input_dev) - input_unregister_device(touch_input_dev); +fail: + wacom_wac->pad_input = NULL; wacom_wac->touch_input = NULL; - wacom_wac->touch_registered = false; -fail_register_touch_input: - if (pen_input_dev) - input_unregister_device(pen_input_dev); wacom_wac->pen_input = NULL; - wacom_wac->pen_registered = false; -fail_register_pen_input: return error; } @@ -1543,14 +1865,14 @@ static void wacom_calculate_res(struct wacom_features *features) void wacom_battery_work(struct work_struct *work) { - struct wacom *wacom = container_of(work, struct wacom, work); + struct wacom *wacom = container_of(work, struct wacom, battery_work); if ((wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && - !wacom->battery) { + !wacom->battery.battery) { wacom_initialize_battery(wacom); } else if (!(wacom->wacom_wac.features.quirks & WACOM_QUIRK_BATTERY) && - wacom->battery) { + wacom->battery.battery) { wacom_destroy_battery(wacom); } } @@ -1606,6 +1928,9 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) strlcpy(name, features->name, sizeof(name)); } + snprintf(wacom_wac->name, sizeof(wacom_wac->name), "%s%s", + name, suffix); + /* Append the device type to the name */ snprintf(wacom_wac->pen_name, sizeof(wacom_wac->pen_name), "%s%s Pen", name, suffix); @@ -1615,6 +1940,22 @@ static void wacom_update_name(struct wacom *wacom, const char *suffix) "%s%s Pad", name, suffix); } +static void wacom_release_resources(struct wacom *wacom) +{ + struct hid_device *hdev = wacom->hdev; + + if (!wacom->resources) + return; + + devres_release_group(&hdev->dev, wacom); + + wacom->resources = false; + + wacom->wacom_wac.pen_input = NULL; + wacom->wacom_wac.touch_input = NULL; + wacom->wacom_wac.pad_input = NULL; +} + static int wacom_parse_and_register(struct wacom *wacom, bool wireless) { struct wacom_wac *wacom_wac = &wacom->wacom_wac; @@ -1627,9 +1968,14 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) if (features->pktlen > WACOM_PKGLEN_MAX) return -EINVAL; + if (!devres_open_group(&hdev->dev, wacom, GFP_KERNEL)) + return -ENOMEM; + + wacom->resources = true; + error = wacom_allocate_inputs(wacom); if (error) - return error; + goto fail; /* * Bamboo Pad has a generic hid handling for the Pen, and we switch it @@ -1642,7 +1988,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) } else if ((features->pktlen != WACOM_PKGLEN_BPAD_TOUCH) && (features->pktlen != WACOM_PKGLEN_BPAD_TOUCH_USB)) { error = -ENODEV; - goto fail_allocate_inputs; + goto fail; } } @@ -1662,7 +2008,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) error ? "Ignoring" : "Assuming pen"); if (error) - goto fail_parsed; + goto fail; features->device_type |= WACOM_DEVICETYPE_PEN; } @@ -1673,18 +2019,28 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) error = wacom_add_shared_data(hdev); if (error) - goto fail_shared_data; + goto fail; if (!(features->device_type & WACOM_DEVICETYPE_WL_MONITOR) && (features->quirks & WACOM_QUIRK_BATTERY)) { error = wacom_initialize_battery(wacom); if (error) - goto fail_battery; + goto fail; } error = wacom_register_inputs(wacom); if (error) - goto fail_register_inputs; + goto fail; + + if (wacom->wacom_wac.features.device_type & WACOM_DEVICETYPE_PAD) { + error = wacom_initialize_leds(wacom); + if (error) + goto fail; + + error = wacom_initialize_remotes(wacom); + if (error) + goto fail; + } if (features->type == HID_GENERIC) connect_mask |= HID_CONNECT_DRIVER; @@ -1693,7 +2049,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) error = hid_hw_start(hdev, connect_mask); if (error) { hid_err(hdev, "hw start failed\n"); - goto fail_hw_start; + goto fail; } if (!wireless) { @@ -1705,7 +2061,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) if ((features->type == BAMBOO_TOUCH) && (features->device_type & WACOM_DEVICETYPE_PEN)) { error = -ENODEV; - goto fail_hw_start; + goto fail_quirks; } /* pen only Bamboo neither support touch nor pad */ @@ -1713,37 +2069,33 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless) ((features->device_type & WACOM_DEVICETYPE_TOUCH) || (features->device_type & WACOM_DEVICETYPE_PAD))) { error = -ENODEV; - goto fail_hw_start; + goto fail_quirks; } if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) error = hid_hw_open(hdev); if ((wacom_wac->features.type == INTUOSHT || - wacom_wac->features.type == INTUOSHT2) && + wacom_wac->features.type == INTUOSHT2) && (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)) { - wacom_wac->shared->touch_input = wacom_wac->touch_input; + wacom_wac->shared->type = wacom_wac->features.type; + wacom_wac->shared->touch_input = wacom_wac->touch_input; } + devres_close_group(&hdev->dev, wacom); + return 0; -fail_hw_start: +fail_quirks: hid_hw_stop(hdev); -fail_register_inputs: - wacom_clean_inputs(wacom); - wacom_destroy_battery(wacom); -fail_battery: - wacom_remove_shared_data(wacom); -fail_shared_data: -fail_parsed: -fail_allocate_inputs: - wacom_clean_inputs(wacom); +fail: + wacom_release_resources(wacom); return error; } static void wacom_wireless_work(struct work_struct *work) { - struct wacom *wacom = container_of(work, struct wacom, work); + struct wacom *wacom = container_of(work, struct wacom, wireless_work); struct usb_device *usbdev = wacom->usbdev; struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct hid_device *hdev1, *hdev2; @@ -1762,17 +2114,16 @@ static void wacom_wireless_work(struct work_struct *work) hdev1 = usb_get_intfdata(usbdev->config->interface[1]); wacom1 = hid_get_drvdata(hdev1); wacom_wac1 = &(wacom1->wacom_wac); - wacom_clean_inputs(wacom1); + wacom_release_resources(wacom1); /* Touch interface */ hdev2 = usb_get_intfdata(usbdev->config->interface[2]); wacom2 = hid_get_drvdata(hdev2); wacom_wac2 = &(wacom2->wacom_wac); - wacom_clean_inputs(wacom2); + wacom_release_resources(wacom2); if (wacom_wac->pid == 0) { hid_info(wacom->hdev, "wireless tablet disconnected\n"); - wacom_wac1->shared->type = 0; } else { const struct hid_device_id *id = wacom_ids; @@ -1814,6 +2165,8 @@ static void wacom_wireless_work(struct work_struct *work) goto fail; } + strlcpy(wacom_wac->name, wacom_wac1->name, + sizeof(wacom_wac->name)); error = wacom_initialize_battery(wacom); if (error) goto fail; @@ -1822,11 +2175,177 @@ static void wacom_wireless_work(struct work_struct *work) return; fail: - wacom_clean_inputs(wacom1); - wacom_clean_inputs(wacom2); + wacom_release_resources(wacom1); + wacom_release_resources(wacom2); return; } +static void wacom_remote_destroy_one(struct wacom *wacom, unsigned int index) +{ + struct wacom_remote *remote = wacom->remote; + u32 serial = remote->remotes[index].serial; + int i; + unsigned long flags; + + spin_lock_irqsave(&remote->remote_lock, flags); + remote->remotes[index].registered = false; + spin_unlock_irqrestore(&remote->remote_lock, flags); + + if (remote->remotes[index].battery.battery) + devres_release_group(&wacom->hdev->dev, + &remote->remotes[index].battery.bat_desc); + + if (remote->remotes[index].group.name) + devres_release_group(&wacom->hdev->dev, + &remote->remotes[index]); + + for (i = 0; i < WACOM_MAX_REMOTES; i++) { + if (remote->remotes[i].serial == serial) { + remote->remotes[i].serial = 0; + remote->remotes[i].group.name = NULL; + remote->remotes[i].registered = false; + remote->remotes[i].battery.battery = NULL; + wacom->led.groups[i].select = WACOM_STATUS_UNKNOWN; + } + } +} + +static int wacom_remote_create_one(struct wacom *wacom, u32 serial, + unsigned int index) +{ + struct wacom_remote *remote = wacom->remote; + struct device *dev = &wacom->hdev->dev; + int error, k; + + /* A remote can pair more than once with an EKR, + * check to make sure this serial isn't already paired. + */ + for (k = 0; k < WACOM_MAX_REMOTES; k++) { + if (remote->remotes[k].serial == serial) + break; + } + + if (k < WACOM_MAX_REMOTES) { + remote->remotes[index].serial = serial; + return 0; + } + + if (!devres_open_group(dev, &remote->remotes[index], GFP_KERNEL)) + return -ENOMEM; + + error = wacom_remote_create_attr_group(wacom, serial, index); + if (error) + goto fail; + + remote->remotes[index].input = wacom_allocate_input(wacom); + if (!remote->remotes[index].input) { + error = -ENOMEM; + goto fail; + } + remote->remotes[index].input->uniq = remote->remotes[index].group.name; + remote->remotes[index].input->name = wacom->wacom_wac.pad_name; + + if (!remote->remotes[index].input->name) { + error = -EINVAL; + goto fail; + } + + error = wacom_setup_pad_input_capabilities(remote->remotes[index].input, + &wacom->wacom_wac); + if (error) + goto fail; + + remote->remotes[index].serial = serial; + + error = input_register_device(remote->remotes[index].input); + if (error) + goto fail; + + error = wacom_led_groups_alloc_and_register_one( + &remote->remotes[index].input->dev, + wacom, index, 3, true); + if (error) + goto fail; + + remote->remotes[index].registered = true; + + devres_close_group(dev, &remote->remotes[index]); + return 0; + +fail: + devres_release_group(dev, &remote->remotes[index]); + remote->remotes[index].serial = 0; + return error; +} + +static int wacom_remote_attach_battery(struct wacom *wacom, int index) +{ + struct wacom_remote *remote = wacom->remote; + int error; + + if (!remote->remotes[index].registered) + return 0; + + if (remote->remotes[index].battery.battery) + return 0; + + if (wacom->led.groups[index].select == WACOM_STATUS_UNKNOWN) + return 0; + + error = __wacom_initialize_battery(wacom, + &wacom->remote->remotes[index].battery); + if (error) + return error; + + return 0; +} + +static void wacom_remote_work(struct work_struct *work) +{ + struct wacom *wacom = container_of(work, struct wacom, remote_work); + struct wacom_remote *remote = wacom->remote; + struct wacom_remote_data data; + unsigned long flags; + unsigned int count; + u32 serial; + int i; + + spin_lock_irqsave(&remote->remote_lock, flags); + + count = kfifo_out(&remote->remote_fifo, &data, sizeof(data)); + + if (count != sizeof(data)) { + hid_err(wacom->hdev, + "workitem triggered without status available\n"); + spin_unlock_irqrestore(&remote->remote_lock, flags); + return; + } + + if (!kfifo_is_empty(&remote->remote_fifo)) + wacom_schedule_work(&wacom->wacom_wac, WACOM_WORKER_REMOTE); + + spin_unlock_irqrestore(&remote->remote_lock, flags); + + for (i = 0; i < WACOM_MAX_REMOTES; i++) { + serial = data.remote[i].serial; + if (data.remote[i].connected) { + + if (remote->remotes[i].serial == serial) { + wacom_remote_attach_battery(wacom, i); + continue; + } + + if (remote->remotes[i].serial) + wacom_remote_destroy_one(wacom, i); + + wacom_remote_create_one(wacom, serial, i); + + } else if (remote->remotes[i].serial) { + wacom_remote_destroy_one(wacom, i); + } + } +} + static int wacom_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -1845,7 +2364,7 @@ static int wacom_probe(struct hid_device *hdev, /* hid-core sets this quirk for the boot interface */ hdev->quirks &= ~HID_QUIRK_NOGET; - wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL); + wacom = devm_kzalloc(&hdev->dev, sizeof(struct wacom), GFP_KERNEL); if (!wacom) return -ENOMEM; @@ -1858,7 +2377,7 @@ static int wacom_probe(struct hid_device *hdev, if (features->check_for_hid_type && features->hid_type != hdev->type) { error = -ENODEV; - goto fail_type; + goto fail; } wacom_wac->hid_data.inputmode = -1; @@ -1867,18 +2386,20 @@ static int wacom_probe(struct hid_device *hdev, wacom->usbdev = dev; wacom->intf = intf; mutex_init(&wacom->lock); - INIT_WORK(&wacom->work, wacom_wireless_work); + INIT_WORK(&wacom->wireless_work, wacom_wireless_work); + INIT_WORK(&wacom->battery_work, wacom_battery_work); + INIT_WORK(&wacom->remote_work, wacom_remote_work); /* ask for the report descriptor to be loaded by HID */ error = hid_parse(hdev); if (error) { hid_err(hdev, "parse failed\n"); - goto fail_parse; + goto fail; } error = wacom_parse_and_register(wacom, false); if (error) - goto fail_parse; + goto fail; if (hdev->bus == BUS_BLUETOOTH) { error = device_create_file(&hdev->dev, &dev_attr_speed); @@ -1890,9 +2411,7 @@ static int wacom_probe(struct hid_device *hdev, return 0; -fail_type: -fail_parse: - kfree(wacom); +fail: hid_set_drvdata(hdev, NULL); return error; } @@ -1908,15 +2427,13 @@ static void wacom_remove(struct hid_device *hdev) hid_hw_stop(hdev); - cancel_work_sync(&wacom->work); - wacom_clean_inputs(wacom); + cancel_work_sync(&wacom->wireless_work); + cancel_work_sync(&wacom->battery_work); + cancel_work_sync(&wacom->remote_work); if (hdev->bus == BUS_BLUETOOTH) device_remove_file(&hdev->dev, &dev_attr_speed); - wacom_destroy_battery(wacom); - wacom_remove_shared_data(wacom); hid_set_drvdata(hdev, NULL); - kfree(wacom); } #ifdef CONFIG_PM diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 1eae13cdc502..1cb79925730d 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -34,6 +34,10 @@ */ #define WACOM_CONTACT_AREA_SCALE 2607 +static bool touch_arbitration = 1; +module_param(touch_arbitration, bool, 0644); +MODULE_PARM_DESC(touch_arbitration, " on (Y) off (N)"); + static void wacom_report_numbered_buttons(struct input_dev *input_dev, int button_count, int mask); @@ -48,25 +52,34 @@ static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 }; */ static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 }; +static void __wacom_notify_battery(struct wacom_battery *battery, + int bat_capacity, bool bat_charging, + bool bat_connected, bool ps_connected) +{ + bool changed = battery->battery_capacity != bat_capacity || + battery->bat_charging != bat_charging || + battery->bat_connected != bat_connected || + battery->ps_connected != ps_connected; + + if (changed) { + battery->battery_capacity = bat_capacity; + battery->bat_charging = bat_charging; + battery->bat_connected = bat_connected; + battery->ps_connected = ps_connected; + + if (battery->battery) + power_supply_changed(battery->battery); + } +} + static void wacom_notify_battery(struct wacom_wac *wacom_wac, int bat_capacity, bool bat_charging, bool bat_connected, bool ps_connected) { struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - bool changed = wacom_wac->battery_capacity != bat_capacity || - wacom_wac->bat_charging != bat_charging || - wacom_wac->bat_connected != bat_connected || - wacom_wac->ps_connected != ps_connected; - if (changed) { - wacom_wac->battery_capacity = bat_capacity; - wacom_wac->bat_charging = bat_charging; - wacom_wac->bat_connected = bat_connected; - wacom_wac->ps_connected = ps_connected; - - if (wacom->battery) - power_supply_changed(wacom->battery); - } + __wacom_notify_battery(&wacom->battery, bat_capacity, bat_charging, + bat_connected, ps_connected); } static int wacom_penpartner_irq(struct wacom_wac *wacom) @@ -751,22 +764,37 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) { unsigned char *data = wacom_wac->data; - struct input_dev *input = wacom_wac->pad_input; + struct input_dev *input; struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); - struct wacom_features *features = &wacom_wac->features; + struct wacom_remote *remote = wacom->remote; int bat_charging, bat_percent, touch_ring_mode; __u32 serial; - int i; + int i, index = -1; + unsigned long flags; if (data[0] != WACOM_REPORT_REMOTE) { - dev_dbg(input->dev.parent, - "%s: received unknown report #%d", __func__, data[0]); + hid_dbg(wacom->hdev, "%s: received unknown report #%d", + __func__, data[0]); return 0; } serial = data[3] + (data[4] << 8) + (data[5] << 16); wacom_wac->id[0] = PAD_DEVICE_ID; + spin_lock_irqsave(&remote->remote_lock, flags); + + for (i = 0; i < WACOM_MAX_REMOTES; i++) { + if (remote->remotes[i].serial == serial) { + index = i; + break; + } + } + + if (index < 0 || !remote->remotes[index].registered) + goto out; + + input = remote->remotes[index].input; + input_report_key(input, BTN_0, (data[9] & 0x01)); input_report_key(input, BTN_1, (data[9] & 0x02)); input_report_key(input, BTN_2, (data[9] & 0x04)); @@ -803,73 +831,69 @@ static int wacom_remote_irq(struct wacom_wac *wacom_wac, size_t len) input_event(input, EV_MSC, MSC_SERIAL, serial); + input_sync(input); + /*Which mode select (LED light) is currently on?*/ touch_ring_mode = (data[11] & 0xC0) >> 6; for (i = 0; i < WACOM_MAX_REMOTES; i++) { - if (wacom_wac->serial[i] == serial) - wacom->led.select[i] = touch_ring_mode; - } - - if (!wacom->battery && - !(features->quirks & WACOM_QUIRK_BATTERY)) { - features->quirks |= WACOM_QUIRK_BATTERY; - INIT_WORK(&wacom->work, wacom_battery_work); - wacom_schedule_work(wacom_wac); + if (remote->remotes[i].serial == serial) + wacom->led.groups[i].select = touch_ring_mode; } - wacom_notify_battery(wacom_wac, bat_percent, bat_charging, 1, - bat_charging); + __wacom_notify_battery(&remote->remotes[index].battery, bat_percent, + bat_charging, 1, bat_charging); - return 1; +out: + spin_unlock_irqrestore(&remote->remote_lock, flags); + return 0; } -static int wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len) +static void wacom_remote_status_irq(struct wacom_wac *wacom_wac, size_t len) { struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac); unsigned char *data = wacom_wac->data; - int i; + struct wacom_remote *remote = wacom->remote; + struct wacom_remote_data remote_data; + unsigned long flags; + int i, ret; if (data[0] != WACOM_REPORT_DEVICE_LIST) - return 0; + return; + + memset(&remote_data, 0, sizeof(struct wacom_remote_data)); for (i = 0; i < WACOM_MAX_REMOTES; i++) { int j = i * 6; int serial = (data[j+6] << 16) + (data[j+5] << 8) + data[j+4]; bool connected = data[j+2]; - if (connected) { - int k; + remote_data.remote[i].serial = serial; + remote_data.remote[i].connected = connected; + } - if (wacom_wac->serial[i] == serial) - continue; + spin_lock_irqsave(&remote->remote_lock, flags); - if (wacom_wac->serial[i]) { - wacom_remote_destroy_attr_group(wacom, - wacom_wac->serial[i]); - } + ret = kfifo_in(&remote->remote_fifo, &remote_data, sizeof(remote_data)); + if (ret != sizeof(remote_data)) { + spin_unlock_irqrestore(&remote->remote_lock, flags); + hid_err(wacom->hdev, "Can't queue Remote status event.\n"); + return; + } - /* A remote can pair more than once with an EKR, - * check to make sure this serial isn't already paired. - */ - for (k = 0; k < WACOM_MAX_REMOTES; k++) { - if (wacom_wac->serial[k] == serial) - break; - } + spin_unlock_irqrestore(&remote->remote_lock, flags); - if (k < WACOM_MAX_REMOTES) { - wacom_wac->serial[i] = serial; - continue; - } - wacom_remote_create_attr_group(wacom, serial, i); + wacom_schedule_work(wacom_wac, WACOM_WORKER_REMOTE); +} - } else if (wacom_wac->serial[i]) { - wacom_remote_destroy_attr_group(wacom, - wacom_wac->serial[i]); - } - } +static inline bool report_touch_events(struct wacom_wac *wacom) +{ + return (touch_arbitration ? !wacom->shared->stylus_in_proximity : 1); +} - return 0; +static inline bool delay_pen_events(struct wacom_wac *wacom) +{ + return (wacom->shared->touch_down && touch_arbitration); } static int wacom_intuos_general(struct wacom_wac *wacom) @@ -885,7 +909,7 @@ static int wacom_intuos_general(struct wacom_wac *wacom) data[0] != WACOM_REPORT_INTUOS_PEN) return 0; - if (wacom->shared->touch_down) + if (delay_pen_events(wacom)) return 1; /* don't report events if we don't know the tool ID */ @@ -1145,7 +1169,7 @@ static int wacom_wac_finger_count_touches(struct wacom_wac *wacom) if (touch_max == 1) return test_bit(BTN_TOUCH, input->key) && - !wacom->shared->stylus_in_proximity; + report_touch_events(wacom); for (i = 0; i < input->mt->num_slots; i++) { struct input_mt_slot *ps = &input->mt->slots[i]; @@ -1186,7 +1210,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) for (i = 0; i < contacts_to_send; i++) { int offset = (byte_per_packet * i) + 1; - bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; + bool touch = (data[offset] & 0x1) && report_touch_events(wacom); int slot = input_mt_get_slot_by_key(input, data[offset + 1]); if (slot < 0) @@ -1250,7 +1274,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) for (i = 0; i < contacts_to_send; i++) { int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; - bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; + bool touch = (data[offset] & 0x1) && report_touch_events(wacom); int id = get_unaligned_le16(&data[offset + 1]); int slot = input_mt_get_slot_by_key(input, id); @@ -1284,7 +1308,7 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom) for (i = 0; i < 2; i++) { int p = data[1] & (1 << i); - bool touch = p && !wacom->shared->stylus_in_proximity; + bool touch = p && report_touch_events(wacom); input_mt_slot(input, i); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); @@ -1308,7 +1332,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len) { unsigned char *data = wacom->data; struct input_dev *input = wacom->touch_input; - bool prox = !wacom->shared->stylus_in_proximity; + bool prox = report_touch_events(wacom); int x = 0, y = 0; if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG) @@ -1353,8 +1377,10 @@ static int wacom_tpc_pen(struct wacom_wac *wacom) /* keep pen state for touch events */ wacom->shared->stylus_in_proximity = prox; - /* send pen events only when touch is up or forced out */ - if (!wacom->shared->touch_down) { + /* send pen events only when touch is up or forced out + * or touch arbitration is off + */ + if (!delay_pen_events(wacom)) { input_report_key(input, BTN_STYLUS, data[1] & 0x02); input_report_key(input, BTN_STYLUS2, data[1] & 0x10); input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); @@ -1496,8 +1522,10 @@ static int wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field, return 0; } - /* send pen events only when touch is up or forced out */ - if (!usage->type || wacom_wac->shared->touch_down) + /* send pen events only when touch is up or forced out + * or touch arbitration is off + */ + if (!usage->type || delay_pen_events(wacom_wac)) return 0; input_event(input, usage->type, usage->code, value); @@ -1527,8 +1555,7 @@ static void wacom_wac_pen_report(struct hid_device *hdev, /* keep pen state for touch events */ wacom_wac->shared->stylus_in_proximity = prox; - /* send pen events only when touch is up or forced out */ - if (!wacom_wac->shared->touch_down) { + if (!delay_pen_events(wacom_wac)) { input_report_key(input, BTN_TOUCH, wacom_wac->hid_data.tipswitch); input_report_key(input, wacom_wac->tool[0], prox); @@ -1544,13 +1571,11 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; - struct wacom_features *features = &wacom_wac->features; struct input_dev *input = wacom_wac->touch_input; unsigned touch_max = wacom_wac->features.touch_max; switch (usage->hid) { case HID_GD_X: - features->last_slot_field = usage->hid; if (touch_max == 1) wacom_map_usage(input, usage, field, EV_ABS, ABS_X, 4); else @@ -1558,7 +1583,6 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, ABS_MT_POSITION_X, 4); break; case HID_GD_Y: - features->last_slot_field = usage->hid; if (touch_max == 1) wacom_map_usage(input, usage, field, EV_ABS, ABS_Y, 4); else @@ -1567,22 +1591,11 @@ static void wacom_wac_finger_usage_mapping(struct hid_device *hdev, break; case HID_DG_WIDTH: case HID_DG_HEIGHT: - features->last_slot_field = usage->hid; wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MAJOR, 0); wacom_map_usage(input, usage, field, EV_ABS, ABS_MT_TOUCH_MINOR, 0); input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); break; - case HID_DG_CONTACTID: - features->last_slot_field = usage->hid; - break; - case HID_DG_INRANGE: - features->last_slot_field = usage->hid; - break; - case HID_DG_INVERT: - features->last_slot_field = usage->hid; - break; case HID_DG_TIPSWITCH: - features->last_slot_field = usage->hid; wacom_map_usage(input, usage, field, EV_KEY, BTN_TOUCH, 0); break; case HID_DG_CONTACTCOUNT: @@ -1599,7 +1612,7 @@ static void wacom_wac_finger_slot(struct wacom_wac *wacom_wac, struct hid_data *hid_data = &wacom_wac->hid_data; bool mt = wacom_wac->features.touch_max > 1; bool prox = hid_data->tipswitch && - !wacom_wac->shared->stylus_in_proximity; + report_touch_events(wacom_wac); wacom_wac->hid_data.num_received++; if (wacom_wac->hid_data.num_received > wacom_wac->hid_data.num_expected) @@ -1660,7 +1673,7 @@ static int wacom_wac_finger_event(struct hid_device *hdev, if (usage->usage_index + 1 == field->report_count) { - if (usage->hid == wacom_wac->features.last_slot_field) + if (usage->hid == wacom_wac->hid_data.last_slot_field) wacom_wac_finger_slot(wacom_wac, wacom_wac->touch_input); } @@ -1673,31 +1686,35 @@ static void wacom_wac_finger_pre_report(struct hid_device *hdev, struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; struct hid_data* hid_data = &wacom_wac->hid_data; + int i; - if (hid_data->cc_report != 0 && - hid_data->cc_report != report->id) { - int i; - - hid_data->cc_report = report->id; - hid_data->cc_index = -1; - hid_data->cc_value_index = -1; - - for (i = 0; i < report->maxfield; i++) { - struct hid_field *field = report->field[i]; - int j; - - for (j = 0; j < field->maxusage; j++) { - if (field->usage[j].hid == HID_DG_CONTACTCOUNT) { - hid_data->cc_index = i; - hid_data->cc_value_index = j; - - /* break */ - i = report->maxfield; - j = field->maxusage; - } + for (i = 0; i < report->maxfield; i++) { + struct hid_field *field = report->field[i]; + int j; + + for (j = 0; j < field->maxusage; j++) { + struct hid_usage *usage = &field->usage[j]; + + switch (usage->hid) { + case HID_GD_X: + case HID_GD_Y: + case HID_DG_WIDTH: + case HID_DG_HEIGHT: + case HID_DG_CONTACTID: + case HID_DG_INRANGE: + case HID_DG_INVERT: + case HID_DG_TIPSWITCH: + hid_data->last_slot_field = usage->hid; + break; + case HID_DG_CONTACTCOUNT: + hid_data->cc_report = report->id; + hid_data->cc_index = i; + hid_data->cc_value_index = j; + break; } } } + if (hid_data->cc_report != 0 && hid_data->cc_index >= 0) { struct hid_field *field = report->field[hid_data->cc_index]; @@ -1740,10 +1757,10 @@ void wacom_wac_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_wac *wacom_wac = &wacom->wacom_wac; + struct wacom_features *features = &wacom_wac->features; /* currently, only direct devices have proper hid report descriptors */ - __set_bit(INPUT_PROP_DIRECT, wacom_wac->pen_input->propbit); - __set_bit(INPUT_PROP_DIRECT, wacom_wac->touch_input->propbit); + features->device_type |= WACOM_DEVICETYPE_DIRECT; if (WACOM_PEN_FIELD(field)) return wacom_wac_pen_usage_mapping(hdev, field, usage); @@ -1825,15 +1842,8 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) for (i = 0; i < 2; i++) { int offset = (data[1] & 0x80) ? (8 * i) : (9 * i); - bool touch = data[offset + 3] & 0x80; - - /* - * Touch events need to be disabled while stylus is - * in proximity because user's hand is resting on touchpad - * and sending unwanted events. User expects tablet buttons - * to continue working though. - */ - touch = touch && !wacom->shared->stylus_in_proximity; + bool touch = report_touch_events(wacom) + && (data[offset + 3] & 0x80); input_mt_slot(input, i); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); @@ -1870,7 +1880,7 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) if (slot < 0) return; - touch = touch && !wacom->shared->stylus_in_proximity; + touch = touch && report_touch_events(wacom); input_mt_slot(input, slot); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); @@ -1942,7 +1952,7 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) } /* only update touch if we actually have a touchpad and touch data changed */ - if (wacom->touch_registered && touch_changed) { + if (wacom->touch_input && touch_changed) { input_mt_sync_frame(wacom->touch_input); wacom->shared->touch_down = wacom_wac_finger_count_touches(wacom); } @@ -1983,7 +1993,7 @@ static int wacom_bpt_pen(struct wacom_wac *wacom) } wacom->shared->stylus_in_proximity = prox; - if (wacom->shared->touch_down) + if (delay_pen_events(wacom)) return 0; if (prox) { @@ -2077,7 +2087,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom, for (id = 0; id < wacom->features.touch_max; id++) { valid = !!(prefix & BIT(id)) && - !wacom->shared->stylus_in_proximity; + report_touch_events(wacom); input_mt_slot(input, id); input_mt_report_slot_state(input, MT_TOOL_FINGER, valid); @@ -2099,8 +2109,7 @@ static int wacom_bamboo_pad_touch_event(struct wacom_wac *wacom, input_report_key(input, BTN_RIGHT, prefix & 0x80); /* keep touch state for pen event */ - wacom->shared->touch_down = !!prefix && - !wacom->shared->stylus_in_proximity; + wacom->shared->touch_down = !!prefix && report_touch_events(wacom); return 1; } @@ -2149,16 +2158,15 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len) charging = !!(data[5] & 0x80); if (wacom->pid != pid) { wacom->pid = pid; - wacom_schedule_work(wacom); + wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); } - if (wacom->shared->type) - wacom_notify_battery(wacom, battery, charging, 1, 0); + wacom_notify_battery(wacom, battery, charging, 1, 0); } else if (wacom->pid != 0) { /* disconnected while previously connected */ wacom->pid = 0; - wacom_schedule_work(wacom); + wacom_schedule_work(wacom, WACOM_WORKER_WIRELESS); wacom_notify_battery(wacom, 0, 0, 0, 0); } @@ -2190,18 +2198,16 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len) wacom_notify_battery(wacom_wac, battery, charging, battery || charging, 1); - if (!wacom->battery && + if (!wacom->battery.battery && !(features->quirks & WACOM_QUIRK_BATTERY)) { features->quirks |= WACOM_QUIRK_BATTERY; - INIT_WORK(&wacom->work, wacom_battery_work); - wacom_schedule_work(wacom_wac); + wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY); } } else if ((features->quirks & WACOM_QUIRK_BATTERY) && - wacom->battery) { + wacom->battery.battery) { features->quirks &= ~WACOM_QUIRK_BATTERY; - INIT_WORK(&wacom->work, wacom_battery_work); - wacom_schedule_work(wacom_wac); + wacom_schedule_work(wacom_wac, WACOM_WORKER_BATTERY); wacom_notify_battery(wacom_wac, 0, 0, 0, 0); } return 0; @@ -2312,8 +2318,9 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) break; case REMOTE: + sync = false; if (wacom_wac->data[0] == WACOM_REPORT_DEVICE_LIST) - sync = wacom_remote_status_irq(wacom_wac, len); + wacom_remote_status_irq(wacom_wac, len); else sync = wacom_remote_irq(wacom_wac, len); break; @@ -2451,6 +2458,33 @@ void wacom_setup_device_quirks(struct wacom *wacom) if (features->type == REMOTE) features->device_type = WACOM_DEVICETYPE_PAD; + switch (features->type) { + case PL: + case DTU: + case DTUS: + case DTUSX: + case WACOM_21UX2: + case WACOM_22HD: + case DTK: + case WACOM_24HD: + case WACOM_27QHD: + case CINTIQ_HYBRID: + case CINTIQ_COMPANION_2: + case CINTIQ: + case WACOM_BEE: + case WACOM_13HD: + case WACOM_24HDT: + case WACOM_27QHDT: + case TABLETPC: + case TABLETPCE: + case TABLETPC2FG: + case MTSCREEN: + case MTTPC: + case MTTPC_B: + features->device_type |= WACOM_DEVICETYPE_DIRECT; + break; + } + if (wacom->hdev->bus == BUS_BLUETOOTH) features->quirks |= WACOM_QUIRK_BATTERY; @@ -2469,6 +2503,9 @@ void wacom_setup_device_quirks(struct wacom *wacom) features->quirks |= WACOM_QUIRK_BATTERY; } } + + if (features->type == REMOTE) + features->device_type |= WACOM_DEVICETYPE_WL_MONITOR; } int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, @@ -2481,6 +2518,11 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, if (!(features->device_type & WACOM_DEVICETYPE_PEN)) return -ENODEV; + if (features->device_type & WACOM_DEVICETYPE_DIRECT) + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + else + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (features->type == HID_GENERIC) /* setup has already been done */ return 0; @@ -2499,7 +2541,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, input_abs_set_res(input_dev, ABS_X, features->x_resolution); input_abs_set_res(input_dev, ABS_Y, features->y_resolution); - switch (features->type) { case GRAPHIRE_BT: __clear_bit(ABS_MISC, input_dev->absbit); @@ -2523,8 +2564,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); break; case WACOM_27QHD: @@ -2539,7 +2578,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, case CINTIQ_COMPANION_2: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_abs_set_res(input_dev, ABS_Z, 287); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); wacom_setup_cintiq(wacom_wac); break; @@ -2555,8 +2593,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, /* fall through */ case INTUOS: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - wacom_setup_intuos(wacom_wac); break; @@ -2566,8 +2602,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, case INTUOSPL: case INTUOS5S: case INTUOSPS: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - input_set_abs_params(input_dev, ABS_DISTANCE, 0, features->distance_max, features->distance_fuzz, 0); @@ -2597,8 +2631,6 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); break; case PTU: @@ -2609,16 +2641,12 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_TOOL_PEN, input_dev->keybit); __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); break; case INTUOSHT: case BAMBOO_PT: case BAMBOO_PEN: case INTUOSHT2: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - if (features->type == INTUOSHT2) { wacom_setup_basic_pro_pen(wacom_wac); } else { @@ -2649,6 +2677,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, if (!(features->device_type & WACOM_DEVICETYPE_TOUCH)) return -ENODEV; + if (features->device_type & WACOM_DEVICETYPE_DIRECT) + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + else + __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (features->type == HID_GENERIC) /* setup has already been done */ return 0; @@ -2683,8 +2716,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, case INTUOSPL: case INTUOS5S: case INTUOSPS: - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); @@ -2707,7 +2738,6 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev, case TABLETPC: case TABLETPCE: - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); break; case INTUOSHT: @@ -2752,11 +2782,105 @@ static void wacom_setup_numbered_buttons(struct input_dev *input_dev, __set_bit(BTN_BASE + (i-16), input_dev->keybit); } +static void wacom_24hd_update_leds(struct wacom *wacom, int mask, int group) +{ + struct wacom_led *led; + int i; + bool updated = false; + + /* + * 24HD has LED group 1 to the left and LED group 0 to the right. + * So group 0 matches the second half of the buttons and thus the mask + * needs to be shifted. + */ + if (group == 0) + mask >>= 8; + + for (i = 0; i < 3; i++) { + led = wacom_led_find(wacom, group, i); + if (!led) { + hid_err(wacom->hdev, "can't find LED %d in group %d\n", + i, group); + continue; + } + if (!updated && mask & BIT(i)) { + led->held = true; + led_trigger_event(&led->trigger, LED_FULL); + } else { + led->held = false; + } + } +} + +static bool wacom_is_led_toggled(struct wacom *wacom, int button_count, + int mask, int group) +{ + int button_per_group; + + /* + * 21UX2 has LED group 1 to the left and LED group 0 + * to the right. We need to reverse the group to match this + * historical behavior. + */ + if (wacom->wacom_wac.features.type == WACOM_21UX2) + group = 1 - group; + + button_per_group = button_count/wacom->led.count; + + return mask & (1 << (group * button_per_group)); +} + +static void wacom_update_led(struct wacom *wacom, int button_count, int mask, + int group) +{ + struct wacom_led *led, *next_led; + int cur; + bool pressed; + + if (wacom->wacom_wac.features.type == WACOM_24HD) + return wacom_24hd_update_leds(wacom, mask, group); + + pressed = wacom_is_led_toggled(wacom, button_count, mask, group); + cur = wacom->led.groups[group].select; + + led = wacom_led_find(wacom, group, cur); + if (!led) { + hid_err(wacom->hdev, "can't find current LED %d in group %d\n", + cur, group); + return; + } + + if (!pressed) { + led->held = false; + return; + } + + if (led->held && pressed) + return; + + next_led = wacom_led_next(wacom, led); + if (!next_led) { + hid_err(wacom->hdev, "can't find next LED in group %d\n", + group); + return; + } + if (next_led == led) + return; + + next_led->held = true; + led_trigger_event(&next_led->trigger, + wacom_leds_brightness_get(next_led)); +} + static void wacom_report_numbered_buttons(struct input_dev *input_dev, int button_count, int mask) { + struct wacom *wacom = input_get_drvdata(input_dev); int i; + for (i = 0; i < wacom->led.count; i++) + wacom_update_led(wacom, button_count, mask, i); + for (i = 0; i < button_count && i < 10; i++) input_report_key(input_dev, BTN_0 + i, mask & (1 << i)); for (i = 10; i < button_count && i < 16; i++) @@ -2773,6 +2897,9 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, if (!(features->device_type & WACOM_DEVICETYPE_PAD)) return -ENODEV; + if (features->type == REMOTE && input_dev == wacom_wac->pad_input) + return -ENODEV; + input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); /* kept for making legacy xf86-input-wacom working with the wheels */ @@ -3403,7 +3530,7 @@ static const struct wacom_features wacom_features_0x343 = WACOM_DTU_OFFSET, WACOM_DTU_OFFSET }; static const struct wacom_features wacom_features_HID_ANY_ID = - { "Wacom HID", .type = HID_GENERIC }; + { "Wacom HID", .type = HID_GENERIC, .oVid = HID_ANY_ID, .oPid = HID_ANY_ID }; #define USB_DEVICE_WACOM(prod) \ HID_DEVICE(BUS_USB, HID_GROUP_WACOM, USB_VENDOR_ID_WACOM, prod),\ diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 53d16537fd2a..324c40b0c119 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -82,6 +82,7 @@ #define WACOM_DEVICETYPE_TOUCH 0x0002 #define WACOM_DEVICETYPE_PAD 0x0004 #define WACOM_DEVICETYPE_WL_MONITOR 0x0008 +#define WACOM_DEVICETYPE_DIRECT 0x0010 #define WACOM_VENDORDEFINED_PEN 0xff0d0001 #define WACOM_G9_PAGE 0xff090000 @@ -185,7 +186,6 @@ struct wacom_features { int pktlen; bool check_for_hid_type; int hid_type; - int last_slot_field; }; struct wacom_shared { @@ -214,35 +214,35 @@ struct hid_data { int cc_report; int cc_index; int cc_value_index; + int last_slot_field; int num_expected; int num_received; }; +struct wacom_remote_data { + struct { + u32 serial; + bool connected; + } remote[WACOM_MAX_REMOTES]; +}; + struct wacom_wac { + char name[WACOM_NAME_MAX]; char pen_name[WACOM_NAME_MAX]; char touch_name[WACOM_NAME_MAX]; char pad_name[WACOM_NAME_MAX]; - char bat_name[WACOM_NAME_MAX]; - char ac_name[WACOM_NAME_MAX]; unsigned char data[WACOM_PKGLEN_MAX]; int tool[2]; int id[2]; - __u32 serial[5]; + __u32 serial[2]; bool reporting_data; struct wacom_features features; struct wacom_shared *shared; struct input_dev *pen_input; struct input_dev *touch_input; struct input_dev *pad_input; - bool pen_registered; - bool touch_registered; - bool pad_registered; int pid; - int battery_capacity; int num_contacts_left; - int bat_charging; - int bat_connected; - int ps_connected; u8 bt_features; u8 bt_high_speed; int mode_report; diff --git a/include/linux/hid.h b/include/linux/hid.h index 75b66eccc692..b2ec82712baa 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -837,7 +837,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report, */ static inline void hid_device_io_start(struct hid_device *hid) { if (hid->io_started) { - dev_warn(&hid->dev, "io already started"); + dev_warn(&hid->dev, "io already started\n"); return; } hid->io_started = true; @@ -857,7 +857,7 @@ static inline void hid_device_io_start(struct hid_device *hid) { */ static inline void hid_device_io_stop(struct hid_device *hid) { if (!hid->io_started) { - dev_warn(&hid->dev, "io already stopped"); + dev_warn(&hid->dev, "io already stopped\n"); return; } hid->io_started = false; |