Skip to content

Commit 8430e94

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 d1cd33e commit 8430e94

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

166166

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

169184
### 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
@@ -696,20 +696,6 @@ static u8 *xpadneo_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int
696696
return rdesc;
697697
}
698698

699-
static void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
700-
{
701-
if (xdata->mouse_mode) {
702-
xdata->mouse_mode = false;
703-
hid_info(xdata->hdev, "mouse mode disabled\n");
704-
} else {
705-
xdata->mouse_mode = true;
706-
hid_info(xdata->hdev, "mouse mode enabled\n");
707-
}
708-
709-
/* Indicate that a request was made */
710-
xdata->profile_switched = true;
711-
}
712-
713699
static void xpadneo_switch_profile(struct xpadneo_devdata *xdata, const u8 profile,
714700
const bool emulated)
715701
{
@@ -841,6 +827,7 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
841827
return 0;
842828
default:
843829
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
830+
return 0;
844831
}
845832

846833
if (param_disable_deadzones) {
@@ -884,6 +871,9 @@ static int xpadneo_event(struct hid_device *hdev, struct hid_field *field,
884871
struct input_dev *gamepad = xdata->gamepad;
885872
struct input_dev *keyboard = xdata->keyboard;
886873

874+
if (xpadneo_mouse_event(xdata, usage, value))
875+
goto stop_processing;
876+
887877
if ((usage->type == EV_KEY) && (usage->code == BTN_PADDLES(0))) {
888878
if (gamepad && xdata->profile == 0) {
889879
/* report the paddles individually */
@@ -1169,6 +1159,10 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
11691159
if (ret)
11701160
return ret;
11711161

1162+
ret = xpadneo_init_mouse(xdata);
1163+
if (ret)
1164+
return ret;
1165+
11721166
ret = xpadneo_init_hw(hdev);
11731167
if (ret) {
11741168
hid_err(hdev, "hw init failed: %d\n", ret);
@@ -1180,6 +1174,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
11801174
if (ret)
11811175
hid_err(hdev, "could not initialize ff, continuing anyway\n");
11821176

1177+
timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
1178+
mod_timer(&xdata->mouse_timer, jiffies);
1179+
11831180
hid_info(hdev, "%s connected\n", xdata->battery.name);
11841181

11851182
return 0;
@@ -1215,6 +1212,7 @@ static void xpadneo_remove(struct hid_device *hdev)
12151212
hdev->product = xdata->original_product;
12161213
}
12171214

1215+
del_timer_sync(&xdata->mouse_timer);
12181216
cancel_delayed_work_sync(&xdata->ff_worker);
12191217

12201218
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 {
@@ -185,6 +193,10 @@ struct xpadneo_devdata {
185193

186194
extern int xpadneo_init_consumer(struct xpadneo_devdata *);
187195
extern int xpadneo_init_keyboard(struct xpadneo_devdata *);
196+
extern int xpadneo_init_mouse(struct xpadneo_devdata *);
188197
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
198+
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
199+
extern void xpadneo_mouse_report(struct timer_list *);
200+
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);
189201

190202
#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)