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