Commit 12fb2b99 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 updates from Jiri Kosina:
 "This time it's surprisingly quiet (probably due to the christmas
  break):

   - Logitech HID++ protocol improvements from Mazin Rezk, Pedro
     Vanzella and Adrian Freund

   - support for hidraw uniq ioctl from Marcel Holtmann"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid:
  HID: logitech-hidpp: avoid duplicate error handling code in 'hidpp_probe()'
  hid-logitech-hidpp: read battery voltage from newer devices
  HID: logitech: Add MX Master 3 Mouse
  HID: logitech-hidpp: Support WirelessDeviceStatus connect events
  HID: logitech-hidpp: Support translations from short to long reports
  HID: hidraw: add support uniq ioctl
parents 08c49dc1 fef684af
...@@ -49,6 +49,10 @@ MODULE_PARM_DESC(disable_tap_to_click, ...@@ -49,6 +49,10 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_REPORT_LONG_LENGTH 20 #define HIDPP_REPORT_LONG_LENGTH 20
#define HIDPP_REPORT_VERY_LONG_MAX_LENGTH 64 #define HIDPP_REPORT_VERY_LONG_MAX_LENGTH 64
#define HIDPP_REPORT_SHORT_SUPPORTED BIT(0)
#define HIDPP_REPORT_LONG_SUPPORTED BIT(1)
#define HIDPP_REPORT_VERY_LONG_SUPPORTED BIT(2)
#define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03 #define HIDPP_SUB_ID_CONSUMER_VENDOR_KEYS 0x03
#define HIDPP_SUB_ID_ROLLER 0x05 #define HIDPP_SUB_ID_ROLLER 0x05
#define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06 #define HIDPP_SUB_ID_MOUSE_EXTRA_BTNS 0x06
...@@ -87,6 +91,7 @@ MODULE_PARM_DESC(disable_tap_to_click, ...@@ -87,6 +91,7 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1) #define HIDPP_CAPABILITY_HIDPP20_BATTERY BIT(1)
#define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2) #define HIDPP_CAPABILITY_BATTERY_MILEAGE BIT(2)
#define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3) #define HIDPP_CAPABILITY_BATTERY_LEVEL_STATUS BIT(3)
#define HIDPP_CAPABILITY_BATTERY_VOLTAGE BIT(4)
/* /*
* There are two hidpp protocols in use, the first version hidpp10 is known * There are two hidpp protocols in use, the first version hidpp10 is known
...@@ -135,12 +140,15 @@ struct hidpp_report { ...@@ -135,12 +140,15 @@ struct hidpp_report {
struct hidpp_battery { struct hidpp_battery {
u8 feature_index; u8 feature_index;
u8 solar_feature_index; u8 solar_feature_index;
u8 voltage_feature_index;
struct power_supply_desc desc; struct power_supply_desc desc;
struct power_supply *ps; struct power_supply *ps;
char name[64]; char name[64];
int status; int status;
int capacity; int capacity;
int level; int level;
int voltage;
int charge_type;
bool online; bool online;
}; };
...@@ -183,9 +191,12 @@ struct hidpp_device { ...@@ -183,9 +191,12 @@ struct hidpp_device {
unsigned long quirks; unsigned long quirks;
unsigned long capabilities; unsigned long capabilities;
u8 supported_reports;
struct hidpp_battery battery; struct hidpp_battery battery;
struct hidpp_scroll_counter vertical_wheel_counter; struct hidpp_scroll_counter vertical_wheel_counter;
u8 wireless_feature_index;
}; };
/* HID++ 1.0 error codes */ /* HID++ 1.0 error codes */
...@@ -340,6 +351,11 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev, ...@@ -340,6 +351,11 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
struct hidpp_report *message; struct hidpp_report *message;
int ret, max_count; int ret, max_count;
/* Send as long report if short reports are not supported. */
if (report_id == REPORT_ID_HIDPP_SHORT &&
!(hidpp_dev->supported_reports & HIDPP_REPORT_SHORT_SUPPORTED))
report_id = REPORT_ID_HIDPP_LONG;
switch (report_id) { switch (report_id) {
case REPORT_ID_HIDPP_SHORT: case REPORT_ID_HIDPP_SHORT:
max_count = HIDPP_REPORT_SHORT_LENGTH - 4; max_count = HIDPP_REPORT_SHORT_LENGTH - 4;
...@@ -393,10 +409,13 @@ static inline bool hidpp_match_error(struct hidpp_report *question, ...@@ -393,10 +409,13 @@ static inline bool hidpp_match_error(struct hidpp_report *question,
(answer->fap.params[0] == question->fap.funcindex_clientid); (answer->fap.params[0] == question->fap.funcindex_clientid);
} }
static inline bool hidpp_report_is_connect_event(struct hidpp_report *report) static inline bool hidpp_report_is_connect_event(struct hidpp_device *hidpp,
struct hidpp_report *report)
{ {
return (report->report_id == REPORT_ID_HIDPP_SHORT) && return (hidpp->wireless_feature_index &&
(report->rap.sub_id == 0x41); (report->fap.feature_index == hidpp->wireless_feature_index)) ||
((report->report_id == REPORT_ID_HIDPP_SHORT) &&
(report->rap.sub_id == 0x41));
} }
/** /**
...@@ -1222,6 +1241,144 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp, ...@@ -1222,6 +1241,144 @@ static int hidpp20_battery_event(struct hidpp_device *hidpp,
return 0; return 0;
} }
/* -------------------------------------------------------------------------- */
/* 0x1001: Battery voltage */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_BATTERY_VOLTAGE 0x1001
#define CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE 0x00
#define EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST 0x00
static int hidpp20_battery_map_status_voltage(u8 data[3], int *voltage,
int *level, int *charge_type)
{
int status;
long charge_sts = (long)data[2];
*level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
switch (data[2] & 0xe0) {
case 0x00:
status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x20:
status = POWER_SUPPLY_STATUS_FULL;
*level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
break;
case 0x40:
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0xe0:
status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
default:
status = POWER_SUPPLY_STATUS_UNKNOWN;
}
*charge_type = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
if (test_bit(3, &charge_sts)) {
*charge_type = POWER_SUPPLY_CHARGE_TYPE_FAST;
}
if (test_bit(4, &charge_sts)) {
*charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
}
if (test_bit(5, &charge_sts)) {
*level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
}
*voltage = get_unaligned_be16(data);
return status;
}
static int hidpp20_battery_get_battery_voltage(struct hidpp_device *hidpp,
u8 feature_index,
int *status, int *voltage,
int *level, int *charge_type)
{
struct hidpp_report response;
int ret;
u8 *params = (u8 *)response.fap.params;
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
CMD_BATTERY_VOLTAGE_GET_BATTERY_VOLTAGE,
NULL, 0, &response);
if (ret > 0) {
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
__func__, ret);
return -EPROTO;
}
if (ret)
return ret;
hidpp->capabilities |= HIDPP_CAPABILITY_BATTERY_VOLTAGE;
*status = hidpp20_battery_map_status_voltage(params, voltage,
level, charge_type);
return 0;
}
static int hidpp20_query_battery_voltage_info(struct hidpp_device *hidpp)
{
u8 feature_type;
int ret;
int status, voltage, level, charge_type;
if (hidpp->battery.voltage_feature_index == 0xff) {
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_BATTERY_VOLTAGE,
&hidpp->battery.voltage_feature_index,
&feature_type);
if (ret)
return ret;
}
ret = hidpp20_battery_get_battery_voltage(hidpp,
hidpp->battery.voltage_feature_index,
&status, &voltage, &level, &charge_type);
if (ret)
return ret;
hidpp->battery.status = status;
hidpp->battery.voltage = voltage;
hidpp->battery.level = level;
hidpp->battery.charge_type = charge_type;
hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
}
static int hidpp20_battery_voltage_event(struct hidpp_device *hidpp,
u8 *data, int size)
{
struct hidpp_report *report = (struct hidpp_report *)data;
int status, voltage, level, charge_type;
if (report->fap.feature_index != hidpp->battery.voltage_feature_index ||
report->fap.funcindex_clientid != EVENT_BATTERY_VOLTAGE_STATUS_BROADCAST)
return 0;
status = hidpp20_battery_map_status_voltage(report->fap.params, &voltage,
&level, &charge_type);
hidpp->battery.online = status != POWER_SUPPLY_STATUS_NOT_CHARGING;
if (voltage != hidpp->battery.voltage || status != hidpp->battery.status) {
hidpp->battery.voltage = voltage;
hidpp->battery.status = status;
hidpp->battery.level = level;
hidpp->battery.charge_type = charge_type;
if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps);
}
return 0;
}
static enum power_supply_property hidpp_battery_props[] = { static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_STATUS,
...@@ -1231,6 +1388,7 @@ static enum power_supply_property hidpp_battery_props[] = { ...@@ -1231,6 +1388,7 @@ static enum power_supply_property hidpp_battery_props[] = {
POWER_SUPPLY_PROP_SERIAL_NUMBER, POWER_SUPPLY_PROP_SERIAL_NUMBER,
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */ 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY, */
0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */ 0, /* placeholder for POWER_SUPPLY_PROP_CAPACITY_LEVEL, */
0, /* placeholder for POWER_SUPPLY_PROP_VOLTAGE_NOW, */
}; };
static int hidpp_battery_get_property(struct power_supply *psy, static int hidpp_battery_get_property(struct power_supply *psy,
...@@ -1268,6 +1426,13 @@ static int hidpp_battery_get_property(struct power_supply *psy, ...@@ -1268,6 +1426,13 @@ static int hidpp_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_SERIAL_NUMBER: case POWER_SUPPLY_PROP_SERIAL_NUMBER:
val->strval = hidpp->hid_dev->uniq; val->strval = hidpp->hid_dev->uniq;
break; break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
/* hardware reports voltage in in mV. sysfs expects uV */
val->intval = hidpp->battery.voltage * 1000;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = hidpp->battery.charge_type;
break;
default: default:
ret = -EINVAL; ret = -EINVAL;
break; break;
...@@ -1276,6 +1441,24 @@ static int hidpp_battery_get_property(struct power_supply *psy, ...@@ -1276,6 +1441,24 @@ static int hidpp_battery_get_property(struct power_supply *psy,
return ret; return ret;
} }
/* -------------------------------------------------------------------------- */
/* 0x1d4b: Wireless device status */
/* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp)
{
u8 feature_type;
int ret;
ret = hidpp_root_get_feature(hidpp,
HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
&hidpp->wireless_feature_index,
&feature_type);
return ret;
}
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* 0x2120: Hi-resolution scrolling */ /* 0x2120: Hi-resolution scrolling */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
...@@ -3091,7 +3274,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, ...@@ -3091,7 +3274,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
} }
} }
if (unlikely(hidpp_report_is_connect_event(report))) { if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
atomic_set(&hidpp->connected, atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6))); !(report->rap.params[0] & (1 << 6)));
if (schedule_work(&hidpp->work) == 0) if (schedule_work(&hidpp->work) == 0)
...@@ -3106,6 +3289,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, ...@@ -3106,6 +3289,9 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
ret = hidpp_solar_battery_event(hidpp, data, size); ret = hidpp_solar_battery_event(hidpp, data, size);
if (ret != 0) if (ret != 0)
return ret; return ret;
ret = hidpp20_battery_voltage_event(hidpp, data, size);
if (ret != 0)
return ret;
} }
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) { if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
...@@ -3227,12 +3413,16 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) ...@@ -3227,12 +3413,16 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
hidpp->battery.feature_index = 0xff; hidpp->battery.feature_index = 0xff;
hidpp->battery.solar_feature_index = 0xff; hidpp->battery.solar_feature_index = 0xff;
hidpp->battery.voltage_feature_index = 0xff;
if (hidpp->protocol_major >= 2) { if (hidpp->protocol_major >= 2) {
if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750) if (hidpp->quirks & HIDPP_QUIRK_CLASS_K750)
ret = hidpp_solar_request_battery_event(hidpp); ret = hidpp_solar_request_battery_event(hidpp);
else else {
ret = hidpp20_query_battery_info(hidpp); ret = hidpp20_query_battery_voltage_info(hidpp);
if (ret)
ret = hidpp20_query_battery_info(hidpp);
}
if (ret) if (ret)
return ret; return ret;
...@@ -3257,7 +3447,7 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) ...@@ -3257,7 +3447,7 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
if (!battery_props) if (!battery_props)
return -ENOMEM; return -ENOMEM;
num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2; num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 3;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE) if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE)
battery_props[num_battery_props++] = battery_props[num_battery_props++] =
...@@ -3267,6 +3457,10 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) ...@@ -3267,6 +3457,10 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
battery_props[num_battery_props++] = battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_CAPACITY_LEVEL; POWER_SUPPLY_PROP_CAPACITY_LEVEL;
if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
battery_props[num_battery_props++] =
POWER_SUPPLY_PROP_VOLTAGE_NOW;
battery = &hidpp->battery; battery = &hidpp->battery;
n = atomic_inc_return(&battery_no) - 1; n = atomic_inc_return(&battery_no) - 1;
...@@ -3430,7 +3624,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) ...@@ -3430,7 +3624,10 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
else else
hidpp10_query_battery_status(hidpp); hidpp10_query_battery_status(hidpp);
} else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) { } else if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP20_BATTERY) {
hidpp20_query_battery_info(hidpp); if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_VOLTAGE)
hidpp20_query_battery_voltage_info(hidpp);
else
hidpp20_query_battery_info(hidpp);
} }
if (hidpp->battery.ps) if (hidpp->battery.ps)
power_supply_changed(hidpp->battery.ps); power_supply_changed(hidpp->battery.ps);
...@@ -3481,10 +3678,11 @@ static int hidpp_get_report_length(struct hid_device *hdev, int id) ...@@ -3481,10 +3678,11 @@ static int hidpp_get_report_length(struct hid_device *hdev, int id)
return report->field[0]->report_count + 1; return report->field[0]->report_count + 1;
} }
static bool hidpp_validate_device(struct hid_device *hdev) static u8 hidpp_validate_device(struct hid_device *hdev)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hidpp_device *hidpp = hid_get_drvdata(hdev);
int id, report_length, supported_reports = 0; int id, report_length;
u8 supported_reports = 0;
id = REPORT_ID_HIDPP_SHORT; id = REPORT_ID_HIDPP_SHORT;
report_length = hidpp_get_report_length(hdev, id); report_length = hidpp_get_report_length(hdev, id);
...@@ -3492,7 +3690,7 @@ static bool hidpp_validate_device(struct hid_device *hdev) ...@@ -3492,7 +3690,7 @@ static bool hidpp_validate_device(struct hid_device *hdev)
if (report_length < HIDPP_REPORT_SHORT_LENGTH) if (report_length < HIDPP_REPORT_SHORT_LENGTH)
goto bad_device; goto bad_device;
supported_reports++; supported_reports |= HIDPP_REPORT_SHORT_SUPPORTED;
} }
id = REPORT_ID_HIDPP_LONG; id = REPORT_ID_HIDPP_LONG;
...@@ -3501,7 +3699,7 @@ static bool hidpp_validate_device(struct hid_device *hdev) ...@@ -3501,7 +3699,7 @@ static bool hidpp_validate_device(struct hid_device *hdev)
if (report_length < HIDPP_REPORT_LONG_LENGTH) if (report_length < HIDPP_REPORT_LONG_LENGTH)
goto bad_device; goto bad_device;
supported_reports++; supported_reports |= HIDPP_REPORT_LONG_SUPPORTED;
} }
id = REPORT_ID_HIDPP_VERY_LONG; id = REPORT_ID_HIDPP_VERY_LONG;
...@@ -3511,7 +3709,7 @@ static bool hidpp_validate_device(struct hid_device *hdev) ...@@ -3511,7 +3709,7 @@ static bool hidpp_validate_device(struct hid_device *hdev)
report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH) report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH)
goto bad_device; goto bad_device;
supported_reports++; supported_reports |= HIDPP_REPORT_VERY_LONG_SUPPORTED;
hidpp->very_long_report_length = report_length; hidpp->very_long_report_length = report_length;
} }
...@@ -3560,7 +3758,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -3560,7 +3758,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* /*
* Make sure the device is HID++ capable, otherwise treat as generic HID * Make sure the device is HID++ capable, otherwise treat as generic HID
*/ */
if (!hidpp_validate_device(hdev)) { hidpp->supported_reports = hidpp_validate_device(hdev);
if (!hidpp->supported_reports) {
hid_set_drvdata(hdev, NULL); hid_set_drvdata(hdev, NULL);
devm_kfree(&hdev->dev, hidpp); devm_kfree(&hdev->dev, hidpp);
return hid_hw_start(hdev, HID_CONNECT_DEFAULT); return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
...@@ -3617,7 +3817,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -3617,7 +3817,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0) { if (ret < 0) {
dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n", dev_err(&hdev->dev, "%s:hid_hw_open returned error:%d\n",
__func__, ret); __func__, ret);
hid_hw_stop(hdev);
goto hid_hw_open_fail; goto hid_hw_open_fail;
} }
...@@ -3639,6 +3838,14 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) ...@@ -3639,6 +3838,14 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hidpp_overwrite_name(hdev); hidpp_overwrite_name(hdev);
} }
if (connected && hidpp->protocol_major >= 2) {
ret = hidpp_set_wireless_feature_index(hidpp);
if (ret == -ENOENT)
hidpp->wireless_feature_index = 0;
else if (ret)
goto hid_hw_init_fail;
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) { if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp); ret = wtp_get_config(hidpp);
if (ret) if (ret)
...@@ -3752,6 +3959,8 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -3752,6 +3959,8 @@ static const struct hid_device_id hidpp_devices[] = {
{ LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{ /* Mouse Logitech MX Master 2S */ { /* Mouse Logitech MX Master 2S */
LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{ /* Mouse Logitech MX Master 3 */
LDJ_DEVICE(0x4082), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{ /* Mouse Logitech Performance MX */ { /* Mouse Logitech Performance MX */
LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
{ /* Keyboard logitech K400 */ { /* Keyboard logitech K400 */
...@@ -3808,6 +4017,14 @@ static const struct hid_device_id hidpp_devices[] = { ...@@ -3808,6 +4017,14 @@ static const struct hid_device_id hidpp_devices[] = {
{ /* MX5500 keyboard over Bluetooth */ { /* MX5500 keyboard over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b), HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb30b),
.driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS }, .driver_data = HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS },
{ /* MX Master mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb012),
.driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e),
.driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{ /* MX Master 3 mouse over Bluetooth */
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb023),
.driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
{} {}
}; };
......
...@@ -451,6 +451,15 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd, ...@@ -451,6 +451,15 @@ static long hidraw_ioctl(struct file *file, unsigned int cmd,
-EFAULT : len; -EFAULT : len;
break; break;
} }
if (_IOC_NR(cmd) == _IOC_NR(HIDIOCGRAWUNIQ(0))) {
int len = strlen(hid->uniq) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
ret = copy_to_user(user_arg, hid->uniq, len) ?
-EFAULT : len;
break;
}
} }
ret = -ENOTTY; ret = -ENOTTY;
......
...@@ -39,6 +39,7 @@ struct hidraw_devinfo { ...@@ -39,6 +39,7 @@ struct hidraw_devinfo {
/* The first byte of SFEATURE and GFEATURE is the report number */ /* The first byte of SFEATURE and GFEATURE is the report number */
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) #define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) #define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#define HIDIOCGRAWUNIQ(len) _IOC(_IOC_READ, 'H', 0x08, len)
#define HIDRAW_FIRST_MINOR 0 #define HIDRAW_FIRST_MINOR 0
#define HIDRAW_MAX_DEVICES 64 #define HIDRAW_MAX_DEVICES 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