xref: /netbsd-src/sys/dev/i2c/ims.c (revision 7d62b00eb9ad855ffcd7da46b41e23feb5476fac)
1 /* $NetBSD: ims.c,v 1.4 2022/01/14 22:28:42 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.4 2022/01/14 22:28:42 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 	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(KERNEL_LOCKED_P());
167 
168 	KASSERT(sc->sc_ms.hidms_wsmousedev == child);
169 	sc->sc_ms.hidms_wsmousedev = NULL;
170 }
171 
172 
173 static void
174 ims_intr(struct ihidev *addr, void *buf, u_int len)
175 {
176 	struct ims_softc *sc = (struct ims_softc *)addr;
177 	struct hidms *ms = &sc->sc_ms;
178 
179 	if (sc->sc_enabled)
180 		hidms_intr(ms, buf, len);
181 }
182 
183 static int
184 ims_enable(void *v)
185 {
186 	struct ims_softc *sc = v;
187 	int error;
188 
189 	KASSERT(KERNEL_LOCKED_P());
190 
191 	if (sc->sc_enabled)
192 		return EBUSY;
193 
194 	sc->sc_enabled = 1;
195 	sc->sc_ms.hidms_buttons = 0;
196 
197 	error = ihidev_open(&sc->sc_hdev);
198 	if (error)
199 		sc->sc_enabled = 0;
200 	return error;
201 }
202 
203 static void
204 ims_disable(void *v)
205 {
206 	struct ims_softc *sc = v;
207 
208 	KASSERT(KERNEL_LOCKED_P());
209 
210 #ifdef DIAGNOSTIC
211 	if (!sc->sc_enabled) {
212 		printf("ums_disable: not enabled\n");
213 		return;
214 	}
215 #endif
216 
217 	if (sc->sc_enabled) {
218 		sc->sc_enabled = 0;
219 		ihidev_close(&sc->sc_hdev);
220 	}
221 
222 }
223 
224 static int
225 ims_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
226 {
227 	struct ims_softc *sc = v;
228 
229 	switch (cmd) {
230 	case WSMOUSEIO_GTYPE:
231 		if (sc->sc_ms.flags & HIDMS_ABS) {
232 			*(u_int *)data = WSMOUSE_TYPE_TPANEL;
233 		} else {
234 			/* XXX: should we set something else? */
235 			*(u_int *)data = WSMOUSE_TYPE_USB;
236 		}
237 		return 0;
238 	case WSMOUSEIO_SCALIBCOORDS:
239 	case WSMOUSEIO_GCALIBCOORDS:
240 		return tpcalib_ioctl(&sc->sc_ms.sc_tpcalib, cmd, data, flag, l);
241 	}
242 	return EPASSTHROUGH;
243 }
244