Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid
Pull HID fixes from Jiri Kosina: - HID++ device support regression fixes (race condition during cleanup, device detection fix, opps fix) from Andrey Smirnov - disable PM on i2c-hid, as it's causing problems with a lot of devices; other OSes apparently don't implement/enable it either; from Kai-Heng Feng - error handling fix in intel-ish driver, from Zhang Lixu - syzbot fuzzer fix for HID core code from Alan Stern - a few other tiny fixups (printk message cleanup, new device ID) * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid: HID: i2c-hid: add Trekstor Primebook C11B to descriptor override HID: logitech-hidpp: do all FF cleanup in hidpp_ff_destroy() HID: logitech-hidpp: rework device validation HID: logitech-hidpp: split g920_get_config() HID: i2c-hid: Remove runtime power management HID: intel-ish-hid: fix wrong error handling in ishtp_cl_alloc_tx_ring() HID: google: add magnemite/masterball USB ids HID: Fix assumption that devices have inputs HID: prodikeys: make array keys static const, makes object smaller HID: fix error message in hid_open_report()
This commit is contained in:
commit
0365fb6bae
@ -63,13 +63,20 @@ static int axff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct axff_device *axff;
|
struct axff_device *axff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int field_count = 0;
|
int field_count = 0;
|
||||||
int i, j;
|
int i, j;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1139,6 +1139,7 @@ int hid_open_report(struct hid_device *device)
|
|||||||
__u8 *start;
|
__u8 *start;
|
||||||
__u8 *buf;
|
__u8 *buf;
|
||||||
__u8 *end;
|
__u8 *end;
|
||||||
|
__u8 *next;
|
||||||
int ret;
|
int ret;
|
||||||
static int (*dispatch_type[])(struct hid_parser *parser,
|
static int (*dispatch_type[])(struct hid_parser *parser,
|
||||||
struct hid_item *item) = {
|
struct hid_item *item) = {
|
||||||
@ -1192,7 +1193,8 @@ int hid_open_report(struct hid_device *device)
|
|||||||
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
|
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
|
||||||
|
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
while ((start = fetch_item(start, end, &item)) != NULL) {
|
while ((next = fetch_item(start, end, &item)) != NULL) {
|
||||||
|
start = next;
|
||||||
|
|
||||||
if (item.format != HID_ITEM_FORMAT_SHORT) {
|
if (item.format != HID_ITEM_FORMAT_SHORT) {
|
||||||
hid_err(device, "unexpected long global item\n");
|
hid_err(device, "unexpected long global item\n");
|
||||||
@ -1230,7 +1232,8 @@ int hid_open_report(struct hid_device *device)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hid_err(device, "item fetching failed at offset %d\n", (int)(end - start));
|
hid_err(device, "item fetching failed at offset %u/%u\n",
|
||||||
|
size - (unsigned int)(end - start), size);
|
||||||
err:
|
err:
|
||||||
kfree(parser->collection_stack);
|
kfree(parser->collection_stack);
|
||||||
alloc_err:
|
alloc_err:
|
||||||
|
@ -75,13 +75,19 @@ static int drff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct drff_device *drff;
|
struct drff_device *drff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -47,13 +47,19 @@ static int emsff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct emsff_device *emsff;
|
struct emsff_device *emsff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -64,14 +64,20 @@ static int gaff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct gaff_device *gaff;
|
struct gaff_device *gaff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct list_head *report_ptr = report_list;
|
struct list_head *report_ptr = report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -469,6 +469,10 @@ static int hammer_probe(struct hid_device *hdev,
|
|||||||
static const struct hid_device_id hammer_devices[] = {
|
static const struct hid_device_id hammer_devices[] = {
|
||||||
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||||
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
|
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_HAMMER) },
|
||||||
|
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||||
|
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MAGNEMITE) },
|
||||||
|
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||||
|
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_MASTERBALL) },
|
||||||
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||||
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
|
USB_VENDOR_ID_GOOGLE, USB_DEVICE_ID_GOOGLE_STAFF) },
|
||||||
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
|
||||||
|
@ -124,13 +124,19 @@ static int holtekff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct holtekff_device *holtekff;
|
struct holtekff_device *holtekff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output report found\n");
|
hid_err(hid, "no output report found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -476,6 +476,8 @@
|
|||||||
#define USB_DEVICE_ID_GOOGLE_STAFF 0x502b
|
#define USB_DEVICE_ID_GOOGLE_STAFF 0x502b
|
||||||
#define USB_DEVICE_ID_GOOGLE_WAND 0x502d
|
#define USB_DEVICE_ID_GOOGLE_WAND 0x502d
|
||||||
#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030
|
#define USB_DEVICE_ID_GOOGLE_WHISKERS 0x5030
|
||||||
|
#define USB_DEVICE_ID_GOOGLE_MASTERBALL 0x503c
|
||||||
|
#define USB_DEVICE_ID_GOOGLE_MAGNEMITE 0x503d
|
||||||
|
|
||||||
#define USB_VENDOR_ID_GOTOP 0x08f2
|
#define USB_VENDOR_ID_GOTOP 0x08f2
|
||||||
#define USB_DEVICE_ID_SUPER_Q2 0x007f
|
#define USB_DEVICE_ID_SUPER_Q2 0x007f
|
||||||
|
@ -50,11 +50,17 @@ int lg2ff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct lg2ff_device *lg2ff;
|
struct lg2ff_device *lg2ff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *dev;
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
|
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
|
||||||
if (!report)
|
if (!report)
|
||||||
|
@ -117,12 +117,19 @@ static const signed short ff3_joystick_ac[] = {
|
|||||||
|
|
||||||
int lg3ff_init(struct hid_device *hid)
|
int lg3ff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
const signed short *ff_bits = ff3_joystick_ac;
|
const signed short *ff_bits = ff3_joystick_ac;
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1253,8 +1253,8 @@ static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_produc
|
|||||||
|
|
||||||
int lg4ff_init(struct hid_device *hid)
|
int lg4ff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
|
||||||
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
|
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
|
||||||
@ -1266,6 +1266,13 @@ int lg4ff_init(struct hid_device *hid)
|
|||||||
int mmode_ret, mmode_idx = -1;
|
int mmode_ret, mmode_idx = -1;
|
||||||
u16 real_product_id;
|
u16 real_product_id;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -115,12 +115,19 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
|||||||
|
|
||||||
int lgff_init(struct hid_device* hid)
|
int lgff_init(struct hid_device* hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
const signed short *ff_bits = ff_joystick;
|
const signed short *ff_bits = ff_joystick;
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@ -1669,6 +1669,7 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
|
|||||||
|
|
||||||
#define HIDPP_FF_EFFECTID_NONE -1
|
#define HIDPP_FF_EFFECTID_NONE -1
|
||||||
#define HIDPP_FF_EFFECTID_AUTOCENTER -2
|
#define HIDPP_FF_EFFECTID_AUTOCENTER -2
|
||||||
|
#define HIDPP_AUTOCENTER_PARAMS_LENGTH 18
|
||||||
|
|
||||||
#define HIDPP_FF_MAX_PARAMS 20
|
#define HIDPP_FF_MAX_PARAMS 20
|
||||||
#define HIDPP_FF_RESERVED_SLOTS 1
|
#define HIDPP_FF_RESERVED_SLOTS 1
|
||||||
@ -2009,7 +2010,7 @@ static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id)
|
|||||||
static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
||||||
{
|
{
|
||||||
struct hidpp_ff_private_data *data = dev->ff->private;
|
struct hidpp_ff_private_data *data = dev->ff->private;
|
||||||
u8 params[18];
|
u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH];
|
||||||
|
|
||||||
dbg_hid("Setting autocenter to %d.\n", magnitude);
|
dbg_hid("Setting autocenter to %d.\n", magnitude);
|
||||||
|
|
||||||
@ -2077,23 +2078,34 @@ static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp
|
|||||||
static void hidpp_ff_destroy(struct ff_device *ff)
|
static void hidpp_ff_destroy(struct ff_device *ff)
|
||||||
{
|
{
|
||||||
struct hidpp_ff_private_data *data = ff->private;
|
struct hidpp_ff_private_data *data = ff->private;
|
||||||
|
struct hid_device *hid = data->hidpp->hid_dev;
|
||||||
|
|
||||||
|
hid_info(hid, "Unloading HID++ force feedback.\n");
|
||||||
|
|
||||||
|
device_remove_file(&hid->dev, &dev_attr_range);
|
||||||
|
destroy_workqueue(data->wq);
|
||||||
kfree(data->effect_ids);
|
kfree(data->effect_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
|
static int hidpp_ff_init(struct hidpp_device *hidpp,
|
||||||
|
struct hidpp_ff_private_data *data)
|
||||||
{
|
{
|
||||||
struct hid_device *hid = hidpp->hid_dev;
|
struct hid_device *hid = hidpp->hid_dev;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
|
const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
|
||||||
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
|
const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
|
||||||
struct ff_device *ff;
|
struct ff_device *ff;
|
||||||
struct hidpp_report response;
|
int error, j, num_slots = data->num_effects;
|
||||||
struct hidpp_ff_private_data *data;
|
|
||||||
int error, j, num_slots;
|
|
||||||
u8 version;
|
u8 version;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
hid_err(hid, "Struct input_dev not set!\n");
|
hid_err(hid, "Struct input_dev not set!\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -2109,27 +2121,17 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
|
|||||||
for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++)
|
for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++)
|
||||||
set_bit(hidpp_ff_effects_v2[j], dev->ffbit);
|
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,
|
|
||||||
HIDPP_FF_GET_INFO, NULL, 0, &response);
|
|
||||||
if (error) {
|
|
||||||
if (error < 0)
|
|
||||||
return error;
|
|
||||||
hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n",
|
|
||||||
__func__, error);
|
|
||||||
return -EPROTO;
|
|
||||||
}
|
|
||||||
|
|
||||||
num_slots = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
|
|
||||||
|
|
||||||
error = input_ff_create(dev, num_slots);
|
error = input_ff_create(dev, num_slots);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
hid_err(dev, "Failed to create FF device!\n");
|
hid_err(dev, "Failed to create FF device!\n");
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
* Create a copy of passed data, so we can transfer memory
|
||||||
|
* ownership to FF core
|
||||||
|
*/
|
||||||
|
data = kmemdup(data, sizeof(*data), GFP_KERNEL);
|
||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
|
data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL);
|
||||||
@ -2145,10 +2147,7 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
|
|||||||
}
|
}
|
||||||
|
|
||||||
data->hidpp = hidpp;
|
data->hidpp = hidpp;
|
||||||
data->feature_index = feature_index;
|
|
||||||
data->version = version;
|
data->version = version;
|
||||||
data->slot_autocenter = 0;
|
|
||||||
data->num_effects = num_slots;
|
|
||||||
for (j = 0; j < num_slots; j++)
|
for (j = 0; j < num_slots; j++)
|
||||||
data->effect_ids[j] = -1;
|
data->effect_ids[j] = -1;
|
||||||
|
|
||||||
@ -2162,68 +2161,20 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index)
|
|||||||
ff->set_autocenter = hidpp_ff_set_autocenter;
|
ff->set_autocenter = hidpp_ff_set_autocenter;
|
||||||
ff->destroy = hidpp_ff_destroy;
|
ff->destroy = hidpp_ff_destroy;
|
||||||
|
|
||||||
|
|
||||||
/* reset all forces */
|
|
||||||
error = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
||||||
HIDPP_FF_RESET_ALL, NULL, 0, &response);
|
|
||||||
|
|
||||||
/* Read current Range */
|
|
||||||
error = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
||||||
HIDPP_FF_GET_APERTURE, NULL, 0, &response);
|
|
||||||
if (error)
|
|
||||||
hid_warn(hidpp->hid_dev, "Failed to read range from device!\n");
|
|
||||||
data->range = error ? 900 : get_unaligned_be16(&response.fap.params[0]);
|
|
||||||
|
|
||||||
/* Create sysfs interface */
|
/* Create sysfs interface */
|
||||||
error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
|
error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range);
|
||||||
if (error)
|
if (error)
|
||||||
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
|
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
|
||||||
|
|
||||||
/* Read the current gain values */
|
|
||||||
error = hidpp_send_fap_command_sync(hidpp, feature_index,
|
|
||||||
HIDPP_FF_GET_GLOBAL_GAINS, NULL, 0, &response);
|
|
||||||
if (error)
|
|
||||||
hid_warn(hidpp->hid_dev, "Failed to read gain values from device!\n");
|
|
||||||
data->gain = error ? 0xffff : get_unaligned_be16(&response.fap.params[0]);
|
|
||||||
/* ignore boost value at response.fap.params[2] */
|
|
||||||
|
|
||||||
/* init the hardware command queue */
|
/* init the hardware command queue */
|
||||||
atomic_set(&data->workqueue_size, 0);
|
atomic_set(&data->workqueue_size, 0);
|
||||||
|
|
||||||
/* initialize with zero autocenter to get wheel in usable state */
|
|
||||||
hidpp_ff_set_autocenter(dev, 0);
|
|
||||||
|
|
||||||
hid_info(hid, "Force feedback support loaded (firmware release %d).\n",
|
hid_info(hid, "Force feedback support loaded (firmware release %d).\n",
|
||||||
version);
|
version);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hidpp_ff_deinit(struct hid_device *hid)
|
|
||||||
{
|
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
struct hidpp_ff_private_data *data;
|
|
||||||
|
|
||||||
if (!dev) {
|
|
||||||
hid_err(hid, "Struct input_dev not found!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hid_info(hid, "Unloading HID++ force feedback.\n");
|
|
||||||
data = dev->ff->private;
|
|
||||||
if (!data) {
|
|
||||||
hid_err(hid, "Private data not found!\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy_workqueue(data->wq);
|
|
||||||
device_remove_file(&hid->dev, &dev_attr_range);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ************************************************************************** */
|
/* ************************************************************************** */
|
||||||
/* */
|
/* */
|
||||||
/* Device Support */
|
/* Device Support */
|
||||||
@ -2725,24 +2676,93 @@ static int k400_connect(struct hid_device *hdev, bool connected)
|
|||||||
|
|
||||||
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
|
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
|
||||||
|
|
||||||
static int g920_get_config(struct hidpp_device *hidpp)
|
static int g920_ff_set_autocenter(struct hidpp_device *hidpp,
|
||||||
|
struct hidpp_ff_private_data *data)
|
||||||
{
|
{
|
||||||
u8 feature_type;
|
struct hidpp_report response;
|
||||||
u8 feature_index;
|
u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH] = {
|
||||||
|
[1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART,
|
||||||
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* initialize with zero autocenter to get wheel in usable state */
|
||||||
|
|
||||||
|
dbg_hid("Setting autocenter to 0.\n");
|
||||||
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
||||||
|
HIDPP_FF_DOWNLOAD_EFFECT,
|
||||||
|
params, ARRAY_SIZE(params),
|
||||||
|
&response);
|
||||||
|
if (ret)
|
||||||
|
hid_warn(hidpp->hid_dev, "Failed to autocenter device!\n");
|
||||||
|
else
|
||||||
|
data->slot_autocenter = response.fap.params[0];
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int g920_get_config(struct hidpp_device *hidpp,
|
||||||
|
struct hidpp_ff_private_data *data)
|
||||||
|
{
|
||||||
|
struct hidpp_report response;
|
||||||
|
u8 feature_type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(data, 0, sizeof(*data));
|
||||||
|
|
||||||
/* Find feature and store for later use */
|
/* Find feature and store for later use */
|
||||||
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
|
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
|
||||||
&feature_index, &feature_type);
|
&data->feature_index, &feature_type);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = hidpp_ff_init(hidpp, feature_index);
|
/* Read number of slots available in device */
|
||||||
if (ret)
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
||||||
hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n",
|
HIDPP_FF_GET_INFO,
|
||||||
ret);
|
NULL, 0,
|
||||||
|
&response);
|
||||||
|
if (ret) {
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
hid_err(hidpp->hid_dev,
|
||||||
|
"%s: received protocol error 0x%02x\n", __func__, ret);
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
data->num_effects = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS;
|
||||||
|
|
||||||
|
/* reset all forces */
|
||||||
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
||||||
|
HIDPP_FF_RESET_ALL,
|
||||||
|
NULL, 0,
|
||||||
|
&response);
|
||||||
|
if (ret)
|
||||||
|
hid_warn(hidpp->hid_dev, "Failed to reset all forces!\n");
|
||||||
|
|
||||||
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
||||||
|
HIDPP_FF_GET_APERTURE,
|
||||||
|
NULL, 0,
|
||||||
|
&response);
|
||||||
|
if (ret) {
|
||||||
|
hid_warn(hidpp->hid_dev,
|
||||||
|
"Failed to read range from device!\n");
|
||||||
|
}
|
||||||
|
data->range = ret ?
|
||||||
|
900 : get_unaligned_be16(&response.fap.params[0]);
|
||||||
|
|
||||||
|
/* Read the current gain values */
|
||||||
|
ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
|
||||||
|
HIDPP_FF_GET_GLOBAL_GAINS,
|
||||||
|
NULL, 0,
|
||||||
|
&response);
|
||||||
|
if (ret)
|
||||||
|
hid_warn(hidpp->hid_dev,
|
||||||
|
"Failed to read gain values from device!\n");
|
||||||
|
data->gain = ret ?
|
||||||
|
0xffff : get_unaligned_be16(&response.fap.params[0]);
|
||||||
|
|
||||||
|
/* ignore boost value at response.fap.params[2] */
|
||||||
|
|
||||||
|
return g920_ff_set_autocenter(hidpp, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
@ -3458,34 +3478,45 @@ static int hidpp_get_report_length(struct hid_device *hdev, int id)
|
|||||||
return report->field[0]->report_count + 1;
|
return report->field[0]->report_count + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hidpp_validate_report(struct hid_device *hdev, int id,
|
|
||||||
int expected_length, bool optional)
|
|
||||||
{
|
|
||||||
int report_length;
|
|
||||||
|
|
||||||
if (id >= HID_MAX_IDS || id < 0) {
|
|
||||||
hid_err(hdev, "invalid HID report id %u\n", id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
report_length = hidpp_get_report_length(hdev, id);
|
|
||||||
if (!report_length)
|
|
||||||
return optional;
|
|
||||||
|
|
||||||
if (report_length < expected_length) {
|
|
||||||
hid_warn(hdev, "not enough values in hidpp report %d\n", id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool hidpp_validate_device(struct hid_device *hdev)
|
static bool hidpp_validate_device(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
return hidpp_validate_report(hdev, REPORT_ID_HIDPP_SHORT,
|
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
|
||||||
HIDPP_REPORT_SHORT_LENGTH, false) &&
|
int id, report_length, supported_reports = 0;
|
||||||
hidpp_validate_report(hdev, REPORT_ID_HIDPP_LONG,
|
|
||||||
HIDPP_REPORT_LONG_LENGTH, true);
|
id = REPORT_ID_HIDPP_SHORT;
|
||||||
|
report_length = hidpp_get_report_length(hdev, id);
|
||||||
|
if (report_length) {
|
||||||
|
if (report_length < HIDPP_REPORT_SHORT_LENGTH)
|
||||||
|
goto bad_device;
|
||||||
|
|
||||||
|
supported_reports++;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = REPORT_ID_HIDPP_LONG;
|
||||||
|
report_length = hidpp_get_report_length(hdev, id);
|
||||||
|
if (report_length) {
|
||||||
|
if (report_length < HIDPP_REPORT_LONG_LENGTH)
|
||||||
|
goto bad_device;
|
||||||
|
|
||||||
|
supported_reports++;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = REPORT_ID_HIDPP_VERY_LONG;
|
||||||
|
report_length = hidpp_get_report_length(hdev, id);
|
||||||
|
if (report_length) {
|
||||||
|
if (report_length < HIDPP_REPORT_LONG_LENGTH ||
|
||||||
|
report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH)
|
||||||
|
goto bad_device;
|
||||||
|
|
||||||
|
supported_reports++;
|
||||||
|
hidpp->very_long_report_length = report_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return supported_reports;
|
||||||
|
|
||||||
|
bad_device:
|
||||||
|
hid_warn(hdev, "not enough values in hidpp report %d\n", id);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hidpp_application_equals(struct hid_device *hdev,
|
static bool hidpp_application_equals(struct hid_device *hdev,
|
||||||
@ -3505,6 +3536,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||||||
int ret;
|
int ret;
|
||||||
bool connected;
|
bool connected;
|
||||||
unsigned int connect_mask = HID_CONNECT_DEFAULT;
|
unsigned int connect_mask = HID_CONNECT_DEFAULT;
|
||||||
|
struct hidpp_ff_private_data data;
|
||||||
|
|
||||||
/* report_fixup needs drvdata to be set before we call hid_parse */
|
/* report_fixup needs drvdata to be set before we call hid_parse */
|
||||||
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
|
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
|
||||||
@ -3531,11 +3563,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||||||
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
hidpp->very_long_report_length =
|
|
||||||
hidpp_get_report_length(hdev, REPORT_ID_HIDPP_VERY_LONG);
|
|
||||||
if (hidpp->very_long_report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH)
|
|
||||||
hidpp->very_long_report_length = HIDPP_REPORT_VERY_LONG_MAX_LENGTH;
|
|
||||||
|
|
||||||
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
|
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
|
||||||
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
|
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
|
||||||
|
|
||||||
@ -3614,7 +3641,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto hid_hw_init_fail;
|
goto hid_hw_init_fail;
|
||||||
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
|
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
|
||||||
ret = g920_get_config(hidpp);
|
ret = g920_get_config(hidpp, &data);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto hid_hw_init_fail;
|
goto hid_hw_init_fail;
|
||||||
}
|
}
|
||||||
@ -3636,6 +3663,14 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||||||
goto hid_hw_start_fail;
|
goto hid_hw_start_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
|
||||||
|
ret = hidpp_ff_init(hidpp, &data);
|
||||||
|
if (ret)
|
||||||
|
hid_warn(hidpp->hid_dev,
|
||||||
|
"Unable to initialize force feedback support, errno %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
hid_hw_init_fail:
|
hid_hw_init_fail:
|
||||||
@ -3658,9 +3693,6 @@ static void hidpp_remove(struct hid_device *hdev)
|
|||||||
|
|
||||||
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
|
sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group);
|
||||||
|
|
||||||
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)
|
|
||||||
hidpp_ff_deinit(hdev);
|
|
||||||
|
|
||||||
hid_hw_stop(hdev);
|
hid_hw_stop(hdev);
|
||||||
cancel_work_sync(&hidpp->work);
|
cancel_work_sync(&hidpp->work);
|
||||||
mutex_destroy(&hidpp->send_mutex);
|
mutex_destroy(&hidpp->send_mutex);
|
||||||
|
@ -328,11 +328,17 @@ static int ms_play_effect(struct input_dev *dev, void *data,
|
|||||||
|
|
||||||
static int ms_init_ff(struct hid_device *hdev)
|
static int ms_init_ff(struct hid_device *hdev)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hdev->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *input_dev;
|
||||||
struct input_dev *input_dev = hidinput->input;
|
|
||||||
struct ms_data *ms = hid_get_drvdata(hdev);
|
struct ms_data *ms = hid_get_drvdata(hdev);
|
||||||
|
|
||||||
|
if (list_empty(&hdev->inputs)) {
|
||||||
|
hid_err(hdev, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
|
||||||
|
input_dev = hidinput->input;
|
||||||
|
|
||||||
if (!(ms->quirks & MS_QUIRK_FF))
|
if (!(ms->quirks & MS_QUIRK_FF))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -516,7 +516,7 @@ static void pcmidi_setup_extra_keys(
|
|||||||
MY PICTURES => KEY_WORDPROCESSOR
|
MY PICTURES => KEY_WORDPROCESSOR
|
||||||
MY MUSIC=> KEY_SPREADSHEET
|
MY MUSIC=> KEY_SPREADSHEET
|
||||||
*/
|
*/
|
||||||
unsigned int keys[] = {
|
static const unsigned int keys[] = {
|
||||||
KEY_FN,
|
KEY_FN,
|
||||||
KEY_MESSENGER, KEY_CALENDAR,
|
KEY_MESSENGER, KEY_CALENDAR,
|
||||||
KEY_ADDRESSBOOK, KEY_DOCUMENTS,
|
KEY_ADDRESSBOOK, KEY_DOCUMENTS,
|
||||||
@ -532,7 +532,7 @@ static void pcmidi_setup_extra_keys(
|
|||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned int *pkeys = &keys[0];
|
const unsigned int *pkeys = &keys[0];
|
||||||
unsigned short i;
|
unsigned short i;
|
||||||
|
|
||||||
if (pm->ifnum != 1) /* only set up ONCE for interace 1 */
|
if (pm->ifnum != 1) /* only set up ONCE for interace 1 */
|
||||||
|
@ -2254,9 +2254,15 @@ static int sony_play_effect(struct input_dev *dev, void *data,
|
|||||||
|
|
||||||
static int sony_init_ff(struct sony_sc *sc)
|
static int sony_init_ff(struct sony_sc *sc)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *input_dev;
|
||||||
struct input_dev *input_dev = hidinput->input;
|
|
||||||
|
if (list_empty(&sc->hdev->inputs)) {
|
||||||
|
hid_err(sc->hdev, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(sc->hdev->inputs.next, struct hid_input, list);
|
||||||
|
input_dev = hidinput->input;
|
||||||
|
|
||||||
input_set_capability(input_dev, EV_FF, FF_RUMBLE);
|
input_set_capability(input_dev, EV_FF, FF_RUMBLE);
|
||||||
return input_ff_create_memless(input_dev, NULL, sony_play_effect);
|
return input_ff_create_memless(input_dev, NULL, sony_play_effect);
|
||||||
|
@ -124,12 +124,18 @@ static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
|
|||||||
struct tmff_device *tmff;
|
struct tmff_device *tmff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct list_head *report_list;
|
struct list_head *report_list;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *input_dev;
|
||||||
struct input_dev *input_dev = hidinput->input;
|
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
input_dev = hidinput->input;
|
||||||
|
|
||||||
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||||
if (!tmff)
|
if (!tmff)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -54,11 +54,17 @@ static int zpff_init(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct zpff_device *zpff;
|
struct zpff_device *zpff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *dev;
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
int i, error;
|
int i, error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
|
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
|
||||||
if (!report)
|
if (!report)
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
@ -48,8 +47,6 @@
|
|||||||
/* quirks to control the device */
|
/* quirks to control the device */
|
||||||
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
|
#define I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV BIT(0)
|
||||||
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
|
#define I2C_HID_QUIRK_NO_IRQ_AFTER_RESET BIT(1)
|
||||||
#define I2C_HID_QUIRK_NO_RUNTIME_PM BIT(2)
|
|
||||||
#define I2C_HID_QUIRK_DELAY_AFTER_SLEEP BIT(3)
|
|
||||||
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
|
#define I2C_HID_QUIRK_BOGUS_IRQ BIT(4)
|
||||||
|
|
||||||
/* flags */
|
/* flags */
|
||||||
@ -172,14 +169,7 @@ static const struct i2c_hid_quirks {
|
|||||||
{ USB_VENDOR_ID_WEIDA, HID_ANY_ID,
|
{ USB_VENDOR_ID_WEIDA, HID_ANY_ID,
|
||||||
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
|
I2C_HID_QUIRK_SET_PWR_WAKEUP_DEV },
|
||||||
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
|
{ I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288,
|
||||||
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET |
|
I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
|
||||||
I2C_HID_QUIRK_NO_RUNTIME_PM },
|
|
||||||
{ I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_4B33,
|
|
||||||
I2C_HID_QUIRK_DELAY_AFTER_SLEEP },
|
|
||||||
{ USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001,
|
|
||||||
I2C_HID_QUIRK_NO_RUNTIME_PM },
|
|
||||||
{ I2C_VENDOR_ID_GOODIX, I2C_DEVICE_ID_GOODIX_01F0,
|
|
||||||
I2C_HID_QUIRK_NO_RUNTIME_PM },
|
|
||||||
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
|
{ USB_VENDOR_ID_ELAN, HID_ANY_ID,
|
||||||
I2C_HID_QUIRK_BOGUS_IRQ },
|
I2C_HID_QUIRK_BOGUS_IRQ },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
@ -397,7 +387,6 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
|
|||||||
{
|
{
|
||||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long now, delay;
|
|
||||||
|
|
||||||
i2c_hid_dbg(ihid, "%s\n", __func__);
|
i2c_hid_dbg(ihid, "%s\n", __func__);
|
||||||
|
|
||||||
@ -415,22 +404,9 @@ static int i2c_hid_set_power(struct i2c_client *client, int power_state)
|
|||||||
goto set_pwr_exit;
|
goto set_pwr_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ihid->quirks & I2C_HID_QUIRK_DELAY_AFTER_SLEEP &&
|
|
||||||
power_state == I2C_HID_PWR_ON) {
|
|
||||||
now = jiffies;
|
|
||||||
if (time_after(ihid->sleep_delay, now)) {
|
|
||||||
delay = jiffies_to_usecs(ihid->sleep_delay - now);
|
|
||||||
usleep_range(delay, delay + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
|
ret = __i2c_hid_command(client, &hid_set_power_cmd, power_state,
|
||||||
0, NULL, 0, NULL, 0);
|
0, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
if (ihid->quirks & I2C_HID_QUIRK_DELAY_AFTER_SLEEP &&
|
|
||||||
power_state == I2C_HID_PWR_SLEEP)
|
|
||||||
ihid->sleep_delay = jiffies + msecs_to_jiffies(20);
|
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(&client->dev, "failed to change power setting.\n");
|
dev_err(&client->dev, "failed to change power setting.\n");
|
||||||
|
|
||||||
@ -791,11 +767,6 @@ static int i2c_hid_open(struct hid_device *hid)
|
|||||||
{
|
{
|
||||||
struct i2c_client *client = hid->driver_data;
|
struct i2c_client *client = hid->driver_data;
|
||||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(&client->dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
set_bit(I2C_HID_STARTED, &ihid->flags);
|
set_bit(I2C_HID_STARTED, &ihid->flags);
|
||||||
return 0;
|
return 0;
|
||||||
@ -807,27 +778,6 @@ static void i2c_hid_close(struct hid_device *hid)
|
|||||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||||
|
|
||||||
clear_bit(I2C_HID_STARTED, &ihid->flags);
|
clear_bit(I2C_HID_STARTED, &ihid->flags);
|
||||||
|
|
||||||
/* Save some power */
|
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_hid_power(struct hid_device *hid, int lvl)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = hid->driver_data;
|
|
||||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
|
||||||
|
|
||||||
i2c_hid_dbg(ihid, "%s lvl:%d\n", __func__, lvl);
|
|
||||||
|
|
||||||
switch (lvl) {
|
|
||||||
case PM_HINT_FULLON:
|
|
||||||
pm_runtime_get_sync(&client->dev);
|
|
||||||
break;
|
|
||||||
case PM_HINT_NORMAL:
|
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hid_ll_driver i2c_hid_ll_driver = {
|
struct hid_ll_driver i2c_hid_ll_driver = {
|
||||||
@ -836,7 +786,6 @@ struct hid_ll_driver i2c_hid_ll_driver = {
|
|||||||
.stop = i2c_hid_stop,
|
.stop = i2c_hid_stop,
|
||||||
.open = i2c_hid_open,
|
.open = i2c_hid_open,
|
||||||
.close = i2c_hid_close,
|
.close = i2c_hid_close,
|
||||||
.power = i2c_hid_power,
|
|
||||||
.output_report = i2c_hid_output_report,
|
.output_report = i2c_hid_output_report,
|
||||||
.raw_request = i2c_hid_raw_request,
|
.raw_request = i2c_hid_raw_request,
|
||||||
};
|
};
|
||||||
@ -1104,9 +1053,6 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
i2c_hid_acpi_fix_up_power(&client->dev);
|
i2c_hid_acpi_fix_up_power(&client->dev);
|
||||||
|
|
||||||
pm_runtime_get_noresume(&client->dev);
|
|
||||||
pm_runtime_set_active(&client->dev);
|
|
||||||
pm_runtime_enable(&client->dev);
|
|
||||||
device_enable_async_suspend(&client->dev);
|
device_enable_async_suspend(&client->dev);
|
||||||
|
|
||||||
/* Make sure there is something at this address */
|
/* Make sure there is something at this address */
|
||||||
@ -1114,16 +1060,16 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
|
dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
|
||||||
ret = -ENXIO;
|
ret = -ENXIO;
|
||||||
goto err_pm;
|
goto err_regulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = i2c_hid_fetch_hid_descriptor(ihid);
|
ret = i2c_hid_fetch_hid_descriptor(ihid);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_pm;
|
goto err_regulator;
|
||||||
|
|
||||||
ret = i2c_hid_init_irq(client);
|
ret = i2c_hid_init_irq(client);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto err_pm;
|
goto err_regulator;
|
||||||
|
|
||||||
hid = hid_allocate_device();
|
hid = hid_allocate_device();
|
||||||
if (IS_ERR(hid)) {
|
if (IS_ERR(hid)) {
|
||||||
@ -1154,9 +1100,6 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||||||
goto err_mem_free;
|
goto err_mem_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
|
|
||||||
pm_runtime_put(&client->dev);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_mem_free:
|
err_mem_free:
|
||||||
@ -1165,10 +1108,6 @@ static int i2c_hid_probe(struct i2c_client *client,
|
|||||||
err_irq:
|
err_irq:
|
||||||
free_irq(client->irq, ihid);
|
free_irq(client->irq, ihid);
|
||||||
|
|
||||||
err_pm:
|
|
||||||
pm_runtime_put_noidle(&client->dev);
|
|
||||||
pm_runtime_disable(&client->dev);
|
|
||||||
|
|
||||||
err_regulator:
|
err_regulator:
|
||||||
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
|
||||||
ihid->pdata.supplies);
|
ihid->pdata.supplies);
|
||||||
@ -1181,12 +1120,6 @@ static int i2c_hid_remove(struct i2c_client *client)
|
|||||||
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
struct i2c_hid *ihid = i2c_get_clientdata(client);
|
||||||
struct hid_device *hid;
|
struct hid_device *hid;
|
||||||
|
|
||||||
if (!(ihid->quirks & I2C_HID_QUIRK_NO_RUNTIME_PM))
|
|
||||||
pm_runtime_get_sync(&client->dev);
|
|
||||||
pm_runtime_disable(&client->dev);
|
|
||||||
pm_runtime_set_suspended(&client->dev);
|
|
||||||
pm_runtime_put_noidle(&client->dev);
|
|
||||||
|
|
||||||
hid = ihid->hid;
|
hid = ihid->hid;
|
||||||
hid_destroy_device(hid);
|
hid_destroy_device(hid);
|
||||||
|
|
||||||
@ -1219,25 +1152,15 @@ static int i2c_hid_suspend(struct device *dev)
|
|||||||
int wake_status;
|
int wake_status;
|
||||||
|
|
||||||
if (hid->driver && hid->driver->suspend) {
|
if (hid->driver && hid->driver->suspend) {
|
||||||
/*
|
|
||||||
* Wake up the device so that IO issues in
|
|
||||||
* HID driver's suspend code can succeed.
|
|
||||||
*/
|
|
||||||
ret = pm_runtime_resume(dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
|
ret = hid->driver->suspend(hid, PMSG_SUSPEND);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pm_runtime_suspended(dev)) {
|
/* Save some power */
|
||||||
/* Save some power */
|
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
||||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
|
||||||
|
|
||||||
disable_irq(client->irq);
|
disable_irq(client->irq);
|
||||||
}
|
|
||||||
|
|
||||||
if (device_may_wakeup(&client->dev)) {
|
if (device_may_wakeup(&client->dev)) {
|
||||||
wake_status = enable_irq_wake(client->irq);
|
wake_status = enable_irq_wake(client->irq);
|
||||||
@ -1279,11 +1202,6 @@ static int i2c_hid_resume(struct device *dev)
|
|||||||
wake_status);
|
wake_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We'll resume to full power */
|
|
||||||
pm_runtime_disable(dev);
|
|
||||||
pm_runtime_set_active(dev);
|
|
||||||
pm_runtime_enable(dev);
|
|
||||||
|
|
||||||
enable_irq(client->irq);
|
enable_irq(client->irq);
|
||||||
|
|
||||||
/* Instead of resetting device, simply powers the device on. This
|
/* Instead of resetting device, simply powers the device on. This
|
||||||
@ -1304,30 +1222,8 @@ static int i2c_hid_resume(struct device *dev)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
static int i2c_hid_runtime_suspend(struct device *dev)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
|
|
||||||
i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
|
|
||||||
disable_irq(client->irq);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int i2c_hid_runtime_resume(struct device *dev)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
|
||||||
|
|
||||||
enable_irq(client->irq);
|
|
||||||
i2c_hid_set_power(client, I2C_HID_PWR_ON);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const struct dev_pm_ops i2c_hid_pm = {
|
static const struct dev_pm_ops i2c_hid_pm = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
|
SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
|
||||||
SET_RUNTIME_PM_OPS(i2c_hid_runtime_suspend, i2c_hid_runtime_resume,
|
|
||||||
NULL)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct i2c_device_id i2c_hid_id_table[] = {
|
static const struct i2c_device_id i2c_hid_id_table[] = {
|
||||||
|
@ -322,6 +322,25 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
|
|||||||
},
|
},
|
||||||
.driver_data = (void *)&sipodev_desc
|
.driver_data = (void *)&sipodev_desc
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There are at least 2 Primebook C11B versions, the older
|
||||||
|
* version has a product-name of "Primebook C11B", and a
|
||||||
|
* bios version / release / firmware revision of:
|
||||||
|
* V2.1.2 / 05/03/2018 / 18.2
|
||||||
|
* The new version has "PRIMEBOOK C11B" as product-name and a
|
||||||
|
* bios version / release / firmware revision of:
|
||||||
|
* CFALKSW05_BIOS_V1.1.2 / 11/19/2018 / 19.2
|
||||||
|
* Only the older version needs this quirk, note the newer
|
||||||
|
* version will not match as it has a different product-name.
|
||||||
|
*/
|
||||||
|
.ident = "Trekstor Primebook C11B",
|
||||||
|
.matches = {
|
||||||
|
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
|
||||||
|
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11B"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&sipodev_desc
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.ident = "Direkt-Tek DTLAPY116-2",
|
.ident = "Direkt-Tek DTLAPY116-2",
|
||||||
.matches = {
|
.matches = {
|
||||||
|
@ -84,7 +84,7 @@ int ishtp_cl_alloc_tx_ring(struct ishtp_cl *cl)
|
|||||||
return 0;
|
return 0;
|
||||||
out:
|
out:
|
||||||
dev_err(&cl->device->dev, "error in allocating Tx pool\n");
|
dev_err(&cl->device->dev, "error in allocating Tx pool\n");
|
||||||
ishtp_cl_free_rx_ring(cl);
|
ishtp_cl_free_tx_ring(cl);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user