ms.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Mouse streams module.
*/
#include <sys/sysmacros.h>
#include <sys/vuid_event.h>
/*
* This is the loadable module wrapper.
*/
"ms",
&ms_info,
};
/*
* Module linkage information for the kernel.
*/
static struct modlstrmod modlstrmod = {
};
static struct modlinkage modlinkage = {
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (EBUSY);
}
int
{
}
struct msdata {
int msd_flags; /* random flags */
int msd_iocid; /* ID of "ioctl" being waited for */
int msd_iocerror; /* error return from "ioctl" */
char msd_oldbutt; /* button state at last sample */
short msd_state; /* state counter for input routine */
short msd_jitter;
int msd_baud_rate; /* mouse baud rate */
int msd_rcnt_baud_chng; /* baud changed recently */
int msd_data_pkt_cnt; /* no of packets since last baud change */
int msd_qenable_more; /* enable msrserv if baud changed recently */
int msd_hold_baud_stup; /* # of packets to wait for baud setup */
};
/*
* Input routine states. See msinput().
*/
#define MS_WAIT_BUTN 0
#define MS_WAIT_X 1
#define MS_WAIT_Y 2
#define MS_WAIT_X2 3
#define MS_WAIT_Y2 4
#define MS_PKT_SZ 5
/*
* This module supports mice runing at 1200, 4800 and 9600 baud rates.
*
* If there was a baud change recently, then we want to wait
* for some time to make sure that no other baud change is on its way.
* If the second baud rate change is done then the packets between
* changes are garbage and are thrown away during the baud change.
*/
/*
* The following #defines were tuned by experimentations.
*/
#define MS_HOLD_BAUD_STUP 48
#define MS_CNT_TOB1200 7
static int ms_overrun_msg; /* Message when overrun circular buffer */
static int ms_overrun_cnt; /* Increment when overrun circular buffer */
/*
* Max pixel delta of jitter controlled. As this number increases the jumpiness
* of the ms increases, i.e., the coarser the motion for medium speeds.
*/
static int ms_jitter_thresh = 0;
/*
* ms_jitter_thresh is the maximum number of jitters suppressed. Thus,
* hz/ms_jitter_thresh is the maximum interval of jitters suppressed. As
* ms_jitter_thresh increases, a wider range of jitter is suppressed. However,
* the more inertia the mouse seems to have, i.e., the slower the mouse is to
* react.
*/
/*
* Measure how many (ms_speed_count) ms deltas exceed threshold
* (ms_speedlimit). If ms_speedlaw then throw away deltas over ms_speedlimit.
* This is to keep really bad mice that jump around from getting too far.
*/
static int ms_speedlimit = 48;
static int ms_speedlaw = 0;
static int ms_speed_count;
static int msjitterrate = 12;
/*
* Mouse buffer size in bytes. Place here as variable so that one could
* massage it using adb if it turns out to be too small.
*/
static int MS_BUF_BYTES = 4096;
static int MS_DEBUG;
/*
* Most of these should be "void", but the people who defined the "streams"
* data structures for S5 didn't understand data types.
*/
static struct module_info msmiinfo = {
0,
"ms",
0,
2048,
128
};
(int (*)())msrput,
(int (*)())msrserv,
(int (*)())NULL,
};
static struct module_info msmoinfo = {
0,
"ms",
0,
2048,
128
};
(int (*)())mswput,
(int (*)())NULL,
(int (*)())NULL,
};
&msrinit,
&mswinit,
NULL,
NULL,
};
static void msresched(void *);
static void msreioctl(void *);
static void msincr(void *);
/*
* Dummy qbufcall callback routine used by open and close.
* The framework will wake up qwait_sig when we return from
* this routine (as part of leaving the perimeters.)
* (The framework enters the perimeters before calling the qbufcall() callback
* and leaves the perimeters after the callback routine has executed. The
* when it leaves the perimeter. See qwait(9E).)
*/
/* ARGSUSED */
static void
dummy_callback(void *arg)
{}
/*
* Open a mouse.
*/
/*ARGSUSED*/
static int
queue_t *q;
{
register struct mousebuf *b;
int error = 0;
return (0); /* already attached */
return (EINVAL);
/*
* Allocate an msdata structure.
*/
/*
* Set up queue pointers, so that the "put" procedure will accept
* the reply to the "ioctl" message we send down.
*/
qprocson(q);
/*
* Setup tty modes.
*/
if (!qwait_sig(q)) {
qunbufcall(q, id);
qprocsoff(q);
return (EINTR);
}
}
if (!qwait_sig(q)) {
qunbufcall(q, id);
qprocsoff(q);
return (EINTR);
}
}
msd->msd_data_pkt_cnt = 0;
msd->msd_qenable_more = 0;
/*
* Now wait for it. Let our read queue put routine wake us up
* when it arrives.
*/
if (!qwait_sig(q)) {
goto error;
}
}
goto error;
/*
* Set up private data.
*/
/*
* Allocate buffer and initialize data.
*/
/ sizeof (struct mouseinfo);
}
/*
* Tell the module below us that it should return input immediately.
*/
return (0);
qprocsoff(q);
return (error);
}
/*
* Close the mouse
*/
/* ARGSUSED1 */
static int
queue_t *q;
int flag;
{
/*
* Tell the module below us that it need not return input immediately.
*/
qprocsoff(q);
/*
* Since we're about to destroy our private data, turn off
* our open flag first, so we don't accept any more input
* and try to use that data.
*/
if (msd->msd_jitter) {
msd->msd_jitter = 0;
}
if (msd->msd_reioctl_id) {
msd->msd_reioctl_id = 0;
}
if (msd->msd_resched_id) {
msd->msd_resched_id = 0;
}
/*
* We were holding an "ioctl" response pending the
* availability of an "mblk" to hold data to be passed up;
* another "ioctl" came through, which means that "ioctl"
* must have timed out or been aborted.
*/
}
/* Free mouse buffer */
/* Free msdata structure */
return (0);
}
/*
* Read queue service routine.
* Turn buffered mouse events into stream messages.
*/
static void
msrserv(q)
register queue_t *q;
{
register struct mousebuf *b;
register int button_number;
register int hwbit;
/*
* Handle the case of a queue which is backenabled before
* initialization is complete.
*/
return;
}
int i;
return;
} else {
/*
* throw away packets in beginning (mostly garbage)
*/
for (i = 0; i < msd->msd_hold_baud_stup; i++) {
/* circular buffer wraparound */
}
msd->msd_rcnt_baud_chng = 0;
msd->msd_data_pkt_cnt = 0;
msd->msd_qenable_more = 0;
}
}
switch (ms->ms_readformat) {
case MS_3BYTE_FORMAT: {
register char *cp;
/* Update read buttons */
/* lower pri to avoid mouse droppings */
} else {
if (msd->msd_resched_id)
if (msd->msd_resched_id == 0)
return; /* try again later */
/* bufcall failed; just pitch this event */
/* or maybe flush queue? */
}
/* circular buffer wraparound */
break;
}
case MS_VUID_FORMAT: {
register Firm_event *fep;
switch (ms->ms_eventstate) {
case EVENT_BUT3:
case EVENT_BUT2:
case EVENT_BUT1:
/* Test the button. Send an event if it changed. */
/* Update read buttons and set value */
} else {
}
} else {
if (msd->msd_resched_id)
sizeof (Firm_event),
if (msd->msd_resched_id == 0)
return; /* try again later */
/* bufcall failed; just pitch this event */
/* or maybe flush queue? */
}
}
break;
case EVENT_Y:
/* Send y if changed. */
} else {
if (msd->msd_resched_id)
sizeof (Firm_event),
if (msd->msd_resched_id == 0)
return; /* try again later */
/* bufcall failed; just pitch this event */
/* or maybe flush queue? */
}
}
break;
case EVENT_X:
/* Send x if changed. */
} else {
if (msd->msd_resched_id)
sizeof (Firm_event),
if (msd->msd_resched_id == 0)
return; /* try again later */
/* bufcall failed; just pitch this event */
/* or maybe flush queue? */
}
}
break;
}
/* lower pri to avoid mouse droppings */
}
/* circular buffer wraparound */
} else
ms->ms_eventstate--;
}
}
}
}
static void
{
queue_t *q;
msd->msd_resched_id = 0;
qenable(q); /* run the service procedure */
}
/*
* Line discipline output queue put procedure: handles M_IOCTL
* messages.
*/
static void
register queue_t *q;
{
/*
* Process M_FLUSH, and some M_IOCTL, messages here; pass
* everything else down.
*/
case M_FLUSH:
default:
break;
case M_IOCTL:
break;
}
}
static void
{
queue_t *q;
msd->msd_reioctl_id = 0;
}
}
static void
register queue_t *q;
{
int err = 0;
goto out;
}
if (MS_DEBUG)
case VUIDSFORMAT:
if (err != 0)
break;
break;
/*
* Flush mouse buffer because the messages upstream of us
* are in the old format.
*/
break;
case VUIDGFORMAT:
ioctlrespsize = sizeof (int);
goto allocfailure;
}
break;
case VUIDSADDR:
case VUIDGADDR:
if (err != 0)
break;
break;
}
else
break;
case MSIOGETPARMS:
if (MS_DEBUG)
printf("ms_getparms\n");
ioctlrespsize = sizeof (Ms_parms);
goto allocfailure;
}
break;
case MSIOSETPARMS:
if (MS_DEBUG)
printf("ms_setparms\n");
if (err != 0)
break;
break;
default:
return;
}
out:
if (err != 0)
else {
}
return;
/*
* We needed to allocate something to handle this "ioctl", but
* couldn't; save this "ioctl" and arrange to get called back when
* it's more likely that we can get what we need.
* If there's already one being saved, throw it out, since it
* must have timed out.
*/
if (msd->msd_reioctl_id)
}
static int
{
return (0);
}
static int
{
return (0);
}
static void
{
register queue_t *q;
}
/*
* Mouse read queue put procedure.
*/
static void
register queue_t *q;
{
register char *readp;
if (msd == 0)
return;
case M_FLUSH:
default:
return;
case M_BREAK:
return;
}
return;
}
/*
* If we are sampling a 4800 baud mouse at 9600,
* we want to wait for long time because there is no
* fixed timeframe for receiving break. If we are sampling
* a 1200 baud mouse at 4800 or 9600 baud rate then
* it is guaranteed that break will be received very soon.
*/
if (msd->msd_rcnt_baud_chng) {
switch (msd->msd_baud_rate) {
case B9600:
break;
case B4800:
} else {
}
break;
case B1200:
default:
break;
}
} else {
}
/*
* Change baud rate.
*/
return;
}
return;
}
msd->msd_data_pkt_cnt = 0;
if (MS_DEBUG)
return;
case M_IOCACK:
case M_IOCNAK:
/*
* If we are doing an "ioctl" ourselves, check if this
* is the reply to that code. If so, wake up the
* "open" routine, and toss the reply, otherwise just
* pass it up.
*/
/*
* This isn't the reply we're looking for. Move along.
*/
} else {
/*
* If we sent down a request to change the baud rate.
* This is the reply. Just ignore it.
*/
}
}
return;
case M_DATA:
return;
}
break;
}
/*
* A data message, consisting of bytes from the mouse.
* Hand each byte to our input routine.
*/
do {
if (msd->msd_rcnt_baud_chng)
msd->msd_data_pkt_cnt++;
}
}
/*
* Mouse input routine; process a byte received from a mouse and
* assemble into a mouseinfo message for the window system.
*
* The MSC mice send a five-byte packet organized as
* button, dx, dy, dx, dy
* where dx and dy can be any signed byte value. The mouseinfo message
* is organized as
* dx, dy, button, timestamp
* Our strategy is to add up the 2 dx and the 2 dy in the five-byte
* packet, then send the mouseinfo message up.
*
* Basic algorithm: throw away bytes until we get a [potential]
* button byte. Collect button; Collect dx1; Collect dy1; Collect dx2
* and add it to dx1; Collect dy2 and add it to dy1; Send button,
* dx, dy, timestamp.
*
* Watch out for overflow!
*/
static void
char c;
{
register struct mousebuf *b;
register int jitter_radius;
register int temp;
if (b == NULL)
return;
case MS_WAIT_BUTN:
if ((c & 0xf8) != 0x80) {
if (MS_DEBUG)
printf("Mouse input char %x discarded\n",
(int)c & 0xff);
if (msd->msd_rcnt_baud_chng) {
}
return;
}
/*
* Probably a button byte.
* Lower 3 bits are left, middle, right.
*/
break;
case MS_WAIT_X:
/*
* Delta X byte. Add the delta X from this sample to
* the delta X we're accumulating in the current event.
*/
break;
case MS_WAIT_Y:
/*
* Delta Y byte. Add the delta Y from this sample to
* the delta Y we're accumulating in the current event.
* (Subtract, actually, because the mouse reports
* increasing Y up the screen.)
*/
break;
case MS_WAIT_X2:
/*
* Second delta X byte.
*/
break;
case MS_WAIT_Y2:
/*
* Second delta Y byte.
*/
break;
}
/*
* Done yet?
*/
else {
return;
}
if (msd->msd_jitter) {
msd->msd_jitter = 0;
}
/*
* Buttons did not change; did position?
*/
/* no, position did not change - boring event */
return;
}
/*
* Did the mouse move more than the jitter threshhold?
*/
/*
* Mouse moved less than the jitter threshhold.
* Don't indicate an event; keep accumulating motions.
* After "msjittertimeout" ticks expire, treat
* the accumulated delta as the real delta.
*/
return;
}
}
}
/*
* Increment the mouse sample pointer.
* Called either immediately after a sample or after a jitter timeout.
*/
static void
{
register struct mousebuf *b;
char oldbutt;
register int wake;
register int speedlimit = ms_speedlimit;
/*
* No longer waiting for jitter timeout
*/
msd->msd_jitter = 0;
if (b == NULL)
return;
if (ms_speedlaw) {
if (xabs > speedlimit)
if (yabs > speedlimit)
}
/* See if we need to wake up anyone waiting for input */
/* Adjust circular buffer pointer */
b->mb_off = 0;
} else {
mi++;
}
/*
* If over-took read index then flush buffer so that mouse state
* is consistent.
*/
if (ms_overrun_msg)
"Mouse buffer flushed when overrun.\n");
}
/* Remember current buttons and fractional part of x & y */
}