consms.c revision d5007c14714a317d8cf986a22ec8ac39f16e168f
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Console mouse driver for Sun.
* The console "zs" port is linked under us, with the "ms" module pushed
* on top of it.
*
* have the "ms" module present. Due to problems with the way the "specfs"
* file system works, you can't use an indirect device (a "stat" on
* of last access), and due to problems with the kernel window system code,
* even though operations on it get turned into operations on the real stream).
*
* This module supports multiple mice connected to the system at the same time.
* All the mice are linked under consms, and act as a mouse with replicated
* clicks. Only USB and PS/2 mouse are supported to be virtual mouse now.
*/
#include <sys/vuid_wheel.h>
static void
static void consms_add_lq(consms_lq_t *);
static void consms_check_caps(void);
static mblk_t *consms_new_firm_event(int, int);
static void consms_mux_max_wheel_report(mblk_t *);
static void consms_mux_cache_states(mblk_t *);
static void consms_mux_link_msg(consms_msg_t *);
static void consms_mux_disp_data(mblk_t *);
static int consmsopen();
static int consmsclose();
static void consmsuwput();
static void consmslrput();
static void consmslwserv();
static struct module_info consmsm_info = {
0,
"consms",
0,
1024,
2048,
128
};
static struct qinit consmsurinit = {
putq,
(int (*)())NULL,
(int (*)())NULL,
};
static struct qinit consmsuwinit = {
(int (*)())consmsuwput,
(int (*)())NULL,
(int (*)())NULL,
};
static struct qinit consmslrinit = {
(int (*)())consmslrput,
(int (*)())NULL,
(int (*)())NULL,
(int (*)())NULL,
(int (*)())NULL,
};
static struct qinit consmslwinit = {
putq,
(int (*)())consmslwserv,
(int (*)())NULL,
(int (*)())NULL,
(int (*)())NULL,
};
static struct streamtab consms_str_info = {
};
void **result);
static int consms_kstat_update(kstat_t *, int);
/*
* Module global data are protected by the per-module inner perimeter.
*/
static long consms_idle_stamp; /* seconds tstamp of latest mouse op */
static kmutex_t consmslock;
/*
* Normally, kstats of type KSTAT_TYPE_NAMED have multiple elements. In
* this case we use this type for a single element because the ioctl code
* will be easier to add new statistics later.
*/
static struct {
} consms_kstat = {
{ "idle_sec", KSTAT_DATA_LONG, }
};
static struct cb_ops cb_consms_ops = {
nulldev, /* cb_open */
nulldev, /* cb_close */
nodev, /* cb_strategy */
nodev, /* cb_print */
nodev, /* cb_dump */
nodev, /* cb_read */
nodev, /* cb_write */
nodev, /* cb_ioctl */
nodev, /* cb_devmap */
nodev, /* cb_mmap */
nodev, /* cb_segmap */
nochpoll, /* cb_chpoll */
ddi_prop_op, /* cb_prop_op */
&consms_str_info, /* cb_stream */
};
static struct dev_ops consms_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
consms_info, /* devo_getinfo */
nulldev, /* devo_identify */
nulldev, /* devo_probe */
consms_attach, /* devo_attach */
consms_detach, /* devo_detach */
nodev, /* devo_reset */
&(cb_consms_ops), /* devo_cb_ops */
NULL /* devo_power */
};
/*
* Module linkage information for the kernel.
*/
&mod_driverops, /* Type of module. This one is a pseudo driver */
"Mouse Driver for Sun 'consms' %I%",
&consms_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
(void *)&modldrv,
};
int
_init(void)
{
int error;
if (error != 0) {
}
return (error);
}
int
_fini(void)
{
int error;
if (error != 0)
return (error);
return (0);
}
int
{
}
static int
{
switch (cmd) {
case DDI_ATTACH:
break;
default:
return (DDI_FAILURE);
}
return (-1);
}
consms_dip = devi;
if (ksp) {
}
/* default consms state values */
return (DDI_SUCCESS);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case DDI_DETACH:
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
void **result)
{
register int error;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
if (consms_dip == NULL) {
error = DDI_FAILURE;
} else {
*result = (void *) consms_dip;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
/*ARGSUSED*/
static int
queue_t *q;
{
upperqueue = q;
qprocson(q);
return (0);
}
/*ARGSUSED*/
static int
queue_t *q;
int flag;
{
qprocsoff(q);
upperqueue = NULL;
return (0);
}
/*
* Put procedure for upper write queue.
*/
static void
consmsuwput(q, mp)
register queue_t *q;
{
int error = 0;
case M_IOCTL:
consmsioctl(q, mp);
break;
case M_FLUSH:
if (consms_state.consms_num_lqs > 0) {
} else {
/*
* No lower queue; just reflect this back upstream.
*/
else
}
break;
case M_DATA:
if (consms_state.consms_num_lqs > 0) {
} else {
}
break;
case M_IOCDATA:
} else {
}
break;
default:
break;
}
if (error) {
/*
* Pass an error message up.
*/
}
}
}
static void
consmsioctl(q, mp)
register queue_t *q;
{
int error;
case I_LINK:
case I_PLINK:
consms_plink(q, mp);
return;
case I_UNLINK:
case I_PUNLINK:
return;
}
break;
case MSIOBUTTONS: /* query the number of buttons */
if ((consms_state.consms_num_lqs <= 0) ||
return;
}
}
break;
default:
/*
* Pass this through, if there's something to pass it
* through to; otherwise, reject it.
*/
if (consms_state.consms_num_lqs <= 0) {
return;
}
return;
}
/*
* Common exit path for calls that return a positive
* acknowledgment with a return value of 0.
*/
}
/*
* Service procedure for lower write queue.
* Puts things on the queue below us, if it lets us.
*/
static void
consmslwserv(q)
register queue_t *q;
{
}
/*
* Put procedure for lower read queue.
*/
static void
consmslrput(q, mp)
register queue_t *q;
{
case M_FLUSH:
if (upperqueue != NULL)
else {
/*
* No upper queue; just reflect this back downstream.
*/
else
}
break;
case M_DATA:
if (upperqueue != NULL)
else
break;
case M_IOCACK:
case M_IOCNAK:
/*
* First, check to see if this device
* is still being initialized.
*/
break;
}
/*
* This is normal ioctl ack for upper layer.
*/
} else {
}
break;
case M_COPYIN:
case M_COPYOUT:
} else
break;
case M_ERROR:
case M_HANGUP:
default:
break;
}
}
/* ARGSUSED */
static int
{
if (rw == KSTAT_WRITE)
return (EACCES);
return (0);
}
/*ARGSUSED*/
static int
{
if (prev_lq)
else
/*
* Check to see if mouse capabilities
* have changed.
*/
return (0);
}
}
return (EINVAL);
}
/*
* Link a specific mouse into our mouse list.
*/
static void
{
lq->lq_pending_queue = q;
/*
* Set the number of buttons to 3 by default
* in case the following MSIOBUTTONS ioctl fails.
*/
/*
* Begin to initialize this mouse.
*/
}
/*
* Initialize the newly hotplugged-in mouse,
* e.g. get the number of buttons, set event
* format. Then we add it into our list.
*/
static void
{
/*
* We try each ioctl even if the previous one fails
* until we reach LQS_DONE, and then add this lq
* into our lq list.
*
* If the message allocation fails, we skip this ioctl,
* set skipped flag to B_TRUE in order to skip the ioctl
* result, then we try next ioctl, go to next state.
*/
case LQS_START:
/*
* First, issue MSIOBUTTONS ioctl
* to get the number of buttons.
*/
}
break;
case LQS_BUTTON_COUNT_PENDING:
lq->lq_num_buttons =
/*
* Second, issue VUIDGWHEELCOUNT ioctl
* to get the count of wheels.
*/
}
break;
case LQS_WHEEL_COUNT_PENDING:
lq->lq_num_wheels =
/*
* Third, issue VUIDSFORMAT ioctl
* to set the event format.
*/
}
if (req) {
}
break;
/*
* Fourth, issue VUIDSWHEELSTATE ioctl
* to set the wheel state (enable or disable).
*/
}
if (req) {
ws->stateflags =
}
break;
/*
* Fifth, issue MSIOSETPARMS ioctl
* to set the parameters for USB mouse.
*/
}
if (req) {
}
break;
case LQS_SET_PARMS_PENDING:
/*
* Sixth, issue MSIOSRESOLUTION ioctl
* to set the screen resolution for absolute mouse.
*/
allocb(sizeof (Ms_screen_resolution),
}
if (req) {
sr =
sizeof (Ms_screen_resolution);
}
break;
/*
* All jobs are done, lq->lq_state is turned into
* LQS_DONE, and this lq is added into our list.
*/
break;
}
}
}
}
/*
* Add this specific lq into our list, finally reply
* the previous pending I_PLINK ioctl. Also check to
* see if mouse capabilities have changed, and send
* a dynamical notification event to upper layer if
* necessary.
*/
static void
{
/* Reply to the I_PLINK ioctl. */
/*
* Add this lq into list.
*/
/*
* Check to see if mouse capabilities
* have changed.
*/
}
static void
consms_check_caps(void)
{
int max_buttons = 0;
int max_wheels = 0;
/*
* Check to see if the number of buttons
* and the number of wheels have changed.
*/
}
/*
* Since the number of buttons have changed,
* send a MOUSE_CAP_CHANGE_NUM_BUT dynamical
* notification event to upper layer.
*/
if (upperqueue != NULL) {
if ((mp = consms_new_firm_event(
}
}
}
/*
* Since the number of wheels have changed,
* send a MOUSE_CAP_CHANGE_NUM_WHEEL dynamical
* notification event to upper layer.
*/
if (upperqueue != NULL) {
if ((mp = consms_new_firm_event(
}
}
}
}
/*
* Allocate a dynamical notification event.
*/
static mblk_t *
{
}
return (tmp);
}
/*
* Start of dispatching interfaces as a multiplexor
*/
/*
* There is a global msg list (consms_mux_msg),
* which is used to link all ioctl messages from
* upper layer, which are currently being processed.
*
* consms_mux_link_msg links a msg into the list,
* consms_mux_unlink_msg unlinks a msg from the list,
* consms_mux_find_msg finds a msg from the list
* according to its unique id.
*
* The id of each msg is taken from stream's mp,
* so the id is supposed to be unique.
*/
static void
{
}
static consms_msg_t *
{
break;
}
} else {
}
}
return (msg);
}
static consms_msg_t *
{
break;
}
return (msg);
}
/*
* Received ACK or NAK from lower mice
*
* For non-transparent ioctl, the msg->msg_rsp_list
* is always NULL; for transparent ioctl, it
* messages from lower mice. So here if msg->msg_rsp_list
* are done with this specific ioctl.
*
* As long as one of lower mice responds success,
* we treat it success for a ioctl.
*/
static void
{
/* increment response_nums */
msg->msg_num_responses++;
/*
* Received ACK from lower, then
* this is the last step for both
* non-transparent and transparent
* ioctl. We only need to remember
* one of the ACKs, finally reply
* this ACK to upper layer for this
* specific ioctl.
*/
}
}
/*
* Check to see if all lower mice have responded
* to our dispatching ioctl.
*/
/*
* All are NAKed.
*/
/*
* The last step and at least one ACKed.
*/
} else {
/*
* This is a NAK, but we have
* already received M_COPYIN
* or M_COPYOUT request from
* at least one of lower mice.
* (msg->msg_rsp_list != NULL)
*
* Still copyin or copyout.
*/
}
/*
* We are done with this ioctl.
*/
if (msg->msg_request)
}
}
if (mp) {
}
}
/*
* Received M_COPYIN or M_COPYOUT request from
* lower mice for transparent ioctl
*
* msg->msg_rsp_list, reply upper layer using the first
* all responses from lower mice, even if some of
* them return NAKs.
*/
static void
{
if (msg->msg_rsp_list) {
}
msg->msg_num_responses++;
}
}
/*
* request with the mp of M_IOCDATA, then put it
* down to lower mice.
*/
static void
{
/*
* Update the rval.
*/
/*
* Update the db_type to M_IOCDATA.
*/
/*
* Update the b_cont.
*/
}
}
/*
* Put it down.
*/
}
/*
* Dispatch M_IOCDATA down to all lower mice
* for transparent ioctl.
*
* msg->msg_rsp_list with the M_IOCDATA.
*/
static void
{
int request_nums;
/*
* We should remember the ioc data for
* VUIDSWHEELSTATE, and MSIOSRESOLUTION,
* for we will cache the wheel state and
* the screen resolution later if ACKed.
*/
}
/*
* Update request numbers and response numbers.
*/
msg->msg_num_responses = 0;
request_nums = 1;
/*
* in the msg_rsp_list to reply upper layer, the mp
* of M_IOCDATA can be directly used for that.
*/
request_nums++;
}
/* Must set the request number before the last q. */
/* the first one */
}
/*
* Here we update the number of wheels with
* the virtual mouse for VUIDGWHEELCOUNT ioctl.
*/
static void
{
int num_wheels;
return;
}
}
}
/*
* Update the virtual mouse state variables with
* the latest value from upper layer when these
* set ioctls return success. Thus we can update
* low mice with the latest state values during
* hotplug.
*/
static void
{
return;
case VUIDSFORMAT:
break;
case MSIOSETPARMS:
break;
case MSIOSRESOLUTION:
break;
case VUIDSWHEELSTATE:
break;
}
}
/*
* Dispatch ioctl mp (non-transparent and transparent)
* down to all lower mice.
*
* First, create a pending message for this mp, link it into
* ioctl.
*/
static int
{
int error = 0;
} else {
/*
* If copymsg fails, we ignore this lq and
* try next one. As long as one of them succeeds,
* we dispatch this ioctl down. And later as long
* as one of the lower drivers return success, we
* reply to this ioctl with success.
*/
msg->msg_num_requests--;
}
}
if (msg->msg_num_requests <= 0) {
/*
* Since copymsg fails for all lqs, we NAK this ioctl.
*/
}
return (error);
}
/*
* Dispatch M_DATA and M_FLUSH message down to all
* lower mice, and there are no acknowledgements
* for them. Here we just copy the mp and then
* put it into the lower queues.
*/
static void
{
}
}
}