Home | History | Annotate | Line # | Download | only in i2c
ims.c revision 1.5
      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 (at) 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 
     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 	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 
    160 void
    161 ims_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 
    172 static void
    173 ims_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 
    182 static int
    183 ims_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 
    202 static void
    203 ims_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 
    223 static int
    224 ims_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