1/* $NetBSD: ims.c,v 1.5 2023/05/10 00:10:02 riastradh Exp $ */ 2/* $OpenBSD ims.c,v 1.1 2016/01/12 01:11:15 jcs Exp $ */ 3 4/* 5 * HID-over-i2c mouse/trackpad driver 6 * 7 * Copyright (c) 2015, 2016 joshua stein <jcs@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21 22#include <sys/cdefs.h> 23__KERNEL_RCSID(0, "$NetBSD: ims.c,v 1.5 2023/05/10 00:10:02 riastradh Exp $"); 24 25#include <sys/param.h> 26#include <sys/systm.h> 27#include <sys/kernel.h> 28#include <sys/device.h> 29#include <sys/ioctl.h> 30 31#include <dev/i2c/i2cvar.h> 32#include <dev/i2c/ihidev.h> 33 34#include <dev/hid/hid.h> 35#include <dev/hid/hidms.h> 36 37struct ims_softc { 38 struct ihidev sc_hdev; 39 struct hidms sc_ms; 40 bool sc_enabled; 41}; 42 43static void ims_intr(struct ihidev *addr, void *ibuf, u_int len); 44 45static int ims_enable(void *); 46static void ims_disable(void *); 47static int ims_ioctl(void *, u_long, void *, int, struct lwp *); 48 49const struct wsmouse_accessops ims_accessops = { 50 ims_enable, 51 ims_ioctl, 52 ims_disable, 53}; 54 55static int ims_match(device_t, cfdata_t, void *); 56static void ims_attach(device_t, device_t, void *); 57static int ims_detach(device_t, int); 58static void ims_childdet(device_t, device_t); 59 60CFATTACH_DECL2_NEW(ims, sizeof(struct ims_softc), ims_match, ims_attach, 61 ims_detach, NULL, NULL, ims_childdet); 62 63static int 64ims_match(device_t parent, cfdata_t match, void *aux) 65{ 66 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 67 int size; 68 void *desc; 69 70 ihidev_get_report_desc(iha->parent, &desc, &size); 71 72 if (hid_is_collection(desc, size, iha->reportid, 73 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_POINTER))) 74 return (IMATCH_IFACECLASS); 75 76 if (hid_is_collection(desc, size, iha->reportid, 77 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) 78 return (IMATCH_IFACECLASS); 79 80 if (hid_is_collection(desc, size, iha->reportid, 81 HID_USAGE2(HUP_DIGITIZERS, HUD_PEN))) 82 return (IMATCH_IFACECLASS); 83 84 if (hid_is_collection(desc, size, iha->reportid, 85 HID_USAGE2(HUP_DIGITIZERS, HUD_TOUCH_SCREEN))) 86 return (IMATCH_IFACECLASS); 87 88 return (IMATCH_NONE); 89} 90 91static void 92ims_attach(device_t parent, device_t self, void *aux) 93{ 94 struct ims_softc *sc = device_private(self); 95 struct hidms *ms = &sc->sc_ms; 96 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 97 int size, repid; 98 void *desc; 99 struct hid_data * d __debugused; 100 struct hid_item item __debugused; 101 102 sc->sc_hdev.sc_idev = self; 103 sc->sc_hdev.sc_intr = ims_intr; 104 sc->sc_hdev.sc_parent = iha->parent; 105 sc->sc_hdev.sc_report_id = iha->reportid; 106 107 if (!pmf_device_register(self, NULL, NULL)) 108 aprint_error_dev(self, "couldn't establish power handler\n"); 109 110 ihidev_get_report_desc(iha->parent, &desc, &size); 111 repid = iha->reportid; 112 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 113 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 114 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 115 116 if (!hidms_setup(self, ms, iha->reportid, desc, size) != 0) 117 return; 118 119#if defined(DEBUG) 120 /* calibrate the touchscreen */ 121 memset(&sc->sc_ms.sc_calibcoords, 0, sizeof(sc->sc_ms.sc_calibcoords)); 122 d = hid_start_parse(desc, size, hid_input); 123 if (d != NULL) { 124 while (hid_get_item(d, &item)) { 125 if (item.kind != hid_input 126 || HID_GET_USAGE_PAGE(item.usage) != HUP_GENERIC_DESKTOP 127 || item.report_ID != sc->sc_hdev.sc_report_id) 128 continue; 129 if (HID_GET_USAGE(item.usage) == HUG_X) { 130 aprint_normal("X range: %d - %d\n", item.logical_minimum, item.logical_maximum); 131 } 132 if (HID_GET_USAGE(item.usage) == HUG_Y) { 133 aprint_normal("Y range: %d - %d\n", item.logical_minimum, item.logical_maximum); 134 } 135 } 136 hid_end_parse(d); 137 } 138#endif 139 tpcalib_init(&sc->sc_ms.sc_tpcalib); 140 tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, 141 (void *)&sc->sc_ms.sc_calibcoords, 0, 0); 142 143 hidms_attach(self, ms, &ims_accessops); 144} 145 146static int 147ims_detach(device_t self, int flags) 148{ 149 int error; 150 151 /* No need to do reference counting of ums, wsmouse has all the goo. */ 152 error = config_detach_children(self, flags); 153 if (error) 154 return error; 155 156 pmf_device_deregister(self); 157 return 0; 158} 159 160void 161ims_childdet(device_t self, device_t child) 162{ 163 struct ims_softc *sc = device_private(self); 164 165 KASSERT(KERNEL_LOCKED_P()); 166 167 KASSERT(sc->sc_ms.hidms_wsmousedev == child); 168 sc->sc_ms.hidms_wsmousedev = NULL; 169} 170 171 172static void 173ims_intr(struct ihidev *addr, void *buf, u_int len) 174{ 175 struct ims_softc *sc = (struct ims_softc *)addr; 176 struct hidms *ms = &sc->sc_ms; 177 178 if (sc->sc_enabled) 179 hidms_intr(ms, buf, len); 180} 181 182static int 183ims_enable(void *v) 184{ 185 struct ims_softc *sc = v; 186 int error; 187 188 KASSERT(KERNEL_LOCKED_P()); 189 190 if (sc->sc_enabled) 191 return EBUSY; 192 193 sc->sc_enabled = 1; 194 sc->sc_ms.hidms_buttons = 0; 195 196 error = ihidev_open(&sc->sc_hdev); 197 if (error) 198 sc->sc_enabled = 0; 199 return error; 200} 201 202static void 203ims_disable(void *v) 204{ 205 struct ims_softc *sc = v; 206 207 KASSERT(KERNEL_LOCKED_P()); 208 209#ifdef DIAGNOSTIC 210 if (!sc->sc_enabled) { 211 printf("ums_disable: not enabled\n"); 212 return; 213 } 214#endif 215 216 if (sc->sc_enabled) { 217 sc->sc_enabled = 0; 218 ihidev_close(&sc->sc_hdev); 219 } 220 221} 222 223static int 224ims_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 225{ 226 struct ims_softc *sc = v; 227 228 switch (cmd) { 229 case WSMOUSEIO_GTYPE: 230 if (sc->sc_ms.flags & HIDMS_ABS) { 231 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 232 } else { 233 /* XXX: should we set something else? */ 234 *(u_int *)data = WSMOUSE_TYPE_USB; 235 } 236 return 0; 237 case WSMOUSEIO_SCALIBCOORDS: 238 case WSMOUSEIO_GCALIBCOORDS: 239 return tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data, flag, l); 240 } 241 return EPASSTHROUGH; 242} 243