/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* PS/2 type Mouse Module - Streams
*/
/*
*
* Local Static Data
*
*/
/*
* We only support one instance. Yes, it's theoretically possible to
* plug in more than one, but it's not worth the implementation cost.
*
* The introduction of USB keyboards might make it worth reassessing
* this decision, as they might free up the keyboard port for a second
* PS/2 style mouse.
*/
/*
* RESET states
*/
typedef enum {
struct mouse_state {
int ready;
};
/*
* Streams module info.
*/
23, /* Module ID number */
0, INFPSZ, /* minimum & maximum packet sizes */
256, 128 /* hi and low water marks */
};
NULL, /* put */
NULL, /* service */
NULL, /* admin */
NULL /* statistics */
};
mouse8042_wput, /* put */
mouse8042_wsrv, /* service */
NULL, /* open */
NULL, /* close */
NULL, /* admin */
NULL /* statistics */
};
NULL, /* muxrinit */
NULL, /* muxwinit */
};
/*
* Local Function Declarations
*/
nodev, /* open */
nodev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
&mouse8042_strinfo, /* streamtab */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
mouse8042_getinfo, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
mouse8042_attach, /* attach */
mouse8042_detach, /* detach */
nodev, /* reset */
&mouse8042_cb_ops, /* driver operations */
(struct bus_ops *)0, /* bus operations */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
/*
* This is the loadable module wrapper.
*/
extern struct mod_ops mod_driverops;
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a driver */
"PS/2 Mouse",
&mouse8042_ops, /* driver ops */
};
(void *)&modldrv,
};
/*
* This is the driver initialization routine.
*/
int
_init()
{
int rv;
return (rv);
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static int
{
};
int rc;
if (cmd == DDI_RESUME) {
/* Ready to handle inbound data from mouse8042_intr */
/*
* Send a 0xaa 0x00 upstream.
* This causes the vuid module to reset the mouse.
*/
}
}
}
return (DDI_SUCCESS);
}
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (mouse8042_dip != NULL)
return (DDI_FAILURE);
/* allocate and initialize state structure */
/*
* between internal virtual open and external physical open.
*
* When the physical devices are opened by application, they will
* be unlinked from the virtual device and their data stream will
* not be sent to the virtual device. When the opened physical
* devices are closed, they will be relinked to the virtual devices.
*
* All these automatic switch between virtual and physical are
* transparent.
*
* So we change minor node numbering scheme to be:
* external node minor num == instance * 2
* internal node minor num == instance * 2 + 1
*/
DDI_NT_MOUSE, NULL);
if (rc != DDI_SUCCESS) {
goto fail_1;
}
goto fail_2;
}
if (rc != DDI_SUCCESS) {
goto fail_2;
}
if (rc != DDI_SUCCESS) {
goto fail_3;
}
if (rc != DDI_SUCCESS) {
goto fail_3;
}
mouse8042_dip = dip;
/* Ready to handle inbound data from mouse8042_intr */
/* Now that we're attached, announce our presence to the world. */
return (DDI_SUCCESS);
return (rc);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_SUSPEND:
/* Ignore all data from mouse8042_intr until we fully resume */
return (DDI_SUCCESS);
case DDI_DETACH:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
void *arg,
void **result)
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (mouse8042_dip == NULL)
return (DDI_FAILURE);
*result = (void *)mouse8042_dip;
break;
case DDI_INFO_DEVT2INSTANCE:
break;
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
queue_t *q,
int flag,
int sflag,
{
int rval;
if (mouse8042_dip == NULL)
return (ENXIO);
/*
* Exit if the same minor node is already open
*/
return (0);
}
/*
* Check whether it is switch between physical and virtual
*
* Opening from virtual while the device is being physically
* opened by an application should not happen. So we ASSERT
* this in DEBUG version, and return error in the non-DEBUG
* case.
*/
if (MOUSE8042_INTERNAL_OPEN(minor)) {
return (EINVAL);
}
/*
* Opening the physical one while it is being underneath
* the virtual one.
*
* consconfig_unlink is called to unlink this device from
* the virtual one, thus the old stream serving for this
* device under the virtual one is closed, and then the
* lower driver's close routine (here is mouse8042_close)
* is also called to accomplish the whole stream close.
* Here we have to drop the lock because mouse8042_close
* also needs the lock.
*
* For mouse, the old stream is:
* consms->["pushmod"->]"mouse_vp driver"
*
* After the consconfig_unlink returns, the old stream is closed
* and we grab the lock again to reopen this device as normal.
*/
/*
* If unlink fails, fail the physical open.
*/
MOUSE8042_INTERNAL_MINOR(minor))) != 0) {
return (rval);
}
}
qprocson(q);
return (0);
}
/*ARGSUSED*/
static int
{
/*
* Disable queue processing now, so that another reset cannot get in
* after we wait for the current reset (if any) to complete.
*/
qprocsoff(q);
/*
* Waiting for the previous reset to finish is
* non-interruptible. Some upper-level clients
* cannot deal with EINTR and will not close the
* STREAM properly, resulting in failure to reopen it
* within the same process.
*/
}
}
}
}
}
if (!MOUSE8042_INTERNAL_OPEN(minor)) {
/*
* Closing physical PS/2 mouse
*
* Link it back to virtual mouse, and
* mouse8042_open will be called as a result
* of the consconfig_link call. Do NOT try
* this if the mouse is about to be detached!
*
* If linking back fails, this specific mouse
* will not be available underneath the virtual
* mouse, and can only be accessed via physical
* open.
*/
}
return (0);
}
static void
int error,
int rval)
{
}
static void
{
/*
* If the interrupt handler hasn't completed the reset handling
* (reset_state would be IDLE or FAILED in that case), then
* drop the 8042 lock, and send a faked retry reply upstream,
* then enable the queue for further message processing.
*/
else
}
}
/*
* Returns 1 if the caller should put the message (bp) back on the queue
*/
static int
{
/*
* If we're in the middle of a reset, put the message back on the queue
* for processing later.
*/
/*
* We noenable the queue again here in case it was backenabled
* by an upper-level module.
*/
noenable(q);
return (1);
}
/*
* Drop the reset state lock before allocating the response message and
* grabbing the 8042 exclusive-access lock (since those operations
* may take an extended period of time to complete).
*/
/*
* Allocation failed -- set up a bufcall to enable the queue
* whenever there is enough memory to allocate the response
* message.
*/
/*
* If the qbufcall failed, we cannot proceed, so use the
* message we were sent to respond with an error.
*/
return (0);
}
return (1);
} else {
/* Bufcall completed successfully (or wasn't needed) */
}
/*
* Gain exclusive access to the 8042 for the duration of the reset.
* The unlock will occur when the reset has either completed or timed
* out.
*/
noenable(q);
return (1);
}
/*
* Returns 1 if the caller should stop processing messages
*/
static int
{
do {
/*
* Detect an attempt to reset the mouse. Lock out any
* further mouse writes until the reset has completed.
*/
/*
* If we couldn't allocate memory and we
* we couldn't register a bufcall,
* mouse8042_initiate_reset returns 0 and
* has already used the message to send an
* error reply back upstream, so there is no
* need to deallocate or put this message back
* on the queue.
*/
return (1);
/*
* If there's no data remaining in this block,
* free this block and put the following blocks
* of this message back on the queue. If putting
* the rest of the message back on the queue
* fails, free the the message.
*/
}
}
return (1);
}
}
return (0);
}
static int
{
int rv = 0;
case M_FLUSH:
}
} else
break;
case M_IOCTL:
break;
case M_IOCDATA:
break;
case M_DATA:
break;
default:
break;
}
return (rv);
}
/*
* This is the main mouse input routine. Commands and parameters
* from upstream are sent to the mouse device immediately, unless
* the mouse is in the process of being reset, in which case
* commands are queued and executed later in the service procedure.
*/
static int
{
/*
* Process all messages immediately, unless a reset is in
* progress. If a reset is in progress, deflect processing to
* the service procedure.
*/
/*
* If there are still messages outstanding in the queue that
* the service procedure hasn't processed yet, put this
* message in the queue also, to ensure proper message
* ordering.
*/
if (q->q_first)
return (0);
}
static int
{
break;
}
return (0);
}
/*
* Returns the next reset state, given the current state and the byte
* received from the mouse. Error and Resend codes are handled by the
* caller.
*/
static mouse8042_reset_state_e
{
switch (reset_state) {
case MSE_RESET_PRE: /* RESET sent, now we expect an ACK */
return (MSE_RESET_ACK);
break;
case MSE_RESET_ACK: /* ACK received; now we expect 0xAA */
return (MSE_RESET_AA);
break;
case MSE_RESET_AA: /* 0xAA received; now we expect 0x00 */
return (MSE_RESET_IDLE);
break;
}
return (reset_state);
}
static uint_t
{
unsigned char mdata;
int rc;
for (;;) {
break;
}
/*
* If we're not ready for this data, discard it.
*/
continue;
} else {
state->reset_state =
mdata);
}
/*
* We received an ACK from the mouse, so
* send it upstream immediately so that
* consumers depending on the immediate
* ACK don't time out.
*/
} else
}
}
/*
* If we transitioned back to the idle reset state (or
* the reset failed), disable the timeout, release the
* 8042 exclusive-access lock, then send the response
* the the upper-level modules. Finally, enable the
* queue and schedule queue service procedures so that
* upper-level modules can process the response.
* Otherwise, if we're still in the middle of the
* reset sequence, do not send the data up (since the
* response is sent at the end of the sequence, or
*/
if (state->reset_state ==
} else {
}
} else {
}
else
}
}
}
return (rc);
}
}
}
return (rc);
}