xref: /titanic_52/usr/src/uts/common/io/srn.c (revision b785f896a48d487b0368ef79094294b7f0e9b1d5)
12df1fe9cSrandyf /*
22df1fe9cSrandyf  * CDDL HEADER START
32df1fe9cSrandyf  *
42df1fe9cSrandyf  * The contents of this file are subject to the terms of the
52df1fe9cSrandyf  * Common Development and Distribution License (the "License").
62df1fe9cSrandyf  * You may not use this file except in compliance with the License.
72df1fe9cSrandyf  *
82df1fe9cSrandyf  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92df1fe9cSrandyf  * or http://www.opensolaris.org/os/licensing.
102df1fe9cSrandyf  * See the License for the specific language governing permissions
112df1fe9cSrandyf  * and limitations under the License.
122df1fe9cSrandyf  *
132df1fe9cSrandyf  * When distributing Covered Code, include this CDDL HEADER in each
142df1fe9cSrandyf  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152df1fe9cSrandyf  * If applicable, add the following below this CDDL HEADER, with the
162df1fe9cSrandyf  * fields enclosed by brackets "[]" replaced with your own identifying
172df1fe9cSrandyf  * information: Portions Copyright [yyyy] [name of copyright owner]
182df1fe9cSrandyf  *
192df1fe9cSrandyf  * CDDL HEADER END
202df1fe9cSrandyf  */
212df1fe9cSrandyf 
222df1fe9cSrandyf /*
232fc97812SRandy Fishel  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
242df1fe9cSrandyf  * Use is subject to license terms.
252df1fe9cSrandyf  */
262df1fe9cSrandyf 
272df1fe9cSrandyf 
282df1fe9cSrandyf /*
292df1fe9cSrandyf  * srn	Provide apm-like interfaces to Xorg
302df1fe9cSrandyf  */
312df1fe9cSrandyf 
322df1fe9cSrandyf #include <sys/types.h>
332df1fe9cSrandyf #include <sys/errno.h>
342df1fe9cSrandyf #include <sys/modctl.h>
352df1fe9cSrandyf #include <sys/conf.h>		/* driver flags and functions */
362df1fe9cSrandyf #include <sys/open.h>		/* OTYP_CHR definition */
372df1fe9cSrandyf #include <sys/stat.h>		/* S_IFCHR definition */
382df1fe9cSrandyf #include <sys/pathname.h>	/* name -> dev_info xlation */
392df1fe9cSrandyf #include <sys/kmem.h>		/* memory alloc stuff */
402df1fe9cSrandyf #include <sys/debug.h>
412df1fe9cSrandyf #include <sys/pm.h>
422df1fe9cSrandyf #include <sys/ddi.h>
432df1fe9cSrandyf #include <sys/sunddi.h>
442df1fe9cSrandyf #include <sys/epm.h>
452df1fe9cSrandyf #include <sys/vfs.h>
462df1fe9cSrandyf #include <sys/mode.h>
472df1fe9cSrandyf #include <sys/mkdev.h>
482df1fe9cSrandyf #include <sys/promif.h>
492df1fe9cSrandyf #include <sys/consdev.h>
502df1fe9cSrandyf #include <sys/ddi_impldefs.h>
512df1fe9cSrandyf #include <sys/poll.h>
522df1fe9cSrandyf #include <sys/note.h>
532df1fe9cSrandyf #include <sys/taskq.h>
542df1fe9cSrandyf #include <sys/policy.h>
552df1fe9cSrandyf #include <sys/srn.h>
562df1fe9cSrandyf 
572df1fe9cSrandyf /*
582df1fe9cSrandyf  * Minor number is instance<<8 + clone minor from range 1-255;
592df1fe9cSrandyf  * But only one will be allocated
602df1fe9cSrandyf  */
612df1fe9cSrandyf #define	SRN_MINOR_TO_CLONE(minor) ((minor) & (SRN_MAX_CLONE - 1))
622df1fe9cSrandyf #define	SU		0x002
632df1fe9cSrandyf #define	SG		0x004
642df1fe9cSrandyf 
652df1fe9cSrandyf extern kmutex_t	srn_clone_lock;	/* protects srn_clones array */
662df1fe9cSrandyf extern kcondvar_t srn_clones_cv[SRN_MAX_CLONE];
672df1fe9cSrandyf extern uint_t	srn_poll_cnt[SRN_MAX_CLONE];
682df1fe9cSrandyf 
692df1fe9cSrandyf /*
702df1fe9cSrandyf  * The soft state of the srn driver.  Since there will only be
712df1fe9cSrandyf  * one of these, just reference it through a static struct.
722df1fe9cSrandyf  */
732df1fe9cSrandyf static struct srnstate {
742df1fe9cSrandyf 	dev_info_t	*srn_dip;		/* ptr to our dev_info node */
752df1fe9cSrandyf 	int		srn_instance;		/* for ddi_get_instance() */
762df1fe9cSrandyf 	uchar_t		srn_clones[SRN_MAX_CLONE]; /* unique opens	*/
772df1fe9cSrandyf 	struct cred	*srn_cred[SRN_MAX_CLONE]; /* cred for each open	*/
782df1fe9cSrandyf 	int		srn_type[SRN_MAX_CLONE]; /* type of handshake */
792df1fe9cSrandyf 	int		srn_delivered[SRN_MAX_CLONE];
802df1fe9cSrandyf 	srn_event_info_t srn_pending[SRN_MAX_CLONE];
812fc97812SRandy Fishel 	int		srn_fault[SRN_MAX_CLONE];
822df1fe9cSrandyf } srn = { NULL, -1};
832df1fe9cSrandyf typedef struct srnstate *srn_state_t;
842df1fe9cSrandyf 
852df1fe9cSrandyf kcondvar_t	srn_clones_cv[SRN_MAX_CLONE];
862df1fe9cSrandyf uint_t		srn_poll_cnt[SRN_MAX_CLONE];	/* count of events for poll */
872df1fe9cSrandyf int		srn_apm_count;
882df1fe9cSrandyf int		srn_autosx_count;
892fc97812SRandy Fishel /* Number of seconds to wait for clients to ack a poll */
902fc97812SRandy Fishel int		srn_timeout = 10;
912fc97812SRandy Fishel 
922df1fe9cSrandyf struct pollhead	srn_pollhead[SRN_MAX_CLONE];
932df1fe9cSrandyf 
942df1fe9cSrandyf static int	srn_open(dev_t *, int, int, cred_t *);
952df1fe9cSrandyf static int	srn_close(dev_t, int, int, cred_t *);
962df1fe9cSrandyf static int	srn_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
972df1fe9cSrandyf static int	srn_chpoll(dev_t, short, int, short *, struct pollhead **);
982df1fe9cSrandyf 
992df1fe9cSrandyf static struct cb_ops srn_cb_ops = {
1002df1fe9cSrandyf 	srn_open,	/* open */
1012df1fe9cSrandyf 	srn_close,	/* close */
1022df1fe9cSrandyf 	nodev,		/* strategy */
1032df1fe9cSrandyf 	nodev,		/* print */
1042df1fe9cSrandyf 	nodev,		/* dump */
1052df1fe9cSrandyf 	nodev,		/* read */
1062df1fe9cSrandyf 	nodev,		/* write */
1072df1fe9cSrandyf 	srn_ioctl,	/* ioctl */
1082df1fe9cSrandyf 	nodev,		/* devmap */
1092df1fe9cSrandyf 	nodev,		/* mmap */
1102df1fe9cSrandyf 	nodev,		/* segmap */
1112df1fe9cSrandyf 	srn_chpoll,	/* poll */
1122df1fe9cSrandyf 	ddi_prop_op,	/* prop_op */
1132df1fe9cSrandyf 	NULL,		/* streamtab */
1142df1fe9cSrandyf 	D_NEW | D_MP	/* driver compatibility flag */
1152df1fe9cSrandyf };
1162df1fe9cSrandyf 
1172df1fe9cSrandyf static int srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1182df1fe9cSrandyf     void **result);
1192df1fe9cSrandyf static int srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
1202df1fe9cSrandyf static int srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
1212df1fe9cSrandyf static void srn_notify(int type, int event);
1222df1fe9cSrandyf 
1232df1fe9cSrandyf static struct dev_ops srn_ops = {
1242df1fe9cSrandyf 	DEVO_REV,		/* devo_rev */
1252df1fe9cSrandyf 	0,			/* refcnt */
1262df1fe9cSrandyf 	srn_getinfo,		/* info */
1272df1fe9cSrandyf 	nulldev,		/* identify */
1282df1fe9cSrandyf 	nulldev,		/* probe */
1292df1fe9cSrandyf 	srn_attach,		/* attach */
1302df1fe9cSrandyf 	srn_detach,		/* detach */
1312df1fe9cSrandyf 	nodev,			/* reset */
1322df1fe9cSrandyf 	&srn_cb_ops,		/* driver operations */
1332df1fe9cSrandyf 	NULL,			/* bus operations */
13419397407SSherry Moore 	NULL,			/* power */
13519397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
1362df1fe9cSrandyf };
1372df1fe9cSrandyf 
1382df1fe9cSrandyf static struct modldrv modldrv = {
1392df1fe9cSrandyf 	&mod_driverops,
14019397407SSherry Moore 	"srn driver",
1412df1fe9cSrandyf 	&srn_ops
1422df1fe9cSrandyf };
1432df1fe9cSrandyf 
1442df1fe9cSrandyf static struct modlinkage modlinkage = {
1452df1fe9cSrandyf 	MODREV_1, &modldrv, 0
1462df1fe9cSrandyf };
1472df1fe9cSrandyf 
1482df1fe9cSrandyf /* Local functions */
1492df1fe9cSrandyf 
1502df1fe9cSrandyf int
1512df1fe9cSrandyf _init(void)
1522df1fe9cSrandyf {
1532df1fe9cSrandyf 	return (mod_install(&modlinkage));
1542df1fe9cSrandyf }
1552df1fe9cSrandyf 
1562df1fe9cSrandyf int
1572df1fe9cSrandyf _fini(void)
1582df1fe9cSrandyf {
1592df1fe9cSrandyf 	return (mod_remove(&modlinkage));
1602df1fe9cSrandyf }
1612df1fe9cSrandyf 
1622df1fe9cSrandyf int
1632df1fe9cSrandyf _info(struct modinfo *modinfop)
1642df1fe9cSrandyf {
1652df1fe9cSrandyf 	return (mod_info(&modlinkage, modinfop));
1662df1fe9cSrandyf }
1672df1fe9cSrandyf 
1682df1fe9cSrandyf static int
1692df1fe9cSrandyf srn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1702df1fe9cSrandyf {
1712df1fe9cSrandyf 	int		i;
1722df1fe9cSrandyf 	extern void (*srn_signal)(int, int);
1732df1fe9cSrandyf 
1742df1fe9cSrandyf 	switch (cmd) {
1752df1fe9cSrandyf 
1762df1fe9cSrandyf 	case DDI_ATTACH:
1772df1fe9cSrandyf 		if (srn.srn_instance != -1)	/* Only allow one instance */
1782df1fe9cSrandyf 			return (DDI_FAILURE);
1792df1fe9cSrandyf 		srn.srn_instance = ddi_get_instance(dip);
1802df1fe9cSrandyf 		if (ddi_create_minor_node(dip, "srn", S_IFCHR,
1812df1fe9cSrandyf 		    (srn.srn_instance << 8) + 0, DDI_PSEUDO, 0)
1822df1fe9cSrandyf 		    != DDI_SUCCESS) {
1832df1fe9cSrandyf 			return (DDI_FAILURE);
1842df1fe9cSrandyf 		}
1852df1fe9cSrandyf 		srn.srn_dip = dip;	/* srn_init and getinfo depend on it */
1862df1fe9cSrandyf 
1872df1fe9cSrandyf 		for (i = 0; i < SRN_MAX_CLONE; i++)
1882df1fe9cSrandyf 			cv_init(&srn_clones_cv[i], NULL, CV_DEFAULT, NULL);
1892df1fe9cSrandyf 
1902df1fe9cSrandyf 		srn.srn_instance = ddi_get_instance(dip);
1912df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
1922df1fe9cSrandyf 		srn_signal = srn_notify;
1932df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
1942df1fe9cSrandyf 		ddi_report_dev(dip);
1952df1fe9cSrandyf 		return (DDI_SUCCESS);
1962df1fe9cSrandyf 
1972df1fe9cSrandyf 	default:
1982df1fe9cSrandyf 		return (DDI_FAILURE);
1992df1fe9cSrandyf 	}
2002df1fe9cSrandyf }
2012df1fe9cSrandyf 
2022df1fe9cSrandyf /* ARGSUSED */
2032df1fe9cSrandyf static int
2042df1fe9cSrandyf srn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2052df1fe9cSrandyf {
2062df1fe9cSrandyf 	int i;
2072df1fe9cSrandyf 	extern int srn_inuse;
2082df1fe9cSrandyf 	extern void (*srn_signal)(int, int);
2092df1fe9cSrandyf 
2102df1fe9cSrandyf 	switch (cmd) {
2112df1fe9cSrandyf 	case DDI_DETACH:
2122df1fe9cSrandyf 
2132df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
2142df1fe9cSrandyf 		while (srn_inuse) {
2152df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
2162df1fe9cSrandyf 			delay(1);
2172df1fe9cSrandyf 			mutex_enter(&srn_clone_lock);
2182df1fe9cSrandyf 		}
2192df1fe9cSrandyf 		srn_signal = NULL;
2202df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
2212df1fe9cSrandyf 
2222df1fe9cSrandyf 		for (i = 0; i < SRN_MAX_CLONE; i++)
2232df1fe9cSrandyf 			cv_destroy(&srn_clones_cv[i]);
2242df1fe9cSrandyf 
2252df1fe9cSrandyf 		ddi_remove_minor_node(dip, NULL);
2262df1fe9cSrandyf 		srn.srn_instance = -1;
2272df1fe9cSrandyf 		return (DDI_SUCCESS);
2282df1fe9cSrandyf 
2292df1fe9cSrandyf 	default:
2302df1fe9cSrandyf 		return (DDI_FAILURE);
2312df1fe9cSrandyf 	}
2322df1fe9cSrandyf }
2332df1fe9cSrandyf 
2342df1fe9cSrandyf 
2352df1fe9cSrandyf #ifdef DEBUG
2362df1fe9cSrandyf char *srn_cmd_string;
2372df1fe9cSrandyf int srn_cmd;
2382df1fe9cSrandyf #endif
2392df1fe9cSrandyf 
2402df1fe9cSrandyf /*
2412df1fe9cSrandyf  * Returns true if permission granted by credentials
2422df1fe9cSrandyf  * XXX
2432df1fe9cSrandyf  */
2442df1fe9cSrandyf static int
2452df1fe9cSrandyf srn_perms(int perm, cred_t *cr)
2462df1fe9cSrandyf {
2472df1fe9cSrandyf 	if ((perm & SU) && secpolicy_power_mgmt(cr) == 0) /* privileged? */
2482df1fe9cSrandyf 		return (1);
2492df1fe9cSrandyf 	if ((perm & SG) && (crgetgid(cr) == 0))	/* group 0 is ok */
2502df1fe9cSrandyf 		return (1);
2512df1fe9cSrandyf 	return (0);
2522df1fe9cSrandyf }
2532df1fe9cSrandyf 
2542df1fe9cSrandyf static int
2552df1fe9cSrandyf srn_chpoll(dev_t dev, short events, int anyyet, short *reventsp,
2562df1fe9cSrandyf 	struct pollhead **phpp)
2572df1fe9cSrandyf {
2582fc97812SRandy Fishel 	extern struct pollhead srn_pollhead[];
2592df1fe9cSrandyf 	int	clone;
2602df1fe9cSrandyf 
2612df1fe9cSrandyf 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
2622df1fe9cSrandyf 	if ((events & (POLLIN | POLLRDNORM)) && srn_poll_cnt[clone]) {
2632df1fe9cSrandyf 		*reventsp |= (POLLIN | POLLRDNORM);
2642df1fe9cSrandyf 	} else {
2652df1fe9cSrandyf 		*reventsp = 0;
2662df1fe9cSrandyf 		if (!anyyet) {
2672df1fe9cSrandyf 			*phpp = &srn_pollhead[clone];
2682df1fe9cSrandyf 		}
2692df1fe9cSrandyf 	}
2702df1fe9cSrandyf 	return (0);
2712df1fe9cSrandyf }
2722df1fe9cSrandyf 
2732df1fe9cSrandyf /*ARGSUSED*/
2742df1fe9cSrandyf static int
2752df1fe9cSrandyf srn_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2762df1fe9cSrandyf {
2772df1fe9cSrandyf 	dev_t	dev;
2782df1fe9cSrandyf 	int	instance;
2792df1fe9cSrandyf 
2802df1fe9cSrandyf 	switch (infocmd) {
2812df1fe9cSrandyf 	case DDI_INFO_DEVT2DEVINFO:
2822df1fe9cSrandyf 		if (srn.srn_instance == -1)
2832df1fe9cSrandyf 			return (DDI_FAILURE);
2842df1fe9cSrandyf 		*result = srn.srn_dip;
2852df1fe9cSrandyf 		return (DDI_SUCCESS);
2862df1fe9cSrandyf 
2872df1fe9cSrandyf 	case DDI_INFO_DEVT2INSTANCE:
2882df1fe9cSrandyf 		dev = (dev_t)arg;
2892df1fe9cSrandyf 		instance = getminor(dev) >> 8;
2902df1fe9cSrandyf 		*result = (void *)(uintptr_t)instance;
2912df1fe9cSrandyf 		return (DDI_SUCCESS);
2922df1fe9cSrandyf 
2932df1fe9cSrandyf 	default:
2942df1fe9cSrandyf 		return (DDI_FAILURE);
2952df1fe9cSrandyf 	}
2962df1fe9cSrandyf }
2972df1fe9cSrandyf 
2982df1fe9cSrandyf 
2992df1fe9cSrandyf /*ARGSUSED1*/
3002df1fe9cSrandyf static int
3012df1fe9cSrandyf srn_open(dev_t *devp, int flag, int otyp, cred_t *cr)
3022df1fe9cSrandyf {
3032df1fe9cSrandyf 	int		clone;
3042df1fe9cSrandyf 
3052df1fe9cSrandyf 	if (otyp != OTYP_CHR)
3062df1fe9cSrandyf 		return (EINVAL);
3072df1fe9cSrandyf 
3082df1fe9cSrandyf 	mutex_enter(&srn_clone_lock);
3092df1fe9cSrandyf 	for (clone = 1; clone < SRN_MAX_CLONE - 1; clone++)
3102df1fe9cSrandyf 		if (!srn.srn_clones[clone])
3112df1fe9cSrandyf 			break;
3122df1fe9cSrandyf 
3132df1fe9cSrandyf 	if (clone == SRN_MAX_CLONE) {
3142df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
3152df1fe9cSrandyf 		return (ENXIO);
3162df1fe9cSrandyf 	}
3172df1fe9cSrandyf 	srn.srn_cred[clone] = cr;
3182df1fe9cSrandyf 	ASSERT(srn_apm_count >= 0);
3192df1fe9cSrandyf 	srn_apm_count++;
3202df1fe9cSrandyf 	srn.srn_type[clone] = SRN_TYPE_APM;
3212df1fe9cSrandyf 	crhold(cr);
3222df1fe9cSrandyf 
3232df1fe9cSrandyf 	*devp = makedevice(getmajor(*devp), (srn.srn_instance << 8) +
3242df1fe9cSrandyf 	    clone);
3252df1fe9cSrandyf 	srn.srn_clones[clone] = 1;
3262df1fe9cSrandyf 	srn.srn_cred[clone] = cr;
3272df1fe9cSrandyf 	crhold(cr);
3282df1fe9cSrandyf 	mutex_exit(&srn_clone_lock);
3292df1fe9cSrandyf 	PMD(PMD_SX, ("srn open OK\n"))
3302df1fe9cSrandyf 	return (0);
3312df1fe9cSrandyf }
3322df1fe9cSrandyf 
3332df1fe9cSrandyf /*ARGSUSED1*/
3342df1fe9cSrandyf static int
3352df1fe9cSrandyf srn_close(dev_t dev, int flag, int otyp, cred_t *cr)
3362df1fe9cSrandyf {
3372df1fe9cSrandyf 	int clone;
3382df1fe9cSrandyf 
3392df1fe9cSrandyf 	if (otyp != OTYP_CHR)
3402df1fe9cSrandyf 		return (EINVAL);
3412df1fe9cSrandyf 
3422df1fe9cSrandyf 	clone = SRN_MINOR_TO_CLONE(getminor(dev));
3432df1fe9cSrandyf 	PMD(PMD_SX, ("srn_close: minor %x, clone %x\n", getminor(dev),
3442df1fe9cSrandyf 	    clone))
3452df1fe9cSrandyf 	mutex_enter(&srn_clone_lock);
3462df1fe9cSrandyf 	crfree(srn.srn_cred[clone]);
3472df1fe9cSrandyf 	srn.srn_cred[clone] = 0;
3482df1fe9cSrandyf 	srn_poll_cnt[clone] = 0;
3492fc97812SRandy Fishel 	srn.srn_fault[clone] = 0;
3502df1fe9cSrandyf 	if (srn.srn_pending[clone].ae_type || srn.srn_delivered[clone]) {
3512df1fe9cSrandyf 		srn.srn_pending[clone].ae_type = 0;
3522df1fe9cSrandyf 		srn.srn_delivered[clone] = 0;
3532df1fe9cSrandyf 		cv_signal(&srn_clones_cv[clone]);
3542df1fe9cSrandyf 	}
3552df1fe9cSrandyf 	switch (srn.srn_type[clone]) {
3562df1fe9cSrandyf 	case SRN_TYPE_AUTOSX:
3572df1fe9cSrandyf 		ASSERT(srn_autosx_count);
3582df1fe9cSrandyf 		srn_autosx_count--;
3592df1fe9cSrandyf 		break;
3602df1fe9cSrandyf 	case SRN_TYPE_APM:
3612df1fe9cSrandyf 		ASSERT(srn_apm_count);
3622df1fe9cSrandyf 		srn_apm_count--;
3632df1fe9cSrandyf 		break;
3642df1fe9cSrandyf 	default:
3652df1fe9cSrandyf 		ASSERT(0);
3662df1fe9cSrandyf 		return (EINVAL);
3672df1fe9cSrandyf 	}
3682df1fe9cSrandyf 	srn.srn_clones[clone] = 0;
3692df1fe9cSrandyf 	mutex_exit(&srn_clone_lock);
3702df1fe9cSrandyf 	return (0);
3712df1fe9cSrandyf }
3722df1fe9cSrandyf 
3732df1fe9cSrandyf /*ARGSUSED*/
3742df1fe9cSrandyf static int
3752df1fe9cSrandyf srn_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval_p)
3762df1fe9cSrandyf {
3772df1fe9cSrandyf 	int clone = SRN_MINOR_TO_CLONE(getminor(dev));
3782df1fe9cSrandyf 
3792df1fe9cSrandyf 	PMD(PMD_SX, ("ioctl: %x: begin\n", cmd))
3802df1fe9cSrandyf 
3812df1fe9cSrandyf 	switch (cmd) {
3822df1fe9cSrandyf 	case SRN_IOC_NEXTEVENT:
3832df1fe9cSrandyf 	case SRN_IOC_SUSPEND:
3842df1fe9cSrandyf 	case SRN_IOC_RESUME:
3852df1fe9cSrandyf 	case SRN_IOC_AUTOSX:
3862df1fe9cSrandyf 		break;
3872df1fe9cSrandyf 	default:
3882df1fe9cSrandyf 		return (ENOTTY);
3892df1fe9cSrandyf 	}
3902df1fe9cSrandyf 
3912df1fe9cSrandyf 	if (!srn_perms(SU | SG, srn.srn_cred[clone])) {
3922df1fe9cSrandyf 		return (EPERM);
3932df1fe9cSrandyf 	}
3942df1fe9cSrandyf 	switch (cmd) {
3952df1fe9cSrandyf 	case SRN_IOC_AUTOSX:
3962df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_AUTOSX entered\n"))
3972df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
3982df1fe9cSrandyf 		if (!srn.srn_clones[clone]) {
3992df1fe9cSrandyf 			PMD(PMD_SX, (" ioctl !srn_clones--EINVAL\n"))
4002df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4012df1fe9cSrandyf 			return (EINVAL);
4022df1fe9cSrandyf 		}
4032df1fe9cSrandyf 		if (srn.srn_pending[clone].ae_type) {
4042df1fe9cSrandyf 			PMD(PMD_SX, ("AUTOSX while pending--EBUSY\n"))
4052df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4062df1fe9cSrandyf 			return (EBUSY);
4072df1fe9cSrandyf 		}
4082df1fe9cSrandyf 		if (srn.srn_type[clone] == SRN_TYPE_AUTOSX) {
4092df1fe9cSrandyf 			PMD(PMD_SX, ("AUTOSX already--EBUSY\n"))
4102df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4112df1fe9cSrandyf 			return (EBUSY);
4122df1fe9cSrandyf 		}
4132df1fe9cSrandyf 		ASSERT(srn.srn_type[clone] == SRN_TYPE_APM);
4142df1fe9cSrandyf 		srn.srn_type[clone] = SRN_TYPE_AUTOSX;
4152fc97812SRandy Fishel 		srn.srn_fault[clone] = 0;
4162df1fe9cSrandyf 		srn_apm_count--;
4172df1fe9cSrandyf 		ASSERT(srn_apm_count >= 0);
4182df1fe9cSrandyf 		ASSERT(srn_autosx_count >= 0);
4192df1fe9cSrandyf 		srn_autosx_count++;
4202df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
4212df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_AUTOSX returns success\n"))
4222df1fe9cSrandyf 		return (0);
4232df1fe9cSrandyf 
4242df1fe9cSrandyf 	case SRN_IOC_NEXTEVENT:
4252df1fe9cSrandyf 		/*
4262df1fe9cSrandyf 		 * return the next suspend or resume event;  there should
4272df1fe9cSrandyf 		 * be one, cause we only get called if we've signalled a
4282df1fe9cSrandyf 		 * poll data completion
4292df1fe9cSrandyf 		 * then wake up the kernel thread sleeping for the delivery
4302df1fe9cSrandyf 		 */
4312df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT entered\n"))
4322fc97812SRandy Fishel 		if (srn.srn_fault[clone]) {
4332fc97812SRandy Fishel 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d fault "
4342fc97812SRandy Fishel 			    "cleared\n", clone))
4352fc97812SRandy Fishel 			srn.srn_fault[clone] = 0;
4362fc97812SRandy Fishel 		}
4372df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
4382df1fe9cSrandyf 		if (srn_poll_cnt[clone] == 0) {
4392df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4402df1fe9cSrandyf 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d "
4412df1fe9cSrandyf 			    "EWOULDBLOCK\n", clone))
4422df1fe9cSrandyf 			return (EWOULDBLOCK);
4432df1fe9cSrandyf 		}
4442df1fe9cSrandyf 		ASSERT(srn.srn_pending[clone].ae_type);
4452df1fe9cSrandyf 		if (ddi_copyout(&srn.srn_pending[clone], (void *)arg,
4462df1fe9cSrandyf 		    sizeof (srn_event_info_t), mode) != 0) {
4472df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4482df1fe9cSrandyf 			PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d EFAULT\n",
4492df1fe9cSrandyf 			    clone))
4502df1fe9cSrandyf 			return (EFAULT);
4512df1fe9cSrandyf 		}
4522df1fe9cSrandyf 		if (srn.srn_type[clone] == SRN_TYPE_APM)
4532df1fe9cSrandyf 			srn.srn_delivered[clone] =
4542df1fe9cSrandyf 			    srn.srn_pending[clone].ae_type;
4552df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_NEXTEVENT clone %d delivered %x\n",
4562df1fe9cSrandyf 		    clone, srn.srn_pending[clone].ae_type))
4572df1fe9cSrandyf 		srn_poll_cnt[clone] = 0;
4582df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
4592df1fe9cSrandyf 		return (0);
4602df1fe9cSrandyf 
4612df1fe9cSrandyf 	case SRN_IOC_SUSPEND:
4622df1fe9cSrandyf 		/* ack suspend */
4632df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_SUSPEND entered clone %d\n", clone))
4642fc97812SRandy Fishel 		if (srn.srn_fault[clone]) {
4652fc97812SRandy Fishel 			PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d fault "
4662fc97812SRandy Fishel 			    "cleared\n", clone))
4672fc97812SRandy Fishel 			srn.srn_fault[clone] = 0;
4682fc97812SRandy Fishel 		}
4692df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
4702df1fe9cSrandyf 		if (srn.srn_delivered[clone] != SRN_SUSPEND_REQ) {
4712df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4722df1fe9cSrandyf 			PMD(PMD_SX, ("SRN_IOC_SUSPEND EINVAL\n"))
4732df1fe9cSrandyf 			return (EINVAL);
4742df1fe9cSrandyf 		}
4752df1fe9cSrandyf 		srn.srn_delivered[clone] = 0;
4762df1fe9cSrandyf 		srn.srn_pending[clone].ae_type = 0;
4772df1fe9cSrandyf 		/* notify the kernel suspend thread  to continue */
4782df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_SUSPEND clone %d ok\n", clone))
4792df1fe9cSrandyf 		cv_signal(&srn_clones_cv[clone]);
4802df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
4812df1fe9cSrandyf 		return (0);
4822df1fe9cSrandyf 
4832df1fe9cSrandyf 	case SRN_IOC_RESUME:
4842df1fe9cSrandyf 		/* ack resume */
4852df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_RESUME entered clone %d\n", clone))
4862fc97812SRandy Fishel 		if (srn.srn_fault[clone]) {
4872fc97812SRandy Fishel 			PMD(PMD_SX, ("SRN_IOC_RESUME clone %d fault "
4882fc97812SRandy Fishel 			    "cleared\n", clone))
4892fc97812SRandy Fishel 			srn.srn_fault[clone] = 0;
4902fc97812SRandy Fishel 		}
4912df1fe9cSrandyf 		mutex_enter(&srn_clone_lock);
4922df1fe9cSrandyf 		if (srn.srn_delivered[clone] != SRN_NORMAL_RESUME) {
4932df1fe9cSrandyf 			mutex_exit(&srn_clone_lock);
4942df1fe9cSrandyf 			PMD(PMD_SX, ("SRN_IOC_RESUME EINVAL\n"))
4952df1fe9cSrandyf 			return (EINVAL);
4962df1fe9cSrandyf 		}
4972df1fe9cSrandyf 		srn.srn_delivered[clone] = 0;
4982df1fe9cSrandyf 		srn.srn_pending[clone].ae_type = 0;
4992df1fe9cSrandyf 		/* notify the kernel resume thread  to continue */
5002df1fe9cSrandyf 		PMD(PMD_SX, ("SRN_IOC_RESUME ok for clone %d\n", clone))
5012df1fe9cSrandyf 		cv_signal(&srn_clones_cv[clone]);
5022df1fe9cSrandyf 		mutex_exit(&srn_clone_lock);
5032df1fe9cSrandyf 		return (0);
5042df1fe9cSrandyf 
5052df1fe9cSrandyf 	default:
5062df1fe9cSrandyf 		PMD(PMD_SX, ("srn_ioctl unknown cmd EINVAL\n"))
5072df1fe9cSrandyf 		return (EINVAL);
5082df1fe9cSrandyf 	}
5092df1fe9cSrandyf }
5102df1fe9cSrandyf /*
5112df1fe9cSrandyf  * A very simple handshake with the srn driver,
5122df1fe9cSrandyf  * only one outstanding event at a time.
5132df1fe9cSrandyf  * The OS delivers the event and depending on type,
5142df1fe9cSrandyf  * either blocks waiting for the ack, or drives on
5152df1fe9cSrandyf  */
5162df1fe9cSrandyf void
5172df1fe9cSrandyf srn_notify(int type, int event)
5182df1fe9cSrandyf {
5192df1fe9cSrandyf 	int clone, count;
5202df1fe9cSrandyf 	PMD(PMD_SX, ("srn_notify entered with type %d, event 0x%x\n",
5212df1fe9cSrandyf 	    type, event));
5222df1fe9cSrandyf 	ASSERT(mutex_owned(&srn_clone_lock));
5232df1fe9cSrandyf 	switch (type) {
5242df1fe9cSrandyf 	case SRN_TYPE_APM:
5252df1fe9cSrandyf 		if (srn_apm_count == 0) {
5262df1fe9cSrandyf 			PMD(PMD_SX, ("no apm types\n"))
5272df1fe9cSrandyf 			return;
5282df1fe9cSrandyf 		}
5292df1fe9cSrandyf 		count = srn_apm_count;
5302df1fe9cSrandyf 		break;
5312df1fe9cSrandyf 	case SRN_TYPE_AUTOSX:
5322df1fe9cSrandyf 		if (srn_autosx_count == 0) {
5332df1fe9cSrandyf 			PMD(PMD_SX, ("no autosx types\n"))
5342df1fe9cSrandyf 			return;
5352df1fe9cSrandyf 		}
5362df1fe9cSrandyf 		count = srn_autosx_count;
5372df1fe9cSrandyf 		break;
5382df1fe9cSrandyf 	default:
5392df1fe9cSrandyf 		ASSERT(0);
5402df1fe9cSrandyf 		break;
5412df1fe9cSrandyf 	}
5422df1fe9cSrandyf 	ASSERT(count > 0);
5432df1fe9cSrandyf 	PMD(PMD_SX, ("count %d\n", count))
5442df1fe9cSrandyf 	for (clone = 0; clone < SRN_MAX_CLONE; clone++) {
5452df1fe9cSrandyf 		if (srn.srn_type[clone] == type) {
5462fc97812SRandy Fishel #ifdef DEBUG
547*b785f896SRandy Fishel 			if (type == SRN_TYPE_APM && !srn.srn_fault[clone]) {
5482df1fe9cSrandyf 				ASSERT(srn.srn_pending[clone].ae_type == 0);
5492df1fe9cSrandyf 				ASSERT(srn_poll_cnt[clone] == 0);
5502df1fe9cSrandyf 				ASSERT(srn.srn_delivered[clone] == 0);
5512df1fe9cSrandyf 			}
5522fc97812SRandy Fishel #endif
5532df1fe9cSrandyf 			srn.srn_pending[clone].ae_type = event;
5542df1fe9cSrandyf 			srn_poll_cnt[clone] = 1;
5552df1fe9cSrandyf 			PMD(PMD_SX, ("pollwake %d\n", clone))
5562df1fe9cSrandyf 			pollwakeup(&srn_pollhead[clone], (POLLRDNORM | POLLIN));
5572df1fe9cSrandyf 			count--;
5582df1fe9cSrandyf 			if (count == 0)
5592df1fe9cSrandyf 				break;
5602df1fe9cSrandyf 		}
5612df1fe9cSrandyf 	}
5622df1fe9cSrandyf 	if (type == SRN_TYPE_AUTOSX) {		/* we don't wait */
5632df1fe9cSrandyf 		PMD(PMD_SX, ("Not waiting for AUTOSX ack\n"))
5642df1fe9cSrandyf 		return;
5652df1fe9cSrandyf 	}
5662df1fe9cSrandyf 	ASSERT(type == SRN_TYPE_APM);
5672df1fe9cSrandyf 	/* otherwise wait for acks */
5682df1fe9cSrandyf restart:
5692df1fe9cSrandyf 	/*
5702fc97812SRandy Fishel 	 * We wait until all of the pending events are cleared.
5712df1fe9cSrandyf 	 * We have to start over every time we do a cv_wait because
5722df1fe9cSrandyf 	 * we give up the mutex and can be re-entered
5732df1fe9cSrandyf 	 */
5742df1fe9cSrandyf 	for (clone = 1; clone < SRN_MAX_CLONE; clone++) {
5752df1fe9cSrandyf 		if (srn.srn_clones[clone] == 0 ||
5762df1fe9cSrandyf 		    srn.srn_type[clone] != SRN_TYPE_APM)
5772df1fe9cSrandyf 			continue;
5782fc97812SRandy Fishel 		if (srn.srn_pending[clone].ae_type && !srn.srn_fault[clone]) {
5792df1fe9cSrandyf 			PMD(PMD_SX, ("srn_notify waiting for ack for clone %d, "
5802df1fe9cSrandyf 			    "event %x\n", clone, event))
5812fc97812SRandy Fishel 			if (cv_timedwait(&srn_clones_cv[clone],
5822fc97812SRandy Fishel 			    &srn_clone_lock, ddi_get_lbolt() +
5832fc97812SRandy Fishel 			    drv_usectohz(srn_timeout * 1000000)) == -1) {
5842fc97812SRandy Fishel 				/*
5852fc97812SRandy Fishel 				 * Client didn't respond, mark it as faulted
5862fc97812SRandy Fishel 				 * and continue as if a regular signal.
5872fc97812SRandy Fishel 				 */
5882fc97812SRandy Fishel 				PMD(PMD_SX, ("srn_notify: clone %d did not "
5892fc97812SRandy Fishel 				    "ack event %x\n", clone, event))
5902fc97812SRandy Fishel 				cmn_err(CE_WARN, "srn_notify: clone %d did "
5912fc97812SRandy Fishel 				    "not ack event %x\n", clone, event);
5922fc97812SRandy Fishel 				srn.srn_fault[clone] = 1;
5932fc97812SRandy Fishel 			}
5942df1fe9cSrandyf 			goto restart;
5952df1fe9cSrandyf 		}
5962df1fe9cSrandyf 	}
5972df1fe9cSrandyf 	PMD(PMD_SX, ("srn_notify done with %x\n", event))
5982df1fe9cSrandyf }
599