diff options
Diffstat (limited to 'drivers/hid')
-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 |
19 files changed, 1805 insertions, 670 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 5f590e1..cd4599c 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 048befd..ed9c0ea 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 08f53c7..2b89c70 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 4ed9a4f..cd59c79 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 bcfaf32..fb9ace1 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 32e6d8d..0dd1167 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 feb2be7..76f644d 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 af3a8ec..1fc12e3 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 66201af..de1f350 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 e924d55..c6cd392 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 2f84b26..39e6426 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 310436a..b0bb99a 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 85ac435..1509d72 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 059931d..a91aabe 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 b4b8c6a..0a0eca5 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 4681a65..b4800ea 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 499cc82..5e7a564 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 1eae13c..1cb7992 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 53d1653..324c40b 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; |