/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* srn Provide apm-like interfaces to Xorg
*/
#include <sys/ddi_impldefs.h>
/*
* Minor number is instance<<8 + clone minor from range 1-255;
* But only one will be allocated
*/
/*
* The soft state of the srn driver. Since there will only be
* one of these, just reference it through a static struct.
*/
static struct srnstate {
int srn_apm_count;
int srn_autosx_count;
/* Number of seconds to wait for clients to ack a poll */
srn_open, /* open */
srn_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
srn_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
srn_chpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
};
void **result);
DEVO_REV, /* devo_rev */
0, /* refcnt */
srn_getinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
srn_attach, /* attach */
srn_detach, /* detach */
nodev, /* reset */
&srn_cb_ops, /* driver operations */
NULL, /* bus operations */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
"srn driver",
};
};
/* Local functions */
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
{
}
static int
{
int i;
extern void (*srn_signal)(int, int);
switch (cmd) {
case DDI_ATTACH:
return (DDI_FAILURE);
!= DDI_SUCCESS) {
return (DDI_FAILURE);
}
for (i = 0; i < SRN_MAX_CLONE; i++)
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/* ARGSUSED */
static int
{
int i;
extern int srn_inuse;
extern void (*srn_signal)(int, int);
switch (cmd) {
case DDI_DETACH:
while (srn_inuse) {
delay(1);
}
srn_signal = NULL;
for (i = 0; i < SRN_MAX_CLONE; i++)
cv_destroy(&srn_clones_cv[i]);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
#ifdef DEBUG
char *srn_cmd_string;
int srn_cmd;
#endif
/*
* Returns true if permission granted by credentials
* XXX
*/
static int
{
return (1);
return (1);
return (0);
}
static int
{
extern struct pollhead srn_pollhead[];
int clone;
} else {
*reventsp = 0;
if (!anyyet) {
}
}
return (0);
}
/*ARGSUSED*/
static int
{
int instance;
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED1*/
static int
{
int clone;
return (EINVAL);
break;
if (clone == SRN_MAX_CLONE) {
return (ENXIO);
}
ASSERT(srn_apm_count >= 0);
clone);
return (0);
}
/*ARGSUSED1*/
static int
{
int clone;
return (EINVAL);
clone))
srn_poll_cnt[clone] = 0;
}
case SRN_TYPE_AUTOSX:
break;
case SRN_TYPE_APM:
break;
default:
ASSERT(0);
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
switch (cmd) {
case SRN_IOC_NEXTEVENT:
case SRN_IOC_SUSPEND:
case SRN_IOC_RESUME:
case SRN_IOC_AUTOSX:
break;
default:
return (ENOTTY);
}
return (EPERM);
}
switch (cmd) {
case SRN_IOC_AUTOSX:
return (EINVAL);
}
return (EBUSY);
}
return (EBUSY);
}
ASSERT(srn_apm_count >= 0);
ASSERT(srn_autosx_count >= 0);
return (0);
case SRN_IOC_NEXTEVENT:
/*
* return the next suspend or resume event; there should
* be one, cause we only get called if we've signalled a
* poll data completion
* then wake up the kernel thread sleeping for the delivery
*/
"cleared\n", clone))
}
if (srn_poll_cnt[clone] == 0) {
"EWOULDBLOCK\n", clone))
return (EWOULDBLOCK);
}
sizeof (srn_event_info_t), mode) != 0) {
clone))
return (EFAULT);
}
srn_poll_cnt[clone] = 0;
return (0);
case SRN_IOC_SUSPEND:
/* ack suspend */
"cleared\n", clone))
}
return (EINVAL);
}
/* notify the kernel suspend thread to continue */
return (0);
case SRN_IOC_RESUME:
/* ack resume */
"cleared\n", clone))
}
return (EINVAL);
}
/* notify the kernel resume thread to continue */
return (0);
default:
return (EINVAL);
}
}
/*
* A very simple handshake with the srn driver,
* only one outstanding event at a time.
* The OS delivers the event and depending on type,
* either blocks waiting for the ack, or drives on
*/
void
{
switch (type) {
case SRN_TYPE_APM:
if (srn_apm_count == 0) {
return;
}
break;
case SRN_TYPE_AUTOSX:
if (srn_autosx_count == 0) {
return;
}
break;
default:
ASSERT(0);
break;
}
#ifdef DEBUG
}
#endif
count--;
if (count == 0)
break;
}
}
return;
}
/* otherwise wait for acks */
/*
* We wait until all of the pending events are cleared.
* We have to start over every time we do a cv_wait because
* we give up the mutex and can be re-entered
*/
continue;
&srn_clone_lock, ddi_get_lbolt() +
/*
* Client didn't respond, mark it as faulted
* and continue as if a regular signal.
*/
}
goto restart;
}
}
}