Skip to content

Commit c6162a8

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 8c2e2af commit c6162a8

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
@@ -166,6 +166,21 @@ or Y while holding down the Xbox logo button. However, the following caveats app
166166
- If you hold the button for too long, the controller will turn off - we cannot prevent that.
167167

168168

169+
### Mouse profile support
170+
171+
The driver can switch to emulating a mouse (and limited keyboard) on all supported controllers. Press
172+
<key>Guide</key>+<key>Select</key> to switch to mouse mode or back to controller mode:
173+
174+
- Left stick moves the mouse pointer
175+
- Right stick can be used as a scrolling wheel/ball
176+
- Triggers for left and right mouse button
177+
- Shoulder buttons for back and forward button
178+
- D-pad for cursor movement
179+
- Menu to show on-screen keyboard (FIXME possible? KEY_KEYBOARD)
180+
- A for <key>Enter</key>
181+
- B for <key>Escape</key>
182+
183+
169184
## Getting started
170185

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

hid-xpadneo/src/hid-xpadneo.c

Lines changed: 12 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
{
@@ -904,6 +890,7 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
904890
return 0;
905891
default:
906892
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
893+
return 0;
907894
}
908895

909896
if (param_disable_deadzones) {
@@ -949,6 +936,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
949936
struct input_dev *gamepad = xdata->gamepad;
950937
struct input_dev *consumer = xdata->consumer;
951938

939+
if (xpadneo_mouse_event(xdata, usage, value))
940+
goto stop_processing;
941+
952942
if ((usage->type == EV_KEY) && (usage->code == BTN_PADDLES(0))) {
953943
if (gamepad && xdata->profile == 0) {
954944
/* report the paddles individually */
@@ -1231,6 +1221,10 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12311221
if (ret)
12321222
return ret;
12331223

1224+
ret = xpadneo_init_mouse(xdata);
1225+
if (ret)
1226+
return ret;
1227+
12341228
ret = xpadneo_init_hw(hdev);
12351229
if (ret) {
12361230
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1242,6 +1236,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12421236
if (ret)
12431237
hid_err(hdev, "could not initialize ff, continuing anyway\n");
12441238

1239+
timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
1240+
mod_timer(&xdata->mouse_timer, jiffies);
1241+
12451242
hid_info(hdev, "%s connected\n", xdata->battery.name);
12461243

12471244
return 0;
@@ -1277,6 +1274,7 @@ static void xpadneo_remove(struct hid_device *hdev)
12771274
hdev->product = xdata->original_product;
12781275
}
12791276

1277+
del_timer_sync(&xdata->mouse_timer);
12801278
cancel_delayed_work_sync(&xdata->ff_worker);
12811279

12821280
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"
@@ -131,7 +132,7 @@ struct xpadneo_devdata {
131132

132133
/* logical device interfaces */
133134
struct hid_device *hdev;
134-
struct input_dev *consumer, *gamepad, *keyboard;
135+
struct input_dev *consumer, *gamepad, *keyboard, *mouse;
135136
short int missing_reported;
136137

137138
/* revert fixups on removal */
@@ -148,6 +149,13 @@ struct xpadneo_devdata {
148149

149150
/* mouse mode */
150151
bool mouse_mode;
152+
struct timer_list mouse_timer;
153+
struct {
154+
s32 rel_x, rel_y, wheel_x, wheel_y;
155+
struct {
156+
bool left, right;
157+
} analog_button;
158+
} mouse_state;
151159

152160
/* trigger scale */
153161
struct {
@@ -184,6 +192,10 @@ struct xpadneo_devdata {
184192
};
185193

186194
extern int xpadneo_init_consumer(struct xpadneo_devdata *);
195+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
187196
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
197+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
198+
extern void xpadneo_mouse_report(struct timer_list *);
199+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
188200

189201
#endif

hid-xpadneo/src/xpadneo/consumer.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ extern int xpadneo_init_consumer(struct xpadneo_devdata *xdata)
2121
/* enable key events for consumer control */
2222
input_set_capability(xdata->consumer, EV_KEY, BTN_XBOX);
2323
input_set_capability(xdata->consumer, EV_KEY, BTN_SHARE);
24+
input_set_capability(xdata->consumer, EV_KEY, KEY_KEYBOARD);
2425

2526
if (synth) {
2627
ret = input_register_device(xdata->consumer);

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)