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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * Console mouse driver for Sun.
29 * The console "zs" port is linked under us, with the "ms" module pushed
30 * on top of it.
31 *
32 * This device merely provides a way to have "/dev/mouse" automatically
33 * have the "ms" module present. Due to problems with the way the "specfs"
34 * file system works, you can't use an indirect device (a "stat" on
35 * "/dev/mouse" won't get the right snode, so you won't get the right time
36 * of last access), and due to problems with the kernel window system code,
37 * you can't use a "cons"-like driver ("/dev/mouse" won't be a streams device,
38 * even though operations on it get turned into operations on the real stream).
39 *
40 * This module supports multiple mice connected to the system at the same time.
41 * All the mice are linked under consms, and act as a mouse with replicated
42 * clicks. Only USB and PS/2 mouse are supported to be virtual mouse now.
43 */
44
45 #include <sys/types.h>
46 #include <sys/param.h>
47 #include <sys/stropts.h>
48 #include <sys/stream.h>
49 #include <sys/strsun.h>
50 #include <sys/conf.h>
51 #include <sys/stat.h>
52 #include <sys/errno.h>
53 #include <sys/modctl.h>
54 #include <sys/consdev.h>
55 #include <sys/ddi.h>
56 #include <sys/sunddi.h>
57 #include <sys/kstat.h>
58 #include <sys/vuid_wheel.h>
59 #include <sys/msio.h>
60 #include <sys/consms.h>
61
62 static void consms_plink(queue_t *, mblk_t *);
63 static int consms_punlink(queue_t *, mblk_t *);
64 static void
65 consms_lqs_ack_complete(consms_lq_t *, mblk_t *);
66 static void consms_add_lq(consms_lq_t *);
67 static void consms_check_caps(void);
68 static mblk_t *consms_new_firm_event(ushort_t, int);
69
70 static void consms_mux_max_wheel_report(mblk_t *);
71 static void consms_mux_cache_states(mblk_t *);
72 static void consms_mux_link_msg(consms_msg_t *);
73 static consms_msg_t *consms_mux_unlink_msg(uint_t);
74 static consms_msg_t *consms_mux_find_msg(uint_t);
75
76 static void consms_mux_iocdata(consms_msg_t *, mblk_t *);
77 static void consms_mux_disp_iocdata(consms_response_t *, mblk_t *);
78 static int consms_mux_disp_ioctl(queue_t *, mblk_t *);
79 static void consms_mux_copyreq(queue_t *, consms_msg_t *, mblk_t *);
80 static void consms_mux_ack(consms_msg_t *, mblk_t *);
81 static void consms_mux_disp_data(mblk_t *);
82
83
84 static int consmsopen();
85 static int consmsclose();
86 static void consmsuwput();
87 static void consmslrput();
88 static void consmslwserv();
89
90 static struct module_info consmsm_info = {
91 0,
92 "consms",
93 0,
94 1024,
95 2048,
96 128
97 };
98
99 static struct qinit consmsurinit = {
100 putq,
101 (int (*)())NULL,
102 consmsopen,
103 consmsclose,
104 (int (*)())NULL,
105 &consmsm_info,
106 NULL
107 };
108
109 static struct qinit consmsuwinit = {
110 (int (*)())consmsuwput,
111 (int (*)())NULL,
112 consmsopen,
113 consmsclose,
114 (int (*)())NULL,
115 &consmsm_info,
116 NULL
117 };
118
119 static struct qinit consmslrinit = {
120 (int (*)())consmslrput,
121 (int (*)())NULL,
122 (int (*)())NULL,
123 (int (*)())NULL,
124 (int (*)())NULL,
125 &consmsm_info,
126 NULL
127 };
128
129 static struct qinit consmslwinit = {
130 putq,
131 (int (*)())consmslwserv,
132 (int (*)())NULL,
133 (int (*)())NULL,
134 (int (*)())NULL,
135 &consmsm_info,
136 NULL
137 };
138
139 static struct streamtab consms_str_info = {
140 &consmsurinit,
141 &consmsuwinit,
142 &consmslrinit,
143 &consmslwinit,
144 };
145
146 static void consmsioctl(queue_t *q, mblk_t *mp);
147 static int consms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
148 void **result);
149 static int consms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
150 static int consms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
151 static int consms_kstat_update(kstat_t *, int);
152
153 /*
154 * Module global data are protected by the per-module inner perimeter.
155 */
156 static queue_t *upperqueue; /* regular mouse queue above us */
157 static dev_info_t *consms_dip; /* private copy of devinfo pointer */
158 static long consms_idle_stamp; /* seconds tstamp of latest mouse op */
159
160 static consms_msg_t *consms_mux_msg; /* ioctl messages being processed */
161 static kmutex_t consms_msg_lock; /* protect ioctl messages list */
162
163 static consms_state_t consms_state; /* the global virtual mouse state */
164 static kmutex_t consmslock;
165
166
167 /*
168 * Normally, kstats of type KSTAT_TYPE_NAMED have multiple elements. In
169 * this case we use this type for a single element because the ioctl code
170 * for it knows how to handle mixed kernel/user data models. Also, it
171 * will be easier to add new statistics later.
172 */
173 static struct {
174 kstat_named_t idle_sec; /* seconds since last user op */
175 } consms_kstat = {
176 { "idle_sec", KSTAT_DATA_LONG, }
177 };
178
179
180 static struct cb_ops cb_consms_ops = {
181 nulldev, /* cb_open */
182 nulldev, /* cb_close */
183 nodev, /* cb_strategy */
184 nodev, /* cb_print */
185 nodev, /* cb_dump */
186 nodev, /* cb_read */
187 nodev, /* cb_write */
188 nodev, /* cb_ioctl */
189 nodev, /* cb_devmap */
190 nodev, /* cb_mmap */
191 nodev, /* cb_segmap */
192 nochpoll, /* cb_chpoll */
193 ddi_prop_op, /* cb_prop_op */
194 &consms_str_info, /* cb_stream */
195 D_MP | D_MTPERMOD /* cb_flag */
196 };
197
198 static struct dev_ops consms_ops = {
199 DEVO_REV, /* devo_rev */
200 0, /* devo_refcnt */
201 consms_info, /* devo_getinfo */
202 nulldev, /* devo_identify */
203 nulldev, /* devo_probe */
204 consms_attach, /* devo_attach */
205 consms_detach, /* devo_detach */
206 nodev, /* devo_reset */
207 &(cb_consms_ops), /* devo_cb_ops */
208 (struct bus_ops *)NULL, /* devo_bus_ops */
209 NULL, /* devo_power */
210 ddi_quiesce_not_needed, /* devo_quiesce */
211 };
212
213
214 /*
215 * Module linkage information for the kernel.
216 */
217
218 static struct modldrv modldrv = {
219 &mod_driverops, /* Type of module. This one is a pseudo driver */
220 "Mouse Driver for Sun 'consms' 5.57",
221 &consms_ops, /* driver ops */
222 };
223
224 static struct modlinkage modlinkage = {
225 MODREV_1,
226 { (void *)&modldrv, NULL }
227 };
228
229 int
230 _init(void)
231 {
232 int error;
233
234 mutex_init(&consmslock, NULL, MUTEX_DRIVER, NULL);
235 mutex_init(&consms_msg_lock, NULL, MUTEX_DRIVER, NULL);
236 error = mod_install(&modlinkage);
237 if (error != 0) {
238 mutex_destroy(&consmslock);
239 mutex_destroy(&consms_msg_lock);
240 }
241 return (error);
242 }
243
244 int
245 _fini(void)
246 {
247 int error;
248
249 error = mod_remove(&modlinkage);
250 if (error != 0)
251 return (error);
252 mutex_destroy(&consmslock);
253 mutex_destroy(&consms_msg_lock);
254 return (0);
255 }
256
257 int
258 _info(struct modinfo *modinfop)
259 {
260 return (mod_info(&modlinkage, modinfop));
261 }
262
263 static int
264 consms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
265 {
266 kstat_t *ksp;
267
268 switch (cmd) {
269 case DDI_ATTACH:
270 break;
271 default:
272 return (DDI_FAILURE);
273 }
274
275 if (ddi_create_minor_node(devi, "mouse", S_IFCHR,
276 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
277 ddi_remove_minor_node(devi, NULL);
278 return (-1);
279 }
280 consms_dip = devi;
281 (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1);
282
283 ksp = kstat_create("consms", 0, "activity", "misc", KSTAT_TYPE_NAMED,
284 sizeof (consms_kstat) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
285 if (ksp) {
286 ksp->ks_data = (void *)&consms_kstat;
287 ksp->ks_update = consms_kstat_update;
288 kstat_install(ksp);
289 consms_idle_stamp = gethrestime_sec(); /* initial value */
290 }
291
292 consms_state.consms_lqs = NULL;
293 consms_state.consms_num_lqs = 0;
294
295 /* default consms state values */
296 consms_state.consms_vuid_format = VUID_FIRM_EVENT;
297 consms_state.consms_num_buttons = 0;
298 consms_state.consms_num_wheels = 0;
299 consms_state.consms_wheel_state_bf |= VUID_WHEEL_STATE_ENABLED;
300 consms_state.consms_ms_parms.jitter_thresh =
301 CONSMS_PARMS_DEFAULT_JITTER;
302 consms_state.consms_ms_parms.speed_limit =
303 CONSMS_PARMS_DEFAULT_SPEED_LIMIT;
304 consms_state.consms_ms_parms.speed_law =
305 CONSMS_PARMS_DEFAULT_SPEED_LAW;
306 consms_state.consms_ms_sr.height = CONSMS_SR_DEFAULT_HEIGHT;
307 consms_state.consms_ms_sr.width = CONSMS_SR_DEFAULT_WIDTH;
308
309 return (DDI_SUCCESS);
310 }
311
312 /*ARGSUSED*/
313 static int
314 consms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
315 {
316 switch (cmd) {
317 case DDI_DETACH:
318 default:
319 return (DDI_FAILURE);
320 }
321 }
322
323 /*ARGSUSED*/
324 static int
325 consms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
326 void **result)
327 {
328 register int error;
329
330 switch (infocmd) {
331 case DDI_INFO_DEVT2DEVINFO:
332 if (consms_dip == NULL) {
333 error = DDI_FAILURE;
334 } else {
335 *result = (void *) consms_dip;
336 error = DDI_SUCCESS;
337 }
338 break;
339 case DDI_INFO_DEVT2INSTANCE:
340 *result = (void *)0;
341 error = DDI_SUCCESS;
342 break;
343 default:
344 error = DDI_FAILURE;
345 }
346 return (error);
347 }
348
349
350 /*ARGSUSED*/
351 static int
352 consmsopen(q, devp, flag, sflag, crp)
353 queue_t *q;
354 dev_t *devp;
355 int flag, sflag;
356 cred_t *crp;
357 {
358 upperqueue = q;
359 qprocson(q);
360 return (0);
361 }
362
363 /*ARGSUSED*/
364 static int
365 consmsclose(q, flag, crp)
366 queue_t *q;
367 int flag;
368 cred_t *crp;
369 {
370 qprocsoff(q);
371 upperqueue = NULL;
372 return (0);
373 }
374
375 /*
376 * Put procedure for upper write queue.
377 */
378 static void
379 consmsuwput(q, mp)
380 register queue_t *q;
381 register mblk_t *mp;
382 {
383 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
384 consms_msg_t *msg;
385 int error = 0;
386
387 switch (mp->b_datap->db_type) {
388
389 case M_IOCTL:
390 consmsioctl(q, mp);
391 break;
392
393 case M_FLUSH:
394 if (*mp->b_rptr & FLUSHW)
395 flushq(q, FLUSHDATA);
396 if (*mp->b_rptr & FLUSHR)
397 flushq(RD(q), FLUSHDATA);
398 if (consms_state.consms_num_lqs > 0) {
399 consms_mux_disp_data(mp);
400 } else {
401 /*
402 * No lower queue; just reflect this back upstream.
403 */
404 *mp->b_rptr &= ~FLUSHW;
405 if (*mp->b_rptr & FLUSHR)
406 qreply(q, mp);
407 else
408 freemsg(mp);
409 }
410 break;
411
412 case M_DATA:
413 if (consms_state.consms_num_lqs > 0) {
414 consms_mux_disp_data(mp);
415 } else {
416 freemsg(mp);
417 }
418 break;
419
420 case M_IOCDATA:
421 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
422 consms_mux_iocdata(msg, mp);
423 } else {
424 error = EINVAL;
425 }
426 break;
427
428 default:
429 error = EINVAL;
430 break;
431 }
432
433 if (error) {
434 /*
435 * Pass an error message up.
436 */
437 mp->b_datap->db_type = M_ERROR;
438 if (mp->b_cont) {
439 freemsg(mp->b_cont);
440 mp->b_cont = NULL;
441 }
442 mp->b_rptr = mp->b_datap->db_base;
443 mp->b_wptr = mp->b_rptr + sizeof (char);
444 *mp->b_rptr = (char)error;
445 qreply(q, mp);
446 }
447 }
448
449 static void
450 consmsioctl(q, mp)
451 register queue_t *q;
452 register mblk_t *mp;
453 {
454 register struct iocblk *iocp;
455 int error;
456 mblk_t *datap;
457
458 iocp = (struct iocblk *)mp->b_rptr;
459
460 switch (iocp->ioc_cmd) {
461
462 case I_LINK:
463 case I_PLINK:
464 mutex_enter(&consmslock);
465 consms_plink(q, mp);
466 mutex_exit(&consmslock);
467 return;
468
469 case I_UNLINK:
470 case I_PUNLINK:
471 mutex_enter(&consmslock);
472 if ((error = consms_punlink(q, mp)) != 0) {
473 mutex_exit(&consmslock);
474 miocnak(q, mp, 0, error);
475 return;
476 }
477 mutex_exit(&consmslock);
478 iocp->ioc_count = 0;
479 break;
480
481 case MSIOBUTTONS: /* query the number of buttons */
482 if ((consms_state.consms_num_lqs <= 0) ||
483 ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)) {
484 miocnak(q, mp, 0, ENOMEM);
485 return;
486 }
487 *(int *)datap->b_wptr = consms_state.consms_num_buttons;
488 datap->b_wptr += sizeof (int);
489 if (mp->b_cont) {
490 freemsg(mp->b_cont);
491 }
492 mp->b_cont = datap;
493 iocp->ioc_count = sizeof (int);
494 break;
495
496 default:
497 /*
498 * Pass this through, if there's something to pass it
499 * through to; otherwise, reject it.
500 */
501 if (consms_state.consms_num_lqs <= 0) {
502 miocnak(q, mp, 0, EINVAL);
503 return;
504 }
505 if ((error = consms_mux_disp_ioctl(q, mp)) != 0)
506 miocnak(q, mp, 0, error);
507
508 return;
509 }
510
511 /*
512 * Common exit path for calls that return a positive
513 * acknowledgment with a return value of 0.
514 */
515 miocack(q, mp, iocp->ioc_count, 0);
516 }
517
518 /*
519 * Service procedure for lower write queue.
520 * Puts things on the queue below us, if it lets us.
521 */
522 static void
523 consmslwserv(q)
524 register queue_t *q;
525 {
526 register mblk_t *mp;
527
528 while (canput(q->q_next) && (mp = getq(q)) != NULL)
529 putnext(q, mp);
530 }
531
532 /*
533 * Put procedure for lower read queue.
534 */
535 static void
536 consmslrput(q, mp)
537 register queue_t *q;
538 register mblk_t *mp;
539 {
540 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
541 struct copyreq *copyreq = (struct copyreq *)mp->b_rptr;
542 consms_msg_t *msg;
543 consms_lq_t *lq = (consms_lq_t *)q->q_ptr;
544
545 ASSERT(lq != NULL);
546
547 switch (mp->b_datap->db_type) {
548 case M_FLUSH:
549 if (*mp->b_rptr & FLUSHW)
550 flushq(WR(q), FLUSHDATA);
551 if (*mp->b_rptr & FLUSHR)
552 flushq(q, FLUSHDATA);
553 if (upperqueue != NULL)
554 putnext(upperqueue, mp); /* pass it through */
555 else {
556 /*
557 * No upper queue; just reflect this back downstream.
558 */
559 *mp->b_rptr &= ~FLUSHR;
560 if (*mp->b_rptr & FLUSHW)
561 qreply(q, mp);
562 else
563 freemsg(mp);
564 }
565 break;
566
567 case M_DATA:
568 if (upperqueue != NULL)
569 putnext(upperqueue, mp);
570 else
571 freemsg(mp);
572 consms_idle_stamp = gethrestime_sec();
573 break;
574
575 case M_IOCACK:
576 case M_IOCNAK:
577 /*
578 * First, check to see if this device
579 * is still being initialized.
580 */
581 if (lq->lq_ioc_reply_func != NULL) {
582 mutex_enter(&consmslock);
583 lq->lq_ioc_reply_func(lq, mp);
584 mutex_exit(&consmslock);
585 freemsg(mp);
586 break;
587 }
588
589 /*
590 * This is normal ioctl ack for upper layer.
591 */
592 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
593 consms_mux_ack(msg, mp);
594 } else {
595 freemsg(mp);
596 }
597 consms_idle_stamp = gethrestime_sec();
598 break;
599
600 case M_COPYIN:
601 case M_COPYOUT:
602 if ((msg = consms_mux_find_msg(copyreq->cq_id)) != NULL) {
603 consms_mux_copyreq(q, msg, mp);
604 } else
605 freemsg(mp);
606 consms_idle_stamp = gethrestime_sec();
607 break;
608
609 case M_ERROR:
610 case M_HANGUP:
611 default:
612 freemsg(mp); /* anything useful here? */
613 break;
614 }
615 }
616
617 /* ARGSUSED */
618 static int
619 consms_kstat_update(kstat_t *ksp, int rw)
620 {
621 if (rw == KSTAT_WRITE)
622 return (EACCES);
623
624 consms_kstat.idle_sec.value.l = gethrestime_sec() - consms_idle_stamp;
625 return (0);
626 }
627
628 /*ARGSUSED*/
629 static int
630 consms_punlink(queue_t *q, mblk_t *mp)
631 {
632 struct linkblk *linkp;
633 consms_lq_t *lq;
634 consms_lq_t *prev_lq;
635
636 ASSERT(MUTEX_HELD(&consmslock));
637
638 linkp = (struct linkblk *)mp->b_cont->b_rptr;
639
640 prev_lq = NULL;
641 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
642 if (lq->lq_queue == linkp->l_qbot) {
643 if (prev_lq)
644 prev_lq->lq_next = lq->lq_next;
645 else
646 consms_state.consms_lqs = lq->lq_next;
647 kmem_free(lq, sizeof (*lq));
648 consms_state.consms_num_lqs--;
649
650 /*
651 * Check to see if mouse capabilities
652 * have changed.
653 */
654 consms_check_caps();
655
656 return (0);
657 }
658 prev_lq = lq;
659 }
660
661 return (EINVAL);
662 }
663
664 /*
665 * Link a specific mouse into our mouse list.
666 */
667 static void
668 consms_plink(queue_t *q, mblk_t *mp)
669 {
670 struct linkblk *linkp;
671 consms_lq_t *lq;
672 queue_t *lowq;
673
674 ASSERT(MUTEX_HELD(&consmslock));
675
676 linkp = (struct linkblk *)mp->b_cont->b_rptr;
677 lowq = linkp->l_qbot;
678
679 lq = kmem_zalloc(sizeof (*lq), KM_SLEEP);
680
681 lowq->q_ptr = (void *)lq;
682 OTHERQ(lowq)->q_ptr = (void *)lq;
683 lq->lq_queue = lowq;
684 lq->lq_pending_plink = mp;
685 lq->lq_pending_queue = q;
686
687 /*
688 * Set the number of buttons to 3 by default
689 * in case the following MSIOBUTTONS ioctl fails.
690 */
691 lq->lq_num_buttons = 3;
692
693 /*
694 * Begin to initialize this mouse.
695 */
696 lq->lq_state = LQS_START;
697 consms_lqs_ack_complete(lq, NULL);
698 }
699
700 /*
701 * Initialize the newly hotplugged-in mouse,
702 * e.g. get the number of buttons, set event
703 * format. Then we add it into our list.
704 */
705 static void
706 consms_lqs_ack_complete(consms_lq_t *lq, mblk_t *mp)
707 {
708 mblk_t *req = NULL;
709 boolean_t skipped = B_FALSE;
710 wheel_state *ws;
711 Ms_screen_resolution *sr;
712 Ms_parms *params;
713
714 ASSERT(MUTEX_HELD(&consmslock));
715
716 /*
717 * We try each ioctl even if the previous one fails
718 * until we reach LQS_DONE, and then add this lq
719 * into our lq list.
720 *
721 * If the message allocation fails, we skip this ioctl,
722 * set skipped flag to B_TRUE in order to skip the ioctl
723 * result, then we try next ioctl, go to next state.
724 */
725 while ((lq->lq_state < LQS_DONE) && (req == NULL)) {
726 switch (lq->lq_state) {
727 case LQS_START:
728 /*
729 * First, issue MSIOBUTTONS ioctl
730 * to get the number of buttons.
731 */
732 req = mkiocb(MSIOBUTTONS);
733 if (req && ((req->b_cont = allocb(sizeof (int),
734 BPRI_MED)) == NULL)) {
735 freemsg(req);
736 req = NULL;
737 }
738 if (req == NULL)
739 skipped = B_TRUE;
740 lq->lq_state++;
741 break;
742
743 case LQS_BUTTON_COUNT_PENDING:
744 if (!skipped && mp && mp->b_cont &&
745 (mp->b_datap->db_type == M_IOCACK))
746 lq->lq_num_buttons =
747 *(int *)mp->b_cont->b_rptr;
748
749 /*
750 * Second, issue VUIDGWHEELCOUNT ioctl
751 * to get the count of wheels.
752 */
753 req = mkiocb(VUIDGWHEELCOUNT);
754 if (req && ((req->b_cont = allocb(sizeof (int),
755 BPRI_MED)) == NULL)) {
756 freemsg(req);
757 req = NULL;
758 }
759 if (req == NULL)
760 skipped = B_TRUE;
761 lq->lq_state++;
762 break;
763
764 case LQS_WHEEL_COUNT_PENDING:
765 if (!skipped && mp && mp->b_cont &&
766 (mp->b_datap->db_type == M_IOCACK))
767 lq->lq_num_wheels =
768 *(int *)mp->b_cont->b_rptr;
769
770 /*
771 * Third, issue VUIDSFORMAT ioctl
772 * to set the event format.
773 */
774 req = mkiocb(VUIDSFORMAT);
775 if (req && ((req->b_cont = allocb(sizeof (int),
776 BPRI_MED)) == NULL)) {
777 freemsg(req);
778 req = NULL;
779 }
780 if (req) {
781 *(int *)req->b_cont->b_wptr =
782 consms_state.consms_vuid_format;
783 req->b_cont->b_wptr += sizeof (int);
784 }
785 lq->lq_state++;
786 break;
787
788 case LQS_SET_VUID_FORMAT_PENDING:
789 /*
790 * Fourth, issue VUIDSWHEELSTATE ioctl
791 * to set the wheel state (enable or disable).
792 */
793 req = mkiocb(VUIDSWHEELSTATE);
794 if (req && ((req->b_cont = allocb(sizeof (wheel_state),
795 BPRI_MED)) == NULL)) {
796 freemsg(req);
797 req = NULL;
798 }
799 if (req) {
800 ws = (wheel_state *)req->b_cont->b_wptr;
801 ws->vers = VUID_WHEEL_STATE_VERS;
802 ws->id = 0; /* the first wheel */
803 ws->stateflags =
804 consms_state.consms_wheel_state_bf & 1;
805 req->b_cont->b_wptr += sizeof (wheel_state);
806 }
807 lq->lq_state++;
808 break;
809
810 case LQS_SET_WHEEL_STATE_PENDING:
811 /*
812 * Fifth, issue MSIOSETPARMS ioctl
813 * to set the parameters for USB mouse.
814 */
815 req = mkiocb(MSIOSETPARMS);
816 if (req && ((req->b_cont = allocb(sizeof (Ms_parms),
817 BPRI_MED)) == NULL)) {
818 freemsg(req);
819 req = NULL;
820 }
821 if (req) {
822 params = (Ms_parms *)req->b_cont->b_wptr;
823 *params = consms_state.consms_ms_parms;
824 req->b_cont->b_wptr += sizeof (Ms_parms);
825 }
826 lq->lq_state++;
827 break;
828
829 case LQS_SET_PARMS_PENDING:
830 /*
831 * Sixth, issue MSIOSRESOLUTION ioctl
832 * to set the screen resolution for absolute mouse.
833 */
834 req = mkiocb(MSIOSRESOLUTION);
835 if (req && ((req->b_cont =
836 allocb(sizeof (Ms_screen_resolution),
837 BPRI_MED)) == NULL)) {
838 freemsg(req);
839 req = NULL;
840 }
841 if (req) {
842 sr =
843 (Ms_screen_resolution *)req->b_cont->b_wptr;
844 *sr = consms_state.consms_ms_sr;
845 req->b_cont->b_wptr +=
846 sizeof (Ms_screen_resolution);
847 }
848 lq->lq_state++;
849 break;
850
851 case LQS_SET_RESOLUTION_PENDING:
852 /*
853 * All jobs are done, lq->lq_state is turned into
854 * LQS_DONE, and this lq is added into our list.
855 */
856 lq->lq_state++;
857 consms_add_lq(lq);
858 break;
859 }
860 }
861
862 if (lq->lq_state < LQS_DONE) {
863 lq->lq_ioc_reply_func = consms_lqs_ack_complete;
864 (void) putq(lq->lq_queue, req);
865 }
866 }
867
868 /*
869 * Add this specific lq into our list, finally reply
870 * the previous pending I_PLINK ioctl. Also check to
871 * see if mouse capabilities have changed, and send
872 * a dynamical notification event to upper layer if
873 * necessary.
874 */
875 static void
876 consms_add_lq(consms_lq_t *lq)
877 {
878 struct iocblk *iocp;
879
880 ASSERT(MUTEX_HELD(&consmslock));
881
882 lq->lq_ioc_reply_func = NULL;
883 iocp = (struct iocblk *)lq->lq_pending_plink->b_rptr;
884 iocp->ioc_error = 0;
885 iocp->ioc_count = 0;
886 iocp->ioc_rval = 0;
887 lq->lq_pending_plink->b_datap->db_type = M_IOCACK;
888
889 /* Reply to the I_PLINK ioctl. */
890 qreply(lq->lq_pending_queue, lq->lq_pending_plink);
891
892 lq->lq_pending_plink = NULL;
893 lq->lq_pending_queue = NULL;
894
895 /*
896 * Add this lq into list.
897 */
898 consms_state.consms_num_lqs++;
899
900 lq->lq_next = consms_state.consms_lqs;
901 consms_state.consms_lqs = lq;
902
903 /*
904 * Check to see if mouse capabilities
905 * have changed.
906 */
907 consms_check_caps();
908
909 }
910
911
912 static void
913 consms_check_caps(void)
914 {
915 consms_lq_t *lq;
916 int max_buttons = 0;
917 int max_wheels = 0;
918 mblk_t *mp;
919
920 /*
921 * Check to see if the number of buttons
922 * and the number of wheels have changed.
923 */
924 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
925 max_buttons = CONSMS_MAX(max_buttons, lq->lq_num_buttons);
926 max_wheels = CONSMS_MAX(max_wheels, lq->lq_num_wheels);
927 }
928
929 if (max_buttons != consms_state.consms_num_buttons) {
930 /*
931 * Since the number of buttons have changed,
932 * send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
933 * notification event to upper layer.
934 */
935 consms_state.consms_num_buttons = max_buttons;
936 if (upperqueue != NULL) {
937 if ((mp = consms_new_firm_event(
938 MOUSE_CAP_CHANGE_NUM_BUT,
939 consms_state.consms_num_buttons)) != NULL) {
940 putnext(upperqueue, mp);
941 }
942 }
943 }
944
945 if (max_wheels != consms_state.consms_num_wheels) {
946 /*
947 * Since the number of wheels have changed,
948 * send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
949 * notification event to upper layer.
950 */
951 consms_state.consms_num_wheels = max_wheels;
952 if (upperqueue != NULL) {
953 if ((mp = consms_new_firm_event(
954 MOUSE_CAP_CHANGE_NUM_WHEEL,
955 consms_state.consms_num_wheels)) != NULL) {
956 putnext(upperqueue, mp);
957 }
958 }
959 }
960 }
961
962 /*
963 * Allocate a dynamical notification event.
964 */
965 static mblk_t *
966 consms_new_firm_event(ushort_t id, int value)
967 {
968 Firm_event *fep;
969 mblk_t *tmp;
970
971 if ((tmp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
972 fep = (Firm_event *)tmp->b_wptr;
973 fep->id = id;
974 fep->pair_type = FE_PAIR_NONE;
975 fep->pair = NULL;
976 fep->value = value;
977 tmp->b_wptr += sizeof (Firm_event);
978 }
979
980 return (tmp);
981 }
982
983 /*
984 * Start of dispatching interfaces as a multiplexor
985 */
986
987 /*
988 * There is a global msg list (consms_mux_msg),
989 * which is used to link all ioctl messages from
990 * upper layer, which are currently being processed.
991 *
992 * consms_mux_link_msg links a msg into the list,
993 * consms_mux_unlink_msg unlinks a msg from the list,
994 * consms_mux_find_msg finds a msg from the list
995 * according to its unique id.
996 *
997 * The id of each msg is taken from stream's mp,
998 * so the id is supposed to be unique.
999 */
1000 static void
1001 consms_mux_link_msg(consms_msg_t *msg)
1002 {
1003 mutex_enter(&consms_msg_lock);
1004 msg->msg_next = consms_mux_msg;
1005 consms_mux_msg = msg;
1006 mutex_exit(&consms_msg_lock);
1007 }
1008
1009 static consms_msg_t *
1010 consms_mux_unlink_msg(uint_t msg_id)
1011 {
1012 consms_msg_t *msg;
1013 consms_msg_t *prev_msg;
1014
1015 mutex_enter(&consms_msg_lock);
1016 prev_msg = NULL;
1017 for (msg = consms_mux_msg; msg != NULL;
1018 prev_msg = msg, msg = msg->msg_next) {
1019 if (msg->msg_id == msg_id)
1020 break;
1021 }
1022
1023 if (msg != NULL) {
1024 if (prev_msg != NULL) {
1025 prev_msg->msg_next = msg->msg_next;
1026 } else {
1027 consms_mux_msg = consms_mux_msg->msg_next;
1028 }
1029 msg->msg_next = NULL;
1030 }
1031 mutex_exit(&consms_msg_lock);
1032
1033 return (msg);
1034 }
1035
1036 static consms_msg_t *
1037 consms_mux_find_msg(uint_t msg_id)
1038 {
1039 consms_msg_t *msg;
1040
1041 mutex_enter(&consms_msg_lock);
1042 for (msg = consms_mux_msg; msg != NULL; msg = msg->msg_next) {
1043 if (msg->msg_id == msg_id)
1044 break;
1045 }
1046 mutex_exit(&consms_msg_lock);
1047
1048 return (msg);
1049 }
1050
1051 /*
1052 * Received ACK or NAK from lower mice
1053 *
1054 * For non-transparent ioctl, the msg->msg_rsp_list
1055 * is always NULL; for transparent ioctl, it
1056 * remembers the M_COPYIN/M_COPYOUT request
1057 * messages from lower mice. So here if msg->msg_rsp_list
1058 * is NULL (after receiving all ACK/NAKs), we
1059 * are done with this specific ioctl.
1060 *
1061 * As long as one of lower mice responds success,
1062 * we treat it success for a ioctl.
1063 */
1064 static void
1065 consms_mux_ack(consms_msg_t *msg, mblk_t *mp)
1066 {
1067 mblk_t *ack_mp;
1068
1069 /* increment response_nums */
1070 msg->msg_num_responses++;
1071
1072 if (mp->b_datap->db_type == M_IOCACK) {
1073 /*
1074 * Received ACK from lower, then
1075 * this is the last step for both
1076 * non-transparent and transparent
1077 * ioctl. We only need to remember
1078 * one of the ACKs, finally reply
1079 * this ACK to upper layer for this
1080 * specific ioctl.
1081 */
1082 ASSERT(msg->msg_rsp_list == NULL);
1083 if (msg->msg_ack_mp == NULL) {
1084 msg->msg_ack_mp = mp;
1085 mp = NULL;
1086 }
1087 }
1088
1089 /*
1090 * Check to see if all lower mice have responded
1091 * to our dispatching ioctl.
1092 */
1093 if (msg->msg_num_responses == msg->msg_num_requests) {
1094 if ((msg->msg_ack_mp == NULL) &&
1095 (msg->msg_rsp_list == NULL)) {
1096 /*
1097 * All are NAKed.
1098 */
1099 ack_mp = mp;
1100 mp = NULL;
1101 } else if (msg->msg_rsp_list == NULL) {
1102 /*
1103 * The last step and at least one ACKed.
1104 */
1105 ack_mp = msg->msg_ack_mp;
1106 consms_mux_cache_states(msg->msg_request);
1107 consms_mux_max_wheel_report(ack_mp);
1108 } else {
1109 /*
1110 * This is a NAK, but we have
1111 * already received M_COPYIN
1112 * or M_COPYOUT request from
1113 * at least one of lower mice.
1114 * (msg->msg_rsp_list != NULL)
1115 *
1116 * Still copyin or copyout.
1117 */
1118 ack_mp = msg->msg_rsp_list->rsp_mp;
1119 consms_mux_max_wheel_report(ack_mp);
1120 }
1121
1122 qreply(msg->msg_queue, ack_mp);
1123
1124 if (msg->msg_rsp_list == NULL) {
1125 /*
1126 * We are done with this ioctl.
1127 */
1128 if (msg->msg_request)
1129 freemsg(msg->msg_request);
1130 (void) consms_mux_unlink_msg(msg->msg_id);
1131 kmem_free(msg, sizeof (*msg));
1132 }
1133 }
1134
1135 if (mp) {
1136 freemsg(mp);
1137 }
1138 }
1139
1140 /*
1141 * Received M_COPYIN or M_COPYOUT request from
1142 * lower mice for transparent ioctl
1143 *
1144 * We remember each M_COPYIN/M_COPYOUT into the
1145 * msg->msg_rsp_list, reply upper layer using the first
1146 * M_COPYIN/M_COPYOUT in the list after receiving
1147 * all responses from lower mice, even if some of
1148 * them return NAKs.
1149 */
1150 static void
1151 consms_mux_copyreq(queue_t *q, consms_msg_t *msg, mblk_t *mp)
1152 {
1153 consms_response_t *rsp;
1154
1155 rsp = (consms_response_t *)kmem_zalloc(sizeof (*rsp), KM_SLEEP);
1156 rsp->rsp_mp = mp;
1157 rsp->rsp_queue = q;
1158 if (msg->msg_rsp_list) {
1159 rsp->rsp_next = msg->msg_rsp_list;
1160 }
1161 msg->msg_rsp_list = rsp;
1162 msg->msg_num_responses++;
1163
1164 if (msg->msg_num_responses == msg->msg_num_requests) {
1165 consms_mux_max_wheel_report(msg->msg_rsp_list->rsp_mp);
1166 qreply(msg->msg_queue, msg->msg_rsp_list->rsp_mp);
1167 }
1168 }
1169
1170 /*
1171 * Do the real job for updating M_COPYIN/M_COPYOUT
1172 * request with the mp of M_IOCDATA, then put it
1173 * down to lower mice.
1174 */
1175 static void
1176 consms_mux_disp_iocdata(consms_response_t *rsp, mblk_t *mp)
1177 {
1178 mblk_t *down_mp = rsp->rsp_mp;
1179 struct copyresp *copyresp = (struct copyresp *)mp->b_rptr;
1180 struct copyresp *newresp = (struct copyresp *)down_mp->b_rptr;
1181
1182 /*
1183 * Update the rval.
1184 */
1185 newresp->cp_rval = copyresp->cp_rval;
1186
1187 /*
1188 * Update the db_type to M_IOCDATA.
1189 */
1190 down_mp->b_datap->db_type = mp->b_datap->db_type;
1191
1192 /*
1193 * Update the b_cont.
1194 */
1195 if (down_mp->b_cont != NULL) {
1196 freemsg(down_mp->b_cont);
1197 down_mp->b_cont = NULL;
1198 }
1199 if (mp->b_cont != NULL) {
1200 down_mp->b_cont = copymsg(mp->b_cont);
1201 }
1202
1203 /*
1204 * Put it down.
1205 */
1206 (void) putq(WR(rsp->rsp_queue), down_mp);
1207 }
1208
1209 /*
1210 * Dispatch M_IOCDATA down to all lower mice
1211 * for transparent ioctl.
1212 *
1213 * We update each M_COPYIN/M_COPYOUT in the
1214 * msg->msg_rsp_list with the M_IOCDATA.
1215 */
1216 static void
1217 consms_mux_iocdata(consms_msg_t *msg, mblk_t *mp)
1218 {
1219 consms_response_t *rsp;
1220 consms_response_t *tmp;
1221 consms_response_t *first;
1222 struct copyresp *copyresp;
1223 int request_nums;
1224
1225 ASSERT(msg->msg_rsp_list != NULL);
1226
1227 /*
1228 * We should remember the ioc data for
1229 * VUIDSWHEELSTATE, and MSIOSRESOLUTION,
1230 * for we will cache the wheel state and
1231 * the screen resolution later if ACKed.
1232 */
1233 copyresp = (struct copyresp *)mp->b_rptr;
1234 if ((copyresp->cp_cmd == VUIDSWHEELSTATE) ||
1235 (copyresp->cp_cmd == MSIOSRESOLUTION)) {
1236 freemsg(msg->msg_request);
1237 msg->msg_request = copymsg(mp);
1238 }
1239
1240 /*
1241 * Update request numbers and response numbers.
1242 */
1243 msg->msg_num_requests = msg->msg_num_responses;
1244 msg->msg_num_responses = 0;
1245 request_nums = 1;
1246
1247 /*
1248 * Since we have use the first M_COPYIN/M_COPYOUT
1249 * in the msg_rsp_list to reply upper layer, the mp
1250 * of M_IOCDATA can be directly used for that.
1251 */
1252 first = msg->msg_rsp_list;
1253 rsp = first->rsp_next;
1254 msg->msg_rsp_list = NULL;
1255
1256 for (rsp = first->rsp_next; rsp != NULL; ) {
1257 tmp = rsp;
1258 rsp = rsp->rsp_next;
1259 consms_mux_disp_iocdata(tmp, mp);
1260 kmem_free(tmp, sizeof (*tmp));
1261 request_nums++;
1262 }
1263
1264 /* Must set the request number before the last q. */
1265 msg->msg_num_requests = request_nums;
1266
1267 /* the first one */
1268 (void) putq(WR(first->rsp_queue), mp);
1269 kmem_free(first, sizeof (*first));
1270 }
1271
1272
1273 /*
1274 * Here we update the number of wheels with
1275 * the virtual mouse for VUIDGWHEELCOUNT ioctl.
1276 */
1277 static void
1278 consms_mux_max_wheel_report(mblk_t *mp)
1279 {
1280 struct iocblk *iocp;
1281 int num_wheels;
1282
1283 if (mp == NULL || mp->b_cont == NULL)
1284 return;
1285
1286 iocp = (struct iocblk *)mp->b_rptr;
1287
1288 if ((iocp->ioc_cmd == VUIDGWHEELCOUNT) &&
1289 (mp->b_datap->db_type == M_COPYOUT)) {
1290 num_wheels = *(int *)mp->b_cont->b_rptr;
1291 if (num_wheels < consms_state.consms_num_wheels) {
1292 *(int *)mp->b_cont->b_rptr =
1293 consms_state.consms_num_wheels;
1294 }
1295 }
1296 }
1297
1298 /*
1299 * Update the virtual mouse state variables with
1300 * the latest value from upper layer when these
1301 * set ioctls return success. Thus we can update
1302 * low mice with the latest state values during
1303 * hotplug.
1304 */
1305 static void
1306 consms_mux_cache_states(mblk_t *mp)
1307 {
1308 struct iocblk *iocp;
1309 Ms_parms *parms;
1310 Ms_screen_resolution *sr;
1311 wheel_state *ws;
1312
1313 if (mp == NULL || mp->b_cont == NULL)
1314 return;
1315
1316 iocp = (struct iocblk *)mp->b_rptr;
1317 switch (iocp->ioc_cmd) {
1318 case VUIDSFORMAT:
1319 consms_state.consms_vuid_format = *(int *)mp->b_cont->b_rptr;
1320 break;
1321
1322 case MSIOSETPARMS:
1323 parms = (Ms_parms *)mp->b_cont->b_rptr;
1324 consms_state.consms_ms_parms = *parms;
1325 break;
1326
1327 case MSIOSRESOLUTION:
1328 sr = (Ms_screen_resolution *)mp->b_cont->b_rptr;
1329 consms_state.consms_ms_sr = *sr;
1330 break;
1331
1332 case VUIDSWHEELSTATE:
1333 ws = (wheel_state *)mp->b_cont->b_rptr;
1334 consms_state.consms_wheel_state_bf =
1335 (ws->stateflags << ws->id) |
1336 (consms_state.consms_wheel_state_bf & ~(1 << ws->id));
1337 break;
1338 }
1339 }
1340
1341 /*
1342 * Dispatch ioctl mp (non-transparent and transparent)
1343 * down to all lower mice.
1344 *
1345 * First, create a pending message for this mp, link it into
1346 * the global messages list. Then wait for ACK/NAK for
1347 * non-transparent ioctl, COPYIN/COPYOUT for transparent
1348 * ioctl.
1349 */
1350 static int
1351 consms_mux_disp_ioctl(queue_t *q, mblk_t *mp)
1352 {
1353 struct iocblk *iocp;
1354 consms_msg_t *msg;
1355 consms_lq_t *lq;
1356 mblk_t *copy_mp;
1357 int error = 0;
1358
1359 iocp = (struct iocblk *)mp->b_rptr;
1360 msg = (consms_msg_t *)kmem_zalloc(sizeof (*msg), KM_SLEEP);
1361 msg->msg_id = iocp->ioc_id;
1362 msg->msg_request = mp;
1363 msg->msg_queue = q;
1364 msg->msg_num_requests = consms_state.consms_num_lqs;
1365 consms_mux_link_msg(msg);
1366
1367 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1368 if ((copy_mp = copymsg(mp)) != NULL) {
1369 (void) putq(lq->lq_queue, copy_mp);
1370 } else {
1371 /*
1372 * If copymsg fails, we ignore this lq and
1373 * try next one. As long as one of them succeeds,
1374 * we dispatch this ioctl down. And later as long
1375 * as one of the lower drivers return success, we
1376 * reply to this ioctl with success.
1377 */
1378 msg->msg_num_requests--;
1379 }
1380 }
1381
1382 if (msg->msg_num_requests <= 0) {
1383 /*
1384 * Since copymsg fails for all lqs, we NAK this ioctl.
1385 */
1386 (void) consms_mux_unlink_msg(msg->msg_id);
1387 kmem_free(msg, sizeof (*msg));
1388 error = ENOMEM;
1389 }
1390
1391 return (error);
1392 }
1393
1394 /*
1395 * Dispatch M_DATA and M_FLUSH message down to all
1396 * lower mice, and there are no acknowledgements
1397 * for them. Here we just copy the mp and then
1398 * put it into the lower queues.
1399 */
1400 static void
1401 consms_mux_disp_data(mblk_t *mp)
1402 {
1403 consms_lq_t *lq;
1404 mblk_t *copy_mp;
1405
1406 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1407 if ((copy_mp = copymsg(mp)) != NULL) {
1408 (void) putq(lq->lq_queue, copy_mp);
1409 }
1410 }
1411
1412 freemsg(mp);
1413 }