18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Force feedback support for ACRUX game controllers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * From what I have gathered, these devices are mass produced in China 68c2ecf20Sopenharmony_ci * by several vendors. They often share the same design as the original 78c2ecf20Sopenharmony_ci * Xbox 360 controller. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * 1a34:0802 "ACRUX USB GAMEPAD 8116" 108c2ecf20Sopenharmony_ci * - tested with an EXEQ EQ-PCU-02090 game controller. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (c) 2010 Sergei Kolzun <x0r@dv-life.ru> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/input.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/hid.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "hid-ids.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_HID_ACRUX_FF 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct axff_device { 288c2ecf20Sopenharmony_ci struct hid_report *report; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int axff_play(struct input_dev *dev, void *data, struct ff_effect *effect) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct hid_device *hid = input_get_drvdata(dev); 348c2ecf20Sopenharmony_ci struct axff_device *axff = data; 358c2ecf20Sopenharmony_ci struct hid_report *report = axff->report; 368c2ecf20Sopenharmony_ci int field_count = 0; 378c2ecf20Sopenharmony_ci int left, right; 388c2ecf20Sopenharmony_ci int i, j; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci left = effect->u.rumble.strong_magnitude; 418c2ecf20Sopenharmony_ci right = effect->u.rumble.weak_magnitude; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci dbg_hid("called with 0x%04x 0x%04x", left, right); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci left = left * 0xff / 0xffff; 468c2ecf20Sopenharmony_ci right = right * 0xff / 0xffff; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (i = 0; i < report->maxfield; i++) { 498c2ecf20Sopenharmony_ci for (j = 0; j < report->field[i]->report_count; j++) { 508c2ecf20Sopenharmony_ci report->field[i]->value[j] = 518c2ecf20Sopenharmony_ci field_count % 2 ? right : left; 528c2ecf20Sopenharmony_ci field_count++; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci dbg_hid("running with 0x%02x 0x%02x", left, right); 578c2ecf20Sopenharmony_ci hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int axff_init(struct hid_device *hid) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct axff_device *axff; 658c2ecf20Sopenharmony_ci struct hid_report *report; 668c2ecf20Sopenharmony_ci struct hid_input *hidinput; 678c2ecf20Sopenharmony_ci struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list; 688c2ecf20Sopenharmony_ci struct input_dev *dev; 698c2ecf20Sopenharmony_ci int field_count = 0; 708c2ecf20Sopenharmony_ci int i, j; 718c2ecf20Sopenharmony_ci int error; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (list_empty(&hid->inputs)) { 748c2ecf20Sopenharmony_ci hid_err(hid, "no inputs found\n"); 758c2ecf20Sopenharmony_ci return -ENODEV; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci hidinput = list_first_entry(&hid->inputs, struct hid_input, list); 788c2ecf20Sopenharmony_ci dev = hidinput->input; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (list_empty(report_list)) { 818c2ecf20Sopenharmony_ci hid_err(hid, "no output reports found\n"); 828c2ecf20Sopenharmony_ci return -ENODEV; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci report = list_first_entry(report_list, struct hid_report, list); 868c2ecf20Sopenharmony_ci for (i = 0; i < report->maxfield; i++) { 878c2ecf20Sopenharmony_ci for (j = 0; j < report->field[i]->report_count; j++) { 888c2ecf20Sopenharmony_ci report->field[i]->value[j] = 0x00; 898c2ecf20Sopenharmony_ci field_count++; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (field_count < 4 && hid->product != 0xf705) { 948c2ecf20Sopenharmony_ci hid_err(hid, "not enough fields in the report: %d\n", 958c2ecf20Sopenharmony_ci field_count); 968c2ecf20Sopenharmony_ci return -ENODEV; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci axff = kzalloc(sizeof(struct axff_device), GFP_KERNEL); 1008c2ecf20Sopenharmony_ci if (!axff) 1018c2ecf20Sopenharmony_ci return -ENOMEM; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci set_bit(FF_RUMBLE, dev->ffbit); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci error = input_ff_create_memless(dev, axff, axff_play); 1068c2ecf20Sopenharmony_ci if (error) 1078c2ecf20Sopenharmony_ci goto err_free_mem; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci axff->report = report; 1108c2ecf20Sopenharmony_ci hid_hw_request(hid, axff->report, HID_REQ_SET_REPORT); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci hid_info(hid, "Force Feedback for ACRUX game controllers by Sergei Kolzun <x0r@dv-life.ru>\n"); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cierr_free_mem: 1178c2ecf20Sopenharmony_ci kfree(axff); 1188c2ecf20Sopenharmony_ci return error; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci#else 1218c2ecf20Sopenharmony_cistatic inline int axff_init(struct hid_device *hid) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci#endif 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic int ax_probe(struct hid_device *hdev, const struct hid_device_id *id) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int error; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci dev_dbg(&hdev->dev, "ACRUX HID hardware probe...\n"); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci error = hid_parse(hdev); 1348c2ecf20Sopenharmony_ci if (error) { 1358c2ecf20Sopenharmony_ci hid_err(hdev, "parse failed\n"); 1368c2ecf20Sopenharmony_ci return error; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 1408c2ecf20Sopenharmony_ci if (error) { 1418c2ecf20Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 1428c2ecf20Sopenharmony_ci return error; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci error = axff_init(hdev); 1468c2ecf20Sopenharmony_ci if (error) { 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Do not fail device initialization completely as device 1498c2ecf20Sopenharmony_ci * may still be partially operable, just warn. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci hid_warn(hdev, 1528c2ecf20Sopenharmony_ci "Failed to enable force feedback support, error: %d\n", 1538c2ecf20Sopenharmony_ci error); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * We need to start polling device right away, otherwise 1588c2ecf20Sopenharmony_ci * it will go into a coma. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci error = hid_hw_open(hdev); 1618c2ecf20Sopenharmony_ci if (error) { 1628c2ecf20Sopenharmony_ci dev_err(&hdev->dev, "hw open failed\n"); 1638c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 1648c2ecf20Sopenharmony_ci return error; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void ax_remove(struct hid_device *hdev) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci hid_hw_close(hdev); 1738c2ecf20Sopenharmony_ci hid_hw_stop(hdev); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct hid_device_id ax_devices[] = { 1778c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0x0802), }, 1788c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ACRUX, 0xf705), }, 1798c2ecf20Sopenharmony_ci { } 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, ax_devices); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic struct hid_driver ax_driver = { 1848c2ecf20Sopenharmony_ci .name = "acrux", 1858c2ecf20Sopenharmony_ci .id_table = ax_devices, 1868c2ecf20Sopenharmony_ci .probe = ax_probe, 1878c2ecf20Sopenharmony_ci .remove = ax_remove, 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_cimodule_hid_driver(ax_driver); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sergei Kolzun"); 1928c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Force feedback support for ACRUX game controllers"); 1938c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 194