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