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