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