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