📄 hid-pidff.c
字号:
/* * Force feedback driver for USB HID PID compliant devices * * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com> *//* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* #define DEBUG */#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg)#include <linux/input.h>#include <linux/usb.h>#include <linux/hid.h>#include "usbhid.h"#define PID_EFFECTS_MAX 64/* 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 12static 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 ofthe only field in that report *//* Value usage tables used to put fields and values into arrays */#define PID_EFFECT_BLOCK_INDEX 0#define PID_DURATION 1#define PID_GAIN 2#define PID_TRIGGER_BUTTON 3#define PID_TRIGGER_REPEAT_INT 4#define PID_DIRECTION_ENABLE 5#define PID_START_DELAY 6static const u8 pidff_set_effect[] = { 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7};#define PID_ATTACK_LEVEL 1#define PID_ATTACK_TIME 2#define PID_FADE_LEVEL 3#define PID_FADE_TIME 4static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e };#define PID_PARAM_BLOCK_OFFSET 1#define PID_CP_OFFSET 2#define PID_POS_COEFFICIENT 3#define PID_NEG_COEFFICIENT 4#define PID_POS_SATURATION 5#define PID_NEG_SATURATION 6#define PID_DEAD_BAND 7static const u8 pidff_set_condition[] = { 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65};#define PID_MAGNITUDE 1#define PID_OFFSET 2#define PID_PHASE 3#define PID_PERIOD 4static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 };static const u8 pidff_set_constant[] = { 0x22, 0x70 };#define PID_RAMP_START 1#define PID_RAMP_END 2static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 };#define PID_RAM_POOL_AVAILABLE 1static const u8 pidff_block_load[] = { 0x22, 0xac };#define PID_LOOP_COUNT 1static const u8 pidff_effect_operation[] = { 0x22, 0x7c };static const u8 pidff_block_free[] = { 0x22 };#define PID_DEVICE_GAIN_FIELD 0static const u8 pidff_device_gain[] = { 0x7e };#define PID_RAM_POOL_SIZE 0#define PID_SIMULTANEOUS_MAX 1#define PID_DEVICE_MANAGED_POOL 2static 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 1static const u8 pidff_device_control[] = { 0x97, 0x9a };#define PID_CONSTANT 0#define PID_RAMP 1#define PID_SQUARE 2#define PID_SINE 3#define PID_TRIANGLE 4#define PID_SAW_UP 5#define PID_SAW_DOWN 6#define PID_SPRING 7#define PID_DAMPER 8#define PID_INERTIA 9#define PID_FRICTION 10static const u8 pidff_effect_types[] = { 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, 0x40, 0x41, 0x42, 0x43};#define PID_BLOCK_LOAD_SUCCESS 0#define PID_BLOCK_LOAD_FULL 1static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };#define PID_EFFECT_START 0#define PID_EFFECT_STOP 1static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };struct pidff_usage { struct hid_field *field; s32 *value;};struct pidff_device { struct hid_device *hid; struct hid_report *reports[sizeof(pidff_reports)]; struct pidff_usage set_effect[sizeof(pidff_set_effect)]; struct pidff_usage set_envelope[sizeof(pidff_set_envelope)]; struct pidff_usage set_condition[sizeof(pidff_set_condition)]; struct pidff_usage set_periodic[sizeof(pidff_set_periodic)]; struct pidff_usage set_constant[sizeof(pidff_set_constant)]; struct pidff_usage set_ramp[sizeof(pidff_set_ramp)]; struct pidff_usage device_gain[sizeof(pidff_device_gain)]; struct pidff_usage block_load[sizeof(pidff_block_load)]; struct pidff_usage pool[sizeof(pidff_pool)]; 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 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)]; int pid_id[PID_EFFECTS_MAX];};/* * 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;}/* * Scale a signed value in range -0x8000..0x7fff 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;}static void pidff_set(struct pidff_usage *usage, u16 value){ usage->value[0] = pidff_rescale(value, 0xffff, usage->field); debug("calculated from %d to %d", value, usage->value[0]);}static void pidff_set_signed(struct pidff_usage *usage, s16 value){ if (usage->field->logical_minimum < 0) usage->value[0] = pidff_rescale_signed(value, usage->field); else { if (value < 0) usage->value[0] = pidff_rescale(-value, 0x8000, usage->field); else usage->value[0] = pidff_rescale(value, 0x7fff, usage->field); } debug("calculated from %d to %d", value, usage->value[0]);}/* * Send envelope report to the device */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->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->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->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; debug("attack %u => %d", envelope->attack_level, pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE], USB_DIR_OUT);}/* * Test if the new envelope differs from old one */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 || envelope->attack_length != old->attack_length || envelope->fade_length != old->fade_length;}/* * Send constant force report to the device */static void pidff_set_constant_force_report(struct pidff_device *pidff, struct ff_effect *effect){ pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE], effect->u.constant.level); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT], USB_DIR_OUT);}/* * Test if the constant parameters have changed between effects */static int 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 */static void 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]; pidff->set_effect[PID_DURATION].value[0] = 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_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; usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT], USB_DIR_OUT);}/* * Test if the values used in set_effect have changed */static int pidff_needs_set_effect(struct ff_effect *effect, struct ff_effect *old){ return effect->replay.length != old->replay.length || effect->trigger.interval != old->trigger.interval || effect->trigger.button != old->trigger.button || effect->direction != old->direction || effect->replay.delay != old->replay.delay;}/* * Send periodic effect report to the device */static void pidff_set_periodic_report(struct pidff_device *pidff, struct ff_effect *effect){ pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE], effect->u.periodic.magnitude); 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; usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC], USB_DIR_OUT);}/* * Test if periodic effect parameters have changed */static int pidff_needs_set_periodic(struct ff_effect *effect, struct ff_effect *old){ return effect->u.periodic.magnitude != old->u.periodic.magnitude || effect->u.periodic.offset != old->u.periodic.offset || effect->u.periodic.phase != old->u.periodic.phase || effect->u.periodic.period != old->u.periodic.period;}/* * Send condition effect reports to the device */static void pidff_set_condition_report(struct pidff_device *pidff, struct ff_effect *effect){ int i; 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; pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET], effect->u.condition[i].center); pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT], effect->u.condition[i].right_coeff); pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT], effect->u.condition[i].left_coeff); pidff_set(&pidff->set_condition[PID_POS_SATURATION], effect->u.condition[i].right_saturation); pidff_set(&pidff->set_condition[PID_NEG_SATURATION], effect->u.condition[i].left_saturation); pidff_set(&pidff->set_condition[PID_DEAD_BAND], effect->u.condition[i].deadband); usbhid_wait_io(pidff->hid); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION], USB_DIR_OUT); }}/* * Test if condition effect parameters have changed */static int 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]; ret |= cond->center != old_cond->center || cond->right_coeff != old_cond->right_coeff || cond->left_coeff != old_cond->left_coeff || cond->right_saturation != old_cond->right_saturation || cond->left_saturation != old_cond->left_saturation || cond->deadband != old_cond->deadband; } return ret;}/* * Send ramp force report to the device */static void pidff_set_ramp_force_report(struct pidff_device *pidff, struct ff_effect *effect){ pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff_set_signed(&pidff->set_ramp[PID_RAMP_START], effect->u.ramp.start_level); pidff_set_signed(&pidff->set_ramp[PID_RAMP_END], effect->u.ramp.end_level); usbhid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP], USB_DIR_OUT);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -