1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Force feedback support for Mayflash game controller adapters. |
4 | * |
5 | * These devices are manufactured by Mayflash but identify themselves |
6 | * using the vendor ID of DragonRise Inc. |
7 | * |
8 | * Tested with: |
9 | * 0079:1801 "DragonRise Inc. Mayflash PS3 Game Controller Adapter" |
10 | * 0079:1803 "DragonRise Inc. Mayflash Wireless Sensor DolphinBar" |
11 | * 0079:1843 "DragonRise Inc. Mayflash GameCube Game Controller Adapter" |
12 | * 0079:1844 "DragonRise Inc. Mayflash GameCube Game Controller Adapter (v04)" |
13 | * |
14 | * The following adapters probably work too, but need to be tested: |
15 | * 0079:1800 "DragonRise Inc. Mayflash WIIU Game Controller Adapter" |
16 | * |
17 | * Copyright (c) 2016-2017 Marcel Hasler <mahasler@gmail.com> |
18 | */ |
19 | |
20 | /* |
21 | */ |
22 | |
23 | #include <linux/input.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/hid.h> |
26 | #include <linux/module.h> |
27 | |
28 | #include "hid-ids.h" |
29 | |
30 | struct mf_device { |
31 | struct hid_report *report; |
32 | }; |
33 | |
34 | static int mf_play(struct input_dev *dev, void *data, struct ff_effect *effect) |
35 | { |
36 | struct hid_device *hid = input_get_drvdata(dev); |
37 | struct mf_device *mf = data; |
38 | int strong, weak; |
39 | |
40 | strong = effect->u.rumble.strong_magnitude; |
41 | weak = effect->u.rumble.weak_magnitude; |
42 | |
43 | dbg_hid("Called with 0x%04x 0x%04x.\n" , strong, weak); |
44 | |
45 | strong = strong * 0xff / 0xffff; |
46 | weak = weak * 0xff / 0xffff; |
47 | |
48 | dbg_hid("Running with 0x%02x 0x%02x.\n" , strong, weak); |
49 | |
50 | mf->report->field[0]->value[0] = weak; |
51 | mf->report->field[0]->value[1] = strong; |
52 | hid_hw_request(hdev: hid, report: mf->report, reqtype: HID_REQ_SET_REPORT); |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static int mf_init(struct hid_device *hid) |
58 | { |
59 | struct mf_device *mf; |
60 | |
61 | struct list_head *report_list = |
62 | &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
63 | |
64 | struct list_head *report_ptr; |
65 | struct hid_report *report; |
66 | |
67 | struct list_head *input_ptr = &hid->inputs; |
68 | struct hid_input *input; |
69 | |
70 | struct input_dev *dev; |
71 | |
72 | int error; |
73 | |
74 | /* Setup each of the four inputs */ |
75 | list_for_each(report_ptr, report_list) { |
76 | report = list_entry(report_ptr, struct hid_report, list); |
77 | |
78 | if (report->maxfield < 1 || report->field[0]->report_count < 2) { |
79 | hid_err(hid, "Invalid report, this should never happen!\n" ); |
80 | return -ENODEV; |
81 | } |
82 | |
83 | if (list_is_last(list: input_ptr, head: &hid->inputs)) { |
84 | hid_err(hid, "Missing input, this should never happen!\n" ); |
85 | return -ENODEV; |
86 | } |
87 | |
88 | input_ptr = input_ptr->next; |
89 | input = list_entry(input_ptr, struct hid_input, list); |
90 | |
91 | mf = kzalloc(size: sizeof(struct mf_device), GFP_KERNEL); |
92 | if (!mf) |
93 | return -ENOMEM; |
94 | |
95 | dev = input->input; |
96 | set_bit(FF_RUMBLE, addr: dev->ffbit); |
97 | |
98 | error = input_ff_create_memless(dev, data: mf, play_effect: mf_play); |
99 | if (error) { |
100 | kfree(objp: mf); |
101 | return error; |
102 | } |
103 | |
104 | mf->report = report; |
105 | mf->report->field[0]->value[0] = 0x00; |
106 | mf->report->field[0]->value[1] = 0x00; |
107 | hid_hw_request(hdev: hid, report: mf->report, reqtype: HID_REQ_SET_REPORT); |
108 | } |
109 | |
110 | hid_info(hid, "Force feedback for HJZ Mayflash game controller " |
111 | "adapters by Marcel Hasler <mahasler@gmail.com>\n" ); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static int mf_probe(struct hid_device *hid, const struct hid_device_id *id) |
117 | { |
118 | int error; |
119 | |
120 | dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n" ); |
121 | |
122 | /* Apply quirks as needed */ |
123 | hid->quirks |= id->driver_data; |
124 | |
125 | error = hid_parse(hdev: hid); |
126 | if (error) { |
127 | hid_err(hid, "HID parse failed.\n" ); |
128 | return error; |
129 | } |
130 | |
131 | error = hid_hw_start(hdev: hid, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); |
132 | if (error) { |
133 | hid_err(hid, "HID hw start failed\n" ); |
134 | return error; |
135 | } |
136 | |
137 | error = mf_init(hid); |
138 | if (error) { |
139 | hid_err(hid, "Force feedback init failed.\n" ); |
140 | hid_hw_stop(hdev: hid); |
141 | return error; |
142 | } |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | static const struct hid_device_id mf_devices[] = { |
148 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), |
149 | .driver_data = HID_QUIRK_MULTI_INPUT }, |
150 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), |
151 | .driver_data = HID_QUIRK_MULTI_INPUT }, |
152 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), |
153 | .driver_data = HID_QUIRK_MULTI_INPUT }, |
154 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2), |
155 | .driver_data = 0 }, /* No quirk required */ |
156 | { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE3), |
157 | .driver_data = HID_QUIRK_MULTI_INPUT }, |
158 | { } |
159 | }; |
160 | MODULE_DEVICE_TABLE(hid, mf_devices); |
161 | |
162 | static struct hid_driver mf_driver = { |
163 | .name = "hid_mf" , |
164 | .id_table = mf_devices, |
165 | .probe = mf_probe, |
166 | }; |
167 | module_hid_driver(mf_driver); |
168 | |
169 | MODULE_LICENSE("GPL" ); |
170 | |