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 }