Skip to content

Commit 7558671

Browse files
kakraJacob Essexatar-axis
committed
[WIP] xpadneo, mouse: Implement mouse support
Co-authored-by: Jacob Essex <git@jacobessex.com> Co-authored-by: Florian Dollinger <dollinger.florian@gmx.de> Closes: atar-axis#160 Closes: atar-axis#105 Closes: atar-axis#99 Signed-off-by: Kai Krakow <kai@kaishome.de>
1 parent 08106d8 commit 7558671

File tree

6 files changed

+192
-16
lines changed

6 files changed

+192
-16
lines changed

docs/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ or Y while holding down the Xbox logo button. However, the following caveats app
218218
- If you hold the button for too long, the controller will turn off - we cannot prevent that.
219219

220220

221+
### Mouse Profile Support
222+
223+
The driver can switch to emulating a mouse (and limited keyboard) on all supported controllers. Press
224+
<key>Guide</key>+<key>Select</key> to switch to mouse mode or back to controller mode:
225+
226+
- Left stick moves the mouse pointer
227+
- Right stick can be used as a scrolling wheel/ball
228+
- Triggers for left and right mouse button
229+
- Shoulder buttons for back and forward button
230+
- D-pad for cursor movement
231+
- Menu to show on-screen keyboard (FIXME possible? KEY_KEYBOARD)
232+
- A for <key>Enter</key>
233+
- B for <key>Escape</key>
234+
235+
221236
## Getting Started
222237

223238
### Distribution Packages

hid-xpadneo/src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ hid-xpadneo-y += xpadneo.o
99
$(obj)/xpadneo.c: $(src)/hid-xpadneo.c
1010
cp $< $@
1111

12-
hid-xpadneo-y += xpadneo/core.o xpadneo/consumer.o xpadneo/keyboard.o
12+
hid-xpadneo-y += xpadneo/core.o xpadneo/consumer.o xpadneo/keyboard.o xpadneo/mouse.o

hid-xpadneo/src/hid-xpadneo.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -710,20 +710,6 @@ static u8 *xpadneo_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int
710710
return rdesc;
711711
}
712712

713-
static void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
714-
{
715-
if (xdata->mouse_mode) {
716-
xdata->mouse_mode = false;
717-
hid_info(xdata->hdev, "mouse mode disabled\n");
718-
} else {
719-
xdata->mouse_mode = true;
720-
hid_info(xdata->hdev, "mouse mode enabled\n");
721-
}
722-
723-
/* Indicate that a request was made */
724-
xdata->profile_switched = true;
725-
}
726-
727713
static void xpadneo_switch_profile(struct xpadneo_devdata *xdata, const u8 profile,
728714
const bool emulated)
729715
{
@@ -862,6 +848,7 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
862848
return 0;
863849
default:
864850
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
851+
return 0;
865852
}
866853

867854
if (param_disable_deadzones) {
@@ -911,6 +898,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
911898
struct input_dev *gamepad = xdata->gamepad;
912899
struct input_dev *keyboard = xdata->keyboard;
913900

901+
if (xpadneo_mouse_event(xdata, usage, value))
902+
goto stop_processing;
903+
914904
if ((usage->type == EV_KEY) && (usage->code == BTN_PADDLES(0))) {
915905
if (gamepad && xdata->profile == 0) {
916906
/* report the paddles individually */
@@ -1197,6 +1187,10 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
11971187
if (ret)
11981188
return ret;
11991189

1190+
ret = xpadneo_init_mouse(xdata);
1191+
if (ret)
1192+
return ret;
1193+
12001194
ret = xpadneo_init_hw(hdev);
12011195
if (ret) {
12021196
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1208,6 +1202,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12081202
if (ret)
12091203
hid_err(hdev, "could not initialize ff, continuing anyway\n");
12101204

1205+
timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
1206+
mod_timer(&xdata->mouse_timer, jiffies);
1207+
12111208
hid_info(hdev, "%s connected\n", xdata->battery.name);
12121209

12131210
return 0;
@@ -1243,6 +1240,7 @@ static void xpadneo_remove(struct hid_device *hdev)
12431240
hdev->product = xdata->original_product;
12441241
}
12451242

1243+
del_timer_sync(&xdata->mouse_timer);
12461244
cancel_delayed_work_sync(&xdata->ff_worker);
12471245

12481246
kfree(xdata->battery.name);

hid-xpadneo/src/xpadneo.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define XPADNEO_H_FILE
1313

1414
#include <linux/hid.h>
15+
#include <linux/timer.h>
1516
#include <linux/version.h>
1617

1718
#include "hid-ids.h"
@@ -138,7 +139,7 @@ struct xpadneo_devdata {
138139

139140
/* logical device interfaces */
140141
struct hid_device *hdev;
141-
struct input_dev *consumer, *gamepad, *keyboard;
142+
struct input_dev *consumer, *gamepad, *keyboard, *mouse;
142143
short int missing_reported;
143144

144145
/* revert fixups on removal */
@@ -155,6 +156,13 @@ struct xpadneo_devdata {
155156

156157
/* mouse mode */
157158
bool mouse_mode;
159+
struct timer_list mouse_timer;
160+
struct {
161+
s32 rel_x, rel_y, wheel_x, wheel_y;
162+
struct {
163+
bool left, right;
164+
} analog_button;
165+
} mouse_state;
158166

159167
/* trigger scale */
160168
struct {
@@ -192,6 +200,10 @@ struct xpadneo_devdata {
192200

193201
extern int xpadneo_init_consumer(struct xpadneo_devdata *);
194202
extern int xpadneo_init_keyboard(struct xpadneo_devdata *);
203+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
195204
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
205+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
206+
extern void xpadneo_mouse_report(struct timer_list *);
207+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
196208

197209
#endif

hid-xpadneo/src/xpadneo/keyboard.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extern int xpadneo_init_keyboard(struct xpadneo_devdata *xdata)
2020

2121
/* enable key events for keyboard */
2222
input_set_capability(xdata->keyboard, EV_KEY, BTN_SHARE);
23+
input_set_capability(xdata->consumer, EV_KEY, KEY_KEYBOARD);
2324

2425
if (synth) {
2526
ret = input_register_device(xdata->keyboard);

hid-xpadneo/src/xpadneo/mouse.c

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* xpadneo mouse driver
3+
*
4+
* Copyright (c) 2021 Kai Krakow <kai@kaishome.de>
5+
*/
6+
7+
#include "../xpadneo.h"
8+
9+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
10+
{
11+
if (!xdata->mouse) {
12+
xdata->mouse_mode = false;
13+
hid_info(xdata->hdev, "mouse not available\n");
14+
} else if (xdata->mouse_mode) {
15+
xdata->mouse_mode = false;
16+
hid_info(xdata->hdev, "mouse mode disabled\n");
17+
} else {
18+
xdata->mouse_mode = true;
19+
hid_info(xdata->hdev, "mouse mode enabled\n");
20+
}
21+
22+
/* Indicate that a request was made */
23+
xdata->profile_switched = true;
24+
}
25+
26+
#define mouse_report_rel(a,v) if((v)!=0)input_report_rel(mouse,a,v)
27+
extern void xpadneo_mouse_report(struct timer_list *t)
28+
{
29+
struct xpadneo_devdata *xdata = from_timer(xdata, t, mouse_timer);
30+
struct input_dev *mouse = xdata->mouse;
31+
32+
mod_timer(&xdata->mouse_timer, jiffies + msecs_to_jiffies(10));
33+
34+
if (xdata->mouse_mode) {
35+
mouse_report_rel(REL_X, xdata->mouse_state.rel_x);
36+
mouse_report_rel(REL_Y, xdata->mouse_state.rel_y);
37+
mouse_report_rel(REL_HWHEEL, xdata->mouse_state.wheel_x);
38+
mouse_report_rel(REL_WHEEL, xdata->mouse_state.wheel_y);
39+
input_sync(xdata->mouse);
40+
}
41+
42+
}
43+
44+
#define rescale_axis(v,d) (((v)<(d)&&(v)>-(d))?0:(32768*((v)>0?(v)-(d):(v)+(d))/(32768-(d))))
45+
extern int xpadneo_mouse_event(struct xpadneo_devdata *xdata, struct hid_usage *usage, __s32 value)
46+
{
47+
if (!xdata->mouse_mode)
48+
return 0;
49+
50+
if (usage->type == EV_ABS) {
51+
switch (usage->code) {
52+
case ABS_X:
53+
xdata->mouse_state.rel_x = rescale_axis(value - 32768, 3072) / 2048;
54+
return 1;
55+
case ABS_Y:
56+
xdata->mouse_state.rel_y = rescale_axis(value - 32768, 3072) / 2048;
57+
return 1;
58+
case ABS_RX:
59+
xdata->mouse_state.wheel_x = rescale_axis(value - 32768, 3072) / 8192;
60+
return 1;
61+
case ABS_RY:
62+
xdata->mouse_state.wheel_y = rescale_axis(value - 32768, 3072) / 8192;
63+
return 1;
64+
case ABS_Z:
65+
if (xdata->mouse_state.analog_button.left && value < 384) {
66+
xdata->mouse_state.analog_button.left = false;
67+
input_report_key(xdata->mouse, BTN_LEFT, 0);
68+
input_sync(xdata->mouse);
69+
} else if (!xdata->mouse_state.analog_button.left && value > 640) {
70+
xdata->mouse_state.analog_button.left = true;
71+
input_report_key(xdata->mouse, BTN_LEFT, 1);
72+
input_sync(xdata->mouse);
73+
}
74+
return 1;
75+
case ABS_RZ:
76+
if (xdata->mouse_state.analog_button.right && value < 384) {
77+
xdata->mouse_state.analog_button.right = false;
78+
input_report_key(xdata->mouse, BTN_RIGHT, 0);
79+
input_sync(xdata->mouse);
80+
} else if (!xdata->mouse_state.analog_button.right && value > 640) {
81+
xdata->mouse_state.analog_button.right = true;
82+
input_report_key(xdata->mouse, BTN_RIGHT, 1);
83+
input_sync(xdata->mouse);
84+
}
85+
return 1;
86+
}
87+
} else if (usage->type == EV_KEY) {
88+
switch (usage->code) {
89+
case BTN_TL:
90+
input_report_key(xdata->mouse, BTN_SIDE, value);
91+
input_sync(xdata->mouse);
92+
return 1;
93+
case BTN_TR:
94+
input_report_key(xdata->mouse, BTN_EXTRA, value);
95+
input_sync(xdata->mouse);
96+
return 1;
97+
case BTN_START:
98+
if (xdata->consumer) {
99+
input_report_key(xdata->consumer, KEY_KEYBOARD, value);
100+
input_sync(xdata->consumer);
101+
}
102+
return 1;
103+
}
104+
}
105+
106+
return 0;
107+
}
108+
109+
extern int xpadneo_init_mouse(struct xpadneo_devdata *xdata)
110+
{
111+
struct hid_device *hdev = xdata->hdev;
112+
int ret, synth = 0;
113+
114+
if (!xdata->mouse) {
115+
synth = 1;
116+
ret = xpadneo_init_synthetic(xdata, "Mouse", &xdata->mouse);
117+
if (ret || !xdata->mouse)
118+
return ret;
119+
}
120+
121+
/* enable relative events for mouse emulation */
122+
__set_bit(EV_REL, xdata->mouse->evbit);
123+
__set_bit(REL_X, xdata->mouse->relbit);
124+
__set_bit(REL_Y, xdata->mouse->relbit);
125+
__set_bit(REL_HWHEEL, xdata->mouse->relbit);
126+
__set_bit(REL_WHEEL, xdata->mouse->relbit);
127+
128+
/* enable button events for mouse emulation */
129+
__set_bit(EV_KEY, xdata->mouse->evbit);
130+
__set_bit(BTN_LEFT, xdata->mouse->keybit);
131+
__set_bit(BTN_RIGHT, xdata->mouse->keybit);
132+
__set_bit(BTN_MIDDLE, xdata->mouse->keybit);
133+
__set_bit(BTN_SIDE, xdata->mouse->keybit);
134+
__set_bit(BTN_EXTRA, xdata->mouse->keybit);
135+
__set_bit(BTN_FORWARD, xdata->mouse->keybit);
136+
__set_bit(BTN_BACK, xdata->mouse->keybit);
137+
__set_bit(BTN_TASK, xdata->mouse->keybit);
138+
139+
if (synth) {
140+
ret = input_register_device(xdata->mouse);
141+
if (ret) {
142+
hid_err(hdev, "failed to register mouse\n");
143+
return ret;
144+
}
145+
146+
hid_info(hdev, "mouse added\n");
147+
}
148+
149+
return 0;
150+
}

0 commit comments

Comments
 (0)