Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.19 146 lines 4.1 kB view raw
1/* 2 * Force feedback support for various HID compliant devices by ThrustMaster: 3 * ThrustMaster FireStorm Dual Power 2 4 * and possibly others whose device ids haven't been added. 5 * 6 * Modified to support ThrustMaster devices by Zinx Verituse 7 * on 2003-01-25 from the Logitech force feedback driver, 8 * which is by Johann Deneux. 9 * 10 * Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org> 11 * Copyright (c) 2002 Johann Deneux 12 */ 13 14/* 15 * This program is free software; you can redistribute it and/or modify 16 * it under the terms of the GNU General Public License as published by 17 * the Free Software Foundation; either version 2 of the License, or 18 * (at your option) any later version. 19 * 20 * This program is distributed in the hope that it will be useful, 21 * but WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 * GNU General Public License for more details. 24 * 25 * You should have received a copy of the GNU General Public License 26 * along with this program; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 */ 29 30#include <linux/input.h> 31 32#undef DEBUG 33#include <linux/usb.h> 34 35#include "hid.h" 36 37/* Usages for thrustmaster devices I know about */ 38#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb) 39 40 41struct tmff_device { 42 struct hid_report *report; 43 struct hid_field *rumble; 44}; 45 46/* Changes values from 0 to 0xffff into values from minimum to maximum */ 47static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum) 48{ 49 int ret; 50 51 ret = (in * (maximum - minimum) / 0xffff) + minimum; 52 if (ret < minimum) 53 return minimum; 54 if (ret > maximum) 55 return maximum; 56 return ret; 57} 58 59static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect) 60{ 61 struct hid_device *hid = dev->private; 62 struct tmff_device *tmff = data; 63 int left, right; /* Rumbling */ 64 65 left = hid_tmff_scale(effect->u.rumble.weak_magnitude, 66 tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); 67 right = hid_tmff_scale(effect->u.rumble.strong_magnitude, 68 tmff->rumble->logical_minimum, tmff->rumble->logical_maximum); 69 70 tmff->rumble->value[0] = left; 71 tmff->rumble->value[1] = right; 72 dbg("(left,right)=(%08x, %08x)", left, right); 73 hid_submit_report(hid, tmff->report, USB_DIR_OUT); 74 75 return 0; 76} 77 78int hid_tmff_init(struct hid_device *hid) 79{ 80 struct tmff_device *tmff; 81 struct list_head *pos; 82 struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list); 83 struct input_dev *input_dev = hidinput->input; 84 int error; 85 86 tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); 87 if (!tmff) 88 return -ENOMEM; 89 90 /* Find the report to use */ 91 __list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) { 92 struct hid_report *report = (struct hid_report *)pos; 93 int fieldnum; 94 95 for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { 96 struct hid_field *field = report->field[fieldnum]; 97 98 if (field->maxusage <= 0) 99 continue; 100 101 switch (field->usage[0].hid) { 102 case THRUSTMASTER_USAGE_RUMBLE_LR: 103 if (field->report_count < 2) { 104 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with report_count < 2"); 105 continue; 106 } 107 108 if (field->logical_maximum == field->logical_minimum) { 109 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR with logical_maximum == logical_minimum"); 110 continue; 111 } 112 113 if (tmff->report && tmff->report != report) { 114 warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report"); 115 continue; 116 } 117 118 if (tmff->rumble && tmff->rumble != field) { 119 warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR"); 120 continue; 121 } 122 123 tmff->report = report; 124 tmff->rumble = field; 125 126 set_bit(FF_RUMBLE, input_dev->ffbit); 127 break; 128 129 default: 130 warn("ignoring unknown output usage %08x", field->usage[0].hid); 131 continue; 132 } 133 } 134 } 135 136 error = input_ff_create_memless(input_dev, tmff, hid_tmff_play); 137 if (error) { 138 kfree(tmff); 139 return error; 140 } 141 142 info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>"); 143 144 return 0; 145} 146