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