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