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 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 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 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 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