ims.c revision 1.1 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 (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.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