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