1 /* $NetBSD: ims.c,v 1.3 2019/07/09 12:56:30 ryoon 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.3 2019/07/09 12:56:30 ryoon 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 37 struct ims_softc { 38 struct ihidev sc_hdev; 39 struct hidms sc_ms; 40 bool sc_enabled; 41 }; 42 43 static void ims_intr(struct ihidev *addr, void *ibuf, u_int len); 44 45 static int ims_enable(void *); 46 static void ims_disable(void *); 47 static int ims_ioctl(void *, u_long, void *, int, struct lwp *); 48 49 const struct wsmouse_accessops ims_accessops = { 50 ims_enable, 51 ims_ioctl, 52 ims_disable, 53 }; 54 55 static int ims_match(device_t, cfdata_t, void *); 56 static void ims_attach(device_t, device_t, void *); 57 static int ims_detach(device_t, int); 58 static void ims_childdet(device_t, device_t); 59 60 CFATTACH_DECL2_NEW(ims, sizeof(struct ims_softc), ims_match, ims_attach, 61 ims_detach, NULL, NULL, ims_childdet); 62 63 static int 64 ims_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 91 static void 92 ims_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 146 static int 147 ims_detach(device_t self, int flags) 148 { 149 struct ims_softc *sc = device_private(self); 150 int rv = 0; 151 152 /* No need to do reference counting of ums, wsmouse has all the goo. */ 153 if (sc->sc_ms.hidms_wsmousedev != NULL) 154 rv = config_detach(sc->sc_ms.hidms_wsmousedev, flags); 155 156 pmf_device_deregister(self); 157 158 return rv; 159 } 160 161 void 162 ims_childdet(device_t self, device_t child) 163 { 164 struct ims_softc *sc = device_private(self); 165 166 KASSERT(sc->sc_ms.hidms_wsmousedev == child); 167 sc->sc_ms.hidms_wsmousedev = NULL; 168 } 169 170 171 static void 172 ims_intr(struct ihidev *addr, void *buf, u_int len) 173 { 174 struct ims_softc *sc = (struct ims_softc *)addr; 175 struct hidms *ms = &sc->sc_ms; 176 177 if (sc->sc_enabled) 178 hidms_intr(ms, buf, len); 179 } 180 181 static int 182 ims_enable(void *v) 183 { 184 struct ims_softc *sc = v; 185 int error; 186 187 if (sc->sc_enabled) 188 return EBUSY; 189 190 sc->sc_enabled = 1; 191 sc->sc_ms.hidms_buttons = 0; 192 193 error = ihidev_open(&sc->sc_hdev); 194 if (error) 195 sc->sc_enabled = 0; 196 return error; 197 } 198 199 static void 200 ims_disable(void *v) 201 { 202 struct ims_softc *sc = v; 203 204 #ifdef DIAGNOSTIC 205 if (!sc->sc_enabled) { 206 printf("ums_disable: not enabled\n"); 207 return; 208 } 209 #endif 210 211 if (sc->sc_enabled) { 212 sc->sc_enabled = 0; 213 ihidev_close(&sc->sc_hdev); 214 } 215 216 } 217 218 static int 219 ims_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 220 { 221 struct ims_softc *sc = v; 222 223 switch (cmd) { 224 case WSMOUSEIO_GTYPE: 225 if (sc->sc_ms.flags & HIDMS_ABS) { 226 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 227 } else { 228 /* XXX: should we set something else? */ 229 *(u_int *)data = WSMOUSE_TYPE_USB; 230 } 231 return 0; 232 case WSMOUSEIO_SCALIBCOORDS: 233 case WSMOUSEIO_GCALIBCOORDS: 234 return tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data, flag, l); 235 } 236 return EPASSTHROUGH; 237 } 238