/* 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 #define PID_POOL 3 #define PID_BLOCK_LOAD 4 #define PID_BLOCK_FREE 5 #define PID_DEVICE_CONTROL 6 #define PID_CREATE_NEW_EFFECT 7
#define PID_REQUIRED_REPORTS 7
#define PID_SET_ENVELOPE 8 #define PID_SET_CONDITION 9 #define PID_SET_PERIODIC 10 #define PID_SET_CONSTANT 11 #define PID_SET_RAMP 12 staticconst 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.
*/
/* * 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;
/* Special fields in set_effect */ struct hid_field *set_effect_type; struct hid_field *effect_direction;
/* Special field in device_control */ struct hid_field *device_control;
/* Special field in block_load */ struct hid_field *block_load_status;
/* Special field in effect_operation */ struct hid_field *effect_operation_status;
int control_id[sizeof(pidff_device_control)]; int type_id[sizeof(pidff_effect_types)]; int status_id[sizeof(pidff_block_load_status)]; int operation_id[sizeof(pidff_effect_operation_status)];
/* * Clamp value for a given field
*/ static s32 pidff_clamp(s32 i, struct hid_field *field)
{ return (s32)clamp(i, field->logical_minimum, field->logical_maximum);
}
/* * Scale an unsigned value with range 0..max for the given field
*/ staticint pidff_rescale(int i, int max, struct hid_field *field)
{ return i * (field->logical_maximum - field->logical_minimum) / max +
field->logical_minimum;
}
/* * Scale a signed value in range S16_MIN..S16_MAX for the given field
*/ staticint pidff_rescale_signed(int i, struct hid_field *field)
{ 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;
}
staticvoid pidff_set(struct pidff_usage *usage, u16 value)
{
usage->value[0] = pidff_rescale(value, U16_MAX, usage->field);
pr_debug("calculated from %d to %d\n", value, usage->value[0]);
}
staticvoid 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;
}
/* Use fixed direction if needed */ if (pidff->quirks & HID_PIDFF_QUIRK_FIX_CONDITIONAL_DIRECTION &&
pidff_is_effect_conditional(effect))
direction = PIDFF_FIXED_WHEEL_DIRECTION;
/* * Test if the new envelope differs from old one
*/ staticint pidff_needs_set_envelope(struct ff_envelope *envelope, struct ff_envelope *old)
{ bool needs_new_envelope;
/* * Test if the constant parameters have changed between effects
*/ staticint pidff_needs_set_constant(struct ff_effect *effect, struct ff_effect *old)
{ return effect->u.constant.level != old->u.constant.level;
}
/* * Send set effect report to the device
*/ staticvoid pidff_set_effect_report(struct pidff_device *pidff, struct ff_effect *effect)
{
pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
pidff->set_effect_type->value[0] =
pidff->create_new_effect_type->value[0];
/* 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);
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;
/* * Test if condition effect parameters have changed
*/ staticint pidff_needs_set_condition(struct ff_effect *effect, struct ff_effect *old)
{ int i; int ret = 0;
for (i = 0; i < 2; i++) { struct ff_condition_effect *cond = &effect->u.condition[i]; struct ff_condition_effect *old_cond = &old->u.condition[i];
/* * Send device control report to the device
*/ staticvoid 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;
}
/* * Fetch pool report
*/ staticvoid 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.
*/ staticint pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
{ int j;
/* * Play the effect with PID id n times
*/ staticvoid pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
{
pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
/* * Play the effect with effect id @effect_id for @value times
*/ staticint pidff_playback(struct input_dev *dev, int effect_id, int value)
{ struct pidff_device *pidff = dev->ff->private;
/* * Erase effect with PID id * Decrease the device effect counter
*/ staticvoid 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--;
}
/* * Stop and erase effect with effect_id
*/ staticint pidff_erase_effect(struct input_dev *dev, int effect_id)
{ struct pidff_device *pidff = dev->ff->private; int pid_id = pidff->pid_id[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.
*/
hid_hw_wait(pidff->hid);
pidff_playback_pid(pidff, pid_id, 0);
pidff_erase_pid(pidff, pid_id);
/* * Find fields from a report and fill a pidff_usage
*/ staticint pidff_find_fields(struct pidff_usage *usage, const u8 *table, struct hid_report *report, int count, int strict)
{ if (!report) {
pr_debug("%s, null report\n", __func__); return -1;
}
int i, j, k, found; int return_value = 0;
for (k = 0; k < count; k++) {
found = 0; for (i = 0; i < report->maxfield; i++) { if (report->field[i]->maxusage !=
report->field[i]->report_count) {
pr_debug("maxusage and report_count do not match, skipping\n"); continue;
} for (j = 0; j < report->field[i]->maxusage; j++) { if (report->field[i]->usage[j].hid ==
(HID_UP_PID | table[k])) {
pr_debug("found %d at %d->%d\n",
k, i, j);
usage[k].field = report->field[i];
usage[k].value =
&report->field[i]->value[j];
found = 1; break;
}
} if (found) break;
} 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;
} elseif (!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;
} elseif (!found && strict) {
pr_debug("failed to locate %d\n", k); return -1;
}
} return return_value;
}
/* * Return index into pidff_reports for the given usage
*/ staticint pidff_check_usage(int usage)
{ int i;
for (i = 0; i < sizeof(pidff_reports); i++) if (usage == (HID_UP_PID | pidff_reports[i])) return i;
return -1;
}
/* * Find the reports and fill pidff->reports[] * report_type specifies either OUTPUT or FEATURE reports
*/ staticvoid pidff_find_reports(struct hid_device *hid, int report_type, struct pidff_device *pidff)
{ struct hid_report *report; int i, ret;
list_for_each_entry(report,
&hid->report_enum[report_type].report_list, list) { if (report->maxfield < 1) continue;
ret = pidff_check_usage(report->field[0]->logical); if (ret != -1) {
hid_dbg(hid, "found usage 0x%02x from field->logical\n",
pidff_reports[ret]);
pidff->reports[ret] = report; continue;
}
/* * Sometimes logical collections are stacked to indicate * different usages for the report and the field, in which * case we want the usage of the parent. However, Linux HID * implementation hides this fact, so we have to dig it up * ourselves
*/
i = report->field[0]->usage[0].collection_index; if (i <= 0 ||
hid->collection[i - 1].type != HID_COLLECTION_LOGICAL) continue;
ret = pidff_check_usage(hid->collection[i - 1].usage); if (ret != -1 && !pidff->reports[ret]) {
hid_dbg(hid, "found usage 0x%02x from collection array\n",
pidff_reports[ret]);
pidff->reports[ret] = report;
}
}
}
/* * Test if the required reports have been found
*/ staticint pidff_reports_ok(struct pidff_device *pidff)
{ int i;
for (i = 0; i <= PID_REQUIRED_REPORTS; i++) { if (!pidff->reports[i]) {
hid_dbg(pidff->hid, "%d missing\n", i); return 0;
}
}
return 1;
}
/* * Find a field with a specific usage within a report
*/ staticstruct hid_field *pidff_find_special_field(struct hid_report *report, int usage, int enforce_min)
{ if (!report) {
pr_debug("%s, null report\n", __func__); return NULL;
}
int i;
for (i = 0; i < report->maxfield; i++) { if (report->field[i]->logical == (HID_UP_PID | usage) &&
report->field[i]->report_count > 0) { if (!enforce_min ||
report->field[i]->logical_minimum == 1) return report->field[i];
pr_err("logical_minimum is not 1 as it should be\n"); return NULL;
}
} return NULL;
}
/* * Fill a pidff->*_id struct table
*/ staticint pidff_find_special_keys(int *keys, struct hid_field *fld, const u8 *usagetable, int count)
{
int i, j; int found = 0;
for (i = 0; i < count; i++) { for (j = 0; j < fld->maxusage; j++) { if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) {
keys[i] = j + 1;
found++; break;
}
}
} return found;
}
/* * Find and check the special fields
*/ staticint pidff_find_special_fields(struct pidff_device *pidff)
{
hid_dbg(pidff->hid, "finding special fields\n");
if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
effect_types)) {
hid_err(pidff->hid, "no effect types found\n"); return -1;
}
if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status,
block_load_status) != sizeof(pidff_block_load_status)) {
hid_err(pidff->hid, "block load status identifiers not found\n"); return -1;
}
if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status,
effect_operation_status) != sizeof(pidff_effect_operation_status)) {
hid_err(pidff->hid, "effect operation identifiers not found\n"); return -1;
}
return 0;
}
/* * Find the implemented effect types
*/ staticint pidff_find_effects(struct pidff_device *pidff, struct input_dev *dev)
{ int i;
for (i = 0; i < sizeof(pidff_effect_types); i++) { int pidff_type = pidff->type_id[i];
if (pidff->set_effect_type->usage[pidff_type].hid !=
pidff->create_new_effect_type->usage[pidff_type].hid) {
hid_err(pidff->hid, "effect type number %d is invalid\n", i); return -1;
}
}
if (pidff->type_id[PID_CONSTANT])
set_bit(FF_CONSTANT, dev->ffbit); if (pidff->type_id[PID_RAMP])
set_bit(FF_RAMP, dev->ffbit); if (pidff->type_id[PID_SQUARE]) {
set_bit(FF_SQUARE, dev->ffbit);
set_bit(FF_PERIODIC, dev->ffbit);
} if (pidff->type_id[PID_SINE]) {
set_bit(FF_SINE, dev->ffbit);
set_bit(FF_PERIODIC, dev->ffbit);
} if (pidff->type_id[PID_TRIANGLE]) {
set_bit(FF_TRIANGLE, dev->ffbit);
set_bit(FF_PERIODIC, dev->ffbit);
} if (pidff->type_id[PID_SAW_UP]) {
set_bit(FF_SAW_UP, dev->ffbit);
set_bit(FF_PERIODIC, dev->ffbit);
} if (pidff->type_id[PID_SAW_DOWN]) {
set_bit(FF_SAW_DOWN, dev->ffbit);
set_bit(FF_PERIODIC, dev->ffbit);
} if (pidff->type_id[PID_SPRING])
set_bit(FF_SPRING, dev->ffbit); if (pidff->type_id[PID_DAMPER])
set_bit(FF_DAMPER, dev->ffbit); if (pidff->type_id[PID_INERTIA])
set_bit(FF_INERTIA, dev->ffbit); if (pidff->type_id[PID_FRICTION])
set_bit(FF_FRICTION, dev->ffbit);
/* * Fill and check the pidff_usages
*/ staticint pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
{ int status = 0;
/* 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");
if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev)) return -ENODEV;
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"); if (test_and_clear_bit(FF_RAMP, dev->ffbit))
hid_warn(pidff->hid, "has ramp effect but no envelope\n");
if (test_and_clear_bit(FF_PERIODIC, dev->ffbit))
hid_warn(pidff->hid, "has periodic effect but no envelope\n");
}
if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1))
set_bit(FF_GAIN, dev->ffbit);
return 0;
}
/* * Test if autocenter modification is using the supported method
*/ staticint pidff_check_autocenter(struct pidff_device *pidff, struct input_dev *dev)
{ int error;
/* * Let's find out if autocenter modification is supported * Specification doesn't specify anything, so we request an * effect upload and cancel it immediately. If the approved * effect id was one above the minimum, then we assume the first * effect id is a built-in spring type effect used for autocenter
*/
if (!pidff_reports_ok(pidff)) {
hid_dbg(hid, "reports not ok, aborting\n");
error = -ENODEV; goto fail;
}
error = pidff_init_fields(pidff, dev); if (error) goto fail;
/* 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;
/* * 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);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-04-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.