1 /*- 2 * (MPSAFE) 3 * 4 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 5 * All rights reserved. 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 as 12 * the first lines of this file unmodified. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD: src/sys/dev/syscons/sysmouse.c,v 1.2.2.2 2001/07/16 05:21:24 yokota Exp $ 29 */ 30 31 /* MPSAFE NOTE: Take care with locking in sysmouse_event which is called 32 * from syscons. 33 */ 34 #include "opt_syscons.h" 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/conf.h> 39 #include <sys/device.h> 40 #include <sys/event.h> 41 #include <sys/uio.h> 42 #include <sys/priv.h> 43 #include <sys/vnode.h> 44 #include <sys/kernel.h> 45 #include <sys/thread2.h> 46 #include <sys/signalvar.h> 47 #include <sys/filio.h> 48 49 #include <machine/console.h> 50 #include <sys/mouse.h> 51 52 #include "syscons.h" 53 54 #ifndef SC_NO_SYSMOUSE 55 56 #define FIFO_SIZE 256 57 58 struct event_fifo { 59 mouse_info_t buf[FIFO_SIZE]; 60 int start; 61 int fill; 62 unsigned int dropped; 63 }; 64 65 struct sysmouse_state { 66 struct event_fifo *fifo; 67 int level; /* sysmouse protocol level */ 68 mousestatus_t syncstatus; 69 mousestatus_t readstatus; /* Only needed for button status */ 70 int opened; 71 int asyncio; 72 struct lock sm_lock; 73 struct sigio *sm_sigio; 74 struct kqinfo rkq; 75 }; 76 77 static d_open_t smopen; 78 static d_close_t smclose; 79 static d_read_t smread; 80 static d_ioctl_t smioctl; 81 static d_kqfilter_t smkqfilter; 82 83 static struct dev_ops sm_ops = { 84 { "sysmouse", 0, D_MPSAFE }, 85 .d_open = smopen, 86 .d_close = smclose, 87 .d_read = smread, 88 .d_ioctl = smioctl, 89 .d_kqfilter = smkqfilter, 90 }; 91 92 /* local variables */ 93 static struct sysmouse_state mouse_state; 94 95 static int sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info, 96 u_char *buf); 97 static void smqueue(struct sysmouse_state *sc, mouse_info_t *info); 98 static int pktlen(struct sysmouse_state *sc); 99 static void smfilter_detach(struct knote *); 100 static int smfilter(struct knote *, long); 101 static void smget(struct sysmouse_state *sc, mouse_info_t *info); 102 static void smpop(struct sysmouse_state *sc); 103 104 static int 105 pktlen(struct sysmouse_state *sc) 106 { 107 if (sc->level == 0) 108 return 5; 109 else 110 return 8; 111 } 112 113 static int 114 smopen(struct dev_open_args *ap) 115 { 116 cdev_t dev = ap->a_head.a_dev; 117 struct sysmouse_state *sc = &mouse_state; 118 int ret; 119 120 DPRINTF(5, ("smopen: dev:%d,%d\n", 121 major(dev), minor(dev))); 122 123 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 124 if (!sc->opened) { 125 sc->fifo = kmalloc(sizeof(struct event_fifo), 126 M_SYSCONS, M_WAITOK | M_ZERO); 127 sc->opened = 1; 128 sc->asyncio = 0; 129 sc->sm_sigio = NULL; 130 bzero(&sc->readstatus, sizeof(sc->readstatus)); 131 bzero(&sc->syncstatus, sizeof(sc->syncstatus)); 132 ret = 0; 133 } else { 134 ret = EBUSY; 135 } 136 lockmgr(&sc->sm_lock, LK_RELEASE); 137 138 return ret; 139 } 140 141 static int 142 smclose(struct dev_close_args *ap) 143 { 144 struct sysmouse_state *sc = &mouse_state; 145 146 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 147 funsetown(&sc->sm_sigio); 148 sc->opened = 0; 149 sc->asyncio = 0; 150 sc->level = 0; 151 kfree(sc->fifo, M_SYSCONS); 152 sc->fifo = NULL; 153 lockmgr(&sc->sm_lock, LK_RELEASE); 154 155 return 0; 156 } 157 158 static int 159 smread(struct dev_read_args *ap) 160 { 161 struct sysmouse_state *sc = &mouse_state; 162 mousestatus_t backupstatus; 163 mouse_info_t info; 164 u_char buf[8]; 165 struct uio *uio = ap->a_uio; 166 int error = 0, val, cnt = 0; 167 168 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 169 while (sc->fifo->fill <= 0) { 170 /* Buffer too small to fit a complete mouse packet */ 171 if (uio->uio_resid < pktlen(sc)) { 172 error = EIO; 173 goto done; 174 } 175 if (ap->a_ioflag & IO_NDELAY) { 176 error = EAGAIN; 177 goto done; 178 } 179 error = lksleep(sc, &sc->sm_lock, PCATCH, "smread", 0); 180 if (error == EINTR || error == ERESTART) { 181 goto done; 182 } 183 } 184 185 do { 186 /* Buffer too small to fit a complete mouse packet */ 187 if (uio->uio_resid < pktlen(sc)) { 188 error = EIO; 189 goto done; 190 } 191 smget(sc, &info); 192 backupstatus = sc->readstatus; 193 val = sysmouse_evtopkt(sc, &info, buf); 194 if (val > 0) { 195 error = uiomove(buf, val, uio); 196 if (error != 0) { 197 sc->readstatus = backupstatus; 198 goto done; 199 } 200 cnt++; 201 } 202 smpop(sc); 203 } while (sc->fifo->fill > 0); 204 205 done: 206 lockmgr(&sc->sm_lock, LK_RELEASE); 207 if (cnt > 0 && error != EFAULT) 208 return 0; 209 return error; 210 } 211 212 static int 213 smioctl(struct dev_ioctl_args *ap) 214 { 215 struct sysmouse_state *sc = &mouse_state; 216 mousehw_t *hw; 217 mousemode_t *mode; 218 219 switch (ap->a_cmd) { 220 case FIOSETOWN: 221 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 222 fsetown(*(int *)ap->a_data, &sc->sm_sigio); 223 lockmgr(&sc->sm_lock, LK_RELEASE); 224 return 0; 225 case FIOGETOWN: 226 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 227 *(int *)ap->a_data = fgetown(&sc->sm_sigio); 228 lockmgr(&sc->sm_lock, LK_RELEASE); 229 return 0; 230 case FIOASYNC: 231 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 232 if (*(int *)ap->a_data) { 233 sc->asyncio = 1; 234 } else { 235 sc->asyncio = 0; 236 } 237 lockmgr(&sc->sm_lock, LK_RELEASE); 238 return 0; 239 case MOUSE_GETHWINFO: /* get device information */ 240 hw = (mousehw_t *)ap->a_data; 241 hw->buttons = 10; /* XXX unknown */ 242 hw->iftype = MOUSE_IF_SYSMOUSE; 243 hw->type = MOUSE_MOUSE; 244 hw->model = MOUSE_MODEL_GENERIC; 245 hw->hwid = 0; 246 return 0; 247 248 case MOUSE_GETMODE: /* get protocol/mode */ 249 mode = (mousemode_t *)ap->a_data; 250 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 251 mode->level = sc->level; 252 lockmgr(&sc->sm_lock, LK_RELEASE); 253 switch (mode->level) { 254 case 0: /* emulate MouseSystems protocol */ 255 mode->protocol = MOUSE_PROTO_MSC; 256 mode->rate = -1; /* unknown */ 257 mode->resolution = -1; /* unknown */ 258 mode->accelfactor = 0; /* disabled */ 259 mode->packetsize = MOUSE_MSC_PACKETSIZE; 260 mode->syncmask[0] = MOUSE_MSC_SYNCMASK; 261 mode->syncmask[1] = MOUSE_MSC_SYNC; 262 break; 263 264 case 1: /* sysmouse protocol */ 265 mode->protocol = MOUSE_PROTO_SYSMOUSE; 266 mode->rate = -1; 267 mode->resolution = -1; 268 mode->accelfactor = 0; 269 mode->packetsize = MOUSE_SYS_PACKETSIZE; 270 mode->syncmask[0] = MOUSE_SYS_SYNCMASK; 271 mode->syncmask[1] = MOUSE_SYS_SYNC; 272 break; 273 } 274 return 0; 275 276 case MOUSE_SETMODE: /* set protocol/mode */ 277 mode = (mousemode_t *)ap->a_data; 278 if (mode->level == -1) 279 ; /* don't change the current setting */ 280 else if ((mode->level < 0) || (mode->level > 1)) { 281 return EINVAL; 282 } else { 283 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 284 sc->level = mode->level; 285 lockmgr(&sc->sm_lock, LK_RELEASE); 286 } 287 return 0; 288 289 case MOUSE_GETLEVEL: /* get operation level */ 290 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 291 *(int *)ap->a_data = sc->level; 292 lockmgr(&sc->sm_lock, LK_RELEASE); 293 return 0; 294 295 case MOUSE_SETLEVEL: /* set operation level */ 296 if ((*(int *)ap->a_data < 0) || (*(int *)ap->a_data > 1)) { 297 return EINVAL; 298 } 299 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 300 sc->level = *(int *)ap->a_data; 301 lockmgr(&sc->sm_lock, LK_RELEASE); 302 return 0; 303 304 case MOUSE_GETSTATUS: /* get accumulated mouse events */ 305 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 306 *(mousestatus_t *)ap->a_data = sc->syncstatus; 307 sc->syncstatus.flags = 0; 308 sc->syncstatus.obutton = sc->syncstatus.button; 309 sc->syncstatus.dx = 0; 310 sc->syncstatus.dy = 0; 311 sc->syncstatus.dz = 0; 312 lockmgr(&sc->sm_lock, LK_RELEASE); 313 return 0; 314 315 #if 0 /* notyet */ 316 case MOUSE_GETVARS: /* get internal mouse variables */ 317 case MOUSE_SETVARS: /* set internal mouse variables */ 318 return ENODEV; 319 #endif 320 321 case MOUSE_READSTATE: /* read status from the device */ 322 case MOUSE_READDATA: /* read data from the device */ 323 return ENODEV; 324 } 325 326 return ENOTTY; 327 } 328 329 static struct filterops smfiltops = 330 { FILTEROP_MPSAFE | FILTEROP_ISFD, NULL, smfilter_detach, smfilter }; 331 332 static int 333 smkqfilter(struct dev_kqfilter_args *ap) 334 { 335 struct sysmouse_state *sc = &mouse_state; 336 struct knote *kn = ap->a_kn; 337 struct klist *klist; 338 339 ap->a_result = 0; 340 341 switch (kn->kn_filter) { 342 case EVFILT_READ: 343 kn->kn_fop = &smfiltops; 344 kn->kn_hook = (caddr_t)sc; 345 break; 346 default: 347 ap->a_result = EOPNOTSUPP; 348 return (0); 349 } 350 351 klist = &sc->rkq.ki_note; 352 knote_insert(klist, kn); 353 354 return (0); 355 } 356 357 static void 358 smfilter_detach(struct knote *kn) 359 { 360 struct sysmouse_state *sc = &mouse_state; 361 struct klist *klist; 362 363 klist = &sc->rkq.ki_note; 364 knote_remove(klist, kn); 365 } 366 367 static int 368 smfilter(struct knote *kn, long hint) 369 { 370 struct sysmouse_state *sc = &mouse_state; 371 int ready = 0; 372 373 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 374 if (sc->fifo->fill > 0) { 375 ready = 1; 376 kn->kn_data = 0; 377 } 378 lockmgr(&sc->sm_lock, LK_RELEASE); 379 380 return ready; 381 } 382 383 static void 384 smqueue(struct sysmouse_state *sc, mouse_info_t *info) 385 { 386 struct event_fifo *f = sc->fifo; 387 388 if (f->fill >= FIFO_SIZE) { 389 f->fill = FIFO_SIZE; 390 f->buf[f->start] = *info; 391 f->start = (f->start + 1) % FIFO_SIZE; 392 f->dropped++; 393 } else { 394 f->buf[(f->start + f->fill) % FIFO_SIZE] = *info; 395 f->fill++; 396 } 397 398 } 399 400 static void 401 smget(struct sysmouse_state *sc, mouse_info_t *info) 402 { 403 struct event_fifo *f = sc->fifo; 404 405 if (f->fill > 0) 406 *info = f->buf[f->start]; 407 } 408 409 static void 410 smpop(struct sysmouse_state *sc) 411 { 412 struct event_fifo *f = sc->fifo; 413 414 if (f->fill > 0) { 415 f->fill--; 416 f->start = (f->start + 1) % FIFO_SIZE; 417 } 418 } 419 420 static void 421 sm_attach_mouse(void *unused) 422 { 423 struct sysmouse_state *sc = &mouse_state; 424 cdev_t dev; 425 426 lockinit(&mouse_state.sm_lock, "sysmouse", 0, LK_CANRECURSE); 427 sc->fifo = NULL; 428 429 dev = make_dev(&sm_ops, 0, UID_ROOT, GID_WHEEL, 0600, "sysmouse"); 430 } 431 432 SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_ANY, sm_attach_mouse, NULL); 433 434 static int 435 sysmouse_updatestatus(mousestatus_t *status, mouse_info_t *info) 436 { 437 int x, y, z; 438 439 status->obutton = status->button; 440 441 switch (info->operation) { 442 case MOUSE_ACTION: 443 status->button = info->u.data.buttons; 444 /* FALL THROUGH */ 445 case MOUSE_MOTION_EVENT: 446 x = info->u.data.x; 447 y = info->u.data.y; 448 z = info->u.data.z; 449 break; 450 case MOUSE_BUTTON_EVENT: 451 x = y = z = 0; 452 if (info->u.event.value > 0) 453 status->button |= info->u.event.id; 454 else 455 status->button &= ~info->u.event.id; 456 break; 457 default: 458 return 0; 459 } 460 461 status->dx += x; 462 status->dy += y; 463 status->dz += z; 464 status->flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) 465 | (status->obutton ^ status->button); 466 467 return 1; 468 } 469 470 /* Requires buf to hold at least 8 bytes, returns number of bytes written */ 471 static int 472 sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info, u_char *buf) 473 { 474 /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ 475 static int butmap[8] = { 476 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 477 MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 478 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, 479 MOUSE_MSC_BUTTON3UP, 480 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, 481 MOUSE_MSC_BUTTON2UP, 482 MOUSE_MSC_BUTTON1UP, 483 0, 484 }; 485 int x, y, z; 486 487 sc->readstatus.dx = 0; 488 sc->readstatus.dy = 0; 489 sc->readstatus.dz = 0; 490 sc->readstatus.flags = 0; 491 if (sysmouse_updatestatus(&sc->readstatus, info) == 0) 492 return 0; 493 494 /* We aren't using the sc->readstatus.dx/dy/dz values */ 495 496 if (sc->readstatus.flags == 0) 497 return 0; 498 499 x = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.x); 500 y = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.y); 501 z = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.z); 502 503 /* the first five bytes are compatible with MouseSystems' */ 504 buf[0] = MOUSE_MSC_SYNC 505 | butmap[sc->readstatus.button & MOUSE_STDBUTTONS]; 506 x = imax(imin(x, 255), -256); 507 buf[1] = x >> 1; 508 buf[3] = x - buf[1]; 509 y = -imax(imin(y, 255), -256); 510 buf[2] = y >> 1; 511 buf[4] = y - buf[2]; 512 if (sc->level >= 1) { 513 /* extended part */ 514 z = imax(imin(z, 127), -128); 515 buf[5] = (z >> 1) & 0x7f; 516 buf[6] = (z - (z >> 1)) & 0x7f; 517 /* buttons 4-10 */ 518 buf[7] = (~sc->readstatus.button >> 3) & 0x7f; 519 } 520 521 if (sc->level >= 1) 522 return 8; 523 524 return 5; 525 } 526 527 int 528 sysmouse_event(mouse_info_t *info) 529 { 530 struct sysmouse_state *sc = &mouse_state; 531 int ret; 532 533 lockmgr(&sc->sm_lock, LK_EXCLUSIVE); 534 ret = sysmouse_updatestatus(&sc->syncstatus, info); 535 if (ret != 0) 536 ret = sc->syncstatus.flags; 537 if (!sc->opened) { 538 lockmgr(&sc->sm_lock, LK_RELEASE); 539 return ret; 540 } 541 542 switch (info->operation) { 543 case MOUSE_ACTION: 544 case MOUSE_MOTION_EVENT: 545 case MOUSE_BUTTON_EVENT: 546 smqueue(sc, info); 547 if (sc->asyncio) 548 pgsigio(sc->sm_sigio, SIGIO, 0); 549 lockmgr(&sc->sm_lock, LK_RELEASE); 550 wakeup(sc); 551 KNOTE(&sc->rkq.ki_note, 0); 552 break; 553 default: 554 lockmgr(&sc->sm_lock, LK_RELEASE); 555 break; 556 } 557 558 return ret; 559 } 560 561 #endif /* !SC_NO_SYSMOUSE */ 562