xref: /illumos-gate/usr/src/uts/sun4v/io/vcc.c (revision bbf21555)
11ae08745Sheppo /*
21ae08745Sheppo  * CDDL HEADER START
31ae08745Sheppo  *
41ae08745Sheppo  * The contents of this file are subject to the terms of the
51ae08745Sheppo  * Common Development and Distribution License (the "License").
61ae08745Sheppo  * You may not use this file except in compliance with the License.
71ae08745Sheppo  *
81ae08745Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91ae08745Sheppo  * or http://www.opensolaris.org/os/licensing.
101ae08745Sheppo  * See the License for the specific language governing permissions
111ae08745Sheppo  * and limitations under the License.
121ae08745Sheppo  *
131ae08745Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141ae08745Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151ae08745Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161ae08745Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171ae08745Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181ae08745Sheppo  *
191ae08745Sheppo  * CDDL HEADER END
201ae08745Sheppo  */
211ae08745Sheppo 
221ae08745Sheppo /*
2319397407SSherry Moore  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241ae08745Sheppo  * Use is subject to license terms.
251ae08745Sheppo  */
261ae08745Sheppo 
27a5eb7107SBryan Cantrill /*
28a5eb7107SBryan Cantrill  * Copyright (c) 2014, Joyent, Inc. All rights reserved.
29a5eb7107SBryan Cantrill  */
301ae08745Sheppo 
311ae08745Sheppo #include <sys/types.h>
321ae08745Sheppo #include <sys/file.h>
331ae08745Sheppo #include <sys/errno.h>
341ae08745Sheppo #include <sys/uio.h>
351ae08745Sheppo #include <sys/open.h>
361ae08745Sheppo #include <sys/cred.h>
371ae08745Sheppo #include <sys/kmem.h>
381ae08745Sheppo #include <sys/conf.h>
391ae08745Sheppo #include <sys/cmn_err.h>
401ae08745Sheppo #include <sys/ksynch.h>
411ae08745Sheppo #include <sys/modctl.h>
421ae08745Sheppo #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */
431ae08745Sheppo #include <sys/debug.h>
441ae08745Sheppo #include <sys/promif.h>
451ae08745Sheppo #include <sys/ddi.h>
461ae08745Sheppo #include <sys/sunddi.h>
471ae08745Sheppo #include <sys/cyclic.h>
481ae08745Sheppo #include <sys/termio.h>
491ae08745Sheppo #include <sys/intr.h>
501ae08745Sheppo #include <sys/ivintr.h>
511ae08745Sheppo #include <sys/note.h>
521ae08745Sheppo #include <sys/stat.h>
531ae08745Sheppo #include <sys/fcntl.h>
541ae08745Sheppo #include <sys/sysmacros.h>
551ae08745Sheppo 
561ae08745Sheppo #include <sys/ldc.h>
571ae08745Sheppo #include <sys/mdeg.h>
581ae08745Sheppo #include <sys/vcc_impl.h>
591ae08745Sheppo 
60445b4c2eSsb #define	VCC_LDC_RETRIES		5
61445b4c2eSsb #define	VCC_LDC_DELAY		1000 /* usec */
62445b4c2eSsb 
631ae08745Sheppo /*
641ae08745Sheppo  * Function prototypes.
651ae08745Sheppo  */
661ae08745Sheppo 
671ae08745Sheppo /* DDI entrypoints */
681ae08745Sheppo static int	vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
691ae08745Sheppo static int	vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
701ae08745Sheppo static int	vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
711ae08745Sheppo static int	vcc_close(dev_t dev, int flag, int otyp, cred_t *cred);
721ae08745Sheppo static int	vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
731ae08745Sheppo 			cred_t *credp, int *rvalp);
741ae08745Sheppo static int	vcc_read(dev_t dev, struct uio *uiop, cred_t *credp);
751ae08745Sheppo static int	vcc_write(dev_t dev, struct uio *uiop, cred_t *credp);
761ae08745Sheppo static int	vcc_chpoll(dev_t dev, short events, int anyyet,
771ae08745Sheppo 			short *reventsp, struct pollhead **phpp);
781ae08745Sheppo static int	vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
791ae08745Sheppo 			void *arg, void **resultp);
801ae08745Sheppo 
811ae08745Sheppo /* callback functions */
821ae08745Sheppo static uint_t	vcc_ldc_cb(uint64_t event, caddr_t arg);
831ae08745Sheppo static int	vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
841ae08745Sheppo 
851ae08745Sheppo /* Internal functions */
861ae08745Sheppo static int	i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport);
871ae08745Sheppo static int	i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
881ae08745Sheppo 			uint_t portno, char *domain_name);
891ae08745Sheppo static int	i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id);
901ae08745Sheppo static int	i_vcc_reset_events(vcc_t *vccp);
911ae08745Sheppo static int	i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports,
921ae08745Sheppo 			caddr_t buf, int mode);
931ae08745Sheppo static int	i_vcc_del_cons_ok(vcc_t *vccp, caddr_t buf, int mode);
941ae08745Sheppo static int	i_vcc_close_port(vcc_port_t *vport);
951ae08745Sheppo static int	i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf);
967636cb21Slm static int	i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz);
971ae08745Sheppo 
981ae08745Sheppo static void *vcc_ssp;
991ae08745Sheppo 
1001ae08745Sheppo static struct cb_ops vcc_cb_ops = {
1011ae08745Sheppo 	vcc_open,	    /* open */
1021ae08745Sheppo 	vcc_close,	    /* close */
1031ae08745Sheppo 	nodev,		    /* strategy */
1041ae08745Sheppo 	nodev,		    /* print */
1051ae08745Sheppo 	nodev,		    /* dump */
1061ae08745Sheppo 	vcc_read,	    /* read */
1071ae08745Sheppo 	vcc_write,	    /* write */
1081ae08745Sheppo 	vcc_ioctl,	    /* ioctl */
1091ae08745Sheppo 	nodev,		    /* devmap */
1101ae08745Sheppo 	nodev,		    /* mmap */
1111ae08745Sheppo 	ddi_segmap,	    /* segmap */
1121ae08745Sheppo 	vcc_chpoll,	    /* chpoll */
1131ae08745Sheppo 	ddi_prop_op,	    /* prop_op */
1141ae08745Sheppo 	NULL,		    /* stream */
1151ae08745Sheppo 	D_NEW | D_MP	    /* flags */
1161ae08745Sheppo };
1171ae08745Sheppo 
1181ae08745Sheppo 
1191ae08745Sheppo static struct dev_ops vcc_ops = {
1201ae08745Sheppo 	DEVO_REV,		/* rev */
1211ae08745Sheppo 	0,			/* ref count */
1221ae08745Sheppo 	vcc_getinfo,		/* getinfo */
1231ae08745Sheppo 	nulldev,		/* identify */
1241ae08745Sheppo 	nulldev,		/* probe */
1251ae08745Sheppo 	vcc_attach,		/* attach */
1261ae08745Sheppo 	vcc_detach,		/* detach */
1271ae08745Sheppo 	nodev,			/* reset */
1281ae08745Sheppo 	&vcc_cb_ops,		/* cb_ops */
12919397407SSherry Moore 	(struct bus_ops *)NULL,	/* bus_ops */
13019397407SSherry Moore 	NULL,			/* power */
13119397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
1321ae08745Sheppo };
1331ae08745Sheppo 
1341ae08745Sheppo extern struct mod_ops mod_driverops;
1351ae08745Sheppo 
1361ae08745Sheppo #define	    VCC_CHANNEL_ENDPOINT	"channel-endpoint"
1371ae08745Sheppo #define	    VCC_ID_PROP		"id"
1381ae08745Sheppo 
1391ae08745Sheppo /*
140*bbf21555SRichard Lowe  * This is the string displayed by modinfo(8).
1411ae08745Sheppo  */
14219397407SSherry Moore static char vcc_ident[] = "sun4v Virtual Console Concentrator Driver";
1431ae08745Sheppo 
1441ae08745Sheppo static struct modldrv md = {
1451ae08745Sheppo 	&mod_driverops, 	/* Type - it is a driver */
1461ae08745Sheppo 	vcc_ident,		/* Name of the module */
1471ae08745Sheppo 	&vcc_ops,		/* driver specfic opts */
1481ae08745Sheppo };
1491ae08745Sheppo 
1501ae08745Sheppo static struct modlinkage ml = {
1511ae08745Sheppo 	MODREV_1,
1521ae08745Sheppo 	&md,
1531ae08745Sheppo 	NULL
1541ae08745Sheppo };
1551ae08745Sheppo 
1561ae08745Sheppo /*
1571ae08745Sheppo  * Matching criteria passed to the MDEG to register interest
1581ae08745Sheppo  * in changes to 'virtual-device-port' nodes identified by their
1591ae08745Sheppo  * 'id' property.
1601ae08745Sheppo  */
1611ae08745Sheppo static md_prop_match_t vcc_port_prop_match[] = {
1621ae08745Sheppo 	{ MDET_PROP_VAL,	    "id"   },
1631ae08745Sheppo 	{ MDET_LIST_END,	    NULL    }
1641ae08745Sheppo };
1651ae08745Sheppo 
1661ae08745Sheppo static mdeg_node_match_t vcc_port_match = {"virtual-device-port",
1671ae08745Sheppo 					vcc_port_prop_match};
1681ae08745Sheppo 
1691ae08745Sheppo /*
1701ae08745Sheppo  * Specification of an MD node passed to the MDEG to filter any
1711ae08745Sheppo  * 'virtual-device-port' nodes that do not belong to the specified node.
1721ae08745Sheppo  * This template is copied for each vldc instance and filled in with
1731ae08745Sheppo  * the appropriate 'cfg-handle' value before being passed to the MDEG.
1741ae08745Sheppo  */
1751ae08745Sheppo static mdeg_prop_spec_t vcc_prop_template[] = {
1761ae08745Sheppo 	{ MDET_PROP_STR,    "name",	"virtual-console-concentrator"	},
1771ae08745Sheppo 	{ MDET_PROP_VAL,    "cfg-handle",	NULL	},
1781ae08745Sheppo 	{ MDET_LIST_END,    NULL,		NULL	}
1791ae08745Sheppo };
1801ae08745Sheppo 
1811ae08745Sheppo #define	VCC_SET_MDEG_PROP_INST(specp, val) (specp)[1].ps_val = (val);
1821ae08745Sheppo 
1831ae08745Sheppo 
1841ae08745Sheppo #ifdef DEBUG
1851ae08745Sheppo 
1861ae08745Sheppo /*
1871ae08745Sheppo  * Print debug messages
1881ae08745Sheppo  *
1891ae08745Sheppo  * set vldcdbg to 0xf to enable all messages
1901ae08745Sheppo  *
1911ae08745Sheppo  * 0x8 - Errors
1921ae08745Sheppo  * 0x4 - Warnings
1931ae08745Sheppo  * 0x2 - All debug messages (most verbose)
1941ae08745Sheppo  * 0x1 - Minimal debug messages
1951ae08745Sheppo  */
1961ae08745Sheppo 
1971ae08745Sheppo int vccdbg = 0x8;
1981ae08745Sheppo 
1991ae08745Sheppo static void
vccdebug(const char * fmt,...)2001ae08745Sheppo vccdebug(const char *fmt, ...)
2011ae08745Sheppo {
2021ae08745Sheppo 	char buf[512];
2031ae08745Sheppo 	va_list ap;
2041ae08745Sheppo 
2051ae08745Sheppo 	va_start(ap, fmt);
2061ae08745Sheppo 	(void) vsprintf(buf, fmt, ap);
2071ae08745Sheppo 	va_end(ap);
2081ae08745Sheppo 
2091ae08745Sheppo 	cmn_err(CE_CONT, "%s\n", buf);
2101ae08745Sheppo }
2111ae08745Sheppo 
2121ae08745Sheppo #define	D1		\
2131ae08745Sheppo if (vccdbg & 0x01)	\
2141ae08745Sheppo 	vccdebug
2151ae08745Sheppo 
2161ae08745Sheppo #define	D2		\
2171ae08745Sheppo if (vccdbg & 0x02)	\
2181ae08745Sheppo 	vccdebug
2191ae08745Sheppo 
2201ae08745Sheppo #define	DWARN		\
2211ae08745Sheppo if (vccdbg & 0x04)	\
2221ae08745Sheppo 	vccdebug
2231ae08745Sheppo 
2241ae08745Sheppo #else
2251ae08745Sheppo 
2261ae08745Sheppo #define	D1
2271ae08745Sheppo #define	D2
2281ae08745Sheppo #define	DWARN
2291ae08745Sheppo 
2301ae08745Sheppo #endif
2311ae08745Sheppo 
2321ae08745Sheppo /* _init(9E): initialize the loadable module */
2331ae08745Sheppo int
_init(void)2341ae08745Sheppo _init(void)
2351ae08745Sheppo {
2361ae08745Sheppo 	int error;
2371ae08745Sheppo 
2381ae08745Sheppo 	/* init the soft state structure */
2391ae08745Sheppo 	error = ddi_soft_state_init(&vcc_ssp, sizeof (vcc_t), 1);
2401ae08745Sheppo 	if (error != 0) {
2411ae08745Sheppo 		return (error);
2421ae08745Sheppo 	}
2431ae08745Sheppo 
2441ae08745Sheppo 	/* Link the driver into the system */
2451ae08745Sheppo 	error = mod_install(&ml);
2461ae08745Sheppo 
2471ae08745Sheppo 	return (error);
2481ae08745Sheppo 
2491ae08745Sheppo }
2501ae08745Sheppo 
2511ae08745Sheppo /* _info(9E): return information about the loadable module */
2521ae08745Sheppo int
_info(struct modinfo * modinfop)2531ae08745Sheppo _info(struct modinfo *modinfop)
2541ae08745Sheppo {
2551ae08745Sheppo 	/* Report status of the dynamically loadable driver module */
2561ae08745Sheppo 	return (mod_info(&ml, modinfop));
2571ae08745Sheppo }
2581ae08745Sheppo 
2591ae08745Sheppo /* _fini(9E): prepare the module for unloading. */
2601ae08745Sheppo int
_fini(void)2611ae08745Sheppo _fini(void)
2621ae08745Sheppo {
2631ae08745Sheppo 	int error;
2641ae08745Sheppo 
2651ae08745Sheppo 	/* Unlink the driver module from the system */
2661ae08745Sheppo 	if ((error = mod_remove(&ml)) == 0) {
2671ae08745Sheppo 		/*
2681ae08745Sheppo 		 * We have successfully "removed" the driver.
2691ae08745Sheppo 		 * destroy soft state
2701ae08745Sheppo 		 */
2711ae08745Sheppo 		ddi_soft_state_fini(&vcc_ssp);
2721ae08745Sheppo 	}
2731ae08745Sheppo 
2741ae08745Sheppo 	return (error);
2751ae08745Sheppo }
2761ae08745Sheppo 
2771ae08745Sheppo /* getinfo(9E) */
2781ae08745Sheppo static int
vcc_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)2791ae08745Sheppo vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,  void *arg, void **resultp)
2801ae08745Sheppo {
2811ae08745Sheppo 	_NOTE(ARGUNUSED(dip))
2821ae08745Sheppo 
2831ae08745Sheppo 	int	instance = VCCINST(getminor((dev_t)arg));
2841ae08745Sheppo 	vcc_t	*vccp = NULL;
2851ae08745Sheppo 
2861ae08745Sheppo 	switch (cmd) {
2871ae08745Sheppo 
2881ae08745Sheppo 	case DDI_INFO_DEVT2DEVINFO:
2891ae08745Sheppo 		if ((vccp = ddi_get_soft_state(vcc_ssp, instance)) == NULL) {
2901ae08745Sheppo 			*resultp = NULL;
2911ae08745Sheppo 			return (DDI_FAILURE);
2921ae08745Sheppo 		}
2931ae08745Sheppo 		*resultp = vccp->dip;
2941ae08745Sheppo 		return (DDI_SUCCESS);
2951ae08745Sheppo 
2961ae08745Sheppo 	case DDI_INFO_DEVT2INSTANCE:
2971ae08745Sheppo 		*resultp = (void *)(uintptr_t)instance;
2981ae08745Sheppo 		return (DDI_SUCCESS);
2991ae08745Sheppo 
3001ae08745Sheppo 	default:
3011ae08745Sheppo 		*resultp = NULL;
3021ae08745Sheppo 		return (DDI_FAILURE);
3031ae08745Sheppo 	}
3041ae08745Sheppo }
3051ae08745Sheppo 
3061ae08745Sheppo /*
3071ae08745Sheppo  * There are two cases that need special blocking. One of them is to block
3081ae08745Sheppo  * a minor node without a port and another is to block application other
3091ae08745Sheppo  * than vntsd.
3101ae08745Sheppo  *
3111ae08745Sheppo  * A minor node can exist in the file system without associated with a port
3121ae08745Sheppo  * because when a port is deleted, ddi_remove_minor does not unlink it.
3131ae08745Sheppo  * Clients might try to open a minor node even after the corresponding port
3141ae08745Sheppo  * node has been removed.  To identify and block these calls,
3151ae08745Sheppo  * we need to validate the association between a port and its minor node.
3161ae08745Sheppo  *
3171ae08745Sheppo  * An application other than vntsd can access a console port as long
3181ae08745Sheppo  * as vntsd is not using the port. A port opened by an application other
3191ae08745Sheppo  * than vntsd will be closed when vntsd wants to use the port.
3201ae08745Sheppo  * However, other application could use same file descriptor
3211ae08745Sheppo  * access vcc cb_ops. So we need to identify and block caller other
3221ae08745Sheppo  * than vntsd, when vntsd is using the port.
3231ae08745Sheppo  */
3241ae08745Sheppo static int
i_vcc_can_use_port(vcc_minor_t * minorp,vcc_port_t * vport)3251ae08745Sheppo i_vcc_can_use_port(vcc_minor_t *minorp, vcc_port_t *vport)
3261ae08745Sheppo {
3271ae08745Sheppo 	if (vport->minorp != minorp) {
3281ae08745Sheppo 		/* port config changed */
3291ae08745Sheppo 		return (ENXIO);
3301ae08745Sheppo 	}
3311ae08745Sheppo 
3321ae08745Sheppo 	if (vport->valid_pid == VCC_NO_PID_BLOCKING) {
3331ae08745Sheppo 		/* no blocking needed */
3341ae08745Sheppo 		return (0);
3351ae08745Sheppo 	}
3361ae08745Sheppo 
3371ae08745Sheppo 	if (vport->valid_pid != ddi_get_pid()) {
3381ae08745Sheppo 		return (EIO);
3391ae08745Sheppo 	}
3401ae08745Sheppo 
3411ae08745Sheppo 	return (0);
3421ae08745Sheppo }
3431ae08745Sheppo 
3441ae08745Sheppo 
3451ae08745Sheppo /* Syncronization between thread using cv_wait */
3461ae08745Sheppo static int
i_vcc_wait_port_status(vcc_port_t * vport,kcondvar_t * cv,uint32_t status)3471ae08745Sheppo i_vcc_wait_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
3481ae08745Sheppo {
3491ae08745Sheppo 
3501ae08745Sheppo 	int	    rv;
3511ae08745Sheppo 
3521ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
3531ae08745Sheppo 
3541ae08745Sheppo 	for (; ; ) {
3551ae08745Sheppo 
3561ae08745Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
3571ae08745Sheppo 			/* port has been deleted */
3581ae08745Sheppo 			D1("i_vcc_wait_port_status: port%d deleted\n",
3591ae08745Sheppo 			    vport->number);
3601ae08745Sheppo 			return (EIO);
3611ae08745Sheppo 		}
3621ae08745Sheppo 
3631ae08745Sheppo 		if ((vport->status & VCC_PORT_OPEN) == 0) {
3641ae08745Sheppo 			D1("i_vcc_wait_port_status: port%d is closed \n",
3651ae08745Sheppo 			    vport->number);
3661ae08745Sheppo 			return (EIO);
3671ae08745Sheppo 		}
3681ae08745Sheppo 
3691ae08745Sheppo 		if (vport->status & VCC_PORT_LDC_LINK_DOWN) {
3701ae08745Sheppo 			return (EIO);
3711ae08745Sheppo 		}
3721ae08745Sheppo 
3731ae08745Sheppo 		if ((vport->valid_pid != VCC_NO_PID_BLOCKING) &&
3741ae08745Sheppo 		    (vport->valid_pid != ddi_get_pid())) {
3751ae08745Sheppo 			return (EIO);
3761ae08745Sheppo 		}
3771ae08745Sheppo 
3781ae08745Sheppo 		if ((vport->status & status) == status) {
3791ae08745Sheppo 			return (0);
3801ae08745Sheppo 		}
3811ae08745Sheppo 
3821ae08745Sheppo 		if (!ddi_can_receive_sig()) {
3831ae08745Sheppo 			return (EIO);
3841ae08745Sheppo 		}
3851ae08745Sheppo 
3861ae08745Sheppo 		rv = cv_wait_sig(cv, &vport->lock);
3871ae08745Sheppo 		if (rv == 0) {
3881ae08745Sheppo 			D1("i_vcc_wait_port_status: port%d get intr \n",
3891ae08745Sheppo 			    vport->number);
3901ae08745Sheppo 			/* got signal */
3911ae08745Sheppo 			return (EINTR);
3921ae08745Sheppo 		}
3931ae08745Sheppo 	}
3941ae08745Sheppo 
3951ae08745Sheppo }
3961ae08745Sheppo 
3971ae08745Sheppo /* Syncronization between threads, signal state change */
3981ae08745Sheppo static void
i_vcc_set_port_status(vcc_port_t * vport,kcondvar_t * cv,uint32_t status)3991ae08745Sheppo i_vcc_set_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
4001ae08745Sheppo {
4011ae08745Sheppo 
4021ae08745Sheppo 	mutex_enter(&vport->lock);
4031ae08745Sheppo 	vport->status |= status;
4041ae08745Sheppo 	cv_broadcast(cv);
4051ae08745Sheppo 	mutex_exit(&vport->lock);
4061ae08745Sheppo }
4071ae08745Sheppo 
4081ae08745Sheppo /* initialize a ldc channel */
4091ae08745Sheppo static int
i_vcc_ldc_init(vcc_t * vccp,vcc_port_t * vport)4101ae08745Sheppo i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport)
4111ae08745Sheppo {
4121ae08745Sheppo 	ldc_attr_t 	attr;
4131ae08745Sheppo 	int		rv = EIO;
4141ae08745Sheppo 
4151ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
4161ae08745Sheppo 	ASSERT(vport->ldc_id != VCC_INVALID_CHANNEL);
4171ae08745Sheppo 
4181ae08745Sheppo 	/* initialize the channel */
4191ae08745Sheppo 	attr.devclass = LDC_DEV_SERIAL;
4201ae08745Sheppo 	attr.instance = ddi_get_instance(vccp->dip);
421e1ebb9ecSlm 	attr.mtu = VCC_MTU_SZ;
4221ae08745Sheppo 	attr.mode = LDC_MODE_RAW;
4231ae08745Sheppo 
4241ae08745Sheppo 	if ((rv = ldc_init(vport->ldc_id, &attr, &(vport->ldc_handle))) != 0) {
4254d39be2bSsg 		cmn_err(CE_CONT, "i_vcc_ldc_init: port %d ldc channel %ld"
4264d39be2bSsg 		    " failed ldc_init %d \n", vport->number, vport->ldc_id, rv);
4271ae08745Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4281ae08745Sheppo 		return (rv);
4291ae08745Sheppo 	}
4301ae08745Sheppo 
4311ae08745Sheppo 	/* register it */
4321ae08745Sheppo 	if ((rv = ldc_reg_callback(vport->ldc_handle, vcc_ldc_cb,
43319397407SSherry Moore 	    (caddr_t)vport)) != 0) {
4341ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_register_cb"
43519397407SSherry Moore 		    "failed\n", vport->number);
4361ae08745Sheppo 		(void) ldc_fini(vport->ldc_handle);
4371ae08745Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4381ae08745Sheppo 		return (rv);
4391ae08745Sheppo 	}
4401ae08745Sheppo 
4411ae08745Sheppo 	/* open and bring channel up */
4421ae08745Sheppo 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
4431ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d inv channel 0x%lx\n",
4441ae08745Sheppo 		    vport->number, vport->ldc_id);
4451ae08745Sheppo 		(void) ldc_unreg_callback(vport->ldc_handle);
4461ae08745Sheppo 		(void) ldc_fini(vport->ldc_handle);
4471ae08745Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4481ae08745Sheppo 		return (rv);
4491ae08745Sheppo 	}
4501ae08745Sheppo 
4511ae08745Sheppo 	/* init the channel status */
4521ae08745Sheppo 	if ((rv = ldc_status(vport->ldc_handle, &vport->ldc_status)) != 0) {
4531ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_status failed\n",
4541ae08745Sheppo 		    vport->number);
4551ae08745Sheppo 		(void) ldc_close(vport->ldc_handle);
4561ae08745Sheppo 		(void) ldc_unreg_callback(vport->ldc_handle);
4571ae08745Sheppo 		(void) ldc_fini(vport->ldc_handle);
4581ae08745Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4591ae08745Sheppo 		return (rv);
4601ae08745Sheppo 	}
4611ae08745Sheppo 
4621ae08745Sheppo 	return (0);
4631ae08745Sheppo }
4641ae08745Sheppo 
4651ae08745Sheppo /*  release a ldc channel */
466445b4c2eSsb static void
i_vcc_ldc_fini(vcc_port_t * vport)4671ae08745Sheppo i_vcc_ldc_fini(vcc_port_t *vport)
4681ae08745Sheppo {
4691ae08745Sheppo 	int 		rv = EIO;
4701ae08745Sheppo 	vcc_msg_t	buf;
4717636cb21Slm 	size_t		sz;
472445b4c2eSsb 	int		retry = 0;
4731ae08745Sheppo 
4741ae08745Sheppo 	D1("i_vcc_ldc_fini: port@%lld, ldc_id%%llx\n", vport->number,
4751ae08745Sheppo 	    vport->ldc_id);
4761ae08745Sheppo 
4771ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
4781ae08745Sheppo 
4791ae08745Sheppo 	/* wait for write available */
4801ae08745Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
4811ae08745Sheppo 	    VCC_PORT_USE_WRITE_LDC);
4821ae08745Sheppo 
483445b4c2eSsb 	if (rv == 0) {
484445b4c2eSsb 		vport->status &= ~VCC_PORT_USE_WRITE_LDC;
4851ae08745Sheppo 
486445b4c2eSsb 		/* send a HUP message */
487445b4c2eSsb 		buf.type = LDC_CONSOLE_CTRL;
488445b4c2eSsb 		buf.ctrl_msg = LDC_CONSOLE_HUP;
489445b4c2eSsb 		buf.size = 0;
490445b4c2eSsb 
491445b4c2eSsb 		/*
492445b4c2eSsb 		 * ignore write error since we still want to clean up
493445b4c2eSsb 		 * ldc channel.
494445b4c2eSsb 		 */
495445b4c2eSsb 		(void) i_vcc_write_ldc(vport, &buf);
496445b4c2eSsb 
497445b4c2eSsb 		mutex_exit(&vport->lock);
498445b4c2eSsb 		i_vcc_set_port_status(vport, &vport->write_cv,
499445b4c2eSsb 		    VCC_PORT_USE_WRITE_LDC);
500445b4c2eSsb 		mutex_enter(&vport->lock);
501445b4c2eSsb 	}
5021ae08745Sheppo 
5037636cb21Slm 	/* flush ldc channel */
5047636cb21Slm 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
5057636cb21Slm 	    VCC_PORT_USE_READ_LDC);
5067636cb21Slm 
507445b4c2eSsb 	if (rv == 0) {
508445b4c2eSsb 		vport->status &= ~VCC_PORT_USE_READ_LDC;
509445b4c2eSsb 		do {
510445b4c2eSsb 			sz = sizeof (buf);
511445b4c2eSsb 			rv = i_vcc_read_ldc(vport, (char *)&buf, &sz);
512445b4c2eSsb 		} while (rv == 0 && sz > 0);
5137636cb21Slm 
514445b4c2eSsb 		vport->status |= VCC_PORT_USE_READ_LDC;
5157636cb21Slm 
5161ae08745Sheppo 	}
5171ae08745Sheppo 
518445b4c2eSsb 	/*
519445b4c2eSsb 	 * ignore read error since we still want to clean up
520445b4c2eSsb 	 * ldc channel.
521445b4c2eSsb 	 */
522445b4c2eSsb 
523445b4c2eSsb 	(void) ldc_set_cb_mode(vport->ldc_handle, LDC_CB_DISABLE);
524445b4c2eSsb 
525445b4c2eSsb 	/* close LDC channel - retry on EAGAIN */
526445b4c2eSsb 	while ((rv = ldc_close(vport->ldc_handle)) == EAGAIN) {
527445b4c2eSsb 
528445b4c2eSsb 		if (++retry > VCC_LDC_RETRIES) {
529445b4c2eSsb 			cmn_err(CE_CONT, "i_vcc_ldc_fini: cannot close channel"
530445b4c2eSsb 			    " %ld\n", vport->ldc_id);
531445b4c2eSsb 			break;
532445b4c2eSsb 		}
533445b4c2eSsb 
534445b4c2eSsb 		drv_usecwait(VCC_LDC_DELAY);
5351ae08745Sheppo 	}
5361ae08745Sheppo 
537445b4c2eSsb 	if (rv == 0) {
538445b4c2eSsb 		(void) ldc_unreg_callback(vport->ldc_handle);
539445b4c2eSsb 		(void) ldc_fini(vport->ldc_handle);
540445b4c2eSsb 	} else {
541445b4c2eSsb 		/*
542445b4c2eSsb 		 * Closing the LDC channel has failed. Ideally we should
543445b4c2eSsb 		 * fail here but there is no Zeus level infrastructure
544445b4c2eSsb 		 * to handle this. The MD has already been changed and
545445b4c2eSsb 		 * we have to do the close. So we try to do as much
546445b4c2eSsb 		 * clean up as we can.
547445b4c2eSsb 		 */
548445b4c2eSsb 		while (ldc_unreg_callback(vport->ldc_handle) == EAGAIN)
549445b4c2eSsb 			drv_usecwait(VCC_LDC_DELAY);
5501ae08745Sheppo 	}
5511ae08745Sheppo 
5521ae08745Sheppo }
5531ae08745Sheppo 
5541ae08745Sheppo /* read data from ldc channel */
5551ae08745Sheppo 
5561ae08745Sheppo static int
i_vcc_read_ldc(vcc_port_t * vport,char * data_buf,size_t * sz)5571ae08745Sheppo i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz)
5581ae08745Sheppo {
5591ae08745Sheppo 
5601ae08745Sheppo 	int		rv;
5611ae08745Sheppo 	size_t		size;
5621ae08745Sheppo 	size_t		space_left = *sz;
5631ae08745Sheppo 	vcc_msg_t  	buf;
5641ae08745Sheppo 	int 		i;
5651ae08745Sheppo 
5661ae08745Sheppo 
5671ae08745Sheppo 
5681ae08745Sheppo 
5691ae08745Sheppo 	/* make sure holding read lock */
5701ae08745Sheppo 	ASSERT((vport->status & VCC_PORT_USE_READ_LDC) == 0);
5711ae08745Sheppo 	ASSERT(space_left >= VCC_MTU_SZ);
5721ae08745Sheppo 
5731ae08745Sheppo 	*sz = 0;
5741ae08745Sheppo 	while (space_left >= VCC_MTU_SZ)  {
5751ae08745Sheppo 		size = sizeof (buf);
5761ae08745Sheppo 
5771ae08745Sheppo 		rv = ldc_read(vport->ldc_handle, (caddr_t)&buf, &size);
5781ae08745Sheppo 
5791ae08745Sheppo 		if (rv) {
5801ae08745Sheppo 			return (rv);
5811ae08745Sheppo 		}
5821ae08745Sheppo 
5831ae08745Sheppo 
5841ae08745Sheppo 		/*
5851ae08745Sheppo 		 * FIXME: ldc_read should not reaturn 0 with
5861ae08745Sheppo 		 * either size == 0, buf.size == 0 or size < VCC_HDR_SZ
5871ae08745Sheppo 		 */
5881ae08745Sheppo 		if (size == 0) {
5891ae08745Sheppo 			if (*sz > 0) {
5901ae08745Sheppo 				return (0);
5911ae08745Sheppo 			}
5921ae08745Sheppo 			return (EAGAIN);
5931ae08745Sheppo 		}
5941ae08745Sheppo 
5951ae08745Sheppo 		if (size < VCC_HDR_SZ) {
5961ae08745Sheppo 			return (EIO);
5971ae08745Sheppo 		}
5981ae08745Sheppo 
5991ae08745Sheppo 		/*
6001ae08745Sheppo 		 * only data is expected from console - otherwise
6011ae08745Sheppo 		 * return error
6021ae08745Sheppo 		 */
6031ae08745Sheppo 		if (buf.type != LDC_CONSOLE_DATA) {
6041ae08745Sheppo 			return (EIO);
6051ae08745Sheppo 		}
6061ae08745Sheppo 
6071ae08745Sheppo 		if (buf.size == 0) {
6081ae08745Sheppo 			if (*sz > 0) {
6091ae08745Sheppo 				return (0);
6101ae08745Sheppo 			}
6111ae08745Sheppo 			return (EAGAIN);
6121ae08745Sheppo 		}
6131ae08745Sheppo 
6141ae08745Sheppo 		/* copy  data */
6151ae08745Sheppo 		for (i = 0; i < buf.size; i++, (*sz)++) {
6161ae08745Sheppo 			data_buf[*sz] = buf.data[i];
6171ae08745Sheppo 		}
6181ae08745Sheppo 
6191ae08745Sheppo 		space_left -= buf.size;
6201ae08745Sheppo 	}
6211ae08745Sheppo 
6221ae08745Sheppo 	return (0);
6231ae08745Sheppo }
6241ae08745Sheppo 
6251ae08745Sheppo /* callback from ldc */
6261ae08745Sheppo static uint_t
vcc_ldc_cb(uint64_t event,caddr_t arg)6271ae08745Sheppo vcc_ldc_cb(uint64_t event, caddr_t arg)
6281ae08745Sheppo {
6291ae08745Sheppo 
6301ae08745Sheppo 	vcc_port_t  *vport = (vcc_port_t *)arg;
631e1ebb9ecSlm 	boolean_t   hasdata;
6321ae08745Sheppo 
6331ae08745Sheppo 	/*
6341ae08745Sheppo 	 * do not need to hold lock because if ldc calls back, the
6351ae08745Sheppo 	 * ldc_handle must be valid.
6361ae08745Sheppo 	 */
6371ae08745Sheppo 	D2("vcc_ldc_cb: callback invoked port=%d events=%llx\n",
6381ae08745Sheppo 	    vport->number, event);
6391ae08745Sheppo 
6401ae08745Sheppo 	/* check event from ldc */
6411ae08745Sheppo 	if (event & LDC_EVT_WRITE) {
6421ae08745Sheppo 		/* channel has space for write */
6431ae08745Sheppo 
6441ae08745Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
64519397407SSherry Moore 		    VCC_PORT_LDC_WRITE_READY);
6461ae08745Sheppo 		return (LDC_SUCCESS);
6471ae08745Sheppo 	}
6481ae08745Sheppo 
6491ae08745Sheppo 	if (event & LDC_EVT_READ) {
6501ae08745Sheppo 
6511ae08745Sheppo 		/* channel has data for read */
652e1ebb9ecSlm 		(void) ldc_chkq(vport->ldc_handle, &hasdata);
653e1ebb9ecSlm 		if (!hasdata) {
6541ae08745Sheppo 			/* data already read */
6551ae08745Sheppo 			return (LDC_SUCCESS);
6561ae08745Sheppo 		}
6571ae08745Sheppo 
6581ae08745Sheppo 		i_vcc_set_port_status(vport, &vport->read_cv,
65919397407SSherry Moore 		    VCC_PORT_LDC_DATA_READY);
6601ae08745Sheppo 		return (LDC_SUCCESS);
6611ae08745Sheppo 	}
6621ae08745Sheppo 
6631ae08745Sheppo 	if (event & LDC_EVT_DOWN) {
6641ae08745Sheppo 		/* channel is down */
6651ae08745Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
66619397407SSherry Moore 		    VCC_PORT_LDC_LINK_DOWN);
6671ae08745Sheppo 		cv_broadcast(&vport->read_cv);
6681ae08745Sheppo 
6691ae08745Sheppo 	}
6701ae08745Sheppo 
6711ae08745Sheppo 	return (LDC_SUCCESS);
6721ae08745Sheppo 
6731ae08745Sheppo }
6741ae08745Sheppo 
6751ae08745Sheppo 
6761ae08745Sheppo /* configure a vcc port with ldc channel */
6771ae08745Sheppo static int
i_vcc_config_port(vcc_t * vccp,uint_t portno,uint64_t ldc_id)6781ae08745Sheppo i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id)
6791ae08745Sheppo {
6801ae08745Sheppo 	int 		rv = EIO;
6811ae08745Sheppo 	vcc_port_t 	*vport;
6821ae08745Sheppo 
6831ae08745Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
6841ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: invalid port number %d\n",
6851ae08745Sheppo 		    portno);
6861ae08745Sheppo 		return (EINVAL);
6871ae08745Sheppo 	}
6881ae08745Sheppo 
6891ae08745Sheppo 	vport = &(vccp->port[portno]);
6901ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
6911ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d does not exist\n",
6921ae08745Sheppo 		    portno);
6931ae08745Sheppo 		return (EINVAL);
6941ae08745Sheppo 	}
6951ae08745Sheppo 
6961ae08745Sheppo 
6971ae08745Sheppo 	if (vport->ldc_id != VCC_INVALID_CHANNEL) {
6981ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d channel already"
6991ae08745Sheppo 		    "configured\n", portno);
7001ae08745Sheppo 		return (EINVAL);
7011ae08745Sheppo 	}
7021ae08745Sheppo 
7031ae08745Sheppo 	mutex_enter(&vport->lock);
7041ae08745Sheppo 
7051ae08745Sheppo 	/* store the ldc ID */
7061ae08745Sheppo 	vport->ldc_id = ldc_id;
7071ae08745Sheppo 	/* check if someone has already opened this port */
7081ae08745Sheppo 	if (vport->status & VCC_PORT_OPEN) {
7091ae08745Sheppo 
7101ae08745Sheppo 		if ((rv = i_vcc_ldc_init(vccp, vport)) != 0) {
7111ae08745Sheppo 			mutex_exit(&vport->lock);
7121ae08745Sheppo 			return (rv);
7131ae08745Sheppo 		}
7141ae08745Sheppo 
7151ae08745Sheppo 		/* mark port as ready */
7161ae08745Sheppo 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
7171ae08745Sheppo 		cv_broadcast(&vport->read_cv);
7181ae08745Sheppo 		cv_broadcast(&vport->write_cv);
7191ae08745Sheppo 	}
7201ae08745Sheppo 
7211ae08745Sheppo 	mutex_exit(&vport->lock);
7221ae08745Sheppo 
7231ae08745Sheppo 	D1("i_vcc_config_port: port@%d ldc=%d, domain=%s",
7241ae08745Sheppo 	    vport->number, vport->ldc_id, vport->minorp->domain_name);
7251ae08745Sheppo 
7261ae08745Sheppo 	return (0);
7271ae08745Sheppo }
7281ae08745Sheppo 
7291ae08745Sheppo /* add a vcc console port */
7301ae08745Sheppo static int
i_vcc_add_port(vcc_t * vccp,char * group_name,uint64_t tcp_port,uint_t portno,char * domain_name)7311ae08745Sheppo i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
7321ae08745Sheppo     uint_t portno, char *domain_name)
7331ae08745Sheppo {
7341ae08745Sheppo 	int 		instance;
7351ae08745Sheppo 	int		rv = MDEG_FAILURE;
7361ae08745Sheppo 	minor_t 	minor;
7371ae08745Sheppo 	vcc_port_t 	*vport;
7381ae08745Sheppo 	uint_t		minor_idx;
7391ae08745Sheppo 	char		name[MAXPATHLEN];
7401ae08745Sheppo 
7411ae08745Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
7421ae08745Sheppo 		DWARN("i_vcc_add_port: invalid port number %d\n", portno);
7431ae08745Sheppo 		return (MDEG_FAILURE);
7441ae08745Sheppo 	}
7451ae08745Sheppo 
7461ae08745Sheppo 	vport = &(vccp->port[portno]);
7471ae08745Sheppo 	if (vport->status & VCC_PORT_AVAIL) {
7481ae08745Sheppo 		/* this port already exists */
7491ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid port - port@%d "
75019397407SSherry Moore 		    "exists\n", portno);
7511ae08745Sheppo 		return (MDEG_FAILURE);
7521ae08745Sheppo 	}
7531ae08745Sheppo 
7541ae08745Sheppo 	vport->number = portno;
7551ae08745Sheppo 	vport->ldc_id = VCC_INVALID_CHANNEL;
7561ae08745Sheppo 
7571ae08745Sheppo 	if (domain_name == NULL) {
7581ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid domain name\n");
7591ae08745Sheppo 		return (MDEG_FAILURE);
7601ae08745Sheppo 	}
7611ae08745Sheppo 
7621ae08745Sheppo 	if (group_name == NULL) {
7631ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid group name\n");
7641ae08745Sheppo 		return (MDEG_FAILURE);
7651ae08745Sheppo 	}
7661ae08745Sheppo 
7671ae08745Sheppo 	/* look up minor number */
7681ae08745Sheppo 	for (minor_idx = 0; minor_idx < vccp->minors_assigned; minor_idx++) {
7691ae08745Sheppo 		if (strcmp(vccp->minor_tbl[minor_idx].domain_name,
77019397407SSherry Moore 		    domain_name) == 0) {
7711ae08745Sheppo 			/* found previous assigned minor number */
7721ae08745Sheppo 			break;
7731ae08745Sheppo 		}
7741ae08745Sheppo 	}
7751ae08745Sheppo 
7761ae08745Sheppo 	if (minor_idx == vccp->minors_assigned) {
7771ae08745Sheppo 		/* end of lookup - assign new minor number */
7781ae08745Sheppo 		if (minor_idx == VCC_MAX_PORTS) {
7791ae08745Sheppo 			cmn_err(CE_CONT, "i_vcc_add_port:"
7801ae08745Sheppo 			    "too many minornodes (%d)\n",
7811ae08745Sheppo 			    minor_idx);
7821ae08745Sheppo 			return (MDEG_FAILURE);
7831ae08745Sheppo 		}
7841ae08745Sheppo 
7851ae08745Sheppo 		(void) strlcpy(vccp->minor_tbl[minor_idx].domain_name,
7861ae08745Sheppo 		    domain_name, MAXPATHLEN);
7871ae08745Sheppo 
7881ae08745Sheppo 		vccp->minors_assigned++;
7891ae08745Sheppo 	}
7901ae08745Sheppo 
7911ae08745Sheppo 	vport->minorp = &vccp->minor_tbl[minor_idx];
7921ae08745Sheppo 	vccp->minor_tbl[minor_idx].portno = portno;
7931ae08745Sheppo 
7941ae08745Sheppo 	(void) strlcpy(vport->group_name, group_name, MAXPATHLEN);
7951ae08745Sheppo 
7961ae08745Sheppo 	vport->tcp_port = tcp_port;
7971ae08745Sheppo 	D1("i_vcc_add_port:@%d domain=%s, group=%s, tcp=%lld",
7981ae08745Sheppo 	    vport->number, vport->minorp->domain_name,
7991ae08745Sheppo 	    vport->group_name, vport->tcp_port);
8001ae08745Sheppo 
8011ae08745Sheppo 
8021ae08745Sheppo 	/*
8031ae08745Sheppo 	 * Create a minor node. The minor number is
8041ae08745Sheppo 	 * (instance << VCC_INST_SHIFT) | minor_idx
8051ae08745Sheppo 	 */
8061ae08745Sheppo 	instance = ddi_get_instance(vccp->dip);
8071ae08745Sheppo 
8081ae08745Sheppo 	minor = (instance << VCC_INST_SHIFT) | (minor_idx);
8091ae08745Sheppo 
8101ae08745Sheppo 	(void) snprintf(name, MAXPATHLEN - 1, "%s%s", VCC_MINOR_NAME_PREFIX,
8111ae08745Sheppo 	    domain_name);
8121ae08745Sheppo 
8131ae08745Sheppo 	rv = ddi_create_minor_node(vccp->dip, name, S_IFCHR, minor,
8141ae08745Sheppo 	    DDI_NT_SERIAL, 0);
8151ae08745Sheppo 
8161ae08745Sheppo 	if (rv != DDI_SUCCESS) {
8171ae08745Sheppo 		vccp->minors_assigned--;
8181ae08745Sheppo 		return (MDEG_FAILURE);
8191ae08745Sheppo 	}
8201ae08745Sheppo 
8211ae08745Sheppo 	mutex_enter(&vport->lock);
8221ae08745Sheppo 	vport->status = VCC_PORT_AVAIL | VCC_PORT_ADDED;
8231ae08745Sheppo 	mutex_exit(&vport->lock);
8241ae08745Sheppo 
8251ae08745Sheppo 
8261ae08745Sheppo 	return (MDEG_SUCCESS);
8271ae08745Sheppo }
8281ae08745Sheppo 
8291ae08745Sheppo /* delete a port */
8301ae08745Sheppo static int
i_vcc_delete_port(vcc_t * vccp,vcc_port_t * vport)8311ae08745Sheppo i_vcc_delete_port(vcc_t *vccp, vcc_port_t *vport)
8321ae08745Sheppo {
8331ae08745Sheppo 
8341ae08745Sheppo 	char	name[MAXPATHLEN];
8351ae08745Sheppo 	int	rv;
8361ae08745Sheppo 
8371ae08745Sheppo 
8381ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
8391ae08745Sheppo 
8401ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
8411ae08745Sheppo 		D1("vcc_del_port port already deleted \n");
8421ae08745Sheppo 		return (0);
8431ae08745Sheppo 	}
8441ae08745Sheppo 
8451ae08745Sheppo 	if (vport->status & VCC_PORT_OPEN) {
8461ae08745Sheppo 		/* do not block mdeg callback */
8471ae08745Sheppo 		vport->valid_pid = VCC_NO_PID_BLOCKING;
8481ae08745Sheppo 		rv = i_vcc_close_port(vport);
8491ae08745Sheppo 	}
8501ae08745Sheppo 
8511ae08745Sheppo 	/* remove minor node */
8521ae08745Sheppo 	(void) snprintf(name, MAXPATHLEN-1, "%s%s", VCC_MINOR_NAME_PREFIX,
8531ae08745Sheppo 	    vport->minorp->domain_name);
8541ae08745Sheppo 
8551ae08745Sheppo 	ddi_remove_minor_node(vccp->dip, name);
8561ae08745Sheppo 
8571ae08745Sheppo 	/* let read and write thread know */
8581ae08745Sheppo 	cv_broadcast(&vport->read_cv);
8591ae08745Sheppo 	cv_broadcast(&vport->write_cv);
8601ae08745Sheppo 	vport->status = 0;
8611ae08745Sheppo 	return (rv);
8621ae08745Sheppo 
8631ae08745Sheppo 
8641ae08745Sheppo }
8651ae08745Sheppo 
8661ae08745Sheppo /* register callback to MDEG */
8671ae08745Sheppo static int
i_vcc_mdeg_register(vcc_t * vccp,int instance)8681ae08745Sheppo i_vcc_mdeg_register(vcc_t *vccp, int instance)
8691ae08745Sheppo {
8701ae08745Sheppo 	mdeg_prop_spec_t	*pspecp;
8711ae08745Sheppo 	mdeg_node_spec_t	*ispecp;
8721ae08745Sheppo 	mdeg_handle_t		mdeg_hdl;
8731ae08745Sheppo 	int			sz;
8741ae08745Sheppo 	int			rv;
8751ae08745Sheppo 
8761ae08745Sheppo 	/*
8771ae08745Sheppo 	 * Allocate and initialize a per-instance copy
8781ae08745Sheppo 	 * of the global property spec array that will
8791ae08745Sheppo 	 * uniquely identify this vcc instance.
8801ae08745Sheppo 	 */
8811ae08745Sheppo 	sz = sizeof (vcc_prop_template);
8821ae08745Sheppo 	pspecp = kmem_alloc(sz, KM_SLEEP);
8831ae08745Sheppo 
8841ae08745Sheppo 	bcopy(vcc_prop_template, pspecp, sz);
8851ae08745Sheppo 
8861ae08745Sheppo 	VCC_SET_MDEG_PROP_INST(pspecp, instance);
8871ae08745Sheppo 
8881ae08745Sheppo 	/* initialize the complete prop spec structure */
8891ae08745Sheppo 	ispecp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
8901ae08745Sheppo 	ispecp->namep = "virtual-device";
8911ae08745Sheppo 	ispecp->specp = pspecp;
8921ae08745Sheppo 
8931ae08745Sheppo 	/* perform the registration */
8941ae08745Sheppo 	rv = mdeg_register(ispecp, &vcc_port_match, vcc_mdeg_cb,
8951ae08745Sheppo 	    vccp, &mdeg_hdl);
8961ae08745Sheppo 
8971ae08745Sheppo 	if (rv != MDEG_SUCCESS) {
8981ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_mdeg_register:"
8991ae08745Sheppo 		    "mdeg_register failed (%d)\n", rv);
9001ae08745Sheppo 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
9011ae08745Sheppo 		kmem_free(pspecp, sz);
9021ae08745Sheppo 		return (DDI_FAILURE);
9031ae08745Sheppo 	}
9041ae08745Sheppo 
9051ae08745Sheppo 	/* save off data that will be needed later */
9061ae08745Sheppo 	vccp->md_ispecp = (void *)ispecp;
9071ae08745Sheppo 	vccp->mdeg_hdl = mdeg_hdl;
9081ae08745Sheppo 
9091ae08745Sheppo 	return (0);
9101ae08745Sheppo }
9111ae08745Sheppo 
9121ae08745Sheppo /* destroy all mutex from port table */
9131ae08745Sheppo static void
i_vcc_cleanup_port_table(vcc_t * vccp)9141ae08745Sheppo i_vcc_cleanup_port_table(vcc_t *vccp)
9151ae08745Sheppo {
9161ae08745Sheppo 	int i;
9171ae08745Sheppo 	vcc_port_t *vport;
9181ae08745Sheppo 
9191ae08745Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
9201ae08745Sheppo 		vport = &(vccp->port[i]);
9211ae08745Sheppo 		mutex_destroy(&vport->lock);
9221ae08745Sheppo 		cv_destroy(&vport->read_cv);
9231ae08745Sheppo 		cv_destroy(&vport->write_cv);
9241ae08745Sheppo 	}
9251ae08745Sheppo }
9261ae08745Sheppo 
9271ae08745Sheppo /*
9281ae08745Sheppo  * attach(9E): attach a device to the system.
9291ae08745Sheppo  * called once for each instance of the device on the system.
9301ae08745Sheppo  */
9311ae08745Sheppo static int
vcc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)9321ae08745Sheppo vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
9331ae08745Sheppo {
9341ae08745Sheppo 	int 		i, instance, inst;
9351ae08745Sheppo 	int 		rv = DDI_FAILURE;
9361ae08745Sheppo 	vcc_t		*vccp;
9371ae08745Sheppo 	minor_t 	minor;
9381ae08745Sheppo 	vcc_port_t	*vport;
9391ae08745Sheppo 
9401ae08745Sheppo 	switch (cmd) {
9411ae08745Sheppo 
9421ae08745Sheppo 	case DDI_ATTACH:
9431ae08745Sheppo 
9441ae08745Sheppo 		instance = ddi_get_instance(dip);
9451ae08745Sheppo 		if (ddi_soft_state_zalloc(vcc_ssp, instance) != DDI_SUCCESS)
9461ae08745Sheppo 			return (DDI_FAILURE);
9471ae08745Sheppo 
9481ae08745Sheppo 		vccp = ddi_get_soft_state(vcc_ssp, instance);
9491ae08745Sheppo 		if (vccp == NULL) {
9501ae08745Sheppo 			ddi_soft_state_free(vccp, instance);
9511ae08745Sheppo 			return (ENXIO);
9521ae08745Sheppo 		}
9531ae08745Sheppo 
9541ae08745Sheppo 		D1("vcc_attach: DDI_ATTACH instance=%d\n", instance);
9551ae08745Sheppo 
9561ae08745Sheppo 		/* initialize the mutex */
9571ae08745Sheppo 		mutex_init(&vccp->lock, NULL, MUTEX_DRIVER, NULL);
9581ae08745Sheppo 
9591ae08745Sheppo 		mutex_enter(&vccp->lock);
9601ae08745Sheppo 
9611ae08745Sheppo 		vccp->dip = dip;
9621ae08745Sheppo 
9631ae08745Sheppo 		for (i = 0; i < VCC_MAX_PORTS; i++) {
9641ae08745Sheppo 			vport = &(vccp->port[i]);
9651ae08745Sheppo 			mutex_init(&vport->lock, NULL, MUTEX_DRIVER, NULL);
9661ae08745Sheppo 			cv_init(&vport->read_cv, NULL, CV_DRIVER, NULL);
9671ae08745Sheppo 			cv_init(&vport->write_cv, NULL, CV_DRIVER, NULL);
9681ae08745Sheppo 			vport->valid_pid = VCC_NO_PID_BLOCKING;
9691ae08745Sheppo 		}
9701ae08745Sheppo 
9711ae08745Sheppo 		vport = &vccp->port[VCC_CONTROL_PORT];
9721ae08745Sheppo 		mutex_enter(&vport->lock);
9731ae08745Sheppo 
9741ae08745Sheppo 		vport->minorp = &vccp->minor_tbl[VCC_CONTROL_MINOR_IDX];
9751ae08745Sheppo 		vport->status |= VCC_PORT_AVAIL;
9761ae08745Sheppo 
9771ae08745Sheppo 		/* create a minor node for vcc control */
9781ae08745Sheppo 		minor = (instance << VCC_INST_SHIFT) | VCC_CONTROL_MINOR_IDX;
9791ae08745Sheppo 
9801ae08745Sheppo 		vccp->minor_tbl[VCC_CONTROL_PORT].portno =
9811ae08745Sheppo 		    VCC_CONTROL_MINOR_IDX;
9821ae08745Sheppo 
9831ae08745Sheppo 
9841ae08745Sheppo 		rv = ddi_create_minor_node(vccp->dip, "ctl", S_IFCHR, minor,
9851ae08745Sheppo 		    DDI_NT_SERIAL, 0);
9861ae08745Sheppo 
9871ae08745Sheppo 		mutex_exit(&vport->lock);
9881ae08745Sheppo 
9891ae08745Sheppo 		if (rv != DDI_SUCCESS) {
9901ae08745Sheppo 			cmn_err(CE_CONT, "vcc_attach: error"
9911ae08745Sheppo 			    "creating control minor node\n");
9921ae08745Sheppo 
9931ae08745Sheppo 			i_vcc_cleanup_port_table(vccp);
9941ae08745Sheppo 
9951ae08745Sheppo 			mutex_exit(&vccp->lock);
9961ae08745Sheppo 			/* clean up soft state */
9971ae08745Sheppo 			ddi_soft_state_free(vccp, instance);
9981ae08745Sheppo 
9991ae08745Sheppo 			return (DDI_FAILURE);
10001ae08745Sheppo 		}
10011ae08745Sheppo 
10021ae08745Sheppo 		/* get the instance number by reading 'reg' property */
10031ae08745Sheppo 		inst = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10041ae08745Sheppo 		    "reg", -1);
10051ae08745Sheppo 		if (inst == -1) {
10061ae08745Sheppo 			cmn_err(CE_CONT, "vcc_attach: vcc%d has no "
100719397407SSherry Moore 			    "'reg' property\n",
10081ae08745Sheppo 			    ddi_get_instance(dip));
10091ae08745Sheppo 
10101ae08745Sheppo 			i_vcc_cleanup_port_table(vccp);
10111ae08745Sheppo 
10121ae08745Sheppo 			/* remove minor */
10131ae08745Sheppo 			ddi_remove_minor_node(vccp->dip, NULL);
10141ae08745Sheppo 
10151ae08745Sheppo 			/* clean up soft state */
10161ae08745Sheppo 			mutex_exit(&vccp->lock);
10171ae08745Sheppo 			ddi_soft_state_free(vccp, instance);
10181ae08745Sheppo 
10191ae08745Sheppo 			return (DDI_FAILURE);
10201ae08745Sheppo 		}
10211ae08745Sheppo 
10221ae08745Sheppo 		/*
10231ae08745Sheppo 		 * Mdeg might invoke callback in the same call sequence
10241ae08745Sheppo 		 * if there is a domain port at the time of registration.
10251ae08745Sheppo 		 * Since the callback also grabs vcc->lock mutex, to avoid
10261ae08745Sheppo 		 * mutex reentry error, release the lock before registration
10271ae08745Sheppo 		 */
10281ae08745Sheppo 		mutex_exit(&vccp->lock);
10291ae08745Sheppo 
10301ae08745Sheppo 		/* register for notifications from Zeus */
10311ae08745Sheppo 		rv = i_vcc_mdeg_register(vccp, inst);
10321ae08745Sheppo 		if (rv != MDEG_SUCCESS) {
10331ae08745Sheppo 			cmn_err(CE_CONT, "vcc_attach: error register to MD\n");
10341ae08745Sheppo 
10351ae08745Sheppo 			i_vcc_cleanup_port_table(vccp);
10361ae08745Sheppo 
10371ae08745Sheppo 			/* remove minor */
10381ae08745Sheppo 			ddi_remove_minor_node(vccp->dip, NULL);
10391ae08745Sheppo 
10401ae08745Sheppo 			/* clean up soft state */
10411ae08745Sheppo 			ddi_soft_state_free(vccp, instance);
10421ae08745Sheppo 
10431ae08745Sheppo 			return (DDI_FAILURE);
10441ae08745Sheppo 		}
10451ae08745Sheppo 
10461ae08745Sheppo 		return (DDI_SUCCESS);
10471ae08745Sheppo 
10481ae08745Sheppo 	case DDI_RESUME:
10491ae08745Sheppo 
10501ae08745Sheppo 		return (DDI_SUCCESS);
10511ae08745Sheppo 
10521ae08745Sheppo 	default:
10531ae08745Sheppo 
10541ae08745Sheppo 		return (DDI_FAILURE);
10551ae08745Sheppo 	}
10561ae08745Sheppo }
10571ae08745Sheppo 
10581ae08745Sheppo /*
10591ae08745Sheppo  * detach(9E): detach a device from the system.
10601ae08745Sheppo  */
10611ae08745Sheppo static int
vcc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)10621ae08745Sheppo vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
10631ae08745Sheppo {
10641ae08745Sheppo 	int		    i, instance;
10651ae08745Sheppo 	vcc_t		    *vccp;
10661ae08745Sheppo 	mdeg_node_spec_t    *ispecp;
10671ae08745Sheppo 	vcc_port_t	    *vport;
10681ae08745Sheppo 
10691ae08745Sheppo 	switch (cmd) {
10701ae08745Sheppo 
10711ae08745Sheppo 	case DDI_DETACH:
10721ae08745Sheppo 
10731ae08745Sheppo 		instance = ddi_get_instance(dip);
10741ae08745Sheppo 		vccp = ddi_get_soft_state(vcc_ssp, instance);
10751ae08745Sheppo 		if (vccp == NULL)
10761ae08745Sheppo 			return (ENXIO);
10771ae08745Sheppo 
10781ae08745Sheppo 		D1("vcc_detach: DDI_DETACH instance=%d\n", instance);
10791ae08745Sheppo 
10801ae08745Sheppo 		mutex_enter(&vccp->lock);
10811ae08745Sheppo 
10821ae08745Sheppo 		/* unregister from MD event generator */
10831ae08745Sheppo 
10841ae08745Sheppo 		ASSERT(vccp->mdeg_hdl);
10851ae08745Sheppo 		(void) mdeg_unregister(vccp->mdeg_hdl);
10861ae08745Sheppo 
10871ae08745Sheppo 		ispecp = (mdeg_node_spec_t *)vccp->md_ispecp;
10881ae08745Sheppo 		ASSERT(ispecp);
10891ae08745Sheppo 
10901ae08745Sheppo 		kmem_free(ispecp->specp, sizeof (vcc_prop_template));
10911ae08745Sheppo 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
10921ae08745Sheppo 
10931ae08745Sheppo 		/* remove minor nodes */
10941ae08745Sheppo 		ddi_remove_minor_node(vccp->dip, NULL);
10951ae08745Sheppo 		mutex_exit(&vccp->lock);
10961ae08745Sheppo 
10971ae08745Sheppo 		for (i = 0; i < VCC_MAX_PORTS; i++) {
10981ae08745Sheppo 
10991ae08745Sheppo 			vport = &vccp->port[i];
11001ae08745Sheppo 			mutex_enter(&vport->lock);
11011ae08745Sheppo 			if (i == VCC_CONTROL_PORT) {
11021ae08745Sheppo 				if (vport->status & VCC_PORT_OPEN) {
11031ae08745Sheppo 					(void) i_vcc_close_port(vport);
11041ae08745Sheppo 				}
11051ae08745Sheppo 			}
11061ae08745Sheppo 
11071ae08745Sheppo 			if ((vccp->port[i].status & VCC_PORT_AVAIL) &&
11081ae08745Sheppo 			    (i != VCC_CONTROL_PORT)) {
11091ae08745Sheppo 				D1("vcc_detach: removing port port@%d\n", i);
11101ae08745Sheppo 				(void) i_vcc_delete_port(vccp, vport);
11111ae08745Sheppo 			}
11121ae08745Sheppo 			mutex_exit(&vport->lock);
11131ae08745Sheppo 			cv_destroy(&vport->read_cv);
11141ae08745Sheppo 			cv_destroy(&vport->write_cv);
11151ae08745Sheppo 			mutex_destroy(&vport->lock);
11161ae08745Sheppo 		}
11171ae08745Sheppo 
11181ae08745Sheppo 
11191ae08745Sheppo 
11201ae08745Sheppo 		/* destroy mutex and free the soft state */
11211ae08745Sheppo 		mutex_destroy(&vccp->lock);
11221ae08745Sheppo 		ddi_soft_state_free(vcc_ssp, instance);
11231ae08745Sheppo 
11241ae08745Sheppo 		return (DDI_SUCCESS);
11251ae08745Sheppo 
11261ae08745Sheppo 	case DDI_SUSPEND:
11271ae08745Sheppo 
11281ae08745Sheppo 		return (DDI_SUCCESS);
11291ae08745Sheppo 
11301ae08745Sheppo 	default:
11311ae08745Sheppo 
11321ae08745Sheppo 		return (DDI_FAILURE);
11331ae08745Sheppo 	}
11341ae08745Sheppo }
11351ae08745Sheppo 
11361ae08745Sheppo /* cb_open */
11371ae08745Sheppo static int
vcc_open(dev_t * devp,int flag,int otyp,cred_t * cred)11381ae08745Sheppo vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
11391ae08745Sheppo {
11401ae08745Sheppo 	_NOTE(ARGUNUSED(otyp, cred))
11411ae08745Sheppo 
11421ae08745Sheppo 	int	    instance;
11431ae08745Sheppo 	int	    rv = EIO;
11441ae08745Sheppo 	minor_t	    minor;
11451ae08745Sheppo 	uint_t	    portno;
11461ae08745Sheppo 	vcc_t	    *vccp;
11471ae08745Sheppo 	vcc_port_t  *vport;
11481ae08745Sheppo 
11491ae08745Sheppo 	minor = getminor(*devp);
11501ae08745Sheppo 	instance = VCCINST(minor);
11511ae08745Sheppo 
11521ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
11531ae08745Sheppo 	if (vccp == NULL) {
11541ae08745Sheppo 		return (ENXIO);
11551ae08745Sheppo 	}
11561ae08745Sheppo 
11571ae08745Sheppo 	portno = VCCPORT(vccp, minor);
11581ae08745Sheppo 
11591ae08745Sheppo 	vport = &(vccp->port[portno]);
11601ae08745Sheppo 
11611ae08745Sheppo 	mutex_enter(&vport->lock);
11621ae08745Sheppo 
11634d39be2bSsg 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
11644d39be2bSsg 		/* port may be removed */
11654d39be2bSsg 		mutex_exit(&vport->lock);
11664d39be2bSsg 		return (ENXIO);
11674d39be2bSsg 	}
11684d39be2bSsg 
11691ae08745Sheppo 	if (vport->status & VCC_PORT_OPEN) {
11701ae08745Sheppo 		/* only one open per port */
11711ae08745Sheppo 		cmn_err(CE_CONT, "vcc_open: virtual-console-concentrator@%d:%d "
11721ae08745Sheppo 		    "is already open\n", instance, portno);
11731ae08745Sheppo 		mutex_exit(&vport->lock);
11741ae08745Sheppo 		return (EAGAIN);
11751ae08745Sheppo 	}
11761ae08745Sheppo 
11771ae08745Sheppo 	/* check minor no and pid */
11781ae08745Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
117919397407SSherry Moore 	    vport)) != 0) {
11801ae08745Sheppo 		mutex_exit(&vport->lock);
11811ae08745Sheppo 		return (rv);
11821ae08745Sheppo 	}
11831ae08745Sheppo 
11841ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
11851ae08745Sheppo 		vport->status |= VCC_PORT_OPEN;
11861ae08745Sheppo 		mutex_exit(&vport->lock);
11871ae08745Sheppo 		return (0);
11881ae08745Sheppo 	}
11891ae08745Sheppo 
1190445b4c2eSsb 	/*
1191445b4c2eSsb 	 * the port may just be added by mdeg callback and may
1192445b4c2eSsb 	 * not be configured yet.
1193445b4c2eSsb 	 */
1194445b4c2eSsb 	if (vport->ldc_id == VCC_INVALID_CHANNEL) {
1195445b4c2eSsb 		mutex_exit(&vport->lock);
1196445b4c2eSsb 		return (ENXIO);
1197445b4c2eSsb 	}
1198445b4c2eSsb 
11991ae08745Sheppo 
12001ae08745Sheppo 	/* check if channel has been initialized */
12011ae08745Sheppo 	if ((vport->status & VCC_PORT_LDC_CHANNEL_READY) == 0) {
12021ae08745Sheppo 		rv = i_vcc_ldc_init(vccp, vport);
12031ae08745Sheppo 		if (rv) {
12041ae08745Sheppo 			mutex_exit(&vport->lock);
12051ae08745Sheppo 			return (EIO);
12061ae08745Sheppo 		}
12071ae08745Sheppo 
12081ae08745Sheppo 		/* mark port as ready */
12091ae08745Sheppo 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
12101ae08745Sheppo 	}
12111ae08745Sheppo 
12121ae08745Sheppo 	vport->status |= VCC_PORT_USE_READ_LDC | VCC_PORT_USE_WRITE_LDC|
12131ae08745Sheppo 	    VCC_PORT_TERM_RD|VCC_PORT_TERM_WR|VCC_PORT_OPEN;
12141ae08745Sheppo 
12151ae08745Sheppo 	if ((flag & O_NONBLOCK) || (flag & O_NDELAY)) {
12161ae08745Sheppo 		vport->status |= VCC_PORT_NONBLOCK;
12171ae08745Sheppo 	}
12181ae08745Sheppo 
12191ae08745Sheppo 	mutex_exit(&vport->lock);
12201ae08745Sheppo 
12211ae08745Sheppo 	return (0);
12221ae08745Sheppo }
12231ae08745Sheppo 
12241ae08745Sheppo /* close port */
12251ae08745Sheppo static int
i_vcc_close_port(vcc_port_t * vport)12261ae08745Sheppo i_vcc_close_port(vcc_port_t *vport)
12271ae08745Sheppo {
12281ae08745Sheppo 
12291ae08745Sheppo 	if ((vport->status & VCC_PORT_OPEN) == 0) {
12301ae08745Sheppo 		return (0);
12311ae08745Sheppo 	}
12321ae08745Sheppo 
12331ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
12341ae08745Sheppo 
12351ae08745Sheppo 	if (vport->status & VCC_PORT_LDC_CHANNEL_READY) {
12361ae08745Sheppo 		/* clean up ldc channel */
1237445b4c2eSsb 		i_vcc_ldc_fini(vport);
12381ae08745Sheppo 		vport->status &= ~VCC_PORT_LDC_CHANNEL_READY;
12391ae08745Sheppo 	}
12401ae08745Sheppo 
12411ae08745Sheppo 	/* reset  rd/wr suspends  */
12421ae08745Sheppo 	vport->status |= VCC_PORT_TERM_RD | VCC_PORT_TERM_WR;
12431ae08745Sheppo 	vport->status &= ~VCC_PORT_NONBLOCK;
12441ae08745Sheppo 	vport->status &= ~VCC_PORT_OPEN;
12451ae08745Sheppo 	vport->valid_pid = VCC_NO_PID_BLOCKING;
12461ae08745Sheppo 
12471ae08745Sheppo 	/* signal any blocked read and write thread */
12481ae08745Sheppo 	cv_broadcast(&vport->read_cv);
12491ae08745Sheppo 	cv_broadcast(&vport->write_cv);
12501ae08745Sheppo 
12511ae08745Sheppo 	return (0);
12521ae08745Sheppo }
12531ae08745Sheppo 
12541ae08745Sheppo /* cb_close */
12551ae08745Sheppo static int
vcc_close(dev_t dev,int flag,int otyp,cred_t * cred)12561ae08745Sheppo vcc_close(dev_t dev, int flag, int otyp, cred_t *cred)
12571ae08745Sheppo {
12581ae08745Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
12591ae08745Sheppo 
12601ae08745Sheppo 	int	    instance;
12611ae08745Sheppo 	minor_t	    minor;
12621ae08745Sheppo 	int	    rv = EIO;
12631ae08745Sheppo 	uint_t	    portno;
12641ae08745Sheppo 	vcc_t	    *vccp;
12651ae08745Sheppo 	vcc_port_t  *vport;
12661ae08745Sheppo 
12671ae08745Sheppo 	minor = getminor(dev);
12681ae08745Sheppo 
12691ae08745Sheppo 	instance = VCCINST(minor);
12701ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
12711ae08745Sheppo 	if (vccp == NULL) {
12721ae08745Sheppo 		return (ENXIO);
12731ae08745Sheppo 	}
12741ae08745Sheppo 
12751ae08745Sheppo 	portno = VCCPORT(vccp, minor);
12761ae08745Sheppo 
12771ae08745Sheppo 	D1("vcc_close: closing virtual-console-concentrator@%d:%d\n",
12781ae08745Sheppo 	    instance, portno);
12791ae08745Sheppo 	vport = &(vccp->port[portno]);
12801ae08745Sheppo 
12811ae08745Sheppo 
1282445b4c2eSsb 	/*
1283445b4c2eSsb 	 * needs lock to provent i_vcc_delete_port, which is called by
1284445b4c2eSsb 	 * the mdeg callback, from closing port.
1285445b4c2eSsb 	 */
1286445b4c2eSsb 	mutex_enter(&vport->lock);
1287445b4c2eSsb 
12881ae08745Sheppo 	if ((vport->status & VCC_PORT_OPEN) == 0) {
1289445b4c2eSsb 		mutex_exit(&vport->lock);
12901ae08745Sheppo 		return (0);
12911ae08745Sheppo 	}
12921ae08745Sheppo 
12931ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
12941ae08745Sheppo 		/*
12951ae08745Sheppo 		 * vntsd closes control port before it exits. There
12961ae08745Sheppo 		 * could be events still pending for vntsd.
12971ae08745Sheppo 		 */
1298445b4c2eSsb 		mutex_exit(&vport->lock);
12991ae08745Sheppo 		rv = i_vcc_reset_events(vccp);
13001ae08745Sheppo 		return (0);
13011ae08745Sheppo 	}
13021ae08745Sheppo 
13031ae08745Sheppo 
13041ae08745Sheppo 	/* check minor no and pid */
13051ae08745Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
130619397407SSherry Moore 	    vport)) != 0) {
13071ae08745Sheppo 		mutex_exit(&vport->lock);
13081ae08745Sheppo 		return (rv);
13091ae08745Sheppo 	}
13101ae08745Sheppo 
13111ae08745Sheppo 	rv = i_vcc_close_port(vport);
13121ae08745Sheppo 	mutex_exit(&vport->lock);
13131ae08745Sheppo 
13141ae08745Sheppo 	return (rv);
13151ae08745Sheppo }
13161ae08745Sheppo 
13171ae08745Sheppo /*
13181ae08745Sheppo  * ioctl VCC_CONS_TBL - vntsd allocates buffer according to return of
13191ae08745Sheppo  * VCC_NUM_PORTS. However, when vntsd requests for the console table, console
13201ae08745Sheppo  * ports could be deleted or added. parameter num_ports is number of structures
13211ae08745Sheppo  * that vntsd allocated for the table. If there are more ports than
13221ae08745Sheppo  * num_ports, set up to wakeup vntsd to add ports.
13231ae08745Sheppo  * If there less ports than num_ports, fill (-1) for cons_no to tell vntsd.
13241ae08745Sheppo  */
13251ae08745Sheppo static int
i_vcc_cons_tbl(vcc_t * vccp,uint_t num_ports,caddr_t buf,int mode)13261ae08745Sheppo i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports, caddr_t buf, int mode)
13271ae08745Sheppo {
13281ae08745Sheppo 	vcc_console_t	cons;
13291ae08745Sheppo 	int		i;
13301ae08745Sheppo 	vcc_port_t	*vport;
13311ae08745Sheppo 	boolean_t	notify_vntsd = B_FALSE;
13321ae08745Sheppo 	char pathname[MAXPATHLEN];
13331ae08745Sheppo 
13341ae08745Sheppo 
13351ae08745Sheppo 	(void) ddi_pathname(vccp->dip, pathname);
13361ae08745Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
13371ae08745Sheppo 
13381ae08745Sheppo 		vport = &vccp->port[i];
13391ae08745Sheppo 
13401ae08745Sheppo 		if (i == VCC_CONTROL_PORT) {
13411ae08745Sheppo 			continue;
13421ae08745Sheppo 		}
13431ae08745Sheppo 
13441ae08745Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
13451ae08745Sheppo 			continue;
13461ae08745Sheppo 		}
13471ae08745Sheppo 
13481ae08745Sheppo 		/* a port exists before vntsd becomes online */
13491ae08745Sheppo 		mutex_enter(&vport->lock);
13501ae08745Sheppo 
13511ae08745Sheppo 		if (num_ports == 0) {
13521ae08745Sheppo 			/* more ports than vntsd's buffer can hold */
13531ae08745Sheppo 			vport->status |= VCC_PORT_ADDED;
13541ae08745Sheppo 			notify_vntsd = B_TRUE;
13551ae08745Sheppo 			mutex_exit(&vport->lock);
13561ae08745Sheppo 			continue;
13571ae08745Sheppo 		}
13581ae08745Sheppo 
13591ae08745Sheppo 		bzero(&cons, sizeof (vcc_console_t));
13601ae08745Sheppo 
13611ae08745Sheppo 		/* construct console buffer */
13621ae08745Sheppo 		cons.cons_no = vport->number;
13631ae08745Sheppo 		cons.tcp_port = vport->tcp_port;
13641ae08745Sheppo 		(void) memcpy(cons.domain_name,
13651ae08745Sheppo 		    vport->minorp->domain_name, MAXPATHLEN);
13661ae08745Sheppo 
13671ae08745Sheppo 		(void) memcpy(cons.group_name, vport->group_name,
13681ae08745Sheppo 		    MAXPATHLEN);
13691ae08745Sheppo 		vport->status &= ~VCC_PORT_ADDED;
13701ae08745Sheppo 		mutex_exit(&vport->lock);
13711ae08745Sheppo 
13721ae08745Sheppo 		(void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
13731ae08745Sheppo 		    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
13741ae08745Sheppo 
13751ae08745Sheppo 		/* copy out data */
13761ae08745Sheppo 		if (ddi_copyout(&cons, (void *)buf,
137719397407SSherry Moore 		    sizeof (vcc_console_t), mode)) {
13781ae08745Sheppo 			mutex_exit(&vport->lock);
13791ae08745Sheppo 			return (EFAULT);
13801ae08745Sheppo 		}
13811ae08745Sheppo 		buf += sizeof (vcc_console_t);
13821ae08745Sheppo 
13831ae08745Sheppo 		num_ports--;
13841ae08745Sheppo 
13851ae08745Sheppo 	}
13861ae08745Sheppo 
13871ae08745Sheppo 	if (num_ports == 0) {
13881ae08745Sheppo 		/* vntsd's buffer is full */
13891ae08745Sheppo 
13901ae08745Sheppo 		if (notify_vntsd) {
13911ae08745Sheppo 			/* more ports need to notify vntsd */
13921ae08745Sheppo 			vport = &vccp->port[VCC_CONTROL_PORT];
13931ae08745Sheppo 			mutex_enter(&vport->lock);
13941ae08745Sheppo 			vport->pollevent |= VCC_POLL_ADD_PORT;
13951ae08745Sheppo 			mutex_exit(&vport->lock);
13961ae08745Sheppo 		}
13971ae08745Sheppo 
13981ae08745Sheppo 		return (0);
13991ae08745Sheppo 	}
14001ae08745Sheppo 
14011ae08745Sheppo 	/* less ports than vntsd expected */
14021ae08745Sheppo 	bzero(&cons, sizeof (vcc_console_t));
14031ae08745Sheppo 	cons.cons_no = -1;
14041ae08745Sheppo 
14051ae08745Sheppo 	while (num_ports > 0) {
14061ae08745Sheppo 		/* fill vntsd buffer with no console */
14071ae08745Sheppo 		if (ddi_copyout(&cons, (void *)buf,
140819397407SSherry Moore 		    sizeof (vcc_console_t), mode) != 0) {
14091ae08745Sheppo 			mutex_exit(&vport->lock);
14101ae08745Sheppo 			return (EFAULT);
14111ae08745Sheppo 		}
14121ae08745Sheppo 		D1("i_vcc_cons_tbl: a port is  deleted\n");
14131ae08745Sheppo 		buf += sizeof (vcc_console_t) +MAXPATHLEN;
14141ae08745Sheppo 		num_ports--;
14151ae08745Sheppo 	}
14161ae08745Sheppo 
14171ae08745Sheppo 	return (0);
14181ae08745Sheppo }
14191ae08745Sheppo 
14201ae08745Sheppo 
14211ae08745Sheppo /* turn off event flag if there is no more change */
14221ae08745Sheppo static void
i_vcc_turn_off_event(vcc_t * vccp,uint32_t port_status,uint32_t event)14231ae08745Sheppo i_vcc_turn_off_event(vcc_t *vccp, uint32_t port_status, uint32_t event)
14241ae08745Sheppo {
14251ae08745Sheppo 
14261ae08745Sheppo 	vcc_port_t *vport;
14271ae08745Sheppo 	int i;
14281ae08745Sheppo 
14291ae08745Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
14301ae08745Sheppo 
14311ae08745Sheppo 		vport = &(vccp->port[i]);
14321ae08745Sheppo 
14331ae08745Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
14341ae08745Sheppo 			continue;
14351ae08745Sheppo 		}
14361ae08745Sheppo 
14371ae08745Sheppo 
14381ae08745Sheppo 		if (vport->status & port_status) {
14391ae08745Sheppo 			/* more port changes status */
14401ae08745Sheppo 			return;
14411ae08745Sheppo 		}
14421ae08745Sheppo 
14431ae08745Sheppo 	}
14441ae08745Sheppo 
14451ae08745Sheppo 	/* no more changed port  */
14461ae08745Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
14471ae08745Sheppo 
14481ae08745Sheppo 	/* turn off event */
14491ae08745Sheppo 	mutex_enter(&vport->lock);
14501ae08745Sheppo 	vport->pollevent &= ~event;
14511ae08745Sheppo 	mutex_exit(&vport->lock);
14521ae08745Sheppo }
14531ae08745Sheppo 
14541ae08745Sheppo /* ioctl VCC_CONS_INFO */
14551ae08745Sheppo static int
i_vcc_cons_info(vcc_t * vccp,caddr_t buf,int mode)14561ae08745Sheppo i_vcc_cons_info(vcc_t *vccp, caddr_t buf, int mode)
14571ae08745Sheppo {
14581ae08745Sheppo 	vcc_console_t	cons;
14591ae08745Sheppo 	uint_t		portno;
14601ae08745Sheppo 	vcc_port_t	*vport;
14611ae08745Sheppo 	char pathname[MAXPATHLEN];
14621ae08745Sheppo 
14631ae08745Sheppo 	/* read in portno */
14641ae08745Sheppo 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
14651ae08745Sheppo 		return (EFAULT);
14661ae08745Sheppo 	}
14671ae08745Sheppo 
14681ae08745Sheppo 	D1("i_vcc_cons_info@%d:\n", portno);
14691ae08745Sheppo 
14701ae08745Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
14711ae08745Sheppo 		return (EINVAL);
14721ae08745Sheppo 	}
14731ae08745Sheppo 
14741ae08745Sheppo 	vport = &vccp->port[portno];
14751ae08745Sheppo 
14761ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
14771ae08745Sheppo 		return (EINVAL);
14781ae08745Sheppo 	}
14791ae08745Sheppo 
14801ae08745Sheppo 	mutex_enter(&vport->lock);
14811ae08745Sheppo 	vport->status &= ~VCC_PORT_ADDED;
14821ae08745Sheppo 
14831ae08745Sheppo 	/* construct configruation data  */
14841ae08745Sheppo 	bzero(&cons, sizeof (vcc_console_t));
14851ae08745Sheppo 
14861ae08745Sheppo 	cons.cons_no = vport->number;
14871ae08745Sheppo 	cons.tcp_port = vport->tcp_port;
14881ae08745Sheppo 
14891ae08745Sheppo 	(void) memcpy(cons.domain_name, vport->minorp->domain_name, MAXPATHLEN);
14901ae08745Sheppo 
14911ae08745Sheppo 	(void) memcpy(cons.group_name, vport->group_name, MAXPATHLEN);
14921ae08745Sheppo 
14931ae08745Sheppo 	mutex_exit(&vport->lock);
14941ae08745Sheppo 
14951ae08745Sheppo 	(void) ddi_pathname(vccp->dip, pathname),
14961ae08745Sheppo 
149719397407SSherry Moore 	    /* copy device name */
149819397407SSherry Moore 	    (void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
14991ae08745Sheppo 	    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
15001ae08745Sheppo 	/* copy data */
15011ae08745Sheppo 	if (ddi_copyout(&cons, (void *)buf,
150219397407SSherry Moore 	    sizeof (vcc_console_t), mode) != 0) {
15031ae08745Sheppo 		mutex_exit(&vport->lock);
15041ae08745Sheppo 		return (EFAULT);
15051ae08745Sheppo 	}
15061ae08745Sheppo 
15071ae08745Sheppo 	D1("i_vcc_cons_info@%d:domain:%s serv:%s tcp@%lld %s\n",
15081ae08745Sheppo 	    cons.cons_no, cons.domain_name,
15091ae08745Sheppo 	    cons.group_name, cons.tcp_port, cons.dev_name);
15101ae08745Sheppo 
15111ae08745Sheppo 	i_vcc_turn_off_event(vccp, VCC_PORT_ADDED, VCC_POLL_ADD_PORT);
15121ae08745Sheppo 
15131ae08745Sheppo 	return (0);
15141ae08745Sheppo }
15151ae08745Sheppo 
15161ae08745Sheppo 
15171ae08745Sheppo /* response to vntsd inquiry ioctl call */
15181ae08745Sheppo static int
i_vcc_inquiry(vcc_t * vccp,caddr_t buf,int mode)15191ae08745Sheppo i_vcc_inquiry(vcc_t *vccp, caddr_t buf, int mode)
15201ae08745Sheppo {
15211ae08745Sheppo 	vcc_port_t	*vport;
15221ae08745Sheppo 	uint_t		i;
15231ae08745Sheppo 	vcc_response_t	msg;
15241ae08745Sheppo 
15251ae08745Sheppo 	vport = &(vccp->port[VCC_CONTROL_PORT]);
15261ae08745Sheppo 
15271ae08745Sheppo 	if ((vport->pollevent & VCC_POLL_ADD_PORT) == 0) {
15281ae08745Sheppo 		return (EINVAL);
15291ae08745Sheppo 	}
15301ae08745Sheppo 
15314d39be2bSsg 	/* an added port */
15321ae08745Sheppo 
15331ae08745Sheppo 	D1("i_vcc_inquiry\n");
15341ae08745Sheppo 
15351ae08745Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
15361ae08745Sheppo 		if ((vccp->port[i].status & VCC_PORT_AVAIL) == 0) {
15371ae08745Sheppo 			continue;
15381ae08745Sheppo 		}
15391ae08745Sheppo 
15401ae08745Sheppo 		if (vccp->port[i].status & VCC_PORT_ADDED) {
15411ae08745Sheppo 			/* port added */
15421ae08745Sheppo 			msg.reason = VCC_CONS_ADDED;
15431ae08745Sheppo 			msg.cons_no = i;
15441ae08745Sheppo 
15451ae08745Sheppo 			if (ddi_copyout((void *)&msg, (void *)buf,
154619397407SSherry Moore 			    sizeof (msg), mode) == -1) {
15471ae08745Sheppo 				cmn_err(CE_CONT, "i_vcc_find_changed_port:"
154819397407SSherry Moore 				    "ddi_copyout"
15491ae08745Sheppo 				    " failed\n");
15501ae08745Sheppo 				return (EFAULT);
15511ae08745Sheppo 			}
15521ae08745Sheppo 			return (0);
15531ae08745Sheppo 		}
15541ae08745Sheppo 	}
15551ae08745Sheppo 
15567636cb21Slm 	/* the added port was deleted before vntsd wakes up */
15577636cb21Slm 	msg.reason = VCC_CONS_MISS_ADDED;
15587636cb21Slm 
15597636cb21Slm 	if (ddi_copyout((void *)&msg, (void *)buf,
156019397407SSherry Moore 	    sizeof (msg), mode) == -1) {
15617636cb21Slm 		cmn_err(CE_CONT, "i_vcc_find_changed_port: ddi_copyout"
15627636cb21Slm 		    " failed\n");
15637636cb21Slm 		return (EFAULT);
15647636cb21Slm 	}
15657636cb21Slm 
15667636cb21Slm 	return (0);
15671ae08745Sheppo }
15681ae08745Sheppo 
15691ae08745Sheppo /* clean up events after vntsd exits */
15701ae08745Sheppo static int
i_vcc_reset_events(vcc_t * vccp)15711ae08745Sheppo i_vcc_reset_events(vcc_t *vccp)
15721ae08745Sheppo {
15731ae08745Sheppo 	uint_t	    i;
15741ae08745Sheppo 	vcc_port_t  *vport;
15751ae08745Sheppo 
15761ae08745Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
15771ae08745Sheppo 		vport = &(vccp->port[i]);
15781ae08745Sheppo 
15791ae08745Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
15801ae08745Sheppo 			continue;
15811ae08745Sheppo 		}
15821ae08745Sheppo 
15831ae08745Sheppo 		ASSERT(!mutex_owned(&vport->lock));
15841ae08745Sheppo 
15851ae08745Sheppo 		if (i == VCC_CONTROL_PORT) {
15861ae08745Sheppo 			/* close control port */
15871ae08745Sheppo 			mutex_enter(&vport->lock);
15881ae08745Sheppo 			vport->status &= ~VCC_PORT_OPEN;
15891ae08745Sheppo 
15901ae08745Sheppo 			/* clean up poll events */
15911ae08745Sheppo 			vport->pollevent = 0;
15921ae08745Sheppo 			vport->pollflag = 0;
15931ae08745Sheppo 			mutex_exit(&vport->lock);
15941ae08745Sheppo 			continue;
15951ae08745Sheppo 		}
15961ae08745Sheppo 		if (vport->status & VCC_PORT_ADDED) {
15971ae08745Sheppo 			/* pending added port event to vntsd */
15981ae08745Sheppo 			mutex_enter(&vport->lock);
15991ae08745Sheppo 			vport->status &= ~VCC_PORT_ADDED;
16001ae08745Sheppo 			mutex_exit(&vport->lock);
16011ae08745Sheppo 		}
16021ae08745Sheppo 
16031ae08745Sheppo 	}
16041ae08745Sheppo 
16051ae08745Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
16061ae08745Sheppo 
16071ae08745Sheppo 	return (0);
16081ae08745Sheppo }
16091ae08745Sheppo 
16101ae08745Sheppo /* ioctl VCC_FORCE_CLOSE */
16111ae08745Sheppo static int
i_vcc_force_close(vcc_t * vccp,caddr_t buf,int mode)16121ae08745Sheppo i_vcc_force_close(vcc_t *vccp, caddr_t buf, int mode)
16131ae08745Sheppo {
16141ae08745Sheppo 	uint_t		portno;
16151ae08745Sheppo 	vcc_port_t	*vport;
16161ae08745Sheppo 	int		rv;
16171ae08745Sheppo 
16181ae08745Sheppo 	/* read in portno */
16191ae08745Sheppo 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
16201ae08745Sheppo 		return (EFAULT);
16211ae08745Sheppo 	}
16221ae08745Sheppo 
16231ae08745Sheppo 	D1("i_vcc_force_close@%d:\n", portno);
16241ae08745Sheppo 
16251ae08745Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
16261ae08745Sheppo 		return (EINVAL);
16271ae08745Sheppo 	}
16281ae08745Sheppo 
16291ae08745Sheppo 	vport = &vccp->port[portno];
16301ae08745Sheppo 
16311ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
16321ae08745Sheppo 		return (EINVAL);
16331ae08745Sheppo 	}
16341ae08745Sheppo 
16351ae08745Sheppo 	mutex_enter(&vport->lock);
16361ae08745Sheppo 
16371ae08745Sheppo 	rv = i_vcc_close_port(vport);
16381ae08745Sheppo 
16391ae08745Sheppo 	/* block callers other than vntsd */
16401ae08745Sheppo 	vport->valid_pid = ddi_get_pid();
16411ae08745Sheppo 
16421ae08745Sheppo 	mutex_exit(&vport->lock);
16431ae08745Sheppo 	return (rv);
16441ae08745Sheppo 
16451ae08745Sheppo }
16461ae08745Sheppo 
16471ae08745Sheppo /* ioctl VCC_CONS_STATUS */
16481ae08745Sheppo static int
i_vcc_cons_status(vcc_t * vccp,caddr_t buf,int mode)16491ae08745Sheppo i_vcc_cons_status(vcc_t *vccp, caddr_t buf, int mode)
16501ae08745Sheppo {
16511ae08745Sheppo 	vcc_console_t	console;
16521ae08745Sheppo 	vcc_port_t	*vport;
16531ae08745Sheppo 
16541ae08745Sheppo 	/* read in portno */
16551ae08745Sheppo 	if (ddi_copyin((void*)buf, &console, sizeof (console), mode)) {
16561ae08745Sheppo 		return (EFAULT);
16571ae08745Sheppo 	}
16581ae08745Sheppo 
16591ae08745Sheppo 	D1("i_vcc_cons_status@%d:\n", console.cons_no);
16601ae08745Sheppo 
16611ae08745Sheppo 	if ((console.cons_no >= VCC_MAX_PORTS) ||
166219397407SSherry Moore 	    (console.cons_no == VCC_CONTROL_PORT)) {
16631ae08745Sheppo 		return (EINVAL);
16641ae08745Sheppo 	}
16651ae08745Sheppo 
16661ae08745Sheppo 
16671ae08745Sheppo 	vport = &vccp->port[console.cons_no];
16681ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
16691ae08745Sheppo 		console.cons_no = -1;
16701ae08745Sheppo 	} else  if (strncmp(console.domain_name, vport->minorp->domain_name,
167119397407SSherry Moore 	    MAXPATHLEN)) {
16721ae08745Sheppo 		console.cons_no = -1;
16731ae08745Sheppo 	} else if (strncmp(console.group_name, vport->group_name,
167419397407SSherry Moore 	    MAXPATHLEN)) {
16751ae08745Sheppo 		console.cons_no = -1;
16761ae08745Sheppo 	} else if (console.tcp_port != vport->tcp_port) {
16771ae08745Sheppo 		console.cons_no = -1;
16784d39be2bSsg 	} else if (vport->ldc_id == VCC_INVALID_CHANNEL) {
16794d39be2bSsg 		console.cons_no = -1;
16801ae08745Sheppo 	}
16811ae08745Sheppo 
16821ae08745Sheppo 	D1("i_vcc_cons_status@%d: %s %s %llx\n", console.cons_no,
16831ae08745Sheppo 	    console.group_name, console.domain_name, console.tcp_port);
16841ae08745Sheppo 	if (ddi_copyout(&console, (void *)buf, sizeof (console), mode) == -1) {
16851ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_cons_status ddi_copyout failed\n");
16861ae08745Sheppo 		return (EFAULT);
16871ae08745Sheppo 	}
16881ae08745Sheppo 
16891ae08745Sheppo 	return (0);
16901ae08745Sheppo }
16911ae08745Sheppo 
16921ae08745Sheppo /* cb_ioctl handler for vcc control port */
16931ae08745Sheppo static int
i_vcc_ctrl_ioctl(vcc_t * vccp,int cmd,void * arg,int mode)16941ae08745Sheppo i_vcc_ctrl_ioctl(vcc_t *vccp, int cmd, void* arg, int mode)
16951ae08745Sheppo {
16961ae08745Sheppo 
16971ae08745Sheppo 	static uint_t	num_ports;
16981ae08745Sheppo 
16991ae08745Sheppo 
17001ae08745Sheppo 	switch (cmd) {
17011ae08745Sheppo 
17021ae08745Sheppo 	case VCC_NUM_CONSOLE:
17031ae08745Sheppo 
17041ae08745Sheppo 		mutex_enter(&vccp->lock);
17051ae08745Sheppo 		num_ports = vccp->num_ports;
17061ae08745Sheppo 		mutex_exit(&vccp->lock);
17071ae08745Sheppo 		/* number of consoles */
17081ae08745Sheppo 
17091ae08745Sheppo 		return (ddi_copyout((void *)&num_ports, arg,
171019397407SSherry Moore 		    sizeof (int), mode));
17111ae08745Sheppo 	case VCC_CONS_TBL:
17121ae08745Sheppo 
17131ae08745Sheppo 		/* console config table */
17141ae08745Sheppo 		return (i_vcc_cons_tbl(vccp, num_ports, (caddr_t)arg, mode));
17151ae08745Sheppo 
17161ae08745Sheppo 	case VCC_INQUIRY:
17171ae08745Sheppo 
17181ae08745Sheppo 		/* reason for wakeup */
17191ae08745Sheppo 		return (i_vcc_inquiry(vccp, (caddr_t)arg, mode));
17201ae08745Sheppo 
17211ae08745Sheppo 	case VCC_CONS_INFO:
17221ae08745Sheppo 		/* a console config */
17231ae08745Sheppo 		return (i_vcc_cons_info(vccp, (caddr_t)arg, mode));
17241ae08745Sheppo 
17251ae08745Sheppo 	case VCC_FORCE_CLOSE:
17261ae08745Sheppo 		/* force to close a console */
17271ae08745Sheppo 		return (i_vcc_force_close(vccp, (caddr_t)arg, mode));
17281ae08745Sheppo 
17291ae08745Sheppo 	case VCC_CONS_STATUS:
17301ae08745Sheppo 		/* console status */
17311ae08745Sheppo 		return (i_vcc_cons_status(vccp, (caddr_t)arg, mode));
17321ae08745Sheppo 
17331ae08745Sheppo 	default:
17341ae08745Sheppo 
17351ae08745Sheppo 		/* unknown command */
17361ae08745Sheppo 		return (ENODEV);
17371ae08745Sheppo 	}
17381ae08745Sheppo 
17391ae08745Sheppo 
17401ae08745Sheppo }
17411ae08745Sheppo 
17421ae08745Sheppo /* write data to ldc. may block if channel has no space for write */
17431ae08745Sheppo static int
i_vcc_write_ldc(vcc_port_t * vport,vcc_msg_t * buf)17441ae08745Sheppo i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf)
17451ae08745Sheppo {
17461ae08745Sheppo 	int	rv = EIO;
17471ae08745Sheppo 	size_t	size;
17481ae08745Sheppo 
17491ae08745Sheppo 	ASSERT(mutex_owned(&vport->lock));
17501ae08745Sheppo 	ASSERT((vport->status & VCC_PORT_USE_WRITE_LDC) == 0);
17511ae08745Sheppo 
17521ae08745Sheppo 	for (; ; ) {
17531ae08745Sheppo 
17541ae08745Sheppo 		size = VCC_HDR_SZ + buf->size;
17551ae08745Sheppo 		rv = ldc_write(vport->ldc_handle, (caddr_t)buf, &size);
17561ae08745Sheppo 
17571ae08745Sheppo 		D1("i_vcc_write_ldc: port@%d: err=%d %d bytes\n",
17581ae08745Sheppo 		    vport->number, rv, size);
17591ae08745Sheppo 
17601ae08745Sheppo 		if (rv == 0) {
17611ae08745Sheppo 			return (rv);
17621ae08745Sheppo 		}
17631ae08745Sheppo 
17641ae08745Sheppo 		if (rv != EWOULDBLOCK) {
17651ae08745Sheppo 			return (EIO);
17661ae08745Sheppo 		}
17671ae08745Sheppo 
17681ae08745Sheppo 		if (vport->status & VCC_PORT_NONBLOCK) {
17691ae08745Sheppo 			return (EAGAIN);
17701ae08745Sheppo 		}
17711ae08745Sheppo 
17721ae08745Sheppo 		/*  block util ldc has more space */
17731ae08745Sheppo 
17741ae08745Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
17751ae08745Sheppo 		    VCC_PORT_LDC_WRITE_READY);
17761ae08745Sheppo 
17771ae08745Sheppo 		if (rv) {
17781ae08745Sheppo 			return (rv);
17791ae08745Sheppo 		}
17801ae08745Sheppo 
17811ae08745Sheppo 		vport->status &= ~VCC_PORT_LDC_WRITE_READY;
17821ae08745Sheppo 
17831ae08745Sheppo 	}
17841ae08745Sheppo 
17851ae08745Sheppo }
17861ae08745Sheppo 
17871ae08745Sheppo 
17881ae08745Sheppo 
17891ae08745Sheppo /* cb_ioctl handler for port ioctl */
17901ae08745Sheppo static int
i_vcc_port_ioctl(vcc_t * vccp,minor_t minor,int portno,int cmd,void * arg,int mode)17911ae08745Sheppo i_vcc_port_ioctl(vcc_t *vccp, minor_t minor, int portno, int cmd, void *arg,
17921ae08745Sheppo     int mode)
17931ae08745Sheppo {
17941ae08745Sheppo 
17951ae08745Sheppo 	vcc_port_t	*vport;
17961ae08745Sheppo 	struct termios	term;
17971ae08745Sheppo 	vcc_msg_t	buf;
17981ae08745Sheppo 	int		rv;
17991ae08745Sheppo 
18001ae08745Sheppo 	D1("i_vcc_port_ioctl@%d cmd %d\n", portno, cmd);
18011ae08745Sheppo 
18021ae08745Sheppo 	vport = &(vccp->port[portno]);
18031ae08745Sheppo 
18041ae08745Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
18051ae08745Sheppo 		return (EIO);
18061ae08745Sheppo 	}
18071ae08745Sheppo 
18081ae08745Sheppo 
18091ae08745Sheppo 	switch (cmd) {
18101ae08745Sheppo 
18111ae08745Sheppo 	/* terminal support */
18121ae08745Sheppo 	case TCGETA:
18131ae08745Sheppo 	case TCGETS:
18141ae08745Sheppo 
18151ae08745Sheppo 		mutex_enter(&vport->lock);
18161ae08745Sheppo 
18171ae08745Sheppo 		/* check minor no and pid */
18181ae08745Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
181919397407SSherry Moore 		    vport)) != 0) {
18201ae08745Sheppo 			mutex_exit(&vport->lock);
18211ae08745Sheppo 			return (rv);
18221ae08745Sheppo 		}
18231ae08745Sheppo 
18241ae08745Sheppo 		(void) memcpy(&term, &vport->term, sizeof (term));
18251ae08745Sheppo 		mutex_exit(&vport->lock);
18261ae08745Sheppo 
18271ae08745Sheppo 		return (ddi_copyout(&term, arg, sizeof (term), mode));
18281ae08745Sheppo 
18291ae08745Sheppo 	case TCSETS:
18301ae08745Sheppo 	case TCSETA:
18311ae08745Sheppo 	case TCSETAW:
18321ae08745Sheppo 	case TCSETAF:
18331ae08745Sheppo 
18341ae08745Sheppo 		if (ddi_copyin(arg, &term, sizeof (term), mode) != 0) {
18351ae08745Sheppo 			return (EFAULT);
18361ae08745Sheppo 		}
18371ae08745Sheppo 
18381ae08745Sheppo 		mutex_enter(&vport->lock);
18391ae08745Sheppo 
18401ae08745Sheppo 		/* check minor no and pid */
18411ae08745Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
184219397407SSherry Moore 		    vport)) != 0) {
18431ae08745Sheppo 			mutex_exit(&vport->lock);
18441ae08745Sheppo 			return (rv);
18451ae08745Sheppo 		}
18461ae08745Sheppo 
18471ae08745Sheppo 		(void) memcpy(&vport->term, &term, sizeof (term));
18481ae08745Sheppo 		mutex_exit(&vport->lock);
18491ae08745Sheppo 		return (0);
18501ae08745Sheppo 
18511ae08745Sheppo 
18521ae08745Sheppo 	case TCSBRK:
18531ae08745Sheppo 
18541ae08745Sheppo 		/* send break to console */
18551ae08745Sheppo 		mutex_enter(&vport->lock);
18561ae08745Sheppo 
18571ae08745Sheppo 		/* check minor no and pid */
18581ae08745Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
185919397407SSherry Moore 		    vport)) != 0) {
18601ae08745Sheppo 			mutex_exit(&vport->lock);
18611ae08745Sheppo 			return (rv);
18621ae08745Sheppo 		}
18631ae08745Sheppo 
18641ae08745Sheppo 		/* wait for write available */
18651ae08745Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
18661ae08745Sheppo 		    VCC_PORT_LDC_CHANNEL_READY| VCC_PORT_USE_WRITE_LDC);
18671ae08745Sheppo 		if (rv) {
18681ae08745Sheppo 			mutex_exit(&vport->lock);
18691ae08745Sheppo 			return (rv);
18701ae08745Sheppo 		}
18711ae08745Sheppo 
18721ae08745Sheppo 		vport->status &= ~VCC_PORT_USE_WRITE_LDC;
18731ae08745Sheppo 
18741ae08745Sheppo 		buf.type = LDC_CONSOLE_CTRL;
18751ae08745Sheppo 		buf.ctrl_msg = LDC_CONSOLE_BREAK;
18761ae08745Sheppo 		buf.size = 0;
18771ae08745Sheppo 
18781ae08745Sheppo 		rv = i_vcc_write_ldc(vport, &buf);
18791ae08745Sheppo 
18801ae08745Sheppo 		mutex_exit(&vport->lock);
18811ae08745Sheppo 
18821ae08745Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
188319397407SSherry Moore 		    VCC_PORT_USE_WRITE_LDC);
18841ae08745Sheppo 		return (0);
18851ae08745Sheppo 
18861ae08745Sheppo 	case TCXONC:
18871ae08745Sheppo 		/* suspend read or write */
18881ae08745Sheppo 		if (ddi_copyin(arg, &cmd, sizeof (int), mode) != 0) {
18891ae08745Sheppo 			return (EFAULT);
18901ae08745Sheppo 		}
18911ae08745Sheppo 
18921ae08745Sheppo 		mutex_enter(&vport->lock);
18931ae08745Sheppo 
18941ae08745Sheppo 		/* check minor no and pid */
18951ae08745Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
189619397407SSherry Moore 		    vport)) != 0) {
18971ae08745Sheppo 			mutex_exit(&vport->lock);
18981ae08745Sheppo 			return (rv);
18991ae08745Sheppo 		}
19001ae08745Sheppo 
19011ae08745Sheppo 
19021ae08745Sheppo 		switch (cmd) {
19031ae08745Sheppo 
19041ae08745Sheppo 		case 0:
19057636cb21Slm 			/* suspend read */
19067636cb21Slm 			vport->status &= ~VCC_PORT_TERM_RD;
19071ae08745Sheppo 			break;
19087636cb21Slm 
19091ae08745Sheppo 		case 1:
19107636cb21Slm 			/* resume read */
19111ae08745Sheppo 			vport->status |= VCC_PORT_TERM_RD;
19121ae08745Sheppo 			cv_broadcast(&vport->read_cv);
19131ae08745Sheppo 			break;
19147636cb21Slm 
19157636cb21Slm 		case 2:
19167636cb21Slm 			/* suspend write */
19177636cb21Slm 			vport->status &= ~VCC_PORT_TERM_WR;
19187636cb21Slm 			break;
19197636cb21Slm 
19201ae08745Sheppo 		case 3:
19217636cb21Slm 			/* resume write */
19227636cb21Slm 			vport->status |= VCC_PORT_TERM_WR;
19237636cb21Slm 			cv_broadcast(&vport->write_cv);
19241ae08745Sheppo 			break;
19251ae08745Sheppo 
19261ae08745Sheppo 		default:
19277636cb21Slm 			mutex_exit(&vport->lock);
19287636cb21Slm 			return (EINVAL);
19291ae08745Sheppo 		}
19301ae08745Sheppo 
19311ae08745Sheppo 		mutex_exit(&vport->lock);
19321ae08745Sheppo 		return (0);
19331ae08745Sheppo 
19341ae08745Sheppo 	case TCFLSH:
19351ae08745Sheppo 		return (0);
19361ae08745Sheppo 
19371ae08745Sheppo 	default:
19387636cb21Slm 		return (EINVAL);
19391ae08745Sheppo 	}
19401ae08745Sheppo 
19411ae08745Sheppo }
19421ae08745Sheppo 
19431ae08745Sheppo /* cb_ioctl */
19441ae08745Sheppo static int
vcc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)19451ae08745Sheppo vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
19461ae08745Sheppo     cred_t *credp, int *rvalp)
19471ae08745Sheppo {
19481ae08745Sheppo 	_NOTE(ARGUNUSED(credp, rvalp))
19491ae08745Sheppo 
19501ae08745Sheppo 	int instance;
19511ae08745Sheppo 	minor_t minor;
19521ae08745Sheppo 	int portno;
19531ae08745Sheppo 	vcc_t *vccp;
19541ae08745Sheppo 
19551ae08745Sheppo 	minor = getminor(dev);
19561ae08745Sheppo 
19571ae08745Sheppo 	instance = VCCINST(minor);
19581ae08745Sheppo 
19591ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
19601ae08745Sheppo 	if (vccp == NULL) {
19611ae08745Sheppo 		return (ENXIO);
19621ae08745Sheppo 	}
19631ae08745Sheppo 
19641ae08745Sheppo 	portno = VCCPORT(vccp, minor);
19651ae08745Sheppo 
19661ae08745Sheppo 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d\n", instance, portno);
19671ae08745Sheppo 
19681ae08745Sheppo 	if (portno >= VCC_MAX_PORTS) {
19691ae08745Sheppo 		cmn_err(CE_CONT, "vcc_ioctl:virtual-console-concentrator@%d"
197019397407SSherry Moore 		    " invalid portno\n", portno);
19711ae08745Sheppo 		return (EINVAL);
19721ae08745Sheppo 	}
19731ae08745Sheppo 
19741ae08745Sheppo 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d ioctl cmd=%d\n",
19751ae08745Sheppo 	    instance, portno, cmd);
19761ae08745Sheppo 
19771ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
19781ae08745Sheppo 		/* control ioctl */
19791ae08745Sheppo 		return (i_vcc_ctrl_ioctl(vccp, cmd, (void *)arg, mode));
19801ae08745Sheppo 	}
19811ae08745Sheppo 
19821ae08745Sheppo 	/* data port ioctl */
19831ae08745Sheppo 	return (i_vcc_port_ioctl(vccp, minor, portno, cmd, (void *)arg, mode));
19841ae08745Sheppo }
19851ae08745Sheppo 
19861ae08745Sheppo /* cb_read */
19871ae08745Sheppo static int
vcc_read(dev_t dev,struct uio * uiop,cred_t * credp)19881ae08745Sheppo vcc_read(dev_t dev, struct uio *uiop, cred_t *credp)
19891ae08745Sheppo {
19901ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
19911ae08745Sheppo 
19921ae08745Sheppo 	int	    instance;
19931ae08745Sheppo 	minor_t	    minor;
19941ae08745Sheppo 	uint_t	    portno;
19951ae08745Sheppo 	vcc_t	    *vccp;
19961ae08745Sheppo 	vcc_port_t  *vport;
19971ae08745Sheppo 	int	    rv = EIO;	/* by default fail ! */
19981ae08745Sheppo 	char 		*buf;
19991ae08745Sheppo 	size_t		uio_size;
20001ae08745Sheppo 	size_t		size;
20011ae08745Sheppo 
20021ae08745Sheppo 	minor = getminor(dev);
20031ae08745Sheppo 
20041ae08745Sheppo 	instance = VCCINST(minor);
20051ae08745Sheppo 
20061ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
20071ae08745Sheppo 	if (vccp == NULL) {
20081ae08745Sheppo 		return (ENXIO);
20091ae08745Sheppo 	}
20101ae08745Sheppo 
20111ae08745Sheppo 	portno = VCCPORT(vccp, minor);
20121ae08745Sheppo 
20131ae08745Sheppo 	/* no read for control port */
20141ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
20151ae08745Sheppo 		return (EIO);
20161ae08745Sheppo 	}
20171ae08745Sheppo 
20181ae08745Sheppo 	/* temp buf to hold ldc data */
20191ae08745Sheppo 	uio_size = uiop->uio_resid;
20201ae08745Sheppo 
20211ae08745Sheppo 	if (uio_size < VCC_MTU_SZ) {
20221ae08745Sheppo 		return (EINVAL);
20231ae08745Sheppo 	}
20241ae08745Sheppo 
20251ae08745Sheppo 	vport = &(vccp->port[portno]);
20261ae08745Sheppo 
20271ae08745Sheppo 	mutex_enter(&vport->lock);
20281ae08745Sheppo 
20291ae08745Sheppo 	/* check minor no and pid */
20301ae08745Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
203119397407SSherry Moore 	    vport)) != 0) {
20321ae08745Sheppo 		mutex_exit(&vport->lock);
20331ae08745Sheppo 		return (rv);
20341ae08745Sheppo 	}
20351ae08745Sheppo 
20361ae08745Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
203719397407SSherry Moore 	    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
203819397407SSherry Moore 	    VCC_PORT_USE_READ_LDC);
20391ae08745Sheppo 	if (rv) {
20401ae08745Sheppo 		mutex_exit(&vport->lock);
20411ae08745Sheppo 		return (rv);
20421ae08745Sheppo 	}
20431ae08745Sheppo 
20441ae08745Sheppo 	buf = kmem_alloc(uio_size, KM_SLEEP);
20451ae08745Sheppo 
20461ae08745Sheppo 	vport->status &= ~VCC_PORT_USE_READ_LDC;
20471ae08745Sheppo 
20481ae08745Sheppo 	for (; ; ) {
20491ae08745Sheppo 
20501ae08745Sheppo 		size = uio_size;
20511ae08745Sheppo 		rv = i_vcc_read_ldc(vport, buf, &size);
20521ae08745Sheppo 
20531ae08745Sheppo 
20541ae08745Sheppo 		if (rv == EAGAIN) {
20551ae08745Sheppo 			/* should block? */
20561ae08745Sheppo 			if (vport->status & VCC_PORT_NONBLOCK) {
20571ae08745Sheppo 				break;
20581ae08745Sheppo 			}
20591ae08745Sheppo 
20601ae08745Sheppo 		} else if (rv) {
20611ae08745Sheppo 			/* error */
20621ae08745Sheppo 			break;
20631ae08745Sheppo 		}
20641ae08745Sheppo 
20651ae08745Sheppo 		if (size > 0) {
20661ae08745Sheppo 			/* got data */
20671ae08745Sheppo 			break;
20681ae08745Sheppo 		}
20691ae08745Sheppo 
20701ae08745Sheppo 		/* wait for data from ldc */
20711ae08745Sheppo 		vport->status &= ~VCC_PORT_LDC_DATA_READY;
20727636cb21Slm 
20737636cb21Slm 		mutex_exit(&vport->lock);
20747636cb21Slm 		i_vcc_set_port_status(vport, &vport->read_cv,
20757636cb21Slm 		    VCC_PORT_USE_READ_LDC);
20767636cb21Slm 		mutex_enter(&vport->lock);
20777636cb21Slm 
20781ae08745Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->read_cv,
20797636cb21Slm 		    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
20807636cb21Slm 		    VCC_PORT_USE_READ_LDC| VCC_PORT_LDC_DATA_READY);
20811ae08745Sheppo 		if (rv) {
20821ae08745Sheppo 			break;
20831ae08745Sheppo 		}
20847636cb21Slm 
20857636cb21Slm 		vport->status &= ~VCC_PORT_USE_READ_LDC;
20861ae08745Sheppo 	}
20871ae08745Sheppo 
20881ae08745Sheppo 	mutex_exit(&vport->lock);
20891ae08745Sheppo 
20901ae08745Sheppo 	if ((rv == 0) && (size > 0)) {
20911ae08745Sheppo 		/* data is in buf */
20921ae08745Sheppo 		rv = uiomove(buf, size, UIO_READ, uiop);
20931ae08745Sheppo 	}
20941ae08745Sheppo 
20951ae08745Sheppo 	kmem_free(buf, uio_size);
20961ae08745Sheppo 	i_vcc_set_port_status(vport, &vport->read_cv, VCC_PORT_USE_READ_LDC);
20971ae08745Sheppo 
20981ae08745Sheppo 	return (rv);
20991ae08745Sheppo }
21001ae08745Sheppo 
21011ae08745Sheppo 
21021ae08745Sheppo /* cb_write */
21031ae08745Sheppo static int
vcc_write(dev_t dev,struct uio * uiop,cred_t * credp)21041ae08745Sheppo vcc_write(dev_t dev, struct uio *uiop, cred_t *credp)
21051ae08745Sheppo {
21061ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
21071ae08745Sheppo 
21081ae08745Sheppo 	int	    instance;
21091ae08745Sheppo 	minor_t	    minor;
21101ae08745Sheppo 	size_t	    size;
21111ae08745Sheppo 	size_t	    bytes;
21121ae08745Sheppo 	uint_t	    portno;
21131ae08745Sheppo 	vcc_t	    *vccp;
21141ae08745Sheppo 
21151ae08745Sheppo 	vcc_port_t  *vport;
21161ae08745Sheppo 	int	    rv = EIO;
21171ae08745Sheppo 
21181ae08745Sheppo 	vcc_msg_t	buf;
21191ae08745Sheppo 
21201ae08745Sheppo 	minor = getminor(dev);
21211ae08745Sheppo 
21221ae08745Sheppo 	instance = VCCINST(minor);
21231ae08745Sheppo 
21241ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
21251ae08745Sheppo 	if (vccp == NULL) {
21261ae08745Sheppo 		return (ENXIO);
21271ae08745Sheppo 	}
21281ae08745Sheppo 
21291ae08745Sheppo 	portno = VCCPORT(vccp, minor);
21301ae08745Sheppo 
21311ae08745Sheppo 	/* no write for control port */
21321ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
21331ae08745Sheppo 		return (EIO);
21341ae08745Sheppo 	}
21351ae08745Sheppo 	vport = &(vccp->port[portno]);
21361ae08745Sheppo 
21371ae08745Sheppo 	/*
21381ae08745Sheppo 	 * check if the channel has been configured,
21391ae08745Sheppo 	 * if write has been suspend and grab write lock.
21401ae08745Sheppo 	 */
21411ae08745Sheppo 	mutex_enter(&vport->lock);
21421ae08745Sheppo 
21431ae08745Sheppo 	/* check minor no and pid */
21441ae08745Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
214519397407SSherry Moore 	    vport)) != 0) {
21461ae08745Sheppo 		mutex_exit(&vport->lock);
21471ae08745Sheppo 		return (rv);
21481ae08745Sheppo 	}
21491ae08745Sheppo 
21501ae08745Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
215119397407SSherry Moore 	    VCC_PORT_TERM_WR|VCC_PORT_LDC_CHANNEL_READY|
215219397407SSherry Moore 	    VCC_PORT_USE_WRITE_LDC);
21531ae08745Sheppo 	if (rv) {
21541ae08745Sheppo 		mutex_exit(&vport->lock);
21551ae08745Sheppo 		return (rv);
21561ae08745Sheppo 	}
21571ae08745Sheppo 
21581ae08745Sheppo 	vport->status &= ~VCC_PORT_USE_WRITE_LDC;
21591ae08745Sheppo 	mutex_exit(&vport->lock);
21601ae08745Sheppo 	size = uiop->uio_resid;
21611ae08745Sheppo 
21621ae08745Sheppo 	D2("vcc_write: virtual-console-concentrator@%d:%d writing %d bytes\n",
21631ae08745Sheppo 	    instance, portno, size);
21641ae08745Sheppo 
21651ae08745Sheppo 
21661ae08745Sheppo 
21671ae08745Sheppo 	buf.type = LDC_CONSOLE_DATA;
21681ae08745Sheppo 
21691ae08745Sheppo 	while (size) {
21701ae08745Sheppo 
21711ae08745Sheppo 		bytes = MIN(size, VCC_MTU_SZ);
21721ae08745Sheppo 		/* move data */
21731ae08745Sheppo 		rv = uiomove(&(buf.data), bytes, UIO_WRITE, uiop);
21741ae08745Sheppo 
21751ae08745Sheppo 		if (rv) {
21761ae08745Sheppo 			break;
21771ae08745Sheppo 		}
21781ae08745Sheppo 
21791ae08745Sheppo 		/* write to ldc */
21801ae08745Sheppo 		buf.size = bytes;
21811ae08745Sheppo 
21821ae08745Sheppo 		mutex_enter(&vport->lock);
21831ae08745Sheppo 
21841ae08745Sheppo 		/* check minor no and pid */
21851ae08745Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
218619397407SSherry Moore 		    vport)) != 0) {
21871ae08745Sheppo 			mutex_exit(&vport->lock);
21881ae08745Sheppo 			return (rv);
21891ae08745Sheppo 		}
21901ae08745Sheppo 
21911ae08745Sheppo 		rv = i_vcc_write_ldc(vport, &buf);
21921ae08745Sheppo 
21931ae08745Sheppo 		mutex_exit(&vport->lock);
21941ae08745Sheppo 
21951ae08745Sheppo 		if (rv) {
21961ae08745Sheppo 			break;
21971ae08745Sheppo 		}
21981ae08745Sheppo 
21991ae08745Sheppo 		size -= bytes;
22001ae08745Sheppo 
22011ae08745Sheppo 	}
22021ae08745Sheppo 
22031ae08745Sheppo 	i_vcc_set_port_status(vport, &vport->write_cv, VCC_PORT_USE_WRITE_LDC);
22041ae08745Sheppo 	return (rv);
22051ae08745Sheppo }
22061ae08745Sheppo 
22071ae08745Sheppo /* mdeg callback for a removed port */
22081ae08745Sheppo static int
i_vcc_md_remove_port(md_t * mdp,mde_cookie_t mdep,vcc_t * vccp)22091ae08745Sheppo i_vcc_md_remove_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
22101ae08745Sheppo {
22111ae08745Sheppo 	uint64_t  portno;	/* md requires 64bit for port number */
22121ae08745Sheppo 	int rv = MDEG_FAILURE;
22131ae08745Sheppo 	vcc_port_t *vport;
22141ae08745Sheppo 
22151ae08745Sheppo 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
22161ae08745Sheppo 		cmn_err(CE_CONT, "vcc_mdeg_cb: port has no 'id' property\n");
22171ae08745Sheppo 		return (MDEG_FAILURE);
22181ae08745Sheppo 	}
22191ae08745Sheppo 
22201ae08745Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno < 0)) {
22211ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld invalid port no\n",
222219397407SSherry Moore 		    portno);
22231ae08745Sheppo 		return (MDEG_FAILURE);
22241ae08745Sheppo 	}
22251ae08745Sheppo 
22261ae08745Sheppo 	if (portno == VCC_CONTROL_PORT) {
22271ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld can not remove"
222819397407SSherry Moore 		    "control port\n",
22291ae08745Sheppo 		    portno);
22301ae08745Sheppo 		return (MDEG_FAILURE);
22311ae08745Sheppo 	}
22321ae08745Sheppo 
22331ae08745Sheppo 	vport = &(vccp->port[portno]);
22341ae08745Sheppo 
22351ae08745Sheppo 	/* delete the port */
22361ae08745Sheppo 	mutex_enter(&vport->lock);
22371ae08745Sheppo 	rv = i_vcc_delete_port(vccp, vport);
22381ae08745Sheppo 	mutex_exit(&vport->lock);
22391ae08745Sheppo 
22401ae08745Sheppo 	mutex_enter(&vccp->lock);
22411ae08745Sheppo 	vccp->num_ports--;
22421ae08745Sheppo 	mutex_exit(&vccp->lock);
22431ae08745Sheppo 
22441ae08745Sheppo 	return (rv ? MDEG_FAILURE : MDEG_SUCCESS);
22451ae08745Sheppo }
22461ae08745Sheppo 
22471ae08745Sheppo static int
i_vcc_get_ldc_id(md_t * md,mde_cookie_t mdep,uint64_t * ldc_id)22481ae08745Sheppo i_vcc_get_ldc_id(md_t *md, mde_cookie_t mdep, uint64_t *ldc_id)
22491ae08745Sheppo {
22501ae08745Sheppo 	int		num_nodes;
22511ae08745Sheppo 	size_t		size;
22521ae08745Sheppo 	mde_cookie_t	*channel;
22531ae08745Sheppo 	int		num_channels;
22541ae08745Sheppo 
22551ae08745Sheppo 
22561ae08745Sheppo 	if ((num_nodes = md_node_count(md)) <= 0) {
22571ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc_channel_id:"
22581ae08745Sheppo 		    "  Invalid node count in Machine Description subtree");
22591ae08745Sheppo 		return (-1);
22601ae08745Sheppo 	}
22611ae08745Sheppo 	size = num_nodes*(sizeof (*channel));
22621ae08745Sheppo 	channel = kmem_zalloc(size, KM_SLEEP);
22631ae08745Sheppo 	ASSERT(channel != NULL);	/* because KM_SLEEP */
22641ae08745Sheppo 
22651ae08745Sheppo 
22661ae08745Sheppo 	/* Look for channel endpoint child(ren) of the vdisk MD node */
22671ae08745Sheppo 	if ((num_channels = md_scan_dag(md, mdep,
226819397407SSherry Moore 	    md_find_name(md, "channel-endpoint"),
226919397407SSherry Moore 	    md_find_name(md, "fwd"), channel)) <= 0) {
22701ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc_id:  No 'channel-endpoint'"
22711ae08745Sheppo 		    " found for vcc");
22721ae08745Sheppo 		kmem_free(channel, size);
22731ae08745Sheppo 		return (-1);
22741ae08745Sheppo 	}
22751ae08745Sheppo 
22761ae08745Sheppo 	/* Get the "id" value for the first channel endpoint node */
22771ae08745Sheppo 	if (md_get_prop_val(md, channel[0], "id", ldc_id) != 0) {
22781ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc:  No id property found "
22791ae08745Sheppo 		    "for channel-endpoint of vcc");
22801ae08745Sheppo 		kmem_free(channel, size);
22811ae08745Sheppo 		return (-1);
22821ae08745Sheppo 	}
22831ae08745Sheppo 
22841ae08745Sheppo 	if (num_channels > 1) {
22851ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc:  Warning:  Using ID of first"
22861ae08745Sheppo 		    " of multiple channels for this vcc");
22871ae08745Sheppo 	}
22881ae08745Sheppo 
22891ae08745Sheppo 	kmem_free(channel, size);
22901ae08745Sheppo 	return (0);
22911ae08745Sheppo }
22921ae08745Sheppo /* mdeg callback for an added port  */
22931ae08745Sheppo static int
i_vcc_md_add_port(md_t * mdp,mde_cookie_t mdep,vcc_t * vccp)22941ae08745Sheppo i_vcc_md_add_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
22951ae08745Sheppo {
22961ae08745Sheppo 	uint64_t	portno;		/* md requires 64 bit */
22971ae08745Sheppo 	char		*domain_name;
22981ae08745Sheppo 	char		*group_name;
22991ae08745Sheppo 	uint64_t	ldc_id;
23001ae08745Sheppo 	uint64_t	tcp_port;
23011ae08745Sheppo 	vcc_port_t	*vport;
23021ae08745Sheppo 
23031ae08745Sheppo 	/* read in the port's reg property */
23041ae08745Sheppo 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
23051ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port_: port has no 'id' "
230619397407SSherry Moore 		    "property\n");
23071ae08745Sheppo 		return (MDEG_FAILURE);
23081ae08745Sheppo 	}
23091ae08745Sheppo 
23101ae08745Sheppo 	/* read in the port's "vcc-doman-name" property */
23111ae08745Sheppo 	if (md_get_prop_str(mdp, mdep, "vcc-domain-name", &domain_name)) {
23121ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has "
231319397407SSherry Moore 		    "no 'vcc-domain-name' property\n", portno);
23141ae08745Sheppo 		return (MDEG_FAILURE);
23151ae08745Sheppo 	}
23161ae08745Sheppo 
23171ae08745Sheppo 
23181ae08745Sheppo 	/* read in the port's "vcc-group-name" property */
23191ae08745Sheppo 	if (md_get_prop_str(mdp, mdep, "vcc-group-name", &group_name)) {
23201ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no "
232119397407SSherry Moore 		    "'vcc-group-name'property\n", portno);
23221ae08745Sheppo 		return (MDEG_FAILURE);
23231ae08745Sheppo 	}
23241ae08745Sheppo 
23251ae08745Sheppo 
23261ae08745Sheppo 	/* read in the port's "vcc-tcp-port" property */
23271ae08745Sheppo 	if (md_get_prop_val(mdp, mdep, "vcc-tcp-port", &tcp_port)) {
23281ae08745Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no"
232919397407SSherry Moore 		    "'vcc-tcp-port' property\n", portno);
23301ae08745Sheppo 		return (MDEG_FAILURE);
23311ae08745Sheppo 	}
23321ae08745Sheppo 
23331ae08745Sheppo 	D1("i_vcc_md_add_port: port@%d domain-name=%s group-name=%s"
23341ae08745Sheppo 	    " tcp-port=%lld\n", portno, domain_name, group_name, tcp_port);
23351ae08745Sheppo 
23361ae08745Sheppo 	/* add the port */
23371ae08745Sheppo 	if (i_vcc_add_port(vccp, group_name, tcp_port, portno, domain_name)) {
23381ae08745Sheppo 		return (MDEG_FAILURE);
23391ae08745Sheppo 	}
23401ae08745Sheppo 
23411ae08745Sheppo 	vport = &vccp->port[portno];
23421ae08745Sheppo 	if (i_vcc_get_ldc_id(mdp, mdep, &ldc_id)) {
23431ae08745Sheppo 		mutex_enter(&vport->lock);
23441ae08745Sheppo 		(void) i_vcc_delete_port(vccp, vport);
23451ae08745Sheppo 		mutex_exit(&vport->lock);
23461ae08745Sheppo 		return (MDEG_FAILURE);
23471ae08745Sheppo 	}
23481ae08745Sheppo 
23491ae08745Sheppo 	/* configure the port */
23501ae08745Sheppo 	if (i_vcc_config_port(vccp, portno, ldc_id)) {
23511ae08745Sheppo 		mutex_enter(&vport->lock);
23521ae08745Sheppo 		(void) i_vcc_delete_port(vccp, vport);
23531ae08745Sheppo 		mutex_exit(&vport->lock);
23541ae08745Sheppo 		return (MDEG_FAILURE);
23551ae08745Sheppo 	}
23561ae08745Sheppo 
23571ae08745Sheppo 	mutex_enter(&vccp->lock);
23581ae08745Sheppo 	vccp->num_ports++;
23591ae08745Sheppo 	mutex_exit(&vccp->lock);
23601ae08745Sheppo 
23611ae08745Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
23621ae08745Sheppo 
23631ae08745Sheppo 	if (vport->pollflag & VCC_POLL_CONFIG) {
23641ae08745Sheppo 		/* wakeup vntsd */
23651ae08745Sheppo 		mutex_enter(&vport->lock);
23661ae08745Sheppo 		vport->pollevent |= VCC_POLL_ADD_PORT;
23671ae08745Sheppo 		mutex_exit(&vport->lock);
23681ae08745Sheppo 		pollwakeup(&vport->poll, POLLIN);
23691ae08745Sheppo 	}
23701ae08745Sheppo 
23711ae08745Sheppo 	return (MDEG_SUCCESS);
23721ae08745Sheppo }
23731ae08745Sheppo 
23741ae08745Sheppo /* mdeg callback */
23751ae08745Sheppo static int
vcc_mdeg_cb(void * cb_argp,mdeg_result_t * resp)23761ae08745Sheppo vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
23771ae08745Sheppo {
23781ae08745Sheppo 	int	idx;
23791ae08745Sheppo 	vcc_t 	*vccp;
23801ae08745Sheppo 	int	rv;
23811ae08745Sheppo 
23821ae08745Sheppo 	vccp = (vcc_t *)cb_argp;
23831ae08745Sheppo 	ASSERT(vccp);
23841ae08745Sheppo 
23851ae08745Sheppo 	if (resp == NULL) {
23861ae08745Sheppo 		return (MDEG_FAILURE);
23871ae08745Sheppo 	}
23881ae08745Sheppo 
23891ae08745Sheppo 	/* added port */
23901ae08745Sheppo 	D1("vcc_mdeg_cb: added %d port(s)\n", resp->added.nelem);
23911ae08745Sheppo 
23921ae08745Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
23931ae08745Sheppo 		rv = i_vcc_md_add_port(resp->added.mdp,
23941ae08745Sheppo 		    resp->added.mdep[idx], vccp);
23951ae08745Sheppo 
23961ae08745Sheppo 		if (rv !=  MDEG_SUCCESS) {
23971ae08745Sheppo 			return (rv);
23981ae08745Sheppo 		}
23991ae08745Sheppo 	}
24001ae08745Sheppo 
24011ae08745Sheppo 	/* removed port */
24021ae08745Sheppo 	D1("vcc_mdeg_cb: removed %d port(s)\n", resp->removed.nelem);
24031ae08745Sheppo 
24041ae08745Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
24051ae08745Sheppo 		rv = i_vcc_md_remove_port(resp->removed.mdp,
24061ae08745Sheppo 		    resp->removed.mdep[idx], vccp);
24071ae08745Sheppo 
24081ae08745Sheppo 		if (rv !=  MDEG_SUCCESS) {
24091ae08745Sheppo 			return (rv);
24101ae08745Sheppo 		}
24114d39be2bSsg 
24121ae08745Sheppo 	}
24131ae08745Sheppo 
24141ae08745Sheppo 	/*
24151ae08745Sheppo 	 * XXX - Currently no support for updating already active
24161ae08745Sheppo 	 * ports. So, ignore the match_curr and match_prev arrays
24171ae08745Sheppo 	 * for now.
24181ae08745Sheppo 	 */
24191ae08745Sheppo 
24201ae08745Sheppo 	return (MDEG_SUCCESS);
24211ae08745Sheppo }
24221ae08745Sheppo 
24231ae08745Sheppo 
24241ae08745Sheppo /* cb_chpoll */
24251ae08745Sheppo static int
vcc_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)24261ae08745Sheppo vcc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
24271ae08745Sheppo     struct pollhead **phpp)
24281ae08745Sheppo {
24291ae08745Sheppo 	int	    instance;
24301ae08745Sheppo 	minor_t	    minor;
24311ae08745Sheppo 	uint_t	    portno;
24321ae08745Sheppo 	vcc_t	    *vccp;
24331ae08745Sheppo 	vcc_port_t  *vport;
24341ae08745Sheppo 
24351ae08745Sheppo 	minor = getminor(dev);
24361ae08745Sheppo 
24371ae08745Sheppo 	instance = VCCINST(minor);
24381ae08745Sheppo 
24391ae08745Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
24401ae08745Sheppo 	if (vccp == NULL) {
24411ae08745Sheppo 		return (ENXIO);
24421ae08745Sheppo 	}
24431ae08745Sheppo 
24441ae08745Sheppo 	portno = VCCPORT(vccp, minor);
24451ae08745Sheppo 
24461ae08745Sheppo 	vport = &(vccp->port[portno]);
24471ae08745Sheppo 
24481ae08745Sheppo 	D1("vcc_chpoll: virtual-console-concentrator@%d events 0x%x\n",
24491ae08745Sheppo 	    portno, events);
24501ae08745Sheppo 
24511ae08745Sheppo 	*reventsp = 0;
24521ae08745Sheppo 
24531ae08745Sheppo 	if (portno != VCC_CONTROL_PORT) {
24541ae08745Sheppo 		return (ENXIO);
24551ae08745Sheppo 	}
24561ae08745Sheppo 
24571ae08745Sheppo 	/* poll for config change */
24581ae08745Sheppo 	if (vport->pollevent) {
24591ae08745Sheppo 		*reventsp |= (events & POLLIN);
24601ae08745Sheppo 	}
24611ae08745Sheppo 
2462a5eb7107SBryan Cantrill 	if ((((*reventsp) == 0) && (!anyyet)) || (events & POLLET)) {
24631ae08745Sheppo 		*phpp = &vport->poll;
24641ae08745Sheppo 		if (events & POLLIN) {
24651ae08745Sheppo 			mutex_enter(&vport->lock);
24661ae08745Sheppo 			vport->pollflag |= VCC_POLL_CONFIG;
24671ae08745Sheppo 			mutex_exit(&vport->lock);
24681ae08745Sheppo 		} else {
24691ae08745Sheppo 			return (ENXIO);
24701ae08745Sheppo 		}
24711ae08745Sheppo 	}
24721ae08745Sheppo 
24731ae08745Sheppo 	D1("vcc_chpoll: virtual-console-concentrator@%d:%d ev=0x%x, "
24741ae08745Sheppo 	    "rev=0x%x pev=0x%x, flag=0x%x\n",
24751ae08745Sheppo 	    instance, portno, events, (*reventsp),
24761ae08745Sheppo 	    vport->pollevent, vport->pollflag);
24771ae08745Sheppo 
24781ae08745Sheppo 
24791ae08745Sheppo 	return (0);
24801ae08745Sheppo }
2481