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