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, ®, 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