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