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 <sys/param.h> 37 #include <sys/condvar.h> 38 #include <sys/consio.h> 39 #include <sys/fcntl.h> 40 #include <sys/filio.h> 41 #include <sys/kernel.h> 42 #include <sys/malloc.h> 43 #include <sys/poll.h> 44 #include <sys/random.h> 45 #include <sys/selinfo.h> 46 #include <sys/sigio.h> 47 #include <sys/signalvar.h> 48 #include <sys/systm.h> 49 #include <sys/uio.h> 50 51 #include <dev/vt/vt.h> 52 53 static d_open_t sysmouse_open; 54 static d_close_t sysmouse_close; 55 static d_read_t sysmouse_read; 56 static d_ioctl_t sysmouse_ioctl; 57 static d_poll_t sysmouse_poll; 58 59 static struct cdevsw sysmouse_cdevsw = { 60 .d_version = D_VERSION, 61 .d_open = sysmouse_open, 62 .d_close = sysmouse_close, 63 .d_read = sysmouse_read, 64 .d_ioctl = sysmouse_ioctl, 65 .d_poll = sysmouse_poll, 66 .d_name = "sysmouse", 67 }; 68 69 static struct mtx sysmouse_lock; 70 static struct cv sysmouse_sleep; 71 static struct selinfo sysmouse_bufpoll; 72 73 static int sysmouse_level; 74 static mousestatus_t sysmouse_status; 75 static int sysmouse_flags; 76 #define SM_ASYNC 0x1 77 static struct sigio *sysmouse_sigio; 78 79 #define SYSMOUSE_MAXFRAMES 250 /* 2 KB */ 80 static MALLOC_DEFINE(M_SYSMOUSE, "sysmouse", "sysmouse device"); 81 static unsigned char *sysmouse_buffer; 82 static unsigned int sysmouse_start, sysmouse_length; 83 84 static int 85 sysmouse_buf_read(struct uio *uio, unsigned int length) 86 { 87 unsigned char buf[MOUSE_SYS_PACKETSIZE]; 88 int error; 89 90 if (sysmouse_buffer == NULL) 91 return (ENXIO); 92 else if (sysmouse_length == 0) 93 return (EWOULDBLOCK); 94 95 memcpy(buf, sysmouse_buffer + 96 sysmouse_start * MOUSE_SYS_PACKETSIZE, MOUSE_SYS_PACKETSIZE); 97 sysmouse_start = (sysmouse_start + 1) % SYSMOUSE_MAXFRAMES; 98 sysmouse_length--; 99 100 mtx_unlock(&sysmouse_lock); 101 error = uiomove(buf, length, uio); 102 mtx_lock(&sysmouse_lock); 103 104 return (error); 105 } 106 107 static void 108 sysmouse_buf_store(const unsigned char buf[MOUSE_SYS_PACKETSIZE]) 109 { 110 unsigned int idx; 111 112 if (sysmouse_buffer == NULL || sysmouse_length == SYSMOUSE_MAXFRAMES) 113 return; 114 115 idx = (sysmouse_start + sysmouse_length) % SYSMOUSE_MAXFRAMES; 116 memcpy(sysmouse_buffer + idx * MOUSE_SYS_PACKETSIZE, buf, 117 MOUSE_SYS_PACKETSIZE); 118 sysmouse_length++; 119 cv_broadcast(&sysmouse_sleep); 120 selwakeup(&sysmouse_bufpoll); 121 if (sysmouse_flags & SM_ASYNC && sysmouse_sigio != NULL) 122 pgsigio(&sysmouse_sigio, SIGIO, 0); 123 } 124 125 void 126 sysmouse_process_event(mouse_info_t *mi) 127 { 128 /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ 129 static const int buttonmap[8] = { 130 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 131 MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 132 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, 133 MOUSE_MSC_BUTTON3UP, 134 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, 135 MOUSE_MSC_BUTTON2UP, 136 MOUSE_MSC_BUTTON1UP, 137 0, 138 }; 139 unsigned char buf[MOUSE_SYS_PACKETSIZE]; 140 int x, y, iy, z; 141 142 random_harvest_queue(mi, sizeof *mi, 2, RANDOM_MOUSE); 143 144 mtx_lock(&sysmouse_lock); 145 switch (mi->operation) { 146 case MOUSE_ACTION: 147 sysmouse_status.button = mi->u.data.buttons; 148 /* FALLTHROUGH */ 149 case MOUSE_MOTION_EVENT: 150 x = mi->u.data.x; 151 y = mi->u.data.y; 152 z = mi->u.data.z; 153 break; 154 case MOUSE_BUTTON_EVENT: 155 x = y = z = 0; 156 if (mi->u.event.value > 0) 157 sysmouse_status.button |= mi->u.event.id; 158 else 159 sysmouse_status.button &= ~mi->u.event.id; 160 break; 161 default: 162 goto done; 163 } 164 165 sysmouse_status.dx += x; 166 sysmouse_status.dy += y; 167 sysmouse_status.dz += z; 168 sysmouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) | 169 (sysmouse_status.obutton ^ sysmouse_status.button); 170 if (sysmouse_status.flags == 0) 171 goto done; 172 173 174 /* The first five bytes are compatible with MouseSystems. */ 175 buf[0] = MOUSE_MSC_SYNC | 176 buttonmap[sysmouse_status.button & MOUSE_STDBUTTONS]; 177 x = imax(imin(x, 255), -256); 178 buf[1] = x >> 1; 179 buf[3] = x - buf[1]; 180 iy = -imax(imin(y, 255), -256); 181 buf[2] = iy >> 1; 182 buf[4] = iy - buf[2]; 183 /* Extended part. */ 184 z = imax(imin(z, 127), -128); 185 buf[5] = (z >> 1) & 0x7f; 186 buf[6] = (z - (z >> 1)) & 0x7f; 187 /* Buttons 4-10. */ 188 buf[7] = (~sysmouse_status.button >> 3) & 0x7f; 189 190 sysmouse_buf_store(buf); 191 192 #ifndef SC_NO_CUTPASTE 193 mtx_unlock(&sysmouse_lock); 194 vt_mouse_event(mi->operation, x, y, mi->u.event.id, mi->u.event.value, 195 sysmouse_level); 196 return; 197 #endif 198 199 done: mtx_unlock(&sysmouse_lock); 200 } 201 202 static int 203 sysmouse_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 204 { 205 void *buf; 206 207 buf = malloc(MOUSE_SYS_PACKETSIZE * SYSMOUSE_MAXFRAMES, 208 M_SYSMOUSE, M_WAITOK); 209 mtx_lock(&sysmouse_lock); 210 if (sysmouse_buffer == NULL) { 211 sysmouse_buffer = buf; 212 sysmouse_start = sysmouse_length = 0; 213 sysmouse_level = 0; 214 } else { 215 free(buf, M_SYSMOUSE); 216 } 217 mtx_unlock(&sysmouse_lock); 218 219 return (0); 220 } 221 222 static int 223 sysmouse_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 224 { 225 226 mtx_lock(&sysmouse_lock); 227 free(sysmouse_buffer, M_SYSMOUSE); 228 sysmouse_buffer = NULL; 229 sysmouse_level = 0; 230 mtx_unlock(&sysmouse_lock); 231 232 return (0); 233 } 234 235 static int 236 sysmouse_read(struct cdev *dev, struct uio *uio, int ioflag) 237 { 238 unsigned int length; 239 ssize_t oresid; 240 int error = 0; 241 242 oresid = uio->uio_resid; 243 244 mtx_lock(&sysmouse_lock); 245 length = sysmouse_level >= 1 ? MOUSE_SYS_PACKETSIZE : 246 MOUSE_MSC_PACKETSIZE; 247 248 while (uio->uio_resid >= length) { 249 error = sysmouse_buf_read(uio, length); 250 if (error == 0) { 251 /* Process the next frame. */ 252 continue; 253 } else if (error != EWOULDBLOCK) { 254 /* Error (e.g. EFAULT). */ 255 break; 256 } else { 257 /* Block. */ 258 if (oresid != uio->uio_resid || ioflag & O_NONBLOCK) 259 break; 260 error = cv_wait_sig(&sysmouse_sleep, &sysmouse_lock); 261 if (error != 0) 262 break; 263 } 264 } 265 mtx_unlock(&sysmouse_lock); 266 267 return (error); 268 } 269 270 static int 271 sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 272 struct thread *td) 273 { 274 275 switch (cmd) { 276 case FIOASYNC: 277 mtx_lock(&sysmouse_lock); 278 if (*(int *)data) 279 sysmouse_flags |= SM_ASYNC; 280 else 281 sysmouse_flags &= ~SM_ASYNC; 282 mtx_unlock(&sysmouse_lock); 283 return (0); 284 case FIONBIO: 285 return (0); 286 case FIOGETOWN: 287 *(int *)data = fgetown(&sysmouse_sigio); 288 return (0); 289 case FIOSETOWN: 290 return (fsetown(*(int *)data, &sysmouse_sigio)); 291 case MOUSE_GETHWINFO: { 292 mousehw_t *hw = (mousehw_t *)data; 293 294 hw->buttons = 10; 295 hw->iftype = MOUSE_IF_SYSMOUSE; 296 hw->type = MOUSE_MOUSE; 297 hw->model = MOUSE_MODEL_GENERIC; 298 hw->hwid = 0; 299 300 return (0); 301 } 302 case MOUSE_GETLEVEL: 303 *(int *)data = sysmouse_level; 304 return (0); 305 case MOUSE_GETMODE: { 306 mousemode_t *mode = (mousemode_t *)data; 307 308 mode->rate = -1; 309 mode->resolution = -1; 310 mode->accelfactor = 0; 311 mode->level = sysmouse_level; 312 313 switch (mode->level) { 314 case 0: 315 mode->protocol = MOUSE_PROTO_MSC; 316 mode->packetsize = MOUSE_MSC_PACKETSIZE; 317 mode->syncmask[0] = MOUSE_MSC_SYNCMASK; 318 mode->syncmask[1] = MOUSE_MSC_SYNC; 319 break; 320 case 1: 321 mode->protocol = MOUSE_PROTO_SYSMOUSE; 322 mode->packetsize = MOUSE_SYS_PACKETSIZE; 323 mode->syncmask[0] = MOUSE_SYS_SYNCMASK; 324 mode->syncmask[1] = MOUSE_SYS_SYNC; 325 break; 326 } 327 328 return (0); 329 } 330 case MOUSE_GETSTATUS: 331 mtx_lock(&sysmouse_lock); 332 *(mousestatus_t *)data = sysmouse_status; 333 334 sysmouse_status.flags = 0; 335 sysmouse_status.obutton = sysmouse_status.button; 336 sysmouse_status.dx = 0; 337 sysmouse_status.dy = 0; 338 sysmouse_status.dz = 0; 339 mtx_unlock(&sysmouse_lock); 340 341 return (0); 342 case MOUSE_SETLEVEL: { 343 int level; 344 345 level = *(int *)data; 346 if (level != 0 && level != 1) 347 return (EINVAL); 348 349 sysmouse_level = level; 350 return (0); 351 } 352 case MOUSE_SETMODE: { 353 mousemode_t *mode = (mousemode_t *)data; 354 355 switch (mode->level) { 356 case -1: 357 /* Do nothing. */ 358 break; 359 case 0: 360 case 1: 361 sysmouse_level = mode->level; 362 break; 363 default: 364 return (EINVAL); 365 } 366 367 return (0); 368 } 369 case MOUSE_MOUSECHAR: 370 return (0); 371 default: 372 #ifdef VT_SYSMOUSE_DEBUG 373 printf("sysmouse: unknown ioctl: %c:%lx\n", 374 (char)IOCGROUP(cmd), IOCBASECMD(cmd)); 375 #endif 376 return (ENOIOCTL); 377 } 378 } 379 380 static int 381 sysmouse_poll(struct cdev *dev, int events, struct thread *td) 382 { 383 int revents = 0; 384 385 mtx_lock(&sysmouse_lock); 386 if (events & (POLLIN|POLLRDNORM)) { 387 if (sysmouse_length > 0) 388 revents = events & (POLLIN|POLLRDNORM); 389 else 390 selrecord(td, &sysmouse_bufpoll); 391 } 392 mtx_unlock(&sysmouse_lock); 393 394 return (revents); 395 } 396 397 static void 398 sysmouse_drvinit(void *unused) 399 { 400 401 if (!vty_enabled(VTY_VT)) 402 return; 403 mtx_init(&sysmouse_lock, "sysmouse", NULL, MTX_DEF); 404 cv_init(&sysmouse_sleep, "sysmrd"); 405 make_dev(&sysmouse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 406 "sysmouse"); 407 } 408 409 SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sysmouse_drvinit, NULL); 410