xref: /onnv-gate/usr/src/uts/sun4v/io/vldc.c (revision 11066:cebb50cbe4f9)
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 /*
23*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241991Sheppo  * Use is subject to license terms.
251991Sheppo  */
261991Sheppo 
271991Sheppo 
281991Sheppo #include <sys/types.h>
291991Sheppo #include <sys/file.h>
301991Sheppo #include <sys/errno.h>
311991Sheppo #include <sys/uio.h>
321991Sheppo #include <sys/open.h>
331991Sheppo #include <sys/cred.h>
341991Sheppo #include <sys/kmem.h>
351991Sheppo #include <sys/conf.h>
361991Sheppo #include <sys/cmn_err.h>
371991Sheppo #include <sys/ksynch.h>
381991Sheppo #include <sys/modctl.h>
391991Sheppo #include <sys/stat.h>			/* needed for S_IFBLK and S_IFCHR */
401991Sheppo #include <sys/debug.h>
411991Sheppo #include <sys/sysmacros.h>
421991Sheppo #include <sys/types.h>
431991Sheppo #include <sys/cred.h>
441991Sheppo #include <sys/promif.h>
451991Sheppo #include <sys/ddi.h>
461991Sheppo #include <sys/sunddi.h>
471991Sheppo #include <sys/cyclic.h>
481991Sheppo #include <sys/note.h>
491991Sheppo #include <sys/mach_descrip.h>
501991Sheppo #include <sys/mdeg.h>
511991Sheppo #include <sys/ldc.h>
521991Sheppo #include <sys/vldc_impl.h>
531991Sheppo 
541991Sheppo /*
551991Sheppo  * Function prototypes.
561991Sheppo  */
571991Sheppo 
581991Sheppo /* DDI entrypoints */
591991Sheppo static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
601991Sheppo static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
611991Sheppo static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
621991Sheppo static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred);
631991Sheppo static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
641991Sheppo     cred_t *credp, int *rvalp);
651991Sheppo static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp);
661991Sheppo static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp);
671991Sheppo static int vldc_chpoll(dev_t dev, short events, int anyyet,
681991Sheppo     short *reventsp, struct pollhead **phpp);
691991Sheppo 
701991Sheppo /* Internal functions */
711991Sheppo static uint_t i_vldc_cb(uint64_t event, caddr_t arg);
721991Sheppo static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
731991Sheppo static int i_vldc_mdeg_register(vldc_t *vldcp);
741991Sheppo static int i_vldc_mdeg_unregister(vldc_t *vldcp);
751991Sheppo static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node);
761991Sheppo static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno);
771991Sheppo static int i_vldc_close_port(vldc_t *vldcp, uint_t portno);
781991Sheppo 
791991Sheppo /* soft state structure */
801991Sheppo static void *vldc_ssp;
811991Sheppo 
821991Sheppo /*
831991Sheppo  * Matching criteria passed to the MDEG to register interest
841991Sheppo  * in changes to 'virtual-device-port' nodes identified by their
851991Sheppo  * 'id' property.
861991Sheppo  */
871991Sheppo static md_prop_match_t vport_prop_match[] = {
881991Sheppo 	{ MDET_PROP_VAL,    "id"   },
891991Sheppo 	{ MDET_LIST_END,    NULL    }
901991Sheppo };
911991Sheppo 
921991Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
931991Sheppo 					vport_prop_match };
941991Sheppo 
951991Sheppo /*
961991Sheppo  * Specification of an MD node passed to the MDEG to filter any
971991Sheppo  * 'virtual-device-port' nodes that do not belong to the specified
981991Sheppo  * node. This template is copied for each vldc instance and filled
991991Sheppo  * in with the appropriate 'name' and 'cfg-handle' values before
1001991Sheppo  * being passed to the MDEG.
1011991Sheppo  */
1021991Sheppo static mdeg_prop_spec_t vldc_prop_template[] = {
1031991Sheppo 	{ MDET_PROP_STR,    "name",		NULL	},
1041991Sheppo 	{ MDET_PROP_VAL,    "cfg-handle",	NULL    },
1051991Sheppo 	{ MDET_LIST_END,    NULL,		NULL    }
1061991Sheppo };
1071991Sheppo 
1081991Sheppo #define	VLDC_MDEG_PROP_NAME(specp)		((specp)[0].ps_str)
1091991Sheppo #define	VLDC_SET_MDEG_PROP_NAME(specp, name)	((specp)[0].ps_str = (name))
1101991Sheppo #define	VLDC_SET_MDEG_PROP_INST(specp, inst)	((specp)[1].ps_val = (inst))
1111991Sheppo 
1121991Sheppo 
1131991Sheppo static struct cb_ops vldc_cb_ops = {
1141991Sheppo 	vldc_open,	/* open */
1151991Sheppo 	vldc_close,	/* close */
1161991Sheppo 	nodev,		/* strategy */
1171991Sheppo 	nodev,		/* print */
1181991Sheppo 	nodev,		/* dump */
1191991Sheppo 	vldc_read,	/* read */
1201991Sheppo 	vldc_write,	/* write */
1211991Sheppo 	vldc_ioctl,	/* ioctl */
1221991Sheppo 	nodev,		/* devmap */
1231991Sheppo 	nodev,		/* mmap */
1241991Sheppo 	ddi_segmap,	/* segmap */
1251991Sheppo 	vldc_chpoll,	/* chpoll */
1261991Sheppo 	ddi_prop_op,	/* prop_op */
1271991Sheppo 	NULL,		/* stream */
1281991Sheppo 	D_NEW | D_MP	/* flag */
1291991Sheppo };
1301991Sheppo 
1311991Sheppo static struct dev_ops vldc_ops = {
1321991Sheppo 	DEVO_REV,		/* rev */
1331991Sheppo 	0,			/* ref count */
1341991Sheppo 	ddi_getinfo_1to1,	/* getinfo */
1351991Sheppo 	nulldev,		/* identify */
1361991Sheppo 	nulldev,		/* probe */
1371991Sheppo 	vldc_attach,		/* attach */
1381991Sheppo 	vldc_detach,		/* detach */
1391991Sheppo 	nodev,			/* reset */
1401991Sheppo 	&vldc_cb_ops,		/* cb_ops */
1417656SSherry.Moore@Sun.COM 	(struct bus_ops *)NULL,	/* bus_ops */
1427656SSherry.Moore@Sun.COM 	NULL,			/* power */
1437656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1441991Sheppo };
1451991Sheppo 
1461991Sheppo extern struct mod_ops mod_driverops;
1471991Sheppo 
1481991Sheppo static struct modldrv md = {
1491991Sheppo 	&mod_driverops, 			/* Type - it is a driver */
1507656SSherry.Moore@Sun.COM 	"sun4v Virtual LDC Driver",		/* Name of the module */
1511991Sheppo 	&vldc_ops,				/* driver specific ops */
1521991Sheppo };
1531991Sheppo 
1541991Sheppo static struct modlinkage ml = {
1551991Sheppo 	MODREV_1,
1561991Sheppo 	&md,
1571991Sheppo 	NULL
1581991Sheppo };
1591991Sheppo 
1601991Sheppo /* maximum MTU and cookie size tunables */
1611991Sheppo uint32_t vldc_max_mtu = VLDC_MAX_MTU;
1621991Sheppo uint64_t vldc_max_cookie = VLDC_MAX_COOKIE;
1631991Sheppo 
1643010Slm66018 /*
1653151Ssg70180  * when ldc_close() returns EAGAIN, it is retried with a wait
1663151Ssg70180  * of 'vldc_close_delay' between each retry.
1673010Slm66018  */
1683151Ssg70180 static clock_t	vldc_close_delay = VLDC_CLOSE_DELAY;
1691991Sheppo 
1701991Sheppo #ifdef DEBUG
1711991Sheppo 
1721991Sheppo /*
1731991Sheppo  * Print debug messages
1741991Sheppo  *
1751991Sheppo  * set vldcdbg to 0x7 to enable all messages
1761991Sheppo  *
1771991Sheppo  * 0x4 - Warnings
1781991Sheppo  * 0x2 - All debug messages (most verbose)
1791991Sheppo  * 0x1 - Minimal debug messages
1801991Sheppo  */
1811991Sheppo 
1821991Sheppo int vldcdbg = 0x0;
1831991Sheppo 
1841991Sheppo static void
vldcdebug(const char * fmt,...)1851991Sheppo vldcdebug(const char *fmt, ...)
1861991Sheppo {
1871991Sheppo 	char buf[512];
1881991Sheppo 	va_list ap;
1891991Sheppo 
1901991Sheppo 	va_start(ap, fmt);
1911991Sheppo 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
1921991Sheppo 	va_end(ap);
1931991Sheppo 
1941991Sheppo 	cmn_err(CE_CONT, "?%s", buf);
1951991Sheppo }
1961991Sheppo 
1971991Sheppo #define	D1	if (vldcdbg & 0x01) vldcdebug
1981991Sheppo #define	D2	if (vldcdbg & 0x02) vldcdebug
1991991Sheppo #define	DWARN	if (vldcdbg & 0x04) vldcdebug
2001991Sheppo 
2011991Sheppo #else /* not DEBUG */
2021991Sheppo 
2031991Sheppo #define	D1	if (0) printf
2041991Sheppo #define	D2	if (0) printf
2051991Sheppo #define	DWARN	if (0) printf
2061991Sheppo 
2071991Sheppo #endif /* not DEBUG */
2081991Sheppo 
2091991Sheppo 
2101991Sheppo /* _init(9E): initialize the loadable module */
2111991Sheppo int
_init(void)2121991Sheppo _init(void)
2131991Sheppo {
2141991Sheppo 	int error;
2151991Sheppo 
2161991Sheppo 	/* init the soft state structure */
2171991Sheppo 	error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1);
2181991Sheppo 	if (error != 0) {
2191991Sheppo 		return (error);
2201991Sheppo 	}
2211991Sheppo 
2221991Sheppo 	/* Link the driver into the system */
2231991Sheppo 	error = mod_install(&ml);
2241991Sheppo 
2251991Sheppo 	return (error);
2261991Sheppo }
2271991Sheppo 
2281991Sheppo /* _info(9E): return information about the loadable module */
2291991Sheppo int
_info(struct modinfo * modinfop)2301991Sheppo _info(struct modinfo *modinfop)
2311991Sheppo {
2321991Sheppo 	/* Report status of the dynamically loadable driver module */
2331991Sheppo 	return (mod_info(&ml, modinfop));
2341991Sheppo }
2351991Sheppo 
2361991Sheppo /* _fini(9E): prepare the module for unloading. */
2371991Sheppo int
_fini(void)2381991Sheppo _fini(void)
2391991Sheppo {
2401991Sheppo 	int error;
2411991Sheppo 
2421991Sheppo 	/* Unlink the driver module from the system */
2431991Sheppo 	if ((error = mod_remove(&ml)) == 0) {
2441991Sheppo 		/*
2451991Sheppo 		 * We have successfully "removed" the driver.
2461991Sheppo 		 * destroy soft state
2471991Sheppo 		 */
2481991Sheppo 		ddi_soft_state_fini(&vldc_ssp);
2491991Sheppo 	}
2501991Sheppo 
2511991Sheppo 	return (error);
2521991Sheppo }
2531991Sheppo 
2541991Sheppo /* ldc callback */
2551991Sheppo static uint_t
i_vldc_cb(uint64_t event,caddr_t arg)2561991Sheppo i_vldc_cb(uint64_t event, caddr_t arg)
2571991Sheppo {
2582841Snarayan 	int 		rv;
2592793Slm66018 	vldc_port_t	*vport = (vldc_port_t *)arg;
2602841Snarayan 	ldc_status_t	old_status;
2612793Slm66018 	short		pollevents = 0;
2621991Sheppo 
2633010Slm66018 	ASSERT(vport != NULL);
2643010Slm66018 	ASSERT(vport->minorp != NULL);
2653010Slm66018 
2662793Slm66018 	D1("i_vldc_cb: vldc@%d:%d callback invoked, channel=0x%lx, "
2672793Slm66018 	    "event=0x%lx\n", vport->inst, vport->number, vport->ldc_id, event);
2681991Sheppo 
2693010Slm66018 	/* ensure the port can't be destroyed while we are handling the cb */
2703010Slm66018 	mutex_enter(&vport->minorp->lock);
2713010Slm66018 
2723151Ssg70180 	if (vport->status == VLDC_PORT_CLOSED) {
2733151Ssg70180 		return (LDC_SUCCESS);
2743151Ssg70180 	}
2753151Ssg70180 
2762841Snarayan 	old_status = vport->ldc_status;
2772841Snarayan 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
2782841Snarayan 	if (rv != 0) {
2792841Snarayan 		DWARN("i_vldc_cb: vldc@%d:%d could not get ldc status, "
2802841Snarayan 		    "rv=%d\n", vport->inst, vport->number, rv);
2813010Slm66018 		mutex_exit(&vport->minorp->lock);
2822841Snarayan 		return (LDC_SUCCESS);
2832841Snarayan 	}
2842841Snarayan 
2851991Sheppo 	if (event & LDC_EVT_UP) {
2861991Sheppo 		pollevents |= POLLOUT;
2871991Sheppo 		vport->hanged_up = B_FALSE;
2881991Sheppo 
2892793Slm66018 	} else if (event & LDC_EVT_RESET) {
2902793Slm66018 		/*
2912841Snarayan 		 * Mark the port in reset, if it is not CLOSED and
2922841Snarayan 		 * the channel was previously in LDC_UP state. This
2932841Snarayan 		 * implies that the port cannot be used until it has
2942841Snarayan 		 * been closed and reopened.
2952793Slm66018 		 */
2963151Ssg70180 		if (old_status == LDC_UP) {
2972793Slm66018 			vport->status = VLDC_PORT_RESET;
2982841Snarayan 			vport->hanged_up = B_TRUE;
2992841Snarayan 			pollevents = POLLHUP;
3002841Snarayan 		} else {
3012841Snarayan 			rv = ldc_up(vport->ldc_handle);
3022841Snarayan 			if (rv) {
3032841Snarayan 				DWARN("i_vldc_cb: vldc@%d:%d cannot bring "
3042841Snarayan 				    "channel UP rv=%d\n", vport->inst,
3052841Snarayan 				    vport->number, rv);
3063010Slm66018 				mutex_exit(&vport->minorp->lock);
3072841Snarayan 				return (LDC_SUCCESS);
3082841Snarayan 			}
3092841Snarayan 			rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
3102841Snarayan 			if (rv != 0) {
3112841Snarayan 				DWARN("i_vldc_cb: vldc@%d:%d could not get "
3122841Snarayan 				    "ldc status, rv=%d\n", vport->inst,
3132841Snarayan 				    vport->number, rv);
3143010Slm66018 				mutex_exit(&vport->minorp->lock);
3152841Snarayan 				return (LDC_SUCCESS);
3162841Snarayan 			}
3172841Snarayan 			if (vport->ldc_status == LDC_UP) {
3182841Snarayan 				pollevents |= POLLOUT;
3192841Snarayan 				vport->hanged_up = B_FALSE;
3202841Snarayan 			}
3212841Snarayan 		}
3221991Sheppo 
3232793Slm66018 	} else if (event & LDC_EVT_DOWN) {
3242793Slm66018 		/*
3252841Snarayan 		 * The other side went away - mark port in RESET state
3262793Slm66018 		 */
3272841Snarayan 		vport->status = VLDC_PORT_RESET;
3282793Slm66018 		vport->hanged_up = B_TRUE;
3292793Slm66018 		pollevents = POLLHUP;
3301991Sheppo 	}
3311991Sheppo 
3321991Sheppo 	if (event & LDC_EVT_READ)
3331991Sheppo 		pollevents |= POLLIN;
3341991Sheppo 
3353010Slm66018 	mutex_exit(&vport->minorp->lock);
3363010Slm66018 
3371991Sheppo 	if (pollevents != 0) {
3381991Sheppo 		D1("i_vldc_cb: port@%d pollwakeup=0x%x\n",
3391991Sheppo 		    vport->number, pollevents);
3401991Sheppo 		pollwakeup(&vport->poll, pollevents);
3411991Sheppo 	}
3421991Sheppo 
3431991Sheppo 	return (LDC_SUCCESS);
3441991Sheppo }
3451991Sheppo 
3461991Sheppo /* mdeg callback */
3471991Sheppo static int
i_vldc_mdeg_cb(void * cb_argp,mdeg_result_t * resp)3481991Sheppo i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
3491991Sheppo {
3501991Sheppo 	vldc_t		*vldcp;
3511991Sheppo 	int		idx;
3521991Sheppo 	uint64_t	portno;
3531991Sheppo 	int		rv;
3541991Sheppo 	md_t		*mdp;
3551991Sheppo 	mde_cookie_t	node;
3561991Sheppo 
3571991Sheppo 	if (resp == NULL) {
3581991Sheppo 		D1("i_vldc_mdeg_cb: no result returned\n");
3591991Sheppo 		return (MDEG_FAILURE);
3601991Sheppo 	}
3611991Sheppo 
3621991Sheppo 	vldcp = (vldc_t *)cb_argp;
3631991Sheppo 
3641991Sheppo 	mutex_enter(&vldcp->lock);
3651991Sheppo 	if (vldcp->detaching == B_TRUE) {
3661991Sheppo 		D1("i_vldc_mdeg_cb: detach in progress\n");
3671991Sheppo 		mutex_exit(&vldcp->lock);
3681991Sheppo 		return (MDEG_FAILURE);
3691991Sheppo 	}
3701991Sheppo 
3711991Sheppo 	D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n",
3721991Sheppo 	    resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
3731991Sheppo 
3741991Sheppo 	/* process added ports */
3751991Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
3761991Sheppo 		mdp = resp->added.mdp;
3771991Sheppo 		node = resp->added.mdep[idx];
3781991Sheppo 
3791991Sheppo 		D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node);
3801991Sheppo 
3811991Sheppo 		/* attempt to add a port */
3821991Sheppo 		if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) {
3831991Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, "
3841991Sheppo 			    "err = %d", rv);
3851991Sheppo 		}
3861991Sheppo 	}
3871991Sheppo 
3881991Sheppo 	/* process removed ports */
3891991Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
3901991Sheppo 		mdp = resp->removed.mdp;
3911991Sheppo 		node = resp->removed.mdep[idx];
3921991Sheppo 
3931991Sheppo 		D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node);
3941991Sheppo 
3951991Sheppo 		/* read in the port's id property */
3961991Sheppo 		if (md_get_prop_val(mdp, node, "id", &portno)) {
3971991Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of "
3981991Sheppo 			    "removed list has no 'id' property", node);
3991991Sheppo 			continue;
4001991Sheppo 		}
4011991Sheppo 
4021991Sheppo 		/* attempt to remove a port */
4031991Sheppo 		if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) {
4041991Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove "
4051991Sheppo 			    "port %lu, err %d", portno, rv);
4061991Sheppo 		}
4071991Sheppo 	}
4081991Sheppo 
4091991Sheppo 	/*
4101991Sheppo 	 * Currently no support for updating already active ports. So, ignore
4111991Sheppo 	 * the match_curr and match_prev arrays for now.
4121991Sheppo 	 */
4131991Sheppo 
4141991Sheppo 	mutex_exit(&vldcp->lock);
4151991Sheppo 
4161991Sheppo 	return (MDEG_SUCCESS);
4171991Sheppo }
4181991Sheppo 
4191991Sheppo /* register callback to mdeg */
4201991Sheppo static int
i_vldc_mdeg_register(vldc_t * vldcp)4211991Sheppo i_vldc_mdeg_register(vldc_t *vldcp)
4221991Sheppo {
4231991Sheppo 	mdeg_prop_spec_t *pspecp;
4241991Sheppo 	mdeg_node_spec_t *inst_specp;
4251991Sheppo 	mdeg_handle_t	mdeg_hdl;
4261991Sheppo 	size_t		templatesz;
4271991Sheppo 	int		inst;
4281991Sheppo 	char		*name;
4291991Sheppo 	size_t		namesz;
4301991Sheppo 	char		*nameprop;
4311991Sheppo 	int		rv;
4321991Sheppo 
4331991Sheppo 	/* get the unique vldc instance assigned by the LDom manager */
4341991Sheppo 	inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip,
4351991Sheppo 	    DDI_PROP_DONTPASS, "reg", -1);
4361991Sheppo 	if (inst == -1) {
4371991Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'reg' property",
4381991Sheppo 		    ddi_get_instance(vldcp->dip));
4391991Sheppo 		return (DDI_FAILURE);
4401991Sheppo 	}
4411991Sheppo 
4421991Sheppo 	/* get the name of the vldc instance */
4431991Sheppo 	rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip,
4441991Sheppo 	    DDI_PROP_DONTPASS, "name", &nameprop);
4451991Sheppo 	if (rv != DDI_PROP_SUCCESS) {
4461991Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'name' property",
4471991Sheppo 		    ddi_get_instance(vldcp->dip));
4481991Sheppo 		return (DDI_FAILURE);
4491991Sheppo 	}
4501991Sheppo 
4511991Sheppo 	D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst);
4521991Sheppo 
4531991Sheppo 	/*
4541991Sheppo 	 * Allocate and initialize a per-instance copy
4551991Sheppo 	 * of the global property spec array that will
4561991Sheppo 	 * uniquely identify this vldc instance.
4571991Sheppo 	 */
4581991Sheppo 	templatesz = sizeof (vldc_prop_template);
4591991Sheppo 	pspecp = kmem_alloc(templatesz, KM_SLEEP);
4601991Sheppo 
4611991Sheppo 	bcopy(vldc_prop_template, pspecp, templatesz);
4621991Sheppo 
4631991Sheppo 	/* copy in the name property */
4641991Sheppo 	namesz = strlen(nameprop) + 1;
4651991Sheppo 	name = kmem_alloc(namesz, KM_SLEEP);
4661991Sheppo 
4671991Sheppo 	bcopy(nameprop, name, namesz);
4681991Sheppo 	VLDC_SET_MDEG_PROP_NAME(pspecp, name);
4692336Snarayan 	ddi_prop_free(nameprop);
4701991Sheppo 
4711991Sheppo 	/* copy in the instance property */
4721991Sheppo 	VLDC_SET_MDEG_PROP_INST(pspecp, inst);
4731991Sheppo 
4741991Sheppo 	/* initialize the complete prop spec structure */
4751991Sheppo 	inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
4761991Sheppo 	inst_specp->namep = "virtual-device";
4771991Sheppo 	inst_specp->specp = pspecp;
4781991Sheppo 
4791991Sheppo 	/* perform the registration */
4801991Sheppo 	rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb,
4811991Sheppo 	    vldcp, &mdeg_hdl);
4821991Sheppo 
4831991Sheppo 	if (rv != MDEG_SUCCESS) {
4841991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register "
4851991Sheppo 		    "failed, err = %d", rv);
4861991Sheppo 		kmem_free(name, namesz);
4871991Sheppo 		kmem_free(pspecp, templatesz);
4881991Sheppo 		kmem_free(inst_specp, sizeof (mdeg_node_spec_t));
4891991Sheppo 		return (DDI_FAILURE);
4901991Sheppo 	}
4911991Sheppo 
4921991Sheppo 	/* save off data that will be needed later */
4931991Sheppo 	vldcp->inst_spec = inst_specp;
4941991Sheppo 	vldcp->mdeg_hdl = mdeg_hdl;
4951991Sheppo 
4961991Sheppo 	return (DDI_SUCCESS);
4971991Sheppo }
4981991Sheppo 
4991991Sheppo /* unregister callback from mdeg */
5001991Sheppo static int
i_vldc_mdeg_unregister(vldc_t * vldcp)5011991Sheppo i_vldc_mdeg_unregister(vldc_t *vldcp)
5021991Sheppo {
5031991Sheppo 	char	*name;
5041991Sheppo 	int	rv;
5051991Sheppo 
5061991Sheppo 	D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl);
5071991Sheppo 
5081991Sheppo 	rv = mdeg_unregister(vldcp->mdeg_hdl);
5091991Sheppo 	if (rv != MDEG_SUCCESS) {
5101991Sheppo 		return (rv);
5111991Sheppo 	}
5121991Sheppo 
5131991Sheppo 	/*
5141991Sheppo 	 * Clean up cached MDEG data
5151991Sheppo 	 */
5161991Sheppo 	name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp);
5171991Sheppo 	if (name != NULL) {
5181991Sheppo 		kmem_free(name, strlen(name) + 1);
5191991Sheppo 	}
5201991Sheppo 	kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template));
5211991Sheppo 	vldcp->inst_spec->specp = NULL;
5221991Sheppo 
5231991Sheppo 	kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t));
5241991Sheppo 	vldcp->inst_spec = NULL;
5251991Sheppo 
5261991Sheppo 	return (MDEG_SUCCESS);
5271991Sheppo }
5281991Sheppo 
5291991Sheppo static int
i_vldc_get_port_channel(md_t * mdp,mde_cookie_t node,uint64_t * ldc_id)5301991Sheppo i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
5311991Sheppo {
5321991Sheppo 	int num_nodes, nchan;
5331991Sheppo 	size_t listsz;
5341991Sheppo 	mde_cookie_t *listp;
5351991Sheppo 
5361991Sheppo 	/*
5371991Sheppo 	 * Find the channel-endpoint node(s) (which should be under this
5381991Sheppo 	 * port node) which contain the channel id(s).
5391991Sheppo 	 */
5401991Sheppo 	if ((num_nodes = md_node_count(mdp)) <= 0) {
5411991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of "
5421991Sheppo 		    "channel-endpoint nodes found (%d)", num_nodes);
5431991Sheppo 		return (-1);
5441991Sheppo 	}
5451991Sheppo 
5461991Sheppo 	/* allocate space for node list */
5471991Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
5481991Sheppo 	listp = kmem_alloc(listsz, KM_SLEEP);
5491991Sheppo 
5501991Sheppo 	nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
5511991Sheppo 	    md_find_name(mdp, "fwd"), listp);
5521991Sheppo 
5531991Sheppo 	if (nchan <= 0) {
5541991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint"
5551991Sheppo 		    " nodes found");
5561991Sheppo 		kmem_free(listp, listsz);
5571991Sheppo 		return (-1);
5581991Sheppo 	}
5591991Sheppo 
5601991Sheppo 	D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan);
5611991Sheppo 
5621991Sheppo 	/* use property from first node found */
5631991Sheppo 	if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
5641991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint "
5651991Sheppo 		    "has no 'id' property");
5661991Sheppo 		kmem_free(listp, listsz);
5671991Sheppo 		return (-1);
5681991Sheppo 	}
5691991Sheppo 
5701991Sheppo 	kmem_free(listp, listsz);
5711991Sheppo 
5721991Sheppo 	return (0);
5731991Sheppo }
5741991Sheppo 
5751991Sheppo /* add a vldc port */
5761991Sheppo static int
i_vldc_add_port(vldc_t * vldcp,md_t * mdp,mde_cookie_t node)5771991Sheppo i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node)
5781991Sheppo {
5791991Sheppo 	vldc_port_t	*vport;
5801991Sheppo 	char		*sname;
5811991Sheppo 	uint64_t	portno;
5821991Sheppo 	int		vldc_inst;
5831991Sheppo 	minor_t		minor;
5841991Sheppo 	int		minor_idx;
5851991Sheppo 	boolean_t	new_minor;
5861991Sheppo 	int		rv;
5871991Sheppo 
5883010Slm66018 	ASSERT(MUTEX_HELD(&vldcp->lock));
5893010Slm66018 
5901991Sheppo 	/* read in the port's id property */
5911991Sheppo 	if (md_get_prop_val(mdp, node, "id", &portno)) {
5921991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added "
5931991Sheppo 		    "list has no 'id' property", node);
5941991Sheppo 		return (MDEG_FAILURE);
5951991Sheppo 	}
5961991Sheppo 
5971991Sheppo 	if (portno >= VLDC_MAX_PORTS) {
5981991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) "
5991991Sheppo 		    "larger than maximum supported number of ports", portno);
6001991Sheppo 		return (MDEG_FAILURE);
6011991Sheppo 	}
6021991Sheppo 
6031991Sheppo 	vport = &(vldcp->port[portno]);
6041991Sheppo 
6051991Sheppo 	if (vport->minorp != NULL) {
6061991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
6071991Sheppo 		    " which is already bound", portno);
6081991Sheppo 		return (MDEG_FAILURE);
6091991Sheppo 	}
6101991Sheppo 
6111991Sheppo 	vport->number = portno;
6121991Sheppo 
6131991Sheppo 	/* get all channels for this device (currently only one) */
6141991Sheppo 	if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) {
6151991Sheppo 		return (MDEG_FAILURE);
6161991Sheppo 	}
6171991Sheppo 
6181991Sheppo 	/* set the default MTU */
6192600Sjm22469 	vport->mtu = VLDC_DEFAULT_MTU;
6201991Sheppo 
6211991Sheppo 	/* get the service being exported by this port */
6221991Sheppo 	if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) {
6231991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no "
6241991Sheppo 		    "'vldc-svc-name' property");
6251991Sheppo 		return (MDEG_FAILURE);
6261991Sheppo 	}
6271991Sheppo 
6281991Sheppo 	/* minor number look up */
6291991Sheppo 	for (minor_idx = 0; minor_idx < vldcp->minors_assigned;
6301991Sheppo 	    minor_idx++) {
6311991Sheppo 		if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) {
6321991Sheppo 			/* found previously assigned minor number */
6331991Sheppo 			break;
6341991Sheppo 		}
6351991Sheppo 	}
6361991Sheppo 
6371991Sheppo 	new_minor = B_FALSE;
6381991Sheppo 	if (minor_idx == vldcp->minors_assigned) {
6391991Sheppo 		/* end of lookup - assign new minor number */
6401991Sheppo 		if (vldcp->minors_assigned == VLDC_MAX_MINORS) {
6411991Sheppo 			cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor "
6421991Sheppo 			    "nodes (%d)", minor_idx);
6431991Sheppo 			return (MDEG_FAILURE);
6441991Sheppo 		}
6451991Sheppo 
6461991Sheppo 		(void) strlcpy(vldcp->minor_tbl[minor_idx].sname,
6471991Sheppo 		    sname, MAXPATHLEN);
6481991Sheppo 
6491991Sheppo 		vldcp->minors_assigned++;
6501991Sheppo 		new_minor = B_TRUE;
6511991Sheppo 	}
6521991Sheppo 
6533010Slm66018 	if (vldcp->minor_tbl[minor_idx].portno != VLDC_INVALID_PORTNO) {
6543010Slm66018 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
6553010Slm66018 		    " which has a minor number in use by port (%u)",
6563010Slm66018 		    portno, vldcp->minor_tbl[minor_idx].portno);
6573010Slm66018 		return (MDEG_FAILURE);
6583010Slm66018 	}
6591991Sheppo 
6602793Slm66018 	vldc_inst = ddi_get_instance(vldcp->dip);
6612793Slm66018 
6622793Slm66018 	vport->inst = vldc_inst;
6631991Sheppo 	vport->minorp = &vldcp->minor_tbl[minor_idx];
6641991Sheppo 	vldcp->minor_tbl[minor_idx].portno = portno;
6651991Sheppo 	vldcp->minor_tbl[minor_idx].in_use = 0;
6661991Sheppo 
6672793Slm66018 	D1("i_vldc_add_port: vldc@%d:%d  mtu=%d, ldc=%ld, service=%s\n",
6682793Slm66018 	    vport->inst, vport->number, vport->mtu, vport->ldc_id, sname);
6691991Sheppo 
6701991Sheppo 	/*
6711991Sheppo 	 * Create a minor node. The minor number is
6721991Sheppo 	 * (vldc_inst << VLDC_INST_SHIFT) | minor_idx
6731991Sheppo 	 */
6741991Sheppo 	minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx);
6751991Sheppo 
6761991Sheppo 	rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR,
6771991Sheppo 	    minor, DDI_NT_SERIAL, 0);
6781991Sheppo 
6791991Sheppo 	if (rv != DDI_SUCCESS) {
6801991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor"
6811991Sheppo 		    "node (%u), err = %d", minor, rv);
6821991Sheppo 		vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO;
6831991Sheppo 		if (new_minor) {
6841991Sheppo 			vldcp->minors_assigned--;
6851991Sheppo 		}
6861991Sheppo 		return (MDEG_FAILURE);
6871991Sheppo 	}
6881991Sheppo 
6891991Sheppo 	/*
6901991Sheppo 	 * The port is now bound to a minor node and is initially in the
6911991Sheppo 	 * closed state.
6921991Sheppo 	 */
6931991Sheppo 	vport->status = VLDC_PORT_CLOSED;
6941991Sheppo 
6951991Sheppo 	D1("i_vldc_add_port: port %lu initialized\n", portno);
6961991Sheppo 
6971991Sheppo 	return (MDEG_SUCCESS);
6981991Sheppo }
6991991Sheppo 
7001991Sheppo /* remove a vldc port */
7011991Sheppo static int
i_vldc_remove_port(vldc_t * vldcp,uint_t portno)7021991Sheppo i_vldc_remove_port(vldc_t *vldcp, uint_t portno)
7031991Sheppo {
7041991Sheppo 	vldc_port_t *vport;
7051991Sheppo 	vldc_minor_t *vminor;
7061991Sheppo 
7073010Slm66018 	ASSERT(vldcp != NULL);
7083010Slm66018 	ASSERT(MUTEX_HELD(&vldcp->lock));
7093010Slm66018 
7101991Sheppo 	vport = &(vldcp->port[portno]);
7111991Sheppo 	vminor = vport->minorp;
7121991Sheppo 	if (vminor == NULL) {
7131991Sheppo 		cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a "
7141991Sheppo 		    "port (%u) which is not bound", portno);
7151991Sheppo 		return (MDEG_FAILURE);
7161991Sheppo 	}
7171991Sheppo 
7181991Sheppo 	/*
7191991Sheppo 	 * Make sure that all new attempts to open or use the minor node
7201991Sheppo 	 * associated with the port will fail.
7211991Sheppo 	 */
7221991Sheppo 	mutex_enter(&vminor->lock);
7231991Sheppo 	vminor->portno = VLDC_INVALID_PORTNO;
7241991Sheppo 	mutex_exit(&vminor->lock);
7251991Sheppo 
7261991Sheppo 	/* send hangup to anyone polling */
7271991Sheppo 	pollwakeup(&vport->poll, POLLHUP);
7281991Sheppo 
7291991Sheppo 	/* Now wait for all current users of the minor node to finish. */
7301991Sheppo 	mutex_enter(&vminor->lock);
7311991Sheppo 	while (vminor->in_use > 0) {
7321991Sheppo 		cv_wait(&vminor->cv, &vminor->lock);
7331991Sheppo 	}
7341991Sheppo 
7352793Slm66018 	if (vport->status != VLDC_PORT_CLOSED) {
7361991Sheppo 		/* close the port before it is torn down */
7371991Sheppo 		(void) i_vldc_close_port(vldcp, portno);
7381991Sheppo 	}
7391991Sheppo 
7401991Sheppo 	/* remove minor node */
7411991Sheppo 	ddi_remove_minor_node(vldcp->dip, vport->minorp->sname);
7421991Sheppo 	vport->minorp = NULL;
7431991Sheppo 
7441991Sheppo 	mutex_exit(&vminor->lock);
7451991Sheppo 
7461991Sheppo 	D1("i_vldc_remove_port: removed vldc port %u\n", portno);
7471991Sheppo 
7481991Sheppo 	return (MDEG_SUCCESS);
7491991Sheppo }
7501991Sheppo 
7513010Slm66018 /*
7523010Slm66018  * Close and destroy the ldc channel associated with the port 'vport'
7533010Slm66018  *
7543010Slm66018  * NOTE It may not be possible close and destroy the channel if resources
7553010Slm66018  *	are still in use so the fucntion may exit before all the teardown
7563010Slm66018  *	operations are completed and would have to be called again by the
7573010Slm66018  *	vldc framework.
7583010Slm66018  *
7593010Slm66018  *	This function needs to be able to handle the case where it is called
7603010Slm66018  *	more than once and has to pick up from where it left off.
7613010Slm66018  */
7621991Sheppo static int
i_vldc_ldc_close(vldc_port_t * vport)7631991Sheppo i_vldc_ldc_close(vldc_port_t *vport)
7641991Sheppo {
7653010Slm66018 	int err = 0;
7663010Slm66018 
7673010Slm66018 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
7681991Sheppo 
7693010Slm66018 	/*
7703010Slm66018 	 * If ldc_close() succeeded or if the channel was already closed[*]
7713010Slm66018 	 * (possibly by a previously unsuccessful call to this function)
7723010Slm66018 	 * we keep going and try to teardown the rest of the LDC state,
7733010Slm66018 	 * otherwise we bail out.
7743010Slm66018 	 *
7753010Slm66018 	 * [*] indicated by ldc_close() returning a value of EFAULT
7763010Slm66018 	 */
7773151Ssg70180 	err = ldc_close(vport->ldc_handle);
7783010Slm66018 	if ((err != 0) && (err != EFAULT))
7793010Slm66018 		return (err);
7803010Slm66018 
7813010Slm66018 	err = ldc_unreg_callback(vport->ldc_handle);
7821991Sheppo 	if (err != 0)
7833010Slm66018 		return (err);
7843010Slm66018 
7851991Sheppo 	err = ldc_fini(vport->ldc_handle);
7863010Slm66018 	if (err != 0)
7873010Slm66018 		return (err);
7881991Sheppo 
7892793Slm66018 	vport->status = VLDC_PORT_OPEN;
7902793Slm66018 
7913010Slm66018 	return (0);
7921991Sheppo }
7931991Sheppo 
7941991Sheppo /* close a vldc port */
7951991Sheppo static int
i_vldc_close_port(vldc_t * vldcp,uint_t portno)7961991Sheppo i_vldc_close_port(vldc_t *vldcp, uint_t portno)
7971991Sheppo {
7982793Slm66018 	vldc_port_t	*vport;
7993151Ssg70180 	vldc_minor_t	*vminor;
8002793Slm66018 	int		rv = DDI_SUCCESS;
8011991Sheppo 
8021991Sheppo 	vport = &(vldcp->port[portno]);
8031991Sheppo 
8041991Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
8051991Sheppo 
8062793Slm66018 	D1("i_vldc_close_port: vldc@%d:%d: closing port\n",
8072793Slm66018 	    vport->inst, vport->minorp->portno);
8082793Slm66018 
8093151Ssg70180 	vminor = vport->minorp;
8103151Ssg70180 
8112793Slm66018 	switch (vport->status) {
8122793Slm66018 	case VLDC_PORT_CLOSED:
8131991Sheppo 		/* nothing to do */
8141991Sheppo 		DWARN("i_vldc_close_port: port %d in an unexpected "
8151991Sheppo 		    "state (%d)\n", portno, vport->status);
8161991Sheppo 		return (DDI_SUCCESS);
8172793Slm66018 
8182793Slm66018 	case VLDC_PORT_READY:
8192793Slm66018 	case VLDC_PORT_RESET:
8203151Ssg70180 		do {
8213151Ssg70180 			rv = i_vldc_ldc_close(vport);
8223151Ssg70180 			if (rv != EAGAIN)
8233151Ssg70180 				break;
8243151Ssg70180 
8253151Ssg70180 			/*
8263151Ssg70180 			 * EAGAIN indicates that ldc_close() failed because
8273151Ssg70180 			 * ldc callback thread is active for the channel.
8283151Ssg70180 			 * cv_timedwait() is used to release vminor->lock and
8293151Ssg70180 			 * allow ldc callback thread to complete.
8303151Ssg70180 			 * after waking up, check if the port has been closed
8313151Ssg70180 			 * by another thread in the meantime.
8323151Ssg70180 			 */
833*11066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&vminor->cv, &vminor->lock,
834*11066Srafael.vanoni@sun.com 			    drv_usectohz(vldc_close_delay), TR_CLOCK_TICK);
8353151Ssg70180 			rv = 0;
8363151Ssg70180 		} while (vport->status != VLDC_PORT_CLOSED);
8373151Ssg70180 
8383151Ssg70180 		if ((rv != 0) || (vport->status == VLDC_PORT_CLOSED))
8393010Slm66018 			return (rv);
8403151Ssg70180 
8412793Slm66018 		break;
8423010Slm66018 
8433010Slm66018 	case VLDC_PORT_OPEN:
8443010Slm66018 		break;
8453010Slm66018 
8463010Slm66018 	default:
8473010Slm66018 		DWARN("i_vldc_close_port: port %d in an unexpected "
8483010Slm66018 		    "state (%d)\n", portno, vport->status);
8493010Slm66018 		ASSERT(0);	/* fail quickly to help diagnosis */
8503010Slm66018 		return (EINVAL);
8511991Sheppo 	}
8521991Sheppo 
8532793Slm66018 	ASSERT(vport->status == VLDC_PORT_OPEN);
8541991Sheppo 
8551991Sheppo 	/* free memory */
8561991Sheppo 	kmem_free(vport->send_buf, vport->mtu);
8571991Sheppo 	kmem_free(vport->recv_buf, vport->mtu);
8581991Sheppo 
8593151Ssg70180 	if (strcmp(vminor->sname, VLDC_HVCTL_SVCNAME) == 0)
8602336Snarayan 		kmem_free(vport->cookie_buf, vldc_max_cookie);
8612336Snarayan 
8621991Sheppo 	vport->status = VLDC_PORT_CLOSED;
8631991Sheppo 
8641991Sheppo 	return (rv);
8651991Sheppo }
8661991Sheppo 
8671991Sheppo /*
8681991Sheppo  * attach(9E): attach a device to the system.
8691991Sheppo  * called once for each instance of the device on the system.
8701991Sheppo  */
8711991Sheppo static int
vldc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)8721991Sheppo vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
8731991Sheppo {
8741991Sheppo 	int 	i, instance;
8751991Sheppo 	vldc_t	*vldcp;
8761991Sheppo 
8771991Sheppo 	switch (cmd) {
8781991Sheppo 
8791991Sheppo 	case DDI_ATTACH:
8801991Sheppo 
8811991Sheppo 		instance = ddi_get_instance(dip);
8821991Sheppo 
8831991Sheppo 		if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) {
8841991Sheppo 			return (DDI_FAILURE);
8851991Sheppo 		}
8861991Sheppo 
8871991Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
8881991Sheppo 		if (vldcp == NULL) {
8891991Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
8901991Sheppo 			return (ENXIO);
8911991Sheppo 		}
8921991Sheppo 
8931991Sheppo 		D1("vldc_attach: DDI_ATTACH instance=%d\n", instance);
8941991Sheppo 
8951991Sheppo 		mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL);
8961991Sheppo 		vldcp->dip = dip;
8971991Sheppo 		vldcp->detaching = B_FALSE;
8981991Sheppo 
8991991Sheppo 		for (i = 0; i < VLDC_MAX_PORTS; i++) {
9001991Sheppo 			/* No minor node association to start with */
9011991Sheppo 			vldcp->port[i].minorp = NULL;
9021991Sheppo 		}
9031991Sheppo 
9041991Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
9051991Sheppo 			mutex_init(&(vldcp->minor_tbl[i].lock), NULL,
9061991Sheppo 			    MUTEX_DRIVER, NULL);
9071991Sheppo 			cv_init(&(vldcp->minor_tbl[i].cv), NULL,
9081991Sheppo 			    CV_DRIVER, NULL);
9091991Sheppo 			/* No port association to start with */
9101991Sheppo 			vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO;
9111991Sheppo 		}
9121991Sheppo 
9131991Sheppo 		/* Register for MD update notification */
9141991Sheppo 		if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) {
9151991Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
9161991Sheppo 			return (DDI_FAILURE);
9171991Sheppo 		}
9181991Sheppo 
9191991Sheppo 		return (DDI_SUCCESS);
9201991Sheppo 
9211991Sheppo 	case DDI_RESUME:
9221991Sheppo 
9231991Sheppo 		return (DDI_SUCCESS);
9241991Sheppo 
9251991Sheppo 	default:
9261991Sheppo 
9271991Sheppo 		return (DDI_FAILURE);
9281991Sheppo 	}
9291991Sheppo }
9301991Sheppo 
9311991Sheppo /*
9321991Sheppo  * detach(9E): detach a device from the system.
9331991Sheppo  */
9341991Sheppo static int
vldc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)9351991Sheppo vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
9361991Sheppo {
9371991Sheppo 	int 		i, instance;
9381991Sheppo 	vldc_t		*vldcp;
9391991Sheppo 
9401991Sheppo 	switch (cmd) {
9411991Sheppo 
9421991Sheppo 	case DDI_DETACH:
9431991Sheppo 
9441991Sheppo 		instance = ddi_get_instance(dip);
9451991Sheppo 
9461991Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
9471991Sheppo 		if (vldcp == NULL) {
9481991Sheppo 			return (DDI_FAILURE);
9491991Sheppo 		}
9501991Sheppo 
9511991Sheppo 		D1("vldc_detach: DDI_DETACH instance=%d\n", instance);
9521991Sheppo 
9531991Sheppo 		mutex_enter(&vldcp->lock);
9541991Sheppo 
9551991Sheppo 		/* Fail the detach if all ports have not been removed. */
9561991Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
9571991Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9581991Sheppo 				D1("vldc_detach: vldc@%d:%d is bound, "
9591991Sheppo 				    "detach failed\n",
9601991Sheppo 				    instance, vldcp->minor_tbl[i].portno);
9611991Sheppo 				mutex_exit(&vldcp->lock);
9621991Sheppo 				return (DDI_FAILURE);
9631991Sheppo 			}
9641991Sheppo 		}
9651991Sheppo 
9661991Sheppo 		/*
9671991Sheppo 		 * Prevent MDEG from adding new ports before the callback can
9681991Sheppo 		 * be unregistered. The lock can't be held accross the
9691991Sheppo 		 * unregistration call because a callback may be in progress
9701991Sheppo 		 * and blocked on the lock.
9711991Sheppo 		 */
9721991Sheppo 		vldcp->detaching = B_TRUE;
9731991Sheppo 
9741991Sheppo 		mutex_exit(&vldcp->lock);
9751991Sheppo 
9761991Sheppo 		if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) {
9771991Sheppo 			vldcp->detaching = B_FALSE;
9781991Sheppo 			return (DDI_FAILURE);
9791991Sheppo 		}
9801991Sheppo 
9811991Sheppo 		/* Tear down all bound ports and free resources. */
9821991Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
9831991Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
9841991Sheppo 				(void) i_vldc_remove_port(vldcp, i);
9851991Sheppo 			}
9861991Sheppo 			mutex_destroy(&(vldcp->minor_tbl[i].lock));
9871991Sheppo 			cv_destroy(&(vldcp->minor_tbl[i].cv));
9881991Sheppo 		}
9891991Sheppo 
9901991Sheppo 		mutex_destroy(&vldcp->lock);
9911991Sheppo 		ddi_soft_state_free(vldc_ssp, instance);
9921991Sheppo 
9931991Sheppo 		return (DDI_SUCCESS);
9941991Sheppo 
9951991Sheppo 	case DDI_SUSPEND:
9961991Sheppo 
9971991Sheppo 		return (DDI_SUCCESS);
9981991Sheppo 
9991991Sheppo 	default:
10001991Sheppo 
10011991Sheppo 		return (DDI_FAILURE);
10021991Sheppo 	}
10031991Sheppo }
10041991Sheppo 
10051991Sheppo /* cb_open */
10061991Sheppo static int
vldc_open(dev_t * devp,int flag,int otyp,cred_t * cred)10071991Sheppo vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
10081991Sheppo {
10091991Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
10101991Sheppo 
10111991Sheppo 	int instance;
10121991Sheppo 	minor_t minor;
10131991Sheppo 	uint64_t portno;
10141991Sheppo 	vldc_t *vldcp;
10151991Sheppo 	vldc_port_t *vport;
10161991Sheppo 	vldc_minor_t *vminor;
10171991Sheppo 
10181991Sheppo 	minor = getminor(*devp);
10191991Sheppo 	instance = VLDCINST(minor);
10201991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
10211991Sheppo 	if (vldcp == NULL)
10221991Sheppo 		return (ENXIO);
10231991Sheppo 
10241991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
10251991Sheppo 	mutex_enter(&vminor->lock);
10261991Sheppo 	portno = vminor->portno;
10271991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
10281991Sheppo 		mutex_exit(&vminor->lock);
10291991Sheppo 		return (ENXIO);
10301991Sheppo 	}
10311991Sheppo 
10321991Sheppo 	vport = &(vldcp->port[portno]);
10331991Sheppo 
10341991Sheppo 	D1("vldc_open: opening vldc@%d:%lu\n", instance, portno);
10351991Sheppo 
10361991Sheppo 	if (vport->status != VLDC_PORT_CLOSED) {
10371991Sheppo 		mutex_exit(&vminor->lock);
10381991Sheppo 		return (EBUSY);
10391991Sheppo 	}
10401991Sheppo 
10411991Sheppo 	vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10421991Sheppo 	vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP);
10431991Sheppo 
10442336Snarayan 	if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0)
10452336Snarayan 		vport->cookie_buf = kmem_alloc(vldc_max_cookie, KM_SLEEP);
10462336Snarayan 
10471991Sheppo 	vport->is_stream = B_FALSE;	/* assume not a stream */
10481991Sheppo 	vport->hanged_up = B_FALSE;
10491991Sheppo 
10501991Sheppo 	vport->status = VLDC_PORT_OPEN;
10511991Sheppo 
10521991Sheppo 	mutex_exit(&vminor->lock);
10531991Sheppo 
10541991Sheppo 	return (DDI_SUCCESS);
10551991Sheppo }
10561991Sheppo 
10571991Sheppo /* cb_close */
10581991Sheppo static int
vldc_close(dev_t dev,int flag,int otyp,cred_t * cred)10591991Sheppo vldc_close(dev_t dev, int flag, int otyp, cred_t *cred)
10601991Sheppo {
10611991Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
10621991Sheppo 
10631991Sheppo 	int instance;
10641991Sheppo 	minor_t minor;
10651991Sheppo 	uint64_t portno;
10661991Sheppo 	vldc_t *vldcp;
10671991Sheppo 	vldc_minor_t *vminor;
10681991Sheppo 	int rv;
10691991Sheppo 
10701991Sheppo 	minor = getminor(dev);
10711991Sheppo 	instance = VLDCINST(minor);
10721991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
10731991Sheppo 	if (vldcp == NULL) {
10741991Sheppo 		return (ENXIO);
10751991Sheppo 	}
10761991Sheppo 
10771991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
10781991Sheppo 	mutex_enter(&vminor->lock);
10791991Sheppo 	portno = vminor->portno;
10801991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
10811991Sheppo 		mutex_exit(&vminor->lock);
10821991Sheppo 		return (ENOLINK);
10831991Sheppo 	}
10841991Sheppo 
10851991Sheppo 	D1("vldc_close: closing vldc@%d:%lu\n", instance, portno);
10861991Sheppo 
10871991Sheppo 	rv = i_vldc_close_port(vldcp, portno);
10881991Sheppo 
10891991Sheppo 	mutex_exit(&vminor->lock);
10901991Sheppo 
10911991Sheppo 	return (rv);
10921991Sheppo }
10931991Sheppo 
10941991Sheppo static int
vldc_set_ldc_mode(vldc_port_t * vport,vldc_t * vldcp,int channel_mode)10951991Sheppo vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode)
10961991Sheppo {
10971991Sheppo 	ldc_attr_t attr;
10981991Sheppo 	int rv;
10991991Sheppo 
11001991Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
11011991Sheppo 
11021991Sheppo 	/* validate mode */
11031991Sheppo 	switch (channel_mode) {
11046408Sha137994 	case LDC_MODE_RELIABLE:
11051991Sheppo 		vport->is_stream = B_TRUE;
11061991Sheppo 		break;
11071991Sheppo 	case LDC_MODE_RAW:
11081991Sheppo 	case LDC_MODE_UNRELIABLE:
11091991Sheppo 		vport->is_stream = B_FALSE;
11101991Sheppo 		break;
11111991Sheppo 	default:
11121991Sheppo 		return (EINVAL);
11131991Sheppo 	}
11141991Sheppo 
11151991Sheppo 	if (vport->status == VLDC_PORT_READY) {
11161991Sheppo 		rv = i_vldc_ldc_close(vport);
11171991Sheppo 		if (rv != 0) {
11181991Sheppo 			DWARN("vldc_set_ldc_mode: i_vldc_ldc_close "
11191991Sheppo 			    "failed, rv=%d\n", rv);
11201991Sheppo 			return (rv);
11211991Sheppo 		}
11221991Sheppo 	}
11231991Sheppo 
11241991Sheppo 	D1("vldc_set_ldc_mode: vport status %d, mode %d\n",
11251991Sheppo 	    vport->status, channel_mode);
11261991Sheppo 
11271991Sheppo 	vport->ldc_mode = channel_mode;
11281991Sheppo 
11291991Sheppo 	/* initialize the channel */
11301991Sheppo 	attr.devclass = LDC_DEV_SERIAL;
11311991Sheppo 	attr.instance = ddi_get_instance(vldcp->dip);
11322410Slm66018 	attr.mtu = vport->mtu;
11331991Sheppo 	attr.mode = vport->ldc_mode;
11341991Sheppo 
11351991Sheppo 	if ((rv = ldc_init(vport->ldc_id, &attr,
11361991Sheppo 	    &vport->ldc_handle)) != 0) {
11371991Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv);
11381991Sheppo 		goto error_init;
11391991Sheppo 	}
11401991Sheppo 
11411991Sheppo 	/* register it */
11421991Sheppo 	if ((rv = ldc_reg_callback(vport->ldc_handle,
11431991Sheppo 	    i_vldc_cb, (caddr_t)vport)) != 0) {
11441991Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n",
11451991Sheppo 		    rv);
11461991Sheppo 		goto error_reg;
11471991Sheppo 	}
11481991Sheppo 
11491991Sheppo 	/* open the channel */
11501991Sheppo 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
11511991Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv);
11521991Sheppo 		goto error_open;
11531991Sheppo 	}
11541991Sheppo 
11551991Sheppo 	vport->status = VLDC_PORT_READY;
11561991Sheppo 
11571991Sheppo 	/*
11581991Sheppo 	 * Attempt to bring the channel up, but do not
11591991Sheppo 	 * fail if the other end is not up yet.
11601991Sheppo 	 */
11611991Sheppo 	rv = ldc_up(vport->ldc_handle);
11621991Sheppo 	if (rv == ECONNREFUSED) {
11631991Sheppo 		D1("vldc_ioctl_opt_op: remote endpoint not up yet\n");
11641991Sheppo 	} else if (rv != 0) {
11651991Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv);
11661991Sheppo 		goto error_up;
11671991Sheppo 	}
11681991Sheppo 
11692841Snarayan 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
11702841Snarayan 	if (rv != 0) {
11712841Snarayan 		DWARN("vldc_ioctl_opt_op: vldc@%d:%d could not get ldc "
11722841Snarayan 		    "status, rv=%d\n", vport->inst, vport->number, rv);
11732841Snarayan 		goto error_up;
11742841Snarayan 	}
11752841Snarayan 
11761991Sheppo 	D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n",
11771991Sheppo 	    vport->ldc_id);
11781991Sheppo 
11791991Sheppo 	return (0);
11801991Sheppo 
11811991Sheppo error_up:
11821991Sheppo 	vport->status = VLDC_PORT_OPEN;
11831991Sheppo 	(void) ldc_close(vport->ldc_handle);
11841991Sheppo error_open:
11851991Sheppo 	(void) ldc_unreg_callback(vport->ldc_handle);
11861991Sheppo error_reg:
11871991Sheppo 	(void) ldc_fini(vport->ldc_handle);
11881991Sheppo error_init:
11891991Sheppo 	return (rv);
11901991Sheppo }
11911991Sheppo 
11921991Sheppo /* ioctl to read cookie */
11931991Sheppo static int
i_vldc_ioctl_read_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)11941991Sheppo i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
11951991Sheppo     int mode)
11961991Sheppo {
11971991Sheppo 	vldc_data_t copy_info;
11982336Snarayan 	uint64_t len, balance, copy_size;
11992336Snarayan 	caddr_t src_addr, dst_addr;
12001991Sheppo 	int rv;
12011991Sheppo 
12021991Sheppo 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) == -1) {
12031991Sheppo 		return (EFAULT);
12041991Sheppo 	}
12051991Sheppo 
12062336Snarayan 	len = balance = copy_info.length;
12072336Snarayan 	src_addr = (caddr_t)copy_info.src_addr;
12082336Snarayan 	dst_addr = (caddr_t)copy_info.dst_addr;
12092336Snarayan 	while (balance > 0) {
12101991Sheppo 
12112336Snarayan 		/* get the max amount to the copied */
12122336Snarayan 		copy_size = MIN(balance, vldc_max_cookie);
12131991Sheppo 
12142336Snarayan 		mutex_enter(&vport->minorp->lock);
12151991Sheppo 
12162336Snarayan 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%p "
12172336Snarayan 		    "size 0x%lx to 0x%p\n", vldc_instance, vport->number,
12182336Snarayan 		    dst_addr, copy_size, src_addr);
12191991Sheppo 
12202336Snarayan 		/* read from the HV into the temporary buffer */
12212793Slm66018 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
12222336Snarayan 		    &copy_size, dst_addr, LDC_COPY_IN);
12232336Snarayan 		if (rv != 0) {
12242336Snarayan 			DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot "
12252336Snarayan 			    "read address 0x%p, rv=%d\n",
12262336Snarayan 			    vldc_instance, vport->number, dst_addr, rv);
12272336Snarayan 			mutex_exit(&vport->minorp->lock);
12282336Snarayan 			return (EFAULT);
12292336Snarayan 		}
12302336Snarayan 
12312336Snarayan 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n",
12322336Snarayan 		    vldc_instance, vport->number);
12332336Snarayan 
12341991Sheppo 		mutex_exit(&vport->minorp->lock);
12351991Sheppo 
12362336Snarayan 		/*
12372336Snarayan 		 * copy data from temporary buffer out to the
12382336Snarayan 		 * caller and free buffer
12392336Snarayan 		 */
12402336Snarayan 		rv = ddi_copyout(vport->cookie_buf, src_addr, copy_size, mode);
12412336Snarayan 		if (rv != 0) {
12422336Snarayan 			return (EFAULT);
12432336Snarayan 		}
12441991Sheppo 
12452336Snarayan 		/* adjust len, source and dest */
12462336Snarayan 		balance -= copy_size;
12472336Snarayan 		src_addr += copy_size;
12482336Snarayan 		dst_addr += copy_size;
12491991Sheppo 	}
12501991Sheppo 
12511991Sheppo 	/* set the structure to reflect outcome */
12521991Sheppo 	copy_info.length = len;
12531991Sheppo 	if (ddi_copyout(&copy_info, arg, sizeof (copy_info), mode) != 0) {
12541991Sheppo 		return (EFAULT);
12551991Sheppo 	}
12561991Sheppo 
12571991Sheppo 	return (0);
12581991Sheppo }
12591991Sheppo 
12601991Sheppo /* ioctl to write cookie */
12611991Sheppo static int
i_vldc_ioctl_write_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)12621991Sheppo i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
12631991Sheppo     int mode)
12641991Sheppo {
12651991Sheppo 	vldc_data_t copy_info;
12662336Snarayan 	uint64_t len, balance, copy_size;
12672336Snarayan 	caddr_t src_addr, dst_addr;
12681991Sheppo 	int rv;
12691991Sheppo 
12702336Snarayan 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) != 0) {
12711991Sheppo 		return (EFAULT);
12721991Sheppo 	}
12731991Sheppo 
12741991Sheppo 	D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx "
12751991Sheppo 	    "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr,
12761991Sheppo 	    copy_info.length, copy_info.dst_addr);
12771991Sheppo 
12782336Snarayan 	len = balance = copy_info.length;
12792336Snarayan 	src_addr = (caddr_t)copy_info.src_addr;
12802336Snarayan 	dst_addr = (caddr_t)copy_info.dst_addr;
12812336Snarayan 	while (balance > 0) {
12822336Snarayan 
12832336Snarayan 		/* get the max amount to the copied */
12842336Snarayan 		copy_size = MIN(balance, vldc_max_cookie);
12851991Sheppo 
12862336Snarayan 		/*
12872336Snarayan 		 * copy into the temporary buffer the data
12882336Snarayan 		 * to be written to the HV
12892336Snarayan 		 */
12902336Snarayan 		if (ddi_copyin((caddr_t)src_addr, vport->cookie_buf,
12912336Snarayan 		    copy_size, mode) != 0) {
12922336Snarayan 			return (EFAULT);
12932336Snarayan 		}
12941991Sheppo 
12952336Snarayan 		mutex_enter(&vport->minorp->lock);
12961991Sheppo 
12972336Snarayan 		/* write the data from the temporary buffer to the HV */
12982793Slm66018 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
12992336Snarayan 		    &copy_size, dst_addr, LDC_COPY_OUT);
13002336Snarayan 		if (rv != 0) {
13012336Snarayan 			DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d "
13022336Snarayan 			    "failed to write at address 0x%p\n, rv=%d",
13032336Snarayan 			    vldc_instance, vport->number, dst_addr, rv);
13042336Snarayan 			mutex_exit(&vport->minorp->lock);
13052336Snarayan 			return (EFAULT);
13062336Snarayan 		}
13072336Snarayan 
13082336Snarayan 		D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n",
13092336Snarayan 		    vldc_instance, vport->number);
13102336Snarayan 
13111991Sheppo 		mutex_exit(&vport->minorp->lock);
13122336Snarayan 
13132336Snarayan 		/* adjust len, source and dest */
13142336Snarayan 		balance -= copy_size;
13152336Snarayan 		src_addr += copy_size;
13162336Snarayan 		dst_addr += copy_size;
13171991Sheppo 	}
13181991Sheppo 
13191991Sheppo 	/* set the structure to reflect outcome */
13201991Sheppo 	copy_info.length = len;
13211991Sheppo 	if (ddi_copyout(&copy_info, (caddr_t)arg,
13221991Sheppo 	    sizeof (copy_info), mode) != 0) {
13231991Sheppo 		return (EFAULT);
13241991Sheppo 	}
13251991Sheppo 
13261991Sheppo 	return (0);
13271991Sheppo }
13281991Sheppo 
13291991Sheppo /* vldc specific ioctl option commands */
13301991Sheppo static int
i_vldc_ioctl_opt_op(vldc_port_t * vport,vldc_t * vldcp,void * arg,int mode)13311991Sheppo i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode)
13321991Sheppo {
13331991Sheppo 	vldc_opt_op_t 	vldc_cmd;
13341991Sheppo 	uint32_t	new_mtu;
13351991Sheppo 	int		rv = 0;
13361991Sheppo 
13371991Sheppo 	if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) {
13381991Sheppo 		return (EFAULT);
13391991Sheppo 	}
13401991Sheppo 
13411991Sheppo 	D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel);
13421991Sheppo 
13431991Sheppo 	switch (vldc_cmd.opt_sel) {
13441991Sheppo 
13451991Sheppo 	case VLDC_OPT_MTU_SZ:
13461991Sheppo 
13471991Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
13481991Sheppo 			vldc_cmd.opt_val = vport->mtu;
13491991Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
13501991Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
13511991Sheppo 				return (EFAULT);
13521991Sheppo 			}
13531991Sheppo 		} else {
13541991Sheppo 			new_mtu = vldc_cmd.opt_val;
13551991Sheppo 
13561991Sheppo 			if ((new_mtu < LDC_PACKET_SIZE) ||
13571991Sheppo 			    (new_mtu > vldc_max_mtu)) {
13581991Sheppo 				return (EINVAL);
13591991Sheppo 			}
13601991Sheppo 
13611991Sheppo 			mutex_enter(&vport->minorp->lock);
13621991Sheppo 
13631991Sheppo 			if ((vport->status != VLDC_PORT_CLOSED) &&
13641991Sheppo 			    (new_mtu != vport->mtu)) {
13651991Sheppo 				/*
13661991Sheppo 				 * The port has buffers allocated since it is
13671991Sheppo 				 * not closed plus the MTU size has changed.
13681991Sheppo 				 * Reallocate the buffers to the new MTU size.
13691991Sheppo 				 */
13701991Sheppo 				kmem_free(vport->recv_buf, vport->mtu);
13711991Sheppo 				vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP);
13721991Sheppo 
13731991Sheppo 				kmem_free(vport->send_buf, vport->mtu);
13741991Sheppo 				vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP);
13751991Sheppo 
13761991Sheppo 				vport->mtu = new_mtu;
13771991Sheppo 			}
13781991Sheppo 
13791991Sheppo 			mutex_exit(&vport->minorp->lock);
13801991Sheppo 		}
13811991Sheppo 
13821991Sheppo 		break;
13831991Sheppo 
13841991Sheppo 	case VLDC_OPT_STATUS:
13851991Sheppo 
13861991Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
13871991Sheppo 			vldc_cmd.opt_val = vport->status;
13881991Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
13891991Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
13901991Sheppo 				return (EFAULT);
13911991Sheppo 			}
13921991Sheppo 		} else {
13931991Sheppo 			return (ENOTSUP);
13941991Sheppo 		}
13951991Sheppo 
13961991Sheppo 		break;
13971991Sheppo 
13981991Sheppo 	case VLDC_OPT_MODE:
13991991Sheppo 
14001991Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
14011991Sheppo 			vldc_cmd.opt_val = vport->ldc_mode;
14021991Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
14031991Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
14041991Sheppo 				return (EFAULT);
14051991Sheppo 			}
14061991Sheppo 		} else {
14071991Sheppo 			mutex_enter(&vport->minorp->lock);
14081991Sheppo 			rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val);
14091991Sheppo 			mutex_exit(&vport->minorp->lock);
14101991Sheppo 		}
14111991Sheppo 
14121991Sheppo 		break;
14131991Sheppo 
14141991Sheppo 	default:
14151991Sheppo 
14161991Sheppo 		D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel);
14171991Sheppo 		return (ENOTSUP);
14181991Sheppo 	}
14191991Sheppo 
14201991Sheppo 	return (rv);
14211991Sheppo }
14221991Sheppo 
14231991Sheppo /* cb_ioctl */
14241991Sheppo static int
vldc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)14251991Sheppo vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
14261991Sheppo     int *rvalp)
14271991Sheppo {
14281991Sheppo 	_NOTE(ARGUNUSED(credp, rvalp))
14291991Sheppo 
14301991Sheppo 	int rv = EINVAL;
14311991Sheppo 	int instance;
14321991Sheppo 	minor_t minor;
14331991Sheppo 	uint64_t portno;
14341991Sheppo 	vldc_t *vldcp;
14351991Sheppo 	vldc_port_t *vport;
14361991Sheppo 	vldc_minor_t *vminor;
14371991Sheppo 
14381991Sheppo 	minor = getminor(dev);
14391991Sheppo 	instance = VLDCINST(minor);
14401991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
14411991Sheppo 	if (vldcp == NULL) {
14421991Sheppo 		return (ENXIO);
14431991Sheppo 	}
14441991Sheppo 
14451991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
14461991Sheppo 	mutex_enter(&vminor->lock);
14471991Sheppo 	portno = vminor->portno;
14481991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
14491991Sheppo 		mutex_exit(&vminor->lock);
14501991Sheppo 		return (ENOLINK);
14511991Sheppo 	}
14521991Sheppo 	vminor->in_use += 1;
14531991Sheppo 	mutex_exit(&vminor->lock);
14541991Sheppo 
14551991Sheppo 	vport = &(vldcp->port[portno]);
14561991Sheppo 
14571991Sheppo 	D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd);
14581991Sheppo 
14591991Sheppo 	switch (cmd) {
14601991Sheppo 
14611991Sheppo 	case VLDC_IOCTL_OPT_OP:
14621991Sheppo 		rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg,  mode);
14631991Sheppo 		break;
14641991Sheppo 
14651991Sheppo 	case VLDC_IOCTL_READ_COOKIE:
14662336Snarayan 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
14672336Snarayan 			rv = EINVAL;
14682336Snarayan 			break;
14692336Snarayan 		}
14701991Sheppo 		rv = i_vldc_ioctl_read_cookie(vport, instance,
14711991Sheppo 		    (void *)arg, mode);
14721991Sheppo 		break;
14731991Sheppo 
14741991Sheppo 	case VLDC_IOCTL_WRITE_COOKIE:
14752336Snarayan 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
14762336Snarayan 			rv = EINVAL;
14772336Snarayan 			break;
14782336Snarayan 		}
14791991Sheppo 		rv = i_vldc_ioctl_write_cookie(vport, instance,
14801991Sheppo 		    (void *)arg, mode);
14811991Sheppo 		break;
14821991Sheppo 
14831991Sheppo 	default:
14841991Sheppo 		DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n",
14851991Sheppo 		    instance, portno, cmd);
14861991Sheppo 		rv = EINVAL;
14871991Sheppo 		break;
14881991Sheppo 	}
14891991Sheppo 
14901991Sheppo 	mutex_enter(&vminor->lock);
14911991Sheppo 	vminor->in_use -= 1;
14921991Sheppo 	if (vminor->in_use == 0) {
14931991Sheppo 		cv_signal(&vminor->cv);
14941991Sheppo 	}
14951991Sheppo 	mutex_exit(&vminor->lock);
14961991Sheppo 
14971991Sheppo 	D1("vldc_ioctl: rv=%d\n", rv);
14981991Sheppo 
14991991Sheppo 	return (rv);
15001991Sheppo }
15011991Sheppo 
15021991Sheppo /* cb_read */
15031991Sheppo static int
vldc_read(dev_t dev,struct uio * uiop,cred_t * credp)15041991Sheppo vldc_read(dev_t dev, struct uio *uiop, cred_t *credp)
15051991Sheppo {
15061991Sheppo 	_NOTE(ARGUNUSED(credp))
15071991Sheppo 
15081991Sheppo 	int instance;
15091991Sheppo 	minor_t minor;
15101991Sheppo 	size_t size = 0;
15111991Sheppo 	uint64_t portno;
15121991Sheppo 	vldc_t *vldcp;
15131991Sheppo 	vldc_port_t *vport;
15141991Sheppo 	vldc_minor_t *vminor;
15151991Sheppo 	int rv = 0;
15161991Sheppo 
15171991Sheppo 	minor = getminor(dev);
15181991Sheppo 	instance = VLDCINST(minor);
15191991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
15201991Sheppo 	if (vldcp == NULL) {
15211991Sheppo 		return (ENXIO);
15221991Sheppo 	}
15231991Sheppo 
15241991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
15251991Sheppo 	mutex_enter(&vminor->lock);
15261991Sheppo 	portno = vminor->portno;
15271991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
15281991Sheppo 		mutex_exit(&vminor->lock);
15291991Sheppo 		return (ENOLINK);
15301991Sheppo 	}
15311991Sheppo 
15321991Sheppo 	D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno);
15331991Sheppo 
15341991Sheppo 	vport = &(vldcp->port[portno]);
15351991Sheppo 
15361991Sheppo 	/* check the port status */
15371991Sheppo 	if (vport->status != VLDC_PORT_READY) {
15381991Sheppo 		DWARN("vldc_read: vldc@%d:%lu not in the ready state\n",
15391991Sheppo 		    instance, portno);
15401991Sheppo 		mutex_exit(&vminor->lock);
15411991Sheppo 		return (ENOTACTIVE);
15421991Sheppo 	}
15431991Sheppo 
15441991Sheppo 	/* read data */
15451991Sheppo 	size = MIN(vport->mtu, uiop->uio_resid);
15461991Sheppo 	rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size);
15471991Sheppo 
15481991Sheppo 	D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n",
15491991Sheppo 	    instance, portno, size, rv);
15501991Sheppo 
15511991Sheppo 	if (rv == 0) {
15521991Sheppo 		if (size != 0) {
15531991Sheppo 			rv = uiomove(vport->recv_buf, size, UIO_READ, uiop);
15541991Sheppo 		} else {
15551991Sheppo 			rv = EWOULDBLOCK;
15561991Sheppo 		}
15571991Sheppo 	} else {
15581991Sheppo 		switch (rv) {
15591991Sheppo 		case ENOBUFS:
15601991Sheppo 			break;
15611991Sheppo 		case ETIMEDOUT:
15621991Sheppo 		case EWOULDBLOCK:
15631991Sheppo 			rv = EWOULDBLOCK;
15641991Sheppo 			break;
15651991Sheppo 		default:
15661991Sheppo 			rv = ECONNRESET;
15671991Sheppo 			break;
15681991Sheppo 		}
15691991Sheppo 	}
15701991Sheppo 
15711991Sheppo 	mutex_exit(&vminor->lock);
15721991Sheppo 
15731991Sheppo 	return (rv);
15741991Sheppo }
15751991Sheppo 
15761991Sheppo /* cb_write */
15771991Sheppo static int
vldc_write(dev_t dev,struct uio * uiop,cred_t * credp)15781991Sheppo vldc_write(dev_t dev, struct uio *uiop, cred_t *credp)
15791991Sheppo {
15801991Sheppo 	_NOTE(ARGUNUSED(credp))
15811991Sheppo 
15821991Sheppo 	int instance;
15831991Sheppo 	minor_t minor;
15841991Sheppo 	size_t size;
15851991Sheppo 	size_t orig_size;
15861991Sheppo 	uint64_t portno;
15871991Sheppo 	vldc_t *vldcp;
15881991Sheppo 	vldc_port_t *vport;
15891991Sheppo 	vldc_minor_t *vminor;
15901991Sheppo 	int rv = EINVAL;
15911991Sheppo 
15921991Sheppo 	minor = getminor(dev);
15931991Sheppo 	instance = VLDCINST(minor);
15941991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
15951991Sheppo 	if (vldcp == NULL) {
15961991Sheppo 		return (ENXIO);
15971991Sheppo 	}
15981991Sheppo 
15991991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
16001991Sheppo 	mutex_enter(&vminor->lock);
16011991Sheppo 	portno = vminor->portno;
16021991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
16031991Sheppo 		mutex_exit(&vminor->lock);
16041991Sheppo 		return (ENOLINK);
16051991Sheppo 	}
16061991Sheppo 
16071991Sheppo 	vport = &(vldcp->port[portno]);
16081991Sheppo 
16091991Sheppo 	/* check the port status */
16101991Sheppo 	if (vport->status != VLDC_PORT_READY) {
16111991Sheppo 		DWARN("vldc_write: vldc@%d:%lu not in the ready state\n",
16121991Sheppo 		    instance, portno);
16131991Sheppo 		mutex_exit(&vminor->lock);
16141991Sheppo 		return (ENOTACTIVE);
16151991Sheppo 	}
16161991Sheppo 
16171991Sheppo 	orig_size = uiop->uio_resid;
16181991Sheppo 	size = orig_size;
16191991Sheppo 
16201991Sheppo 	if (size > vport->mtu) {
16211991Sheppo 		if (vport->is_stream) {
16221991Sheppo 			/* can only send MTU size at a time */
16231991Sheppo 			size = vport->mtu;
16241991Sheppo 		} else {
16251991Sheppo 			mutex_exit(&vminor->lock);
16261991Sheppo 			return (EMSGSIZE);
16271991Sheppo 		}
16281991Sheppo 	}
16291991Sheppo 
16301991Sheppo 	D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno,
16311991Sheppo 	    size);
16321991Sheppo 
16331991Sheppo 	rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop);
16341991Sheppo 	if (rv == 0) {
16351991Sheppo 		rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf,
16362600Sjm22469 		    &size);
16371991Sheppo 		if (rv != 0) {
16381991Sheppo 			DWARN("vldc_write: vldc@%d:%lu failed writing %lu "
16391991Sheppo 			    "bytes rv=%d\n", instance, portno, size, rv);
16401991Sheppo 		}
16411991Sheppo 	} else {
16421991Sheppo 		size = 0;
16431991Sheppo 	}
16441991Sheppo 
16451991Sheppo 	mutex_exit(&vminor->lock);
16461991Sheppo 
16471991Sheppo 	/* resid is total number of bytes *not* sent */
16481991Sheppo 	uiop->uio_resid = orig_size - size;
16491991Sheppo 
16501991Sheppo 	return (rv);
16511991Sheppo }
16521991Sheppo 
16531991Sheppo /* cb_chpoll */
16541991Sheppo static int
vldc_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)16551991Sheppo vldc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
16561991Sheppo     struct pollhead **phpp)
16571991Sheppo {
16581991Sheppo 	int instance;
16591991Sheppo 	minor_t minor;
16601991Sheppo 	uint64_t portno;
16611991Sheppo 	vldc_t *vldcp;
16621991Sheppo 	vldc_port_t *vport;
16631991Sheppo 	vldc_minor_t *vminor;
16642410Slm66018 	boolean_t haspkts;
16651991Sheppo 
16661991Sheppo 	minor = getminor(dev);
16671991Sheppo 	instance = VLDCINST(minor);
16681991Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
16691991Sheppo 	if (vldcp == NULL) {
16701991Sheppo 		return (ENXIO);
16711991Sheppo 	}
16721991Sheppo 
16731991Sheppo 	vminor = VLDCMINOR(vldcp, minor);
16741991Sheppo 	mutex_enter(&vminor->lock);
16751991Sheppo 	portno = vminor->portno;
16761991Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
16771991Sheppo 		mutex_exit(&vminor->lock);
16781991Sheppo 		return (ENOLINK);
16791991Sheppo 	}
16801991Sheppo 
16811991Sheppo 	vport = &(vldcp->port[portno]);
16821991Sheppo 
16831991Sheppo 	/* check the port status */
16841991Sheppo 	if (vport->status != VLDC_PORT_READY) {
16851991Sheppo 		mutex_exit(&vminor->lock);
16861991Sheppo 		return (ENOTACTIVE);
16871991Sheppo 	}
16881991Sheppo 
16891991Sheppo 	D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n",
16901991Sheppo 	    instance, portno, events);
16911991Sheppo 
16921991Sheppo 	*reventsp = 0;
16931991Sheppo 
16942841Snarayan 	if (vport->ldc_status == LDC_UP) {
16951991Sheppo 		/*
16961991Sheppo 		 * Check if the receive queue is empty and if not, signal that
16971991Sheppo 		 * there is data ready to read.
16981991Sheppo 		 */
16991991Sheppo 		if (events & POLLIN) {
17002410Slm66018 			if ((ldc_chkq(vport->ldc_handle, &haspkts) == 0) &&
17012410Slm66018 			    haspkts) {
17021991Sheppo 				*reventsp |= POLLIN;
17031991Sheppo 			}
17041991Sheppo 		}
17051991Sheppo 
17061991Sheppo 		if (events & POLLOUT)
17071991Sheppo 			*reventsp |= POLLOUT;
17081991Sheppo 
17091991Sheppo 	} else if (vport->hanged_up) {
17101991Sheppo 		*reventsp |= POLLHUP;
17111991Sheppo 		vport->hanged_up = B_FALSE;
17121991Sheppo 	}
17131991Sheppo 
17141991Sheppo 	mutex_exit(&vminor->lock);
17151991Sheppo 
17161991Sheppo 	if (((*reventsp) == 0) && (!anyyet)) {
17171991Sheppo 		*phpp = &vport->poll;
17181991Sheppo 	}
17191991Sheppo 
17201991Sheppo 	D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n",
17211991Sheppo 	    instance, portno, events, *reventsp);
17221991Sheppo 
17231991Sheppo 	return (0);
17241991Sheppo }
1725