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