1 /* $NetBSD: oj6sh.c,v 1.5 2019/08/21 08:03:22 martin 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.5 2019/08/21 08:03:22 martin 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 const struct device_compatible_entry compat_data[] = { 130 { "oj6sh", 0 }, 131 { NULL, 0 } 132 }; 133 134 static int 135 oj6sh_match(device_t parent, cfdata_t cf, void *aux) 136 { 137 struct spi_attach_args *sa = aux; 138 139 if (spi_compatible_match(sa, cf, compat_data) == 0) 140 return 0; 141 if (spi_configure(sa->sa_handle, SPI_MODE_0, 2500000)) 142 return 0; 143 144 return 2; 145 } 146 147 static void 148 oj6sh_doattach(device_t self) 149 { 150 struct oj6sh_softc *sc = device_private(self); 151 uint8_t product; 152 uint8_t rev; 153 uint8_t product_inv; 154 uint8_t rev_inv; 155 156 /* reset */ 157 oj6sh_write(sc->sc_sh, OJ6SH_RESET, POWERON_RESET); 158 delay(10000); 159 160 /* resolution */ 161 oj6sh_write(sc->sc_sh, OJ6SH_CONFIG, 0x80); 162 163 product = oj6sh_read(sc->sc_sh, OJ6SH_PRODUCT); 164 rev = oj6sh_read(sc->sc_sh, OJ6SH_REVISION); 165 product_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_PRODUCT); 166 rev_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_REVISION); 167 168 if (((product | product_inv) != 0xff) || ((rev | rev_inv) != 0xff)) { 169 aprint_error_dev(self, 170 "mismatch product (%02x:%02x), rev (%02x:%02x)\n", 171 product, product_inv, rev, rev_inv); 172 return; 173 } 174 175 aprint_normal("%s: id 0x%02x, revision 0x%02x\n", 176 device_xname(sc->sc_dev), product, rev); 177 178 return; 179 } 180 181 static void 182 oj6sh_attach(device_t parent, device_t self, void *aux) 183 { 184 struct oj6sh_softc *sc = device_private(self); 185 struct spi_attach_args *sa = aux; 186 struct wsmousedev_attach_args a; 187 188 aprint_naive("\n"); 189 aprint_normal(": OJ6SH-T25 Optical Joystick\n"); 190 191 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 192 193 sc->sc_dev = self; 194 sc->sc_enabled = 0; 195 196 callout_init(&sc->sc_c, 0); 197 workqueue_create(&sc->sc_wq, "oj6sh", 198 oj6sh_cb, sc, PRI_NONE, IPL_BIO, 0); 199 200 sc->sc_sh = sa->sa_handle; 201 202 a.accessops = &oj6sh_accessops; 203 a.accesscookie = sc; 204 205 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint); 206 207 config_interrupts(self, oj6sh_doattach); 208 } 209 210 static void 211 oj6sh_poll(void *arg) 212 { 213 struct oj6sh_softc *sc = (struct oj6sh_softc *)arg; 214 workqueue_enqueue(sc->sc_wq, &sc->sc_wk, NULL); 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(OJ6SH_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 if (sc->sc_enabled) 259 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 260 } 261 262 static uint8_t 263 oj6sh_read(struct spi_handle *spi, uint8_t reg) 264 { 265 uint8_t ret = 0; 266 267 spi_send_recv(spi, 1, ®, 1, &ret); 268 DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, ret)); 269 return ret; 270 } 271 272 static void 273 oj6sh_write(struct spi_handle *spi, uint8_t reg, uint8_t val) 274 { 275 uint8_t tmp[2] = {reg | 0x80, val}; 276 277 spi_send(spi, 2, tmp); 278 DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, val)); 279 return; 280 } 281 282 static bool 283 oj6sh_motion(struct spi_handle *spi) 284 { 285 uint16_t motion; 286 motion = oj6sh_read(spi, OJ6SH_MOTION); 287 return (motion & __BIT(7) ? true : false); 288 } 289 290 static bool 291 oj6sh_squal(struct spi_handle *spi) 292 { 293 uint16_t squal; 294 squal = oj6sh_read(spi, OJ6SH_SQUAL); 295 return (squal < 25 ? true : false); 296 } 297 298 static bool 299 oj6sh_shuttrer(struct spi_handle *spi) 300 { 301 uint16_t shutter; 302 shutter = oj6sh_read(spi, OJ6SH_SHUTTER) << 8; 303 shutter |= oj6sh_read(spi, OJ6SH_SHUTTER + 1); 304 return (shutter > 600 ? true : false); 305 } 306 307 static int 308 oj6sh_readdelta(struct spi_handle *spi, struct oj6sh_delta *delta) 309 { 310 delta->x = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_X); 311 delta->y = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_Y); 312 return 0; 313 } 314 315 int 316 oj6sh_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 317 { 318 struct wsmouse_id *id; 319 320 switch (cmd) { 321 case WSMOUSEIO_GTYPE: 322 *(u_int *)data = WSMOUSE_TYPE_PS2; 323 return 0; 324 case WSMOUSEIO_GETID: 325 id = (struct wsmouse_id *)data; 326 if (id->type != WSMOUSE_ID_TYPE_UIDSTR) 327 return EINVAL; 328 strlcpy(id->data, "OJ6SH-T25", WSMOUSE_ID_MAXLEN); 329 id->length = strlen(id->data); 330 return 0; 331 } 332 333 return EPASSTHROUGH; 334 } 335 336 int 337 oj6sh_enable(void *v) 338 { 339 struct oj6sh_softc *sc = (struct oj6sh_softc *)v; 340 341 DPRINTF(3,("%s: oj6sh_enable()\n", device_xname(sc->sc_dev))); 342 if (sc->sc_enabled) { 343 DPRINTF(3,("%s: already enabled\n", device_xname(sc->sc_dev))); 344 return EBUSY; 345 } 346 347 if (!pmf_device_register(sc->sc_dev, oj6sh_suspend, oj6sh_resume)) 348 aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n"); 349 350 sc->sc_enabled = 1; 351 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 352 353 return 0; 354 } 355 356 void 357 oj6sh_disable(void *v) 358 { 359 struct oj6sh_softc *sc = (struct oj6sh_softc *)v; 360 361 DPRINTF(3,("%s: oj6sh_disable()\n", device_xname(sc->sc_dev))); 362 if (!sc->sc_enabled) { 363 DPRINTF(3,("%s: already disabled()\n", device_xname(sc->sc_dev))); 364 return; 365 } 366 367 pmf_device_deregister(sc->sc_dev); 368 369 sc->sc_enabled = 0; 370 371 return; 372 } 373 374 static bool 375 oj6sh_suspend(device_t dv, const pmf_qual_t *qual) 376 { 377 struct oj6sh_softc *sc = device_private(dv); 378 379 DPRINTF(3,("%s: oj6sh_suspend()\n", device_xname(sc->sc_dev))); 380 callout_stop(&sc->sc_c); 381 sc->sc_enabled = 0; 382 383 return true; 384 } 385 386 static bool 387 oj6sh_resume(device_t dv, const pmf_qual_t *qual) 388 { 389 struct oj6sh_softc *sc = device_private(dv); 390 391 DPRINTF(3,("%s: oj6sh_resume()\n", device_xname(sc->sc_dev))); 392 sc->sc_enabled = 1; 393 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 394 395 return true; 396 } 397 398