summaryrefslogtreecommitdiff
path: root/drivers/hid/hid-input.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-input.c')
-rw-r--r--drivers/hid/hid-input.c41
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;