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 sjoyff_init(struct hid_device *hid) 52 { 53 struct sjoyff_device *sjoyff; 54 struct hid_report *report; 55 struct hid_input *hidinput; 56 struct list_head *report_list = 57 &hid->report_enum[HID_OUTPUT_REPORT].report_list; 58 struct list_head *report_ptr = report_list; 59 struct input_dev *dev; 60 int error; 61 62 if (list_empty(report_list)) { 63 hid_err(hid, "no output reports found\n"); 64 return -ENODEV; 65 } 66 67 list_for_each_entry(hidinput, &hid->inputs, list) { 68 report_ptr = report_ptr->next; 69 70 if (report_ptr == report_list) { 71 hid_err(hid, "required output report is missing\n"); 72 return -ENODEV; 73 } 74 75 report = list_entry(report_ptr, struct hid_report, list); 76 if (report->maxfield < 1) { 77 hid_err(hid, "no fields in the report\n"); 78 return -ENODEV; 79 } 80 81 if (report->field[0]->report_count < 3) { 82 hid_err(hid, "not enough values in the field\n"); 83 return -ENODEV; 84 } 85 86 sjoyff = kzalloc(sizeof(struct sjoyff_device), GFP_KERNEL); 87 if (!sjoyff) 88 return -ENOMEM; 89 90 dev = hidinput->input; 91 92 set_bit(FF_RUMBLE, dev->ffbit); 93 94 error = input_ff_create_memless(dev, sjoyff, hid_sjoyff_play); 95 if (error) { 96 kfree(sjoyff); 97 return error; 98 } 99 100 sjoyff->report = report; 101 sjoyff->report->field[0]->value[0] = 0x01; 102 sjoyff->report->field[0]->value[1] = 0x00; 103 sjoyff->report->field[0]->value[2] = 0x00; 104 hid_hw_request(hid, sjoyff->report, HID_REQ_SET_REPORT); 105 } 106 107 hid_info(hid, "Force feedback for SmartJoy PLUS PS2/USB adapter\n"); 108 109 return 0; 110 } 111 #else 112 static inline int sjoyff_init(struct hid_device *hid) 113 { 114 return 0; 115 } 116 #endif 117 118 static int sjoy_probe(struct hid_device *hdev, const struct hid_device_id *id) 119 { 120 int ret; 121 122 hdev->quirks |= id->driver_data; 123 124 ret = hid_parse(hdev); 125 if (ret) { 126 hid_err(hdev, "parse failed\n"); 127 goto err; 128 } 129 130 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 131 if (ret) { 132 hid_err(hdev, "hw start failed\n"); 133 goto err; 134 } 135 136 sjoyff_init(hdev); 137 138 return 0; 139 err: 140 return ret; 141 } 142 143 static const struct hid_device_id sjoy_devices[] = { 144 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO), 145 .driver_data = HID_QUIRK_NOGET }, 146 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO), 147 .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | 148 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 149 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO), 150 .driver_data = HID_QUIRK_MULTI_INPUT | HID_QUIRK_NOGET | 151 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 152 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) }, 153 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) }, 154 { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD), 155 .driver_data = HID_QUIRK_MULTI_INPUT | 156 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 157 { HID_USB_DEVICE(USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII), 158 .driver_data = HID_QUIRK_MULTI_INPUT | 159 HID_QUIRK_SKIP_OUTPUT_REPORTS }, 160 { } 161 }; 162 MODULE_DEVICE_TABLE(hid, sjoy_devices); 163 164 static struct hid_driver sjoy_driver = { 165 .name = "smartjoyplus", 166 .id_table = sjoy_devices, 167 .probe = sjoy_probe, 168 }; 169 module_hid_driver(sjoy_driver); 170 171 MODULE_DESCRIPTION("Force feedback support for SmartJoy PLUS PS2->USB adapter"); 172 MODULE_LICENSE("GPL"); 173 MODULE_AUTHOR("Jussi Kivilinna"); 174 175