1/* 2 * Force feedback support for PantherLord USB/PS2 2in1 Adapter devices 3 * 4 * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> 5 */ 6 7/* 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23 24/* #define DEBUG */ 25 26#define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) 27 28#include <linux/input.h> 29#include <linux/usb.h> 30#include <linux/hid.h> 31#include "usbhid.h" 32 33struct plff_device { 34 struct hid_report *report; 35}; 36 37static int hid_plff_play(struct input_dev *dev, void *data, 38 struct ff_effect *effect) 39{ 40 struct hid_device *hid = input_get_drvdata(dev); 41 struct plff_device *plff = data; 42 int left, right; 43 44 left = effect->u.rumble.strong_magnitude; 45 right = effect->u.rumble.weak_magnitude; 46 debug("called with 0x%04x 0x%04x", left, right); 47 48 left = left * 0x7f / 0xffff; 49 right = right * 0x7f / 0xffff; 50 51 plff->report->field[0]->value[2] = left; 52 plff->report->field[0]->value[3] = right; 53 debug("running with 0x%02x 0x%02x", left, right); 54 usbhid_submit_report(hid, plff->report, USB_DIR_OUT); 55 56 return 0; 57} 58 59int hid_plff_init(struct hid_device *hid) 60{ 61 struct plff_device *plff; 62 struct hid_report *report; 63 struct hid_input *hidinput; 64 struct list_head *report_list = 65 &hid->report_enum[HID_OUTPUT_REPORT].report_list; 66 struct list_head *report_ptr = report_list; 67 struct input_dev *dev; 68 int error; 69 70 /* The device contains 2 output reports (one for each 71 HID_QUIRK_MULTI_INPUT device), both containing 1 field, which 72 contains 4 ff00.0002 usages and 4 16bit absolute values. 73 74 The 2 input reports also contain a field which contains 75 8 ff00.0001 usages and 8 boolean values. Their meaning is 76 currently unknown. */ 77 78 if (list_empty(report_list)) { 79 printk(KERN_ERR "hid-plff: no output reports found\n"); 80 return -ENODEV; 81 } 82 83 list_for_each_entry(hidinput, &hid->inputs, list) { 84 85 report_ptr = report_ptr->next; 86 87 if (report_ptr == report_list) { 88 printk(KERN_ERR "hid-plff: required output report is missing\n"); 89 return -ENODEV; 90 } 91 92 report = list_entry(report_ptr, struct hid_report, list); 93 if (report->maxfield < 1) { 94 printk(KERN_ERR "hid-plff: no fields in the report\n"); 95 return -ENODEV; 96 } 97 98 if (report->field[0]->report_count < 4) { 99 printk(KERN_ERR "hid-plff: not enough values in the field\n"); 100 return -ENODEV; 101 } 102 103 plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); 104 if (!plff) 105 return -ENOMEM; 106 107 dev = hidinput->input; 108 109 set_bit(FF_RUMBLE, dev->ffbit); 110 111 error = input_ff_create_memless(dev, plff, hid_plff_play); 112 if (error) { 113 kfree(plff); 114 return error; 115 } 116 117 plff->report = report; 118 plff->report->field[0]->value[0] = 0x00; 119 plff->report->field[0]->value[1] = 0x00; 120 plff->report->field[0]->value[2] = 0x00; 121 plff->report->field[0]->value[3] = 0x00; 122 usbhid_submit_report(hid, plff->report, USB_DIR_OUT); 123 } 124 125 printk(KERN_INFO "hid-plff: Force feedback for PantherLord USB/PS2 " 126 "2in1 Adapters by Anssi Hannula <anssi.hannula@gmail.com>\n"); 127 128 return 0; 129} 130