xref: /freebsd/sys/dev/hid/xb360gp.c (revision d97d5c0ce89da10c1de150e0555774dec2ed7aed)
1*d97d5c0cSVladimir Kondratyev /*-
2*d97d5c0cSVladimir Kondratyev  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*d97d5c0cSVladimir Kondratyev  *
4*d97d5c0cSVladimir Kondratyev  * Copyright (c) 2020 Vladimir Kondratyev <wulf@FreeBSD.org>
5*d97d5c0cSVladimir Kondratyev  * Copyright (c) 2020 Greg V <greg@unrelenting.technology>
6*d97d5c0cSVladimir Kondratyev  *
7*d97d5c0cSVladimir Kondratyev  * Redistribution and use in source and binary forms, with or without
8*d97d5c0cSVladimir Kondratyev  * modification, are permitted provided that the following conditions
9*d97d5c0cSVladimir Kondratyev  * are met:
10*d97d5c0cSVladimir Kondratyev  * 1. Redistributions of source code must retain the above copyright
11*d97d5c0cSVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer.
12*d97d5c0cSVladimir Kondratyev  * 2. Redistributions in binary form must reproduce the above copyright
13*d97d5c0cSVladimir Kondratyev  *    notice, this list of conditions and the following disclaimer in the
14*d97d5c0cSVladimir Kondratyev  *    documentation and/or other materials provided with the distribution.
15*d97d5c0cSVladimir Kondratyev  *
16*d97d5c0cSVladimir Kondratyev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*d97d5c0cSVladimir Kondratyev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*d97d5c0cSVladimir Kondratyev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*d97d5c0cSVladimir Kondratyev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*d97d5c0cSVladimir Kondratyev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*d97d5c0cSVladimir Kondratyev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*d97d5c0cSVladimir Kondratyev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*d97d5c0cSVladimir Kondratyev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*d97d5c0cSVladimir Kondratyev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*d97d5c0cSVladimir Kondratyev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*d97d5c0cSVladimir Kondratyev  * SUCH DAMAGE.
27*d97d5c0cSVladimir Kondratyev  */
28*d97d5c0cSVladimir Kondratyev 
29*d97d5c0cSVladimir Kondratyev #include <sys/cdefs.h>
30*d97d5c0cSVladimir Kondratyev __FBSDID("$FreeBSD$");
31*d97d5c0cSVladimir Kondratyev 
32*d97d5c0cSVladimir Kondratyev /*
33*d97d5c0cSVladimir Kondratyev  * XBox 360 gamepad driver thanks to the custom descriptor in usbhid.
34*d97d5c0cSVladimir Kondratyev  *
35*d97d5c0cSVladimir Kondratyev  * Tested on: SVEN GC-5070 in both XInput (XBox 360) and DirectInput modes
36*d97d5c0cSVladimir Kondratyev  */
37*d97d5c0cSVladimir Kondratyev 
38*d97d5c0cSVladimir Kondratyev #include <sys/param.h>
39*d97d5c0cSVladimir Kondratyev #include <sys/bus.h>
40*d97d5c0cSVladimir Kondratyev #include <sys/kernel.h>
41*d97d5c0cSVladimir Kondratyev #include <sys/module.h>
42*d97d5c0cSVladimir Kondratyev #include <sys/sysctl.h>
43*d97d5c0cSVladimir Kondratyev 
44*d97d5c0cSVladimir Kondratyev #include <dev/evdev/input.h>
45*d97d5c0cSVladimir Kondratyev #include <dev/evdev/evdev.h>
46*d97d5c0cSVladimir Kondratyev 
47*d97d5c0cSVladimir Kondratyev #include <dev/hid/hgame.h>
48*d97d5c0cSVladimir Kondratyev #include <dev/hid/hid.h>
49*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidbus.h>
50*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidmap.h>
51*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidquirk.h>
52*d97d5c0cSVladimir Kondratyev #include <dev/hid/hidrdesc.h>
53*d97d5c0cSVladimir Kondratyev 
54*d97d5c0cSVladimir Kondratyev #include <dev/usb/usb.h>
55*d97d5c0cSVladimir Kondratyev #include <dev/usb/usbdi.h>
56*d97d5c0cSVladimir Kondratyev 
57*d97d5c0cSVladimir Kondratyev static const uint8_t	xb360gp_rdesc[] = {HID_XB360GP_REPORT_DESCR()};
58*d97d5c0cSVladimir Kondratyev 
59*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_BUT(number, code)	\
60*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_KEY(HUP_BUTTON, number, code) }
61*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS(usage, code)	\
62*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code) }
63*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS_FLT(usage, code)	\
64*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code),	\
65*d97d5c0cSVladimir Kondratyev 	    .fuzz = 16, .flat = 128 }
66*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_ABS_INV(usage, code)	\
67*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_ABS(HUP_GENERIC_DESKTOP, HUG_##usage, code),	\
68*d97d5c0cSVladimir Kondratyev 	    .fuzz = 16, .flat = 128, .invert_value = true }
69*d97d5c0cSVladimir Kondratyev #define XB360GP_MAP_CRG(usage_from, usage_to, callback)	\
70*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_ANY_CB_RANGE(HUP_GENERIC_DESKTOP,	\
71*d97d5c0cSVladimir Kondratyev 	    HUG_##usage_from, HUG_##usage_to, callback) }
72*d97d5c0cSVladimir Kondratyev #define XB360GP_FINALCB(cb)		\
73*d97d5c0cSVladimir Kondratyev 	{ HIDMAP_FINAL_CB(&cb) }
74*d97d5c0cSVladimir Kondratyev 
75*d97d5c0cSVladimir Kondratyev /* Customized to match usbhid's XBox 360 descriptor */
76*d97d5c0cSVladimir Kondratyev static const struct hidmap_item xb360gp_map[] = {
77*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(1,		BTN_SOUTH),
78*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(2,		BTN_EAST),
79*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(3,		BTN_WEST),
80*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(4,		BTN_NORTH),
81*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(5,		BTN_TL),
82*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(6,		BTN_TR),
83*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(7,		BTN_SELECT),
84*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(8,		BTN_START),
85*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(9,		BTN_THUMBL),
86*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(10,		BTN_THUMBR),
87*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_BUT(11,		BTN_MODE),
88*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_CRG(D_PAD_UP, D_PAD_LEFT, hgame_dpad_cb),
89*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS_FLT(X,		ABS_X),
90*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS_INV(Y,		ABS_Y),
91*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS(Z,		ABS_Z),
92*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS_FLT(RX,		ABS_RX),
93*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS_INV(RY,		ABS_RY),
94*d97d5c0cSVladimir Kondratyev 	XB360GP_MAP_ABS(RZ,		ABS_RZ),
95*d97d5c0cSVladimir Kondratyev 	XB360GP_FINALCB(		hgame_final_cb),
96*d97d5c0cSVladimir Kondratyev };
97*d97d5c0cSVladimir Kondratyev 
98*d97d5c0cSVladimir Kondratyev static const STRUCT_USB_HOST_ID xb360gp_devs[] = {
99*d97d5c0cSVladimir Kondratyev 	/* the Xbox 360 gamepad doesn't use the HID class */
100*d97d5c0cSVladimir Kondratyev 	{USB_IFACE_CLASS(UICLASS_VENDOR),
101*d97d5c0cSVladimir Kondratyev 	 USB_IFACE_SUBCLASS(UISUBCLASS_XBOX360_CONTROLLER),
102*d97d5c0cSVladimir Kondratyev 	 USB_IFACE_PROTOCOL(UIPROTO_XBOX360_GAMEPAD),},
103*d97d5c0cSVladimir Kondratyev };
104*d97d5c0cSVladimir Kondratyev 
105*d97d5c0cSVladimir Kondratyev static void
106*d97d5c0cSVladimir Kondratyev xb360gp_identify(driver_t *driver, device_t parent)
107*d97d5c0cSVladimir Kondratyev {
108*d97d5c0cSVladimir Kondratyev 	const struct hid_device_info *hw = hid_get_device_info(parent);
109*d97d5c0cSVladimir Kondratyev 
110*d97d5c0cSVladimir Kondratyev 	/* the Xbox 360 gamepad has no report descriptor */
111*d97d5c0cSVladimir Kondratyev 	if (hid_test_quirk(hw, HQ_IS_XBOX360GP))
112*d97d5c0cSVladimir Kondratyev 		hid_set_report_descr(parent, xb360gp_rdesc,
113*d97d5c0cSVladimir Kondratyev 		    sizeof(xb360gp_rdesc));
114*d97d5c0cSVladimir Kondratyev }
115*d97d5c0cSVladimir Kondratyev 
116*d97d5c0cSVladimir Kondratyev static int
117*d97d5c0cSVladimir Kondratyev xb360gp_probe(device_t dev)
118*d97d5c0cSVladimir Kondratyev {
119*d97d5c0cSVladimir Kondratyev 	struct hgame_softc *sc = device_get_softc(dev);
120*d97d5c0cSVladimir Kondratyev 	const struct hid_device_info *hw = hid_get_device_info(dev);
121*d97d5c0cSVladimir Kondratyev 	int error;
122*d97d5c0cSVladimir Kondratyev 
123*d97d5c0cSVladimir Kondratyev 	if (!hid_test_quirk(hw, HQ_IS_XBOX360GP))
124*d97d5c0cSVladimir Kondratyev 		return (ENXIO);
125*d97d5c0cSVladimir Kondratyev 
126*d97d5c0cSVladimir Kondratyev 	hidmap_set_dev(&sc->hm, dev);
127*d97d5c0cSVladimir Kondratyev 
128*d97d5c0cSVladimir Kondratyev 	error = HIDMAP_ADD_MAP(&sc->hm, xb360gp_map, NULL);
129*d97d5c0cSVladimir Kondratyev 	if (error != 0)
130*d97d5c0cSVladimir Kondratyev 		return (error);
131*d97d5c0cSVladimir Kondratyev 
132*d97d5c0cSVladimir Kondratyev 	device_set_desc(dev, "XBox 360 Gamepad");
133*d97d5c0cSVladimir Kondratyev 
134*d97d5c0cSVladimir Kondratyev 	return (BUS_PROBE_DEFAULT);
135*d97d5c0cSVladimir Kondratyev }
136*d97d5c0cSVladimir Kondratyev 
137*d97d5c0cSVladimir Kondratyev static int
138*d97d5c0cSVladimir Kondratyev xb360gp_attach(device_t dev)
139*d97d5c0cSVladimir Kondratyev {
140*d97d5c0cSVladimir Kondratyev 	struct hgame_softc *sc = device_get_softc(dev);
141*d97d5c0cSVladimir Kondratyev 	int error;
142*d97d5c0cSVladimir Kondratyev 
143*d97d5c0cSVladimir Kondratyev 	/*
144*d97d5c0cSVladimir Kondratyev 	 * Turn off the four LEDs on the gamepad which
145*d97d5c0cSVladimir Kondratyev 	 * are blinking by default:
146*d97d5c0cSVladimir Kondratyev 	 */
147*d97d5c0cSVladimir Kondratyev 	static const uint8_t reportbuf[3] = {1, 3, 0};
148*d97d5c0cSVladimir Kondratyev 	error = hid_set_report(dev, reportbuf, sizeof(reportbuf),
149*d97d5c0cSVladimir Kondratyev 	    HID_OUTPUT_REPORT, 0);
150*d97d5c0cSVladimir Kondratyev 	if (error)
151*d97d5c0cSVladimir Kondratyev 		device_printf(dev, "set output report failed, error=%d "
152*d97d5c0cSVladimir Kondratyev 		    "(ignored)\n", error);
153*d97d5c0cSVladimir Kondratyev 
154*d97d5c0cSVladimir Kondratyev 	return (hidmap_attach(&sc->hm));
155*d97d5c0cSVladimir Kondratyev }
156*d97d5c0cSVladimir Kondratyev 
157*d97d5c0cSVladimir Kondratyev static int
158*d97d5c0cSVladimir Kondratyev xb360gp_detach(device_t dev)
159*d97d5c0cSVladimir Kondratyev {
160*d97d5c0cSVladimir Kondratyev 	struct hgame_softc *sc = device_get_softc(dev);
161*d97d5c0cSVladimir Kondratyev 
162*d97d5c0cSVladimir Kondratyev 	return (hidmap_detach(&sc->hm));
163*d97d5c0cSVladimir Kondratyev }
164*d97d5c0cSVladimir Kondratyev 
165*d97d5c0cSVladimir Kondratyev static devclass_t xb360gp_devclass;
166*d97d5c0cSVladimir Kondratyev static device_method_t xb360gp_methods[] = {
167*d97d5c0cSVladimir Kondratyev 	DEVMETHOD(device_identify,	xb360gp_identify),
168*d97d5c0cSVladimir Kondratyev 	DEVMETHOD(device_probe,		xb360gp_probe),
169*d97d5c0cSVladimir Kondratyev 	DEVMETHOD(device_attach,	xb360gp_attach),
170*d97d5c0cSVladimir Kondratyev 	DEVMETHOD(device_detach,	xb360gp_detach),
171*d97d5c0cSVladimir Kondratyev 	DEVMETHOD_END
172*d97d5c0cSVladimir Kondratyev };
173*d97d5c0cSVladimir Kondratyev 
174*d97d5c0cSVladimir Kondratyev DEFINE_CLASS_0(xb360gp, xb360gp_driver, xb360gp_methods,
175*d97d5c0cSVladimir Kondratyev     sizeof(struct hgame_softc));
176*d97d5c0cSVladimir Kondratyev DRIVER_MODULE(xb360gp, hidbus, xb360gp_driver, xb360gp_devclass, NULL, 0);
177*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hid, 1, 1, 1);
178*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hidbus, 1, 1, 1);
179*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hidmap, 1, 1, 1);
180*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, hgame, 1, 1, 1);
181*d97d5c0cSVladimir Kondratyev MODULE_DEPEND(xb360gp, evdev, 1, 1, 1);
182*d97d5c0cSVladimir Kondratyev MODULE_VERSION(xb360gp, 1);
183*d97d5c0cSVladimir Kondratyev USB_PNP_HOST_INFO(xb360gp_devs);
184