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