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