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