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