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