1 /* 2 * Force feedback support for PantherLord/GreenAsia based devices 3 * 4 * The devices are distributed under various names and the same USB device ID 5 * can be used in both adapters and actual game controllers. 6 * 7 * 0810:0001 "Twin USB Joystick" 8 * - tested with PantherLord USB/PS2 2in1 Adapter 9 * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) 10 * 11 * 0e8f:0003 "GreenAsia Inc. USB Joystick " 12 * - tested with K??ng Gaming gamepad 13 * 14 * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> 15 */ 16 17 /* 18 * This program is free software; you can redistribute it and/or modify 19 * it under the terms of the GNU General Public License as published by 20 * the Free Software Foundation; either version 2 of the License, or 21 * (at your option) any later version. 22 * 23 * This program is distributed in the hope that it will be useful, 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * GNU General Public License for more details. 27 * 28 * You should have received a copy of the GNU General Public License 29 * along with this program; if not, write to the Free Software 30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 31 */ 32 33 34 /* #define DEBUG */ 35 36 #define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg) 37 38 #include <linux/input.h> 39 #include <linux/usb.h> 40 #include <linux/hid.h> 41 42 #include "hid-ids.h" 43 44 #ifdef CONFIG_PANTHERLORD_FF 45 #include "usbhid/usbhid.h" 46 47 struct plff_device { 48 struct hid_report *report; 49 }; 50 51 static int hid_plff_play(struct input_dev *dev, void *data, 52 struct ff_effect *effect) 53 { 54 struct hid_device *hid = input_get_drvdata(dev); 55 struct plff_device *plff = data; 56 int left, right; 57 58 left = effect->u.rumble.strong_magnitude; 59 right = effect->u.rumble.weak_magnitude; 60 debug("called with 0x%04x 0x%04x", left, right); 61 62 left = left * 0x7f / 0xffff; 63 right = right * 0x7f / 0xffff; 64 65 plff->report->field[0]->value[2] = left; 66 plff->report->field[0]->value[3] = right; 67 debug("running with 0x%02x 0x%02x", left, right); 68 usbhid_submit_report(hid, plff->report, USB_DIR_OUT); 69 70 return 0; 71 } 72 73 static int plff_init(struct hid_device *hid) 74 { 75 struct plff_device *plff; 76 struct hid_report *report; 77 struct hid_input *hidinput; 78 struct list_head *report_list = 79 &hid->report_enum[HID_OUTPUT_REPORT].report_list; 80 struct list_head *report_ptr = report_list; 81 struct input_dev *dev; 82 int error; 83 84 /* The device contains one output report per physical device, all 85 containing 1 field, which contains 4 ff00.0002 usages and 4 16bit 86 absolute values. 87 88 The input reports also contain a field which contains 89 8 ff00.0001 usages and 8 boolean values. Their meaning is 90 currently unknown. */ 91 92 if (list_empty(report_list)) { 93 dev_err(&hid->dev, "no output reports found\n"); 94 return -ENODEV; 95 } 96 97 list_for_each_entry(hidinput, &hid->inputs, list) { 98 99 report_ptr = report_ptr->next; 100 101 if (report_ptr == report_list) { 102 dev_err(&hid->dev, "required output report is " 103 "missing\n"); 104 return -ENODEV; 105 } 106 107 report = list_entry(report_ptr, struct hid_report, list); 108 if (report->maxfield < 1) { 109 dev_err(&hid->dev, "no fields in the report\n"); 110 return -ENODEV; 111 } 112 113 if (report->field[0]->report_count < 4) { 114 dev_err(&hid->dev, "not enough values in the field\n"); 115 return -ENODEV; 116 } 117 118 plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL); 119 if (!plff) 120 return -ENOMEM; 121 122 dev = hidinput->input; 123 124 set_bit(FF_RUMBLE, dev->ffbit); 125 126 error = input_ff_create_memless(dev, plff, hid_plff_play); 127 if (error) { 128 kfree(plff); 129 return error; 130 } 131 132 plff->report = report; 133 plff->report->field[0]->value[0] = 0x00; 134 plff->report->field[0]->value[1] = 0x00; 135 plff->report->field[0]->value[2] = 0x00; 136 plff->report->field[0]->value[3] = 0x00; 137 usbhid_submit_report(hid, plff->report, USB_DIR_OUT); 138 } 139 140 dev_info(&hid->dev, "Force feedback for PantherLord/GreenAsia " 141 "devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); 142 143 return 0; 144 } 145 #else 146 static inline int plff_init(struct hid_device *hid) 147 { 148 return 0; 149 } 150 #endif 151 152 static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id) 153 { 154 int ret; 155 156 if (id->driver_data) 157 hdev->quirks |= HID_QUIRK_MULTI_INPUT; 158 159 ret = hid_parse(hdev); 160 if (ret) { 161 dev_err(&hdev->dev, "parse failed\n"); 162 goto err; 163 } 164 165 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 166 if (ret) { 167 dev_err(&hdev->dev, "hw start failed\n"); 168 goto err; 169 } 170 171 plff_init(hdev); 172 173 return 0; 174 err: 175 return ret; 176 } 177 178 static const struct hid_device_id pl_devices[] = { 179 { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR), 180 .driver_data = 1 }, /* Twin USB Joystick */ 181 { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR), 182 .driver_data = 1 }, /* Twin USB Joystick */ 183 { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ 184 { } 185 }; 186 MODULE_DEVICE_TABLE(hid, pl_devices); 187 188 static struct hid_driver pl_driver = { 189 .name = "pantherlord", 190 .id_table = pl_devices, 191 .probe = pl_probe, 192 }; 193 194 static int pl_init(void) 195 { 196 return hid_register_driver(&pl_driver); 197 } 198 199 static void pl_exit(void) 200 { 201 hid_unregister_driver(&pl_driver); 202 } 203 204 module_init(pl_init); 205 module_exit(pl_exit); 206 MODULE_LICENSE("GPL"); 207 208 HID_COMPAT_LOAD_DRIVER(pantherlord); 209