/* $NetBSD: ims.c,v 1.5 2023/05/10 00:10:02 riastradh Exp $ */ /* $OpenBSD ims.c,v 1.1 2016/01/12 01:11:15 jcs Exp $ */ /* * HID-over-i2c mouse/trackpad driver * * Copyright (c) 2015, 2016 joshua stein * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include __KERNEL_RCSID(0, "$NetBSD: ims.c,v 1.5 2023/05/10 00:10:02 riastradh Exp $"); #include #include #include #include #include #include #include #include #include struct ims_softc { struct ihidev sc_hdev; struct hidms sc_ms; bool sc_enabled; }; static void ims_intr(struct ihidev *addr, void *ibuf, u_int len); static int ims_enable(void *); static void ims_disable(void *); static int ims_ioctl(void *, u_long, void *, int, struct lwp *); const struct wsmouse_accessops ims_accessops = { ims_enable, ims_ioctl, ims_disable, }; static int ims_match(device_t, cfdata_t, void *); static void ims_attach(device_t, device_t, void *); static int ims_detach(device_t, int); static void ims_childdet(device_t, device_t); CFATTACH_DECL2_NEW(ims, sizeof(struct ims_softc), ims_match, ims_attach, ims_detach, NULL, NULL, ims_childdet); static int ims_match(device_t parent, cfdata_t match, void *aux) { struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; int size; void *desc; ihidev_get_report_desc(iha->parent, &desc, &size); if (hid_is_collection(desc, size, iha->reportid, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER))) return (IMATCH_IFACECLASS); if (hid_is_collection(desc, size, iha->reportid, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) return (IMATCH_IFACECLASS); if (hid_is_collection(desc, size, iha->reportid, HID_USAGE2(HUP_DIGITIZERS, HUD_PEN))) return (IMATCH_IFACECLASS); if (hid_is_collection(desc, size, iha->reportid, HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCH_SCREEN))) return (IMATCH_IFACECLASS); return (IMATCH_NONE); } static void ims_attach(device_t parent, device_t self, void *aux) { struct ims_softc *sc = device_private(self); struct hidms *ms = &sc->sc_ms; struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; int size, repid; void *desc; struct hid_data * d __debugused; struct hid_item item __debugused; sc->sc_hdev.sc_idev = self; sc->sc_hdev.sc_intr = ims_intr; sc->sc_hdev.sc_parent = iha->parent; sc->sc_hdev.sc_report_id = iha->reportid; if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "couldn't establish power handler\n"); ihidev_get_report_desc(iha->parent, &desc, &size); repid = iha->reportid; sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); if (!hidms_setup(self, ms, iha->reportid, desc, size) != 0) return; #if defined(DEBUG) /* calibrate the touchscreen */ memset(&sc->sc_ms.sc_calibcoords, 0, sizeof(sc->sc_ms.sc_calibcoords)); d = hid_start_parse(desc, size, hid_input); if (d != NULL) { while (hid_get_item(d, &item)) { if (item.kind != hid_input || HID_GET_USAGE_PAGE(item.usage) != HUP_GENERIC_DESKTOP || item.report_ID != sc->sc_hdev.sc_report_id) continue; if (HID_GET_USAGE(item.usage) == HUG_X) { aprint_normal("X range: %d - %d\n", item.logical_minimum, item.logical_maximum); } if (HID_GET_USAGE(item.usage) == HUG_Y) { aprint_normal("Y range: %d - %d\n", item.logical_minimum, item.logical_maximum); } } hid_end_parse(d); } #endif tpcalib_init(&sc->sc_ms.sc_tpcalib); tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, (void *)&sc->sc_ms.sc_calibcoords, 0, 0); hidms_attach(self, ms, &ims_accessops); } static int ims_detach(device_t self, int flags) { int error; /* No need to do reference counting of ums, wsmouse has all the goo. */ error = config_detach_children(self, flags); if (error) return error; pmf_device_deregister(self); return 0; } void ims_childdet(device_t self, device_t child) { struct ims_softc *sc = device_private(self); KASSERT(KERNEL_LOCKED_P()); KASSERT(sc->sc_ms.hidms_wsmousedev == child); sc->sc_ms.hidms_wsmousedev = NULL; } static void ims_intr(struct ihidev *addr, void *buf, u_int len) { struct ims_softc *sc = (struct ims_softc *)addr; struct hidms *ms = &sc->sc_ms; if (sc->sc_enabled) hidms_intr(ms, buf, len); } static int ims_enable(void *v) { struct ims_softc *sc = v; int error; KASSERT(KERNEL_LOCKED_P()); if (sc->sc_enabled) return EBUSY; sc->sc_enabled = 1; sc->sc_ms.hidms_buttons = 0; error = ihidev_open(&sc->sc_hdev); if (error) sc->sc_enabled = 0; return error; } static void ims_disable(void *v) { struct ims_softc *sc = v; KASSERT(KERNEL_LOCKED_P()); #ifdef DIAGNOSTIC if (!sc->sc_enabled) { printf("ums_disable: not enabled\n"); return; } #endif if (sc->sc_enabled) { sc->sc_enabled = 0; ihidev_close(&sc->sc_hdev); } } static int ims_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) { struct ims_softc *sc = v; switch (cmd) { case WSMOUSEIO_GTYPE: if (sc->sc_ms.flags & HIDMS_ABS) { *(u_int *)data = WSMOUSE_TYPE_TPANEL; } else { /* XXX: should we set something else? */ *(u_int *)data = WSMOUSE_TYPE_USB; } return 0; case WSMOUSEIO_SCALIBCOORDS: case WSMOUSEIO_GCALIBCOORDS: return tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data, flag, l); } return EPASSTHROUGH; }