1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Force feedback support for EMS Trio Linker Plus II |
4 | * |
5 | * Copyright (c) 2010 Ignaz Forster <ignaz.forster@gmx.de> |
6 | */ |
7 | |
8 | /* |
9 | */ |
10 | |
11 | |
12 | #include <linux/hid.h> |
13 | #include <linux/input.h> |
14 | #include <linux/module.h> |
15 | |
16 | #include "hid-ids.h" |
17 | |
18 | struct emsff_device { |
19 | struct hid_report *report; |
20 | }; |
21 | |
22 | static int emsff_play(struct input_dev *dev, void *data, |
23 | struct ff_effect *effect) |
24 | { |
25 | struct hid_device *hid = input_get_drvdata(dev); |
26 | struct emsff_device *emsff = data; |
27 | int weak, strong; |
28 | |
29 | weak = effect->u.rumble.weak_magnitude; |
30 | strong = effect->u.rumble.strong_magnitude; |
31 | |
32 | dbg_hid("called with 0x%04x 0x%04x\n" , strong, weak); |
33 | |
34 | weak = weak * 0xff / 0xffff; |
35 | strong = strong * 0xff / 0xffff; |
36 | |
37 | emsff->report->field[0]->value[1] = weak; |
38 | emsff->report->field[0]->value[2] = strong; |
39 | |
40 | dbg_hid("running with 0x%02x 0x%02x\n" , strong, weak); |
41 | hid_hw_request(hdev: hid, report: emsff->report, reqtype: HID_REQ_SET_REPORT); |
42 | |
43 | return 0; |
44 | } |
45 | |
46 | static int emsff_init(struct hid_device *hid) |
47 | { |
48 | struct emsff_device *emsff; |
49 | struct hid_report *report; |
50 | struct hid_input *hidinput; |
51 | struct list_head *report_list = |
52 | &hid->report_enum[HID_OUTPUT_REPORT].report_list; |
53 | struct input_dev *dev; |
54 | int error; |
55 | |
56 | if (list_empty(head: &hid->inputs)) { |
57 | hid_err(hid, "no inputs found\n" ); |
58 | return -ENODEV; |
59 | } |
60 | hidinput = list_first_entry(&hid->inputs, struct hid_input, list); |
61 | dev = hidinput->input; |
62 | |
63 | if (list_empty(head: report_list)) { |
64 | hid_err(hid, "no output reports found\n" ); |
65 | return -ENODEV; |
66 | } |
67 | |
68 | report = list_first_entry(report_list, struct hid_report, list); |
69 | if (report->maxfield < 1) { |
70 | hid_err(hid, "no fields in the report\n" ); |
71 | return -ENODEV; |
72 | } |
73 | |
74 | if (report->field[0]->report_count < 7) { |
75 | hid_err(hid, "not enough values in the field\n" ); |
76 | return -ENODEV; |
77 | } |
78 | |
79 | emsff = kzalloc(size: sizeof(struct emsff_device), GFP_KERNEL); |
80 | if (!emsff) |
81 | return -ENOMEM; |
82 | |
83 | set_bit(FF_RUMBLE, addr: dev->ffbit); |
84 | |
85 | error = input_ff_create_memless(dev, data: emsff, play_effect: emsff_play); |
86 | if (error) { |
87 | kfree(objp: emsff); |
88 | return error; |
89 | } |
90 | |
91 | emsff->report = report; |
92 | emsff->report->field[0]->value[0] = 0x01; |
93 | emsff->report->field[0]->value[1] = 0x00; |
94 | emsff->report->field[0]->value[2] = 0x00; |
95 | emsff->report->field[0]->value[3] = 0x00; |
96 | emsff->report->field[0]->value[4] = 0x00; |
97 | emsff->report->field[0]->value[5] = 0x00; |
98 | emsff->report->field[0]->value[6] = 0x00; |
99 | hid_hw_request(hdev: hid, report: emsff->report, reqtype: HID_REQ_SET_REPORT); |
100 | |
101 | hid_info(hid, "force feedback for EMS based devices by Ignaz Forster <ignaz.forster@gmx.de>\n" ); |
102 | |
103 | return 0; |
104 | } |
105 | |
106 | static int ems_probe(struct hid_device *hdev, const struct hid_device_id *id) |
107 | { |
108 | int ret; |
109 | |
110 | ret = hid_parse(hdev); |
111 | if (ret) { |
112 | hid_err(hdev, "parse failed\n" ); |
113 | goto err; |
114 | } |
115 | |
116 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); |
117 | if (ret) { |
118 | hid_err(hdev, "hw start failed\n" ); |
119 | goto err; |
120 | } |
121 | |
122 | ret = emsff_init(hid: hdev); |
123 | if (ret) { |
124 | dev_err(&hdev->dev, "force feedback init failed\n" ); |
125 | hid_hw_stop(hdev); |
126 | goto err; |
127 | } |
128 | |
129 | return 0; |
130 | err: |
131 | return ret; |
132 | } |
133 | |
134 | static const struct hid_device_id ems_devices[] = { |
135 | { HID_USB_DEVICE(USB_VENDOR_ID_EMS, USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II) }, |
136 | { } |
137 | }; |
138 | MODULE_DEVICE_TABLE(hid, ems_devices); |
139 | |
140 | static struct hid_driver ems_driver = { |
141 | .name = "hkems" , |
142 | .id_table = ems_devices, |
143 | .probe = ems_probe, |
144 | }; |
145 | module_hid_driver(ems_driver); |
146 | |
147 | MODULE_LICENSE("GPL" ); |
148 | |
149 | |