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