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 }