1219888Sed/*- 2219888Sed * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp> 3219888Sed * All rights reserved. 4219888Sed * 5219888Sed * Copyright (c) 2009 The FreeBSD Foundation 6219888Sed * All rights reserved. 7219888Sed * 8219888Sed * This software was developed by Ed Schouten under sponsorship from the 9219888Sed * FreeBSD Foundation. 10219888Sed * 11219888Sed * Redistribution and use in source and binary forms, with or without 12219888Sed * modification, are permitted provided that the following conditions 13219888Sed * are met: 14219888Sed * 1. Redistributions of source code must retain the above copyright 15219888Sed * notice, this list of conditions and the following disclaimer. 16219888Sed * 2. Redistributions in binary form must reproduce the above copyright 17219888Sed * notice, this list of conditions and the following disclaimer in the 18219888Sed * documentation and/or other materials provided with the distribution. 19219888Sed * 20219888Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21219888Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22219888Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23219888Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24219888Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25219888Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26219888Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27219888Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28219888Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29219888Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30219888Sed * SUCH DAMAGE. 31219888Sed */ 32219888Sed 33219888Sed#include <sys/cdefs.h> 34219888Sed__FBSDID("$FreeBSD$"); 35219888Sed 36219888Sed#include <sys/param.h> 37219888Sed#include <sys/condvar.h> 38219888Sed#include <sys/consio.h> 39219888Sed#include <sys/fcntl.h> 40219888Sed#include <sys/filio.h> 41219888Sed#include <sys/kernel.h> 42219888Sed#include <sys/malloc.h> 43219888Sed#include <sys/poll.h> 44219888Sed#include <sys/random.h> 45219888Sed#include <sys/selinfo.h> 46219888Sed#include <sys/sigio.h> 47219888Sed#include <sys/signalvar.h> 48219888Sed#include <sys/systm.h> 49219888Sed#include <sys/uio.h> 50219888Sed 51219888Sed#include <dev/vt/vt.h> 52219888Sed 53219888Sedstatic d_open_t sysmouse_open; 54219888Sedstatic d_close_t sysmouse_close; 55219888Sedstatic d_read_t sysmouse_read; 56219888Sedstatic d_ioctl_t sysmouse_ioctl; 57219888Sedstatic d_poll_t sysmouse_poll; 58219888Sed 59219888Sedstatic struct cdevsw sysmouse_cdevsw = { 60219888Sed .d_version = D_VERSION, 61219888Sed .d_open = sysmouse_open, 62219888Sed .d_close = sysmouse_close, 63219888Sed .d_read = sysmouse_read, 64219888Sed .d_ioctl = sysmouse_ioctl, 65219888Sed .d_poll = sysmouse_poll, 66219888Sed .d_name = "sysmouse", 67219888Sed}; 68219888Sed 69219888Sedstatic struct mtx sysmouse_lock; 70219888Sedstatic struct cv sysmouse_sleep; 71219888Sedstatic struct selinfo sysmouse_bufpoll; 72219888Sed 73219888Sedstatic int sysmouse_level; 74219888Sedstatic mousestatus_t sysmouse_status; 75219888Sedstatic int sysmouse_flags; 76219888Sed#define SM_ASYNC 0x1 77219888Sedstatic struct sigio *sysmouse_sigio; 78219888Sed 79219888Sed#define SYSMOUSE_MAXFRAMES 250 /* 2 KB */ 80219888Sedstatic MALLOC_DEFINE(M_SYSMOUSE, "sysmouse", "sysmouse device"); 81219888Sedstatic unsigned char *sysmouse_buffer; 82219888Sedstatic unsigned int sysmouse_start, sysmouse_length; 83219888Sed 84219888Sedstatic int 85219888Sedsysmouse_buf_read(struct uio *uio, unsigned int length) 86219888Sed{ 87219888Sed unsigned char buf[MOUSE_SYS_PACKETSIZE]; 88219888Sed int error; 89219888Sed 90219888Sed if (sysmouse_buffer == NULL) 91219888Sed return (ENXIO); 92219888Sed else if (sysmouse_length == 0) 93219888Sed return (EWOULDBLOCK); 94219888Sed 95219888Sed memcpy(buf, sysmouse_buffer + 96219888Sed sysmouse_start * MOUSE_SYS_PACKETSIZE, MOUSE_SYS_PACKETSIZE); 97219888Sed sysmouse_start = (sysmouse_start + 1) % SYSMOUSE_MAXFRAMES; 98219888Sed sysmouse_length--; 99219888Sed 100219888Sed mtx_unlock(&sysmouse_lock); 101219888Sed error = uiomove(buf, length, uio); 102219888Sed mtx_lock(&sysmouse_lock); 103219888Sed 104219888Sed return (error); 105219888Sed} 106219888Sed 107219888Sedstatic void 108219888Sedsysmouse_buf_store(const unsigned char buf[MOUSE_SYS_PACKETSIZE]) 109219888Sed{ 110219888Sed unsigned int idx; 111219888Sed 112219888Sed if (sysmouse_buffer == NULL || sysmouse_length == SYSMOUSE_MAXFRAMES) 113219888Sed return; 114219888Sed 115219888Sed idx = (sysmouse_start + sysmouse_length) % SYSMOUSE_MAXFRAMES; 116219888Sed memcpy(sysmouse_buffer + idx * MOUSE_SYS_PACKETSIZE, buf, 117219888Sed MOUSE_SYS_PACKETSIZE); 118219888Sed sysmouse_length++; 119219888Sed cv_broadcast(&sysmouse_sleep); 120219888Sed selwakeup(&sysmouse_bufpoll); 121219888Sed if (sysmouse_flags & SM_ASYNC && sysmouse_sigio != NULL) 122219888Sed pgsigio(&sysmouse_sigio, SIGIO, 0); 123219888Sed} 124219888Sed 125219888Sedvoid 126219888Sedsysmouse_process_event(mouse_info_t *mi) 127219888Sed{ 128219888Sed /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ 129219888Sed static const int buttonmap[8] = { 130219888Sed MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 131219888Sed MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, 132219888Sed MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, 133219888Sed MOUSE_MSC_BUTTON3UP, 134219888Sed MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, 135219888Sed MOUSE_MSC_BUTTON2UP, 136219888Sed MOUSE_MSC_BUTTON1UP, 137219888Sed 0, 138219888Sed }; 139219888Sed unsigned char buf[MOUSE_SYS_PACKETSIZE]; 140258091Sray int x, y, iy, z; 141219888Sed 142263817Sray random_harvest(mi, sizeof *mi, 2, 0, RANDOM_MOUSE); 143219888Sed 144219888Sed mtx_lock(&sysmouse_lock); 145219888Sed switch (mi->operation) { 146219888Sed case MOUSE_ACTION: 147219888Sed sysmouse_status.button = mi->u.data.buttons; 148219888Sed /* FALLTHROUGH */ 149219888Sed case MOUSE_MOTION_EVENT: 150219888Sed x = mi->u.data.x; 151219888Sed y = mi->u.data.y; 152219888Sed z = mi->u.data.z; 153219888Sed break; 154219888Sed case MOUSE_BUTTON_EVENT: 155219888Sed x = y = z = 0; 156219888Sed if (mi->u.event.value > 0) 157219888Sed sysmouse_status.button |= mi->u.event.id; 158219888Sed else 159219888Sed sysmouse_status.button &= ~mi->u.event.id; 160219888Sed break; 161219888Sed default: 162219888Sed goto done; 163219888Sed } 164219888Sed 165219888Sed sysmouse_status.dx += x; 166219888Sed sysmouse_status.dy += y; 167219888Sed sysmouse_status.dz += z; 168219888Sed sysmouse_status.flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0) | 169219888Sed (sysmouse_status.obutton ^ sysmouse_status.button); 170219888Sed if (sysmouse_status.flags == 0) 171219888Sed goto done; 172219888Sed 173257976Sray 174219888Sed /* The first five bytes are compatible with MouseSystems. */ 175219888Sed buf[0] = MOUSE_MSC_SYNC | 176219888Sed buttonmap[sysmouse_status.button & MOUSE_STDBUTTONS]; 177219888Sed x = imax(imin(x, 255), -256); 178219888Sed buf[1] = x >> 1; 179219888Sed buf[3] = x - buf[1]; 180258091Sray iy = -imax(imin(y, 255), -256); 181258091Sray buf[2] = iy >> 1; 182258091Sray buf[4] = iy - buf[2]; 183219888Sed /* Extended part. */ 184219888Sed z = imax(imin(z, 127), -128); 185219888Sed buf[5] = (z >> 1) & 0x7f; 186219888Sed buf[6] = (z - (z >> 1)) & 0x7f; 187219888Sed /* Buttons 4-10. */ 188219888Sed buf[7] = (~sysmouse_status.button >> 3) & 0x7f; 189219888Sed 190219888Sed sysmouse_buf_store(buf); 191258091Sray 192263817Sray#ifndef SC_NO_CUTPASTE 193258091Sray mtx_unlock(&sysmouse_lock); 194263817Sray vt_mouse_event(mi->operation, x, y, mi->u.event.id, mi->u.event.value, 195263817Sray sysmouse_level); 196258091Sray return; 197263817Sray#endif 198258091Sray 199219888Seddone: mtx_unlock(&sysmouse_lock); 200219888Sed} 201219888Sed 202219888Sedstatic int 203219888Sedsysmouse_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 204219888Sed{ 205219888Sed void *buf; 206219888Sed 207219888Sed buf = malloc(MOUSE_SYS_PACKETSIZE * SYSMOUSE_MAXFRAMES, 208219888Sed M_SYSMOUSE, M_WAITOK); 209219888Sed mtx_lock(&sysmouse_lock); 210219888Sed if (sysmouse_buffer == NULL) { 211219888Sed sysmouse_buffer = buf; 212219888Sed sysmouse_start = sysmouse_length = 0; 213219888Sed sysmouse_level = 0; 214219888Sed } else { 215219888Sed free(buf, M_SYSMOUSE); 216219888Sed } 217219888Sed mtx_unlock(&sysmouse_lock); 218219888Sed 219219888Sed return (0); 220219888Sed} 221219888Sed 222219888Sedstatic int 223219888Sedsysmouse_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 224219888Sed{ 225219888Sed 226219888Sed mtx_lock(&sysmouse_lock); 227219888Sed free(sysmouse_buffer, M_SYSMOUSE); 228219888Sed sysmouse_buffer = NULL; 229258327Sray sysmouse_level = 0; 230219888Sed mtx_unlock(&sysmouse_lock); 231219888Sed 232219888Sed return (0); 233219888Sed} 234219888Sed 235219888Sedstatic int 236219888Sedsysmouse_read(struct cdev *dev, struct uio *uio, int ioflag) 237219888Sed{ 238219888Sed unsigned int length; 239219888Sed ssize_t oresid; 240219888Sed int error = 0; 241219888Sed 242219888Sed oresid = uio->uio_resid; 243219888Sed 244219888Sed mtx_lock(&sysmouse_lock); 245219888Sed length = sysmouse_level >= 1 ? MOUSE_SYS_PACKETSIZE : 246219888Sed MOUSE_MSC_PACKETSIZE; 247219888Sed 248219888Sed while (uio->uio_resid >= length) { 249219888Sed error = sysmouse_buf_read(uio, length); 250219888Sed if (error == 0) { 251219888Sed /* Process the next frame. */ 252219888Sed continue; 253219888Sed } else if (error != EWOULDBLOCK) { 254219888Sed /* Error (e.g. EFAULT). */ 255219888Sed break; 256219888Sed } else { 257219888Sed /* Block. */ 258219888Sed if (oresid != uio->uio_resid || ioflag & O_NONBLOCK) 259219888Sed break; 260219888Sed error = cv_wait_sig(&sysmouse_sleep, &sysmouse_lock); 261219888Sed if (error != 0) 262219888Sed break; 263219888Sed } 264219888Sed } 265219888Sed mtx_unlock(&sysmouse_lock); 266219888Sed 267219888Sed return (error); 268219888Sed} 269219888Sed 270219888Sedstatic int 271219888Sedsysmouse_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 272219888Sed struct thread *td) 273219888Sed{ 274219888Sed 275219888Sed switch (cmd) { 276219888Sed case FIOASYNC: 277219888Sed mtx_lock(&sysmouse_lock); 278219888Sed if (*(int *)data) 279219888Sed sysmouse_flags |= SM_ASYNC; 280219888Sed else 281219888Sed sysmouse_flags &= ~SM_ASYNC; 282219888Sed mtx_unlock(&sysmouse_lock); 283219888Sed return (0); 284219888Sed case FIONBIO: 285219888Sed return (0); 286219888Sed case FIOGETOWN: 287219888Sed *(int *)data = fgetown(&sysmouse_sigio); 288219888Sed return (0); 289219888Sed case FIOSETOWN: 290219888Sed return (fsetown(*(int *)data, &sysmouse_sigio)); 291219888Sed case MOUSE_GETHWINFO: { 292219888Sed mousehw_t *hw = (mousehw_t *)data; 293219888Sed 294219888Sed hw->buttons = 10; 295219888Sed hw->iftype = MOUSE_IF_SYSMOUSE; 296219888Sed hw->type = MOUSE_MOUSE; 297219888Sed hw->model = MOUSE_MODEL_GENERIC; 298219888Sed hw->hwid = 0; 299219888Sed 300219888Sed return (0); 301219888Sed } 302219888Sed case MOUSE_GETLEVEL: 303219888Sed *(int *)data = sysmouse_level; 304219888Sed return (0); 305219888Sed case MOUSE_GETMODE: { 306219888Sed mousemode_t *mode = (mousemode_t *)data; 307219888Sed 308219888Sed mode->rate = -1; 309219888Sed mode->resolution = -1; 310219888Sed mode->accelfactor = 0; 311219888Sed mode->level = sysmouse_level; 312219888Sed 313219888Sed switch (mode->level) { 314219888Sed case 0: 315219888Sed mode->protocol = MOUSE_PROTO_MSC; 316219888Sed mode->packetsize = MOUSE_MSC_PACKETSIZE; 317219888Sed mode->syncmask[0] = MOUSE_MSC_SYNCMASK; 318219888Sed mode->syncmask[1] = MOUSE_MSC_SYNC; 319219888Sed break; 320219888Sed case 1: 321219888Sed mode->protocol = MOUSE_PROTO_SYSMOUSE; 322219888Sed mode->packetsize = MOUSE_SYS_PACKETSIZE; 323219888Sed mode->syncmask[0] = MOUSE_SYS_SYNCMASK; 324219888Sed mode->syncmask[1] = MOUSE_SYS_SYNC; 325219888Sed break; 326219888Sed } 327219888Sed 328219888Sed return (0); 329219888Sed } 330219888Sed case MOUSE_GETSTATUS: 331219888Sed mtx_lock(&sysmouse_lock); 332219888Sed *(mousestatus_t *)data = sysmouse_status; 333219888Sed 334219888Sed sysmouse_status.flags = 0; 335219888Sed sysmouse_status.obutton = sysmouse_status.button; 336219888Sed sysmouse_status.dx = 0; 337219888Sed sysmouse_status.dy = 0; 338219888Sed sysmouse_status.dz = 0; 339219888Sed mtx_unlock(&sysmouse_lock); 340257976Sray 341219888Sed return (0); 342219888Sed case MOUSE_SETLEVEL: { 343219888Sed int level; 344219888Sed 345219888Sed level = *(int *)data; 346219888Sed if (level != 0 && level != 1) 347219888Sed return (EINVAL); 348219888Sed 349219888Sed sysmouse_level = level; 350263817Sray#ifndef SC_NO_CUTPASTE 351258327Sray vt_mouse_state((level == 0)?VT_MOUSE_SHOW:VT_MOUSE_HIDE); 352263817Sray#endif 353219888Sed return (0); 354219888Sed } 355219888Sed case MOUSE_SETMODE: { 356219888Sed mousemode_t *mode = (mousemode_t *)data; 357219888Sed 358219888Sed switch (mode->level) { 359219888Sed case -1: 360219888Sed /* Do nothing. */ 361219888Sed break; 362219888Sed case 0: 363219888Sed case 1: 364219888Sed sysmouse_level = mode->level; 365263817Sray#ifndef SC_NO_CUTPASTE 366258327Sray vt_mouse_state((mode->level == 0)?VT_MOUSE_SHOW: 367258327Sray VT_MOUSE_HIDE); 368263817Sray#endif 369219888Sed break; 370219888Sed default: 371219888Sed return (EINVAL); 372219888Sed } 373219888Sed 374219888Sed return (0); 375219888Sed } 376257976Sray case MOUSE_MOUSECHAR: 377257976Sray return (0); 378219888Sed default: 379219888Sed printf("sysmouse: unknown ioctl: %c:%lx\n", 380219888Sed (char)IOCGROUP(cmd), IOCBASECMD(cmd)); 381219888Sed return (ENOIOCTL); 382219888Sed } 383219888Sed} 384219888Sed 385219888Sedstatic int 386219888Sedsysmouse_poll(struct cdev *dev, int events, struct thread *td) 387219888Sed{ 388219888Sed int revents = 0; 389219888Sed 390219888Sed mtx_lock(&sysmouse_lock); 391219888Sed if (events & (POLLIN|POLLRDNORM)) { 392219888Sed if (sysmouse_length > 0) 393219888Sed revents = events & (POLLIN|POLLRDNORM); 394219888Sed else 395219888Sed selrecord(td, &sysmouse_bufpoll); 396219888Sed } 397219888Sed mtx_unlock(&sysmouse_lock); 398219888Sed 399219888Sed return (revents); 400219888Sed} 401219888Sed 402219888Sedstatic void 403219888Sedsysmouse_drvinit(void *unused) 404219888Sed{ 405219888Sed 406219888Sed mtx_init(&sysmouse_lock, "sysmouse", NULL, MTX_DEF); 407219888Sed cv_init(&sysmouse_sleep, "sysmrd"); 408219888Sed make_dev(&sysmouse_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, 409219888Sed "sysmouse"); 410219888Sed} 411219888Sed 412219888SedSYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, sysmouse_drvinit, NULL); 413