xref: /titanic_44/usr/src/uts/sun/io/ms.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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
_init(void)86 _init(void)
87 {
88 	return (mod_install(&modlinkage));
89 }
90 
91 int
_fini(void)92 _fini(void)
93 {
94 	return (EBUSY);
95 }
96 
97 int
_info(struct modinfo * modinfop)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
dummy_callback(void * arg)269 dummy_callback(void *arg)
270 {}
271 
272 /*
273  * Open a mouse.
274  */
275 /*ARGSUSED*/
276 static int
msopen(q,devp,oflag,sflag,credp)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
msclose(q,flag,credp)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
msrserv(q)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
msresched(void * msdptr)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
mswput(q,mp)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
msreioctl(void * msdptr)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
msioctl(q,mp)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
ms_getparms(data)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
ms_setparms(data)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
msflush(msd)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
msrput(q,mp)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
msinput(msd,c)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
msincr(void * arg)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