1 /*- 2 * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3 * All rights reserved. 4 * 5 * Copyright (c) 2009 The FreeBSD Foundation 6 * All rights reserved. 7 * 8 * This software was developed by Ed Schouten under sponsorship from the 9 * FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "opt_evdev.h" 37 38 #include <sys/param.h> 39 #include <sys/condvar.h> 40 #include <sys/consio.h> 41 #include <sys/fcntl.h> 42 #include <sys/filio.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/poll.h> 46 #include <sys/random.h> 47 #include <sys/selinfo.h> 48 #include <sys/sigio.h> 49 #include <sys/signalvar.h> 50 #include <sys/systm.h> 51 #include <sys/uio.h> 52 53 #include <dev/vt/vt.h> 54 55 #ifdef EVDEV_SUPPORT 56 #include <dev/evdev/input.h> 57 #include <dev/evdev/evdev.h> 58 #endif 59 60 static d_open_t sysmouse_open; 61 static d_close_t sysmouse_close; 62 static d_read_t sysmouse_read; 63 static d_ioctl_t sysmouse_ioctl; 64 static d_poll_t sysmouse_poll; 65 66 static struct cdevsw sysmouse_cdevsw = { 67 .d_version = D_VERSION, 68 .d_open = sysmouse_open, 69 .d_close = sysmouse_close, 70 .d_read = sysmouse_read, 71 .d_ioctl = sysmouse_ioctl, 72 .d_poll = sysmouse_poll, 73 .d_name = "sysmouse", 74 }; 75 76 static struct mtx sysmouse_lock; 77 static struct cv sysmouse_sleep; 78 static struct selinfo sysmouse_bufpoll; 79 80 static int sysmouse_level; 81 static mousestatus_t sysmouse_status; 82 static int sysmouse_flags; 83 #define SM_ASYNC 0x1 84 static struct sigio *sysmouse_sigio; 85 86 #define SYSMOUSE_MAXFRAMES 250 /* 2 KB */ 87 static MALLOC_DEFINE(M_SYSMOUSE, "sysmouse", "sysmouse device"); 88 static unsigned char *sysmouse_buffer; 89 static unsigned int sysmouse_start, sysmouse_length; 90 91 #ifdef EVDEV_SUPPORT 92 static struct evdev_dev *sysmouse_evdev; 93 94 static void 95 sysmouse_evdev_init(void) 96 { 97 int i; 98 99 sysmouse_evdev = evdev_alloc(); 100 evdev_set_name(sysmouse_evdev, "System mouse"); 101 evdev_set_phys(sysmouse_evdev, "sysmouse"); 102 evdev_set_id(sysmouse_evdev, BUS_VIRTUAL, 0, 0, 0); 103 evdev_support_prop(sysmouse_evdev, INPUT_PROP_POINTER); 104 evdev_support_event(sysmouse_evdev, EV_SYN); 105 evdev_support_event(sysmouse_evdev, EV_REL); 106 evdev_support_event(sysmouse_evdev, EV_KEY); 107 evdev_support_rel(sysmouse_evdev, REL_X); 108 evdev_support_rel(sysmouse_evdev, REL_Y); 109 evdev_support_rel(sysmouse_evdev, REL_WHEEL); 110 evdev_support_rel(sysmouse_evdev, REL_HWHEEL); 111 for (i = 0; i < 8; i++) 112 evdev_support_key(sysmouse_evdev, BTN_MOUSE + i); 113 if (evdev_register(sysmouse_evdev)) { 114 evdev_free(sysmouse_evdev); 115 sysmouse_evdev = NULL; 116 } 117 } 118 119 static void 120 sysmouse_evdev_store(int x, int y, int z, int buttons) 121 { 122 123 if (sysmouse_evdev == NULL || !(evdev_rcpt_mask & EVDEV_RCPT_SYSMOUSE)) 124 return; 125 126 evdev_push_event(sysmouse_evdev, EV_REL, REL_X, x); 127 evdev_push_event(sysmouse_evdev, EV_REL, REL_Y, y); 128 switch (evdev_sysmouse_t_axis) { 129 case EVDEV_SYSMOUSE_T_AXIS_PSM: 130 switch (z) { 131 case 1: 132 case -1: 133 evdev_push_rel(sysmouse_evdev, REL_WHEEL, -z); 134 break; 135 case 2: 136 case -2: 137 evdev_push_rel(sysmouse_evdev, REL_HWHEEL, z / 2); 138 break; 139 } 140 break; 141 case EVDEV_SYSMOUSE_T_AXIS_UMS: 142 /* XXX: Edge triggering should be used here */ 143 if (buttons & (1 << 5)) 144 evdev_push_rel(sysmouse_evdev, REL_HWHEEL, 1); 145 else if (buttons & (1 << 6)) 146 evdev_push_rel(sysmouse_evdev, REL_HWHEEL, -1); 147 /* PASSTHROUGH */ 148 case EVDEV_SYSMOUSE_T_AXIS_NONE: 149 default: 150 evdev_push_rel(sysmouse_evdev, REL_WHEEL, -z); 151 } 152 evdev_push_mouse_btn(sysmouse_evdev, buttons); 153 evdev_sync(sysmouse_evdev); 154 } 155 #endif 156 157 static int 158 sysmouse_buf_read(struct uio *uio, unsigned int length) 159 { 160 unsigned char buf[MOUSE_SYS_PACKETSIZE]; 161 int error; 162 163 if (sysmouse_buffer == NULL) 164 return (ENXIO); 165 else if (sysmouse_length == 0) 166 return (EWOULDBLOCK); 167 168 memcpy(buf, sysmouse_buffer + 169 sysmouse_start * MOUSE_SYS_PACKETSIZE, MOUSE_SYS_PACKETSIZE); 170 sysmouse_start = (sysmouse_start + 1) % SYSMOUSE_MAXFRAMES; 171 sysmouse_length--; 172 173 mtx_unlock(&sysmouse_lock); 174 error = uiomove(buf, length, uio); 175 mtx_lock(&sysmouse_lock); 176 177 return (error); 178 } 179 180 static void 181 sysmouse_buf_store(const unsigned char buf[MOUSE_SYS_PACKETSIZE]) 182 { 183 unsigned int idx; 184 185 if (sysmouse_buffer == NULL || sysmouse_length == SYSMOUSE_MAXFRAMES) 186 return; 187 188 idx = (sysmouse_start + sysmouse_length) % SYSMOUSE_MAXFRAMES; 189 memcpy(sysmouse_buffer + idx * MOUSE_SYS_PACKETSIZE, buf, 190 MOUSE_SYS_PACKETSIZE); 191 sysmouse_length++; 192 cv_broadcast(&sysmouse_sleep); 193 selwakeup(&sysmouse_bufpoll); 194 if (sysmouse_flags & SM_ASYNC && sysmouse_sigio != NULL) 195 pgsigio(&sysmouse_sigio, SIGIO, 0); 196 } 197 198 void 199 sysmouse_process_event(mouse_info_t *mi) 200 { 201 /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ 202 static const int buttonmap[8] = { 203 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 204 MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 205 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, 206 MOUSE_MSC_BUTTON3UP, 207 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, 208 MOUSE_MSC_BUTTON2UP, 209 MOUSE_MSC_BUTTON1UP, 210 0, 211 }; 212 unsigned char buf[MOUSE_SYS_PACKETSIZE]; 213 int x, y, iy, z; 214 215 random_harvest_queue(mi, sizeof *mi, 2, RANDOM_MOUSE); 216 217 mtx_lock(&sysmouse_lock); 218 switch (mi->operation) { 219 case MOUSE_ACTION: 220 sysmouse_status.button = mi->u.data.buttons; 221 /* FALLTHROUGH */ 222 case MOUSE_MOTION_EVENT: 223 x = mi->u.data.x; 224 y = mi->u.data.y; 225 z = mi->u.data.z; 226 break; 227 case MOUSE_BUTTON_EVENT: 228 x = y = z = 0; 229 if (mi->u.event.value > 0) 230 sysmouse_status.button |= mi->u.event.id; 231 else 232 sysmouse_status.button &= ~mi->u.event.id; 233 break; 234 default: 235 goto done; 236 } 237 238 sysmouse_status.dx += x; 239 sysmouse_status.dy += y; 240 sysmouse_status.dz += z; 241 sysmouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) | 242 (sysmouse_status.obutton ^ sysmouse_status.button); 243 if (sysmouse_status.flags == 0) 244 goto done; 245 246 #ifdef EVDEV_SUPPORT 247 sysmouse_evdev_store(x, y, z, sysmouse_status.button); 248 #endif 249 250 /* The first five bytes are compatible with MouseSystems. */ 251 buf[0] = MOUSE_MSC_SYNC | 252 buttonmap[sysmouse_status.button & MOUSE_STDBUTTONS]; 253 x = imax(imin(x, 255), -256); 254 buf[1] = x >> 1; 255 buf[3] = x - buf[1]; 256 iy = -imax(imin(y, 255), -256); 257 buf[2] = iy >> 1; 258 buf[4] = iy - buf[2]; 259 /* Extended part. */ 260 z = imax(imin(z, 127), -128); 261 buf[5] = (z >> 1) & 0x7f; 262 buf[6] = (z - (z >> 1)) & 0x7f; 263 /* Buttons 4-10. */ 264 buf[7] = (~sysmouse_status.button >> 3) & 0x7f; 265 266 sysmouse_buf_store(buf); 267 268 #ifndef SC_NO_CUTPASTE 269 mtx_unlock(&sysmouse_lock); 270 vt_mouse_event(mi->operation, x, y, mi->u.event.id, mi->u.event.value, 271 sysmouse_level); 272 return; 273 #endif 274 275 done: mtx_unlock(&sysmouse_lock); 276 } 277 278 static int 279 sysmouse_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 280 { 281 void *buf; 282 283 buf = malloc(MOUSE_SYS_PACKETSIZE * SYSMOUSE_MAXFRAMES, 284 M_SYSMOUSE, M_WAITOK); 285 mtx_lock(&sysmouse_lock); 286 if (sysmouse_buffer == NULL) { 287 sysmouse_buffer = buf; 288 sysmouse_start = sysmouse_length = 0; 289 sysmouse_level = 0; 290 } else { 291 free(buf, M_SYSMOUSE); 292 } 293 mtx_unlock(&sysmouse_lock); 294 295 return (0); 296 } 297 298 static int 299 sysmouse_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 300 { 301 302 mtx_lock(&sysmouse_lock); 303 free(sysmouse_buffer, M_SYSMOUSE); 304 sysmouse_buffer = NULL; 305 sysmouse_level = 0; 306 mtx_unlock(&sysmouse_lock); 307 308 return (0); 309 } 310 311 static int 312 sysmouse_read(struct cdev *dev, struct uio *uio, int ioflag) 313 { 314 unsigned int length; 315 ssize_t oresid; 316 int error = 0; 317 318 oresid = uio->uio_resid; 319 320 mtx_lock(&sysmouse_lock); 321 length = sysmouse_level >= 1 ? MOUSE_SYS_PACKETSIZE : 322 MOUSE_MSC_PACKETSIZE; 323 324 while (uio->uio_resid >= length) { 325 error = sysmouse_buf_read(uio, length); 326 if (error == 0) { 327 /* Process the next frame. */ 328 continue; 329 } else if (error != EWOULDBLOCK) { 330 /* Error (e.g. EFAULT). */ 331 break; 332 } else { 333 /* Block. */ 334 if (oresid != uio->uio_resid || ioflag & O_NONBLOCK) 335 break; 336 error = cv_wait_sig(&sysmouse_sleep, &sysmouse_lock); 337 if (error != 0) 338 break; 339 } 340 } 341 mtx_unlock(&sysmouse_lock); 342 343 return (error); 344 } 345 346 static int 347 sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 348 struct thread *td) 349 { 350 351 switch (cmd) { 352 case FIOASYNC: 353 mtx_lock(&sysmouse_lock); 354 if (*(int *)data) 355 sysmouse_flags |= SM_ASYNC; 356 else 357 sysmouse_flags &= ~SM_ASYNC; 358 mtx_unlock(&sysmouse_lock); 359 return (0); 360 case FIONBIO: 361 return (0); 362 case FIOGETOWN: 363 *(int *)data = fgetown(&sysmouse_sigio); 364 return (0); 365 case FIOSETOWN: 366 return (fsetown(*(int *)data, &sysmouse_sigio)); 367 case MOUSE_GETHWINFO: { 368 mousehw_t *hw = (mousehw_t *)data; 369 370 hw->buttons = 10; 371 hw->iftype = MOUSE_IF_SYSMOUSE; 372 hw->type = MOUSE_MOUSE; 373 hw->model = MOUSE_MODEL_GENERIC; 374 hw->hwid = 0; 375 376 return (0); 377 } 378 case MOUSE_GETLEVEL: 379 *(int *)data = sysmouse_level; 380 return (0); 381 case MOUSE_GETMODE: { 382 mousemode_t *mode = (mousemode_t *)data; 383 384 mode->rate = -1; 385 mode->resolution = -1; 386 mode->accelfactor = 0; 387 mode->level = sysmouse_level; 388 389 switch (mode->level) { 390 case 0: 391 mode->protocol = MOUSE_PROTO_MSC; 392 mode->packetsize = MOUSE_MSC_PACKETSIZE; 393 mode->syncmask[0] = MOUSE_MSC_SYNCMASK; 394 mode->syncmask[1] = MOUSE_MSC_SYNC; 395 break; 396 case 1: 397 mode->protocol = MOUSE_PROTO_SYSMOUSE; 398 mode->packetsize = MOUSE_SYS_PACKETSIZE; 399 mode->syncmask[0] = MOUSE_SYS_SYNCMASK; 400 mode->syncmask[1] = MOUSE_SYS_SYNC; 401 break; 402 } 403 404 return (0); 405 } 406 case MOUSE_GETSTATUS: 407 mtx_lock(&sysmouse_lock); 408 *(mousestatus_t *)data = sysmouse_status; 409 410 sysmouse_status.flags = 0; 411 sysmouse_status.obutton = sysmouse_status.button; 412 sysmouse_status.dx = 0; 413 sysmouse_status.dy = 0; 414 sysmouse_status.dz = 0; 415 mtx_unlock(&sysmouse_lock); 416 417 return (0); 418 case MOUSE_SETLEVEL: { 419 int level; 420 421 level = *(int *)data; 422 if (level != 0 && level != 1) 423 return (EINVAL); 424 425 sysmouse_level = level; 426 return (0); 427 } 428 case MOUSE_SETMODE: { 429 mousemode_t *mode = (mousemode_t *)data; 430 431 switch (mode->level) { 432 case -1: 433 /* Do nothing. */ 434 break; 435 case 0: 436 case 1: 437 sysmouse_level = mode->level; 438 break; 439 default: 440 return (EINVAL); 441 } 442 443 return (0); 444 } 445 case MOUSE_MOUSECHAR: 446 return (0); 447 default: 448 #ifdef VT_SYSMOUSE_DEBUG 449 printf("sysmouse: unknown ioctl: %c:%lx\n", 450 (char)IOCGROUP(cmd), IOCBASECMD(cmd)); 451 #endif 452 return (ENOIOCTL); 453 } 454 } 455 456 static int 457 sysmouse_poll(struct cdev *dev, int events, struct thread *td) 458 { 459 int revents = 0; 460 461 mtx_lock(&sysmouse_lock); 462 if (events & (POLLIN|POLLRDNORM)) { 463 if (sysmouse_length > 0) 464 revents = events & (POLLIN|POLLRDNORM); 465 else 466 selrecord(td, &sysmouse_bufpoll); 467 } 468 mtx_unlock(&sysmouse_lock); 469 470 return (revents); 471 } 472 473 static void 474 sysmouse_drvinit(void *unused) 475 { 476 477 if (!vty_enabled(VTY_VT)) 478 return; 479 mtx_init(&sysmouse_lock, "sysmouse", NULL, MTX_DEF); 480 cv_init(&sysmouse_sleep, "sysmrd"); 481 make_dev(&sysmouse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 482 "sysmouse"); 483 #ifdef EVDEV_SUPPORT 484 sysmouse_evdev_init(); 485 #endif 486 } 487 488 SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sysmouse_drvinit, NULL); 489