1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Force feedback support for SmartJoy PLUS PS2->USB adapter 4 * 5 * Copyright (c) 2009 Jussi Kivilinna <jussi.kivilinna@mbnet.fi> 6 * 7 * Based of hid-pl.c and hid-gaff.c 8 * Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com> 9 * Copyright (c) 2008 Lukasz Lubojanski <lukasz@lubojanski.info> 10 */ 11 12 /* 13 */ 14 15 /* #define DEBUG */ 16 17 #include <linux/input.h> 18 #include <linux/slab.h> 19 #include <linux/hid.h> 20 #include <linux/module.h> 21 #include "hid-ids.h" 22 23 #ifdef CONFIG_SMARTJOYPLUS_FF 24 25 struct sjoyff_device { 26 struct hid_report *report; 27 }; 28 29 static int hid_sjoyff_play(struct input_dev *dev, void *data, 30 struct ff_effect *effect) 31 { 32 struct hid_device *hid = input_get_drvdata(dev); 33 struct sjoyff_device *sjoyff = data; 34 u32 left, right; 35 36 left = effect->u.rumble.strong_magnitude; 37 right = effect->u.rumble.weak_magnitude; 38 dev_dbg(&dev->dev, "called with 0x%08x 0x%08x\n", left, right); 39 40 left = left * 0xff / 0xffff; 41 right = (right != 0); /* on/off only */ 42 43 sjoyff->report->field[0]->value[1] = right; 44 sjoyff->report->field[0]->value[2] = left; 45 dev_dbg(&dev->dev, "running with 0x%02x 0x%02x\n", left, right); 46 hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); 47 48 return 0; 49 } 50 51 static int sjoy_input_configured(struct hid_device *hid, struct hid_input *hidinput) 52 { 53 struct sjoyff_device *sjoyff; 54 struct hid_report *report; 55 struct list_head *report_list = 56 &hid->report_enum[HID_OUTPUT_REPORT].report_list; 57 struct input_dev *dev = hidinput->input; 58 int error; 59 60 if (!list_is_first(&hidinput->list, &hid->inputs)) 61 return 0; 62 63 report = list_first_entry_or_null(report_list, struct hid_report, list); 64 if (!report) { 65 hid_err(hid, "no output reports found\n"); 66 return -ENODEV; 67 } 68 if (report->maxfield < 1) { 69 hid_err(hid, "no fields in the report\n"); 70 return -ENODEV; 71 } 72 73 if (report->field[0]->report_count < 3) { 74 hid_err(hid, "not enough values in the field\n"); 75 return -ENODEV; 76 } 77 78 sjoyff = kzalloc_obj(struct sjoyff_device); 79 if (!sjoyff) 80 return -ENOMEM; 81 82 set_bit(FF_RUMBLE, dev->ffbit); 83 84 sjoyff->report = report; 85 sjoyff->report->field[0]->value[0] = 0x01; 86 sjoyff->report->field[0]->value[1] = 0x00; 87 sjoyff->report->field[0]->value[2] = 0x00; 88 hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); 89 90 error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); 91 if (error) { 92 kfree(sjoyff); 93 return error; 94 } 95 96 return 0; 97 } 98 #else 99 static inline int sjoy_input_configured(struct hid_device *hid, 100 struct hid_input *hidinput) 101 { 102 return 0; 103 } 104 #endif 105 106 static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id) 107 { 108 int ret; 109 110 hdev->quirks |= id->driver_data; 111 112 ret = hid_parse(hdev); 113 if (ret) { 114 hid_err(hdev, "parse failed\n"); 115 return ret; 116 } 117 118 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 119 if (ret) { 120 hid_err(hdev, "hw start failed\n"); 121 return ret; 122 } 123 124 return 0; 125 } 126 127 static const struct hid_device_id sjoy_devices[] = { 128 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO), 129 .driver_data = HID_QUIRK_NOGET }, 130 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO), 131 .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | 132 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 133 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO), 134 .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | 135 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 136 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, 137 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, 138 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD), 139 .driver_data = HID_QUIRK_MULTI_INPUT | 140 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 141 { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII), 142 .driver_data = HID_QUIRK_MULTI_INPUT | 143 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 144 { } 145 }; 146 MODULE_DEVICE_TABLE(hid, sjoy_devices); 147 148 static struct hid_driver sjoy_driver = { 149 .name = "smartjoyplus", 150 .id_table = sjoy_devices, 151 .probe = sjoy_probe, 152 .input_configured = sjoy_input_configured, 153 }; 154 module_hid_driver(sjoy_driver); 155 156 MODULE_DESCRIPTION("Force feedback support for SmartJoy PLUS PS2->USB adapter"); 157 MODULE_LICENSE("GPL"); 158 MODULE_AUTHOR("Jussi Kivilinna"); 159 160