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