diff options
Diffstat (limited to 'drivers/hid/hid-logitech-hidpp.c')
-rw-r--r-- | drivers/hid/hid-logitech-hidpp.c | 248 |
1 files changed, 140 insertions, 108 deletions
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 0179f7ed77e5..8e91e2f06cb4 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c | |||
@@ -1669,6 +1669,7 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev, | |||
1669 | 1669 | ||
1670 | #define HIDPP_FF_EFFECTID_NONE -1 | 1670 | #define HIDPP_FF_EFFECTID_NONE -1 |
1671 | #define HIDPP_FF_EFFECTID_AUTOCENTER -2 | 1671 | #define HIDPP_FF_EFFECTID_AUTOCENTER -2 |
1672 | #define HIDPP_AUTOCENTER_PARAMS_LENGTH 18 | ||
1672 | 1673 | ||
1673 | #define HIDPP_FF_MAX_PARAMS 20 | 1674 | #define HIDPP_FF_MAX_PARAMS 20 |
1674 | #define HIDPP_FF_RESERVED_SLOTS 1 | 1675 | #define HIDPP_FF_RESERVED_SLOTS 1 |
@@ -2009,7 +2010,7 @@ static int hidpp_ff_erase_effect(struct input_dev *dev, int effect_id) | |||
2009 | static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude) | 2010 | static void hidpp_ff_set_autocenter(struct input_dev *dev, u16 magnitude) |
2010 | { | 2011 | { |
2011 | struct hidpp_ff_private_data *data = dev->ff->private; | 2012 | struct hidpp_ff_private_data *data = dev->ff->private; |
2012 | u8 params[18]; | 2013 | u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH]; |
2013 | 2014 | ||
2014 | dbg_hid("Setting autocenter to %d.\n", magnitude); | 2015 | dbg_hid("Setting autocenter to %d.\n", magnitude); |
2015 | 2016 | ||
@@ -2077,23 +2078,34 @@ static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp | |||
2077 | static void hidpp_ff_destroy(struct ff_device *ff) | 2078 | static void hidpp_ff_destroy(struct ff_device *ff) |
2078 | { | 2079 | { |
2079 | struct hidpp_ff_private_data *data = ff->private; | 2080 | struct hidpp_ff_private_data *data = ff->private; |
2081 | struct hid_device *hid = data->hidpp->hid_dev; | ||
2080 | 2082 | ||
2083 | hid_info(hid, "Unloading HID++ force feedback.\n"); | ||
2084 | |||
2085 | device_remove_file(&hid->dev, &dev_attr_range); | ||
2086 | destroy_workqueue(data->wq); | ||
2081 | kfree(data->effect_ids); | 2087 | kfree(data->effect_ids); |
2082 | } | 2088 | } |
2083 | 2089 | ||
2084 | static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index) | 2090 | static int hidpp_ff_init(struct hidpp_device *hidpp, |
2091 | struct hidpp_ff_private_data *data) | ||
2085 | { | 2092 | { |
2086 | struct hid_device *hid = hidpp->hid_dev; | 2093 | struct hid_device *hid = hidpp->hid_dev; |
2087 | struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); | 2094 | struct hid_input *hidinput; |
2088 | struct input_dev *dev = hidinput->input; | 2095 | struct input_dev *dev; |
2089 | const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor); | 2096 | const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor); |
2090 | const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice); | 2097 | const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice); |
2091 | struct ff_device *ff; | 2098 | struct ff_device *ff; |
2092 | struct hidpp_report response; | 2099 | int error, j, num_slots = data->num_effects; |
2093 | struct hidpp_ff_private_data *data; | ||
2094 | int error, j, num_slots; | ||
2095 | u8 version; | 2100 | u8 version; |
2096 | 2101 | ||
2102 | if (list_empty(&hid->inputs)) { | ||
2103 | hid_err(hid, "no inputs found\n"); | ||
2104 | return -ENODEV; | ||
2105 | } | ||
2106 | hidinput = list_entry(hid->inputs.next, struct hid_input, list); | ||
2107 | dev = hidinput->input; | ||
2108 | |||
2097 | if (!dev) { | 2109 | if (!dev) { |
2098 | hid_err(hid, "Struct input_dev not set!\n"); | 2110 | hid_err(hid, "Struct input_dev not set!\n"); |
2099 | return -EINVAL; | 2111 | return -EINVAL; |
@@ -2109,27 +2121,17 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index) | |||
2109 | for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++) | 2121 | for (j = 0; hidpp_ff_effects_v2[j] >= 0; j++) |
2110 | set_bit(hidpp_ff_effects_v2[j], dev->ffbit); | 2122 | set_bit(hidpp_ff_effects_v2[j], dev->ffbit); |
2111 | 2123 | ||
2112 | /* Read number of slots available in device */ | ||
2113 | error = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
2114 | HIDPP_FF_GET_INFO, NULL, 0, &response); | ||
2115 | if (error) { | ||
2116 | if (error < 0) | ||
2117 | return error; | ||
2118 | hid_err(hidpp->hid_dev, "%s: received protocol error 0x%02x\n", | ||
2119 | __func__, error); | ||
2120 | return -EPROTO; | ||
2121 | } | ||
2122 | |||
2123 | num_slots = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS; | ||
2124 | |||
2125 | error = input_ff_create(dev, num_slots); | 2124 | error = input_ff_create(dev, num_slots); |
2126 | 2125 | ||
2127 | if (error) { | 2126 | if (error) { |
2128 | hid_err(dev, "Failed to create FF device!\n"); | 2127 | hid_err(dev, "Failed to create FF device!\n"); |
2129 | return error; | 2128 | return error; |
2130 | } | 2129 | } |
2131 | 2130 | /* | |
2132 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 2131 | * Create a copy of passed data, so we can transfer memory |
2132 | * ownership to FF core | ||
2133 | */ | ||
2134 | data = kmemdup(data, sizeof(*data), GFP_KERNEL); | ||
2133 | if (!data) | 2135 | if (!data) |
2134 | return -ENOMEM; | 2136 | return -ENOMEM; |
2135 | data->effect_ids = kcalloc(num_slots, sizeof(int), GFP_KERNEL); | 2137 | 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) | |||
2145 | } | 2147 | } |
2146 | 2148 | ||
2147 | data->hidpp = hidpp; | 2149 | data->hidpp = hidpp; |
2148 | data->feature_index = feature_index; | ||
2149 | data->version = version; | 2150 | data->version = version; |
2150 | data->slot_autocenter = 0; | ||
2151 | data->num_effects = num_slots; | ||
2152 | for (j = 0; j < num_slots; j++) | 2151 | for (j = 0; j < num_slots; j++) |
2153 | data->effect_ids[j] = -1; | 2152 | data->effect_ids[j] = -1; |
2154 | 2153 | ||
@@ -2162,68 +2161,20 @@ static int hidpp_ff_init(struct hidpp_device *hidpp, u8 feature_index) | |||
2162 | ff->set_autocenter = hidpp_ff_set_autocenter; | 2161 | ff->set_autocenter = hidpp_ff_set_autocenter; |
2163 | ff->destroy = hidpp_ff_destroy; | 2162 | ff->destroy = hidpp_ff_destroy; |
2164 | 2163 | ||
2165 | |||
2166 | /* reset all forces */ | ||
2167 | error = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
2168 | HIDPP_FF_RESET_ALL, NULL, 0, &response); | ||
2169 | |||
2170 | /* Read current Range */ | ||
2171 | error = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
2172 | HIDPP_FF_GET_APERTURE, NULL, 0, &response); | ||
2173 | if (error) | ||
2174 | hid_warn(hidpp->hid_dev, "Failed to read range from device!\n"); | ||
2175 | data->range = error ? 900 : get_unaligned_be16(&response.fap.params[0]); | ||
2176 | |||
2177 | /* Create sysfs interface */ | 2164 | /* Create sysfs interface */ |
2178 | error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range); | 2165 | error = device_create_file(&(hidpp->hid_dev->dev), &dev_attr_range); |
2179 | if (error) | 2166 | if (error) |
2180 | hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error); | 2167 | hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error); |
2181 | 2168 | ||
2182 | /* Read the current gain values */ | ||
2183 | error = hidpp_send_fap_command_sync(hidpp, feature_index, | ||
2184 | HIDPP_FF_GET_GLOBAL_GAINS, NULL, 0, &response); | ||
2185 | if (error) | ||
2186 | hid_warn(hidpp->hid_dev, "Failed to read gain values from device!\n"); | ||
2187 | data->gain = error ? 0xffff : get_unaligned_be16(&response.fap.params[0]); | ||
2188 | /* ignore boost value at response.fap.params[2] */ | ||
2189 | |||
2190 | /* init the hardware command queue */ | 2169 | /* init the hardware command queue */ |
2191 | atomic_set(&data->workqueue_size, 0); | 2170 | atomic_set(&data->workqueue_size, 0); |
2192 | 2171 | ||
2193 | /* initialize with zero autocenter to get wheel in usable state */ | ||
2194 | hidpp_ff_set_autocenter(dev, 0); | ||
2195 | |||
2196 | hid_info(hid, "Force feedback support loaded (firmware release %d).\n", | 2172 | hid_info(hid, "Force feedback support loaded (firmware release %d).\n", |
2197 | version); | 2173 | version); |
2198 | 2174 | ||
2199 | return 0; | 2175 | return 0; |
2200 | } | 2176 | } |
2201 | 2177 | ||
2202 | static int hidpp_ff_deinit(struct hid_device *hid) | ||
2203 | { | ||
2204 | struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); | ||
2205 | struct input_dev *dev = hidinput->input; | ||
2206 | struct hidpp_ff_private_data *data; | ||
2207 | |||
2208 | if (!dev) { | ||
2209 | hid_err(hid, "Struct input_dev not found!\n"); | ||
2210 | return -EINVAL; | ||
2211 | } | ||
2212 | |||
2213 | hid_info(hid, "Unloading HID++ force feedback.\n"); | ||
2214 | data = dev->ff->private; | ||
2215 | if (!data) { | ||
2216 | hid_err(hid, "Private data not found!\n"); | ||
2217 | return -EINVAL; | ||
2218 | } | ||
2219 | |||
2220 | destroy_workqueue(data->wq); | ||
2221 | device_remove_file(&hid->dev, &dev_attr_range); | ||
2222 | |||
2223 | return 0; | ||
2224 | } | ||
2225 | |||
2226 | |||
2227 | /* ************************************************************************** */ | 2178 | /* ************************************************************************** */ |
2228 | /* */ | 2179 | /* */ |
2229 | /* Device Support */ | 2180 | /* Device Support */ |
@@ -2725,24 +2676,93 @@ static int k400_connect(struct hid_device *hdev, bool connected) | |||
2725 | 2676 | ||
2726 | #define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123 | 2677 | #define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123 |
2727 | 2678 | ||
2728 | static int g920_get_config(struct hidpp_device *hidpp) | 2679 | static int g920_ff_set_autocenter(struct hidpp_device *hidpp, |
2680 | struct hidpp_ff_private_data *data) | ||
2729 | { | 2681 | { |
2682 | struct hidpp_report response; | ||
2683 | u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH] = { | ||
2684 | [1] = HIDPP_FF_EFFECT_SPRING | HIDPP_FF_EFFECT_AUTOSTART, | ||
2685 | }; | ||
2686 | int ret; | ||
2687 | |||
2688 | /* initialize with zero autocenter to get wheel in usable state */ | ||
2689 | |||
2690 | dbg_hid("Setting autocenter to 0.\n"); | ||
2691 | ret = hidpp_send_fap_command_sync(hidpp, data->feature_index, | ||
2692 | HIDPP_FF_DOWNLOAD_EFFECT, | ||
2693 | params, ARRAY_SIZE(params), | ||
2694 | &response); | ||
2695 | if (ret) | ||
2696 | hid_warn(hidpp->hid_dev, "Failed to autocenter device!\n"); | ||
2697 | else | ||
2698 | data->slot_autocenter = response.fap.params[0]; | ||
2699 | |||
2700 | return ret; | ||
2701 | } | ||
2702 | |||
2703 | static int g920_get_config(struct hidpp_device *hidpp, | ||
2704 | struct hidpp_ff_private_data *data) | ||
2705 | { | ||
2706 | struct hidpp_report response; | ||
2730 | u8 feature_type; | 2707 | u8 feature_type; |
2731 | u8 feature_index; | ||
2732 | int ret; | 2708 | int ret; |
2733 | 2709 | ||
2710 | memset(data, 0, sizeof(*data)); | ||
2711 | |||
2734 | /* Find feature and store for later use */ | 2712 | /* Find feature and store for later use */ |
2735 | ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK, | 2713 | ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK, |
2736 | &feature_index, &feature_type); | 2714 | &data->feature_index, &feature_type); |
2737 | if (ret) | 2715 | if (ret) |
2738 | return ret; | 2716 | return ret; |
2739 | 2717 | ||
2740 | ret = hidpp_ff_init(hidpp, feature_index); | 2718 | /* Read number of slots available in device */ |
2719 | ret = hidpp_send_fap_command_sync(hidpp, data->feature_index, | ||
2720 | HIDPP_FF_GET_INFO, | ||
2721 | NULL, 0, | ||
2722 | &response); | ||
2723 | if (ret) { | ||
2724 | if (ret < 0) | ||
2725 | return ret; | ||
2726 | hid_err(hidpp->hid_dev, | ||
2727 | "%s: received protocol error 0x%02x\n", __func__, ret); | ||
2728 | return -EPROTO; | ||
2729 | } | ||
2730 | |||
2731 | data->num_effects = response.fap.params[0] - HIDPP_FF_RESERVED_SLOTS; | ||
2732 | |||
2733 | /* reset all forces */ | ||
2734 | ret = hidpp_send_fap_command_sync(hidpp, data->feature_index, | ||
2735 | HIDPP_FF_RESET_ALL, | ||
2736 | NULL, 0, | ||
2737 | &response); | ||
2741 | if (ret) | 2738 | if (ret) |
2742 | hid_warn(hidpp->hid_dev, "Unable to initialize force feedback support, errno %d\n", | 2739 | hid_warn(hidpp->hid_dev, "Failed to reset all forces!\n"); |
2743 | ret); | ||
2744 | 2740 | ||
2745 | return 0; | 2741 | ret = hidpp_send_fap_command_sync(hidpp, data->feature_index, |
2742 | HIDPP_FF_GET_APERTURE, | ||
2743 | NULL, 0, | ||
2744 | &response); | ||
2745 | if (ret) { | ||
2746 | hid_warn(hidpp->hid_dev, | ||
2747 | "Failed to read range from device!\n"); | ||
2748 | } | ||
2749 | data->range = ret ? | ||
2750 | 900 : get_unaligned_be16(&response.fap.params[0]); | ||
2751 | |||
2752 | /* Read the current gain values */ | ||
2753 | ret = hidpp_send_fap_command_sync(hidpp, data->feature_index, | ||
2754 | HIDPP_FF_GET_GLOBAL_GAINS, | ||
2755 | NULL, 0, | ||
2756 | &response); | ||
2757 | if (ret) | ||
2758 | hid_warn(hidpp->hid_dev, | ||
2759 | "Failed to read gain values from device!\n"); | ||
2760 | data->gain = ret ? | ||
2761 | 0xffff : get_unaligned_be16(&response.fap.params[0]); | ||
2762 | |||
2763 | /* ignore boost value at response.fap.params[2] */ | ||
2764 | |||
2765 | return g920_ff_set_autocenter(hidpp, data); | ||
2746 | } | 2766 | } |
2747 | 2767 | ||
2748 | /* -------------------------------------------------------------------------- */ | 2768 | /* -------------------------------------------------------------------------- */ |
@@ -3458,34 +3478,45 @@ static int hidpp_get_report_length(struct hid_device *hdev, int id) | |||
3458 | return report->field[0]->report_count + 1; | 3478 | return report->field[0]->report_count + 1; |
3459 | } | 3479 | } |
3460 | 3480 | ||
3461 | static bool hidpp_validate_report(struct hid_device *hdev, int id, | 3481 | static bool hidpp_validate_device(struct hid_device *hdev) |
3462 | int expected_length, bool optional) | ||
3463 | { | 3482 | { |
3464 | int report_length; | 3483 | struct hidpp_device *hidpp = hid_get_drvdata(hdev); |
3484 | int id, report_length, supported_reports = 0; | ||
3465 | 3485 | ||
3466 | if (id >= HID_MAX_IDS || id < 0) { | 3486 | id = REPORT_ID_HIDPP_SHORT; |
3467 | hid_err(hdev, "invalid HID report id %u\n", id); | 3487 | report_length = hidpp_get_report_length(hdev, id); |
3468 | return false; | 3488 | if (report_length) { |
3489 | if (report_length < HIDPP_REPORT_SHORT_LENGTH) | ||
3490 | goto bad_device; | ||
3491 | |||
3492 | supported_reports++; | ||
3469 | } | 3493 | } |
3470 | 3494 | ||
3495 | id = REPORT_ID_HIDPP_LONG; | ||
3471 | report_length = hidpp_get_report_length(hdev, id); | 3496 | report_length = hidpp_get_report_length(hdev, id); |
3472 | if (!report_length) | 3497 | if (report_length) { |
3473 | return optional; | 3498 | if (report_length < HIDPP_REPORT_LONG_LENGTH) |
3499 | goto bad_device; | ||
3474 | 3500 | ||
3475 | if (report_length < expected_length) { | 3501 | supported_reports++; |
3476 | hid_warn(hdev, "not enough values in hidpp report %d\n", id); | ||
3477 | return false; | ||
3478 | } | 3502 | } |
3479 | 3503 | ||
3480 | return true; | 3504 | id = REPORT_ID_HIDPP_VERY_LONG; |
3481 | } | 3505 | report_length = hidpp_get_report_length(hdev, id); |
3506 | if (report_length) { | ||
3507 | if (report_length < HIDPP_REPORT_LONG_LENGTH || | ||
3508 | report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH) | ||
3509 | goto bad_device; | ||
3482 | 3510 | ||
3483 | static bool hidpp_validate_device(struct hid_device *hdev) | 3511 | supported_reports++; |
3484 | { | 3512 | hidpp->very_long_report_length = report_length; |
3485 | return hidpp_validate_report(hdev, REPORT_ID_HIDPP_SHORT, | 3513 | } |
3486 | HIDPP_REPORT_SHORT_LENGTH, false) && | 3514 | |
3487 | hidpp_validate_report(hdev, REPORT_ID_HIDPP_LONG, | 3515 | return supported_reports; |
3488 | HIDPP_REPORT_LONG_LENGTH, true); | 3516 | |
3517 | bad_device: | ||
3518 | hid_warn(hdev, "not enough values in hidpp report %d\n", id); | ||
3519 | return false; | ||
3489 | } | 3520 | } |
3490 | 3521 | ||
3491 | static bool hidpp_application_equals(struct hid_device *hdev, | 3522 | 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) | |||
3505 | int ret; | 3536 | int ret; |
3506 | bool connected; | 3537 | bool connected; |
3507 | unsigned int connect_mask = HID_CONNECT_DEFAULT; | 3538 | unsigned int connect_mask = HID_CONNECT_DEFAULT; |
3539 | struct hidpp_ff_private_data data; | ||
3508 | 3540 | ||
3509 | /* report_fixup needs drvdata to be set before we call hid_parse */ | 3541 | /* report_fixup needs drvdata to be set before we call hid_parse */ |
3510 | hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL); | 3542 | 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) | |||
3531 | return hid_hw_start(hdev, HID_CONNECT_DEFAULT); | 3563 | return hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
3532 | } | 3564 | } |
3533 | 3565 | ||
3534 | hidpp->very_long_report_length = | ||
3535 | hidpp_get_report_length(hdev, REPORT_ID_HIDPP_VERY_LONG); | ||
3536 | if (hidpp->very_long_report_length > HIDPP_REPORT_VERY_LONG_MAX_LENGTH) | ||
3537 | hidpp->very_long_report_length = HIDPP_REPORT_VERY_LONG_MAX_LENGTH; | ||
3538 | |||
3539 | if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE) | 3566 | if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE) |
3540 | hidpp->quirks |= HIDPP_QUIRK_UNIFYING; | 3567 | hidpp->quirks |= HIDPP_QUIRK_UNIFYING; |
3541 | 3568 | ||
@@ -3614,7 +3641,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
3614 | if (ret) | 3641 | if (ret) |
3615 | goto hid_hw_init_fail; | 3642 | goto hid_hw_init_fail; |
3616 | } else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) { | 3643 | } else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) { |
3617 | ret = g920_get_config(hidpp); | 3644 | ret = g920_get_config(hidpp, &data); |
3618 | if (ret) | 3645 | if (ret) |
3619 | goto hid_hw_init_fail; | 3646 | goto hid_hw_init_fail; |
3620 | } | 3647 | } |
@@ -3636,6 +3663,14 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) | |||
3636 | goto hid_hw_start_fail; | 3663 | goto hid_hw_start_fail; |
3637 | } | 3664 | } |
3638 | 3665 | ||
3666 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { | ||
3667 | ret = hidpp_ff_init(hidpp, &data); | ||
3668 | if (ret) | ||
3669 | hid_warn(hidpp->hid_dev, | ||
3670 | "Unable to initialize force feedback support, errno %d\n", | ||
3671 | ret); | ||
3672 | } | ||
3673 | |||
3639 | return ret; | 3674 | return ret; |
3640 | 3675 | ||
3641 | hid_hw_init_fail: | 3676 | hid_hw_init_fail: |
@@ -3658,9 +3693,6 @@ static void hidpp_remove(struct hid_device *hdev) | |||
3658 | 3693 | ||
3659 | sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group); | 3694 | sysfs_remove_group(&hdev->dev.kobj, &ps_attribute_group); |
3660 | 3695 | ||
3661 | if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) | ||
3662 | hidpp_ff_deinit(hdev); | ||
3663 | |||
3664 | hid_hw_stop(hdev); | 3696 | hid_hw_stop(hdev); |
3665 | cancel_work_sync(&hidpp->work); | 3697 | cancel_work_sync(&hidpp->work); |
3666 | mutex_destroy(&hidpp->send_mutex); | 3698 | mutex_destroy(&hidpp->send_mutex); |