Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID updates from Jiri Kosina: - fix for some modern devices that return multi-byte battery report, from Grant Likely - fix for devices with Resolution Multiplier, from Peter Hutterer - device probing speed increase, from Dmitry Torokhov - ThinkPad 10 Ultrabook Keyboard support, from Hans de Goede - other small assorted fixes and device ID additions * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: HID: quirks: add NOGET quirk for Logitech GROUP HID: Replace HTTP links with HTTPS ones HID: udraw-ps3: Replace HTTP links with HTTPS ones HID: mcp2221: Replace HTTP links with HTTPS ones HID: input: Fix devices that return multiple bytes in battery report HID: lenovo: Fix spurious F23 key press report during resume from suspend HID: lenovo: Add ThinkPad 10 Ultrabook Keyboard fn_lock support HID: lenovo: Add ThinkPad 10 Ultrabook Keyboard support HID: lenovo: Rename fn_lock sysfs attr handlers to make them generic HID: lenovo: Factor out generic parts of the LED code HID: lenovo: Merge tpkbd and cptkbd data structures HID: intel-ish-hid: Replace PCI_DEV_FLAGS_NO_D3 with pci_save_state HID: Wiimote: Treat the d-pad as an analogue stick HID: input: do not run GET_REPORT unless there's a Resolution Multiplier HID: usbhid: remove redundant assignment to variable retval HID: usbhid: do not sleep when opening device
This commit is contained in:
commit
b7b8e3689a
|
@ -20,7 +20,7 @@ config HID
|
||||||
removed from the HID bus by the transport-layer drivers, such as
|
removed from the HID bus by the transport-layer drivers, such as
|
||||||
usbhid (USB_HID) and hidp (BT_HIDP).
|
usbhid (USB_HID) and hidp (BT_HIDP).
|
||||||
|
|
||||||
For docs and specs, see http://www.usb.org/developers/hidpage/
|
For docs and specs, see https://www.usb.org/developers/hidpage/
|
||||||
|
|
||||||
If unsure, say Y.
|
If unsure, say Y.
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* host communicates with the CP2112 via raw HID reports.
|
* host communicates with the CP2112 via raw HID reports.
|
||||||
*
|
*
|
||||||
* Data Sheet:
|
* Data Sheet:
|
||||||
* http://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
|
* https://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
|
||||||
* Programming Interface Specification:
|
* Programming Interface Specification:
|
||||||
* https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf
|
* https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -724,6 +724,7 @@
|
||||||
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
|
#define USB_DEVICE_ID_LENOVO_CUSBKBD 0x6047
|
||||||
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
|
#define USB_DEVICE_ID_LENOVO_CBTKBD 0x6048
|
||||||
#define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL 0x6049
|
#define USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL 0x6049
|
||||||
|
#define USB_DEVICE_ID_LENOVO_TP10UBKBD 0x6062
|
||||||
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
|
#define USB_DEVICE_ID_LENOVO_TPPRODOCK 0x6067
|
||||||
#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085
|
#define USB_DEVICE_ID_LENOVO_X1_COVER 0x6085
|
||||||
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d
|
#define USB_DEVICE_ID_LENOVO_PIXART_USB_MOUSE_608D 0x608d
|
||||||
|
@ -773,6 +774,7 @@
|
||||||
#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
|
#define USB_DEVICE_ID_LOGITECH_G27_WHEEL 0xc29b
|
||||||
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
|
#define USB_DEVICE_ID_LOGITECH_WII_WHEEL 0xc29c
|
||||||
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
|
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
|
||||||
|
#define USB_DEVICE_ID_LOGITECH_GROUP_AUDIO 0x0882
|
||||||
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
|
#define USB_DEVICE_ID_S510_RECEIVER 0xc50c
|
||||||
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
|
#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517
|
||||||
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
|
#define USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500 0xc512
|
||||||
|
|
|
@ -350,13 +350,13 @@ static int hidinput_query_battery_capacity(struct hid_device *dev)
|
||||||
u8 *buf;
|
u8 *buf;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
buf = kmalloc(2, GFP_KERNEL);
|
buf = kmalloc(4, GFP_KERNEL);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
|
ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 4,
|
||||||
dev->battery_report_type, HID_REQ_GET_REPORT);
|
dev->battery_report_type, HID_REQ_GET_REPORT);
|
||||||
if (ret != 2) {
|
if (ret < 2) {
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return -ENODATA;
|
return -ENODATA;
|
||||||
}
|
}
|
||||||
|
@ -1560,21 +1560,12 @@ static bool __hidinput_change_resolution_multipliers(struct hid_device *hid,
|
||||||
{
|
{
|
||||||
struct hid_usage *usage;
|
struct hid_usage *usage;
|
||||||
bool update_needed = false;
|
bool update_needed = false;
|
||||||
|
bool get_report_completed = false;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (report->maxfield == 0)
|
if (report->maxfield == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 (report->maxfield > 1) {
|
|
||||||
hid_hw_request(hid, report, HID_REQ_GET_REPORT);
|
|
||||||
hid_hw_wait(hid);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < report->maxfield; i++) {
|
for (i = 0; i < report->maxfield; i++) {
|
||||||
__s32 value = use_logical_max ?
|
__s32 value = use_logical_max ?
|
||||||
report->field[i]->logical_maximum :
|
report->field[i]->logical_maximum :
|
||||||
|
@ -1593,6 +1584,25 @@ static bool __hidinput_change_resolution_multipliers(struct hid_device *hid,
|
||||||
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
|
if (usage->hid != HID_GD_RESOLUTION_MULTIPLIER)
|
||||||
continue;
|
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.
|
||||||
|
*
|
||||||
|
* But if we're not allowed to read from the device,
|
||||||
|
* we just bail. Such a device should not exist
|
||||||
|
* anyway.
|
||||||
|
*/
|
||||||
|
if (!get_report_completed && report->maxfield > 1) {
|
||||||
|
if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
|
||||||
|
return update_needed;
|
||||||
|
|
||||||
|
hid_hw_request(hid, report, HID_REQ_GET_REPORT);
|
||||||
|
hid_hw_wait(hid);
|
||||||
|
get_report_completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
report->field[i]->value[j] = value;
|
report->field[i]->value[j] = value;
|
||||||
update_needed = true;
|
update_needed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,29 +29,67 @@
|
||||||
#include <linux/hid.h>
|
#include <linux/hid.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/leds.h>
|
#include <linux/leds.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include "hid-ids.h"
|
#include "hid-ids.h"
|
||||||
|
|
||||||
struct lenovo_drvdata_tpkbd {
|
struct lenovo_drvdata {
|
||||||
|
u8 led_report[3]; /* Must be first for proper alignment */
|
||||||
int led_state;
|
int led_state;
|
||||||
|
struct mutex led_report_mutex;
|
||||||
struct led_classdev led_mute;
|
struct led_classdev led_mute;
|
||||||
struct led_classdev led_micmute;
|
struct led_classdev led_micmute;
|
||||||
|
struct work_struct fn_lock_sync_work;
|
||||||
|
struct hid_device *hdev;
|
||||||
int press_to_select;
|
int press_to_select;
|
||||||
int dragging;
|
int dragging;
|
||||||
int release_to_select;
|
int release_to_select;
|
||||||
int select_right;
|
int select_right;
|
||||||
int sensitivity;
|
int sensitivity;
|
||||||
int press_speed;
|
int press_speed;
|
||||||
};
|
|
||||||
|
|
||||||
struct lenovo_drvdata_cptkbd {
|
|
||||||
u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
|
u8 middlebutton_state; /* 0:Up, 1:Down (undecided), 2:Scrolling */
|
||||||
bool fn_lock;
|
bool fn_lock;
|
||||||
int sensitivity;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
#define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
|
||||||
|
|
||||||
|
#define TP10UBKBD_LED_OUTPUT_REPORT 9
|
||||||
|
|
||||||
|
#define TP10UBKBD_FN_LOCK_LED 0x54
|
||||||
|
#define TP10UBKBD_MUTE_LED 0x64
|
||||||
|
#define TP10UBKBD_MICMUTE_LED 0x74
|
||||||
|
|
||||||
|
#define TP10UBKBD_LED_OFF 1
|
||||||
|
#define TP10UBKBD_LED_ON 2
|
||||||
|
|
||||||
|
static void lenovo_led_set_tp10ubkbd(struct hid_device *hdev, u8 led_code,
|
||||||
|
enum led_brightness value)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->led_report_mutex);
|
||||||
|
|
||||||
|
data->led_report[0] = TP10UBKBD_LED_OUTPUT_REPORT;
|
||||||
|
data->led_report[1] = led_code;
|
||||||
|
data->led_report[2] = value ? TP10UBKBD_LED_ON : TP10UBKBD_LED_OFF;
|
||||||
|
ret = hid_hw_raw_request(hdev, data->led_report[0], data->led_report, 3,
|
||||||
|
HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
|
||||||
|
if (ret)
|
||||||
|
hid_err(hdev, "Set LED output report error: %d\n", ret);
|
||||||
|
|
||||||
|
mutex_unlock(&data->led_report_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lenovo_tp10ubkbd_sync_fn_lock(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data =
|
||||||
|
container_of(work, struct lenovo_drvdata, fn_lock_sync_work);
|
||||||
|
|
||||||
|
lenovo_led_set_tp10ubkbd(data->hdev, TP10UBKBD_FN_LOCK_LED,
|
||||||
|
data->fn_lock);
|
||||||
|
}
|
||||||
|
|
||||||
static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
|
static const __u8 lenovo_pro_dock_need_fixup_collection[] = {
|
||||||
0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
|
0x05, 0x88, /* Usage Page (Vendor Usage Page 0x88) */
|
||||||
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
0x09, 0x01, /* Usage (Vendor Usage 0x01) */
|
||||||
|
@ -179,6 +217,44 @@ static int lenovo_input_mapping_scrollpoint(struct hid_device *hdev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lenovo_input_mapping_tp10_ultrabook_kbd(struct hid_device *hdev,
|
||||||
|
struct hid_input *hi, struct hid_field *field,
|
||||||
|
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The ThinkPad 10 Ultrabook Keyboard uses 0x000c0001 usage for
|
||||||
|
* a bunch of keys which have no standard consumer page code.
|
||||||
|
*/
|
||||||
|
if (usage->hid == 0x000c0001) {
|
||||||
|
switch (usage->usage_index) {
|
||||||
|
case 8: /* Fn-Esc: Fn-lock toggle */
|
||||||
|
map_key_clear(KEY_FN_ESC);
|
||||||
|
return 1;
|
||||||
|
case 9: /* Fn-F4: Mic mute */
|
||||||
|
map_key_clear(KEY_MICMUTE);
|
||||||
|
return 1;
|
||||||
|
case 10: /* Fn-F7: Control panel */
|
||||||
|
map_key_clear(KEY_CONFIG);
|
||||||
|
return 1;
|
||||||
|
case 11: /* Fn-F8: Search (magnifier glass) */
|
||||||
|
map_key_clear(KEY_SEARCH);
|
||||||
|
return 1;
|
||||||
|
case 12: /* Fn-F10: Open My computer (6 boxes) */
|
||||||
|
map_key_clear(KEY_FILE);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Ultrabook Keyboard sends a spurious F23 key-press when resuming
|
||||||
|
* from suspend and it does not actually have a F23 key, ignore it.
|
||||||
|
*/
|
||||||
|
if (usage->hid == 0x00070072)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int lenovo_input_mapping(struct hid_device *hdev,
|
static int lenovo_input_mapping(struct hid_device *hdev,
|
||||||
struct hid_input *hi, struct hid_field *field,
|
struct hid_input *hi, struct hid_field *field,
|
||||||
struct hid_usage *usage, unsigned long **bit, int *max)
|
struct hid_usage *usage, unsigned long **bit, int *max)
|
||||||
|
@ -199,6 +275,9 @@ static int lenovo_input_mapping(struct hid_device *hdev,
|
||||||
case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
|
case USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL:
|
||||||
return lenovo_input_mapping_scrollpoint(hdev, hi, field,
|
return lenovo_input_mapping_scrollpoint(hdev, hi, field,
|
||||||
usage, bit, max);
|
usage, bit, max);
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
return lenovo_input_mapping_tp10_ultrabook_kbd(hdev, hi, field,
|
||||||
|
usage, bit, max);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -242,7 +321,7 @@ static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
|
||||||
static void lenovo_features_set_cptkbd(struct hid_device *hdev)
|
static void lenovo_features_set_cptkbd(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
|
ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -253,23 +332,23 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev)
|
||||||
hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
|
hid_err(hdev, "Sensitivity setting failed: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
|
static ssize_t attr_fn_lock_show(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n", cptkbd_data->fn_lock);
|
return snprintf(buf, PAGE_SIZE, "%u\n", data->fn_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
|
static ssize_t attr_fn_lock_store(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value))
|
if (kstrtoint(buf, 10, &value))
|
||||||
|
@ -277,8 +356,17 @@ static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
|
||||||
if (value < 0 || value > 1)
|
if (value < 0 || value > 1)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
cptkbd_data->fn_lock = !!value;
|
data->fn_lock = !!value;
|
||||||
lenovo_features_set_cptkbd(hdev);
|
|
||||||
|
switch (hdev->product) {
|
||||||
|
case USB_DEVICE_ID_LENOVO_CUSBKBD:
|
||||||
|
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
||||||
|
lenovo_features_set_cptkbd(hdev);
|
||||||
|
break;
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
@ -288,7 +376,7 @@ static ssize_t attr_sensitivity_show_cptkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
cptkbd_data->sensitivity);
|
cptkbd_data->sensitivity);
|
||||||
|
@ -300,7 +388,7 @@ static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
||||||
|
@ -313,10 +401,10 @@ static ssize_t attr_sensitivity_store_cptkbd(struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static struct device_attribute dev_attr_fn_lock_cptkbd =
|
static struct device_attribute dev_attr_fn_lock =
|
||||||
__ATTR(fn_lock, S_IWUSR | S_IRUGO,
|
__ATTR(fn_lock, S_IWUSR | S_IRUGO,
|
||||||
attr_fn_lock_show_cptkbd,
|
attr_fn_lock_show,
|
||||||
attr_fn_lock_store_cptkbd);
|
attr_fn_lock_store);
|
||||||
|
|
||||||
static struct device_attribute dev_attr_sensitivity_cptkbd =
|
static struct device_attribute dev_attr_sensitivity_cptkbd =
|
||||||
__ATTR(sensitivity, S_IWUSR | S_IRUGO,
|
__ATTR(sensitivity, S_IWUSR | S_IRUGO,
|
||||||
|
@ -325,7 +413,7 @@ static struct device_attribute dev_attr_sensitivity_cptkbd =
|
||||||
|
|
||||||
|
|
||||||
static struct attribute *lenovo_attributes_cptkbd[] = {
|
static struct attribute *lenovo_attributes_cptkbd[] = {
|
||||||
&dev_attr_fn_lock_cptkbd.attr,
|
&dev_attr_fn_lock.attr,
|
||||||
&dev_attr_sensitivity_cptkbd.attr,
|
&dev_attr_sensitivity_cptkbd.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -354,10 +442,28 @@ static int lenovo_raw_event(struct hid_device *hdev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lenovo_event_tp10ubkbd(struct hid_device *hdev,
|
||||||
|
struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
|
if (usage->type == EV_KEY && usage->code == KEY_FN_ESC && value == 1) {
|
||||||
|
/*
|
||||||
|
* The user has toggled the Fn-lock state. Toggle our own
|
||||||
|
* cached value of it and sync our value to the keyboard to
|
||||||
|
* ensure things are in sync (the sycning should be a no-op).
|
||||||
|
*/
|
||||||
|
data->fn_lock = !data->fn_lock;
|
||||||
|
schedule_work(&data->fn_lock_sync_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int lenovo_event_cptkbd(struct hid_device *hdev,
|
static int lenovo_event_cptkbd(struct hid_device *hdev,
|
||||||
struct hid_field *field, struct hid_usage *usage, __s32 value)
|
struct hid_field *field, struct hid_usage *usage, __s32 value)
|
||||||
{
|
{
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *cptkbd_data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
/* "wheel" scroll events */
|
/* "wheel" scroll events */
|
||||||
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
|
if (usage->type == EV_REL && (usage->code == REL_WHEEL ||
|
||||||
|
@ -396,6 +502,8 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
|
||||||
case USB_DEVICE_ID_LENOVO_CUSBKBD:
|
case USB_DEVICE_ID_LENOVO_CUSBKBD:
|
||||||
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
||||||
return lenovo_event_cptkbd(hdev, field, usage, value);
|
return lenovo_event_cptkbd(hdev, field, usage, value);
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
return lenovo_event_tp10ubkbd(hdev, field, usage, value);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +512,7 @@ static int lenovo_event(struct hid_device *hdev, struct hid_field *field,
|
||||||
static int lenovo_features_set_tpkbd(struct hid_device *hdev)
|
static int lenovo_features_set_tpkbd(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
|
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
|
||||||
|
|
||||||
|
@ -425,7 +533,7 @@ static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
|
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
|
||||||
}
|
}
|
||||||
|
@ -436,7 +544,7 @@ static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value))
|
if (kstrtoint(buf, 10, &value))
|
||||||
|
@ -455,7 +563,7 @@ static ssize_t attr_dragging_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
|
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
|
||||||
}
|
}
|
||||||
|
@ -466,7 +574,7 @@ static ssize_t attr_dragging_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value))
|
if (kstrtoint(buf, 10, &value))
|
||||||
|
@ -485,7 +593,7 @@ static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
|
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
|
||||||
}
|
}
|
||||||
|
@ -496,7 +604,7 @@ static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value))
|
if (kstrtoint(buf, 10, &value))
|
||||||
|
@ -515,7 +623,7 @@ static ssize_t attr_select_right_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
|
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
|
||||||
}
|
}
|
||||||
|
@ -526,7 +634,7 @@ static ssize_t attr_select_right_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value))
|
if (kstrtoint(buf, 10, &value))
|
||||||
|
@ -545,7 +653,7 @@ static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
data_pointer->sensitivity);
|
data_pointer->sensitivity);
|
||||||
|
@ -557,7 +665,7 @@ static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
||||||
|
@ -574,7 +682,7 @@ static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
|
||||||
char *buf)
|
char *buf)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
return snprintf(buf, PAGE_SIZE, "%u\n",
|
return snprintf(buf, PAGE_SIZE, "%u\n",
|
||||||
data_pointer->press_speed);
|
data_pointer->press_speed);
|
||||||
|
@ -586,7 +694,7 @@ static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
|
||||||
|
@ -642,12 +750,23 @@ static const struct attribute_group lenovo_attr_group_tpkbd = {
|
||||||
.attrs = lenovo_attributes_tpkbd,
|
.attrs = lenovo_attributes_tpkbd,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum led_brightness lenovo_led_brightness_get_tpkbd(
|
static void lenovo_led_set_tpkbd(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
struct hid_report *report;
|
||||||
|
|
||||||
|
report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
|
||||||
|
report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
|
||||||
|
report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
|
||||||
|
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum led_brightness lenovo_led_brightness_get(
|
||||||
struct led_classdev *led_cdev)
|
struct led_classdev *led_cdev)
|
||||||
{
|
{
|
||||||
struct device *dev = led_cdev->dev->parent;
|
struct device *dev = led_cdev->dev->parent;
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
int led_nr = 0;
|
int led_nr = 0;
|
||||||
|
|
||||||
if (led_cdev == &data_pointer->led_micmute)
|
if (led_cdev == &data_pointer->led_micmute)
|
||||||
|
@ -658,13 +777,13 @@ static enum led_brightness lenovo_led_brightness_get_tpkbd(
|
||||||
: LED_OFF;
|
: LED_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
|
static void lenovo_led_brightness_set(struct led_classdev *led_cdev,
|
||||||
enum led_brightness value)
|
enum led_brightness value)
|
||||||
{
|
{
|
||||||
struct device *dev = led_cdev->dev->parent;
|
struct device *dev = led_cdev->dev->parent;
|
||||||
struct hid_device *hdev = to_hid_device(dev);
|
struct hid_device *hdev = to_hid_device(dev);
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
struct hid_report *report;
|
u8 tp10ubkbd_led[] = { TP10UBKBD_MUTE_LED, TP10UBKBD_MICMUTE_LED };
|
||||||
int led_nr = 0;
|
int led_nr = 0;
|
||||||
|
|
||||||
if (led_cdev == &data_pointer->led_micmute)
|
if (led_cdev == &data_pointer->led_micmute)
|
||||||
|
@ -675,20 +794,57 @@ static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
|
||||||
else
|
else
|
||||||
data_pointer->led_state |= 1 << led_nr;
|
data_pointer->led_state |= 1 << led_nr;
|
||||||
|
|
||||||
report = hdev->report_enum[HID_OUTPUT_REPORT].report_id_hash[3];
|
switch (hdev->product) {
|
||||||
report->field[0]->value[0] = (data_pointer->led_state >> 0) & 1;
|
case USB_DEVICE_ID_LENOVO_TPKBD:
|
||||||
report->field[0]->value[1] = (data_pointer->led_state >> 1) & 1;
|
lenovo_led_set_tpkbd(hdev);
|
||||||
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
|
break;
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
lenovo_led_set_tp10ubkbd(hdev, tp10ubkbd_led[led_nr], value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lenovo_register_leds(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
|
size_t name_sz = strlen(dev_name(&hdev->dev)) + 16;
|
||||||
|
char *name_mute, *name_micm;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
|
||||||
|
name_micm = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
|
||||||
|
if (name_mute == NULL || name_micm == NULL) {
|
||||||
|
hid_err(hdev, "Could not allocate memory for led data\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(&hdev->dev));
|
||||||
|
snprintf(name_micm, name_sz, "%s:amber:micmute", dev_name(&hdev->dev));
|
||||||
|
|
||||||
|
data->led_mute.name = name_mute;
|
||||||
|
data->led_mute.brightness_get = lenovo_led_brightness_get;
|
||||||
|
data->led_mute.brightness_set = lenovo_led_brightness_set;
|
||||||
|
data->led_mute.dev = &hdev->dev;
|
||||||
|
ret = led_classdev_register(&hdev->dev, &data->led_mute);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
data->led_micmute.name = name_micm;
|
||||||
|
data->led_micmute.brightness_get = lenovo_led_brightness_get;
|
||||||
|
data->led_micmute.brightness_set = lenovo_led_brightness_set;
|
||||||
|
data->led_micmute.dev = &hdev->dev;
|
||||||
|
ret = led_classdev_register(&hdev->dev, &data->led_micmute);
|
||||||
|
if (ret < 0) {
|
||||||
|
led_classdev_unregister(&data->led_mute);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &hdev->dev;
|
struct lenovo_drvdata *data_pointer;
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer;
|
int i, ret;
|
||||||
size_t name_sz = strlen(dev_name(dev)) + 16;
|
|
||||||
char *name_mute, *name_micmute;
|
|
||||||
int i;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only register extra settings against subdevice where input_mapping
|
* Only register extra settings against subdevice where input_mapping
|
||||||
|
@ -712,7 +868,7 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
||||||
hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
|
hid_warn(hdev, "Could not create sysfs group: %d\n", ret);
|
||||||
|
|
||||||
data_pointer = devm_kzalloc(&hdev->dev,
|
data_pointer = devm_kzalloc(&hdev->dev,
|
||||||
sizeof(struct lenovo_drvdata_tpkbd),
|
sizeof(struct lenovo_drvdata),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (data_pointer == NULL) {
|
if (data_pointer == NULL) {
|
||||||
hid_err(hdev, "Could not allocate memory for driver data\n");
|
hid_err(hdev, "Could not allocate memory for driver data\n");
|
||||||
|
@ -724,38 +880,12 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev)
|
||||||
data_pointer->sensitivity = 0xa0;
|
data_pointer->sensitivity = 0xa0;
|
||||||
data_pointer->press_speed = 0x38;
|
data_pointer->press_speed = 0x38;
|
||||||
|
|
||||||
name_mute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
|
|
||||||
name_micmute = devm_kzalloc(&hdev->dev, name_sz, GFP_KERNEL);
|
|
||||||
if (name_mute == NULL || name_micmute == NULL) {
|
|
||||||
hid_err(hdev, "Could not allocate memory for led data\n");
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
snprintf(name_mute, name_sz, "%s:amber:mute", dev_name(dev));
|
|
||||||
snprintf(name_micmute, name_sz, "%s:amber:micmute", dev_name(dev));
|
|
||||||
|
|
||||||
hid_set_drvdata(hdev, data_pointer);
|
hid_set_drvdata(hdev, data_pointer);
|
||||||
|
|
||||||
data_pointer->led_mute.name = name_mute;
|
ret = lenovo_register_leds(hdev);
|
||||||
data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
|
if (ret)
|
||||||
data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
|
|
||||||
data_pointer->led_mute.dev = dev;
|
|
||||||
ret = led_classdev_register(dev, &data_pointer->led_mute);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
data_pointer->led_micmute.name = name_micmute;
|
|
||||||
data_pointer->led_micmute.brightness_get =
|
|
||||||
lenovo_led_brightness_get_tpkbd;
|
|
||||||
data_pointer->led_micmute.brightness_set =
|
|
||||||
lenovo_led_brightness_set_tpkbd;
|
|
||||||
data_pointer->led_micmute.dev = dev;
|
|
||||||
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);
|
lenovo_features_set_tpkbd(hdev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -767,7 +897,7 @@ err:
|
||||||
static int lenovo_probe_cptkbd(struct hid_device *hdev)
|
static int lenovo_probe_cptkbd(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct lenovo_drvdata_cptkbd *cptkbd_data;
|
struct lenovo_drvdata *cptkbd_data;
|
||||||
|
|
||||||
/* All the custom action happens on the USBMOUSE device for USB */
|
/* All the custom action happens on the USBMOUSE device for USB */
|
||||||
if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
|
if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
|
||||||
|
@ -811,6 +941,57 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct attribute *lenovo_attributes_tp10ubkbd[] = {
|
||||||
|
&dev_attr_fn_lock.attr,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group lenovo_attr_group_tp10ubkbd = {
|
||||||
|
.attrs = lenovo_attributes_tp10ubkbd,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int lenovo_probe_tp10ubkbd(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* All the custom action happens on the USBMOUSE device for USB */
|
||||||
|
if (hdev->type != HID_TYPE_USBMOUSE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
|
||||||
|
if (!data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
mutex_init(&data->led_report_mutex);
|
||||||
|
INIT_WORK(&data->fn_lock_sync_work, lenovo_tp10ubkbd_sync_fn_lock);
|
||||||
|
data->hdev = hdev;
|
||||||
|
|
||||||
|
hid_set_drvdata(hdev, data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Thinkpad 10 ultrabook USB kbd dock's Fn-lock defaults to on.
|
||||||
|
* We cannot read the state, only set it, so we force it to on here
|
||||||
|
* (which should be a no-op) to make sure that our state matches the
|
||||||
|
* keyboard's FN-lock state. This is the same as what Windows does.
|
||||||
|
*/
|
||||||
|
data->fn_lock = true;
|
||||||
|
lenovo_led_set_tp10ubkbd(hdev, TP10UBKBD_FN_LOCK_LED, data->fn_lock);
|
||||||
|
|
||||||
|
ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = lenovo_register_leds(hdev);
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
err:
|
||||||
|
sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int lenovo_probe(struct hid_device *hdev,
|
static int lenovo_probe(struct hid_device *hdev,
|
||||||
const struct hid_device_id *id)
|
const struct hid_device_id *id)
|
||||||
{
|
{
|
||||||
|
@ -836,6 +1017,9 @@ static int lenovo_probe(struct hid_device *hdev,
|
||||||
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
||||||
ret = lenovo_probe_cptkbd(hdev);
|
ret = lenovo_probe_cptkbd(hdev);
|
||||||
break;
|
break;
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
ret = lenovo_probe_tp10ubkbd(hdev);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
|
@ -852,7 +1036,7 @@ err:
|
||||||
|
|
||||||
static void lenovo_remove_tpkbd(struct hid_device *hdev)
|
static void lenovo_remove_tpkbd(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
|
struct lenovo_drvdata *data_pointer = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only the trackpoint half of the keyboard has drvdata and stuff that
|
* Only the trackpoint half of the keyboard has drvdata and stuff that
|
||||||
|
@ -874,6 +1058,20 @@ static void lenovo_remove_cptkbd(struct hid_device *hdev)
|
||||||
&lenovo_attr_group_cptkbd);
|
&lenovo_attr_group_cptkbd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void lenovo_remove_tp10ubkbd(struct hid_device *hdev)
|
||||||
|
{
|
||||||
|
struct lenovo_drvdata *data = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
|
if (data == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
led_classdev_unregister(&data->led_micmute);
|
||||||
|
led_classdev_unregister(&data->led_mute);
|
||||||
|
|
||||||
|
sysfs_remove_group(&hdev->dev.kobj, &lenovo_attr_group_tp10ubkbd);
|
||||||
|
cancel_work_sync(&data->fn_lock_sync_work);
|
||||||
|
}
|
||||||
|
|
||||||
static void lenovo_remove(struct hid_device *hdev)
|
static void lenovo_remove(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
switch (hdev->product) {
|
switch (hdev->product) {
|
||||||
|
@ -884,6 +1082,9 @@ static void lenovo_remove(struct hid_device *hdev)
|
||||||
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
case USB_DEVICE_ID_LENOVO_CBTKBD:
|
||||||
lenovo_remove_cptkbd(hdev);
|
lenovo_remove_cptkbd(hdev);
|
||||||
break;
|
break;
|
||||||
|
case USB_DEVICE_ID_LENOVO_TP10UBKBD:
|
||||||
|
lenovo_remove_tp10ubkbd(hdev);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_hw_stop(hdev);
|
hid_hw_stop(hdev);
|
||||||
|
@ -920,6 +1121,7 @@ static const struct hid_device_id lenovo_devices[] = {
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_IBM, USB_DEVICE_ID_IBM_SCROLLPOINT_800DPI_OPTICAL_PRO) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_SCROLLPOINT_OPTICAL) },
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TP10UBKBD) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 2020, Rishi Gupta <gupt21@gmail.com>
|
* Copyright (c) 2020, Rishi Gupta <gupt21@gmail.com>
|
||||||
*
|
*
|
||||||
* Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf
|
* Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/20005565B.pdf
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
|
@ -179,6 +179,7 @@ static const struct hid_device_id hid_quirks[] = {
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD2, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD), HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_XIN_MO, USB_DEVICE_ID_XIN_MO_DUAL_ARCADE), HID_QUIRK_MULTI_INPUT },
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_GROUP_AUDIO), HID_QUIRK_NOGET },
|
||||||
|
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,7 +16,7 @@ MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Protocol information from:
|
* Protocol information from:
|
||||||
* http://brandonw.net/udraw/
|
* https://brandonw.net/udraw/
|
||||||
* and the source code of:
|
* and the source code of:
|
||||||
* https://vvvv.org/contribution/udraw-hid
|
* https://vvvv.org/contribution/udraw-hid
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1870,6 +1870,11 @@ static const struct hid_device_id wiimote_hid_devices[] = {
|
||||||
USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
|
USB_DEVICE_ID_NINTENDO_WIIMOTE2) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool wiimote_dpad_as_analog = false;
|
||||||
|
module_param_named(dpad_as_analog, wiimote_dpad_as_analog, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(dpad_as_analog, "Use D-Pad as main analog input");
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
|
MODULE_DEVICE_TABLE(hid, wiimote_hid_devices);
|
||||||
|
|
||||||
static struct hid_driver wiimote_hid_driver = {
|
static struct hid_driver wiimote_hid_driver = {
|
||||||
|
|
|
@ -1088,12 +1088,28 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
* is the same as before.
|
* is the same as before.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static const s8 digital_to_analog[3] = {0x20, 0, -0x20};
|
||||||
|
|
||||||
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
||||||
lx = ext[0] & 0x3e;
|
if (wiimote_dpad_as_analog) {
|
||||||
ly = ext[1] & 0x3e;
|
lx = digital_to_analog[1 - !(ext[4] & 0x80)
|
||||||
|
+ !(ext[1] & 0x01)];
|
||||||
|
ly = digital_to_analog[1 - !(ext[4] & 0x40)
|
||||||
|
+ !(ext[0] & 0x01)];
|
||||||
|
} else {
|
||||||
|
lx = (ext[0] & 0x3e) - 0x20;
|
||||||
|
ly = (ext[1] & 0x3e) - 0x20;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lx = ext[0] & 0x3f;
|
if (wiimote_dpad_as_analog) {
|
||||||
ly = ext[1] & 0x3f;
|
lx = digital_to_analog[1 - !(ext[4] & 0x80)
|
||||||
|
+ !(ext[5] & 0x02)];
|
||||||
|
ly = digital_to_analog[1 - !(ext[4] & 0x40)
|
||||||
|
+ !(ext[5] & 0x01)];
|
||||||
|
} else {
|
||||||
|
lx = (ext[0] & 0x3f) - 0x20;
|
||||||
|
ly = (ext[1] & 0x3f) - 0x20;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rx = (ext[0] >> 3) & 0x18;
|
rx = (ext[0] >> 3) & 0x18;
|
||||||
|
@ -1110,19 +1126,13 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
rt <<= 1;
|
rt <<= 1;
|
||||||
lt <<= 1;
|
lt <<= 1;
|
||||||
|
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT1X, lx - 0x20);
|
input_report_abs(wdata->extension.input, ABS_HAT1X, lx);
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT1Y, ly - 0x20);
|
input_report_abs(wdata->extension.input, ABS_HAT1Y, ly);
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
|
input_report_abs(wdata->extension.input, ABS_HAT2X, rx - 0x20);
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
|
input_report_abs(wdata->extension.input, ABS_HAT2Y, ry - 0x20);
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT3X, rt);
|
input_report_abs(wdata->extension.input, ABS_HAT3X, rt);
|
||||||
input_report_abs(wdata->extension.input, ABS_HAT3Y, lt);
|
input_report_abs(wdata->extension.input, ABS_HAT3Y, lt);
|
||||||
|
|
||||||
input_report_key(wdata->extension.input,
|
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
|
|
||||||
!(ext[4] & 0x80));
|
|
||||||
input_report_key(wdata->extension.input,
|
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
|
|
||||||
!(ext[4] & 0x40));
|
|
||||||
input_report_key(wdata->extension.input,
|
input_report_key(wdata->extension.input,
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT],
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LT],
|
||||||
!(ext[4] & 0x20));
|
!(ext[4] & 0x20));
|
||||||
|
@ -1157,20 +1167,29 @@ static void wiimod_classic_in_ext(struct wiimote_data *wdata, const __u8 *ext)
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR],
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_ZR],
|
||||||
!(ext[5] & 0x04));
|
!(ext[5] & 0x04));
|
||||||
|
|
||||||
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
if (!wiimote_dpad_as_analog) {
|
||||||
input_report_key(wdata->extension.input,
|
input_report_key(wdata->extension.input,
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_RIGHT],
|
||||||
!(ext[1] & 0x01));
|
!(ext[4] & 0x80));
|
||||||
input_report_key(wdata->extension.input,
|
input_report_key(wdata->extension.input,
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_DOWN],
|
||||||
!(ext[0] & 0x01));
|
!(ext[4] & 0x40));
|
||||||
} else {
|
|
||||||
input_report_key(wdata->extension.input,
|
if (wdata->state.flags & WIIPROTO_FLAG_MP_ACTIVE) {
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
|
input_report_key(wdata->extension.input,
|
||||||
!(ext[5] & 0x02));
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
|
||||||
input_report_key(wdata->extension.input,
|
!(ext[1] & 0x01));
|
||||||
wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
|
input_report_key(wdata->extension.input,
|
||||||
!(ext[5] & 0x01));
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
|
||||||
|
!(ext[0] & 0x01));
|
||||||
|
} else {
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_LEFT],
|
||||||
|
!(ext[5] & 0x02));
|
||||||
|
input_report_key(wdata->extension.input,
|
||||||
|
wiimod_classic_map[WIIMOD_CLASSIC_KEY_UP],
|
||||||
|
!(ext[5] & 0x01));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input_sync(wdata->extension.input);
|
input_sync(wdata->extension.input);
|
||||||
|
|
|
@ -162,6 +162,8 @@ struct wiimote_data {
|
||||||
struct work_struct init_worker;
|
struct work_struct init_worker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern bool wiimote_dpad_as_analog;
|
||||||
|
|
||||||
/* wiimote modules */
|
/* wiimote modules */
|
||||||
|
|
||||||
enum wiimod_module {
|
enum wiimod_module {
|
||||||
|
|
|
@ -106,6 +106,11 @@ static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
|
||||||
return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
return !pm_suspend_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
return !pm_resume_via_firmware() || pdev->device == CHV_DEVICE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ish_probe() - PCI driver probe callback
|
* ish_probe() - PCI driver probe callback
|
||||||
* @pdev: pci device
|
* @pdev: pci device
|
||||||
|
@ -215,9 +220,7 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
|
||||||
struct ishtp_device *dev = pci_get_drvdata(pdev);
|
struct ishtp_device *dev = pci_get_drvdata(pdev);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Check the NO_D3 flag to distinguish the resume paths */
|
if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag) {
|
||||||
if (pdev->dev_flags & PCI_DEV_FLAGS_NO_D3) {
|
|
||||||
pdev->dev_flags &= ~PCI_DEV_FLAGS_NO_D3;
|
|
||||||
disable_irq_wake(pdev->irq);
|
disable_irq_wake(pdev->irq);
|
||||||
|
|
||||||
ishtp_send_resume(dev);
|
ishtp_send_resume(dev);
|
||||||
|
@ -281,8 +284,11 @@ static int __maybe_unused ish_suspend(struct device *device)
|
||||||
*/
|
*/
|
||||||
ish_disable_dma(dev);
|
ish_disable_dma(dev);
|
||||||
} else {
|
} else {
|
||||||
/* Set the NO_D3 flag, the ISH would enter D0i3 */
|
/*
|
||||||
pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
|
* Save state so PCI core will keep the device at D0,
|
||||||
|
* the ISH would enter D0i3
|
||||||
|
*/
|
||||||
|
pci_save_state(pdev);
|
||||||
|
|
||||||
enable_irq_wake(pdev->irq);
|
enable_irq_wake(pdev->irq);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
|
#include <linux/timekeeping.h>
|
||||||
|
|
||||||
#include <linux/usb.h>
|
#include <linux/usb.h>
|
||||||
|
|
||||||
|
@ -95,6 +96,18 @@ static int hid_start_in(struct hid_device *hid)
|
||||||
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
set_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
||||||
} else {
|
} else {
|
||||||
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
clear_bit(HID_NO_BANDWIDTH, &usbhid->iofl);
|
||||||
|
|
||||||
|
if (test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
|
||||||
|
/*
|
||||||
|
* In case events are generated while nobody was
|
||||||
|
* listening, some are released when the device
|
||||||
|
* is re-opened. Wait 50 msec for the queue to
|
||||||
|
* empty before allowing events to go through
|
||||||
|
* hid.
|
||||||
|
*/
|
||||||
|
usbhid->input_start_time =
|
||||||
|
ktime_add_ms(ktime_get_coarse(), 50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&usbhid->lock, flags);
|
spin_unlock_irqrestore(&usbhid->lock, flags);
|
||||||
|
@ -280,20 +293,23 @@ static void hid_irq_in(struct urb *urb)
|
||||||
if (!test_bit(HID_OPENED, &usbhid->iofl))
|
if (!test_bit(HID_OPENED, &usbhid->iofl))
|
||||||
break;
|
break;
|
||||||
usbhid_mark_busy(usbhid);
|
usbhid_mark_busy(usbhid);
|
||||||
if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
|
if (test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
|
||||||
hid_input_report(urb->context, HID_INPUT_REPORT,
|
if (ktime_before(ktime_get_coarse(),
|
||||||
urb->transfer_buffer,
|
usbhid->input_start_time))
|
||||||
urb->actual_length, 1);
|
break;
|
||||||
/*
|
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
|
||||||
* autosuspend refused while keys are pressed
|
|
||||||
* because most keyboards don't wake up when
|
|
||||||
* a key is released
|
|
||||||
*/
|
|
||||||
if (hid_check_keys_pressed(hid))
|
|
||||||
set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
|
||||||
else
|
|
||||||
clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
|
||||||
}
|
}
|
||||||
|
hid_input_report(urb->context, HID_INPUT_REPORT,
|
||||||
|
urb->transfer_buffer, urb->actual_length, 1);
|
||||||
|
/*
|
||||||
|
* autosuspend refused while keys are pressed
|
||||||
|
* because most keyboards don't wake up when
|
||||||
|
* a key is released
|
||||||
|
*/
|
||||||
|
if (hid_check_keys_pressed(hid))
|
||||||
|
set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
||||||
|
else
|
||||||
|
clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
|
||||||
break;
|
break;
|
||||||
case -EPIPE: /* stall */
|
case -EPIPE: /* stall */
|
||||||
usbhid_mark_busy(usbhid);
|
usbhid_mark_busy(usbhid);
|
||||||
|
@ -720,17 +736,6 @@ static int usbhid_open(struct hid_device *hid)
|
||||||
|
|
||||||
usb_autopm_put_interface(usbhid->intf);
|
usb_autopm_put_interface(usbhid->intf);
|
||||||
|
|
||||||
/*
|
|
||||||
* In case events are generated while nobody was listening,
|
|
||||||
* some are released when the device is re-opened.
|
|
||||||
* Wait 50 msec for the queue to empty before allowing events
|
|
||||||
* to go through hid.
|
|
||||||
*/
|
|
||||||
if (res == 0)
|
|
||||||
msleep(50);
|
|
||||||
|
|
||||||
clear_bit(HID_RESUME_RUNNING, &usbhid->iofl);
|
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
mutex_unlock(&usbhid->mutex);
|
mutex_unlock(&usbhid->mutex);
|
||||||
return res;
|
return res;
|
||||||
|
@ -1667,7 +1672,7 @@ struct usb_interface *usbhid_find_interface(int minor)
|
||||||
|
|
||||||
static int __init hid_init(void)
|
static int __init hid_init(void)
|
||||||
{
|
{
|
||||||
int retval = -ENOMEM;
|
int retval;
|
||||||
|
|
||||||
retval = hid_quirks_init(quirks_param, BUS_USB, MAX_USBHID_BOOT_QUIRKS);
|
retval = hid_quirks_init(quirks_param, BUS_USB, MAX_USBHID_BOOT_QUIRKS);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
|
@ -83,6 +84,7 @@ struct usbhid_device {
|
||||||
struct mutex mutex; /* start/stop/open/close */
|
struct mutex mutex; /* start/stop/open/close */
|
||||||
spinlock_t lock; /* fifo spinlock */
|
spinlock_t lock; /* fifo spinlock */
|
||||||
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
|
||||||
|
ktime_t input_start_time; /* When to start handling input */
|
||||||
struct timer_list io_retry; /* Retry timer */
|
struct timer_list io_retry; /* Retry timer */
|
||||||
unsigned long stop_retry; /* Time to give up, in jiffies */
|
unsigned long stop_retry; /* Time to give up, in jiffies */
|
||||||
unsigned int retry_delay; /* Delay length in ms */
|
unsigned int retry_delay; /* Delay length in ms */
|
||||||
|
|
Loading…
Reference in New Issue