xref: /titanic_52/usr/src/uts/common/io/mouse8042.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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 /*	Copyright (c) 1990, 1991 UNIX System Laboratories, Inc.	*/
23 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
24 /*	  All Rights Reserved  	*/
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 /*
34  * PS/2 type Mouse Module - Streams
35  */
36 
37 #include <sys/param.h>
38 #include <sys/types.h>
39 #include <sys/kmem.h>
40 #include <sys/signal.h>
41 #include <sys/errno.h>
42 #include <sys/file.h>
43 #include <sys/termio.h>
44 #include <sys/stream.h>
45 #include <sys/stropts.h>
46 #include <sys/strtty.h>
47 #include <sys/debug.h>
48 #include <sys/ddi.h>
49 #include <sys/stat.h>
50 #include <sys/cmn_err.h>
51 #include <sys/sunddi.h>
52 
53 #include <sys/promif.h>
54 #include <sys/cred.h>
55 
56 #include <sys/i8042.h>
57 #include <sys/note.h>
58 
59 #define	DRIVER_NAME(dip)	ddi_driver_name(dip)
60 
61 #ifdef	DEBUG
62 #define	MOUSE8042_DEBUG
63 #endif
64 
65 #define	MOUSE8042_INTERNAL_OPEN(minor)	(((minor) & 0x1) == 1)
66 #define	MOUSE8042_MINOR_TO_INSTANCE(minor)	((minor) / 2)
67 #define	MOUSE8042_INTERNAL_MINOR(minor)		((minor) + 1)
68 
69 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
70 extern void consconfig_link(major_t major, minor_t minor);
71 extern int consconfig_unlink(major_t major, minor_t minor);
72 
73 
74 /*
75  *
76  * Local Static Data
77  *
78  */
79 
80 /*
81  * We only support one instance.  Yes, it's theoretically possible to
82  * plug in more than one, but it's not worth the implementation cost.
83  *
84  * The introduction of USB keyboards might make it worth reassessing
85  * this decision, as they might free up the keyboard port for a second
86  * PS/2 style mouse.
87  */
88 static dev_info_t *mouse8042_dip;
89 
90 struct mouse_state {
91 	queue_t	*ms_rqp;
92 	queue_t	*ms_wqp;
93 	ddi_iblock_cookie_t	ms_iblock_cookie;
94 	ddi_acc_handle_t	ms_handle;
95 	uint8_t			*ms_addr;
96 	kmutex_t		ms_mutex;
97 
98 	minor_t			ms_minor;
99 	boolean_t		ms_opened;
100 };
101 
102 #if	defined(MOUSE8042_DEBUG)
103 int mouse8042_debug = 0;
104 int mouse8042_debug_minimal = 0;
105 #endif
106 
107 static uint_t mouse8042_intr(caddr_t arg);
108 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
109 		cred_t *cred_p);
110 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
111 static int mouse8042_wput(queue_t *q, mblk_t *mp);
112 
113 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
114 		void *arg, void **result);
115 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
116 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
117 
118 
119 /*
120  * Streams module info.
121  */
122 #define	MODULE_NAME	"mouse8042"
123 
124 static struct module_info	mouse8042_minfo = {
125 	23,		/* Module ID number */
126 	MODULE_NAME,
127 	0, INFPSZ,	/* minimum & maximum packet sizes */
128 	256, 128	/* hi and low water marks */
129 };
130 
131 static struct qinit mouse8042_rinit = {
132 	NULL,		/* put */
133 	NULL,		/* service */
134 	mouse8042_open,
135 	mouse8042_close,
136 	NULL,		/* admin */
137 	&mouse8042_minfo,
138 	NULL		/* statistics */
139 };
140 
141 static struct qinit mouse8042_winit = {
142 	mouse8042_wput,	/* put */
143 	NULL,		/* service */
144 	NULL,		/* open */
145 	NULL,		/* close */
146 	NULL,		/* admin */
147 	&mouse8042_minfo,
148 	NULL		/* statistics */
149 };
150 
151 static struct streamtab mouse8042_strinfo = {
152 	&mouse8042_rinit,
153 	&mouse8042_winit,
154 	NULL,		/* muxrinit */
155 	NULL,		/* muxwinit */
156 };
157 
158 /*
159  * Local Function Declarations
160  */
161 
162 static struct cb_ops	mouse8042_cb_ops = {
163 	nodev,			/* open */
164 	nodev,			/* close */
165 	nodev,			/* strategy */
166 	nodev,			/* print */
167 	nodev,			/* dump */
168 	nodev,			/* read */
169 	nodev,			/* write */
170 	nodev,			/* ioctl */
171 	nodev,			/* devmap */
172 	nodev,			/* mmap */
173 	nodev,			/* segmap */
174 	nochpoll,		/* poll */
175 	ddi_prop_op,		/* cb_prop_op */
176 	&mouse8042_strinfo,	/* streamtab  */
177 	D_MP | D_NEW
178 };
179 
180 
181 static struct dev_ops	mouse8042_ops = {
182 	DEVO_REV,		/* devo_rev, */
183 	0,			/* refcnt  */
184 	mouse8042_getinfo,	/* getinfo */
185 	nulldev,		/* identify */
186 	nulldev,		/* probe */
187 	mouse8042_attach,	/* attach */
188 	mouse8042_detach,	/* detach */
189 	nodev,			/* reset */
190 	&mouse8042_cb_ops,	/* driver operations */
191 	(struct bus_ops *)0	/* bus operations */
192 };
193 
194 /*
195  * This is the loadable module wrapper.
196  */
197 #include <sys/modctl.h>
198 
199 extern struct mod_ops mod_driverops;
200 
201 /*
202  * Module linkage information for the kernel.
203  */
204 
205 static struct modldrv modldrv = {
206 	&mod_driverops, /* Type of module.  This one is a driver */
207 	"PS/2 Mouse %I%, %E%",
208 	&mouse8042_ops,	/* driver ops */
209 };
210 
211 static struct modlinkage modlinkage = {
212 	MODREV_1,
213 	(void *)&modldrv,
214 	NULL
215 };
216 
217 /*
218  * This is the driver initialization routine.
219  */
220 int
221 _init()
222 {
223 	int	rv;
224 
225 	rv = mod_install(&modlinkage);
226 	return (rv);
227 }
228 
229 
230 int
231 _fini(void)
232 {
233 	return (mod_remove(&modlinkage));
234 }
235 
236 
237 int
238 _info(struct modinfo *modinfop)
239 {
240 	return (mod_info(&modlinkage, modinfop));
241 }
242 
243 static int
244 mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
245 {
246 	struct mouse_state *state;
247 	int instance = ddi_get_instance(dip);
248 	static ddi_device_acc_attr_t attr = {
249 		DDI_DEVICE_ATTR_V0,
250 		DDI_NEVERSWAP_ACC,
251 		DDI_STRICTORDER_ACC,
252 	};
253 	int rc;
254 
255 
256 #ifdef MOUSE8042_DEBUG
257 	if (mouse8042_debug) {
258 		cmn_err(CE_CONT, MODULE_NAME "_attach entry\n");
259 	}
260 #endif
261 
262 	if (cmd != DDI_ATTACH)
263 		return (DDI_FAILURE);
264 
265 	if (mouse8042_dip != NULL)
266 		return (DDI_FAILURE);
267 
268 	/* allocate and initialize state structure */
269 	state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
270 	state->ms_opened = B_FALSE;
271 	ddi_set_driver_private(dip, state);
272 
273 	/*
274 	 * In order to support virtual keyboard/mouse, we should distinguish
275 	 * between internal virtual open and external physical open.
276 	 *
277 	 * When the physical devices are opened by application, they will
278 	 * be unlinked from the virtual device and their data stream will
279 	 * not be sent to the virtual device. When the opened physical
280 	 * devices are closed, they will be relinked to the virtual devices.
281 	 *
282 	 * All these automatic switch between virtual and physical are
283 	 * transparent.
284 	 *
285 	 * So we change minor node numbering scheme to be:
286 	 * 	external node minor num == instance * 2
287 	 *	internal node minor num == instance * 2 + 1
288 	 */
289 	rc = ddi_create_minor_node(dip, "l", S_IFCHR, instance * 2,
290 	    DDI_NT_MOUSE, NULL);
291 	if (rc != DDI_SUCCESS) {
292 #if	defined(MOUSE8042_DEBUG)
293 		cmn_err(CE_CONT,
294 		    MODULE_NAME "_attach: ddi_create_minor_node failed\n");
295 #endif
296 		goto fail_1;
297 	}
298 
299 	if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
300 		    instance * 2 + 1) != DDI_SUCCESS) {
301 		goto fail_2;
302 	}
303 
304 	rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
305 		(offset_t)0, (offset_t)0, &attr, &state->ms_handle);
306 	if (rc != DDI_SUCCESS) {
307 #if	defined(MOUSE8042_DEBUG)
308 		cmn_err(CE_WARN, MODULE_NAME "_attach:  can't map registers");
309 #endif
310 		goto fail_2;
311 	}
312 
313 	rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
314 	if (rc != DDI_SUCCESS) {
315 #if	defined(MOUSE8042_DEBUG)
316 		cmn_err(CE_WARN,
317 		    MODULE_NAME "_attach:  Can't get iblock cookie");
318 #endif
319 		goto fail_3;
320 	}
321 
322 	mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
323 	    state->ms_iblock_cookie);
324 
325 	rc = ddi_add_intr(dip, 0,
326 		(ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
327 		mouse8042_intr, (caddr_t)state);
328 	if (rc != DDI_SUCCESS) {
329 #if	defined(MOUSE8042_DEBUG)
330 		cmn_err(CE_WARN, MODULE_NAME "_attach: cannot add interrupt");
331 #endif
332 		goto fail_3;
333 	}
334 
335 	mouse8042_dip = dip;
336 
337 	/* Now that we're attached, announce our presence to the world. */
338 	ddi_report_dev(dip);
339 #if	defined(MOUSE8042_DEBUG)
340 	cmn_err(CE_CONT, "?%s #%d: version %s\n",
341 	    DRIVER_NAME(dip), ddi_get_instance(dip), "%I% (%E%)");
342 #endif
343 	return (DDI_SUCCESS);
344 
345 fail_3:
346 	ddi_regs_map_free(&state->ms_handle);
347 
348 fail_2:
349 	ddi_remove_minor_node(dip, NULL);
350 
351 fail_1:
352 	kmem_free(state, sizeof (struct mouse_state));
353 	return (rc);
354 }
355 
356 /*ARGSUSED*/
357 static int
358 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
359 {
360 	struct mouse_state *state;
361 
362 	state = ddi_get_driver_private(dip);
363 
364 	switch (cmd) {
365 
366 	case DDI_DETACH:
367 		ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
368 		mouse8042_dip = NULL;
369 		mutex_destroy(&state->ms_mutex);
370 		ddi_prop_remove_all(dip);
371 		ddi_regs_map_free(&state->ms_handle);
372 		ddi_remove_minor_node(dip, NULL);
373 		kmem_free(state, sizeof (struct mouse_state));
374 		return (DDI_SUCCESS);
375 
376 	default:
377 #ifdef MOUSE8042_DEBUG
378 		if (mouse8042_debug) {
379 			cmn_err(CE_CONT,
380 			    "mouse8042_detach: cmd = %d unknown\n", cmd);
381 		}
382 #endif
383 		return (DDI_FAILURE);
384 	}
385 }
386 
387 
388 /* ARGSUSED */
389 static int
390 mouse8042_getinfo(
391     dev_info_t *dip,
392     ddi_info_cmd_t infocmd,
393     void *arg,
394     void **result)
395 {
396 	dev_t dev = (dev_t)arg;
397 	minor_t	minor = getminor(dev);
398 	int	instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
399 
400 #ifdef MOUSE8042_DEBUG
401 	if (mouse8042_debug)
402 		cmn_err(CE_CONT, "mouse8042_getinfo: call\n");
403 #endif
404 	switch (infocmd) {
405 	case DDI_INFO_DEVT2DEVINFO:
406 		if (mouse8042_dip == NULL)
407 			return (DDI_FAILURE);
408 
409 		*result = (void *)mouse8042_dip;
410 		break;
411 	case DDI_INFO_DEVT2INSTANCE:
412 		*result = (void *)(uintptr_t)instance;
413 		break;
414 	default:
415 		return (DDI_FAILURE);
416 	}
417 	return (DDI_SUCCESS);
418 }
419 
420 /*ARGSUSED*/
421 static int
422 mouse8042_open(
423 	queue_t	*q,
424 	dev_t	*devp,
425 	int	flag,
426 	int	sflag,
427 	cred_t	*cred_p)
428 {
429 	struct mouse_state *state;
430 	minor_t	minor = getminor(*devp);
431 	int rval;
432 
433 	if (mouse8042_dip == NULL)
434 		return (ENXIO);
435 
436 	state = ddi_get_driver_private(mouse8042_dip);
437 
438 #ifdef MOUSE8042_DEBUG
439 	if (mouse8042_debug)
440 		cmn_err(CE_CONT, "mouse8042_open:entered\n");
441 #endif
442 
443 	mutex_enter(&state->ms_mutex);
444 
445 	if (state->ms_opened) {
446 		/*
447 		 * Exit if the same minor node is already open
448 		 */
449 		if (state->ms_minor == minor) {
450 			mutex_exit(&state->ms_mutex);
451 			return (0);
452 		}
453 
454 		/*
455 		 * Check whether it is switch between physical and virtual
456 		 *
457 		 * Opening from virtual while the device is being physically
458 		 * opened by an application should not happen. So we ASSERT
459 		 * this in DEBUG version, and return error in the non-DEBUG
460 		 * case.
461 		 */
462 		ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
463 
464 		if (MOUSE8042_INTERNAL_OPEN(minor)) {
465 			mutex_exit(&state->ms_mutex);
466 			return (EINVAL);
467 		}
468 
469 		/*
470 		 * Opening the physical one while it is being underneath
471 		 * the virtual one.
472 		 *
473 		 * consconfig_unlink is called to unlink this device from
474 		 * the virtual one, thus the old stream serving for this
475 		 * device under the virtual one is closed, and then the
476 		 * lower driver's close routine (here is mouse8042_close)
477 		 * is also called to accomplish the whole stream close.
478 		 * Here we have to drop the lock because mouse8042_close
479 		 * also needs the lock.
480 		 *
481 		 * For mouse, the old stream is:
482 		 *	consms->["pushmod"->]"mouse_vp driver"
483 		 *
484 		 * After the consconfig_unlink returns, the old stream is closed
485 		 * and we grab the lock again to reopen this device as normal.
486 		 */
487 		mutex_exit(&state->ms_mutex);
488 
489 		/*
490 		 * If unlink fails, fail the physical open.
491 		 */
492 		if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
493 		    MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
494 			return (rval);
495 		}
496 
497 		mutex_enter(&state->ms_mutex);
498 	}
499 
500 
501 	q->q_ptr = (caddr_t)state;
502 	WR(q)->q_ptr = (caddr_t)state;
503 	state->ms_rqp = q;
504 	state->ms_wqp = WR(q);
505 
506 	qprocson(q);
507 
508 	state->ms_minor = minor;
509 	state->ms_opened = B_TRUE;
510 
511 	mutex_exit(&state->ms_mutex);
512 
513 	return (0);
514 }
515 
516 
517 /*ARGSUSED*/
518 static int
519 mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
520 {
521 	struct mouse_state *state;
522 	minor_t	minor;
523 
524 	state = (struct mouse_state *)q->q_ptr;
525 
526 #ifdef MOUSE8042_DEBUG
527 	if (mouse8042_debug)
528 		cmn_err(CE_CONT, "mouse8042_close:entered\n");
529 #endif
530 
531 	mutex_enter(&state->ms_mutex);
532 
533 	qprocsoff(q);
534 
535 	q->q_ptr = NULL;
536 	WR(q)->q_ptr = NULL;
537 	state->ms_rqp = NULL;
538 	state->ms_wqp = NULL;
539 
540 	state->ms_opened = B_FALSE;
541 
542 	minor = state->ms_minor;
543 
544 	mutex_exit(&state->ms_mutex);
545 
546 	if (!MOUSE8042_INTERNAL_OPEN(minor)) {
547 		/*
548 		 * Closing physical PS/2 mouse
549 		 *
550 		 * Link it back to virtual mouse, and
551 		 * mouse8042_open will be called as a result
552 		 * of the consconfig_link call.
553 		 *
554 		 * If linking back fails, this specific mouse
555 		 * will not be available underneath the virtual
556 		 * mouse, and can only be accessed via physical
557 		 * open.
558 		 */
559 		consconfig_link(ddi_driver_major(mouse8042_dip),
560 		    MOUSE8042_INTERNAL_MINOR(minor));
561 	}
562 
563 	return (0);
564 }
565 
566 static void
567 mouse8042_iocnack(
568     queue_t *qp,
569     mblk_t *mp,
570     struct iocblk *iocp,
571     int error,
572     int rval)
573 {
574 	mp->b_datap->db_type = M_IOCNAK;
575 	iocp->ioc_rval = rval;
576 	iocp->ioc_error = error;
577 	qreply(qp, mp);
578 }
579 
580 static int
581 mouse8042_wput(queue_t *q, mblk_t *mp)
582 {
583 	struct iocblk *iocbp;
584 	mblk_t *bp;
585 	mblk_t *next;
586 	struct mouse_state *state;
587 
588 	state = (struct mouse_state *)q->q_ptr;
589 
590 #ifdef MOUSE8042_DEBUG
591 	if (mouse8042_debug)
592 		cmn_err(CE_CONT, "mouse8042_wput:entered\n");
593 #endif
594 	iocbp = (struct iocblk *)mp->b_rptr;
595 	switch (mp->b_datap->db_type) {
596 	case M_FLUSH:
597 #ifdef MOUSE8042_DEBUG
598 		if (mouse8042_debug)
599 			cmn_err(CE_CONT, "mouse8042_wput:M_FLUSH\n");
600 #endif
601 
602 		if (*mp->b_rptr & FLUSHW)
603 			flushq(q, FLUSHDATA);
604 		qreply(q, mp);
605 		break;
606 	case M_IOCTL:
607 #ifdef MOUSE8042_DEBUG
608 		if (mouse8042_debug)
609 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCTL\n");
610 #endif
611 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
612 		break;
613 	case M_IOCDATA:
614 #ifdef MOUSE8042_DEBUG
615 		if (mouse8042_debug)
616 			cmn_err(CE_CONT, "mouse8042_wput:M_IOCDATA\n");
617 #endif
618 		mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
619 		break;
620 	case M_DATA:
621 		bp = mp;
622 		do {
623 			while (bp->b_rptr < bp->b_wptr) {
624 #if	defined(MOUSE8042_DEBUG)
625 				if (mouse8042_debug) {
626 					cmn_err(CE_CONT,
627 					    "mouse8042:  send %2x\n",
628 					    *bp->b_rptr);
629 				}
630 				if (mouse8042_debug_minimal) {
631 					cmn_err(CE_CONT, ">a:%2x ",
632 					    *bp->b_rptr);
633 				}
634 #endif
635 				ddi_put8(state->ms_handle,
636 					state->ms_addr + I8042_INT_OUTPUT_DATA,
637 					*bp->b_rptr++);
638 			}
639 			next = bp->b_cont;
640 			freeb(bp);
641 		} while ((bp = next) != NULL);
642 		break;
643 	default:
644 		freemsg(mp);
645 		break;
646 	}
647 #ifdef MOUSE8042_DEBUG
648 	if (mouse8042_debug)
649 		cmn_err(CE_CONT, "mouse8042_wput:leaving\n");
650 #endif
651 	return (0);	/* ignored */
652 }
653 
654 static uint_t
655 mouse8042_intr(caddr_t arg)
656 {
657 	unsigned char    mdata;
658 	mblk_t *mp;
659 	struct mouse_state *state = (struct mouse_state *)arg;
660 	int rc;
661 
662 	mutex_enter(&state->ms_mutex);
663 
664 #if	defined(MOUSE8042_DEBUG)
665 	if (mouse8042_debug)
666 		cmn_err(CE_CONT, "mouse8042_intr()\n");
667 #endif
668 	rc = DDI_INTR_UNCLAIMED;
669 
670 	for (;;) {
671 
672 		if (ddi_get8(state->ms_handle,
673 			    state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
674 			break;
675 		}
676 
677 		mdata = ddi_get8(state->ms_handle,
678 				state->ms_addr + I8042_INT_INPUT_DATA);
679 
680 #if	defined(MOUSE8042_DEBUG)
681 		if (mouse8042_debug)
682 			cmn_err(CE_CONT, "mouse8042_intr:  got %2x\n", mdata);
683 		if (mouse8042_debug_minimal)
684 			cmn_err(CE_CONT, "<A:%2x ", mdata);
685 #endif
686 
687 		rc = DDI_INTR_CLAIMED;
688 
689 		if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
690 			*mp->b_wptr++ = mdata;
691 			putnext(state->ms_rqp, mp);
692 		}
693 	}
694 #ifdef MOUSE8042_DEBUG
695 	if (mouse8042_debug)
696 		cmn_err(CE_CONT, "mouse8042_intr() ok\n");
697 #endif
698 	mutex_exit(&state->ms_mutex);
699 
700 	return (rc);
701 }
702