/* $NetBSD: ms.c,v 1.28 2022/06/26 06:25:09 tsutsui Exp $ */ /* * Copyright (c) 1995 Leo Weppelman. * All rights reserved. * * based on: * * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ms.c 8.1 (Berkeley) 6/11/93 * * Header: ms.c,v 1.5 92/11/26 01:28:47 torek Exp (LBL) */ /* * Mouse driver. */ #include __KERNEL_RCSID(0, "$NetBSD: ms.c,v 1.28 2022/06/26 06:25:09 tsutsui Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mouse.h" #if NMOUSE > 0 /* there's really no more physical ports on an atari. */ #if NMOUSE > 1 #undef NMOUSE #define NMOUSE 1 #endif typedef void (*FPV)(void *); static struct ms_softc ms_softc[NMOUSE]; static dev_type_open(msopen); static dev_type_close(msclose); static dev_type_read(msread); static dev_type_ioctl(msioctl); static dev_type_poll(mspoll); static dev_type_kqfilter(mskqfilter); const struct cdevsw ms_cdevsw = { .d_open = msopen, .d_close = msclose, .d_read = msread, .d_write = nowrite, .d_ioctl = msioctl, .d_stop = nostop, .d_tty = notty, .d_poll = mspoll, .d_mmap = nommap, .d_kqfilter = mskqfilter, .d_discard = nodiscard, .d_flag = 0 }; static void ms_3b_delay(struct ms_softc *); int mouseattach(int cnt) { printf("1 mouse configured\n"); ms_softc[0].ms_emul3b = 1; callout_init(&ms_softc[0].ms_delay_ch, 0); return NMOUSE; } static void ms_3b_delay(struct ms_softc *ms) { REL_MOUSE rel_ms; rel_ms.id = TIMEOUT_ID; rel_ms.dx = rel_ms.dy = 0; mouse_soft(&rel_ms, sizeof(rel_ms), KBD_TIMEO_PKG); } /* * Note that we are called from the keyboard software interrupt! */ void mouse_soft(REL_MOUSE *rel_ms, int size, int type) { struct ms_softc *ms = &ms_softc[0]; struct firm_event *fe, *fe2; REL_MOUSE fake_mouse; int get, put; int s; uint8_t mbut, bmask; int flush_buttons; if (ms->ms_events.ev_io == NULL) return; switch (type) { case KBD_JOY1_PKG: /* * Ignore if in emulation mode */ if (ms->ms_emul3b) return; /* * There are some mice that have their middle button * wired to the 'up' bit of joystick 1.... * Simulate a mouse packet with dx = dy = 0, the middle * button state set by UP and the other buttons unchanged. * Flush all button changes. */ flush_buttons = 1; fake_mouse.id = ((rel_ms->dx & 0x01) != 0 ? 0x04 : 0x00) | (ms->ms_buttons & 0x03); fake_mouse.dx = fake_mouse.dy = 0; rel_ms = &fake_mouse; break; case KBD_TIMEO_PKG: /* * Timeout package. No button changes and no movement. * Flush all button changes. */ flush_buttons = 1; fake_mouse.id = ms->ms_buttons; fake_mouse.dx = fake_mouse.dy = 0; rel_ms = &fake_mouse; break; case KBD_RMS_PKG: /* * Normal mouse package. Always copy the middle button * status. The emulation code decides if button changes * must be flushed. */ rel_ms->id = (ms->ms_buttons & 0x04) | (rel_ms->id & 0x03); flush_buttons = ms->ms_emul3b ? 0 : 1; break; default: return; } s = splev(); get = ms->ms_events.ev_get; put = ms->ms_events.ev_put; fe = &ms->ms_events.ev_q[put]; if ((type != KBD_TIMEO_PKG) && ms->ms_emul3b && ms->ms_bq_idx != 0) callout_stop(&ms->ms_delay_ch); /* * Button states are encoded in the lower 3 bits of 'id' */ if ((mbut = (rel_ms->id ^ ms->ms_buttons)) == 0 && (put != get)) { /* * Compact dx/dy messages. Always generate an event when * a button is pressed or the event queue is empty. */ ms->ms_dx += rel_ms->dx; ms->ms_dy += rel_ms->dy; goto out; } rel_ms->dx += ms->ms_dx; rel_ms->dy += ms->ms_dy; ms->ms_dx = ms->ms_dy = 0; /* * Output location events _before_ button events ie. make sure * the button is pressed at the correct location. */ if (rel_ms->dx != 0) { if ((++put) % EV_QSIZE == get) { put--; goto out; } fe->id = LOC_X_DELTA; fe->value = rel_ms->dx; firm_gettime(fe); if (put >= EV_QSIZE) { put = 0; fe = &ms->ms_events.ev_q[0]; } else { fe++; } } if (rel_ms->dy != 0) { if ((++put) % EV_QSIZE == get) { put--; goto out; } fe->id = LOC_Y_DELTA; fe->value = rel_ms->dy; firm_gettime(fe); if (put >= EV_QSIZE) { put = 0; fe = &ms->ms_events.ev_q[0]; } else { fe++; } } if (mbut != 0 && (type != KBD_TIMEO_PKG)) { for (bmask = 0x01; bmask < 0x08; bmask <<= 1) { if ((mbut & bmask) == 0) continue; fe2 = &ms->ms_bq[ms->ms_bq_idx++]; if (bmask == 0x01) fe2->id = MS_RIGHT; else if (bmask == 0x02) fe2->id = MS_LEFT; else fe2->id = MS_MIDDLE; fe2->value = (rel_ms->id & bmask) != 0 ? VKEY_DOWN : VKEY_UP; firm_gettime(fe2); } } /* * Handle 3rd button emulation. */ if (ms->ms_emul3b && ms->ms_bq_idx != 0 && (type != KBD_TIMEO_PKG)) { /* * If the middle button is pressed, any change to * one of the other buttons releases all. */ if ((ms->ms_buttons & 0x04) != 0 && (mbut & 0x03) != 0) { ms->ms_bq[0].id = MS_MIDDLE; ms->ms_bq_idx = 1; rel_ms->id = 0; flush_buttons = 1; goto out; } if (ms->ms_bq_idx == 2) { if (ms->ms_bq[0].value == ms->ms_bq[1].value) { /* Must be 2 button presses! */ ms->ms_bq[0].id = MS_MIDDLE; ms->ms_bq_idx = 1; rel_ms->id = 7; } } else if (ms->ms_bq[0].value == VKEY_DOWN) { callout_reset(&ms->ms_delay_ch, 10, (FPV)ms_3b_delay, (void *)ms); goto out; } flush_buttons = 1; } out: if (flush_buttons) { int i; for (i = 0; i < ms->ms_bq_idx; i++) { if ((++put) % EV_QSIZE == get) { ms->ms_bq_idx = 0; put--; goto out; } *fe = ms->ms_bq[i]; if (put >= EV_QSIZE) { put = 0; fe = &ms->ms_events.ev_q[0]; } else { fe++; } } ms->ms_bq_idx = 0; } ms->ms_events.ev_put = put; ms->ms_buttons = rel_ms->id; splx(s); EV_WAKEUP(&ms->ms_events); } static int msopen(dev_t dev, int flags, int mode, struct lwp *l) { uint8_t report_ms_joy[] = { 0x14, 0x08 }; struct ms_softc *ms; int unit; unit = minor(dev); ms = &ms_softc[unit]; if (unit >= NMOUSE) return EXDEV; if (ms->ms_events.ev_io) return EBUSY; ms->ms_events.ev_io = l->l_proc; ms->ms_dx = ms->ms_dy = 0; ms->ms_buttons = 0; ms->ms_bq[0].id = ms->ms_bq[1].id = 0; ms->ms_bq_idx = 0; ev_init(&ms->ms_events); /* may cause sleep */ /* * Enable mouse reporting. */ kbd_write(report_ms_joy, sizeof(report_ms_joy)); return 0; } static int msclose(dev_t dev, int flags, int mode, struct lwp *l) { uint8_t disable_ms_joy[] = { 0x12, 0x1a }; int unit; struct ms_softc *ms; unit = minor(dev); ms = &ms_softc[unit]; /* * Turn off mouse interrogation. */ kbd_write(disable_ms_joy, sizeof(disable_ms_joy)); ev_fini(&ms->ms_events); ms->ms_events.ev_io = NULL; return 0; } static int msread(dev_t dev, struct uio *uio, int flags) { struct ms_softc *ms; ms = &ms_softc[minor(dev)]; return ev_read(&ms->ms_events, uio, flags); } static int msioctl(dev_t dev, u_long cmd, register void * data, int flag, struct lwp *l) { struct ms_softc *ms; int unit; unit = minor(dev); ms = &ms_softc[unit]; switch (cmd) { case MIOCS3B_EMUL: ms->ms_emul3b = (*(int *)data != 0) ? 1 : 0; return 0; case MIOCG3B_EMUL: *(int *)data = ms->ms_emul3b; return 0; case FIONBIO: /* we will remove this someday (soon???) */ return 0; case FIOASYNC: ms->ms_events.ev_async = *(int *)data != 0; return 0; case FIOSETOWN: if (-*(int *)data != ms->ms_events.ev_io->p_pgid && *(int *)data != ms->ms_events.ev_io->p_pid) return EPERM; return 0; case TIOCSPGRP: if (*(int *)data != ms->ms_events.ev_io->p_pgid) return EPERM; return 0; case VUIDGFORMAT: /* we only do firm_events */ *(int *)data = VUID_FIRM_EVENT; return 0; case VUIDSFORMAT: if (*(int *)data != VUID_FIRM_EVENT) return EINVAL; return 0; } return ENOTTY; } static int mspoll(dev_t dev, int events, struct lwp *l) { struct ms_softc *ms; ms = &ms_softc[minor(dev)]; return ev_poll(&ms->ms_events, events, l); } static int mskqfilter(dev_t dev, struct knote *kn) { struct ms_softc *ms; ms = &ms_softc[minor(dev)]; return ev_kqfilter(&ms->ms_events, kn); } #endif /* NMOUSE > 0 */