nrelease - fix/improve livecd
[dragonfly.git] / sys / dev / misc / syscons / sysmouse.c
1 /*-
2  * (MPSAFE)
3  *
4  * Copyright (c) 1999 Kazutaka YOKOTA <yokota@zodiac.mech.utsunomiya-u.ac.jp>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer as
12  *    the first lines of this file unmodified.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD: src/sys/dev/syscons/sysmouse.c,v 1.2.2.2 2001/07/16 05:21:24 yokota Exp $
29  */
30
31 /* MPSAFE NOTE: Take care with locking in sysmouse_event which is called
32  *              from syscons.
33  */
34 #include "opt_syscons.h"
35 #include "opt_evdev.h"
36
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/conf.h>
40 #include <sys/device.h>
41 #include <sys/event.h>
42 #include <sys/uio.h>
43 #include <sys/caps.h>
44 #include <sys/vnode.h>
45 #include <sys/kernel.h>
46 #include <sys/signalvar.h>
47 #include <sys/filio.h>
48
49 #include <machine/console.h>
50 #include <sys/mouse.h>
51
52 #ifdef EVDEV_SUPPORT
53 #include <dev/misc/evdev/input.h>
54 #include <dev/misc/evdev/evdev.h>
55 #endif
56
57 #include "syscons.h"
58
59 #ifndef SC_NO_SYSMOUSE
60
61 #define FIFO_SIZE       256
62
63 struct event_fifo {
64         mouse_info_t buf[FIFO_SIZE];
65         int start;
66         int fill;
67         unsigned int dropped;
68 };
69
70 struct sysmouse_state {
71         struct event_fifo *fifo;
72         int level;      /* sysmouse protocol level */
73         mousestatus_t syncstatus;
74         mousestatus_t readstatus;       /* Only needed for button status */
75         int opened;
76         int asyncio;
77         struct lock sm_lock;
78         struct sigio *sm_sigio;
79         struct kqinfo rkq;
80 };
81
82 static d_open_t         smopen;
83 static d_close_t        smclose;
84 static d_read_t         smread;
85 static d_ioctl_t        smioctl;
86 static d_kqfilter_t     smkqfilter;
87
88 static struct dev_ops sm_ops = {
89         { "sysmouse", 0, D_MPSAFE },
90         .d_open =       smopen,
91         .d_close =      smclose,
92         .d_read =       smread,
93         .d_ioctl =      smioctl,
94         .d_kqfilter =   smkqfilter,
95 };
96
97 /* local variables */
98 static struct sysmouse_state mouse_state;
99
100 static int      sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info,
101                     u_char *buf);
102 static void     smqueue(struct sysmouse_state *sc, mouse_info_t *info);
103 static int      pktlen(struct sysmouse_state *sc);
104 static void     smfilter_detach(struct knote *);
105 static int      smfilter(struct knote *, long);
106 static void     smget(struct sysmouse_state *sc, mouse_info_t *info);
107 static void     smpop(struct sysmouse_state *sc);
108
109 #ifdef EVDEV_SUPPORT
110 static struct evdev_dev *sysmouse_evdev;
111
112 static void
113 smdev_evdev_init(void)
114 {
115         int i;
116
117         sysmouse_evdev = evdev_alloc();
118         evdev_set_name(sysmouse_evdev, "System mouse");
119         evdev_set_phys(sysmouse_evdev, "sysmouse");
120         evdev_set_id(sysmouse_evdev, BUS_VIRTUAL, 0, 0, 0);
121         evdev_support_prop(sysmouse_evdev, INPUT_PROP_POINTER);
122         evdev_support_event(sysmouse_evdev, EV_SYN);
123         evdev_support_event(sysmouse_evdev, EV_REL);
124         evdev_support_event(sysmouse_evdev, EV_KEY);
125         evdev_support_rel(sysmouse_evdev, REL_X);
126         evdev_support_rel(sysmouse_evdev, REL_Y);
127         evdev_support_rel(sysmouse_evdev, REL_WHEEL);
128         evdev_support_rel(sysmouse_evdev, REL_HWHEEL);
129         for (i = 0; i < 8; i++)
130                 evdev_support_key(sysmouse_evdev, BTN_MOUSE + i);
131         if (evdev_register(sysmouse_evdev)) {
132                 evdev_free(sysmouse_evdev);
133                 sysmouse_evdev = NULL;
134         }
135 }
136
137 static void
138 smdev_evdev_write(int x, int y, int z, int buttons)
139 {
140
141         if (sysmouse_evdev == NULL || !(evdev_rcpt_mask & EVDEV_RCPT_SYSMOUSE))
142                 return;
143
144         evdev_push_event(sysmouse_evdev, EV_REL, REL_X, x);
145         evdev_push_event(sysmouse_evdev, EV_REL, REL_Y, y);
146         switch (evdev_sysmouse_t_axis) {
147         case EVDEV_SYSMOUSE_T_AXIS_PSM:
148                 switch (z) {
149                 case 1:
150                 case -1:
151                         evdev_push_rel(sysmouse_evdev, REL_WHEEL, -z);
152                         break;
153                 case 2:
154                 case -2:
155                         evdev_push_rel(sysmouse_evdev, REL_HWHEEL, z / 2);
156                         break;
157                 }
158                 break;
159         case EVDEV_SYSMOUSE_T_AXIS_UMS:
160                 if (buttons & (1 << 6))
161                         evdev_push_rel(sysmouse_evdev, REL_HWHEEL, 1);
162                 else if (buttons & (1 << 5))
163                         evdev_push_rel(sysmouse_evdev, REL_HWHEEL, -1);
164                 buttons &= ~((1 << 5)|(1 << 6));
165                 /* PASSTHROUGH */
166         case EVDEV_SYSMOUSE_T_AXIS_NONE:
167         default:
168                 evdev_push_rel(sysmouse_evdev, REL_WHEEL, -z);
169         }
170         evdev_push_mouse_btn(sysmouse_evdev, buttons);
171         evdev_sync(sysmouse_evdev);
172 }
173 #endif
174
175 static int
176 pktlen(struct sysmouse_state *sc)
177 {
178         if (sc->level == 0)
179                 return 5;
180         else
181                 return 8;
182 }
183
184 static int
185 smopen(struct dev_open_args *ap)
186 {
187         cdev_t dev = ap->a_head.a_dev;
188         struct sysmouse_state *sc = &mouse_state;
189         int ret;
190
191         DPRINTF(5, ("smopen: dev:%d,%d\n",
192                 major(dev), minor(dev)));
193
194         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
195         if (!sc->opened) {
196                 sc->fifo = kmalloc(sizeof(struct event_fifo),
197                     M_SYSCONS, M_WAITOK | M_ZERO);
198                 sc->opened = 1;
199                 sc->asyncio = 0;
200                 sc->sm_sigio = NULL;
201                 bzero(&sc->readstatus, sizeof(sc->readstatus));
202                 bzero(&sc->syncstatus, sizeof(sc->syncstatus));
203                 ret = 0;
204         } else {
205                 ret = EBUSY;
206         }
207         lockmgr(&sc->sm_lock, LK_RELEASE);
208
209         return ret;
210 }
211
212 static int
213 smclose(struct dev_close_args *ap)
214 {
215         struct sysmouse_state *sc = &mouse_state;
216
217         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
218         funsetown(&sc->sm_sigio);
219         sc->opened = 0;
220         sc->asyncio = 0;
221         sc->level = 0;
222         kfree(sc->fifo, M_SYSCONS);
223         sc->fifo = NULL;
224         lockmgr(&sc->sm_lock, LK_RELEASE);
225
226         return 0;
227 }
228
229 static int
230 smread(struct dev_read_args *ap)
231 {
232         struct sysmouse_state *sc = &mouse_state;
233         mousestatus_t backupstatus;
234         mouse_info_t info;
235         u_char buf[8];
236         struct uio *uio = ap->a_uio;
237         int error = 0, val, cnt = 0;
238
239         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
240         while (sc->fifo->fill <= 0) {
241                 /* Buffer too small to fit a complete mouse packet */
242                 if (uio->uio_resid < pktlen(sc)) {
243                         error = EIO;
244                         goto done;
245                 }
246                 if (ap->a_ioflag & IO_NDELAY) {
247                         error = EAGAIN;
248                         goto done;
249                 }
250                 error = lksleep(sc, &sc->sm_lock, PCATCH, "smread", 0);
251                 if (error == EINTR || error == ERESTART) {
252                         goto done;
253                 }
254         }
255
256         do {
257                 /* Buffer too small to fit a complete mouse packet */
258                 if (uio->uio_resid < pktlen(sc)) {
259                         error = EIO;
260                         goto done;
261                 }
262                 smget(sc, &info);
263                 backupstatus = sc->readstatus;
264                 val = sysmouse_evtopkt(sc, &info, buf);
265                 if (val > 0) {
266                         error = uiomove(buf, val, uio);
267                         if (error != 0) {
268                                 sc->readstatus = backupstatus;
269                                 goto done;
270                         }
271                         cnt++;
272                 }
273                 smpop(sc);
274         } while (sc->fifo->fill > 0);
275
276 done:
277         lockmgr(&sc->sm_lock, LK_RELEASE);
278         if (cnt > 0 && error != EFAULT)
279                 return 0;
280         return error;
281 }
282
283 static int
284 smioctl(struct dev_ioctl_args *ap)
285 {
286         struct sysmouse_state *sc = &mouse_state;
287         mousehw_t *hw;
288         mousemode_t *mode;
289
290         switch (ap->a_cmd) {
291         case FIOSETOWN:
292                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
293                 fsetown(*(int *)ap->a_data, &sc->sm_sigio);
294                 lockmgr(&sc->sm_lock, LK_RELEASE);
295                 return 0;
296         case FIOGETOWN:
297                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
298                 *(int *)ap->a_data = fgetown(&sc->sm_sigio);
299                 lockmgr(&sc->sm_lock, LK_RELEASE);
300                 return 0;
301         case FIOASYNC:
302                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
303                 if (*(int *)ap->a_data) {
304                         sc->asyncio = 1;
305                 } else {
306                         sc->asyncio = 0;
307                 }
308                 lockmgr(&sc->sm_lock, LK_RELEASE);
309                 return 0;
310         case MOUSE_GETHWINFO:   /* get device information */
311                 hw = (mousehw_t *)ap->a_data;
312                 hw->buttons = 10;               /* XXX unknown */
313                 hw->iftype = MOUSE_IF_SYSMOUSE;
314                 hw->type = MOUSE_MOUSE;
315                 hw->model = MOUSE_MODEL_GENERIC;
316                 hw->hwid = 0;
317                 return 0;
318
319         case MOUSE_GETMODE:     /* get protocol/mode */
320                 mode = (mousemode_t *)ap->a_data;
321                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
322                 mode->level = sc->level;
323                 lockmgr(&sc->sm_lock, LK_RELEASE);
324                 switch (mode->level) {
325                 case 0: /* emulate MouseSystems protocol */
326                         mode->protocol = MOUSE_PROTO_MSC;
327                         mode->rate = -1;                /* unknown */
328                         mode->resolution = -1;  /* unknown */
329                         mode->accelfactor = 0;  /* disabled */
330                         mode->packetsize = MOUSE_MSC_PACKETSIZE;
331                         mode->syncmask[0] = MOUSE_MSC_SYNCMASK;
332                         mode->syncmask[1] = MOUSE_MSC_SYNC;
333                         break;
334
335                 case 1: /* sysmouse protocol */
336                         mode->protocol = MOUSE_PROTO_SYSMOUSE;
337                         mode->rate = -1;
338                         mode->resolution = -1;
339                         mode->accelfactor = 0;
340                         mode->packetsize = MOUSE_SYS_PACKETSIZE;
341                         mode->syncmask[0] = MOUSE_SYS_SYNCMASK;
342                         mode->syncmask[1] = MOUSE_SYS_SYNC;
343                         break;
344                 }
345                 return 0;
346
347         case MOUSE_SETMODE:     /* set protocol/mode */
348                 mode = (mousemode_t *)ap->a_data;
349                 if (mode->level == -1)
350                         ;       /* don't change the current setting */
351                 else if ((mode->level < 0) || (mode->level > 1)) {
352                         return EINVAL;
353                 } else {
354                         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
355                         sc->level = mode->level;
356                         lockmgr(&sc->sm_lock, LK_RELEASE);
357                 }
358                 return 0;
359
360         case MOUSE_GETLEVEL:    /* get operation level */
361                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
362                 *(int *)ap->a_data = sc->level;
363                 lockmgr(&sc->sm_lock, LK_RELEASE);
364                 return 0;
365
366         case MOUSE_SETLEVEL:    /* set operation level */
367                 if ((*(int *)ap->a_data  < 0) || (*(int *)ap->a_data > 1)) {
368                         return EINVAL;
369                 }
370                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
371                 sc->level = *(int *)ap->a_data;
372                 lockmgr(&sc->sm_lock, LK_RELEASE);
373                 return 0;
374
375         case MOUSE_GETSTATUS:   /* get accumulated mouse events */
376                 lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
377                 *(mousestatus_t *)ap->a_data = sc->syncstatus;
378                 sc->syncstatus.flags = 0;
379                 sc->syncstatus.obutton = sc->syncstatus.button;
380                 sc->syncstatus.dx = 0;
381                 sc->syncstatus.dy = 0;
382                 sc->syncstatus.dz = 0;
383                 lockmgr(&sc->sm_lock, LK_RELEASE);
384                 return 0;
385
386         case MOUSE_READSTATE:   /* read status from the device */
387         case MOUSE_READDATA:    /* read data from the device */
388                 return ENODEV;
389         }
390
391         return ENOTTY;
392 }
393
394 static struct filterops smfiltops =
395         { FILTEROP_MPSAFE | FILTEROP_ISFD, NULL, smfilter_detach, smfilter };
396
397 static int
398 smkqfilter(struct dev_kqfilter_args *ap)
399 {
400         struct sysmouse_state *sc = &mouse_state;
401         struct knote *kn = ap->a_kn;
402         struct klist *klist;
403
404         ap->a_result = 0;
405
406         switch (kn->kn_filter) {
407         case EVFILT_READ:
408                 kn->kn_fop = &smfiltops;
409                 kn->kn_hook = (caddr_t)sc;
410                 break;
411         default:
412                 ap->a_result = EOPNOTSUPP;
413                 return (0);
414         }
415
416         klist = &sc->rkq.ki_note;
417         knote_insert(klist, kn);
418
419         return (0);
420 }
421
422 static void
423 smfilter_detach(struct knote *kn)
424 {
425         struct sysmouse_state *sc = &mouse_state;
426         struct klist *klist;
427
428         klist = &sc->rkq.ki_note;
429         knote_remove(klist, kn);
430 }
431
432 static int
433 smfilter(struct knote *kn, long hint)
434 {
435         struct sysmouse_state *sc = &mouse_state;
436         int ready = 0;
437
438         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
439         if (sc->fifo->fill > 0) {
440                 ready = 1;
441                 kn->kn_data = 0;
442         }
443         lockmgr(&sc->sm_lock, LK_RELEASE);
444
445         return ready;
446 }
447
448 static void
449 smqueue(struct sysmouse_state *sc, mouse_info_t *info)
450 {
451         struct event_fifo *f = sc->fifo;
452
453         if (f->fill >= FIFO_SIZE) {
454                 f->fill = FIFO_SIZE;
455                 f->buf[f->start] = *info;
456                 f->start = (f->start + 1) % FIFO_SIZE;
457                 f->dropped++;
458         } else {
459                 f->buf[(f->start + f->fill) % FIFO_SIZE] = *info;
460                 f->fill++;
461         }
462
463 }
464
465 static void
466 smget(struct sysmouse_state *sc, mouse_info_t *info)
467 {
468         struct event_fifo *f = sc->fifo;
469
470         if (f->fill > 0)
471                 *info = f->buf[f->start];
472 }
473
474 static void
475 smpop(struct sysmouse_state *sc)
476 {
477         struct event_fifo *f = sc->fifo;
478
479         if (f->fill > 0) {
480                 f->fill--;
481                 f->start = (f->start + 1) % FIFO_SIZE;
482         }
483 }
484
485 static void
486 sm_attach_mouse(void *unused)
487 {
488         struct sysmouse_state *sc = &mouse_state;
489         cdev_t dev;
490
491         lockinit(&mouse_state.sm_lock, "sysmouse", 0, LK_CANRECURSE);
492         sc->fifo = NULL;
493
494         dev = make_dev(&sm_ops, 0, UID_ROOT, GID_WHEEL, 0600, "sysmouse");
495 #ifdef EVDEV_SUPPORT
496         smdev_evdev_init();
497 #endif
498 }
499
500 SYSINIT(sysmouse, SI_SUB_DRIVERS, SI_ORDER_ANY, sm_attach_mouse, NULL);
501
502 static int
503 sysmouse_updatestatus(mousestatus_t *status, mouse_info_t *info)
504 {
505         int x, y, z;
506
507         status->obutton = status->button;
508
509         switch (info->operation) {
510         case MOUSE_ACTION:
511                 status->button = info->u.data.buttons;
512                 /* FALL THROUGH */
513         case MOUSE_MOTION_EVENT:
514                 x = info->u.data.x;
515                 y = info->u.data.y;
516                 z = info->u.data.z;
517                 break;
518         case MOUSE_BUTTON_EVENT:
519                 x = y = z = 0;
520                 if (info->u.event.value > 0)
521                         status->button |= info->u.event.id;
522                 else
523                         status->button &= ~info->u.event.id;
524                 break;
525         default:
526                 return 0;
527         }
528
529         status->dx += x;
530         status->dy += y;
531         status->dz += z;
532         status->flags |= ((x || y || z) ? MOUSE_POSCHANGED : 0)
533                          | (status->obutton ^ status->button);
534
535 #ifdef EVDEV_SUPPORT
536         smdev_evdev_write(x, y, z, status->button);
537 #endif
538
539         return 1;
540 }
541
542 /* Requires buf to hold at least 8 bytes, returns number of bytes written */
543 static int
544 sysmouse_evtopkt(struct sysmouse_state *sc, mouse_info_t *info, u_char *buf)
545 {
546         /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */
547         static int butmap[8] = {
548             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
549             MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP,
550             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP,
551             MOUSE_MSC_BUTTON3UP,
552             MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP,
553             MOUSE_MSC_BUTTON2UP,
554             MOUSE_MSC_BUTTON1UP,
555             0,
556         };
557         int x, y, z;
558
559         sc->readstatus.dx = 0;
560         sc->readstatus.dy = 0;
561         sc->readstatus.dz = 0;
562         sc->readstatus.flags = 0;
563         if (sysmouse_updatestatus(&sc->readstatus, info) == 0)
564                 return 0;
565
566         /* We aren't using the sc->readstatus.dx/dy/dz values */
567
568         if (sc->readstatus.flags == 0)
569                 return 0;
570
571         x = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.x);
572         y = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.y);
573         z = (info->operation == MOUSE_BUTTON_EVENT ? 0 : info->u.data.z);
574
575         /* the first five bytes are compatible with MouseSystems' */
576         buf[0] = MOUSE_MSC_SYNC
577                  | butmap[sc->readstatus.button & MOUSE_STDBUTTONS];
578         x = imax(imin(x, 255), -256);
579         buf[1] = x >> 1;
580         buf[3] = x - buf[1];
581         y = -imax(imin(y, 255), -256);
582         buf[2] = y >> 1;
583         buf[4] = y - buf[2];
584         if (sc->level >= 1) {
585                 /* extended part */
586                 z = imax(imin(z, 127), -128);
587                 buf[5] = (z >> 1) & 0x7f;
588                 buf[6] = (z - (z >> 1)) & 0x7f;
589                 /* buttons 4-10 */
590                 buf[7] = (~sc->readstatus.button >> 3) & 0x7f;
591         }
592
593         if (sc->level >= 1)
594                 return 8;
595
596         return 5;
597 }
598
599 int
600 sysmouse_event(mouse_info_t *info)
601 {
602         struct sysmouse_state *sc = &mouse_state;
603         int ret;
604
605         lockmgr(&sc->sm_lock, LK_EXCLUSIVE);
606         ret = sysmouse_updatestatus(&sc->syncstatus, info);
607         if (ret != 0)
608                 ret = sc->syncstatus.flags;
609         if (!sc->opened) {
610                 lockmgr(&sc->sm_lock, LK_RELEASE);
611                 return ret;
612         }
613
614         switch (info->operation) {
615         case MOUSE_ACTION:
616         case MOUSE_MOTION_EVENT:
617         case MOUSE_BUTTON_EVENT:
618                 smqueue(sc, info);
619                 if (sc->asyncio)
620                         pgsigio(sc->sm_sigio, SIGIO, 0);
621                 lockmgr(&sc->sm_lock, LK_RELEASE);
622                 wakeup(sc);
623                 KNOTE(&sc->rkq.ki_note, 0);
624                 break;
625         default:
626                 lockmgr(&sc->sm_lock, LK_RELEASE);
627                 break;
628         }
629
630         return ret;
631 }
632
633 #endif /* !SC_NO_SYSMOUSE */