vt_sysmouse.c revision 219888
1185029Spjd/*- 2185029Spjd * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3185029Spjd * All rights reserved. 4185029Spjd * 5185029Spjd * Copyright (c) 2009 The FreeBSD Foundation 6185029Spjd * All rights reserved. 7185029Spjd * 8185029Spjd * This software was developed by Ed Schouten under sponsorship from the 9185029Spjd * FreeBSD Foundation. 10185029Spjd * 11185029Spjd * Redistribution and use in source and binary forms, with or without 12185029Spjd * modification, are permitted provided that the following conditions 13185029Spjd * are met: 14185029Spjd * 1. Redistributions of source code must retain the above copyright 15185029Spjd * notice, this list of conditions and the following disclaimer. 16185029Spjd * 2. Redistributions in binary form must reproduce the above copyright 17185029Spjd * notice, this list of conditions and the following disclaimer in the 18185029Spjd * documentation and/or other materials provided with the distribution. 19185029Spjd * 20185029Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21185029Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22185029Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23185029Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24185029Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25185029Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26185029Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27185029Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28185029Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29185029Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30185029Spjd * SUCH DAMAGE. 31185029Spjd */ 32185029Spjd 33185029Spjd#include <sys/cdefs.h> 34185029Spjd__FBSDID("$FreeBSD: user/ed/newcons/sys/dev/vt/vt_sysmouse.c 219888 2011-03-22 21:31:31Z ed $"); 35185029Spjd 36185029Spjd#include <sys/param.h> 37185029Spjd#include <sys/condvar.h> 38185029Spjd#include <sys/conf.h> 39185029Spjd#include <sys/consio.h> 40185029Spjd#include <sys/fcntl.h> 41185029Spjd#include <sys/filio.h> 42185029Spjd#include <sys/kernel.h> 43185029Spjd#include <sys/malloc.h> 44185029Spjd#include <sys/mouse.h> 45185029Spjd#include <sys/poll.h> 46185029Spjd#include <sys/random.h> 47185029Spjd#include <sys/selinfo.h> 48185029Spjd#include <sys/sigio.h> 49185029Spjd#include <sys/signalvar.h> 50185029Spjd#include <sys/systm.h> 51185029Spjd#include <sys/uio.h> 52185029Spjd 53185029Spjd#include <dev/vt/vt.h> 54185029Spjd 55185029Spjdstatic d_open_t sysmouse_open; 56185029Spjdstatic d_close_t sysmouse_close; 57185029Spjdstatic d_read_t sysmouse_read; 58185029Spjdstatic d_ioctl_t sysmouse_ioctl; 59185029Spjdstatic d_poll_t sysmouse_poll; 60185029Spjd 61185029Spjdstatic struct cdevsw sysmouse_cdevsw = { 62185029Spjd .d_version = D_VERSION, 63185029Spjd .d_open = sysmouse_open, 64185029Spjd .d_close = sysmouse_close, 65185029Spjd .d_read = sysmouse_read, 66185029Spjd .d_ioctl = sysmouse_ioctl, 67185029Spjd .d_poll = sysmouse_poll, 68185029Spjd .d_name = "sysmouse", 69185029Spjd}; 70185029Spjd 71185029Spjdstatic struct mtx sysmouse_lock; 72185029Spjdstatic struct cv sysmouse_sleep; 73185029Spjdstatic struct selinfo sysmouse_bufpoll; 74185029Spjd 75185029Spjdstatic int sysmouse_level; 76185029Spjdstatic mousestatus_t sysmouse_status; 77185029Spjdstatic int sysmouse_flags; 78185029Spjd#define SM_ASYNC 0x1 79185029Spjdstatic struct sigio *sysmouse_sigio; 80185029Spjd 81185029Spjd#define SYSMOUSE_MAXFRAMES 250 /* 2 KB */ 82185029Spjdstatic MALLOC_DEFINE(M_SYSMOUSE, "sysmouse", "sysmouse device"); 83185029Spjdstatic unsigned char *sysmouse_buffer; 84185029Spjdstatic unsigned int sysmouse_start, sysmouse_length; 85185029Spjd 86185029Spjdstatic int 87185029Spjdsysmouse_buf_read(struct uio *uio, unsigned int length) 88185029Spjd{ 89185029Spjd unsigned char buf[MOUSE_SYS_PACKETSIZE]; 90185029Spjd int error; 91185029Spjd 92185029Spjd if (sysmouse_buffer == NULL) 93185029Spjd return (ENXIO); 94185029Spjd else if (sysmouse_length == 0) 95185029Spjd return (EWOULDBLOCK); 96185029Spjd 97185029Spjd memcpy(buf, sysmouse_buffer + 98185029Spjd sysmouse_start * MOUSE_SYS_PACKETSIZE, MOUSE_SYS_PACKETSIZE); 99185029Spjd sysmouse_start = (sysmouse_start + 1) % SYSMOUSE_MAXFRAMES; 100185029Spjd sysmouse_length--; 101185029Spjd 102185029Spjd mtx_unlock(&sysmouse_lock); 103185029Spjd error = uiomove(buf, length, uio); 104185029Spjd mtx_lock(&sysmouse_lock); 105185029Spjd 106185029Spjd return (error); 107185029Spjd} 108185029Spjd 109185029Spjdstatic void 110185029Spjdsysmouse_buf_store(const unsigned char buf[MOUSE_SYS_PACKETSIZE]) 111185029Spjd{ 112185029Spjd unsigned int idx; 113185029Spjd 114185029Spjd if (sysmouse_buffer == NULL || sysmouse_length == SYSMOUSE_MAXFRAMES) 115185029Spjd return; 116185029Spjd 117185029Spjd idx = (sysmouse_start + sysmouse_length) % SYSMOUSE_MAXFRAMES; 118185029Spjd memcpy(sysmouse_buffer + idx * MOUSE_SYS_PACKETSIZE, buf, 119185029Spjd MOUSE_SYS_PACKETSIZE); 120185029Spjd sysmouse_length++; 121185029Spjd cv_broadcast(&sysmouse_sleep); 122185029Spjd selwakeup(&sysmouse_bufpoll); 123185029Spjd if (sysmouse_flags & SM_ASYNC && sysmouse_sigio != NULL) 124185029Spjd pgsigio(&sysmouse_sigio, SIGIO, 0); 125185029Spjd} 126185029Spjd 127185029Spjdvoid 128sysmouse_process_event(mouse_info_t *mi) 129{ 130 /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ 131 static const int buttonmap[8] = { 132 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 133 MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 134 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, 135 MOUSE_MSC_BUTTON3UP, 136 MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, 137 MOUSE_MSC_BUTTON2UP, 138 MOUSE_MSC_BUTTON1UP, 139 0, 140 }; 141 unsigned char buf[MOUSE_SYS_PACKETSIZE]; 142 int x, y, z; 143 144 random_harvest(mi, sizeof *mi, 2, 0, RANDOM_MOUSE); 145 146 mtx_lock(&sysmouse_lock); 147 switch (mi->operation) { 148 case MOUSE_ACTION: 149 sysmouse_status.button = mi->u.data.buttons; 150 /* FALLTHROUGH */ 151 case MOUSE_MOTION_EVENT: 152 x = mi->u.data.x; 153 y = mi->u.data.y; 154 z = mi->u.data.z; 155 break; 156 case MOUSE_BUTTON_EVENT: 157 x = y = z = 0; 158 if (mi->u.event.value > 0) 159 sysmouse_status.button |= mi->u.event.id; 160 else 161 sysmouse_status.button &= ~mi->u.event.id; 162 break; 163 default: 164 goto done; 165 } 166 167 sysmouse_status.dx += x; 168 sysmouse_status.dy += y; 169 sysmouse_status.dz += z; 170 sysmouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) | 171 (sysmouse_status.obutton ^ sysmouse_status.button); 172 if (sysmouse_status.flags == 0) 173 goto done; 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 y = -imax(imin(y, 255), -256); 182 buf[2] = y >> 1; 183 buf[4] = y - 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); 192done: mtx_unlock(&sysmouse_lock); 193} 194 195static int 196sysmouse_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 197{ 198 void *buf; 199 200 buf = malloc(MOUSE_SYS_PACKETSIZE * SYSMOUSE_MAXFRAMES, 201 M_SYSMOUSE, M_WAITOK); 202 mtx_lock(&sysmouse_lock); 203 if (sysmouse_buffer == NULL) { 204 sysmouse_buffer = buf; 205 sysmouse_start = sysmouse_length = 0; 206 sysmouse_level = 0; 207 } else { 208 free(buf, M_SYSMOUSE); 209 } 210 mtx_unlock(&sysmouse_lock); 211 212 return (0); 213} 214 215static int 216sysmouse_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 217{ 218 219 mtx_lock(&sysmouse_lock); 220 free(sysmouse_buffer, M_SYSMOUSE); 221 sysmouse_buffer = NULL; 222 mtx_unlock(&sysmouse_lock); 223 224 return (0); 225} 226 227static int 228sysmouse_read(struct cdev *dev, struct uio *uio, int ioflag) 229{ 230 unsigned int length; 231 ssize_t oresid; 232 int error = 0; 233 234 oresid = uio->uio_resid; 235 236 mtx_lock(&sysmouse_lock); 237 length = sysmouse_level >= 1 ? MOUSE_SYS_PACKETSIZE : 238 MOUSE_MSC_PACKETSIZE; 239 240 while (uio->uio_resid >= length) { 241 error = sysmouse_buf_read(uio, length); 242 if (error == 0) { 243 /* Process the next frame. */ 244 continue; 245 } else if (error != EWOULDBLOCK) { 246 /* Error (e.g. EFAULT). */ 247 break; 248 } else { 249 /* Block. */ 250 if (oresid != uio->uio_resid || ioflag & O_NONBLOCK) 251 break; 252 error = cv_wait_sig(&sysmouse_sleep, &sysmouse_lock); 253 if (error != 0) 254 break; 255 } 256 } 257 mtx_unlock(&sysmouse_lock); 258 259 return (error); 260} 261 262static int 263sysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 264 struct thread *td) 265{ 266 267 switch (cmd) { 268 case FIOASYNC: 269 mtx_lock(&sysmouse_lock); 270 if (*(int *)data) 271 sysmouse_flags |= SM_ASYNC; 272 else 273 sysmouse_flags &= ~SM_ASYNC; 274 mtx_unlock(&sysmouse_lock); 275 return (0); 276 case FIONBIO: 277 return (0); 278 case FIOGETOWN: 279 *(int *)data = fgetown(&sysmouse_sigio); 280 return (0); 281 case FIOSETOWN: 282 return (fsetown(*(int *)data, &sysmouse_sigio)); 283 case MOUSE_GETHWINFO: { 284 mousehw_t *hw = (mousehw_t *)data; 285 286 hw->buttons = 10; 287 hw->iftype = MOUSE_IF_SYSMOUSE; 288 hw->type = MOUSE_MOUSE; 289 hw->model = MOUSE_MODEL_GENERIC; 290 hw->hwid = 0; 291 292 return (0); 293 } 294 case MOUSE_GETLEVEL: 295 *(int *)data = sysmouse_level; 296 return (0); 297 case MOUSE_GETMODE: { 298 mousemode_t *mode = (mousemode_t *)data; 299 300 mode->rate = -1; 301 mode->resolution = -1; 302 mode->accelfactor = 0; 303 mode->level = sysmouse_level; 304 305 switch (mode->level) { 306 case 0: 307 mode->protocol = MOUSE_PROTO_MSC; 308 mode->packetsize = MOUSE_MSC_PACKETSIZE; 309 mode->syncmask[0] = MOUSE_MSC_SYNCMASK; 310 mode->syncmask[1] = MOUSE_MSC_SYNC; 311 break; 312 case 1: 313 mode->protocol = MOUSE_PROTO_SYSMOUSE; 314 mode->packetsize = MOUSE_SYS_PACKETSIZE; 315 mode->syncmask[0] = MOUSE_SYS_SYNCMASK; 316 mode->syncmask[1] = MOUSE_SYS_SYNC; 317 break; 318 } 319 320 return (0); 321 } 322 case MOUSE_GETSTATUS: 323 mtx_lock(&sysmouse_lock); 324 *(mousestatus_t *)data = sysmouse_status; 325 326 sysmouse_status.flags = 0; 327 sysmouse_status.obutton = sysmouse_status.button; 328 sysmouse_status.dx = 0; 329 sysmouse_status.dy = 0; 330 sysmouse_status.dz = 0; 331 mtx_unlock(&sysmouse_lock); 332 333 return (0); 334 case MOUSE_SETLEVEL: { 335 int level; 336 337 level = *(int *)data; 338 if (level != 0 && level != 1) 339 return (EINVAL); 340 341 sysmouse_level = level; 342 return (0); 343 } 344 case MOUSE_SETMODE: { 345 mousemode_t *mode = (mousemode_t *)data; 346 347 switch (mode->level) { 348 case -1: 349 /* Do nothing. */ 350 break; 351 case 0: 352 case 1: 353 sysmouse_level = mode->level; 354 break; 355 default: 356 return (EINVAL); 357 } 358 359 return (0); 360 } 361 default: 362 printf("sysmouse: unknown ioctl: %c:%lx\n", 363 (char)IOCGROUP(cmd), IOCBASECMD(cmd)); 364 return (ENOIOCTL); 365 } 366} 367 368static int 369sysmouse_poll(struct cdev *dev, int events, struct thread *td) 370{ 371 int revents = 0; 372 373 mtx_lock(&sysmouse_lock); 374 if (events & (POLLIN|POLLRDNORM)) { 375 if (sysmouse_length > 0) 376 revents = events & (POLLIN|POLLRDNORM); 377 else 378 selrecord(td, &sysmouse_bufpoll); 379 } 380 mtx_unlock(&sysmouse_lock); 381 382 return (revents); 383} 384 385static void 386sysmouse_drvinit(void *unused) 387{ 388 389 mtx_init(&sysmouse_lock, "sysmouse", NULL, MTX_DEF); 390 cv_init(&sysmouse_sleep, "sysmrd"); 391 make_dev(&sysmouse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 392 "sysmouse"); 393} 394 395SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sysmouse_drvinit, NULL); 396