1 /* $NetBSD: oj6sh.c,v 1.11 2022/01/19 05:21:44 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 /* 30 * Sharp NetWalker's Optical Joystick 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: oj6sh.c,v 1.11 2022/01/19 05:21:44 thorpej 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 { .compat = "oj6sh" }, 131 DEVICE_COMPAT_EOL 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 142 return 2; 143 } 144 145 static void 146 oj6sh_doattach(device_t self) 147 { 148 struct oj6sh_softc *sc = device_private(self); 149 uint8_t product; 150 uint8_t rev; 151 uint8_t product_inv; 152 uint8_t rev_inv; 153 154 /* reset */ 155 oj6sh_write(sc->sc_sh, OJ6SH_RESET, POWERON_RESET); 156 delay(10000); 157 158 /* resolution */ 159 oj6sh_write(sc->sc_sh, OJ6SH_CONFIG, 0x80); 160 161 product = oj6sh_read(sc->sc_sh, OJ6SH_PRODUCT); 162 rev = oj6sh_read(sc->sc_sh, OJ6SH_REVISION); 163 product_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_PRODUCT); 164 rev_inv = oj6sh_read(sc->sc_sh, OJ6SH_N_REVISION); 165 166 if (((product | product_inv) != 0xff) || ((rev | rev_inv) != 0xff)) { 167 aprint_error_dev(self, 168 "mismatch product (%02x:%02x), rev (%02x:%02x)\n", 169 product, product_inv, rev, rev_inv); 170 return; 171 } 172 173 aprint_normal("%s: id 0x%02x, revision 0x%02x\n", 174 device_xname(sc->sc_dev), product, rev); 175 176 return; 177 } 178 179 static void 180 oj6sh_attach(device_t parent, device_t self, void *aux) 181 { 182 struct oj6sh_softc *sc = device_private(self); 183 struct spi_attach_args *sa = aux; 184 struct wsmousedev_attach_args a; 185 int error; 186 187 aprint_naive("\n"); 188 aprint_normal(": OJ6SH-T25 Optical Joystick\n"); 189 190 error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 2500000); 191 if (error) { 192 return; 193 } 194 195 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); 196 197 sc->sc_dev = self; 198 sc->sc_enabled = 0; 199 200 callout_init(&sc->sc_c, 0); 201 workqueue_create(&sc->sc_wq, "oj6sh", 202 oj6sh_cb, sc, PRI_NONE, IPL_BIO, 0); 203 204 sc->sc_sh = sa->sa_handle; 205 206 a.accessops = &oj6sh_accessops; 207 a.accesscookie = sc; 208 209 sc->sc_wsmousedev = config_found(self, &a, wsmousedevprint, CFARGS_NONE); 210 211 config_interrupts(self, oj6sh_doattach); 212 } 213 214 static void 215 oj6sh_poll(void *arg) 216 { 217 struct oj6sh_softc *sc = (struct oj6sh_softc *)arg; 218 workqueue_enqueue(sc->sc_wq, &sc->sc_wk, NULL); 219 } 220 221 static void 222 oj6sh_cb(struct work *wk, void *arg) 223 { 224 struct oj6sh_softc *sc = (struct oj6sh_softc *)arg; 225 struct oj6sh_delta delta = {0, 0}; 226 uint32_t buttons = 0; 227 int s; 228 int x, y; 229 230 mutex_enter(&sc->sc_lock); 231 232 if (oj6sh_motion(sc->sc_sh) == false) 233 goto out; 234 else if ((oj6sh_squal(sc->sc_sh) == true) && 235 (oj6sh_shuttrer(sc->sc_sh) == true)) 236 goto out; 237 238 oj6sh_readdelta(sc->sc_sh, &delta); 239 DPRINTF(3,("%s: x = %d, y = %d\n", device_xname(sc->sc_dev), 240 delta.x, delta.y)); 241 242 #if defined(OJ6SH_DOWN_Y_LEFT_X) 243 y = -delta.y; 244 x = -delta.x; 245 #elif defined(OJ6SH_UP_X_LEFT_Y) 246 y = delta.x; 247 x = -delta.y; 248 #elif defined(OJ6SH_DOWN_X_RIGHT_Y) 249 y = -delta.x; 250 x = delta.y; 251 #else /* OJ6SH_UP_Y_RIGHT_X */ 252 y = delta.y; 253 x = delta.x; 254 #endif 255 s = spltty(); 256 wsmouse_input(sc->sc_wsmousedev, buttons, x, y, 0, 0, 257 WSMOUSE_INPUT_DELTA); 258 splx(s); 259 out: 260 mutex_exit(&sc->sc_lock); 261 262 if (sc->sc_enabled) 263 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 264 } 265 266 static uint8_t 267 oj6sh_read(struct spi_handle *spi, uint8_t reg) 268 { 269 uint8_t ret = 0; 270 271 spi_send_recv(spi, 1, ®, 1, &ret); 272 DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, ret)); 273 return ret; 274 } 275 276 static void 277 oj6sh_write(struct spi_handle *spi, uint8_t reg, uint8_t val) 278 { 279 uint8_t tmp[2] = {reg | 0x80, val}; 280 281 spi_send(spi, 2, tmp); 282 DPRINTF(4,("%s: 0x%02x = 0x%02x\n", __func__, reg, val)); 283 return; 284 } 285 286 static bool 287 oj6sh_motion(struct spi_handle *spi) 288 { 289 uint16_t motion; 290 motion = oj6sh_read(spi, OJ6SH_MOTION); 291 return (motion & __BIT(7) ? true : false); 292 } 293 294 static bool 295 oj6sh_squal(struct spi_handle *spi) 296 { 297 uint16_t squal; 298 squal = oj6sh_read(spi, OJ6SH_SQUAL); 299 return (squal < 25 ? true : false); 300 } 301 302 static bool 303 oj6sh_shuttrer(struct spi_handle *spi) 304 { 305 uint16_t shutter; 306 shutter = oj6sh_read(spi, OJ6SH_SHUTTER) << 8; 307 shutter |= oj6sh_read(spi, OJ6SH_SHUTTER + 1); 308 return (shutter > 600 ? true : false); 309 } 310 311 static int 312 oj6sh_readdelta(struct spi_handle *spi, struct oj6sh_delta *delta) 313 { 314 delta->x = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_X); 315 delta->y = (int8_t)oj6sh_read(spi, OJ6SH_DELTA_Y); 316 return 0; 317 } 318 319 int 320 oj6sh_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 321 { 322 struct wsmouse_id *id; 323 324 switch (cmd) { 325 case WSMOUSEIO_GTYPE: 326 *(u_int *)data = WSMOUSE_TYPE_PS2; 327 return 0; 328 case WSMOUSEIO_GETID: 329 id = (struct wsmouse_id *)data; 330 if (id->type != WSMOUSE_ID_TYPE_UIDSTR) 331 return EINVAL; 332 strlcpy(id->data, "OJ6SH-T25", WSMOUSE_ID_MAXLEN); 333 id->length = strlen(id->data); 334 return 0; 335 } 336 337 return EPASSTHROUGH; 338 } 339 340 int 341 oj6sh_enable(void *v) 342 { 343 struct oj6sh_softc *sc = (struct oj6sh_softc *)v; 344 345 DPRINTF(3,("%s: oj6sh_enable()\n", device_xname(sc->sc_dev))); 346 if (sc->sc_enabled) { 347 DPRINTF(3,("%s: already enabled\n", device_xname(sc->sc_dev))); 348 return EBUSY; 349 } 350 351 if (!pmf_device_register(sc->sc_dev, oj6sh_suspend, oj6sh_resume)) 352 aprint_error_dev(sc->sc_dev, "couldn't establish power handler\n"); 353 354 sc->sc_enabled = 1; 355 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 356 357 return 0; 358 } 359 360 void 361 oj6sh_disable(void *v) 362 { 363 struct oj6sh_softc *sc = (struct oj6sh_softc *)v; 364 365 DPRINTF(3,("%s: oj6sh_disable()\n", device_xname(sc->sc_dev))); 366 if (!sc->sc_enabled) { 367 DPRINTF(3,("%s: already disabled()\n", device_xname(sc->sc_dev))); 368 return; 369 } 370 371 pmf_device_deregister(sc->sc_dev); 372 373 sc->sc_enabled = 0; 374 375 return; 376 } 377 378 static bool 379 oj6sh_suspend(device_t dv, const pmf_qual_t *qual) 380 { 381 struct oj6sh_softc *sc = device_private(dv); 382 383 DPRINTF(3,("%s: oj6sh_suspend()\n", device_xname(sc->sc_dev))); 384 callout_stop(&sc->sc_c); 385 sc->sc_enabled = 0; 386 387 return true; 388 } 389 390 static bool 391 oj6sh_resume(device_t dv, const pmf_qual_t *qual) 392 { 393 struct oj6sh_softc *sc = device_private(dv); 394 395 DPRINTF(3,("%s: oj6sh_resume()\n", device_xname(sc->sc_dev))); 396 sc->sc_enabled = 1; 397 callout_reset(&sc->sc_c, POLLRATE, oj6sh_poll, sc); 398 399 return true; 400 } 401 402