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