xref: /netbsd-src/sys/dev/spi/oj6sh.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: oj6sh.c,v 1.1 2014/03/29 12:00:27 hkenken 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 /*
30  * Sharp NetWalker's Optical Joystick
31  */
32 
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: oj6sh.c,v 1.1 2014/03/29 12:00:27 hkenken Exp $");
35 
36 #include "opt_oj6sh.h"
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/device.h>
42 #include <sys/lock.h>
43 #include <sys/callout.h>
44 #include <sys/bus.h>
45 #include <sys/mutex.h>
46 
47 #include <dev/wscons/wsconsio.h>
48 #include <dev/wscons/wsmousevar.h>
49 #include <dev/wscons/wsdisplayvar.h>
50 
51 #include <dev/hpc/hpcfbio.h>
52 #include <dev/hpc/hpctpanelvar.h>
53 
54 #include <dev/spi/spivar.h>
55 
56 #ifdef OJ6SH_DEBUG
57 int oj6sh_debug = OJ6SH_DEBUG;
58 #define DPRINTF(n,x)	if (oj6sh_debug>(n)) printf x;
59 #else
60 #define DPRINTF(n,x)
61 #endif
62 
63 #define POLLRATE (hz/30)
64 
65 /* register address */
66 #define OJ6SH_PRODUCT		0x00
67 #define OJ6SH_REVISION		0x01
68 #define OJ6SH_MOTION		0x02
69 #define OJ6SH_DELTA_X		0x03
70 #define OJ6SH_DELTA_Y		0x04
71 #define OJ6SH_SQUAL		0x05
72 #define OJ6SH_SHUTTER		0x06
73 #define OJ6SH_CONFIG		0x11
74 #define OJ6SH_RESET		0x3a
75 #define  POWERON_RESET		0x5a
76 #define OJ6SH_N_REVISION	0x3e
77 #define OJ6SH_N_PRODUCT		0x3f
78 
79 struct oj6sh_softc {
80 	device_t sc_dev;
81 
82 	struct spi_handle *sc_sh;
83 	struct callout sc_c;
84 
85 	kmutex_t sc_lock;
86 	int sc_enabled;
87 
88 	device_t sc_wsmousedev;
89 };
90 
91 struct oj6sh_delta {
92 	int x;
93 	int y;
94 };
95 
96 static uint8_t oj6sh_read(struct spi_handle *, uint8_t);
97 static void oj6sh_write(struct spi_handle *, uint8_t, uint8_t);
98 
99 static int oj6sh_match(device_t , cfdata_t , void *);
100 static void oj6sh_attach(device_t , device_t , void *);
101 
102 CFATTACH_DECL_NEW(oj6sh, sizeof(struct oj6sh_softc),
103     oj6sh_match, oj6sh_attach, NULL, NULL);
104 
105 static bool oj6sh_motion(struct spi_handle *);
106 static bool oj6sh_squal(struct spi_handle *);
107 static bool oj6sh_shuttrer(struct spi_handle *);
108 static int oj6sh_readdelta(struct spi_handle *, struct oj6sh_delta *);
109 
110 static void oj6sh_poll(void *);
111 static int oj6sh_enable(void *v);
112 static void oj6sh_disable(void *v);
113 static int oj6sh_ioctl(void *, u_long, void *, int, struct lwp *);
114 
115 static bool oj6sh_resume(device_t, const pmf_qual_t *);
116 static bool oj6sh_suspend(device_t, const pmf_qual_t *);
117 
118 static const struct wsmouse_accessops oj6sh_accessops = {
119 	.enable = oj6sh_enable,
120 	.ioctl = oj6sh_ioctl,
121 	.disable = oj6sh_disable
122 };
123 
124 static int
125 oj6sh_match(device_t parent, cfdata_t match, void *aux)
126 {
127 	struct spi_attach_args *sa = aux;
128 
129 	if (strcmp(match->cf_name, "oj6sh"))
130 		return 0;
131 	if (spi_configure(sa->sa_handle, SPI_MODE_0, 2500000))
132 		return 0;
133 
134 	return 2;
135 }
136 
137 static void
138 oj6sh_doattach(device_t self)
139 {
140 	struct oj6sh_softc *sc = device_private(self);
141 	uint8_t product;
142 	uint8_t rev;
143 	uint8_t product_inv;
144 	uint8_t rev_inv;
145 
146 	/* reset */
147 	oj6sh_write(sc->sc_sh, OJ6SH_RESET, POWERON_RESET);
148 	delay(10000);
149 
150 	/* resolution */
151 	oj6sh_write(sc->sc_sh, OJ6SH_CONFIG, 0x80);
152 
153 	product = oj6sh_read(sc->sc_sh, OJ6SH_PRODUCT);
154 	rev = oj6sh_read(sc->sc_sh, OJ6SH_REVISION);
155 	product_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_PRODUCT);
156 	rev_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_REVISION);
157 
158 	if (((product | product_inv) != 0xff) || ((rev | rev_inv) != 0xff)) {
159 		aprint_error_dev(self,
160 		    "mismatch product (%02x:%02x), rev (%02x:%02x)\n",
161 		    product, product_inv, rev, rev_inv);
162 		return;
163 	}
164 
165 	aprint_normal("%s: id 0x%02x, revision 0x%02x\n",
166 	    device_xname(sc->sc_dev), product, rev);
167 
168 	return;
169 }
170 
171 static void
172 oj6sh_attach(device_t parent, device_t self, void *aux)
173 {
174 	struct oj6sh_softc *sc = device_private(self);
175 	struct spi_attach_args *sa = aux;
176 	struct wsmousedev_attach_args a;
177 
178 	aprint_normal(": OJ6SH-T25 Optical Joystick\n");
179 
180 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
181 
182 	sc->sc_dev = self;
183 	sc->sc_enabled = 0;
184 
185 	callout_init(&sc->sc_c, 0);
186 
187 	sc->sc_sh = sa->sa_handle;
188 
189 	a.accessops = &oj6sh_accessops;
190 	a.accesscookie = sc;
191 
192 	sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint);
193 
194 	config_interrupts(self, oj6sh_doattach);
195 }
196 
197 static void
198 oj6sh_poll(void *arg)
199 {
200 	struct oj6sh_softc *sc = (struct oj6sh_softc *)arg;
201 	struct oj6sh_delta delta = {0, 0};
202 	uint32_t buttons = 0;
203 	int s;
204 	int x, y;
205 
206 	mutex_enter(&sc->sc_lock);
207 
208 	if (oj6sh_motion(sc->sc_sh) == false)
209 		goto out;
210 	else if ((oj6sh_squal(sc->sc_sh) == true) &&
211 	    (oj6sh_shuttrer(sc->sc_sh) == true))
212 		goto out;
213 
214 	oj6sh_readdelta(sc->sc_sh, &delta);
215 	DPRINTF(3,("%s: x = %d, y = %d\n", device_xname(sc->sc_dev),
216 		delta.x, delta.y));
217 
218 #if defined(J6SH_DOWN_Y_LEFT_X)
219 	y = -delta.y;
220 	x = -delta.x;
221 #elif defined(OJ6SH_UP_X_LEFT_Y)
222 	y = delta.x;
223 	x = -delta.y;
224 #elif defined(OJ6SH_DOWN_X_RIGHT_Y)
225 	y = -delta.x;
226 	x = delta.y;
227 #else /* OJ6SH_UP_Y_RIGHT_X */
228 	y = delta.y;
229 	x = delta.x;
230 #endif
231 	s = spltty();
232 	wsmouse_input(sc->sc_wsmousedev, buttons, x, y, 0, 0,
233 	    WSMOUSE_INPUT_DELTA);
234 	splx(s);
235 out:
236 	mutex_exit(&sc->sc_lock);
237 
238 	if (sc->sc_enabled)
239 		callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc);
240 
241 	return;
242 }
243 
244 static uint8_t
245 oj6sh_read(struct spi_handle *spi, uint8_t reg)
246 {
247 	uint8_t ret = 0;
248 
249 	spi_send_recv(spi, 1, &reg, 1, &ret);
250 	DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, ret));
251 	return ret;
252 }
253 
254 static void
255 oj6sh_write(struct spi_handle *spi, uint8_t reg, uint8_t val)
256 {
257 	uint8_t tmp[2] = {reg | 0x80, val};
258 
259 	spi_send(spi, 2, tmp);
260 	DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, val));
261 	return;
262 }
263 
264 static bool
265 oj6sh_motion(struct spi_handle *spi)
266 {
267 	uint16_t motion;
268 	motion = oj6sh_read(spi, OJ6SH_MOTION);
269 	return (motion & __BIT(7) ? true : false);
270 }
271 
272 static bool
273 oj6sh_squal(struct spi_handle *spi)
274 {
275 	uint16_t squal;
276 	squal = oj6sh_read(spi, OJ6SH_SQUAL);
277 	return (squal < 25 ? true : false);
278 }
279 
280 static bool
281 oj6sh_shuttrer(struct spi_handle *spi)
282 {
283 	uint16_t shutter;
284 	shutter = oj6sh_read(spi, OJ6SH_SHUTTER) << 8;
285 	shutter |= oj6sh_read(spi, OJ6SH_SHUTTER + 1);
286 	return (shutter > 600 ? true : false);
287 }
288 
289 static int
290 oj6sh_readdelta(struct spi_handle *spi, struct oj6sh_delta *delta)
291 {
292 	delta->x = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_X);
293 	delta->y = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_Y);
294 	return 0;
295 }
296 
297 int
298 oj6sh_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
299 {
300 	struct wsmouse_id *id;
301 
302 	switch (cmd) {
303 	case WSMOUSEIO_GTYPE:
304 		*(u_int *)data = WSMOUSE_TYPE_PS2;
305 		return 0;
306 	case WSMOUSEIO_GETID:
307 		id = (struct wsmouse_id *)data;
308 		if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
309 			return EINVAL;
310 		strlcpy(id->data, "OJ6SH-T25", WSMOUSE_ID_MAXLEN);
311 		id->length = strlen(id->data);
312 		return 0;
313 	}
314 
315 	return EPASSTHROUGH;
316 }
317 
318 int
319 oj6sh_enable(void *v)
320 {
321 	struct oj6sh_softc *sc = (struct oj6sh_softc *)v;
322 
323 	DPRINTF(3,("%s: oj6sh_enable()\n", device_xname(sc->sc_dev)));
324 	if (sc->sc_enabled) {
325 		DPRINTF(3,("%s: already enabled\n", device_xname(sc->sc_dev)));
326 		return EBUSY;
327 	}
328 
329 	if (!pmf_device_register(sc->sc_dev, oj6sh_suspend, oj6sh_resume))
330 		aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n");
331 
332 	sc->sc_enabled = 1;
333 	callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc);
334 
335 	return 0;
336 }
337 
338 void
339 oj6sh_disable(void *v)
340 {
341 	struct oj6sh_softc *sc = (struct oj6sh_softc *)v;
342 
343 	DPRINTF(3,("%s: oj6sh_disable()\n", device_xname(sc->sc_dev)));
344 	if (!sc->sc_enabled) {
345 		DPRINTF(3,("%s: already disabled()\n", device_xname(sc->sc_dev)));
346 		return;
347 	}
348 
349 	pmf_device_deregister(sc->sc_dev);
350 
351 	sc->sc_enabled = 0;
352 
353 	return;
354 }
355 
356 static bool
357 oj6sh_suspend(device_t dv, const pmf_qual_t *qual)
358 {
359 	struct oj6sh_softc *sc = device_private(dv);
360 
361 	DPRINTF(3,("%s: oj6sh_suspend()\n", device_xname(sc->sc_dev)));
362 	callout_stop(&sc->sc_c);
363 	sc->sc_enabled = 0;
364 
365 	return true;
366 }
367 
368 static bool
369 oj6sh_resume(device_t dv, const pmf_qual_t *qual)
370 {
371 	struct oj6sh_softc *sc = device_private(dv);
372 
373 	DPRINTF(3,("%s: oj6sh_resume()\n", device_xname(sc->sc_dev)));
374 	sc->sc_enabled = 1;
375 	callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc);
376 
377 	return true;
378 }
379 
380