| 1 | |
| 2 | |
| 3 | * Force feedback support for PantherLord/GreenAsia based devices
|
| 4 | *
|
| 5 | * The devices are distributed under various names and the same USB device ID
|
| 6 | * can be used in both adapters and actual game controllers.
|
| 7 | *
|
| 8 | * 0810:0001 "Twin USB Joystick"
|
| 9 | * - tested with PantherLord USB/PS2 2in1 Adapter
|
| 10 | * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
|
| 11 | *
|
| 12 | * 0e8f:0003 "GreenAsia Inc. USB Joystick "
|
| 13 | * - tested with König Gaming gamepad
|
| 14 | *
|
| 15 | * 0e8f:0003 "GASIA USB Gamepad"
|
| 16 | * - another version of the König gamepad
|
| 17 | *
|
| 18 | * 0f30:0111 "Saitek Color Rumble Pad"
|
| 19 | *
|
| 20 | * Copyright (c) 2007, 2009 Anssi Hannula <[email protected]>
|
| 21 |
|
| 22 |
|
| 23 | |
| 24 |
|
| 25 |
|
| 26 |
|
| 27 | #include <linux/input.h>
|
| 28 | #include <linux/slab.h>
|
| 29 | #include <linux/module.h>
|
| 30 | #include <linux/hid.h>
|
| 31 |
|
| 32 | #include "hid-ids.h"
|
| 33 |
|
| 34 | #ifdef CONFIG_PANTHERLORD_FF
|
| 35 |
|
| 36 | struct plff_device {
|
| 37 | struct hid_report *report;
|
| 38 | s32 maxval;
|
| 39 | s32 *strong;
|
| 40 | s32 *weak;
|
| 41 | };
|
| 42 |
|
| 43 | static int hid_plff_play(struct input_dev *dev, void *data,
|
| 44 | struct ff_effect *effect)
|
| 45 | {
|
| 46 | struct hid_device *hid = input_get_drvdata(dev);
|
| 47 | struct plff_device *plff = data;
|
| 48 | int left, right;
|
| 49 |
|
| 50 | left = effect->u.rumble.strong_magnitude;
|
| 51 | right = effect->u.rumble.weak_magnitude;
|
| 52 | hid_dbg(dev, "called with 0x%04x 0x%04x", left, right);
|
| 53 |
|
| 54 | left = left * plff->maxval / 0xffff;
|
| 55 | right = right * plff->maxval / 0xffff;
|
| 56 |
|
| 57 | *plff->strong = left;
|
| 58 | *plff->weak = right;
|
| 59 | hid_dbg(dev, "running with 0x%02x 0x%02x", left, right);
|
| 60 | hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
|
| 61 |
|
| 62 | return 0;
|
| 63 | }
|
| 64 |
|
| 65 | static int pl_input_configured(struct hid_device *hid, struct hid_input *hidinput)
|
| 66 | {
|
| 67 | struct plff_device *plff;
|
| 68 | struct hid_report *report;
|
| 69 | struct list_head *report_list =
|
| 70 | &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
| 71 | struct input_dev *dev = hidinput->input;
|
| 72 | int error;
|
| 73 | s32 maxval;
|
| 74 | s32 *strong;
|
| 75 | s32 *weak;
|
| 76 |
|
| 77 | |
| 78 | containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
|
| 79 | absolute values.
|
| 80 |
|
| 81 | The input reports also contain a field which contains
|
| 82 | 8 ff00.0001 usages and 8 boolean values. Their meaning is
|
| 83 | currently unknown.
|
| 84 |
|
| 85 | A version of the 0e8f:0003 exists that has all the values in
|
| 86 | separate fields and misses the extra input field, thus resembling
|
| 87 | Zeroplus (hid-zpff) devices.
|
| 88 |
|
| 89 |
|
| 90 | if (!list_is_first(&hidinput->list, &hid->inputs))
|
| 91 | return 0;
|
| 92 |
|
| 93 | report = list_first_entry_or_null(report_list, struct hid_report, list);
|
| 94 | if (!report) {
|
| 95 | hid_err(hid, "no output reports found\n");
|
| 96 | return -ENODEV;
|
| 97 | }
|
| 98 | if (report->maxfield < 1) {
|
| 99 | hid_err(hid, "no fields in the report\n");
|
| 100 | return -ENODEV;
|
| 101 | }
|
| 102 |
|
| 103 | maxval = 0x7f;
|
| 104 | if (report->field[0]->report_count >= 4) {
|
| 105 | report->field[0]->value[0] = 0x00;
|
| 106 | report->field[0]->value[1] = 0x00;
|
| 107 | strong = &report->field[0]->value[2];
|
| 108 | weak = &report->field[0]->value[3];
|
| 109 | hid_dbg(hid, "detected single-field device");
|
| 110 | } else if (report->field[0]->maxusage == 1 &&
|
| 111 | report->field[0]->usage[0].hid ==
|
| 112 | (HID_UP_LED | 0x43) &&
|
| 113 | report->maxfield >= 4 &&
|
| 114 | report->field[0]->report_count >= 1 &&
|
| 115 | report->field[1]->report_count >= 1 &&
|
| 116 | report->field[2]->report_count >= 1 &&
|
| 117 | report->field[3]->report_count >= 1) {
|
| 118 | report->field[0]->value[0] = 0x00;
|
| 119 | report->field[1]->value[0] = 0x00;
|
| 120 | strong = &report->field[2]->value[0];
|
| 121 | weak = &report->field[3]->value[0];
|
| 122 | if (hid->vendor == USB_VENDOR_ID_JESS2)
|
| 123 | maxval = 0xff;
|
| 124 | hid_dbg(hid, "detected 4-field device");
|
| 125 | } else {
|
| 126 | hid_err(hid, "not enough fields or values\n");
|
| 127 | return -ENODEV;
|
| 128 | }
|
| 129 |
|
| 130 | plff = kzalloc_obj(struct plff_device);
|
| 131 | if (!plff)
|
| 132 | return -ENOMEM;
|
| 133 |
|
| 134 | dev = hidinput->input;
|
| 135 |
|
| 136 | set_bit(FF_RUMBLE, dev->ffbit);
|
| 137 |
|
| 138 | error = input_ff_create_memless(dev, plff, hid_plff_play);
|
| 139 | if (error) {
|
| 140 | kfree(plff);
|
| 141 | return error;
|
| 142 | }
|
| 143 |
|
| 144 | plff->report = report;
|
| 145 | plff->strong = strong;
|
| 146 | plff->weak = weak;
|
| 147 | plff->maxval = maxval;
|
| 148 |
|
| 149 | *strong = 0x00;
|
| 150 | *weak = 0x00;
|
| 151 | hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
|
| 152 |
|
| 153 | return 0;
|
| 154 | }
|
| 155 | #else
|
| 156 | static inline int pl_input_configured(struct hid_device *hid,
|
| 157 | struct hid_input *hidinput)
|
| 158 | {
|
| 159 | return 0;
|
| 160 | }
|
| 161 | #endif
|
| 162 |
|
| 163 | static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
| 164 | {
|
| 165 | int ret;
|
| 166 |
|
| 167 | if (id->driver_data)
|
| 168 | hdev->quirks |= HID_QUIRK_MULTI_INPUT;
|
| 169 |
|
| 170 | ret = hid_parse(hdev);
|
| 171 | if (ret) {
|
| 172 | hid_err(hdev, "parse failed\n");
|
| 173 | return ret;
|
| 174 | }
|
| 175 |
|
| 176 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
| 177 | if (ret) {
|
| 178 | hid_err(hdev, "hw start failed\n");
|
| 179 | return ret;
|
| 180 | }
|
| 181 |
|
| 182 | return 0;
|
| 183 | }
|
| 184 | static const struct hid_device_id pl_devices[] = {
|
| 185 | { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
|
| 186 | .driver_data = 1 },
|
| 187 | { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
|
| 188 | .driver_data = 1 },
|
| 189 | { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), },
|
| 190 | { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD), },
|
| 191 | { }
|
| 192 | };
|
| 193 | MODULE_DEVICE_TABLE(hid, pl_devices);
|
| 194 |
|
| 195 | static struct hid_driver pl_driver = {
|
| 196 | .name = "pantherlord",
|
| 197 | .id_table = pl_devices,
|
| 198 | .probe = pl_probe,
|
| 199 | .input_configured = pl_input_configured,
|
| 200 | };
|
| 201 | module_hid_driver(pl_driver);
|
| 202 |
|
| 203 | MODULE_DESCRIPTION("Force feedback support for PantherLord/GreenAsia based devices");
|
| 204 | MODULE_LICENSE("GPL");
|