Skip to content

Commit 814208e

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 3e4faed commit 814208e

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
@@ -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 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
{
@@ -899,6 +885,7 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
899885
return 0;
900886
default:
901887
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
888+
return 0;
902889
}
903890

904891
if (param_disable_deadzones) {
@@ -937,6 +924,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
937924
struct input_dev *gamepad = xdata->gamepad;
938925
struct input_dev *consumer = xdata->consumer;
939926

927+
if (xpadneo_mouse_event(xdata, usage, value))
928+
goto stop_processing;
929+
940930
if (usage->type == EV_ABS) {
941931
switch (usage->code) {
942932
case ABS_X:
@@ -1207,6 +1197,10 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12071197
if (ret)
12081198
return ret;
12091199

1200+
ret = xpadneo_init_mouse(xdata);
1201+
if (ret)
1202+
return ret;
1203+
12101204
ret = xpadneo_init_hw(hdev);
12111205
if (ret) {
12121206
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1218,6 +1212,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
12181212
if (ret)
12191213
hid_err(hdev, "could not initialize ff, continuing anyway\n");
12201214

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

12231220
return 0;
@@ -1237,6 +1234,7 @@ static void xpadneo_remove(struct hid_device *hdev)
12371234

12381235
hid_hw_close(hdev);
12391236

1237+
del_timer_sync(&xdata->mouse_timer);
12401238
cancel_delayed_work_sync(&xdata->ff_worker);
12411239

12421240
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 {
@@ -179,6 +187,10 @@ struct xpadneo_devdata {
179187
};
180188

181189
extern int xpadneo_init_consumer(struct xpadneo_devdata *);
190+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
182191
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
192+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
193+
extern void xpadneo_mouse_report(struct timer_list *);
194+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
183195

184196
#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 button events for mouse emulation */
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)