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 /* 30 * VUIDMICE module: put mouse events into vuid format 31 */ 32 33 #include <sys/param.h> 34 #include <sys/stream.h> 35 #include <sys/stropts.h> 36 #include <sys/strsun.h> 37 #include <sys/errno.h> 38 #include <sys/debug.h> 39 #include <sys/cmn_err.h> 40 #include <sys/sad.h> 41 #include <sys/vuid_event.h> 42 #include <sys/vuidmice.h> 43 #include <sys/vuid_wheel.h> 44 #include <sys/msio.h> 45 46 #include <sys/conf.h> 47 #include <sys/modctl.h> 48 49 #include <sys/kmem.h> 50 #include <sys/ddi.h> 51 #include <sys/sunddi.h> 52 53 static int vuidmice_open(queue_t *const, const dev_t *const, 54 const int, const int, const cred_t *const); 55 static int vuidmice_close(queue_t *const, const int, const cred_t *const); 56 static int vuidmice_rput(queue_t *const, mblk_t *); 57 static int vuidmice_rsrv(queue_t *const); 58 static int vuidmice_wput(queue_t *const, mblk_t *); 59 static void vuidmice_miocdata(queue_t *const, mblk_t *); 60 static int vuidmice_handle_wheel_resolution_ioctl(queue_t *const, 61 mblk_t *, int); 62 63 static int vuidmice_service_wheel_info(mblk_t *); 64 static int vuidmice_service_wheel_state(queue_t *, mblk_t *, uint_t); 65 66 void VUID_QUEUE(queue_t *const, mblk_t *); 67 int VUID_OPEN(queue_t *const); 68 void VUID_CLOSE(queue_t *const); 69 70 static kmutex_t vuidmice_lock; 71 72 static struct module_info vuidmice_iinfo = { 73 0, 74 VUID_NAME, 75 0, 76 INFPSZ, 77 1000, 78 100 79 }; 80 81 static struct qinit vuidmice_rinit = { 82 vuidmice_rput, 83 vuidmice_rsrv, 84 vuidmice_open, 85 vuidmice_close, 86 NULL, 87 &vuidmice_iinfo, 88 NULL 89 }; 90 91 static struct module_info vuidmice_oinfo = { 92 0, 93 VUID_NAME, 94 0, 95 INFPSZ, 96 1000, 97 100 98 }; 99 100 static struct qinit vuidmice_winit = { 101 vuidmice_wput, 102 NULL, 103 NULL, 104 NULL, 105 NULL, 106 &vuidmice_oinfo, 107 NULL 108 }; 109 110 struct streamtab vuidmice_info = { 111 &vuidmice_rinit, 112 &vuidmice_winit, 113 NULL, 114 NULL 115 }; 116 117 /* 118 * This is the loadable module wrapper. 119 */ 120 121 /* 122 * D_MTQPAIR effectively makes the module single threaded. 123 * There can be only one thread active in the module at any time. 124 * It may be a read or write thread. 125 */ 126 #define VUIDMICE_CONF_FLAG (D_MP | D_MTQPAIR) 127 128 static struct fmodsw fsw = { 129 VUID_NAME, 130 &vuidmice_info, 131 VUIDMICE_CONF_FLAG 132 }; 133 134 static struct modlstrmod modlstrmod = { 135 &mod_strmodops, 136 "mouse events to vuid events", 137 &fsw 138 }; 139 140 /* 141 * Module linkage information for the kernel. 142 */ 143 static struct modlinkage modlinkage = { 144 MODREV_1, 145 &modlstrmod, 146 NULL 147 }; 148 149 static int module_open = 0; /* allow only one open of this module */ 150 151 int 152 _init(void) 153 { 154 register int rc; 155 156 mutex_init(&vuidmice_lock, NULL, MUTEX_DEFAULT, NULL); 157 if ((rc = mod_install(&modlinkage)) != 0) { 158 mutex_destroy(&vuidmice_lock); 159 } 160 return (rc); 161 } 162 163 int 164 _fini(void) 165 { 166 register int rc; 167 168 if ((rc = mod_remove(&modlinkage)) == 0) 169 mutex_destroy(&vuidmice_lock); 170 return (rc); 171 } 172 173 int 174 _info(struct modinfo *modinfop) 175 { 176 return (mod_info(&modlinkage, modinfop)); 177 } 178 179 180 /* ARGSUSED1 */ 181 static int 182 vuidmice_open(queue_t *const qp, const dev_t *const devp, 183 const int oflag, const int sflag, const cred_t *const crp) 184 { 185 if (qp->q_ptr != NULL) 186 return (0); /* reopen */ 187 188 mutex_enter(&vuidmice_lock); 189 190 /* Allow only 1 open of this module */ 191 if (module_open) { 192 mutex_exit(&vuidmice_lock); 193 return (EBUSY); 194 } 195 196 module_open++; 197 mutex_exit(&vuidmice_lock); 198 199 /* 200 * Both the read and write queues share the same state structures. 201 */ 202 qp->q_ptr = kmem_zalloc(sizeof (struct MouseStateInfo), KM_SLEEP); 203 WR(qp)->q_ptr = qp->q_ptr; 204 205 /* initialize state */ 206 STATEP->format = VUID_NATIVE; 207 208 qprocson(qp); 209 210 #ifdef VUID_OPEN 211 if (VUID_OPEN(qp) != 0) { 212 qprocsoff(qp); 213 214 mutex_enter(&vuidmice_lock); 215 module_open--; 216 mutex_exit(&vuidmice_lock); 217 kmem_free(qp->q_ptr, sizeof (struct MouseStateInfo)); 218 qp->q_ptr = NULL; 219 return (ENXIO); 220 } 221 #endif 222 223 return (0); 224 } 225 226 /* ARGSUSED1 */ 227 static int 228 vuidmice_close(queue_t *const qp, const int flag, const cred_t *const crp) 229 { 230 ASSERT(qp != NULL); 231 232 qprocsoff(qp); 233 flushq(qp, FLUSHALL); 234 flushq(OTHERQ(qp), FLUSHALL); 235 236 #ifdef VUID_CLOSE 237 VUID_CLOSE(qp); 238 #endif 239 mutex_enter(&vuidmice_lock); 240 module_open--; 241 mutex_exit(&vuidmice_lock); 242 kmem_free(qp->q_ptr, sizeof (struct MouseStateInfo)); 243 qp->q_ptr = NULL; 244 245 return (0); 246 } 247 248 /* 249 * Put procedure for input from driver end of stream (read queue). 250 */ 251 static int 252 vuidmice_rput(queue_t *const qp, mblk_t *mp) 253 { 254 ASSERT(qp != NULL); 255 ASSERT(mp != NULL); 256 257 /* 258 * Handle all the related high priority messages here, hence 259 * should spend the least amount of time here. 260 */ 261 262 if (DB_TYPE(mp) == M_DATA) { 263 if ((int)STATEP->format == VUID_FIRM_EVENT) 264 return (putq(qp, mp)); /* queue message & return */ 265 } else if (DB_TYPE(mp) == M_FLUSH) { 266 if (*mp->b_rptr & FLUSHR) 267 flushq(qp, FLUSHALL); 268 } 269 270 putnext(qp, mp); /* pass it on */ 271 return (0); 272 } 273 274 static int 275 vuidmice_rsrv(queue_t *const qp) 276 { 277 register mblk_t *mp; 278 279 ASSERT(qp != NULL); 280 281 while ((mp = getq(qp)) != NULL) { 282 ASSERT(DB_TYPE(mp) == M_DATA); 283 284 if (!canputnext(qp)) 285 return (putbq(qp, mp)); /* read side is blocked */ 286 287 switch (DB_TYPE(mp)) { 288 case M_DATA: 289 if ((int)STATEP->format == VUID_FIRM_EVENT) 290 (void) VUID_QUEUE(qp, mp); 291 else 292 (void) putnext(qp, mp); 293 break; 294 295 default: 296 cmn_err(CE_WARN, 297 "vuidmice_rsrv: bad message type (0x%x)\n", 298 DB_TYPE(mp)); 299 300 (void) putnext(qp, mp); 301 break; 302 } 303 } 304 return (0); 305 } 306 307 /* 308 * Put procedure for write from user end of stream (write queue). 309 */ 310 static int 311 vuidmice_wput(queue_t *const qp, mblk_t *mp) 312 { 313 int error = 0; 314 315 ASSERT(qp != NULL); 316 ASSERT(mp != NULL); 317 318 /* 319 * Handle all the related high priority messages here, hence 320 * should spend the least amount of time here. 321 */ 322 switch (DB_TYPE(mp)) { /* handle hi pri messages here */ 323 case M_FLUSH: 324 if (*mp->b_rptr & FLUSHW) 325 flushq(qp, FLUSHALL); 326 putnext(qp, mp); /* pass it on */ 327 return (0); 328 329 case M_IOCTL: { 330 struct iocblk *iocbp = (void *)mp->b_rptr; 331 332 switch (iocbp->ioc_cmd) { 333 case VUIDSFORMAT: 334 335 /* 336 * VUIDSFORMAT is known to the stream head and thus 337 * is guaranteed to be an I_STR ioctl. 338 */ 339 if (iocbp->ioc_count == TRANSPARENT) { 340 miocnak(qp, mp, 0, EINVAL); 341 return (0); 342 } else { 343 int format_type; 344 345 error = miocpullup(mp, sizeof (int)); 346 if (error != 0) { 347 miocnak(qp, mp, 0, error); 348 return (0); 349 } 350 351 format_type = 352 *(int *)(void *)mp->b_cont->b_rptr; 353 STATEP->format = (uchar_t)format_type; 354 iocbp->ioc_rval = 0; 355 iocbp->ioc_count = 0; 356 iocbp->ioc_error = 0; 357 mp->b_datap->db_type = M_IOCACK; 358 } 359 360 /* return buffer to pool ASAP */ 361 if (mp->b_cont) { 362 freemsg(mp->b_cont); 363 mp->b_cont = NULL; 364 } 365 366 qreply(qp, mp); 367 return (0); 368 369 case VUIDGFORMAT: 370 371 /* return buffer to pool ASAP */ 372 if (mp->b_cont) { 373 freemsg(mp->b_cont); /* over written below */ 374 mp->b_cont = NULL; 375 } 376 377 /* 378 * VUIDGFORMAT is known to the stream head and thus 379 * is guaranteed to be an I_STR ioctl. 380 */ 381 if (iocbp->ioc_count == TRANSPARENT) { 382 miocnak(qp, mp, 0, EINVAL); 383 return (0); 384 } 385 386 mp->b_cont = allocb(sizeof (int), BPRI_MED); 387 if (mp->b_cont == NULL) { 388 miocnak(qp, mp, 0, EAGAIN); 389 return (0); 390 } 391 392 *(int *)(void *)mp->b_cont->b_rptr = 393 (int)STATEP->format; 394 mp->b_cont->b_wptr += sizeof (int); 395 396 iocbp->ioc_count = sizeof (int); 397 mp->b_datap->db_type = M_IOCACK; 398 qreply(qp, mp); 399 return (0); 400 401 case VUID_NATIVE: 402 case VUIDSADDR: 403 case VUIDGADDR: 404 miocnak(qp, mp, 0, ENOTTY); 405 return (0); 406 407 case MSIOBUTTONS: 408 /* return buffer to pool ASAP */ 409 if (mp->b_cont) { 410 freemsg(mp->b_cont); /* over written below */ 411 mp->b_cont = NULL; 412 } 413 414 /* 415 * MSIOBUTTONS is known to streamio.c and this 416 * is assume to be non-I_STR & non-TRANSPARENT ioctl 417 */ 418 419 if (iocbp->ioc_count == TRANSPARENT) { 420 miocnak(qp, mp, 0, EINVAL); 421 return (0); 422 } 423 424 if (STATEP->nbuttons == 0) { 425 miocnak(qp, mp, 0, EINVAL); 426 return (0); 427 } 428 429 mp->b_cont = allocb(sizeof (int), BPRI_MED); 430 if (mp->b_cont == NULL) { 431 miocnak(qp, mp, 0, EAGAIN); 432 return (0); 433 } 434 435 *(int *)(void *)mp->b_cont->b_rptr = 436 (int)STATEP->nbuttons; 437 mp->b_cont->b_wptr += sizeof (int); 438 439 iocbp->ioc_count = sizeof (int); 440 mp->b_datap->db_type = M_IOCACK; 441 qreply(qp, mp); 442 return (0); 443 444 /* 445 * New IOCTL support. Since it's explicitly mentioned 446 * that you can't add more ioctls to stream head's 447 * hard coded list, we have to do the transparent 448 * ioctl processing which is not very exciting. 449 */ 450 case VUIDGWHEELCOUNT: 451 case VUIDGWHEELINFO: 452 case VUIDGWHEELSTATE: 453 case VUIDSWHEELSTATE: 454 case MSIOSRESOLUTION: 455 error = vuidmice_handle_wheel_resolution_ioctl(qp, 456 mp, iocbp->ioc_cmd); 457 if (!error) { 458 return (0); 459 } else { 460 miocnak(qp, mp, 0, error); 461 return (0); 462 } 463 default: 464 putnext(qp, mp); /* nothing to process here */ 465 466 return (0); 467 } 468 469 } /* End of case M_IOCTL */ 470 471 case M_IOCDATA: 472 vuidmice_miocdata(qp, mp); 473 474 return (0); 475 default: 476 putnext(qp, mp); /* pass it on */ 477 return (0); 478 } 479 /*NOTREACHED*/ 480 } 481 482 void 483 VUID_PUTNEXT(queue_t *const qp, uchar_t event_id, uchar_t event_pair_type, 484 uchar_t event_pair, int event_value) 485 { 486 int strikes = 1; 487 mblk_t *bp; 488 Firm_event *fep; 489 490 /* 491 * Give this event 3 chances to allocate blocks, 492 * otherwise discard this mouse event. 3 Strikes and you're out. 493 */ 494 while ((bp = allocb((int)sizeof (Firm_event), BPRI_HI)) == NULL) { 495 if (++strikes > 3) 496 return; 497 drv_usecwait(10); 498 } 499 500 fep = (void *)bp->b_wptr; 501 fep->id = vuid_id_addr(VKEY_FIRST) | vuid_id_offset(event_id); 502 503 fep->pair_type = event_pair_type; 504 fep->pair = event_pair; 505 fep->value = event_value; 506 uniqtime32(&fep->time); 507 bp->b_wptr += sizeof (Firm_event); 508 509 if (canput(qp->q_next)) 510 putnext(qp, bp); 511 else 512 (void) putbq(qp, bp); /* read side is blocked */ 513 } 514 515 516 /* 517 * vuidmice_miocdata 518 * M_IOCDATA processing for IOCTL's: VUIDGWHEELCOUNT, VUIDGWHEELINFO, 519 * VUIDGWHEELSTATE, VUIDSWHEELSTATE & MSIOSRESOLUTION. 520 */ 521 static void 522 vuidmice_miocdata(queue_t *qp, mblk_t *mp) 523 { 524 struct copyresp *copyresp; 525 struct iocblk *iocbp; 526 mblk_t *ioctmp; 527 mblk_t *datap; 528 Mouse_iocstate_t *Mouseioc; 529 size_t size; 530 int err = 0; 531 532 533 copyresp = (void *)mp->b_rptr; 534 iocbp = (void *)mp->b_rptr; 535 536 if (copyresp->cp_rval) { 537 err = EAGAIN; 538 539 goto err; 540 } 541 switch (copyresp->cp_cmd) { 542 case VUIDGWHEELCOUNT: 543 mp->b_datap->db_type = M_IOCACK; 544 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 545 iocbp->ioc_error = 0; 546 iocbp->ioc_count = 0; 547 iocbp->ioc_rval = 0; 548 if (mp->b_cont != NULL) { 549 freemsg(mp->b_cont); 550 mp->b_cont = NULL; 551 } 552 553 break; 554 case VUIDGWHEELINFO: 555 case VUIDGWHEELSTATE: 556 ioctmp = copyresp->cp_private; 557 Mouseioc = (void *)ioctmp->b_rptr; 558 if (Mouseioc->ioc_state == GETSTRUCT) { 559 if (mp->b_cont == NULL) { 560 err = EINVAL; 561 562 break; 563 } 564 datap = mp->b_cont; 565 if (copyresp->cp_cmd == VUIDGWHEELSTATE) { 566 err = vuidmice_service_wheel_state(qp, datap, 567 VUIDGWHEELSTATE); 568 } else { 569 err = vuidmice_service_wheel_info(datap); 570 } 571 if (err) { 572 break; 573 } 574 575 if (copyresp->cp_cmd == VUIDGWHEELSTATE) { 576 size = sizeof (wheel_state); 577 } else { 578 size = sizeof (wheel_info); 579 } 580 581 Mouseioc->ioc_state = GETRESULT; 582 ASSERT(Mouseioc->u_addr != NULL); 583 mcopyout(mp, ioctmp, size, Mouseioc->u_addr, NULL); 584 } else if (Mouseioc->ioc_state == GETRESULT) { 585 freemsg(ioctmp); 586 mp->b_datap->db_type = M_IOCACK; 587 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 588 iocbp->ioc_error = 0; 589 iocbp->ioc_count = 0; 590 iocbp->ioc_rval = 0; 591 if (mp->b_cont != NULL) { 592 freemsg(mp->b_cont); 593 mp->b_cont = NULL; 594 } 595 } 596 597 break; 598 case VUIDSWHEELSTATE: 599 case MSIOSRESOLUTION: 600 ioctmp = copyresp->cp_private; 601 Mouseioc = (void *)ioctmp->b_rptr; 602 if (mp->b_cont == NULL) { 603 err = EINVAL; 604 605 break; 606 } 607 datap = mp->b_cont; 608 609 if (copyresp->cp_cmd == VUIDSWHEELSTATE) { 610 err = vuidmice_service_wheel_state(qp, 611 datap, VUIDSWHEELSTATE); 612 } 613 614 if (err) { 615 break; 616 } 617 618 if (mp->b_cont) { 619 freemsg(mp->b_cont); 620 mp->b_cont = NULL; 621 } 622 freemsg(ioctmp); 623 iocbp->ioc_count = 0; 624 iocbp->ioc_error = 0; 625 iocbp->ioc_rval = 0; 626 mp->b_datap->db_type = M_IOCACK; 627 628 break; 629 default: 630 err = EINVAL; 631 632 break; 633 } 634 635 err: 636 if (err) { 637 mp->b_datap->db_type = M_IOCNAK; 638 if (mp->b_cont) { 639 freemsg(mp->b_cont); 640 mp->b_cont = NULL; 641 } 642 if (copyresp->cp_private) { 643 freemsg(copyresp->cp_private); 644 copyresp->cp_private = NULL; 645 } 646 iocbp->ioc_count = 0; 647 iocbp->ioc_error = err; 648 } 649 qreply(qp, mp); 650 } 651 652 653 /* 654 * vuidmice_handle_wheel_resolution_ioctl 655 * Handle wheel mouse and MSIOSRESOLUTION ioctls. 656 * 657 * Here we also support non-transparent way of these ioctls 658 * just like usb mouse driver does, so the consms module is 659 * very simple to deal with these ioctls. 660 */ 661 static int 662 vuidmice_handle_wheel_resolution_ioctl(queue_t *qp, mblk_t *mp, int cmd) 663 { 664 int err = 0; 665 Mouse_iocstate_t *Mouseioc; 666 caddr_t useraddr; 667 size_t size; 668 mblk_t *ioctmp; 669 mblk_t *datap; 670 671 struct iocblk *iocbp = (void *)mp->b_rptr; 672 673 if (iocbp->ioc_count == TRANSPARENT) { 674 if (mp->b_cont == NULL) 675 return (EINVAL); 676 useraddr = *((caddr_t *)(void *)mp->b_cont->b_rptr); 677 switch (cmd) { 678 case VUIDGWHEELCOUNT: 679 size = sizeof (int); 680 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) 681 return (EAGAIN); 682 *((int *)(void *)datap->b_wptr) = 683 STATEP->vuid_mouse_mode; 684 mcopyout(mp, NULL, size, NULL, datap); 685 qreply(qp, mp); 686 687 return (err); 688 case VUIDGWHEELINFO: 689 size = sizeof (wheel_info); 690 break; 691 692 case VUIDSWHEELSTATE: 693 case VUIDGWHEELSTATE: 694 size = sizeof (wheel_state); 695 break; 696 697 case MSIOSRESOLUTION: 698 size = sizeof (Ms_screen_resolution); 699 break; 700 } 701 702 if ((ioctmp = allocb(sizeof (Mouse_iocstate_t), 703 BPRI_MED)) == NULL) 704 return (EAGAIN); 705 Mouseioc = (void *)ioctmp->b_rptr; 706 Mouseioc->ioc_state = GETSTRUCT; 707 Mouseioc->u_addr = useraddr; 708 ioctmp->b_wptr = ioctmp->b_rptr + sizeof (Mouse_iocstate_t); 709 mcopyin(mp, ioctmp, size, NULL); 710 qreply(qp, mp); 711 712 return (err); 713 } else { 714 switch (cmd) { 715 case VUIDGWHEELCOUNT: 716 if (mp->b_cont) { 717 freemsg(mp->b_cont); 718 mp->b_cont = NULL; 719 } 720 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) { 721 err = EAGAIN; 722 break; 723 } 724 *((int *)(void *)datap->b_wptr) = 725 STATEP->vuid_mouse_mode; 726 datap->b_wptr += sizeof (int); 727 mp->b_cont = datap; 728 break; 729 730 case VUIDGWHEELINFO: 731 if (mp->b_cont == NULL || 732 iocbp->ioc_count != sizeof (wheel_info)) { 733 err = EINVAL; 734 break; 735 } 736 datap = mp->b_cont; 737 err = vuidmice_service_wheel_info(datap); 738 break; 739 740 case VUIDSWHEELSTATE: 741 case VUIDGWHEELSTATE: 742 if (mp->b_cont == NULL || 743 iocbp->ioc_count != sizeof (wheel_state)) { 744 err = EINVAL; 745 break; 746 } 747 datap = mp->b_cont; 748 err = vuidmice_service_wheel_state(qp, datap, cmd); 749 break; 750 751 case MSIOSRESOLUTION: 752 /* 753 * Now we just make Xserver and 754 * the virtual mouse happy. Of course, 755 * the screen resolution value may 756 * be used later for absolute PS/2 mouse. 757 */ 758 err = 0; 759 break; 760 } 761 762 if (!err) { 763 mp->b_datap->db_type = M_IOCACK; 764 iocbp->ioc_rval = 0; 765 iocbp->ioc_error = 0; 766 qreply(qp, mp); 767 } 768 769 return (err); 770 } 771 } 772 773 static int 774 vuidmice_service_wheel_info(register mblk_t *datap) 775 { 776 wheel_info *wi; 777 int err = 0; 778 779 wi = (void *)datap->b_rptr; 780 if (wi->vers != VUID_WHEEL_INFO_VERS) { 781 err = EINVAL; 782 return (err); 783 } 784 785 if (wi->id > (VUIDMICE_NUM_WHEELS - 1)) { 786 err = EINVAL; 787 return (err); 788 } 789 wi->format = (wi->id == VUIDMICE_VERTICAL_WHEEL_ID) ? 790 VUID_WHEEL_FORMAT_VERTICAL : VUID_WHEEL_FORMAT_HORIZONTAL; 791 792 return (err); 793 } 794 795 796 static int 797 vuidmice_service_wheel_state(register queue_t *qp, 798 register mblk_t *datap, 799 register uint_t cmd) 800 { 801 wheel_state *ws; 802 uint_t err = 0; 803 804 ws = (void *)datap->b_rptr; 805 if (ws->vers != VUID_WHEEL_STATE_VERS) { 806 err = EINVAL; 807 return (err); 808 } 809 810 if (ws->id > (VUIDMICE_NUM_WHEELS - 1)) { 811 err = EINVAL; 812 return (err); 813 } 814 815 switch (cmd) { 816 case VUIDGWHEELSTATE: 817 ws->stateflags = 818 (STATEP->wheel_state_bf >> ws->id) & 1; 819 820 break; 821 case VUIDSWHEELSTATE: 822 STATEP->wheel_state_bf = (ws->stateflags << ws->id) | 823 (STATEP->wheel_state_bf & ~(1 << ws->id)); 824 825 break; 826 default: 827 err = EINVAL; 828 829 return (err); 830 } 831 832 return (err); 833 } 834