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