Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina: - high-resolution scrolling support that gracefully handles differences between MS and Logitech implementations in HW, from Peter Hutterer and Harry Cutts - MSI IRQ support for intel-ish driver, from Song Hongyan - support for new hardware (Cougar 700K, Odys Winbook 13, ASUS FX503VD, ASUS T101HA) from Daniel M. Lambea, Hans de Goede and Aleix Roca Nonell - other small assorted fixups * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: (22 commits) HID: i2c-hid: Add Odys Winbook 13 to descriptor override HID: lenovo: Add checks to fix of_led_classdev_register HID: intel-ish-hid: add MSI interrupt support HID: debug: Change to use DEFINE_SHOW_ATTRIBUTE macro HID: doc: fix wrong data structure reference for UHID_OUTPUT HID: intel-ish-hid: fixes incorrect error handling HID: asus: Add support for the ASUS T101HA keyboard dock HID: logitech: Use LDJ_DEVICE macro for existing Logitech mice HID: logitech: Enable high-resolution scrolling on Logitech mice HID: logitech: Add function to enable HID++ 1.0 "scrolling acceleration" HID: logitech-hidpp: fix typo, hiddpp to hidpp HID: input: use the Resolution Multiplier for high-resolution scrolling HID: core: process the Resolution Multiplier HID: core: store the collections as a basic tree Input: add `REL_WHEEL_HI_RES` and `REL_HWHEEL_HI_RES` HID: input: support Microsoft wireless radio control hotkey HID: use macros in IS_INPUT_APPLICATION HID: asus: Add support for the ASUS FX503VD laptop HID: asus: Add event handler to catch unmapped Asus Vendor UsagePage codes HID: cougar: Add support for Cougar 700K Gaming Keyboard ...
This commit is contained in:
commit
cf26057a94
|
@ -160,7 +160,7 @@ them but you should handle them according to your needs.
|
|||
UHID_OUTPUT:
|
||||
This is sent if the HID device driver wants to send raw data to the I/O
|
||||
device on the interrupt channel. You should read the payload and forward it to
|
||||
the device. The payload is of type "struct uhid_data_req".
|
||||
the device. The payload is of type "struct uhid_output_req".
|
||||
This may be received even though you haven't received UHID_OPEN, yet.
|
||||
|
||||
UHID_GET_REPORT:
|
||||
|
|
|
@ -190,7 +190,26 @@ A few EV_REL codes have special meanings:
|
|||
* REL_WHEEL, REL_HWHEEL:
|
||||
|
||||
- These codes are used for vertical and horizontal scroll wheels,
|
||||
respectively.
|
||||
respectively. The value is the number of detents moved on the wheel, the
|
||||
physical size of which varies by device. For high-resolution wheels
|
||||
this may be an approximation based on the high-resolution scroll events,
|
||||
see REL_WHEEL_HI_RES. These event codes are legacy codes and
|
||||
REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES should be preferred where
|
||||
available.
|
||||
|
||||
* REL_WHEEL_HI_RES, REL_HWHEEL_HI_RES:
|
||||
|
||||
- High-resolution scroll wheel data. The accumulated value 120 represents
|
||||
movement by one detent. For devices that do not provide high-resolution
|
||||
scrolling, the value is always a multiple of 120. For devices with
|
||||
high-resolution scrolling, the value may be a fraction of 120.
|
||||
|
||||
If a vertical scroll wheel supports high-resolution scrolling, this code
|
||||
will be emitted in addition to REL_WHEEL or REL_HWHEEL. The REL_WHEEL
|
||||
and REL_HWHEEL may be an approximation based on the high-resolution
|
||||
scroll events. There is no guarantee that the high-resolution data
|
||||
is a multiple of 120 at the time of an emulated REL_WHEEL or REL_HWHEEL
|
||||
event.
|
||||
|
||||
EV_ABS
|
||||
------
|
||||
|
|
|
@ -70,6 +70,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
|
|||
#define QUIRK_T100_KEYBOARD BIT(6)
|
||||
#define QUIRK_T100CHI BIT(7)
|
||||
#define QUIRK_G752_KEYBOARD BIT(8)
|
||||
#define QUIRK_T101HA_DOCK BIT(9)
|
||||
|
||||
#define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
|
||||
QUIRK_NO_INIT_REPORTS | \
|
||||
|
@ -241,6 +242,18 @@ static int asus_report_input(struct asus_drvdata *drvdat, u8 *data, int size)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int asus_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
if ((usage->hid & HID_USAGE_PAGE) == 0xff310000 &&
|
||||
(usage->hid & HID_USAGE) != 0x00 && !usage->type) {
|
||||
hid_warn(hdev, "Unmapped Asus vendor usagepage code 0x%02x\n",
|
||||
usage->hid & HID_USAGE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
|
@ -510,6 +523,7 @@ static int asus_input_mapping(struct hid_device *hdev,
|
|||
case 0x20: asus_map_key_clear(KEY_BRIGHTNESSUP); break;
|
||||
case 0x35: asus_map_key_clear(KEY_DISPLAY_OFF); break;
|
||||
case 0x6c: asus_map_key_clear(KEY_SLEEP); break;
|
||||
case 0x7c: asus_map_key_clear(KEY_MICMUTE); break;
|
||||
case 0x82: asus_map_key_clear(KEY_CAMERA); break;
|
||||
case 0x88: asus_map_key_clear(KEY_RFKILL); break;
|
||||
case 0xb5: asus_map_key_clear(KEY_CALC); break;
|
||||
|
@ -528,6 +542,9 @@ static int asus_input_mapping(struct hid_device *hdev,
|
|||
/* Fn+Space Power4Gear Hybrid */
|
||||
case 0x5c: asus_map_key_clear(KEY_PROG3); break;
|
||||
|
||||
/* Fn+F5 "fan" symbol on FX503VD */
|
||||
case 0x99: asus_map_key_clear(KEY_PROG4); break;
|
||||
|
||||
default:
|
||||
/* ASUS lazily declares 256 usages, ignore the rest,
|
||||
* as some make the keyboard appear as a pointer device. */
|
||||
|
@ -683,6 +700,11 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* use hid-multitouch for T101HA touchpad */
|
||||
if (id->driver_data & QUIRK_T101HA_DOCK &&
|
||||
hdev->collection->usage == HID_GD_MOUSE)
|
||||
return -ENODEV;
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "Asus hw start failed: %d\n", ret);
|
||||
|
@ -805,12 +827,17 @@ static const struct hid_device_id asus_devices[] = {
|
|||
USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2), QUIRK_USE_KBD_BACKLIGHT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3), QUIRK_G752_KEYBOARD },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD),
|
||||
QUIRK_USE_KBD_BACKLIGHT },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD),
|
||||
QUIRK_T100_KEYBOARD | QUIRK_NO_CONSUMER_USAGES },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
|
||||
USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD), QUIRK_T101HA_DOCK },
|
||||
{ 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) },
|
||||
|
@ -832,6 +859,7 @@ static struct hid_driver asus_driver = {
|
|||
#ifdef CONFIG_PM
|
||||
.reset_resume = asus_reset_resume,
|
||||
#endif
|
||||
.event = asus_event,
|
||||
.raw_event = asus_raw_event
|
||||
};
|
||||
module_hid_driver(asus_driver);
|
||||
|
|
|
@ -172,6 +172,8 @@ static int open_collection(struct hid_parser *parser, unsigned type)
|
|||
collection->type = type;
|
||||
collection->usage = usage;
|
||||
collection->level = parser->collection_stack_ptr - 1;
|
||||
collection->parent = parser->active_collection;
|
||||
parser->active_collection = collection;
|
||||
|
||||
if (type == HID_COLLECTION_APPLICATION)
|
||||
parser->device->maxapplication++;
|
||||
|
@ -190,6 +192,8 @@ static int close_collection(struct hid_parser *parser)
|
|||
return -EINVAL;
|
||||
}
|
||||
parser->collection_stack_ptr--;
|
||||
if (parser->active_collection)
|
||||
parser->active_collection = parser->active_collection->parent;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -290,6 +294,7 @@ static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsign
|
|||
field->usage[i].collection_index =
|
||||
parser->local.collection_index[j];
|
||||
field->usage[i].usage_index = i;
|
||||
field->usage[i].resolution_multiplier = 1;
|
||||
}
|
||||
|
||||
field->maxusage = usages;
|
||||
|
@ -943,6 +948,167 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hid_validate_values);
|
||||
|
||||
static int hid_calculate_multiplier(struct hid_device *hid,
|
||||
struct hid_field *multiplier)
|
||||
{
|
||||
int m;
|
||||
__s32 v = *multiplier->value;
|
||||
__s32 lmin = multiplier->logical_minimum;
|
||||
__s32 lmax = multiplier->logical_maximum;
|
||||
__s32 pmin = multiplier->physical_minimum;
|
||||
__s32 pmax = multiplier->physical_maximum;
|
||||
|
||||
/*
|
||||
* "Because OS implementations will generally divide the control's
|
||||
* reported count by the Effective Resolution Multiplier, designers
|
||||
* should take care not to establish a potential Effective
|
||||
* Resolution Multiplier of zero."
|
||||
* HID Usage Table, v1.12, Section 4.3.1, p31
|
||||
*/
|
||||
if (lmax - lmin == 0)
|
||||
return 1;
|
||||
/*
|
||||
* Handling the unit exponent is left as an exercise to whoever
|
||||
* finds a device where that exponent is not 0.
|
||||
*/
|
||||
m = ((v - lmin)/(lmax - lmin) * (pmax - pmin) + pmin);
|
||||
if (unlikely(multiplier->unit_exponent != 0)) {
|
||||
hid_warn(hid,
|
||||
"unsupported Resolution Multiplier unit exponent %d\n",
|
||||
multiplier->unit_exponent);
|
||||
}
|
||||
|
||||
/* There are no devices with an effective multiplier > 255 */
|
||||
if (unlikely(m == 0 || m > 255 || m < -255)) {
|
||||
hid_warn(hid, "unsupported Resolution Multiplier %d\n", m);
|
||||
m = 1;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static void hid_apply_multiplier_to_field(struct hid_device *hid,
|
||||
struct hid_field *field,
|
||||
struct hid_collection *multiplier_collection,
|
||||
int effective_multiplier)
|
||||
{
|
||||
struct hid_collection *collection;
|
||||
struct hid_usage *usage;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* If multiplier_collection is NULL, the multiplier applies
|
||||
* to all fields in the report.
|
||||
* Otherwise, it is the Logical Collection the multiplier applies to
|
||||
* but our field may be in a subcollection of that collection.
|
||||
*/
|
||||
for (i = 0; i < field->maxusage; i++) {
|
||||
usage = &field->usage[i];
|
||||
|
||||
collection = &hid->collection[usage->collection_index];
|
||||
while (collection && collection != multiplier_collection)
|
||||
collection = collection->parent;
|
||||
|
||||
if (collection || multiplier_collection == NULL)
|
||||
usage->resolution_multiplier = effective_multiplier;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_apply_multiplier(struct hid_device *hid,
|
||||
struct hid_field *multiplier)
|
||||
{
|
||||
struct hid_report_enum *rep_enum;
|
||||
struct hid_report *rep;
|
||||
struct hid_field *field;
|
||||
struct hid_collection *multiplier_collection;
|
||||
int effective_multiplier;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* "The Resolution Multiplier control must be contained in the same
|
||||
* Logical Collection as the control(s) to which it is to be applied.
|
||||
* If no Resolution Multiplier is defined, then the Resolution
|
||||
* Multiplier defaults to 1. If more than one control exists in a
|
||||
* Logical Collection, the Resolution Multiplier is associated with
|
||||
* all controls in the collection. If no Logical Collection is
|
||||
* defined, the Resolution Multiplier is associated with all
|
||||
* controls in the report."
|
||||
* HID Usage Table, v1.12, Section 4.3.1, p30
|
||||
*
|
||||
* Thus, search from the current collection upwards until we find a
|
||||
* logical collection. Then search all fields for that same parent
|
||||
* collection. Those are the fields the multiplier applies to.
|
||||
*
|
||||
* If we have more than one multiplier, it will overwrite the
|
||||
* applicable fields later.
|
||||
*/
|
||||
multiplier_collection = &hid->collection[multiplier->usage->collection_index];
|
||||
while (multiplier_collection &&
|
||||
multiplier_collection->type != HID_COLLECTION_LOGICAL)
|
||||
multiplier_collection = multiplier_collection->parent;
|
||||
|
||||
effective_multiplier = hid_calculate_multiplier(hid, multiplier);
|
||||
|
||||
rep_enum = &hid->report_enum[HID_INPUT_REPORT];
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list) {
|
||||
for (i = 0; i < rep->maxfield; i++) {
|
||||
field = rep->field[i];
|
||||
hid_apply_multiplier_to_field(hid, field,
|
||||
multiplier_collection,
|
||||
effective_multiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* hid_setup_resolution_multiplier - set up all resolution multipliers
|
||||
*
|
||||
* @device: hid device
|
||||
*
|
||||
* Search for all Resolution Multiplier Feature Reports and apply their
|
||||
* value to all matching Input items. This only updates the internal struct
|
||||
* fields.
|
||||
*
|
||||
* The Resolution Multiplier is applied by the hardware. If the multiplier
|
||||
* is anything other than 1, the hardware will send pre-multiplied events
|
||||
* so that the same physical interaction generates an accumulated
|
||||
* accumulated_value = value * * multiplier
|
||||
* This may be achieved by sending
|
||||
* - "value * multiplier" for each event, or
|
||||
* - "value" but "multiplier" times as frequently, or
|
||||
* - a combination of the above
|
||||
* The only guarantee is that the same physical interaction always generates
|
||||
* an accumulated 'value * multiplier'.
|
||||
*
|
||||
* This function must be called before any event processing and after
|
||||
* any SetRequest to the Resolution Multiplier.
|
||||
*/
|
||||
void hid_setup_resolution_multiplier(struct hid_device *hid)
|
||||
{
|
||||
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];
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list) {
|
||||
for (i = 0; i < rep->maxfield; i++) {
|
||||
/* Ignore if report count is out of bounds. */
|
||||
if (rep->field[i]->report_count < 1)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < rep->field[i]->maxusage; j++) {
|
||||
usage = &rep->field[i]->usage[j];
|
||||
if (usage->hid == HID_GD_RESOLUTION_MULTIPLIER)
|
||||
hid_apply_multiplier(hid,
|
||||
rep->field[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hid_setup_resolution_multiplier);
|
||||
|
||||
/**
|
||||
* hid_open_report - open a driver-specific device report
|
||||
*
|
||||
|
@ -1039,9 +1205,17 @@ int hid_open_report(struct hid_device *device)
|
|||
hid_err(device, "unbalanced delimiter at end of report description\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* fetch initial values in case the device's
|
||||
* default multiplier isn't the recommended 1
|
||||
*/
|
||||
hid_setup_resolution_multiplier(device);
|
||||
|
||||
kfree(parser->collection_stack);
|
||||
vfree(parser);
|
||||
device->status |= HID_STAT_PARSED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -326,6 +326,8 @@ module_param_cb(g6_is_space, &cougar_g6_is_space_ops, &g6_is_space, 0644);
|
|||
static struct hid_device_id cougar_id_table[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
|
||||
USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_SOLID_YEAR,
|
||||
USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD) },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, cougar_id_table);
|
||||
|
|
|
@ -1072,11 +1072,6 @@ static int hid_debug_rdesc_show(struct seq_file *f, void *p)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, hid_debug_rdesc_show, inode->i_private);
|
||||
}
|
||||
|
||||
static int hid_debug_events_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = 0;
|
||||
|
@ -1211,12 +1206,7 @@ static int hid_debug_events_release(struct inode *inode, struct file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations hid_debug_rdesc_fops = {
|
||||
.open = hid_debug_rdesc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(hid_debug_rdesc);
|
||||
|
||||
static const struct file_operations hid_debug_events_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
@ -187,12 +187,14 @@
|
|||
#define USB_DEVICE_ID_ASUSTEK_T100TA_KEYBOARD 0x17e0
|
||||
#define USB_DEVICE_ID_ASUSTEK_T100TAF_KEYBOARD 0x1807
|
||||
#define USB_DEVICE_ID_ASUSTEK_T100CHI_KEYBOARD 0x8502
|
||||
#define USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD 0x183d
|
||||
#define USB_DEVICE_ID_ASUSTEK_T304_KEYBOARD 0x184a
|
||||
#define USB_DEVICE_ID_ASUSTEK_I2C_KEYBOARD 0x8585
|
||||
#define USB_DEVICE_ID_ASUSTEK_I2C_TOUCHPAD 0x0101
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD1 0x1854
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD2 0x1837
|
||||
#define USB_DEVICE_ID_ASUSTEK_ROG_KEYBOARD3 0x1822
|
||||
#define USB_DEVICE_ID_ASUSTEK_FX503VD_KEYBOARD 0x1869
|
||||
|
||||
#define USB_VENDOR_ID_ATEN 0x0557
|
||||
#define USB_DEVICE_ID_ATEN_UC100KM 0x2004
|
||||
|
@ -1025,6 +1027,7 @@
|
|||
|
||||
#define USB_VENDOR_ID_SOLID_YEAR 0x060b
|
||||
#define USB_DEVICE_ID_COUGAR_500K_GAMING_KEYBOARD 0x500a
|
||||
#define USB_DEVICE_ID_COUGAR_700K_GAMING_KEYBOARD 0x700a
|
||||
|
||||
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
|
||||
#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
|
||||
|
|
|
@ -712,7 +712,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
map_abs_clear(usage->hid & 0xf);
|
||||
break;
|
||||
|
||||
case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL:
|
||||
case HID_GD_WHEEL:
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
|
||||
set_bit(REL_WHEEL, input->relbit);
|
||||
map_rel(REL_WHEEL_HI_RES);
|
||||
} else {
|
||||
map_abs(usage->hid & 0xf);
|
||||
}
|
||||
break;
|
||||
case HID_GD_SLIDER: case HID_GD_DIAL:
|
||||
if (field->flags & HID_MAIN_ITEM_RELATIVE)
|
||||
map_rel(usage->hid & 0xf);
|
||||
else
|
||||
|
@ -1012,7 +1020,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
case 0x22f: map_key_clear(KEY_ZOOMRESET); break;
|
||||
case 0x233: map_key_clear(KEY_SCROLLUP); break;
|
||||
case 0x234: map_key_clear(KEY_SCROLLDOWN); break;
|
||||
case 0x238: map_rel(REL_HWHEEL); break;
|
||||
case 0x238: /* AC Pan */
|
||||
set_bit(REL_HWHEEL, input->relbit);
|
||||
map_rel(REL_HWHEEL_HI_RES);
|
||||
break;
|
||||
case 0x23d: map_key_clear(KEY_EDIT); break;
|
||||
case 0x25f: map_key_clear(KEY_CANCEL); break;
|
||||
case 0x269: map_key_clear(KEY_INSERT); break;
|
||||
|
@ -1200,6 +1211,38 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
|
|||
|
||||
}
|
||||
|
||||
static void hidinput_handle_scroll(struct hid_usage *usage,
|
||||
struct input_dev *input,
|
||||
__s32 value)
|
||||
{
|
||||
int code;
|
||||
int hi_res, lo_res;
|
||||
|
||||
if (value == 0)
|
||||
return;
|
||||
|
||||
if (usage->code == REL_WHEEL_HI_RES)
|
||||
code = REL_WHEEL;
|
||||
else
|
||||
code = REL_HWHEEL;
|
||||
|
||||
/*
|
||||
* Windows reports one wheel click as value 120. Where a high-res
|
||||
* scroll wheel is present, a fraction of 120 is reported instead.
|
||||
* Our REL_WHEEL_HI_RES axis does the same because all HW must
|
||||
* adhere to the 120 expectation.
|
||||
*/
|
||||
hi_res = value * 120/usage->resolution_multiplier;
|
||||
|
||||
usage->wheel_accumulated += hi_res;
|
||||
lo_res = usage->wheel_accumulated/120;
|
||||
if (lo_res)
|
||||
usage->wheel_accumulated -= lo_res * 120;
|
||||
|
||||
input_event(input, EV_REL, code, lo_res);
|
||||
input_event(input, EV_REL, usage->code, hi_res);
|
||||
}
|
||||
|
||||
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
struct input_dev *input;
|
||||
|
@ -1262,6 +1305,12 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
|
|||
if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
|
||||
return;
|
||||
|
||||
if ((usage->type == EV_REL) && (usage->code == REL_WHEEL_HI_RES ||
|
||||
usage->code == REL_HWHEEL_HI_RES)) {
|
||||
hidinput_handle_scroll(usage, input, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((usage->type == EV_ABS) && (field->flags & HID_MAIN_ITEM_RELATIVE) &&
|
||||
(usage->code == ABS_VOLUME)) {
|
||||
int count = abs(value);
|
||||
|
@ -1489,6 +1538,58 @@ static void hidinput_close(struct input_dev *dev)
|
|||
hid_hw_close(hid);
|
||||
}
|
||||
|
||||
static void hidinput_change_resolution_multipliers(struct hid_device *hid)
|
||||
{
|
||||
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];
|
||||
list_for_each_entry(rep, &rep_enum->report_list, list) {
|
||||
bool update_needed = false;
|
||||
|
||||
if (rep->maxfield == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If we have more than one feature within this report we
|
||||
* need to fill in the bits from the others before we can
|
||||
* overwrite the ones for the Resolution Multiplier.
|
||||
*/
|
||||
if (rep->maxfield > 1) {
|
||||
hid_hw_request(hid, rep, HID_REQ_GET_REPORT);
|
||||
hid_hw_wait(hid);
|
||||
}
|
||||
|
||||
for (i = 0; i < rep->maxfield; i++) {
|
||||
__s32 logical_max = rep->field[i]->logical_maximum;
|
||||
|
||||
/* There is no good reason for a Resolution
|
||||
* Multiplier to have a count other than 1.
|
||||
* Ignore that case.
|
||||
*/
|
||||
if (rep->field[i]->report_count != 1)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < rep->field[i]->maxusage; j++) {
|
||||
usage = &rep->field[i]->usage[j];
|
||||
|
||||
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
|
||||
continue;
|
||||
|
||||
*rep->field[i]->value = logical_max;
|
||||
update_needed = true;
|
||||
}
|
||||
}
|
||||
if (update_needed)
|
||||
hid_hw_request(hid, rep, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
/* refresh our structs */
|
||||
hid_setup_resolution_multiplier(hid);
|
||||
}
|
||||
|
||||
static void report_features(struct hid_device *hid)
|
||||
{
|
||||
struct hid_driver *drv = hid->driver;
|
||||
|
@ -1782,6 +1883,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
|
|||
}
|
||||
}
|
||||
|
||||
hidinput_change_resolution_multipliers(hid);
|
||||
|
||||
list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
|
||||
if (drv->input_configured &&
|
||||
drv->input_configured(hid, hidinput))
|
||||
|
@ -1840,4 +1943,3 @@ void hidinput_disconnect(struct hid_device *hid)
|
|||
cancel_work_sync(&hid->led_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hidinput_disconnect);
|
||||
|
||||
|
|
|
@ -743,7 +743,9 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
|||
data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
|
||||
data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
|
||||
data_pointer->led_mute.dev = dev;
|
||||
led_classdev_register(dev, &data_pointer->led_mute);
|
||||
ret = led_classdev_register(dev, &data_pointer->led_mute);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
data_pointer->led_micmute.name = name_micmute;
|
||||
data_pointer->led_micmute.brightness_get =
|
||||
|
@ -751,7 +753,11 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
|||
data_pointer->led_micmute.brightness_set =
|
||||
lenovo_led_brightness_set_tpkbd;
|
||||
data_pointer->led_micmute.dev = dev;
|
||||
led_classdev_register(dev, &data_pointer->led_micmute);
|
||||
ret = led_classdev_register(dev, &data_pointer->led_micmute);
|
||||
if (ret < 0) {
|
||||
led_classdev_unregister(&data_pointer->led_mute);
|
||||
goto err;
|
||||
}
|
||||
|
||||
lenovo_features_set_tpkbd(hdev);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
@ -64,6 +65,14 @@ MODULE_PARM_DESC(disable_tap_to_click,
|
|||
#define HIDPP_QUIRK_NO_HIDINPUT BIT(23)
|
||||
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
|
||||
#define HIDPP_QUIRK_UNIFYING BIT(25)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27)
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28)
|
||||
|
||||
/* Convenience constant to check for any high-res support. */
|
||||
#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \
|
||||
HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \
|
||||
HIDPP_QUIRK_HI_RES_SCROLL_X2121)
|
||||
|
||||
#define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT
|
||||
|
||||
|
@ -128,6 +137,25 @@ struct hidpp_battery {
|
|||
bool online;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct hidpp_scroll_counter - Utility class for processing high-resolution
|
||||
* scroll events.
|
||||
* @dev: the input device for which events should be reported.
|
||||
* @wheel_multiplier: the scalar multiplier to be applied to each wheel event
|
||||
* @remainder: counts the number of high-resolution units moved since the last
|
||||
* low-resolution event (REL_WHEEL or REL_HWHEEL) was sent. Should
|
||||
* only be used by class methods.
|
||||
* @direction: direction of last movement (1 or -1)
|
||||
* @last_time: last event time, used to reset remainder after inactivity
|
||||
*/
|
||||
struct hidpp_scroll_counter {
|
||||
struct input_dev *dev;
|
||||
int wheel_multiplier;
|
||||
int remainder;
|
||||
int direction;
|
||||
unsigned long long last_time;
|
||||
};
|
||||
|
||||
struct hidpp_device {
|
||||
struct hid_device *hid_dev;
|
||||
struct mutex send_mutex;
|
||||
|
@ -149,6 +177,7 @@ struct hidpp_device {
|
|||
unsigned long capabilities;
|
||||
|
||||
struct hidpp_battery battery;
|
||||
struct hidpp_scroll_counter vertical_wheel_counter;
|
||||
};
|
||||
|
||||
/* HID++ 1.0 error codes */
|
||||
|
@ -391,6 +420,67 @@ static void hidpp_prefix_name(char **name, int name_length)
|
|||
*name = new_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidpp_scroll_counter_handle_scroll() - Send high- and low-resolution scroll
|
||||
* events given a high-resolution wheel
|
||||
* movement.
|
||||
* @counter: a hid_scroll_counter struct describing the wheel.
|
||||
* @hi_res_value: the movement of the wheel, in the mouse's high-resolution
|
||||
* units.
|
||||
*
|
||||
* Given a high-resolution movement, this function converts the movement into
|
||||
* fractions of 120 and emits high-resolution scroll events for the input
|
||||
* device. It also uses the multiplier from &struct hid_scroll_counter to
|
||||
* emit low-resolution scroll events when appropriate for
|
||||
* backwards-compatibility with userspace input libraries.
|
||||
*/
|
||||
static void hidpp_scroll_counter_handle_scroll(struct hidpp_scroll_counter *counter,
|
||||
int hi_res_value)
|
||||
{
|
||||
int low_res_value, remainder, direction;
|
||||
unsigned long long now, previous;
|
||||
|
||||
hi_res_value = hi_res_value * 120/counter->wheel_multiplier;
|
||||
input_report_rel(counter->dev, REL_WHEEL_HI_RES, hi_res_value);
|
||||
|
||||
remainder = counter->remainder;
|
||||
direction = hi_res_value > 0 ? 1 : -1;
|
||||
|
||||
now = sched_clock();
|
||||
previous = counter->last_time;
|
||||
counter->last_time = now;
|
||||
/*
|
||||
* Reset the remainder after a period of inactivity or when the
|
||||
* direction changes. This prevents the REL_WHEEL emulation point
|
||||
* from sliding for devices that don't always provide the same
|
||||
* number of movements per detent.
|
||||
*/
|
||||
if (now - previous > 1000000000 || direction != counter->direction)
|
||||
remainder = 0;
|
||||
|
||||
counter->direction = direction;
|
||||
remainder += hi_res_value;
|
||||
|
||||
/* Some wheels will rest 7/8ths of a detent from the previous detent
|
||||
* after slow movement, so we want the threshold for low-res events to
|
||||
* be in the middle between two detents (e.g. after 4/8ths) as
|
||||
* opposed to on the detents themselves (8/8ths).
|
||||
*/
|
||||
if (abs(remainder) >= 60) {
|
||||
/* Add (or subtract) 1 because we want to trigger when the wheel
|
||||
* is half-way to the next detent (i.e. scroll 1 detent after a
|
||||
* 1/2 detent movement, 2 detents after a 1 1/2 detent movement,
|
||||
* etc.).
|
||||
*/
|
||||
low_res_value = remainder / 120;
|
||||
if (low_res_value == 0)
|
||||
low_res_value = (hi_res_value > 0 ? 1 : -1);
|
||||
input_report_rel(counter->dev, REL_WHEEL, low_res_value);
|
||||
remainder -= low_res_value * 120;
|
||||
}
|
||||
counter->remainder = remainder;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* HIDP++ 1.0 commands */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -400,32 +490,53 @@ static void hidpp_prefix_name(char **name, int name_length)
|
|||
#define HIDPP_SET_LONG_REGISTER 0x82
|
||||
#define HIDPP_GET_LONG_REGISTER 0x83
|
||||
|
||||
#define HIDPP_REG_GENERAL 0x00
|
||||
|
||||
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
||||
/**
|
||||
* hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register.
|
||||
* @hidpp_dev: the device to set the register on.
|
||||
* @register_address: the address of the register to modify.
|
||||
* @byte: the byte of the register to modify. Should be less than 3.
|
||||
* Return: 0 if successful, otherwise a negative error code.
|
||||
*/
|
||||
static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev,
|
||||
u8 register_address, u8 byte, u8 bit)
|
||||
{
|
||||
struct hidpp_report response;
|
||||
int ret;
|
||||
u8 params[3] = { 0 };
|
||||
|
||||
ret = hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
NULL, 0, &response);
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_GET_REGISTER,
|
||||
register_address,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(params, response.rap.params, 3);
|
||||
|
||||
/* Set the battery bit */
|
||||
params[0] |= BIT(4);
|
||||
params[byte] |= BIT(bit);
|
||||
|
||||
return hidpp_send_rap_command_sync(hidpp_dev,
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_SET_REGISTER,
|
||||
HIDPP_REG_GENERAL,
|
||||
params, 3, &response);
|
||||
REPORT_ID_HIDPP_SHORT,
|
||||
HIDPP_SET_REGISTER,
|
||||
register_address,
|
||||
params, 3, &response);
|
||||
}
|
||||
|
||||
|
||||
#define HIDPP_REG_GENERAL 0x00
|
||||
|
||||
static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev)
|
||||
{
|
||||
return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4);
|
||||
}
|
||||
|
||||
#define HIDPP_REG_FEATURES 0x01
|
||||
|
||||
/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */
|
||||
static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev)
|
||||
{
|
||||
return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6);
|
||||
}
|
||||
|
||||
#define HIDPP_REG_BATTERY_STATUS 0x07
|
||||
|
@ -1136,6 +1247,99 @@ static int hidpp_battery_get_property(struct power_supply *psy,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x2120: Hi-resolution scrolling */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120
|
||||
|
||||
#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10
|
||||
|
||||
static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp,
|
||||
bool enabled, u8 *multiplier)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
u8 params[1];
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp,
|
||||
HIDPP_PAGE_HI_RESOLUTION_SCROLLING,
|
||||
&feature_index,
|
||||
&feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
params[0] = enabled ? BIT(0) : 0;
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE,
|
||||
params, sizeof(params), &response);
|
||||
if (ret)
|
||||
return ret;
|
||||
*multiplier = response.fap.params[1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x2121: HiRes Wheel */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define HIDPP_PAGE_HIRES_WHEEL 0x2121
|
||||
|
||||
#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00
|
||||
#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20
|
||||
|
||||
static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp,
|
||||
u8 *multiplier)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
||||
&feature_index, &feature_type);
|
||||
if (ret)
|
||||
goto return_default;
|
||||
|
||||
ret = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY,
|
||||
NULL, 0, &response);
|
||||
if (ret)
|
||||
goto return_default;
|
||||
|
||||
*multiplier = response.fap.params[0];
|
||||
return 0;
|
||||
return_default:
|
||||
hid_warn(hidpp->hid_dev,
|
||||
"Couldn't get wheel multiplier (error %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert,
|
||||
bool high_resolution, bool use_hidpp)
|
||||
{
|
||||
u8 feature_index;
|
||||
u8 feature_type;
|
||||
int ret;
|
||||
u8 params[1];
|
||||
struct hidpp_report response;
|
||||
|
||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL,
|
||||
&feature_index, &feature_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
params[0] = (invert ? BIT(2) : 0) |
|
||||
(high_resolution ? BIT(1) : 0) |
|
||||
(use_hidpp ? BIT(0) : 0);
|
||||
|
||||
return hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
CMD_HIRES_WHEEL_SET_WHEEL_MODE,
|
||||
params, sizeof(params), &response);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 0x4301: Solar Keyboard */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -1465,7 +1669,7 @@ struct hidpp_ff_work_data {
|
|||
u8 size;
|
||||
};
|
||||
|
||||
static const signed short hiddpp_ff_effects[] = {
|
||||
static const signed short hidpp_ff_effects[] = {
|
||||
FF_CONSTANT,
|
||||
FF_PERIODIC,
|
||||
FF_SINE,
|
||||
|
@ -1480,7 +1684,7 @@ static const signed short hiddpp_ff_effects[] = {
|
|||
-1
|
||||
};
|
||||
|
||||
static const signed short hiddpp_ff_effects_v2[] = {
|
||||
static const signed short hidpp_ff_effects_v2[] = {
|
||||
FF_RAMP,
|
||||
FF_FRICTION,
|
||||
FF_INERTIA,
|
||||
|
@ -1873,11 +2077,11 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
|
|||
version = bcdDevice & 255;
|
||||
|
||||
/* Set supported force feedback capabilities */
|
||||
for (j = 0; hiddpp_ff_effects[j] >= 0; j++)
|
||||
set_bit(hiddpp_ff_effects[j], dev->ffbit);
|
||||
for (j = 0; hidpp_ff_effects[j] >= 0; j++)
|
||||
set_bit(hidpp_ff_effects[j], dev->ffbit);
|
||||
if (version > 1)
|
||||
for (j = 0; hiddpp_ff_effects_v2[j] >= 0; j++)
|
||||
set_bit(hiddpp_ff_effects_v2[j], dev->ffbit);
|
||||
for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++)
|
||||
set_bit(hidpp_ff_effects_v2[j], dev->ffbit);
|
||||
|
||||
/* Read number of slots available in device */
|
||||
error = hidpp_send_fap_command_sync(hidpp, feature_index,
|
||||
|
@ -2387,10 +2591,15 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
|
|||
input_report_key(mydata->input, BTN_RIGHT,
|
||||
!!(data[1] & M560_MOUSE_BTN_RIGHT));
|
||||
|
||||
if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT)
|
||||
if (data[1] & M560_MOUSE_BTN_WHEEL_LEFT) {
|
||||
input_report_rel(mydata->input, REL_HWHEEL, -1);
|
||||
else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT)
|
||||
input_report_rel(mydata->input, REL_HWHEEL_HI_RES,
|
||||
-120);
|
||||
} else if (data[1] & M560_MOUSE_BTN_WHEEL_RIGHT) {
|
||||
input_report_rel(mydata->input, REL_HWHEEL, 1);
|
||||
input_report_rel(mydata->input, REL_HWHEEL_HI_RES,
|
||||
120);
|
||||
}
|
||||
|
||||
v = hid_snto32(hid_field_extract(hdev, data+3, 0, 12), 12);
|
||||
input_report_rel(mydata->input, REL_X, v);
|
||||
|
@ -2399,7 +2608,8 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size)
|
|||
input_report_rel(mydata->input, REL_Y, v);
|
||||
|
||||
v = hid_snto32(data[6], 8);
|
||||
input_report_rel(mydata->input, REL_WHEEL, v);
|
||||
hidpp_scroll_counter_handle_scroll(
|
||||
&hidpp->vertical_wheel_counter, v);
|
||||
|
||||
input_sync(mydata->input);
|
||||
}
|
||||
|
@ -2426,6 +2636,8 @@ static void m560_populate_input(struct hidpp_device *hidpp,
|
|||
__set_bit(REL_Y, mydata->input->relbit);
|
||||
__set_bit(REL_WHEEL, mydata->input->relbit);
|
||||
__set_bit(REL_HWHEEL, mydata->input->relbit);
|
||||
__set_bit(REL_WHEEL_HI_RES, mydata->input->relbit);
|
||||
__set_bit(REL_HWHEEL_HI_RES, mydata->input->relbit);
|
||||
}
|
||||
|
||||
static int m560_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
||||
|
@ -2527,6 +2739,37 @@ static int g920_get_config(struct hidpp_device *hidpp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* High-resolution scroll wheels */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int hi_res_scroll_enable(struct hidpp_device *hidpp)
|
||||
{
|
||||
int ret;
|
||||
u8 multiplier = 1;
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) {
|
||||
ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false);
|
||||
if (ret == 0)
|
||||
ret = hidpp_hrw_get_wheel_capability(hidpp, &multiplier);
|
||||
} else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) {
|
||||
ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true,
|
||||
&multiplier);
|
||||
} else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ {
|
||||
ret = hidpp10_enable_scrolling_acceleration(hidpp);
|
||||
multiplier = 8;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (multiplier == 0)
|
||||
multiplier = 1;
|
||||
|
||||
hidpp->vertical_wheel_counter.wheel_multiplier = multiplier;
|
||||
hid_info(hidpp->hid_dev, "multiplier = %d\n", multiplier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Generic HID++ devices */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
@ -2572,6 +2815,9 @@ static void hidpp_populate_input(struct hidpp_device *hidpp,
|
|||
wtp_populate_input(hidpp, input, origin_is_hid_core);
|
||||
else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560)
|
||||
m560_populate_input(hidpp, input, origin_is_hid_core);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
|
||||
hidpp->vertical_wheel_counter.dev = input;
|
||||
}
|
||||
|
||||
static int hidpp_input_configured(struct hid_device *hdev,
|
||||
|
@ -2690,6 +2936,27 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hidpp_event(struct hid_device *hdev, struct hid_field *field,
|
||||
struct hid_usage *usage, __s32 value)
|
||||
{
|
||||
/* This function will only be called for scroll events, due to the
|
||||
* restriction imposed in hidpp_usages.
|
||||
*/
|
||||
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||
struct hidpp_scroll_counter *counter = &hidpp->vertical_wheel_counter;
|
||||
/* A scroll event may occur before the multiplier has been retrieved or
|
||||
* the input device set, or high-res scroll enabling may fail. In such
|
||||
* cases we must return early (falling back to default behaviour) to
|
||||
* avoid a crash in hidpp_scroll_counter_handle_scroll.
|
||||
*/
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0
|
||||
|| counter->dev == NULL || counter->wheel_multiplier == 0)
|
||||
return 0;
|
||||
|
||||
hidpp_scroll_counter_handle_scroll(counter, value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hidpp_initialize_battery(struct hidpp_device *hidpp)
|
||||
{
|
||||
static atomic_t battery_no = ATOMIC_INIT(0);
|
||||
|
@ -2901,6 +3168,9 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
|
|||
if (hidpp->battery.ps)
|
||||
power_supply_changed(hidpp->battery.ps);
|
||||
|
||||
if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL)
|
||||
hi_res_scroll_enable(hidpp);
|
||||
|
||||
if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input)
|
||||
/* if the input nodes are already created, we can stop now */
|
||||
return;
|
||||
|
@ -3086,35 +3356,63 @@ static void hidpp_remove(struct hid_device *hdev)
|
|||
mutex_destroy(&hidpp->send_mutex);
|
||||
}
|
||||
|
||||
#define LDJ_DEVICE(product) \
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \
|
||||
USB_VENDOR_ID_LOGITECH, (product))
|
||||
|
||||
static const struct hid_device_id hidpp_devices[] = {
|
||||
{ /* wireless touchpad */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4011),
|
||||
LDJ_DEVICE(0x4011),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT |
|
||||
HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS },
|
||||
{ /* wireless touchpad T650 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4101),
|
||||
LDJ_DEVICE(0x4101),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT },
|
||||
{ /* wireless touchpad T651 */
|
||||
HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
|
||||
USB_DEVICE_ID_LOGITECH_T651),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_WTP },
|
||||
{ /* Mouse Logitech Anywhere MX */
|
||||
LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Mouse Logitech Cube */
|
||||
LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse Logitech M335 */
|
||||
LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech M515 */
|
||||
LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse logitech M560 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x402d),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 },
|
||||
LDJ_DEVICE(0x402d),
|
||||
.driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560
|
||||
| HIDPP_QUIRK_HI_RES_SCROLL_X2120 },
|
||||
{ /* Mouse Logitech M705 (firmware RQM17) */
|
||||
LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Mouse Logitech M705 (firmware RQM67) */
|
||||
LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech M720 */
|
||||
LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Anywhere 2 */
|
||||
LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Anywhere 2S */
|
||||
LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Master */
|
||||
LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech MX Master 2S */
|
||||
LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
|
||||
{ /* Mouse Logitech Performance MX */
|
||||
LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 },
|
||||
{ /* Keyboard logitech K400 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4024),
|
||||
LDJ_DEVICE(0x4024),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_K400 },
|
||||
{ /* Solar Keyboard Logitech K750 */
|
||||
HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, 0x4002),
|
||||
LDJ_DEVICE(0x4002),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_K750 },
|
||||
|
||||
{ HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE,
|
||||
USB_VENDOR_ID_LOGITECH, HID_ANY_ID)},
|
||||
{ LDJ_DEVICE(HID_ANY_ID) },
|
||||
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL),
|
||||
.driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS},
|
||||
|
@ -3123,12 +3421,19 @@ static const struct hid_device_id hidpp_devices[] = {
|
|||
|
||||
MODULE_DEVICE_TABLE(hid, hidpp_devices);
|
||||
|
||||
static const struct hid_usage_id hidpp_usages[] = {
|
||||
{ HID_GD_WHEEL, EV_REL, REL_WHEEL_HI_RES },
|
||||
{ HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1}
|
||||
};
|
||||
|
||||
static struct hid_driver hidpp_driver = {
|
||||
.name = "logitech-hidpp-device",
|
||||
.id_table = hidpp_devices,
|
||||
.probe = hidpp_probe,
|
||||
.remove = hidpp_remove,
|
||||
.raw_event = hidpp_raw_event,
|
||||
.usage_table = hidpp_usages,
|
||||
.event = hidpp_event,
|
||||
.input_configured = hidpp_input_configured,
|
||||
.input_mapping = hidpp_input_mapping,
|
||||
.input_mapped = hidpp_input_mapped,
|
||||
|
|
|
@ -107,8 +107,6 @@ static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count,
|
|||
|
||||
/*
|
||||
* The first byte of the report buffer is expected to be a report number.
|
||||
*
|
||||
* This function is to be called with the minors_lock mutex held.
|
||||
*/
|
||||
static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, size_t count, unsigned char report_type)
|
||||
{
|
||||
|
@ -117,6 +115,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
|
|||
__u8 *buf;
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(&minors_lock);
|
||||
|
||||
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
|
@ -181,8 +181,6 @@ static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t
|
|||
* of buffer is the report number to request, or 0x0 if the defice does not
|
||||
* use numbered reports. The report_type parameter can be HID_FEATURE_REPORT
|
||||
* or HID_INPUT_REPORT.
|
||||
*
|
||||
* This function is to be called with the minors_lock mutex held.
|
||||
*/
|
||||
static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t count, unsigned char report_type)
|
||||
{
|
||||
|
@ -192,6 +190,8 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t
|
|||
int ret = 0, len;
|
||||
unsigned char report_number;
|
||||
|
||||
lockdep_assert_held(&minors_lock);
|
||||
|
||||
if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
|
|
|
@ -346,6 +346,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
|
|||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{
|
||||
.ident = "Odys Winbook 13",
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AXDIA International GmbH"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WINBOOK 13"),
|
||||
},
|
||||
.driver_data = (void *)&sipodev_desc
|
||||
},
|
||||
{ } /* Terminate list */
|
||||
};
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
{
|
||||
int ret;
|
||||
struct ish_hw *hw;
|
||||
unsigned long irq_flag = 0;
|
||||
struct ishtp_device *ishtp;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
|
@ -156,8 +157,12 @@ static int ish_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
|
||||
|
||||
/* request and enable interrupt */
|
||||
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
|
||||
if (!pdev->msi_enabled && !pdev->msix_enabled)
|
||||
irq_flag = IRQF_SHARED;
|
||||
|
||||
ret = devm_request_irq(dev, pdev->irq, ish_irq_handler,
|
||||
IRQF_SHARED, KBUILD_MODNAME, ishtp);
|
||||
irq_flag, KBUILD_MODNAME, ishtp);
|
||||
if (ret) {
|
||||
dev_err(dev, "ISH: request IRQ %d failed\n", pdev->irq);
|
||||
return ret;
|
||||
|
|
|
@ -222,7 +222,7 @@ int ishtp_hid_probe(unsigned int cur_hid_dev,
|
|||
err_hid_device:
|
||||
kfree(hid_data);
|
||||
err_hid_data:
|
||||
kfree(hid);
|
||||
hid_destroy_device(hid);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
|
|
@ -219,6 +219,7 @@ struct hid_item {
|
|||
#define HID_GD_VBRZ 0x00010045
|
||||
#define HID_GD_VNO 0x00010046
|
||||
#define HID_GD_FEATURE 0x00010047
|
||||
#define HID_GD_RESOLUTION_MULTIPLIER 0x00010048
|
||||
#define HID_GD_SYSTEM_CONTROL 0x00010080
|
||||
#define HID_GD_UP 0x00010090
|
||||
#define HID_GD_DOWN 0x00010091
|
||||
|
@ -232,12 +233,14 @@ struct hid_item {
|
|||
#define HID_DC_BATTERYSTRENGTH 0x00060020
|
||||
|
||||
#define HID_CP_CONSUMER_CONTROL 0x000c0001
|
||||
#define HID_CP_AC_PAN 0x000c0238
|
||||
|
||||
#define HID_DG_DIGITIZER 0x000d0001
|
||||
#define HID_DG_PEN 0x000d0002
|
||||
#define HID_DG_LIGHTPEN 0x000d0003
|
||||
#define HID_DG_TOUCHSCREEN 0x000d0004
|
||||
#define HID_DG_TOUCHPAD 0x000d0005
|
||||
#define HID_DG_WHITEBOARD 0x000d0006
|
||||
#define HID_DG_STYLUS 0x000d0020
|
||||
#define HID_DG_PUCK 0x000d0021
|
||||
#define HID_DG_FINGER 0x000d0022
|
||||
|
@ -427,6 +430,7 @@ struct hid_local {
|
|||
*/
|
||||
|
||||
struct hid_collection {
|
||||
struct hid_collection *parent;
|
||||
unsigned type;
|
||||
unsigned usage;
|
||||
unsigned level;
|
||||
|
@ -436,12 +440,16 @@ struct hid_usage {
|
|||
unsigned hid; /* hid usage code */
|
||||
unsigned collection_index; /* index into collection array */
|
||||
unsigned usage_index; /* index into usage array */
|
||||
__s8 resolution_multiplier;/* Effective Resolution Multiplier
|
||||
(HUT v1.12, 4.3.1), default: 1 */
|
||||
/* hidinput data */
|
||||
__s8 wheel_factor; /* 120/resolution_multiplier */
|
||||
__u16 code; /* input driver code */
|
||||
__u8 type; /* input driver type */
|
||||
__s8 hat_min; /* hat switch fun */
|
||||
__s8 hat_max; /* ditto */
|
||||
__s8 hat_dir; /* ditto */
|
||||
__s16 wheel_accumulated; /* hi-res wheel */
|
||||
};
|
||||
|
||||
struct hid_input;
|
||||
|
@ -650,6 +658,7 @@ struct hid_parser {
|
|||
unsigned int *collection_stack;
|
||||
unsigned int collection_stack_ptr;
|
||||
unsigned int collection_stack_size;
|
||||
struct hid_collection *active_collection;
|
||||
struct hid_device *device;
|
||||
unsigned int scan_flags;
|
||||
};
|
||||
|
@ -836,7 +845,11 @@ static inline bool hid_is_using_ll_driver(struct hid_device *hdev,
|
|||
|
||||
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
|
||||
/* We ignore a few input applications that are not widely used */
|
||||
#define IS_INPUT_APPLICATION(a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || ((a >= 0x000d0002) && (a <= 0x000d0006)))
|
||||
#define IS_INPUT_APPLICATION(a) \
|
||||
(((a >= HID_UP_GENDESK) && (a <= HID_GD_MULTIAXIS)) \
|
||||
|| ((a >= HID_DG_PEN) && (a <= HID_DG_WHITEBOARD)) \
|
||||
|| (a == HID_GD_SYSTEM_CONTROL) || (a == HID_CP_CONSUMER_CONTROL) \
|
||||
|| (a == HID_GD_WIRELESS_RADIO_CTLS))
|
||||
|
||||
/* HID core API */
|
||||
|
||||
|
@ -892,6 +905,8 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
|
|||
unsigned int type, unsigned int id,
|
||||
unsigned int field_index,
|
||||
unsigned int report_counts);
|
||||
|
||||
void hid_setup_resolution_multiplier(struct hid_device *hid);
|
||||
int hid_open_report(struct hid_device *device);
|
||||
int hid_check_keys_pressed(struct hid_device *hid);
|
||||
int hid_connect(struct hid_device *hid, unsigned int connect_mask);
|
||||
|
|
|
@ -716,6 +716,8 @@
|
|||
* the situation described above.
|
||||
*/
|
||||
#define REL_RESERVED 0x0a
|
||||
#define REL_WHEEL_HI_RES 0x0b
|
||||
#define REL_HWHEEL_HI_RES 0x0c
|
||||
#define REL_MAX 0x0f
|
||||
#define REL_CNT (REL_MAX+1)
|
||||
|
||||
|
|
|
@ -119,7 +119,7 @@ int main(int argc, char **argv)
|
|||
if (res < 0)
|
||||
perror("HIDIOCSFEATURE");
|
||||
else
|
||||
printf("ioctl HIDIOCGFEATURE returned: %d\n", res);
|
||||
printf("ioctl HIDIOCSFEATURE returned: %d\n", res);
|
||||
|
||||
/* Get Feature */
|
||||
buf[0] = 0x9; /* Report Number */
|
||||
|
|
Loading…
Reference in New Issue