1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Force feedback support for Betop based devices
*
* The devices are distributed under various names and the same USB device ID
* can be used in both adapters and actual game controllers.
*
* 0x11c2:0x2208 "BTP2185 BFM mode Joystick"
* - tested with BTP2185 BFM Mode.
*
* 0x11C0:0x5506 "BTP2185 PC mode Joystick"
* - tested with BTP2185 PC Mode.
*
* 0x8380:0x1850 "BTP2185 V2 PC mode USB Gamepad"
* - tested with BTP2185 PC Mode with another version.
*
* 0x20bc:0x5500 "BTP2185 V2 BFM mode Joystick"
* - tested with BTP2171s.
* Copyright (c) 2014 Huang Bo <huangbobupt@163.com>
*/
/*
*/
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/hid.h>
#include "hid-ids.h"
struct betopff_device {
struct hid_report *report;
};
static int hid_betopff_play(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct betopff_device *betopff = data;
__u16 left, right;
left = effect->u.rumble.strong_magnitude;
right = effect->u.rumble.weak_magnitude;
betopff->report->field[2]->value[0] = left / 256;
betopff->report->field[3]->value[0] = right / 256;
hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT);
return 0;
}
static int betop_input_configured(struct hid_device *hid, struct hid_input *hidinput)
{
struct betopff_device *betopff;
struct hid_report *report;
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev = hidinput->input;
int error;
int i, j;
if (!list_is_first(&hidinput->list, &hid->inputs))
return 0;
report = list_first_entry_or_null(report_list, struct hid_report, list);
if (!report) {
hid_err(hid, "no output reports found\n");
return -ENODEV;
}
/*
* Actually there are 4 fields for 4 Bytes as below:
* -----------------------------------------
* Byte0 Byte1 Byte2 Byte3
* 0x00 0x00 left_motor right_motor
* -----------------------------------------
* Do init them with default value.
*/
if (report->maxfield < 4) {
hid_err(hid, "not enough fields in the report: %d\n",
report->maxfield);
return -ENODEV;
}
for (i = 0; i < report->maxfield; i++) {
if (report->field[i]->report_count < 1) {
hid_err(hid, "no values in the field\n");
return -ENODEV;
}
for (j = 0; j < report->field[i]->report_count; j++) {
report->field[i]->value[j] = 0x00;
}
}
betopff = kzalloc_obj(*betopff);
if (!betopff)
return -ENOMEM;
betopff->report = report;
set_bit(FF_RUMBLE, dev->ffbit);
error = input_ff_create_memless(dev, betopff, hid_betopff_play);
if (error) {
kfree(betopff);
return error;
}
hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT);
hid_info(hid, "Force feedback for betop devices by huangbo <huangbobupt@163.com>\n");
return 0;
}
static int betop_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret;
if (id->driver_data)
hdev->quirks |= HID_QUIRK_MULTI_INPUT;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
return ret;
}
return 0;
}
static const struct hid_device_id betop_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185PC, 0x5506) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2PC, 0x1850) },
{ HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2BFM, 0x5500) },
{ }
};
MODULE_DEVICE_TABLE(hid, betop_devices);
static struct hid_driver betop_driver = {
.name = "betop",
.id_table = betop_devices,
.probe = betop_probe,
.input_configured = betop_input_configured,
};
module_hid_driver(betop_driver);
MODULE_DESCRIPTION("Force feedback support for Betop based devices");
MODULE_LICENSE("GPL");
|