xref: /titanic_53/usr/src/uts/sun4v/io/vldc.c (revision cb112a141f667f84bf442a77589d1705a2336dbe)
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 /*
231ae08745Sheppo  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241ae08745Sheppo  * Use is subject to license terms.
251ae08745Sheppo  */
261ae08745Sheppo 
271ae08745Sheppo #pragma ident	"%Z%%M%	%I%	%E% SMI"
281ae08745Sheppo 
291ae08745Sheppo #include <sys/types.h>
301ae08745Sheppo #include <sys/file.h>
311ae08745Sheppo #include <sys/errno.h>
321ae08745Sheppo #include <sys/uio.h>
331ae08745Sheppo #include <sys/open.h>
341ae08745Sheppo #include <sys/cred.h>
351ae08745Sheppo #include <sys/kmem.h>
361ae08745Sheppo #include <sys/conf.h>
371ae08745Sheppo #include <sys/cmn_err.h>
381ae08745Sheppo #include <sys/ksynch.h>
391ae08745Sheppo #include <sys/modctl.h>
401ae08745Sheppo #include <sys/stat.h>			/* needed for S_IFBLK and S_IFCHR */
411ae08745Sheppo #include <sys/debug.h>
421ae08745Sheppo #include <sys/sysmacros.h>
431ae08745Sheppo #include <sys/types.h>
441ae08745Sheppo #include <sys/cred.h>
451ae08745Sheppo #include <sys/promif.h>
461ae08745Sheppo #include <sys/ddi.h>
471ae08745Sheppo #include <sys/sunddi.h>
481ae08745Sheppo #include <sys/cyclic.h>
491ae08745Sheppo #include <sys/note.h>
501ae08745Sheppo #include <sys/mach_descrip.h>
511ae08745Sheppo #include <sys/mdeg.h>
521ae08745Sheppo #include <sys/ldc.h>
531ae08745Sheppo #include <sys/vldc_impl.h>
541ae08745Sheppo 
551ae08745Sheppo /*
561ae08745Sheppo  * Function prototypes.
571ae08745Sheppo  */
581ae08745Sheppo 
591ae08745Sheppo /* DDI entrypoints */
601ae08745Sheppo static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
611ae08745Sheppo static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
621ae08745Sheppo static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
631ae08745Sheppo static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred);
641ae08745Sheppo static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
651ae08745Sheppo     cred_t *credp, int *rvalp);
661ae08745Sheppo static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp);
671ae08745Sheppo static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp);
681ae08745Sheppo static int vldc_chpoll(dev_t dev, short events, int anyyet,
691ae08745Sheppo     short *reventsp, struct pollhead **phpp);
701ae08745Sheppo 
711ae08745Sheppo /* Internal functions */
721ae08745Sheppo static uint_t i_vldc_cb(uint64_t event, caddr_t arg);
731ae08745Sheppo static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
741ae08745Sheppo static int i_vldc_mdeg_register(vldc_t *vldcp);
751ae08745Sheppo static int i_vldc_mdeg_unregister(vldc_t *vldcp);
761ae08745Sheppo static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node);
771ae08745Sheppo static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno);
781ae08745Sheppo static int i_vldc_close_port(vldc_t *vldcp, uint_t portno);
791ae08745Sheppo 
801ae08745Sheppo /* soft state structure */
811ae08745Sheppo static void *vldc_ssp;
821ae08745Sheppo 
831ae08745Sheppo /*
841ae08745Sheppo  * Matching criteria passed to the MDEG to register interest
851ae08745Sheppo  * in changes to 'virtual-device-port' nodes identified by their
861ae08745Sheppo  * 'id' property.
871ae08745Sheppo  */
881ae08745Sheppo static md_prop_match_t vport_prop_match[] = {
891ae08745Sheppo 	{ MDET_PROP_VAL,    "id"   },
901ae08745Sheppo 	{ MDET_LIST_END,    NULL    }
911ae08745Sheppo };
921ae08745Sheppo 
931ae08745Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
941ae08745Sheppo 					vport_prop_match };
951ae08745Sheppo 
961ae08745Sheppo /*
971ae08745Sheppo  * Specification of an MD node passed to the MDEG to filter any
981ae08745Sheppo  * 'virtual-device-port' nodes that do not belong to the specified
991ae08745Sheppo  * node. This template is copied for each vldc instance and filled
1001ae08745Sheppo  * in with the appropriate 'name' and 'cfg-handle' values before
1011ae08745Sheppo  * being passed to the MDEG.
1021ae08745Sheppo  */
1031ae08745Sheppo static mdeg_prop_spec_t vldc_prop_template[] = {
1041ae08745Sheppo 	{ MDET_PROP_STR,    "name",		NULL	},
1051ae08745Sheppo 	{ MDET_PROP_VAL,    "cfg-handle",	NULL    },
1061ae08745Sheppo 	{ MDET_LIST_END,    NULL,		NULL    }
1071ae08745Sheppo };
1081ae08745Sheppo 
1091ae08745Sheppo #define	VLDC_MDEG_PROP_NAME(specp)		((specp)[0].ps_str)
1101ae08745Sheppo #define	VLDC_SET_MDEG_PROP_NAME(specp, name)	((specp)[0].ps_str = (name))
1111ae08745Sheppo #define	VLDC_SET_MDEG_PROP_INST(specp, inst)	((specp)[1].ps_val = (inst))
1121ae08745Sheppo 
1131ae08745Sheppo 
1141ae08745Sheppo static struct cb_ops vldc_cb_ops = {
1151ae08745Sheppo 	vldc_open,	/* open */
1161ae08745Sheppo 	vldc_close,	/* close */
1171ae08745Sheppo 	nodev,		/* strategy */
1181ae08745Sheppo 	nodev,		/* print */
1191ae08745Sheppo 	nodev,		/* dump */
1201ae08745Sheppo 	vldc_read,	/* read */
1211ae08745Sheppo 	vldc_write,	/* write */
1221ae08745Sheppo 	vldc_ioctl,	/* ioctl */
1231ae08745Sheppo 	nodev,		/* devmap */
1241ae08745Sheppo 	nodev,		/* mmap */
1251ae08745Sheppo 	ddi_segmap,	/* segmap */
1261ae08745Sheppo 	vldc_chpoll,	/* chpoll */
1271ae08745Sheppo 	ddi_prop_op,	/* prop_op */
1281ae08745Sheppo 	NULL,		/* stream */
1291ae08745Sheppo 	D_NEW | D_MP	/* flag */
1301ae08745Sheppo };
1311ae08745Sheppo 
1321ae08745Sheppo static struct dev_ops vldc_ops = {
1331ae08745Sheppo 	DEVO_REV,		/* rev */
1341ae08745Sheppo 	0,			/* ref count */
1351ae08745Sheppo 	ddi_getinfo_1to1,	/* getinfo */
1361ae08745Sheppo 	nulldev,		/* identify */
1371ae08745Sheppo 	nulldev,		/* probe */
1381ae08745Sheppo 	vldc_attach,		/* attach */
1391ae08745Sheppo 	vldc_detach,		/* detach */
1401ae08745Sheppo 	nodev,			/* reset */
1411ae08745Sheppo 	&vldc_cb_ops,		/* cb_ops */
1421ae08745Sheppo 	(struct bus_ops *)NULL	/* bus_ops */
1431ae08745Sheppo };
1441ae08745Sheppo 
1451ae08745Sheppo extern struct mod_ops mod_driverops;
1461ae08745Sheppo 
1471ae08745Sheppo static struct modldrv md = {
1481ae08745Sheppo 	&mod_driverops, 			/* Type - it is a driver */
1491ae08745Sheppo 	"sun4v Virtual LDC Driver %I%",	/* Name of the module */
1501ae08745Sheppo 	&vldc_ops,				/* driver specific ops */
1511ae08745Sheppo };
1521ae08745Sheppo 
1531ae08745Sheppo static struct modlinkage ml = {
1541ae08745Sheppo 	MODREV_1,
1551ae08745Sheppo 	&md,
1561ae08745Sheppo 	NULL
1571ae08745Sheppo };
1581ae08745Sheppo 
1591ae08745Sheppo /* maximum MTU and cookie size tunables */
1601ae08745Sheppo uint32_t vldc_max_mtu = VLDC_MAX_MTU;
1611ae08745Sheppo uint64_t vldc_max_cookie = VLDC_MAX_COOKIE;
1621ae08745Sheppo 
163*cb112a14Slm66018 /*
164*cb112a14Slm66018  * when calls to LDC return EWOULDBLOCK or EAGAIN the operation is retried
165*cb112a14Slm66018  * up to 'vldc_retries' times with a wait of 'vldc_delay' microseconds
166*cb112a14Slm66018  * between each retry.
167*cb112a14Slm66018  */
168*cb112a14Slm66018 static clock_t	vldc_delay = 100;
169*cb112a14Slm66018 static int	vldc_retries = 3;
1701ae08745Sheppo 
1711ae08745Sheppo #ifdef DEBUG
1721ae08745Sheppo 
1731ae08745Sheppo /*
1741ae08745Sheppo  * Print debug messages
1751ae08745Sheppo  *
1761ae08745Sheppo  * set vldcdbg to 0x7 to enable all messages
1771ae08745Sheppo  *
1781ae08745Sheppo  * 0x4 - Warnings
1791ae08745Sheppo  * 0x2 - All debug messages (most verbose)
1801ae08745Sheppo  * 0x1 - Minimal debug messages
1811ae08745Sheppo  */
1821ae08745Sheppo 
1831ae08745Sheppo int vldcdbg = 0x0;
1841ae08745Sheppo 
1851ae08745Sheppo static void
1861ae08745Sheppo vldcdebug(const char *fmt, ...)
1871ae08745Sheppo {
1881ae08745Sheppo 	char buf[512];
1891ae08745Sheppo 	va_list ap;
1901ae08745Sheppo 
1911ae08745Sheppo 	va_start(ap, fmt);
1921ae08745Sheppo 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
1931ae08745Sheppo 	va_end(ap);
1941ae08745Sheppo 
1951ae08745Sheppo 	cmn_err(CE_CONT, "?%s", buf);
1961ae08745Sheppo }
1971ae08745Sheppo 
1981ae08745Sheppo #define	D1	if (vldcdbg & 0x01) vldcdebug
1991ae08745Sheppo #define	D2	if (vldcdbg & 0x02) vldcdebug
2001ae08745Sheppo #define	DWARN	if (vldcdbg & 0x04) vldcdebug
2011ae08745Sheppo 
2021ae08745Sheppo #else /* not DEBUG */
2031ae08745Sheppo 
2041ae08745Sheppo #define	D1	if (0) printf
2051ae08745Sheppo #define	D2	if (0) printf
2061ae08745Sheppo #define	DWARN	if (0) printf
2071ae08745Sheppo 
2081ae08745Sheppo #endif /* not DEBUG */
2091ae08745Sheppo 
2101ae08745Sheppo 
2111ae08745Sheppo /* _init(9E): initialize the loadable module */
2121ae08745Sheppo int
2131ae08745Sheppo _init(void)
2141ae08745Sheppo {
2151ae08745Sheppo 	int error;
2161ae08745Sheppo 
2171ae08745Sheppo 	/* init the soft state structure */
2181ae08745Sheppo 	error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1);
2191ae08745Sheppo 	if (error != 0) {
2201ae08745Sheppo 		return (error);
2211ae08745Sheppo 	}
2221ae08745Sheppo 
2231ae08745Sheppo 	/* Link the driver into the system */
2241ae08745Sheppo 	error = mod_install(&ml);
2251ae08745Sheppo 
2261ae08745Sheppo 	return (error);
2271ae08745Sheppo }
2281ae08745Sheppo 
2291ae08745Sheppo /* _info(9E): return information about the loadable module */
2301ae08745Sheppo int
2311ae08745Sheppo _info(struct modinfo *modinfop)
2321ae08745Sheppo {
2331ae08745Sheppo 	/* Report status of the dynamically loadable driver module */
2341ae08745Sheppo 	return (mod_info(&ml, modinfop));
2351ae08745Sheppo }
2361ae08745Sheppo 
2371ae08745Sheppo /* _fini(9E): prepare the module for unloading. */
2381ae08745Sheppo int
2391ae08745Sheppo _fini(void)
2401ae08745Sheppo {
2411ae08745Sheppo 	int error;
2421ae08745Sheppo 
2431ae08745Sheppo 	/* Unlink the driver module from the system */
2441ae08745Sheppo 	if ((error = mod_remove(&ml)) == 0) {
2451ae08745Sheppo 		/*
2461ae08745Sheppo 		 * We have successfully "removed" the driver.
2471ae08745Sheppo 		 * destroy soft state
2481ae08745Sheppo 		 */
2491ae08745Sheppo 		ddi_soft_state_fini(&vldc_ssp);
2501ae08745Sheppo 	}
2511ae08745Sheppo 
2521ae08745Sheppo 	return (error);
2531ae08745Sheppo }
2541ae08745Sheppo 
2551ae08745Sheppo /* ldc callback */
2561ae08745Sheppo static uint_t
2571ae08745Sheppo i_vldc_cb(uint64_t event, caddr_t arg)
2581ae08745Sheppo {
259a8ea4edeSnarayan 	int 		rv;
2601ae08745Sheppo 	vldc_port_t	*vport = (vldc_port_t *)arg;
261a8ea4edeSnarayan 	ldc_status_t	old_status;
2621ae08745Sheppo 	short		pollevents = 0;
2631ae08745Sheppo 
264*cb112a14Slm66018 	ASSERT(vport != NULL);
265*cb112a14Slm66018 	ASSERT(vport->minorp != NULL);
266*cb112a14Slm66018 
2673af08d82Slm66018 	D1("i_vldc_cb: vldc@%d:%d callback invoked, channel=0x%lx, "
2683af08d82Slm66018 	    "event=0x%lx\n", vport->inst, vport->number, vport->ldc_id, event);
2691ae08745Sheppo 
270*cb112a14Slm66018 	/* ensure the port can't be destroyed while we are handling the cb */
271*cb112a14Slm66018 	mutex_enter(&vport->minorp->lock);
272*cb112a14Slm66018 
273a8ea4edeSnarayan 	old_status = vport->ldc_status;
274a8ea4edeSnarayan 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
275a8ea4edeSnarayan 	if (rv != 0) {
276a8ea4edeSnarayan 		DWARN("i_vldc_cb: vldc@%d:%d could not get ldc status, "
277a8ea4edeSnarayan 		    "rv=%d\n", vport->inst, vport->number, rv);
278*cb112a14Slm66018 		mutex_exit(&vport->minorp->lock);
279a8ea4edeSnarayan 		return (LDC_SUCCESS);
280a8ea4edeSnarayan 	}
281a8ea4edeSnarayan 
2821ae08745Sheppo 	if (event & LDC_EVT_UP) {
2831ae08745Sheppo 		pollevents |= POLLOUT;
2841ae08745Sheppo 		vport->hanged_up = B_FALSE;
2851ae08745Sheppo 
2861ae08745Sheppo 	} else if (event & LDC_EVT_RESET) {
2873af08d82Slm66018 		/*
288a8ea4edeSnarayan 		 * Mark the port in reset, if it is not CLOSED and
289a8ea4edeSnarayan 		 * the channel was previously in LDC_UP state. This
290a8ea4edeSnarayan 		 * implies that the port cannot be used until it has
291a8ea4edeSnarayan 		 * been closed and reopened.
2923af08d82Slm66018 		 */
293a8ea4edeSnarayan 		if (vport->status != VLDC_PORT_CLOSED && old_status == LDC_UP) {
2943af08d82Slm66018 			vport->status = VLDC_PORT_RESET;
295a8ea4edeSnarayan 			vport->hanged_up = B_TRUE;
296a8ea4edeSnarayan 			pollevents = POLLHUP;
297a8ea4edeSnarayan 		} else {
298a8ea4edeSnarayan 			rv = ldc_up(vport->ldc_handle);
299a8ea4edeSnarayan 			if (rv) {
300a8ea4edeSnarayan 				DWARN("i_vldc_cb: vldc@%d:%d cannot bring "
301a8ea4edeSnarayan 				    "channel UP rv=%d\n", vport->inst,
302a8ea4edeSnarayan 				    vport->number, rv);
303*cb112a14Slm66018 				mutex_exit(&vport->minorp->lock);
3043af08d82Slm66018 				return (LDC_SUCCESS);
305a8ea4edeSnarayan 			}
306a8ea4edeSnarayan 			rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
307a8ea4edeSnarayan 			if (rv != 0) {
308a8ea4edeSnarayan 				DWARN("i_vldc_cb: vldc@%d:%d could not get "
309a8ea4edeSnarayan 				    "ldc status, rv=%d\n", vport->inst,
310a8ea4edeSnarayan 				    vport->number, rv);
311*cb112a14Slm66018 				mutex_exit(&vport->minorp->lock);
312a8ea4edeSnarayan 				return (LDC_SUCCESS);
313a8ea4edeSnarayan 			}
314a8ea4edeSnarayan 			if (vport->ldc_status == LDC_UP) {
315a8ea4edeSnarayan 				pollevents |= POLLOUT;
316a8ea4edeSnarayan 				vport->hanged_up = B_FALSE;
317a8ea4edeSnarayan 			}
318a8ea4edeSnarayan 		}
3193af08d82Slm66018 
3203af08d82Slm66018 	} else if (event & LDC_EVT_DOWN) {
3213af08d82Slm66018 		/*
322a8ea4edeSnarayan 		 * The other side went away - mark port in RESET state
3233af08d82Slm66018 		 */
324a8ea4edeSnarayan 		vport->status = VLDC_PORT_RESET;
3253af08d82Slm66018 		vport->hanged_up = B_TRUE;
3263af08d82Slm66018 		pollevents = POLLHUP;
3271ae08745Sheppo 	}
3281ae08745Sheppo 
3291ae08745Sheppo 	if (event & LDC_EVT_READ)
3301ae08745Sheppo 		pollevents |= POLLIN;
3311ae08745Sheppo 
332*cb112a14Slm66018 	mutex_exit(&vport->minorp->lock);
333*cb112a14Slm66018 
3341ae08745Sheppo 	if (pollevents != 0) {
3351ae08745Sheppo 		D1("i_vldc_cb: port@%d pollwakeup=0x%x\n",
3361ae08745Sheppo 		    vport->number, pollevents);
3371ae08745Sheppo 		pollwakeup(&vport->poll, pollevents);
3381ae08745Sheppo 	}
3391ae08745Sheppo 
3401ae08745Sheppo 	return (LDC_SUCCESS);
3411ae08745Sheppo }
3421ae08745Sheppo 
3431ae08745Sheppo /* mdeg callback */
3441ae08745Sheppo static int
3451ae08745Sheppo i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
3461ae08745Sheppo {
3471ae08745Sheppo 	vldc_t		*vldcp;
3481ae08745Sheppo 	int		idx;
3491ae08745Sheppo 	uint64_t	portno;
3501ae08745Sheppo 	int		rv;
3511ae08745Sheppo 	md_t		*mdp;
3521ae08745Sheppo 	mde_cookie_t	node;
3531ae08745Sheppo 
3541ae08745Sheppo 	if (resp == NULL) {
3551ae08745Sheppo 		D1("i_vldc_mdeg_cb: no result returned\n");
3561ae08745Sheppo 		return (MDEG_FAILURE);
3571ae08745Sheppo 	}
3581ae08745Sheppo 
3591ae08745Sheppo 	vldcp = (vldc_t *)cb_argp;
3601ae08745Sheppo 
3611ae08745Sheppo 	mutex_enter(&vldcp->lock);
3621ae08745Sheppo 	if (vldcp->detaching == B_TRUE) {
3631ae08745Sheppo 		D1("i_vldc_mdeg_cb: detach in progress\n");
3641ae08745Sheppo 		mutex_exit(&vldcp->lock);
3651ae08745Sheppo 		return (MDEG_FAILURE);
3661ae08745Sheppo 	}
3671ae08745Sheppo 
3681ae08745Sheppo 	D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n",
3691ae08745Sheppo 	    resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
3701ae08745Sheppo 
3711ae08745Sheppo 	/* process added ports */
3721ae08745Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
3731ae08745Sheppo 		mdp = resp->added.mdp;
3741ae08745Sheppo 		node = resp->added.mdep[idx];
3751ae08745Sheppo 
3761ae08745Sheppo 		D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node);
3771ae08745Sheppo 
3781ae08745Sheppo 		/* attempt to add a port */
3791ae08745Sheppo 		if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) {
3801ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, "
3811ae08745Sheppo 			    "err = %d", rv);
3821ae08745Sheppo 		}
3831ae08745Sheppo 	}
3841ae08745Sheppo 
3851ae08745Sheppo 	/* process removed ports */
3861ae08745Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
3871ae08745Sheppo 		mdp = resp->removed.mdp;
3881ae08745Sheppo 		node = resp->removed.mdep[idx];
3891ae08745Sheppo 
3901ae08745Sheppo 		D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node);
3911ae08745Sheppo 
3921ae08745Sheppo 		/* read in the port's id property */
3931ae08745Sheppo 		if (md_get_prop_val(mdp, node, "id", &portno)) {
3941ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of "
3951ae08745Sheppo 			    "removed list has no 'id' property", node);
3961ae08745Sheppo 			continue;
3971ae08745Sheppo 		}
3981ae08745Sheppo 
3991ae08745Sheppo 		/* attempt to remove a port */
4001ae08745Sheppo 		if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) {
4011ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove "
4021ae08745Sheppo 			    "port %lu, err %d", portno, rv);
4031ae08745Sheppo 		}
4041ae08745Sheppo 	}
4051ae08745Sheppo 
4061ae08745Sheppo 	/*
4071ae08745Sheppo 	 * Currently no support for updating already active ports. So, ignore
4081ae08745Sheppo 	 * the match_curr and match_prev arrays for now.
4091ae08745Sheppo 	 */
4101ae08745Sheppo 
4111ae08745Sheppo 	mutex_exit(&vldcp->lock);
4121ae08745Sheppo 
4131ae08745Sheppo 	return (MDEG_SUCCESS);
4141ae08745Sheppo }
4151ae08745Sheppo 
4161ae08745Sheppo /* register callback to mdeg */
4171ae08745Sheppo static int
4181ae08745Sheppo i_vldc_mdeg_register(vldc_t *vldcp)
4191ae08745Sheppo {
4201ae08745Sheppo 	mdeg_prop_spec_t *pspecp;
4211ae08745Sheppo 	mdeg_node_spec_t *inst_specp;
4221ae08745Sheppo 	mdeg_handle_t	mdeg_hdl;
4231ae08745Sheppo 	size_t		templatesz;
4241ae08745Sheppo 	int		inst;
4251ae08745Sheppo 	char		*name;
4261ae08745Sheppo 	size_t		namesz;
4271ae08745Sheppo 	char		*nameprop;
4281ae08745Sheppo 	int		rv;
4291ae08745Sheppo 
4301ae08745Sheppo 	/* get the unique vldc instance assigned by the LDom manager */
4311ae08745Sheppo 	inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip,
4321ae08745Sheppo 	    DDI_PROP_DONTPASS, "reg", -1);
4331ae08745Sheppo 	if (inst == -1) {
4341ae08745Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'reg' property",
4351ae08745Sheppo 		    ddi_get_instance(vldcp->dip));
4361ae08745Sheppo 		return (DDI_FAILURE);
4371ae08745Sheppo 	}
4381ae08745Sheppo 
4391ae08745Sheppo 	/* get the name of the vldc instance */
4401ae08745Sheppo 	rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip,
4411ae08745Sheppo 	    DDI_PROP_DONTPASS, "name", &nameprop);
4421ae08745Sheppo 	if (rv != DDI_PROP_SUCCESS) {
4431ae08745Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'name' property",
4441ae08745Sheppo 		    ddi_get_instance(vldcp->dip));
4451ae08745Sheppo 		return (DDI_FAILURE);
4461ae08745Sheppo 	}
4471ae08745Sheppo 
4481ae08745Sheppo 	D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst);
4491ae08745Sheppo 
4501ae08745Sheppo 	/*
4511ae08745Sheppo 	 * Allocate and initialize a per-instance copy
4521ae08745Sheppo 	 * of the global property spec array that will
4531ae08745Sheppo 	 * uniquely identify this vldc instance.
4541ae08745Sheppo 	 */
4551ae08745Sheppo 	templatesz = sizeof (vldc_prop_template);
4561ae08745Sheppo 	pspecp = kmem_alloc(templatesz, KM_SLEEP);
4571ae08745Sheppo 
4581ae08745Sheppo 	bcopy(vldc_prop_template, pspecp, templatesz);
4591ae08745Sheppo 
4601ae08745Sheppo 	/* copy in the name property */
4611ae08745Sheppo 	namesz = strlen(nameprop) + 1;
4621ae08745Sheppo 	name = kmem_alloc(namesz, KM_SLEEP);
4631ae08745Sheppo 
4641ae08745Sheppo 	bcopy(nameprop, name, namesz);
4651ae08745Sheppo 	VLDC_SET_MDEG_PROP_NAME(pspecp, name);
466d10e4ef2Snarayan 	ddi_prop_free(nameprop);
4671ae08745Sheppo 
4681ae08745Sheppo 	/* copy in the instance property */
4691ae08745Sheppo 	VLDC_SET_MDEG_PROP_INST(pspecp, inst);
4701ae08745Sheppo 
4711ae08745Sheppo 	/* initialize the complete prop spec structure */
4721ae08745Sheppo 	inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
4731ae08745Sheppo 	inst_specp->namep = "virtual-device";
4741ae08745Sheppo 	inst_specp->specp = pspecp;
4751ae08745Sheppo 
4761ae08745Sheppo 	/* perform the registration */
4771ae08745Sheppo 	rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb,
4781ae08745Sheppo 	    vldcp, &mdeg_hdl);
4791ae08745Sheppo 
4801ae08745Sheppo 	if (rv != MDEG_SUCCESS) {
4811ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register "
4821ae08745Sheppo 		    "failed, err = %d", rv);
4831ae08745Sheppo 		kmem_free(name, namesz);
4841ae08745Sheppo 		kmem_free(pspecp, templatesz);
4851ae08745Sheppo 		kmem_free(inst_specp, sizeof (mdeg_node_spec_t));
4861ae08745Sheppo 		return (DDI_FAILURE);
4871ae08745Sheppo 	}
4881ae08745Sheppo 
4891ae08745Sheppo 	/* save off data that will be needed later */
4901ae08745Sheppo 	vldcp->inst_spec = inst_specp;
4911ae08745Sheppo 	vldcp->mdeg_hdl = mdeg_hdl;
4921ae08745Sheppo 
4931ae08745Sheppo 	return (DDI_SUCCESS);
4941ae08745Sheppo }
4951ae08745Sheppo 
4961ae08745Sheppo /* unregister callback from mdeg */
4971ae08745Sheppo static int
4981ae08745Sheppo i_vldc_mdeg_unregister(vldc_t *vldcp)
4991ae08745Sheppo {
5001ae08745Sheppo 	char	*name;
5011ae08745Sheppo 	int	rv;
5021ae08745Sheppo 
5031ae08745Sheppo 	D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl);
5041ae08745Sheppo 
5051ae08745Sheppo 	rv = mdeg_unregister(vldcp->mdeg_hdl);
5061ae08745Sheppo 	if (rv != MDEG_SUCCESS) {
5071ae08745Sheppo 		return (rv);
5081ae08745Sheppo 	}
5091ae08745Sheppo 
5101ae08745Sheppo 	/*
5111ae08745Sheppo 	 * Clean up cached MDEG data
5121ae08745Sheppo 	 */
5131ae08745Sheppo 	name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp);
5141ae08745Sheppo 	if (name != NULL) {
5151ae08745Sheppo 		kmem_free(name, strlen(name) + 1);
5161ae08745Sheppo 	}
5171ae08745Sheppo 	kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template));
5181ae08745Sheppo 	vldcp->inst_spec->specp = NULL;
5191ae08745Sheppo 
5201ae08745Sheppo 	kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t));
5211ae08745Sheppo 	vldcp->inst_spec = NULL;
5221ae08745Sheppo 
5231ae08745Sheppo 	return (MDEG_SUCCESS);
5241ae08745Sheppo }
5251ae08745Sheppo 
5261ae08745Sheppo static int
5271ae08745Sheppo i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
5281ae08745Sheppo {
5291ae08745Sheppo 	int num_nodes, nchan;
5301ae08745Sheppo 	size_t listsz;
5311ae08745Sheppo 	mde_cookie_t *listp;
5321ae08745Sheppo 
5331ae08745Sheppo 	/*
5341ae08745Sheppo 	 * Find the channel-endpoint node(s) (which should be under this
5351ae08745Sheppo 	 * port node) which contain the channel id(s).
5361ae08745Sheppo 	 */
5371ae08745Sheppo 	if ((num_nodes = md_node_count(mdp)) <= 0) {
5381ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of "
5391ae08745Sheppo 		    "channel-endpoint nodes found (%d)", num_nodes);
5401ae08745Sheppo 		return (-1);
5411ae08745Sheppo 	}
5421ae08745Sheppo 
5431ae08745Sheppo 	/* allocate space for node list */
5441ae08745Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
5451ae08745Sheppo 	listp = kmem_alloc(listsz, KM_SLEEP);
5461ae08745Sheppo 
5471ae08745Sheppo 	nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
5481ae08745Sheppo 	    md_find_name(mdp, "fwd"), listp);
5491ae08745Sheppo 
5501ae08745Sheppo 	if (nchan <= 0) {
5511ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint"
5521ae08745Sheppo 		    " nodes found");
5531ae08745Sheppo 		kmem_free(listp, listsz);
5541ae08745Sheppo 		return (-1);
5551ae08745Sheppo 	}
5561ae08745Sheppo 
5571ae08745Sheppo 	D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan);
5581ae08745Sheppo 
5591ae08745Sheppo 	/* use property from first node found */
5601ae08745Sheppo 	if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
5611ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint "
5621ae08745Sheppo 		    "has no 'id' property");
5631ae08745Sheppo 		kmem_free(listp, listsz);
5641ae08745Sheppo 		return (-1);
5651ae08745Sheppo 	}
5661ae08745Sheppo 
5671ae08745Sheppo 	kmem_free(listp, listsz);
5681ae08745Sheppo 
5691ae08745Sheppo 	return (0);
5701ae08745Sheppo }
5711ae08745Sheppo 
5721ae08745Sheppo /* add a vldc port */
5731ae08745Sheppo static int
5741ae08745Sheppo i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node)
5751ae08745Sheppo {
5761ae08745Sheppo 	vldc_port_t	*vport;
5771ae08745Sheppo 	char		*sname;
5781ae08745Sheppo 	uint64_t	portno;
5791ae08745Sheppo 	int		vldc_inst;
5801ae08745Sheppo 	minor_t		minor;
5811ae08745Sheppo 	int		minor_idx;
5821ae08745Sheppo 	boolean_t	new_minor;
5831ae08745Sheppo 	int		rv;
5841ae08745Sheppo 
585*cb112a14Slm66018 	ASSERT(MUTEX_HELD(&vldcp->lock));
586*cb112a14Slm66018 
5871ae08745Sheppo 	/* read in the port's id property */
5881ae08745Sheppo 	if (md_get_prop_val(mdp, node, "id", &portno)) {
5891ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added "
5901ae08745Sheppo 		    "list has no 'id' property", node);
5911ae08745Sheppo 		return (MDEG_FAILURE);
5921ae08745Sheppo 	}
5931ae08745Sheppo 
5941ae08745Sheppo 	if (portno >= VLDC_MAX_PORTS) {
5951ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) "
5961ae08745Sheppo 		    "larger than maximum supported number of ports", portno);
5971ae08745Sheppo 		return (MDEG_FAILURE);
5981ae08745Sheppo 	}
5991ae08745Sheppo 
6001ae08745Sheppo 	vport = &(vldcp->port[portno]);
6011ae08745Sheppo 
6021ae08745Sheppo 	if (vport->minorp != NULL) {
6031ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
6041ae08745Sheppo 		    " which is already bound", portno);
6051ae08745Sheppo 		return (MDEG_FAILURE);
6061ae08745Sheppo 	}
6071ae08745Sheppo 
6081ae08745Sheppo 	vport->number = portno;
6091ae08745Sheppo 
6101ae08745Sheppo 	/* get all channels for this device (currently only one) */
6111ae08745Sheppo 	if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) {
6121ae08745Sheppo 		return (MDEG_FAILURE);
6131ae08745Sheppo 	}
6141ae08745Sheppo 
6151ae08745Sheppo 	/* set the default MTU */
616d1a9c4c1Sjm22469 	vport->mtu = VLDC_DEFAULT_MTU;
6171ae08745Sheppo 
6181ae08745Sheppo 	/* get the service being exported by this port */
6191ae08745Sheppo 	if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) {
6201ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no "
6211ae08745Sheppo 		    "'vldc-svc-name' property");
6221ae08745Sheppo 		return (MDEG_FAILURE);
6231ae08745Sheppo 	}
6241ae08745Sheppo 
6251ae08745Sheppo 	/* minor number look up */
6261ae08745Sheppo 	for (minor_idx = 0; minor_idx < vldcp->minors_assigned;
6271ae08745Sheppo 	    minor_idx++) {
6281ae08745Sheppo 		if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) {
6291ae08745Sheppo 			/* found previously assigned minor number */
6301ae08745Sheppo 			break;
6311ae08745Sheppo 		}
6321ae08745Sheppo 	}
6331ae08745Sheppo 
6341ae08745Sheppo 	new_minor = B_FALSE;
6351ae08745Sheppo 	if (minor_idx == vldcp->minors_assigned) {
6361ae08745Sheppo 		/* end of lookup - assign new minor number */
6371ae08745Sheppo 		if (vldcp->minors_assigned == VLDC_MAX_MINORS) {
6381ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor "
6391ae08745Sheppo 			    "nodes (%d)", minor_idx);
6401ae08745Sheppo 			return (MDEG_FAILURE);
6411ae08745Sheppo 		}
6421ae08745Sheppo 
6431ae08745Sheppo 		(void) strlcpy(vldcp->minor_tbl[minor_idx].sname,
6441ae08745Sheppo 		    sname, MAXPATHLEN);
6451ae08745Sheppo 
6461ae08745Sheppo 		vldcp->minors_assigned++;
6471ae08745Sheppo 		new_minor = B_TRUE;
6481ae08745Sheppo 	}
6491ae08745Sheppo 
650*cb112a14Slm66018 	if (vldcp->minor_tbl[minor_idx].portno != VLDC_INVALID_PORTNO) {
651*cb112a14Slm66018 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
652*cb112a14Slm66018 		    " which has a minor number in use by port (%u)",
653*cb112a14Slm66018 		    portno, vldcp->minor_tbl[minor_idx].portno);
654*cb112a14Slm66018 		return (MDEG_FAILURE);
655*cb112a14Slm66018 	}
6561ae08745Sheppo 
6573af08d82Slm66018 	vldc_inst = ddi_get_instance(vldcp->dip);
6583af08d82Slm66018 
6593af08d82Slm66018 	vport->inst = vldc_inst;
6601ae08745Sheppo 	vport->minorp = &vldcp->minor_tbl[minor_idx];
6611ae08745Sheppo 	vldcp->minor_tbl[minor_idx].portno = portno;
6621ae08745Sheppo 	vldcp->minor_tbl[minor_idx].in_use = 0;
6631ae08745Sheppo 
6643af08d82Slm66018 	D1("i_vldc_add_port: vldc@%d:%d  mtu=%d, ldc=%ld, service=%s\n",
6653af08d82Slm66018 	    vport->inst, vport->number, vport->mtu, vport->ldc_id, sname);
6661ae08745Sheppo 
6671ae08745Sheppo 	/*
6681ae08745Sheppo 	 * Create a minor node. The minor number is
6691ae08745Sheppo 	 * (vldc_inst << VLDC_INST_SHIFT) | minor_idx
6701ae08745Sheppo 	 */
6711ae08745Sheppo 	minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx);
6721ae08745Sheppo 
6731ae08745Sheppo 	rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR,
6741ae08745Sheppo 	    minor, DDI_NT_SERIAL, 0);
6751ae08745Sheppo 
6761ae08745Sheppo 	if (rv != DDI_SUCCESS) {
6771ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor"
6781ae08745Sheppo 		    "node (%u), err = %d", minor, rv);
6791ae08745Sheppo 		vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO;
6801ae08745Sheppo 		if (new_minor) {
6811ae08745Sheppo 			vldcp->minors_assigned--;
6821ae08745Sheppo 		}
6831ae08745Sheppo 		return (MDEG_FAILURE);
6841ae08745Sheppo 	}
6851ae08745Sheppo 
6861ae08745Sheppo 	/*
6871ae08745Sheppo 	 * The port is now bound to a minor node and is initially in the
6881ae08745Sheppo 	 * closed state.
6891ae08745Sheppo 	 */
6901ae08745Sheppo 	vport->status = VLDC_PORT_CLOSED;
6911ae08745Sheppo 
6921ae08745Sheppo 	D1("i_vldc_add_port: port %lu initialized\n", portno);
6931ae08745Sheppo 
6941ae08745Sheppo 	return (MDEG_SUCCESS);
6951ae08745Sheppo }
6961ae08745Sheppo 
6971ae08745Sheppo /* remove a vldc port */
6981ae08745Sheppo static int
6991ae08745Sheppo i_vldc_remove_port(vldc_t *vldcp, uint_t portno)
7001ae08745Sheppo {
7011ae08745Sheppo 	vldc_port_t *vport;
7021ae08745Sheppo 	vldc_minor_t *vminor;
7031ae08745Sheppo 
704*cb112a14Slm66018 	ASSERT(vldcp != NULL);
705*cb112a14Slm66018 	ASSERT(MUTEX_HELD(&vldcp->lock));
706*cb112a14Slm66018 
7071ae08745Sheppo 	vport = &(vldcp->port[portno]);
7081ae08745Sheppo 	vminor = vport->minorp;
7091ae08745Sheppo 	if (vminor == NULL) {
7101ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a "
7111ae08745Sheppo 		    "port (%u) which is not bound", portno);
7121ae08745Sheppo 		return (MDEG_FAILURE);
7131ae08745Sheppo 	}
7141ae08745Sheppo 
7151ae08745Sheppo 	/*
7161ae08745Sheppo 	 * Make sure that all new attempts to open or use the minor node
7171ae08745Sheppo 	 * associated with the port will fail.
7181ae08745Sheppo 	 */
7191ae08745Sheppo 	mutex_enter(&vminor->lock);
7201ae08745Sheppo 	vminor->portno = VLDC_INVALID_PORTNO;
7211ae08745Sheppo 	mutex_exit(&vminor->lock);
7221ae08745Sheppo 
7231ae08745Sheppo 	/* send hangup to anyone polling */
7241ae08745Sheppo 	pollwakeup(&vport->poll, POLLHUP);
7251ae08745Sheppo 
7261ae08745Sheppo 	/* Now wait for all current users of the minor node to finish. */
7271ae08745Sheppo 	mutex_enter(&vminor->lock);
7281ae08745Sheppo 	while (vminor->in_use > 0) {
7291ae08745Sheppo 		cv_wait(&vminor->cv, &vminor->lock);
7301ae08745Sheppo 	}
7311ae08745Sheppo 
7323af08d82Slm66018 	if (vport->status != VLDC_PORT_CLOSED) {
7331ae08745Sheppo 		/* close the port before it is torn down */
7341ae08745Sheppo 		(void) i_vldc_close_port(vldcp, portno);
7351ae08745Sheppo 	}
7361ae08745Sheppo 
7371ae08745Sheppo 	/* remove minor node */
7381ae08745Sheppo 	ddi_remove_minor_node(vldcp->dip, vport->minorp->sname);
7391ae08745Sheppo 	vport->minorp = NULL;
7401ae08745Sheppo 
7411ae08745Sheppo 	mutex_exit(&vminor->lock);
7421ae08745Sheppo 
7431ae08745Sheppo 	D1("i_vldc_remove_port: removed vldc port %u\n", portno);
7441ae08745Sheppo 
7451ae08745Sheppo 	return (MDEG_SUCCESS);
7461ae08745Sheppo }
7471ae08745Sheppo 
748*cb112a14Slm66018 /*
749*cb112a14Slm66018  * Close and destroy the ldc channel associated with the port 'vport'
750*cb112a14Slm66018  *
751*cb112a14Slm66018  * NOTE It may not be possible close and destroy the channel if resources
752*cb112a14Slm66018  *	are still in use so the fucntion may exit before all the teardown
753*cb112a14Slm66018  *	operations are completed and would have to be called again by the
754*cb112a14Slm66018  *	vldc framework.
755*cb112a14Slm66018  *
756*cb112a14Slm66018  *	This function needs to be able to handle the case where it is called
757*cb112a14Slm66018  *	more than once and has to pick up from where it left off.
758*cb112a14Slm66018  */
7591ae08745Sheppo static int
7601ae08745Sheppo i_vldc_ldc_close(vldc_port_t *vport)
7611ae08745Sheppo {
762*cb112a14Slm66018 	int retries = 0;	/* count of number of retries attempted */
763*cb112a14Slm66018 	int err = 0;
7641ae08745Sheppo 
765*cb112a14Slm66018 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
766*cb112a14Slm66018 
767*cb112a14Slm66018 	while ((err = ldc_close(vport->ldc_handle)) == EAGAIN) {
768*cb112a14Slm66018 		drv_usecwait(vldc_delay);
769*cb112a14Slm66018 		if (++retries > vldc_retries)
770*cb112a14Slm66018 			break;
771*cb112a14Slm66018 	}
772*cb112a14Slm66018 	/*
773*cb112a14Slm66018 	 * If ldc_close() succeeded or if the channel was already closed[*]
774*cb112a14Slm66018 	 * (possibly by a previously unsuccessful call to this function)
775*cb112a14Slm66018 	 * we keep going and try to teardown the rest of the LDC state,
776*cb112a14Slm66018 	 * otherwise we bail out.
777*cb112a14Slm66018 	 *
778*cb112a14Slm66018 	 * [*] indicated by ldc_close() returning a value of EFAULT
779*cb112a14Slm66018 	 */
780*cb112a14Slm66018 	if ((err != 0) && (err != EFAULT))
781*cb112a14Slm66018 		return (err);
782*cb112a14Slm66018 
7831ae08745Sheppo 	err = ldc_unreg_callback(vport->ldc_handle);
784*cb112a14Slm66018 	if (err != 0)
785*cb112a14Slm66018 		return (err);
786*cb112a14Slm66018 
7871ae08745Sheppo 	err = ldc_fini(vport->ldc_handle);
788*cb112a14Slm66018 	if (err != 0)
789*cb112a14Slm66018 		return (err);
7901ae08745Sheppo 
7913af08d82Slm66018 	vport->status = VLDC_PORT_OPEN;
7923af08d82Slm66018 
793*cb112a14Slm66018 	return (0);
7941ae08745Sheppo }
7951ae08745Sheppo 
7961ae08745Sheppo /* close a vldc port */
7971ae08745Sheppo static int
7981ae08745Sheppo i_vldc_close_port(vldc_t *vldcp, uint_t portno)
7991ae08745Sheppo {
8001ae08745Sheppo 	vldc_port_t	*vport;
8013af08d82Slm66018 	int		rv = DDI_SUCCESS;
8021ae08745Sheppo 
8031ae08745Sheppo 	vport = &(vldcp->port[portno]);
8041ae08745Sheppo 
8051ae08745Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
8061ae08745Sheppo 
8073af08d82Slm66018 	D1("i_vldc_close_port: vldc@%d:%d: closing port\n",
8083af08d82Slm66018 	    vport->inst, vport->minorp->portno);
8093af08d82Slm66018 
8103af08d82Slm66018 	switch (vport->status) {
8113af08d82Slm66018 	case VLDC_PORT_CLOSED:
8121ae08745Sheppo 		/* nothing to do */
8131ae08745Sheppo 		DWARN("i_vldc_close_port: port %d in an unexpected "
8141ae08745Sheppo 		    "state (%d)\n", portno, vport->status);
8151ae08745Sheppo 		return (DDI_SUCCESS);
8163af08d82Slm66018 
8173af08d82Slm66018 	case VLDC_PORT_READY:
8183af08d82Slm66018 	case VLDC_PORT_RESET:
8193af08d82Slm66018 		rv = i_vldc_ldc_close(vport);
820*cb112a14Slm66018 		if (rv != 0)
821*cb112a14Slm66018 			return (rv);
8223af08d82Slm66018 		break;
823*cb112a14Slm66018 
824*cb112a14Slm66018 	case VLDC_PORT_OPEN:
825*cb112a14Slm66018 		break;
826*cb112a14Slm66018 
827*cb112a14Slm66018 	default:
828*cb112a14Slm66018 		DWARN("i_vldc_close_port: port %d in an unexpected "
829*cb112a14Slm66018 		    "state (%d)\n", portno, vport->status);
830*cb112a14Slm66018 		ASSERT(0);	/* fail quickly to help diagnosis */
831*cb112a14Slm66018 		return (EINVAL);
8321ae08745Sheppo 	}
8331ae08745Sheppo 
8341ae08745Sheppo 	ASSERT(vport->status == VLDC_PORT_OPEN);
8351ae08745Sheppo 
8361ae08745Sheppo 	/* free memory */
8371ae08745Sheppo 	kmem_free(vport->send_buf, vport->mtu);
8381ae08745Sheppo 	kmem_free(vport->recv_buf, vport->mtu);
8391ae08745Sheppo 
840d10e4ef2Snarayan 	if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0)
841d10e4ef2Snarayan 		kmem_free(vport->cookie_buf, vldc_max_cookie);
842d10e4ef2Snarayan 
8431ae08745Sheppo 	vport->status = VLDC_PORT_CLOSED;
8441ae08745Sheppo 
8451ae08745Sheppo 	return (rv);
8461ae08745Sheppo }
8471ae08745Sheppo 
8481ae08745Sheppo /*
8491ae08745Sheppo  * attach(9E): attach a device to the system.
8501ae08745Sheppo  * called once for each instance of the device on the system.
8511ae08745Sheppo  */
8521ae08745Sheppo static int
8531ae08745Sheppo vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
8541ae08745Sheppo {
8551ae08745Sheppo 	int 	i, instance;
8561ae08745Sheppo 	vldc_t	*vldcp;
8571ae08745Sheppo 
8581ae08745Sheppo 	switch (cmd) {
8591ae08745Sheppo 
8601ae08745Sheppo 	case DDI_ATTACH:
8611ae08745Sheppo 
8621ae08745Sheppo 		instance = ddi_get_instance(dip);
8631ae08745Sheppo 
8641ae08745Sheppo 		if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) {
8651ae08745Sheppo 			return (DDI_FAILURE);
8661ae08745Sheppo 		}
8671ae08745Sheppo 
8681ae08745Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
8691ae08745Sheppo 		if (vldcp == NULL) {
8701ae08745Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
8711ae08745Sheppo 			return (ENXIO);
8721ae08745Sheppo 		}
8731ae08745Sheppo 
8741ae08745Sheppo 		D1("vldc_attach: DDI_ATTACH instance=%d\n", instance);
8751ae08745Sheppo 
8761ae08745Sheppo 		mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL);
8771ae08745Sheppo 		vldcp->dip = dip;
8781ae08745Sheppo 		vldcp->detaching = B_FALSE;
8791ae08745Sheppo 
8801ae08745Sheppo 		for (i = 0; i < VLDC_MAX_PORTS; i++) {
8811ae08745Sheppo 			/* No minor node association to start with */
8821ae08745Sheppo 			vldcp->port[i].minorp = NULL;
8831ae08745Sheppo 		}
8841ae08745Sheppo 
8851ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
8861ae08745Sheppo 			mutex_init(&(vldcp->minor_tbl[i].lock), NULL,
8871ae08745Sheppo 			    MUTEX_DRIVER, NULL);
8881ae08745Sheppo 			cv_init(&(vldcp->minor_tbl[i].cv), NULL,
8891ae08745Sheppo 			    CV_DRIVER, NULL);
8901ae08745Sheppo 			/* No port association to start with */
8911ae08745Sheppo 			vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO;
8921ae08745Sheppo 		}
8931ae08745Sheppo 
8941ae08745Sheppo 		/* Register for MD update notification */
8951ae08745Sheppo 		if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) {
8961ae08745Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
8971ae08745Sheppo 			return (DDI_FAILURE);
8981ae08745Sheppo 		}
8991ae08745Sheppo 
9001ae08745Sheppo 		return (DDI_SUCCESS);
9011ae08745Sheppo 
9021ae08745Sheppo 	case DDI_RESUME:
9031ae08745Sheppo 
9041ae08745Sheppo 		return (DDI_SUCCESS);
9051ae08745Sheppo 
9061ae08745Sheppo 	default:
9071ae08745Sheppo 
9081ae08745Sheppo 		return (DDI_FAILURE);
9091ae08745Sheppo 	}
9101ae08745Sheppo }
9111ae08745Sheppo 
9121ae08745Sheppo /*
9131ae08745Sheppo  * detach(9E): detach a device from the system.
9141ae08745Sheppo  */
9151ae08745Sheppo static int
9161ae08745Sheppo vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9171ae08745Sheppo {
9181ae08745Sheppo 	int 		i, instance;
9191ae08745Sheppo 	vldc_t		*vldcp;
9201ae08745Sheppo 
9211ae08745Sheppo 	switch (cmd) {
9221ae08745Sheppo 
9231ae08745Sheppo 	case DDI_DETACH:
9241ae08745Sheppo 
9251ae08745Sheppo 		instance = ddi_get_instance(dip);
9261ae08745Sheppo 
9271ae08745Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
9281ae08745Sheppo 		if (vldcp == NULL) {
9291ae08745Sheppo 			return (DDI_FAILURE);
9301ae08745Sheppo 		}
9311ae08745Sheppo 
9321ae08745Sheppo 		D1("vldc_detach: DDI_DETACH instance=%d\n", instance);
9331ae08745Sheppo 
9341ae08745Sheppo 		mutex_enter(&vldcp->lock);
9351ae08745Sheppo 
9361ae08745Sheppo 		/* Fail the detach if all ports have not been removed. */
9371ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
9381ae08745Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9391ae08745Sheppo 				D1("vldc_detach: vldc@%d:%d is bound, "
9401ae08745Sheppo 				    "detach failed\n",
9411ae08745Sheppo 				    instance, vldcp->minor_tbl[i].portno);
9421ae08745Sheppo 				mutex_exit(&vldcp->lock);
9431ae08745Sheppo 				return (DDI_FAILURE);
9441ae08745Sheppo 			}
9451ae08745Sheppo 		}
9461ae08745Sheppo 
9471ae08745Sheppo 		/*
9481ae08745Sheppo 		 * Prevent MDEG from adding new ports before the callback can
9491ae08745Sheppo 		 * be unregistered. The lock can't be held accross the
9501ae08745Sheppo 		 * unregistration call because a callback may be in progress
9511ae08745Sheppo 		 * and blocked on the lock.
9521ae08745Sheppo 		 */
9531ae08745Sheppo 		vldcp->detaching = B_TRUE;
9541ae08745Sheppo 
9551ae08745Sheppo 		mutex_exit(&vldcp->lock);
9561ae08745Sheppo 
9571ae08745Sheppo 		if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) {
9581ae08745Sheppo 			vldcp->detaching = B_FALSE;
9591ae08745Sheppo 			return (DDI_FAILURE);
9601ae08745Sheppo 		}
9611ae08745Sheppo 
9621ae08745Sheppo 		/* Tear down all bound ports and free resources. */
9631ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
9641ae08745Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9651ae08745Sheppo 				(void) i_vldc_remove_port(vldcp, i);
9661ae08745Sheppo 			}
9671ae08745Sheppo 			mutex_destroy(&(vldcp->minor_tbl[i].lock));
9681ae08745Sheppo 			cv_destroy(&(vldcp->minor_tbl[i].cv));
9691ae08745Sheppo 		}
9701ae08745Sheppo 
9711ae08745Sheppo 		mutex_destroy(&vldcp->lock);
9721ae08745Sheppo 		ddi_soft_state_free(vldc_ssp, instance);
9731ae08745Sheppo 
9741ae08745Sheppo 		return (DDI_SUCCESS);
9751ae08745Sheppo 
9761ae08745Sheppo 	case DDI_SUSPEND:
9771ae08745Sheppo 
9781ae08745Sheppo 		return (DDI_SUCCESS);
9791ae08745Sheppo 
9801ae08745Sheppo 	default:
9811ae08745Sheppo 
9821ae08745Sheppo 		return (DDI_FAILURE);
9831ae08745Sheppo 	}
9841ae08745Sheppo }
9851ae08745Sheppo 
9861ae08745Sheppo /* cb_open */
9871ae08745Sheppo static int
9881ae08745Sheppo vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
9891ae08745Sheppo {
9901ae08745Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
9911ae08745Sheppo 
9921ae08745Sheppo 	int instance;
9931ae08745Sheppo 	minor_t minor;
9941ae08745Sheppo 	uint64_t portno;
9951ae08745Sheppo 	vldc_t *vldcp;
9961ae08745Sheppo 	vldc_port_t *vport;
9971ae08745Sheppo 	vldc_minor_t *vminor;
9981ae08745Sheppo 
9991ae08745Sheppo 	minor = getminor(*devp);
10001ae08745Sheppo 	instance = VLDCINST(minor);
10011ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
10021ae08745Sheppo 	if (vldcp == NULL)
10031ae08745Sheppo 		return (ENXIO);
10041ae08745Sheppo 
10051ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
10061ae08745Sheppo 	mutex_enter(&vminor->lock);
10071ae08745Sheppo 	portno = vminor->portno;
10081ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
10091ae08745Sheppo 		mutex_exit(&vminor->lock);
10101ae08745Sheppo 		return (ENXIO);
10111ae08745Sheppo 	}
10121ae08745Sheppo 
10131ae08745Sheppo 	vport = &(vldcp->port[portno]);
10141ae08745Sheppo 
10151ae08745Sheppo 	D1("vldc_open: opening vldc@%d:%lu\n", instance, portno);
10161ae08745Sheppo 
10171ae08745Sheppo 	if (vport->status != VLDC_PORT_CLOSED) {
10181ae08745Sheppo 		mutex_exit(&vminor->lock);
10191ae08745Sheppo 		return (EBUSY);
10201ae08745Sheppo 	}
10211ae08745Sheppo 
10221ae08745Sheppo 	vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10231ae08745Sheppo 	vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10241ae08745Sheppo 
1025d10e4ef2Snarayan 	if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0)
1026d10e4ef2Snarayan 		vport->cookie_buf = kmem_alloc(vldc_max_cookie, KM_SLEEP);
1027d10e4ef2Snarayan 
10281ae08745Sheppo 	vport->is_stream = B_FALSE;	/* assume not a stream */
10291ae08745Sheppo 	vport->hanged_up = B_FALSE;
10301ae08745Sheppo 
10311ae08745Sheppo 	vport->status = VLDC_PORT_OPEN;
10321ae08745Sheppo 
10331ae08745Sheppo 	mutex_exit(&vminor->lock);
10341ae08745Sheppo 
10351ae08745Sheppo 	return (DDI_SUCCESS);
10361ae08745Sheppo }
10371ae08745Sheppo 
10381ae08745Sheppo /* cb_close */
10391ae08745Sheppo static int
10401ae08745Sheppo vldc_close(dev_t dev, int flag, int otyp, cred_t *cred)
10411ae08745Sheppo {
10421ae08745Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
10431ae08745Sheppo 
10441ae08745Sheppo 	int instance;
10451ae08745Sheppo 	minor_t minor;
10461ae08745Sheppo 	uint64_t portno;
10471ae08745Sheppo 	vldc_t *vldcp;
10481ae08745Sheppo 	vldc_minor_t *vminor;
10491ae08745Sheppo 	int rv;
10501ae08745Sheppo 
10511ae08745Sheppo 	minor = getminor(dev);
10521ae08745Sheppo 	instance = VLDCINST(minor);
10531ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
10541ae08745Sheppo 	if (vldcp == NULL) {
10551ae08745Sheppo 		return (ENXIO);
10561ae08745Sheppo 	}
10571ae08745Sheppo 
10581ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
10591ae08745Sheppo 	mutex_enter(&vminor->lock);
10601ae08745Sheppo 	portno = vminor->portno;
10611ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
10621ae08745Sheppo 		mutex_exit(&vminor->lock);
10631ae08745Sheppo 		return (ENOLINK);
10641ae08745Sheppo 	}
10651ae08745Sheppo 
10661ae08745Sheppo 	D1("vldc_close: closing vldc@%d:%lu\n", instance, portno);
10671ae08745Sheppo 
10681ae08745Sheppo 	rv = i_vldc_close_port(vldcp, portno);
10691ae08745Sheppo 
10701ae08745Sheppo 	mutex_exit(&vminor->lock);
10711ae08745Sheppo 
10721ae08745Sheppo 	return (rv);
10731ae08745Sheppo }
10741ae08745Sheppo 
10751ae08745Sheppo static int
10761ae08745Sheppo vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode)
10771ae08745Sheppo {
10781ae08745Sheppo 	ldc_attr_t attr;
10791ae08745Sheppo 	int rv;
10801ae08745Sheppo 
10811ae08745Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
10821ae08745Sheppo 
10831ae08745Sheppo 	/* validate mode */
10841ae08745Sheppo 	switch (channel_mode) {
10851ae08745Sheppo 	case LDC_MODE_STREAM:
10861ae08745Sheppo 		vport->is_stream = B_TRUE;
10871ae08745Sheppo 		break;
10881ae08745Sheppo 	case LDC_MODE_RAW:
10891ae08745Sheppo 	case LDC_MODE_UNRELIABLE:
10901ae08745Sheppo 	case LDC_MODE_RELIABLE:
10911ae08745Sheppo 		vport->is_stream = B_FALSE;
10921ae08745Sheppo 		break;
10931ae08745Sheppo 	default:
10941ae08745Sheppo 		return (EINVAL);
10951ae08745Sheppo 	}
10961ae08745Sheppo 
10971ae08745Sheppo 	if (vport->status == VLDC_PORT_READY) {
10981ae08745Sheppo 		rv = i_vldc_ldc_close(vport);
10991ae08745Sheppo 		if (rv != 0) {
11001ae08745Sheppo 			DWARN("vldc_set_ldc_mode: i_vldc_ldc_close "
11011ae08745Sheppo 			    "failed, rv=%d\n", rv);
11021ae08745Sheppo 			return (rv);
11031ae08745Sheppo 		}
11041ae08745Sheppo 	}
11051ae08745Sheppo 
11061ae08745Sheppo 	D1("vldc_set_ldc_mode: vport status %d, mode %d\n",
11071ae08745Sheppo 	    vport->status, channel_mode);
11081ae08745Sheppo 
11091ae08745Sheppo 	vport->ldc_mode = channel_mode;
11101ae08745Sheppo 
11111ae08745Sheppo 	/* initialize the channel */
11121ae08745Sheppo 	attr.devclass = LDC_DEV_SERIAL;
11131ae08745Sheppo 	attr.instance = ddi_get_instance(vldcp->dip);
1114e1ebb9ecSlm66018 	attr.mtu = vport->mtu;
11151ae08745Sheppo 	attr.mode = vport->ldc_mode;
11161ae08745Sheppo 
11171ae08745Sheppo 	if ((rv = ldc_init(vport->ldc_id, &attr,
11181ae08745Sheppo 	    &vport->ldc_handle)) != 0) {
11191ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv);
11201ae08745Sheppo 		goto error_init;
11211ae08745Sheppo 	}
11221ae08745Sheppo 
11231ae08745Sheppo 	/* register it */
11241ae08745Sheppo 	if ((rv = ldc_reg_callback(vport->ldc_handle,
11251ae08745Sheppo 	    i_vldc_cb, (caddr_t)vport)) != 0) {
11261ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n",
11271ae08745Sheppo 		    rv);
11281ae08745Sheppo 		goto error_reg;
11291ae08745Sheppo 	}
11301ae08745Sheppo 
11311ae08745Sheppo 	/* open the channel */
11321ae08745Sheppo 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
11331ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv);
11341ae08745Sheppo 		goto error_open;
11351ae08745Sheppo 	}
11361ae08745Sheppo 
11371ae08745Sheppo 	vport->status = VLDC_PORT_READY;
11381ae08745Sheppo 
11391ae08745Sheppo 	/*
11401ae08745Sheppo 	 * Attempt to bring the channel up, but do not
11411ae08745Sheppo 	 * fail if the other end is not up yet.
11421ae08745Sheppo 	 */
11431ae08745Sheppo 	rv = ldc_up(vport->ldc_handle);
11441ae08745Sheppo 	if (rv == ECONNREFUSED) {
11451ae08745Sheppo 		D1("vldc_ioctl_opt_op: remote endpoint not up yet\n");
11461ae08745Sheppo 	} else if (rv != 0) {
11471ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv);
11481ae08745Sheppo 		goto error_up;
11491ae08745Sheppo 	}
11501ae08745Sheppo 
1151a8ea4edeSnarayan 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
1152a8ea4edeSnarayan 	if (rv != 0) {
1153a8ea4edeSnarayan 		DWARN("vldc_ioctl_opt_op: vldc@%d:%d could not get ldc "
1154a8ea4edeSnarayan 		    "status, rv=%d\n", vport->inst, vport->number, rv);
1155a8ea4edeSnarayan 		goto error_up;
1156a8ea4edeSnarayan 	}
1157a8ea4edeSnarayan 
11581ae08745Sheppo 	D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n",
11591ae08745Sheppo 	    vport->ldc_id);
11601ae08745Sheppo 
11611ae08745Sheppo 	return (0);
11621ae08745Sheppo 
11631ae08745Sheppo error_up:
11641ae08745Sheppo 	vport->status = VLDC_PORT_OPEN;
11651ae08745Sheppo 	(void) ldc_close(vport->ldc_handle);
11661ae08745Sheppo error_open:
11671ae08745Sheppo 	(void) ldc_unreg_callback(vport->ldc_handle);
11681ae08745Sheppo error_reg:
11691ae08745Sheppo 	(void) ldc_fini(vport->ldc_handle);
11701ae08745Sheppo error_init:
11711ae08745Sheppo 	return (rv);
11721ae08745Sheppo }
11731ae08745Sheppo 
11741ae08745Sheppo /* ioctl to read cookie */
11751ae08745Sheppo static int
11761ae08745Sheppo i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
11771ae08745Sheppo     int mode)
11781ae08745Sheppo {
11791ae08745Sheppo 	vldc_data_t copy_info;
1180d10e4ef2Snarayan 	uint64_t len, balance, copy_size;
1181d10e4ef2Snarayan 	caddr_t src_addr, dst_addr;
11821ae08745Sheppo 	int rv;
11831ae08745Sheppo 
11841ae08745Sheppo 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) == -1) {
11851ae08745Sheppo 		return (EFAULT);
11861ae08745Sheppo 	}
11871ae08745Sheppo 
1188d10e4ef2Snarayan 	len = balance = copy_info.length;
1189d10e4ef2Snarayan 	src_addr = (caddr_t)copy_info.src_addr;
1190d10e4ef2Snarayan 	dst_addr = (caddr_t)copy_info.dst_addr;
1191d10e4ef2Snarayan 	while (balance > 0) {
11921ae08745Sheppo 
1193d10e4ef2Snarayan 		/* get the max amount to the copied */
1194d10e4ef2Snarayan 		copy_size = MIN(balance, vldc_max_cookie);
11951ae08745Sheppo 
11961ae08745Sheppo 		mutex_enter(&vport->minorp->lock);
11971ae08745Sheppo 
1198d10e4ef2Snarayan 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%p "
1199d10e4ef2Snarayan 		    "size 0x%lx to 0x%p\n", vldc_instance, vport->number,
1200d10e4ef2Snarayan 		    dst_addr, copy_size, src_addr);
12011ae08745Sheppo 
12021ae08745Sheppo 		/* read from the HV into the temporary buffer */
12033af08d82Slm66018 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1204d10e4ef2Snarayan 		    &copy_size, dst_addr, LDC_COPY_IN);
12051ae08745Sheppo 		if (rv != 0) {
1206d10e4ef2Snarayan 			DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot "
1207d10e4ef2Snarayan 			    "read address 0x%p, rv=%d\n",
1208d10e4ef2Snarayan 			    vldc_instance, vport->number, dst_addr, rv);
12091ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
12101ae08745Sheppo 			return (EFAULT);
12111ae08745Sheppo 		}
12121ae08745Sheppo 
12131ae08745Sheppo 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n",
12141ae08745Sheppo 		    vldc_instance, vport->number);
12151ae08745Sheppo 
12161ae08745Sheppo 		mutex_exit(&vport->minorp->lock);
12171ae08745Sheppo 
1218d10e4ef2Snarayan 		/*
1219d10e4ef2Snarayan 		 * copy data from temporary buffer out to the
1220d10e4ef2Snarayan 		 * caller and free buffer
1221d10e4ef2Snarayan 		 */
1222d10e4ef2Snarayan 		rv = ddi_copyout(vport->cookie_buf, src_addr, copy_size, mode);
12231ae08745Sheppo 		if (rv != 0) {
12241ae08745Sheppo 			return (EFAULT);
12251ae08745Sheppo 		}
12261ae08745Sheppo 
1227d10e4ef2Snarayan 		/* adjust len, source and dest */
1228d10e4ef2Snarayan 		balance -= copy_size;
1229d10e4ef2Snarayan 		src_addr += copy_size;
1230d10e4ef2Snarayan 		dst_addr += copy_size;
1231d10e4ef2Snarayan 	}
1232d10e4ef2Snarayan 
12331ae08745Sheppo 	/* set the structure to reflect outcome */
12341ae08745Sheppo 	copy_info.length = len;
12351ae08745Sheppo 	if (ddi_copyout(&copy_info, arg, sizeof (copy_info), mode) != 0) {
12361ae08745Sheppo 		return (EFAULT);
12371ae08745Sheppo 	}
12381ae08745Sheppo 
12391ae08745Sheppo 	return (0);
12401ae08745Sheppo }
12411ae08745Sheppo 
12421ae08745Sheppo /* ioctl to write cookie */
12431ae08745Sheppo static int
12441ae08745Sheppo i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
12451ae08745Sheppo     int mode)
12461ae08745Sheppo {
12471ae08745Sheppo 	vldc_data_t copy_info;
1248d10e4ef2Snarayan 	uint64_t len, balance, copy_size;
1249d10e4ef2Snarayan 	caddr_t src_addr, dst_addr;
12501ae08745Sheppo 	int rv;
12511ae08745Sheppo 
1252d10e4ef2Snarayan 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) != 0) {
12531ae08745Sheppo 		return (EFAULT);
12541ae08745Sheppo 	}
12551ae08745Sheppo 
12561ae08745Sheppo 	D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx "
12571ae08745Sheppo 	    "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr,
12581ae08745Sheppo 	    copy_info.length, copy_info.dst_addr);
12591ae08745Sheppo 
1260d10e4ef2Snarayan 	len = balance = copy_info.length;
1261d10e4ef2Snarayan 	src_addr = (caddr_t)copy_info.src_addr;
1262d10e4ef2Snarayan 	dst_addr = (caddr_t)copy_info.dst_addr;
1263d10e4ef2Snarayan 	while (balance > 0) {
12641ae08745Sheppo 
1265d10e4ef2Snarayan 		/* get the max amount to the copied */
1266d10e4ef2Snarayan 		copy_size = MIN(balance, vldc_max_cookie);
1267d10e4ef2Snarayan 
1268d10e4ef2Snarayan 		/*
1269d10e4ef2Snarayan 		 * copy into the temporary buffer the data
1270d10e4ef2Snarayan 		 * to be written to the HV
1271d10e4ef2Snarayan 		 */
1272d10e4ef2Snarayan 		if (ddi_copyin((caddr_t)src_addr, vport->cookie_buf,
1273d10e4ef2Snarayan 		    copy_size, mode) != 0) {
12741ae08745Sheppo 			return (EFAULT);
12751ae08745Sheppo 		}
12761ae08745Sheppo 
12771ae08745Sheppo 		mutex_enter(&vport->minorp->lock);
12781ae08745Sheppo 
12791ae08745Sheppo 		/* write the data from the temporary buffer to the HV */
12803af08d82Slm66018 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1281d10e4ef2Snarayan 		    &copy_size, dst_addr, LDC_COPY_OUT);
12821ae08745Sheppo 		if (rv != 0) {
1283d10e4ef2Snarayan 			DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d "
1284d10e4ef2Snarayan 			    "failed to write at address 0x%p\n, rv=%d",
1285d10e4ef2Snarayan 			    vldc_instance, vport->number, dst_addr, rv);
12861ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
12871ae08745Sheppo 			return (EFAULT);
12881ae08745Sheppo 		}
12891ae08745Sheppo 
12901ae08745Sheppo 		D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n",
12911ae08745Sheppo 		    vldc_instance, vport->number);
12921ae08745Sheppo 
12931ae08745Sheppo 		mutex_exit(&vport->minorp->lock);
12941ae08745Sheppo 
1295d10e4ef2Snarayan 		/* adjust len, source and dest */
1296d10e4ef2Snarayan 		balance -= copy_size;
1297d10e4ef2Snarayan 		src_addr += copy_size;
1298d10e4ef2Snarayan 		dst_addr += copy_size;
1299d10e4ef2Snarayan 	}
13001ae08745Sheppo 
13011ae08745Sheppo 	/* set the structure to reflect outcome */
13021ae08745Sheppo 	copy_info.length = len;
13031ae08745Sheppo 	if (ddi_copyout(&copy_info, (caddr_t)arg,
13041ae08745Sheppo 	    sizeof (copy_info), mode) != 0) {
13051ae08745Sheppo 		return (EFAULT);
13061ae08745Sheppo 	}
13071ae08745Sheppo 
13081ae08745Sheppo 	return (0);
13091ae08745Sheppo }
13101ae08745Sheppo 
13111ae08745Sheppo /* vldc specific ioctl option commands */
13121ae08745Sheppo static int
13131ae08745Sheppo i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode)
13141ae08745Sheppo {
13151ae08745Sheppo 	vldc_opt_op_t 	vldc_cmd;
13161ae08745Sheppo 	uint32_t	new_mtu;
13171ae08745Sheppo 	int		rv = 0;
13181ae08745Sheppo 
13191ae08745Sheppo 	if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) {
13201ae08745Sheppo 		return (EFAULT);
13211ae08745Sheppo 	}
13221ae08745Sheppo 
13231ae08745Sheppo 	D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel);
13241ae08745Sheppo 
13251ae08745Sheppo 	switch (vldc_cmd.opt_sel) {
13261ae08745Sheppo 
13271ae08745Sheppo 	case VLDC_OPT_MTU_SZ:
13281ae08745Sheppo 
13291ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
13301ae08745Sheppo 			vldc_cmd.opt_val = vport->mtu;
13311ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
13321ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
13331ae08745Sheppo 				return (EFAULT);
13341ae08745Sheppo 			}
13351ae08745Sheppo 		} else {
13361ae08745Sheppo 			new_mtu = vldc_cmd.opt_val;
13371ae08745Sheppo 
13381ae08745Sheppo 			if ((new_mtu < LDC_PACKET_SIZE) ||
13391ae08745Sheppo 			    (new_mtu > vldc_max_mtu)) {
13401ae08745Sheppo 				return (EINVAL);
13411ae08745Sheppo 			}
13421ae08745Sheppo 
13431ae08745Sheppo 			mutex_enter(&vport->minorp->lock);
13441ae08745Sheppo 
13451ae08745Sheppo 			if ((vport->status != VLDC_PORT_CLOSED) &&
13461ae08745Sheppo 			    (new_mtu != vport->mtu)) {
13471ae08745Sheppo 				/*
13481ae08745Sheppo 				 * The port has buffers allocated since it is
13491ae08745Sheppo 				 * not closed plus the MTU size has changed.
13501ae08745Sheppo 				 * Reallocate the buffers to the new MTU size.
13511ae08745Sheppo 				 */
13521ae08745Sheppo 				kmem_free(vport->recv_buf, vport->mtu);
13531ae08745Sheppo 				vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP);
13541ae08745Sheppo 
13551ae08745Sheppo 				kmem_free(vport->send_buf, vport->mtu);
13561ae08745Sheppo 				vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP);
13571ae08745Sheppo 
13581ae08745Sheppo 				vport->mtu = new_mtu;
13591ae08745Sheppo 			}
13601ae08745Sheppo 
13611ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
13621ae08745Sheppo 		}
13631ae08745Sheppo 
13641ae08745Sheppo 		break;
13651ae08745Sheppo 
13661ae08745Sheppo 	case VLDC_OPT_STATUS:
13671ae08745Sheppo 
13681ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
13691ae08745Sheppo 			vldc_cmd.opt_val = vport->status;
13701ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
13711ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
13721ae08745Sheppo 				return (EFAULT);
13731ae08745Sheppo 			}
13741ae08745Sheppo 		} else {
13751ae08745Sheppo 			return (ENOTSUP);
13761ae08745Sheppo 		}
13771ae08745Sheppo 
13781ae08745Sheppo 		break;
13791ae08745Sheppo 
13801ae08745Sheppo 	case VLDC_OPT_MODE:
13811ae08745Sheppo 
13821ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
13831ae08745Sheppo 			vldc_cmd.opt_val = vport->ldc_mode;
13841ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
13851ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
13861ae08745Sheppo 				return (EFAULT);
13871ae08745Sheppo 			}
13881ae08745Sheppo 		} else {
13891ae08745Sheppo 			mutex_enter(&vport->minorp->lock);
13901ae08745Sheppo 			rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val);
13911ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
13921ae08745Sheppo 		}
13931ae08745Sheppo 
13941ae08745Sheppo 		break;
13951ae08745Sheppo 
13961ae08745Sheppo 	default:
13971ae08745Sheppo 
13981ae08745Sheppo 		D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel);
13991ae08745Sheppo 		return (ENOTSUP);
14001ae08745Sheppo 	}
14011ae08745Sheppo 
14021ae08745Sheppo 	return (rv);
14031ae08745Sheppo }
14041ae08745Sheppo 
14051ae08745Sheppo /* cb_ioctl */
14061ae08745Sheppo static int
14071ae08745Sheppo vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
14081ae08745Sheppo     int *rvalp)
14091ae08745Sheppo {
14101ae08745Sheppo 	_NOTE(ARGUNUSED(credp, rvalp))
14111ae08745Sheppo 
14121ae08745Sheppo 	int rv = EINVAL;
14131ae08745Sheppo 	int instance;
14141ae08745Sheppo 	minor_t minor;
14151ae08745Sheppo 	uint64_t portno;
14161ae08745Sheppo 	vldc_t *vldcp;
14171ae08745Sheppo 	vldc_port_t *vport;
14181ae08745Sheppo 	vldc_minor_t *vminor;
14191ae08745Sheppo 
14201ae08745Sheppo 	minor = getminor(dev);
14211ae08745Sheppo 	instance = VLDCINST(minor);
14221ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
14231ae08745Sheppo 	if (vldcp == NULL) {
14241ae08745Sheppo 		return (ENXIO);
14251ae08745Sheppo 	}
14261ae08745Sheppo 
14271ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
14281ae08745Sheppo 	mutex_enter(&vminor->lock);
14291ae08745Sheppo 	portno = vminor->portno;
14301ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
14311ae08745Sheppo 		mutex_exit(&vminor->lock);
14321ae08745Sheppo 		return (ENOLINK);
14331ae08745Sheppo 	}
14341ae08745Sheppo 	vminor->in_use += 1;
14351ae08745Sheppo 	mutex_exit(&vminor->lock);
14361ae08745Sheppo 
14371ae08745Sheppo 	vport = &(vldcp->port[portno]);
14381ae08745Sheppo 
14391ae08745Sheppo 	D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd);
14401ae08745Sheppo 
14411ae08745Sheppo 	switch (cmd) {
14421ae08745Sheppo 
14431ae08745Sheppo 	case VLDC_IOCTL_OPT_OP:
14441ae08745Sheppo 		rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg,  mode);
14451ae08745Sheppo 		break;
14461ae08745Sheppo 
14471ae08745Sheppo 	case VLDC_IOCTL_READ_COOKIE:
1448d10e4ef2Snarayan 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1449d10e4ef2Snarayan 			rv = EINVAL;
1450d10e4ef2Snarayan 			break;
1451d10e4ef2Snarayan 		}
14521ae08745Sheppo 		rv = i_vldc_ioctl_read_cookie(vport, instance,
14531ae08745Sheppo 		    (void *)arg, mode);
14541ae08745Sheppo 		break;
14551ae08745Sheppo 
14561ae08745Sheppo 	case VLDC_IOCTL_WRITE_COOKIE:
1457d10e4ef2Snarayan 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1458d10e4ef2Snarayan 			rv = EINVAL;
1459d10e4ef2Snarayan 			break;
1460d10e4ef2Snarayan 		}
14611ae08745Sheppo 		rv = i_vldc_ioctl_write_cookie(vport, instance,
14621ae08745Sheppo 		    (void *)arg, mode);
14631ae08745Sheppo 		break;
14641ae08745Sheppo 
14651ae08745Sheppo 	default:
14661ae08745Sheppo 		DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n",
14671ae08745Sheppo 		    instance, portno, cmd);
14681ae08745Sheppo 		rv = EINVAL;
14691ae08745Sheppo 		break;
14701ae08745Sheppo 	}
14711ae08745Sheppo 
14721ae08745Sheppo 	mutex_enter(&vminor->lock);
14731ae08745Sheppo 	vminor->in_use -= 1;
14741ae08745Sheppo 	if (vminor->in_use == 0) {
14751ae08745Sheppo 		cv_signal(&vminor->cv);
14761ae08745Sheppo 	}
14771ae08745Sheppo 	mutex_exit(&vminor->lock);
14781ae08745Sheppo 
14791ae08745Sheppo 	D1("vldc_ioctl: rv=%d\n", rv);
14801ae08745Sheppo 
14811ae08745Sheppo 	return (rv);
14821ae08745Sheppo }
14831ae08745Sheppo 
14841ae08745Sheppo /* cb_read */
14851ae08745Sheppo static int
14861ae08745Sheppo vldc_read(dev_t dev, struct uio *uiop, cred_t *credp)
14871ae08745Sheppo {
14881ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
14891ae08745Sheppo 
14901ae08745Sheppo 	int instance;
14911ae08745Sheppo 	minor_t minor;
14921ae08745Sheppo 	size_t size = 0;
14931ae08745Sheppo 	uint64_t portno;
14941ae08745Sheppo 	vldc_t *vldcp;
14951ae08745Sheppo 	vldc_port_t *vport;
14961ae08745Sheppo 	vldc_minor_t *vminor;
14971ae08745Sheppo 	int rv = 0;
14981ae08745Sheppo 
14991ae08745Sheppo 	minor = getminor(dev);
15001ae08745Sheppo 	instance = VLDCINST(minor);
15011ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
15021ae08745Sheppo 	if (vldcp == NULL) {
15031ae08745Sheppo 		return (ENXIO);
15041ae08745Sheppo 	}
15051ae08745Sheppo 
15061ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
15071ae08745Sheppo 	mutex_enter(&vminor->lock);
15081ae08745Sheppo 	portno = vminor->portno;
15091ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
15101ae08745Sheppo 		mutex_exit(&vminor->lock);
15111ae08745Sheppo 		return (ENOLINK);
15121ae08745Sheppo 	}
15131ae08745Sheppo 
15141ae08745Sheppo 	D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno);
15151ae08745Sheppo 
15161ae08745Sheppo 	vport = &(vldcp->port[portno]);
15171ae08745Sheppo 
15181ae08745Sheppo 	/* check the port status */
15191ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
15201ae08745Sheppo 		DWARN("vldc_read: vldc@%d:%lu not in the ready state\n",
15211ae08745Sheppo 		    instance, portno);
15221ae08745Sheppo 		mutex_exit(&vminor->lock);
15231ae08745Sheppo 		return (ENOTACTIVE);
15241ae08745Sheppo 	}
15251ae08745Sheppo 
15261ae08745Sheppo 	/* read data */
15271ae08745Sheppo 	size = MIN(vport->mtu, uiop->uio_resid);
15281ae08745Sheppo 	rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size);
15291ae08745Sheppo 
15301ae08745Sheppo 	D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n",
15311ae08745Sheppo 	    instance, portno, size, rv);
15321ae08745Sheppo 
15331ae08745Sheppo 	if (rv == 0) {
15341ae08745Sheppo 		if (size != 0) {
15351ae08745Sheppo 			rv = uiomove(vport->recv_buf, size, UIO_READ, uiop);
15361ae08745Sheppo 		} else {
15371ae08745Sheppo 			rv = EWOULDBLOCK;
15381ae08745Sheppo 		}
15391ae08745Sheppo 	} else {
15401ae08745Sheppo 		switch (rv) {
15411ae08745Sheppo 		case ENOBUFS:
15421ae08745Sheppo 			break;
15431ae08745Sheppo 		case ETIMEDOUT:
15441ae08745Sheppo 		case EWOULDBLOCK:
15451ae08745Sheppo 			rv = EWOULDBLOCK;
15461ae08745Sheppo 			break;
15471ae08745Sheppo 		default:
15481ae08745Sheppo 			rv = ECONNRESET;
15491ae08745Sheppo 			break;
15501ae08745Sheppo 		}
15511ae08745Sheppo 	}
15521ae08745Sheppo 
15531ae08745Sheppo 	mutex_exit(&vminor->lock);
15541ae08745Sheppo 
15551ae08745Sheppo 	return (rv);
15561ae08745Sheppo }
15571ae08745Sheppo 
15581ae08745Sheppo /* cb_write */
15591ae08745Sheppo static int
15601ae08745Sheppo vldc_write(dev_t dev, struct uio *uiop, cred_t *credp)
15611ae08745Sheppo {
15621ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
15631ae08745Sheppo 
15641ae08745Sheppo 	int instance;
15651ae08745Sheppo 	minor_t minor;
15661ae08745Sheppo 	size_t size;
15671ae08745Sheppo 	size_t orig_size;
15681ae08745Sheppo 	uint64_t portno;
15691ae08745Sheppo 	vldc_t *vldcp;
15701ae08745Sheppo 	vldc_port_t *vport;
15711ae08745Sheppo 	vldc_minor_t *vminor;
15721ae08745Sheppo 	int rv = EINVAL;
15731ae08745Sheppo 
15741ae08745Sheppo 	minor = getminor(dev);
15751ae08745Sheppo 	instance = VLDCINST(minor);
15761ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
15771ae08745Sheppo 	if (vldcp == NULL) {
15781ae08745Sheppo 		return (ENXIO);
15791ae08745Sheppo 	}
15801ae08745Sheppo 
15811ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
15821ae08745Sheppo 	mutex_enter(&vminor->lock);
15831ae08745Sheppo 	portno = vminor->portno;
15841ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
15851ae08745Sheppo 		mutex_exit(&vminor->lock);
15861ae08745Sheppo 		return (ENOLINK);
15871ae08745Sheppo 	}
15881ae08745Sheppo 
15891ae08745Sheppo 	vport = &(vldcp->port[portno]);
15901ae08745Sheppo 
15911ae08745Sheppo 	/* check the port status */
15921ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
15931ae08745Sheppo 		DWARN("vldc_write: vldc@%d:%lu not in the ready state\n",
15941ae08745Sheppo 		    instance, portno);
15951ae08745Sheppo 		mutex_exit(&vminor->lock);
15961ae08745Sheppo 		return (ENOTACTIVE);
15971ae08745Sheppo 	}
15981ae08745Sheppo 
15991ae08745Sheppo 	orig_size = uiop->uio_resid;
16001ae08745Sheppo 	size = orig_size;
16011ae08745Sheppo 
16021ae08745Sheppo 	if (size > vport->mtu) {
16031ae08745Sheppo 		if (vport->is_stream) {
16041ae08745Sheppo 			/* can only send MTU size at a time */
16051ae08745Sheppo 			size = vport->mtu;
16061ae08745Sheppo 		} else {
16071ae08745Sheppo 			mutex_exit(&vminor->lock);
16081ae08745Sheppo 			return (EMSGSIZE);
16091ae08745Sheppo 		}
16101ae08745Sheppo 	}
16111ae08745Sheppo 
16121ae08745Sheppo 	D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno,
16131ae08745Sheppo 	    size);
16141ae08745Sheppo 
16151ae08745Sheppo 	rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop);
16161ae08745Sheppo 	if (rv == 0) {
16171ae08745Sheppo 		rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf,
16181ae08745Sheppo 		    &size);
16191ae08745Sheppo 		if (rv != 0) {
16201ae08745Sheppo 			DWARN("vldc_write: vldc@%d:%lu failed writing %lu "
16211ae08745Sheppo 			    "bytes rv=%d\n", instance, portno, size, rv);
16221ae08745Sheppo 		}
16231ae08745Sheppo 	} else {
16241ae08745Sheppo 		size = 0;
16251ae08745Sheppo 	}
16261ae08745Sheppo 
16271ae08745Sheppo 	mutex_exit(&vminor->lock);
16281ae08745Sheppo 
16291ae08745Sheppo 	/* resid is total number of bytes *not* sent */
16301ae08745Sheppo 	uiop->uio_resid = orig_size - size;
16311ae08745Sheppo 
16321ae08745Sheppo 	return (rv);
16331ae08745Sheppo }
16341ae08745Sheppo 
16351ae08745Sheppo /* cb_chpoll */
16361ae08745Sheppo static int
16371ae08745Sheppo vldc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
16381ae08745Sheppo     struct pollhead **phpp)
16391ae08745Sheppo {
16401ae08745Sheppo 	int instance;
16411ae08745Sheppo 	minor_t minor;
16421ae08745Sheppo 	uint64_t portno;
16431ae08745Sheppo 	vldc_t *vldcp;
16441ae08745Sheppo 	vldc_port_t *vport;
16451ae08745Sheppo 	vldc_minor_t *vminor;
1646e1ebb9ecSlm66018 	boolean_t haspkts;
16471ae08745Sheppo 
16481ae08745Sheppo 	minor = getminor(dev);
16491ae08745Sheppo 	instance = VLDCINST(minor);
16501ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
16511ae08745Sheppo 	if (vldcp == NULL) {
16521ae08745Sheppo 		return (ENXIO);
16531ae08745Sheppo 	}
16541ae08745Sheppo 
16551ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
16561ae08745Sheppo 	mutex_enter(&vminor->lock);
16571ae08745Sheppo 	portno = vminor->portno;
16581ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
16591ae08745Sheppo 		mutex_exit(&vminor->lock);
16601ae08745Sheppo 		return (ENOLINK);
16611ae08745Sheppo 	}
16621ae08745Sheppo 
16631ae08745Sheppo 	vport = &(vldcp->port[portno]);
16641ae08745Sheppo 
16651ae08745Sheppo 	/* check the port status */
16661ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
16671ae08745Sheppo 		mutex_exit(&vminor->lock);
16681ae08745Sheppo 		return (ENOTACTIVE);
16691ae08745Sheppo 	}
16701ae08745Sheppo 
16711ae08745Sheppo 	D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n",
16721ae08745Sheppo 	    instance, portno, events);
16731ae08745Sheppo 
16741ae08745Sheppo 	*reventsp = 0;
16751ae08745Sheppo 
1676a8ea4edeSnarayan 	if (vport->ldc_status == LDC_UP) {
16771ae08745Sheppo 		/*
16781ae08745Sheppo 		 * Check if the receive queue is empty and if not, signal that
16791ae08745Sheppo 		 * there is data ready to read.
16801ae08745Sheppo 		 */
16811ae08745Sheppo 		if (events & POLLIN) {
1682e1ebb9ecSlm66018 			if ((ldc_chkq(vport->ldc_handle, &haspkts) == 0) &&
1683e1ebb9ecSlm66018 			    haspkts) {
16841ae08745Sheppo 				*reventsp |= POLLIN;
16851ae08745Sheppo 			}
16861ae08745Sheppo 		}
16871ae08745Sheppo 
16881ae08745Sheppo 		if (events & POLLOUT)
16891ae08745Sheppo 			*reventsp |= POLLOUT;
16901ae08745Sheppo 
16911ae08745Sheppo 	} else if (vport->hanged_up) {
16921ae08745Sheppo 		*reventsp |= POLLHUP;
16931ae08745Sheppo 		vport->hanged_up = B_FALSE;
16941ae08745Sheppo 	}
16951ae08745Sheppo 
16961ae08745Sheppo 	mutex_exit(&vminor->lock);
16971ae08745Sheppo 
16981ae08745Sheppo 	if (((*reventsp) == 0) && (!anyyet)) {
16991ae08745Sheppo 		*phpp = &vport->poll;
17001ae08745Sheppo 	}
17011ae08745Sheppo 
17021ae08745Sheppo 	D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n",
17031ae08745Sheppo 	    instance, portno, events, *reventsp);
17041ae08745Sheppo 
17051ae08745Sheppo 	return (0);
17061ae08745Sheppo }
1707