xref: /illumos-gate/usr/src/uts/sun4v/io/vcc.c (revision 5a342f146a946ddf6f5f5afd4a5dd5baf11d7dd4)
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  * Copyright (c) 2014, Joyent, Inc. All rights reserved.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/file.h>
33 #include <sys/errno.h>
34 #include <sys/uio.h>
35 #include <sys/open.h>
36 #include <sys/cred.h>
37 #include <sys/kmem.h>
38 #include <sys/conf.h>
39 #include <sys/cmn_err.h>
40 #include <sys/ksynch.h>
41 #include <sys/modctl.h>
42 #include <sys/stat.h> /* needed for S_IFBLK and S_IFCHR */
43 #include <sys/debug.h>
44 #include <sys/promif.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/cyclic.h>
48 #include <sys/termio.h>
49 #include <sys/intr.h>
50 #include <sys/ivintr.h>
51 #include <sys/note.h>
52 #include <sys/stat.h>
53 #include <sys/fcntl.h>
54 #include <sys/sysmacros.h>
55 
56 #include <sys/ldc.h>
57 #include <sys/mdeg.h>
58 #include <sys/vcc_impl.h>
59 
60 #define	VCC_LDC_RETRIES		5
61 #define	VCC_LDC_DELAY		1000 /* usec */
62 
63 /*
64  * Function prototypes.
65  */
66 
67 /* DDI entrypoints */
68 static int	vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
69 static int	vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
70 static int	vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
71 static int	vcc_close(dev_t dev, int flag, int otyp, cred_t *cred);
72 static int	vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
73 			cred_t *credp, int *rvalp);
74 static int	vcc_read(dev_t dev, struct uio *uiop, cred_t *credp);
75 static int	vcc_write(dev_t dev, struct uio *uiop, cred_t *credp);
76 static int	vcc_chpoll(dev_t dev, short events, int anyyet,
77 			short *reventsp, struct pollhead **phpp);
78 static int	vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
79 			void *arg, void **resultp);
80 
81 /* callback functions */
82 static uint_t	vcc_ldc_cb(uint64_t event, caddr_t arg);
83 static int	vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
84 
85 /* Internal functions */
86 static int	i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport);
87 static int	i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
88 			uint_t portno, char *domain_name);
89 static int	i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id);
90 static int	i_vcc_reset_events(vcc_t *vccp);
91 static int	i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports,
92 			caddr_t buf, int mode);
93 static int	i_vcc_del_cons_ok(vcc_t *vccp, caddr_t buf, int mode);
94 static int	i_vcc_close_port(vcc_port_t *vport);
95 static int	i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf);
96 static int	i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz);
97 
98 static void *vcc_ssp;
99 
100 static struct cb_ops vcc_cb_ops = {
101 	vcc_open,	    /* open */
102 	vcc_close,	    /* close */
103 	nodev,		    /* strategy */
104 	nodev,		    /* print */
105 	nodev,		    /* dump */
106 	vcc_read,	    /* read */
107 	vcc_write,	    /* write */
108 	vcc_ioctl,	    /* ioctl */
109 	nodev,		    /* devmap */
110 	nodev,		    /* mmap */
111 	ddi_segmap,	    /* segmap */
112 	vcc_chpoll,	    /* chpoll */
113 	ddi_prop_op,	    /* prop_op */
114 	NULL,		    /* stream */
115 	D_NEW | D_MP	    /* flags */
116 };
117 
118 
119 static struct dev_ops vcc_ops = {
120 	DEVO_REV,		/* rev */
121 	0,			/* ref count */
122 	vcc_getinfo,		/* getinfo */
123 	nulldev,		/* identify */
124 	nulldev,		/* probe */
125 	vcc_attach,		/* attach */
126 	vcc_detach,		/* detach */
127 	nodev,			/* reset */
128 	&vcc_cb_ops,		/* cb_ops */
129 	(struct bus_ops *)NULL,	/* bus_ops */
130 	NULL,			/* power */
131 	ddi_quiesce_not_needed,		/* quiesce */
132 };
133 
134 extern struct mod_ops mod_driverops;
135 
136 #define	    VCC_CHANNEL_ENDPOINT	"channel-endpoint"
137 #define	    VCC_ID_PROP		"id"
138 
139 /*
140  * This is the string displayed by modinfo(1m).
141  */
142 static char vcc_ident[] = "sun4v Virtual Console Concentrator Driver";
143 
144 static struct modldrv md = {
145 	&mod_driverops, 	/* Type - it is a driver */
146 	vcc_ident,		/* Name of the module */
147 	&vcc_ops,		/* driver specfic opts */
148 };
149 
150 static struct modlinkage ml = {
151 	MODREV_1,
152 	&md,
153 	NULL
154 };
155 
156 /*
157  * Matching criteria passed to the MDEG to register interest
158  * in changes to 'virtual-device-port' nodes identified by their
159  * 'id' property.
160  */
161 static md_prop_match_t vcc_port_prop_match[] = {
162 	{ MDET_PROP_VAL,	    "id"   },
163 	{ MDET_LIST_END,	    NULL    }
164 };
165 
166 static mdeg_node_match_t vcc_port_match = {"virtual-device-port",
167 					vcc_port_prop_match};
168 
169 /*
170  * Specification of an MD node passed to the MDEG to filter any
171  * 'virtual-device-port' nodes that do not belong to the specified node.
172  * This template is copied for each vldc instance and filled in with
173  * the appropriate 'cfg-handle' value before being passed to the MDEG.
174  */
175 static mdeg_prop_spec_t vcc_prop_template[] = {
176 	{ MDET_PROP_STR,    "name",	"virtual-console-concentrator"	},
177 	{ MDET_PROP_VAL,    "cfg-handle",	NULL	},
178 	{ MDET_LIST_END,    NULL,		NULL	}
179 };
180 
181 #define	VCC_SET_MDEG_PROP_INST(specp, val) (specp)[1].ps_val = (val);
182 
183 
184 #ifdef DEBUG
185 
186 /*
187  * Print debug messages
188  *
189  * set vldcdbg to 0xf to enable all messages
190  *
191  * 0x8 - Errors
192  * 0x4 - Warnings
193  * 0x2 - All debug messages (most verbose)
194  * 0x1 - Minimal debug messages
195  */
196 
197 int vccdbg = 0x8;
198 
199 static void
200 vccdebug(const char *fmt, ...)
201 {
202 	char buf[512];
203 	va_list ap;
204 
205 	va_start(ap, fmt);
206 	(void) vsprintf(buf, fmt, ap);
207 	va_end(ap);
208 
209 	cmn_err(CE_CONT, "%s\n", buf);
210 }
211 
212 #define	D1		\
213 if (vccdbg & 0x01)	\
214 	vccdebug
215 
216 #define	D2		\
217 if (vccdbg & 0x02)	\
218 	vccdebug
219 
220 #define	DWARN		\
221 if (vccdbg & 0x04)	\
222 	vccdebug
223 
224 #else
225 
226 #define	D1
227 #define	D2
228 #define	DWARN
229 
230 #endif
231 
232 /* _init(9E): initialize the loadable module */
233 int
234 _init(void)
235 {
236 	int error;
237 
238 	/* init the soft state structure */
239 	error = ddi_soft_state_init(&vcc_ssp, sizeof (vcc_t), 1);
240 	if (error != 0) {
241 		return (error);
242 	}
243 
244 	/* Link the driver into the system */
245 	error = mod_install(&ml);
246 
247 	return (error);
248 
249 }
250 
251 /* _info(9E): return information about the loadable module */
252 int
253 _info(struct modinfo *modinfop)
254 {
255 	/* Report status of the dynamically loadable driver module */
256 	return (mod_info(&ml, modinfop));
257 }
258 
259 /* _fini(9E): prepare the module for unloading. */
260 int
261 _fini(void)
262 {
263 	int error;
264 
265 	/* Unlink the driver module from the system */
266 	if ((error = mod_remove(&ml)) == 0) {
267 		/*
268 		 * We have successfully "removed" the driver.
269 		 * destroy soft state
270 		 */
271 		ddi_soft_state_fini(&vcc_ssp);
272 	}
273 
274 	return (error);
275 }
276 
277 /* getinfo(9E) */
278 static int
279 vcc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,  void *arg, void **resultp)
280 {
281 	_NOTE(ARGUNUSED(dip))
282 
283 	int	instance = VCCINST(getminor((dev_t)arg));
284 	vcc_t	*vccp = NULL;
285 
286 	switch (cmd) {
287 
288 	case DDI_INFO_DEVT2DEVINFO:
289 		if ((vccp = ddi_get_soft_state(vcc_ssp, instance)) == NULL) {
290 			*resultp = NULL;
291 			return (DDI_FAILURE);
292 		}
293 		*resultp = vccp->dip;
294 		return (DDI_SUCCESS);
295 
296 	case DDI_INFO_DEVT2INSTANCE:
297 		*resultp = (void *)(uintptr_t)instance;
298 		return (DDI_SUCCESS);
299 
300 	default:
301 		*resultp = NULL;
302 		return (DDI_FAILURE);
303 	}
304 }
305 
306 /*
307  * There are two cases that need special blocking. One of them is to block
308  * a minor node without a port and another is to block application other
309  * than vntsd.
310  *
311  * A minor node can exist in the file system without associated with a port
312  * because when a port is deleted, ddi_remove_minor does not unlink it.
313  * Clients might try to open a minor node even after the corresponding port
314  * node has been removed.  To identify and block these calls,
315  * we need to validate the association between a port and its minor node.
316  *
317  * An application other than vntsd can access a console port as long
318  * as vntsd is not using the port. A port opened by an application other
319  * than vntsd will be closed when vntsd wants to use the port.
320  * However, other application could use same file descriptor
321  * access vcc cb_ops. So we need to identify and block caller other
322  * than vntsd, when vntsd is using the port.
323  */
324 static int
325 i_vcc_can_use_port(vcc_minor_t *minorp, vcc_port_t *vport)
326 {
327 	if (vport->minorp != minorp) {
328 		/* port config changed */
329 		return (ENXIO);
330 	}
331 
332 	if (vport->valid_pid == VCC_NO_PID_BLOCKING) {
333 		/* no blocking needed */
334 		return (0);
335 	}
336 
337 	if (vport->valid_pid != ddi_get_pid()) {
338 		return (EIO);
339 	}
340 
341 	return (0);
342 }
343 
344 
345 /* Syncronization between thread using cv_wait */
346 static int
347 i_vcc_wait_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
348 {
349 
350 	int	    rv;
351 
352 	ASSERT(mutex_owned(&vport->lock));
353 
354 	for (; ; ) {
355 
356 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
357 			/* port has been deleted */
358 			D1("i_vcc_wait_port_status: port%d deleted\n",
359 			    vport->number);
360 			return (EIO);
361 		}
362 
363 		if ((vport->status & VCC_PORT_OPEN) == 0) {
364 			D1("i_vcc_wait_port_status: port%d is closed \n",
365 			    vport->number);
366 			return (EIO);
367 		}
368 
369 		if (vport->status & VCC_PORT_LDC_LINK_DOWN) {
370 			return (EIO);
371 		}
372 
373 		if ((vport->valid_pid != VCC_NO_PID_BLOCKING) &&
374 		    (vport->valid_pid != ddi_get_pid())) {
375 			return (EIO);
376 		}
377 
378 		if ((vport->status & status) == status) {
379 			return (0);
380 		}
381 
382 		if (!ddi_can_receive_sig()) {
383 			return (EIO);
384 		}
385 
386 		rv = cv_wait_sig(cv, &vport->lock);
387 		if (rv == 0) {
388 			D1("i_vcc_wait_port_status: port%d get intr \n",
389 			    vport->number);
390 			/* got signal */
391 			return (EINTR);
392 		}
393 	}
394 
395 }
396 
397 /* Syncronization between threads, signal state change */
398 static void
399 i_vcc_set_port_status(vcc_port_t *vport, kcondvar_t *cv, uint32_t status)
400 {
401 
402 	mutex_enter(&vport->lock);
403 	vport->status |= status;
404 	cv_broadcast(cv);
405 	mutex_exit(&vport->lock);
406 }
407 
408 /* initialize a ldc channel */
409 static int
410 i_vcc_ldc_init(vcc_t *vccp, vcc_port_t *vport)
411 {
412 	ldc_attr_t 	attr;
413 	int		rv = EIO;
414 
415 	ASSERT(mutex_owned(&vport->lock));
416 	ASSERT(vport->ldc_id != VCC_INVALID_CHANNEL);
417 
418 	/* initialize the channel */
419 	attr.devclass = LDC_DEV_SERIAL;
420 	attr.instance = ddi_get_instance(vccp->dip);
421 	attr.mtu = VCC_MTU_SZ;
422 	attr.mode = LDC_MODE_RAW;
423 
424 	if ((rv = ldc_init(vport->ldc_id, &attr, &(vport->ldc_handle))) != 0) {
425 		cmn_err(CE_CONT, "i_vcc_ldc_init: port %d ldc channel %ld"
426 		    " failed ldc_init %d \n", vport->number, vport->ldc_id, rv);
427 		vport->ldc_id = VCC_INVALID_CHANNEL;
428 		return (rv);
429 	}
430 
431 	/* register it */
432 	if ((rv = ldc_reg_callback(vport->ldc_handle, vcc_ldc_cb,
433 	    (caddr_t)vport)) != 0) {
434 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_register_cb"
435 		    "failed\n", vport->number);
436 		(void) ldc_fini(vport->ldc_handle);
437 		vport->ldc_id = VCC_INVALID_CHANNEL;
438 		return (rv);
439 	}
440 
441 	/* open and bring channel up */
442 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
443 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d inv channel 0x%lx\n",
444 		    vport->number, vport->ldc_id);
445 		(void) ldc_unreg_callback(vport->ldc_handle);
446 		(void) ldc_fini(vport->ldc_handle);
447 		vport->ldc_id = VCC_INVALID_CHANNEL;
448 		return (rv);
449 	}
450 
451 	/* init the channel status */
452 	if ((rv = ldc_status(vport->ldc_handle, &vport->ldc_status)) != 0) {
453 		cmn_err(CE_CONT, "i_vcc_ldc_init: port@%d ldc_status failed\n",
454 		    vport->number);
455 		(void) ldc_close(vport->ldc_handle);
456 		(void) ldc_unreg_callback(vport->ldc_handle);
457 		(void) ldc_fini(vport->ldc_handle);
458 		vport->ldc_id = VCC_INVALID_CHANNEL;
459 		return (rv);
460 	}
461 
462 	return (0);
463 }
464 
465 /*  release a ldc channel */
466 static void
467 i_vcc_ldc_fini(vcc_port_t *vport)
468 {
469 	int 		rv = EIO;
470 	vcc_msg_t	buf;
471 	size_t		sz;
472 	int		retry = 0;
473 
474 	D1("i_vcc_ldc_fini: port@%lld, ldc_id%%llx\n", vport->number,
475 	    vport->ldc_id);
476 
477 	ASSERT(mutex_owned(&vport->lock));
478 
479 	/* wait for write available */
480 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
481 	    VCC_PORT_USE_WRITE_LDC);
482 
483 	if (rv == 0) {
484 		vport->status &= ~VCC_PORT_USE_WRITE_LDC;
485 
486 		/* send a HUP message */
487 		buf.type = LDC_CONSOLE_CTRL;
488 		buf.ctrl_msg = LDC_CONSOLE_HUP;
489 		buf.size = 0;
490 
491 		/*
492 		 * ignore write error since we still want to clean up
493 		 * ldc channel.
494 		 */
495 		(void) i_vcc_write_ldc(vport, &buf);
496 
497 		mutex_exit(&vport->lock);
498 		i_vcc_set_port_status(vport, &vport->write_cv,
499 		    VCC_PORT_USE_WRITE_LDC);
500 		mutex_enter(&vport->lock);
501 	}
502 
503 	/* flush ldc channel */
504 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
505 	    VCC_PORT_USE_READ_LDC);
506 
507 	if (rv == 0) {
508 		vport->status &= ~VCC_PORT_USE_READ_LDC;
509 		do {
510 			sz = sizeof (buf);
511 			rv = i_vcc_read_ldc(vport, (char *)&buf, &sz);
512 		} while (rv == 0 && sz > 0);
513 
514 		vport->status |= VCC_PORT_USE_READ_LDC;
515 
516 	}
517 
518 	/*
519 	 * ignore read error since we still want to clean up
520 	 * ldc channel.
521 	 */
522 
523 	(void) ldc_set_cb_mode(vport->ldc_handle, LDC_CB_DISABLE);
524 
525 	/* close LDC channel - retry on EAGAIN */
526 	while ((rv = ldc_close(vport->ldc_handle)) == EAGAIN) {
527 
528 		if (++retry > VCC_LDC_RETRIES) {
529 			cmn_err(CE_CONT, "i_vcc_ldc_fini: cannot close channel"
530 			    " %ld\n", vport->ldc_id);
531 			break;
532 		}
533 
534 		drv_usecwait(VCC_LDC_DELAY);
535 	}
536 
537 	if (rv == 0) {
538 		(void) ldc_unreg_callback(vport->ldc_handle);
539 		(void) ldc_fini(vport->ldc_handle);
540 	} else {
541 		/*
542 		 * Closing the LDC channel has failed. Ideally we should
543 		 * fail here but there is no Zeus level infrastructure
544 		 * to handle this. The MD has already been changed and
545 		 * we have to do the close. So we try to do as much
546 		 * clean up as we can.
547 		 */
548 		while (ldc_unreg_callback(vport->ldc_handle) == EAGAIN)
549 			drv_usecwait(VCC_LDC_DELAY);
550 	}
551 
552 }
553 
554 /* read data from ldc channel */
555 
556 static int
557 i_vcc_read_ldc(vcc_port_t *vport, char *data_buf, size_t *sz)
558 {
559 
560 	int		rv;
561 	size_t		size;
562 	size_t		space_left = *sz;
563 	vcc_msg_t  	buf;
564 	int 		i;
565 
566 
567 
568 
569 	/* make sure holding read lock */
570 	ASSERT((vport->status & VCC_PORT_USE_READ_LDC) == 0);
571 	ASSERT(space_left >= VCC_MTU_SZ);
572 
573 	*sz = 0;
574 	while (space_left >= VCC_MTU_SZ)  {
575 		size = sizeof (buf);
576 
577 		rv = ldc_read(vport->ldc_handle, (caddr_t)&buf, &size);
578 
579 		if (rv) {
580 			return (rv);
581 		}
582 
583 
584 		/*
585 		 * FIXME: ldc_read should not reaturn 0 with
586 		 * either size == 0, buf.size == 0 or size < VCC_HDR_SZ
587 		 */
588 		if (size == 0) {
589 			if (*sz > 0) {
590 				return (0);
591 			}
592 			return (EAGAIN);
593 		}
594 
595 		if (size < VCC_HDR_SZ) {
596 			return (EIO);
597 		}
598 
599 		/*
600 		 * only data is expected from console - otherwise
601 		 * return error
602 		 */
603 		if (buf.type != LDC_CONSOLE_DATA) {
604 			return (EIO);
605 		}
606 
607 		if (buf.size == 0) {
608 			if (*sz > 0) {
609 				return (0);
610 			}
611 			return (EAGAIN);
612 		}
613 
614 		/* copy  data */
615 		for (i = 0; i < buf.size; i++, (*sz)++) {
616 			data_buf[*sz] = buf.data[i];
617 		}
618 
619 		space_left -= buf.size;
620 	}
621 
622 	return (0);
623 }
624 
625 /* callback from ldc */
626 static uint_t
627 vcc_ldc_cb(uint64_t event, caddr_t arg)
628 {
629 
630 	vcc_port_t  *vport = (vcc_port_t *)arg;
631 	boolean_t   hasdata;
632 
633 	/*
634 	 * do not need to hold lock because if ldc calls back, the
635 	 * ldc_handle must be valid.
636 	 */
637 	D2("vcc_ldc_cb: callback invoked port=%d events=%llx\n",
638 	    vport->number, event);
639 
640 	/* check event from ldc */
641 	if (event & LDC_EVT_WRITE) {
642 		/* channel has space for write */
643 
644 		i_vcc_set_port_status(vport, &vport->write_cv,
645 		    VCC_PORT_LDC_WRITE_READY);
646 		return (LDC_SUCCESS);
647 	}
648 
649 	if (event & LDC_EVT_READ) {
650 
651 		/* channel has data for read */
652 		(void) ldc_chkq(vport->ldc_handle, &hasdata);
653 		if (!hasdata) {
654 			/* data already read */
655 			return (LDC_SUCCESS);
656 		}
657 
658 		i_vcc_set_port_status(vport, &vport->read_cv,
659 		    VCC_PORT_LDC_DATA_READY);
660 		return (LDC_SUCCESS);
661 	}
662 
663 	if (event & LDC_EVT_DOWN) {
664 		/* channel is down */
665 		i_vcc_set_port_status(vport, &vport->write_cv,
666 		    VCC_PORT_LDC_LINK_DOWN);
667 		cv_broadcast(&vport->read_cv);
668 
669 	}
670 
671 	return (LDC_SUCCESS);
672 
673 }
674 
675 
676 /* configure a vcc port with ldc channel */
677 static int
678 i_vcc_config_port(vcc_t *vccp, uint_t portno, uint64_t ldc_id)
679 {
680 	int 		rv = EIO;
681 	vcc_port_t 	*vport;
682 
683 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
684 		cmn_err(CE_CONT, "i_vcc_config_port: invalid port number %d\n",
685 		    portno);
686 		return (EINVAL);
687 	}
688 
689 	vport = &(vccp->port[portno]);
690 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
691 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d does not exist\n",
692 		    portno);
693 		return (EINVAL);
694 	}
695 
696 
697 	if (vport->ldc_id != VCC_INVALID_CHANNEL) {
698 		cmn_err(CE_CONT, "i_vcc_config_port: port@%d channel already"
699 		    "configured\n", portno);
700 		return (EINVAL);
701 	}
702 
703 	mutex_enter(&vport->lock);
704 
705 	/* store the ldc ID */
706 	vport->ldc_id = ldc_id;
707 	/* check if someone has already opened this port */
708 	if (vport->status & VCC_PORT_OPEN) {
709 
710 		if ((rv = i_vcc_ldc_init(vccp, vport)) != 0) {
711 			mutex_exit(&vport->lock);
712 			return (rv);
713 		}
714 
715 		/* mark port as ready */
716 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
717 		cv_broadcast(&vport->read_cv);
718 		cv_broadcast(&vport->write_cv);
719 	}
720 
721 	mutex_exit(&vport->lock);
722 
723 	D1("i_vcc_config_port: port@%d ldc=%d, domain=%s",
724 	    vport->number, vport->ldc_id, vport->minorp->domain_name);
725 
726 	return (0);
727 }
728 
729 /* add a vcc console port */
730 static int
731 i_vcc_add_port(vcc_t *vccp, char *group_name, uint64_t tcp_port,
732     uint_t portno, char *domain_name)
733 {
734 	int 		instance;
735 	int		rv = MDEG_FAILURE;
736 	minor_t 	minor;
737 	vcc_port_t 	*vport;
738 	uint_t		minor_idx;
739 	char		name[MAXPATHLEN];
740 
741 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
742 		DWARN("i_vcc_add_port: invalid port number %d\n", portno);
743 		return (MDEG_FAILURE);
744 	}
745 
746 	vport = &(vccp->port[portno]);
747 	if (vport->status & VCC_PORT_AVAIL) {
748 		/* this port already exists */
749 		cmn_err(CE_CONT, "i_vcc_add_port: invalid port - port@%d "
750 		    "exists\n", portno);
751 		return (MDEG_FAILURE);
752 	}
753 
754 	vport->number = portno;
755 	vport->ldc_id = VCC_INVALID_CHANNEL;
756 
757 	if (domain_name == NULL) {
758 		cmn_err(CE_CONT, "i_vcc_add_port: invalid domain name\n");
759 		return (MDEG_FAILURE);
760 	}
761 
762 	if (group_name == NULL) {
763 		cmn_err(CE_CONT, "i_vcc_add_port: invalid group name\n");
764 		return (MDEG_FAILURE);
765 	}
766 
767 	/* look up minor number */
768 	for (minor_idx = 0; minor_idx < vccp->minors_assigned; minor_idx++) {
769 		if (strcmp(vccp->minor_tbl[minor_idx].domain_name,
770 		    domain_name) == 0) {
771 			/* found previous assigned minor number */
772 			break;
773 		}
774 	}
775 
776 	if (minor_idx == vccp->minors_assigned) {
777 		/* end of lookup - assign new minor number */
778 		if (minor_idx == VCC_MAX_PORTS) {
779 			cmn_err(CE_CONT, "i_vcc_add_port:"
780 			    "too many minornodes (%d)\n",
781 			    minor_idx);
782 			return (MDEG_FAILURE);
783 		}
784 
785 		(void) strlcpy(vccp->minor_tbl[minor_idx].domain_name,
786 		    domain_name, MAXPATHLEN);
787 
788 		vccp->minors_assigned++;
789 	}
790 
791 	vport->minorp = &vccp->minor_tbl[minor_idx];
792 	vccp->minor_tbl[minor_idx].portno = portno;
793 
794 	(void) strlcpy(vport->group_name, group_name, MAXPATHLEN);
795 
796 	vport->tcp_port = tcp_port;
797 	D1("i_vcc_add_port:@%d domain=%s, group=%s, tcp=%lld",
798 	    vport->number, vport->minorp->domain_name,
799 	    vport->group_name, vport->tcp_port);
800 
801 
802 	/*
803 	 * Create a minor node. The minor number is
804 	 * (instance << VCC_INST_SHIFT) | minor_idx
805 	 */
806 	instance = ddi_get_instance(vccp->dip);
807 
808 	minor = (instance << VCC_INST_SHIFT) | (minor_idx);
809 
810 	(void) snprintf(name, MAXPATHLEN - 1, "%s%s", VCC_MINOR_NAME_PREFIX,
811 	    domain_name);
812 
813 	rv = ddi_create_minor_node(vccp->dip, name, S_IFCHR, minor,
814 	    DDI_NT_SERIAL, 0);
815 
816 	if (rv != DDI_SUCCESS) {
817 		vccp->minors_assigned--;
818 		return (MDEG_FAILURE);
819 	}
820 
821 	mutex_enter(&vport->lock);
822 	vport->status = VCC_PORT_AVAIL | VCC_PORT_ADDED;
823 	mutex_exit(&vport->lock);
824 
825 
826 	return (MDEG_SUCCESS);
827 }
828 
829 /* delete a port */
830 static int
831 i_vcc_delete_port(vcc_t *vccp, vcc_port_t *vport)
832 {
833 
834 	char	name[MAXPATHLEN];
835 	int	rv;
836 
837 
838 	ASSERT(mutex_owned(&vport->lock));
839 
840 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
841 		D1("vcc_del_port port already deleted \n");
842 		return (0);
843 	}
844 
845 	if (vport->status & VCC_PORT_OPEN) {
846 		/* do not block mdeg callback */
847 		vport->valid_pid = VCC_NO_PID_BLOCKING;
848 		rv = i_vcc_close_port(vport);
849 	}
850 
851 	/* remove minor node */
852 	(void) snprintf(name, MAXPATHLEN-1, "%s%s", VCC_MINOR_NAME_PREFIX,
853 	    vport->minorp->domain_name);
854 
855 	ddi_remove_minor_node(vccp->dip, name);
856 
857 	/* let read and write thread know */
858 	cv_broadcast(&vport->read_cv);
859 	cv_broadcast(&vport->write_cv);
860 	vport->status = 0;
861 	return (rv);
862 
863 
864 }
865 
866 /* register callback to MDEG */
867 static int
868 i_vcc_mdeg_register(vcc_t *vccp, int instance)
869 {
870 	mdeg_prop_spec_t	*pspecp;
871 	mdeg_node_spec_t	*ispecp;
872 	mdeg_handle_t		mdeg_hdl;
873 	int			sz;
874 	int			rv;
875 
876 	/*
877 	 * Allocate and initialize a per-instance copy
878 	 * of the global property spec array that will
879 	 * uniquely identify this vcc instance.
880 	 */
881 	sz = sizeof (vcc_prop_template);
882 	pspecp = kmem_alloc(sz, KM_SLEEP);
883 
884 	bcopy(vcc_prop_template, pspecp, sz);
885 
886 	VCC_SET_MDEG_PROP_INST(pspecp, instance);
887 
888 	/* initialize the complete prop spec structure */
889 	ispecp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
890 	ispecp->namep = "virtual-device";
891 	ispecp->specp = pspecp;
892 
893 	/* perform the registration */
894 	rv = mdeg_register(ispecp, &vcc_port_match, vcc_mdeg_cb,
895 	    vccp, &mdeg_hdl);
896 
897 	if (rv != MDEG_SUCCESS) {
898 		cmn_err(CE_CONT, "i_vcc_mdeg_register:"
899 		    "mdeg_register failed (%d)\n", rv);
900 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
901 		kmem_free(pspecp, sz);
902 		return (DDI_FAILURE);
903 	}
904 
905 	/* save off data that will be needed later */
906 	vccp->md_ispecp = (void *)ispecp;
907 	vccp->mdeg_hdl = mdeg_hdl;
908 
909 	return (0);
910 }
911 
912 /* destroy all mutex from port table */
913 static void
914 i_vcc_cleanup_port_table(vcc_t *vccp)
915 {
916 	int i;
917 	vcc_port_t *vport;
918 
919 	for (i = 0; i < VCC_MAX_PORTS; i++) {
920 		vport = &(vccp->port[i]);
921 		mutex_destroy(&vport->lock);
922 		cv_destroy(&vport->read_cv);
923 		cv_destroy(&vport->write_cv);
924 	}
925 }
926 
927 /*
928  * attach(9E): attach a device to the system.
929  * called once for each instance of the device on the system.
930  */
931 static int
932 vcc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
933 {
934 	int 		i, instance, inst;
935 	int 		rv = DDI_FAILURE;
936 	vcc_t		*vccp;
937 	minor_t 	minor;
938 	vcc_port_t	*vport;
939 
940 	switch (cmd) {
941 
942 	case DDI_ATTACH:
943 
944 		instance = ddi_get_instance(dip);
945 		if (ddi_soft_state_zalloc(vcc_ssp, instance) != DDI_SUCCESS)
946 			return (DDI_FAILURE);
947 
948 		vccp = ddi_get_soft_state(vcc_ssp, instance);
949 		if (vccp == NULL) {
950 			ddi_soft_state_free(vccp, instance);
951 			return (ENXIO);
952 		}
953 
954 		D1("vcc_attach: DDI_ATTACH instance=%d\n", instance);
955 
956 		/* initialize the mutex */
957 		mutex_init(&vccp->lock, NULL, MUTEX_DRIVER, NULL);
958 
959 		mutex_enter(&vccp->lock);
960 
961 		vccp->dip = dip;
962 
963 		for (i = 0; i < VCC_MAX_PORTS; i++) {
964 			vport = &(vccp->port[i]);
965 			mutex_init(&vport->lock, NULL, MUTEX_DRIVER, NULL);
966 			cv_init(&vport->read_cv, NULL, CV_DRIVER, NULL);
967 			cv_init(&vport->write_cv, NULL, CV_DRIVER, NULL);
968 			vport->valid_pid = VCC_NO_PID_BLOCKING;
969 		}
970 
971 		vport = &vccp->port[VCC_CONTROL_PORT];
972 		mutex_enter(&vport->lock);
973 
974 		vport->minorp = &vccp->minor_tbl[VCC_CONTROL_MINOR_IDX];
975 		vport->status |= VCC_PORT_AVAIL;
976 
977 		/* create a minor node for vcc control */
978 		minor = (instance << VCC_INST_SHIFT) | VCC_CONTROL_MINOR_IDX;
979 
980 		vccp->minor_tbl[VCC_CONTROL_PORT].portno =
981 		    VCC_CONTROL_MINOR_IDX;
982 
983 
984 		rv = ddi_create_minor_node(vccp->dip, "ctl", S_IFCHR, minor,
985 		    DDI_NT_SERIAL, 0);
986 
987 		mutex_exit(&vport->lock);
988 
989 		if (rv != DDI_SUCCESS) {
990 			cmn_err(CE_CONT, "vcc_attach: error"
991 			    "creating control minor node\n");
992 
993 			i_vcc_cleanup_port_table(vccp);
994 
995 			mutex_exit(&vccp->lock);
996 			/* clean up soft state */
997 			ddi_soft_state_free(vccp, instance);
998 
999 			return (DDI_FAILURE);
1000 		}
1001 
1002 		/* get the instance number by reading 'reg' property */
1003 		inst = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
1004 		    "reg", -1);
1005 		if (inst == -1) {
1006 			cmn_err(CE_CONT, "vcc_attach: vcc%d has no "
1007 			    "'reg' property\n",
1008 			    ddi_get_instance(dip));
1009 
1010 			i_vcc_cleanup_port_table(vccp);
1011 
1012 			/* remove minor */
1013 			ddi_remove_minor_node(vccp->dip, NULL);
1014 
1015 			/* clean up soft state */
1016 			mutex_exit(&vccp->lock);
1017 			ddi_soft_state_free(vccp, instance);
1018 
1019 			return (DDI_FAILURE);
1020 		}
1021 
1022 		/*
1023 		 * Mdeg might invoke callback in the same call sequence
1024 		 * if there is a domain port at the time of registration.
1025 		 * Since the callback also grabs vcc->lock mutex, to avoid
1026 		 * mutex reentry error, release the lock before registration
1027 		 */
1028 		mutex_exit(&vccp->lock);
1029 
1030 		/* register for notifications from Zeus */
1031 		rv = i_vcc_mdeg_register(vccp, inst);
1032 		if (rv != MDEG_SUCCESS) {
1033 			cmn_err(CE_CONT, "vcc_attach: error register to MD\n");
1034 
1035 			i_vcc_cleanup_port_table(vccp);
1036 
1037 			/* remove minor */
1038 			ddi_remove_minor_node(vccp->dip, NULL);
1039 
1040 			/* clean up soft state */
1041 			ddi_soft_state_free(vccp, instance);
1042 
1043 			return (DDI_FAILURE);
1044 		}
1045 
1046 		return (DDI_SUCCESS);
1047 
1048 	case DDI_RESUME:
1049 
1050 		return (DDI_SUCCESS);
1051 
1052 	default:
1053 
1054 		return (DDI_FAILURE);
1055 	}
1056 }
1057 
1058 /*
1059  * detach(9E): detach a device from the system.
1060  */
1061 static int
1062 vcc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1063 {
1064 	int		    i, instance;
1065 	vcc_t		    *vccp;
1066 	mdeg_node_spec_t    *ispecp;
1067 	vcc_port_t	    *vport;
1068 
1069 	switch (cmd) {
1070 
1071 	case DDI_DETACH:
1072 
1073 		instance = ddi_get_instance(dip);
1074 		vccp = ddi_get_soft_state(vcc_ssp, instance);
1075 		if (vccp == NULL)
1076 			return (ENXIO);
1077 
1078 		D1("vcc_detach: DDI_DETACH instance=%d\n", instance);
1079 
1080 		mutex_enter(&vccp->lock);
1081 
1082 		/* unregister from MD event generator */
1083 
1084 		ASSERT(vccp->mdeg_hdl);
1085 		(void) mdeg_unregister(vccp->mdeg_hdl);
1086 
1087 		ispecp = (mdeg_node_spec_t *)vccp->md_ispecp;
1088 		ASSERT(ispecp);
1089 
1090 		kmem_free(ispecp->specp, sizeof (vcc_prop_template));
1091 		kmem_free(ispecp, sizeof (mdeg_node_spec_t));
1092 
1093 		/* remove minor nodes */
1094 		ddi_remove_minor_node(vccp->dip, NULL);
1095 		mutex_exit(&vccp->lock);
1096 
1097 		for (i = 0; i < VCC_MAX_PORTS; i++) {
1098 
1099 			vport = &vccp->port[i];
1100 			mutex_enter(&vport->lock);
1101 			if (i == VCC_CONTROL_PORT) {
1102 				if (vport->status & VCC_PORT_OPEN) {
1103 					(void) i_vcc_close_port(vport);
1104 				}
1105 			}
1106 
1107 			if ((vccp->port[i].status & VCC_PORT_AVAIL) &&
1108 			    (i != VCC_CONTROL_PORT)) {
1109 				D1("vcc_detach: removing port port@%d\n", i);
1110 				(void) i_vcc_delete_port(vccp, vport);
1111 			}
1112 			mutex_exit(&vport->lock);
1113 			cv_destroy(&vport->read_cv);
1114 			cv_destroy(&vport->write_cv);
1115 			mutex_destroy(&vport->lock);
1116 		}
1117 
1118 
1119 
1120 		/* destroy mutex and free the soft state */
1121 		mutex_destroy(&vccp->lock);
1122 		ddi_soft_state_free(vcc_ssp, instance);
1123 
1124 		return (DDI_SUCCESS);
1125 
1126 	case DDI_SUSPEND:
1127 
1128 		return (DDI_SUCCESS);
1129 
1130 	default:
1131 
1132 		return (DDI_FAILURE);
1133 	}
1134 }
1135 
1136 /* cb_open */
1137 static int
1138 vcc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
1139 {
1140 	_NOTE(ARGUNUSED(otyp, cred))
1141 
1142 	int	    instance;
1143 	int	    rv = EIO;
1144 	minor_t	    minor;
1145 	uint_t	    portno;
1146 	vcc_t	    *vccp;
1147 	vcc_port_t  *vport;
1148 
1149 	minor = getminor(*devp);
1150 	instance = VCCINST(minor);
1151 
1152 	vccp = ddi_get_soft_state(vcc_ssp, instance);
1153 	if (vccp == NULL) {
1154 		return (ENXIO);
1155 	}
1156 
1157 	portno = VCCPORT(vccp, minor);
1158 
1159 	vport = &(vccp->port[portno]);
1160 
1161 	mutex_enter(&vport->lock);
1162 
1163 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1164 		/* port may be removed */
1165 		mutex_exit(&vport->lock);
1166 		return (ENXIO);
1167 	}
1168 
1169 	if (vport->status & VCC_PORT_OPEN) {
1170 		/* only one open per port */
1171 		cmn_err(CE_CONT, "vcc_open: virtual-console-concentrator@%d:%d "
1172 		    "is already open\n", instance, portno);
1173 		mutex_exit(&vport->lock);
1174 		return (EAGAIN);
1175 	}
1176 
1177 	/* check minor no and pid */
1178 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1179 	    vport)) != 0) {
1180 		mutex_exit(&vport->lock);
1181 		return (rv);
1182 	}
1183 
1184 	if (portno == VCC_CONTROL_PORT) {
1185 		vport->status |= VCC_PORT_OPEN;
1186 		mutex_exit(&vport->lock);
1187 		return (0);
1188 	}
1189 
1190 	/*
1191 	 * the port may just be added by mdeg callback and may
1192 	 * not be configured yet.
1193 	 */
1194 	if (vport->ldc_id == VCC_INVALID_CHANNEL) {
1195 		mutex_exit(&vport->lock);
1196 		return (ENXIO);
1197 	}
1198 
1199 
1200 	/* check if channel has been initialized */
1201 	if ((vport->status & VCC_PORT_LDC_CHANNEL_READY) == 0) {
1202 		rv = i_vcc_ldc_init(vccp, vport);
1203 		if (rv) {
1204 			mutex_exit(&vport->lock);
1205 			return (EIO);
1206 		}
1207 
1208 		/* mark port as ready */
1209 		vport->status |= VCC_PORT_LDC_CHANNEL_READY;
1210 	}
1211 
1212 	vport->status |= VCC_PORT_USE_READ_LDC | VCC_PORT_USE_WRITE_LDC|
1213 	    VCC_PORT_TERM_RD|VCC_PORT_TERM_WR|VCC_PORT_OPEN;
1214 
1215 	if ((flag & O_NONBLOCK) || (flag & O_NDELAY)) {
1216 		vport->status |= VCC_PORT_NONBLOCK;
1217 	}
1218 
1219 	mutex_exit(&vport->lock);
1220 
1221 	return (0);
1222 }
1223 
1224 /* close port */
1225 static int
1226 i_vcc_close_port(vcc_port_t *vport)
1227 {
1228 
1229 	if ((vport->status & VCC_PORT_OPEN) == 0) {
1230 		return (0);
1231 	}
1232 
1233 	ASSERT(mutex_owned(&vport->lock));
1234 
1235 	if (vport->status & VCC_PORT_LDC_CHANNEL_READY) {
1236 		/* clean up ldc channel */
1237 		i_vcc_ldc_fini(vport);
1238 		vport->status &= ~VCC_PORT_LDC_CHANNEL_READY;
1239 	}
1240 
1241 	/* reset  rd/wr suspends  */
1242 	vport->status |= VCC_PORT_TERM_RD | VCC_PORT_TERM_WR;
1243 	vport->status &= ~VCC_PORT_NONBLOCK;
1244 	vport->status &= ~VCC_PORT_OPEN;
1245 	vport->valid_pid = VCC_NO_PID_BLOCKING;
1246 
1247 	/* signal any blocked read and write thread */
1248 	cv_broadcast(&vport->read_cv);
1249 	cv_broadcast(&vport->write_cv);
1250 
1251 	return (0);
1252 }
1253 
1254 /* cb_close */
1255 static int
1256 vcc_close(dev_t dev, int flag, int otyp, cred_t *cred)
1257 {
1258 	_NOTE(ARGUNUSED(flag, otyp, cred))
1259 
1260 	int	    instance;
1261 	minor_t	    minor;
1262 	int	    rv = EIO;
1263 	uint_t	    portno;
1264 	vcc_t	    *vccp;
1265 	vcc_port_t  *vport;
1266 
1267 	minor = getminor(dev);
1268 
1269 	instance = VCCINST(minor);
1270 	vccp = ddi_get_soft_state(vcc_ssp, instance);
1271 	if (vccp == NULL) {
1272 		return (ENXIO);
1273 	}
1274 
1275 	portno = VCCPORT(vccp, minor);
1276 
1277 	D1("vcc_close: closing virtual-console-concentrator@%d:%d\n",
1278 	    instance, portno);
1279 	vport = &(vccp->port[portno]);
1280 
1281 
1282 	/*
1283 	 * needs lock to provent i_vcc_delete_port, which is called by
1284 	 * the mdeg callback, from closing port.
1285 	 */
1286 	mutex_enter(&vport->lock);
1287 
1288 	if ((vport->status & VCC_PORT_OPEN) == 0) {
1289 		mutex_exit(&vport->lock);
1290 		return (0);
1291 	}
1292 
1293 	if (portno == VCC_CONTROL_PORT) {
1294 		/*
1295 		 * vntsd closes control port before it exits. There
1296 		 * could be events still pending for vntsd.
1297 		 */
1298 		mutex_exit(&vport->lock);
1299 		rv = i_vcc_reset_events(vccp);
1300 		return (0);
1301 	}
1302 
1303 
1304 	/* check minor no and pid */
1305 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1306 	    vport)) != 0) {
1307 		mutex_exit(&vport->lock);
1308 		return (rv);
1309 	}
1310 
1311 	rv = i_vcc_close_port(vport);
1312 	mutex_exit(&vport->lock);
1313 
1314 	return (rv);
1315 }
1316 
1317 /*
1318  * ioctl VCC_CONS_TBL - vntsd allocates buffer according to return of
1319  * VCC_NUM_PORTS. However, when vntsd requests for the console table, console
1320  * ports could be deleted or added. parameter num_ports is number of structures
1321  * that vntsd allocated for the table. If there are more ports than
1322  * num_ports, set up to wakeup vntsd to add ports.
1323  * If there less ports than num_ports, fill (-1) for cons_no to tell vntsd.
1324  */
1325 static int
1326 i_vcc_cons_tbl(vcc_t *vccp, uint_t num_ports, caddr_t buf, int mode)
1327 {
1328 	vcc_console_t	cons;
1329 	int		i;
1330 	vcc_port_t	*vport;
1331 	boolean_t	notify_vntsd = B_FALSE;
1332 	char pathname[MAXPATHLEN];
1333 
1334 
1335 	(void) ddi_pathname(vccp->dip, pathname);
1336 	for (i = 0; i < VCC_MAX_PORTS; i++) {
1337 
1338 		vport = &vccp->port[i];
1339 
1340 		if (i == VCC_CONTROL_PORT) {
1341 			continue;
1342 		}
1343 
1344 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
1345 			continue;
1346 		}
1347 
1348 		/* a port exists before vntsd becomes online */
1349 		mutex_enter(&vport->lock);
1350 
1351 		if (num_ports == 0) {
1352 			/* more ports than vntsd's buffer can hold */
1353 			vport->status |= VCC_PORT_ADDED;
1354 			notify_vntsd = B_TRUE;
1355 			mutex_exit(&vport->lock);
1356 			continue;
1357 		}
1358 
1359 		bzero(&cons, sizeof (vcc_console_t));
1360 
1361 		/* construct console buffer */
1362 		cons.cons_no = vport->number;
1363 		cons.tcp_port = vport->tcp_port;
1364 		(void) memcpy(cons.domain_name,
1365 		    vport->minorp->domain_name, MAXPATHLEN);
1366 
1367 		(void) memcpy(cons.group_name, vport->group_name,
1368 		    MAXPATHLEN);
1369 		vport->status &= ~VCC_PORT_ADDED;
1370 		mutex_exit(&vport->lock);
1371 
1372 		(void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
1373 		    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
1374 
1375 		/* copy out data */
1376 		if (ddi_copyout(&cons, (void *)buf,
1377 		    sizeof (vcc_console_t), mode)) {
1378 			mutex_exit(&vport->lock);
1379 			return (EFAULT);
1380 		}
1381 		buf += sizeof (vcc_console_t);
1382 
1383 		num_ports--;
1384 
1385 	}
1386 
1387 	if (num_ports == 0) {
1388 		/* vntsd's buffer is full */
1389 
1390 		if (notify_vntsd) {
1391 			/* more ports need to notify vntsd */
1392 			vport = &vccp->port[VCC_CONTROL_PORT];
1393 			mutex_enter(&vport->lock);
1394 			vport->pollevent |= VCC_POLL_ADD_PORT;
1395 			mutex_exit(&vport->lock);
1396 		}
1397 
1398 		return (0);
1399 	}
1400 
1401 	/* less ports than vntsd expected */
1402 	bzero(&cons, sizeof (vcc_console_t));
1403 	cons.cons_no = -1;
1404 
1405 	while (num_ports > 0) {
1406 		/* fill vntsd buffer with no console */
1407 		if (ddi_copyout(&cons, (void *)buf,
1408 		    sizeof (vcc_console_t), mode) != 0) {
1409 			mutex_exit(&vport->lock);
1410 			return (EFAULT);
1411 		}
1412 		D1("i_vcc_cons_tbl: a port is  deleted\n");
1413 		buf += sizeof (vcc_console_t) +MAXPATHLEN;
1414 		num_ports--;
1415 	}
1416 
1417 	return (0);
1418 }
1419 
1420 
1421 /* turn off event flag if there is no more change */
1422 static void
1423 i_vcc_turn_off_event(vcc_t *vccp, uint32_t port_status, uint32_t event)
1424 {
1425 
1426 	vcc_port_t *vport;
1427 	int i;
1428 
1429 	for (i = 0; i < VCC_MAX_PORTS; i++) {
1430 
1431 		vport = &(vccp->port[i]);
1432 
1433 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
1434 			continue;
1435 		}
1436 
1437 
1438 		if (vport->status & port_status) {
1439 			/* more port changes status */
1440 			return;
1441 		}
1442 
1443 	}
1444 
1445 	/* no more changed port  */
1446 	vport = &vccp->port[VCC_CONTROL_PORT];
1447 
1448 	/* turn off event */
1449 	mutex_enter(&vport->lock);
1450 	vport->pollevent &= ~event;
1451 	mutex_exit(&vport->lock);
1452 }
1453 
1454 /* ioctl VCC_CONS_INFO */
1455 static int
1456 i_vcc_cons_info(vcc_t *vccp, caddr_t buf, int mode)
1457 {
1458 	vcc_console_t	cons;
1459 	uint_t		portno;
1460 	vcc_port_t	*vport;
1461 	char pathname[MAXPATHLEN];
1462 
1463 	/* read in portno */
1464 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
1465 		return (EFAULT);
1466 	}
1467 
1468 	D1("i_vcc_cons_info@%d:\n", portno);
1469 
1470 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
1471 		return (EINVAL);
1472 	}
1473 
1474 	vport = &vccp->port[portno];
1475 
1476 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1477 		return (EINVAL);
1478 	}
1479 
1480 	mutex_enter(&vport->lock);
1481 	vport->status &= ~VCC_PORT_ADDED;
1482 
1483 	/* construct configruation data  */
1484 	bzero(&cons, sizeof (vcc_console_t));
1485 
1486 	cons.cons_no = vport->number;
1487 	cons.tcp_port = vport->tcp_port;
1488 
1489 	(void) memcpy(cons.domain_name, vport->minorp->domain_name, MAXPATHLEN);
1490 
1491 	(void) memcpy(cons.group_name, vport->group_name, MAXPATHLEN);
1492 
1493 	mutex_exit(&vport->lock);
1494 
1495 	(void) ddi_pathname(vccp->dip, pathname),
1496 
1497 	    /* copy device name */
1498 	    (void) snprintf(cons.dev_name, MAXPATHLEN-1, "%s:%s%s",
1499 	    pathname, VCC_MINOR_NAME_PREFIX, cons.domain_name);
1500 	/* copy data */
1501 	if (ddi_copyout(&cons, (void *)buf,
1502 	    sizeof (vcc_console_t), mode) != 0) {
1503 		mutex_exit(&vport->lock);
1504 		return (EFAULT);
1505 	}
1506 
1507 	D1("i_vcc_cons_info@%d:domain:%s serv:%s tcp@%lld %s\n",
1508 	    cons.cons_no, cons.domain_name,
1509 	    cons.group_name, cons.tcp_port, cons.dev_name);
1510 
1511 	i_vcc_turn_off_event(vccp, VCC_PORT_ADDED, VCC_POLL_ADD_PORT);
1512 
1513 	return (0);
1514 }
1515 
1516 
1517 /* response to vntsd inquiry ioctl call */
1518 static int
1519 i_vcc_inquiry(vcc_t *vccp, caddr_t buf, int mode)
1520 {
1521 	vcc_port_t	*vport;
1522 	uint_t		i;
1523 	vcc_response_t	msg;
1524 
1525 	vport = &(vccp->port[VCC_CONTROL_PORT]);
1526 
1527 	if ((vport->pollevent & VCC_POLL_ADD_PORT) == 0) {
1528 		return (EINVAL);
1529 	}
1530 
1531 	/* an added port */
1532 
1533 	D1("i_vcc_inquiry\n");
1534 
1535 	for (i = 0; i < VCC_MAX_PORTS; i++) {
1536 		if ((vccp->port[i].status & VCC_PORT_AVAIL) == 0) {
1537 			continue;
1538 		}
1539 
1540 		if (vccp->port[i].status & VCC_PORT_ADDED) {
1541 			/* port added */
1542 			msg.reason = VCC_CONS_ADDED;
1543 			msg.cons_no = i;
1544 
1545 			if (ddi_copyout((void *)&msg, (void *)buf,
1546 			    sizeof (msg), mode) == -1) {
1547 				cmn_err(CE_CONT, "i_vcc_find_changed_port:"
1548 				    "ddi_copyout"
1549 				    " failed\n");
1550 				return (EFAULT);
1551 			}
1552 			return (0);
1553 		}
1554 	}
1555 
1556 	/* the added port was deleted before vntsd wakes up */
1557 	msg.reason = VCC_CONS_MISS_ADDED;
1558 
1559 	if (ddi_copyout((void *)&msg, (void *)buf,
1560 	    sizeof (msg), mode) == -1) {
1561 		cmn_err(CE_CONT, "i_vcc_find_changed_port: ddi_copyout"
1562 		    " failed\n");
1563 		return (EFAULT);
1564 	}
1565 
1566 	return (0);
1567 }
1568 
1569 /* clean up events after vntsd exits */
1570 static int
1571 i_vcc_reset_events(vcc_t *vccp)
1572 {
1573 	uint_t	    i;
1574 	vcc_port_t  *vport;
1575 
1576 	for (i = 0; i < VCC_MAX_PORTS; i++) {
1577 		vport = &(vccp->port[i]);
1578 
1579 		if ((vport->status & VCC_PORT_AVAIL) == 0) {
1580 			continue;
1581 		}
1582 
1583 		ASSERT(!mutex_owned(&vport->lock));
1584 
1585 		if (i == VCC_CONTROL_PORT) {
1586 			/* close control port */
1587 			mutex_enter(&vport->lock);
1588 			vport->status &= ~VCC_PORT_OPEN;
1589 
1590 			/* clean up poll events */
1591 			vport->pollevent = 0;
1592 			vport->pollflag = 0;
1593 			mutex_exit(&vport->lock);
1594 			continue;
1595 		}
1596 		if (vport->status & VCC_PORT_ADDED) {
1597 			/* pending added port event to vntsd */
1598 			mutex_enter(&vport->lock);
1599 			vport->status &= ~VCC_PORT_ADDED;
1600 			mutex_exit(&vport->lock);
1601 		}
1602 
1603 	}
1604 
1605 	vport = &vccp->port[VCC_CONTROL_PORT];
1606 
1607 	return (0);
1608 }
1609 
1610 /* ioctl VCC_FORCE_CLOSE */
1611 static int
1612 i_vcc_force_close(vcc_t *vccp, caddr_t buf, int mode)
1613 {
1614 	uint_t		portno;
1615 	vcc_port_t	*vport;
1616 	int		rv;
1617 
1618 	/* read in portno */
1619 	if (ddi_copyin((void*)buf, &portno, sizeof (uint_t), mode)) {
1620 		return (EFAULT);
1621 	}
1622 
1623 	D1("i_vcc_force_close@%d:\n", portno);
1624 
1625 	if ((portno >= VCC_MAX_PORTS) || (portno == VCC_CONTROL_PORT)) {
1626 		return (EINVAL);
1627 	}
1628 
1629 	vport = &vccp->port[portno];
1630 
1631 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1632 		return (EINVAL);
1633 	}
1634 
1635 	mutex_enter(&vport->lock);
1636 
1637 	rv = i_vcc_close_port(vport);
1638 
1639 	/* block callers other than vntsd */
1640 	vport->valid_pid = ddi_get_pid();
1641 
1642 	mutex_exit(&vport->lock);
1643 	return (rv);
1644 
1645 }
1646 
1647 /* ioctl VCC_CONS_STATUS */
1648 static int
1649 i_vcc_cons_status(vcc_t *vccp, caddr_t buf, int mode)
1650 {
1651 	vcc_console_t	console;
1652 	vcc_port_t	*vport;
1653 
1654 	/* read in portno */
1655 	if (ddi_copyin((void*)buf, &console, sizeof (console), mode)) {
1656 		return (EFAULT);
1657 	}
1658 
1659 	D1("i_vcc_cons_status@%d:\n", console.cons_no);
1660 
1661 	if ((console.cons_no >= VCC_MAX_PORTS) ||
1662 	    (console.cons_no == VCC_CONTROL_PORT)) {
1663 		return (EINVAL);
1664 	}
1665 
1666 
1667 	vport = &vccp->port[console.cons_no];
1668 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1669 		console.cons_no = -1;
1670 	} else  if (strncmp(console.domain_name, vport->minorp->domain_name,
1671 	    MAXPATHLEN)) {
1672 		console.cons_no = -1;
1673 	} else if (strncmp(console.group_name, vport->group_name,
1674 	    MAXPATHLEN)) {
1675 		console.cons_no = -1;
1676 	} else if (console.tcp_port != vport->tcp_port) {
1677 		console.cons_no = -1;
1678 	} else if (vport->ldc_id == VCC_INVALID_CHANNEL) {
1679 		console.cons_no = -1;
1680 	}
1681 
1682 	D1("i_vcc_cons_status@%d: %s %s %llx\n", console.cons_no,
1683 	    console.group_name, console.domain_name, console.tcp_port);
1684 	if (ddi_copyout(&console, (void *)buf, sizeof (console), mode) == -1) {
1685 		cmn_err(CE_CONT, "i_vcc_cons_status ddi_copyout failed\n");
1686 		return (EFAULT);
1687 	}
1688 
1689 	return (0);
1690 }
1691 
1692 /* cb_ioctl handler for vcc control port */
1693 static int
1694 i_vcc_ctrl_ioctl(vcc_t *vccp, int cmd, void* arg, int mode)
1695 {
1696 
1697 	static uint_t	num_ports;
1698 
1699 
1700 	switch (cmd) {
1701 
1702 	case VCC_NUM_CONSOLE:
1703 
1704 		mutex_enter(&vccp->lock);
1705 		num_ports = vccp->num_ports;
1706 		mutex_exit(&vccp->lock);
1707 		/* number of consoles */
1708 
1709 		return (ddi_copyout((void *)&num_ports, arg,
1710 		    sizeof (int), mode));
1711 	case VCC_CONS_TBL:
1712 
1713 		/* console config table */
1714 		return (i_vcc_cons_tbl(vccp, num_ports, (caddr_t)arg, mode));
1715 
1716 	case VCC_INQUIRY:
1717 
1718 		/* reason for wakeup */
1719 		return (i_vcc_inquiry(vccp, (caddr_t)arg, mode));
1720 
1721 	case VCC_CONS_INFO:
1722 		/* a console config */
1723 		return (i_vcc_cons_info(vccp, (caddr_t)arg, mode));
1724 
1725 	case VCC_FORCE_CLOSE:
1726 		/* force to close a console */
1727 		return (i_vcc_force_close(vccp, (caddr_t)arg, mode));
1728 
1729 	case VCC_CONS_STATUS:
1730 		/* console status */
1731 		return (i_vcc_cons_status(vccp, (caddr_t)arg, mode));
1732 
1733 	default:
1734 
1735 		/* unknown command */
1736 		return (ENODEV);
1737 	}
1738 
1739 
1740 }
1741 
1742 /* write data to ldc. may block if channel has no space for write */
1743 static int
1744 i_vcc_write_ldc(vcc_port_t *vport, vcc_msg_t *buf)
1745 {
1746 	int	rv = EIO;
1747 	size_t	size;
1748 
1749 	ASSERT(mutex_owned(&vport->lock));
1750 	ASSERT((vport->status & VCC_PORT_USE_WRITE_LDC) == 0);
1751 
1752 	for (; ; ) {
1753 
1754 		size = VCC_HDR_SZ + buf->size;
1755 		rv = ldc_write(vport->ldc_handle, (caddr_t)buf, &size);
1756 
1757 		D1("i_vcc_write_ldc: port@%d: err=%d %d bytes\n",
1758 		    vport->number, rv, size);
1759 
1760 		if (rv == 0) {
1761 			return (rv);
1762 		}
1763 
1764 		if (rv != EWOULDBLOCK) {
1765 			return (EIO);
1766 		}
1767 
1768 		if (vport->status & VCC_PORT_NONBLOCK) {
1769 			return (EAGAIN);
1770 		}
1771 
1772 		/*  block util ldc has more space */
1773 
1774 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
1775 		    VCC_PORT_LDC_WRITE_READY);
1776 
1777 		if (rv) {
1778 			return (rv);
1779 		}
1780 
1781 		vport->status &= ~VCC_PORT_LDC_WRITE_READY;
1782 
1783 	}
1784 
1785 }
1786 
1787 
1788 
1789 /* cb_ioctl handler for port ioctl */
1790 static int
1791 i_vcc_port_ioctl(vcc_t *vccp, minor_t minor, int portno, int cmd, void *arg,
1792     int mode)
1793 {
1794 
1795 	vcc_port_t	*vport;
1796 	struct termios	term;
1797 	vcc_msg_t	buf;
1798 	int		rv;
1799 
1800 	D1("i_vcc_port_ioctl@%d cmd %d\n", portno, cmd);
1801 
1802 	vport = &(vccp->port[portno]);
1803 
1804 	if ((vport->status & VCC_PORT_AVAIL) == 0) {
1805 		return (EIO);
1806 	}
1807 
1808 
1809 	switch (cmd) {
1810 
1811 	/* terminal support */
1812 	case TCGETA:
1813 	case TCGETS:
1814 
1815 		mutex_enter(&vport->lock);
1816 
1817 		/* check minor no and pid */
1818 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1819 		    vport)) != 0) {
1820 			mutex_exit(&vport->lock);
1821 			return (rv);
1822 		}
1823 
1824 		(void) memcpy(&term, &vport->term, sizeof (term));
1825 		mutex_exit(&vport->lock);
1826 
1827 		return (ddi_copyout(&term, arg, sizeof (term), mode));
1828 
1829 	case TCSETS:
1830 	case TCSETA:
1831 	case TCSETAW:
1832 	case TCSETAF:
1833 
1834 		if (ddi_copyin(arg, &term, sizeof (term), mode) != 0) {
1835 			return (EFAULT);
1836 		}
1837 
1838 		mutex_enter(&vport->lock);
1839 
1840 		/* check minor no and pid */
1841 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1842 		    vport)) != 0) {
1843 			mutex_exit(&vport->lock);
1844 			return (rv);
1845 		}
1846 
1847 		(void) memcpy(&vport->term, &term, sizeof (term));
1848 		mutex_exit(&vport->lock);
1849 		return (0);
1850 
1851 
1852 	case TCSBRK:
1853 
1854 		/* send break to console */
1855 		mutex_enter(&vport->lock);
1856 
1857 		/* check minor no and pid */
1858 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1859 		    vport)) != 0) {
1860 			mutex_exit(&vport->lock);
1861 			return (rv);
1862 		}
1863 
1864 		/* wait for write available */
1865 		rv = i_vcc_wait_port_status(vport, &vport->write_cv,
1866 		    VCC_PORT_LDC_CHANNEL_READY| VCC_PORT_USE_WRITE_LDC);
1867 		if (rv) {
1868 			mutex_exit(&vport->lock);
1869 			return (rv);
1870 		}
1871 
1872 		vport->status &= ~VCC_PORT_USE_WRITE_LDC;
1873 
1874 		buf.type = LDC_CONSOLE_CTRL;
1875 		buf.ctrl_msg = LDC_CONSOLE_BREAK;
1876 		buf.size = 0;
1877 
1878 		rv = i_vcc_write_ldc(vport, &buf);
1879 
1880 		mutex_exit(&vport->lock);
1881 
1882 		i_vcc_set_port_status(vport, &vport->write_cv,
1883 		    VCC_PORT_USE_WRITE_LDC);
1884 		return (0);
1885 
1886 	case TCXONC:
1887 		/* suspend read or write */
1888 		if (ddi_copyin(arg, &cmd, sizeof (int), mode) != 0) {
1889 			return (EFAULT);
1890 		}
1891 
1892 		mutex_enter(&vport->lock);
1893 
1894 		/* check minor no and pid */
1895 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
1896 		    vport)) != 0) {
1897 			mutex_exit(&vport->lock);
1898 			return (rv);
1899 		}
1900 
1901 
1902 		switch (cmd) {
1903 
1904 		case 0:
1905 			/* suspend read */
1906 			vport->status &= ~VCC_PORT_TERM_RD;
1907 			break;
1908 
1909 		case 1:
1910 			/* resume read */
1911 			vport->status |= VCC_PORT_TERM_RD;
1912 			cv_broadcast(&vport->read_cv);
1913 			break;
1914 
1915 		case 2:
1916 			/* suspend write */
1917 			vport->status &= ~VCC_PORT_TERM_WR;
1918 			break;
1919 
1920 		case 3:
1921 			/* resume write */
1922 			vport->status |= VCC_PORT_TERM_WR;
1923 			cv_broadcast(&vport->write_cv);
1924 			break;
1925 
1926 		default:
1927 			mutex_exit(&vport->lock);
1928 			return (EINVAL);
1929 		}
1930 
1931 		mutex_exit(&vport->lock);
1932 		return (0);
1933 
1934 	case TCFLSH:
1935 		return (0);
1936 
1937 	default:
1938 		return (EINVAL);
1939 	}
1940 
1941 }
1942 
1943 /* cb_ioctl */
1944 static int
1945 vcc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
1946     cred_t *credp, int *rvalp)
1947 {
1948 	_NOTE(ARGUNUSED(credp, rvalp))
1949 
1950 	int instance;
1951 	minor_t minor;
1952 	int portno;
1953 	vcc_t *vccp;
1954 
1955 	minor = getminor(dev);
1956 
1957 	instance = VCCINST(minor);
1958 
1959 	vccp = ddi_get_soft_state(vcc_ssp, instance);
1960 	if (vccp == NULL) {
1961 		return (ENXIO);
1962 	}
1963 
1964 	portno = VCCPORT(vccp, minor);
1965 
1966 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d\n", instance, portno);
1967 
1968 	if (portno >= VCC_MAX_PORTS) {
1969 		cmn_err(CE_CONT, "vcc_ioctl:virtual-console-concentrator@%d"
1970 		    " invalid portno\n", portno);
1971 		return (EINVAL);
1972 	}
1973 
1974 	D1("vcc_ioctl: virtual-console-concentrator@%d:%d ioctl cmd=%d\n",
1975 	    instance, portno, cmd);
1976 
1977 	if (portno == VCC_CONTROL_PORT) {
1978 		/* control ioctl */
1979 		return (i_vcc_ctrl_ioctl(vccp, cmd, (void *)arg, mode));
1980 	}
1981 
1982 	/* data port ioctl */
1983 	return (i_vcc_port_ioctl(vccp, minor, portno, cmd, (void *)arg, mode));
1984 }
1985 
1986 /* cb_read */
1987 static int
1988 vcc_read(dev_t dev, struct uio *uiop, cred_t *credp)
1989 {
1990 	_NOTE(ARGUNUSED(credp))
1991 
1992 	int	    instance;
1993 	minor_t	    minor;
1994 	uint_t	    portno;
1995 	vcc_t	    *vccp;
1996 	vcc_port_t  *vport;
1997 	int	    rv = EIO;	/* by default fail ! */
1998 	char 		*buf;
1999 	size_t		uio_size;
2000 	size_t		size;
2001 
2002 	minor = getminor(dev);
2003 
2004 	instance = VCCINST(minor);
2005 
2006 	vccp = ddi_get_soft_state(vcc_ssp, instance);
2007 	if (vccp == NULL) {
2008 		return (ENXIO);
2009 	}
2010 
2011 	portno = VCCPORT(vccp, minor);
2012 
2013 	/* no read for control port */
2014 	if (portno == VCC_CONTROL_PORT) {
2015 		return (EIO);
2016 	}
2017 
2018 	/* temp buf to hold ldc data */
2019 	uio_size = uiop->uio_resid;
2020 
2021 	if (uio_size < VCC_MTU_SZ) {
2022 		return (EINVAL);
2023 	}
2024 
2025 	vport = &(vccp->port[portno]);
2026 
2027 	mutex_enter(&vport->lock);
2028 
2029 	/* check minor no and pid */
2030 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
2031 	    vport)) != 0) {
2032 		mutex_exit(&vport->lock);
2033 		return (rv);
2034 	}
2035 
2036 	rv = i_vcc_wait_port_status(vport, &vport->read_cv,
2037 	    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
2038 	    VCC_PORT_USE_READ_LDC);
2039 	if (rv) {
2040 		mutex_exit(&vport->lock);
2041 		return (rv);
2042 	}
2043 
2044 	buf = kmem_alloc(uio_size, KM_SLEEP);
2045 
2046 	vport->status &= ~VCC_PORT_USE_READ_LDC;
2047 
2048 	for (; ; ) {
2049 
2050 		size = uio_size;
2051 		rv = i_vcc_read_ldc(vport, buf, &size);
2052 
2053 
2054 		if (rv == EAGAIN) {
2055 			/* should block? */
2056 			if (vport->status & VCC_PORT_NONBLOCK) {
2057 				break;
2058 			}
2059 
2060 		} else if (rv) {
2061 			/* error */
2062 			break;
2063 		}
2064 
2065 		if (size > 0) {
2066 			/* got data */
2067 			break;
2068 		}
2069 
2070 		/* wait for data from ldc */
2071 		vport->status &= ~VCC_PORT_LDC_DATA_READY;
2072 
2073 		mutex_exit(&vport->lock);
2074 		i_vcc_set_port_status(vport, &vport->read_cv,
2075 		    VCC_PORT_USE_READ_LDC);
2076 		mutex_enter(&vport->lock);
2077 
2078 		rv = i_vcc_wait_port_status(vport, &vport->read_cv,
2079 		    VCC_PORT_TERM_RD|VCC_PORT_LDC_CHANNEL_READY|
2080 		    VCC_PORT_USE_READ_LDC| VCC_PORT_LDC_DATA_READY);
2081 		if (rv) {
2082 			break;
2083 		}
2084 
2085 		vport->status &= ~VCC_PORT_USE_READ_LDC;
2086 	}
2087 
2088 	mutex_exit(&vport->lock);
2089 
2090 	if ((rv == 0) && (size > 0)) {
2091 		/* data is in buf */
2092 		rv = uiomove(buf, size, UIO_READ, uiop);
2093 	}
2094 
2095 	kmem_free(buf, uio_size);
2096 	i_vcc_set_port_status(vport, &vport->read_cv, VCC_PORT_USE_READ_LDC);
2097 
2098 	return (rv);
2099 }
2100 
2101 
2102 /* cb_write */
2103 static int
2104 vcc_write(dev_t dev, struct uio *uiop, cred_t *credp)
2105 {
2106 	_NOTE(ARGUNUSED(credp))
2107 
2108 	int	    instance;
2109 	minor_t	    minor;
2110 	size_t	    size;
2111 	size_t	    bytes;
2112 	uint_t	    portno;
2113 	vcc_t	    *vccp;
2114 
2115 	vcc_port_t  *vport;
2116 	int	    rv = EIO;
2117 
2118 	vcc_msg_t	buf;
2119 
2120 	minor = getminor(dev);
2121 
2122 	instance = VCCINST(minor);
2123 
2124 	vccp = ddi_get_soft_state(vcc_ssp, instance);
2125 	if (vccp == NULL) {
2126 		return (ENXIO);
2127 	}
2128 
2129 	portno = VCCPORT(vccp, minor);
2130 
2131 	/* no write for control port */
2132 	if (portno == VCC_CONTROL_PORT) {
2133 		return (EIO);
2134 	}
2135 	vport = &(vccp->port[portno]);
2136 
2137 	/*
2138 	 * check if the channel has been configured,
2139 	 * if write has been suspend and grab write lock.
2140 	 */
2141 	mutex_enter(&vport->lock);
2142 
2143 	/* check minor no and pid */
2144 	if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
2145 	    vport)) != 0) {
2146 		mutex_exit(&vport->lock);
2147 		return (rv);
2148 	}
2149 
2150 	rv = i_vcc_wait_port_status(vport, &vport->write_cv,
2151 	    VCC_PORT_TERM_WR|VCC_PORT_LDC_CHANNEL_READY|
2152 	    VCC_PORT_USE_WRITE_LDC);
2153 	if (rv) {
2154 		mutex_exit(&vport->lock);
2155 		return (rv);
2156 	}
2157 
2158 	vport->status &= ~VCC_PORT_USE_WRITE_LDC;
2159 	mutex_exit(&vport->lock);
2160 	size = uiop->uio_resid;
2161 
2162 	D2("vcc_write: virtual-console-concentrator@%d:%d writing %d bytes\n",
2163 	    instance, portno, size);
2164 
2165 
2166 
2167 	buf.type = LDC_CONSOLE_DATA;
2168 
2169 	while (size) {
2170 
2171 		bytes = MIN(size, VCC_MTU_SZ);
2172 		/* move data */
2173 		rv = uiomove(&(buf.data), bytes, UIO_WRITE, uiop);
2174 
2175 		if (rv) {
2176 			break;
2177 		}
2178 
2179 		/* write to ldc */
2180 		buf.size = bytes;
2181 
2182 		mutex_enter(&vport->lock);
2183 
2184 		/* check minor no and pid */
2185 		if ((rv = i_vcc_can_use_port(VCCMINORP(vccp, minor),
2186 		    vport)) != 0) {
2187 			mutex_exit(&vport->lock);
2188 			return (rv);
2189 		}
2190 
2191 		rv = i_vcc_write_ldc(vport, &buf);
2192 
2193 		mutex_exit(&vport->lock);
2194 
2195 		if (rv) {
2196 			break;
2197 		}
2198 
2199 		size -= bytes;
2200 
2201 	}
2202 
2203 	i_vcc_set_port_status(vport, &vport->write_cv, VCC_PORT_USE_WRITE_LDC);
2204 	return (rv);
2205 }
2206 
2207 /* mdeg callback for a removed port */
2208 static int
2209 i_vcc_md_remove_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
2210 {
2211 	uint64_t  portno;	/* md requires 64bit for port number */
2212 	int rv = MDEG_FAILURE;
2213 	vcc_port_t *vport;
2214 
2215 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
2216 		cmn_err(CE_CONT, "vcc_mdeg_cb: port has no 'id' property\n");
2217 		return (MDEG_FAILURE);
2218 	}
2219 
2220 	if ((portno >= VCC_MAX_PORTS) || (portno < 0)) {
2221 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld invalid port no\n",
2222 		    portno);
2223 		return (MDEG_FAILURE);
2224 	}
2225 
2226 	if (portno == VCC_CONTROL_PORT) {
2227 		cmn_err(CE_CONT, "i_vcc_md_remove_port@%ld can not remove"
2228 		    "control port\n",
2229 		    portno);
2230 		return (MDEG_FAILURE);
2231 	}
2232 
2233 	vport = &(vccp->port[portno]);
2234 
2235 	/* delete the port */
2236 	mutex_enter(&vport->lock);
2237 	rv = i_vcc_delete_port(vccp, vport);
2238 	mutex_exit(&vport->lock);
2239 
2240 	mutex_enter(&vccp->lock);
2241 	vccp->num_ports--;
2242 	mutex_exit(&vccp->lock);
2243 
2244 	return (rv ? MDEG_FAILURE : MDEG_SUCCESS);
2245 }
2246 
2247 static int
2248 i_vcc_get_ldc_id(md_t *md, mde_cookie_t mdep, uint64_t *ldc_id)
2249 {
2250 	int		num_nodes;
2251 	size_t		size;
2252 	mde_cookie_t	*channel;
2253 	int		num_channels;
2254 
2255 
2256 	if ((num_nodes = md_node_count(md)) <= 0) {
2257 		cmn_err(CE_CONT, "i_vcc_get_ldc_channel_id:"
2258 		    "  Invalid node count in Machine Description subtree");
2259 		return (-1);
2260 	}
2261 	size = num_nodes*(sizeof (*channel));
2262 	channel = kmem_zalloc(size, KM_SLEEP);
2263 	ASSERT(channel != NULL);	/* because KM_SLEEP */
2264 
2265 
2266 	/* Look for channel endpoint child(ren) of the vdisk MD node */
2267 	if ((num_channels = md_scan_dag(md, mdep,
2268 	    md_find_name(md, "channel-endpoint"),
2269 	    md_find_name(md, "fwd"), channel)) <= 0) {
2270 		cmn_err(CE_CONT, "i_vcc_get_ldc_id:  No 'channel-endpoint'"
2271 		    " found for vcc");
2272 		kmem_free(channel, size);
2273 		return (-1);
2274 	}
2275 
2276 	/* Get the "id" value for the first channel endpoint node */
2277 	if (md_get_prop_val(md, channel[0], "id", ldc_id) != 0) {
2278 		cmn_err(CE_CONT, "i_vcc_get_ldc:  No id property found "
2279 		    "for channel-endpoint of vcc");
2280 		kmem_free(channel, size);
2281 		return (-1);
2282 	}
2283 
2284 	if (num_channels > 1) {
2285 		cmn_err(CE_CONT, "i_vcc_get_ldc:  Warning:  Using ID of first"
2286 		    " of multiple channels for this vcc");
2287 	}
2288 
2289 	kmem_free(channel, size);
2290 	return (0);
2291 }
2292 /* mdeg callback for an added port  */
2293 static int
2294 i_vcc_md_add_port(md_t *mdp, mde_cookie_t mdep, vcc_t *vccp)
2295 {
2296 	uint64_t	portno;		/* md requires 64 bit */
2297 	char		*domain_name;
2298 	char		*group_name;
2299 	uint64_t	ldc_id;
2300 	uint64_t	tcp_port;
2301 	vcc_port_t	*vport;
2302 
2303 	/* read in the port's reg property */
2304 	if (md_get_prop_val(mdp, mdep, "id", &portno)) {
2305 		cmn_err(CE_CONT, "i_vcc_md_add_port_: port has no 'id' "
2306 		    "property\n");
2307 		return (MDEG_FAILURE);
2308 	}
2309 
2310 	/* read in the port's "vcc-doman-name" property */
2311 	if (md_get_prop_str(mdp, mdep, "vcc-domain-name", &domain_name)) {
2312 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has "
2313 		    "no 'vcc-domain-name' property\n", portno);
2314 		return (MDEG_FAILURE);
2315 	}
2316 
2317 
2318 	/* read in the port's "vcc-group-name" property */
2319 	if (md_get_prop_str(mdp, mdep, "vcc-group-name", &group_name)) {
2320 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no "
2321 		    "'vcc-group-name'property\n", portno);
2322 		return (MDEG_FAILURE);
2323 	}
2324 
2325 
2326 	/* read in the port's "vcc-tcp-port" property */
2327 	if (md_get_prop_val(mdp, mdep, "vcc-tcp-port", &tcp_port)) {
2328 		cmn_err(CE_CONT, "i_vcc_md_add_port: port%ld has no"
2329 		    "'vcc-tcp-port' property\n", portno);
2330 		return (MDEG_FAILURE);
2331 	}
2332 
2333 	D1("i_vcc_md_add_port: port@%d domain-name=%s group-name=%s"
2334 	    " tcp-port=%lld\n", portno, domain_name, group_name, tcp_port);
2335 
2336 	/* add the port */
2337 	if (i_vcc_add_port(vccp, group_name, tcp_port, portno, domain_name)) {
2338 		return (MDEG_FAILURE);
2339 	}
2340 
2341 	vport = &vccp->port[portno];
2342 	if (i_vcc_get_ldc_id(mdp, mdep, &ldc_id)) {
2343 		mutex_enter(&vport->lock);
2344 		(void) i_vcc_delete_port(vccp, vport);
2345 		mutex_exit(&vport->lock);
2346 		return (MDEG_FAILURE);
2347 	}
2348 
2349 	/* configure the port */
2350 	if (i_vcc_config_port(vccp, portno, ldc_id)) {
2351 		mutex_enter(&vport->lock);
2352 		(void) i_vcc_delete_port(vccp, vport);
2353 		mutex_exit(&vport->lock);
2354 		return (MDEG_FAILURE);
2355 	}
2356 
2357 	mutex_enter(&vccp->lock);
2358 	vccp->num_ports++;
2359 	mutex_exit(&vccp->lock);
2360 
2361 	vport = &vccp->port[VCC_CONTROL_PORT];
2362 
2363 	if (vport->pollflag & VCC_POLL_CONFIG) {
2364 		/* wakeup vntsd */
2365 		mutex_enter(&vport->lock);
2366 		vport->pollevent |= VCC_POLL_ADD_PORT;
2367 		mutex_exit(&vport->lock);
2368 		pollwakeup(&vport->poll, POLLIN);
2369 	}
2370 
2371 	return (MDEG_SUCCESS);
2372 }
2373 
2374 /* mdeg callback */
2375 static int
2376 vcc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
2377 {
2378 	int	idx;
2379 	vcc_t 	*vccp;
2380 	int	rv;
2381 
2382 	vccp = (vcc_t *)cb_argp;
2383 	ASSERT(vccp);
2384 
2385 	if (resp == NULL) {
2386 		return (MDEG_FAILURE);
2387 	}
2388 
2389 	/* added port */
2390 	D1("vcc_mdeg_cb: added %d port(s)\n", resp->added.nelem);
2391 
2392 	for (idx = 0; idx < resp->added.nelem; idx++) {
2393 		rv = i_vcc_md_add_port(resp->added.mdp,
2394 		    resp->added.mdep[idx], vccp);
2395 
2396 		if (rv !=  MDEG_SUCCESS) {
2397 			return (rv);
2398 		}
2399 	}
2400 
2401 	/* removed port */
2402 	D1("vcc_mdeg_cb: removed %d port(s)\n", resp->removed.nelem);
2403 
2404 	for (idx = 0; idx < resp->removed.nelem; idx++) {
2405 		rv = i_vcc_md_remove_port(resp->removed.mdp,
2406 		    resp->removed.mdep[idx], vccp);
2407 
2408 		if (rv !=  MDEG_SUCCESS) {
2409 			return (rv);
2410 		}
2411 
2412 	}
2413 
2414 	/*
2415 	 * XXX - Currently no support for updating already active
2416 	 * ports. So, ignore the match_curr and match_prev arrays
2417 	 * for now.
2418 	 */
2419 
2420 	return (MDEG_SUCCESS);
2421 }
2422 
2423 
2424 /* cb_chpoll */
2425 static int
2426 vcc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
2427     struct pollhead **phpp)
2428 {
2429 	int	    instance;
2430 	minor_t	    minor;
2431 	uint_t	    portno;
2432 	vcc_t	    *vccp;
2433 	vcc_port_t  *vport;
2434 
2435 	minor = getminor(dev);
2436 
2437 	instance = VCCINST(minor);
2438 
2439 	vccp = ddi_get_soft_state(vcc_ssp, instance);
2440 	if (vccp == NULL) {
2441 		return (ENXIO);
2442 	}
2443 
2444 	portno = VCCPORT(vccp, minor);
2445 
2446 	vport = &(vccp->port[portno]);
2447 
2448 	D1("vcc_chpoll: virtual-console-concentrator@%d events 0x%x\n",
2449 	    portno, events);
2450 
2451 	*reventsp = 0;
2452 
2453 	if (portno != VCC_CONTROL_PORT) {
2454 		return (ENXIO);
2455 	}
2456 
2457 	/* poll for config change */
2458 	if (vport->pollevent) {
2459 		*reventsp |= (events & POLLIN);
2460 	}
2461 
2462 	if ((((*reventsp) == 0) && (!anyyet)) || (events & POLLET)) {
2463 		*phpp = &vport->poll;
2464 		if (events & POLLIN) {
2465 			mutex_enter(&vport->lock);
2466 			vport->pollflag |= VCC_POLL_CONFIG;
2467 			mutex_exit(&vport->lock);
2468 		} else {
2469 			return (ENXIO);
2470 		}
2471 	}
2472 
2473 	D1("vcc_chpoll: virtual-console-concentrator@%d:%d ev=0x%x, "
2474 	    "rev=0x%x pev=0x%x, flag=0x%x\n",
2475 	    instance, portno, events, (*reventsp),
2476 	    vport->pollevent, vport->pollflag);
2477 
2478 
2479 	return (0);
2480 }
2481