| 1 | |
| 2 | |
| 3 | * Force feedback support for DragonRise Inc. game controllers
|
| 4 | *
|
| 5 | * From what I have gathered, these devices are mass produced in China and are
|
| 6 | * distributed under several vendors. They often share the same design as
|
| 7 | * the original PlayStation DualShock controller.
|
| 8 | *
|
| 9 | * 0079:0006 "DragonRise Inc. Generic USB Joystick "
|
| 10 | * - tested with a Tesun USB-703 game controller.
|
| 11 | *
|
| 12 | * Copyright (c) 2009 Richard Walmsley <[email protected]>
|
| 13 |
|
| 14 |
|
| 15 | |
| 16 |
|
| 17 |
|
| 18 | #include <linux/input.h>
|
| 19 | #include <linux/slab.h>
|
| 20 | #include <linux/hid.h>
|
| 21 | #include <linux/module.h>
|
| 22 |
|
| 23 | #include "hid-ids.h"
|
| 24 |
|
| 25 | #ifdef CONFIG_DRAGONRISE_FF
|
| 26 |
|
| 27 | struct drff_device {
|
| 28 | struct hid_report *report;
|
| 29 | };
|
| 30 |
|
| 31 | static int drff_play(struct input_dev *dev, void *data,
|
| 32 | struct ff_effect *effect)
|
| 33 | {
|
| 34 | struct hid_device *hid = input_get_drvdata(dev);
|
| 35 | struct drff_device *drff = data;
|
| 36 | int strong, weak;
|
| 37 |
|
| 38 | strong = effect->u.rumble.strong_magnitude;
|
| 39 | weak = effect->u.rumble.weak_magnitude;
|
| 40 |
|
| 41 | dbg_hid("called with 0x%04x 0x%04x", strong, weak);
|
| 42 |
|
| 43 | if (strong || weak) {
|
| 44 | strong = strong * 0xff / 0xffff;
|
| 45 | weak = weak * 0xff / 0xffff;
|
| 46 |
|
| 47 | |
| 48 | this value is set, it causes the strong rumble to function
|
| 49 | at a near maximum speed, so we'll bypass it.
|
| 50 | if (weak == 0x0a)
|
| 51 | weak = 0x0b;
|
| 52 |
|
| 53 | drff->report->field[0]->value[0] = 0x51;
|
| 54 | drff->report->field[0]->value[1] = 0x00;
|
| 55 | drff->report->field[0]->value[2] = weak;
|
| 56 | drff->report->field[0]->value[4] = strong;
|
| 57 | hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
|
| 58 |
|
| 59 | drff->report->field[0]->value[0] = 0xfa;
|
| 60 | drff->report->field[0]->value[1] = 0xfe;
|
| 61 | } else {
|
| 62 | drff->report->field[0]->value[0] = 0xf3;
|
| 63 | drff->report->field[0]->value[1] = 0x00;
|
| 64 | }
|
| 65 |
|
| 66 | drff->report->field[0]->value[2] = 0x00;
|
| 67 | drff->report->field[0]->value[4] = 0x00;
|
| 68 | dbg_hid("running with 0x%02x 0x%02x", strong, weak);
|
| 69 | hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
|
| 70 |
|
| 71 | return 0;
|
| 72 | }
|
| 73 |
|
| 74 | static int dr_input_configured(struct hid_device *hid, struct hid_input *hidinput)
|
| 75 | {
|
| 76 | struct drff_device *drff;
|
| 77 | struct hid_report *report;
|
| 78 | struct list_head *report_list =
|
| 79 | &hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
| 80 | struct input_dev *dev = hidinput->input;
|
| 81 | int error;
|
| 82 |
|
| 83 | if (!list_is_first(&hidinput->list, &hid->inputs))
|
| 84 | return 0;
|
| 85 |
|
| 86 | if (hid->product != 0x0006)
|
| 87 | return 0;
|
| 88 |
|
| 89 | report = list_first_entry_or_null(report_list, struct hid_report, list);
|
| 90 | if (!report) {
|
| 91 | hid_err(hid, "no output reports found\n");
|
| 92 | return -ENODEV;
|
| 93 | }
|
| 94 | if (report->maxfield < 1) {
|
| 95 | hid_err(hid, "no fields in the report\n");
|
| 96 | return -ENODEV;
|
| 97 | }
|
| 98 |
|
| 99 | if (report->field[0]->report_count < 7) {
|
| 100 | hid_err(hid, "not enough values in the field\n");
|
| 101 | return -ENODEV;
|
| 102 | }
|
| 103 |
|
| 104 | drff = kzalloc_obj(struct drff_device);
|
| 105 | if (!drff)
|
| 106 | return -ENOMEM;
|
| 107 |
|
| 108 | drff->report = report;
|
| 109 | set_bit(FF_RUMBLE, dev->ffbit);
|
| 110 |
|
| 111 | error = input_ff_create_memless(dev, drff, drff_play);
|
| 112 | if (error) {
|
| 113 | kfree(drff);
|
| 114 | return error;
|
| 115 | }
|
| 116 |
|
| 117 | drff->report->field[0]->value[0] = 0xf3;
|
| 118 | drff->report->field[0]->value[1] = 0x00;
|
| 119 | drff->report->field[0]->value[2] = 0x00;
|
| 120 | drff->report->field[0]->value[3] = 0x00;
|
| 121 | drff->report->field[0]->value[4] = 0x00;
|
| 122 | drff->report->field[0]->value[5] = 0x00;
|
| 123 | drff->report->field[0]->value[6] = 0x00;
|
| 124 | hid_hw_request(hid, drff->report, HID_REQ_SET_REPORT);
|
| 125 |
|
| 126 | hid_info(hid, "Force Feedback for DragonRise Inc. "
|
| 127 | "game controllers by Richard Walmsley <[email protected]>\n");
|
| 128 |
|
| 129 | return 0;
|
| 130 | }
|
| 131 | #else
|
| 132 | static inline int dr_input_configured(struct hid_device *hid,
|
| 133 | struct hid_input *hidinput)
|
| 134 | {
|
| 135 | return 0;
|
| 136 | }
|
| 137 | #endif
|
| 138 |
|
| 139 | |
| 140 | * The original descriptor of joystick with PID 0x0011, represented by DVTech PC
|
| 141 | * JS19. It seems both copied from another device and a result of confusion
|
| 142 | * either about the specification or about the program used to create the
|
| 143 | * descriptor. In any case, it's a wonder it works on Windows.
|
| 144 | *
|
| 145 | * Usage Page (Desktop), ; Generic desktop controls (01h)
|
| 146 | * Usage (Joystick), ; Joystick (04h, application collection)
|
| 147 | * Collection (Application),
|
| 148 | * Collection (Logical),
|
| 149 | * Report Size (8),
|
| 150 | * Report Count (5),
|
| 151 | * Logical Minimum (0),
|
| 152 | * Logical Maximum (255),
|
| 153 | * Physical Minimum (0),
|
| 154 | * Physical Maximum (255),
|
| 155 | * Usage (X), ; X (30h, dynamic value)
|
| 156 | * Usage (X), ; X (30h, dynamic value)
|
| 157 | * Usage (X), ; X (30h, dynamic value)
|
| 158 | * Usage (X), ; X (30h, dynamic value)
|
| 159 | * Usage (Y), ; Y (31h, dynamic value)
|
| 160 | * Input (Variable),
|
| 161 | * Report Size (4),
|
| 162 | * Report Count (1),
|
| 163 | * Logical Maximum (7),
|
| 164 | * Physical Maximum (315),
|
| 165 | * Unit (Degrees),
|
| 166 | * Usage (00h),
|
| 167 | * Input (Variable, Null State),
|
| 168 | * Unit,
|
| 169 | * Report Size (1),
|
| 170 | * Report Count (10),
|
| 171 | * Logical Maximum (1),
|
| 172 | * Physical Maximum (1),
|
| 173 | * Usage Page (Button), ; Button (09h)
|
| 174 | * Usage Minimum (01h),
|
| 175 | * Usage Maximum (0Ah),
|
| 176 | * Input (Variable),
|
| 177 | * Usage Page (FF00h), ; FF00h, vendor-defined
|
| 178 | * Report Size (1),
|
| 179 | * Report Count (10),
|
| 180 | * Logical Maximum (1),
|
| 181 | * Physical Maximum (1),
|
| 182 | * Usage (01h),
|
| 183 | * Input (Variable),
|
| 184 | * End Collection,
|
| 185 | * Collection (Logical),
|
| 186 | * Report Size (8),
|
| 187 | * Report Count (4),
|
| 188 | * Physical Maximum (255),
|
| 189 | * Logical Maximum (255),
|
| 190 | * Usage (02h),
|
| 191 | * Output (Variable),
|
| 192 | * End Collection,
|
| 193 | * End Collection
|
| 194 |
|
| 195 |
|
| 196 |
|
| 197 | #define PID0011_RDESC_ORIG_SIZE 101
|
| 198 |
|
| 199 |
|
| 200 | static const __u8 pid0011_rdesc_fixed[] = {
|
| 201 | 0x05, 0x01,
|
| 202 | 0x09, 0x04,
|
| 203 | 0xA1, 0x01,
|
| 204 | 0xA1, 0x02,
|
| 205 | 0x14,
|
| 206 | 0x75, 0x08,
|
| 207 | 0x95, 0x03,
|
| 208 | 0x81, 0x01,
|
| 209 | 0x26, 0xFF, 0x00,
|
| 210 | 0x95, 0x02,
|
| 211 | 0x09, 0x30,
|
| 212 | 0x09, 0x31,
|
| 213 | 0x81, 0x02,
|
| 214 | 0x75, 0x01,
|
| 215 | 0x95, 0x04,
|
| 216 | 0x81, 0x01,
|
| 217 | 0x25, 0x01,
|
| 218 | 0x95, 0x0A,
|
| 219 | 0x05, 0x09,
|
| 220 | 0x19, 0x01,
|
| 221 | 0x29, 0x0A,
|
| 222 | 0x81, 0x02,
|
| 223 | 0x95, 0x0A,
|
| 224 | 0x81, 0x01,
|
| 225 | 0xC0,
|
| 226 | 0xC0
|
| 227 | };
|
| 228 |
|
| 229 | static const __u8 *dr_report_fixup(struct hid_device *hdev, __u8 *rdesc,
|
| 230 | unsigned int *rsize)
|
| 231 | {
|
| 232 | switch (hdev->product) {
|
| 233 | case 0x0011:
|
| 234 | if (*rsize == PID0011_RDESC_ORIG_SIZE) {
|
| 235 | *rsize = sizeof(pid0011_rdesc_fixed);
|
| 236 | return pid0011_rdesc_fixed;
|
| 237 | }
|
| 238 | break;
|
| 239 | }
|
| 240 | return rdesc;
|
| 241 | }
|
| 242 |
|
| 243 | #define map_abs(c) hid_map_usage(hi, usage, bit, max, EV_ABS, (c))
|
| 244 | #define map_rel(c) hid_map_usage(hi, usage, bit, max, EV_REL, (c))
|
| 245 |
|
| 246 | static int dr_input_mapping(struct hid_device *hdev, struct hid_input *hi,
|
| 247 | struct hid_field *field, struct hid_usage *usage,
|
| 248 | unsigned long **bit, int *max)
|
| 249 | {
|
| 250 | switch (usage->hid) {
|
| 251 | |
| 252 | * revert to the old hid-input behavior where axes
|
| 253 | * can be randomly assigned when hid->usage is reused.
|
| 254 |
|
| 255 | case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
|
| 256 | case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
|
| 257 | if (field->flags & HID_MAIN_ITEM_RELATIVE)
|
| 258 | map_rel(usage->hid & 0xf);
|
| 259 | else
|
| 260 | map_abs(usage->hid & 0xf);
|
| 261 | return 1;
|
| 262 | }
|
| 263 |
|
| 264 | return 0;
|
| 265 | }
|
| 266 |
|
| 267 | static const struct hid_device_id dr_devices[] = {
|
| 268 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), },
|
| 269 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0011), },
|
| 270 | { }
|
| 271 | };
|
| 272 | MODULE_DEVICE_TABLE(hid, dr_devices);
|
| 273 |
|
| 274 | static struct hid_driver dr_driver = {
|
| 275 | .name = "dragonrise",
|
| 276 | .id_table = dr_devices,
|
| 277 | .report_fixup = dr_report_fixup,
|
| 278 | .input_mapping = dr_input_mapping,
|
| 279 | .input_configured = dr_input_configured,
|
| 280 | };
|
| 281 | module_hid_driver(dr_driver);
|
| 282 |
|
| 283 | MODULE_DESCRIPTION("Force feedback support for DragonRise Inc. game controllers");
|
| 284 | MODULE_LICENSE("GPL");
|