6324 Add an `ndp' tool for manipulating the neighbors table
[illumos-gate.git] / usr / src / uts / common / io / consms.c
blob3e573e0466da0215a7b13b875869c10f6394295a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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.
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).
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.
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>
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);
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);
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 *);
84 static int consmsopen();
85 static int consmsclose();
86 static void consmsuwput();
87 static void consmslrput();
88 static void consmslwserv();
90 static struct module_info consmsm_info = {
92 "consms",
94 1024,
95 2048,
96 128
99 static struct qinit consmsurinit = {
100 putq,
101 (int (*)())NULL,
102 consmsopen,
103 consmsclose,
104 (int (*)())NULL,
105 &consmsm_info,
106 NULL
109 static struct qinit consmsuwinit = {
110 (int (*)())consmsuwput,
111 (int (*)())NULL,
112 consmsopen,
113 consmsclose,
114 (int (*)())NULL,
115 &consmsm_info,
116 NULL
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
129 static struct qinit consmslwinit = {
130 putq,
131 (int (*)())consmslwserv,
132 (int (*)())NULL,
133 (int (*)())NULL,
134 (int (*)())NULL,
135 &consmsm_info,
136 NULL
139 static struct streamtab consms_str_info = {
140 &consmsurinit,
141 &consmsuwinit,
142 &consmslrinit,
143 &consmslwinit,
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);
154 * Module global data are protected by the per-module inner perimeter.
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 */
160 static consms_msg_t *consms_mux_msg; /* ioctl messages being processed */
161 static kmutex_t consms_msg_lock; /* protect ioctl messages list */
163 static consms_state_t consms_state; /* the global virtual mouse state */
164 static kmutex_t consmslock;
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.
173 static struct {
174 kstat_named_t idle_sec; /* seconds since last user op */
175 } consms_kstat = {
176 { "idle_sec", KSTAT_DATA_LONG, }
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 */
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 */
215 * Module linkage information for the kernel.
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 */
224 static struct modlinkage modlinkage = {
225 MODREV_1,
226 (void *)&modldrv,
227 NULL
231 _init(void)
233 int error;
235 mutex_init(&consmslock, NULL, MUTEX_DRIVER, NULL);
236 mutex_init(&consms_msg_lock, NULL, MUTEX_DRIVER, NULL);
237 error = mod_install(&modlinkage);
238 if (error != 0) {
239 mutex_destroy(&consmslock);
240 mutex_destroy(&consms_msg_lock);
242 return (error);
246 _fini(void)
248 int error;
250 error = mod_remove(&modlinkage);
251 if (error != 0)
252 return (error);
253 mutex_destroy(&consmslock);
254 mutex_destroy(&consms_msg_lock);
255 return (0);
259 _info(struct modinfo *modinfop)
261 return (mod_info(&modlinkage, modinfop));
264 static int
265 consms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
267 kstat_t *ksp;
269 switch (cmd) {
270 case DDI_ATTACH:
271 break;
272 default:
273 return (DDI_FAILURE);
276 if (ddi_create_minor_node(devi, "mouse", S_IFCHR,
277 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
278 ddi_remove_minor_node(devi, NULL);
279 return (-1);
281 consms_dip = devi;
282 (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1);
284 ksp = kstat_create("consms", 0, "activity", "misc", KSTAT_TYPE_NAMED,
285 sizeof (consms_kstat) / sizeof (kstat_named_t), KSTAT_FLAG_VIRTUAL);
286 if (ksp) {
287 ksp->ks_data = (void *)&consms_kstat;
288 ksp->ks_update = consms_kstat_update;
289 kstat_install(ksp);
290 consms_idle_stamp = gethrestime_sec(); /* initial value */
293 consms_state.consms_lqs = NULL;
294 consms_state.consms_num_lqs = 0;
296 /* default consms state values */
297 consms_state.consms_vuid_format = VUID_FIRM_EVENT;
298 consms_state.consms_num_buttons = 0;
299 consms_state.consms_num_wheels = 0;
300 consms_state.consms_wheel_state_bf |= VUID_WHEEL_STATE_ENABLED;
301 consms_state.consms_ms_parms.jitter_thresh =
302 CONSMS_PARMS_DEFAULT_JITTER;
303 consms_state.consms_ms_parms.speed_limit =
304 CONSMS_PARMS_DEFAULT_SPEED_LIMIT;
305 consms_state.consms_ms_parms.speed_law =
306 CONSMS_PARMS_DEFAULT_SPEED_LAW;
307 consms_state.consms_ms_sr.height = CONSMS_SR_DEFAULT_HEIGHT;
308 consms_state.consms_ms_sr.width = CONSMS_SR_DEFAULT_WIDTH;
310 return (DDI_SUCCESS);
313 /*ARGSUSED*/
314 static int
315 consms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
317 switch (cmd) {
318 case DDI_DETACH:
319 default:
320 return (DDI_FAILURE);
324 /*ARGSUSED*/
325 static int
326 consms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
327 void **result)
329 register int error;
331 switch (infocmd) {
332 case DDI_INFO_DEVT2DEVINFO:
333 if (consms_dip == NULL) {
334 error = DDI_FAILURE;
335 } else {
336 *result = (void *) consms_dip;
337 error = DDI_SUCCESS;
339 break;
340 case DDI_INFO_DEVT2INSTANCE:
341 *result = (void *)0;
342 error = DDI_SUCCESS;
343 break;
344 default:
345 error = DDI_FAILURE;
347 return (error);
351 /*ARGSUSED*/
352 static int
353 consmsopen(q, devp, flag, sflag, crp)
354 queue_t *q;
355 dev_t *devp;
356 int flag, sflag;
357 cred_t *crp;
359 upperqueue = q;
360 qprocson(q);
361 return (0);
364 /*ARGSUSED*/
365 static int
366 consmsclose(q, flag, crp)
367 queue_t *q;
368 int flag;
369 cred_t *crp;
371 qprocsoff(q);
372 upperqueue = NULL;
373 return (0);
377 * Put procedure for upper write queue.
379 static void
380 consmsuwput(q, mp)
381 register queue_t *q;
382 register mblk_t *mp;
384 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
385 consms_msg_t *msg;
386 int error = 0;
388 switch (mp->b_datap->db_type) {
390 case M_IOCTL:
391 consmsioctl(q, mp);
392 break;
394 case M_FLUSH:
395 if (*mp->b_rptr & FLUSHW)
396 flushq(q, FLUSHDATA);
397 if (*mp->b_rptr & FLUSHR)
398 flushq(RD(q), FLUSHDATA);
399 if (consms_state.consms_num_lqs > 0) {
400 consms_mux_disp_data(mp);
401 } else {
403 * No lower queue; just reflect this back upstream.
405 *mp->b_rptr &= ~FLUSHW;
406 if (*mp->b_rptr & FLUSHR)
407 qreply(q, mp);
408 else
409 freemsg(mp);
411 break;
413 case M_DATA:
414 if (consms_state.consms_num_lqs > 0) {
415 consms_mux_disp_data(mp);
416 } else {
417 freemsg(mp);
419 break;
421 case M_IOCDATA:
422 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
423 consms_mux_iocdata(msg, mp);
424 } else {
425 error = EINVAL;
427 break;
429 default:
430 error = EINVAL;
431 break;
434 if (error) {
436 * Pass an error message up.
438 mp->b_datap->db_type = M_ERROR;
439 if (mp->b_cont) {
440 freemsg(mp->b_cont);
441 mp->b_cont = NULL;
443 mp->b_rptr = mp->b_datap->db_base;
444 mp->b_wptr = mp->b_rptr + sizeof (char);
445 *mp->b_rptr = (char)error;
446 qreply(q, mp);
450 static void
451 consmsioctl(q, mp)
452 register queue_t *q;
453 register mblk_t *mp;
455 register struct iocblk *iocp;
456 int error;
457 mblk_t *datap;
459 iocp = (struct iocblk *)mp->b_rptr;
461 switch (iocp->ioc_cmd) {
463 case I_LINK:
464 case I_PLINK:
465 mutex_enter(&consmslock);
466 consms_plink(q, mp);
467 mutex_exit(&consmslock);
468 return;
470 case I_UNLINK:
471 case I_PUNLINK:
472 mutex_enter(&consmslock);
473 if ((error = consms_punlink(q, mp)) != 0) {
474 mutex_exit(&consmslock);
475 miocnak(q, mp, 0, error);
476 return;
478 mutex_exit(&consmslock);
479 iocp->ioc_count = 0;
480 break;
482 case MSIOBUTTONS: /* query the number of buttons */
483 if ((consms_state.consms_num_lqs <= 0) ||
484 ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)) {
485 miocnak(q, mp, 0, ENOMEM);
486 return;
488 *(int *)datap->b_wptr = consms_state.consms_num_buttons;
489 datap->b_wptr += sizeof (int);
490 if (mp->b_cont) {
491 freemsg(mp->b_cont);
493 mp->b_cont = datap;
494 iocp->ioc_count = sizeof (int);
495 break;
497 default:
499 * Pass this through, if there's something to pass it
500 * through to; otherwise, reject it.
502 if (consms_state.consms_num_lqs <= 0) {
503 miocnak(q, mp, 0, EINVAL);
504 return;
506 if ((error = consms_mux_disp_ioctl(q, mp)) != 0)
507 miocnak(q, mp, 0, error);
509 return;
513 * Common exit path for calls that return a positive
514 * acknowledgment with a return value of 0.
516 miocack(q, mp, iocp->ioc_count, 0);
520 * Service procedure for lower write queue.
521 * Puts things on the queue below us, if it lets us.
523 static void
524 consmslwserv(q)
525 register queue_t *q;
527 register mblk_t *mp;
529 while (canput(q->q_next) && (mp = getq(q)) != NULL)
530 putnext(q, mp);
534 * Put procedure for lower read queue.
536 static void
537 consmslrput(q, mp)
538 register queue_t *q;
539 register mblk_t *mp;
541 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
542 struct copyreq *copyreq = (struct copyreq *)mp->b_rptr;
543 consms_msg_t *msg;
544 consms_lq_t *lq = (consms_lq_t *)q->q_ptr;
546 ASSERT(lq != NULL);
548 switch (mp->b_datap->db_type) {
549 case M_FLUSH:
550 if (*mp->b_rptr & FLUSHW)
551 flushq(WR(q), FLUSHDATA);
552 if (*mp->b_rptr & FLUSHR)
553 flushq(q, FLUSHDATA);
554 if (upperqueue != NULL)
555 putnext(upperqueue, mp); /* pass it through */
556 else {
558 * No upper queue; just reflect this back downstream.
560 *mp->b_rptr &= ~FLUSHR;
561 if (*mp->b_rptr & FLUSHW)
562 qreply(q, mp);
563 else
564 freemsg(mp);
566 break;
568 case M_DATA:
569 if (upperqueue != NULL)
570 putnext(upperqueue, mp);
571 else
572 freemsg(mp);
573 consms_idle_stamp = gethrestime_sec();
574 break;
576 case M_IOCACK:
577 case M_IOCNAK:
579 * First, check to see if this device
580 * is still being initialized.
582 if (lq->lq_ioc_reply_func != NULL) {
583 mutex_enter(&consmslock);
584 lq->lq_ioc_reply_func(lq, mp);
585 mutex_exit(&consmslock);
586 freemsg(mp);
587 break;
591 * This is normal ioctl ack for upper layer.
593 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
594 consms_mux_ack(msg, mp);
595 } else {
596 freemsg(mp);
598 consms_idle_stamp = gethrestime_sec();
599 break;
601 case M_COPYIN:
602 case M_COPYOUT:
603 if ((msg = consms_mux_find_msg(copyreq->cq_id)) != NULL) {
604 consms_mux_copyreq(q, msg, mp);
605 } else
606 freemsg(mp);
607 consms_idle_stamp = gethrestime_sec();
608 break;
610 case M_ERROR:
611 case M_HANGUP:
612 default:
613 freemsg(mp); /* anything useful here? */
614 break;
618 /* ARGSUSED */
619 static int
620 consms_kstat_update(kstat_t *ksp, int rw)
622 if (rw == KSTAT_WRITE)
623 return (EACCES);
625 consms_kstat.idle_sec.value.l = gethrestime_sec() - consms_idle_stamp;
626 return (0);
629 /*ARGSUSED*/
630 static int
631 consms_punlink(queue_t *q, mblk_t *mp)
633 struct linkblk *linkp;
634 consms_lq_t *lq;
635 consms_lq_t *prev_lq;
637 ASSERT(MUTEX_HELD(&consmslock));
639 linkp = (struct linkblk *)mp->b_cont->b_rptr;
641 prev_lq = NULL;
642 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
643 if (lq->lq_queue == linkp->l_qbot) {
644 if (prev_lq)
645 prev_lq->lq_next = lq->lq_next;
646 else
647 consms_state.consms_lqs = lq->lq_next;
648 kmem_free(lq, sizeof (*lq));
649 consms_state.consms_num_lqs--;
652 * Check to see if mouse capabilities
653 * have changed.
655 consms_check_caps();
657 return (0);
659 prev_lq = lq;
662 return (EINVAL);
666 * Link a specific mouse into our mouse list.
668 static void
669 consms_plink(queue_t *q, mblk_t *mp)
671 struct linkblk *linkp;
672 consms_lq_t *lq;
673 queue_t *lowq;
675 ASSERT(MUTEX_HELD(&consmslock));
677 linkp = (struct linkblk *)mp->b_cont->b_rptr;
678 lowq = linkp->l_qbot;
680 lq = kmem_zalloc(sizeof (*lq), KM_SLEEP);
682 lowq->q_ptr = (void *)lq;
683 OTHERQ(lowq)->q_ptr = (void *)lq;
684 lq->lq_queue = lowq;
685 lq->lq_pending_plink = mp;
686 lq->lq_pending_queue = q;
689 * Set the number of buttons to 3 by default
690 * in case the following MSIOBUTTONS ioctl fails.
692 lq->lq_num_buttons = 3;
695 * Begin to initialize this mouse.
697 lq->lq_state = LQS_START;
698 consms_lqs_ack_complete(lq, NULL);
702 * Initialize the newly hotplugged-in mouse,
703 * e.g. get the number of buttons, set event
704 * format. Then we add it into our list.
706 static void
707 consms_lqs_ack_complete(consms_lq_t *lq, mblk_t *mp)
709 mblk_t *req = NULL;
710 boolean_t skipped = B_FALSE;
711 wheel_state *ws;
712 Ms_screen_resolution *sr;
713 Ms_parms *params;
715 ASSERT(MUTEX_HELD(&consmslock));
718 * We try each ioctl even if the previous one fails
719 * until we reach LQS_DONE, and then add this lq
720 * into our lq list.
722 * If the message allocation fails, we skip this ioctl,
723 * set skipped flag to B_TRUE in order to skip the ioctl
724 * result, then we try next ioctl, go to next state.
726 while ((lq->lq_state < LQS_DONE) && (req == NULL)) {
727 switch (lq->lq_state) {
728 case LQS_START:
730 * First, issue MSIOBUTTONS ioctl
731 * to get the number of buttons.
733 req = mkiocb(MSIOBUTTONS);
734 if (req && ((req->b_cont = allocb(sizeof (int),
735 BPRI_MED)) == NULL)) {
736 freemsg(req);
737 req = NULL;
739 if (req == NULL)
740 skipped = B_TRUE;
741 lq->lq_state++;
742 break;
744 case LQS_BUTTON_COUNT_PENDING:
745 if (!skipped && mp && mp->b_cont &&
746 (mp->b_datap->db_type == M_IOCACK))
747 lq->lq_num_buttons =
748 *(int *)mp->b_cont->b_rptr;
751 * Second, issue VUIDGWHEELCOUNT ioctl
752 * to get the count of wheels.
754 req = mkiocb(VUIDGWHEELCOUNT);
755 if (req && ((req->b_cont = allocb(sizeof (int),
756 BPRI_MED)) == NULL)) {
757 freemsg(req);
758 req = NULL;
760 if (req == NULL)
761 skipped = B_TRUE;
762 lq->lq_state++;
763 break;
765 case LQS_WHEEL_COUNT_PENDING:
766 if (!skipped && mp && mp->b_cont &&
767 (mp->b_datap->db_type == M_IOCACK))
768 lq->lq_num_wheels =
769 *(int *)mp->b_cont->b_rptr;
772 * Third, issue VUIDSFORMAT ioctl
773 * to set the event format.
775 req = mkiocb(VUIDSFORMAT);
776 if (req && ((req->b_cont = allocb(sizeof (int),
777 BPRI_MED)) == NULL)) {
778 freemsg(req);
779 req = NULL;
781 if (req) {
782 *(int *)req->b_cont->b_wptr =
783 consms_state.consms_vuid_format;
784 req->b_cont->b_wptr += sizeof (int);
786 lq->lq_state++;
787 break;
789 case LQS_SET_VUID_FORMAT_PENDING:
791 * Fourth, issue VUIDSWHEELSTATE ioctl
792 * to set the wheel state (enable or disable).
794 req = mkiocb(VUIDSWHEELSTATE);
795 if (req && ((req->b_cont = allocb(sizeof (wheel_state),
796 BPRI_MED)) == NULL)) {
797 freemsg(req);
798 req = NULL;
800 if (req) {
801 ws = (wheel_state *)req->b_cont->b_wptr;
802 ws->vers = VUID_WHEEL_STATE_VERS;
803 ws->id = 0; /* the first wheel */
804 ws->stateflags =
805 consms_state.consms_wheel_state_bf & 1;
806 req->b_cont->b_wptr += sizeof (wheel_state);
808 lq->lq_state++;
809 break;
811 case LQS_SET_WHEEL_STATE_PENDING:
813 * Fifth, issue MSIOSETPARMS ioctl
814 * to set the parameters for USB mouse.
816 req = mkiocb(MSIOSETPARMS);
817 if (req && ((req->b_cont = allocb(sizeof (Ms_parms),
818 BPRI_MED)) == NULL)) {
819 freemsg(req);
820 req = NULL;
822 if (req) {
823 params = (Ms_parms *)req->b_cont->b_wptr;
824 *params = consms_state.consms_ms_parms;
825 req->b_cont->b_wptr += sizeof (Ms_parms);
827 lq->lq_state++;
828 break;
830 case LQS_SET_PARMS_PENDING:
832 * Sixth, issue MSIOSRESOLUTION ioctl
833 * to set the screen resolution for absolute mouse.
835 req = mkiocb(MSIOSRESOLUTION);
836 if (req && ((req->b_cont =
837 allocb(sizeof (Ms_screen_resolution),
838 BPRI_MED)) == NULL)) {
839 freemsg(req);
840 req = NULL;
842 if (req) {
843 sr =
844 (Ms_screen_resolution *)req->b_cont->b_wptr;
845 *sr = consms_state.consms_ms_sr;
846 req->b_cont->b_wptr +=
847 sizeof (Ms_screen_resolution);
849 lq->lq_state++;
850 break;
852 case LQS_SET_RESOLUTION_PENDING:
854 * All jobs are done, lq->lq_state is turned into
855 * LQS_DONE, and this lq is added into our list.
857 lq->lq_state++;
858 consms_add_lq(lq);
859 break;
863 if (lq->lq_state < LQS_DONE) {
864 lq->lq_ioc_reply_func = consms_lqs_ack_complete;
865 (void) putq(lq->lq_queue, req);
870 * Add this specific lq into our list, finally reply
871 * the previous pending I_PLINK ioctl. Also check to
872 * see if mouse capabilities have changed, and send
873 * a dynamical notification event to upper layer if
874 * necessary.
876 static void
877 consms_add_lq(consms_lq_t *lq)
879 struct iocblk *iocp;
881 ASSERT(MUTEX_HELD(&consmslock));
883 lq->lq_ioc_reply_func = NULL;
884 iocp = (struct iocblk *)lq->lq_pending_plink->b_rptr;
885 iocp->ioc_error = 0;
886 iocp->ioc_count = 0;
887 iocp->ioc_rval = 0;
888 lq->lq_pending_plink->b_datap->db_type = M_IOCACK;
890 /* Reply to the I_PLINK ioctl. */
891 qreply(lq->lq_pending_queue, lq->lq_pending_plink);
893 lq->lq_pending_plink = NULL;
894 lq->lq_pending_queue = NULL;
897 * Add this lq into list.
899 consms_state.consms_num_lqs++;
901 lq->lq_next = consms_state.consms_lqs;
902 consms_state.consms_lqs = lq;
905 * Check to see if mouse capabilities
906 * have changed.
908 consms_check_caps();
913 static void
914 consms_check_caps(void)
916 consms_lq_t *lq;
917 int max_buttons = 0;
918 int max_wheels = 0;
919 mblk_t *mp;
922 * Check to see if the number of buttons
923 * and the number of wheels have changed.
925 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
926 max_buttons = CONSMS_MAX(max_buttons, lq->lq_num_buttons);
927 max_wheels = CONSMS_MAX(max_wheels, lq->lq_num_wheels);
930 if (max_buttons != consms_state.consms_num_buttons) {
932 * Since the number of buttons have changed,
933 * send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
934 * notification event to upper layer.
936 consms_state.consms_num_buttons = max_buttons;
937 if (upperqueue != NULL) {
938 if ((mp = consms_new_firm_event(
939 MOUSE_CAP_CHANGE_NUM_BUT,
940 consms_state.consms_num_buttons)) != NULL) {
941 putnext(upperqueue, mp);
946 if (max_wheels != consms_state.consms_num_wheels) {
948 * Since the number of wheels have changed,
949 * send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
950 * notification event to upper layer.
952 consms_state.consms_num_wheels = max_wheels;
953 if (upperqueue != NULL) {
954 if ((mp = consms_new_firm_event(
955 MOUSE_CAP_CHANGE_NUM_WHEEL,
956 consms_state.consms_num_wheels)) != NULL) {
957 putnext(upperqueue, mp);
964 * Allocate a dynamical notification event.
966 static mblk_t *
967 consms_new_firm_event(ushort_t id, int value)
969 Firm_event *fep;
970 mblk_t *tmp;
972 if ((tmp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
973 fep = (Firm_event *)tmp->b_wptr;
974 fep->id = id;
975 fep->pair_type = FE_PAIR_NONE;
976 fep->pair = NULL;
977 fep->value = value;
978 tmp->b_wptr += sizeof (Firm_event);
981 return (tmp);
985 * Start of dispatching interfaces as a multiplexor
989 * There is a global msg list (consms_mux_msg),
990 * which is used to link all ioctl messages from
991 * upper layer, which are currently being processed.
993 * consms_mux_link_msg links a msg into the list,
994 * consms_mux_unlink_msg unlinks a msg from the list,
995 * consms_mux_find_msg finds a msg from the list
996 * according to its unique id.
998 * The id of each msg is taken from stream's mp,
999 * so the id is supposed to be unique.
1001 static void
1002 consms_mux_link_msg(consms_msg_t *msg)
1004 mutex_enter(&consms_msg_lock);
1005 msg->msg_next = consms_mux_msg;
1006 consms_mux_msg = msg;
1007 mutex_exit(&consms_msg_lock);
1010 static consms_msg_t *
1011 consms_mux_unlink_msg(uint_t msg_id)
1013 consms_msg_t *msg;
1014 consms_msg_t *prev_msg;
1016 mutex_enter(&consms_msg_lock);
1017 prev_msg = NULL;
1018 for (msg = consms_mux_msg; msg != NULL;
1019 prev_msg = msg, msg = msg->msg_next) {
1020 if (msg->msg_id == msg_id)
1021 break;
1024 if (msg != NULL) {
1025 if (prev_msg != NULL) {
1026 prev_msg->msg_next = msg->msg_next;
1027 } else {
1028 consms_mux_msg = consms_mux_msg->msg_next;
1030 msg->msg_next = NULL;
1032 mutex_exit(&consms_msg_lock);
1034 return (msg);
1037 static consms_msg_t *
1038 consms_mux_find_msg(uint_t msg_id)
1040 consms_msg_t *msg;
1042 mutex_enter(&consms_msg_lock);
1043 for (msg = consms_mux_msg; msg != NULL; msg = msg->msg_next) {
1044 if (msg->msg_id == msg_id)
1045 break;
1047 mutex_exit(&consms_msg_lock);
1049 return (msg);
1053 * Received ACK or NAK from lower mice
1055 * For non-transparent ioctl, the msg->msg_rsp_list
1056 * is always NULL; for transparent ioctl, it
1057 * remembers the M_COPYIN/M_COPYOUT request
1058 * messages from lower mice. So here if msg->msg_rsp_list
1059 * is NULL (after receiving all ACK/NAKs), we
1060 * are done with this specific ioctl.
1062 * As long as one of lower mice responds success,
1063 * we treat it success for a ioctl.
1065 static void
1066 consms_mux_ack(consms_msg_t *msg, mblk_t *mp)
1068 mblk_t *ack_mp;
1070 /* increment response_nums */
1071 msg->msg_num_responses++;
1073 if (mp->b_datap->db_type == M_IOCACK) {
1075 * Received ACK from lower, then
1076 * this is the last step for both
1077 * non-transparent and transparent
1078 * ioctl. We only need to remember
1079 * one of the ACKs, finally reply
1080 * this ACK to upper layer for this
1081 * specific ioctl.
1083 ASSERT(msg->msg_rsp_list == NULL);
1084 if (msg->msg_ack_mp == NULL) {
1085 msg->msg_ack_mp = mp;
1086 mp = NULL;
1091 * Check to see if all lower mice have responded
1092 * to our dispatching ioctl.
1094 if (msg->msg_num_responses == msg->msg_num_requests) {
1095 if ((msg->msg_ack_mp == NULL) &&
1096 (msg->msg_rsp_list == NULL)) {
1098 * All are NAKed.
1100 ack_mp = mp;
1101 mp = NULL;
1102 } else if (msg->msg_rsp_list == NULL) {
1104 * The last step and at least one ACKed.
1106 ack_mp = msg->msg_ack_mp;
1107 consms_mux_cache_states(msg->msg_request);
1108 consms_mux_max_wheel_report(ack_mp);
1109 } else {
1111 * This is a NAK, but we have
1112 * already received M_COPYIN
1113 * or M_COPYOUT request from
1114 * at least one of lower mice.
1115 * (msg->msg_rsp_list != NULL)
1117 * Still copyin or copyout.
1119 ack_mp = msg->msg_rsp_list->rsp_mp;
1120 consms_mux_max_wheel_report(ack_mp);
1123 qreply(msg->msg_queue, ack_mp);
1125 if (msg->msg_rsp_list == NULL) {
1127 * We are done with this ioctl.
1129 if (msg->msg_request)
1130 freemsg(msg->msg_request);
1131 (void) consms_mux_unlink_msg(msg->msg_id);
1132 kmem_free(msg, sizeof (*msg));
1136 if (mp) {
1137 freemsg(mp);
1142 * Received M_COPYIN or M_COPYOUT request from
1143 * lower mice for transparent ioctl
1145 * We remember each M_COPYIN/M_COPYOUT into the
1146 * msg->msg_rsp_list, reply upper layer using the first
1147 * M_COPYIN/M_COPYOUT in the list after receiving
1148 * all responses from lower mice, even if some of
1149 * them return NAKs.
1151 static void
1152 consms_mux_copyreq(queue_t *q, consms_msg_t *msg, mblk_t *mp)
1154 consms_response_t *rsp;
1156 rsp = (consms_response_t *)kmem_zalloc(sizeof (*rsp), KM_SLEEP);
1157 rsp->rsp_mp = mp;
1158 rsp->rsp_queue = q;
1159 if (msg->msg_rsp_list) {
1160 rsp->rsp_next = msg->msg_rsp_list;
1162 msg->msg_rsp_list = rsp;
1163 msg->msg_num_responses++;
1165 if (msg->msg_num_responses == msg->msg_num_requests) {
1166 consms_mux_max_wheel_report(msg->msg_rsp_list->rsp_mp);
1167 qreply(msg->msg_queue, msg->msg_rsp_list->rsp_mp);
1172 * Do the real job for updating M_COPYIN/M_COPYOUT
1173 * request with the mp of M_IOCDATA, then put it
1174 * down to lower mice.
1176 static void
1177 consms_mux_disp_iocdata(consms_response_t *rsp, mblk_t *mp)
1179 mblk_t *down_mp = rsp->rsp_mp;
1180 struct copyresp *copyresp = (struct copyresp *)mp->b_rptr;
1181 struct copyresp *newresp = (struct copyresp *)down_mp->b_rptr;
1184 * Update the rval.
1186 newresp->cp_rval = copyresp->cp_rval;
1189 * Update the db_type to M_IOCDATA.
1191 down_mp->b_datap->db_type = mp->b_datap->db_type;
1194 * Update the b_cont.
1196 if (down_mp->b_cont != NULL) {
1197 freemsg(down_mp->b_cont);
1198 down_mp->b_cont = NULL;
1200 if (mp->b_cont != NULL) {
1201 down_mp->b_cont = copymsg(mp->b_cont);
1205 * Put it down.
1207 (void) putq(WR(rsp->rsp_queue), down_mp);
1211 * Dispatch M_IOCDATA down to all lower mice
1212 * for transparent ioctl.
1214 * We update each M_COPYIN/M_COPYOUT in the
1215 * msg->msg_rsp_list with the M_IOCDATA.
1217 static void
1218 consms_mux_iocdata(consms_msg_t *msg, mblk_t *mp)
1220 consms_response_t *rsp;
1221 consms_response_t *tmp;
1222 consms_response_t *first;
1223 struct copyresp *copyresp;
1224 int request_nums;
1226 ASSERT(msg->msg_rsp_list != NULL);
1229 * We should remember the ioc data for
1230 * VUIDSWHEELSTATE, and MSIOSRESOLUTION,
1231 * for we will cache the wheel state and
1232 * the screen resolution later if ACKed.
1234 copyresp = (struct copyresp *)mp->b_rptr;
1235 if ((copyresp->cp_cmd == VUIDSWHEELSTATE) ||
1236 (copyresp->cp_cmd == MSIOSRESOLUTION)) {
1237 freemsg(msg->msg_request);
1238 msg->msg_request = copymsg(mp);
1242 * Update request numbers and response numbers.
1244 msg->msg_num_requests = msg->msg_num_responses;
1245 msg->msg_num_responses = 0;
1246 request_nums = 1;
1249 * Since we have use the first M_COPYIN/M_COPYOUT
1250 * in the msg_rsp_list to reply upper layer, the mp
1251 * of M_IOCDATA can be directly used for that.
1253 first = msg->msg_rsp_list;
1254 rsp = first->rsp_next;
1255 msg->msg_rsp_list = NULL;
1257 for (rsp = first->rsp_next; rsp != NULL; ) {
1258 tmp = rsp;
1259 rsp = rsp->rsp_next;
1260 consms_mux_disp_iocdata(tmp, mp);
1261 kmem_free(tmp, sizeof (*tmp));
1262 request_nums++;
1265 /* Must set the request number before the last q. */
1266 msg->msg_num_requests = request_nums;
1268 /* the first one */
1269 (void) putq(WR(first->rsp_queue), mp);
1270 kmem_free(first, sizeof (*first));
1275 * Here we update the number of wheels with
1276 * the virtual mouse for VUIDGWHEELCOUNT ioctl.
1278 static void
1279 consms_mux_max_wheel_report(mblk_t *mp)
1281 struct iocblk *iocp;
1282 int num_wheels;
1284 if (mp == NULL || mp->b_cont == NULL)
1285 return;
1287 iocp = (struct iocblk *)mp->b_rptr;
1289 if ((iocp->ioc_cmd == VUIDGWHEELCOUNT) &&
1290 (mp->b_datap->db_type == M_COPYOUT)) {
1291 num_wheels = *(int *)mp->b_cont->b_rptr;
1292 if (num_wheels < consms_state.consms_num_wheels) {
1293 *(int *)mp->b_cont->b_rptr =
1294 consms_state.consms_num_wheels;
1300 * Update the virtual mouse state variables with
1301 * the latest value from upper layer when these
1302 * set ioctls return success. Thus we can update
1303 * low mice with the latest state values during
1304 * hotplug.
1306 static void
1307 consms_mux_cache_states(mblk_t *mp)
1309 struct iocblk *iocp;
1310 Ms_parms *parms;
1311 Ms_screen_resolution *sr;
1312 wheel_state *ws;
1314 if (mp == NULL || mp->b_cont == NULL)
1315 return;
1317 iocp = (struct iocblk *)mp->b_rptr;
1318 switch (iocp->ioc_cmd) {
1319 case VUIDSFORMAT:
1320 consms_state.consms_vuid_format = *(int *)mp->b_cont->b_rptr;
1321 break;
1323 case MSIOSETPARMS:
1324 parms = (Ms_parms *)mp->b_cont->b_rptr;
1325 consms_state.consms_ms_parms = *parms;
1326 break;
1328 case MSIOSRESOLUTION:
1329 sr = (Ms_screen_resolution *)mp->b_cont->b_rptr;
1330 consms_state.consms_ms_sr = *sr;
1331 break;
1333 case VUIDSWHEELSTATE:
1334 ws = (wheel_state *)mp->b_cont->b_rptr;
1335 consms_state.consms_wheel_state_bf =
1336 (ws->stateflags << ws->id) |
1337 (consms_state.consms_wheel_state_bf & ~(1 << ws->id));
1338 break;
1343 * Dispatch ioctl mp (non-transparent and transparent)
1344 * down to all lower mice.
1346 * First, create a pending message for this mp, link it into
1347 * the global messages list. Then wait for ACK/NAK for
1348 * non-transparent ioctl, COPYIN/COPYOUT for transparent
1349 * ioctl.
1351 static int
1352 consms_mux_disp_ioctl(queue_t *q, mblk_t *mp)
1354 struct iocblk *iocp;
1355 consms_msg_t *msg;
1356 consms_lq_t *lq;
1357 mblk_t *copy_mp;
1358 int error = 0;
1360 iocp = (struct iocblk *)mp->b_rptr;
1361 msg = (consms_msg_t *)kmem_zalloc(sizeof (*msg), KM_SLEEP);
1362 msg->msg_id = iocp->ioc_id;
1363 msg->msg_request = mp;
1364 msg->msg_queue = q;
1365 msg->msg_num_requests = consms_state.consms_num_lqs;
1366 consms_mux_link_msg(msg);
1368 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1369 if ((copy_mp = copymsg(mp)) != NULL) {
1370 (void) putq(lq->lq_queue, copy_mp);
1371 } else {
1373 * If copymsg fails, we ignore this lq and
1374 * try next one. As long as one of them succeeds,
1375 * we dispatch this ioctl down. And later as long
1376 * as one of the lower drivers return success, we
1377 * reply to this ioctl with success.
1379 msg->msg_num_requests--;
1383 if (msg->msg_num_requests <= 0) {
1385 * Since copymsg fails for all lqs, we NAK this ioctl.
1387 (void) consms_mux_unlink_msg(msg->msg_id);
1388 kmem_free(msg, sizeof (*msg));
1389 error = ENOMEM;
1392 return (error);
1396 * Dispatch M_DATA and M_FLUSH message down to all
1397 * lower mice, and there are no acknowledgements
1398 * for them. Here we just copy the mp and then
1399 * put it into the lower queues.
1401 static void
1402 consms_mux_disp_data(mblk_t *mp)
1404 consms_lq_t *lq;
1405 mblk_t *copy_mp;
1407 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1408 if ((copy_mp = copymsg(mp)) != NULL) {
1409 (void) putq(lq->lq_queue, copy_mp);
1413 freemsg(mp);