1 /* $NetBSD: netwalker_btn.c,v 1.4 2021/08/07 16:18:50 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2014 Genetec Corporation. All rights reserved.
5 * Written by Hashimoto Kenichi for Genetec Corporation.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORPORATION
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: netwalker_btn.c,v 1.4 2021/08/07 16:18:50 thorpej Exp $");
31
32 #include "opt_imxspi.h"
33 #include "opt_mousebtn.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/lock.h>
40 #include <sys/callout.h>
41 #include <sys/gpio.h>
42 #include <sys/bus.h>
43 #include <sys/mutex.h>
44
45 #include <dev/wscons/wsconsio.h>
46 #include <dev/wscons/wsmousevar.h>
47
48 #include <dev/gpio/gpiovar.h>
49
50 #define GPIO1_BASE 160
51
52 #define MOUSEBTN_PIN_LEFT 0
53 #define MOUSEBTN_PIN_RIGHT 1
54 #define MOUSEBTN_NPINS 2
55
56 #define POLLRATE (hz/10)
57
58 struct mousebtn_softc
59 {
60 device_t sc_dev;
61 void *sc_gpio;
62 void *sc_intr[MOUSEBTN_NPINS];
63
64 struct gpio_pinmap sc_map;
65 int sc_map_pins[MOUSEBTN_NPINS];
66
67 int sc_buttons;
68
69 struct callout sc_c;
70 kmutex_t sc_lock;
71
72 int sc_enabled;
73 struct device *sc_wsmousedev;
74 };
75
76 static int mousebtn_match(device_t, cfdata_t, void *);
77 static void mousebtn_attach(device_t, device_t, void *);
78 static int mousebtn_detach(device_t, int);
79
80 CFATTACH_DECL_NEW(netwalker_btn, sizeof(struct mousebtn_softc),
81 mousebtn_match, mousebtn_attach, mousebtn_detach, NULL);
82
83 static void mousebtn_poll(void *);
84 static int mousebtn_intr(void *);
85
86 static int mousebtn_enable(void *v);
87 static void mousebtn_disable(void *v);
88 static int mousebtn_ioctl(void *, u_long, void *, int, struct lwp *);
89
90 static bool mousebtn_resume(device_t, const pmf_qual_t *);
91 static bool mousebtn_suspend(device_t, const pmf_qual_t *);
92
93 static const struct wsmouse_accessops mousebtn_accessops = {
94 .enable = mousebtn_enable,
95 .ioctl = mousebtn_ioctl,
96 .disable = mousebtn_disable
97 };
98
99 static int
mousebtn_match(device_t parent,cfdata_t cf,void * aux)100 mousebtn_match(device_t parent, cfdata_t cf, void * aux)
101 {
102 struct gpio_attach_args *ga = aux;
103
104 if (strcmp(ga->ga_dvname, cf->cf_name))
105 return 0;
106 if (ga->ga_offset == -1)
107 return 0;
108
109 /* check that we have enough pins */
110 if (gpio_npins(ga->ga_mask) != MOUSEBTN_NPINS) {
111 aprint_debug("%s: invalid pin mask 0x%02x\n", cf->cf_name,
112 ga->ga_mask);
113 return 0;
114 }
115
116 return 1;
117 }
118
119 static void
mousebtn_attach(device_t parent,device_t self,void * aux)120 mousebtn_attach(device_t parent, device_t self, void *aux)
121 {
122 struct mousebtn_softc *sc = device_private(self);
123 struct gpio_attach_args *ga = aux;
124 int caps;
125 struct wsmousedev_attach_args a;
126
127 sc->sc_dev = self;
128 sc->sc_gpio = ga->ga_gpio;
129
130 /* map pins */
131 sc->sc_map.pm_map = sc->sc_map_pins;
132 if (gpio_pin_map(sc->sc_gpio, ga->ga_offset, ga->ga_mask,
133 &sc->sc_map)) {
134 aprint_error(": couldn't map the pins\n");
135 return;
136 }
137
138 /* configure left pin */
139 caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_LEFT);
140 if (!(caps & GPIO_PIN_INPUT)) {
141 aprint_error(": pin is unable to read input\n");
142 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
143 return;
144 }
145 gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_LEFT,
146 GPIO_PIN_INPUT);
147
148 /* configure right pin */
149 caps = gpio_pin_caps(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_RIGHT);
150 if (!(caps & GPIO_PIN_INPUT)) {
151 aprint_error(": pin is unable to read input\n");
152 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
153 return;
154 }
155 gpio_pin_ctl(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_RIGHT,
156 GPIO_PIN_INPUT);
157
158 /* interrupt settings */
159 sc->sc_intr[0] = intr_establish(GPIO1_BASE + ga->ga_offset,
160 IPL_VM, IST_EDGE_BOTH, mousebtn_intr, sc);
161 if (sc->sc_intr[0] == NULL) {
162 aprint_error(": couldn't establish interrupt\n");
163 return;
164 }
165 sc->sc_intr[1] = intr_establish(GPIO1_BASE + ga->ga_offset + 1,
166 IPL_VM, IST_EDGE_BOTH, mousebtn_intr, sc);
167 if (sc->sc_intr[1] == NULL) {
168 aprint_error(": couldn't establish interrupt\n");
169 intr_disestablish(sc->sc_intr[0]);
170 return;
171 }
172
173 aprint_normal(": NetWalker mouse button\n");
174 aprint_naive(": NetWalker mouse button\n");
175
176 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
177 callout_init(&sc->sc_c, 0);
178
179 a.accessops = &mousebtn_accessops;
180 a.accesscookie = sc;
181
182 sc->sc_buttons = 0;
183 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE);
184 }
185
186 static int
mousebtn_detach(device_t self,int flags)187 mousebtn_detach(device_t self, int flags)
188 {
189 struct mousebtn_softc *sc = device_private(self);
190
191 if (sc->sc_intr[0] != NULL)
192 intr_disestablish(sc->sc_intr[0]);
193 if (sc->sc_intr[1] != NULL)
194 intr_disestablish(sc->sc_intr[1]);
195
196 gpio_pin_unmap(sc->sc_gpio, &sc->sc_map);
197
198 return 0;
199 }
200
201 static void
mousebtn_poll(void * arg)202 mousebtn_poll(void *arg)
203 {
204 struct mousebtn_softc *sc = (struct mousebtn_softc *)arg;
205 uint32_t buttons = 0;
206 int s;
207 int left;
208 int right;
209
210 mutex_enter(&sc->sc_lock);
211
212 left = !gpio_pin_read(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_LEFT);
213 right = !gpio_pin_read(sc->sc_gpio, &sc->sc_map, MOUSEBTN_PIN_RIGHT);
214 buttons = (right << 2) | left;
215
216 if (sc->sc_buttons != buttons) {
217 s = spltty();
218 wsmouse_input(sc->sc_wsmousedev, buttons, 0, 0, 0, 0,
219 WSMOUSE_INPUT_DELTA);
220 sc->sc_buttons = buttons;
221 splx(s);
222 }
223
224 mutex_exit(&sc->sc_lock);
225
226 #if defined(MOUSEBTN_POLLING)
227 if (sc->sc_enabled)
228 callout_reset(&sc->sc_c, POLLRATE, mousebtn_poll, sc);
229 #endif
230 return;
231 }
232
233 static int
mousebtn_intr(void * v)234 mousebtn_intr(void *v)
235 {
236 struct mousebtn_softc *sc = v;
237
238 if (sc->sc_enabled)
239 callout_reset(&sc->sc_c, POLLRATE, mousebtn_poll, sc);
240
241 return 1;
242 }
243
244
245 int
mousebtn_ioctl(void * v,u_long cmd,void * data,int flag,struct lwp * l)246 mousebtn_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
247 {
248 struct wsmouse_id *id;
249
250 switch (cmd) {
251 case WSMOUSEIO_GTYPE:
252 *(u_int *)data = WSMOUSE_TYPE_PS2;
253 return 0;
254 case WSMOUSEIO_GETID:
255 id = (struct wsmouse_id *)data;
256 if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
257 return EINVAL;
258 strlcpy(id->data, "NetWalker Mouse Button", WSMOUSE_ID_MAXLEN);
259 id->length = strlen(id->data);
260 return 0;
261 }
262
263 return EPASSTHROUGH;
264 }
265
266 int
mousebtn_enable(void * v)267 mousebtn_enable(void *v)
268 {
269 struct mousebtn_softc *sc = (struct mousebtn_softc *)v;
270
271 if (sc->sc_enabled)
272 return EBUSY;
273
274 if (!pmf_device_register(sc->sc_dev, mousebtn_suspend, mousebtn_resume))
275 aprint_error_dev(sc->sc_dev,
276 "couldn't establish power handler\n");
277
278 sc->sc_enabled = 1;
279 #if defined(MOUSEBTN_POLLING)
280 callout_reset(&sc->sc_c, POLLRATE, mousebtn_poll, sc);
281 #endif
282
283 return 0;
284 }
285
286 void
mousebtn_disable(void * v)287 mousebtn_disable(void *v)
288 {
289 struct mousebtn_softc *sc = (struct mousebtn_softc *)v;
290
291 if (!sc->sc_enabled)
292 return;
293
294 pmf_device_deregister(sc->sc_dev);
295
296 sc->sc_enabled = 0;
297
298 return;
299 }
300
301 static bool
mousebtn_suspend(device_t dv,const pmf_qual_t * qual)302 mousebtn_suspend(device_t dv, const pmf_qual_t *qual)
303 {
304 struct mousebtn_softc *sc = device_private(dv);
305
306 #if defined(MOUSEBTN_POLLING)
307 callout_stop(&sc->sc_c);
308 #endif
309 sc->sc_enabled = 0;
310
311 return true;
312 }
313
314 static bool
mousebtn_resume(device_t dv,const pmf_qual_t * qual)315 mousebtn_resume(device_t dv, const pmf_qual_t *qual)
316 {
317 struct mousebtn_softc *sc = device_private(dv);
318
319 sc->sc_enabled = 1;
320 #if defined(MOUSEBTN_POLLING)
321 callout_reset(&sc->sc_c, POLLRATE, mousebtn_poll, sc);
322 #endif
323 return true;
324 }
325
326