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 }