⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hid-pidff.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  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 + -