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