1 /* $OpenBSD: ujoy.c,v 1.6 2024/12/30 02:46:00 guenther 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/device.h>
23 #include <sys/ioctl.h>
24 #include <sys/conf.h>
25 #include <sys/tty.h>
26 #include <sys/fcntl.h>
27
28 #include <dev/usb/usb.h>
29 #include <dev/usb/usbhid.h>
30
31 #include <dev/usb/usbdi.h>
32
33 #include <dev/usb/uhidev.h>
34 #include <dev/usb/uhid.h>
35
36 int ujoy_match(struct device *, void *, void *);
37
38 struct cfdriver ujoy_cd = {
39 NULL, "ujoy", DV_DULL
40 };
41
42 const struct cfattach ujoy_ca = {
43 sizeof(struct uhid_softc),
44 ujoy_match,
45 uhid_attach,
46 uhid_detach,
47 };
48
49 /*
50 * XXX workaround:
51 *
52 * This is a copy of sys/dev/hid/hid.c:hid_is_collection(), synced up to the
53 * NetBSD version. Our current hid_is_collection() is not playing nice with
54 * all HID devices like the PS4 controller. But applying this version
55 * globally breaks other HID devices like ims(4) and imt(4). Until our global
56 * hid_is_collection() can't be fixed to play nice with all HID devices, we
57 * go for this dedicated ujoy(4) version.
58 */
59 int
ujoy_hid_is_collection(const void * desc,int size,uint8_t id,int32_t usage)60 ujoy_hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage)
61 {
62 struct hid_data *hd;
63 struct hid_item hi;
64 uint32_t coll_usage = ~0;
65
66 hd = hid_start_parse(desc, size, hid_input);
67 if (hd == NULL)
68 return (0);
69
70 while (hid_get_item(hd, &hi)) {
71 if (hi.kind == hid_collection &&
72 hi.collection == HCOLL_APPLICATION)
73 coll_usage = hi.usage;
74
75 if (hi.kind == hid_endcollection)
76 coll_usage = ~0;
77
78 if (hi.kind == hid_input &&
79 coll_usage == usage &&
80 hi.report_ID == id) {
81 hid_end_parse(hd);
82 return (1);
83 }
84 }
85 hid_end_parse(hd);
86
87 return (0);
88 }
89
90 int
ujoy_match(struct device * parent,void * match,void * aux)91 ujoy_match(struct device *parent, void *match, void *aux)
92 {
93 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux;
94 int size;
95 void *desc;
96 int ret = UMATCH_NONE;
97
98 if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
99 return (ret);
100
101 /* Find the general usage page and gamecontroller collections */
102 uhidev_get_report_desc(uha->parent, &desc, &size);
103
104 if (ujoy_hid_is_collection(desc, size, uha->reportid,
105 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_JOYSTICK)))
106 ret = UMATCH_IFACECLASS;
107
108 if (ujoy_hid_is_collection(desc, size, uha->reportid,
109 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_GAME_PAD)))
110 ret = UMATCH_IFACECLASS;
111
112 return (ret);
113 }
114
115 int
ujoyopen(dev_t dev,int flag,int mode,struct proc * p)116 ujoyopen(dev_t dev, int flag, int mode, struct proc *p)
117 {
118 /* Restrict ujoy devices to read operations */
119 if ((flag & FWRITE))
120 return (EPERM);
121 return (uhid_do_open(dev, flag, mode, p));
122 }
123
124 int
ujoyioctl(dev_t dev,u_long cmd,caddr_t addr,int flag,struct proc * p)125 ujoyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p)
126 {
127 switch (cmd) {
128 case FIOASYNC:
129 case USB_GET_DEVICEINFO:
130 case USB_GET_REPORT:
131 case USB_GET_REPORT_DESC:
132 case USB_GET_REPORT_ID:
133 break;
134 default:
135 return (EPERM);
136 }
137
138 return (uhidioctl(dev, cmd, addr, flag, p));
139 }
140