xref: /openbsd-src/sys/dev/usb/ujoy.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: ujoy.c,v 1.4 2022/07/02 08:50:42 visa Exp $ */
2 
3 /*
4  * Copyright (c) 2021 Thomas Frohwein	<thfr@openbsd.org>
5  * Copyright (c) 2021 Bryan Steele	<brynet@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/kernel.h>
23 #include <sys/malloc.h>
24 #include <sys/signalvar.h>
25 #include <sys/device.h>
26 #include <sys/ioctl.h>
27 #include <sys/conf.h>
28 #include <sys/tty.h>
29 #include <sys/proc.h>
30 #include <sys/vnode.h>
31 #include <sys/fcntl.h>
32 
33 #include <dev/usb/usb.h>
34 #include <dev/usb/usbhid.h>
35 
36 #include <dev/usb/usbdevs.h>
37 #include <dev/usb/usbdi.h>
38 #include <dev/usb/usbdi_util.h>
39 
40 #include <dev/usb/uhidev.h>
41 #include <dev/usb/uhid.h>
42 
43 int ujoy_match(struct device *, void *, void *);
44 
45 struct cfdriver ujoy_cd = {
46 	NULL, "ujoy", DV_DULL
47 };
48 
49 const struct cfattach ujoy_ca = {
50 	sizeof(struct uhid_softc),
51 	ujoy_match,
52 	uhid_attach,
53 	uhid_detach,
54 };
55 
56 /*
57  * XXX workaround:
58  *
59  * This is a copy of sys/dev/hid/hid.c:hid_is_collection(), synced up to the
60  * NetBSD version.  Our current hid_is_collection() is not playing nice with
61  * all HID devices like the PS4 controller.  But applying this version
62  * globally breaks other HID devices like ims(4) and imt(4).  Until our global
63  * hid_is_collection() can't be fixed to play nice with all HID devices, we
64  * go for this dedicated ujoy(4) version.
65  */
66 int
67 ujoy_hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
68 {
69 	struct hid_data *hd;
70 	struct hid_item hi;
71 	uint32_t coll_usage = ~0;
72 
73 	hd = hid_start_parse(desc, size, hid_input);
74 	if (hd == NULL)
75 		return (0);
76 
77 	while (hid_get_item(hd, &hi)) {
78 		if (hi.kind == hid_collection &&
79 		    hi.collection == HCOLL_APPLICATION)
80 			coll_usage = hi.usage;
81 
82 		if (hi.kind == hid_endcollection)
83 			coll_usage = ~0;
84 
85 		if (hi.kind == hid_input &&
86 		    coll_usage == usage &&
87 		    hi.report_ID == id) {
88 			hid_end_parse(hd);
89 			return (1);
90 		}
91 	}
92 	hid_end_parse(hd);
93 
94 	return (0);
95 }
96 
97 int
98 ujoy_match(struct device *parent, void *match, void *aux)
99 {
100 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
101 	int			  size;
102 	void			 *desc;
103 	int			  ret = UMATCH_NONE;
104 
105 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
106 		return (ret);
107 
108 	/* Find the general usage page and gamecontroller collections */
109 	uhidev_get_report_desc(uha->parent, &desc, &size);
110 
111 	if (ujoy_hid_is_collection(desc, size, uha->reportid,
112 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_JOYSTICK)))
113     		ret = UMATCH_IFACECLASS;
114 
115 	if (ujoy_hid_is_collection(desc, size, uha->reportid,
116 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_GAME_PAD)))
117     		ret = UMATCH_IFACECLASS;
118 
119 	return (ret);
120 }
121 
122 int
123 ujoyopen(dev_t dev, int flag, int mode, struct proc *p)
124 {
125 	/* Restrict ujoy devices to read operations */
126 	if ((flag & FWRITE))
127     		return (EPERM);
128 	return (uhid_do_open(dev, flag, mode, p));
129 }
130 
131 int
132 ujoyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
133 {
134 	switch (cmd) {
135 	case FIONBIO:
136 	case FIOASYNC:
137 	case USB_GET_DEVICEINFO:
138     	case USB_GET_REPORT:
139     	case USB_GET_REPORT_DESC:
140     	case USB_GET_REPORT_ID:
141 		break;
142 	default:
143 		return (EPERM);
144 	}
145 
146 	return (uhidioctl(dev, cmd, addr, flag, p));
147 }
148