diff options
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/hid-asus.c | 218 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 16 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 4 | ||||
-rw-r--r-- | drivers/hid/hid-input.c | 196 | ||||
-rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-multitouch.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-ntrig.c | 2 | ||||
-rw-r--r-- | drivers/hid/hid-sensor-custom.c | 4 | ||||
-rw-r--r-- | drivers/hid/hid-sensor-hub.c | 94 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-quirks.c | 1 | ||||
-rw-r--r-- | drivers/hid/wacom_wac.c | 8 |
11 files changed, 329 insertions, 218 deletions
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index a4a3c38bc145..50c294be8324 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -29,6 +29,7 @@ #include <linux/hid.h> #include <linux/module.h> #include <linux/input/mt.h> +#include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */ #include "hid-ids.h" @@ -38,24 +39,19 @@ MODULE_AUTHOR("Victor Vlasenko <victor.vlasenko@sysgears.com>"); MODULE_AUTHOR("Frederik Wenigwieser <frederik.wenigwieser@gmail.com>"); MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); +#define T100_TPAD_INTF 2 + +#define T100CHI_MOUSE_REPORT_ID 0x06 #define FEATURE_REPORT_ID 0x0d #define INPUT_REPORT_ID 0x5d #define FEATURE_KBD_REPORT_ID 0x5a - -#define INPUT_REPORT_SIZE 28 #define FEATURE_KBD_REPORT_SIZE 16 #define SUPPORT_KBD_BACKLIGHT BIT(0) -#define MAX_CONTACTS 5 - -#define MAX_X 2794 -#define MAX_Y 1758 #define MAX_TOUCH_MAJOR 8 #define MAX_PRESSURE 128 -#define CONTACT_DATA_SIZE 5 - #define BTN_LEFT_MASK 0x01 #define CONTACT_TOOL_TYPE_MASK 0x80 #define CONTACT_X_MSB_MASK 0xf0 @@ -70,6 +66,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad"); #define QUIRK_NO_CONSUMER_USAGES BIT(4) #define QUIRK_USE_KBD_BACKLIGHT BIT(5) #define QUIRK_T100_KEYBOARD BIT(6) +#define QUIRK_T100CHI BIT(7) #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \ QUIRK_NO_INIT_REPORTS | \ @@ -88,19 +85,62 @@ struct asus_kbd_leds { bool removed; }; +struct asus_touchpad_info { + int max_x; + int max_y; + int res_x; + int res_y; + int contact_size; + int max_contacts; +}; + struct asus_drvdata { unsigned long quirks; struct input_dev *input; struct asus_kbd_leds *kbd_backlight; + const struct asus_touchpad_info *tp; bool enable_backlight; }; -static void asus_report_contact_down(struct input_dev *input, +static const struct asus_touchpad_info asus_i2c_tp = { + .max_x = 2794, + .max_y = 1758, + .contact_size = 5, + .max_contacts = 5, +}; + +static const struct asus_touchpad_info asus_t100ta_tp = { + .max_x = 2240, + .max_y = 1120, + .res_x = 30, /* units/mm */ + .res_y = 27, /* units/mm */ + .contact_size = 5, + .max_contacts = 5, +}; + +static const struct asus_touchpad_info asus_t100chi_tp = { + .max_x = 2640, + .max_y = 1320, + .res_x = 31, /* units/mm */ + .res_y = 29, /* units/mm */ + .contact_size = 3, + .max_contacts = 4, +}; + +static void asus_report_contact_down(struct asus_drvdata *drvdat, int toolType, u8 *data) { - int touch_major, pressure; - int x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1]; - int y = MAX_Y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]); + struct input_dev *input = drvdat->input; + int touch_major, pressure, x, y; + + x = (data[0] & CONTACT_X_MSB_MASK) << 4 | data[1]; + y = drvdat->tp->max_y - ((data[0] & CONTACT_Y_MSB_MASK) << 8 | data[2]); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + + if (drvdat->tp->contact_size < 5) + return; if (toolType == MT_TOOL_PALM) { touch_major = MAX_TOUCH_MAJOR; @@ -110,19 +150,20 @@ static void asus_report_contact_down(struct input_dev *input, pressure = data[4] & CONTACT_PRESSURE_MASK; } - input_report_abs(input, ABS_MT_POSITION_X, x); - input_report_abs(input, ABS_MT_POSITION_Y, y); input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major); input_report_abs(input, ABS_MT_PRESSURE, pressure); } /* Required for Synaptics Palm Detection */ -static void asus_report_tool_width(struct input_dev *input) +static void asus_report_tool_width(struct asus_drvdata *drvdat) { - struct input_mt *mt = input->mt; + struct input_mt *mt = drvdat->input->mt; struct input_mt_slot *oldest; int oldid, count, i; + if (drvdat->tp->contact_size < 5) + return; + oldest = NULL; oldid = mt->trkid; count = 0; @@ -141,35 +182,42 @@ static void asus_report_tool_width(struct input_dev *input) } if (oldest) { - input_report_abs(input, ABS_TOOL_WIDTH, + input_report_abs(drvdat->input, ABS_TOOL_WIDTH, input_mt_get_value(oldest, ABS_MT_TOUCH_MAJOR)); } } -static void asus_report_input(struct input_dev *input, u8 *data) +static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size) { - int i; + int i, toolType = MT_TOOL_FINGER; u8 *contactData = data + 2; - for (i = 0; i < MAX_CONTACTS; i++) { + if (size != 3 + drvdat->tp->contact_size * drvdat->tp->max_contacts) + return 0; + + for (i = 0; i < drvdat->tp->max_contacts; i++) { bool down = !!(data[1] & BIT(i+3)); - int toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ? + + if (drvdat->tp->contact_size >= 5) + toolType = contactData[3] & CONTACT_TOOL_TYPE_MASK ? MT_TOOL_PALM : MT_TOOL_FINGER; - input_mt_slot(input, i); - input_mt_report_slot_state(input, toolType, down); + input_mt_slot(drvdat->input, i); + input_mt_report_slot_state(drvdat->input, toolType, down); if (down) { - asus_report_contact_down(input, toolType, contactData); - contactData += CONTACT_DATA_SIZE; + asus_report_contact_down(drvdat, toolType, contactData); + contactData += drvdat->tp->contact_size; } } - input_report_key(input, BTN_LEFT, data[1] & BTN_LEFT_MASK); - asus_report_tool_width(input); + input_report_key(drvdat->input, BTN_LEFT, data[1] & BTN_LEFT_MASK); + asus_report_tool_width(drvdat); + + input_mt_sync_frame(drvdat->input); + input_sync(drvdat->input); - input_mt_sync_frame(input); - input_sync(input); + return 1; } static int asus_raw_event(struct hid_device *hdev, @@ -177,12 +225,8 @@ static int asus_raw_event(struct hid_device *hdev, { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); - if (drvdata->quirks & QUIRK_IS_MULTITOUCH && - data[0] == INPUT_REPORT_ID && - size == INPUT_REPORT_SIZE) { - asus_report_input(drvdata->input, data); - return 1; - } + if (drvdata->tp && data[0] == INPUT_REPORT_ID) + return asus_report_input(drvdata, data, size); return 0; } @@ -334,19 +378,35 @@ static int asus_input_configured(struct hid_device *hdev, struct hid_input *hi) struct input_dev *input = hi->input; struct asus_drvdata *drvdata = hid_get_drvdata(hdev); - if (drvdata->quirks & QUIRK_IS_MULTITOUCH) { + /* T100CHI uses MULTI_INPUT, bind the touchpad to the mouse hid_input */ + if (drvdata->quirks & QUIRK_T100CHI && + hi->report->id != T100CHI_MOUSE_REPORT_ID) + return 0; + + if (drvdata->tp) { int ret; - input_set_abs_params(input, ABS_MT_POSITION_X, 0, MAX_X, 0, 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, MAX_Y, 0, 0); - input_set_abs_params(input, ABS_TOOL_WIDTH, 0, MAX_TOUCH_MAJOR, 0, 0); - input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, MAX_TOUCH_MAJOR, 0, 0); - input_set_abs_params(input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, + drvdata->tp->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, + drvdata->tp->max_y, 0, 0); + input_abs_set_res(input, ABS_MT_POSITION_X, drvdata->tp->res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, drvdata->tp->res_y); + + if (drvdata->tp->contact_size >= 5) { + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, + MAX_TOUCH_MAJOR, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, + MAX_TOUCH_MAJOR, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, + MAX_PRESSURE, 0, 0); + } __set_bit(BTN_LEFT, input->keybit); __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); - ret = input_mt_init_slots(input, MAX_CONTACTS, INPUT_MT_POINTER); + ret = input_mt_init_slots(input, drvdata->tp->max_contacts, + INPUT_MT_POINTER); if (ret) { hid_err(hdev, "Asus input mt init slots failed: %d\n", ret); @@ -378,6 +438,26 @@ static int asus_input_mapping(struct hid_device *hdev, return -1; } + /* + * Ignore a bunch of bogus collections in the T100CHI descriptor. + * This avoids a bunch of non-functional hid_input devices getting + * created because of the T100CHI using HID_QUIRK_MULTI_INPUT. + */ + if (drvdata->quirks & QUIRK_T100CHI) { + if (field->application == (HID_UP_GENDESK | 0x0080) || + usage->hid == (HID_UP_GENDEVCTRLS | 0x0024) || + usage->hid == (HID_UP_GENDEVCTRLS | 0x0025) || + usage->hid == (HID_UP_GENDEVCTRLS | 0x0026)) + return -1; + /* + * We use the hid_input for the mouse report for the touchpad, + * keep the left button, to avoid the core removing it. + */ + if (field->application == HID_GD_MOUSE && + usage->hid != (HID_UP_BUTTON | 1)) + return -1; + } + /* ASUS-specific keyboard hotkeys */ if ((usage->hid & HID_USAGE_PAGE) == 0xff310000) { set_bit(EV_REP, hi->input->evbit); @@ -496,7 +576,7 @@ static int __maybe_unused asus_reset_resume(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); - if (drvdata->quirks & QUIRK_IS_MULTITOUCH) + if (drvdata->tp) return asus_start_multitouch(hdev); return 0; @@ -517,6 +597,28 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) drvdata->quirks = id->driver_data; + if (drvdata->quirks & QUIRK_IS_MULTITOUCH) + drvdata->tp = &asus_i2c_tp; + + if (drvdata->quirks & QUIRK_T100_KEYBOARD) { + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); + + if (intf->altsetting->desc.bInterfaceNumber == T100_TPAD_INTF) { + drvdata->quirks = QUIRK_SKIP_INPUT_MAPPING; + drvdata->tp = &asus_t100ta_tp; + } + } + + if (drvdata->quirks & QUIRK_T100CHI) { + /* + * All functionality is on a single HID interface and for + * userspace the touchpad must be a separate input_dev. + */ + hdev->quirks |= HID_QUIRK_MULTI_INPUT | + HID_QUIRK_NO_EMPTY_INPUT; + drvdata->tp = &asus_t100chi_tp; + } + if (drvdata->quirks & QUIRK_NO_INIT_REPORTS) hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS; @@ -538,13 +640,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop_hw; } - if (drvdata->quirks & QUIRK_IS_MULTITOUCH) { + if (drvdata->tp) { drvdata->input->name = "Asus TouchPad"; } else { drvdata->input->name = "Asus Keyboard"; } - if (drvdata->quirks & QUIRK_IS_MULTITOUCH) { + if (drvdata->tp) { ret = asus_start_multitouch(hdev); if (ret) goto err_stop_hw; @@ -578,11 +680,34 @@ static __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc, hid_info(hdev, "Fixing up Asus notebook report descriptor\n"); rdesc[55] = 0xdd; } + /* For the T100TA keyboard dock */ if (drvdata->quirks & QUIRK_T100_KEYBOARD && *rsize == 76 && rdesc[73] == 0x81 && rdesc[74] == 0x01) { hid_info(hdev, "Fixing up Asus T100 keyb report descriptor\n"); rdesc[74] &= ~HID_MAIN_ITEM_CONSTANT; } + /* For the T100CHI keyboard dock */ + if (drvdata->quirks & QUIRK_T100CHI && + *rsize == 403 && rdesc[388] == 0x09 && rdesc[389] == 0x76) { + /* + * Change Usage (76h) to Usage Minimum (00h), Usage Maximum + * (FFh) and clear the flags in the Input() byte. + * Note the descriptor has a bogus 0 byte at the end so we + * only need 1 extra byte. + */ + *rsize = 404; + rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL); + if (!rdesc) + return NULL; + + hid_info(hdev, "Fixing up T100CHI keyb report descriptor\n"); + memmove(rdesc + 392, rdesc + 390, 12); + rdesc[388] = 0x19; + rdesc[389] = 0x00; + rdesc[390] = 0x29; + rdesc[391] = 0xff; + rdesc[402] = 0x00; + } return rdesc; } @@ -602,6 +727,9 @@ static const struct hid_device_id asus_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_ASUS_AK1D) }, { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, + USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD), QUIRK_T100CHI }, + { } }; MODULE_DEVICE_TABLE(hid, asus_devices); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 9017dcc14502..9bc91160819b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1982,6 +1982,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_JESS, USB_DEVICE_ID_ASUS_MD_5112) }, { HID_USB_DEVICE(USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_ASUS_MD_5110) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD) }, #endif #if IS_ENABLED(CONFIG_HID_AUREAL) { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, @@ -2487,11 +2488,9 @@ static int hid_device_probe(struct device *dev) const struct hid_device_id *id; int ret = 0; - if (down_interruptible(&hdev->driver_lock)) - return -EINTR; if (down_interruptible(&hdev->driver_input_lock)) { ret = -EINTR; - goto unlock_driver_lock; + goto end; } hdev->io_started = false; @@ -2518,8 +2517,7 @@ static int hid_device_probe(struct device *dev) unlock: if (!hdev->io_started) up(&hdev->driver_input_lock); -unlock_driver_lock: - up(&hdev->driver_lock); +end: return ret; } @@ -2529,11 +2527,9 @@ static int hid_device_remove(struct device *dev) struct hid_driver *hdrv; int ret = 0; - if (down_interruptible(&hdev->driver_lock)) - return -EINTR; if (down_interruptible(&hdev->driver_input_lock)) { ret = -EINTR; - goto unlock_driver_lock; + goto end; } hdev->io_started = false; @@ -2549,8 +2545,7 @@ static int hid_device_remove(struct device *dev) if (!hdev->io_started) up(&hdev->driver_input_lock); -unlock_driver_lock: - up(&hdev->driver_lock); +end: return ret; } @@ -3008,7 +3003,6 @@ struct hid_device *hid_allocate_device(void) init_waitqueue_head(&hdev->debug_wait); INIT_LIST_HEAD(&hdev->debug_list); spin_lock_init(&hdev->debug_list_lock); - sema_init(&hdev->driver_lock, 1); sema_init(&hdev->driver_input_lock, 1); mutex_init(&hdev->ll_open_lock); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 90fcf7457908..b397a14ab970 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -176,6 +176,7 @@ #define USB_DEVICE_ID_ASUSTEK_LCM 0x1726 #define USB_DEVICE_ID_ASUSTEK_LCM2 0x175b #define USB_DEVICE_ID_ASUSTEK_T100_KEYBOARD 0x17e0 +#define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD 0x8502 #define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD 0x184a #define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585 #define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101 @@ -667,7 +668,8 @@ #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e #define USB_DEVICE_ID_LOGITECH_T651 0xb00c -#define USB_DEVICE_ID_LOGITECH_C077 0xc007 +#define USB_DEVICE_ID_LOGITECH_C007 0xc007 +#define USB_DEVICE_ID_LOGITECH_C077 0xc077 #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index ccdff1ee1f0c..199f6a01fc62 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -340,13 +340,45 @@ static unsigned find_battery_quirk(struct hid_device *hdev) return quirks; } +static int hidinput_scale_battery_capacity(struct hid_device *dev, + int value) +{ + if (dev->battery_min < dev->battery_max && + value >= dev->battery_min && value <= dev->battery_max) + value = ((value - dev->battery_min) * 100) / + (dev->battery_max - dev->battery_min); + + return value; +} + +static int hidinput_query_battery_capacity(struct hid_device *dev) +{ + u8 *buf; + int ret; + + buf = kmalloc(2, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, + dev->battery_report_type, HID_REQ_GET_REPORT); + if (ret != 2) { + kfree(buf); + return -ENODATA; + } + + ret = hidinput_scale_battery_capacity(dev, buf[1]); + kfree(buf); + return ret; +} + static int hidinput_get_battery_property(struct power_supply *psy, enum power_supply_property prop, union power_supply_propval *val) { struct hid_device *dev = power_supply_get_drvdata(psy); + int value; int ret = 0; - __u8 *buf; switch (prop) { case POWER_SUPPLY_PROP_PRESENT: @@ -355,29 +387,15 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CAPACITY: - - buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - break; - } - ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, - dev->battery_report_type, - HID_REQ_GET_REPORT); - - if (ret != 2) { - ret = -ENODATA; - kfree(buf); - break; + if (dev->battery_report_type == HID_FEATURE_REPORT) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + } else { + value = dev->battery_capacity; } - ret = 0; - if (dev->battery_min < dev->battery_max && - buf[1] >= dev->battery_min && - buf[1] <= dev->battery_max) - val->intval = (100 * (buf[1] - dev->battery_min)) / - (dev->battery_max - dev->battery_min); - kfree(buf); + val->intval = value; break; case POWER_SUPPLY_PROP_MODEL_NAME: @@ -385,7 +403,22 @@ static int hidinput_get_battery_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_STATUS: - val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + if (!dev->battery_reported && + dev->battery_report_type == HID_FEATURE_REPORT) { + value = hidinput_query_battery_capacity(dev); + if (value < 0) + return value; + + dev->battery_capacity = value; + dev->battery_reported = true; + } + + if (!dev->battery_reported) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else if (dev->battery_capacity == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; case POWER_SUPPLY_PROP_SCOPE: @@ -400,18 +433,16 @@ static int hidinput_get_battery_property(struct power_supply *psy, return ret; } -static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field) { - struct power_supply_desc *psy_desc = NULL; + struct power_supply_desc *psy_desc; struct power_supply_config psy_cfg = { .drv_data = dev, }; unsigned quirks; s32 min, max; + int error; - if (field->usage->hid != HID_DC_BATTERYSTRENGTH) - return false; /* no match */ - - if (dev->battery != NULL) - goto out; /* already initialized? */ + if (dev->battery) + return 0; /* already initialized? */ quirks = find_battery_quirk(dev); @@ -419,16 +450,18 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->bus, dev->vendor, dev->product, dev->version, quirks); if (quirks & HID_BATTERY_QUIRK_IGNORE) - goto out; + return 0; psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL); - if (psy_desc == NULL) - goto out; - - psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq); - if (psy_desc->name == NULL) { - kfree(psy_desc); - goto out; + if (!psy_desc) + return -ENOMEM; + + psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", + strlen(dev->uniq) ? + dev->uniq : dev_name(&dev->dev)); + if (!psy_desc->name) { + error = -ENOMEM; + goto err_free_mem; } psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; @@ -455,17 +488,20 @@ static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg); if (IS_ERR(dev->battery)) { - hid_warn(dev, "can't register power supply: %ld\n", - PTR_ERR(dev->battery)); - kfree(psy_desc->name); - kfree(psy_desc); - dev->battery = NULL; - } else { - power_supply_powers(dev->battery, &dev->dev); + error = PTR_ERR(dev->battery); + hid_warn(dev, "can't register power supply: %d\n", error); + goto err_free_name; } -out: - return true; + power_supply_powers(dev->battery, &dev->dev); + return 0; + +err_free_name: + kfree(psy_desc->name); +err_free_mem: + kfree(psy_desc); + dev->battery = NULL; + return error; } static void hidinput_cleanup_battery(struct hid_device *dev) @@ -481,16 +517,39 @@ static void hidinput_cleanup_battery(struct hid_device *dev) kfree(psy_desc); dev->battery = NULL; } + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ + int capacity; + + if (!dev->battery) + return; + + if (value == 0 || value < dev->battery_min || value > dev->battery_max) + return; + + capacity = hidinput_scale_battery_capacity(dev, value); + + if (!dev->battery_reported || capacity != dev->battery_capacity) { + dev->battery_capacity = capacity; + dev->battery_reported = true; + power_supply_changed(dev->battery); + } +} #else /* !CONFIG_HID_BATTERY_STRENGTH */ -static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, - struct hid_field *field) +static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, + struct hid_field *field) { - return false; + return 0; } static void hidinput_cleanup_battery(struct hid_device *dev) { } + +static void hidinput_update_battery(struct hid_device *dev, int value) +{ +} #endif /* CONFIG_HID_BATTERY_STRENGTH */ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, @@ -710,6 +769,11 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } break; + case 0x3b: /* Battery Strength */ + hidinput_setup_battery(device, HID_INPUT_REPORT, field); + usage->type = EV_PWR; + goto ignore; + case 0x3c: /* Invert */ map_key_clear(BTN_TOOL_RUBBER); break; @@ -944,11 +1008,13 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel break; case HID_UP_GENDEVCTRLS: - if (hidinput_setup_battery(device, HID_INPUT_REPORT, field)) + switch (usage->hid) { + case HID_DC_BATTERYSTRENGTH: + hidinput_setup_battery(device, HID_INPUT_REPORT, field); + usage->type = EV_PWR; goto ignore; - else - goto unknown; - break; + } + goto unknown; case HID_UP_HPVENDOR: /* Reported on a Dutch layout HP5308 */ set_bit(EV_REP, input->evbit); @@ -1031,7 +1097,6 @@ mapped: if (usage->code > max) goto ignore; - if (usage->type == EV_ABS) { int a = field->logical_minimum; @@ -1090,14 +1155,19 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct struct input_dev *input; unsigned *quirks = &hid->quirks; - if (!field->hidinput) + if (!usage->type) return; - input = field->hidinput->input; + if (usage->type == EV_PWR) { + hidinput_update_battery(hid, value); + return; + } - if (!usage->type) + if (!field->hidinput) return; + input = field->hidinput->input; + if (usage->hat_min < usage->hat_max || usage->hat_dir) { int hat_dir = usage->hat_dir; if (!hat_dir) @@ -1373,6 +1443,7 @@ static void report_features(struct hid_device *hid) struct hid_driver *drv = hid->driver; struct hid_report_enum *rep_enum; struct hid_report *rep; + struct hid_usage *usage; int i, j; rep_enum = &hid->report_enum[HID_FEATURE_REPORT]; @@ -1383,12 +1454,15 @@ static void report_features(struct hid_device *hid) continue; for (j = 0; j < rep->field[i]->maxusage; j++) { + usage = &rep->field[i]->usage[j]; + /* Verify if Battery Strength feature is available */ - hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]); + if (usage->hid == HID_DC_BATTERYSTRENGTH) + hidinput_setup_battery(hid, HID_FEATURE_REPORT, + rep->field[i]); if (drv->feature_mapping) - drv->feature_mapping(hid, rep->field[i], - rep->field[i]->usage + j); + drv->feature_mapping(hid, rep->field[i], usage); } } } diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 501e16a9227d..614054af904a 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -2926,7 +2926,7 @@ static struct attribute *sysfs_attrs[] = { NULL }; -static struct attribute_group ps_attribute_group = { +static const struct attribute_group ps_attribute_group = { .attrs = sysfs_attrs }; diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 0a8689bb08e9..440b999304a5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -347,7 +347,7 @@ static struct attribute *sysfs_attrs[] = { NULL }; -static struct attribute_group mt_attribute_group = { +static const struct attribute_group mt_attribute_group = { .attrs = sysfs_attrs }; diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index 1b0084d4af2e..3d121d8ee980 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -445,7 +445,7 @@ static struct attribute *sysfs_attrs[] = { NULL }; -static struct attribute_group ntrig_attribute_group = { +static const struct attribute_group ntrig_attribute_group = { .attrs = sysfs_attrs }; diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c index 3a84aaf1418b..0bcf041368c7 100644 --- a/drivers/hid/hid-sensor-custom.c +++ b/drivers/hid/hid-sensor-custom.c @@ -276,7 +276,7 @@ static struct attribute *enable_sensor_attrs[] = { NULL, }; -static struct attribute_group enable_sensor_attr_group = { +static const struct attribute_group enable_sensor_attr_group = { .attrs = enable_sensor_attrs, }; @@ -823,7 +823,7 @@ static int hid_sensor_custom_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id hid_sensor_custom_ids[] = { +static const struct platform_device_id hid_sensor_custom_ids[] = { { .name = "HID-SENSOR-2000e1", }, diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c index 4ef73374a8f9..25363fc571bc 100644 --- a/drivers/hid/hid-sensor-hub.c +++ b/drivers/hid/hid-sensor-hub.c @@ -579,54 +579,6 @@ void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev) } EXPORT_SYMBOL_GPL(sensor_hub_device_close); -static __u8 *sensor_hub_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - int index; - struct sensor_hub_data *sd = hid_get_drvdata(hdev); - unsigned char report_block[] = { - 0x0a, 0x16, 0x03, 0x15, 0x00, 0x25, 0x05}; - unsigned char power_block[] = { - 0x0a, 0x19, 0x03, 0x15, 0x00, 0x25, 0x05}; - - if (!(sd->quirks & HID_SENSOR_HUB_ENUM_QUIRK)) { - hid_dbg(hdev, "No Enum quirks\n"); - return rdesc; - } - - /* Looks for power and report state usage id and force to 1 */ - for (index = 0; index < *rsize; ++index) { - if (((*rsize - index) > sizeof(report_block)) && - !memcmp(&rdesc[index], report_block, - sizeof(report_block))) { - rdesc[index + 4] = 0x01; - index += sizeof(report_block); - } - if (((*rsize - index) > sizeof(power_block)) && - !memcmp(&rdesc[index], power_block, - sizeof(power_block))) { - rdesc[index + 4] = 0x01; - index += sizeof(power_block); - } - } - - /* Checks if the report descriptor of Thinkpad Helix 2 has a logical - * minimum for magnetic flux axis greater than the maximum */ - if (hdev->product == USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA && - *rsize == 2558 && rdesc[913] == 0x17 && rdesc[914] == 0x40 && - rdesc[915] == 0x81 && rdesc[916] == 0x08 && - rdesc[917] == 0x00 && rdesc[918] == 0x27 && - rdesc[921] == 0x07 && rdesc[922] == 0x00) { - /* Sets negative logical minimum for mag x, y and z */ - rdesc[914] = rdesc[935] = rdesc[956] = 0xc0; - rdesc[915] = rdesc[936] = rdesc[957] = 0x7e; - rdesc[916] = rdesc[937] = rdesc[958] = 0xf7; - rdesc[917] = rdesc[938] = rdesc[959] = 0xff; - } - - return rdesc; -} - static int sensor_hub_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -778,51 +730,6 @@ static void sensor_hub_remove(struct hid_device *hdev) } static const struct hid_device_id sensor_hub_devices[] = { - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_0, - USB_DEVICE_ID_INTEL_HID_SENSOR_0), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, - USB_DEVICE_ID_INTEL_HID_SENSOR_0), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_1, - USB_DEVICE_ID_INTEL_HID_SENSOR_1), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, - USB_DEVICE_ID_MS_SURFACE_PRO_2), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, - USB_DEVICE_ID_MS_TOUCH_COVER_2), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, - USB_DEVICE_ID_MS_TYPE_COVER_2), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROSOFT, - 0x07bd), /* Microsoft Surface 3 */ - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_MICROCHIP, - 0x0f01), /* MM7150 */ - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, - USB_DEVICE_ID_STM_HID_SENSOR), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_STM_0, - USB_DEVICE_ID_STM_HID_SENSOR_1), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_TEXAS_INSTRUMENTS, - USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, - USB_DEVICE_ID_ITE_LENOVO_YOGA), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, - USB_DEVICE_ID_ITE_LENOVO_YOGA2), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_ITE, - USB_DEVICE_ID_ITE_LENOVO_YOGA900), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, - { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, USB_VENDOR_ID_INTEL_0, - 0x22D8), - .driver_data = HID_SENSOR_HUB_ENUM_QUIRK}, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_SENSOR_HUB, HID_ANY_ID, HID_ANY_ID) }, { } @@ -835,7 +742,6 @@ static struct hid_driver sensor_hub_driver = { .probe = sensor_hub_probe, .remove = sensor_hub_remove, .raw_event = sensor_hub_raw_event, - .report_fixup = sensor_hub_report_fixup, #ifdef CONFIG_PM .suspend = sensor_hub_suspend, .resume = sensor_hub_resume, diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index a88e7c7bea0a..a83fa76655b9 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -99,6 +99,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0B4A, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_HP, USB_PRODUCT_ID_HP_PIXART_OEM_USB_OPTICAL_MOUSE, HID_QUIRK_ALWAYS_POLL }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C007, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_C077, HID_QUIRK_ALWAYS_POLL }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C01A, HID_QUIRK_ALWAYS_POLL }, diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9f940293ede4..bb17d7bbefd3 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1846,7 +1846,13 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHRINGSTATUS: - wacom_map_usage(input, usage, field, EV_ABS, ABS_WHEEL, 0); + /* + * Only set up type/code association. Completely mapping + * this usage may overwrite the axis resolution and range. + */ + usage->type = EV_ABS; + usage->code = ABS_WHEEL; + set_bit(EV_ABS, input->evbit); features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_BUTTONCONFIG: |