Skip to content

Commit f9b832b

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 6f84d2b commit f9b832b

File tree

5 files changed

+188
-16
lines changed

5 files changed

+188
-16
lines changed

docs/README.md

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

156156

157+
### Mouse profile support
158+
159+
The driver can switch to emulating a mouse (and limited keyboard) on all supported controllers. Press
160+
<key>Guide</key>+<key>Select</key> to switch to mouse mode or back to controller mode:
161+
162+
- Left stick moves the mouse pointer
163+
- Right stick can be used as a scrolling wheel/ball
164+
- Triggers for left and right mouse button
165+
- Shoulder buttons for back and forward button
166+
- D-pad for cursor movement
167+
- Menu to show on-screen keyboard (FIXME possible? KEY_KEYBOARD)
168+
- A for <key>Enter</key>
169+
- B for <key>Escape</key>
170+
171+
157172
## Getting started
158173

159174
### Prerequisites

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
12+
hid-xpadneo-y += xpadneo/core.o xpadneo/mouse.o

hid-xpadneo/src/hid-xpadneo.c

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -759,20 +759,6 @@ static u8 *xpadneo_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int
759759
return rdesc;
760760
}
761761

762-
static void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
763-
{
764-
if (xdata->mouse_mode) {
765-
xdata->mouse_mode = false;
766-
hid_info(xdata->hdev, "mouse mode disabled\n");
767-
} else {
768-
xdata->mouse_mode = true;
769-
hid_info(xdata->hdev, "mouse mode enabled\n");
770-
}
771-
772-
/* Indicate that a request was made */
773-
xdata->profile_switched = true;
774-
}
775-
776762
static void xpadneo_switch_profile(struct xpadneo_devdata *xdata, const u8 profile,
777763
const bool emulated)
778764
{
@@ -894,13 +880,15 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
894880
xdata->consumer = hi->input;
895881
input_set_capability(hi->input, EV_KEY, BTN_XBOX);
896882
input_set_capability(hi->input, EV_KEY, BTN_SHARE);
883+
input_set_capability(hi->input, EV_KEY, KEY_KEYBOARD);
897884
return 0;
898885
case 0xFF000005:
899886
hid_info(hdev, "mapping profiles detected\n");
900887
xdata->quirks |= XPADNEO_QUIRK_USE_HW_PROFILES;
901888
return 0;
902889
default:
903890
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
891+
return 0;
904892
}
905893

906894
if (param_disable_deadzones) {
@@ -939,6 +927,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
939927
struct input_dev *gamepad = xdata->gamepad;
940928
struct input_dev *consumer = xdata->consumer;
941929

930+
if (xpadneo_mouse_event(xdata, usage, value))
931+
goto stop_processing;
932+
942933
if (usage->type == EV_ABS) {
943934
switch (usage->code) {
944935
case ABS_X:
@@ -1205,6 +1196,12 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12051196
return ret;
12061197
}
12071198

1199+
if (!xdata->mouse) {
1200+
ret = xpadneo_init_mouse(xdata);
1201+
if (ret)
1202+
return ret;
1203+
}
1204+
12081205
ret = xpadneo_init_hw(hdev);
12091206
if (ret) {
12101207
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1216,6 +1213,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12161213
if (ret)
12171214
hid_err(hdev, "could not initialize ff, continuing anyway\n");
12181215

1216+
timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
1217+
mod_timer(&xdata->mouse_timer, jiffies);
1218+
12191219
hid_info(hdev, "%s connected\n", xdata->battery.name);
12201220

12211221
return 0;
@@ -1235,6 +1235,7 @@ static void xpadneo_remove(struct hid_device *hdev)
12351235

12361236
hid_hw_close(hdev);
12371237

1238+
del_timer_sync(&xdata->mouse_timer);
12381239
cancel_delayed_work_sync(&xdata->ff_worker);
12391240

12401241
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"
@@ -130,7 +131,7 @@ struct xpadneo_devdata {
130131

131132
/* logical device interfaces */
132133
struct hid_device *hdev;
133-
struct input_dev *consumer, *gamepad, *keyboard;
134+
struct input_dev *consumer, *gamepad, *keyboard, *mouse;
134135
short int missing_reported;
135136

136137
/* quirk flags */
@@ -143,6 +144,13 @@ struct xpadneo_devdata {
143144

144145
/* mouse mode */
145146
bool mouse_mode;
147+
struct timer_list mouse_timer;
148+
struct {
149+
s32 rel_x, rel_y, wheel_x, wheel_y;
150+
struct {
151+
bool left, right;
152+
} analog_button;
153+
} mouse_state;
146154

147155
/* trigger scale */
148156
struct {
@@ -178,6 +186,10 @@ struct xpadneo_devdata {
178186
void *output_report_dmabuf;
179187
};
180188

189+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
181190
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
191+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
192+
extern void xpadneo_mouse_report(struct timer_list *);
193+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
182194

183195
#endif

hid-xpadneo/src/xpadneo/mouse.c

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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;
113+
114+
ret = xpadneo_init_synthetic(xdata, "Mouse", &xdata->mouse);
115+
if (ret || !xdata->mouse)
116+
return ret;
117+
118+
/* enable relative events for mouse emulation */
119+
__set_bit(EV_REL, xdata->mouse->evbit);
120+
__set_bit(REL_X, xdata->mouse->relbit);
121+
__set_bit(REL_Y, xdata->mouse->relbit);
122+
__set_bit(REL_HWHEEL, xdata->mouse->relbit);
123+
__set_bit(REL_WHEEL, xdata->mouse->relbit);
124+
125+
/* enable button events for mouse emulation */
126+
__set_bit(EV_KEY, xdata->mouse->evbit);
127+
__set_bit(BTN_LEFT, xdata->mouse->keybit);
128+
__set_bit(BTN_RIGHT, xdata->mouse->keybit);
129+
__set_bit(BTN_MIDDLE, xdata->mouse->keybit);
130+
__set_bit(BTN_SIDE, xdata->mouse->keybit);
131+
__set_bit(BTN_EXTRA, xdata->mouse->keybit);
132+
__set_bit(BTN_FORWARD, xdata->mouse->keybit);
133+
__set_bit(BTN_BACK, xdata->mouse->keybit);
134+
__set_bit(BTN_TASK, xdata->mouse->keybit);
135+
136+
ret = input_register_device(xdata->mouse);
137+
if (ret) {
138+
hid_err(hdev, "failed to register mouse\n");
139+
return ret;
140+
}
141+
142+
hid_info(hdev, "mouse added\n");
143+
return 0;
144+
}

0 commit comments

Comments
 (0)