1*b9ae17a0Sguenther /* $OpenBSD: ujoy.c,v 1.6 2024/12/30 02:46:00 guenther Exp $ */ 21d44892eSthfr 31d44892eSthfr /* 41d44892eSthfr * Copyright (c) 2021 Thomas Frohwein <thfr@openbsd.org> 51d44892eSthfr * Copyright (c) 2021 Bryan Steele <brynet@openbsd.org> 61d44892eSthfr * 71d44892eSthfr * Permission to use, copy, modify, and distribute this software for any 81d44892eSthfr * purpose with or without fee is hereby granted, provided that the above 91d44892eSthfr * copyright notice and this permission notice appear in all copies. 101d44892eSthfr * 111d44892eSthfr * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 121d44892eSthfr * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 131d44892eSthfr * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 141d44892eSthfr * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 151d44892eSthfr * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 161d44892eSthfr * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 171d44892eSthfr * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 181d44892eSthfr */ 191d44892eSthfr 201d44892eSthfr #include <sys/param.h> 211d44892eSthfr #include <sys/systm.h> 221d44892eSthfr #include <sys/device.h> 231d44892eSthfr #include <sys/ioctl.h> 241d44892eSthfr #include <sys/conf.h> 251d44892eSthfr #include <sys/tty.h> 261d44892eSthfr #include <sys/fcntl.h> 271d44892eSthfr 281d44892eSthfr #include <dev/usb/usb.h> 291d44892eSthfr #include <dev/usb/usbhid.h> 301d44892eSthfr 311d44892eSthfr #include <dev/usb/usbdi.h> 321d44892eSthfr 331d44892eSthfr #include <dev/usb/uhidev.h> 341d44892eSthfr #include <dev/usb/uhid.h> 351d44892eSthfr 361d44892eSthfr int ujoy_match(struct device *, void *, void *); 371d44892eSthfr 381d44892eSthfr struct cfdriver ujoy_cd = { 391d44892eSthfr NULL, "ujoy", DV_DULL 401d44892eSthfr }; 411d44892eSthfr 421d44892eSthfr const struct cfattach ujoy_ca = { 431d44892eSthfr sizeof(struct uhid_softc), 441d44892eSthfr ujoy_match, 451d44892eSthfr uhid_attach, 461d44892eSthfr uhid_detach, 471d44892eSthfr }; 481d44892eSthfr 491d44892eSthfr /* 501d44892eSthfr * XXX workaround: 511d44892eSthfr * 521d44892eSthfr * This is a copy of sys/dev/hid/hid.c:hid_is_collection(), synced up to the 531d44892eSthfr * NetBSD version. Our current hid_is_collection() is not playing nice with 541d44892eSthfr * all HID devices like the PS4 controller. But applying this version 551d44892eSthfr * globally breaks other HID devices like ims(4) and imt(4). Until our global 561d44892eSthfr * hid_is_collection() can't be fixed to play nice with all HID devices, we 571d44892eSthfr * go for this dedicated ujoy(4) version. 581d44892eSthfr */ 591d44892eSthfr int 601d44892eSthfr ujoy_hid_is_collection(const void *desc, int size, uint8_t id, int32_t usage) 611d44892eSthfr { 621d44892eSthfr struct hid_data *hd; 631d44892eSthfr struct hid_item hi; 641d44892eSthfr uint32_t coll_usage = ~0; 651d44892eSthfr 661d44892eSthfr hd = hid_start_parse(desc, size, hid_input); 671d44892eSthfr if (hd == NULL) 681d44892eSthfr return (0); 691d44892eSthfr 701d44892eSthfr while (hid_get_item(hd, &hi)) { 711d44892eSthfr if (hi.kind == hid_collection && 721d44892eSthfr hi.collection == HCOLL_APPLICATION) 731d44892eSthfr coll_usage = hi.usage; 741d44892eSthfr 751d44892eSthfr if (hi.kind == hid_endcollection) 761d44892eSthfr coll_usage = ~0; 771d44892eSthfr 781d44892eSthfr if (hi.kind == hid_input && 791d44892eSthfr coll_usage == usage && 801d44892eSthfr hi.report_ID == id) { 811d44892eSthfr hid_end_parse(hd); 821d44892eSthfr return (1); 831d44892eSthfr } 841d44892eSthfr } 851d44892eSthfr hid_end_parse(hd); 861d44892eSthfr 871d44892eSthfr return (0); 881d44892eSthfr } 891d44892eSthfr 901d44892eSthfr int 911d44892eSthfr ujoy_match(struct device *parent, void *match, void *aux) 921d44892eSthfr { 931d44892eSthfr struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 941d44892eSthfr int size; 951d44892eSthfr void *desc; 961d44892eSthfr int ret = UMATCH_NONE; 971d44892eSthfr 98faac88c0Santon if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha)) 991d44892eSthfr return (ret); 1001d44892eSthfr 1011d44892eSthfr /* Find the general usage page and gamecontroller collections */ 1021d44892eSthfr uhidev_get_report_desc(uha->parent, &desc, &size); 1031d44892eSthfr 1041d44892eSthfr if (ujoy_hid_is_collection(desc, size, uha->reportid, 1051d44892eSthfr HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_JOYSTICK))) 1061d44892eSthfr ret = UMATCH_IFACECLASS; 1071d44892eSthfr 1081d44892eSthfr if (ujoy_hid_is_collection(desc, size, uha->reportid, 1091d44892eSthfr HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_GAME_PAD))) 1101d44892eSthfr ret = UMATCH_IFACECLASS; 1111d44892eSthfr 1121d44892eSthfr return (ret); 1131d44892eSthfr } 1141d44892eSthfr 1151d44892eSthfr int 1161d44892eSthfr ujoyopen(dev_t dev, int flag, int mode, struct proc *p) 1171d44892eSthfr { 1181d44892eSthfr /* Restrict ujoy devices to read operations */ 1191d44892eSthfr if ((flag & FWRITE)) 1201d44892eSthfr return (EPERM); 1211d44892eSthfr return (uhid_do_open(dev, flag, mode, p)); 1221d44892eSthfr } 1231d44892eSthfr 1241d44892eSthfr int 1251d44892eSthfr ujoyioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 1261d44892eSthfr { 1271d44892eSthfr switch (cmd) { 1281d44892eSthfr case FIOASYNC: 1291d44892eSthfr case USB_GET_DEVICEINFO: 1301d44892eSthfr case USB_GET_REPORT: 1311d44892eSthfr case USB_GET_REPORT_DESC: 1321d44892eSthfr case USB_GET_REPORT_ID: 1331d44892eSthfr break; 1341d44892eSthfr default: 1351d44892eSthfr return (EPERM); 1361d44892eSthfr } 1371d44892eSthfr 1381d44892eSthfr return (uhidioctl(dev, cmd, addr, flag, p)); 1391d44892eSthfr } 140