1 /* $NetBSD: ims.c,v 1.1 2017/12/10 17:05:54 bouyer 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.1 2017/12/10 17:05:54 bouyer 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 return (IMATCH_NONE); 85 } 86 87 static void 88 ims_attach(device_t parent, device_t self, void *aux) 89 { 90 struct ims_softc *sc = device_private(self); 91 struct hidms *ms = &sc->sc_ms; 92 struct ihidev_attach_arg *iha = (struct ihidev_attach_arg *)aux; 93 int size, repid; 94 void *desc; 95 96 sc->sc_hdev.sc_idev = self; 97 sc->sc_hdev.sc_intr = ims_intr; 98 sc->sc_hdev.sc_parent = iha->parent; 99 sc->sc_hdev.sc_report_id = iha->reportid; 100 101 if (!pmf_device_register(self, NULL, NULL)) 102 aprint_error_dev(self, "couldn't establish power handler\n"); 103 104 ihidev_get_report_desc(iha->parent, &desc, &size); 105 repid = iha->reportid; 106 sc->sc_hdev.sc_isize = hid_report_size(desc, size, hid_input, repid); 107 sc->sc_hdev.sc_osize = hid_report_size(desc, size, hid_output, repid); 108 sc->sc_hdev.sc_fsize = hid_report_size(desc, size, hid_feature, repid); 109 110 if (!hidms_setup(self, ms, iha->reportid, desc, size) != 0) 111 return; 112 113 hidms_attach(self, ms, &ims_accessops); 114 } 115 116 static int 117 ims_detach(device_t self, int flags) 118 { 119 struct ims_softc *sc = device_private(self); 120 int rv = 0; 121 122 /* No need to do reference counting of ums, wsmouse has all the goo. */ 123 if (sc->sc_ms.hidms_wsmousedev != NULL) 124 rv = config_detach(sc->sc_ms.hidms_wsmousedev, flags); 125 126 pmf_device_deregister(self); 127 128 return rv; 129 } 130 131 void 132 ims_childdet(device_t self, device_t child) 133 { 134 struct ims_softc *sc = device_private(self); 135 136 KASSERT(sc->sc_ms.hidms_wsmousedev == child); 137 sc->sc_ms.hidms_wsmousedev = NULL; 138 } 139 140 141 static void 142 ims_intr(struct ihidev *addr, void *buf, u_int len) 143 { 144 struct ims_softc *sc = (struct ims_softc *)addr; 145 struct hidms *ms = &sc->sc_ms; 146 147 if (sc->sc_enabled) 148 hidms_intr(ms, buf, len); 149 } 150 151 static int 152 ims_enable(void *v) 153 { 154 struct ims_softc *sc = v; 155 int error; 156 157 if (sc->sc_enabled) 158 return EBUSY; 159 160 sc->sc_enabled = 1; 161 sc->sc_ms.hidms_buttons = 0; 162 163 error = ihidev_open(&sc->sc_hdev); 164 if (error) 165 sc->sc_enabled = 0; 166 return error; 167 } 168 169 static void 170 ims_disable(void *v) 171 { 172 struct ims_softc *sc = v; 173 174 #ifdef DIAGNOSTIC 175 if (!sc->sc_enabled) { 176 printf("ums_disable: not enabled\n"); 177 return; 178 } 179 #endif 180 181 if (sc->sc_enabled) { 182 sc->sc_enabled = 0; 183 ihidev_close(&sc->sc_hdev); 184 } 185 186 } 187 188 static int 189 ims_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 190 { 191 struct ims_softc *sc = v; 192 193 switch (cmd) { 194 case WSMOUSEIO_GTYPE: 195 if (sc->sc_ms.flags & HIDMS_ABS) { 196 *(u_int *)data = WSMOUSE_TYPE_TPANEL; 197 } else { 198 /* XXX: should we set something else? */ 199 *(u_int *)data = WSMOUSE_TYPE_USB; 200 } 201 return 0; 202 } 203 return EPASSTHROUGH; 204 } 205