diff options
Diffstat (limited to 'drivers/hid/hid-input.c')
| -rw-r--r-- | drivers/hid/hid-input.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 3487600cadb4..edd6bbf8342b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -375,6 +375,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICTRACKPAD), HID_BATTERY_QUIRK_IGNORE }, + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), + HID_BATTERY_QUIRK_AVOID_QUERY }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084), HID_BATTERY_QUIRK_IGNORE }, @@ -432,17 +435,25 @@ static int hidinput_scale_battery_capacity(struct hid_battery *bat, static int hidinput_query_battery_capacity(struct hid_battery *bat) { int ret; + /* + * The capacity field may not be the first field in the report: some + * devices (e.g. the Apple Magic Trackpad 2 over Bluetooth) precede it + * with status flags. Read it from its actual byte offset in the report + * (report_offset is in bits; the leading byte is the report id). + */ + int offset = 1 + bat->report_offset / 8; + int len = offset + 1; - u8 *buf __free(kfree) = kmalloc(4, GFP_KERNEL); + u8 *buf __free(kfree) = kmalloc(max(len, 4), GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, 4, + ret = hid_hw_raw_request(bat->dev, bat->report_id, buf, max(len, 4), bat->report_type, HID_REQ_GET_REPORT); - if (ret < 2) + if (ret < len) return -ENODATA; - return hidinput_scale_battery_capacity(bat, buf[1]); + return hidinput_scale_battery_capacity(bat, buf[offset]); } static int hidinput_get_battery_property(struct power_supply *psy, @@ -593,6 +604,7 @@ static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, bat->max = max; bat->report_type = report_type; bat->report_id = field->report->id; + bat->report_offset = field->report_offset; bat->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; bat->status = HID_BATTERY_UNKNOWN; @@ -2317,7 +2329,19 @@ static inline void hidinput_configure_usages(struct hid_input *hidinput, * Read all reports and initialize the absolute field values. */ -int hidinput_connect(struct hid_device *hid, unsigned int force) +static bool hid_has_ff_input(struct hid_device *hdev) +{ + struct hid_input *hidinput; + + list_for_each_entry(hidinput, &hdev->inputs, list) { + if (test_bit(EV_FF, hidinput->input->evbit)) + return true; + } + + return false; +} + +int hidinput_connect(struct hid_device *hid, unsigned int connect_mask) { struct hid_driver *drv = hid->driver; struct hid_report *report; @@ -2330,7 +2354,7 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) hid->status &= ~HID_STAT_DUP_DETECTED; - if (!force) { + if (!(connect_mask & HID_CONNECT_HIDINPUT_FORCE)) { for (i = 0; i < hid->maxcollection; i++) { struct hid_collection *col = &hid->collection[i]; if (col->type == HID_COLLECTION_APPLICATION || @@ -2396,6 +2420,11 @@ int hidinput_connect(struct hid_device *hid, unsigned int force) continue; } + if (list_is_first(&hidinput->list, &hid->inputs) && + (connect_mask & HID_CONNECT_FF) && hid->ff_init && + !hid_has_ff_input(hid)) + hid->ff_init(hid); + if (input_register_device(hidinput->input)) goto out_unwind; hidinput->registered = true; |
