Commit 278de45e authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid

Pull HID subsystem fixes from Jiri Kosina:

 - syzkaller-reported error handling fixes in various drivers, from
   various people

 - increase of HID report buffer size to 8K, which is apparently needed
   by certain modern devices

 - a few new device-ID-specific fixes / quirks

 - battery charging status reporting fix in logitech-hidpp, from Filipe
   Laíns

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: hid-bigbenff: fix race condition for scheduled work during removal
  HID: hid-bigbenff: call hid_hw_stop() in case of error
  HID: hid-bigbenff: fix general protection fault caused by double kfree
  HID: i2c-hid: add Trekstor Surfbook E11B to descriptor override
  HID: alps: Fix an error handling path in 'alps_input_configured()'
  HID: hiddev: Fix race in in hiddev_disconnect()
  HID: core: increase HID report buffer size to 8KiB
  HID: core: fix off-by-one memset in hid_report_raw_event()
  HID: apple: Add support for recent firmware on Magic Keyboards
  HID: ite: Only bind to keyboard USB interface on Acer SW5-012 keyboard dock
  HID: logitech-hidpp: BatteryVoltage: only read chargeStatus if extPower is active
parents e46bfaba 4eb1b01d
...@@ -730,7 +730,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi) ...@@ -730,7 +730,7 @@ static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
if (data->has_sp) { if (data->has_sp) {
input2 = input_allocate_device(); input2 = input_allocate_device();
if (!input2) { if (!input2) {
input_free_device(input2); ret = -ENOMEM;
goto exit; goto exit;
} }
......
...@@ -340,7 +340,8 @@ static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi, ...@@ -340,7 +340,8 @@ static int apple_input_mapping(struct hid_device *hdev, struct hid_input *hi,
unsigned long **bit, int *max) unsigned long **bit, int *max)
{ {
if (usage->hid == (HID_UP_CUSTOM | 0x0003) || if (usage->hid == (HID_UP_CUSTOM | 0x0003) ||
usage->hid == (HID_UP_MSVENDOR | 0x0003)) { usage->hid == (HID_UP_MSVENDOR | 0x0003) ||
usage->hid == (HID_UP_HPVENDOR2 | 0x0003)) {
/* The fn key on Apple USB keyboards */ /* The fn key on Apple USB keyboards */
set_bit(EV_REP, hi->input->evbit); set_bit(EV_REP, hi->input->evbit);
hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN); hid_map_usage_clear(hi, usage, bit, max, EV_KEY, KEY_FN);
......
...@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = { ...@@ -174,6 +174,7 @@ static __u8 pid0902_rdesc_fixed[] = {
struct bigben_device { struct bigben_device {
struct hid_device *hid; struct hid_device *hid;
struct hid_report *report; struct hid_report *report;
bool removed;
u8 led_state; /* LED1 = 1 .. LED4 = 8 */ u8 led_state; /* LED1 = 1 .. LED4 = 8 */
u8 right_motor_on; /* right motor off/on 0/1 */ u8 right_motor_on; /* right motor off/on 0/1 */
u8 left_motor_force; /* left motor force 0-255 */ u8 left_motor_force; /* left motor force 0-255 */
...@@ -190,6 +191,9 @@ static void bigben_worker(struct work_struct *work) ...@@ -190,6 +191,9 @@ static void bigben_worker(struct work_struct *work)
struct bigben_device, worker); struct bigben_device, worker);
struct hid_field *report_field = bigben->report->field[0]; struct hid_field *report_field = bigben->report->field[0];
if (bigben->removed)
return;
if (bigben->work_led) { if (bigben->work_led) {
bigben->work_led = false; bigben->work_led = false;
report_field->value[0] = 0x01; /* 1 = led message */ report_field->value[0] = 0x01; /* 1 = led message */
...@@ -220,10 +224,16 @@ static void bigben_worker(struct work_struct *work) ...@@ -220,10 +224,16 @@ static void bigben_worker(struct work_struct *work)
static int hid_bigben_play_effect(struct input_dev *dev, void *data, static int hid_bigben_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect) struct ff_effect *effect)
{ {
struct bigben_device *bigben = data; struct hid_device *hid = input_get_drvdata(dev);
struct bigben_device *bigben = hid_get_drvdata(hid);
u8 right_motor_on; u8 right_motor_on;
u8 left_motor_force; u8 left_motor_force;
if (!bigben) {
hid_err(hid, "no device data\n");
return 0;
}
if (effect->type != FF_RUMBLE) if (effect->type != FF_RUMBLE)
return 0; return 0;
...@@ -298,8 +308,8 @@ static void bigben_remove(struct hid_device *hid) ...@@ -298,8 +308,8 @@ static void bigben_remove(struct hid_device *hid)
{ {
struct bigben_device *bigben = hid_get_drvdata(hid); struct bigben_device *bigben = hid_get_drvdata(hid);
bigben->removed = true;
cancel_work_sync(&bigben->worker); cancel_work_sync(&bigben->worker);
hid_hw_close(hid);
hid_hw_stop(hid); hid_hw_stop(hid);
} }
...@@ -319,6 +329,7 @@ static int bigben_probe(struct hid_device *hid, ...@@ -319,6 +329,7 @@ static int bigben_probe(struct hid_device *hid,
return -ENOMEM; return -ENOMEM;
hid_set_drvdata(hid, bigben); hid_set_drvdata(hid, bigben);
bigben->hid = hid; bigben->hid = hid;
bigben->removed = false;
error = hid_parse(hid); error = hid_parse(hid);
if (error) { if (error) {
...@@ -341,10 +352,10 @@ static int bigben_probe(struct hid_device *hid, ...@@ -341,10 +352,10 @@ static int bigben_probe(struct hid_device *hid,
INIT_WORK(&bigben->worker, bigben_worker); INIT_WORK(&bigben->worker, bigben_worker);
error = input_ff_create_memless(hidinput->input, bigben, error = input_ff_create_memless(hidinput->input, NULL,
hid_bigben_play_effect); hid_bigben_play_effect);
if (error) if (error)
return error; goto error_hw_stop;
name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1; name_sz = strlen(dev_name(&hid->dev)) + strlen(":red:bigben#") + 1;
...@@ -354,8 +365,10 @@ static int bigben_probe(struct hid_device *hid, ...@@ -354,8 +365,10 @@ static int bigben_probe(struct hid_device *hid,
sizeof(struct led_classdev) + name_sz, sizeof(struct led_classdev) + name_sz,
GFP_KERNEL GFP_KERNEL
); );
if (!led) if (!led) {
return -ENOMEM; error = -ENOMEM;
goto error_hw_stop;
}
name = (void *)(&led[1]); name = (void *)(&led[1]);
snprintf(name, name_sz, snprintf(name, name_sz,
"%s:red:bigben%d", "%s:red:bigben%d",
...@@ -369,7 +382,7 @@ static int bigben_probe(struct hid_device *hid, ...@@ -369,7 +382,7 @@ static int bigben_probe(struct hid_device *hid,
bigben->leds[n] = led; bigben->leds[n] = led;
error = devm_led_classdev_register(&hid->dev, led); error = devm_led_classdev_register(&hid->dev, led);
if (error) if (error)
return error; goto error_hw_stop;
} }
/* initial state: LED1 is on, no rumble effect */ /* initial state: LED1 is on, no rumble effect */
...@@ -383,6 +396,10 @@ static int bigben_probe(struct hid_device *hid, ...@@ -383,6 +396,10 @@ static int bigben_probe(struct hid_device *hid,
hid_info(hid, "LED and force feedback support for BigBen gamepad\n"); hid_info(hid, "LED and force feedback support for BigBen gamepad\n");
return 0; return 0;
error_hw_stop:
hid_hw_stop(hid);
return error;
} }
static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc, static __u8 *bigben_report_fixup(struct hid_device *hid, __u8 *rdesc,
......
...@@ -1741,7 +1741,9 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size, ...@@ -1741,7 +1741,9 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
rsize = ((report->size - 1) >> 3) + 1; rsize = ((report->size - 1) >> 3) + 1;
if (rsize > HID_MAX_BUFFER_SIZE) if (report_enum->numbered && rsize >= HID_MAX_BUFFER_SIZE)
rsize = HID_MAX_BUFFER_SIZE - 1;
else if (rsize > HID_MAX_BUFFER_SIZE)
rsize = HID_MAX_BUFFER_SIZE; rsize = HID_MAX_BUFFER_SIZE;
if (csize < rsize) { if (csize < rsize) {
......
...@@ -41,7 +41,8 @@ static const struct hid_device_id ite_devices[] = { ...@@ -41,7 +41,8 @@ static const struct hid_device_id ite_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) }, { HID_USB_DEVICE(USB_VENDOR_ID_ITE, USB_DEVICE_ID_ITE8595) },
{ HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) }, { HID_USB_DEVICE(USB_VENDOR_ID_258A, USB_DEVICE_ID_258A_6A88) },
/* ITE8595 USB kbd ctlr, with Synaptics touchpad connected to it. */ /* ITE8595 USB kbd ctlr, with Synaptics touchpad connected to it. */
{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_SYNAPTICS,
USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012) }, USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_012) },
{ } { }
}; };
......
...@@ -1256,36 +1256,35 @@ static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage, ...@@ -1256,36 +1256,35 @@ static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage,
{ {
int status; int status;
long charge_sts = (long)data[2]; long flags = (long) data[2];
*level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; if (flags & 0x80)
switch (data[2] & 0xe0) { switch (flags & 0x07) {
case 0x00: case 0:
status = POWER_SUPPLY_STATUS_CHARGING; status = POWER_SUPPLY_STATUS_CHARGING;
break; break;
case 0x20: case 1:
status = POWER_SUPPLY_STATUS_FULL; status = POWER_SUPPLY_STATUS_FULL;
*level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; *level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
break; break;
case 0x40: case 2:
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0xe0:
status = POWER_SUPPLY_STATUS_NOT_CHARGING; status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break; break;
default: default:
status = POWER_SUPPLY_STATUS_UNKNOWN; status = POWER_SUPPLY_STATUS_UNKNOWN;
break;
} }
else
status = POWER_SUPPLY_STATUS_DISCHARGING;
*charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; *charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
if (test_bit(3, &charge_sts)) { if (test_bit(3, &flags)) {
*charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST; *charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
} }
if (test_bit(4, &charge_sts)) { if (test_bit(4, &flags)) {
*charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; *charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
} }
if (test_bit(5, &flags)) {
if (test_bit(5, &charge_sts)) {
*level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; *level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
} }
......
...@@ -341,6 +341,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = { ...@@ -341,6 +341,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
}, },
.driver_data = (void *)&sipodev_desc .driver_data = (void *)&sipodev_desc
}, },
{
.ident = "Trekstor SURFBOOK E11B",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SURFBOOK E11B"),
},
.driver_data = (void *)&sipodev_desc
},
{ {
.ident = "Direkt-Tek DTLAPY116-2", .ident = "Direkt-Tek DTLAPY116-2",
.matches = { .matches = {
......
...@@ -932,9 +932,9 @@ void hiddev_disconnect(struct hid_device *hid) ...@@ -932,9 +932,9 @@ void hiddev_disconnect(struct hid_device *hid)
hiddev->exist = 0; hiddev->exist = 0;
if (hiddev->open) { if (hiddev->open) {
mutex_unlock(&hiddev->existancelock);
hid_hw_close(hiddev->hid); hid_hw_close(hiddev->hid);
wake_up_interruptible(&hiddev->wait); wake_up_interruptible(&hiddev->wait);
mutex_unlock(&hiddev->existancelock);
} else { } else {
mutex_unlock(&hiddev->existancelock); mutex_unlock(&hiddev->existancelock);
kfree(hiddev); kfree(hiddev);
......
...@@ -492,7 +492,7 @@ struct hid_report_enum { ...@@ -492,7 +492,7 @@ struct hid_report_enum {
}; };
#define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */ #define HID_MIN_BUFFER_SIZE 64 /* make sure there is at least a packet size of space */
#define HID_MAX_BUFFER_SIZE 4096 /* 4kb */ #define HID_MAX_BUFFER_SIZE 8192 /* 8kb */
#define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */ #define HID_CONTROL_FIFO_SIZE 256 /* to init devices with >100 reports */
#define HID_OUTPUT_FIFO_SIZE 64 #define HID_OUTPUT_FIFO_SIZE 64
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment