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,
227 NULL
228 };
229
230 int
_init(void)231 _init(void)
232 {
233 int error;
234
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);
241 }
242 return (error);
243 }
244
245 int
_fini(void)246 _fini(void)
247 {
248 int error;
249
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);
256 }
257
258 int
_info(struct modinfo * modinfop)259 _info(struct modinfo *modinfop)
260 {
261 return (mod_info(&modlinkage, modinfop));
262 }
263
264 static int
consms_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)265 consms_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
266 {
267 kstat_t *ksp;
268
269 switch (cmd) {
270 case DDI_ATTACH:
271 break;
272 default:
273 return (DDI_FAILURE);
274 }
275
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);
280 }
281 consms_dip = devi;
282 (void) ddi_prop_update_int(DDI_DEV_T_NONE, devi, DDI_NO_AUTODETACH, 1);
283
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 */
291 }
292
293 consms_state.consms_lqs = NULL;
294 consms_state.consms_num_lqs = 0;
295
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;
309
310 return (DDI_SUCCESS);
311 }
312
313 /*ARGSUSED*/
314 static int
consms_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)315 consms_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
316 {
317 switch (cmd) {
318 case DDI_DETACH:
319 default:
320 return (DDI_FAILURE);
321 }
322 }
323
324 /*ARGSUSED*/
325 static int
consms_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)326 consms_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
327 void **result)
328 {
329 register int error;
330
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;
338 }
339 break;
340 case DDI_INFO_DEVT2INSTANCE:
341 *result = (void *)0;
342 error = DDI_SUCCESS;
343 break;
344 default:
345 error = DDI_FAILURE;
346 }
347 return (error);
348 }
349
350
351 /*ARGSUSED*/
352 static int
consmsopen(q,devp,flag,sflag,crp)353 consmsopen(q, devp, flag, sflag, crp)
354 queue_t *q;
355 dev_t *devp;
356 int flag, sflag;
357 cred_t *crp;
358 {
359 upperqueue = q;
360 qprocson(q);
361 return (0);
362 }
363
364 /*ARGSUSED*/
365 static int
consmsclose(q,flag,crp)366 consmsclose(q, flag, crp)
367 queue_t *q;
368 int flag;
369 cred_t *crp;
370 {
371 qprocsoff(q);
372 upperqueue = NULL;
373 return (0);
374 }
375
376 /*
377 * Put procedure for upper write queue.
378 */
379 static void
consmsuwput(q,mp)380 consmsuwput(q, mp)
381 register queue_t *q;
382 register mblk_t *mp;
383 {
384 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
385 consms_msg_t *msg;
386 int error = 0;
387
388 switch (mp->b_datap->db_type) {
389
390 case M_IOCTL:
391 consmsioctl(q, mp);
392 break;
393
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 {
402 /*
403 * No lower queue; just reflect this back upstream.
404 */
405 *mp->b_rptr &= ~FLUSHW;
406 if (*mp->b_rptr & FLUSHR)
407 qreply(q, mp);
408 else
409 freemsg(mp);
410 }
411 break;
412
413 case M_DATA:
414 if (consms_state.consms_num_lqs > 0) {
415 consms_mux_disp_data(mp);
416 } else {
417 freemsg(mp);
418 }
419 break;
420
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;
426 }
427 break;
428
429 default:
430 error = EINVAL;
431 break;
432 }
433
434 if (error) {
435 /*
436 * Pass an error message up.
437 */
438 mp->b_datap->db_type = M_ERROR;
439 if (mp->b_cont) {
440 freemsg(mp->b_cont);
441 mp->b_cont = NULL;
442 }
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);
447 }
448 }
449
450 static void
consmsioctl(q,mp)451 consmsioctl(q, mp)
452 register queue_t *q;
453 register mblk_t *mp;
454 {
455 register struct iocblk *iocp;
456 int error;
457 mblk_t *datap;
458
459 iocp = (struct iocblk *)mp->b_rptr;
460
461 switch (iocp->ioc_cmd) {
462
463 case I_LINK:
464 case I_PLINK:
465 mutex_enter(&consmslock);
466 consms_plink(q, mp);
467 mutex_exit(&consmslock);
468 return;
469
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;
477 }
478 mutex_exit(&consmslock);
479 iocp->ioc_count = 0;
480 break;
481
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;
487 }
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);
492 }
493 mp->b_cont = datap;
494 iocp->ioc_count = sizeof (int);
495 break;
496
497 default:
498 /*
499 * Pass this through, if there's something to pass it
500 * through to; otherwise, reject it.
501 */
502 if (consms_state.consms_num_lqs <= 0) {
503 miocnak(q, mp, 0, EINVAL);
504 return;
505 }
506 if ((error = consms_mux_disp_ioctl(q, mp)) != 0)
507 miocnak(q, mp, 0, error);
508
509 return;
510 }
511
512 /*
513 * Common exit path for calls that return a positive
514 * acknowledgment with a return value of 0.
515 */
516 miocack(q, mp, iocp->ioc_count, 0);
517 }
518
519 /*
520 * Service procedure for lower write queue.
521 * Puts things on the queue below us, if it lets us.
522 */
523 static void
consmslwserv(q)524 consmslwserv(q)
525 register queue_t *q;
526 {
527 register mblk_t *mp;
528
529 while (canput(q->q_next) && (mp = getq(q)) != NULL)
530 putnext(q, mp);
531 }
532
533 /*
534 * Put procedure for lower read queue.
535 */
536 static void
consmslrput(q,mp)537 consmslrput(q, mp)
538 register queue_t *q;
539 register mblk_t *mp;
540 {
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;
545
546 ASSERT(lq != NULL);
547
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 {
557 /*
558 * No upper queue; just reflect this back downstream.
559 */
560 *mp->b_rptr &= ~FLUSHR;
561 if (*mp->b_rptr & FLUSHW)
562 qreply(q, mp);
563 else
564 freemsg(mp);
565 }
566 break;
567
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;
575
576 case M_IOCACK:
577 case M_IOCNAK:
578 /*
579 * First, check to see if this device
580 * is still being initialized.
581 */
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;
588 }
589
590 /*
591 * This is normal ioctl ack for upper layer.
592 */
593 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
594 consms_mux_ack(msg, mp);
595 } else {
596 freemsg(mp);
597 }
598 consms_idle_stamp = gethrestime_sec();
599 break;
600
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;
609
610 case M_ERROR:
611 case M_HANGUP:
612 default:
613 freemsg(mp); /* anything useful here? */
614 break;
615 }
616 }
617
618 /* ARGSUSED */
619 static int
consms_kstat_update(kstat_t * ksp,int rw)620 consms_kstat_update(kstat_t *ksp, int rw)
621 {
622 if (rw == KSTAT_WRITE)
623 return (EACCES);
624
625 consms_kstat.idle_sec.value.l = gethrestime_sec() - consms_idle_stamp;
626 return (0);
627 }
628
629 /*ARGSUSED*/
630 static int
consms_punlink(queue_t * q,mblk_t * mp)631 consms_punlink(queue_t *q, mblk_t *mp)
632 {
633 struct linkblk *linkp;
634 consms_lq_t *lq;
635 consms_lq_t *prev_lq;
636
637 ASSERT(MUTEX_HELD(&consmslock));
638
639 linkp = (struct linkblk *)mp->b_cont->b_rptr;
640
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--;
650
651 /*
652 * Check to see if mouse capabilities
653 * have changed.
654 */
655 consms_check_caps();
656
657 return (0);
658 }
659 prev_lq = lq;
660 }
661
662 return (EINVAL);
663 }
664
665 /*
666 * Link a specific mouse into our mouse list.
667 */
668 static void
consms_plink(queue_t * q,mblk_t * mp)669 consms_plink(queue_t *q, mblk_t *mp)
670 {
671 struct linkblk *linkp;
672 consms_lq_t *lq;
673 queue_t *lowq;
674
675 ASSERT(MUTEX_HELD(&consmslock));
676
677 linkp = (struct linkblk *)mp->b_cont->b_rptr;
678 lowq = linkp->l_qbot;
679
680 lq = kmem_zalloc(sizeof (*lq), KM_SLEEP);
681
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;
687
688 /*
689 * Set the number of buttons to 3 by default
690 * in case the following MSIOBUTTONS ioctl fails.
691 */
692 lq->lq_num_buttons = 3;
693
694 /*
695 * Begin to initialize this mouse.
696 */
697 lq->lq_state = LQS_START;
698 consms_lqs_ack_complete(lq, NULL);
699 }
700
701 /*
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.
705 */
706 static void
consms_lqs_ack_complete(consms_lq_t * lq,mblk_t * mp)707 consms_lqs_ack_complete(consms_lq_t *lq, mblk_t *mp)
708 {
709 mblk_t *req = NULL;
710 boolean_t skipped = B_FALSE;
711 wheel_state *ws;
712 Ms_screen_resolution *sr;
713 Ms_parms *params;
714
715 ASSERT(MUTEX_HELD(&consmslock));
716
717 /*
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.
721 *
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.
725 */
726 while ((lq->lq_state < LQS_DONE) && (req == NULL)) {
727 switch (lq->lq_state) {
728 case LQS_START:
729 /*
730 * First, issue MSIOBUTTONS ioctl
731 * to get the number of buttons.
732 */
733 req = mkiocb(MSIOBUTTONS);
734 if (req && ((req->b_cont = allocb(sizeof (int),
735 BPRI_MED)) == NULL)) {
736 freemsg(req);
737 req = NULL;
738 }
739 if (req == NULL)
740 skipped = B_TRUE;
741 lq->lq_state++;
742 break;
743
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;
749
750 /*
751 * Second, issue VUIDGWHEELCOUNT ioctl
752 * to get the count of wheels.
753 */
754 req = mkiocb(VUIDGWHEELCOUNT);
755 if (req && ((req->b_cont = allocb(sizeof (int),
756 BPRI_MED)) == NULL)) {
757 freemsg(req);
758 req = NULL;
759 }
760 if (req == NULL)
761 skipped = B_TRUE;
762 lq->lq_state++;
763 break;
764
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;
770
771 /*
772 * Third, issue VUIDSFORMAT ioctl
773 * to set the event format.
774 */
775 req = mkiocb(VUIDSFORMAT);
776 if (req && ((req->b_cont = allocb(sizeof (int),
777 BPRI_MED)) == NULL)) {
778 freemsg(req);
779 req = NULL;
780 }
781 if (req) {
782 *(int *)req->b_cont->b_wptr =
783 consms_state.consms_vuid_format;
784 req->b_cont->b_wptr += sizeof (int);
785 }
786 lq->lq_state++;
787 break;
788
789 case LQS_SET_VUID_FORMAT_PENDING:
790 /*
791 * Fourth, issue VUIDSWHEELSTATE ioctl
792 * to set the wheel state (enable or disable).
793 */
794 req = mkiocb(VUIDSWHEELSTATE);
795 if (req && ((req->b_cont = allocb(sizeof (wheel_state),
796 BPRI_MED)) == NULL)) {
797 freemsg(req);
798 req = NULL;
799 }
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);
807 }
808 lq->lq_state++;
809 break;
810
811 case LQS_SET_WHEEL_STATE_PENDING:
812 /*
813 * Fifth, issue MSIOSETPARMS ioctl
814 * to set the parameters for USB mouse.
815 */
816 req = mkiocb(MSIOSETPARMS);
817 if (req && ((req->b_cont = allocb(sizeof (Ms_parms),
818 BPRI_MED)) == NULL)) {
819 freemsg(req);
820 req = NULL;
821 }
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);
826 }
827 lq->lq_state++;
828 break;
829
830 case LQS_SET_PARMS_PENDING:
831 /*
832 * Sixth, issue MSIOSRESOLUTION ioctl
833 * to set the screen resolution for absolute mouse.
834 */
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;
841 }
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);
848 }
849 lq->lq_state++;
850 break;
851
852 case LQS_SET_RESOLUTION_PENDING:
853 /*
854 * All jobs are done, lq->lq_state is turned into
855 * LQS_DONE, and this lq is added into our list.
856 */
857 lq->lq_state++;
858 consms_add_lq(lq);
859 break;
860 }
861 }
862
863 if (lq->lq_state < LQS_DONE) {
864 lq->lq_ioc_reply_func = consms_lqs_ack_complete;
865 (void) putq(lq->lq_queue, req);
866 }
867 }
868
869 /*
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.
875 */
876 static void
consms_add_lq(consms_lq_t * lq)877 consms_add_lq(consms_lq_t *lq)
878 {
879 struct iocblk *iocp;
880
881 ASSERT(MUTEX_HELD(&consmslock));
882
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;
889
890 /* Reply to the I_PLINK ioctl. */
891 qreply(lq->lq_pending_queue, lq->lq_pending_plink);
892
893 lq->lq_pending_plink = NULL;
894 lq->lq_pending_queue = NULL;
895
896 /*
897 * Add this lq into list.
898 */
899 consms_state.consms_num_lqs++;
900
901 lq->lq_next = consms_state.consms_lqs;
902 consms_state.consms_lqs = lq;
903
904 /*
905 * Check to see if mouse capabilities
906 * have changed.
907 */
908 consms_check_caps();
909
910 }
911
912
913 static void
consms_check_caps(void)914 consms_check_caps(void)
915 {
916 consms_lq_t *lq;
917 int max_buttons = 0;
918 int max_wheels = 0;
919 mblk_t *mp;
920
921 /*
922 * Check to see if the number of buttons
923 * and the number of wheels have changed.
924 */
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);
928 }
929
930 if (max_buttons != consms_state.consms_num_buttons) {
931 /*
932 * Since the number of buttons have changed,
933 * send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
934 * notification event to upper layer.
935 */
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);
942 }
943 }
944 }
945
946 if (max_wheels != consms_state.consms_num_wheels) {
947 /*
948 * Since the number of wheels have changed,
949 * send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
950 * notification event to upper layer.
951 */
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);
958 }
959 }
960 }
961 }
962
963 /*
964 * Allocate a dynamical notification event.
965 */
966 static mblk_t *
consms_new_firm_event(ushort_t id,int value)967 consms_new_firm_event(ushort_t id, int value)
968 {
969 Firm_event *fep;
970 mblk_t *tmp;
971
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);
979 }
980
981 return (tmp);
982 }
983
984 /*
985 * Start of dispatching interfaces as a multiplexor
986 */
987
988 /*
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.
992 *
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.
997 *
998 * The id of each msg is taken from stream's mp,
999 * so the id is supposed to be unique.
1000 */
1001 static void
consms_mux_link_msg(consms_msg_t * msg)1002 consms_mux_link_msg(consms_msg_t *msg)
1003 {
1004 mutex_enter(&consms_msg_lock);
1005 msg->msg_next = consms_mux_msg;
1006 consms_mux_msg = msg;
1007 mutex_exit(&consms_msg_lock);
1008 }
1009
1010 static consms_msg_t *
consms_mux_unlink_msg(uint_t msg_id)1011 consms_mux_unlink_msg(uint_t msg_id)
1012 {
1013 consms_msg_t *msg;
1014 consms_msg_t *prev_msg;
1015
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;
1022 }
1023
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;
1029 }
1030 msg->msg_next = NULL;
1031 }
1032 mutex_exit(&consms_msg_lock);
1033
1034 return (msg);
1035 }
1036
1037 static consms_msg_t *
consms_mux_find_msg(uint_t msg_id)1038 consms_mux_find_msg(uint_t msg_id)
1039 {
1040 consms_msg_t *msg;
1041
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;
1046 }
1047 mutex_exit(&consms_msg_lock);
1048
1049 return (msg);
1050 }
1051
1052 /*
1053 * Received ACK or NAK from lower mice
1054 *
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.
1061 *
1062 * As long as one of lower mice responds success,
1063 * we treat it success for a ioctl.
1064 */
1065 static void
consms_mux_ack(consms_msg_t * msg,mblk_t * mp)1066 consms_mux_ack(consms_msg_t *msg, mblk_t *mp)
1067 {
1068 mblk_t *ack_mp;
1069
1070 /* increment response_nums */
1071 msg->msg_num_responses++;
1072
1073 if (mp->b_datap->db_type == M_IOCACK) {
1074 /*
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.
1082 */
1083 ASSERT(msg->msg_rsp_list == NULL);
1084 if (msg->msg_ack_mp == NULL) {
1085 msg->msg_ack_mp = mp;
1086 mp = NULL;
1087 }
1088 }
1089
1090 /*
1091 * Check to see if all lower mice have responded
1092 * to our dispatching ioctl.
1093 */
1094 if (msg->msg_num_responses == msg->msg_num_requests) {
1095 if ((msg->msg_ack_mp == NULL) &&
1096 (msg->msg_rsp_list == NULL)) {
1097 /*
1098 * All are NAKed.
1099 */
1100 ack_mp = mp;
1101 mp = NULL;
1102 } else if (msg->msg_rsp_list == NULL) {
1103 /*
1104 * The last step and at least one ACKed.
1105 */
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 {
1110 /*
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)
1116 *
1117 * Still copyin or copyout.
1118 */
1119 ack_mp = msg->msg_rsp_list->rsp_mp;
1120 consms_mux_max_wheel_report(ack_mp);
1121 }
1122
1123 qreply(msg->msg_queue, ack_mp);
1124
1125 if (msg->msg_rsp_list == NULL) {
1126 /*
1127 * We are done with this ioctl.
1128 */
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));
1133 }
1134 }
1135
1136 if (mp) {
1137 freemsg(mp);
1138 }
1139 }
1140
1141 /*
1142 * Received M_COPYIN or M_COPYOUT request from
1143 * lower mice for transparent ioctl
1144 *
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.
1150 */
1151 static void
consms_mux_copyreq(queue_t * q,consms_msg_t * msg,mblk_t * mp)1152 consms_mux_copyreq(queue_t *q, consms_msg_t *msg, mblk_t *mp)
1153 {
1154 consms_response_t *rsp;
1155
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;
1161 }
1162 msg->msg_rsp_list = rsp;
1163 msg->msg_num_responses++;
1164
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);
1168 }
1169 }
1170
1171 /*
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.
1175 */
1176 static void
consms_mux_disp_iocdata(consms_response_t * rsp,mblk_t * mp)1177 consms_mux_disp_iocdata(consms_response_t *rsp, mblk_t *mp)
1178 {
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;
1182
1183 /*
1184 * Update the rval.
1185 */
1186 newresp->cp_rval = copyresp->cp_rval;
1187
1188 /*
1189 * Update the db_type to M_IOCDATA.
1190 */
1191 down_mp->b_datap->db_type = mp->b_datap->db_type;
1192
1193 /*
1194 * Update the b_cont.
1195 */
1196 if (down_mp->b_cont != NULL) {
1197 freemsg(down_mp->b_cont);
1198 down_mp->b_cont = NULL;
1199 }
1200 if (mp->b_cont != NULL) {
1201 down_mp->b_cont = copymsg(mp->b_cont);
1202 }
1203
1204 /*
1205 * Put it down.
1206 */
1207 (void) putq(WR(rsp->rsp_queue), down_mp);
1208 }
1209
1210 /*
1211 * Dispatch M_IOCDATA down to all lower mice
1212 * for transparent ioctl.
1213 *
1214 * We update each M_COPYIN/M_COPYOUT in the
1215 * msg->msg_rsp_list with the M_IOCDATA.
1216 */
1217 static void
consms_mux_iocdata(consms_msg_t * msg,mblk_t * mp)1218 consms_mux_iocdata(consms_msg_t *msg, mblk_t *mp)
1219 {
1220 consms_response_t *rsp;
1221 consms_response_t *tmp;
1222 consms_response_t *first;
1223 struct copyresp *copyresp;
1224 int request_nums;
1225
1226 ASSERT(msg->msg_rsp_list != NULL);
1227
1228 /*
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.
1233 */
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);
1239 }
1240
1241 /*
1242 * Update request numbers and response numbers.
1243 */
1244 msg->msg_num_requests = msg->msg_num_responses;
1245 msg->msg_num_responses = 0;
1246 request_nums = 1;
1247
1248 /*
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.
1252 */
1253 first = msg->msg_rsp_list;
1254 rsp = first->rsp_next;
1255 msg->msg_rsp_list = NULL;
1256
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++;
1263 }
1264
1265 /* Must set the request number before the last q. */
1266 msg->msg_num_requests = request_nums;
1267
1268 /* the first one */
1269 (void) putq(WR(first->rsp_queue), mp);
1270 kmem_free(first, sizeof (*first));
1271 }
1272
1273
1274 /*
1275 * Here we update the number of wheels with
1276 * the virtual mouse for VUIDGWHEELCOUNT ioctl.
1277 */
1278 static void
consms_mux_max_wheel_report(mblk_t * mp)1279 consms_mux_max_wheel_report(mblk_t *mp)
1280 {
1281 struct iocblk *iocp;
1282 int num_wheels;
1283
1284 if (mp == NULL || mp->b_cont == NULL)
1285 return;
1286
1287 iocp = (struct iocblk *)mp->b_rptr;
1288
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;
1295 }
1296 }
1297 }
1298
1299 /*
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.
1305 */
1306 static void
consms_mux_cache_states(mblk_t * mp)1307 consms_mux_cache_states(mblk_t *mp)
1308 {
1309 struct iocblk *iocp;
1310 Ms_parms *parms;
1311 Ms_screen_resolution *sr;
1312 wheel_state *ws;
1313
1314 if (mp == NULL || mp->b_cont == NULL)
1315 return;
1316
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;
1322
1323 case MSIOSETPARMS:
1324 parms = (Ms_parms *)mp->b_cont->b_rptr;
1325 consms_state.consms_ms_parms = *parms;
1326 break;
1327
1328 case MSIOSRESOLUTION:
1329 sr = (Ms_screen_resolution *)mp->b_cont->b_rptr;
1330 consms_state.consms_ms_sr = *sr;
1331 break;
1332
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;
1339 }
1340 }
1341
1342 /*
1343 * Dispatch ioctl mp (non-transparent and transparent)
1344 * down to all lower mice.
1345 *
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.
1350 */
1351 static int
consms_mux_disp_ioctl(queue_t * q,mblk_t * mp)1352 consms_mux_disp_ioctl(queue_t *q, mblk_t *mp)
1353 {
1354 struct iocblk *iocp;
1355 consms_msg_t *msg;
1356 consms_lq_t *lq;
1357 mblk_t *copy_mp;
1358 int error = 0;
1359
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);
1367
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 {
1372 /*
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.
1378 */
1379 msg->msg_num_requests--;
1380 }
1381 }
1382
1383 if (msg->msg_num_requests <= 0) {
1384 /*
1385 * Since copymsg fails for all lqs, we NAK this ioctl.
1386 */
1387 (void) consms_mux_unlink_msg(msg->msg_id);
1388 kmem_free(msg, sizeof (*msg));
1389 error = ENOMEM;
1390 }
1391
1392 return (error);
1393 }
1394
1395 /*
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.
1400 */
1401 static void
consms_mux_disp_data(mblk_t * mp)1402 consms_mux_disp_data(mblk_t *mp)
1403 {
1404 consms_lq_t *lq;
1405 mblk_t *copy_mp;
1406
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);
1410 }
1411 }
1412
1413 freemsg(mp);
1414 }
1415