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