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