xref: /titanic_52/usr/src/uts/sun4v/io/vldc.c (revision 1ae0874509b6811fdde1dfd46f0d93fd09867a3f)
1*1ae08745Sheppo /*
2*1ae08745Sheppo  * CDDL HEADER START
3*1ae08745Sheppo  *
4*1ae08745Sheppo  * The contents of this file are subject to the terms of the
5*1ae08745Sheppo  * Common Development and Distribution License (the "License").
6*1ae08745Sheppo  * You may not use this file except in compliance with the License.
7*1ae08745Sheppo  *
8*1ae08745Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1ae08745Sheppo  * or http://www.opensolaris.org/os/licensing.
10*1ae08745Sheppo  * See the License for the specific language governing permissions
11*1ae08745Sheppo  * and limitations under the License.
12*1ae08745Sheppo  *
13*1ae08745Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
14*1ae08745Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1ae08745Sheppo  * If applicable, add the following below this CDDL HEADER, with the
16*1ae08745Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
17*1ae08745Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1ae08745Sheppo  *
19*1ae08745Sheppo  * CDDL HEADER END
20*1ae08745Sheppo  */
21*1ae08745Sheppo 
22*1ae08745Sheppo /*
23*1ae08745Sheppo  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*1ae08745Sheppo  * Use is subject to license terms.
25*1ae08745Sheppo  */
26*1ae08745Sheppo 
27*1ae08745Sheppo #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*1ae08745Sheppo 
29*1ae08745Sheppo #include <sys/types.h>
30*1ae08745Sheppo #include <sys/file.h>
31*1ae08745Sheppo #include <sys/errno.h>
32*1ae08745Sheppo #include <sys/uio.h>
33*1ae08745Sheppo #include <sys/open.h>
34*1ae08745Sheppo #include <sys/cred.h>
35*1ae08745Sheppo #include <sys/kmem.h>
36*1ae08745Sheppo #include <sys/conf.h>
37*1ae08745Sheppo #include <sys/cmn_err.h>
38*1ae08745Sheppo #include <sys/ksynch.h>
39*1ae08745Sheppo #include <sys/modctl.h>
40*1ae08745Sheppo #include <sys/stat.h>			/* needed for S_IFBLK and S_IFCHR */
41*1ae08745Sheppo #include <sys/debug.h>
42*1ae08745Sheppo #include <sys/sysmacros.h>
43*1ae08745Sheppo #include <sys/types.h>
44*1ae08745Sheppo #include <sys/cred.h>
45*1ae08745Sheppo #include <sys/promif.h>
46*1ae08745Sheppo #include <sys/ddi.h>
47*1ae08745Sheppo #include <sys/sunddi.h>
48*1ae08745Sheppo #include <sys/cyclic.h>
49*1ae08745Sheppo #include <sys/note.h>
50*1ae08745Sheppo #include <sys/mach_descrip.h>
51*1ae08745Sheppo #include <sys/mdeg.h>
52*1ae08745Sheppo #include <sys/ldc.h>
53*1ae08745Sheppo #include <sys/vldc_impl.h>
54*1ae08745Sheppo 
55*1ae08745Sheppo /*
56*1ae08745Sheppo  * Function prototypes.
57*1ae08745Sheppo  */
58*1ae08745Sheppo 
59*1ae08745Sheppo /* DDI entrypoints */
60*1ae08745Sheppo static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
61*1ae08745Sheppo static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
62*1ae08745Sheppo static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
63*1ae08745Sheppo static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred);
64*1ae08745Sheppo static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
65*1ae08745Sheppo     cred_t *credp, int *rvalp);
66*1ae08745Sheppo static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp);
67*1ae08745Sheppo static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp);
68*1ae08745Sheppo static int vldc_chpoll(dev_t dev, short events, int anyyet,
69*1ae08745Sheppo     short *reventsp, struct pollhead **phpp);
70*1ae08745Sheppo 
71*1ae08745Sheppo /* Internal functions */
72*1ae08745Sheppo static uint_t i_vldc_cb(uint64_t event, caddr_t arg);
73*1ae08745Sheppo static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
74*1ae08745Sheppo static int i_vldc_mdeg_register(vldc_t *vldcp);
75*1ae08745Sheppo static int i_vldc_mdeg_unregister(vldc_t *vldcp);
76*1ae08745Sheppo static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node);
77*1ae08745Sheppo static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno);
78*1ae08745Sheppo static int i_vldc_close_port(vldc_t *vldcp, uint_t portno);
79*1ae08745Sheppo 
80*1ae08745Sheppo /* soft state structure */
81*1ae08745Sheppo static void *vldc_ssp;
82*1ae08745Sheppo 
83*1ae08745Sheppo /*
84*1ae08745Sheppo  * Matching criteria passed to the MDEG to register interest
85*1ae08745Sheppo  * in changes to 'virtual-device-port' nodes identified by their
86*1ae08745Sheppo  * 'id' property.
87*1ae08745Sheppo  */
88*1ae08745Sheppo static md_prop_match_t vport_prop_match[] = {
89*1ae08745Sheppo 	{ MDET_PROP_VAL,    "id"   },
90*1ae08745Sheppo 	{ MDET_LIST_END,    NULL    }
91*1ae08745Sheppo };
92*1ae08745Sheppo 
93*1ae08745Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
94*1ae08745Sheppo 					vport_prop_match };
95*1ae08745Sheppo 
96*1ae08745Sheppo /*
97*1ae08745Sheppo  * Specification of an MD node passed to the MDEG to filter any
98*1ae08745Sheppo  * 'virtual-device-port' nodes that do not belong to the specified
99*1ae08745Sheppo  * node. This template is copied for each vldc instance and filled
100*1ae08745Sheppo  * in with the appropriate 'name' and 'cfg-handle' values before
101*1ae08745Sheppo  * being passed to the MDEG.
102*1ae08745Sheppo  */
103*1ae08745Sheppo static mdeg_prop_spec_t vldc_prop_template[] = {
104*1ae08745Sheppo 	{ MDET_PROP_STR,    "name",		NULL	},
105*1ae08745Sheppo 	{ MDET_PROP_VAL,    "cfg-handle",	NULL    },
106*1ae08745Sheppo 	{ MDET_LIST_END,    NULL,		NULL    }
107*1ae08745Sheppo };
108*1ae08745Sheppo 
109*1ae08745Sheppo #define	VLDC_MDEG_PROP_NAME(specp)		((specp)[0].ps_str)
110*1ae08745Sheppo #define	VLDC_SET_MDEG_PROP_NAME(specp, name)	((specp)[0].ps_str = (name))
111*1ae08745Sheppo #define	VLDC_SET_MDEG_PROP_INST(specp, inst)	((specp)[1].ps_val = (inst))
112*1ae08745Sheppo 
113*1ae08745Sheppo 
114*1ae08745Sheppo static struct cb_ops vldc_cb_ops = {
115*1ae08745Sheppo 	vldc_open,	/* open */
116*1ae08745Sheppo 	vldc_close,	/* close */
117*1ae08745Sheppo 	nodev,		/* strategy */
118*1ae08745Sheppo 	nodev,		/* print */
119*1ae08745Sheppo 	nodev,		/* dump */
120*1ae08745Sheppo 	vldc_read,	/* read */
121*1ae08745Sheppo 	vldc_write,	/* write */
122*1ae08745Sheppo 	vldc_ioctl,	/* ioctl */
123*1ae08745Sheppo 	nodev,		/* devmap */
124*1ae08745Sheppo 	nodev,		/* mmap */
125*1ae08745Sheppo 	ddi_segmap,	/* segmap */
126*1ae08745Sheppo 	vldc_chpoll,	/* chpoll */
127*1ae08745Sheppo 	ddi_prop_op,	/* prop_op */
128*1ae08745Sheppo 	NULL,		/* stream */
129*1ae08745Sheppo 	D_NEW | D_MP	/* flag */
130*1ae08745Sheppo };
131*1ae08745Sheppo 
132*1ae08745Sheppo static struct dev_ops vldc_ops = {
133*1ae08745Sheppo 	DEVO_REV,		/* rev */
134*1ae08745Sheppo 	0,			/* ref count */
135*1ae08745Sheppo 	ddi_getinfo_1to1,	/* getinfo */
136*1ae08745Sheppo 	nulldev,		/* identify */
137*1ae08745Sheppo 	nulldev,		/* probe */
138*1ae08745Sheppo 	vldc_attach,		/* attach */
139*1ae08745Sheppo 	vldc_detach,		/* detach */
140*1ae08745Sheppo 	nodev,			/* reset */
141*1ae08745Sheppo 	&vldc_cb_ops,		/* cb_ops */
142*1ae08745Sheppo 	(struct bus_ops *)NULL	/* bus_ops */
143*1ae08745Sheppo };
144*1ae08745Sheppo 
145*1ae08745Sheppo extern struct mod_ops mod_driverops;
146*1ae08745Sheppo 
147*1ae08745Sheppo static struct modldrv md = {
148*1ae08745Sheppo 	&mod_driverops, 			/* Type - it is a driver */
149*1ae08745Sheppo 	"sun4v Virtual LDC Driver %I%",	/* Name of the module */
150*1ae08745Sheppo 	&vldc_ops,				/* driver specific ops */
151*1ae08745Sheppo };
152*1ae08745Sheppo 
153*1ae08745Sheppo static struct modlinkage ml = {
154*1ae08745Sheppo 	MODREV_1,
155*1ae08745Sheppo 	&md,
156*1ae08745Sheppo 	NULL
157*1ae08745Sheppo };
158*1ae08745Sheppo 
159*1ae08745Sheppo /* maximum MTU and cookie size tunables */
160*1ae08745Sheppo uint32_t vldc_max_mtu = VLDC_MAX_MTU;
161*1ae08745Sheppo uint64_t vldc_max_cookie = VLDC_MAX_COOKIE;
162*1ae08745Sheppo 
163*1ae08745Sheppo 
164*1ae08745Sheppo #ifdef DEBUG
165*1ae08745Sheppo 
166*1ae08745Sheppo /*
167*1ae08745Sheppo  * Print debug messages
168*1ae08745Sheppo  *
169*1ae08745Sheppo  * set vldcdbg to 0x7 to enable all messages
170*1ae08745Sheppo  *
171*1ae08745Sheppo  * 0x4 - Warnings
172*1ae08745Sheppo  * 0x2 - All debug messages (most verbose)
173*1ae08745Sheppo  * 0x1 - Minimal debug messages
174*1ae08745Sheppo  */
175*1ae08745Sheppo 
176*1ae08745Sheppo int vldcdbg = 0x0;
177*1ae08745Sheppo 
178*1ae08745Sheppo static void
179*1ae08745Sheppo vldcdebug(const char *fmt, ...)
180*1ae08745Sheppo {
181*1ae08745Sheppo 	char buf[512];
182*1ae08745Sheppo 	va_list ap;
183*1ae08745Sheppo 
184*1ae08745Sheppo 	va_start(ap, fmt);
185*1ae08745Sheppo 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
186*1ae08745Sheppo 	va_end(ap);
187*1ae08745Sheppo 
188*1ae08745Sheppo 	cmn_err(CE_CONT, "?%s", buf);
189*1ae08745Sheppo }
190*1ae08745Sheppo 
191*1ae08745Sheppo #define	D1	if (vldcdbg & 0x01) vldcdebug
192*1ae08745Sheppo #define	D2	if (vldcdbg & 0x02) vldcdebug
193*1ae08745Sheppo #define	DWARN	if (vldcdbg & 0x04) vldcdebug
194*1ae08745Sheppo 
195*1ae08745Sheppo #else /* not DEBUG */
196*1ae08745Sheppo 
197*1ae08745Sheppo #define	D1	if (0) printf
198*1ae08745Sheppo #define	D2	if (0) printf
199*1ae08745Sheppo #define	DWARN	if (0) printf
200*1ae08745Sheppo 
201*1ae08745Sheppo #endif /* not DEBUG */
202*1ae08745Sheppo 
203*1ae08745Sheppo 
204*1ae08745Sheppo /* _init(9E): initialize the loadable module */
205*1ae08745Sheppo int
206*1ae08745Sheppo _init(void)
207*1ae08745Sheppo {
208*1ae08745Sheppo 	int error;
209*1ae08745Sheppo 
210*1ae08745Sheppo 	/* init the soft state structure */
211*1ae08745Sheppo 	error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1);
212*1ae08745Sheppo 	if (error != 0) {
213*1ae08745Sheppo 		return (error);
214*1ae08745Sheppo 	}
215*1ae08745Sheppo 
216*1ae08745Sheppo 	/* Link the driver into the system */
217*1ae08745Sheppo 	error = mod_install(&ml);
218*1ae08745Sheppo 
219*1ae08745Sheppo 	return (error);
220*1ae08745Sheppo }
221*1ae08745Sheppo 
222*1ae08745Sheppo /* _info(9E): return information about the loadable module */
223*1ae08745Sheppo int
224*1ae08745Sheppo _info(struct modinfo *modinfop)
225*1ae08745Sheppo {
226*1ae08745Sheppo 	/* Report status of the dynamically loadable driver module */
227*1ae08745Sheppo 	return (mod_info(&ml, modinfop));
228*1ae08745Sheppo }
229*1ae08745Sheppo 
230*1ae08745Sheppo /* _fini(9E): prepare the module for unloading. */
231*1ae08745Sheppo int
232*1ae08745Sheppo _fini(void)
233*1ae08745Sheppo {
234*1ae08745Sheppo 	int error;
235*1ae08745Sheppo 
236*1ae08745Sheppo 	/* Unlink the driver module from the system */
237*1ae08745Sheppo 	if ((error = mod_remove(&ml)) == 0) {
238*1ae08745Sheppo 		/*
239*1ae08745Sheppo 		 * We have successfully "removed" the driver.
240*1ae08745Sheppo 		 * destroy soft state
241*1ae08745Sheppo 		 */
242*1ae08745Sheppo 		ddi_soft_state_fini(&vldc_ssp);
243*1ae08745Sheppo 	}
244*1ae08745Sheppo 
245*1ae08745Sheppo 	return (error);
246*1ae08745Sheppo }
247*1ae08745Sheppo 
248*1ae08745Sheppo /* ldc callback */
249*1ae08745Sheppo static uint_t
250*1ae08745Sheppo i_vldc_cb(uint64_t event, caddr_t arg)
251*1ae08745Sheppo {
252*1ae08745Sheppo 	vldc_port_t *vport = (vldc_port_t *)arg;
253*1ae08745Sheppo 	short pollevents = 0;
254*1ae08745Sheppo 	int rv;
255*1ae08745Sheppo 
256*1ae08745Sheppo 	D1("i_vldc_cb: callback invoked port=%d, event=0x%lx\n",
257*1ae08745Sheppo 	    vport->number, event);
258*1ae08745Sheppo 
259*1ae08745Sheppo 	if (event & LDC_EVT_UP) {
260*1ae08745Sheppo 		pollevents |= POLLOUT;
261*1ae08745Sheppo 		vport->hanged_up = B_FALSE;
262*1ae08745Sheppo 
263*1ae08745Sheppo 	} else if (event & LDC_EVT_DOWN) {
264*1ae08745Sheppo 		pollevents |= POLLHUP;
265*1ae08745Sheppo 		vport->hanged_up = B_TRUE;
266*1ae08745Sheppo 
267*1ae08745Sheppo 	} else if (event & LDC_EVT_RESET) {
268*1ae08745Sheppo 		/* do an ldc_up because we can't be sure the other side will */
269*1ae08745Sheppo 		if ((rv = ldc_up(vport->ldc_handle)) != 0)
270*1ae08745Sheppo 			if (rv != ECONNREFUSED)
271*1ae08745Sheppo 				DWARN("i_vldc_cb: port@%d failed to"
272*1ae08745Sheppo 				    " bring up LDC channel=%ld, err=%d\n",
273*1ae08745Sheppo 				    vport->number, vport->ldc_id, rv);
274*1ae08745Sheppo 	}
275*1ae08745Sheppo 
276*1ae08745Sheppo 	if (event & LDC_EVT_READ)
277*1ae08745Sheppo 		pollevents |= POLLIN;
278*1ae08745Sheppo 
279*1ae08745Sheppo 	if (pollevents != 0) {
280*1ae08745Sheppo 		D1("i_vldc_cb: port@%d pollwakeup=0x%x\n",
281*1ae08745Sheppo 		    vport->number, pollevents);
282*1ae08745Sheppo 		pollwakeup(&vport->poll, pollevents);
283*1ae08745Sheppo 	}
284*1ae08745Sheppo 
285*1ae08745Sheppo 	return (LDC_SUCCESS);
286*1ae08745Sheppo }
287*1ae08745Sheppo 
288*1ae08745Sheppo /* mdeg callback */
289*1ae08745Sheppo static int
290*1ae08745Sheppo i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
291*1ae08745Sheppo {
292*1ae08745Sheppo 	vldc_t		*vldcp;
293*1ae08745Sheppo 	int		idx;
294*1ae08745Sheppo 	uint64_t	portno;
295*1ae08745Sheppo 	int		rv;
296*1ae08745Sheppo 	md_t		*mdp;
297*1ae08745Sheppo 	mde_cookie_t	node;
298*1ae08745Sheppo 
299*1ae08745Sheppo 	if (resp == NULL) {
300*1ae08745Sheppo 		D1("i_vldc_mdeg_cb: no result returned\n");
301*1ae08745Sheppo 		return (MDEG_FAILURE);
302*1ae08745Sheppo 	}
303*1ae08745Sheppo 
304*1ae08745Sheppo 	vldcp = (vldc_t *)cb_argp;
305*1ae08745Sheppo 
306*1ae08745Sheppo 	mutex_enter(&vldcp->lock);
307*1ae08745Sheppo 	if (vldcp->detaching == B_TRUE) {
308*1ae08745Sheppo 		D1("i_vldc_mdeg_cb: detach in progress\n");
309*1ae08745Sheppo 		mutex_exit(&vldcp->lock);
310*1ae08745Sheppo 		return (MDEG_FAILURE);
311*1ae08745Sheppo 	}
312*1ae08745Sheppo 
313*1ae08745Sheppo 	D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n",
314*1ae08745Sheppo 	    resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
315*1ae08745Sheppo 
316*1ae08745Sheppo 	/* process added ports */
317*1ae08745Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
318*1ae08745Sheppo 		mdp = resp->added.mdp;
319*1ae08745Sheppo 		node = resp->added.mdep[idx];
320*1ae08745Sheppo 
321*1ae08745Sheppo 		D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node);
322*1ae08745Sheppo 
323*1ae08745Sheppo 		/* attempt to add a port */
324*1ae08745Sheppo 		if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) {
325*1ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, "
326*1ae08745Sheppo 			    "err = %d", rv);
327*1ae08745Sheppo 		}
328*1ae08745Sheppo 	}
329*1ae08745Sheppo 
330*1ae08745Sheppo 	/* process removed ports */
331*1ae08745Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
332*1ae08745Sheppo 		mdp = resp->removed.mdp;
333*1ae08745Sheppo 		node = resp->removed.mdep[idx];
334*1ae08745Sheppo 
335*1ae08745Sheppo 		D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node);
336*1ae08745Sheppo 
337*1ae08745Sheppo 		/* read in the port's id property */
338*1ae08745Sheppo 		if (md_get_prop_val(mdp, node, "id", &portno)) {
339*1ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of "
340*1ae08745Sheppo 			    "removed list has no 'id' property", node);
341*1ae08745Sheppo 			continue;
342*1ae08745Sheppo 		}
343*1ae08745Sheppo 
344*1ae08745Sheppo 		/* attempt to remove a port */
345*1ae08745Sheppo 		if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) {
346*1ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove "
347*1ae08745Sheppo 			    "port %lu, err %d", portno, rv);
348*1ae08745Sheppo 		}
349*1ae08745Sheppo 	}
350*1ae08745Sheppo 
351*1ae08745Sheppo 	/*
352*1ae08745Sheppo 	 * Currently no support for updating already active ports. So, ignore
353*1ae08745Sheppo 	 * the match_curr and match_prev arrays for now.
354*1ae08745Sheppo 	 */
355*1ae08745Sheppo 
356*1ae08745Sheppo 	mutex_exit(&vldcp->lock);
357*1ae08745Sheppo 
358*1ae08745Sheppo 	return (MDEG_SUCCESS);
359*1ae08745Sheppo }
360*1ae08745Sheppo 
361*1ae08745Sheppo /* register callback to mdeg */
362*1ae08745Sheppo static int
363*1ae08745Sheppo i_vldc_mdeg_register(vldc_t *vldcp)
364*1ae08745Sheppo {
365*1ae08745Sheppo 	mdeg_prop_spec_t *pspecp;
366*1ae08745Sheppo 	mdeg_node_spec_t *inst_specp;
367*1ae08745Sheppo 	mdeg_handle_t	mdeg_hdl;
368*1ae08745Sheppo 	size_t		templatesz;
369*1ae08745Sheppo 	int		inst;
370*1ae08745Sheppo 	char		*name;
371*1ae08745Sheppo 	size_t		namesz;
372*1ae08745Sheppo 	char		*nameprop;
373*1ae08745Sheppo 	int		rv;
374*1ae08745Sheppo 
375*1ae08745Sheppo 	/* get the unique vldc instance assigned by the LDom manager */
376*1ae08745Sheppo 	inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip,
377*1ae08745Sheppo 	    DDI_PROP_DONTPASS, "reg", -1);
378*1ae08745Sheppo 	if (inst == -1) {
379*1ae08745Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'reg' property",
380*1ae08745Sheppo 		    ddi_get_instance(vldcp->dip));
381*1ae08745Sheppo 		return (DDI_FAILURE);
382*1ae08745Sheppo 	}
383*1ae08745Sheppo 
384*1ae08745Sheppo 	/* get the name of the vldc instance */
385*1ae08745Sheppo 	rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip,
386*1ae08745Sheppo 	    DDI_PROP_DONTPASS, "name", &nameprop);
387*1ae08745Sheppo 	if (rv != DDI_PROP_SUCCESS) {
388*1ae08745Sheppo 		cmn_err(CE_NOTE, "?vldc%d has no 'name' property",
389*1ae08745Sheppo 		    ddi_get_instance(vldcp->dip));
390*1ae08745Sheppo 		return (DDI_FAILURE);
391*1ae08745Sheppo 	}
392*1ae08745Sheppo 
393*1ae08745Sheppo 	D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst);
394*1ae08745Sheppo 
395*1ae08745Sheppo 	/*
396*1ae08745Sheppo 	 * Allocate and initialize a per-instance copy
397*1ae08745Sheppo 	 * of the global property spec array that will
398*1ae08745Sheppo 	 * uniquely identify this vldc instance.
399*1ae08745Sheppo 	 */
400*1ae08745Sheppo 	templatesz = sizeof (vldc_prop_template);
401*1ae08745Sheppo 	pspecp = kmem_alloc(templatesz, KM_SLEEP);
402*1ae08745Sheppo 
403*1ae08745Sheppo 	bcopy(vldc_prop_template, pspecp, templatesz);
404*1ae08745Sheppo 
405*1ae08745Sheppo 	/* copy in the name property */
406*1ae08745Sheppo 	namesz = strlen(nameprop) + 1;
407*1ae08745Sheppo 	name = kmem_alloc(namesz, KM_SLEEP);
408*1ae08745Sheppo 
409*1ae08745Sheppo 	bcopy(nameprop, name, namesz);
410*1ae08745Sheppo 	VLDC_SET_MDEG_PROP_NAME(pspecp, name);
411*1ae08745Sheppo 
412*1ae08745Sheppo 	/* copy in the instance property */
413*1ae08745Sheppo 	VLDC_SET_MDEG_PROP_INST(pspecp, inst);
414*1ae08745Sheppo 
415*1ae08745Sheppo 	/* initialize the complete prop spec structure */
416*1ae08745Sheppo 	inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
417*1ae08745Sheppo 	inst_specp->namep = "virtual-device";
418*1ae08745Sheppo 	inst_specp->specp = pspecp;
419*1ae08745Sheppo 
420*1ae08745Sheppo 	/* perform the registration */
421*1ae08745Sheppo 	rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb,
422*1ae08745Sheppo 	    vldcp, &mdeg_hdl);
423*1ae08745Sheppo 
424*1ae08745Sheppo 	if (rv != MDEG_SUCCESS) {
425*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register "
426*1ae08745Sheppo 		    "failed, err = %d", rv);
427*1ae08745Sheppo 		kmem_free(name, namesz);
428*1ae08745Sheppo 		kmem_free(pspecp, templatesz);
429*1ae08745Sheppo 		kmem_free(inst_specp, sizeof (mdeg_node_spec_t));
430*1ae08745Sheppo 		return (DDI_FAILURE);
431*1ae08745Sheppo 	}
432*1ae08745Sheppo 
433*1ae08745Sheppo 	/* save off data that will be needed later */
434*1ae08745Sheppo 	vldcp->inst_spec = inst_specp;
435*1ae08745Sheppo 	vldcp->mdeg_hdl = mdeg_hdl;
436*1ae08745Sheppo 
437*1ae08745Sheppo 	return (DDI_SUCCESS);
438*1ae08745Sheppo }
439*1ae08745Sheppo 
440*1ae08745Sheppo /* unregister callback from mdeg */
441*1ae08745Sheppo static int
442*1ae08745Sheppo i_vldc_mdeg_unregister(vldc_t *vldcp)
443*1ae08745Sheppo {
444*1ae08745Sheppo 	char	*name;
445*1ae08745Sheppo 	int	rv;
446*1ae08745Sheppo 
447*1ae08745Sheppo 	D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl);
448*1ae08745Sheppo 
449*1ae08745Sheppo 	rv = mdeg_unregister(vldcp->mdeg_hdl);
450*1ae08745Sheppo 	if (rv != MDEG_SUCCESS) {
451*1ae08745Sheppo 		return (rv);
452*1ae08745Sheppo 	}
453*1ae08745Sheppo 
454*1ae08745Sheppo 	/*
455*1ae08745Sheppo 	 * Clean up cached MDEG data
456*1ae08745Sheppo 	 */
457*1ae08745Sheppo 	name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp);
458*1ae08745Sheppo 	if (name != NULL) {
459*1ae08745Sheppo 		kmem_free(name, strlen(name) + 1);
460*1ae08745Sheppo 	}
461*1ae08745Sheppo 	kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template));
462*1ae08745Sheppo 	vldcp->inst_spec->specp = NULL;
463*1ae08745Sheppo 
464*1ae08745Sheppo 	kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t));
465*1ae08745Sheppo 	vldcp->inst_spec = NULL;
466*1ae08745Sheppo 
467*1ae08745Sheppo 	return (MDEG_SUCCESS);
468*1ae08745Sheppo }
469*1ae08745Sheppo 
470*1ae08745Sheppo static int
471*1ae08745Sheppo i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
472*1ae08745Sheppo {
473*1ae08745Sheppo 	int num_nodes, nchan;
474*1ae08745Sheppo 	size_t listsz;
475*1ae08745Sheppo 	mde_cookie_t *listp;
476*1ae08745Sheppo 
477*1ae08745Sheppo 	/*
478*1ae08745Sheppo 	 * Find the channel-endpoint node(s) (which should be under this
479*1ae08745Sheppo 	 * port node) which contain the channel id(s).
480*1ae08745Sheppo 	 */
481*1ae08745Sheppo 	if ((num_nodes = md_node_count(mdp)) <= 0) {
482*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of "
483*1ae08745Sheppo 		    "channel-endpoint nodes found (%d)", num_nodes);
484*1ae08745Sheppo 		return (-1);
485*1ae08745Sheppo 	}
486*1ae08745Sheppo 
487*1ae08745Sheppo 	/* allocate space for node list */
488*1ae08745Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
489*1ae08745Sheppo 	listp = kmem_alloc(listsz, KM_SLEEP);
490*1ae08745Sheppo 
491*1ae08745Sheppo 	nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
492*1ae08745Sheppo 	    md_find_name(mdp, "fwd"), listp);
493*1ae08745Sheppo 
494*1ae08745Sheppo 	if (nchan <= 0) {
495*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint"
496*1ae08745Sheppo 		    " nodes found");
497*1ae08745Sheppo 		kmem_free(listp, listsz);
498*1ae08745Sheppo 		return (-1);
499*1ae08745Sheppo 	}
500*1ae08745Sheppo 
501*1ae08745Sheppo 	D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan);
502*1ae08745Sheppo 
503*1ae08745Sheppo 	/* use property from first node found */
504*1ae08745Sheppo 	if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
505*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint "
506*1ae08745Sheppo 		    "has no 'id' property");
507*1ae08745Sheppo 		kmem_free(listp, listsz);
508*1ae08745Sheppo 		return (-1);
509*1ae08745Sheppo 	}
510*1ae08745Sheppo 
511*1ae08745Sheppo 	kmem_free(listp, listsz);
512*1ae08745Sheppo 
513*1ae08745Sheppo 	return (0);
514*1ae08745Sheppo }
515*1ae08745Sheppo 
516*1ae08745Sheppo /* add a vldc port */
517*1ae08745Sheppo static int
518*1ae08745Sheppo i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node)
519*1ae08745Sheppo {
520*1ae08745Sheppo 	vldc_port_t	*vport;
521*1ae08745Sheppo 	char		*sname;
522*1ae08745Sheppo 	uint64_t	portno;
523*1ae08745Sheppo 	int		vldc_inst;
524*1ae08745Sheppo 	minor_t		minor;
525*1ae08745Sheppo 	int		minor_idx;
526*1ae08745Sheppo 	boolean_t	new_minor;
527*1ae08745Sheppo 	int		rv;
528*1ae08745Sheppo 
529*1ae08745Sheppo 	/* read in the port's id property */
530*1ae08745Sheppo 	if (md_get_prop_val(mdp, node, "id", &portno)) {
531*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added "
532*1ae08745Sheppo 		    "list has no 'id' property", node);
533*1ae08745Sheppo 		return (MDEG_FAILURE);
534*1ae08745Sheppo 	}
535*1ae08745Sheppo 
536*1ae08745Sheppo 	if (portno >= VLDC_MAX_PORTS) {
537*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) "
538*1ae08745Sheppo 		    "larger than maximum supported number of ports", portno);
539*1ae08745Sheppo 		return (MDEG_FAILURE);
540*1ae08745Sheppo 	}
541*1ae08745Sheppo 
542*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
543*1ae08745Sheppo 
544*1ae08745Sheppo 	if (vport->minorp != NULL) {
545*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
546*1ae08745Sheppo 		    " which is already bound", portno);
547*1ae08745Sheppo 		return (MDEG_FAILURE);
548*1ae08745Sheppo 	}
549*1ae08745Sheppo 
550*1ae08745Sheppo 	vport->number = portno;
551*1ae08745Sheppo 
552*1ae08745Sheppo 	/* get all channels for this device (currently only one) */
553*1ae08745Sheppo 	if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) {
554*1ae08745Sheppo 		return (MDEG_FAILURE);
555*1ae08745Sheppo 	}
556*1ae08745Sheppo 
557*1ae08745Sheppo 	/* set the default MTU */
558*1ae08745Sheppo 	vport->mtu = VLDC_DEFAULT_MTU;
559*1ae08745Sheppo 
560*1ae08745Sheppo 	/* get the service being exported by this port */
561*1ae08745Sheppo 	if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) {
562*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no "
563*1ae08745Sheppo 		    "'vldc-svc-name' property");
564*1ae08745Sheppo 		return (MDEG_FAILURE);
565*1ae08745Sheppo 	}
566*1ae08745Sheppo 
567*1ae08745Sheppo 	/* minor number look up */
568*1ae08745Sheppo 	for (minor_idx = 0; minor_idx < vldcp->minors_assigned;
569*1ae08745Sheppo 	    minor_idx++) {
570*1ae08745Sheppo 		if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) {
571*1ae08745Sheppo 			/* found previously assigned minor number */
572*1ae08745Sheppo 			break;
573*1ae08745Sheppo 		}
574*1ae08745Sheppo 	}
575*1ae08745Sheppo 
576*1ae08745Sheppo 	new_minor = B_FALSE;
577*1ae08745Sheppo 	if (minor_idx == vldcp->minors_assigned) {
578*1ae08745Sheppo 		/* end of lookup - assign new minor number */
579*1ae08745Sheppo 		if (vldcp->minors_assigned == VLDC_MAX_MINORS) {
580*1ae08745Sheppo 			cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor "
581*1ae08745Sheppo 			    "nodes (%d)", minor_idx);
582*1ae08745Sheppo 			return (MDEG_FAILURE);
583*1ae08745Sheppo 		}
584*1ae08745Sheppo 
585*1ae08745Sheppo 		(void) strlcpy(vldcp->minor_tbl[minor_idx].sname,
586*1ae08745Sheppo 		    sname, MAXPATHLEN);
587*1ae08745Sheppo 
588*1ae08745Sheppo 		vldcp->minors_assigned++;
589*1ae08745Sheppo 		new_minor = B_TRUE;
590*1ae08745Sheppo 	}
591*1ae08745Sheppo 
592*1ae08745Sheppo 	ASSERT(vldcp->minor_tbl[minor_idx].portno == VLDC_INVALID_PORTNO);
593*1ae08745Sheppo 
594*1ae08745Sheppo 	vport->minorp = &vldcp->minor_tbl[minor_idx];
595*1ae08745Sheppo 	vldcp->minor_tbl[minor_idx].portno = portno;
596*1ae08745Sheppo 	vldcp->minor_tbl[minor_idx].in_use = 0;
597*1ae08745Sheppo 
598*1ae08745Sheppo 	D1("i_vldc_add_port: port@%d  mtu=%d, ldc=%ld, service=%s\n",
599*1ae08745Sheppo 	    vport->number, vport->mtu, vport->ldc_id, sname);
600*1ae08745Sheppo 
601*1ae08745Sheppo 	/*
602*1ae08745Sheppo 	 * Create a minor node. The minor number is
603*1ae08745Sheppo 	 * (vldc_inst << VLDC_INST_SHIFT) | minor_idx
604*1ae08745Sheppo 	 */
605*1ae08745Sheppo 	vldc_inst = ddi_get_instance(vldcp->dip);
606*1ae08745Sheppo 
607*1ae08745Sheppo 	minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx);
608*1ae08745Sheppo 
609*1ae08745Sheppo 	rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR,
610*1ae08745Sheppo 	    minor, DDI_NT_SERIAL, 0);
611*1ae08745Sheppo 
612*1ae08745Sheppo 	if (rv != DDI_SUCCESS) {
613*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor"
614*1ae08745Sheppo 		    "node (%u), err = %d", minor, rv);
615*1ae08745Sheppo 		vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO;
616*1ae08745Sheppo 		if (new_minor) {
617*1ae08745Sheppo 			vldcp->minors_assigned--;
618*1ae08745Sheppo 		}
619*1ae08745Sheppo 		return (MDEG_FAILURE);
620*1ae08745Sheppo 	}
621*1ae08745Sheppo 
622*1ae08745Sheppo 	/*
623*1ae08745Sheppo 	 * The port is now bound to a minor node and is initially in the
624*1ae08745Sheppo 	 * closed state.
625*1ae08745Sheppo 	 */
626*1ae08745Sheppo 	vport->status = VLDC_PORT_CLOSED;
627*1ae08745Sheppo 
628*1ae08745Sheppo 	D1("i_vldc_add_port: port %lu initialized\n", portno);
629*1ae08745Sheppo 
630*1ae08745Sheppo 	return (MDEG_SUCCESS);
631*1ae08745Sheppo }
632*1ae08745Sheppo 
633*1ae08745Sheppo /* remove a vldc port */
634*1ae08745Sheppo static int
635*1ae08745Sheppo i_vldc_remove_port(vldc_t *vldcp, uint_t portno)
636*1ae08745Sheppo {
637*1ae08745Sheppo 	vldc_port_t *vport;
638*1ae08745Sheppo 	vldc_minor_t *vminor;
639*1ae08745Sheppo 
640*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
641*1ae08745Sheppo 	vminor = vport->minorp;
642*1ae08745Sheppo 	if (vminor == NULL) {
643*1ae08745Sheppo 		cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a "
644*1ae08745Sheppo 		    "port (%u) which is not bound", portno);
645*1ae08745Sheppo 		return (MDEG_FAILURE);
646*1ae08745Sheppo 	}
647*1ae08745Sheppo 
648*1ae08745Sheppo 	/*
649*1ae08745Sheppo 	 * Make sure that all new attempts to open or use the minor node
650*1ae08745Sheppo 	 * associated with the port will fail.
651*1ae08745Sheppo 	 */
652*1ae08745Sheppo 	mutex_enter(&vminor->lock);
653*1ae08745Sheppo 	vminor->portno = VLDC_INVALID_PORTNO;
654*1ae08745Sheppo 	mutex_exit(&vminor->lock);
655*1ae08745Sheppo 
656*1ae08745Sheppo 	/* send hangup to anyone polling */
657*1ae08745Sheppo 	pollwakeup(&vport->poll, POLLHUP);
658*1ae08745Sheppo 
659*1ae08745Sheppo 	/* Now wait for all current users of the minor node to finish. */
660*1ae08745Sheppo 	mutex_enter(&vminor->lock);
661*1ae08745Sheppo 	while (vminor->in_use > 0) {
662*1ae08745Sheppo 		cv_wait(&vminor->cv, &vminor->lock);
663*1ae08745Sheppo 	}
664*1ae08745Sheppo 
665*1ae08745Sheppo 	if ((vport->status == VLDC_PORT_READY) ||
666*1ae08745Sheppo 	    (vport->status == VLDC_PORT_OPEN)) {
667*1ae08745Sheppo 		/* close the port before it is torn down */
668*1ae08745Sheppo 		(void) i_vldc_close_port(vldcp, portno);
669*1ae08745Sheppo 	}
670*1ae08745Sheppo 
671*1ae08745Sheppo 	/* remove minor node */
672*1ae08745Sheppo 	ddi_remove_minor_node(vldcp->dip, vport->minorp->sname);
673*1ae08745Sheppo 	vport->minorp = NULL;
674*1ae08745Sheppo 
675*1ae08745Sheppo 	mutex_exit(&vminor->lock);
676*1ae08745Sheppo 
677*1ae08745Sheppo 	D1("i_vldc_remove_port: removed vldc port %u\n", portno);
678*1ae08745Sheppo 
679*1ae08745Sheppo 	return (MDEG_SUCCESS);
680*1ae08745Sheppo }
681*1ae08745Sheppo 
682*1ae08745Sheppo /* close a ldc channel */
683*1ae08745Sheppo static int
684*1ae08745Sheppo i_vldc_ldc_close(vldc_port_t *vport)
685*1ae08745Sheppo {
686*1ae08745Sheppo 	int rv = 0;
687*1ae08745Sheppo 	int err;
688*1ae08745Sheppo 
689*1ae08745Sheppo 	err = ldc_close(vport->ldc_handle);
690*1ae08745Sheppo 	if (err != 0)
691*1ae08745Sheppo 		rv = err;
692*1ae08745Sheppo 	err = ldc_unreg_callback(vport->ldc_handle);
693*1ae08745Sheppo 	if ((err != 0) && (rv != 0))
694*1ae08745Sheppo 		rv = err;
695*1ae08745Sheppo 	err = ldc_fini(vport->ldc_handle);
696*1ae08745Sheppo 	if ((err != 0) && (rv != 0))
697*1ae08745Sheppo 		rv = err;
698*1ae08745Sheppo 
699*1ae08745Sheppo 	return (rv);
700*1ae08745Sheppo }
701*1ae08745Sheppo 
702*1ae08745Sheppo /* close a vldc port */
703*1ae08745Sheppo static int
704*1ae08745Sheppo i_vldc_close_port(vldc_t *vldcp, uint_t portno)
705*1ae08745Sheppo {
706*1ae08745Sheppo 	vldc_port_t *vport;
707*1ae08745Sheppo 	int rv;
708*1ae08745Sheppo 
709*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
710*1ae08745Sheppo 
711*1ae08745Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
712*1ae08745Sheppo 
713*1ae08745Sheppo 	if (vport->status == VLDC_PORT_CLOSED) {
714*1ae08745Sheppo 		/* nothing to do */
715*1ae08745Sheppo 		DWARN("i_vldc_close_port: port %d in an unexpected "
716*1ae08745Sheppo 		    "state (%d)\n", portno, vport->status);
717*1ae08745Sheppo 		return (DDI_SUCCESS);
718*1ae08745Sheppo 	}
719*1ae08745Sheppo 
720*1ae08745Sheppo 	rv = DDI_SUCCESS;
721*1ae08745Sheppo 	if (vport->status == VLDC_PORT_READY) {
722*1ae08745Sheppo 		rv = i_vldc_ldc_close(vport);
723*1ae08745Sheppo 	} else {
724*1ae08745Sheppo 		ASSERT(vport->status == VLDC_PORT_OPEN);
725*1ae08745Sheppo 	}
726*1ae08745Sheppo 
727*1ae08745Sheppo 	/* free memory */
728*1ae08745Sheppo 	kmem_free(vport->send_buf, vport->mtu);
729*1ae08745Sheppo 	kmem_free(vport->recv_buf, vport->mtu);
730*1ae08745Sheppo 
731*1ae08745Sheppo 	vport->status = VLDC_PORT_CLOSED;
732*1ae08745Sheppo 
733*1ae08745Sheppo 	return (rv);
734*1ae08745Sheppo }
735*1ae08745Sheppo 
736*1ae08745Sheppo /*
737*1ae08745Sheppo  * attach(9E): attach a device to the system.
738*1ae08745Sheppo  * called once for each instance of the device on the system.
739*1ae08745Sheppo  */
740*1ae08745Sheppo static int
741*1ae08745Sheppo vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
742*1ae08745Sheppo {
743*1ae08745Sheppo 	int 	i, instance;
744*1ae08745Sheppo 	vldc_t	*vldcp;
745*1ae08745Sheppo 
746*1ae08745Sheppo 	switch (cmd) {
747*1ae08745Sheppo 
748*1ae08745Sheppo 	case DDI_ATTACH:
749*1ae08745Sheppo 
750*1ae08745Sheppo 		instance = ddi_get_instance(dip);
751*1ae08745Sheppo 
752*1ae08745Sheppo 		if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) {
753*1ae08745Sheppo 			return (DDI_FAILURE);
754*1ae08745Sheppo 		}
755*1ae08745Sheppo 
756*1ae08745Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
757*1ae08745Sheppo 		if (vldcp == NULL) {
758*1ae08745Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
759*1ae08745Sheppo 			return (ENXIO);
760*1ae08745Sheppo 		}
761*1ae08745Sheppo 
762*1ae08745Sheppo 		D1("vldc_attach: DDI_ATTACH instance=%d\n", instance);
763*1ae08745Sheppo 
764*1ae08745Sheppo 		mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL);
765*1ae08745Sheppo 		vldcp->dip = dip;
766*1ae08745Sheppo 		vldcp->detaching = B_FALSE;
767*1ae08745Sheppo 
768*1ae08745Sheppo 		for (i = 0; i < VLDC_MAX_PORTS; i++) {
769*1ae08745Sheppo 			/* No minor node association to start with */
770*1ae08745Sheppo 			vldcp->port[i].minorp = NULL;
771*1ae08745Sheppo 		}
772*1ae08745Sheppo 
773*1ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
774*1ae08745Sheppo 			mutex_init(&(vldcp->minor_tbl[i].lock), NULL,
775*1ae08745Sheppo 			    MUTEX_DRIVER, NULL);
776*1ae08745Sheppo 			cv_init(&(vldcp->minor_tbl[i].cv), NULL,
777*1ae08745Sheppo 			    CV_DRIVER, NULL);
778*1ae08745Sheppo 			/* No port association to start with */
779*1ae08745Sheppo 			vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO;
780*1ae08745Sheppo 		}
781*1ae08745Sheppo 
782*1ae08745Sheppo 		/* Register for MD update notification */
783*1ae08745Sheppo 		if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) {
784*1ae08745Sheppo 			ddi_soft_state_free(vldc_ssp, instance);
785*1ae08745Sheppo 			return (DDI_FAILURE);
786*1ae08745Sheppo 		}
787*1ae08745Sheppo 
788*1ae08745Sheppo 		return (DDI_SUCCESS);
789*1ae08745Sheppo 
790*1ae08745Sheppo 	case DDI_RESUME:
791*1ae08745Sheppo 
792*1ae08745Sheppo 		return (DDI_SUCCESS);
793*1ae08745Sheppo 
794*1ae08745Sheppo 	default:
795*1ae08745Sheppo 
796*1ae08745Sheppo 		return (DDI_FAILURE);
797*1ae08745Sheppo 	}
798*1ae08745Sheppo }
799*1ae08745Sheppo 
800*1ae08745Sheppo /*
801*1ae08745Sheppo  * detach(9E): detach a device from the system.
802*1ae08745Sheppo  */
803*1ae08745Sheppo static int
804*1ae08745Sheppo vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
805*1ae08745Sheppo {
806*1ae08745Sheppo 	int 		i, instance;
807*1ae08745Sheppo 	vldc_t		*vldcp;
808*1ae08745Sheppo 
809*1ae08745Sheppo 	switch (cmd) {
810*1ae08745Sheppo 
811*1ae08745Sheppo 	case DDI_DETACH:
812*1ae08745Sheppo 
813*1ae08745Sheppo 		instance = ddi_get_instance(dip);
814*1ae08745Sheppo 
815*1ae08745Sheppo 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
816*1ae08745Sheppo 		if (vldcp == NULL) {
817*1ae08745Sheppo 			return (DDI_FAILURE);
818*1ae08745Sheppo 		}
819*1ae08745Sheppo 
820*1ae08745Sheppo 		D1("vldc_detach: DDI_DETACH instance=%d\n", instance);
821*1ae08745Sheppo 
822*1ae08745Sheppo 		mutex_enter(&vldcp->lock);
823*1ae08745Sheppo 
824*1ae08745Sheppo 		/* Fail the detach if all ports have not been removed. */
825*1ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
826*1ae08745Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
827*1ae08745Sheppo 				D1("vldc_detach: vldc@%d:%d is bound, "
828*1ae08745Sheppo 				    "detach failed\n",
829*1ae08745Sheppo 				    instance, vldcp->minor_tbl[i].portno);
830*1ae08745Sheppo 				mutex_exit(&vldcp->lock);
831*1ae08745Sheppo 				return (DDI_FAILURE);
832*1ae08745Sheppo 			}
833*1ae08745Sheppo 		}
834*1ae08745Sheppo 
835*1ae08745Sheppo 		/*
836*1ae08745Sheppo 		 * Prevent MDEG from adding new ports before the callback can
837*1ae08745Sheppo 		 * be unregistered. The lock can't be held accross the
838*1ae08745Sheppo 		 * unregistration call because a callback may be in progress
839*1ae08745Sheppo 		 * and blocked on the lock.
840*1ae08745Sheppo 		 */
841*1ae08745Sheppo 		vldcp->detaching = B_TRUE;
842*1ae08745Sheppo 
843*1ae08745Sheppo 		mutex_exit(&vldcp->lock);
844*1ae08745Sheppo 
845*1ae08745Sheppo 		if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) {
846*1ae08745Sheppo 			vldcp->detaching = B_FALSE;
847*1ae08745Sheppo 			return (DDI_FAILURE);
848*1ae08745Sheppo 		}
849*1ae08745Sheppo 
850*1ae08745Sheppo 		/* Tear down all bound ports and free resources. */
851*1ae08745Sheppo 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
852*1ae08745Sheppo 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
853*1ae08745Sheppo 				(void) i_vldc_remove_port(vldcp, i);
854*1ae08745Sheppo 			}
855*1ae08745Sheppo 			mutex_destroy(&(vldcp->minor_tbl[i].lock));
856*1ae08745Sheppo 			cv_destroy(&(vldcp->minor_tbl[i].cv));
857*1ae08745Sheppo 		}
858*1ae08745Sheppo 
859*1ae08745Sheppo 		mutex_destroy(&vldcp->lock);
860*1ae08745Sheppo 		ddi_soft_state_free(vldc_ssp, instance);
861*1ae08745Sheppo 
862*1ae08745Sheppo 		return (DDI_SUCCESS);
863*1ae08745Sheppo 
864*1ae08745Sheppo 	case DDI_SUSPEND:
865*1ae08745Sheppo 
866*1ae08745Sheppo 		return (DDI_SUCCESS);
867*1ae08745Sheppo 
868*1ae08745Sheppo 	default:
869*1ae08745Sheppo 
870*1ae08745Sheppo 		return (DDI_FAILURE);
871*1ae08745Sheppo 	}
872*1ae08745Sheppo }
873*1ae08745Sheppo 
874*1ae08745Sheppo /* cb_open */
875*1ae08745Sheppo static int
876*1ae08745Sheppo vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
877*1ae08745Sheppo {
878*1ae08745Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
879*1ae08745Sheppo 
880*1ae08745Sheppo 	int instance;
881*1ae08745Sheppo 	minor_t minor;
882*1ae08745Sheppo 	uint64_t portno;
883*1ae08745Sheppo 	vldc_t *vldcp;
884*1ae08745Sheppo 	vldc_port_t *vport;
885*1ae08745Sheppo 	vldc_minor_t *vminor;
886*1ae08745Sheppo 
887*1ae08745Sheppo 	minor = getminor(*devp);
888*1ae08745Sheppo 	instance = VLDCINST(minor);
889*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
890*1ae08745Sheppo 	if (vldcp == NULL)
891*1ae08745Sheppo 		return (ENXIO);
892*1ae08745Sheppo 
893*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
894*1ae08745Sheppo 	mutex_enter(&vminor->lock);
895*1ae08745Sheppo 	portno = vminor->portno;
896*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
897*1ae08745Sheppo 		mutex_exit(&vminor->lock);
898*1ae08745Sheppo 		return (ENXIO);
899*1ae08745Sheppo 	}
900*1ae08745Sheppo 
901*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
902*1ae08745Sheppo 
903*1ae08745Sheppo 	D1("vldc_open: opening vldc@%d:%lu\n", instance, portno);
904*1ae08745Sheppo 
905*1ae08745Sheppo 	if (vport->status != VLDC_PORT_CLOSED) {
906*1ae08745Sheppo 		mutex_exit(&vminor->lock);
907*1ae08745Sheppo 		return (EBUSY);
908*1ae08745Sheppo 	}
909*1ae08745Sheppo 
910*1ae08745Sheppo 	vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP);
911*1ae08745Sheppo 	vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP);
912*1ae08745Sheppo 
913*1ae08745Sheppo 	vport->is_stream = B_FALSE;	/* assume not a stream */
914*1ae08745Sheppo 	vport->hanged_up = B_FALSE;
915*1ae08745Sheppo 
916*1ae08745Sheppo 	vport->status = VLDC_PORT_OPEN;
917*1ae08745Sheppo 
918*1ae08745Sheppo 	mutex_exit(&vminor->lock);
919*1ae08745Sheppo 
920*1ae08745Sheppo 	return (DDI_SUCCESS);
921*1ae08745Sheppo }
922*1ae08745Sheppo 
923*1ae08745Sheppo /* cb_close */
924*1ae08745Sheppo static int
925*1ae08745Sheppo vldc_close(dev_t dev, int flag, int otyp, cred_t *cred)
926*1ae08745Sheppo {
927*1ae08745Sheppo 	_NOTE(ARGUNUSED(flag, otyp, cred))
928*1ae08745Sheppo 
929*1ae08745Sheppo 	int instance;
930*1ae08745Sheppo 	minor_t minor;
931*1ae08745Sheppo 	uint64_t portno;
932*1ae08745Sheppo 	vldc_t *vldcp;
933*1ae08745Sheppo 	vldc_minor_t *vminor;
934*1ae08745Sheppo 	int rv;
935*1ae08745Sheppo 
936*1ae08745Sheppo 	minor = getminor(dev);
937*1ae08745Sheppo 	instance = VLDCINST(minor);
938*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
939*1ae08745Sheppo 	if (vldcp == NULL) {
940*1ae08745Sheppo 		return (ENXIO);
941*1ae08745Sheppo 	}
942*1ae08745Sheppo 
943*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
944*1ae08745Sheppo 	mutex_enter(&vminor->lock);
945*1ae08745Sheppo 	portno = vminor->portno;
946*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
947*1ae08745Sheppo 		mutex_exit(&vminor->lock);
948*1ae08745Sheppo 		return (ENOLINK);
949*1ae08745Sheppo 	}
950*1ae08745Sheppo 
951*1ae08745Sheppo 	D1("vldc_close: closing vldc@%d:%lu\n", instance, portno);
952*1ae08745Sheppo 
953*1ae08745Sheppo 	rv = i_vldc_close_port(vldcp, portno);
954*1ae08745Sheppo 
955*1ae08745Sheppo 	mutex_exit(&vminor->lock);
956*1ae08745Sheppo 
957*1ae08745Sheppo 	return (rv);
958*1ae08745Sheppo }
959*1ae08745Sheppo 
960*1ae08745Sheppo static int
961*1ae08745Sheppo vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode)
962*1ae08745Sheppo {
963*1ae08745Sheppo 	ldc_attr_t attr;
964*1ae08745Sheppo 	int rv;
965*1ae08745Sheppo 
966*1ae08745Sheppo 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
967*1ae08745Sheppo 
968*1ae08745Sheppo 	/* validate mode */
969*1ae08745Sheppo 	switch (channel_mode) {
970*1ae08745Sheppo 	case LDC_MODE_STREAM:
971*1ae08745Sheppo 		vport->is_stream = B_TRUE;
972*1ae08745Sheppo 		break;
973*1ae08745Sheppo 	case LDC_MODE_RAW:
974*1ae08745Sheppo 	case LDC_MODE_UNRELIABLE:
975*1ae08745Sheppo 	case LDC_MODE_RELIABLE:
976*1ae08745Sheppo 		vport->is_stream = B_FALSE;
977*1ae08745Sheppo 		break;
978*1ae08745Sheppo 	default:
979*1ae08745Sheppo 		return (EINVAL);
980*1ae08745Sheppo 	}
981*1ae08745Sheppo 
982*1ae08745Sheppo 	if (vport->status == VLDC_PORT_READY) {
983*1ae08745Sheppo 		rv = i_vldc_ldc_close(vport);
984*1ae08745Sheppo 		vport->status = VLDC_PORT_OPEN;
985*1ae08745Sheppo 		if (rv != 0) {
986*1ae08745Sheppo 			DWARN("vldc_set_ldc_mode: i_vldc_ldc_close "
987*1ae08745Sheppo 			    "failed, rv=%d\n", rv);
988*1ae08745Sheppo 			return (rv);
989*1ae08745Sheppo 		}
990*1ae08745Sheppo 	}
991*1ae08745Sheppo 
992*1ae08745Sheppo 	D1("vldc_set_ldc_mode: vport status %d, mode %d\n",
993*1ae08745Sheppo 	    vport->status, channel_mode);
994*1ae08745Sheppo 
995*1ae08745Sheppo 	vport->ldc_mode = channel_mode;
996*1ae08745Sheppo 
997*1ae08745Sheppo 	/* initialize the channel */
998*1ae08745Sheppo 	attr.devclass = LDC_DEV_SERIAL;
999*1ae08745Sheppo 	attr.instance = ddi_get_instance(vldcp->dip);
1000*1ae08745Sheppo 	attr.qlen = VLDC_QUEUE_LEN;
1001*1ae08745Sheppo 	attr.mode = vport->ldc_mode;
1002*1ae08745Sheppo 
1003*1ae08745Sheppo 	if ((rv = ldc_init(vport->ldc_id, &attr,
1004*1ae08745Sheppo 	    &vport->ldc_handle)) != 0) {
1005*1ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv);
1006*1ae08745Sheppo 		goto error_init;
1007*1ae08745Sheppo 	}
1008*1ae08745Sheppo 
1009*1ae08745Sheppo 	/* register it */
1010*1ae08745Sheppo 	if ((rv = ldc_reg_callback(vport->ldc_handle,
1011*1ae08745Sheppo 	    i_vldc_cb, (caddr_t)vport)) != 0) {
1012*1ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n",
1013*1ae08745Sheppo 		    rv);
1014*1ae08745Sheppo 		goto error_reg;
1015*1ae08745Sheppo 	}
1016*1ae08745Sheppo 
1017*1ae08745Sheppo 	/* open the channel */
1018*1ae08745Sheppo 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
1019*1ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv);
1020*1ae08745Sheppo 		goto error_open;
1021*1ae08745Sheppo 	}
1022*1ae08745Sheppo 
1023*1ae08745Sheppo 	vport->status = VLDC_PORT_READY;
1024*1ae08745Sheppo 
1025*1ae08745Sheppo 	/*
1026*1ae08745Sheppo 	 * Attempt to bring the channel up, but do not
1027*1ae08745Sheppo 	 * fail if the other end is not up yet.
1028*1ae08745Sheppo 	 */
1029*1ae08745Sheppo 	rv = ldc_up(vport->ldc_handle);
1030*1ae08745Sheppo 
1031*1ae08745Sheppo 	if (rv == ECONNREFUSED) {
1032*1ae08745Sheppo 		D1("vldc_ioctl_opt_op: remote endpoint not up yet\n");
1033*1ae08745Sheppo 	} else if (rv != 0) {
1034*1ae08745Sheppo 		DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv);
1035*1ae08745Sheppo 		goto error_up;
1036*1ae08745Sheppo 	}
1037*1ae08745Sheppo 
1038*1ae08745Sheppo 	D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n",
1039*1ae08745Sheppo 	    vport->ldc_id);
1040*1ae08745Sheppo 
1041*1ae08745Sheppo 	return (0);
1042*1ae08745Sheppo 
1043*1ae08745Sheppo error_up:
1044*1ae08745Sheppo 	vport->status = VLDC_PORT_OPEN;
1045*1ae08745Sheppo 	(void) ldc_close(vport->ldc_handle);
1046*1ae08745Sheppo error_open:
1047*1ae08745Sheppo 	(void) ldc_unreg_callback(vport->ldc_handle);
1048*1ae08745Sheppo error_reg:
1049*1ae08745Sheppo 	(void) ldc_fini(vport->ldc_handle);
1050*1ae08745Sheppo error_init:
1051*1ae08745Sheppo 	return (rv);
1052*1ae08745Sheppo }
1053*1ae08745Sheppo 
1054*1ae08745Sheppo /* ioctl to read cookie */
1055*1ae08745Sheppo static int
1056*1ae08745Sheppo i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
1057*1ae08745Sheppo     int mode)
1058*1ae08745Sheppo {
1059*1ae08745Sheppo 	vldc_data_t copy_info;
1060*1ae08745Sheppo 	caddr_t buf;
1061*1ae08745Sheppo 	uint64_t len;
1062*1ae08745Sheppo 	int rv;
1063*1ae08745Sheppo 
1064*1ae08745Sheppo 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) == -1) {
1065*1ae08745Sheppo 		return (EFAULT);
1066*1ae08745Sheppo 	}
1067*1ae08745Sheppo 
1068*1ae08745Sheppo 	len = copy_info.length;
1069*1ae08745Sheppo 	if (len > vldc_max_cookie) {
1070*1ae08745Sheppo 		return (EINVAL);
1071*1ae08745Sheppo 	}
1072*1ae08745Sheppo 
1073*1ae08745Sheppo 	/* allocate a temporary buffer */
1074*1ae08745Sheppo 	buf = kmem_alloc(len, KM_SLEEP);
1075*1ae08745Sheppo 
1076*1ae08745Sheppo 	mutex_enter(&vport->minorp->lock);
1077*1ae08745Sheppo 
1078*1ae08745Sheppo 	D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%lx "
1079*1ae08745Sheppo 	    "size 0x%lx to 0x%lx\n", vldc_instance, vport->number,
1080*1ae08745Sheppo 	    copy_info.dst_addr, copy_info.length, copy_info.src_addr);
1081*1ae08745Sheppo 
1082*1ae08745Sheppo 	/* read from the HV into the temporary buffer */
1083*1ae08745Sheppo 	rv = ldc_mem_rdwr_pa(vport->ldc_handle, buf, &len,
1084*1ae08745Sheppo 	    (caddr_t)copy_info.dst_addr, LDC_COPY_IN);
1085*1ae08745Sheppo 	if (rv != 0) {
1086*1ae08745Sheppo 		DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot read "
1087*1ae08745Sheppo 		    "address 0x%lx, rv=%d\n", vldc_instance, vport->number,
1088*1ae08745Sheppo 		    copy_info.dst_addr, rv);
1089*1ae08745Sheppo 		mutex_exit(&vport->minorp->lock);
1090*1ae08745Sheppo 		kmem_free(buf, copy_info.length);
1091*1ae08745Sheppo 		return (EFAULT);
1092*1ae08745Sheppo 	}
1093*1ae08745Sheppo 
1094*1ae08745Sheppo 	D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n",
1095*1ae08745Sheppo 	    vldc_instance, vport->number);
1096*1ae08745Sheppo 
1097*1ae08745Sheppo 	mutex_exit(&vport->minorp->lock);
1098*1ae08745Sheppo 
1099*1ae08745Sheppo 	/* copy data from temporary buffer out to the caller and free buffer */
1100*1ae08745Sheppo 	rv = ddi_copyout(buf, (caddr_t)copy_info.src_addr, len, mode);
1101*1ae08745Sheppo 	kmem_free(buf, copy_info.length);
1102*1ae08745Sheppo 	if (rv != 0) {
1103*1ae08745Sheppo 		return (EFAULT);
1104*1ae08745Sheppo 	}
1105*1ae08745Sheppo 
1106*1ae08745Sheppo 	/* set the structure to reflect outcome */
1107*1ae08745Sheppo 	copy_info.length = len;
1108*1ae08745Sheppo 	if (ddi_copyout(&copy_info, arg, sizeof (copy_info), mode) != 0) {
1109*1ae08745Sheppo 		return (EFAULT);
1110*1ae08745Sheppo 	}
1111*1ae08745Sheppo 
1112*1ae08745Sheppo 	return (0);
1113*1ae08745Sheppo }
1114*1ae08745Sheppo 
1115*1ae08745Sheppo /* ioctl to write cookie */
1116*1ae08745Sheppo static int
1117*1ae08745Sheppo i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
1118*1ae08745Sheppo     int mode)
1119*1ae08745Sheppo {
1120*1ae08745Sheppo 	vldc_data_t copy_info;
1121*1ae08745Sheppo 	caddr_t buf;
1122*1ae08745Sheppo 	uint64_t len;
1123*1ae08745Sheppo 	int rv;
1124*1ae08745Sheppo 
1125*1ae08745Sheppo 	if (ddi_copyin((caddr_t)arg, &copy_info,
1126*1ae08745Sheppo 	    sizeof (copy_info), mode) != 0) {
1127*1ae08745Sheppo 		return (EFAULT);
1128*1ae08745Sheppo 	}
1129*1ae08745Sheppo 
1130*1ae08745Sheppo 	len = copy_info.length;
1131*1ae08745Sheppo 	if (len > vldc_max_cookie) {
1132*1ae08745Sheppo 		return (EINVAL);
1133*1ae08745Sheppo 	}
1134*1ae08745Sheppo 
1135*1ae08745Sheppo 	D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx "
1136*1ae08745Sheppo 	    "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr,
1137*1ae08745Sheppo 	    copy_info.length, copy_info.dst_addr);
1138*1ae08745Sheppo 
1139*1ae08745Sheppo 	/* allocate a temporary buffer */
1140*1ae08745Sheppo 	buf = kmem_alloc(len, KM_SLEEP);
1141*1ae08745Sheppo 
1142*1ae08745Sheppo 	/* copy into the temporary buffer the data to be written to the HV */
1143*1ae08745Sheppo 	if (ddi_copyin((caddr_t)copy_info.src_addr, buf,
1144*1ae08745Sheppo 	    copy_info.length, mode) != 0) {
1145*1ae08745Sheppo 		kmem_free(buf, copy_info.length);
1146*1ae08745Sheppo 		return (EFAULT);
1147*1ae08745Sheppo 	}
1148*1ae08745Sheppo 
1149*1ae08745Sheppo 	mutex_enter(&vport->minorp->lock);
1150*1ae08745Sheppo 
1151*1ae08745Sheppo 	/* write the data from the temporary buffer to the HV */
1152*1ae08745Sheppo 	rv = ldc_mem_rdwr_pa(vport->ldc_handle, buf, &len,
1153*1ae08745Sheppo 	    (caddr_t)copy_info.dst_addr, LDC_COPY_OUT);
1154*1ae08745Sheppo 	if (rv != 0) {
1155*1ae08745Sheppo 		DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d failed to write at"
1156*1ae08745Sheppo 		    " address 0x%lx\n, rv=%d", vldc_instance, vport->number,
1157*1ae08745Sheppo 		    copy_info.dst_addr, rv);
1158*1ae08745Sheppo 		mutex_exit(&vport->minorp->lock);
1159*1ae08745Sheppo 		kmem_free(buf, copy_info.length);
1160*1ae08745Sheppo 		return (EFAULT);
1161*1ae08745Sheppo 	}
1162*1ae08745Sheppo 
1163*1ae08745Sheppo 	D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n",
1164*1ae08745Sheppo 	    vldc_instance, vport->number);
1165*1ae08745Sheppo 
1166*1ae08745Sheppo 	mutex_exit(&vport->minorp->lock);
1167*1ae08745Sheppo 
1168*1ae08745Sheppo 	kmem_free(buf, copy_info.length);
1169*1ae08745Sheppo 
1170*1ae08745Sheppo 	/* set the structure to reflect outcome */
1171*1ae08745Sheppo 	copy_info.length = len;
1172*1ae08745Sheppo 	if (ddi_copyout(&copy_info, (caddr_t)arg,
1173*1ae08745Sheppo 	    sizeof (copy_info), mode) != 0) {
1174*1ae08745Sheppo 		return (EFAULT);
1175*1ae08745Sheppo 	}
1176*1ae08745Sheppo 
1177*1ae08745Sheppo 	return (0);
1178*1ae08745Sheppo }
1179*1ae08745Sheppo 
1180*1ae08745Sheppo /* vldc specific ioctl option commands */
1181*1ae08745Sheppo static int
1182*1ae08745Sheppo i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode)
1183*1ae08745Sheppo {
1184*1ae08745Sheppo 	vldc_opt_op_t 	vldc_cmd;
1185*1ae08745Sheppo 	uint32_t	new_mtu;
1186*1ae08745Sheppo 	int		rv = 0;
1187*1ae08745Sheppo 
1188*1ae08745Sheppo 	if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) {
1189*1ae08745Sheppo 		return (EFAULT);
1190*1ae08745Sheppo 	}
1191*1ae08745Sheppo 
1192*1ae08745Sheppo 	D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel);
1193*1ae08745Sheppo 
1194*1ae08745Sheppo 	switch (vldc_cmd.opt_sel) {
1195*1ae08745Sheppo 
1196*1ae08745Sheppo 	case VLDC_OPT_MTU_SZ:
1197*1ae08745Sheppo 
1198*1ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1199*1ae08745Sheppo 			vldc_cmd.opt_val = vport->mtu;
1200*1ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
1201*1ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
1202*1ae08745Sheppo 				return (EFAULT);
1203*1ae08745Sheppo 			}
1204*1ae08745Sheppo 		} else {
1205*1ae08745Sheppo 			new_mtu = vldc_cmd.opt_val;
1206*1ae08745Sheppo 
1207*1ae08745Sheppo 			if ((new_mtu < LDC_PACKET_SIZE) ||
1208*1ae08745Sheppo 			    (new_mtu > vldc_max_mtu)) {
1209*1ae08745Sheppo 				return (EINVAL);
1210*1ae08745Sheppo 			}
1211*1ae08745Sheppo 
1212*1ae08745Sheppo 			mutex_enter(&vport->minorp->lock);
1213*1ae08745Sheppo 
1214*1ae08745Sheppo 			if ((vport->status != VLDC_PORT_CLOSED) &&
1215*1ae08745Sheppo 			    (new_mtu != vport->mtu)) {
1216*1ae08745Sheppo 				/*
1217*1ae08745Sheppo 				 * The port has buffers allocated since it is
1218*1ae08745Sheppo 				 * not closed plus the MTU size has changed.
1219*1ae08745Sheppo 				 * Reallocate the buffers to the new MTU size.
1220*1ae08745Sheppo 				 */
1221*1ae08745Sheppo 				kmem_free(vport->recv_buf, vport->mtu);
1222*1ae08745Sheppo 				vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP);
1223*1ae08745Sheppo 
1224*1ae08745Sheppo 				kmem_free(vport->send_buf, vport->mtu);
1225*1ae08745Sheppo 				vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP);
1226*1ae08745Sheppo 
1227*1ae08745Sheppo 				vport->mtu = new_mtu;
1228*1ae08745Sheppo 			}
1229*1ae08745Sheppo 
1230*1ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
1231*1ae08745Sheppo 		}
1232*1ae08745Sheppo 
1233*1ae08745Sheppo 		break;
1234*1ae08745Sheppo 
1235*1ae08745Sheppo 	case VLDC_OPT_STATUS:
1236*1ae08745Sheppo 
1237*1ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1238*1ae08745Sheppo 			vldc_cmd.opt_val = vport->status;
1239*1ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
1240*1ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
1241*1ae08745Sheppo 				return (EFAULT);
1242*1ae08745Sheppo 			}
1243*1ae08745Sheppo 		} else {
1244*1ae08745Sheppo 			return (ENOTSUP);
1245*1ae08745Sheppo 		}
1246*1ae08745Sheppo 
1247*1ae08745Sheppo 		break;
1248*1ae08745Sheppo 
1249*1ae08745Sheppo 	case VLDC_OPT_MODE:
1250*1ae08745Sheppo 
1251*1ae08745Sheppo 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1252*1ae08745Sheppo 			vldc_cmd.opt_val = vport->ldc_mode;
1253*1ae08745Sheppo 			if (ddi_copyout(&vldc_cmd, arg,
1254*1ae08745Sheppo 			    sizeof (vldc_cmd), mode) == -1) {
1255*1ae08745Sheppo 				return (EFAULT);
1256*1ae08745Sheppo 			}
1257*1ae08745Sheppo 		} else {
1258*1ae08745Sheppo 			mutex_enter(&vport->minorp->lock);
1259*1ae08745Sheppo 			rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val);
1260*1ae08745Sheppo 			mutex_exit(&vport->minorp->lock);
1261*1ae08745Sheppo 		}
1262*1ae08745Sheppo 
1263*1ae08745Sheppo 		break;
1264*1ae08745Sheppo 
1265*1ae08745Sheppo 	default:
1266*1ae08745Sheppo 
1267*1ae08745Sheppo 		D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel);
1268*1ae08745Sheppo 		return (ENOTSUP);
1269*1ae08745Sheppo 	}
1270*1ae08745Sheppo 
1271*1ae08745Sheppo 	return (rv);
1272*1ae08745Sheppo }
1273*1ae08745Sheppo 
1274*1ae08745Sheppo /* cb_ioctl */
1275*1ae08745Sheppo static int
1276*1ae08745Sheppo vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1277*1ae08745Sheppo     int *rvalp)
1278*1ae08745Sheppo {
1279*1ae08745Sheppo 	_NOTE(ARGUNUSED(credp, rvalp))
1280*1ae08745Sheppo 
1281*1ae08745Sheppo 	int rv = EINVAL;
1282*1ae08745Sheppo 	int instance;
1283*1ae08745Sheppo 	minor_t minor;
1284*1ae08745Sheppo 	uint64_t portno;
1285*1ae08745Sheppo 	vldc_t *vldcp;
1286*1ae08745Sheppo 	vldc_port_t *vport;
1287*1ae08745Sheppo 	vldc_minor_t *vminor;
1288*1ae08745Sheppo 
1289*1ae08745Sheppo 	minor = getminor(dev);
1290*1ae08745Sheppo 	instance = VLDCINST(minor);
1291*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1292*1ae08745Sheppo 	if (vldcp == NULL) {
1293*1ae08745Sheppo 		return (ENXIO);
1294*1ae08745Sheppo 	}
1295*1ae08745Sheppo 
1296*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
1297*1ae08745Sheppo 	mutex_enter(&vminor->lock);
1298*1ae08745Sheppo 	portno = vminor->portno;
1299*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
1300*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1301*1ae08745Sheppo 		return (ENOLINK);
1302*1ae08745Sheppo 	}
1303*1ae08745Sheppo 	vminor->in_use += 1;
1304*1ae08745Sheppo 	mutex_exit(&vminor->lock);
1305*1ae08745Sheppo 
1306*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
1307*1ae08745Sheppo 
1308*1ae08745Sheppo 	D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd);
1309*1ae08745Sheppo 
1310*1ae08745Sheppo 	switch (cmd) {
1311*1ae08745Sheppo 
1312*1ae08745Sheppo 	case VLDC_IOCTL_OPT_OP:
1313*1ae08745Sheppo 
1314*1ae08745Sheppo 		rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg,  mode);
1315*1ae08745Sheppo 		break;
1316*1ae08745Sheppo 
1317*1ae08745Sheppo 	case VLDC_IOCTL_READ_COOKIE:
1318*1ae08745Sheppo 
1319*1ae08745Sheppo 		rv = i_vldc_ioctl_read_cookie(vport, instance,
1320*1ae08745Sheppo 		    (void *)arg, mode);
1321*1ae08745Sheppo 		break;
1322*1ae08745Sheppo 
1323*1ae08745Sheppo 	case VLDC_IOCTL_WRITE_COOKIE:
1324*1ae08745Sheppo 
1325*1ae08745Sheppo 		rv = i_vldc_ioctl_write_cookie(vport, instance,
1326*1ae08745Sheppo 		    (void *)arg, mode);
1327*1ae08745Sheppo 		break;
1328*1ae08745Sheppo 
1329*1ae08745Sheppo 	default:
1330*1ae08745Sheppo 
1331*1ae08745Sheppo 		DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n",
1332*1ae08745Sheppo 		    instance, portno, cmd);
1333*1ae08745Sheppo 		rv = EINVAL;
1334*1ae08745Sheppo 		break;
1335*1ae08745Sheppo 	}
1336*1ae08745Sheppo 
1337*1ae08745Sheppo 	mutex_enter(&vminor->lock);
1338*1ae08745Sheppo 	vminor->in_use -= 1;
1339*1ae08745Sheppo 	if (vminor->in_use == 0) {
1340*1ae08745Sheppo 		cv_signal(&vminor->cv);
1341*1ae08745Sheppo 	}
1342*1ae08745Sheppo 	mutex_exit(&vminor->lock);
1343*1ae08745Sheppo 
1344*1ae08745Sheppo 	D1("vldc_ioctl: rv=%d\n", rv);
1345*1ae08745Sheppo 
1346*1ae08745Sheppo 	return (rv);
1347*1ae08745Sheppo }
1348*1ae08745Sheppo 
1349*1ae08745Sheppo /* cb_read */
1350*1ae08745Sheppo static int
1351*1ae08745Sheppo vldc_read(dev_t dev, struct uio *uiop, cred_t *credp)
1352*1ae08745Sheppo {
1353*1ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
1354*1ae08745Sheppo 
1355*1ae08745Sheppo 	int instance;
1356*1ae08745Sheppo 	minor_t minor;
1357*1ae08745Sheppo 	size_t size = 0;
1358*1ae08745Sheppo 	uint64_t portno;
1359*1ae08745Sheppo 	vldc_t *vldcp;
1360*1ae08745Sheppo 	vldc_port_t *vport;
1361*1ae08745Sheppo 	vldc_minor_t *vminor;
1362*1ae08745Sheppo 	int rv = 0;
1363*1ae08745Sheppo 
1364*1ae08745Sheppo 	minor = getminor(dev);
1365*1ae08745Sheppo 	instance = VLDCINST(minor);
1366*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1367*1ae08745Sheppo 	if (vldcp == NULL) {
1368*1ae08745Sheppo 		return (ENXIO);
1369*1ae08745Sheppo 	}
1370*1ae08745Sheppo 
1371*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
1372*1ae08745Sheppo 	mutex_enter(&vminor->lock);
1373*1ae08745Sheppo 	portno = vminor->portno;
1374*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
1375*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1376*1ae08745Sheppo 		return (ENOLINK);
1377*1ae08745Sheppo 	}
1378*1ae08745Sheppo 
1379*1ae08745Sheppo 	D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno);
1380*1ae08745Sheppo 
1381*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
1382*1ae08745Sheppo 
1383*1ae08745Sheppo 	/* check the port status */
1384*1ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
1385*1ae08745Sheppo 		DWARN("vldc_read: vldc@%d:%lu not in the ready state\n",
1386*1ae08745Sheppo 		    instance, portno);
1387*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1388*1ae08745Sheppo 		return (ENOTACTIVE);
1389*1ae08745Sheppo 	}
1390*1ae08745Sheppo 
1391*1ae08745Sheppo 	/* read data */
1392*1ae08745Sheppo 	size = MIN(vport->mtu, uiop->uio_resid);
1393*1ae08745Sheppo 	rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size);
1394*1ae08745Sheppo 
1395*1ae08745Sheppo 	D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n",
1396*1ae08745Sheppo 	    instance, portno, size, rv);
1397*1ae08745Sheppo 
1398*1ae08745Sheppo 	if (rv == 0) {
1399*1ae08745Sheppo 		if (size != 0) {
1400*1ae08745Sheppo 			rv = uiomove(vport->recv_buf, size, UIO_READ, uiop);
1401*1ae08745Sheppo 		} else {
1402*1ae08745Sheppo 			rv = EWOULDBLOCK;
1403*1ae08745Sheppo 		}
1404*1ae08745Sheppo 	} else {
1405*1ae08745Sheppo 		switch (rv) {
1406*1ae08745Sheppo 		case ENOBUFS:
1407*1ae08745Sheppo 			break;
1408*1ae08745Sheppo 		case ETIMEDOUT:
1409*1ae08745Sheppo 		case EWOULDBLOCK:
1410*1ae08745Sheppo 			rv = EWOULDBLOCK;
1411*1ae08745Sheppo 			break;
1412*1ae08745Sheppo 		default:
1413*1ae08745Sheppo 			rv = ECONNRESET;
1414*1ae08745Sheppo 			break;
1415*1ae08745Sheppo 		}
1416*1ae08745Sheppo 	}
1417*1ae08745Sheppo 
1418*1ae08745Sheppo 	mutex_exit(&vminor->lock);
1419*1ae08745Sheppo 
1420*1ae08745Sheppo 	return (rv);
1421*1ae08745Sheppo }
1422*1ae08745Sheppo 
1423*1ae08745Sheppo /* cb_write */
1424*1ae08745Sheppo static int
1425*1ae08745Sheppo vldc_write(dev_t dev, struct uio *uiop, cred_t *credp)
1426*1ae08745Sheppo {
1427*1ae08745Sheppo 	_NOTE(ARGUNUSED(credp))
1428*1ae08745Sheppo 
1429*1ae08745Sheppo 	int instance;
1430*1ae08745Sheppo 	minor_t minor;
1431*1ae08745Sheppo 	size_t size;
1432*1ae08745Sheppo 	size_t orig_size;
1433*1ae08745Sheppo 	uint64_t portno;
1434*1ae08745Sheppo 	vldc_t *vldcp;
1435*1ae08745Sheppo 	vldc_port_t *vport;
1436*1ae08745Sheppo 	vldc_minor_t *vminor;
1437*1ae08745Sheppo 	int rv = EINVAL;
1438*1ae08745Sheppo 
1439*1ae08745Sheppo 	minor = getminor(dev);
1440*1ae08745Sheppo 	instance = VLDCINST(minor);
1441*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1442*1ae08745Sheppo 	if (vldcp == NULL) {
1443*1ae08745Sheppo 		return (ENXIO);
1444*1ae08745Sheppo 	}
1445*1ae08745Sheppo 
1446*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
1447*1ae08745Sheppo 	mutex_enter(&vminor->lock);
1448*1ae08745Sheppo 	portno = vminor->portno;
1449*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
1450*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1451*1ae08745Sheppo 		return (ENOLINK);
1452*1ae08745Sheppo 	}
1453*1ae08745Sheppo 
1454*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
1455*1ae08745Sheppo 
1456*1ae08745Sheppo 	/* check the port status */
1457*1ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
1458*1ae08745Sheppo 		DWARN("vldc_write: vldc@%d:%lu not in the ready state\n",
1459*1ae08745Sheppo 		    instance, portno);
1460*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1461*1ae08745Sheppo 		return (ENOTACTIVE);
1462*1ae08745Sheppo 	}
1463*1ae08745Sheppo 
1464*1ae08745Sheppo 	orig_size = uiop->uio_resid;
1465*1ae08745Sheppo 	size = orig_size;
1466*1ae08745Sheppo 
1467*1ae08745Sheppo 	if (size > vport->mtu) {
1468*1ae08745Sheppo 		if (vport->is_stream) {
1469*1ae08745Sheppo 			/* can only send MTU size at a time */
1470*1ae08745Sheppo 			size = vport->mtu;
1471*1ae08745Sheppo 		} else {
1472*1ae08745Sheppo 			mutex_exit(&vminor->lock);
1473*1ae08745Sheppo 			return (EMSGSIZE);
1474*1ae08745Sheppo 		}
1475*1ae08745Sheppo 	}
1476*1ae08745Sheppo 
1477*1ae08745Sheppo 	D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno,
1478*1ae08745Sheppo 	    size);
1479*1ae08745Sheppo 
1480*1ae08745Sheppo 	rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop);
1481*1ae08745Sheppo 	if (rv == 0) {
1482*1ae08745Sheppo 		rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf,
1483*1ae08745Sheppo 			&size);
1484*1ae08745Sheppo 		if (rv != 0) {
1485*1ae08745Sheppo 			DWARN("vldc_write: vldc@%d:%lu failed writing %lu "
1486*1ae08745Sheppo 			    "bytes rv=%d\n", instance, portno, size, rv);
1487*1ae08745Sheppo 		}
1488*1ae08745Sheppo 	} else {
1489*1ae08745Sheppo 		size = 0;
1490*1ae08745Sheppo 	}
1491*1ae08745Sheppo 
1492*1ae08745Sheppo 	mutex_exit(&vminor->lock);
1493*1ae08745Sheppo 
1494*1ae08745Sheppo 	/* resid is total number of bytes *not* sent */
1495*1ae08745Sheppo 	uiop->uio_resid = orig_size - size;
1496*1ae08745Sheppo 
1497*1ae08745Sheppo 	return (rv);
1498*1ae08745Sheppo }
1499*1ae08745Sheppo 
1500*1ae08745Sheppo /* cb_chpoll */
1501*1ae08745Sheppo static int
1502*1ae08745Sheppo vldc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
1503*1ae08745Sheppo     struct pollhead **phpp)
1504*1ae08745Sheppo {
1505*1ae08745Sheppo 	int instance;
1506*1ae08745Sheppo 	minor_t minor;
1507*1ae08745Sheppo 	uint64_t portno;
1508*1ae08745Sheppo 	vldc_t *vldcp;
1509*1ae08745Sheppo 	vldc_port_t *vport;
1510*1ae08745Sheppo 	vldc_minor_t *vminor;
1511*1ae08745Sheppo 	ldc_status_t ldc_state;
1512*1ae08745Sheppo 	boolean_t isempty;
1513*1ae08745Sheppo 	int rv;
1514*1ae08745Sheppo 
1515*1ae08745Sheppo 	minor = getminor(dev);
1516*1ae08745Sheppo 	instance = VLDCINST(minor);
1517*1ae08745Sheppo 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1518*1ae08745Sheppo 	if (vldcp == NULL) {
1519*1ae08745Sheppo 		return (ENXIO);
1520*1ae08745Sheppo 	}
1521*1ae08745Sheppo 
1522*1ae08745Sheppo 	vminor = VLDCMINOR(vldcp, minor);
1523*1ae08745Sheppo 	mutex_enter(&vminor->lock);
1524*1ae08745Sheppo 	portno = vminor->portno;
1525*1ae08745Sheppo 	if (portno == VLDC_INVALID_PORTNO) {
1526*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1527*1ae08745Sheppo 		return (ENOLINK);
1528*1ae08745Sheppo 	}
1529*1ae08745Sheppo 
1530*1ae08745Sheppo 	vport = &(vldcp->port[portno]);
1531*1ae08745Sheppo 
1532*1ae08745Sheppo 	/* check the port status */
1533*1ae08745Sheppo 	if (vport->status != VLDC_PORT_READY) {
1534*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1535*1ae08745Sheppo 		return (ENOTACTIVE);
1536*1ae08745Sheppo 	}
1537*1ae08745Sheppo 
1538*1ae08745Sheppo 	D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n",
1539*1ae08745Sheppo 	    instance, portno, events);
1540*1ae08745Sheppo 
1541*1ae08745Sheppo 	rv = ldc_status(vport->ldc_handle, &ldc_state);
1542*1ae08745Sheppo 	if (rv != 0) {
1543*1ae08745Sheppo 		DWARN("vldc_chpoll: vldc@%d:%lu could not get ldc status, "
1544*1ae08745Sheppo 		    "rv=%d\n", instance, portno, rv);
1545*1ae08745Sheppo 		mutex_exit(&vminor->lock);
1546*1ae08745Sheppo 		return (EBADFD);
1547*1ae08745Sheppo 	}
1548*1ae08745Sheppo 
1549*1ae08745Sheppo 	*reventsp = 0;
1550*1ae08745Sheppo 
1551*1ae08745Sheppo 	if (ldc_state == LDC_UP) {
1552*1ae08745Sheppo 		/*
1553*1ae08745Sheppo 		 * Check if the receive queue is empty and if not, signal that
1554*1ae08745Sheppo 		 * there is data ready to read.
1555*1ae08745Sheppo 		 */
1556*1ae08745Sheppo 		if (events & POLLIN) {
1557*1ae08745Sheppo 			if ((ldc_chkq(vport->ldc_handle, &isempty) == 0) &&
1558*1ae08745Sheppo 			    (isempty == B_FALSE)) {
1559*1ae08745Sheppo 				*reventsp |= POLLIN;
1560*1ae08745Sheppo 			}
1561*1ae08745Sheppo 		}
1562*1ae08745Sheppo 
1563*1ae08745Sheppo 		if (events & POLLOUT)
1564*1ae08745Sheppo 			*reventsp |= POLLOUT;
1565*1ae08745Sheppo 
1566*1ae08745Sheppo 	} else if (vport->hanged_up) {
1567*1ae08745Sheppo 		*reventsp |= POLLHUP;
1568*1ae08745Sheppo 		vport->hanged_up = B_FALSE;
1569*1ae08745Sheppo 	}
1570*1ae08745Sheppo 
1571*1ae08745Sheppo 	mutex_exit(&vminor->lock);
1572*1ae08745Sheppo 
1573*1ae08745Sheppo 	if (((*reventsp) == 0) && (!anyyet)) {
1574*1ae08745Sheppo 		*phpp = &vport->poll;
1575*1ae08745Sheppo 	}
1576*1ae08745Sheppo 
1577*1ae08745Sheppo 	D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n",
1578*1ae08745Sheppo 	    instance, portno, events, *reventsp);
1579*1ae08745Sheppo 
1580*1ae08745Sheppo 	return (0);
1581*1ae08745Sheppo }
1582