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(queue_t *, dev_t *, int, int, cred_t *);
85 static int consmsclose(queue_t *, int, cred_t *);
86 static int consmsuwput(queue_t *, mblk_t *);
87 static int consmslrput(queue_t *, mblk_t *);
88 static int consmslwserv(queue_t *);
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 consmsuwput,
111 (int (*)())NULL,
112 consmsopen,
113 consmsclose,
114 (int (*)())NULL,
115 &consmsm_info,
116 NULL
117 };
118
119 static struct qinit consmslrinit = {
120 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 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, 0) == 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 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(queue_t * q,dev_t * devp,int flag,int sflag,cred_t * crp)353 consmsopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
354 {
355 upperqueue = q;
356 qprocson(q);
357 return (0);
358 }
359
360 /*ARGSUSED*/
361 static int
consmsclose(queue_t * q,int flag,cred_t * crp)362 consmsclose(queue_t *q, int flag, cred_t *crp)
363 {
364 qprocsoff(q);
365 upperqueue = NULL;
366 return (0);
367 }
368
369 /*
370 * Put procedure for upper write queue.
371 */
372 static int
consmsuwput(queue_t * q,mblk_t * mp)373 consmsuwput(queue_t *q, mblk_t *mp)
374 {
375 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
376 consms_msg_t *msg;
377 int error = 0;
378
379 switch (mp->b_datap->db_type) {
380
381 case M_IOCTL:
382 consmsioctl(q, mp);
383 break;
384
385 case M_FLUSH:
386 if (*mp->b_rptr & FLUSHW)
387 flushq(q, FLUSHDATA);
388 if (*mp->b_rptr & FLUSHR)
389 flushq(RD(q), FLUSHDATA);
390 if (consms_state.consms_num_lqs > 0) {
391 consms_mux_disp_data(mp);
392 } else {
393 /*
394 * No lower queue; just reflect this back upstream.
395 */
396 *mp->b_rptr &= ~FLUSHW;
397 if (*mp->b_rptr & FLUSHR)
398 qreply(q, mp);
399 else
400 freemsg(mp);
401 }
402 break;
403
404 case M_DATA:
405 if (consms_state.consms_num_lqs > 0) {
406 consms_mux_disp_data(mp);
407 } else {
408 freemsg(mp);
409 }
410 break;
411
412 case M_IOCDATA:
413 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
414 consms_mux_iocdata(msg, mp);
415 } else {
416 error = EINVAL;
417 }
418 break;
419
420 default:
421 error = EINVAL;
422 break;
423 }
424
425 if (error) {
426 /*
427 * Pass an error message up.
428 */
429 mp->b_datap->db_type = M_ERROR;
430 if (mp->b_cont) {
431 freemsg(mp->b_cont);
432 mp->b_cont = NULL;
433 }
434 mp->b_rptr = mp->b_datap->db_base;
435 mp->b_wptr = mp->b_rptr + sizeof (char);
436 *mp->b_rptr = (char)error;
437 qreply(q, mp);
438 }
439 return (0);
440 }
441
442 static void
consmsioctl(queue_t * q,mblk_t * mp)443 consmsioctl(queue_t *q, mblk_t *mp)
444 {
445 struct iocblk *iocp;
446 int error;
447 mblk_t *datap;
448
449 iocp = (struct iocblk *)mp->b_rptr;
450
451 switch (iocp->ioc_cmd) {
452
453 case I_LINK:
454 case I_PLINK:
455 mutex_enter(&consmslock);
456 consms_plink(q, mp);
457 mutex_exit(&consmslock);
458 return;
459
460 case I_UNLINK:
461 case I_PUNLINK:
462 mutex_enter(&consmslock);
463 if ((error = consms_punlink(q, mp)) != 0) {
464 mutex_exit(&consmslock);
465 miocnak(q, mp, 0, error);
466 return;
467 }
468 mutex_exit(&consmslock);
469 iocp->ioc_count = 0;
470 break;
471
472 case MSIOBUTTONS: /* query the number of buttons */
473 if ((consms_state.consms_num_lqs <= 0) ||
474 ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)) {
475 miocnak(q, mp, 0, ENOMEM);
476 return;
477 }
478 *(int *)datap->b_wptr = consms_state.consms_num_buttons;
479 datap->b_wptr += sizeof (int);
480 if (mp->b_cont) {
481 freemsg(mp->b_cont);
482 }
483 mp->b_cont = datap;
484 iocp->ioc_count = sizeof (int);
485 break;
486
487 default:
488 /*
489 * Pass this through, if there's something to pass it
490 * through to; otherwise, reject it.
491 */
492 if (consms_state.consms_num_lqs <= 0) {
493 miocnak(q, mp, 0, EINVAL);
494 return;
495 }
496 if ((error = consms_mux_disp_ioctl(q, mp)) != 0)
497 miocnak(q, mp, 0, error);
498
499 return;
500 }
501
502 /*
503 * Common exit path for calls that return a positive
504 * acknowledgment with a return value of 0.
505 */
506 miocack(q, mp, iocp->ioc_count, 0);
507 }
508
509 /*
510 * Service procedure for lower write queue.
511 * Puts things on the queue below us, if it lets us.
512 */
513 static int
consmslwserv(queue_t * q)514 consmslwserv(queue_t *q)
515 {
516 mblk_t *mp;
517
518 while (canput(q->q_next) && (mp = getq(q)) != NULL)
519 putnext(q, mp);
520 return (0);
521 }
522
523 /*
524 * Put procedure for lower read queue.
525 */
526 static int
consmslrput(queue_t * q,mblk_t * mp)527 consmslrput(queue_t *q, mblk_t *mp)
528 {
529 struct iocblk *iocbp = (struct iocblk *)mp->b_rptr;
530 struct copyreq *copyreq = (struct copyreq *)mp->b_rptr;
531 consms_msg_t *msg;
532 consms_lq_t *lq = (consms_lq_t *)q->q_ptr;
533
534 ASSERT(lq != NULL);
535
536 switch (mp->b_datap->db_type) {
537 case M_FLUSH:
538 if (*mp->b_rptr & FLUSHW)
539 flushq(WR(q), FLUSHDATA);
540 if (*mp->b_rptr & FLUSHR)
541 flushq(q, FLUSHDATA);
542 if (upperqueue != NULL)
543 putnext(upperqueue, mp); /* pass it through */
544 else {
545 /*
546 * No upper queue; just reflect this back downstream.
547 */
548 *mp->b_rptr &= ~FLUSHR;
549 if (*mp->b_rptr & FLUSHW)
550 qreply(q, mp);
551 else
552 freemsg(mp);
553 }
554 break;
555
556 case M_DATA:
557 if (upperqueue != NULL)
558 putnext(upperqueue, mp);
559 else
560 freemsg(mp);
561 consms_idle_stamp = gethrestime_sec();
562 break;
563
564 case M_IOCACK:
565 case M_IOCNAK:
566 /*
567 * First, check to see if this device
568 * is still being initialized.
569 */
570 if (lq->lq_ioc_reply_func != NULL) {
571 mutex_enter(&consmslock);
572 lq->lq_ioc_reply_func(lq, mp);
573 mutex_exit(&consmslock);
574 freemsg(mp);
575 break;
576 }
577
578 /*
579 * This is normal ioctl ack for upper layer.
580 */
581 if ((msg = consms_mux_find_msg(iocbp->ioc_id)) != NULL) {
582 consms_mux_ack(msg, mp);
583 } else {
584 freemsg(mp);
585 }
586 consms_idle_stamp = gethrestime_sec();
587 break;
588
589 case M_COPYIN:
590 case M_COPYOUT:
591 if ((msg = consms_mux_find_msg(copyreq->cq_id)) != NULL) {
592 consms_mux_copyreq(q, msg, mp);
593 } else
594 freemsg(mp);
595 consms_idle_stamp = gethrestime_sec();
596 break;
597
598 case M_ERROR:
599 case M_HANGUP:
600 default:
601 freemsg(mp); /* anything useful here? */
602 break;
603 }
604 return (0);
605 }
606
607 /* ARGSUSED */
608 static int
consms_kstat_update(kstat_t * ksp,int rw)609 consms_kstat_update(kstat_t *ksp, int rw)
610 {
611 if (rw == KSTAT_WRITE)
612 return (EACCES);
613
614 consms_kstat.idle_sec.value.l = gethrestime_sec() - consms_idle_stamp;
615 return (0);
616 }
617
618 /*ARGSUSED*/
619 static int
consms_punlink(queue_t * q,mblk_t * mp)620 consms_punlink(queue_t *q, mblk_t *mp)
621 {
622 struct linkblk *linkp;
623 consms_lq_t *lq;
624 consms_lq_t *prev_lq;
625
626 ASSERT(MUTEX_HELD(&consmslock));
627
628 linkp = (struct linkblk *)mp->b_cont->b_rptr;
629
630 prev_lq = NULL;
631 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
632 if (lq->lq_queue == linkp->l_qbot) {
633 if (prev_lq)
634 prev_lq->lq_next = lq->lq_next;
635 else
636 consms_state.consms_lqs = lq->lq_next;
637 kmem_free(lq, sizeof (*lq));
638 consms_state.consms_num_lqs--;
639
640 /*
641 * Check to see if mouse capabilities
642 * have changed.
643 */
644 consms_check_caps();
645
646 return (0);
647 }
648 prev_lq = lq;
649 }
650
651 return (EINVAL);
652 }
653
654 /*
655 * Link a specific mouse into our mouse list.
656 */
657 static void
consms_plink(queue_t * q,mblk_t * mp)658 consms_plink(queue_t *q, mblk_t *mp)
659 {
660 struct linkblk *linkp;
661 consms_lq_t *lq;
662 queue_t *lowq;
663
664 ASSERT(MUTEX_HELD(&consmslock));
665
666 linkp = (struct linkblk *)mp->b_cont->b_rptr;
667 lowq = linkp->l_qbot;
668
669 lq = kmem_zalloc(sizeof (*lq), KM_SLEEP);
670
671 lowq->q_ptr = (void *)lq;
672 OTHERQ(lowq)->q_ptr = (void *)lq;
673 lq->lq_queue = lowq;
674 lq->lq_pending_plink = mp;
675 lq->lq_pending_queue = q;
676
677 /*
678 * Set the number of buttons to 3 by default
679 * in case the following MSIOBUTTONS ioctl fails.
680 */
681 lq->lq_num_buttons = 3;
682
683 /*
684 * Begin to initialize this mouse.
685 */
686 lq->lq_state = LQS_START;
687 consms_lqs_ack_complete(lq, NULL);
688 }
689
690 /*
691 * Initialize the newly hotplugged-in mouse,
692 * e.g. get the number of buttons, set event
693 * format. Then we add it into our list.
694 */
695 static void
consms_lqs_ack_complete(consms_lq_t * lq,mblk_t * mp)696 consms_lqs_ack_complete(consms_lq_t *lq, mblk_t *mp)
697 {
698 mblk_t *req = NULL;
699 boolean_t skipped = B_FALSE;
700 wheel_state *ws;
701 Ms_screen_resolution *sr;
702 Ms_parms *params;
703
704 ASSERT(MUTEX_HELD(&consmslock));
705
706 /*
707 * We try each ioctl even if the previous one fails
708 * until we reach LQS_DONE, and then add this lq
709 * into our lq list.
710 *
711 * If the message allocation fails, we skip this ioctl,
712 * set skipped flag to B_TRUE in order to skip the ioctl
713 * result, then we try next ioctl, go to next state.
714 */
715 while ((lq->lq_state < LQS_DONE) && (req == NULL)) {
716 switch (lq->lq_state) {
717 case LQS_START:
718 /*
719 * First, issue MSIOBUTTONS ioctl
720 * to get the number of buttons.
721 */
722 req = mkiocb(MSIOBUTTONS);
723 if (req && ((req->b_cont = allocb(sizeof (int),
724 BPRI_MED)) == NULL)) {
725 freemsg(req);
726 req = NULL;
727 }
728 if (req == NULL)
729 skipped = B_TRUE;
730 lq->lq_state++;
731 break;
732
733 case LQS_BUTTON_COUNT_PENDING:
734 if (!skipped && mp && mp->b_cont &&
735 (mp->b_datap->db_type == M_IOCACK))
736 lq->lq_num_buttons =
737 *(int *)mp->b_cont->b_rptr;
738
739 /*
740 * Second, issue VUIDGWHEELCOUNT ioctl
741 * to get the count of wheels.
742 */
743 req = mkiocb(VUIDGWHEELCOUNT);
744 if (req && ((req->b_cont = allocb(sizeof (int),
745 BPRI_MED)) == NULL)) {
746 freemsg(req);
747 req = NULL;
748 }
749 if (req == NULL)
750 skipped = B_TRUE;
751 lq->lq_state++;
752 break;
753
754 case LQS_WHEEL_COUNT_PENDING:
755 if (!skipped && mp && mp->b_cont &&
756 (mp->b_datap->db_type == M_IOCACK))
757 lq->lq_num_wheels =
758 *(int *)mp->b_cont->b_rptr;
759
760 /*
761 * Third, issue VUIDSFORMAT ioctl
762 * to set the event format.
763 */
764 req = mkiocb(VUIDSFORMAT);
765 if (req && ((req->b_cont = allocb(sizeof (int),
766 BPRI_MED)) == NULL)) {
767 freemsg(req);
768 req = NULL;
769 }
770 if (req) {
771 *(int *)req->b_cont->b_wptr =
772 consms_state.consms_vuid_format;
773 req->b_cont->b_wptr += sizeof (int);
774 }
775 lq->lq_state++;
776 break;
777
778 case LQS_SET_VUID_FORMAT_PENDING:
779 /*
780 * Fourth, issue VUIDSWHEELSTATE ioctl
781 * to set the wheel state (enable or disable).
782 */
783 req = mkiocb(VUIDSWHEELSTATE);
784 if (req && ((req->b_cont = allocb(sizeof (wheel_state),
785 BPRI_MED)) == NULL)) {
786 freemsg(req);
787 req = NULL;
788 }
789 if (req) {
790 ws = (wheel_state *)req->b_cont->b_wptr;
791 ws->vers = VUID_WHEEL_STATE_VERS;
792 ws->id = 0; /* the first wheel */
793 ws->stateflags =
794 consms_state.consms_wheel_state_bf & 1;
795 req->b_cont->b_wptr += sizeof (wheel_state);
796 }
797 lq->lq_state++;
798 break;
799
800 case LQS_SET_WHEEL_STATE_PENDING:
801 /*
802 * Fifth, issue MSIOSETPARMS ioctl
803 * to set the parameters for USB mouse.
804 */
805 req = mkiocb(MSIOSETPARMS);
806 if (req && ((req->b_cont = allocb(sizeof (Ms_parms),
807 BPRI_MED)) == NULL)) {
808 freemsg(req);
809 req = NULL;
810 }
811 if (req) {
812 params = (Ms_parms *)req->b_cont->b_wptr;
813 *params = consms_state.consms_ms_parms;
814 req->b_cont->b_wptr += sizeof (Ms_parms);
815 }
816 lq->lq_state++;
817 break;
818
819 case LQS_SET_PARMS_PENDING:
820 /*
821 * Sixth, issue MSIOSRESOLUTION ioctl
822 * to set the screen resolution for absolute mouse.
823 */
824 req = mkiocb(MSIOSRESOLUTION);
825 if (req && ((req->b_cont =
826 allocb(sizeof (Ms_screen_resolution),
827 BPRI_MED)) == NULL)) {
828 freemsg(req);
829 req = NULL;
830 }
831 if (req) {
832 sr =
833 (Ms_screen_resolution *)req->b_cont->b_wptr;
834 *sr = consms_state.consms_ms_sr;
835 req->b_cont->b_wptr +=
836 sizeof (Ms_screen_resolution);
837 }
838 lq->lq_state++;
839 break;
840
841 case LQS_SET_RESOLUTION_PENDING:
842 /*
843 * All jobs are done, lq->lq_state is turned into
844 * LQS_DONE, and this lq is added into our list.
845 */
846 lq->lq_state++;
847 consms_add_lq(lq);
848 break;
849 }
850 }
851
852 if (lq->lq_state < LQS_DONE) {
853 lq->lq_ioc_reply_func = consms_lqs_ack_complete;
854 (void) putq(lq->lq_queue, req);
855 }
856 }
857
858 /*
859 * Add this specific lq into our list, finally reply
860 * the previous pending I_PLINK ioctl. Also check to
861 * see if mouse capabilities have changed, and send
862 * a dynamical notification event to upper layer if
863 * necessary.
864 */
865 static void
consms_add_lq(consms_lq_t * lq)866 consms_add_lq(consms_lq_t *lq)
867 {
868 struct iocblk *iocp;
869
870 ASSERT(MUTEX_HELD(&consmslock));
871
872 lq->lq_ioc_reply_func = NULL;
873 iocp = (struct iocblk *)lq->lq_pending_plink->b_rptr;
874 iocp->ioc_error = 0;
875 iocp->ioc_count = 0;
876 iocp->ioc_rval = 0;
877 lq->lq_pending_plink->b_datap->db_type = M_IOCACK;
878
879 /* Reply to the I_PLINK ioctl. */
880 qreply(lq->lq_pending_queue, lq->lq_pending_plink);
881
882 lq->lq_pending_plink = NULL;
883 lq->lq_pending_queue = NULL;
884
885 /*
886 * Add this lq into list.
887 */
888 consms_state.consms_num_lqs++;
889
890 lq->lq_next = consms_state.consms_lqs;
891 consms_state.consms_lqs = lq;
892
893 /*
894 * Check to see if mouse capabilities
895 * have changed.
896 */
897 consms_check_caps();
898
899 }
900
901
902 static void
consms_check_caps(void)903 consms_check_caps(void)
904 {
905 consms_lq_t *lq;
906 int max_buttons = 0;
907 int max_wheels = 0;
908 mblk_t *mp;
909
910 /*
911 * Check to see if the number of buttons
912 * and the number of wheels have changed.
913 */
914 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
915 max_buttons = CONSMS_MAX(max_buttons, lq->lq_num_buttons);
916 max_wheels = CONSMS_MAX(max_wheels, lq->lq_num_wheels);
917 }
918
919 if (max_buttons != consms_state.consms_num_buttons) {
920 /*
921 * Since the number of buttons have changed,
922 * send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
923 * notification event to upper layer.
924 */
925 consms_state.consms_num_buttons = max_buttons;
926 if (upperqueue != NULL) {
927 if ((mp = consms_new_firm_event(
928 MOUSE_CAP_CHANGE_NUM_BUT,
929 consms_state.consms_num_buttons)) != NULL) {
930 putnext(upperqueue, mp);
931 }
932 }
933 }
934
935 if (max_wheels != consms_state.consms_num_wheels) {
936 /*
937 * Since the number of wheels have changed,
938 * send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
939 * notification event to upper layer.
940 */
941 consms_state.consms_num_wheels = max_wheels;
942 if (upperqueue != NULL) {
943 if ((mp = consms_new_firm_event(
944 MOUSE_CAP_CHANGE_NUM_WHEEL,
945 consms_state.consms_num_wheels)) != NULL) {
946 putnext(upperqueue, mp);
947 }
948 }
949 }
950 }
951
952 /*
953 * Allocate a dynamical notification event.
954 */
955 static mblk_t *
consms_new_firm_event(ushort_t id,int value)956 consms_new_firm_event(ushort_t id, int value)
957 {
958 Firm_event *fep;
959 mblk_t *tmp;
960
961 if ((tmp = allocb(sizeof (Firm_event), BPRI_HI)) != NULL) {
962 fep = (Firm_event *)tmp->b_wptr;
963 fep->id = id;
964 fep->pair_type = FE_PAIR_NONE;
965 fep->pair = '\0';
966 fep->value = value;
967 tmp->b_wptr += sizeof (Firm_event);
968 }
969
970 return (tmp);
971 }
972
973 /*
974 * Start of dispatching interfaces as a multiplexor
975 */
976
977 /*
978 * There is a global msg list (consms_mux_msg),
979 * which is used to link all ioctl messages from
980 * upper layer, which are currently being processed.
981 *
982 * consms_mux_link_msg links a msg into the list,
983 * consms_mux_unlink_msg unlinks a msg from the list,
984 * consms_mux_find_msg finds a msg from the list
985 * according to its unique id.
986 *
987 * The id of each msg is taken from stream's mp,
988 * so the id is supposed to be unique.
989 */
990 static void
consms_mux_link_msg(consms_msg_t * msg)991 consms_mux_link_msg(consms_msg_t *msg)
992 {
993 mutex_enter(&consms_msg_lock);
994 msg->msg_next = consms_mux_msg;
995 consms_mux_msg = msg;
996 mutex_exit(&consms_msg_lock);
997 }
998
999 static consms_msg_t *
consms_mux_unlink_msg(uint_t msg_id)1000 consms_mux_unlink_msg(uint_t msg_id)
1001 {
1002 consms_msg_t *msg;
1003 consms_msg_t *prev_msg;
1004
1005 mutex_enter(&consms_msg_lock);
1006 prev_msg = NULL;
1007 for (msg = consms_mux_msg; msg != NULL;
1008 prev_msg = msg, msg = msg->msg_next) {
1009 if (msg->msg_id == msg_id)
1010 break;
1011 }
1012
1013 if (msg != NULL) {
1014 if (prev_msg != NULL) {
1015 prev_msg->msg_next = msg->msg_next;
1016 } else {
1017 consms_mux_msg = consms_mux_msg->msg_next;
1018 }
1019 msg->msg_next = NULL;
1020 }
1021 mutex_exit(&consms_msg_lock);
1022
1023 return (msg);
1024 }
1025
1026 static consms_msg_t *
consms_mux_find_msg(uint_t msg_id)1027 consms_mux_find_msg(uint_t msg_id)
1028 {
1029 consms_msg_t *msg;
1030
1031 mutex_enter(&consms_msg_lock);
1032 for (msg = consms_mux_msg; msg != NULL; msg = msg->msg_next) {
1033 if (msg->msg_id == msg_id)
1034 break;
1035 }
1036 mutex_exit(&consms_msg_lock);
1037
1038 return (msg);
1039 }
1040
1041 /*
1042 * Received ACK or NAK from lower mice
1043 *
1044 * For non-transparent ioctl, the msg->msg_rsp_list
1045 * is always NULL; for transparent ioctl, it
1046 * remembers the M_COPYIN/M_COPYOUT request
1047 * messages from lower mice. So here if msg->msg_rsp_list
1048 * is NULL (after receiving all ACK/NAKs), we
1049 * are done with this specific ioctl.
1050 *
1051 * As long as one of lower mice responds success,
1052 * we treat it success for a ioctl.
1053 */
1054 static void
consms_mux_ack(consms_msg_t * msg,mblk_t * mp)1055 consms_mux_ack(consms_msg_t *msg, mblk_t *mp)
1056 {
1057 mblk_t *ack_mp;
1058
1059 /* increment response_nums */
1060 msg->msg_num_responses++;
1061
1062 if (mp->b_datap->db_type == M_IOCACK) {
1063 /*
1064 * Received ACK from lower, then
1065 * this is the last step for both
1066 * non-transparent and transparent
1067 * ioctl. We only need to remember
1068 * one of the ACKs, finally reply
1069 * this ACK to upper layer for this
1070 * specific ioctl.
1071 */
1072 ASSERT(msg->msg_rsp_list == NULL);
1073 if (msg->msg_ack_mp == NULL) {
1074 msg->msg_ack_mp = mp;
1075 mp = NULL;
1076 }
1077 }
1078
1079 /*
1080 * Check to see if all lower mice have responded
1081 * to our dispatching ioctl.
1082 */
1083 if (msg->msg_num_responses == msg->msg_num_requests) {
1084 if ((msg->msg_ack_mp == NULL) &&
1085 (msg->msg_rsp_list == NULL)) {
1086 /*
1087 * All are NAKed.
1088 */
1089 ack_mp = mp;
1090 mp = NULL;
1091 } else if (msg->msg_rsp_list == NULL) {
1092 /*
1093 * The last step and at least one ACKed.
1094 */
1095 ack_mp = msg->msg_ack_mp;
1096 consms_mux_cache_states(msg->msg_request);
1097 consms_mux_max_wheel_report(ack_mp);
1098 } else {
1099 /*
1100 * This is a NAK, but we have
1101 * already received M_COPYIN
1102 * or M_COPYOUT request from
1103 * at least one of lower mice.
1104 * (msg->msg_rsp_list != NULL)
1105 *
1106 * Still copyin or copyout.
1107 */
1108 ack_mp = msg->msg_rsp_list->rsp_mp;
1109 consms_mux_max_wheel_report(ack_mp);
1110 }
1111
1112 qreply(msg->msg_queue, ack_mp);
1113
1114 if (msg->msg_rsp_list == NULL) {
1115 /*
1116 * We are done with this ioctl.
1117 */
1118 if (msg->msg_request)
1119 freemsg(msg->msg_request);
1120 (void) consms_mux_unlink_msg(msg->msg_id);
1121 kmem_free(msg, sizeof (*msg));
1122 }
1123 }
1124
1125 if (mp) {
1126 freemsg(mp);
1127 }
1128 }
1129
1130 /*
1131 * Received M_COPYIN or M_COPYOUT request from
1132 * lower mice for transparent ioctl
1133 *
1134 * We remember each M_COPYIN/M_COPYOUT into the
1135 * msg->msg_rsp_list, reply upper layer using the first
1136 * M_COPYIN/M_COPYOUT in the list after receiving
1137 * all responses from lower mice, even if some of
1138 * them return NAKs.
1139 */
1140 static void
consms_mux_copyreq(queue_t * q,consms_msg_t * msg,mblk_t * mp)1141 consms_mux_copyreq(queue_t *q, consms_msg_t *msg, mblk_t *mp)
1142 {
1143 consms_response_t *rsp;
1144
1145 rsp = (consms_response_t *)kmem_zalloc(sizeof (*rsp), KM_SLEEP);
1146 rsp->rsp_mp = mp;
1147 rsp->rsp_queue = q;
1148 if (msg->msg_rsp_list) {
1149 rsp->rsp_next = msg->msg_rsp_list;
1150 }
1151 msg->msg_rsp_list = rsp;
1152 msg->msg_num_responses++;
1153
1154 if (msg->msg_num_responses == msg->msg_num_requests) {
1155 consms_mux_max_wheel_report(msg->msg_rsp_list->rsp_mp);
1156 qreply(msg->msg_queue, msg->msg_rsp_list->rsp_mp);
1157 }
1158 }
1159
1160 /*
1161 * Do the real job for updating M_COPYIN/M_COPYOUT
1162 * request with the mp of M_IOCDATA, then put it
1163 * down to lower mice.
1164 */
1165 static void
consms_mux_disp_iocdata(consms_response_t * rsp,mblk_t * mp)1166 consms_mux_disp_iocdata(consms_response_t *rsp, mblk_t *mp)
1167 {
1168 mblk_t *down_mp = rsp->rsp_mp;
1169 struct copyresp *copyresp = (struct copyresp *)mp->b_rptr;
1170 struct copyresp *newresp = (struct copyresp *)down_mp->b_rptr;
1171
1172 /*
1173 * Update the rval.
1174 */
1175 newresp->cp_rval = copyresp->cp_rval;
1176
1177 /*
1178 * Update the db_type to M_IOCDATA.
1179 */
1180 down_mp->b_datap->db_type = mp->b_datap->db_type;
1181
1182 /*
1183 * Update the b_cont.
1184 */
1185 if (down_mp->b_cont != NULL) {
1186 freemsg(down_mp->b_cont);
1187 down_mp->b_cont = NULL;
1188 }
1189 if (mp->b_cont != NULL) {
1190 down_mp->b_cont = copymsg(mp->b_cont);
1191 }
1192
1193 /*
1194 * Put it down.
1195 */
1196 (void) putq(WR(rsp->rsp_queue), down_mp);
1197 }
1198
1199 /*
1200 * Dispatch M_IOCDATA down to all lower mice
1201 * for transparent ioctl.
1202 *
1203 * We update each M_COPYIN/M_COPYOUT in the
1204 * msg->msg_rsp_list with the M_IOCDATA.
1205 */
1206 static void
consms_mux_iocdata(consms_msg_t * msg,mblk_t * mp)1207 consms_mux_iocdata(consms_msg_t *msg, mblk_t *mp)
1208 {
1209 consms_response_t *rsp;
1210 consms_response_t *tmp;
1211 consms_response_t *first;
1212 struct copyresp *copyresp;
1213 int request_nums;
1214
1215 ASSERT(msg->msg_rsp_list != NULL);
1216
1217 /*
1218 * We should remember the ioc data for
1219 * VUIDSWHEELSTATE, and MSIOSRESOLUTION,
1220 * for we will cache the wheel state and
1221 * the screen resolution later if ACKed.
1222 */
1223 copyresp = (struct copyresp *)mp->b_rptr;
1224 if ((copyresp->cp_cmd == VUIDSWHEELSTATE) ||
1225 (copyresp->cp_cmd == MSIOSRESOLUTION)) {
1226 freemsg(msg->msg_request);
1227 msg->msg_request = copymsg(mp);
1228 }
1229
1230 /*
1231 * Update request numbers and response numbers.
1232 */
1233 msg->msg_num_requests = msg->msg_num_responses;
1234 msg->msg_num_responses = 0;
1235 request_nums = 1;
1236
1237 /*
1238 * Since we have use the first M_COPYIN/M_COPYOUT
1239 * in the msg_rsp_list to reply upper layer, the mp
1240 * of M_IOCDATA can be directly used for that.
1241 */
1242 first = msg->msg_rsp_list;
1243 rsp = first->rsp_next;
1244 msg->msg_rsp_list = NULL;
1245
1246 for (rsp = first->rsp_next; rsp != NULL; ) {
1247 tmp = rsp;
1248 rsp = rsp->rsp_next;
1249 consms_mux_disp_iocdata(tmp, mp);
1250 kmem_free(tmp, sizeof (*tmp));
1251 request_nums++;
1252 }
1253
1254 /* Must set the request number before the last q. */
1255 msg->msg_num_requests = request_nums;
1256
1257 /* the first one */
1258 (void) putq(WR(first->rsp_queue), mp);
1259 kmem_free(first, sizeof (*first));
1260 }
1261
1262
1263 /*
1264 * Here we update the number of wheels with
1265 * the virtual mouse for VUIDGWHEELCOUNT ioctl.
1266 */
1267 static void
consms_mux_max_wheel_report(mblk_t * mp)1268 consms_mux_max_wheel_report(mblk_t *mp)
1269 {
1270 struct iocblk *iocp;
1271 int num_wheels;
1272
1273 if (mp == NULL || mp->b_cont == NULL)
1274 return;
1275
1276 iocp = (struct iocblk *)mp->b_rptr;
1277
1278 if ((iocp->ioc_cmd == VUIDGWHEELCOUNT) &&
1279 (mp->b_datap->db_type == M_COPYOUT)) {
1280 num_wheels = *(int *)mp->b_cont->b_rptr;
1281 if (num_wheels < consms_state.consms_num_wheels) {
1282 *(int *)mp->b_cont->b_rptr =
1283 consms_state.consms_num_wheels;
1284 }
1285 }
1286 }
1287
1288 /*
1289 * Update the virtual mouse state variables with
1290 * the latest value from upper layer when these
1291 * set ioctls return success. Thus we can update
1292 * low mice with the latest state values during
1293 * hotplug.
1294 */
1295 static void
consms_mux_cache_states(mblk_t * mp)1296 consms_mux_cache_states(mblk_t *mp)
1297 {
1298 struct iocblk *iocp;
1299 Ms_parms *parms;
1300 Ms_screen_resolution *sr;
1301 wheel_state *ws;
1302
1303 if (mp == NULL || mp->b_cont == NULL)
1304 return;
1305
1306 iocp = (struct iocblk *)mp->b_rptr;
1307 switch (iocp->ioc_cmd) {
1308 case VUIDSFORMAT:
1309 consms_state.consms_vuid_format = *(int *)mp->b_cont->b_rptr;
1310 break;
1311
1312 case MSIOSETPARMS:
1313 parms = (Ms_parms *)mp->b_cont->b_rptr;
1314 consms_state.consms_ms_parms = *parms;
1315 break;
1316
1317 case MSIOSRESOLUTION:
1318 sr = (Ms_screen_resolution *)mp->b_cont->b_rptr;
1319 consms_state.consms_ms_sr = *sr;
1320 break;
1321
1322 case VUIDSWHEELSTATE:
1323 ws = (wheel_state *)mp->b_cont->b_rptr;
1324 consms_state.consms_wheel_state_bf =
1325 (ws->stateflags << ws->id) |
1326 (consms_state.consms_wheel_state_bf & ~(1 << ws->id));
1327 break;
1328 }
1329 }
1330
1331 /*
1332 * Dispatch ioctl mp (non-transparent and transparent)
1333 * down to all lower mice.
1334 *
1335 * First, create a pending message for this mp, link it into
1336 * the global messages list. Then wait for ACK/NAK for
1337 * non-transparent ioctl, COPYIN/COPYOUT for transparent
1338 * ioctl.
1339 */
1340 static int
consms_mux_disp_ioctl(queue_t * q,mblk_t * mp)1341 consms_mux_disp_ioctl(queue_t *q, mblk_t *mp)
1342 {
1343 struct iocblk *iocp;
1344 consms_msg_t *msg;
1345 consms_lq_t *lq;
1346 mblk_t *copy_mp;
1347 int error = 0;
1348
1349 iocp = (struct iocblk *)mp->b_rptr;
1350 msg = (consms_msg_t *)kmem_zalloc(sizeof (*msg), KM_SLEEP);
1351 msg->msg_id = iocp->ioc_id;
1352 msg->msg_request = mp;
1353 msg->msg_queue = q;
1354 msg->msg_num_requests = consms_state.consms_num_lqs;
1355 consms_mux_link_msg(msg);
1356
1357 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1358 if ((copy_mp = copymsg(mp)) != NULL) {
1359 (void) putq(lq->lq_queue, copy_mp);
1360 } else {
1361 /*
1362 * If copymsg fails, we ignore this lq and
1363 * try next one. As long as one of them succeeds,
1364 * we dispatch this ioctl down. And later as long
1365 * as one of the lower drivers return success, we
1366 * reply to this ioctl with success.
1367 */
1368 msg->msg_num_requests--;
1369 }
1370 }
1371
1372 if (msg->msg_num_requests <= 0) {
1373 /*
1374 * Since copymsg fails for all lqs, we NAK this ioctl.
1375 */
1376 (void) consms_mux_unlink_msg(msg->msg_id);
1377 kmem_free(msg, sizeof (*msg));
1378 error = ENOMEM;
1379 }
1380
1381 return (error);
1382 }
1383
1384 /*
1385 * Dispatch M_DATA and M_FLUSH message down to all
1386 * lower mice, and there are no acknowledgements
1387 * for them. Here we just copy the mp and then
1388 * put it into the lower queues.
1389 */
1390 static void
consms_mux_disp_data(mblk_t * mp)1391 consms_mux_disp_data(mblk_t *mp)
1392 {
1393 consms_lq_t *lq;
1394 mblk_t *copy_mp;
1395
1396 for (lq = consms_state.consms_lqs; lq != NULL; lq = lq->lq_next) {
1397 if ((copy_mp = copymsg(mp)) != NULL) {
1398 (void) putq(lq->lq_queue, copy_mp);
1399 }
1400 }
1401
1402 freemsg(mp);
1403 }
1404