Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * Mouse streams module.
     31  */
     32 
     33 #include <sys/types.h>
     34 #include <sys/param.h>
     35 #include <sys/sysmacros.h>
     36 #include <sys/signal.h>
     37 #include <sys/termios.h>
     38 #include <sys/termio.h>
     39 #include <sys/stream.h>
     40 #include <sys/stropts.h>
     41 #include <sys/strsun.h>
     42 #include <sys/tty.h>
     43 #include <sys/strtty.h>
     44 #include <sys/time.h>
     45 #include <sys/kmem.h>
     46 #include <sys/file.h>
     47 #include <sys/uio.h>
     48 #include <sys/errno.h>
     49 #include <sys/debug.h>
     50 
     51 #include <sys/vuid_event.h>
     52 #include <sys/msreg.h>
     53 #include <sys/msio.h>
     54 #include <sys/ddi.h>
     55 #include <sys/sunddi.h>
     56 
     57 #include <sys/modctl.h>
     58 
     59 
     60 /*
     61  * This is the loadable module wrapper.
     62  */
     63 
     64 static struct streamtab ms_info;
     65 
     66 static struct fmodsw fsw = {
     67 	"ms",
     68 	&ms_info,
     69 	D_MP | D_MTPERMOD
     70 };
     71 
     72 /*
     73  * Module linkage information for the kernel.
     74  */
     75 
     76 static struct modlstrmod modlstrmod = {
     77 	&mod_strmodops, "streams module for mouse", &fsw
     78 };
     79 
     80 static struct modlinkage modlinkage = {
     81 	MODREV_1, &modlstrmod, NULL
     82 };
     83 
     84 
     85 int
     86 _init(void)
     87 {
     88 	return (mod_install(&modlinkage));
     89 }
     90 
     91 int
     92 _fini(void)
     93 {
     94 	return (EBUSY);
     95 }
     96 
     97 int
     98 _info(struct modinfo *modinfop)
     99 {
    100 	return (mod_info(&modlinkage, modinfop));
    101 }
    102 
    103 #define	BYTECLIP(x)	(char)((x) > 127 ? 127 : ((x) < -128 ? -128 : (x)))
    104 
    105 struct msdata {
    106 	struct ms_softc	msd_softc;
    107 	queue_t	*msd_readq;	/* upstream read queue */
    108 	mblk_t	*msd_iocpending; /* "ioctl" awaiting buffer */
    109 	int	msd_flags;	/* random flags */
    110 	int	msd_iocid;	/* ID of "ioctl" being waited for */
    111 	int	msd_iocerror;	/* error return from "ioctl" */
    112 	char	msd_oldbutt;	/* button state at last sample */
    113 	short	msd_state;	/* state counter for input routine */
    114 	short	msd_jitter;
    115 	timeout_id_t	msd_timeout_id;	/* id returned by timeout() */
    116 	bufcall_id_t	msd_reioctl_id;	/* id returned by bufcall() */
    117 	bufcall_id_t	msd_resched_id;	/* id returned by bufcall() */
    118 	int	msd_baud_rate;	/* mouse baud rate */
    119 	int	msd_rcnt_baud_chng; /* baud changed recently */
    120 	int	msd_data_pkt_cnt; /* no of packets since last baud change */
    121 	int	msd_qenable_more; /* enable msrserv if baud changed recently */
    122 	int	msd_hold_baud_stup; /* # of packets to wait for baud setup */
    123 };
    124 
    125 #define	MS_OPEN		0x00000001	/* mouse is open for business */
    126 #define	MS_IOCWAIT	0x00000002	/* "open" waiting for ioctl to finish */
    127 #define	MS_IOCTOSS	0x00000004	/* Toss ioctl returns */
    128 
    129 /*
    130  * Input routine states. See msinput().
    131  */
    132 #define	MS_WAIT_BUTN	0
    133 #define	MS_WAIT_X	1
    134 #define	MS_WAIT_Y	2
    135 #define	MS_WAIT_X2	3
    136 #define	MS_WAIT_Y2	4
    137 #define	MS_PKT_SZ	5
    138 
    139 /*
    140  * This module supports mice runing at 1200, 4800 and 9600 baud rates.
    141  *
    142  * If there was a baud change recently, then we want to wait
    143  * for some time to make sure that no other baud change is on its way.
    144  * If the second baud rate change is done then the packets between
    145  * changes are garbage and are thrown away during the baud change.
    146  */
    147 /*
    148  * The following #defines were tuned by experimentations.
    149  */
    150 #define		MS_HOLD_BAUD_STUP	48
    151 #define		MS_CNT_TOB1200		7
    152 
    153 
    154 static int	ms_overrun_msg;	/* Message when overrun circular buffer */
    155 static int	ms_overrun_cnt;	/* Increment when overrun circular buffer */
    156 
    157 /*
    158  * Max pixel delta of jitter controlled. As this number increases the jumpiness
    159  * of the ms increases, i.e., the coarser the motion for medium speeds.
    160  */
    161 static int	ms_jitter_thresh = 0;
    162 
    163 /*
    164  * ms_jitter_thresh is the maximum number of jitters suppressed. Thus,
    165  * hz/ms_jitter_thresh is the maximum interval of jitters suppressed. As
    166  * ms_jitter_thresh increases, a wider range of jitter is suppressed. However,
    167  * the more inertia the mouse seems to have, i.e., the slower the mouse is to
    168  * react.
    169  */
    170 
    171 /*
    172  * Measure how many (ms_speed_count) ms deltas exceed threshold
    173  * (ms_speedlimit). If ms_speedlaw then throw away deltas over ms_speedlimit.
    174  * This is to keep really bad mice that jump around from getting too far.
    175  */
    176 static int	ms_speedlimit = 48;
    177 static int	ms_speedlaw = 0;
    178 static int	ms_speed_count;
    179 static int	msjitterrate = 12;
    180 
    181 #define	JITTER_TIMEOUT (hz/msjitterrate)
    182 
    183 static clock_t	msjittertimeout; /* Timeout used when mstimeout in effect */
    184 
    185 /*
    186  * Mouse buffer size in bytes.  Place here as variable so that one could
    187  * massage it using adb if it turns out to be too small.
    188  */
    189 static int	MS_BUF_BYTES = 4096;
    190 
    191 
    192 static int	MS_DEBUG;
    193 
    194 
    195 /*
    196  * Most of these should be "void", but the people who defined the "streams"
    197  * data structures for S5 didn't understand data types.
    198  */
    199 static int msopen(queue_t *q, dev_t *devp, int oflag, int sflag,
    200 				cred_t *credp);
    201 static int msclose(queue_t *q, int flag, cred_t *credp);
    202 static void mswput(queue_t *q, mblk_t *mp);
    203 static void msrput(queue_t *q, mblk_t *mp);
    204 static void msrserv(queue_t *q);
    205 
    206 static struct module_info msmiinfo = {
    207 	0,
    208 	"ms",
    209 	0,
    210 	INFPSZ,
    211 	2048,
    212 	128
    213 };
    214 
    215 static struct qinit msrinit = {
    216 	(int (*)())msrput,
    217 	(int (*)())msrserv,
    218 	msopen,
    219 	msclose,
    220 	(int (*)())NULL,
    221 	&msmiinfo
    222 };
    223 
    224 static struct module_info msmoinfo = {
    225 	0,
    226 	"ms",
    227 	0,
    228 	INFPSZ,
    229 	2048,
    230 	128
    231 };
    232 
    233 static struct qinit mswinit = {
    234 	(int (*)())mswput,
    235 	(int (*)())NULL,
    236 	msopen,
    237 	msclose,
    238 	(int (*)())NULL,
    239 	&msmoinfo
    240 };
    241 
    242 static struct streamtab ms_info = {
    243 	&msrinit,
    244 	&mswinit,
    245 	NULL,
    246 	NULL,
    247 };
    248 
    249 static void	msresched(void *);
    250 static void	msreioctl(void *);
    251 static void	msioctl(queue_t *q, mblk_t *mp);
    252 static int	ms_getparms(register Ms_parms *data);
    253 static int	ms_setparms(register Ms_parms *data);
    254 static void	msflush(struct msdata *msd);
    255 static void	msinput(/* struct msdata *msd, char c */); /* XXX */
    256 static void	msincr(void *);
    257 
    258 /*
    259  * Dummy qbufcall callback routine used by open and close.
    260  * The framework will wake up qwait_sig when we return from
    261  * this routine (as part of leaving the perimeters.)
    262  * (The framework enters the perimeters before calling the qbufcall() callback
    263  * and leaves the perimeters after the callback routine has executed. The
    264  * framework performs an implicit wakeup of any thread in qwait/qwait_sig
    265  * when it leaves the perimeter. See qwait(9E).)
    266  */
    267 /* ARGSUSED */
    268 static void
    269 dummy_callback(void *arg)
    270 {}
    271 
    272 /*
    273  * Open a mouse.
    274  */
    275 /*ARGSUSED*/
    276 static int
    277 msopen(q, devp, oflag, sflag, credp)
    278 	queue_t	*q;
    279 	dev_t	*devp;
    280 	int	oflag, sflag;
    281 	cred_t	*credp;
    282 {
    283 	register struct mousebuf *b;
    284 	register struct ms_softc *ms;
    285 	register struct msdata *msd;
    286 	mblk_t	 *mp;
    287 	mblk_t	 *datap;
    288 	register struct iocblk *iocb;
    289 	register struct termios *cb;
    290 	int error = 0;
    291 
    292 	if (q->q_ptr != NULL)
    293 		return (0);		/* already attached */
    294 
    295 	if (sflag != MODOPEN)
    296 		return (EINVAL);
    297 
    298 	/*
    299 	 * Allocate an msdata structure.
    300 	 */
    301 	msd = kmem_zalloc(sizeof (struct msdata), KM_SLEEP);
    302 
    303 	/*
    304 	 * Set up queue pointers, so that the "put" procedure will accept
    305 	 * the reply to the "ioctl" message we send down.
    306 	 */
    307 	q->q_ptr = msd;
    308 	WR(q)->q_ptr = msd;
    309 
    310 	qprocson(q);
    311 
    312 	/*
    313 	 * Setup tty modes.
    314 	 */
    315 	while ((mp = mkiocb(TCSETSF)) == NULL) {
    316 		bufcall_id_t id = qbufcall(q, sizeof (struct iocblk),
    317 		    BPRI_HI, dummy_callback, NULL);
    318 		if (!qwait_sig(q)) {
    319 			qunbufcall(q, id);
    320 			kmem_free(msd, sizeof (struct msdata));
    321 			qprocsoff(q);
    322 
    323 			return (EINTR);
    324 		}
    325 	}
    326 	while ((datap = allocb(sizeof (struct termios), BPRI_HI)) == NULL) {
    327 		bufcall_id_t id = qbufcall(q, sizeof (struct termios),
    328 		    BPRI_HI, dummy_callback, NULL);
    329 		if (!qwait_sig(q)) {
    330 			qunbufcall(q, id);
    331 			freemsg(mp);
    332 			kmem_free(msd, sizeof (struct msdata));
    333 			qprocsoff(q);
    334 
    335 			return (EINTR);
    336 		}
    337 	}
    338 
    339 
    340 	iocb = (struct iocblk *)mp->b_rptr;
    341 	iocb->ioc_count = sizeof (struct termios);
    342 
    343 	cb = (struct termios *)datap->b_wptr;
    344 	cb->c_iflag = 0;
    345 	cb->c_oflag = 0;
    346 	cb->c_cflag = CREAD|CS8|B9600;
    347 	cb->c_lflag = 0;
    348 	bzero(cb->c_cc, NCCS);
    349 
    350 	datap->b_wptr += sizeof (*cb);
    351 	datap->b_datap->db_type = M_DATA;
    352 	mp->b_cont = datap;
    353 
    354 	msd->msd_flags |= MS_IOCWAIT;	/* indicate that we're waiting for */
    355 	msd->msd_iocid = iocb->ioc_id;	/* this response */
    356 	msd->msd_baud_rate = B9600;
    357 	msd->msd_rcnt_baud_chng = 1;
    358 	msd->msd_data_pkt_cnt = 0;
    359 	msd->msd_qenable_more = 0;
    360 	msd->msd_hold_baud_stup = MS_HOLD_BAUD_STUP;
    361 	putnext(WR(q), mp);
    362 
    363 	ms = &msd->msd_softc;
    364 	/*
    365 	 * Now wait for it.  Let our read queue put routine wake us up
    366 	 * when it arrives.
    367 	 */
    368 	while (msd->msd_flags & MS_IOCWAIT) {
    369 		if (!qwait_sig(q)) {
    370 			error = EINTR;
    371 			goto error;
    372 		}
    373 	}
    374 	if ((error = msd->msd_iocerror) != 0)
    375 		goto error;
    376 
    377 	/*
    378 	 * Set up private data.
    379 	 */
    380 	msd->msd_state = MS_WAIT_BUTN;
    381 	msd->msd_readq = q;
    382 	msd->msd_iocpending = NULL;
    383 
    384 	/*
    385 	 * Allocate buffer and initialize data.
    386 	 */
    387 	if (ms->ms_buf == 0) {
    388 		ms->ms_bufbytes = MS_BUF_BYTES;
    389 		b = kmem_zalloc((uint_t)ms->ms_bufbytes, KM_SLEEP);
    390 		b->mb_size = 1 + (ms->ms_bufbytes - sizeof (struct mousebuf))
    391 			/ sizeof (struct mouseinfo);
    392 		ms->ms_buf = b;
    393 		ms->ms_vuidaddr = VKEY_FIRST;
    394 		msjittertimeout = JITTER_TIMEOUT;
    395 		msflush(msd);
    396 	}
    397 
    398 	msd->msd_flags = MS_OPEN;
    399 
    400 	/*
    401 	 * Tell the module below us that it should return input immediately.
    402 	 */
    403 	(void) putnextctl1(WR(q), M_CTL, MC_SERVICEIMM);
    404 
    405 	return (0);
    406 
    407 error:
    408 	qprocsoff(q);
    409 	kmem_free(msd, sizeof (struct msdata));
    410 
    411 	return (error);
    412 }
    413 
    414 /*
    415  * Close the mouse
    416  */
    417 /* ARGSUSED1 */
    418 static int
    419 msclose(q, flag, credp)
    420 	queue_t		*q;
    421 	int		flag;
    422 	cred_t		*credp;
    423 {
    424 	register struct msdata *msd = (struct msdata *)q->q_ptr;
    425 	register struct ms_softc *ms;
    426 
    427 	/*
    428 	 * Tell the module below us that it need not return input immediately.
    429 	 */
    430 	(void) putnextctl1(q, M_CTL, MC_SERVICEDEF);
    431 
    432 	qprocsoff(q);
    433 	/*
    434 	 * Since we're about to destroy our private data, turn off
    435 	 * our open flag first, so we don't accept any more input
    436 	 * and try to use that data.
    437 	 */
    438 	msd->msd_flags = 0;
    439 
    440 	if (msd->msd_jitter) {
    441 		(void) quntimeout(q, msd->msd_timeout_id);
    442 		msd->msd_jitter = 0;
    443 	}
    444 	if (msd->msd_reioctl_id) {
    445 		qunbufcall(q, msd->msd_reioctl_id);
    446 		msd->msd_reioctl_id = 0;
    447 	}
    448 	if (msd->msd_resched_id) {
    449 		qunbufcall(q, msd->msd_resched_id);
    450 		msd->msd_resched_id = 0;
    451 	}
    452 	if (msd->msd_iocpending != NULL) {
    453 		/*
    454 		 * We were holding an "ioctl" response pending the
    455 		 * availability of an "mblk" to hold data to be passed up;
    456 		 * another "ioctl" came through, which means that "ioctl"
    457 		 * must have timed out or been aborted.
    458 		 */
    459 		freemsg(msd->msd_iocpending);
    460 		msd->msd_iocpending = NULL;
    461 	}
    462 	ms = &msd->msd_softc;
    463 	/* Free mouse buffer */
    464 	if (ms->ms_buf != NULL)
    465 		kmem_free(ms->ms_buf, (uint_t)ms->ms_bufbytes);
    466 	/* Free msdata structure */
    467 	kmem_free((void *)msd, sizeof (*msd));
    468 	return (0);
    469 }
    470 
    471 /*
    472  * Read queue service routine.
    473  * Turn buffered mouse events into stream messages.
    474  */
    475 static void
    476 msrserv(q)
    477 	register queue_t *q;
    478 {
    479 	struct msdata *msd = (struct msdata *)q->q_ptr;
    480 	register struct ms_softc *ms;
    481 	register struct mousebuf *b;
    482 	register struct mouseinfo *mi;
    483 	register int    button_number;
    484 	register int    hwbit;
    485 	mblk_t	 *bp;
    486 
    487 	/*
    488 	 * Handle the case of a queue which is backenabled before
    489 	 * initialization is complete.
    490 	 */
    491 	if (!(msd->msd_flags & MS_OPEN)) {
    492 		return;
    493 	}
    494 
    495 	ms = &msd->msd_softc;
    496 	b = ms->ms_buf;
    497 	if (msd->msd_rcnt_baud_chng && ms->ms_oldoff != b->mb_off) {
    498 		int	no_pkt = b->mb_off - ms->ms_oldoff;
    499 		int	i;
    500 		no_pkt = no_pkt > 0 ? no_pkt : (b->mb_size - no_pkt);
    501 		if (no_pkt < msd->msd_hold_baud_stup) {
    502 			msd->msd_qenable_more = 1;
    503 			return;
    504 		} else {
    505 			/*
    506 			 * throw away packets in beginning (mostly garbage)
    507 			 */
    508 			for (i = 0; i < msd->msd_hold_baud_stup; i++) {
    509 				ms->ms_oldoff++;	/* next event */
    510 				/* circular buffer wraparound */
    511 				if (ms->ms_oldoff >= b->mb_size)
    512 					ms->ms_oldoff = 0;
    513 			}
    514 			msd->msd_rcnt_baud_chng = 0;
    515 			msd->msd_data_pkt_cnt = 0;
    516 			msd->msd_qenable_more = 0;
    517 		}
    518 	}
    519 	while (canputnext(q) && ms->ms_oldoff != b->mb_off) {
    520 		mi = &b->mb_info[ms->ms_oldoff];
    521 		switch (ms->ms_readformat) {
    522 
    523 		case MS_3BYTE_FORMAT: {
    524 			register char *cp;
    525 
    526 			if ((bp = allocb(3, BPRI_HI)) != NULL) {
    527 				cp = (char *)bp->b_wptr;
    528 
    529 				*cp++ = 0x80 | mi->mi_buttons;
    530 				/* Update read buttons */
    531 				ms->ms_prevbuttons = mi->mi_buttons;
    532 
    533 				*cp++ = mi->mi_x;
    534 				*cp++ = -mi->mi_y;
    535 				/* lower pri to avoid mouse droppings */
    536 				bp->b_wptr = (uchar_t *)cp;
    537 				putnext(q, bp);
    538 			} else {
    539 				if (msd->msd_resched_id)
    540 					qunbufcall(q, msd->msd_resched_id);
    541 				msd->msd_resched_id = qbufcall(q, 3, BPRI_HI,
    542 				    msresched, msd);
    543 				if (msd->msd_resched_id == 0)
    544 					return;	/* try again later */
    545 				/* bufcall failed; just pitch this event */
    546 				/* or maybe flush queue? */
    547 			}
    548 			ms->ms_oldoff++;	/* next event */
    549 
    550 			/* circular buffer wraparound */
    551 			if (ms->ms_oldoff >= b->mb_size)
    552 				ms->ms_oldoff = 0;
    553 			break;
    554 		}
    555 
    556 		case MS_VUID_FORMAT: {
    557 			register Firm_event *fep;
    558 
    559 			bp = NULL;
    560 			switch (ms->ms_eventstate) {
    561 
    562 			case EVENT_BUT3:
    563 			case EVENT_BUT2:
    564 			case EVENT_BUT1:
    565 			    /* Test the button. Send an event if it changed. */
    566 			    button_number = ms->ms_eventstate - EVENT_BUT1;
    567 			    hwbit = MS_HW_BUT1 >> button_number;
    568 			    if ((ms->ms_prevbuttons & hwbit) !=
    569 				(mi->mi_buttons & hwbit)) {
    570 			    if ((bp = allocb(sizeof (Firm_event),
    571 						BPRI_HI)) != NULL) {
    572 				    fep = (Firm_event *)bp->b_wptr;
    573 				    fep->id = vuid_id_addr(ms->ms_vuidaddr) |
    574 					vuid_id_offset(BUT(1) + button_number);
    575 				    fep->pair_type = FE_PAIR_NONE;
    576 				    fep->pair = 0;
    577 				    /* Update read buttons and set value */
    578 				    if (mi->mi_buttons & hwbit) {
    579 					fep->value = 0;
    580 					ms->ms_prevbuttons |= hwbit;
    581 				    } else {
    582 					fep->value = 1;
    583 					ms->ms_prevbuttons &= ~hwbit;
    584 				    }
    585 				    fep->time = mi->mi_time;
    586 
    587 				} else {
    588 				    if (msd->msd_resched_id)
    589 					qunbufcall(q, msd->msd_resched_id);
    590 				    msd->msd_resched_id = qbufcall(q,
    591 					sizeof (Firm_event),
    592 					BPRI_HI, msresched, msd);
    593 				    if (msd->msd_resched_id == 0)
    594 					return;	/* try again later */
    595 				    /* bufcall failed; just pitch this event */
    596 				    /* or maybe flush queue? */
    597 				    ms->ms_eventstate = EVENT_X;
    598 				}
    599 			    }
    600 			    break;
    601 
    602 			case EVENT_Y:
    603 			    /* Send y if changed. */
    604 			    if (mi->mi_y != 0) {
    605 
    606 				if ((bp = allocb(sizeof (Firm_event),
    607 						BPRI_HI)) != NULL) {
    608 				    fep = (Firm_event *)bp->b_wptr;
    609 				    fep->id = vuid_id_addr(ms->ms_vuidaddr) |
    610 					    vuid_id_offset(LOC_Y_DELTA);
    611 				    fep->pair_type = FE_PAIR_ABSOLUTE;
    612 				    fep->pair = (uchar_t)LOC_Y_ABSOLUTE;
    613 				    fep->value = -mi->mi_y;
    614 				    fep->time = mi->mi_time;
    615 				} else {
    616 				    if (msd->msd_resched_id)
    617 					qunbufcall(q, msd->msd_resched_id);
    618 				    msd->msd_resched_id = qbufcall(q,
    619 					sizeof (Firm_event),
    620 					BPRI_HI, msresched, msd);
    621 				    if (msd->msd_resched_id == 0)
    622 					return;	/* try again later */
    623 				    /* bufcall failed; just pitch this event */
    624 				    /* or maybe flush queue? */
    625 				    ms->ms_eventstate = EVENT_X;
    626 				}
    627 			    }
    628 			    break;
    629 
    630 			case EVENT_X:
    631 			    /* Send x if changed. */
    632 			    if (mi->mi_x != 0) {
    633 				if ((bp = allocb(sizeof (Firm_event),
    634 						BPRI_HI)) != NULL) {
    635 				    fep = (Firm_event *)bp->b_wptr;
    636 				    fep->id = vuid_id_addr(ms->ms_vuidaddr) |
    637 					    vuid_id_offset(LOC_X_DELTA);
    638 				    fep->pair_type = FE_PAIR_ABSOLUTE;
    639 				    fep->pair = (uchar_t)LOC_X_ABSOLUTE;
    640 				    fep->value = mi->mi_x;
    641 				    fep->time = mi->mi_time;
    642 				} else {
    643 				    if (msd->msd_resched_id)
    644 					qunbufcall(q, msd->msd_resched_id);
    645 				    msd->msd_resched_id = qbufcall(q,
    646 					sizeof (Firm_event),
    647 					BPRI_HI, msresched, msd);
    648 				    if (msd->msd_resched_id == 0)
    649 					return;	/* try again later */
    650 				    /* bufcall failed; just pitch this event */
    651 				    /* or maybe flush queue? */
    652 				    ms->ms_eventstate = EVENT_X;
    653 				}
    654 			    }
    655 			    break;
    656 
    657 			}
    658 			if (bp != NULL) {
    659 			    /* lower pri to avoid mouse droppings */
    660 			    bp->b_wptr += sizeof (Firm_event);
    661 			    putnext(q, bp);
    662 			}
    663 			if (ms->ms_eventstate == EVENT_X) {
    664 			    ms->ms_eventstate = EVENT_BUT3;
    665 			    ms->ms_oldoff++;	/* next event */
    666 
    667 			    /* circular buffer wraparound */
    668 			    if (ms->ms_oldoff >= b->mb_size)
    669 				ms->ms_oldoff = 0;
    670 			} else
    671 			    ms->ms_eventstate--;
    672 		}
    673 		}
    674 	}
    675 }
    676 
    677 static void
    678 msresched(void *msdptr)
    679 {
    680 	queue_t *q;
    681 	struct msdata *msd = msdptr;
    682 
    683 	msd->msd_resched_id = 0;
    684 	if ((q = msd->msd_readq) != 0)
    685 		qenable(q);	/* run the service procedure */
    686 }
    687 
    688 /*
    689  * Line discipline output queue put procedure: handles M_IOCTL
    690  * messages.
    691  */
    692 static void
    693 mswput(q, mp)
    694 	register queue_t *q;
    695 	register mblk_t *mp;
    696 {
    697 
    698 	/*
    699 	 * Process M_FLUSH, and some M_IOCTL, messages here; pass
    700 	 * everything else down.
    701 	 */
    702 	switch (mp->b_datap->db_type) {
    703 
    704 	case M_FLUSH:
    705 		if (*mp->b_rptr & FLUSHW)
    706 			flushq(q, FLUSHDATA);
    707 		if (*mp->b_rptr & FLUSHR)
    708 			flushq(RD(q), FLUSHDATA);
    709 
    710 	default:
    711 		putnext(q, mp);	/* pass it down the line */
    712 		break;
    713 
    714 	case M_IOCTL:
    715 		msioctl(q, mp);
    716 		break;
    717 	}
    718 }
    719 
    720 static void
    721 msreioctl(void *msdptr)
    722 {
    723 	struct msdata *msd = msdptr;
    724 	queue_t *q;
    725 	mblk_t *mp;
    726 
    727 	msd->msd_reioctl_id = 0;
    728 	q = msd->msd_readq;
    729 	if ((mp = msd->msd_iocpending) != NULL) {
    730 		msd->msd_iocpending = NULL;	/* not pending any more */
    731 		msioctl(WR(q), mp);
    732 	}
    733 }
    734 
    735 static void
    736 msioctl(q, mp)
    737 	register queue_t *q;
    738 	register mblk_t *mp;
    739 {
    740 	struct msdata		*msd;
    741 	register struct ms_softc *ms;
    742 	register struct iocblk	*iocp;
    743 	Vuid_addr_probe		*addr_probe;
    744 	uint_t			ioctlrespsize;
    745 	int			err = 0;
    746 	mblk_t			*datap;
    747 
    748 	msd = (struct msdata *)q->q_ptr;
    749 	if (msd == NULL) {
    750 		err = EINVAL;
    751 		goto out;
    752 	}
    753 	ms = &msd->msd_softc;
    754 
    755 	iocp = (struct iocblk *)mp->b_rptr;
    756 
    757 	if (MS_DEBUG)
    758 		printf("mswput(M_IOCTL,%x)\n", iocp->ioc_cmd);
    759 
    760 	switch (iocp->ioc_cmd) {
    761 	case VUIDSFORMAT:
    762 		err = miocpullup(mp, sizeof (int));
    763 		if (err != 0)
    764 			break;
    765 		if (*(int *)mp->b_cont->b_rptr == ms->ms_readformat)
    766 			break;
    767 		ms->ms_readformat = *(int *)mp->b_cont->b_rptr;
    768 		/*
    769 		 * Flush mouse buffer because the messages upstream of us
    770 		 * are in the old format.
    771 		 */
    772 		msflush(msd);
    773 		break;
    774 
    775 	case VUIDGFORMAT:
    776 		if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
    777 			ioctlrespsize = sizeof (int);
    778 			goto allocfailure;
    779 		}
    780 		*(int *)datap->b_wptr = ms->ms_readformat;
    781 		datap->b_wptr += sizeof (int);
    782 		if (mp->b_cont != NULL)
    783 			freemsg(mp->b_cont);
    784 		mp->b_cont = datap;
    785 		iocp->ioc_count = sizeof (int);
    786 		break;
    787 
    788 	case VUIDSADDR:
    789 	case VUIDGADDR:
    790 		err = miocpullup(mp, sizeof (Vuid_addr_probe));
    791 		if (err != 0)
    792 			break;
    793 		addr_probe = (Vuid_addr_probe *)mp->b_cont->b_rptr;
    794 		if (addr_probe->base != VKEY_FIRST) {
    795 			err = ENODEV;
    796 			break;
    797 		}
    798 		if (iocp->ioc_cmd == VUIDSADDR)
    799 			ms->ms_vuidaddr = addr_probe->data.next;
    800 		else
    801 			addr_probe->data.current = ms->ms_vuidaddr;
    802 		break;
    803 
    804 	case MSIOGETPARMS:
    805 		if (MS_DEBUG)
    806 			printf("ms_getparms\n");
    807 
    808 		if ((datap = allocb(sizeof (Ms_parms), BPRI_HI)) == NULL) {
    809 			ioctlrespsize = sizeof (Ms_parms);
    810 			goto allocfailure;
    811 		}
    812 		err = ms_getparms((Ms_parms *)datap->b_wptr);
    813 		datap->b_wptr += sizeof (Ms_parms);
    814 		if (mp->b_cont != NULL)
    815 			freemsg(mp->b_cont);
    816 		mp->b_cont = datap;
    817 		iocp->ioc_count = sizeof (Ms_parms);
    818 		break;
    819 
    820 	case MSIOSETPARMS:
    821 		if (MS_DEBUG)
    822 			printf("ms_setparms\n");
    823 
    824 		err = miocpullup(mp, sizeof (Ms_parms));
    825 		if (err != 0)
    826 			break;
    827 		err = ms_setparms((Ms_parms *)mp->b_cont->b_rptr);
    828 		break;
    829 
    830 	default:
    831 		putnext(q, mp);	/* pass it down the line */
    832 		return;
    833 	}
    834 
    835 out:
    836 	if (err != 0)
    837 		miocnak(q, mp, 0, err);
    838 	else {
    839 		iocp->ioc_rval = 0;
    840 		iocp->ioc_error = 0;	/* brain rot */
    841 		mp->b_datap->db_type = M_IOCACK;
    842 		qreply(q, mp);
    843 	}
    844 	return;
    845 
    846 allocfailure:
    847 	/*
    848 	 * We needed to allocate something to handle this "ioctl", but
    849 	 * couldn't; save this "ioctl" and arrange to get called back when
    850 	 * it's more likely that we can get what we need.
    851 	 * If there's already one being saved, throw it out, since it
    852 	 * must have timed out.
    853 	 */
    854 	if (msd->msd_iocpending != NULL)
    855 		freemsg(msd->msd_iocpending);
    856 	msd->msd_iocpending = mp;
    857 	if (msd->msd_reioctl_id)
    858 		qunbufcall(q, msd->msd_reioctl_id);
    859 	msd->msd_reioctl_id = qbufcall(q, ioctlrespsize, BPRI_HI,
    860 	    msreioctl, msd);
    861 }
    862 
    863 static int
    864 ms_getparms(data)
    865 	register Ms_parms	*data;
    866 {
    867 	data->jitter_thresh = ms_jitter_thresh;
    868 	data->speed_law = ms_speedlaw;
    869 	data->speed_limit = ms_speedlimit;
    870 	return (0);
    871 }
    872 
    873 static int
    874 ms_setparms(data)
    875 	register Ms_parms	*data;
    876 {
    877 	ms_jitter_thresh = data->jitter_thresh;
    878 	ms_speedlaw = data->speed_law;
    879 	ms_speedlimit = data->speed_limit;
    880 	return (0);
    881 }
    882 
    883 static void
    884 msflush(msd)
    885 	register struct msdata *msd;
    886 {
    887 	register struct ms_softc *ms = &msd->msd_softc;
    888 	register queue_t *q;
    889 
    890 	ms->ms_oldoff = 0;
    891 	ms->ms_eventstate = EVENT_BUT3;
    892 	ms->ms_buf->mb_off = 0;
    893 	ms->ms_prevbuttons = MS_HW_BUT1 | MS_HW_BUT2 | MS_HW_BUT3;
    894 	msd->msd_oldbutt = ms->ms_prevbuttons;
    895 	if ((q = msd->msd_readq) != NULL && q->q_next != NULL)
    896 		(void) putnextctl1(q, M_FLUSH, FLUSHR);
    897 }
    898 
    899 
    900 /*
    901  * Mouse read queue put procedure.
    902  */
    903 static void
    904 msrput(q, mp)
    905 	register queue_t *q;
    906 	register mblk_t *mp;
    907 {
    908 	register struct msdata *msd = (struct msdata *)q->q_ptr;
    909 	register mblk_t *bp;
    910 	register char *readp;
    911 	register mblk_t *imp;
    912 	register mblk_t *datap;
    913 	register struct iocblk *iocb;
    914 	register struct termios *cb;
    915 	struct iocblk *iocp;
    916 
    917 	if (msd == 0)
    918 		return;
    919 
    920 	switch (mp->b_datap->db_type) {
    921 
    922 	case M_FLUSH:
    923 		if (*mp->b_rptr & FLUSHW)
    924 			flushq(WR(q), FLUSHDATA);
    925 		if (*mp->b_rptr & FLUSHR)
    926 			flushq(q, FLUSHDATA);
    927 
    928 	default:
    929 		putnext(q, mp);
    930 		return;
    931 
    932 	case M_BREAK:
    933 		if (msd->msd_flags & MS_IOCTOSS) {
    934 			freemsg(mp);
    935 			return;
    936 		}
    937 
    938 		if (msd->msd_rcnt_baud_chng && msd->msd_data_pkt_cnt == 0) {
    939 			freemsg(mp);
    940 			return;
    941 		}
    942 
    943 		/*
    944 		 * If we are sampling a 4800 baud mouse at 9600,
    945 		 * we want to wait for long time because there is no
    946 		 * fixed timeframe for receiving break. If we are sampling
    947 		 * a 1200 baud mouse at 4800 or 9600 baud rate then
    948 		 * it is guaranteed that break will be received very soon.
    949 		 */
    950 		if (msd->msd_rcnt_baud_chng) {
    951 			switch (msd->msd_baud_rate) {
    952 			case B9600:
    953 				msd->msd_hold_baud_stup = MS_HOLD_BAUD_STUP/2;
    954 				msd->msd_baud_rate = B4800;
    955 				break;
    956 
    957 			case B4800:
    958 				if (msd->msd_data_pkt_cnt <= MS_CNT_TOB1200) {
    959 					msd->msd_hold_baud_stup =
    960 						MS_HOLD_BAUD_STUP/6;
    961 					msd->msd_baud_rate = B1200;
    962 				} else {
    963 					msd->msd_hold_baud_stup =
    964 						MS_HOLD_BAUD_STUP;
    965 					msd->msd_baud_rate = B9600;
    966 				}
    967 				break;
    968 
    969 			case B1200:
    970 			default:
    971 				msd->msd_hold_baud_stup = MS_HOLD_BAUD_STUP;
    972 				msd->msd_baud_rate = B9600;
    973 				break;
    974 			}
    975 		} else {
    976 			msd->msd_hold_baud_stup = MS_HOLD_BAUD_STUP;
    977 			msd->msd_baud_rate = B9600;
    978 		}
    979 
    980 		/*
    981 		 * Change baud rate.
    982 		 */
    983 		if ((imp = mkiocb(TCSETSF)) == NULL) {
    984 			return;
    985 		}
    986 		if ((datap = allocb(sizeof (struct termios),
    987 		    BPRI_HI)) == NULL) {
    988 			freemsg(imp);
    989 			return;
    990 		}
    991 
    992 		iocb = (struct iocblk *)imp->b_rptr;
    993 		iocb->ioc_count = sizeof (struct termios);
    994 
    995 		cb = (struct termios *)datap->b_rptr;
    996 		cb->c_iflag = 0;
    997 		cb->c_oflag = 0;
    998 		cb->c_cflag = CREAD|CS8|msd->msd_baud_rate;
    999 		cb->c_lflag = 0;
   1000 		bzero(cb->c_cc, NCCS);
   1001 
   1002 		datap->b_wptr += sizeof (*cb);
   1003 		datap->b_datap->db_type = M_DATA;
   1004 		imp->b_cont = datap;
   1005 
   1006 		msd->msd_flags |= MS_IOCTOSS|MS_IOCWAIT;
   1007 		msd->msd_iocid = iocb->ioc_id;
   1008 		msflush(msd);
   1009 		flushq(q, FLUSHALL);
   1010 		putnext(WR(q), imp);
   1011 		freemsg(mp);
   1012 		msd->msd_rcnt_baud_chng = 1;
   1013 		msd->msd_data_pkt_cnt = 0;
   1014 		if (MS_DEBUG)
   1015 			printf("baud %x\n", msd->msd_baud_rate);
   1016 		return;
   1017 
   1018 	case M_IOCACK:
   1019 	case M_IOCNAK:
   1020 		/*
   1021 		 * If we are doing an "ioctl" ourselves, check if this
   1022 		 * is the reply to that code.  If so, wake up the
   1023 		 * "open" routine, and toss the reply, otherwise just
   1024 		 * pass it up.
   1025 		 */
   1026 		iocp = (struct iocblk *)mp->b_rptr;
   1027 		if (!(msd->msd_flags & MS_IOCWAIT) ||
   1028 		    iocp->ioc_id != msd->msd_iocid) {
   1029 			/*
   1030 			 * This isn't the reply we're looking for.  Move along.
   1031 			 */
   1032 			putnext(q, mp);
   1033 		} else {
   1034 			msd->msd_flags &= ~MS_IOCWAIT;
   1035 			msd->msd_iocerror = iocp->ioc_error;
   1036 			/*
   1037 			 * If we sent down a request to change the baud rate.
   1038 			 * This is the reply.  Just ignore it.
   1039 			 */
   1040 			if (msd->msd_flags & MS_IOCTOSS) {
   1041 				msd->msd_flags &= ~MS_IOCTOSS;
   1042 				msflush(msd);
   1043 				flushq(q, FLUSHALL);
   1044 			}
   1045 			freemsg(mp);
   1046 		}
   1047 		return;
   1048 
   1049 	case M_DATA:
   1050 		if ((msd->msd_flags & MS_IOCTOSS) ||
   1051 		    !(msd->msd_flags & MS_OPEN)) {
   1052 			freemsg(mp);
   1053 			return;
   1054 		}
   1055 		break;
   1056 	}
   1057 
   1058 	/*
   1059 	 * A data message, consisting of bytes from the mouse.
   1060 	 * Hand each byte to our input routine.
   1061 	 */
   1062 	bp = mp;
   1063 
   1064 	do {
   1065 		readp = (char *)bp->b_rptr;
   1066 		while (readp < (char *)bp->b_wptr) {
   1067 			if (msd->msd_rcnt_baud_chng)
   1068 				msd->msd_data_pkt_cnt++;
   1069 			msinput(msd, *readp++);
   1070 		}
   1071 		bp->b_rptr = (unsigned char *)readp;
   1072 	} while ((bp = bp->b_cont) != NULL);	/* next block, if any */
   1073 
   1074 	freemsg(mp);
   1075 }
   1076 
   1077 /*
   1078  * Mouse input routine; process a byte received from a mouse and
   1079  * assemble into a mouseinfo message for the window system.
   1080  *
   1081  * The MSC mice send a five-byte packet organized as
   1082  *	button, dx, dy, dx, dy
   1083  * where dx and dy can be any signed byte value. The mouseinfo message
   1084  * is organized as
   1085  *	dx, dy, button, timestamp
   1086  * Our strategy is to add up the 2 dx and the 2 dy in the five-byte
   1087  * packet, then send the mouseinfo message up.
   1088  *
   1089  * Basic algorithm: throw away bytes until we get a [potential]
   1090  * button byte. Collect button; Collect dx1; Collect dy1; Collect dx2
   1091  * and add it to dx1; Collect dy2 and add it to dy1; Send button,
   1092  * dx, dy, timestamp.
   1093  *
   1094  * Watch out for overflow!
   1095  */
   1096 
   1097 static void
   1098 msinput(msd, c)
   1099 	register struct msdata *msd;
   1100 	char c;
   1101 {
   1102 	register struct ms_softc *ms;
   1103 	register struct mousebuf *b;
   1104 	register struct mouseinfo *mi;
   1105 	register int    jitter_radius;
   1106 	register int    temp;
   1107 
   1108 	ms = &msd->msd_softc;
   1109 	b = ms->ms_buf;
   1110 	if (b == NULL)
   1111 		return;
   1112 
   1113 	mi = &b->mb_info[b->mb_off];
   1114 
   1115 	switch (msd->msd_state) {
   1116 
   1117 	case MS_WAIT_BUTN:
   1118 		if ((c & 0xf8) != 0x80) {
   1119 			if (MS_DEBUG)
   1120 				printf("Mouse input char %x discarded\n",
   1121 					(int)c & 0xff);
   1122 			if (msd->msd_rcnt_baud_chng) {
   1123 				msflush(msd);
   1124 				flushq(msd->msd_readq, FLUSHALL);
   1125 				msd->msd_hold_baud_stup++;
   1126 			}
   1127 			return;
   1128 		}
   1129 
   1130 		/*
   1131 		 * Probably a button byte.
   1132 		 * Lower 3 bits are left, middle, right.
   1133 		 */
   1134 		mi->mi_buttons = c & (MS_HW_BUT1 | MS_HW_BUT2 | MS_HW_BUT3);
   1135 		break;
   1136 
   1137 	case MS_WAIT_X:
   1138 		/*
   1139 		 * Delta X byte.  Add the delta X from this sample to
   1140 		 * the delta X we're accumulating in the current event.
   1141 		 */
   1142 		temp = (int)(mi->mi_x + c);
   1143 		mi->mi_x = BYTECLIP(temp);
   1144 		uniqtime32(&mi->mi_time); /* record time when sample arrived */
   1145 		break;
   1146 
   1147 	case MS_WAIT_Y:
   1148 		/*
   1149 		 * Delta Y byte.  Add the delta Y from this sample to
   1150 		 * the delta Y we're accumulating in the current event.
   1151 		 * (Subtract, actually, because the mouse reports
   1152 		 * increasing Y up the screen.)
   1153 		 */
   1154 		temp = (int)(mi->mi_y - c);
   1155 		mi->mi_y = BYTECLIP(temp);
   1156 		break;
   1157 
   1158 	case MS_WAIT_X2:
   1159 		/*
   1160 		 * Second delta X byte.
   1161 		 */
   1162 		temp = (int)(mi->mi_x + c);
   1163 		mi->mi_x = BYTECLIP(temp);
   1164 		uniqtime32(&mi->mi_time);
   1165 		break;
   1166 
   1167 	case MS_WAIT_Y2:
   1168 		/*
   1169 		 * Second delta Y byte.
   1170 		 */
   1171 		temp = (int)(mi->mi_y - c);
   1172 		mi->mi_y = BYTECLIP(temp);
   1173 		break;
   1174 
   1175 	}
   1176 
   1177 	/*
   1178 	 * Done yet?
   1179 	 */
   1180 	if (msd->msd_state == MS_WAIT_Y2)
   1181 		msd->msd_state = MS_WAIT_BUTN;	/* BONG. Start again. */
   1182 	else {
   1183 		msd->msd_state += 1;
   1184 		return;
   1185 	}
   1186 
   1187 	if (msd->msd_jitter) {
   1188 		(void) quntimeout(msd->msd_readq, msd->msd_timeout_id);
   1189 		msd->msd_jitter = 0;
   1190 	}
   1191 
   1192 	if (mi->mi_buttons == msd->msd_oldbutt) {
   1193 		/*
   1194 		 * Buttons did not change; did position?
   1195 		 */
   1196 		if (mi->mi_x == 0 && mi->mi_y == 0) {
   1197 			/* no, position did not change - boring event */
   1198 			return;
   1199 		}
   1200 
   1201 		/*
   1202 		 * Did the mouse move more than the jitter threshhold?
   1203 		 */
   1204 		jitter_radius = ms_jitter_thresh;
   1205 		if (ABS((int)mi->mi_x) <= jitter_radius &&
   1206 		    ABS((int)mi->mi_y) <= jitter_radius) {
   1207 			/*
   1208 			 * Mouse moved less than the jitter threshhold.
   1209 			 * Don't indicate an event; keep accumulating motions.
   1210 			 * After "msjittertimeout" ticks expire, treat
   1211 			 * the accumulated delta as the real delta.
   1212 			 */
   1213 			msd->msd_jitter = 1;
   1214 			msd->msd_timeout_id = qtimeout(msd->msd_readq,
   1215 			    msincr, msd, msjittertimeout);
   1216 			return;
   1217 		}
   1218 	}
   1219 	msd->msd_oldbutt = mi->mi_buttons;
   1220 	msincr(msd);
   1221 }
   1222 
   1223 /*
   1224  * Increment the mouse sample pointer.
   1225  * Called either immediately after a sample or after a jitter timeout.
   1226  */
   1227 static void
   1228 msincr(void *arg)
   1229 {
   1230 	struct msdata  *msd = arg;
   1231 	register struct ms_softc *ms = &msd->msd_softc;
   1232 	register struct mousebuf *b;
   1233 	register struct mouseinfo *mi;
   1234 	char			oldbutt;
   1235 	register short		xc, yc;
   1236 	register int		wake;
   1237 	register int		speedlimit = ms_speedlimit;
   1238 	register int		xabs, yabs;
   1239 
   1240 	/*
   1241 	 * No longer waiting for jitter timeout
   1242 	 */
   1243 	msd->msd_jitter = 0;
   1244 
   1245 	b = ms->ms_buf;
   1246 	if (b == NULL)
   1247 		return;
   1248 	mi = &b->mb_info[b->mb_off];
   1249 
   1250 	if (ms_speedlaw) {
   1251 		xabs = ABS((int)mi->mi_x);
   1252 		yabs = ABS((int)mi->mi_y);
   1253 		if (xabs > speedlimit || yabs > speedlimit)
   1254 			ms_speed_count++;
   1255 		if (xabs > speedlimit)
   1256 			mi->mi_x = 0;
   1257 		if (yabs > speedlimit)
   1258 			mi->mi_y = 0;
   1259 	}
   1260 
   1261 	oldbutt = mi->mi_buttons;
   1262 
   1263 	xc = yc = 0;
   1264 
   1265 	/* See if we need to wake up anyone waiting for input */
   1266 	wake = b->mb_off == ms->ms_oldoff;
   1267 
   1268 	/* Adjust circular buffer pointer */
   1269 	if (++b->mb_off >= b->mb_size) {
   1270 		b->mb_off = 0;
   1271 		mi = b->mb_info;
   1272 	} else {
   1273 		mi++;
   1274 	}
   1275 
   1276 	/*
   1277 	 * If over-took read index then flush buffer so that mouse state
   1278 	 * is consistent.
   1279 	 */
   1280 	if (b->mb_off == ms->ms_oldoff) {
   1281 		if (ms_overrun_msg)
   1282 			cmn_err(CE_WARN,
   1283 				"Mouse buffer flushed when overrun.\n");
   1284 		msflush(msd);
   1285 		ms_overrun_cnt++;
   1286 		mi = b->mb_info;
   1287 	}
   1288 
   1289 	/* Remember current buttons and fractional part of x & y */
   1290 	mi->mi_buttons = oldbutt;
   1291 	mi->mi_x = (char)xc;
   1292 	mi->mi_y = (char)yc;
   1293 	if (wake || msd->msd_qenable_more)
   1294 		qenable(msd->msd_readq);	/* run the service procedure */
   1295 }
   1296