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(hid, mf->report, HID_REQ_SET_REPORT); 53 54 return 0; 55 } 56 57 static int mf_input_configured(struct hid_device *hid, struct hid_input *hidinput) 58 { 59 struct mf_device *mf; 60 struct list_head *report_list = 61 &hid->report_enum[HID_OUTPUT_REPORT].report_list; 62 struct hid_report *report; 63 struct input_dev *dev = hidinput->input; 64 int error; 65 66 if (!list_is_first(&hidinput->list, &hid->inputs)) 67 return 0; 68 69 report = list_first_entry_or_null(report_list, struct hid_report, list); 70 if (!report) { 71 hid_err(hid, "no output reports found\n"); 72 return -ENODEV; 73 } 74 75 if (report->maxfield < 1 || report->field[0]->report_count < 2) { 76 hid_err(hid, "Invalid report, this should never happen!\n"); 77 return -ENODEV; 78 } 79 80 mf = kzalloc_obj(struct mf_device); 81 if (!mf) 82 return -ENOMEM; 83 84 mf->report = report; 85 set_bit(FF_RUMBLE, dev->ffbit); 86 87 error = input_ff_create_memless(dev, mf, mf_play); 88 if (error) { 89 kfree(mf); 90 return error; 91 } 92 93 mf->report->field[0]->value[0] = 0x00; 94 mf->report->field[0]->value[1] = 0x00; 95 hid_hw_request(hid, mf->report, HID_REQ_SET_REPORT); 96 97 return 0; 98 } 99 100 static int mf_probe(struct hid_device *hid, const struct hid_device_id *id) 101 { 102 int error; 103 104 dev_dbg(&hid->dev, "Mayflash HID hardware probe...\n"); 105 106 /* Apply quirks as needed */ 107 hid->quirks |= id->driver_data; 108 109 error = hid_parse(hid); 110 if (error) { 111 hid_err(hid, "HID parse failed.\n"); 112 return error; 113 } 114 115 error = hid_hw_start(hid, HID_CONNECT_DEFAULT); 116 if (error) { 117 hid_err(hid, "HID hw start failed\n"); 118 return error; 119 } 120 121 return 0; 122 } 123 static const struct hid_device_id mf_devices[] = { 124 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_PS3), 125 .driver_data = HID_QUIRK_MULTI_INPUT }, 126 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_DOLPHINBAR), 127 .driver_data = HID_QUIRK_MULTI_INPUT }, 128 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE1), 129 .driver_data = HID_QUIRK_MULTI_INPUT }, 130 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE2), 131 .driver_data = 0 }, /* No quirk required */ 132 { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, USB_DEVICE_ID_DRAGONRISE_GAMECUBE3), 133 .driver_data = HID_QUIRK_MULTI_INPUT }, 134 { } 135 }; 136 MODULE_DEVICE_TABLE(hid, mf_devices); 137 138 static struct hid_driver mf_driver = { 139 .name = "hid_mf", 140 .id_table = mf_devices, 141 .probe = mf_probe, 142 .input_configured = mf_input_configured, 143 }; 144 module_hid_driver(mf_driver); 145 146 MODULE_DESCRIPTION("Force feedback support for Mayflash game controller adapters."); 147 MODULE_LICENSE("GPL"); 148