Merge branch 'for-6.15/pidff' into for-linus

From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>

This patch series is focused on improving the compatibility and usability of the
hid-pidff force feedback driver. Last patch introduces a new, universal driver
for PID devices that need some special handling like report fixups, remapping the
button range, managing new pidff quirks and setting desirable fuzz/flat values.

This work has been done in the span of the past months with the help of the great
Linux simracing community, with a little input from sim flight fans from FFBeast.

No changes interfere with compliant and currently working PID devices.
"Generic" codepath was tested as well with Moza and Simxperience AccuForce v2.

I'm not married to the name. It's what we used previously, but if "universal" is
confusing (pidff is already the generic driver), we can come up with something
better like "hid-quirky-pidff" :)

With v8 and  tiny finx in v9, all the outstanding issues were resolved,
additional pidff issues were fixed and hid-pidff defines moved to a dedicated
header file. This patch series could be considered done bar any comments and
requests from input maintainers.

I could save more then a dozen lines of code by changing simple if statements
to only occupy on line instead of two in there's a need for that.
This commit is contained in:
Jiri Kosina 2025-03-26 13:54:04 +01:00
commit 765b8aa0f7
9 changed files with 669 additions and 204 deletions

View File

@ -10316,6 +10316,14 @@ F: drivers/hid/hid-sensor-*
F: drivers/iio/*/hid-*
F: include/linux/hid-sensor-*
HID UNIVERSAL PIDFF DRIVER
M: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
M: Oleg Makarenko <oleg@makarenk.ooo>
L: linux-input@vger.kernel.org
S: Maintained
B: https://github.com/JacKeTUs/universal-pidff/issues
F: drivers/hid/hid-universal-pidff.c
HID VRC-2 CAR CONTROLLER DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
L: linux-input@vger.kernel.org

View File

@ -1246,6 +1246,20 @@ config HID_U2FZERO
allow setting the brightness to anything but 1, which will
trigger a single blink and immediately reset back to 0.
config HID_UNIVERSAL_PIDFF
tristate "universal-pidff: extended USB PID driver compatibility and usage"
depends on USB_HID
depends on HID_PID
help
Extended PID support for selected devices.
Contains report fixups, extended usable button range and
pidff quirk management to extend compatibility with slightly
non-compliant USB PID devices and better fuzz/flat values for
high precision direct drive devices.
Supports Moza Racing, Cammus, VRS, FFBeast and more.
config HID_WACOM
tristate "Wacom Intuos/Graphire tablet support (USB)"
depends on USB_HID

View File

@ -142,6 +142,7 @@ hid-uclogic-objs := hid-uclogic-core.o \
hid-uclogic-params.o
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
obj-$(CONFIG_HID_UNIVERSAL_PIDFF) += hid-universal-pidff.o
obj-$(CONFIG_HID_LED) += hid-led.o
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o

View File

@ -190,6 +190,12 @@
#define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102
#define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302
#define USB_VENDOR_ID_ASETEK 0x2433
#define USB_DEVICE_ID_ASETEK_INVICTA 0xf300
#define USB_DEVICE_ID_ASETEK_FORTE 0xf301
#define USB_DEVICE_ID_ASETEK_LA_PRIMA 0xf303
#define USB_DEVICE_ID_ASETEK_TONY_KANAAN 0xf306
#define USB_VENDOR_ID_ASUS 0x0486
#define USB_DEVICE_ID_ASUS_T91MT 0x0185
#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186
@ -262,6 +268,10 @@
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
#define USB_VENDOR_ID_CAMMUS 0x3416
#define USB_DEVICE_ID_CAMMUS_C5 0x0301
#define USB_DEVICE_ID_CAMMUS_C12 0x0302
#define USB_VENDOR_ID_CANDO 0x2087
#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
@ -453,6 +463,11 @@
#define USB_VENDOR_ID_EVISION 0x320f
#define USB_DEVICE_ID_EVISION_ICL01 0x5041
#define USB_VENDOR_ID_FFBEAST 0x045b
#define USB_DEVICE_ID_FFBEAST_JOYSTICK 0x58f9
#define USB_DEVICE_ID_FFBEAST_RUDDER 0x5968
#define USB_DEVICE_ID_FFBEAST_WHEEL 0x59d7
#define USB_VENDOR_ID_FLATFROG 0x25b5
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
@ -817,6 +832,13 @@
#define I2C_DEVICE_ID_LG_8001 0x8001
#define I2C_DEVICE_ID_LG_7010 0x7010
#define USB_VENDOR_ID_LITE_STAR 0x11ff
#define USB_DEVICE_ID_PXN_V10 0x3245
#define USB_DEVICE_ID_PXN_V12 0x1212
#define USB_DEVICE_ID_PXN_V12_LITE 0x1112
#define USB_DEVICE_ID_PXN_V12_LITE_2 0x1211
#define USB_DEVICE_LITE_STAR_GT987_FF 0x2141
#define USB_VENDOR_ID_LOGITECH 0x046d
#define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
@ -964,6 +986,18 @@
#define USB_VENDOR_ID_MONTEREY 0x0566
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
#define USB_VENDOR_ID_MOZA 0x346e
#define USB_DEVICE_ID_MOZA_R3 0x0005
#define USB_DEVICE_ID_MOZA_R3_2 0x0015
#define USB_DEVICE_ID_MOZA_R5 0x0004
#define USB_DEVICE_ID_MOZA_R5_2 0x0014
#define USB_DEVICE_ID_MOZA_R9 0x0002
#define USB_DEVICE_ID_MOZA_R9_2 0x0012
#define USB_DEVICE_ID_MOZA_R12 0x0006
#define USB_DEVICE_ID_MOZA_R12_2 0x0016
#define USB_DEVICE_ID_MOZA_R16_R21 0x0000
#define USB_DEVICE_ID_MOZA_R16_R21_2 0x0010
#define USB_VENDOR_ID_MSI 0x1770
#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
@ -1377,6 +1411,9 @@
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
#define USB_VENDOR_ID_VRS 0x0483
#define USB_DEVICE_ID_VRS_DFP 0xa355
#define USB_VENDOR_ID_VTL 0x0306
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HID UNIVERSAL PIDFF
* hid-pidff wrapper for PID-enabled devices
* Handles device reports, quirks and extends usable button range
*
* Copyright (c) 2024, 2025 Oleg Makarenko
* Copyright (c) 2024, 2025 Tomasz Pakuła
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include <linux/input-event-codes.h>
#include "hid-ids.h"
#include "usbhid/hid-pidff.h"
#define JOY_RANGE (BTN_DEAD - BTN_JOYSTICK + 1)
/*
* Map buttons manually to extend the default joystick button limit
*/
static int universal_pidff_input_mapping(struct hid_device *hdev,
struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
return 0;
if (field->application != HID_GD_JOYSTICK)
return 0;
int button = ((usage->hid - 1) & HID_USAGE);
int code = button + BTN_JOYSTICK;
/* Detect the end of JOYSTICK buttons range */
if (code > BTN_DEAD)
code = button + KEY_NEXT_FAVORITE - JOY_RANGE;
/*
* Map overflowing buttons to KEY_RESERVED to not ignore
* them and let them still trigger MSC_SCAN
*/
if (code > KEY_MAX)
code = KEY_RESERVED;
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
hid_dbg(hdev, "Button %d: usage %d", button, code);
return 1;
}
/*
* Check if the device is PID and initialize it
* Add quirks after initialisation
*/
static int universal_pidff_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int i, error;
error = hid_parse(hdev);
if (error) {
hid_err(hdev, "HID parse failed\n");
goto err;
}
error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (error) {
hid_err(hdev, "HID hw start failed\n");
goto err;
}
/* Check if device contains PID usage page */
error = 1;
for (i = 0; i < hdev->collection_size; i++)
if ((hdev->collection[i].usage & HID_USAGE_PAGE) == HID_UP_PID) {
error = 0;
hid_dbg(hdev, "PID usage page found\n");
break;
}
/*
* Do not fail as this might be the second "device"
* just for additional buttons/axes. Exit cleanly if force
* feedback usage page wasn't found (included devices were
* tested and confirmed to be USB PID after all).
*/
if (error) {
hid_dbg(hdev, "PID usage page not found in the descriptor\n");
return 0;
}
/* Check if HID_PID support is enabled */
int (*init_function)(struct hid_device *, u32);
init_function = hid_pidff_init_with_quirks;
if (!init_function) {
hid_warn(hdev, "HID_PID support not enabled!\n");
return 0;
}
error = init_function(hdev, id->driver_data);
if (error) {
hid_warn(hdev, "Error initialising force feedback\n");
goto err;
}
hid_info(hdev, "Universal pidff driver loaded successfully!");
return 0;
err:
return error;
}
static int universal_pidff_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
int axis;
struct input_dev *input = hidinput->input;
if (!input->absinfo)
return 0;
/* Decrease fuzz and deadzone on available axes */
for (axis = ABS_X; axis <= ABS_BRAKE; axis++) {
if (!test_bit(axis, input->absbit))
continue;
input_set_abs_params(input, axis,
input->absinfo[axis].minimum,
input->absinfo[axis].maximum,
axis == ABS_X ? 0 : 8, 0);
}
/* Remove fuzz and deadzone from the second joystick axis */
if (hdev->vendor == USB_VENDOR_ID_FFBEAST &&
hdev->product == USB_DEVICE_ID_FFBEAST_JOYSTICK)
input_set_abs_params(input, ABS_Y,
input->absinfo[ABS_Y].minimum,
input->absinfo[ABS_Y].maximum, 0, 0);
return 0;
}
static const struct hid_device_id universal_pidff_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3_2),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5_2),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9_2),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12_2),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21_2),
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
{ HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C12) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VRS, USB_DEVICE_ID_VRS_DFP),
.driver_data = HID_PIDFF_QUIRK_PERMISSIVE_CONTROL },
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_JOYSTICK), },
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_RUDDER), },
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V10),
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12),
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE),
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE_2),
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_LITE_STAR_GT987_FF),
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_INVICTA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_FORTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_LA_PRIMA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_TONY_KANAAN) },
{ }
};
MODULE_DEVICE_TABLE(hid, universal_pidff_devices);
static struct hid_driver universal_pidff = {
.name = "hid-universal-pidff",
.id_table = universal_pidff_devices,
.input_mapping = universal_pidff_input_mapping,
.probe = universal_pidff_probe,
.input_configured = universal_pidff_input_configured
};
module_hid_driver(universal_pidff);
MODULE_DESCRIPTION("Universal driver for USB PID Force Feedback devices");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Oleg Makarenko <oleg@makarenk.ooo>");
MODULE_AUTHOR("Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>");

View File

@ -35,6 +35,7 @@
#include <linux/hid-debug.h>
#include <linux/hidraw.h>
#include "usbhid.h"
#include "hid-pidff.h"
/*
* Version Information

View File

@ -3,27 +3,27 @@
* Force feedback driver for USB HID PID compliant devices
*
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
* Upgraded 2025 by Oleg Makarenko and Tomasz Pakuła
*/
/*
*/
/* #define DEBUG */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "hid-pidff.h"
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include <linux/hid.h>
#include <linux/minmax.h>
#include "usbhid.h"
#define PID_EFFECTS_MAX 64
#define PID_INFINITE U16_MAX
/* Linux Force Feedback API uses miliseconds as time unit */
#define FF_TIME_EXPONENT -3
#define FF_INFINITE 0
/* Report usage table used to put reports into an array */
#define PID_SET_EFFECT 0
#define PID_EFFECT_OPERATION 1
#define PID_DEVICE_GAIN 2
@ -44,12 +44,19 @@ static const u8 pidff_reports[] = {
0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab,
0x5a, 0x5f, 0x6e, 0x73, 0x74
};
/*
* device_control is really 0x95, but 0x96 specified
* as it is the usage of the only field in that report.
*/
/* device_control is really 0x95, but 0x96 specified as it is the usage of
the only field in that report */
/* PID special fields */
#define PID_EFFECT_TYPE 0x25
#define PID_DIRECTION 0x57
#define PID_EFFECT_OPERATION_ARRAY 0x78
#define PID_BLOCK_LOAD_STATUS 0x8b
#define PID_DEVICE_CONTROL_ARRAY 0x96
/* Value usage tables used to put fields and values into arrays */
#define PID_EFFECT_BLOCK_INDEX 0
#define PID_DURATION 1
@ -107,10 +114,13 @@ static const u8 pidff_device_gain[] = { 0x7e };
static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
/* Special field key tables used to put special field keys into arrays */
#define PID_ENABLE_ACTUATORS 0
#define PID_RESET 1
static const u8 pidff_device_control[] = { 0x97, 0x9a };
#define PID_DISABLE_ACTUATORS 1
#define PID_STOP_ALL_EFFECTS 2
#define PID_RESET 3
#define PID_PAUSE 4
#define PID_CONTINUE 5
static const u8 pidff_device_control[] = { 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c };
#define PID_CONSTANT 0
#define PID_RAMP 1
@ -130,12 +140,16 @@ static const u8 pidff_effect_types[] = {
#define PID_BLOCK_LOAD_SUCCESS 0
#define PID_BLOCK_LOAD_FULL 1
static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };
#define PID_BLOCK_LOAD_ERROR 2
static const u8 pidff_block_load_status[] = { 0x8c, 0x8d, 0x8e};
#define PID_EFFECT_START 0
#define PID_EFFECT_STOP 1
static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };
/* Polar direction 90 degrees (East) */
#define PIDFF_FIXED_WHEEL_DIRECTION 0x4000
struct pidff_usage {
struct hid_field *field;
s32 *value;
@ -159,8 +173,10 @@ struct pidff_device {
struct pidff_usage effect_operation[sizeof(pidff_effect_operation)];
struct pidff_usage block_free[sizeof(pidff_block_free)];
/* Special field is a field that is not composed of
usage<->value pairs that pidff_usage values are */
/*
* Special field is a field that is not composed of
* usage<->value pairs that pidff_usage values are
*/
/* Special field in create_new_effect */
struct hid_field *create_new_effect_type;
@ -184,30 +200,61 @@ struct pidff_device {
int operation_id[sizeof(pidff_effect_operation_status)];
int pid_id[PID_EFFECTS_MAX];
u32 quirks;
u8 effect_count;
};
/*
* Clamp value for a given field
*/
static s32 pidff_clamp(s32 i, struct hid_field *field)
{
s32 clamped = clamp(i, field->logical_minimum, field->logical_maximum);
pr_debug("clamped from %d to %d", i, clamped);
return clamped;
}
/*
* Scale an unsigned value with range 0..max for the given field
*/
static int pidff_rescale(int i, int max, struct hid_field *field)
{
return i * (field->logical_maximum - field->logical_minimum) / max +
field->logical_minimum;
field->logical_minimum;
}
/*
* Scale a signed value in range -0x8000..0x7fff for the given field
* Scale a signed value in range S16_MIN..S16_MAX for the given field
*/
static int pidff_rescale_signed(int i, struct hid_field *field)
{
return i == 0 ? 0 : i >
0 ? i * field->logical_maximum / 0x7fff : i *
field->logical_minimum / -0x8000;
if (i > 0) return i * field->logical_maximum / S16_MAX;
if (i < 0) return i * field->logical_minimum / S16_MIN;
return 0;
}
/*
* Scale time value from Linux default (ms) to field units
*/
static u32 pidff_rescale_time(u16 time, struct hid_field *field)
{
u32 scaled_time = time;
int exponent = field->unit_exponent;
pr_debug("time field exponent: %d\n", exponent);
for (;exponent < FF_TIME_EXPONENT; exponent++)
scaled_time *= 10;
for (;exponent > FF_TIME_EXPONENT; exponent--)
scaled_time /= 10;
pr_debug("time calculated from %d to %d\n", time, scaled_time);
return scaled_time;
}
static void pidff_set(struct pidff_usage *usage, u16 value)
{
usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
usage->value[0] = pidff_rescale(value, U16_MAX, usage->field);
pr_debug("calculated from %d to %d\n", value, usage->value[0]);
}
@ -218,14 +265,35 @@ static void pidff_set_signed(struct pidff_usage *usage, s16 value)
else {
if (value < 0)
usage->value[0] =
pidff_rescale(-value, 0x8000, usage->field);
pidff_rescale(-value, -S16_MIN, usage->field);
else
usage->value[0] =
pidff_rescale(value, 0x7fff, usage->field);
pidff_rescale(value, S16_MAX, usage->field);
}
pr_debug("calculated from %d to %d\n", value, usage->value[0]);
}
static void pidff_set_time(struct pidff_usage *usage, u16 time)
{
u32 modified_time = pidff_rescale_time(time, usage->field);
usage->value[0] = pidff_clamp(modified_time, usage->field);
}
static void pidff_set_duration(struct pidff_usage *usage, u16 duration)
{
/* Infinite value conversion from Linux API -> PID */
if (duration == FF_INFINITE)
duration = PID_INFINITE;
/* PID defines INFINITE as the max possible value for duration field */
if (duration == PID_INFINITE) {
usage->value[0] = (1U << usage->field->report_size) - 1;
return;
}
pidff_set_time(usage, duration);
}
/*
* Send envelope report to the device
*/
@ -233,19 +301,21 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
struct ff_envelope *envelope)
{
pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] =
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
pidff_rescale(envelope->attack_level >
0x7fff ? 0x7fff : envelope->attack_level, 0x7fff,
pidff->set_envelope[PID_ATTACK_LEVEL].field);
pidff_rescale(envelope->attack_level >
S16_MAX ? S16_MAX : envelope->attack_level, S16_MAX,
pidff->set_envelope[PID_ATTACK_LEVEL].field);
pidff->set_envelope[PID_FADE_LEVEL].value[0] =
pidff_rescale(envelope->fade_level >
0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
pidff->set_envelope[PID_FADE_LEVEL].field);
pidff_rescale(envelope->fade_level >
S16_MAX ? S16_MAX : envelope->fade_level, S16_MAX,
pidff->set_envelope[PID_FADE_LEVEL].field);
pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
pidff_set_time(&pidff->set_envelope[PID_ATTACK_TIME],
envelope->attack_length);
pidff_set_time(&pidff->set_envelope[PID_FADE_TIME],
envelope->fade_length);
hid_dbg(pidff->hid, "attack %u => %d\n",
envelope->attack_level,
@ -261,10 +331,22 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
static int pidff_needs_set_envelope(struct ff_envelope *envelope,
struct ff_envelope *old)
{
return envelope->attack_level != old->attack_level ||
envelope->fade_level != old->fade_level ||
bool needs_new_envelope;
needs_new_envelope = envelope->attack_level != 0 ||
envelope->fade_level != 0 ||
envelope->attack_length != 0 ||
envelope->fade_length != 0;
if (!needs_new_envelope)
return false;
if (!old)
return needs_new_envelope;
return envelope->attack_level != old->attack_level ||
envelope->fade_level != old->fade_level ||
envelope->attack_length != old->attack_length ||
envelope->fade_length != old->fade_length;
envelope->fade_length != old->fade_length;
}
/*
@ -301,17 +383,27 @@ static void pidff_set_effect_report(struct pidff_device *pidff,
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
pidff->set_effect_type->value[0] =
pidff->create_new_effect_type->value[0];
pidff->set_effect[PID_DURATION].value[0] = effect->replay.length;
pidff_set_duration(&pidff->set_effect[PID_DURATION],
effect->replay.length);
pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button;
pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
effect->trigger.interval;
pidff_set_time(&pidff->set_effect[PID_TRIGGER_REPEAT_INT],
effect->trigger.interval);
pidff->set_effect[PID_GAIN].value[0] =
pidff->set_effect[PID_GAIN].field->logical_maximum;
pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
pidff->effect_direction->value[0] =
pidff_rescale(effect->direction, 0xffff,
pidff->effect_direction);
pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
/* Use fixed direction if needed */
pidff->effect_direction->value[0] = pidff_rescale(
pidff->quirks & HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION ?
PIDFF_FIXED_WHEEL_DIRECTION : effect->direction,
U16_MAX, pidff->effect_direction);
/* Omit setting delay field if it's missing */
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DELAY))
pidff_set_time(&pidff->set_effect[PID_START_DELAY],
effect->replay.delay);
hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT);
@ -343,11 +435,11 @@ static void pidff_set_periodic_report(struct pidff_device *pidff,
pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
effect->u.periodic.offset);
pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period;
pidff_set_time(&pidff->set_periodic[PID_PERIOD],
effect->u.periodic.period);
hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC],
HID_REQ_SET_REPORT);
}
/*
@ -368,13 +460,19 @@ static int pidff_needs_set_periodic(struct ff_effect *effect,
static void pidff_set_condition_report(struct pidff_device *pidff,
struct ff_effect *effect)
{
int i;
int i, max_axis;
/* Devices missing Parameter Block Offset can only have one axis */
max_axis = pidff->quirks & HID_PIDFF_QUIRK_MISSING_PBO ? 1 : 2;
pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] =
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
for (i = 0; i < 2; i++) {
pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i;
for (i = 0; i < max_axis; i++) {
/* Omit Parameter Block Offset if missing */
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_PBO))
pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i;
pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET],
effect->u.condition[i].center);
pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT],
@ -441,9 +539,104 @@ static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
effect->u.ramp.end_level != old->u.ramp.end_level;
}
/*
* Set device gain
*/
static void pidff_set_gain_report(struct pidff_device *pidff, u16 gain)
{
if (!pidff->device_gain[PID_DEVICE_GAIN_FIELD].field)
return;
pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
HID_REQ_SET_REPORT);
}
/*
* Send device control report to the device
*/
static void pidff_set_device_control(struct pidff_device *pidff, int field)
{
int i, index;
int field_index = pidff->control_id[field];
if (field_index < 1)
return;
/* Detect if the field is a bitmask variable or an array */
if (pidff->device_control->flags & HID_MAIN_ITEM_VARIABLE) {
hid_dbg(pidff->hid, "DEVICE_CONTROL is a bitmask\n");
/* Clear current bitmask */
for(i = 0; i < sizeof(pidff_device_control); i++) {
index = pidff->control_id[i];
if (index < 1)
continue;
pidff->device_control->value[index - 1] = 0;
}
pidff->device_control->value[field_index - 1] = 1;
} else {
hid_dbg(pidff->hid, "DEVICE_CONTROL is an array\n");
pidff->device_control->value[0] = field_index;
}
hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(pidff->hid);
}
/*
* Modify actuators state
*/
static void pidff_set_actuators(struct pidff_device *pidff, bool enable)
{
hid_dbg(pidff->hid, "%s actuators\n", enable ? "Enable" : "Disable");
pidff_set_device_control(pidff,
enable ? PID_ENABLE_ACTUATORS : PID_DISABLE_ACTUATORS);
}
/*
* Reset the device, stop all effects, enable actuators
*/
static void pidff_reset(struct pidff_device *pidff)
{
/* We reset twice as sometimes hid_wait_io isn't waiting long enough */
pidff_set_device_control(pidff, PID_RESET);
pidff_set_device_control(pidff, PID_RESET);
pidff->effect_count = 0;
pidff_set_device_control(pidff, PID_STOP_ALL_EFFECTS);
pidff_set_actuators(pidff, 1);
}
/*
* Fetch pool report
*/
static void pidff_fetch_pool(struct pidff_device *pidff)
{
int i;
struct hid_device *hid = pidff->hid;
/* Repeat if PID_SIMULTANEOUS_MAX < 2 to make sure it's correct */
for(i = 0; i < 20; i++) {
hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
hid_hw_wait(hid);
if (!pidff->pool[PID_SIMULTANEOUS_MAX].value)
return;
if (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] >= 2)
return;
}
hid_warn(hid, "device reports %d simultaneous effects\n",
pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
}
/*
* Send a request for effect upload to the device
*
* Reset and enable actuators if no effects were present on the device
*
* Returns 0 if device reported success, -ENOSPC if the device reported memory
* is full. Upon unknown response the function will retry for 60 times, if
* still unsuccessful -EIO is returned.
@ -452,6 +645,9 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
{
int j;
if (!pidff->effect_count)
pidff_reset(pidff);
pidff->create_new_effect_type->value[0] = efnum;
hid_hw_request(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT],
HID_REQ_SET_REPORT);
@ -471,6 +667,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
hid_dbg(pidff->hid, "device reported free memory: %d bytes\n",
pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
pidff->effect_count++;
return 0;
}
if (pidff->block_load_status->value[0] ==
@ -480,6 +678,11 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
return -ENOSPC;
}
if (pidff->block_load_status->value[0] ==
pidff->status_id[PID_BLOCK_LOAD_ERROR]) {
hid_dbg(pidff->hid, "device error during effect creation\n");
return -EREMOTEIO;
}
}
hid_err(pidff->hid, "pid_block_load failed 60 times\n");
return -EIO;
@ -498,7 +701,8 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
} else {
pidff->effect_operation_status->value[0] =
pidff->operation_id[PID_EFFECT_START];
pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
pidff->effect_operation[PID_LOOP_COUNT].value[0] =
pidff_clamp(n, pidff->effect_operation[PID_LOOP_COUNT].field);
}
hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
@ -511,20 +715,22 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
static int pidff_playback(struct input_dev *dev, int effect_id, int value)
{
struct pidff_device *pidff = dev->ff->private;
pidff_playback_pid(pidff, pidff->pid_id[effect_id], value);
return 0;
}
/*
* Erase effect with PID id
* Decrease the device effect counter
*/
static void pidff_erase_pid(struct pidff_device *pidff, int pid_id)
{
pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
hid_hw_request(pidff->hid, pidff->reports[PID_BLOCK_FREE],
HID_REQ_SET_REPORT);
if (pidff->effect_count > 0)
pidff->effect_count--;
}
/*
@ -537,8 +743,11 @@ static int pidff_erase_effect(struct input_dev *dev, int effect_id)
hid_dbg(pidff->hid, "starting to erase %d/%d\n",
effect_id, pidff->pid_id[effect_id]);
/* Wait for the queue to clear. We do not want a full fifo to
prevent the effect removal. */
/*
* Wait for the queue to clear. We do not want
* a full fifo to prevent the effect removal.
*/
hid_hw_wait(pidff->hid);
pidff_playback_pid(pidff, pid_id, 0);
pidff_erase_pid(pidff, pid_id);
@ -574,11 +783,9 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_constant(effect, old))
pidff_set_constant_force_report(pidff, effect);
if (!old ||
pidff_needs_set_envelope(&effect->u.constant.envelope,
&old->u.constant.envelope))
pidff_set_envelope_report(pidff,
&effect->u.constant.envelope);
if (pidff_needs_set_envelope(&effect->u.constant.envelope,
old ? &old->u.constant.envelope : NULL))
pidff_set_envelope_report(pidff, &effect->u.constant.envelope);
break;
case FF_PERIODIC:
@ -604,6 +811,9 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
return -EINVAL;
}
if (pidff->quirks & HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY)
type_id = PID_SINE;
error = pidff_request_effect_upload(pidff,
pidff->type_id[type_id]);
if (error)
@ -613,11 +823,9 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_periodic(effect, old))
pidff_set_periodic_report(pidff, effect);
if (!old ||
pidff_needs_set_envelope(&effect->u.periodic.envelope,
&old->u.periodic.envelope))
pidff_set_envelope_report(pidff,
&effect->u.periodic.envelope);
if (pidff_needs_set_envelope(&effect->u.periodic.envelope,
old ? &old->u.periodic.envelope : NULL))
pidff_set_envelope_report(pidff, &effect->u.periodic.envelope);
break;
case FF_RAMP:
@ -631,56 +839,32 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_ramp(effect, old))
pidff_set_ramp_force_report(pidff, effect);
if (!old ||
pidff_needs_set_envelope(&effect->u.ramp.envelope,
&old->u.ramp.envelope))
pidff_set_envelope_report(pidff,
&effect->u.ramp.envelope);
if (pidff_needs_set_envelope(&effect->u.ramp.envelope,
old ? &old->u.ramp.envelope : NULL))
pidff_set_envelope_report(pidff, &effect->u.ramp.envelope);
break;
case FF_SPRING:
if (!old) {
error = pidff_request_effect_upload(pidff,
pidff->type_id[PID_SPRING]);
if (error)
return error;
}
if (!old || pidff_needs_set_effect(effect, old))
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_condition(effect, old))
pidff_set_condition_report(pidff, effect);
break;
case FF_DAMPER:
case FF_INERTIA:
case FF_FRICTION:
if (!old) {
switch(effect->type) {
case FF_SPRING:
type_id = PID_SPRING;
break;
case FF_DAMPER:
type_id = PID_DAMPER;
break;
case FF_INERTIA:
type_id = PID_INERTIA;
break;
case FF_FRICTION:
type_id = PID_FRICTION;
break;
}
error = pidff_request_effect_upload(pidff,
pidff->type_id[PID_FRICTION]);
if (error)
return error;
}
if (!old || pidff_needs_set_effect(effect, old))
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_condition(effect, old))
pidff_set_condition_report(pidff, effect);
break;
case FF_DAMPER:
if (!old) {
error = pidff_request_effect_upload(pidff,
pidff->type_id[PID_DAMPER]);
if (error)
return error;
}
if (!old || pidff_needs_set_effect(effect, old))
pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_condition(effect, old))
pidff_set_condition_report(pidff, effect);
break;
case FF_INERTIA:
if (!old) {
error = pidff_request_effect_upload(pidff,
pidff->type_id[PID_INERTIA]);
pidff->type_id[type_id]);
if (error)
return error;
}
@ -709,11 +893,7 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
*/
static void pidff_set_gain(struct input_dev *dev, u16 gain)
{
struct pidff_device *pidff = dev->ff->private;
pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
hid_hw_request(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
HID_REQ_SET_REPORT);
pidff_set_gain_report(dev->ff->private, gain);
}
static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
@ -736,7 +916,10 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
pidff->set_effect[PID_START_DELAY].value[0] = 0;
/* Omit setting delay field if it's missing */
if (!(pidff->quirks & HID_PIDFF_QUIRK_MISSING_DELAY))
pidff->set_effect[PID_START_DELAY].value[0] = 0;
hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT);
@ -747,9 +930,7 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
*/
static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
{
struct pidff_device *pidff = dev->ff->private;
pidff_autocenter(pidff, magnitude);
pidff_autocenter(dev->ff->private, magnitude);
}
/*
@ -758,7 +939,13 @@ static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
struct hid_report *report, int count, int strict)
{
if (!report) {
pr_debug("pidff_find_fields, null report\n");
return -1;
}
int i, j, k, found;
int return_value = 0;
for (k = 0; k < count; k++) {
found = 0;
@ -783,12 +970,22 @@ static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
if (found)
break;
}
if (!found && strict) {
if (!found && table[k] == pidff_set_effect[PID_START_DELAY]) {
pr_debug("Delay field not found, but that's OK\n");
pr_debug("Setting MISSING_DELAY quirk\n");
return_value |= HID_PIDFF_QUIRK_MISSING_DELAY;
}
else if (!found && table[k] == pidff_set_condition[PID_PARAM_BLOCK_OFFSET]) {
pr_debug("PBO field not found, but that's OK\n");
pr_debug("Setting MISSING_PBO quirk\n");
return_value |= HID_PIDFF_QUIRK_MISSING_PBO;
}
else if (!found && strict) {
pr_debug("failed to locate %d\n", k);
return -1;
}
}
return 0;
return return_value;
}
/*
@ -871,6 +1068,11 @@ static int pidff_reports_ok(struct pidff_device *pidff)
static struct hid_field *pidff_find_special_field(struct hid_report *report,
int usage, int enforce_min)
{
if (!report) {
pr_debug("pidff_find_special_field, null report\n");
return NULL;
}
int i;
for (i = 0; i < report->maxfield; i++) {
@ -923,22 +1125,24 @@ static int pidff_find_special_fields(struct pidff_device *pidff)
pidff->create_new_effect_type =
pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT],
0x25, 1);
PID_EFFECT_TYPE, 1);
pidff->set_effect_type =
pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
0x25, 1);
PID_EFFECT_TYPE, 1);
pidff->effect_direction =
pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
0x57, 0);
PID_DIRECTION, 0);
pidff->device_control =
pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL],
0x96, 1);
PID_DEVICE_CONTROL_ARRAY,
!(pidff->quirks & HID_PIDFF_QUIRK_PERMISSIVE_CONTROL));
pidff->block_load_status =
pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD],
0x8b, 1);
PID_BLOCK_LOAD_STATUS, 1);
pidff->effect_operation_status =
pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION],
0x78, 1);
PID_EFFECT_OPERATION_ARRAY, 1);
hid_dbg(pidff->hid, "search done\n");
@ -967,10 +1171,6 @@ static int pidff_find_special_fields(struct pidff_device *pidff)
return -1;
}
pidff_find_special_keys(pidff->control_id, pidff->device_control,
pidff_device_control,
sizeof(pidff_device_control));
PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control);
if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
@ -1049,7 +1249,6 @@ static int pidff_find_effects(struct pidff_device *pidff,
set_bit(FF_FRICTION, dev->ffbit);
return 0;
}
#define PIDFF_FIND_FIELDS(name, report, strict) \
@ -1062,12 +1261,19 @@ static int pidff_find_effects(struct pidff_device *pidff,
*/
static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
{
int envelope_ok = 0;
int status = 0;
if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) {
/* Save info about the device not having the DELAY ffb field. */
status = PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1);
if (status == -1) {
hid_err(pidff->hid, "unknown set_effect report layout\n");
return -ENODEV;
}
pidff->quirks |= status;
if (status & HID_PIDFF_QUIRK_MISSING_DELAY)
hid_dbg(pidff->hid, "Adding MISSING_DELAY quirk\n");
PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0);
if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) {
@ -1085,13 +1291,10 @@ static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
return -ENODEV;
}
if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1))
envelope_ok = 1;
if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev))
return -ENODEV;
if (!envelope_ok) {
if (PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1)) {
if (test_and_clear_bit(FF_CONSTANT, dev->ffbit))
hid_warn(pidff->hid,
"has constant effect but no envelope\n");
@ -1116,16 +1319,20 @@ static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
clear_bit(FF_RAMP, dev->ffbit);
}
if ((test_bit(FF_SPRING, dev->ffbit) ||
test_bit(FF_DAMPER, dev->ffbit) ||
test_bit(FF_FRICTION, dev->ffbit) ||
test_bit(FF_INERTIA, dev->ffbit)) &&
PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) {
hid_warn(pidff->hid, "unknown condition effect layout\n");
clear_bit(FF_SPRING, dev->ffbit);
clear_bit(FF_DAMPER, dev->ffbit);
clear_bit(FF_FRICTION, dev->ffbit);
clear_bit(FF_INERTIA, dev->ffbit);
if (test_bit(FF_SPRING, dev->ffbit) ||
test_bit(FF_DAMPER, dev->ffbit) ||
test_bit(FF_FRICTION, dev->ffbit) ||
test_bit(FF_INERTIA, dev->ffbit)) {
status = PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1);
if (status < 0) {
hid_warn(pidff->hid, "unknown condition effect layout\n");
clear_bit(FF_SPRING, dev->ffbit);
clear_bit(FF_DAMPER, dev->ffbit);
clear_bit(FF_FRICTION, dev->ffbit);
clear_bit(FF_INERTIA, dev->ffbit);
}
pidff->quirks |= status;
}
if (test_bit(FF_PERIODIC, dev->ffbit) &&
@ -1142,46 +1349,6 @@ static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
return 0;
}
/*
* Reset the device
*/
static void pidff_reset(struct pidff_device *pidff)
{
struct hid_device *hid = pidff->hid;
int i = 0;
pidff->device_control->value[0] = pidff->control_id[PID_RESET];
/* We reset twice as sometimes hid_wait_io isn't waiting long enough */
hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(hid);
hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(hid);
pidff->device_control->value[0] =
pidff->control_id[PID_ENABLE_ACTUATORS];
hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(hid);
/* pool report is sometimes messed up, refetch it */
hid_hw_request(hid, pidff->reports[PID_POOL], HID_REQ_GET_REPORT);
hid_hw_wait(hid);
if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
while (pidff->pool[PID_SIMULTANEOUS_MAX].value[0] < 2) {
if (i++ > 20) {
hid_warn(pidff->hid,
"device reports %d simultaneous effects\n",
pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
break;
}
hid_dbg(pidff->hid, "pid_pool requested again\n");
hid_hw_request(hid, pidff->reports[PID_POOL],
HID_REQ_GET_REPORT);
hid_hw_wait(hid);
}
}
}
/*
* Test if autocenter modification is using the supported method
*/
@ -1206,24 +1373,23 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] ==
pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) {
pidff_autocenter(pidff, 0xffff);
pidff_autocenter(pidff, U16_MAX);
set_bit(FF_AUTOCENTER, dev->ffbit);
} else {
hid_notice(pidff->hid,
"device has unknown autocenter control method\n");
}
pidff_erase_pid(pidff,
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]);
return 0;
}
/*
* Check if the device is PID and initialize it
* Set initial quirks
*/
int hid_pidff_init(struct hid_device *hid)
int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks)
{
struct pidff_device *pidff;
struct hid_input *hidinput = list_entry(hid->inputs.next,
@ -1245,6 +1411,8 @@ int hid_pidff_init(struct hid_device *hid)
return -ENOMEM;
pidff->hid = hid;
pidff->quirks = initial_quirks;
pidff->effect_count = 0;
hid_device_io_start(hid);
@ -1261,14 +1429,9 @@ int hid_pidff_init(struct hid_device *hid)
if (error)
goto fail;
pidff_reset(pidff);
if (test_bit(FF_GAIN, dev->ffbit)) {
pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
hid_hw_request(hid, pidff->reports[PID_DEVICE_GAIN],
HID_REQ_SET_REPORT);
}
/* pool report is sometimes messed up, refetch it */
pidff_fetch_pool(pidff);
pidff_set_gain_report(pidff, U16_MAX);
error = pidff_check_autocenter(pidff, dev);
if (error)
goto fail;
@ -1311,6 +1474,7 @@ int hid_pidff_init(struct hid_device *hid)
ff->playback = pidff_playback;
hid_info(dev, "Force feedback for USB HID PID devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
hid_dbg(dev, "Active quirks mask: 0x%x\n", pidff->quirks);
hid_device_io_stop(hid);
@ -1322,3 +1486,14 @@ int hid_pidff_init(struct hid_device *hid)
kfree(pidff);
return error;
}
EXPORT_SYMBOL_GPL(hid_pidff_init_with_quirks);
/*
* Check if the device is PID and initialize it
* Wrapper made to keep the compatibility with old
* init function
*/
int hid_pidff_init(struct hid_device *hid)
{
return hid_pidff_init_with_quirks(hid, 0);
}

View File

@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __HID_PIDFF_H
#define __HID_PIDFF_H
#include <linux/hid.h>
/* HID PIDFF quirks */
/* Delay field (0xA7) missing. Skip it during set effect report upload */
#define HID_PIDFF_QUIRK_MISSING_DELAY BIT(0)
/* Missing Paramter block offset (0x23). Skip it during SET_CONDITION
report upload */
#define HID_PIDFF_QUIRK_MISSING_PBO BIT(1)
/* Initialise device control field even if logical_minimum != 1 */
#define HID_PIDFF_QUIRK_PERMISSIVE_CONTROL BIT(2)
/* Use fixed 0x4000 direction during SET_EFFECT report upload */
#define HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION BIT(3)
/* Force all periodic effects to be uploaded as SINE */
#define HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY BIT(4)
#ifdef CONFIG_HID_PID
int hid_pidff_init(struct hid_device *hid);
int hid_pidff_init_with_quirks(struct hid_device *hid, u32 initial_quirks);
#else
#define hid_pidff_init NULL
#define hid_pidff_init_with_quirks NULL
#endif
#endif

View File

@ -1224,12 +1224,6 @@ unsigned long hid_lookup_quirk(const struct hid_device *hdev);
int hid_quirks_init(char **quirks_param, __u16 bus, int count);
void hid_quirks_exit(__u16 bus);
#ifdef CONFIG_HID_PID
int hid_pidff_init(struct hid_device *hid);
#else
#define hid_pidff_init NULL
#endif
#define dbg_hid(fmt, ...) pr_debug("%s: " fmt, __FILE__, ##__VA_ARGS__)
#define hid_err(hid, fmt, ...) \