remove unnecessary offset_t casts of 0
[unleashed.git] / usr / src / uts / common / io / mouse8042.c
blobe7c6c6647d5338f4d94a347cab1c6668348f01bd
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
21 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
23 /* All Rights Reserved */
26 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
32 * PS/2 type Mouse Module - Streams
35 #include <sys/param.h>
36 #include <sys/types.h>
37 #include <sys/kmem.h>
38 #include <sys/signal.h>
39 #include <sys/errno.h>
40 #include <sys/file.h>
41 #include <sys/termio.h>
42 #include <sys/stream.h>
43 #include <sys/stropts.h>
44 #include <sys/strtty.h>
45 #include <sys/strsun.h>
46 #include <sys/debug.h>
47 #include <sys/ddi.h>
48 #include <sys/stat.h>
49 #include <sys/cmn_err.h>
50 #include <sys/sunddi.h>
52 #include <sys/promif.h>
53 #include <sys/cred.h>
55 #include <sys/i8042.h>
56 #include <sys/note.h>
57 #include <sys/mouse.h>
59 #define DRIVER_NAME(dip) ddi_driver_name(dip)
61 #define MOUSE8042_INTERNAL_OPEN(minor) (((minor) & 0x1) == 1)
62 #define MOUSE8042_MINOR_TO_INSTANCE(minor) ((minor) / 2)
63 #define MOUSE8042_INTERNAL_MINOR(minor) ((minor) + 1)
65 #define MOUSE8042_RESET_TIMEOUT_USECS 500000 /* 500 ms */
67 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
68 extern void consconfig_link(major_t major, minor_t minor);
69 extern int consconfig_unlink(major_t major, minor_t minor);
74 * Local Static Data
79 * We only support one instance. Yes, it's theoretically possible to
80 * plug in more than one, but it's not worth the implementation cost.
82 * The introduction of USB keyboards might make it worth reassessing
83 * this decision, as they might free up the keyboard port for a second
84 * PS/2 style mouse.
86 static dev_info_t *mouse8042_dip;
89 * RESET states
91 typedef enum {
92 MSE_RESET_IDLE, /* No reset in progress */
93 MSE_RESET_PRE, /* Send reset, waiting for ACK */
94 MSE_RESET_ACK, /* Got ACK, waiting for 0xAA */
95 MSE_RESET_AA, /* Got 0xAA, waiting for 0x00 */
96 MSE_RESET_FAILED
97 } mouse8042_reset_state_e;
99 struct mouse_state {
100 queue_t *ms_rqp;
101 queue_t *ms_wqp;
102 ddi_iblock_cookie_t ms_iblock_cookie;
103 ddi_acc_handle_t ms_handle;
104 uint8_t *ms_addr;
105 kmutex_t ms_mutex;
107 minor_t ms_minor;
108 boolean_t ms_opened;
109 kmutex_t reset_mutex;
110 kcondvar_t reset_cv;
111 mouse8042_reset_state_e reset_state;
112 timeout_id_t reset_tid;
113 int ready;
114 mblk_t *reply_mp;
115 mblk_t *reset_ack_mp;
116 bufcall_id_t bc_id;
119 static uint_t mouse8042_intr(caddr_t arg);
120 static int mouse8042_open(queue_t *q, dev_t *devp, int flag, int sflag,
121 cred_t *cred_p);
122 static int mouse8042_close(queue_t *q, int flag, cred_t *cred_p);
123 static int mouse8042_wsrv(queue_t *qp);
124 static int mouse8042_wput(queue_t *q, mblk_t *mp);
126 static int mouse8042_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
127 void *arg, void **result);
128 static int mouse8042_attach(dev_info_t *dev, ddi_attach_cmd_t cmd);
129 static int mouse8042_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
133 * Streams module info.
135 #define MODULE_NAME "mouse8042"
137 static struct module_info mouse8042_minfo = {
138 23, /* Module ID number */
139 MODULE_NAME,
140 0, INFPSZ, /* minimum & maximum packet sizes */
141 256, 128 /* hi and low water marks */
144 static struct qinit mouse8042_rinit = {
145 NULL, /* put */
146 NULL, /* service */
147 mouse8042_open,
148 mouse8042_close,
149 NULL, /* admin */
150 &mouse8042_minfo,
151 NULL /* statistics */
154 static struct qinit mouse8042_winit = {
155 mouse8042_wput, /* put */
156 mouse8042_wsrv, /* service */
157 NULL, /* open */
158 NULL, /* close */
159 NULL, /* admin */
160 &mouse8042_minfo,
161 NULL /* statistics */
164 static struct streamtab mouse8042_strinfo = {
165 &mouse8042_rinit,
166 &mouse8042_winit,
167 NULL, /* muxrinit */
168 NULL, /* muxwinit */
172 * Local Function Declarations
175 static struct cb_ops mouse8042_cb_ops = {
176 nodev, /* open */
177 nodev, /* close */
178 nodev, /* strategy */
179 nodev, /* print */
180 nodev, /* dump */
181 nodev, /* read */
182 nodev, /* write */
183 nodev, /* ioctl */
184 nodev, /* devmap */
185 nodev, /* mmap */
186 nodev, /* segmap */
187 nochpoll, /* poll */
188 ddi_prop_op, /* cb_prop_op */
189 &mouse8042_strinfo, /* streamtab */
190 D_MP | D_NEW
194 static struct dev_ops mouse8042_ops = {
195 DEVO_REV, /* devo_rev, */
196 0, /* refcnt */
197 mouse8042_getinfo, /* getinfo */
198 nulldev, /* identify */
199 nulldev, /* probe */
200 mouse8042_attach, /* attach */
201 mouse8042_detach, /* detach */
202 nodev, /* reset */
203 &mouse8042_cb_ops, /* driver operations */
204 NULL, /* bus operations */
205 NULL, /* power */
206 ddi_quiesce_not_needed, /* quiesce */
210 * This is the loadable module wrapper.
212 #include <sys/modctl.h>
214 extern struct mod_ops mod_driverops;
217 * Module linkage information for the kernel.
220 static struct modldrv modldrv = {
221 &mod_driverops, /* Type of module. This one is a driver */
222 "PS/2 Mouse",
223 &mouse8042_ops, /* driver ops */
226 static struct modlinkage modlinkage = {
227 MODREV_1,
228 (void *)&modldrv,
229 NULL
233 * This is the driver initialization routine.
236 _init()
238 int rv;
240 rv = mod_install(&modlinkage);
241 return (rv);
246 _fini(void)
248 return (mod_remove(&modlinkage));
253 _info(struct modinfo *modinfop)
255 return (mod_info(&modlinkage, modinfop));
258 static int
259 mouse8042_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
261 struct mouse_state *state;
262 mblk_t *mp;
263 int instance = ddi_get_instance(dip);
264 static ddi_device_acc_attr_t attr = {
265 DDI_DEVICE_ATTR_V0,
266 DDI_NEVERSWAP_ACC,
267 DDI_STRICTORDER_ACC,
269 int rc;
272 if (cmd == DDI_RESUME) {
273 state = (struct mouse_state *)ddi_get_driver_private(dip);
275 /* Ready to handle inbound data from mouse8042_intr */
276 state->ready = 1;
279 * Send a 0xaa 0x00 upstream.
280 * This causes the vuid module to reset the mouse.
282 if (state->ms_rqp != NULL) {
283 if (mp = allocb(1, BPRI_MED)) {
284 *mp->b_wptr++ = 0xaa;
285 putnext(state->ms_rqp, mp);
287 if (mp = allocb(1, BPRI_MED)) {
288 *mp->b_wptr++ = 0x0;
289 putnext(state->ms_rqp, mp);
292 return (DDI_SUCCESS);
295 if (cmd != DDI_ATTACH)
296 return (DDI_FAILURE);
298 if (mouse8042_dip != NULL)
299 return (DDI_FAILURE);
301 /* allocate and initialize state structure */
302 state = kmem_zalloc(sizeof (struct mouse_state), KM_SLEEP);
303 state->ms_opened = B_FALSE;
304 state->reset_state = MSE_RESET_IDLE;
305 state->reset_tid = 0;
306 state->bc_id = 0;
307 ddi_set_driver_private(dip, state);
310 * In order to support virtual keyboard/mouse, we should distinguish
311 * between internal virtual open and external physical open.
313 * When the physical devices are opened by application, they will
314 * be unlinked from the virtual device and their data stream will
315 * not be sent to the virtual device. When the opened physical
316 * devices are closed, they will be relinked to the virtual devices.
318 * All these automatic switch between virtual and physical are
319 * transparent.
321 * So we change minor node numbering scheme to be:
322 * external node minor num == instance * 2
323 * internal node minor num == instance * 2 + 1
325 rc = ddi_create_minor_node(dip, "mouse", S_IFCHR, instance * 2,
326 DDI_NT_MOUSE, 0);
327 if (rc != DDI_SUCCESS) {
328 goto fail_1;
331 if (ddi_create_internal_pathname(dip, "internal_mouse", S_IFCHR,
332 instance * 2 + 1) != DDI_SUCCESS) {
333 goto fail_2;
336 rc = ddi_regs_map_setup(dip, 0, (caddr_t *)&state->ms_addr,
337 0, 0, &attr, &state->ms_handle);
338 if (rc != DDI_SUCCESS) {
339 goto fail_2;
342 rc = ddi_get_iblock_cookie(dip, 0, &state->ms_iblock_cookie);
343 if (rc != DDI_SUCCESS) {
344 goto fail_3;
347 mutex_init(&state->ms_mutex, NULL, MUTEX_DRIVER,
348 state->ms_iblock_cookie);
349 mutex_init(&state->reset_mutex, NULL, MUTEX_DRIVER,
350 state->ms_iblock_cookie);
351 cv_init(&state->reset_cv, NULL, CV_DRIVER, NULL);
353 rc = ddi_add_intr(dip, 0,
354 (ddi_iblock_cookie_t *)NULL, (ddi_idevice_cookie_t *)NULL,
355 mouse8042_intr, (caddr_t)state);
356 if (rc != DDI_SUCCESS) {
357 goto fail_3;
360 mouse8042_dip = dip;
362 /* Ready to handle inbound data from mouse8042_intr */
363 state->ready = 1;
365 /* Now that we're attached, announce our presence to the world. */
366 ddi_report_dev(dip);
367 return (DDI_SUCCESS);
369 fail_3:
370 ddi_regs_map_free(&state->ms_handle);
372 fail_2:
373 ddi_remove_minor_node(dip, NULL);
375 fail_1:
376 kmem_free(state, sizeof (struct mouse_state));
377 return (rc);
380 /*ARGSUSED*/
381 static int
382 mouse8042_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
384 struct mouse_state *state;
386 state = ddi_get_driver_private(dip);
388 switch (cmd) {
389 case DDI_SUSPEND:
390 /* Ignore all data from mouse8042_intr until we fully resume */
391 state->ready = 0;
392 return (DDI_SUCCESS);
394 case DDI_DETACH:
395 ddi_remove_intr(dip, 0, state->ms_iblock_cookie);
396 mouse8042_dip = NULL;
397 cv_destroy(&state->reset_cv);
398 mutex_destroy(&state->reset_mutex);
399 mutex_destroy(&state->ms_mutex);
400 ddi_prop_remove_all(dip);
401 ddi_regs_map_free(&state->ms_handle);
402 ddi_remove_minor_node(dip, NULL);
403 kmem_free(state, sizeof (struct mouse_state));
404 return (DDI_SUCCESS);
406 default:
407 return (DDI_FAILURE);
412 /* ARGSUSED */
413 static int
414 mouse8042_getinfo(
415 dev_info_t *dip,
416 ddi_info_cmd_t infocmd,
417 void *arg,
418 void **result)
420 dev_t dev = (dev_t)arg;
421 minor_t minor = getminor(dev);
422 int instance = MOUSE8042_MINOR_TO_INSTANCE(minor);
424 switch (infocmd) {
425 case DDI_INFO_DEVT2DEVINFO:
426 if (mouse8042_dip == NULL)
427 return (DDI_FAILURE);
429 *result = (void *)mouse8042_dip;
430 break;
431 case DDI_INFO_DEVT2INSTANCE:
432 *result = (void *)(uintptr_t)instance;
433 break;
434 default:
435 return (DDI_FAILURE);
437 return (DDI_SUCCESS);
440 /*ARGSUSED*/
441 static int
442 mouse8042_open(
443 queue_t *q,
444 dev_t *devp,
445 int flag,
446 int sflag,
447 cred_t *cred_p)
449 struct mouse_state *state;
450 minor_t minor = getminor(*devp);
451 int rval;
453 if (mouse8042_dip == NULL)
454 return (ENXIO);
456 state = ddi_get_driver_private(mouse8042_dip);
458 mutex_enter(&state->ms_mutex);
460 if (state->ms_opened) {
462 * Exit if the same minor node is already open
464 if (state->ms_minor == minor) {
465 mutex_exit(&state->ms_mutex);
466 return (0);
470 * Check whether it is switch between physical and virtual
472 * Opening from virtual while the device is being physically
473 * opened by an application should not happen. So we ASSERT
474 * this in DEBUG version, and return error in the non-DEBUG
475 * case.
477 ASSERT(!MOUSE8042_INTERNAL_OPEN(minor));
479 if (MOUSE8042_INTERNAL_OPEN(minor)) {
480 mutex_exit(&state->ms_mutex);
481 return (EINVAL);
485 * Opening the physical one while it is being underneath
486 * the virtual one.
488 * consconfig_unlink is called to unlink this device from
489 * the virtual one, thus the old stream serving for this
490 * device under the virtual one is closed, and then the
491 * lower driver's close routine (here is mouse8042_close)
492 * is also called to accomplish the whole stream close.
493 * Here we have to drop the lock because mouse8042_close
494 * also needs the lock.
496 * For mouse, the old stream is:
497 * consms->["pushmod"->]"mouse_vp driver"
499 * After the consconfig_unlink returns, the old stream is closed
500 * and we grab the lock again to reopen this device as normal.
502 mutex_exit(&state->ms_mutex);
505 * If unlink fails, fail the physical open.
507 if ((rval = consconfig_unlink(ddi_driver_major(mouse8042_dip),
508 MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
509 return (rval);
512 mutex_enter(&state->ms_mutex);
516 q->q_ptr = (caddr_t)state;
517 WR(q)->q_ptr = (caddr_t)state;
518 state->ms_rqp = q;
519 state->ms_wqp = WR(q);
521 qprocson(q);
523 state->ms_minor = minor;
524 state->ms_opened = B_TRUE;
526 mutex_exit(&state->ms_mutex);
528 return (0);
532 /*ARGSUSED*/
533 static int
534 mouse8042_close(queue_t *q, int flag, cred_t *cred_p)
536 struct mouse_state *state;
537 minor_t minor;
539 state = (struct mouse_state *)q->q_ptr;
542 * Disable queue processing now, so that another reset cannot get in
543 * after we wait for the current reset (if any) to complete.
545 qprocsoff(q);
547 mutex_enter(&state->reset_mutex);
548 while (state->reset_state != MSE_RESET_IDLE) {
550 * Waiting for the previous reset to finish is
551 * non-interruptible. Some upper-level clients
552 * cannot deal with EINTR and will not close the
553 * STREAM properly, resulting in failure to reopen it
554 * within the same process.
556 cv_wait(&state->reset_cv, &state->reset_mutex);
559 if (state->reset_tid != 0) {
560 (void) quntimeout(q, state->reset_tid);
561 state->reset_tid = 0;
564 if (state->reply_mp != NULL) {
565 freemsg(state->reply_mp);
566 state->reply_mp = NULL;
569 if (state->reset_ack_mp != NULL) {
570 freemsg(state->reset_ack_mp);
571 state->reset_ack_mp = NULL;
574 mutex_exit(&state->reset_mutex);
576 mutex_enter(&state->ms_mutex);
578 if (state->bc_id != 0) {
579 (void) qunbufcall(q, state->bc_id);
580 state->bc_id = 0;
583 q->q_ptr = NULL;
584 WR(q)->q_ptr = NULL;
585 state->ms_rqp = NULL;
586 state->ms_wqp = NULL;
588 state->ms_opened = B_FALSE;
590 minor = state->ms_minor;
592 mutex_exit(&state->ms_mutex);
594 if (!MOUSE8042_INTERNAL_OPEN(minor)) {
596 * Closing physical PS/2 mouse
598 * Link it back to virtual mouse, and
599 * mouse8042_open will be called as a result
600 * of the consconfig_link call. Do NOT try
601 * this if the mouse is about to be detached!
603 * If linking back fails, this specific mouse
604 * will not be available underneath the virtual
605 * mouse, and can only be accessed via physical
606 * open.
608 consconfig_link(ddi_driver_major(mouse8042_dip),
609 MOUSE8042_INTERNAL_MINOR(minor));
612 return (0);
615 static void
616 mouse8042_iocnack(
617 queue_t *qp,
618 mblk_t *mp,
619 struct iocblk *iocp,
620 int error,
621 int rval)
623 mp->b_datap->db_type = M_IOCNAK;
624 iocp->ioc_rval = rval;
625 iocp->ioc_error = error;
626 qreply(qp, mp);
629 static void
630 mouse8042_reset_timeout(void *argp)
632 struct mouse_state *state = (struct mouse_state *)argp;
633 mblk_t *mp;
635 mutex_enter(&state->reset_mutex);
638 * If the interrupt handler hasn't completed the reset handling
639 * (reset_state would be IDLE or FAILED in that case), then
640 * drop the 8042 lock, and send a faked retry reply upstream,
641 * then enable the queue for further message processing.
643 if (state->reset_state != MSE_RESET_IDLE &&
644 state->reset_state != MSE_RESET_FAILED) {
646 state->reset_tid = 0;
647 state->reset_state = MSE_RESET_IDLE;
648 cv_signal(&state->reset_cv);
650 (void) ddi_get8(state->ms_handle, state->ms_addr +
651 I8042_UNLOCK);
653 mp = state->reply_mp;
654 *mp->b_wptr++ = MSERESEND;
655 state->reply_mp = NULL;
657 if (state->ms_rqp != NULL)
658 putnext(state->ms_rqp, mp);
659 else
660 freemsg(mp);
662 ASSERT(state->ms_wqp != NULL);
664 enableok(state->ms_wqp);
665 qenable(state->ms_wqp);
668 mutex_exit(&state->reset_mutex);
672 * Returns 1 if the caller should put the message (bp) back on the queue
674 static int
675 mouse8042_initiate_reset(queue_t *q, mblk_t *mp, struct mouse_state *state)
677 mutex_enter(&state->reset_mutex);
679 * If we're in the middle of a reset, put the message back on the queue
680 * for processing later.
682 if (state->reset_state != MSE_RESET_IDLE) {
684 * We noenable the queue again here in case it was backenabled
685 * by an upper-level module.
687 noenable(q);
689 mutex_exit(&state->reset_mutex);
690 return (1);
694 * Drop the reset state lock before allocating the response message and
695 * grabbing the 8042 exclusive-access lock (since those operations
696 * may take an extended period of time to complete).
698 mutex_exit(&state->reset_mutex);
700 if (state->reply_mp == NULL)
701 state->reply_mp = allocb(2, BPRI_MED);
702 if (state->reset_ack_mp == NULL)
703 state->reset_ack_mp = allocb(1, BPRI_MED);
705 if (state->reply_mp == NULL || state->reset_ack_mp == NULL) {
707 * Allocation failed -- set up a bufcall to enable the queue
708 * whenever there is enough memory to allocate the response
709 * message.
711 state->bc_id = qbufcall(q, (state->reply_mp == NULL) ? 2 : 1,
712 BPRI_MED, (void (*)(void *))qenable, q);
714 if (state->bc_id == 0) {
716 * If the qbufcall failed, we cannot proceed, so use the
717 * message we were sent to respond with an error.
719 *mp->b_rptr = MSEERROR;
720 mp->b_wptr = mp->b_rptr + 1;
721 qreply(q, mp);
722 return (0);
725 return (1);
726 } else {
727 /* Bufcall completed successfully (or wasn't needed) */
728 state->bc_id = 0;
732 * Gain exclusive access to the 8042 for the duration of the reset.
733 * The unlock will occur when the reset has either completed or timed
734 * out.
736 (void) ddi_get8(state->ms_handle,
737 state->ms_addr + I8042_LOCK);
739 mutex_enter(&state->reset_mutex);
741 state->reset_state = MSE_RESET_PRE;
742 noenable(q);
744 state->reset_tid = qtimeout(q,
745 mouse8042_reset_timeout,
746 state,
747 drv_usectohz(
748 MOUSE8042_RESET_TIMEOUT_USECS));
750 ddi_put8(state->ms_handle,
751 state->ms_addr +
752 I8042_INT_OUTPUT_DATA, MSERESET);
754 mp->b_rptr++;
756 mutex_exit(&state->reset_mutex);
757 return (1);
761 * Returns 1 if the caller should stop processing messages
763 static int
764 mouse8042_process_data_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
766 mblk_t *bp;
767 mblk_t *next;
769 bp = mp;
770 do {
771 while (bp->b_rptr < bp->b_wptr) {
773 * Detect an attempt to reset the mouse. Lock out any
774 * further mouse writes until the reset has completed.
776 if (*bp->b_rptr == MSERESET) {
779 * If we couldn't allocate memory and we
780 * we couldn't register a bufcall,
781 * mouse8042_initiate_reset returns 0 and
782 * has already used the message to send an
783 * error reply back upstream, so there is no
784 * need to deallocate or put this message back
785 * on the queue.
787 if (mouse8042_initiate_reset(q, bp, state) == 0)
788 return (1);
791 * If there's no data remaining in this block,
792 * free this block and put the following blocks
793 * of this message back on the queue. If putting
794 * the rest of the message back on the queue
795 * fails, free the the message.
797 if (MBLKL(bp) == 0) {
798 next = bp->b_cont;
799 freeb(bp);
800 bp = next;
802 if (bp != NULL) {
803 if (!putbq(q, bp))
804 freemsg(bp);
807 return (1);
810 ddi_put8(state->ms_handle,
811 state->ms_addr + I8042_INT_OUTPUT_DATA,
812 *bp->b_rptr++);
814 next = bp->b_cont;
815 freeb(bp);
816 } while ((bp = next) != NULL);
818 return (0);
821 static int
822 mouse8042_process_msg(queue_t *q, mblk_t *mp, struct mouse_state *state)
824 struct iocblk *iocbp;
825 int rv = 0;
827 iocbp = (struct iocblk *)mp->b_rptr;
829 switch (mp->b_datap->db_type) {
830 case M_FLUSH:
831 if (*mp->b_rptr & FLUSHW) {
832 flushq(q, FLUSHDATA);
833 *mp->b_rptr &= ~FLUSHW;
835 if (*mp->b_rptr & FLUSHR) {
836 qreply(q, mp);
837 } else
838 freemsg(mp);
839 break;
840 case M_IOCTL:
841 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
842 break;
843 case M_IOCDATA:
844 mouse8042_iocnack(q, mp, iocbp, EINVAL, 0);
845 break;
846 case M_DATA:
847 rv = mouse8042_process_data_msg(q, mp, state);
848 break;
849 default:
850 freemsg(mp);
851 break;
854 return (rv);
858 * This is the main mouse input routine. Commands and parameters
859 * from upstream are sent to the mouse device immediately, unless
860 * the mouse is in the process of being reset, in which case
861 * commands are queued and executed later in the service procedure.
863 static int
864 mouse8042_wput(queue_t *q, mblk_t *mp)
866 struct mouse_state *state;
867 state = (struct mouse_state *)q->q_ptr;
870 * Process all messages immediately, unless a reset is in
871 * progress. If a reset is in progress, deflect processing to
872 * the service procedure.
874 if (state->reset_state != MSE_RESET_IDLE)
875 return (putq(q, mp));
878 * If there are still messages outstanding in the queue that
879 * the service procedure hasn't processed yet, put this
880 * message in the queue also, to ensure proper message
881 * ordering.
883 if (q->q_first)
884 return (putq(q, mp));
886 (void) mouse8042_process_msg(q, mp, state);
888 return (0);
891 static int
892 mouse8042_wsrv(queue_t *qp)
894 mblk_t *mp;
895 struct mouse_state *state;
896 state = (struct mouse_state *)qp->q_ptr;
898 while ((mp = getq(qp)) != NULL) {
899 if (mouse8042_process_msg(qp, mp, state) != 0)
900 break;
903 return (0);
907 * Returns the next reset state, given the current state and the byte
908 * received from the mouse. Error and Resend codes are handled by the
909 * caller.
911 static mouse8042_reset_state_e
912 mouse8042_reset_fsm(mouse8042_reset_state_e reset_state, uint8_t mdata)
914 switch (reset_state) {
915 case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */
916 if (mdata == MSE_ACK) /* Got the ACK */
917 return (MSE_RESET_ACK);
918 break;
920 case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */
921 if (mdata == MSE_AA) /* Got the 0xAA */
922 return (MSE_RESET_AA);
923 break;
925 case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */
926 if (mdata == MSE_00)
927 return (MSE_RESET_IDLE);
928 break;
931 return (reset_state);
934 static uint_t
935 mouse8042_intr(caddr_t arg)
937 unsigned char mdata;
938 mblk_t *mp;
939 struct mouse_state *state = (struct mouse_state *)arg;
940 int rc;
942 mutex_enter(&state->ms_mutex);
944 rc = DDI_INTR_UNCLAIMED;
946 for (;;) {
948 if (ddi_get8(state->ms_handle,
949 state->ms_addr + I8042_INT_INPUT_AVAIL) == 0) {
950 break;
953 mdata = ddi_get8(state->ms_handle,
954 state->ms_addr + I8042_INT_INPUT_DATA);
956 rc = DDI_INTR_CLAIMED;
959 * If we're not ready for this data, discard it.
961 if (!state->ready)
962 continue;
964 mutex_enter(&state->reset_mutex);
965 if (state->reset_state != MSE_RESET_IDLE) {
967 if (mdata == MSEERROR || mdata == MSERESET) {
968 state->reset_state = MSE_RESET_FAILED;
969 } else {
970 state->reset_state =
971 mouse8042_reset_fsm(state->reset_state,
972 mdata);
975 if (state->reset_state == MSE_RESET_ACK) {
978 * We received an ACK from the mouse, so
979 * send it upstream immediately so that
980 * consumers depending on the immediate
981 * ACK don't time out.
983 if (state->reset_ack_mp != NULL) {
985 mp = state->reset_ack_mp;
987 state->reset_ack_mp = NULL;
989 if (state->ms_rqp != NULL) {
990 *mp->b_wptr++ = MSE_ACK;
991 putnext(state->ms_rqp, mp);
992 } else
993 freemsg(mp);
996 if (state->ms_wqp != NULL) {
997 enableok(state->ms_wqp);
998 qenable(state->ms_wqp);
1001 } else if (state->reset_state == MSE_RESET_IDLE ||
1002 state->reset_state == MSE_RESET_FAILED) {
1005 * If we transitioned back to the idle reset state (or
1006 * the reset failed), disable the timeout, release the
1007 * 8042 exclusive-access lock, then send the response
1008 * the the upper-level modules. Finally, enable the
1009 * queue and schedule queue service procedures so that
1010 * upper-level modules can process the response.
1011 * Otherwise, if we're still in the middle of the
1012 * reset sequence, do not send the data up (since the
1013 * response is sent at the end of the sequence, or
1014 * on timeout/error).
1017 mutex_exit(&state->reset_mutex);
1018 (void) quntimeout(state->ms_wqp,
1019 state->reset_tid);
1020 mutex_enter(&state->reset_mutex);
1022 (void) ddi_get8(state->ms_handle,
1023 state->ms_addr + I8042_UNLOCK);
1025 state->reset_tid = 0;
1026 if (state->reply_mp != NULL) {
1027 mp = state->reply_mp;
1028 if (state->reset_state ==
1029 MSE_RESET_FAILED) {
1030 *mp->b_wptr++ = mdata;
1031 } else {
1032 *mp->b_wptr++ = MSE_AA;
1033 *mp->b_wptr++ = MSE_00;
1035 state->reply_mp = NULL;
1036 } else {
1037 mp = NULL;
1040 state->reset_state = MSE_RESET_IDLE;
1041 cv_signal(&state->reset_cv);
1043 if (mp != NULL) {
1044 if (state->ms_rqp != NULL)
1045 putnext(state->ms_rqp, mp);
1046 else
1047 freemsg(mp);
1050 if (state->ms_wqp != NULL) {
1051 enableok(state->ms_wqp);
1052 qenable(state->ms_wqp);
1056 mutex_exit(&state->reset_mutex);
1057 mutex_exit(&state->ms_mutex);
1058 return (rc);
1060 mutex_exit(&state->reset_mutex);
1062 if (state->ms_rqp != NULL && (mp = allocb(1, BPRI_MED))) {
1063 *mp->b_wptr++ = mdata;
1064 putnext(state->ms_rqp, mp);
1067 mutex_exit(&state->ms_mutex);
1069 return (rc);