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