Skip to content

Commit

Permalink
[WIP] xpadneo, mouse: Implement mouse support
Browse files Browse the repository at this point in the history
Co-authored-by: Jacob Essex <[email protected]>
Co-authored-by: Florian Dollinger <[email protected]>
Closes: atar-axis#160
Closes: atar-axis#105
Closes: atar-axis#99
Signed-off-by: Kai Krakow <[email protected]>
  • Loading branch information
3 people committed Apr 10, 2021
1 parent 7de494e commit c72d219
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 16 deletions.
15 changes: 15 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ or Y while holding down the Xbox logo button. However, the following caveats app
- If you hold the button for too long, the controller will turn off - we cannot prevent that.


### Mouse profile support

The driver can switch to emulating a mouse (and limited keyboard) on all supported controllers. Press
<key>Guide</key>+<key>Select</key> to switch to mouse mode or back to controller mode:

- Left stick moves the mouse pointer
- Right stick can be used as a scrolling wheel/ball
- Triggers for left and right mouse button
- Shoulder buttons for back and forward button
- D-pad for cursor movement
- Menu to show on-screen keyboard (FIXME possible? KEY_KEYBOARD)
- A for <key>Enter</key>
- B for <key>Escape</key>


## Getting started

### Prerequisites
Expand Down
2 changes: 1 addition & 1 deletion hid-xpadneo/src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ hid-xpadneo-y += xpadneo.o
$(obj)/xpadneo.c: $(src)/hid-xpadneo.c
cp $< $@

hid-xpadneo-y += xpadneo/core.o
hid-xpadneo-y += xpadneo/core.o xpadneo/mouse.o
29 changes: 15 additions & 14 deletions hid-xpadneo/src/hid-xpadneo.c
Original file line number Diff line number Diff line change
Expand Up @@ -759,20 +759,6 @@ static u8 *xpadneo_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int
return rdesc;
}

static void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
{
if (xdata->mouse_mode) {
xdata->mouse_mode = false;
hid_info(xdata->hdev, "mouse mode disabled\n");
} else {
xdata->mouse_mode = true;
hid_info(xdata->hdev, "mouse mode enabled\n");
}

/* Indicate that a request was made */
xdata->profile_switched = true;
}

static void xpadneo_switch_profile(struct xpadneo_devdata *xdata, const u8 profile,
const bool emulated)
{
Expand Down Expand Up @@ -894,13 +880,15 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h
xdata->consumer = hi->input;
input_set_capability(hi->input, EV_KEY, BTN_XBOX);
input_set_capability(hi->input, EV_KEY, BTN_SHARE);
input_set_capability(hi->input, EV_KEY, KEY_KEYBOARD);
return 0;
case 0xFF000005:
hid_info(hdev, "mapping profiles detected\n");
xdata->quirks |= XPADNEO_QUIRK_USE_HW_PROFILES;
return 0;
default:
hid_warn(hdev, "unhandled input application 0x%x\n", hi->application);
return 0;
}

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

if (xpadneo_mouse_event(xdata, usage, value))
goto stop_processing;

if (usage->type == EV_ABS) {
switch (usage->code) {
case ABS_X:
Expand Down Expand Up @@ -1205,6 +1196,12 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
return ret;
}

if (!xdata->mouse) {
ret = xpadneo_init_mouse(xdata);
if (ret)
return ret;
}

ret = xpadneo_init_hw(hdev);
if (ret) {
hid_err(hdev, "hw init failed: %d\n", ret);
Expand All @@ -1216,6 +1213,9 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
if (ret)
hid_err(hdev, "could not initialize ff, continuing anyway\n");

timer_setup(&xdata->mouse_timer, xpadneo_mouse_report, 0);
mod_timer(&xdata->mouse_timer, jiffies);

hid_info(hdev, "%s connected\n", xdata->battery.name);

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

hid_hw_close(hdev);

del_timer_sync(&xdata->mouse_timer);
cancel_delayed_work_sync(&xdata->ff_worker);

kfree(xdata->battery.name);
Expand Down
14 changes: 13 additions & 1 deletion hid-xpadneo/src/xpadneo.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define XPADNEO_H_FILE

#include <linux/hid.h>
#include <linux/timer.h>
#include <linux/version.h>

#include "hid-ids.h"
Expand Down Expand Up @@ -130,7 +131,7 @@ struct xpadneo_devdata {

/* logical device interfaces */
struct hid_device *hdev;
struct input_dev *consumer, *gamepad, *keyboard;
struct input_dev *consumer, *gamepad, *keyboard, *mouse;
short int missing_reported;

/* quirk flags */
Expand All @@ -143,6 +144,13 @@ struct xpadneo_devdata {

/* mouse mode */
bool mouse_mode;
struct timer_list mouse_timer;
struct {
s32 rel_x, rel_y, wheel_x, wheel_y;
struct {
bool left, right;
} analog_button;
} mouse_state;

/* trigger scale */
struct {
Expand Down Expand Up @@ -178,6 +186,10 @@ struct xpadneo_devdata {
void *output_report_dmabuf;
};

extern int xpadneo_init_mouse(struct xpadneo_devdata *);
extern int xpadneo_init_synthetic(struct xpadneo_devdata *, char *, struct input_dev **);
extern int xpadneo_mouse_event(struct xpadneo_devdata *, struct hid_usage *, __s32);
extern void xpadneo_mouse_report(struct timer_list *);
extern void xpadneo_toggle_mouse(struct xpadneo_devdata *);

#endif
144 changes: 144 additions & 0 deletions hid-xpadneo/src/xpadneo/mouse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* xpadneo mouse driver
*
* Copyright (c) 2021 Kai Krakow <[email protected]>
*/

#include "../xpadneo.h"

extern void xpadneo_toggle_mouse(struct xpadneo_devdata *xdata)
{
if (!xdata->mouse) {
xdata->mouse_mode = false;
hid_info(xdata->hdev, "mouse not available\n");
} else if (xdata->mouse_mode) {
xdata->mouse_mode = false;
hid_info(xdata->hdev, "mouse mode disabled\n");
} else {
xdata->mouse_mode = true;
hid_info(xdata->hdev, "mouse mode enabled\n");
}

/* Indicate that a request was made */
xdata->profile_switched = true;
}

#define mouse_report_rel(a,v) if((v)!=0)input_report_rel(mouse,a,v)
extern void xpadneo_mouse_report(struct timer_list *t)
{
struct xpadneo_devdata *xdata = from_timer(xdata, t, mouse_timer);
struct input_dev *mouse = xdata->mouse;

mod_timer(&xdata->mouse_timer, jiffies + msecs_to_jiffies(10));

if (xdata->mouse_mode) {
mouse_report_rel(REL_X, xdata->mouse_state.rel_x);
mouse_report_rel(REL_Y, xdata->mouse_state.rel_y);
mouse_report_rel(REL_HWHEEL, xdata->mouse_state.wheel_x);
mouse_report_rel(REL_WHEEL, xdata->mouse_state.wheel_y);
input_sync(xdata->mouse);
}

}

#define rescale_axis(v,d) (((v)<(d)&&(v)>-(d))?0:(32768*((v)>0?(v)-(d):(v)+(d))/(32768-(d))))
extern int xpadneo_mouse_event(struct xpadneo_devdata *xdata, struct hid_usage *usage, __s32 value)
{
if (!xdata->mouse_mode)
return 0;

if (usage->type == EV_ABS) {
switch (usage->code) {
case ABS_X:
xdata->mouse_state.rel_x = rescale_axis(value - 32768, 3072) / 2048;
return 1;
case ABS_Y:
xdata->mouse_state.rel_y = rescale_axis(value - 32768, 3072) / 2048;
return 1;
case ABS_RX:
xdata->mouse_state.wheel_x = rescale_axis(value - 32768, 3072) / 8192;
return 1;
case ABS_RY:
xdata->mouse_state.wheel_y = rescale_axis(value - 32768, 3072) / 8192;
return 1;
case ABS_Z:
if (xdata->mouse_state.analog_button.left && value < 384) {
xdata->mouse_state.analog_button.left = false;
input_report_key(xdata->mouse, BTN_LEFT, 0);
input_sync(xdata->mouse);
} else if (!xdata->mouse_state.analog_button.left && value > 640) {
xdata->mouse_state.analog_button.left = true;
input_report_key(xdata->mouse, BTN_LEFT, 1);
input_sync(xdata->mouse);
}
return 1;
case ABS_RZ:
if (xdata->mouse_state.analog_button.right && value < 384) {
xdata->mouse_state.analog_button.right = false;
input_report_key(xdata->mouse, BTN_RIGHT, 0);
input_sync(xdata->mouse);
} else if (!xdata->mouse_state.analog_button.right && value > 640) {
xdata->mouse_state.analog_button.right = true;
input_report_key(xdata->mouse, BTN_RIGHT, 1);
input_sync(xdata->mouse);
}
return 1;
}
} else if (usage->type == EV_KEY) {
switch (usage->code) {
case BTN_TL:
input_report_key(xdata->mouse, BTN_SIDE, value);
input_sync(xdata->mouse);
return 1;
case BTN_TR:
input_report_key(xdata->mouse, BTN_EXTRA, value);
input_sync(xdata->mouse);
return 1;
case BTN_START:
if (xdata->consumer) {
input_report_key(xdata->consumer, KEY_KEYBOARD, value);
input_sync(xdata->consumer);
}
return 1;
}
}

return 0;
}

extern int xpadneo_init_mouse(struct xpadneo_devdata *xdata)
{
struct hid_device *hdev = xdata->hdev;
int ret;

ret = xpadneo_init_synthetic(xdata, "Mouse", &xdata->mouse);
if (ret || !xdata->mouse)
return ret;

/* enable relative events for mouse emulation */
__set_bit(EV_REL, xdata->mouse->evbit);
__set_bit(REL_X, xdata->mouse->relbit);
__set_bit(REL_Y, xdata->mouse->relbit);
__set_bit(REL_HWHEEL, xdata->mouse->relbit);
__set_bit(REL_WHEEL, xdata->mouse->relbit);

/* enable button events for mouse emulation */
__set_bit(EV_KEY, xdata->mouse->evbit);
__set_bit(BTN_LEFT, xdata->mouse->keybit);
__set_bit(BTN_RIGHT, xdata->mouse->keybit);
__set_bit(BTN_MIDDLE, xdata->mouse->keybit);
__set_bit(BTN_SIDE, xdata->mouse->keybit);
__set_bit(BTN_EXTRA, xdata->mouse->keybit);
__set_bit(BTN_FORWARD, xdata->mouse->keybit);
__set_bit(BTN_BACK, xdata->mouse->keybit);
__set_bit(BTN_TASK, xdata->mouse->keybit);

ret = input_register_device(xdata->mouse);
if (ret) {
hid_err(hdev, "failed to register mouse\n");
return ret;
}

hid_info(hdev, "mouse added\n");
return 0;
}

0 comments on commit c72d219

Please sign in to comment.