bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / consms.c
blobca6a2bc91d4dfe9ac0824e940bf15eccd4dda290
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 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, 0) == 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, void **result)
328 register int error;
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;
338 break;
339 case DDI_INFO_DEVT2INSTANCE:
340 *result = NULL;
341 error = DDI_SUCCESS;
342 break;
343 default:
344 error = DDI_FAILURE;
346 return (error);
350 /*ARGSUSED*/
351 static int
352 consmsopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
354 upperqueue = q;
355 qprocson(q);
356 return (0);
359 /*ARGSUSED*/
360 static int
361 consmsclose(queue_t *q, int flag, cred_t *crp)
363 qprocsoff(q);
364 upperqueue = NULL;
365 return (0);
369 * Put procedure for upper write queue.
371 static void
372 consmsuwput(register queue_t *q, register mblk_t *mp)
374 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
375 consms_msg_t *msg;
376 int error = 0;
378 switch (mp->b_datap->db_type) {
380 case M_IOCTL:
381 consmsioctl(q, mp);
382 break;
384 case M_FLUSH:
385 if (*mp->b_rptr & FLUSHW)
386 flushq(q, FLUSHDATA);
387 if (*mp->b_rptr & FLUSHR)
388 flushq(RD(q), FLUSHDATA);
389 if (consms_state.consms_num_lqs > 0) {
390 consms_mux_disp_data(mp);
391 } else {
393 * No lower queue; just reflect this back upstream.
395 *mp->b_rptr &= ~FLUSHW;
396 if (*mp->b_rptr & FLUSHR)
397 qreply(q, mp);
398 else
399 freemsg(mp);
401 break;
403 case M_DATA:
404 if (consms_state.consms_num_lqs > 0) {
405 consms_mux_disp_data(mp);
406 } else {
407 freemsg(mp);
409 break;
411 case M_IOCDATA:
412 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
413 consms_mux_iocdata(msg, mp);
414 } else {
415 error = EINVAL;
417 break;
419 default:
420 error = EINVAL;
421 break;
424 if (error) {
426 * Pass an error message up.
428 mp->b_datap->db_type = M_ERROR;
429 if (mp->b_cont) {
430 freemsg(mp->b_cont);
431 mp->b_cont = NULL;
433 mp->b_rptr = mp->b_datap->db_base;
434 mp->b_wptr = mp->b_rptr + sizeof (char);
435 *mp->b_rptr = (char)error;
436 qreply(q, mp);
440 static void
441 consmsioctl(register queue_t *q, register mblk_t *mp)
443 register struct iocblk *iocp;
444 int error;
445 mblk_t *datap;
447 iocp = (struct iocblk *)mp->b_rptr;
449 switch (iocp->ioc_cmd) {
451 case I_LINK:
452 case I_PLINK:
453 mutex_enter(&consmslock);
454 consms_plink(q, mp);
455 mutex_exit(&consmslock);
456 return;
458 case I_UNLINK:
459 case I_PUNLINK:
460 mutex_enter(&consmslock);
461 if ((error = consms_punlink(q, mp)) != 0) {
462 mutex_exit(&consmslock);
463 miocnak(q, mp, 0, error);
464 return;
466 mutex_exit(&consmslock);
467 iocp->ioc_count = 0;
468 break;
470 case MSIOBUTTONS: /* query the number of buttons */
471 if ((consms_state.consms_num_lqs <= 0) ||
472 ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)) {
473 miocnak(q, mp, 0, ENOMEM);
474 return;
476 *(int *)datap->b_wptr = consms_state.consms_num_buttons;
477 datap->b_wptr += sizeof (int);
478 if (mp->b_cont) {
479 freemsg(mp->b_cont);
481 mp->b_cont = datap;
482 iocp->ioc_count = sizeof (int);
483 break;
485 default:
487 * Pass this through, if there's something to pass it
488 * through to; otherwise, reject it.
490 if (consms_state.consms_num_lqs <= 0) {
491 miocnak(q, mp, 0, EINVAL);
492 return;
494 if ((error = consms_mux_disp_ioctl(q, mp)) != 0)
495 miocnak(q, mp, 0, error);
497 return;
501 * Common exit path for calls that return a positive
502 * acknowledgment with a return value of 0.
504 miocack(q, mp, iocp->ioc_count, 0);
508 * Service procedure for lower write queue.
509 * Puts things on the queue below us, if it lets us.
511 static void
512 consmslwserv(register queue_t *q)
514 register mblk_t *mp;
516 while (canput(q->q_next) && (mp = getq(q)) != NULL)
517 putnext(q, mp);
521 * Put procedure for lower read queue.
523 static void
524 consmslrput(register queue_t *q, register mblk_t *mp)
526 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
527 struct copyreq *copyreq = (struct copyreq *)mp->b_rptr;
528 consms_msg_t *msg;
529 consms_lq_t *lq = (consms_lq_t *)q->q_ptr;
531 ASSERT(lq != NULL);
533 switch (mp->b_datap->db_type) {
534 case M_FLUSH:
535 if (*mp->b_rptr & FLUSHW)
536 flushq(WR(q), FLUSHDATA);
537 if (*mp->b_rptr & FLUSHR)
538 flushq(q, FLUSHDATA);
539 if (upperqueue != NULL)
540 putnext(upperqueue, mp); /* pass it through */
541 else {
543 * No upper queue; just reflect this back downstream.
545 *mp->b_rptr &= ~FLUSHR;
546 if (*mp->b_rptr & FLUSHW)
547 qreply(q, mp);
548 else
549 freemsg(mp);
551 break;
553 case M_DATA:
554 if (upperqueue != NULL)
555 putnext(upperqueue, mp);
556 else
557 freemsg(mp);
558 consms_idle_stamp = gethrestime_sec();
559 break;
561 case M_IOCACK:
562 case M_IOCNAK:
564 * First, check to see if this device
565 * is still being initialized.
567 if (lq->lq_ioc_reply_func != NULL) {
568 mutex_enter(&consmslock);
569 lq->lq_ioc_reply_func(lq, mp);
570 mutex_exit(&consmslock);
571 freemsg(mp);
572 break;
576 * This is normal ioctl ack for upper layer.
578 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
579 consms_mux_ack(msg, mp);
580 } else {
581 freemsg(mp);
583 consms_idle_stamp = gethrestime_sec();
584 break;
586 case M_COPYIN:
587 case M_COPYOUT:
588 if ((msg = consms_mux_find_msg(copyreq->cq_id)) != NULL) {
589 consms_mux_copyreq(q, msg, mp);
590 } else
591 freemsg(mp);
592 consms_idle_stamp = gethrestime_sec();
593 break;
595 case M_ERROR:
596 case M_HANGUP:
597 default:
598 freemsg(mp); /* anything useful here? */
599 break;
603 /* ARGSUSED */
604 static int
605 consms_kstat_update(kstat_t *ksp, int rw)
607 if (rw == KSTAT_WRITE)
608 return (EACCES);
610 consms_kstat.idle_sec.value.l = gethrestime_sec() - consms_idle_stamp;
611 return (0);
614 /*ARGSUSED*/
615 static int
616 consms_punlink(queue_t *q, mblk_t *mp)
618 struct linkblk *linkp;
619 consms_lq_t *lq;
620 consms_lq_t *prev_lq;
622 ASSERT(MUTEX_HELD(&consmslock));
624 linkp = (struct linkblk *)mp->b_cont->b_rptr;
626 prev_lq = NULL;
627 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
628 if (lq->lq_queue == linkp->l_qbot) {
629 if (prev_lq)
630 prev_lq->lq_next = lq->lq_next;
631 else
632 consms_state.consms_lqs = lq->lq_next;
633 kmem_free(lq, sizeof (*lq));
634 consms_state.consms_num_lqs--;
637 * Check to see if mouse capabilities
638 * have changed.
640 consms_check_caps();
642 return (0);
644 prev_lq = lq;
647 return (EINVAL);
651 * Link a specific mouse into our mouse list.
653 static void
654 consms_plink(queue_t *q, mblk_t *mp)
656 struct linkblk *linkp;
657 consms_lq_t *lq;
658 queue_t *lowq;
660 ASSERT(MUTEX_HELD(&consmslock));
662 linkp = (struct linkblk *)mp->b_cont->b_rptr;
663 lowq = linkp->l_qbot;
665 lq = kmem_zalloc(sizeof (*lq), KM_SLEEP);
667 lowq->q_ptr = (void *)lq;
668 OTHERQ(lowq)->q_ptr = (void *)lq;
669 lq->lq_queue = lowq;
670 lq->lq_pending_plink = mp;
671 lq->lq_pending_queue = q;
674 * Set the number of buttons to 3 by default
675 * in case the following MSIOBUTTONS ioctl fails.
677 lq->lq_num_buttons = 3;
680 * Begin to initialize this mouse.
682 lq->lq_state = LQS_START;
683 consms_lqs_ack_complete(lq, NULL);
687 * Initialize the newly hotplugged-in mouse,
688 * e.g. get the number of buttons, set event
689 * format. Then we add it into our list.
691 static void
692 consms_lqs_ack_complete(consms_lq_t *lq, mblk_t *mp)
694 mblk_t *req = NULL;
695 boolean_t skipped = B_FALSE;
696 wheel_state *ws;
697 Ms_screen_resolution *sr;
698 Ms_parms *params;
700 ASSERT(MUTEX_HELD(&consmslock));
703 * We try each ioctl even if the previous one fails
704 * until we reach LQS_DONE, and then add this lq
705 * into our lq list.
707 * If the message allocation fails, we skip this ioctl,
708 * set skipped flag to B_TRUE in order to skip the ioctl
709 * result, then we try next ioctl, go to next state.
711 while ((lq->lq_state < LQS_DONE) && (req == NULL)) {
712 switch (lq->lq_state) {
713 case LQS_START:
715 * First, issue MSIOBUTTONS ioctl
716 * to get the number of buttons.
718 req = mkiocb(MSIOBUTTONS);
719 if (req && ((req->b_cont = allocb(sizeof (int),
720 BPRI_MED)) == NULL)) {
721 freemsg(req);
722 req = NULL;
724 if (req == NULL)
725 skipped = B_TRUE;
726 lq->lq_state++;
727 break;
729 case LQS_BUTTON_COUNT_PENDING:
730 if (!skipped && mp && mp->b_cont &&
731 (mp->b_datap->db_type == M_IOCACK))
732 lq->lq_num_buttons =
733 *(int *)mp->b_cont->b_rptr;
736 * Second, issue VUIDGWHEELCOUNT ioctl
737 * to get the count of wheels.
739 req = mkiocb(VUIDGWHEELCOUNT);
740 if (req && ((req->b_cont = allocb(sizeof (int),
741 BPRI_MED)) == NULL)) {
742 freemsg(req);
743 req = NULL;
745 if (req == NULL)
746 skipped = B_TRUE;
747 lq->lq_state++;
748 break;
750 case LQS_WHEEL_COUNT_PENDING:
751 if (!skipped && mp && mp->b_cont &&
752 (mp->b_datap->db_type == M_IOCACK))
753 lq->lq_num_wheels =
754 *(int *)mp->b_cont->b_rptr;
757 * Third, issue VUIDSFORMAT ioctl
758 * to set the event format.
760 req = mkiocb(VUIDSFORMAT);
761 if (req && ((req->b_cont = allocb(sizeof (int),
762 BPRI_MED)) == NULL)) {
763 freemsg(req);
764 req = NULL;
766 if (req) {
767 *(int *)req->b_cont->b_wptr =
768 consms_state.consms_vuid_format;
769 req->b_cont->b_wptr += sizeof (int);
771 lq->lq_state++;
772 break;
774 case LQS_SET_VUID_FORMAT_PENDING:
776 * Fourth, issue VUIDSWHEELSTATE ioctl
777 * to set the wheel state (enable or disable).
779 req = mkiocb(VUIDSWHEELSTATE);
780 if (req && ((req->b_cont = allocb(sizeof (wheel_state),
781 BPRI_MED)) == NULL)) {
782 freemsg(req);
783 req = NULL;
785 if (req) {
786 ws = (wheel_state *)req->b_cont->b_wptr;
787 ws->vers = VUID_WHEEL_STATE_VERS;
788 ws->id = 0; /* the first wheel */
789 ws->stateflags =
790 consms_state.consms_wheel_state_bf & 1;
791 req->b_cont->b_wptr += sizeof (wheel_state);
793 lq->lq_state++;
794 break;
796 case LQS_SET_WHEEL_STATE_PENDING:
798 * Fifth, issue MSIOSETPARMS ioctl
799 * to set the parameters for USB mouse.
801 req = mkiocb(MSIOSETPARMS);
802 if (req && ((req->b_cont = allocb(sizeof (Ms_parms),
803 BPRI_MED)) == NULL)) {
804 freemsg(req);
805 req = NULL;
807 if (req) {
808 params = (Ms_parms *)req->b_cont->b_wptr;
809 *params = consms_state.consms_ms_parms;
810 req->b_cont->b_wptr += sizeof (Ms_parms);
812 lq->lq_state++;
813 break;
815 case LQS_SET_PARMS_PENDING:
817 * Sixth, issue MSIOSRESOLUTION ioctl
818 * to set the screen resolution for absolute mouse.
820 req = mkiocb(MSIOSRESOLUTION);
821 if (req && ((req->b_cont =
822 allocb(sizeof (Ms_screen_resolution),
823 BPRI_MED)) == NULL)) {
824 freemsg(req);
825 req = NULL;
827 if (req) {
828 sr =
829 (Ms_screen_resolution *)req->b_cont->b_wptr;
830 *sr = consms_state.consms_ms_sr;
831 req->b_cont->b_wptr +=
832 sizeof (Ms_screen_resolution);
834 lq->lq_state++;
835 break;
837 case LQS_SET_RESOLUTION_PENDING:
839 * All jobs are done, lq->lq_state is turned into
840 * LQS_DONE, and this lq is added into our list.
842 lq->lq_state++;
843 consms_add_lq(lq);
844 break;
848 if (lq->lq_state < LQS_DONE) {
849 lq->lq_ioc_reply_func = consms_lqs_ack_complete;
850 (void) putq(lq->lq_queue, req);
855 * Add this specific lq into our list, finally reply
856 * the previous pending I_PLINK ioctl. Also check to
857 * see if mouse capabilities have changed, and send
858 * a dynamical notification event to upper layer if
859 * necessary.
861 static void
862 consms_add_lq(consms_lq_t *lq)
864 struct iocblk *iocp;
866 ASSERT(MUTEX_HELD(&consmslock));
868 lq->lq_ioc_reply_func = NULL;
869 iocp = (struct iocblk *)lq->lq_pending_plink->b_rptr;
870 iocp->ioc_error = 0;
871 iocp->ioc_count = 0;
872 iocp->ioc_rval = 0;
873 lq->lq_pending_plink->b_datap->db_type = M_IOCACK;
875 /* Reply to the I_PLINK ioctl. */
876 qreply(lq->lq_pending_queue, lq->lq_pending_plink);
878 lq->lq_pending_plink = NULL;
879 lq->lq_pending_queue = NULL;
882 * Add this lq into list.
884 consms_state.consms_num_lqs++;
886 lq->lq_next = consms_state.consms_lqs;
887 consms_state.consms_lqs = lq;
890 * Check to see if mouse capabilities
891 * have changed.
893 consms_check_caps();
898 static void
899 consms_check_caps(void)
901 consms_lq_t *lq;
902 int max_buttons = 0;
903 int max_wheels = 0;
904 mblk_t *mp;
907 * Check to see if the number of buttons
908 * and the number of wheels have changed.
910 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
911 max_buttons = CONSMS_MAX(max_buttons, lq->lq_num_buttons);
912 max_wheels = CONSMS_MAX(max_wheels, lq->lq_num_wheels);
915 if (max_buttons != consms_state.consms_num_buttons) {
917 * Since the number of buttons have changed,
918 * send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
919 * notification event to upper layer.
921 consms_state.consms_num_buttons = max_buttons;
922 if (upperqueue != NULL) {
923 if ((mp = consms_new_firm_event(
924 MOUSE_CAP_CHANGE_NUM_BUT,
925 consms_state.consms_num_buttons)) != NULL) {
926 putnext(upperqueue, mp);
931 if (max_wheels != consms_state.consms_num_wheels) {
933 * Since the number of wheels have changed,
934 * send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
935 * notification event to upper layer.
937 consms_state.consms_num_wheels = max_wheels;
938 if (upperqueue != NULL) {
939 if ((mp = consms_new_firm_event(
940 MOUSE_CAP_CHANGE_NUM_WHEEL,
941 consms_state.consms_num_wheels)) != NULL) {
942 putnext(upperqueue, mp);
949 * Allocate a dynamical notification event.
951 static mblk_t *
952 consms_new_firm_event(ushort_t id, int value)
954 Firm_event *fep;
955 mblk_t *tmp;
957 if ((tmp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
958 fep = (Firm_event *)tmp->b_wptr;
959 fep->id = id;
960 fep->pair_type = FE_PAIR_NONE;
961 fep->pair = 0;
962 fep->value = value;
963 tmp->b_wptr += sizeof (Firm_event);
966 return (tmp);
970 * Start of dispatching interfaces as a multiplexor
974 * There is a global msg list (consms_mux_msg),
975 * which is used to link all ioctl messages from
976 * upper layer, which are currently being processed.
978 * consms_mux_link_msg links a msg into the list,
979 * consms_mux_unlink_msg unlinks a msg from the list,
980 * consms_mux_find_msg finds a msg from the list
981 * according to its unique id.
983 * The id of each msg is taken from stream's mp,
984 * so the id is supposed to be unique.
986 static void
987 consms_mux_link_msg(consms_msg_t *msg)
989 mutex_enter(&consms_msg_lock);
990 msg->msg_next = consms_mux_msg;
991 consms_mux_msg = msg;
992 mutex_exit(&consms_msg_lock);
995 static consms_msg_t *
996 consms_mux_unlink_msg(uint_t msg_id)
998 consms_msg_t *msg;
999 consms_msg_t *prev_msg;
1001 mutex_enter(&consms_msg_lock);
1002 prev_msg = NULL;
1003 for (msg = consms_mux_msg; msg != NULL;
1004 prev_msg = msg, msg = msg->msg_next) {
1005 if (msg->msg_id == msg_id)
1006 break;
1009 if (msg != NULL) {
1010 if (prev_msg != NULL) {
1011 prev_msg->msg_next = msg->msg_next;
1012 } else {
1013 consms_mux_msg = consms_mux_msg->msg_next;
1015 msg->msg_next = NULL;
1017 mutex_exit(&consms_msg_lock);
1019 return (msg);
1022 static consms_msg_t *
1023 consms_mux_find_msg(uint_t msg_id)
1025 consms_msg_t *msg;
1027 mutex_enter(&consms_msg_lock);
1028 for (msg = consms_mux_msg; msg != NULL; msg = msg->msg_next) {
1029 if (msg->msg_id == msg_id)
1030 break;
1032 mutex_exit(&consms_msg_lock);
1034 return (msg);
1038 * Received ACK or NAK from lower mice
1040 * For non-transparent ioctl, the msg->msg_rsp_list
1041 * is always NULL; for transparent ioctl, it
1042 * remembers the M_COPYIN/M_COPYOUT request
1043 * messages from lower mice. So here if msg->msg_rsp_list
1044 * is NULL (after receiving all ACK/NAKs), we
1045 * are done with this specific ioctl.
1047 * As long as one of lower mice responds success,
1048 * we treat it success for a ioctl.
1050 static void
1051 consms_mux_ack(consms_msg_t *msg, mblk_t *mp)
1053 mblk_t *ack_mp;
1055 /* increment response_nums */
1056 msg->msg_num_responses++;
1058 if (mp->b_datap->db_type == M_IOCACK) {
1060 * Received ACK from lower, then
1061 * this is the last step for both
1062 * non-transparent and transparent
1063 * ioctl. We only need to remember
1064 * one of the ACKs, finally reply
1065 * this ACK to upper layer for this
1066 * specific ioctl.
1068 ASSERT(msg->msg_rsp_list == NULL);
1069 if (msg->msg_ack_mp == NULL) {
1070 msg->msg_ack_mp = mp;
1071 mp = NULL;
1076 * Check to see if all lower mice have responded
1077 * to our dispatching ioctl.
1079 if (msg->msg_num_responses == msg->msg_num_requests) {
1080 if ((msg->msg_ack_mp == NULL) &&
1081 (msg->msg_rsp_list == NULL)) {
1083 * All are NAKed.
1085 ack_mp = mp;
1086 mp = NULL;
1087 } else if (msg->msg_rsp_list == NULL) {
1089 * The last step and at least one ACKed.
1091 ack_mp = msg->msg_ack_mp;
1092 consms_mux_cache_states(msg->msg_request);
1093 consms_mux_max_wheel_report(ack_mp);
1094 } else {
1096 * This is a NAK, but we have
1097 * already received M_COPYIN
1098 * or M_COPYOUT request from
1099 * at least one of lower mice.
1100 * (msg->msg_rsp_list != NULL)
1102 * Still copyin or copyout.
1104 ack_mp = msg->msg_rsp_list->rsp_mp;
1105 consms_mux_max_wheel_report(ack_mp);
1108 qreply(msg->msg_queue, ack_mp);
1110 if (msg->msg_rsp_list == NULL) {
1112 * We are done with this ioctl.
1114 if (msg->msg_request)
1115 freemsg(msg->msg_request);
1116 (void) consms_mux_unlink_msg(msg->msg_id);
1117 kmem_free(msg, sizeof (*msg));
1121 if (mp) {
1122 freemsg(mp);
1127 * Received M_COPYIN or M_COPYOUT request from
1128 * lower mice for transparent ioctl
1130 * We remember each M_COPYIN/M_COPYOUT into the
1131 * msg->msg_rsp_list, reply upper layer using the first
1132 * M_COPYIN/M_COPYOUT in the list after receiving
1133 * all responses from lower mice, even if some of
1134 * them return NAKs.
1136 static void
1137 consms_mux_copyreq(queue_t *q, consms_msg_t *msg, mblk_t *mp)
1139 consms_response_t *rsp;
1141 rsp = (consms_response_t *)kmem_zalloc(sizeof (*rsp), KM_SLEEP);
1142 rsp->rsp_mp = mp;
1143 rsp->rsp_queue = q;
1144 if (msg->msg_rsp_list) {
1145 rsp->rsp_next = msg->msg_rsp_list;
1147 msg->msg_rsp_list = rsp;
1148 msg->msg_num_responses++;
1150 if (msg->msg_num_responses == msg->msg_num_requests) {
1151 consms_mux_max_wheel_report(msg->msg_rsp_list->rsp_mp);
1152 qreply(msg->msg_queue, msg->msg_rsp_list->rsp_mp);
1157 * Do the real job for updating M_COPYIN/M_COPYOUT
1158 * request with the mp of M_IOCDATA, then put it
1159 * down to lower mice.
1161 static void
1162 consms_mux_disp_iocdata(consms_response_t *rsp, mblk_t *mp)
1164 mblk_t *down_mp = rsp->rsp_mp;
1165 struct copyresp *copyresp = (struct copyresp *)mp->b_rptr;
1166 struct copyresp *newresp = (struct copyresp *)down_mp->b_rptr;
1169 * Update the rval.
1171 newresp->cp_rval = copyresp->cp_rval;
1174 * Update the db_type to M_IOCDATA.
1176 down_mp->b_datap->db_type = mp->b_datap->db_type;
1179 * Update the b_cont.
1181 if (down_mp->b_cont != NULL) {
1182 freemsg(down_mp->b_cont);
1183 down_mp->b_cont = NULL;
1185 if (mp->b_cont != NULL) {
1186 down_mp->b_cont = copymsg(mp->b_cont);
1190 * Put it down.
1192 (void) putq(WR(rsp->rsp_queue), down_mp);
1196 * Dispatch M_IOCDATA down to all lower mice
1197 * for transparent ioctl.
1199 * We update each M_COPYIN/M_COPYOUT in the
1200 * msg->msg_rsp_list with the M_IOCDATA.
1202 static void
1203 consms_mux_iocdata(consms_msg_t *msg, mblk_t *mp)
1205 consms_response_t *rsp;
1206 consms_response_t *tmp;
1207 consms_response_t *first;
1208 struct copyresp *copyresp;
1209 int request_nums;
1211 ASSERT(msg->msg_rsp_list != NULL);
1214 * We should remember the ioc data for
1215 * VUIDSWHEELSTATE, and MSIOSRESOLUTION,
1216 * for we will cache the wheel state and
1217 * the screen resolution later if ACKed.
1219 copyresp = (struct copyresp *)mp->b_rptr;
1220 if ((copyresp->cp_cmd == VUIDSWHEELSTATE) ||
1221 (copyresp->cp_cmd == MSIOSRESOLUTION)) {
1222 freemsg(msg->msg_request);
1223 msg->msg_request = copymsg(mp);
1227 * Update request numbers and response numbers.
1229 msg->msg_num_requests = msg->msg_num_responses;
1230 msg->msg_num_responses = 0;
1231 request_nums = 1;
1234 * Since we have use the first M_COPYIN/M_COPYOUT
1235 * in the msg_rsp_list to reply upper layer, the mp
1236 * of M_IOCDATA can be directly used for that.
1238 first = msg->msg_rsp_list;
1239 rsp = first->rsp_next;
1240 msg->msg_rsp_list = NULL;
1242 for (rsp = first->rsp_next; rsp != NULL; ) {
1243 tmp = rsp;
1244 rsp = rsp->rsp_next;
1245 consms_mux_disp_iocdata(tmp, mp);
1246 kmem_free(tmp, sizeof (*tmp));
1247 request_nums++;
1250 /* Must set the request number before the last q. */
1251 msg->msg_num_requests = request_nums;
1253 /* the first one */
1254 (void) putq(WR(first->rsp_queue), mp);
1255 kmem_free(first, sizeof (*first));
1260 * Here we update the number of wheels with
1261 * the virtual mouse for VUIDGWHEELCOUNT ioctl.
1263 static void
1264 consms_mux_max_wheel_report(mblk_t *mp)
1266 struct iocblk *iocp;
1267 int num_wheels;
1269 if (mp == NULL || mp->b_cont == NULL)
1270 return;
1272 iocp = (struct iocblk *)mp->b_rptr;
1274 if ((iocp->ioc_cmd == VUIDGWHEELCOUNT) &&
1275 (mp->b_datap->db_type == M_COPYOUT)) {
1276 num_wheels = *(int *)mp->b_cont->b_rptr;
1277 if (num_wheels < consms_state.consms_num_wheels) {
1278 *(int *)mp->b_cont->b_rptr =
1279 consms_state.consms_num_wheels;
1285 * Update the virtual mouse state variables with
1286 * the latest value from upper layer when these
1287 * set ioctls return success. Thus we can update
1288 * low mice with the latest state values during
1289 * hotplug.
1291 static void
1292 consms_mux_cache_states(mblk_t *mp)
1294 struct iocblk *iocp;
1295 Ms_parms *parms;
1296 Ms_screen_resolution *sr;
1297 wheel_state *ws;
1299 if (mp == NULL || mp->b_cont == NULL)
1300 return;
1302 iocp = (struct iocblk *)mp->b_rptr;
1303 switch (iocp->ioc_cmd) {
1304 case VUIDSFORMAT:
1305 consms_state.consms_vuid_format = *(int *)mp->b_cont->b_rptr;
1306 break;
1308 case MSIOSETPARMS:
1309 parms = (Ms_parms *)mp->b_cont->b_rptr;
1310 consms_state.consms_ms_parms = *parms;
1311 break;
1313 case MSIOSRESOLUTION:
1314 sr = (Ms_screen_resolution *)mp->b_cont->b_rptr;
1315 consms_state.consms_ms_sr = *sr;
1316 break;
1318 case VUIDSWHEELSTATE:
1319 ws = (wheel_state *)mp->b_cont->b_rptr;
1320 consms_state.consms_wheel_state_bf =
1321 (ws->stateflags << ws->id) |
1322 (consms_state.consms_wheel_state_bf & ~(1 << ws->id));
1323 break;
1328 * Dispatch ioctl mp (non-transparent and transparent)
1329 * down to all lower mice.
1331 * First, create a pending message for this mp, link it into
1332 * the global messages list. Then wait for ACK/NAK for
1333 * non-transparent ioctl, COPYIN/COPYOUT for transparent
1334 * ioctl.
1336 static int
1337 consms_mux_disp_ioctl(queue_t *q, mblk_t *mp)
1339 struct iocblk *iocp;
1340 consms_msg_t *msg;
1341 consms_lq_t *lq;
1342 mblk_t *copy_mp;
1343 int error = 0;
1345 iocp = (struct iocblk *)mp->b_rptr;
1346 msg = (consms_msg_t *)kmem_zalloc(sizeof (*msg), KM_SLEEP);
1347 msg->msg_id = iocp->ioc_id;
1348 msg->msg_request = mp;
1349 msg->msg_queue = q;
1350 msg->msg_num_requests = consms_state.consms_num_lqs;
1351 consms_mux_link_msg(msg);
1353 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1354 if ((copy_mp = copymsg(mp)) != NULL) {
1355 (void) putq(lq->lq_queue, copy_mp);
1356 } else {
1358 * If copymsg fails, we ignore this lq and
1359 * try next one. As long as one of them succeeds,
1360 * we dispatch this ioctl down. And later as long
1361 * as one of the lower drivers return success, we
1362 * reply to this ioctl with success.
1364 msg->msg_num_requests--;
1368 if (msg->msg_num_requests <= 0) {
1370 * Since copymsg fails for all lqs, we NAK this ioctl.
1372 (void) consms_mux_unlink_msg(msg->msg_id);
1373 kmem_free(msg, sizeof (*msg));
1374 error = ENOMEM;
1377 return (error);
1381 * Dispatch M_DATA and M_FLUSH message down to all
1382 * lower mice, and there are no acknowledgements
1383 * for them. Here we just copy the mp and then
1384 * put it into the lower queues.
1386 static void
1387 consms_mux_disp_data(mblk_t *mp)
1389 consms_lq_t *lq;
1390 mblk_t *copy_mp;
1392 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1393 if ((copy_mp = copymsg(mp)) != NULL) {
1394 (void) putq(lq->lq_queue, copy_mp);
1398 freemsg(mp);