xref: /onnv-gate/usr/src/uts/sun4v/io/vcc.c (revision 3151:9a7f99c63ca3)
11991Sheppo /*
21991Sheppo  * CDDL HEADER START
31991Sheppo  *
41991Sheppo  * The contents of this file are subject to the terms of the
51991Sheppo  * Common Development and Distribution License (the "License").
61991Sheppo  * You may not use this file except in compliance with the License.
71991Sheppo  *
81991Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo  * or http://www.opensolaris.org/os/licensing.
101991Sheppo  * See the License for the specific language governing permissions
111991Sheppo  * and limitations under the License.
121991Sheppo  *
131991Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo  *
191991Sheppo  * CDDL HEADER END
201991Sheppo  */
211991Sheppo 
221991Sheppo /*
231991Sheppo  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241991Sheppo  * Use is subject to license terms.
251991Sheppo  */
261991Sheppo 
271991Sheppo #pragma ident	"%Z%%M%	%I%	%E% SMI"
281991Sheppo 
291991Sheppo #include <sys/types.h>
301991Sheppo #include <sys/file.h>
311991Sheppo #include <sys/errno.h>
321991Sheppo #include <sys/uio.h>
331991Sheppo #include <sys/open.h>
341991Sheppo #include <sys/cred.h>
351991Sheppo #include <sys/kmem.h>
361991Sheppo #include <sys/conf.h>
371991Sheppo #include <sys/cmn_err.h>
381991Sheppo #include <sys/ksynch.h>
391991Sheppo #include <sys/modctl.h>
401991Sheppo #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */
411991Sheppo #include <sys/debug.h>
421991Sheppo #include <sys/promif.h>
431991Sheppo #include <sys/ddi.h>
441991Sheppo #include <sys/sunddi.h>
451991Sheppo #include <sys/cyclic.h>
461991Sheppo #include <sys/termio.h>
471991Sheppo #include <sys/intr.h>
481991Sheppo #include <sys/ivintr.h>
491991Sheppo #include <sys/note.h>
501991Sheppo #include <sys/stat.h>
511991Sheppo #include <sys/fcntl.h>
521991Sheppo #include <sys/sysmacros.h>
531991Sheppo 
541991Sheppo #include <sys/ldc.h>
551991Sheppo #include <sys/mdeg.h>
561991Sheppo #include <sys/vcc_impl.h>
571991Sheppo 
581991Sheppo /*
591991Sheppo  * Function prototypes.
601991Sheppo  */
611991Sheppo 
621991Sheppo /* DDI entrypoints */
631991Sheppo static int	vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
641991Sheppo static int	vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
651991Sheppo static int	vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
661991Sheppo static int	vcc_close(dev_t dev, int flag, int otyp, cred_t *cred);
671991Sheppo static int	vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
681991Sheppo 			cred_t *credp, int *rvalp);
691991Sheppo static int	vcc_read(dev_t dev, struct uio *uiop, cred_t *credp);
701991Sheppo static int	vcc_write(dev_t dev, struct uio *uiop, cred_t *credp);
711991Sheppo static int	vcc_chpoll(dev_t dev, short events, int anyyet,
721991Sheppo 			short *reventsp, struct pollhead **phpp);
731991Sheppo static int	vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
741991Sheppo 			void *arg, void **resultp);
751991Sheppo 
761991Sheppo /* callback functions */
771991Sheppo static uint_t	vcc_ldc_cb(uint64_t event, caddr_t arg);
781991Sheppo static int	vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
791991Sheppo 
801991Sheppo /* Internal functions */
811991Sheppo static int	i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport);
821991Sheppo static int	i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
831991Sheppo 			uint_t portno, char *domain_name);
841991Sheppo static int	i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id);
851991Sheppo static int	i_vcc_reset_events(vcc_t *vccp);
861991Sheppo static int	i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports,
871991Sheppo 			caddr_t buf, int mode);
881991Sheppo static int	i_vcc_del_cons_ok(vcc_t *vccp, caddr_t buf, int mode);
891991Sheppo static int	i_vcc_close_port(vcc_port_t *vport);
901991Sheppo static int	i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf);
912748Slm66018 static int	i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz);
921991Sheppo 
931991Sheppo static void *vcc_ssp;
941991Sheppo 
951991Sheppo static struct cb_ops vcc_cb_ops = {
961991Sheppo 	vcc_open,	    /* open */
971991Sheppo 	vcc_close,	    /* close */
981991Sheppo 	nodev,		    /* strategy */
991991Sheppo 	nodev,		    /* print */
1001991Sheppo 	nodev,		    /* dump */
1011991Sheppo 	vcc_read,	    /* read */
1021991Sheppo 	vcc_write,	    /* write */
1031991Sheppo 	vcc_ioctl,	    /* ioctl */
1041991Sheppo 	nodev,		    /* devmap */
1051991Sheppo 	nodev,		    /* mmap */
1061991Sheppo 	ddi_segmap,	    /* segmap */
1071991Sheppo 	vcc_chpoll,	    /* chpoll */
1081991Sheppo 	ddi_prop_op,	    /* prop_op */
1091991Sheppo 	NULL,		    /* stream */
1101991Sheppo 	D_NEW | D_MP	    /* flags */
1111991Sheppo };
1121991Sheppo 
1131991Sheppo 
1141991Sheppo static struct dev_ops vcc_ops = {
1151991Sheppo 	DEVO_REV,		/* rev */
1161991Sheppo 	0,			/* ref count */
1171991Sheppo 	vcc_getinfo,		/* getinfo */
1181991Sheppo 	nulldev,		/* identify */
1191991Sheppo 	nulldev,		/* probe */
1201991Sheppo 	vcc_attach,		/* attach */
1211991Sheppo 	vcc_detach,		/* detach */
1221991Sheppo 	nodev,			/* reset */
1231991Sheppo 	&vcc_cb_ops,		/* cb_ops */
1241991Sheppo 	(struct bus_ops *)NULL	/* bus_ops */
1251991Sheppo };
1261991Sheppo 
1271991Sheppo extern struct mod_ops mod_driverops;
1281991Sheppo 
1291991Sheppo #define	    VCC_CHANNEL_ENDPOINT	"channel-endpoint"
1301991Sheppo #define	    VCC_ID_PROP		"id"
1311991Sheppo 
1321991Sheppo /*
1331991Sheppo  * This is the string displayed by modinfo(1m).
1341991Sheppo  */
1351991Sheppo static char vcc_ident[] = "sun4v Virtual Console Concentrator Driver v%I%";
1361991Sheppo 
1371991Sheppo static struct modldrv md = {
1381991Sheppo 	&mod_driverops, 	/* Type - it is a driver */
1391991Sheppo 	vcc_ident,		/* Name of the module */
1401991Sheppo 	&vcc_ops,		/* driver specfic opts */
1411991Sheppo };
1421991Sheppo 
1431991Sheppo static struct modlinkage ml = {
1441991Sheppo 	MODREV_1,
1451991Sheppo 	&md,
1461991Sheppo 	NULL
1471991Sheppo };
1481991Sheppo 
1491991Sheppo /*
1501991Sheppo  * Matching criteria passed to the MDEG to register interest
1511991Sheppo  * in changes to 'virtual-device-port' nodes identified by their
1521991Sheppo  * 'id' property.
1531991Sheppo  */
1541991Sheppo static md_prop_match_t vcc_port_prop_match[] = {
1551991Sheppo 	{ MDET_PROP_VAL,	    "id"   },
1561991Sheppo 	{ MDET_LIST_END,	    NULL    }
1571991Sheppo };
1581991Sheppo 
1591991Sheppo static mdeg_node_match_t vcc_port_match = {"virtual-device-port",
1601991Sheppo 					vcc_port_prop_match};
1611991Sheppo 
1621991Sheppo /*
1631991Sheppo  * Specification of an MD node passed to the MDEG to filter any
1641991Sheppo  * 'virtual-device-port' nodes that do not belong to the specified node.
1651991Sheppo  * This template is copied for each vldc instance and filled in with
1661991Sheppo  * the appropriate 'cfg-handle' value before being passed to the MDEG.
1671991Sheppo  */
1681991Sheppo static mdeg_prop_spec_t vcc_prop_template[] = {
1691991Sheppo 	{ MDET_PROP_STR,    "name",	"virtual-console-concentrator"	},
1701991Sheppo 	{ MDET_PROP_VAL,    "cfg-handle",	NULL	},
1711991Sheppo 	{ MDET_LIST_END,    NULL,		NULL	}
1721991Sheppo };
1731991Sheppo 
1741991Sheppo #define	VCC_SET_MDEG_PROP_INST(specp, val) (specp)[1].ps_val = (val);
1751991Sheppo 
1761991Sheppo 
1771991Sheppo #ifdef DEBUG
1781991Sheppo 
1791991Sheppo /*
1801991Sheppo  * Print debug messages
1811991Sheppo  *
1821991Sheppo  * set vldcdbg to 0xf to enable all messages
1831991Sheppo  *
1841991Sheppo  * 0x8 - Errors
1851991Sheppo  * 0x4 - Warnings
1861991Sheppo  * 0x2 - All debug messages (most verbose)
1871991Sheppo  * 0x1 - Minimal debug messages
1881991Sheppo  */
1891991Sheppo 
1901991Sheppo int vccdbg = 0x8;
1911991Sheppo 
1921991Sheppo static void
1931991Sheppo vccdebug(const char *fmt, ...)
1941991Sheppo {
1951991Sheppo 	char buf[512];
1961991Sheppo 	va_list ap;
1971991Sheppo 
1981991Sheppo 	va_start(ap, fmt);
1991991Sheppo 	(void) vsprintf(buf, fmt, ap);
2001991Sheppo 	va_end(ap);
2011991Sheppo 
2021991Sheppo 	cmn_err(CE_CONT, "%s\n", buf);
2031991Sheppo }
2041991Sheppo 
2051991Sheppo #define	D1		\
2061991Sheppo if (vccdbg & 0x01)	\
2071991Sheppo 	vccdebug
2081991Sheppo 
2091991Sheppo #define	D2		\
2101991Sheppo if (vccdbg & 0x02)	\
2111991Sheppo 	vccdebug
2121991Sheppo 
2131991Sheppo #define	DWARN		\
2141991Sheppo if (vccdbg & 0x04)	\
2151991Sheppo 	vccdebug
2161991Sheppo 
2171991Sheppo #else
2181991Sheppo 
2191991Sheppo #define	D1
2201991Sheppo #define	D2
2211991Sheppo #define	DWARN
2221991Sheppo 
2231991Sheppo #endif
2241991Sheppo 
2251991Sheppo /* _init(9E): initialize the loadable module */
2261991Sheppo int
2271991Sheppo _init(void)
2281991Sheppo {
2291991Sheppo 	int error;
2301991Sheppo 
2311991Sheppo 	/* init the soft state structure */
2321991Sheppo 	error = ddi_soft_state_init(&vcc_ssp, sizeof (vcc_t), 1);
2331991Sheppo 	if (error != 0) {
2341991Sheppo 		return (error);
2351991Sheppo 	}
2361991Sheppo 
2371991Sheppo 	/* Link the driver into the system */
2381991Sheppo 	error = mod_install(&ml);
2391991Sheppo 
2401991Sheppo 	return (error);
2411991Sheppo 
2421991Sheppo }
2431991Sheppo 
2441991Sheppo /* _info(9E): return information about the loadable module */
2451991Sheppo int
2461991Sheppo _info(struct modinfo *modinfop)
2471991Sheppo {
2481991Sheppo 	/* Report status of the dynamically loadable driver module */
2491991Sheppo 	return (mod_info(&ml, modinfop));
2501991Sheppo }
2511991Sheppo 
2521991Sheppo /* _fini(9E): prepare the module for unloading. */
2531991Sheppo int
2541991Sheppo _fini(void)
2551991Sheppo {
2561991Sheppo 	int error;
2571991Sheppo 
2581991Sheppo 	/* Unlink the driver module from the system */
2591991Sheppo 	if ((error = mod_remove(&ml)) == 0) {
2601991Sheppo 		/*
2611991Sheppo 		 * We have successfully "removed" the driver.
2621991Sheppo 		 * destroy soft state
2631991Sheppo 		 */
2641991Sheppo 		ddi_soft_state_fini(&vcc_ssp);
2651991Sheppo 	}
2661991Sheppo 
2671991Sheppo 	return (error);
2681991Sheppo }
2691991Sheppo 
2701991Sheppo /* getinfo(9E) */
2711991Sheppo static int
2721991Sheppo vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,  void *arg, void **resultp)
2731991Sheppo {
2741991Sheppo 	_NOTE(ARGUNUSED(dip))
2751991Sheppo 
2761991Sheppo 	int	instance = VCCINST(getminor((dev_t)arg));
2771991Sheppo 	vcc_t	*vccp = NULL;
2781991Sheppo 
2791991Sheppo 	switch (cmd) {
2801991Sheppo 
2811991Sheppo 	case DDI_INFO_DEVT2DEVINFO:
2821991Sheppo 		if ((vccp = ddi_get_soft_state(vcc_ssp, instance)) == NULL) {
2831991Sheppo 			*resultp = NULL;
2841991Sheppo 			return (DDI_FAILURE);
2851991Sheppo 		}
2861991Sheppo 		*resultp = vccp->dip;
2871991Sheppo 		return (DDI_SUCCESS);
2881991Sheppo 
2891991Sheppo 	case DDI_INFO_DEVT2INSTANCE:
2901991Sheppo 		*resultp = (void *)(uintptr_t)instance;
2911991Sheppo 		return (DDI_SUCCESS);
2921991Sheppo 
2931991Sheppo 	default:
2941991Sheppo 		*resultp = NULL;
2951991Sheppo 		return (DDI_FAILURE);
2961991Sheppo 	}
2971991Sheppo }
2981991Sheppo 
2991991Sheppo /*
3001991Sheppo  * There are two cases that need special blocking. One of them is to block
3011991Sheppo  * a minor node without a port and another is to block application other
3021991Sheppo  * than vntsd.
3031991Sheppo  *
3041991Sheppo  * A minor node can exist in the file system without associated with a port
3051991Sheppo  * because when a port is deleted, ddi_remove_minor does not unlink it.
3061991Sheppo  * Clients might try to open a minor node even after the corresponding port
3071991Sheppo  * node has been removed.  To identify and block these calls,
3081991Sheppo  * we need to validate the association between a port and its minor node.
3091991Sheppo  *
3101991Sheppo  * An application other than vntsd can access a console port as long
3111991Sheppo  * as vntsd is not using the port. A port opened by an application other
3121991Sheppo  * than vntsd will be closed when vntsd wants to use the port.
3131991Sheppo  * However, other application could use same file descriptor
3141991Sheppo  * access vcc cb_ops. So we need to identify and block caller other
3151991Sheppo  * than vntsd, when vntsd is using the port.
3161991Sheppo  */
3171991Sheppo static int
3181991Sheppo i_vcc_can_use_port(vcc_minor_t *minorp, vcc_port_t *vport)
3191991Sheppo {
3201991Sheppo 	if (vport->minorp != minorp) {
3211991Sheppo 		/* port config changed */
3221991Sheppo 		return (ENXIO);
3231991Sheppo 	}
3241991Sheppo 
3251991Sheppo 	if (vport->valid_pid == VCC_NO_PID_BLOCKING) {
3261991Sheppo 		/* no blocking needed */
3271991Sheppo 		return (0);
3281991Sheppo 	}
3291991Sheppo 
3301991Sheppo 	if (vport->valid_pid != ddi_get_pid()) {
3311991Sheppo 		return (EIO);
3321991Sheppo 	}
3331991Sheppo 
3341991Sheppo 	return (0);
3351991Sheppo }
3361991Sheppo 
3371991Sheppo 
3381991Sheppo /* Syncronization between thread using cv_wait */
3391991Sheppo static int
3401991Sheppo i_vcc_wait_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
3411991Sheppo {
3421991Sheppo 
3431991Sheppo 	int	    rv;
3441991Sheppo 
3451991Sheppo 	ASSERT(mutex_owned(&vport->lock));
3461991Sheppo 
3471991Sheppo 	for (; ; ) {
3481991Sheppo 
3491991Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
3501991Sheppo 			/* port has been deleted */
3511991Sheppo 			D1("i_vcc_wait_port_status: port%d deleted\n",
3521991Sheppo 			    vport->number);
3531991Sheppo 			return (EIO);
3541991Sheppo 		}
3551991Sheppo 
3561991Sheppo 		if ((vport->status & VCC_PORT_OPEN) == 0) {
3571991Sheppo 			D1("i_vcc_wait_port_status: port%d is closed \n",
3581991Sheppo 			    vport->number);
3591991Sheppo 			return (EIO);
3601991Sheppo 		}
3611991Sheppo 
3621991Sheppo 		if (vport->status & VCC_PORT_LDC_LINK_DOWN) {
3631991Sheppo 			return (EIO);
3641991Sheppo 		}
3651991Sheppo 
3661991Sheppo 		if ((vport->valid_pid != VCC_NO_PID_BLOCKING) &&
3671991Sheppo 		    (vport->valid_pid != ddi_get_pid())) {
3681991Sheppo 			return (EIO);
3691991Sheppo 		}
3701991Sheppo 
3711991Sheppo 		if ((vport->status & status) == status) {
3721991Sheppo 			return (0);
3731991Sheppo 		}
3741991Sheppo 
3751991Sheppo 		if (!ddi_can_receive_sig()) {
3761991Sheppo 			return (EIO);
3771991Sheppo 		}
3781991Sheppo 
3791991Sheppo 		rv = cv_wait_sig(cv, &vport->lock);
3801991Sheppo 		if (rv == 0) {
3811991Sheppo 			D1("i_vcc_wait_port_status: port%d get intr \n",
3821991Sheppo 			    vport->number);
3831991Sheppo 			/* got signal */
3841991Sheppo 			return (EINTR);
3851991Sheppo 		}
3861991Sheppo 	}
3871991Sheppo 
3881991Sheppo }
3891991Sheppo 
3901991Sheppo /* Syncronization between threads, signal state change */
3911991Sheppo static void
3921991Sheppo i_vcc_set_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
3931991Sheppo {
3941991Sheppo 
3951991Sheppo 	mutex_enter(&vport->lock);
3961991Sheppo 	vport->status |= status;
3971991Sheppo 	cv_broadcast(cv);
3981991Sheppo 	mutex_exit(&vport->lock);
3991991Sheppo }
4001991Sheppo 
4011991Sheppo /* initialize a ldc channel */
4021991Sheppo static int
4031991Sheppo i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport)
4041991Sheppo {
4051991Sheppo 	ldc_attr_t 	attr;
4061991Sheppo 	int		rv = EIO;
4071991Sheppo 
4081991Sheppo 	ASSERT(mutex_owned(&vport->lock));
4091991Sheppo 	ASSERT(vport->ldc_id != VCC_INVALID_CHANNEL);
4101991Sheppo 
4111991Sheppo 	/* initialize the channel */
4121991Sheppo 	attr.devclass = LDC_DEV_SERIAL;
4131991Sheppo 	attr.instance = ddi_get_instance(vccp->dip);
4142410Slm66018 	attr.mtu = VCC_MTU_SZ;
4151991Sheppo 	attr.mode = LDC_MODE_RAW;
4161991Sheppo 
4171991Sheppo 	if ((rv = ldc_init(vport->ldc_id, &attr, &(vport->ldc_handle))) != 0) {
418*3151Ssg70180 		cmn_err(CE_CONT, "i_vcc_ldc_init: port %d ldc channel %ld"
419*3151Ssg70180 		    " failed ldc_init %d \n", vport->number, vport->ldc_id, rv);
4201991Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4211991Sheppo 		return (rv);
4221991Sheppo 	}
4231991Sheppo 
4241991Sheppo 	/* register it */
4251991Sheppo 	if ((rv = ldc_reg_callback(vport->ldc_handle, vcc_ldc_cb,
4261991Sheppo 		(caddr_t)vport)) != 0) {
4271991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_register_cb"
4281991Sheppo 			"failed\n", vport->number);
4291991Sheppo 		(void) ldc_fini(vport->ldc_handle);
4301991Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4311991Sheppo 		return (rv);
4321991Sheppo 	}
4331991Sheppo 
4341991Sheppo 	/* open and bring channel up */
4351991Sheppo 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
4361991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d inv channel 0x%lx\n",
4371991Sheppo 		    vport->number, vport->ldc_id);
4381991Sheppo 		(void) ldc_unreg_callback(vport->ldc_handle);
4391991Sheppo 		(void) ldc_fini(vport->ldc_handle);
4401991Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4411991Sheppo 		return (rv);
4421991Sheppo 	}
4431991Sheppo 
4441991Sheppo 	/* init the channel status */
4451991Sheppo 	if ((rv = ldc_status(vport->ldc_handle, &vport->ldc_status)) != 0) {
4461991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_status failed\n",
4471991Sheppo 		    vport->number);
4481991Sheppo 		(void) ldc_close(vport->ldc_handle);
4491991Sheppo 		(void) ldc_unreg_callback(vport->ldc_handle);
4501991Sheppo 		(void) ldc_fini(vport->ldc_handle);
4511991Sheppo 		vport->ldc_id = VCC_INVALID_CHANNEL;
4521991Sheppo 		return (rv);
4531991Sheppo 	}
4541991Sheppo 
4551991Sheppo 	return (0);
4561991Sheppo }
4571991Sheppo 
4581991Sheppo /*  release a ldc channel */
4591991Sheppo static int
4601991Sheppo i_vcc_ldc_fini(vcc_port_t *vport)
4611991Sheppo {
4621991Sheppo 	int 		rv = EIO;
4631991Sheppo 	vcc_msg_t	buf;
4642748Slm66018 	size_t		sz;
4651991Sheppo 
4661991Sheppo 	D1("i_vcc_ldc_fini: port@%lld, ldc_id%%llx\n", vport->number,
4671991Sheppo 	    vport->ldc_id);
4681991Sheppo 
4691991Sheppo 	ASSERT(mutex_owned(&vport->lock));
4701991Sheppo 
4711991Sheppo 	/* wait for write available */
4721991Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
4731991Sheppo 	    VCC_PORT_USE_WRITE_LDC);
4741991Sheppo 	if (rv) {
4751991Sheppo 		return (rv);
4761991Sheppo 	}
4771991Sheppo 	vport->status &= ~VCC_PORT_USE_WRITE_LDC;
4781991Sheppo 	/* send a HUP message */
4791991Sheppo 	buf.type = LDC_CONSOLE_CTRL;
4801991Sheppo 	buf.ctrl_msg = LDC_CONSOLE_HUP;
4811991Sheppo 	buf.size = 0;
4821991Sheppo 
4831991Sheppo 	/* in case of error, we still want to clean up ldc channel */
4841991Sheppo 	(void) i_vcc_write_ldc(vport, &buf);
4851991Sheppo 
4861991Sheppo 	mutex_exit(&vport->lock);
4871991Sheppo 	i_vcc_set_port_status(vport, &vport->write_cv, VCC_PORT_USE_WRITE_LDC);
4881991Sheppo 	mutex_enter(&vport->lock);
4891991Sheppo 
4902748Slm66018 	/* flush ldc channel */
4912748Slm66018 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
4922748Slm66018 	    VCC_PORT_USE_READ_LDC);
4932748Slm66018 	if (rv) {
4942748Slm66018 		return (rv);
4952748Slm66018 	}
4962748Slm66018 
4972748Slm66018 	vport->status &= ~VCC_PORT_USE_READ_LDC;
4982748Slm66018 	do {
4992748Slm66018 		sz = sizeof (buf);
5002748Slm66018 		rv = i_vcc_read_ldc(vport, (char *)&buf, &sz);
5012748Slm66018 	} while (rv == 0 && sz > 0);
5022748Slm66018 
5032748Slm66018 	vport->status |= VCC_PORT_USE_READ_LDC;
5042748Slm66018 
5051991Sheppo 	(void) ldc_set_cb_mode(vport->ldc_handle, LDC_CB_DISABLE);
5061991Sheppo 	if ((rv = ldc_close(vport->ldc_handle)) != 0) {
5071991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_fini: cannot close channel %ld\n",
5081991Sheppo 		    vport->ldc_id);
5091991Sheppo 		return (rv);
5101991Sheppo 	}
5111991Sheppo 
5121991Sheppo 	if ((rv = ldc_unreg_callback(vport->ldc_handle)) != 0) {
5131991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_fini: port@%d ldc_unreg_callback"
5141991Sheppo 			"failed\n", vport->number);
5151991Sheppo 		return (rv);
5161991Sheppo 	}
5171991Sheppo 
5181991Sheppo 	if ((rv = ldc_fini(vport->ldc_handle)) != 0) {
5191991Sheppo 		cmn_err(CE_CONT, "i_vcc_ldc_fini: cannot finilize channel"
5201991Sheppo 		    "%ld\n", vport->ldc_id);
5211991Sheppo 		return (rv);
5221991Sheppo 	}
5231991Sheppo 
5241991Sheppo 	return (0);
5251991Sheppo }
5261991Sheppo 
5271991Sheppo /* read data from ldc channel */
5281991Sheppo 
5291991Sheppo static int
5301991Sheppo i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz)
5311991Sheppo {
5321991Sheppo 
5331991Sheppo 	int		rv;
5341991Sheppo 	size_t		size;
5351991Sheppo 	size_t		space_left = *sz;
5361991Sheppo 	vcc_msg_t  	buf;
5371991Sheppo 	int 		i;
5381991Sheppo 
5391991Sheppo 
5401991Sheppo 
5411991Sheppo 
5421991Sheppo 	/* make sure holding read lock */
5431991Sheppo 	ASSERT((vport->status & VCC_PORT_USE_READ_LDC) == 0);
5441991Sheppo 	ASSERT(space_left >= VCC_MTU_SZ);
5451991Sheppo 
5461991Sheppo 	*sz = 0;
5471991Sheppo 	while (space_left >= VCC_MTU_SZ)  {
5481991Sheppo 		size = sizeof (buf);
5491991Sheppo 
5501991Sheppo 		rv = ldc_read(vport->ldc_handle, (caddr_t)&buf, &size);
5511991Sheppo 
5521991Sheppo 		if (rv) {
5531991Sheppo 			return (rv);
5541991Sheppo 		}
5551991Sheppo 
5561991Sheppo 
5571991Sheppo 		/*
5581991Sheppo 		 * FIXME: ldc_read should not reaturn 0 with
5591991Sheppo 		 * either size == 0, buf.size == 0 or size < VCC_HDR_SZ
5601991Sheppo 		 */
5611991Sheppo 		if (size == 0) {
5621991Sheppo 			if (*sz > 0) {
5631991Sheppo 				return (0);
5641991Sheppo 			}
5651991Sheppo 			return (EAGAIN);
5661991Sheppo 		}
5671991Sheppo 
5681991Sheppo 		if (size < VCC_HDR_SZ) {
5691991Sheppo 			return (EIO);
5701991Sheppo 		}
5711991Sheppo 
5721991Sheppo 		/*
5731991Sheppo 		 * only data is expected from console - otherwise
5741991Sheppo 		 * return error
5751991Sheppo 		 */
5761991Sheppo 		if (buf.type != LDC_CONSOLE_DATA) {
5771991Sheppo 			return (EIO);
5781991Sheppo 		}
5791991Sheppo 
5801991Sheppo 		if (buf.size == 0) {
5811991Sheppo 			if (*sz > 0) {
5821991Sheppo 				return (0);
5831991Sheppo 			}
5841991Sheppo 			return (EAGAIN);
5851991Sheppo 		}
5861991Sheppo 
5871991Sheppo 		/* copy  data */
5881991Sheppo 		for (i = 0; i < buf.size; i++, (*sz)++) {
5891991Sheppo 			data_buf[*sz] = buf.data[i];
5901991Sheppo 		}
5911991Sheppo 
5921991Sheppo 		space_left -= buf.size;
5931991Sheppo 	}
5941991Sheppo 
5951991Sheppo 	return (0);
5961991Sheppo }
5971991Sheppo 
5981991Sheppo /* callback from ldc */
5991991Sheppo static uint_t
6001991Sheppo vcc_ldc_cb(uint64_t event, caddr_t arg)
6011991Sheppo {
6021991Sheppo 
6031991Sheppo 	vcc_port_t  *vport = (vcc_port_t *)arg;
6042410Slm66018 	boolean_t   hasdata;
6051991Sheppo 
6061991Sheppo 	/*
6071991Sheppo 	 * do not need to hold lock because if ldc calls back, the
6081991Sheppo 	 * ldc_handle must be valid.
6091991Sheppo 	 */
6101991Sheppo 	D2("vcc_ldc_cb: callback invoked port=%d events=%llx\n",
6111991Sheppo 	    vport->number, event);
6121991Sheppo 
6131991Sheppo 	/* check event from ldc */
6141991Sheppo 	if (event & LDC_EVT_WRITE) {
6151991Sheppo 		/* channel has space for write */
6161991Sheppo 
6171991Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
6181991Sheppo 			VCC_PORT_LDC_WRITE_READY);
6191991Sheppo 		return (LDC_SUCCESS);
6201991Sheppo 	}
6211991Sheppo 
6221991Sheppo 	if (event & LDC_EVT_READ) {
6231991Sheppo 
6241991Sheppo 		/* channel has data for read */
6252410Slm66018 		(void) ldc_chkq(vport->ldc_handle, &hasdata);
6262410Slm66018 		if (!hasdata) {
6271991Sheppo 			/* data already read */
6281991Sheppo 			return (LDC_SUCCESS);
6291991Sheppo 		}
6301991Sheppo 
6311991Sheppo 		i_vcc_set_port_status(vport, &vport->read_cv,
6321991Sheppo 			VCC_PORT_LDC_DATA_READY);
6331991Sheppo 		return (LDC_SUCCESS);
6341991Sheppo 	}
6351991Sheppo 
6361991Sheppo 	if (event & LDC_EVT_DOWN) {
6371991Sheppo 		/* channel is down */
6381991Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
6391991Sheppo 					VCC_PORT_LDC_LINK_DOWN);
6401991Sheppo 		cv_broadcast(&vport->read_cv);
6411991Sheppo 
6421991Sheppo 	}
6431991Sheppo 
6441991Sheppo 	return (LDC_SUCCESS);
6451991Sheppo 
6461991Sheppo }
6471991Sheppo 
6481991Sheppo 
6491991Sheppo /* configure a vcc port with ldc channel */
6501991Sheppo static int
6511991Sheppo i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id)
6521991Sheppo {
6531991Sheppo 	int 		rv = EIO;
6541991Sheppo 	vcc_port_t 	*vport;
6551991Sheppo 
6561991Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
6571991Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: invalid port number %d\n",
6581991Sheppo 		    portno);
6591991Sheppo 		return (EINVAL);
6601991Sheppo 	}
6611991Sheppo 
6621991Sheppo 	vport = &(vccp->port[portno]);
6631991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
6641991Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d does not exist\n",
6651991Sheppo 		    portno);
6661991Sheppo 		return (EINVAL);
6671991Sheppo 	}
6681991Sheppo 
6691991Sheppo 
6701991Sheppo 	if (vport->ldc_id != VCC_INVALID_CHANNEL) {
6711991Sheppo 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d channel already"
6721991Sheppo 		    "configured\n", portno);
6731991Sheppo 		return (EINVAL);
6741991Sheppo 	}
6751991Sheppo 
6761991Sheppo 	mutex_enter(&vport->lock);
6771991Sheppo 
6781991Sheppo 	/* store the ldc ID */
6791991Sheppo 	vport->ldc_id = ldc_id;
6801991Sheppo 	/* check if someone has already opened this port */
6811991Sheppo 	if (vport->status & VCC_PORT_OPEN) {
6821991Sheppo 
6831991Sheppo 		if ((rv = i_vcc_ldc_init(vccp, vport)) != 0) {
6841991Sheppo 			mutex_exit(&vport->lock);
6851991Sheppo 			return (rv);
6861991Sheppo 		}
6871991Sheppo 
6881991Sheppo 		/* mark port as ready */
6891991Sheppo 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
6901991Sheppo 		cv_broadcast(&vport->read_cv);
6911991Sheppo 		cv_broadcast(&vport->write_cv);
6921991Sheppo 	}
6931991Sheppo 
6941991Sheppo 	mutex_exit(&vport->lock);
6951991Sheppo 
6961991Sheppo 	D1("i_vcc_config_port: port@%d ldc=%d, domain=%s",
6971991Sheppo 	    vport->number, vport->ldc_id, vport->minorp->domain_name);
6981991Sheppo 
6991991Sheppo 	return (0);
7001991Sheppo }
7011991Sheppo 
7021991Sheppo /* add a vcc console port */
7031991Sheppo static int
7041991Sheppo i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
7051991Sheppo     uint_t portno, char *domain_name)
7061991Sheppo {
7071991Sheppo 	int 		instance;
7081991Sheppo 	int		rv = MDEG_FAILURE;
7091991Sheppo 	minor_t 	minor;
7101991Sheppo 	vcc_port_t 	*vport;
7111991Sheppo 	uint_t		minor_idx;
7121991Sheppo 	char		name[MAXPATHLEN];
7131991Sheppo 
7141991Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
7151991Sheppo 		DWARN("i_vcc_add_port: invalid port number %d\n", portno);
7161991Sheppo 		return (MDEG_FAILURE);
7171991Sheppo 	}
7181991Sheppo 
7191991Sheppo 	vport = &(vccp->port[portno]);
7201991Sheppo 	if (vport->status & VCC_PORT_AVAIL) {
7211991Sheppo 		/* this port already exists */
7221991Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid port - port@%d "
7231991Sheppo 			"exists\n", portno);
7241991Sheppo 		return (MDEG_FAILURE);
7251991Sheppo 	}
7261991Sheppo 
7271991Sheppo 	vport->number = portno;
7281991Sheppo 	vport->ldc_id = VCC_INVALID_CHANNEL;
7291991Sheppo 
7301991Sheppo 	if (domain_name == NULL) {
7311991Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid domain name\n");
7321991Sheppo 		return (MDEG_FAILURE);
7331991Sheppo 	}
7341991Sheppo 
7351991Sheppo 	if (group_name == NULL) {
7361991Sheppo 		cmn_err(CE_CONT, "i_vcc_add_port: invalid group name\n");
7371991Sheppo 		return (MDEG_FAILURE);
7381991Sheppo 	}
7391991Sheppo 
7401991Sheppo 	/* look up minor number */
7411991Sheppo 	for (minor_idx = 0; minor_idx < vccp->minors_assigned; minor_idx++) {
7421991Sheppo 		if (strcmp(vccp->minor_tbl[minor_idx].domain_name,
7431991Sheppo 			    domain_name) == 0) {
7441991Sheppo 			/* found previous assigned minor number */
7451991Sheppo 			break;
7461991Sheppo 		}
7471991Sheppo 	}
7481991Sheppo 
7491991Sheppo 	if (minor_idx == vccp->minors_assigned) {
7501991Sheppo 		/* end of lookup - assign new minor number */
7511991Sheppo 		if (minor_idx == VCC_MAX_PORTS) {
7521991Sheppo 			cmn_err(CE_CONT, "i_vcc_add_port:"
7531991Sheppo 			    "too many minornodes (%d)\n",
7541991Sheppo 			    minor_idx);
7551991Sheppo 			return (MDEG_FAILURE);
7561991Sheppo 		}
7571991Sheppo 
7581991Sheppo 		(void) strlcpy(vccp->minor_tbl[minor_idx].domain_name,
7591991Sheppo 		    domain_name, MAXPATHLEN);
7601991Sheppo 
7611991Sheppo 		vccp->minors_assigned++;
7621991Sheppo 	}
7631991Sheppo 
7641991Sheppo 	vport->minorp = &vccp->minor_tbl[minor_idx];
7651991Sheppo 	vccp->minor_tbl[minor_idx].portno = portno;
7661991Sheppo 
7671991Sheppo 	(void) strlcpy(vport->group_name, group_name, MAXPATHLEN);
7681991Sheppo 
7691991Sheppo 	vport->tcp_port = tcp_port;
7701991Sheppo 	D1("i_vcc_add_port:@%d domain=%s, group=%s, tcp=%lld",
7711991Sheppo 	    vport->number, vport->minorp->domain_name,
7721991Sheppo 	    vport->group_name, vport->tcp_port);
7731991Sheppo 
7741991Sheppo 
7751991Sheppo 	/*
7761991Sheppo 	 * Create a minor node. The minor number is
7771991Sheppo 	 * (instance << VCC_INST_SHIFT) | minor_idx
7781991Sheppo 	 */
7791991Sheppo 	instance = ddi_get_instance(vccp->dip);
7801991Sheppo 
7811991Sheppo 	minor = (instance << VCC_INST_SHIFT) | (minor_idx);
7821991Sheppo 
7831991Sheppo 	(void) snprintf(name, MAXPATHLEN - 1, "%s%s", VCC_MINOR_NAME_PREFIX,
7841991Sheppo 	    domain_name);
7851991Sheppo 
7861991Sheppo 	rv = ddi_create_minor_node(vccp->dip, name, S_IFCHR, minor,
7871991Sheppo 	    DDI_NT_SERIAL, 0);
7881991Sheppo 
7891991Sheppo 	if (rv != DDI_SUCCESS) {
7901991Sheppo 		vccp->minors_assigned--;
7911991Sheppo 		return (MDEG_FAILURE);
7921991Sheppo 	}
7931991Sheppo 
7941991Sheppo 	mutex_enter(&vport->lock);
7951991Sheppo 	vport->status = VCC_PORT_AVAIL | VCC_PORT_ADDED;
7961991Sheppo 	mutex_exit(&vport->lock);
7971991Sheppo 
7981991Sheppo 
7991991Sheppo 	return (MDEG_SUCCESS);
8001991Sheppo }
8011991Sheppo 
8021991Sheppo /* delete a port */
8031991Sheppo static int
8041991Sheppo i_vcc_delete_port(vcc_t *vccp, vcc_port_t *vport)
8051991Sheppo {
8061991Sheppo 
8071991Sheppo 	char	name[MAXPATHLEN];
8081991Sheppo 	int	rv;
8091991Sheppo 
8101991Sheppo 
8111991Sheppo 	ASSERT(mutex_owned(&vport->lock));
8121991Sheppo 
8131991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
8141991Sheppo 		D1("vcc_del_port port already deleted \n");
8151991Sheppo 		return (0);
8161991Sheppo 	}
8171991Sheppo 
8181991Sheppo 	if (vport->status & VCC_PORT_OPEN) {
8191991Sheppo 		/* do not block mdeg callback */
8201991Sheppo 		vport->valid_pid = VCC_NO_PID_BLOCKING;
8211991Sheppo 		rv = i_vcc_close_port(vport);
8221991Sheppo 	}
8231991Sheppo 
8241991Sheppo 	/* remove minor node */
8251991Sheppo 	(void) snprintf(name, MAXPATHLEN-1, "%s%s", VCC_MINOR_NAME_PREFIX,
8261991Sheppo 	    vport->minorp->domain_name);
8271991Sheppo 
8281991Sheppo 	ddi_remove_minor_node(vccp->dip, name);
8291991Sheppo 
8301991Sheppo 	/* let read and write thread know */
8311991Sheppo 	cv_broadcast(&vport->read_cv);
8321991Sheppo 	cv_broadcast(&vport->write_cv);
8331991Sheppo 	vport->status = 0;
8341991Sheppo 	return (rv);
8351991Sheppo 
8361991Sheppo 
8371991Sheppo }
8381991Sheppo 
8391991Sheppo /* register callback to MDEG */
8401991Sheppo static int
8411991Sheppo i_vcc_mdeg_register(vcc_t *vccp, int instance)
8421991Sheppo {
8431991Sheppo 	mdeg_prop_spec_t	*pspecp;
8441991Sheppo 	mdeg_node_spec_t	*ispecp;
8451991Sheppo 	mdeg_handle_t		mdeg_hdl;
8461991Sheppo 	int			sz;
8471991Sheppo 	int			rv;
8481991Sheppo 
8491991Sheppo 	/*
8501991Sheppo 	 * Allocate and initialize a per-instance copy
8511991Sheppo 	 * of the global property spec array that will
8521991Sheppo 	 * uniquely identify this vcc instance.
8531991Sheppo 	 */
8541991Sheppo 	sz = sizeof (vcc_prop_template);
8551991Sheppo 	pspecp = kmem_alloc(sz, KM_SLEEP);
8561991Sheppo 
8571991Sheppo 	bcopy(vcc_prop_template, pspecp, sz);
8581991Sheppo 
8591991Sheppo 	VCC_SET_MDEG_PROP_INST(pspecp, instance);
8601991Sheppo 
8611991Sheppo 	/* initialize the complete prop spec structure */
8621991Sheppo 	ispecp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
8631991Sheppo 	ispecp->namep = "virtual-device";
8641991Sheppo 	ispecp->specp = pspecp;
8651991Sheppo 
8661991Sheppo 	/* perform the registration */
8671991Sheppo 	rv = mdeg_register(ispecp, &vcc_port_match, vcc_mdeg_cb,
8681991Sheppo 	    vccp, &mdeg_hdl);
8691991Sheppo 
8701991Sheppo 	if (rv != MDEG_SUCCESS) {
8711991Sheppo 		cmn_err(CE_CONT, "i_vcc_mdeg_register:"
8721991Sheppo 		    "mdeg_register failed (%d)\n", rv);
8731991Sheppo 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
8741991Sheppo 		kmem_free(pspecp, sz);
8751991Sheppo 		return (DDI_FAILURE);
8761991Sheppo 	}
8771991Sheppo 
8781991Sheppo 	/* save off data that will be needed later */
8791991Sheppo 	vccp->md_ispecp = (void *)ispecp;
8801991Sheppo 	vccp->mdeg_hdl = mdeg_hdl;
8811991Sheppo 
8821991Sheppo 	return (0);
8831991Sheppo }
8841991Sheppo 
8851991Sheppo /* destroy all mutex from port table */
8861991Sheppo static void
8871991Sheppo i_vcc_cleanup_port_table(vcc_t *vccp)
8881991Sheppo {
8891991Sheppo 	int i;
8901991Sheppo 	vcc_port_t *vport;
8911991Sheppo 
8921991Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
8931991Sheppo 		vport = &(vccp->port[i]);
8941991Sheppo 		mutex_destroy(&vport->lock);
8951991Sheppo 		cv_destroy(&vport->read_cv);
8961991Sheppo 		cv_destroy(&vport->write_cv);
8971991Sheppo 	}
8981991Sheppo }
8991991Sheppo 
9001991Sheppo /*
9011991Sheppo  * attach(9E): attach a device to the system.
9021991Sheppo  * called once for each instance of the device on the system.
9031991Sheppo  */
9041991Sheppo static int
9051991Sheppo vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
9061991Sheppo {
9071991Sheppo 	int 		i, instance, inst;
9081991Sheppo 	int 		rv = DDI_FAILURE;
9091991Sheppo 	vcc_t		*vccp;
9101991Sheppo 	minor_t 	minor;
9111991Sheppo 	vcc_port_t	*vport;
9121991Sheppo 
9131991Sheppo 	switch (cmd) {
9141991Sheppo 
9151991Sheppo 	case DDI_ATTACH:
9161991Sheppo 
9171991Sheppo 		instance = ddi_get_instance(dip);
9181991Sheppo 		if (ddi_soft_state_zalloc(vcc_ssp, instance) != DDI_SUCCESS)
9191991Sheppo 			return (DDI_FAILURE);
9201991Sheppo 
9211991Sheppo 		vccp = ddi_get_soft_state(vcc_ssp, instance);
9221991Sheppo 		if (vccp == NULL) {
9231991Sheppo 			ddi_soft_state_free(vccp, instance);
9241991Sheppo 			return (ENXIO);
9251991Sheppo 		}
9261991Sheppo 
9271991Sheppo 		D1("vcc_attach: DDI_ATTACH instance=%d\n", instance);
9281991Sheppo 
9291991Sheppo 		/* initialize the mutex */
9301991Sheppo 		mutex_init(&vccp->lock, NULL, MUTEX_DRIVER, NULL);
9311991Sheppo 
9321991Sheppo 		mutex_enter(&vccp->lock);
9331991Sheppo 
9341991Sheppo 		vccp->dip = dip;
9351991Sheppo 
9361991Sheppo 		for (i = 0; i < VCC_MAX_PORTS; i++) {
9371991Sheppo 			vport = &(vccp->port[i]);
9381991Sheppo 			mutex_init(&vport->lock, NULL, MUTEX_DRIVER, NULL);
9391991Sheppo 			cv_init(&vport->read_cv, NULL, CV_DRIVER, NULL);
9401991Sheppo 			cv_init(&vport->write_cv, NULL, CV_DRIVER, NULL);
9411991Sheppo 			vport->valid_pid = VCC_NO_PID_BLOCKING;
9421991Sheppo 		}
9431991Sheppo 
9441991Sheppo 		vport = &vccp->port[VCC_CONTROL_PORT];
9451991Sheppo 		mutex_enter(&vport->lock);
9461991Sheppo 
9471991Sheppo 		vport->minorp = &vccp->minor_tbl[VCC_CONTROL_MINOR_IDX];
9481991Sheppo 		vport->status |= VCC_PORT_AVAIL;
9491991Sheppo 
9501991Sheppo 		/* create a minor node for vcc control */
9511991Sheppo 		minor = (instance << VCC_INST_SHIFT) | VCC_CONTROL_MINOR_IDX;
9521991Sheppo 
9531991Sheppo 		vccp->minor_tbl[VCC_CONTROL_PORT].portno =
9541991Sheppo 		    VCC_CONTROL_MINOR_IDX;
9551991Sheppo 
9561991Sheppo 
9571991Sheppo 		rv = ddi_create_minor_node(vccp->dip, "ctl", S_IFCHR, minor,
9581991Sheppo 		    DDI_NT_SERIAL, 0);
9591991Sheppo 
9601991Sheppo 		mutex_exit(&vport->lock);
9611991Sheppo 
9621991Sheppo 		if (rv != DDI_SUCCESS) {
9631991Sheppo 			cmn_err(CE_CONT, "vcc_attach: error"
9641991Sheppo 			    "creating control minor node\n");
9651991Sheppo 
9661991Sheppo 			i_vcc_cleanup_port_table(vccp);
9671991Sheppo 
9681991Sheppo 			mutex_exit(&vccp->lock);
9691991Sheppo 			/* clean up soft state */
9701991Sheppo 			ddi_soft_state_free(vccp, instance);
9711991Sheppo 
9721991Sheppo 			return (DDI_FAILURE);
9731991Sheppo 		}
9741991Sheppo 
9751991Sheppo 		/* get the instance number by reading 'reg' property */
9761991Sheppo 		inst = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
9771991Sheppo 		    "reg", -1);
9781991Sheppo 		if (inst == -1) {
9791991Sheppo 			cmn_err(CE_CONT, "vcc_attach: vcc%d has no "
9801991Sheppo 				"'reg' property\n",
9811991Sheppo 			    ddi_get_instance(dip));
9821991Sheppo 
9831991Sheppo 			i_vcc_cleanup_port_table(vccp);
9841991Sheppo 
9851991Sheppo 			/* remove minor */
9861991Sheppo 			ddi_remove_minor_node(vccp->dip, NULL);
9871991Sheppo 
9881991Sheppo 			/* clean up soft state */
9891991Sheppo 			mutex_exit(&vccp->lock);
9901991Sheppo 			ddi_soft_state_free(vccp, instance);
9911991Sheppo 
9921991Sheppo 			return (DDI_FAILURE);
9931991Sheppo 		}
9941991Sheppo 
9951991Sheppo 		/*
9961991Sheppo 		 * Mdeg might invoke callback in the same call sequence
9971991Sheppo 		 * if there is a domain port at the time of registration.
9981991Sheppo 		 * Since the callback also grabs vcc->lock mutex, to avoid
9991991Sheppo 		 * mutex reentry error, release the lock before registration
10001991Sheppo 		 */
10011991Sheppo 		mutex_exit(&vccp->lock);
10021991Sheppo 
10031991Sheppo 		/* register for notifications from Zeus */
10041991Sheppo 		rv = i_vcc_mdeg_register(vccp, inst);
10051991Sheppo 		if (rv != MDEG_SUCCESS) {
10061991Sheppo 			cmn_err(CE_CONT, "vcc_attach: error register to MD\n");
10071991Sheppo 
10081991Sheppo 			i_vcc_cleanup_port_table(vccp);
10091991Sheppo 
10101991Sheppo 			/* remove minor */
10111991Sheppo 			ddi_remove_minor_node(vccp->dip, NULL);
10121991Sheppo 
10131991Sheppo 			/* clean up soft state */
10141991Sheppo 			ddi_soft_state_free(vccp, instance);
10151991Sheppo 
10161991Sheppo 			return (DDI_FAILURE);
10171991Sheppo 		}
10181991Sheppo 
10191991Sheppo 		return (DDI_SUCCESS);
10201991Sheppo 
10211991Sheppo 	case DDI_RESUME:
10221991Sheppo 
10231991Sheppo 		return (DDI_SUCCESS);
10241991Sheppo 
10251991Sheppo 	default:
10261991Sheppo 
10271991Sheppo 		return (DDI_FAILURE);
10281991Sheppo 	}
10291991Sheppo }
10301991Sheppo 
10311991Sheppo /*
10321991Sheppo  * detach(9E): detach a device from the system.
10331991Sheppo  */
10341991Sheppo static int
10351991Sheppo vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
10361991Sheppo {
10371991Sheppo 	int		    i, instance;
10381991Sheppo 	vcc_t		    *vccp;
10391991Sheppo 	mdeg_node_spec_t    *ispecp;
10401991Sheppo 	vcc_port_t	    *vport;
10411991Sheppo 
10421991Sheppo 	switch (cmd) {
10431991Sheppo 
10441991Sheppo 	case DDI_DETACH:
10451991Sheppo 
10461991Sheppo 		instance = ddi_get_instance(dip);
10471991Sheppo 		vccp = ddi_get_soft_state(vcc_ssp, instance);
10481991Sheppo 		if (vccp == NULL)
10491991Sheppo 			return (ENXIO);
10501991Sheppo 
10511991Sheppo 		D1("vcc_detach: DDI_DETACH instance=%d\n", instance);
10521991Sheppo 
10531991Sheppo 		mutex_enter(&vccp->lock);
10541991Sheppo 
10551991Sheppo 		/* unregister from MD event generator */
10561991Sheppo 
10571991Sheppo 		ASSERT(vccp->mdeg_hdl);
10581991Sheppo 		(void) mdeg_unregister(vccp->mdeg_hdl);
10591991Sheppo 
10601991Sheppo 		ispecp = (mdeg_node_spec_t *)vccp->md_ispecp;
10611991Sheppo 		ASSERT(ispecp);
10621991Sheppo 
10631991Sheppo 		kmem_free(ispecp->specp, sizeof (vcc_prop_template));
10641991Sheppo 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
10651991Sheppo 
10661991Sheppo 		/* remove minor nodes */
10671991Sheppo 		ddi_remove_minor_node(vccp->dip, NULL);
10681991Sheppo 		mutex_exit(&vccp->lock);
10691991Sheppo 
10701991Sheppo 		for (i = 0; i < VCC_MAX_PORTS; i++) {
10711991Sheppo 
10721991Sheppo 			vport = &vccp->port[i];
10731991Sheppo 			mutex_enter(&vport->lock);
10741991Sheppo 			if (i == VCC_CONTROL_PORT) {
10751991Sheppo 				if (vport->status & VCC_PORT_OPEN) {
10761991Sheppo 					(void) i_vcc_close_port(vport);
10771991Sheppo 				}
10781991Sheppo 			}
10791991Sheppo 
10801991Sheppo 			if ((vccp->port[i].status & VCC_PORT_AVAIL) &&
10811991Sheppo 			    (i != VCC_CONTROL_PORT)) {
10821991Sheppo 				D1("vcc_detach: removing port port@%d\n", i);
10831991Sheppo 				(void) i_vcc_delete_port(vccp, vport);
10841991Sheppo 			}
10851991Sheppo 			mutex_exit(&vport->lock);
10861991Sheppo 			cv_destroy(&vport->read_cv);
10871991Sheppo 			cv_destroy(&vport->write_cv);
10881991Sheppo 			mutex_destroy(&vport->lock);
10891991Sheppo 		}
10901991Sheppo 
10911991Sheppo 
10921991Sheppo 
10931991Sheppo 		/* destroy mutex and free the soft state */
10941991Sheppo 		mutex_destroy(&vccp->lock);
10951991Sheppo 		ddi_soft_state_free(vcc_ssp, instance);
10961991Sheppo 
10971991Sheppo 		return (DDI_SUCCESS);
10981991Sheppo 
10991991Sheppo 	case DDI_SUSPEND:
11001991Sheppo 
11011991Sheppo 		return (DDI_SUCCESS);
11021991Sheppo 
11031991Sheppo 	default:
11041991Sheppo 
11051991Sheppo 		return (DDI_FAILURE);
11061991Sheppo 	}
11071991Sheppo }
11081991Sheppo 
11091991Sheppo /* cb_open */
11101991Sheppo static int
11111991Sheppo vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
11121991Sheppo {
11131991Sheppo 	_NOTE(ARGUNUSED(otyp, cred))
11141991Sheppo 
11151991Sheppo 	int	    instance;
11161991Sheppo 	int	    rv = EIO;
11171991Sheppo 	minor_t	    minor;
11181991Sheppo 	uint_t	    portno;
11191991Sheppo 	vcc_t	    *vccp;
11201991Sheppo 	vcc_port_t  *vport;
11211991Sheppo 
11221991Sheppo 	minor = getminor(*devp);
11231991Sheppo 	instance = VCCINST(minor);
11241991Sheppo 
11251991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
11261991Sheppo 	if (vccp == NULL) {
11271991Sheppo 		return (ENXIO);
11281991Sheppo 	}
11291991Sheppo 
11301991Sheppo 	portno = VCCPORT(vccp, minor);
11311991Sheppo 
11321991Sheppo 	vport = &(vccp->port[portno]);
11331991Sheppo 
11341991Sheppo 	mutex_enter(&vport->lock);
11351991Sheppo 
1136*3151Ssg70180 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1137*3151Ssg70180 		/* port may be removed */
1138*3151Ssg70180 		mutex_exit(&vport->lock);
1139*3151Ssg70180 		return (ENXIO);
1140*3151Ssg70180 	}
1141*3151Ssg70180 
11421991Sheppo 	if (vport->status & VCC_PORT_OPEN) {
11431991Sheppo 		/* only one open per port */
11441991Sheppo 		cmn_err(CE_CONT, "vcc_open: virtual-console-concentrator@%d:%d "
11451991Sheppo 		    "is already open\n", instance, portno);
11461991Sheppo 		mutex_exit(&vport->lock);
11471991Sheppo 		return (EAGAIN);
11481991Sheppo 	}
11491991Sheppo 
11501991Sheppo 	/* check minor no and pid */
11511991Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
11521991Sheppo 			    vport)) != 0) {
11531991Sheppo 		mutex_exit(&vport->lock);
11541991Sheppo 		return (rv);
11551991Sheppo 	}
11561991Sheppo 
11571991Sheppo 	if (portno == VCC_CONTROL_PORT) {
11581991Sheppo 		vport->status |= VCC_PORT_OPEN;
11591991Sheppo 		mutex_exit(&vport->lock);
11601991Sheppo 		return (0);
11611991Sheppo 	}
11621991Sheppo 
11631991Sheppo 
11641991Sheppo 	/* check if channel has been initialized */
11651991Sheppo 	if ((vport->status & VCC_PORT_LDC_CHANNEL_READY) == 0) {
11661991Sheppo 		rv = i_vcc_ldc_init(vccp, vport);
11671991Sheppo 		if (rv) {
11681991Sheppo 			mutex_exit(&vport->lock);
11691991Sheppo 			return (EIO);
11701991Sheppo 		}
11711991Sheppo 
11721991Sheppo 		/* mark port as ready */
11731991Sheppo 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
11741991Sheppo 	}
11751991Sheppo 
11761991Sheppo 	vport->status |= VCC_PORT_USE_READ_LDC | VCC_PORT_USE_WRITE_LDC|
11771991Sheppo 	    VCC_PORT_TERM_RD|VCC_PORT_TERM_WR|VCC_PORT_OPEN;
11781991Sheppo 
11791991Sheppo 	if ((flag & O_NONBLOCK) || (flag & O_NDELAY)) {
11801991Sheppo 		vport->status |= VCC_PORT_NONBLOCK;
11811991Sheppo 	}
11821991Sheppo 
11831991Sheppo 	mutex_exit(&vport->lock);
11841991Sheppo 
11851991Sheppo 	return (0);
11861991Sheppo }
11871991Sheppo 
11881991Sheppo /* close port */
11891991Sheppo static int
11901991Sheppo i_vcc_close_port(vcc_port_t *vport)
11911991Sheppo {
11921991Sheppo 	int	rv = EIO;
11931991Sheppo 
11941991Sheppo 	if ((vport->status & VCC_PORT_OPEN) == 0) {
11951991Sheppo 		return (0);
11961991Sheppo 	}
11971991Sheppo 
11981991Sheppo 	ASSERT(mutex_owned(&vport->lock));
11991991Sheppo 
12001991Sheppo 	if (vport->status & VCC_PORT_LDC_CHANNEL_READY) {
12011991Sheppo 		/* clean up ldc channel */
12021991Sheppo 		if ((rv = i_vcc_ldc_fini(vport)) != 0) {
12031991Sheppo 			return (rv);
12041991Sheppo 		}
12051991Sheppo 		vport->status &= ~VCC_PORT_LDC_CHANNEL_READY;
12061991Sheppo 	}
12071991Sheppo 
12081991Sheppo 	/* reset  rd/wr suspends  */
12091991Sheppo 	vport->status |= VCC_PORT_TERM_RD | VCC_PORT_TERM_WR;
12101991Sheppo 	vport->status &= ~VCC_PORT_NONBLOCK;
12111991Sheppo 	vport->status &= ~VCC_PORT_OPEN;
12121991Sheppo 	vport->valid_pid = VCC_NO_PID_BLOCKING;
12131991Sheppo 
12141991Sheppo 	/* signal any blocked read and write thread */
12151991Sheppo 	cv_broadcast(&vport->read_cv);
12161991Sheppo 	cv_broadcast(&vport->write_cv);
12171991Sheppo 
12181991Sheppo 	return (0);
12191991Sheppo }
12201991Sheppo 
12211991Sheppo /* cb_close */
12221991Sheppo static int
12231991Sheppo vcc_close(dev_t dev, int flag, int otyp, cred_t *cred)
12241991Sheppo {
12251991Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
12261991Sheppo 
12271991Sheppo 	int	    instance;
12281991Sheppo 	minor_t	    minor;
12291991Sheppo 	int	    rv = EIO;
12301991Sheppo 	uint_t	    portno;
12311991Sheppo 	vcc_t	    *vccp;
12321991Sheppo 	vcc_port_t  *vport;
12331991Sheppo 
12341991Sheppo 	minor = getminor(dev);
12351991Sheppo 
12361991Sheppo 	instance = VCCINST(minor);
12371991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
12381991Sheppo 	if (vccp == NULL) {
12391991Sheppo 		return (ENXIO);
12401991Sheppo 	}
12411991Sheppo 
12421991Sheppo 	portno = VCCPORT(vccp, minor);
12431991Sheppo 
12441991Sheppo 	D1("vcc_close: closing virtual-console-concentrator@%d:%d\n",
12451991Sheppo 	    instance, portno);
12461991Sheppo 	vport = &(vccp->port[portno]);
12471991Sheppo 
12481991Sheppo 
12491991Sheppo 	if ((vport->status & VCC_PORT_OPEN) == 0) {
12501991Sheppo 		return (0);
12511991Sheppo 	}
12521991Sheppo 
12531991Sheppo 	if (portno == VCC_CONTROL_PORT) {
12541991Sheppo 		/*
12551991Sheppo 		 * vntsd closes control port before it exits. There
12561991Sheppo 		 * could be events still pending for vntsd.
12571991Sheppo 		 */
12581991Sheppo 		rv = i_vcc_reset_events(vccp);
12591991Sheppo 		return (0);
12601991Sheppo 	}
12611991Sheppo 
12621991Sheppo 	mutex_enter(&vport->lock);
12631991Sheppo 
12641991Sheppo 	/* check minor no and pid */
12651991Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
12661991Sheppo 			    vport)) != 0) {
12671991Sheppo 		mutex_exit(&vport->lock);
12681991Sheppo 		return (rv);
12691991Sheppo 	}
12701991Sheppo 
12711991Sheppo 	rv = i_vcc_close_port(vport);
12721991Sheppo 	mutex_exit(&vport->lock);
12731991Sheppo 
12741991Sheppo 	return (rv);
12751991Sheppo }
12761991Sheppo 
12771991Sheppo /*
12781991Sheppo  * ioctl VCC_CONS_TBL - vntsd allocates buffer according to return of
12791991Sheppo  * VCC_NUM_PORTS. However, when vntsd requests for the console table, console
12801991Sheppo  * ports could be deleted or added. parameter num_ports is number of structures
12811991Sheppo  * that vntsd allocated for the table. If there are more ports than
12821991Sheppo  * num_ports, set up to wakeup vntsd to add ports.
12831991Sheppo  * If there less ports than num_ports, fill (-1) for cons_no to tell vntsd.
12841991Sheppo  */
12851991Sheppo static int
12861991Sheppo i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports, caddr_t buf, int mode)
12871991Sheppo {
12881991Sheppo 	vcc_console_t	cons;
12891991Sheppo 	int		i;
12901991Sheppo 	vcc_port_t	*vport;
12911991Sheppo 	boolean_t	notify_vntsd = B_FALSE;
12921991Sheppo 	char pathname[MAXPATHLEN];
12931991Sheppo 
12941991Sheppo 
12951991Sheppo 	(void) ddi_pathname(vccp->dip, pathname);
12961991Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
12971991Sheppo 
12981991Sheppo 		vport = &vccp->port[i];
12991991Sheppo 
13001991Sheppo 		if (i == VCC_CONTROL_PORT) {
13011991Sheppo 			continue;
13021991Sheppo 		}
13031991Sheppo 
13041991Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
13051991Sheppo 			continue;
13061991Sheppo 		}
13071991Sheppo 
13081991Sheppo 		/* a port exists before vntsd becomes online */
13091991Sheppo 		mutex_enter(&vport->lock);
13101991Sheppo 
13111991Sheppo 		if (num_ports == 0) {
13121991Sheppo 			/* more ports than vntsd's buffer can hold */
13131991Sheppo 			vport->status |= VCC_PORT_ADDED;
13141991Sheppo 			notify_vntsd = B_TRUE;
13151991Sheppo 			mutex_exit(&vport->lock);
13161991Sheppo 			continue;
13171991Sheppo 		}
13181991Sheppo 
13191991Sheppo 		bzero(&cons, sizeof (vcc_console_t));
13201991Sheppo 
13211991Sheppo 		/* construct console buffer */
13221991Sheppo 		cons.cons_no = vport->number;
13231991Sheppo 		cons.tcp_port = vport->tcp_port;
13241991Sheppo 		(void) memcpy(cons.domain_name,
13251991Sheppo 		    vport->minorp->domain_name, MAXPATHLEN);
13261991Sheppo 
13271991Sheppo 		(void) memcpy(cons.group_name, vport->group_name,
13281991Sheppo 		    MAXPATHLEN);
13291991Sheppo 		vport->status &= ~VCC_PORT_ADDED;
13301991Sheppo 		mutex_exit(&vport->lock);
13311991Sheppo 
13321991Sheppo 		(void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
13331991Sheppo 		    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
13341991Sheppo 
13351991Sheppo 		/* copy out data */
13361991Sheppo 		if (ddi_copyout(&cons, (void *)buf,
13371991Sheppo 			    sizeof (vcc_console_t), mode)) {
13381991Sheppo 			mutex_exit(&vport->lock);
13391991Sheppo 			return (EFAULT);
13401991Sheppo 		}
13411991Sheppo 		buf += sizeof (vcc_console_t);
13421991Sheppo 
13431991Sheppo 		num_ports--;
13441991Sheppo 
13451991Sheppo 	}
13461991Sheppo 
13471991Sheppo 	if (num_ports == 0) {
13481991Sheppo 		/* vntsd's buffer is full */
13491991Sheppo 
13501991Sheppo 		if (notify_vntsd) {
13511991Sheppo 			/* more ports need to notify vntsd */
13521991Sheppo 			vport = &vccp->port[VCC_CONTROL_PORT];
13531991Sheppo 			mutex_enter(&vport->lock);
13541991Sheppo 			vport->pollevent |= VCC_POLL_ADD_PORT;
13551991Sheppo 			mutex_exit(&vport->lock);
13561991Sheppo 		}
13571991Sheppo 
13581991Sheppo 		return (0);
13591991Sheppo 	}
13601991Sheppo 
13611991Sheppo 	/* less ports than vntsd expected */
13621991Sheppo 	bzero(&cons, sizeof (vcc_console_t));
13631991Sheppo 	cons.cons_no = -1;
13641991Sheppo 
13651991Sheppo 	while (num_ports > 0) {
13661991Sheppo 		/* fill vntsd buffer with no console */
13671991Sheppo 		if (ddi_copyout(&cons, (void *)buf,
13681991Sheppo 			    sizeof (vcc_console_t), mode) != 0) {
13691991Sheppo 			mutex_exit(&vport->lock);
13701991Sheppo 			return (EFAULT);
13711991Sheppo 		}
13721991Sheppo 		D1("i_vcc_cons_tbl: a port is  deleted\n");
13731991Sheppo 		buf += sizeof (vcc_console_t) +MAXPATHLEN;
13741991Sheppo 		num_ports--;
13751991Sheppo 	}
13761991Sheppo 
13771991Sheppo 	return (0);
13781991Sheppo }
13791991Sheppo 
13801991Sheppo 
13811991Sheppo /* turn off event flag if there is no more change */
13821991Sheppo static void
13831991Sheppo i_vcc_turn_off_event(vcc_t *vccp, uint32_t port_status, uint32_t event)
13841991Sheppo {
13851991Sheppo 
13861991Sheppo 	vcc_port_t *vport;
13871991Sheppo 	int i;
13881991Sheppo 
13891991Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
13901991Sheppo 
13911991Sheppo 		vport = &(vccp->port[i]);
13921991Sheppo 
13931991Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
13941991Sheppo 			continue;
13951991Sheppo 		}
13961991Sheppo 
13971991Sheppo 
13981991Sheppo 		if (vport->status & port_status) {
13991991Sheppo 			/* more port changes status */
14001991Sheppo 			return;
14011991Sheppo 		}
14021991Sheppo 
14031991Sheppo 	}
14041991Sheppo 
14051991Sheppo 	/* no more changed port  */
14061991Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
14071991Sheppo 
14081991Sheppo 	/* turn off event */
14091991Sheppo 	mutex_enter(&vport->lock);
14101991Sheppo 	vport->pollevent &= ~event;
14111991Sheppo 	mutex_exit(&vport->lock);
14121991Sheppo }
14131991Sheppo 
14141991Sheppo /* ioctl VCC_CONS_INFO */
14151991Sheppo static int
14161991Sheppo i_vcc_cons_info(vcc_t *vccp, caddr_t buf, int mode)
14171991Sheppo {
14181991Sheppo 	vcc_console_t	cons;
14191991Sheppo 	uint_t		portno;
14201991Sheppo 	vcc_port_t	*vport;
14211991Sheppo 	char pathname[MAXPATHLEN];
14221991Sheppo 
14231991Sheppo 	/* read in portno */
14241991Sheppo 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
14251991Sheppo 		return (EFAULT);
14261991Sheppo 	}
14271991Sheppo 
14281991Sheppo 	D1("i_vcc_cons_info@%d:\n", portno);
14291991Sheppo 
14301991Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
14311991Sheppo 		return (EINVAL);
14321991Sheppo 	}
14331991Sheppo 
14341991Sheppo 	vport = &vccp->port[portno];
14351991Sheppo 
14361991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
14371991Sheppo 		return (EINVAL);
14381991Sheppo 	}
14391991Sheppo 
14401991Sheppo 	mutex_enter(&vport->lock);
14411991Sheppo 	vport->status &= ~VCC_PORT_ADDED;
14421991Sheppo 
14431991Sheppo 	/* construct configruation data  */
14441991Sheppo 	bzero(&cons, sizeof (vcc_console_t));
14451991Sheppo 
14461991Sheppo 	cons.cons_no = vport->number;
14471991Sheppo 	cons.tcp_port = vport->tcp_port;
14481991Sheppo 
14491991Sheppo 	(void) memcpy(cons.domain_name, vport->minorp->domain_name, MAXPATHLEN);
14501991Sheppo 
14511991Sheppo 	(void) memcpy(cons.group_name, vport->group_name, MAXPATHLEN);
14521991Sheppo 
14531991Sheppo 	mutex_exit(&vport->lock);
14541991Sheppo 
14551991Sheppo 	(void) ddi_pathname(vccp->dip, pathname),
14561991Sheppo 
14571991Sheppo 	/* copy device name */
14581991Sheppo 	(void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
14591991Sheppo 	    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
14601991Sheppo 	/* copy data */
14611991Sheppo 	if (ddi_copyout(&cons, (void *)buf,
14621991Sheppo 		    sizeof (vcc_console_t), mode) != 0) {
14631991Sheppo 		mutex_exit(&vport->lock);
14641991Sheppo 		return (EFAULT);
14651991Sheppo 	}
14661991Sheppo 
14671991Sheppo 	D1("i_vcc_cons_info@%d:domain:%s serv:%s tcp@%lld %s\n",
14681991Sheppo 	    cons.cons_no, cons.domain_name,
14691991Sheppo 	    cons.group_name, cons.tcp_port, cons.dev_name);
14701991Sheppo 
14711991Sheppo 	i_vcc_turn_off_event(vccp, VCC_PORT_ADDED, VCC_POLL_ADD_PORT);
14721991Sheppo 
14731991Sheppo 	return (0);
14741991Sheppo }
14751991Sheppo 
14761991Sheppo 
14771991Sheppo /* response to vntsd inquiry ioctl call */
14781991Sheppo static int
14791991Sheppo i_vcc_inquiry(vcc_t *vccp, caddr_t buf, int mode)
14801991Sheppo {
14811991Sheppo 	vcc_port_t	*vport;
14821991Sheppo 	uint_t		i;
14831991Sheppo 	vcc_response_t	msg;
14841991Sheppo 
14851991Sheppo 	vport = &(vccp->port[VCC_CONTROL_PORT]);
14861991Sheppo 
14871991Sheppo 	if ((vport->pollevent & VCC_POLL_ADD_PORT) == 0) {
14881991Sheppo 		return (EINVAL);
14891991Sheppo 	}
14901991Sheppo 
1491*3151Ssg70180 	/* an added port */
14921991Sheppo 
14931991Sheppo 	D1("i_vcc_inquiry\n");
14941991Sheppo 
14951991Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
14961991Sheppo 		if ((vccp->port[i].status & VCC_PORT_AVAIL) == 0) {
14971991Sheppo 			continue;
14981991Sheppo 		}
14991991Sheppo 
15001991Sheppo 		if (vccp->port[i].status & VCC_PORT_ADDED) {
15011991Sheppo 			/* port added */
15021991Sheppo 			msg.reason = VCC_CONS_ADDED;
15031991Sheppo 			msg.cons_no = i;
15041991Sheppo 
15051991Sheppo 			if (ddi_copyout((void *)&msg, (void *)buf,
15061991Sheppo 				    sizeof (msg), mode) == -1) {
15071991Sheppo 				cmn_err(CE_CONT, "i_vcc_find_changed_port:"
15081991Sheppo 					"ddi_copyout"
15091991Sheppo 				    " failed\n");
15101991Sheppo 				return (EFAULT);
15111991Sheppo 			}
15121991Sheppo 			return (0);
15131991Sheppo 		}
15141991Sheppo 	}
15151991Sheppo 
15162748Slm66018 	/* the added port was deleted before vntsd wakes up */
15172748Slm66018 	msg.reason = VCC_CONS_MISS_ADDED;
15182748Slm66018 
15192748Slm66018 	if (ddi_copyout((void *)&msg, (void *)buf,
15202748Slm66018 		    sizeof (msg), mode) == -1) {
15212748Slm66018 		cmn_err(CE_CONT, "i_vcc_find_changed_port: ddi_copyout"
15222748Slm66018 		    " failed\n");
15232748Slm66018 		return (EFAULT);
15242748Slm66018 	}
15252748Slm66018 
15262748Slm66018 	return (0);
15271991Sheppo }
15281991Sheppo 
15291991Sheppo /* clean up events after vntsd exits */
15301991Sheppo static int
15311991Sheppo i_vcc_reset_events(vcc_t *vccp)
15321991Sheppo {
15331991Sheppo 	uint_t	    i;
15341991Sheppo 	vcc_port_t  *vport;
15351991Sheppo 
15361991Sheppo 	for (i = 0; i < VCC_MAX_PORTS; i++) {
15371991Sheppo 		vport = &(vccp->port[i]);
15381991Sheppo 
15391991Sheppo 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
15401991Sheppo 			continue;
15411991Sheppo 		}
15421991Sheppo 
15431991Sheppo 		ASSERT(!mutex_owned(&vport->lock));
15441991Sheppo 
15451991Sheppo 		if (i == VCC_CONTROL_PORT) {
15461991Sheppo 			/* close control port */
15471991Sheppo 			mutex_enter(&vport->lock);
15481991Sheppo 			vport->status &= ~VCC_PORT_OPEN;
15491991Sheppo 
15501991Sheppo 			/* clean up poll events */
15511991Sheppo 			vport->pollevent = 0;
15521991Sheppo 			vport->pollflag = 0;
15531991Sheppo 			mutex_exit(&vport->lock);
15541991Sheppo 			continue;
15551991Sheppo 		}
15561991Sheppo 		if (vport->status & VCC_PORT_ADDED) {
15571991Sheppo 			/* pending added port event to vntsd */
15581991Sheppo 			mutex_enter(&vport->lock);
15591991Sheppo 			vport->status &= ~VCC_PORT_ADDED;
15601991Sheppo 			mutex_exit(&vport->lock);
15611991Sheppo 		}
15621991Sheppo 
15631991Sheppo 	}
15641991Sheppo 
15651991Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
15661991Sheppo 
15671991Sheppo 	return (0);
15681991Sheppo }
15691991Sheppo 
15701991Sheppo /* ioctl VCC_FORCE_CLOSE */
15711991Sheppo static int
15721991Sheppo i_vcc_force_close(vcc_t *vccp, caddr_t buf, int mode)
15731991Sheppo {
15741991Sheppo 	uint_t		portno;
15751991Sheppo 	vcc_port_t	*vport;
15761991Sheppo 	int		rv;
15771991Sheppo 
15781991Sheppo 	/* read in portno */
15791991Sheppo 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
15801991Sheppo 		return (EFAULT);
15811991Sheppo 	}
15821991Sheppo 
15831991Sheppo 	D1("i_vcc_force_close@%d:\n", portno);
15841991Sheppo 
15851991Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
15861991Sheppo 		return (EINVAL);
15871991Sheppo 	}
15881991Sheppo 
15891991Sheppo 	vport = &vccp->port[portno];
15901991Sheppo 
15911991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
15921991Sheppo 		return (EINVAL);
15931991Sheppo 	}
15941991Sheppo 
15951991Sheppo 	mutex_enter(&vport->lock);
15961991Sheppo 
15971991Sheppo 	rv = i_vcc_close_port(vport);
15981991Sheppo 
15991991Sheppo 	/* block callers other than vntsd */
16001991Sheppo 	vport->valid_pid = ddi_get_pid();
16011991Sheppo 
16021991Sheppo 	mutex_exit(&vport->lock);
16031991Sheppo 	return (rv);
16041991Sheppo 
16051991Sheppo }
16061991Sheppo 
16071991Sheppo /* ioctl VCC_CONS_STATUS */
16081991Sheppo static int
16091991Sheppo i_vcc_cons_status(vcc_t *vccp, caddr_t buf, int mode)
16101991Sheppo {
16111991Sheppo 	vcc_console_t	console;
16121991Sheppo 	vcc_port_t	*vport;
16131991Sheppo 
16141991Sheppo 	/* read in portno */
16151991Sheppo 	if (ddi_copyin((void*)buf, &console, sizeof (console), mode)) {
16161991Sheppo 		return (EFAULT);
16171991Sheppo 	}
16181991Sheppo 
16191991Sheppo 	D1("i_vcc_cons_status@%d:\n", console.cons_no);
16201991Sheppo 
16211991Sheppo 	if ((console.cons_no >= VCC_MAX_PORTS) ||
16221991Sheppo 		(console.cons_no == VCC_CONTROL_PORT)) {
16231991Sheppo 		return (EINVAL);
16241991Sheppo 	}
16251991Sheppo 
16261991Sheppo 
16271991Sheppo 	vport = &vccp->port[console.cons_no];
16281991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
16291991Sheppo 		console.cons_no = -1;
16301991Sheppo 	} else  if (strncmp(console.domain_name, vport->minorp->domain_name,
16311991Sheppo 		    MAXPATHLEN)) {
16321991Sheppo 		console.cons_no = -1;
16331991Sheppo 	} else if (strncmp(console.group_name, vport->group_name,
16341991Sheppo 		    MAXPATHLEN)) {
16351991Sheppo 		console.cons_no = -1;
16361991Sheppo 	} else if (console.tcp_port != vport->tcp_port) {
16371991Sheppo 		console.cons_no = -1;
1638*3151Ssg70180 	} else if (vport->ldc_id == VCC_INVALID_CHANNEL) {
1639*3151Ssg70180 		console.cons_no = -1;
16401991Sheppo 	}
16411991Sheppo 
16421991Sheppo 	D1("i_vcc_cons_status@%d: %s %s %llx\n", console.cons_no,
16431991Sheppo 	    console.group_name, console.domain_name, console.tcp_port);
16441991Sheppo 	if (ddi_copyout(&console, (void *)buf, sizeof (console), mode) == -1) {
16451991Sheppo 		cmn_err(CE_CONT, "i_vcc_cons_status ddi_copyout failed\n");
16461991Sheppo 		return (EFAULT);
16471991Sheppo 	}
16481991Sheppo 
16491991Sheppo 	return (0);
16501991Sheppo }
16511991Sheppo 
16521991Sheppo /* cb_ioctl handler for vcc control port */
16531991Sheppo static int
16541991Sheppo i_vcc_ctrl_ioctl(vcc_t *vccp, int cmd, void* arg, int mode)
16551991Sheppo {
16561991Sheppo 
16571991Sheppo 	static uint_t	num_ports;
16581991Sheppo 
16591991Sheppo 
16601991Sheppo 	switch (cmd) {
16611991Sheppo 
16621991Sheppo 	case VCC_NUM_CONSOLE:
16631991Sheppo 
16641991Sheppo 		mutex_enter(&vccp->lock);
16651991Sheppo 		num_ports = vccp->num_ports;
16661991Sheppo 		mutex_exit(&vccp->lock);
16671991Sheppo 		/* number of consoles */
16681991Sheppo 
16691991Sheppo 		return (ddi_copyout((void *)&num_ports, arg,
16701991Sheppo 			    sizeof (int), mode));
16711991Sheppo 	case VCC_CONS_TBL:
16721991Sheppo 
16731991Sheppo 		/* console config table */
16741991Sheppo 		return (i_vcc_cons_tbl(vccp, num_ports, (caddr_t)arg, mode));
16751991Sheppo 
16761991Sheppo 	case VCC_INQUIRY:
16771991Sheppo 
16781991Sheppo 		/* reason for wakeup */
16791991Sheppo 		return (i_vcc_inquiry(vccp, (caddr_t)arg, mode));
16801991Sheppo 
16811991Sheppo 	case VCC_CONS_INFO:
16821991Sheppo 		/* a console config */
16831991Sheppo 		return (i_vcc_cons_info(vccp, (caddr_t)arg, mode));
16841991Sheppo 
16851991Sheppo 	case VCC_FORCE_CLOSE:
16861991Sheppo 		/* force to close a console */
16871991Sheppo 		return (i_vcc_force_close(vccp, (caddr_t)arg, mode));
16881991Sheppo 
16891991Sheppo 	case VCC_CONS_STATUS:
16901991Sheppo 		/* console status */
16911991Sheppo 		return (i_vcc_cons_status(vccp, (caddr_t)arg, mode));
16921991Sheppo 
16931991Sheppo 	default:
16941991Sheppo 
16951991Sheppo 		/* unknown command */
16961991Sheppo 		return (ENODEV);
16971991Sheppo 	}
16981991Sheppo 
16991991Sheppo 
17001991Sheppo }
17011991Sheppo 
17021991Sheppo /* write data to ldc. may block if channel has no space for write */
17031991Sheppo static int
17041991Sheppo i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf)
17051991Sheppo {
17061991Sheppo 	int	rv = EIO;
17071991Sheppo 	size_t	size;
17081991Sheppo 
17091991Sheppo 	ASSERT(mutex_owned(&vport->lock));
17101991Sheppo 	ASSERT((vport->status & VCC_PORT_USE_WRITE_LDC) == 0);
17111991Sheppo 
17121991Sheppo 	for (; ; ) {
17131991Sheppo 
17141991Sheppo 		size = VCC_HDR_SZ + buf->size;
17151991Sheppo 		rv = ldc_write(vport->ldc_handle, (caddr_t)buf, &size);
17161991Sheppo 
17171991Sheppo 		D1("i_vcc_write_ldc: port@%d: err=%d %d bytes\n",
17181991Sheppo 		    vport->number, rv, size);
17191991Sheppo 
17201991Sheppo 		if (rv == 0) {
17211991Sheppo 			return (rv);
17221991Sheppo 		}
17231991Sheppo 
17241991Sheppo 		if (rv != EWOULDBLOCK) {
17251991Sheppo 			return (EIO);
17261991Sheppo 		}
17271991Sheppo 
17281991Sheppo 		if (vport->status & VCC_PORT_NONBLOCK) {
17291991Sheppo 			return (EAGAIN);
17301991Sheppo 		}
17311991Sheppo 
17321991Sheppo 		/*  block util ldc has more space */
17331991Sheppo 
17341991Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
17351991Sheppo 		    VCC_PORT_LDC_WRITE_READY);
17361991Sheppo 
17371991Sheppo 		if (rv) {
17381991Sheppo 			return (rv);
17391991Sheppo 		}
17401991Sheppo 
17411991Sheppo 		vport->status &= ~VCC_PORT_LDC_WRITE_READY;
17421991Sheppo 
17431991Sheppo 	}
17441991Sheppo 
17451991Sheppo }
17461991Sheppo 
17471991Sheppo 
17481991Sheppo 
17491991Sheppo /* cb_ioctl handler for port ioctl */
17501991Sheppo static int
17511991Sheppo i_vcc_port_ioctl(vcc_t *vccp, minor_t minor, int portno, int cmd, void *arg,
17521991Sheppo     int mode)
17531991Sheppo {
17541991Sheppo 
17551991Sheppo 	vcc_port_t	*vport;
17561991Sheppo 	struct termios	term;
17571991Sheppo 	vcc_msg_t	buf;
17581991Sheppo 	int		rv;
17591991Sheppo 
17601991Sheppo 	D1("i_vcc_port_ioctl@%d cmd %d\n", portno, cmd);
17611991Sheppo 
17621991Sheppo 	vport = &(vccp->port[portno]);
17631991Sheppo 
17641991Sheppo 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
17651991Sheppo 		return (EIO);
17661991Sheppo 	}
17671991Sheppo 
17681991Sheppo 
17691991Sheppo 	switch (cmd) {
17701991Sheppo 
17711991Sheppo 	/* terminal support */
17721991Sheppo 	case TCGETA:
17731991Sheppo 	case TCGETS:
17741991Sheppo 
17751991Sheppo 		mutex_enter(&vport->lock);
17761991Sheppo 
17771991Sheppo 		/* check minor no and pid */
17781991Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
17791991Sheppo 				    vport)) != 0) {
17801991Sheppo 			mutex_exit(&vport->lock);
17811991Sheppo 			return (rv);
17821991Sheppo 		}
17831991Sheppo 
17841991Sheppo 		(void) memcpy(&term, &vport->term, sizeof (term));
17851991Sheppo 		mutex_exit(&vport->lock);
17861991Sheppo 
17871991Sheppo 		return (ddi_copyout(&term, arg, sizeof (term), mode));
17881991Sheppo 
17891991Sheppo 	case TCSETS:
17901991Sheppo 	case TCSETA:
17911991Sheppo 	case TCSETAW:
17921991Sheppo 	case TCSETAF:
17931991Sheppo 
17941991Sheppo 		if (ddi_copyin(arg, &term, sizeof (term), mode) != 0) {
17951991Sheppo 			return (EFAULT);
17961991Sheppo 		}
17971991Sheppo 
17981991Sheppo 		mutex_enter(&vport->lock);
17991991Sheppo 
18001991Sheppo 		/* check minor no and pid */
18011991Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
18021991Sheppo 				    vport)) != 0) {
18031991Sheppo 			mutex_exit(&vport->lock);
18041991Sheppo 			return (rv);
18051991Sheppo 		}
18061991Sheppo 
18071991Sheppo 		(void) memcpy(&vport->term, &term, sizeof (term));
18081991Sheppo 		mutex_exit(&vport->lock);
18091991Sheppo 		return (0);
18101991Sheppo 
18111991Sheppo 
18121991Sheppo 	case TCSBRK:
18131991Sheppo 
18141991Sheppo 		/* send break to console */
18151991Sheppo 		mutex_enter(&vport->lock);
18161991Sheppo 
18171991Sheppo 		/* check minor no and pid */
18181991Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
18191991Sheppo 				    vport)) != 0) {
18201991Sheppo 			mutex_exit(&vport->lock);
18211991Sheppo 			return (rv);
18221991Sheppo 		}
18231991Sheppo 
18241991Sheppo 		/* wait for write available */
18251991Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
18261991Sheppo 		    VCC_PORT_LDC_CHANNEL_READY| VCC_PORT_USE_WRITE_LDC);
18271991Sheppo 		if (rv) {
18281991Sheppo 			mutex_exit(&vport->lock);
18291991Sheppo 			return (rv);
18301991Sheppo 		}
18311991Sheppo 
18321991Sheppo 		vport->status &= ~VCC_PORT_USE_WRITE_LDC;
18331991Sheppo 
18341991Sheppo 		buf.type = LDC_CONSOLE_CTRL;
18351991Sheppo 		buf.ctrl_msg = LDC_CONSOLE_BREAK;
18361991Sheppo 		buf.size = 0;
18371991Sheppo 
18381991Sheppo 		rv = i_vcc_write_ldc(vport, &buf);
18391991Sheppo 
18401991Sheppo 		mutex_exit(&vport->lock);
18411991Sheppo 
18421991Sheppo 		i_vcc_set_port_status(vport, &vport->write_cv,
18431991Sheppo 			VCC_PORT_USE_WRITE_LDC);
18441991Sheppo 		return (0);
18451991Sheppo 
18461991Sheppo 	case TCXONC:
18471991Sheppo 		/* suspend read or write */
18481991Sheppo 		if (ddi_copyin(arg, &cmd, sizeof (int), mode) != 0) {
18491991Sheppo 			return (EFAULT);
18501991Sheppo 		}
18511991Sheppo 
18521991Sheppo 		mutex_enter(&vport->lock);
18531991Sheppo 
18541991Sheppo 		/* check minor no and pid */
18551991Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
18561991Sheppo 				    vport)) != 0) {
18571991Sheppo 			mutex_exit(&vport->lock);
18581991Sheppo 			return (rv);
18591991Sheppo 		}
18601991Sheppo 
18611991Sheppo 
18621991Sheppo 		switch (cmd) {
18631991Sheppo 
18641991Sheppo 		case 0:
18652748Slm66018 			/* suspend read */
18662748Slm66018 			vport->status &= ~VCC_PORT_TERM_RD;
18672748Slm66018 			break;
18682748Slm66018 
18692748Slm66018 		case 1:
18702748Slm66018 			/* resume read */
18712748Slm66018 			vport->status |= VCC_PORT_TERM_RD;
18722748Slm66018 			cv_broadcast(&vport->read_cv);
18732748Slm66018 			break;
18742748Slm66018 
18752748Slm66018 		case 2:
18762748Slm66018 			/* suspend write */
18772748Slm66018 			vport->status &= ~VCC_PORT_TERM_WR;
18782748Slm66018 			break;
18792748Slm66018 
18802748Slm66018 		case 3:
18812748Slm66018 			/* resume write */
18821991Sheppo 			vport->status |= VCC_PORT_TERM_WR;
18831991Sheppo 			cv_broadcast(&vport->write_cv);
18841991Sheppo 			break;
18851991Sheppo 
18861991Sheppo 		default:
18872748Slm66018 			mutex_exit(&vport->lock);
18882748Slm66018 			return (EINVAL);
18891991Sheppo 		}
18901991Sheppo 
18911991Sheppo 		mutex_exit(&vport->lock);
18921991Sheppo 		return (0);
18931991Sheppo 
18941991Sheppo 	case TCFLSH:
18951991Sheppo 		return (0);
18961991Sheppo 
18971991Sheppo 	default:
18982748Slm66018 		return (EINVAL);
18991991Sheppo 	}
19001991Sheppo 
19011991Sheppo }
19021991Sheppo 
19031991Sheppo /* cb_ioctl */
19041991Sheppo static int
19051991Sheppo vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
19061991Sheppo     cred_t *credp, int *rvalp)
19071991Sheppo {
19081991Sheppo 	_NOTE(ARGUNUSED(credp, rvalp))
19091991Sheppo 
19101991Sheppo 	int instance;
19111991Sheppo 	minor_t minor;
19121991Sheppo 	int portno;
19131991Sheppo 	vcc_t *vccp;
19141991Sheppo 
19151991Sheppo 	minor = getminor(dev);
19161991Sheppo 
19171991Sheppo 	instance = VCCINST(minor);
19181991Sheppo 
19191991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
19201991Sheppo 	if (vccp == NULL) {
19211991Sheppo 		return (ENXIO);
19221991Sheppo 	}
19231991Sheppo 
19241991Sheppo 	portno = VCCPORT(vccp, minor);
19251991Sheppo 
19261991Sheppo 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d\n", instance, portno);
19271991Sheppo 
19281991Sheppo 	if (portno >= VCC_MAX_PORTS) {
19291991Sheppo 		cmn_err(CE_CONT, "vcc_ioctl:virtual-console-concentrator@%d"
19301991Sheppo 			" invalid portno\n", portno);
19311991Sheppo 		return (EINVAL);
19321991Sheppo 	}
19331991Sheppo 
19341991Sheppo 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d ioctl cmd=%d\n",
19351991Sheppo 	    instance, portno, cmd);
19361991Sheppo 
19371991Sheppo 	if (portno == VCC_CONTROL_PORT) {
19381991Sheppo 		/* control ioctl */
19391991Sheppo 		return (i_vcc_ctrl_ioctl(vccp, cmd, (void *)arg, mode));
19401991Sheppo 	}
19411991Sheppo 
19421991Sheppo 	/* data port ioctl */
19431991Sheppo 	return (i_vcc_port_ioctl(vccp, minor, portno, cmd, (void *)arg, mode));
19441991Sheppo }
19451991Sheppo 
19461991Sheppo /* cb_read */
19471991Sheppo static int
19481991Sheppo vcc_read(dev_t dev, struct uio *uiop, cred_t *credp)
19491991Sheppo {
19501991Sheppo 	_NOTE(ARGUNUSED(credp))
19511991Sheppo 
19521991Sheppo 	int	    instance;
19531991Sheppo 	minor_t	    minor;
19541991Sheppo 	uint_t	    portno;
19551991Sheppo 	vcc_t	    *vccp;
19561991Sheppo 	vcc_port_t  *vport;
19571991Sheppo 	int	    rv = EIO;	/* by default fail ! */
19581991Sheppo 	char 		*buf;
19591991Sheppo 	size_t		uio_size;
19601991Sheppo 	size_t		size;
19611991Sheppo 
19621991Sheppo 	minor = getminor(dev);
19631991Sheppo 
19641991Sheppo 	instance = VCCINST(minor);
19651991Sheppo 
19661991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
19671991Sheppo 	if (vccp == NULL) {
19681991Sheppo 		return (ENXIO);
19691991Sheppo 	}
19701991Sheppo 
19711991Sheppo 	portno = VCCPORT(vccp, minor);
19721991Sheppo 
19731991Sheppo 	/* no read for control port */
19741991Sheppo 	if (portno == VCC_CONTROL_PORT) {
19751991Sheppo 		return (EIO);
19761991Sheppo 	}
19771991Sheppo 
19781991Sheppo 	/* temp buf to hold ldc data */
19791991Sheppo 	uio_size = uiop->uio_resid;
19801991Sheppo 
19811991Sheppo 	if (uio_size < VCC_MTU_SZ) {
19821991Sheppo 		return (EINVAL);
19831991Sheppo 	}
19841991Sheppo 
19851991Sheppo 	vport = &(vccp->port[portno]);
19861991Sheppo 
19871991Sheppo 	mutex_enter(&vport->lock);
19881991Sheppo 
19891991Sheppo 	/* check minor no and pid */
19901991Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
19911991Sheppo 			    vport)) != 0) {
19921991Sheppo 		mutex_exit(&vport->lock);
19931991Sheppo 		return (rv);
19941991Sheppo 	}
19951991Sheppo 
19961991Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
19971991Sheppo 		    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
19981991Sheppo 		    VCC_PORT_USE_READ_LDC);
19991991Sheppo 	if (rv) {
20001991Sheppo 		mutex_exit(&vport->lock);
20011991Sheppo 		return (rv);
20021991Sheppo 	}
20031991Sheppo 
20041991Sheppo 	buf = kmem_alloc(uio_size, KM_SLEEP);
20051991Sheppo 
20061991Sheppo 	vport->status &= ~VCC_PORT_USE_READ_LDC;
20071991Sheppo 
20081991Sheppo 	for (; ; ) {
20091991Sheppo 
20101991Sheppo 		size = uio_size;
20111991Sheppo 		rv = i_vcc_read_ldc(vport, buf, &size);
20121991Sheppo 
20131991Sheppo 
20141991Sheppo 		if (rv == EAGAIN) {
20151991Sheppo 			/* should block? */
20161991Sheppo 			if (vport->status & VCC_PORT_NONBLOCK) {
20171991Sheppo 				break;
20181991Sheppo 			}
20191991Sheppo 
20201991Sheppo 		} else if (rv) {
20211991Sheppo 			/* error */
20221991Sheppo 			break;
20231991Sheppo 		}
20241991Sheppo 
20251991Sheppo 		if (size > 0) {
20261991Sheppo 			/* got data */
20271991Sheppo 			break;
20281991Sheppo 		}
20291991Sheppo 
20301991Sheppo 		/* wait for data from ldc */
20311991Sheppo 		vport->status &= ~VCC_PORT_LDC_DATA_READY;
20322748Slm66018 
20332748Slm66018 		mutex_exit(&vport->lock);
20342748Slm66018 		i_vcc_set_port_status(vport, &vport->read_cv,
20352748Slm66018 		    VCC_PORT_USE_READ_LDC);
20362748Slm66018 		mutex_enter(&vport->lock);
20372748Slm66018 
20381991Sheppo 		rv = i_vcc_wait_port_status(vport, &vport->read_cv,
20392748Slm66018 		    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
20402748Slm66018 		    VCC_PORT_USE_READ_LDC| VCC_PORT_LDC_DATA_READY);
20411991Sheppo 		if (rv) {
20421991Sheppo 			break;
20431991Sheppo 		}
20442748Slm66018 
20452748Slm66018 		vport->status &= ~VCC_PORT_USE_READ_LDC;
20461991Sheppo 	}
20471991Sheppo 
20481991Sheppo 	mutex_exit(&vport->lock);
20491991Sheppo 
20501991Sheppo 	if ((rv == 0) && (size > 0)) {
20511991Sheppo 		/* data is in buf */
20521991Sheppo 		rv = uiomove(buf, size, UIO_READ, uiop);
20531991Sheppo 	}
20541991Sheppo 
20551991Sheppo 	kmem_free(buf, uio_size);
20561991Sheppo 	i_vcc_set_port_status(vport, &vport->read_cv, VCC_PORT_USE_READ_LDC);
20571991Sheppo 
20581991Sheppo 	return (rv);
20591991Sheppo }
20601991Sheppo 
20611991Sheppo 
20621991Sheppo /* cb_write */
20631991Sheppo static int
20641991Sheppo vcc_write(dev_t dev, struct uio *uiop, cred_t *credp)
20651991Sheppo {
20661991Sheppo 	_NOTE(ARGUNUSED(credp))
20671991Sheppo 
20681991Sheppo 	int	    instance;
20691991Sheppo 	minor_t	    minor;
20701991Sheppo 	size_t	    size;
20711991Sheppo 	size_t	    bytes;
20721991Sheppo 	uint_t	    portno;
20731991Sheppo 	vcc_t	    *vccp;
20741991Sheppo 
20751991Sheppo 	vcc_port_t  *vport;
20761991Sheppo 	int	    rv = EIO;
20771991Sheppo 
20781991Sheppo 	vcc_msg_t	buf;
20791991Sheppo 
20801991Sheppo 	minor = getminor(dev);
20811991Sheppo 
20821991Sheppo 	instance = VCCINST(minor);
20831991Sheppo 
20841991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
20851991Sheppo 	if (vccp == NULL) {
20861991Sheppo 		return (ENXIO);
20871991Sheppo 	}
20881991Sheppo 
20891991Sheppo 	portno = VCCPORT(vccp, minor);
20901991Sheppo 
20911991Sheppo 	/* no write for control port */
20921991Sheppo 	if (portno == VCC_CONTROL_PORT) {
20931991Sheppo 		return (EIO);
20941991Sheppo 	}
20951991Sheppo 	vport = &(vccp->port[portno]);
20961991Sheppo 
20971991Sheppo 	/*
20981991Sheppo 	 * check if the channel has been configured,
20991991Sheppo 	 * if write has been suspend and grab write lock.
21001991Sheppo 	 */
21011991Sheppo 	mutex_enter(&vport->lock);
21021991Sheppo 
21031991Sheppo 	/* check minor no and pid */
21041991Sheppo 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
21051991Sheppo 			    vport)) != 0) {
21061991Sheppo 		mutex_exit(&vport->lock);
21071991Sheppo 		return (rv);
21081991Sheppo 	}
21091991Sheppo 
21101991Sheppo 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
21111991Sheppo 		VCC_PORT_TERM_WR|VCC_PORT_LDC_CHANNEL_READY|
21121991Sheppo 		VCC_PORT_USE_WRITE_LDC);
21131991Sheppo 	if (rv) {
21141991Sheppo 		mutex_exit(&vport->lock);
21151991Sheppo 		return (rv);
21161991Sheppo 	}
21171991Sheppo 
21181991Sheppo 	vport->status &= ~VCC_PORT_USE_WRITE_LDC;
21191991Sheppo 	mutex_exit(&vport->lock);
21201991Sheppo 	size = uiop->uio_resid;
21211991Sheppo 
21221991Sheppo 	D2("vcc_write: virtual-console-concentrator@%d:%d writing %d bytes\n",
21231991Sheppo 	    instance, portno, size);
21241991Sheppo 
21251991Sheppo 
21261991Sheppo 
21271991Sheppo 	buf.type = LDC_CONSOLE_DATA;
21281991Sheppo 
21291991Sheppo 	while (size) {
21301991Sheppo 
21311991Sheppo 		bytes = MIN(size, VCC_MTU_SZ);
21321991Sheppo 		/* move data */
21331991Sheppo 		rv = uiomove(&(buf.data), bytes, UIO_WRITE, uiop);
21341991Sheppo 
21351991Sheppo 		if (rv) {
21361991Sheppo 			break;
21371991Sheppo 		}
21381991Sheppo 
21391991Sheppo 		/* write to ldc */
21401991Sheppo 		buf.size = bytes;
21411991Sheppo 
21421991Sheppo 		mutex_enter(&vport->lock);
21431991Sheppo 
21441991Sheppo 		/* check minor no and pid */
21451991Sheppo 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
21461991Sheppo 			    vport)) != 0) {
21471991Sheppo 			mutex_exit(&vport->lock);
21481991Sheppo 			return (rv);
21491991Sheppo 		}
21501991Sheppo 
21511991Sheppo 		rv = i_vcc_write_ldc(vport, &buf);
21521991Sheppo 
21531991Sheppo 		mutex_exit(&vport->lock);
21541991Sheppo 
21551991Sheppo 		if (rv) {
21561991Sheppo 			break;
21571991Sheppo 		}
21581991Sheppo 
21591991Sheppo 		size -= bytes;
21601991Sheppo 
21611991Sheppo 	}
21621991Sheppo 
21631991Sheppo 	i_vcc_set_port_status(vport, &vport->write_cv, VCC_PORT_USE_WRITE_LDC);
21641991Sheppo 	return (rv);
21651991Sheppo }
21661991Sheppo 
21671991Sheppo /* mdeg callback for a removed port */
21681991Sheppo static int
21691991Sheppo i_vcc_md_remove_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
21701991Sheppo {
21711991Sheppo 	uint64_t  portno;	/* md requires 64bit for port number */
21721991Sheppo 	int rv = MDEG_FAILURE;
21731991Sheppo 	vcc_port_t *vport;
21741991Sheppo 
21751991Sheppo 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
21761991Sheppo 		cmn_err(CE_CONT, "vcc_mdeg_cb: port has no 'id' property\n");
21771991Sheppo 		return (MDEG_FAILURE);
21781991Sheppo 	}
21791991Sheppo 
21801991Sheppo 	if ((portno >= VCC_MAX_PORTS) || (portno < 0)) {
21811991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld invalid port no\n",
21821991Sheppo 			portno);
21831991Sheppo 		return (MDEG_FAILURE);
21841991Sheppo 	}
21851991Sheppo 
21861991Sheppo 	if (portno == VCC_CONTROL_PORT) {
21871991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld can not remove"
21881991Sheppo 			"control port\n",
21891991Sheppo 		    portno);
21901991Sheppo 		return (MDEG_FAILURE);
21911991Sheppo 	}
21921991Sheppo 
21931991Sheppo 	vport = &(vccp->port[portno]);
21941991Sheppo 
21951991Sheppo 	/* delete the port */
21961991Sheppo 	mutex_enter(&vport->lock);
21971991Sheppo 	rv = i_vcc_delete_port(vccp, vport);
21981991Sheppo 	mutex_exit(&vport->lock);
21991991Sheppo 
22001991Sheppo 	mutex_enter(&vccp->lock);
22011991Sheppo 	vccp->num_ports--;
22021991Sheppo 	mutex_exit(&vccp->lock);
22031991Sheppo 
22041991Sheppo 	return (rv ? MDEG_FAILURE : MDEG_SUCCESS);
22051991Sheppo }
22061991Sheppo 
22071991Sheppo static int
22081991Sheppo i_vcc_get_ldc_id(md_t *md, mde_cookie_t mdep, uint64_t *ldc_id)
22091991Sheppo {
22101991Sheppo 	int		num_nodes;
22111991Sheppo 	size_t		size;
22121991Sheppo 	mde_cookie_t	*channel;
22131991Sheppo 	int		num_channels;
22141991Sheppo 
22151991Sheppo 
22161991Sheppo 	if ((num_nodes = md_node_count(md)) <= 0) {
22171991Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc_channel_id:"
22181991Sheppo 		    "  Invalid node count in Machine Description subtree");
22191991Sheppo 		return (-1);
22201991Sheppo 	}
22211991Sheppo 	size = num_nodes*(sizeof (*channel));
22221991Sheppo 	channel = kmem_zalloc(size, KM_SLEEP);
22231991Sheppo 	ASSERT(channel != NULL);	/* because KM_SLEEP */
22241991Sheppo 
22251991Sheppo 
22261991Sheppo 	/* Look for channel endpoint child(ren) of the vdisk MD node */
22271991Sheppo 	if ((num_channels = md_scan_dag(md, mdep,
22281991Sheppo 		    md_find_name(md, "channel-endpoint"),
22291991Sheppo 		    md_find_name(md, "fwd"), channel)) <= 0) {
22301991Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc_id:  No 'channel-endpoint'"
22311991Sheppo 		    " found for vcc");
22321991Sheppo 		kmem_free(channel, size);
22331991Sheppo 		return (-1);
22341991Sheppo 	}
22351991Sheppo 
22361991Sheppo 	/* Get the "id" value for the first channel endpoint node */
22371991Sheppo 	if (md_get_prop_val(md, channel[0], "id", ldc_id) != 0) {
22381991Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc:  No id property found "
22391991Sheppo 		    "for channel-endpoint of vcc");
22401991Sheppo 		kmem_free(channel, size);
22411991Sheppo 		return (-1);
22421991Sheppo 	}
22431991Sheppo 
22441991Sheppo 	if (num_channels > 1) {
22451991Sheppo 		cmn_err(CE_CONT, "i_vcc_get_ldc:  Warning:  Using ID of first"
22461991Sheppo 		    " of multiple channels for this vcc");
22471991Sheppo 	}
22481991Sheppo 
22491991Sheppo 	kmem_free(channel, size);
22501991Sheppo 	return (0);
22511991Sheppo }
22521991Sheppo /* mdeg callback for an added port  */
22531991Sheppo static int
22541991Sheppo i_vcc_md_add_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
22551991Sheppo {
22561991Sheppo 	uint64_t	portno;		/* md requires 64 bit */
22571991Sheppo 	char		*domain_name;
22581991Sheppo 	char		*group_name;
22591991Sheppo 	uint64_t	ldc_id;
22601991Sheppo 	uint64_t	tcp_port;
22611991Sheppo 	vcc_port_t	*vport;
22621991Sheppo 
22631991Sheppo 	/* read in the port's reg property */
22641991Sheppo 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
22651991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port_: port has no 'id' "
22661991Sheppo 			"property\n");
22671991Sheppo 		return (MDEG_FAILURE);
22681991Sheppo 	}
22691991Sheppo 
22701991Sheppo 	/* read in the port's "vcc-doman-name" property */
22711991Sheppo 	if (md_get_prop_str(mdp, mdep, "vcc-domain-name", &domain_name)) {
22721991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has "
22731991Sheppo 			"no 'vcc-domain-name' property\n", portno);
22741991Sheppo 		return (MDEG_FAILURE);
22751991Sheppo 	}
22761991Sheppo 
22771991Sheppo 
22781991Sheppo 	/* read in the port's "vcc-group-name" property */
22791991Sheppo 	if (md_get_prop_str(mdp, mdep, "vcc-group-name", &group_name)) {
22801991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no "
22811991Sheppo 			"'vcc-group-name'property\n", portno);
22821991Sheppo 		return (MDEG_FAILURE);
22831991Sheppo 	}
22841991Sheppo 
22851991Sheppo 
22861991Sheppo 	/* read in the port's "vcc-tcp-port" property */
22871991Sheppo 	if (md_get_prop_val(mdp, mdep, "vcc-tcp-port", &tcp_port)) {
22881991Sheppo 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no"
22891991Sheppo 			"'vcc-tcp-port' property\n", portno);
22901991Sheppo 		return (MDEG_FAILURE);
22911991Sheppo 	}
22921991Sheppo 
22931991Sheppo 	D1("i_vcc_md_add_port: port@%d domain-name=%s group-name=%s"
22941991Sheppo 	    " tcp-port=%lld\n", portno, domain_name, group_name, tcp_port);
22951991Sheppo 
22961991Sheppo 	/* add the port */
22971991Sheppo 	if (i_vcc_add_port(vccp, group_name, tcp_port, portno, domain_name)) {
22981991Sheppo 		return (MDEG_FAILURE);
22991991Sheppo 	}
23001991Sheppo 
23011991Sheppo 	vport = &vccp->port[portno];
23021991Sheppo 	if (i_vcc_get_ldc_id(mdp, mdep, &ldc_id)) {
23031991Sheppo 		mutex_enter(&vport->lock);
23041991Sheppo 		(void) i_vcc_delete_port(vccp, vport);
23051991Sheppo 		mutex_exit(&vport->lock);
23061991Sheppo 		return (MDEG_FAILURE);
23071991Sheppo 	}
23081991Sheppo 
23091991Sheppo 	/* configure the port */
23101991Sheppo 	if (i_vcc_config_port(vccp, portno, ldc_id)) {
23111991Sheppo 		mutex_enter(&vport->lock);
23121991Sheppo 		(void) i_vcc_delete_port(vccp, vport);
23131991Sheppo 		mutex_exit(&vport->lock);
23141991Sheppo 		return (MDEG_FAILURE);
23151991Sheppo 	}
23161991Sheppo 
23171991Sheppo 	mutex_enter(&vccp->lock);
23181991Sheppo 	vccp->num_ports++;
23191991Sheppo 	mutex_exit(&vccp->lock);
23201991Sheppo 
23211991Sheppo 	vport = &vccp->port[VCC_CONTROL_PORT];
23221991Sheppo 
23231991Sheppo 	if (vport->pollflag & VCC_POLL_CONFIG) {
23241991Sheppo 		/* wakeup vntsd */
23251991Sheppo 		mutex_enter(&vport->lock);
23261991Sheppo 		vport->pollevent |= VCC_POLL_ADD_PORT;
23271991Sheppo 		mutex_exit(&vport->lock);
23281991Sheppo 		pollwakeup(&vport->poll, POLLIN);
23291991Sheppo 	}
23301991Sheppo 
23311991Sheppo 	return (MDEG_SUCCESS);
23321991Sheppo }
23331991Sheppo 
23341991Sheppo /* mdeg callback */
23351991Sheppo static int
23361991Sheppo vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
23371991Sheppo {
23381991Sheppo 	int	idx;
23391991Sheppo 	vcc_t 	*vccp;
23401991Sheppo 	int	rv;
23411991Sheppo 
23421991Sheppo 	vccp = (vcc_t *)cb_argp;
23431991Sheppo 	ASSERT(vccp);
23441991Sheppo 
23451991Sheppo 	if (resp == NULL) {
23461991Sheppo 		return (MDEG_FAILURE);
23471991Sheppo 	}
23481991Sheppo 
23491991Sheppo 	/* added port */
23501991Sheppo 	D1("vcc_mdeg_cb: added %d port(s)\n", resp->added.nelem);
23511991Sheppo 
23521991Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
23531991Sheppo 		rv = i_vcc_md_add_port(resp->added.mdp,
23541991Sheppo 		    resp->added.mdep[idx], vccp);
23551991Sheppo 
23561991Sheppo 		if (rv !=  MDEG_SUCCESS) {
23571991Sheppo 			return (rv);
23581991Sheppo 		}
23591991Sheppo 	}
23601991Sheppo 
23611991Sheppo 	/* removed port */
23621991Sheppo 	D1("vcc_mdeg_cb: removed %d port(s)\n", resp->removed.nelem);
23631991Sheppo 
23641991Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
23651991Sheppo 		rv = i_vcc_md_remove_port(resp->removed.mdp,
23661991Sheppo 		    resp->removed.mdep[idx], vccp);
23671991Sheppo 
23681991Sheppo 		if (rv !=  MDEG_SUCCESS) {
23691991Sheppo 			return (rv);
23701991Sheppo 		}
2371*3151Ssg70180 
23721991Sheppo 	}
23731991Sheppo 
23741991Sheppo 	/*
23751991Sheppo 	 * XXX - Currently no support for updating already active
23761991Sheppo 	 * ports. So, ignore the match_curr and match_prev arrays
23771991Sheppo 	 * for now.
23781991Sheppo 	 */
23791991Sheppo 
23801991Sheppo 	return (MDEG_SUCCESS);
23811991Sheppo }
23821991Sheppo 
23831991Sheppo 
23841991Sheppo /* cb_chpoll */
23851991Sheppo static int
23861991Sheppo vcc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
23871991Sheppo     struct pollhead **phpp)
23881991Sheppo {
23891991Sheppo 	int	    instance;
23901991Sheppo 	minor_t	    minor;
23911991Sheppo 	uint_t	    portno;
23921991Sheppo 	vcc_t	    *vccp;
23931991Sheppo 	vcc_port_t  *vport;
23941991Sheppo 
23951991Sheppo 	minor = getminor(dev);
23961991Sheppo 
23971991Sheppo 	instance = VCCINST(minor);
23981991Sheppo 
23991991Sheppo 	vccp = ddi_get_soft_state(vcc_ssp, instance);
24001991Sheppo 	if (vccp == NULL) {
24011991Sheppo 		return (ENXIO);
24021991Sheppo 	}
24031991Sheppo 
24041991Sheppo 	portno = VCCPORT(vccp, minor);
24051991Sheppo 
24061991Sheppo 	vport = &(vccp->port[portno]);
24071991Sheppo 
24081991Sheppo 	D1("vcc_chpoll: virtual-console-concentrator@%d events 0x%x\n",
24091991Sheppo 	    portno, events);
24101991Sheppo 
24111991Sheppo 	*reventsp = 0;
24121991Sheppo 
24131991Sheppo 	if (portno != VCC_CONTROL_PORT) {
24141991Sheppo 		return (ENXIO);
24151991Sheppo 	}
24161991Sheppo 
24171991Sheppo 	/* poll for config change */
24181991Sheppo 	if (vport->pollevent) {
24191991Sheppo 		*reventsp |= (events & POLLIN);
24201991Sheppo 	}
24211991Sheppo 
24221991Sheppo 	if (((*reventsp) == 0) && (!anyyet)) {
24231991Sheppo 		*phpp = &vport->poll;
24241991Sheppo 		if (events & POLLIN) {
24251991Sheppo 			mutex_enter(&vport->lock);
24261991Sheppo 			vport->pollflag |= VCC_POLL_CONFIG;
24271991Sheppo 			mutex_exit(&vport->lock);
24281991Sheppo 		} else {
24291991Sheppo 			return (ENXIO);
24301991Sheppo 		}
24311991Sheppo 	}
24321991Sheppo 
24331991Sheppo 	D1("vcc_chpoll: virtual-console-concentrator@%d:%d ev=0x%x, "
24341991Sheppo 	    "rev=0x%x pev=0x%x, flag=0x%x\n",
24351991Sheppo 	    instance, portno, events, (*reventsp),
24361991Sheppo 	    vport->pollevent, vport->pollflag);
24371991Sheppo 
24381991Sheppo 
24391991Sheppo 	return (0);
24401991Sheppo }
2441