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 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 24 */ 25 26 /* 27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 /* 32 * Copyright 2023 Oxide Computer Company 33 */ 34 35 #include <sys/errno.h> 36 #include <sys/modctl.h> 37 #include <sys/stat.h> 38 #include <sys/kmem.h> 39 #include <sys/ksynch.h> 40 #include <sys/stream.h> 41 #include <sys/stropts.h> 42 #include <sys/termio.h> 43 #include <sys/ddi.h> 44 #include <sys/file.h> 45 #include <sys/disp.h> 46 #include <sys/sunddi.h> 47 #include <sys/sunldi.h> 48 #include <sys/sunndi.h> 49 #include <sys/kbio.h> 50 #include <sys/prom_plat.h> 51 #include <sys/oplmsu/oplmsu.h> 52 #include <sys/oplmsu/oplmsu_proto.h> 53 54 extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t); 55 56 #define MOD_ID 0xe145 57 #define MOD_NAME "oplmsu" 58 59 #define META_NAME "oplmsu" 60 #define USER_NAME "a" 61 62 struct module_info oplmsu_mod_info = { 63 MOD_ID, 64 MOD_NAME, 65 0, 66 16384, 67 14336, 68 2048 69 }; 70 71 struct qinit oplmsu_urinit = { 72 NULL, 73 oplmsu_ursrv, 74 oplmsu_open, 75 oplmsu_close, 76 NULL, 77 &oplmsu_mod_info, 78 NULL 79 }; 80 81 struct qinit oplmsu_uwinit = { 82 oplmsu_uwput, 83 oplmsu_uwsrv, 84 oplmsu_open, 85 oplmsu_close, 86 NULL, 87 &oplmsu_mod_info, 88 NULL 89 }; 90 91 struct qinit oplmsu_lrinit = { 92 oplmsu_lrput, 93 oplmsu_lrsrv, 94 oplmsu_open, 95 oplmsu_close, 96 NULL, 97 &oplmsu_mod_info, 98 NULL 99 }; 100 101 struct qinit oplmsu_lwinit = { 102 NULL, 103 oplmsu_lwsrv, 104 oplmsu_open, 105 oplmsu_close, 106 NULL, 107 &oplmsu_mod_info, 108 NULL 109 }; 110 111 struct streamtab oplmsu_info = { 112 &oplmsu_urinit, 113 &oplmsu_uwinit, 114 &oplmsu_lrinit, 115 &oplmsu_lwinit 116 }; 117 118 static struct cb_ops cb_oplmsu_ops = { 119 nulldev, /* cb_open */ 120 nulldev, /* cb_close */ 121 nodev, /* cb_strategy */ 122 nodev, /* cb_print */ 123 nodev, /* cb_dump */ 124 nodev, /* cb_read */ 125 nodev, /* cb_write */ 126 nodev, /* cb_ioctl */ 127 nodev, /* cb_devmap */ 128 nodev, /* cb_mmap */ 129 nodev, /* cb_segmap */ 130 nochpoll, /* cb_chpoll */ 131 ddi_prop_op, /* cb_prop_op */ 132 (&oplmsu_info), /* cb_stream */ 133 (int)(D_NEW|D_MP|D_HOTPLUG) /* cb_flag */ 134 }; 135 136 static struct dev_ops oplmsu_ops = { 137 DEVO_REV, /* devo_rev */ 138 0, /* devo_refcnt */ 139 (oplmsu_getinfo), /* devo_getinfo */ 140 (nulldev), /* devo_identify */ 141 (nulldev), /* devo_probe */ 142 (oplmsu_attach), /* devo_attach */ 143 (oplmsu_detach), /* devo_detach */ 144 (nodev), /* devo_reset */ 145 &(cb_oplmsu_ops), /* devo_cb_ops */ 146 (struct bus_ops *)NULL, /* devo_bus_ops */ 147 NULL, /* devo_power */ 148 ddi_quiesce_not_needed, /* dev_quiesce */ 149 }; 150 151 struct modldrv modldrv = { 152 &mod_driverops, 153 "OPL serial mux driver", 154 &oplmsu_ops 155 }; 156 157 struct modlinkage modlinkage = { 158 MODREV_1, 159 (void *)&modldrv, 160 NULL 161 }; 162 163 uinst_t oplmsu_uinst_local; /* upper_instance_table structure */ 164 uinst_t *oplmsu_uinst = &oplmsu_uinst_local; 165 int oplmsu_queue_flag; /* Enable/disable queueing flag */ 166 int oplmsu_check_su; /* Check super-user flag */ 167 168 #ifdef DEBUG 169 int oplmsu_debug_mode = 0; /* Enable/disable debug mode */ 170 int oplmsu_trace_on; /* Enable/disable trace */ 171 uint_t oplmsu_ltrc_size; /* Trace buffer size */ 172 msu_trc_t *oplmsu_ltrc_top; /* Top of trace data area */ 173 msu_trc_t *oplmsu_ltrc_tail; /* Tail of trace data area */ 174 msu_trc_t *oplmsu_ltrc_cur; /* Current pointer of trace data area */ 175 ulong_t oplmsu_ltrc_ccnt; /* Current counter */ 176 kmutex_t oplmsu_ltrc_lock; /* Lock table for trace mode */ 177 #endif 178 179 /* oplmsu_conf_st */ 180 #define MSU_CONFIGURED 2 181 #define MSU_CONFIGURING 1 182 #define MSU_UNCONFIGURED 0 183 184 static kmutex_t oplmsu_bthrd_excl; 185 static kthread_id_t oplmsu_bthrd_id = NULL; 186 static int oplmsu_conf_st = MSU_UNCONFIGURED; 187 static kcondvar_t oplmsu_conf_cv; 188 189 190 /* 191 * Locking hierarcy of oplmsu driver. This driver have 5 locks in uinst_t. 192 * 193 * Each mutex guards as follows. 194 * 195 * uinst_t->lock: This mutex is read/write mutex. 196 * read lock : acquired if the member of uinst_t is refered only. 197 * write lock: acquired if the member of uinst_t is changed. 198 * 199 * uinst_t->u_lock: This mutex is normal mutex. 200 * This mutex is acquired at reading/changing the member of all upath_t. 201 * 202 * uinst_t->l_lock: This mutex is normal mutex. 203 * This mutex is acquired at reading/changing the member of all lpath_t. 204 * 205 * uinst_t->c_lock: This mutex is normal mutex. 206 * This mutex is acquired at reading/changing the member of the ctrl_t. 207 * 208 * oplmsu_bthrd_excl: This mutex is normal mutex. 209 * This mutex is used only to start/stop the configuring thread of the 210 * multiplexed STREAMS. 211 * This mutex is exclusively acquired with the above-mentioned 4 mutexes. 212 * 213 * To guard of the deadlock by cross locking, the base locking hierarcy 214 * is as follows: 215 * 216 * uisnt->lock ==> uinst->u_lock ==> uinst->l_lock ==> uinst->c_lock 217 * 218 */ 219 220 221 int 222 _init(void) 223 { 224 int rval; 225 226 /* Initialize R/W lock for uinst_t */ 227 rw_init(&oplmsu_uinst->lock, "uinst rwlock", RW_DRIVER, NULL); 228 229 /* Initialize mutex for upath_t */ 230 mutex_init(&oplmsu_uinst->u_lock, "upath lock", MUTEX_DRIVER, NULL); 231 232 /* Initialize mutex for lpath_t */ 233 mutex_init(&oplmsu_uinst->l_lock, "lpath lock", MUTEX_DRIVER, NULL); 234 235 /* Initialize mutex for ctrl_t */ 236 mutex_init(&oplmsu_uinst->c_lock, "ctrl lock", MUTEX_DRIVER, NULL); 237 238 /* Initialize mutex for protecting background thread */ 239 mutex_init(&oplmsu_bthrd_excl, NULL, MUTEX_DRIVER, NULL); 240 241 /* Initialize condition variable */ 242 cv_init(&oplmsu_conf_cv, NULL, CV_DRIVER, NULL); 243 244 rval = mod_install(&modlinkage); 245 if (rval != DDI_SUCCESS) { 246 cv_destroy(&oplmsu_conf_cv); 247 mutex_destroy(&oplmsu_bthrd_excl); 248 mutex_destroy(&oplmsu_uinst->c_lock); 249 mutex_destroy(&oplmsu_uinst->l_lock); 250 mutex_destroy(&oplmsu_uinst->u_lock); 251 rw_destroy(&oplmsu_uinst->lock); 252 } 253 return (rval); 254 } 255 256 int 257 _fini(void) 258 { 259 int rval; 260 261 rval = mod_remove(&modlinkage); 262 if (rval == DDI_SUCCESS) { 263 cv_destroy(&oplmsu_conf_cv); 264 mutex_destroy(&oplmsu_bthrd_excl); 265 mutex_destroy(&oplmsu_uinst->c_lock); 266 mutex_destroy(&oplmsu_uinst->l_lock); 267 mutex_destroy(&oplmsu_uinst->u_lock); 268 rw_destroy(&oplmsu_uinst->lock); 269 } 270 return (rval); 271 } 272 273 int 274 _info(struct modinfo *modinfop) 275 { 276 return (mod_info(&modlinkage, modinfop)); 277 } 278 279 /* ARGSUSED */ 280 int 281 oplmsu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 282 { 283 dev_t dev = (dev_t)arg; 284 minor_t inst; 285 int rval = DDI_SUCCESS; 286 287 switch (cmd) { 288 case DDI_INFO_DEVT2DEVINFO : 289 if (oplmsu_uinst->msu_dip == NULL) { 290 rval = DDI_FAILURE; 291 } else { 292 *resultp = oplmsu_uinst->msu_dip; 293 } 294 break; 295 296 case DDI_INFO_DEVT2INSTANCE : 297 inst = getminor(dev) & ~(META_NODE_MASK|USER_NODE_MASK); 298 *resultp = (void *)(uintptr_t)inst; 299 break; 300 301 default : 302 rval = DDI_FAILURE; 303 break; 304 } 305 return (rval); 306 } 307 308 int 309 oplmsu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 310 { 311 minor_t meta_minor, user_minor; 312 int rval = 0; 313 int instance; 314 #define CNTRL(c) ((c) & 037) 315 char abt_ch_seq[3] = { '\r', '~', CNTRL('b') }; 316 317 if (cmd == DDI_RESUME) { 318 return (DDI_SUCCESS); 319 } 320 321 if (cmd != DDI_ATTACH) { 322 return (DDI_FAILURE); 323 } 324 325 instance = ddi_get_instance(dip); 326 if (instance != 0) { 327 cmn_err(CE_WARN, "oplmsu: attach: " 328 "Invaild instance => %d", instance); 329 return (DDI_FAILURE); 330 } 331 332 /* Create minor number for meta control node */ 333 meta_minor = instance | META_NODE_MASK; 334 /* Create minor number for user access node */ 335 user_minor = instance | USER_NODE_MASK; 336 337 /* Create minor node for user access */ 338 rval = ddi_create_minor_node(dip, USER_NAME, S_IFCHR, user_minor, 339 DDI_NT_SERIAL, 0); 340 if (rval != DDI_SUCCESS) { 341 cmn_err(CE_WARN, "oplmsu: attach: " 342 "ddi_create_minor_node failed. errno = %d", rval); 343 ddi_remove_minor_node(dip, NULL); 344 return (rval); 345 } 346 347 /* Create minor node for meta control */ 348 rval = ddi_create_internal_pathname(dip, META_NAME, S_IFCHR, 349 meta_minor); 350 if (rval != DDI_SUCCESS) { 351 cmn_err(CE_WARN, "oplmsu: attach: " 352 "ddi_create_internal_pathname failed. errno = %d", rval); 353 ddi_remove_minor_node(dip, NULL); 354 return (rval); 355 } 356 357 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 358 359 /* Get each properties */ 360 oplmsu_check_su = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 361 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "check-superuser", 1); 362 363 /* 364 * Initialize members of uinst_t 365 */ 366 367 oplmsu_uinst->inst_status = INST_STAT_UNCONFIGURED; 368 oplmsu_uinst->path_num = UNDEFINED; 369 oplmsu_uinst->msu_dip = dip; 370 (void) strcpy(oplmsu_uinst->abts, abt_ch_seq); 371 372 #ifdef DEBUG 373 oplmsu_trace_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 374 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-mode", 1); 375 oplmsu_ltrc_size = (uint_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 376 (DDI_PROP_DONTPASS|DDI_PROP_NOTPROM), "trace-bufsize", 128); 377 378 if (oplmsu_trace_on == MSU_TRACE_ON) { 379 /* Initialize mutex for msu_trc_t */ 380 mutex_init(&oplmsu_ltrc_lock, "trc lock", MUTEX_DRIVER, NULL); 381 382 mutex_enter(&oplmsu_ltrc_lock); 383 oplmsu_ltrc_top = (msu_trc_t *)kmem_zalloc( 384 (sizeof (msu_trc_t) * oplmsu_ltrc_size), KM_SLEEP); 385 oplmsu_ltrc_cur = (msu_trc_t *)(oplmsu_ltrc_top - 1); 386 oplmsu_ltrc_tail = 387 (msu_trc_t *)(oplmsu_ltrc_top + (oplmsu_ltrc_size - 1)); 388 mutex_exit(&oplmsu_ltrc_lock); 389 } 390 #endif 391 rw_exit(&oplmsu_uinst->lock); 392 ddi_report_dev(dip); 393 return (rval); 394 } 395 396 int 397 oplmsu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 398 { 399 lpath_t *lpath, *next_lpath; 400 401 if (cmd == DDI_SUSPEND) { 402 return (DDI_SUCCESS); 403 } 404 405 if (cmd != DDI_DETACH) { 406 return (DDI_FAILURE); 407 } 408 409 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 410 411 /* Delete all upath_t */ 412 oplmsu_delete_upath_info(); 413 414 /* Delete all lpath_t */ 415 mutex_enter(&oplmsu_uinst->l_lock); 416 lpath = oplmsu_uinst->first_lpath; 417 oplmsu_uinst->first_lpath = NULL; 418 oplmsu_uinst->last_lpath = NULL; 419 mutex_exit(&oplmsu_uinst->l_lock); 420 421 #ifdef DEBUG 422 if (oplmsu_trace_on == MSU_TRACE_ON) { 423 mutex_enter(&oplmsu_ltrc_lock); 424 if (oplmsu_ltrc_top != NULL) { 425 kmem_free(oplmsu_ltrc_top, 426 (sizeof (msu_trc_t) * oplmsu_ltrc_size)); 427 } 428 oplmsu_ltrc_top = NULL; 429 oplmsu_ltrc_cur = NULL; 430 oplmsu_ltrc_tail = NULL; 431 mutex_exit(&oplmsu_ltrc_lock); 432 433 mutex_destroy(&oplmsu_ltrc_lock); 434 } 435 #endif 436 rw_exit(&oplmsu_uinst->lock); 437 438 while (lpath) { 439 if (lpath->rbuf_id) { 440 unbufcall(lpath->rbuf_id); 441 } 442 443 if (lpath->rtout_id) { 444 (void) untimeout(lpath->rtout_id); 445 } 446 447 if (lpath->rbuftbl) { 448 kmem_free(lpath->rbuftbl, sizeof (struct buf_tbl)); 449 } 450 451 cv_destroy(&lpath->sw_cv); 452 next_lpath = lpath->l_next; 453 kmem_free(lpath, sizeof (lpath_t)); 454 lpath = next_lpath; 455 } 456 ddi_remove_minor_node(dip, NULL); 457 return (DDI_SUCCESS); 458 } 459 460 /* ARGSUSED */ 461 int 462 oplmsu_open(queue_t *urq, dev_t *dev, int oflag, int sflag, cred_t *cred_p) 463 { 464 ctrl_t *ctrl; 465 minor_t mindev = 0; 466 minor_t qmindev = 0; 467 major_t majdev; 468 ulong_t node_flag; 469 470 DBG_PRINT((CE_NOTE, "oplmsu: open: " 471 "devt = 0x%lx, sflag = 0x%x", *dev, sflag)); 472 473 if (sflag == CLONEOPEN) { 474 return (EINVAL); 475 } 476 477 /* Get minor device number */ 478 qmindev = (minor_t)getminor(*dev); 479 /* Get node type */ 480 node_flag = MSU_NODE_TYPE(qmindev); 481 if ((node_flag != MSU_NODE_USER) && (node_flag != MSU_NODE_META)) { 482 return (EINVAL); 483 } 484 485 mutex_enter(&oplmsu_bthrd_excl); 486 if ((node_flag == MSU_NODE_USER) && 487 (oplmsu_conf_st != MSU_CONFIGURED)) { /* User access & First open */ 488 int cv_rval; 489 490 DBG_PRINT((CE_NOTE, "oplmsu: open: " 491 "oplmsu_conf_st = %x", oplmsu_conf_st)); 492 493 if (oplmsu_conf_st == MSU_UNCONFIGURED) { 494 oplmsu_conf_st = MSU_CONFIGURING; 495 496 /* Start up background thread */ 497 oplmsu_bthrd_id = thread_create(NULL, 2 * DEFAULTSTKSZ, 498 oplmsu_setup, (void *)oplmsu_uinst, 0, &p0, TS_RUN, 499 minclsyspri); 500 } 501 502 /* 503 * Wait with cv_wait_sig() until background thread is 504 * completed. 505 */ 506 while (oplmsu_conf_st == MSU_CONFIGURING) { 507 cv_rval = 508 cv_wait_sig(&oplmsu_conf_cv, &oplmsu_bthrd_excl); 509 if (cv_rval == 0) { 510 mutex_exit(&oplmsu_bthrd_excl); 511 return (EINTR); 512 } 513 } 514 } 515 mutex_exit(&oplmsu_bthrd_excl); 516 517 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 518 519 /* 520 * If the node which will open is meta-control-node or 521 * user-access-node, and q_ptr, this is queue_t queue 522 * table member, is not NULL, then oplmsu returns 523 * SUCCESS immidiately. 524 * This process is used to protect dual open. 525 */ 526 527 if ((urq != NULL) && (urq->q_ptr != NULL)) { 528 rw_exit(&oplmsu_uinst->lock); 529 return (SUCCESS); 530 } 531 532 /* 533 * If the node which will open is User-Access-Node, and instance 534 * status of oplmsu is no ONLINE, then oplmsu_open process fails 535 * with return value 'EIO'. 536 */ 537 538 if ((node_flag == MSU_NODE_USER) && 539 (oplmsu_uinst->inst_status != INST_STAT_ONLINE)) { 540 rw_exit(&oplmsu_uinst->lock); 541 return (EIO); 542 } 543 544 mindev |= qmindev; /* Create minor device number */ 545 majdev = getmajor(*dev); /* Get major device number */ 546 *dev = makedevice(majdev, mindev); /* Make device number */ 547 548 /* Allocate kernel memory for ctrl_t */ 549 ctrl = (ctrl_t *)kmem_zalloc(sizeof (ctrl_t), KM_SLEEP); 550 551 /* 552 * Initialize members of ctrl_t 553 */ 554 ctrl->minor = (minor_t)mindev; 555 ctrl->queue = urq; 556 ctrl->sleep_flag = CV_WAKEUP; 557 ctrl->node_type = node_flag; 558 ctrl->wbuftbl = 559 (struct buf_tbl *)kmem_zalloc(sizeof (struct buf_tbl), KM_SLEEP); 560 cv_init(&ctrl->cvp, "oplmsu ctrl_tbl condvar", CV_DRIVER, NULL); 561 562 mutex_enter(&oplmsu_uinst->c_lock); 563 564 if (node_flag == MSU_NODE_USER) { /* User access node */ 565 566 oplmsu_uinst->user_ctrl = ctrl; 567 oplmsu_queue_flag = 0; 568 569 } else { /* Meta control node */ 570 571 oplmsu_uinst->meta_ctrl = ctrl; 572 } 573 574 RD(urq)->q_ptr = ctrl; 575 WR(urq)->q_ptr = ctrl; 576 577 mutex_exit(&oplmsu_uinst->c_lock); 578 rw_exit(&oplmsu_uinst->lock); 579 580 OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_OPN); 581 582 qprocson(urq); /* Enable put and service routine */ 583 return (SUCCESS); 584 } 585 586 /* ARGSUSED */ 587 int 588 oplmsu_close(queue_t *urq, int flag, cred_t *cred_p) 589 { 590 ctrl_t *ctrl; 591 minor_t qmindev = 0; 592 lpath_t *lpath; 593 ulong_t node_flag; 594 bufcall_id_t wbuf_id; 595 timeout_id_t wtout_id; 596 597 rw_enter(&oplmsu_uinst->lock, RW_READER); 598 mutex_enter(&oplmsu_uinst->l_lock); 599 mutex_enter(&oplmsu_uinst->c_lock); 600 if ((ctrl = urq->q_ptr) == NULL) { 601 mutex_exit(&oplmsu_uinst->c_lock); 602 mutex_exit(&oplmsu_uinst->l_lock); 603 rw_exit(&oplmsu_uinst->lock); 604 605 DBG_PRINT((CE_NOTE, "oplmsu: close: " 606 "close has already been completed")); 607 return (FAILURE); 608 } 609 qmindev = ctrl->minor; 610 611 DBG_PRINT((CE_NOTE, "oplmsu: close: ctrl->minor = 0x%x", qmindev)); 612 613 node_flag = MSU_NODE_TYPE(qmindev); 614 if (node_flag > MSU_NODE_META) { 615 mutex_exit(&oplmsu_uinst->c_lock); 616 mutex_exit(&oplmsu_uinst->l_lock); 617 rw_exit(&oplmsu_uinst->lock); 618 return (EINVAL); 619 } 620 621 /* 622 * Check that queue which is waiting for response from lower stream 623 * exist. If queue exists, oplmsu sets CV_SLEEP to sleep_flag. 624 */ 625 626 for (lpath = oplmsu_uinst->first_lpath; lpath; ) { 627 if (((RD(urq) == lpath->hndl_uqueue) || 628 (WR(urq) == lpath->hndl_uqueue)) && 629 (lpath->hndl_mp != NULL)) { 630 ctrl->sleep_flag = CV_SLEEP; 631 break; 632 } 633 634 lpath = lpath->l_next; 635 } 636 mutex_exit(&oplmsu_uinst->l_lock); 637 rw_exit(&oplmsu_uinst->lock); 638 639 /* If sleep_flag is not CV_SLEEP, oplmsu calls cv_wait. */ 640 if (lpath) { 641 while (ctrl->sleep_flag != CV_WAKEUP) { 642 cv_wait(&ctrl->cvp, &oplmsu_uinst->c_lock); 643 } 644 } 645 646 flushq(RD(urq), FLUSHALL); 647 flushq(WR(urq), FLUSHALL); 648 mutex_exit(&oplmsu_uinst->c_lock); 649 qprocsoff(urq); /* Disable queuing of queue */ 650 651 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 652 switch (node_flag) { 653 case MSU_NODE_USER : /* User access node */ 654 oplmsu_uinst->user_ctrl = NULL; 655 oplmsu_queue_flag = 0; 656 break; 657 658 case MSU_NODE_META : /* Meta control node */ 659 oplmsu_uinst->meta_ctrl = NULL; 660 break; 661 662 default : 663 cmn_err(CE_WARN, "oplmsu: close: node_flag = 0x%lx", node_flag); 664 } 665 666 ctrl->minor = 0; 667 ctrl->queue = NULL; 668 wbuf_id = ctrl->wbuf_id; 669 wtout_id = ctrl->wtout_id; 670 ctrl->wbuf_id = 0; 671 ctrl->wtout_id = 0; 672 673 cv_destroy(&ctrl->cvp); 674 kmem_free(ctrl->wbuftbl, sizeof (struct buf_tbl)); 675 ctrl->wbuftbl = NULL; 676 677 RD(urq)->q_ptr = NULL; 678 WR(urq)->q_ptr = NULL; 679 rw_exit(&oplmsu_uinst->lock); 680 681 if (wbuf_id != 0) { 682 unbufcall(wbuf_id); 683 } 684 685 if (wtout_id != 0) { 686 (void) untimeout(wtout_id); 687 } 688 689 /* Free kernel memory for ctrl_t */ 690 kmem_free(ctrl, sizeof (ctrl_t)); 691 692 OPLMSU_TRACE(urq, (mblk_t *)node_flag, MSU_TRC_CLS); 693 return (SUCCESS); 694 } 695 696 /* 697 * Upper write put procedure 698 */ 699 int 700 oplmsu_uwput(queue_t *uwq, mblk_t *mp) 701 { 702 703 if (mp == NULL) { 704 return (SUCCESS); 705 } 706 707 if ((uwq == NULL) || (uwq->q_ptr == NULL)) { 708 freemsg(mp); 709 return (SUCCESS); 710 } 711 712 OPLMSU_TRACE(uwq, mp, MSU_TRC_UI); 713 714 rw_enter(&oplmsu_uinst->lock, RW_READER); 715 if (mp->b_datap->db_type == M_FLUSH) { 716 oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER); 717 } else if (mp->b_datap->db_type >= QPCTL) { 718 ctrl_t *ctrl; 719 720 mutex_enter(&oplmsu_uinst->c_lock); 721 ctrl = (ctrl_t *)uwq->q_ptr; 722 723 /* Link high priority message to local queue */ 724 oplmsu_link_high_primsg(&ctrl->first_upri_hi, 725 &ctrl->last_upri_hi, mp); 726 727 mutex_exit(&oplmsu_uinst->c_lock); 728 oplmsu_wcmn_high_qenable(WR(uwq), RW_READER); 729 } else { 730 (void) putq(WR(uwq), mp); 731 } 732 rw_exit(&oplmsu_uinst->lock); 733 return (SUCCESS); 734 } 735 736 /* 737 * Upper write service procedure 738 */ 739 int 740 oplmsu_uwsrv(queue_t *uwq) 741 { 742 struct iocblk *iocp = NULL; 743 mblk_t *mp = NULL; 744 int rval; 745 746 if ((uwq == NULL) || (uwq->q_ptr == NULL)) { 747 return (FAILURE); 748 } 749 750 rw_enter(&oplmsu_uinst->lock, RW_READER); 751 752 /* Handle high priority message */ 753 while (mp = oplmsu_wcmn_high_getq(uwq)) { 754 if (mp->b_datap->db_type == M_FLUSH) { 755 oplmsu_wcmn_flush_hndl(uwq, mp, RW_READER); 756 continue; 757 } 758 759 if (oplmsu_wcmn_through_hndl(uwq, mp, MSU_HIGH, RW_READER) == 760 FAILURE) { 761 rw_exit(&oplmsu_uinst->lock); 762 return (SUCCESS); 763 } 764 } 765 rw_exit(&oplmsu_uinst->lock); 766 767 /* Handle normal priority message */ 768 while (mp = getq(uwq)) { 769 rval = SUCCESS; 770 switch (mp->b_datap->db_type) { 771 case M_IOCTL : 772 iocp = (struct iocblk *)mp->b_rptr; 773 switch (iocp->ioc_cmd) { 774 case I_PLINK : 775 if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) { 776 rval = oplmsu_uwioctl_iplink(uwq, mp); 777 } 778 break; 779 780 case I_PUNLINK : 781 if (oplmsu_cmn_pullup_msg(uwq, mp) != FAILURE) { 782 rval = oplmsu_uwioctl_ipunlink(uwq, mp); 783 } 784 break; 785 786 case TCSETS : /* FALLTHRU */ 787 case TCSETSW : /* FALLTHRU */ 788 case TCSETSF : /* FALLTHRU */ 789 case TIOCMSET : /* FALLTHRU */ 790 case TIOCSPPS : /* FALLTHRU */ 791 case TIOCSWINSZ : /* FALLTHRU */ 792 case TIOCSSOFTCAR : 793 rval = oplmsu_uwioctl_termios(uwq, mp); 794 break; 795 796 default : 797 rw_enter(&oplmsu_uinst->lock, RW_READER); 798 rval = oplmsu_wcmn_through_hndl(uwq, mp, 799 MSU_NORM, RW_READER); 800 rw_exit(&oplmsu_uinst->lock); 801 break; 802 } 803 break; 804 805 default : 806 rw_enter(&oplmsu_uinst->lock, RW_READER); 807 rval = oplmsu_wcmn_through_hndl(uwq, mp, MSU_NORM, 808 RW_READER); 809 rw_exit(&oplmsu_uinst->lock); 810 break; 811 } 812 813 if (rval == FAILURE) { 814 break; 815 } 816 } 817 return (SUCCESS); 818 } 819 820 /* 821 * Lower write service procedure 822 */ 823 int 824 oplmsu_lwsrv(queue_t *lwq) 825 { 826 mblk_t *mp; 827 queue_t *dst_queue; 828 lpath_t *lpath; 829 830 rw_enter(&oplmsu_uinst->lock, RW_READER); 831 while (mp = getq(lwq)) { 832 if (mp->b_datap->db_type >= QPCTL) { 833 rw_exit(&oplmsu_uinst->lock); 834 OPLMSU_TRACE(WR(lwq), mp, MSU_TRC_LO); 835 putnext(WR(lwq), mp); 836 rw_enter(&oplmsu_uinst->lock, RW_READER); 837 continue; 838 } 839 840 dst_queue = WR(lwq); 841 if (canputnext(dst_queue)) { 842 rw_exit(&oplmsu_uinst->lock); 843 OPLMSU_TRACE(dst_queue, mp, MSU_TRC_LO); 844 putnext(dst_queue, mp); 845 rw_enter(&oplmsu_uinst->lock, RW_READER); 846 } else { 847 (void) putbq(WR(lwq), mp); 848 break; 849 } 850 } 851 852 mutex_enter(&oplmsu_uinst->l_lock); 853 lpath = (lpath_t *)lwq->q_ptr; 854 if (lpath->uwq_flag != 0) { 855 qenable(WR(lpath->uwq_queue)); 856 lpath->uwq_flag = 0; 857 lpath->uwq_queue = NULL; 858 } 859 mutex_exit(&oplmsu_uinst->l_lock); 860 rw_exit(&oplmsu_uinst->lock); 861 return (SUCCESS); 862 } 863 864 /* 865 * Lower read put procedure 866 */ 867 int 868 oplmsu_lrput(queue_t *lrq, mblk_t *mp) 869 { 870 871 if (mp == NULL) { 872 return (SUCCESS); 873 } 874 875 if ((lrq == NULL) || (lrq->q_ptr == NULL)) { 876 freemsg(mp); 877 return (SUCCESS); 878 } 879 880 OPLMSU_TRACE(lrq, mp, MSU_TRC_LI); 881 882 if (mp->b_datap->db_type == M_FLUSH) { 883 rw_enter(&oplmsu_uinst->lock, RW_READER); 884 oplmsu_rcmn_flush_hndl(lrq, mp); 885 rw_exit(&oplmsu_uinst->lock); 886 } else if (mp->b_datap->db_type >= QPCTL) { 887 lpath_t *lpath; 888 889 rw_enter(&oplmsu_uinst->lock, RW_READER); 890 mutex_enter(&oplmsu_uinst->l_lock); 891 lpath = lrq->q_ptr; 892 893 /* Link high priority message to local queue */ 894 oplmsu_link_high_primsg(&lpath->first_lpri_hi, 895 &lpath->last_lpri_hi, mp); 896 897 mutex_exit(&oplmsu_uinst->l_lock); 898 rw_exit(&oplmsu_uinst->lock); 899 oplmsu_rcmn_high_qenable(lrq); 900 } else { 901 (void) putq(lrq, mp); 902 } 903 return (SUCCESS); 904 } 905 906 /* 907 * Lower read service procedure 908 */ 909 int 910 oplmsu_lrsrv(queue_t *lrq) 911 { 912 mblk_t *mp; 913 boolean_t aborted; 914 int rval; 915 916 if ((lrq == NULL) || (lrq->q_ptr == NULL)) { 917 return (FAILURE); 918 } 919 920 /* Handle normal priority message */ 921 while (mp = getq(lrq)) { 922 if (mp->b_datap->db_type >= QPCTL) { 923 cmn_err(CE_WARN, "oplmsu: lr-srv: " 924 "Invalid db_type => %x", mp->b_datap->db_type); 925 } 926 927 switch (mp->b_datap->db_type) { 928 case M_DATA : 929 aborted = B_FALSE; 930 rw_enter(&oplmsu_uinst->lock, RW_READER); 931 if ((abort_enable == KIOCABORTALTERNATE) && 932 (RD(oplmsu_uinst->lower_queue) == lrq)) { 933 uchar_t *rx_char = mp->b_rptr; 934 lpath_t *lpath; 935 936 mutex_enter(&oplmsu_uinst->l_lock); 937 lpath = lrq->q_ptr; 938 while (rx_char != mp->b_wptr) { 939 if (*rx_char == *lpath->abt_char) { 940 lpath->abt_char++; 941 if (*lpath->abt_char == '\0') { 942 abort_sequence_enter((char *) 943 NULL); 944 lpath->abt_char 945 = oplmsu_uinst->abts; 946 aborted = B_TRUE; 947 break; 948 } 949 } else { 950 lpath->abt_char = (*rx_char == 951 *oplmsu_uinst->abts) ? 952 oplmsu_uinst->abts + 1 : 953 oplmsu_uinst->abts; 954 } 955 rx_char++; 956 } 957 mutex_exit(&oplmsu_uinst->l_lock); 958 } 959 rw_exit(&oplmsu_uinst->lock); 960 961 if (aborted) { 962 freemsg(mp); 963 continue; 964 } 965 966 /* 967 * When 1st byte of the received M_DATA is XON or, 968 * 1st byte is XOFF and 2nd byte is XON. 969 */ 970 971 if ((*(mp->b_rptr) == MSU_XON) || 972 (((mp->b_wptr - mp->b_rptr) == 2) && 973 ((*(mp->b_rptr) == MSU_XOFF) && 974 (*(mp->b_rptr + 1) == MSU_XON)))) { 975 /* Path switching by XOFF/XON */ 976 if (oplmsu_lrdata_xoffxon(lrq, mp) == FAILURE) { 977 return (SUCCESS); 978 } 979 } else { 980 rw_enter(&oplmsu_uinst->lock, RW_READER); 981 rval = 982 oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM); 983 rw_exit(&oplmsu_uinst->lock); 984 985 if (rval == FAILURE) { 986 return (SUCCESS); 987 } 988 } 989 break; 990 991 case M_BREAK : 992 if ((mp->b_wptr - mp->b_rptr) == 0 && msgdsize(mp) 993 == 0) { 994 rw_enter(&oplmsu_uinst->lock, RW_READER); 995 if ((abort_enable != KIOCABORTALTERNATE) && 996 (RD(oplmsu_uinst->lower_queue) == lrq)) { 997 abort_sequence_enter((char *)NULL); 998 } 999 rw_exit(&oplmsu_uinst->lock); 1000 freemsg(mp); 1001 break; 1002 } 1003 /* FALLTHRU */ 1004 1005 default : 1006 rw_enter(&oplmsu_uinst->lock, RW_READER); 1007 (void) oplmsu_rcmn_through_hndl(lrq, mp, MSU_NORM); 1008 rw_exit(&oplmsu_uinst->lock); 1009 break; 1010 } 1011 } 1012 return (SUCCESS); 1013 } 1014 1015 /* 1016 * Upper read service procedure 1017 */ 1018 int 1019 oplmsu_ursrv(queue_t *urq) 1020 { 1021 mblk_t *mp; 1022 queue_t *dst_queue; 1023 lpath_t *lpath; 1024 ctrl_t *ctrl; 1025 int res_chk = 0; 1026 1027 rw_enter(&oplmsu_uinst->lock, RW_READER); 1028 while (mp = getq(urq)) { 1029 if (mp->b_datap->db_type >= QPCTL) { 1030 if ((mp->b_datap->db_type == M_IOCACK) || 1031 (mp->b_datap->db_type == M_IOCNAK)) { 1032 res_chk = 1; 1033 } 1034 rw_exit(&oplmsu_uinst->lock); 1035 OPLMSU_TRACE(RD(urq), mp, MSU_TRC_UO); 1036 putnext(RD(urq), mp); 1037 1038 rw_enter(&oplmsu_uinst->lock, RW_READER); 1039 mutex_enter(&oplmsu_uinst->l_lock); 1040 lpath = oplmsu_uinst->first_lpath; 1041 while (lpath) { 1042 qenable(RD(lpath->lower_queue)); 1043 lpath = lpath->l_next; 1044 } 1045 mutex_exit(&oplmsu_uinst->l_lock); 1046 1047 if (res_chk == 1) { 1048 mutex_enter(&oplmsu_uinst->c_lock); 1049 ctrl = (ctrl_t *)urq->q_ptr; 1050 if (ctrl != NULL) { 1051 if (ctrl->wait_queue != NULL) { 1052 qenable(WR(ctrl->wait_queue)); 1053 ctrl->wait_queue = NULL; 1054 } 1055 } 1056 mutex_exit(&oplmsu_uinst->c_lock); 1057 res_chk = 0; 1058 } 1059 continue; 1060 } 1061 1062 dst_queue = RD(urq); 1063 if (canputnext(dst_queue)) { 1064 rw_exit(&oplmsu_uinst->lock); 1065 OPLMSU_TRACE(dst_queue, mp, MSU_TRC_UO); 1066 putnext(dst_queue, mp); 1067 rw_enter(&oplmsu_uinst->lock, RW_READER); 1068 } else { 1069 (void) putbq(urq, mp); 1070 break; 1071 } 1072 } 1073 1074 mutex_enter(&oplmsu_uinst->c_lock); 1075 ctrl = urq->q_ptr; 1076 if (ctrl->lrq_flag != 0) { 1077 qenable(ctrl->lrq_queue); 1078 ctrl->lrq_flag = 0; 1079 ctrl->lrq_queue = NULL; 1080 } 1081 mutex_exit(&oplmsu_uinst->c_lock); 1082 rw_exit(&oplmsu_uinst->lock); 1083 return (SUCCESS); 1084 } 1085 1086 int 1087 oplmsu_open_msu(dev_info_t *dip, ldi_ident_t *lip, ldi_handle_t *lhp) 1088 { 1089 dev_t devt; 1090 int rval; 1091 1092 /* Allocate LDI identifier */ 1093 rval = ldi_ident_from_dip(dip, lip); 1094 if (rval != 0) { 1095 cmn_err(CE_WARN, "oplmsu: open-msu: " 1096 "ldi_ident_from_dip failed. errno = %d", rval); 1097 return (rval); 1098 } 1099 1100 /* Open oplmsu(meta ctrl node) */ 1101 devt = makedevice(ddi_driver_major(dip), META_NODE_MASK); 1102 rval = 1103 ldi_open_by_dev(&devt, OTYP_CHR, (FREAD|FWRITE), kcred, lhp, *lip); 1104 if (rval != 0) { 1105 cmn_err(CE_WARN, "oplmsu: open-msu: " 1106 "ldi_open_by_dev failed. errno = %d", rval); 1107 ldi_ident_release(*lip); 1108 } 1109 return (rval); 1110 } 1111 1112 int 1113 oplmsu_plink_serial(dev_info_t *dip, ldi_handle_t msu_lh, int *id) 1114 { 1115 ldi_ident_t li = NULL; 1116 ldi_handle_t lh = NULL; 1117 int param; 1118 int rval; 1119 char pathname[MSU_PATHNAME_SIZE]; 1120 char wrkbuf[MSU_PATHNAME_SIZE]; 1121 1122 /* Create physical path-name for serial */ 1123 (void) ddi_pathname(dip, wrkbuf); 1124 *(wrkbuf + strlen(wrkbuf)) = '\0'; 1125 (void) sprintf(pathname, "/devices%s:%c", wrkbuf, 1126 'a'+ ddi_get_instance(dip)); 1127 1128 /* Allocate LDI identifier */ 1129 rval = ldi_ident_from_dip(dip, &li); 1130 if (rval != 0) { 1131 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1132 "%s ldi_ident_from_dip failed. errno = %d", pathname, rval); 1133 return (rval); 1134 } 1135 1136 /* Open serial */ 1137 rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li); 1138 if (rval != 0) { 1139 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1140 "%s open failed. errno = %d", pathname, rval); 1141 ldi_ident_release(li); 1142 return (rval); 1143 } 1144 1145 /* Try to remove the top module from the stream */ 1146 param = 0; 1147 while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, ¶m)) 1148 == 0) { 1149 continue; 1150 } 1151 1152 /* Issue ioctl(I_PLINK) */ 1153 param = 0; 1154 rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, ¶m); 1155 if (rval != 0) { 1156 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1157 "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval); 1158 } 1159 1160 (void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred); 1161 ldi_ident_release(li); 1162 1163 *id = param; /* Save link-id */ 1164 return (rval); 1165 } 1166 1167 int 1168 oplmsu_set_lpathnum(int lnk_id, int instance) 1169 { 1170 lpath_t *lpath; 1171 int rval = SUCCESS; 1172 1173 rw_enter(&oplmsu_uinst->lock, RW_READER); 1174 mutex_enter(&oplmsu_uinst->l_lock); 1175 lpath = oplmsu_uinst->first_lpath; 1176 while (lpath) { 1177 if ((lpath->path_no == UNDEFINED) && 1178 (lpath->link_id == lnk_id)) { 1179 lpath->path_no = instance; /* Set instance number */ 1180 lpath->src_upath = NULL; 1181 lpath->status = MSU_SETID_NU; 1182 break; 1183 } 1184 lpath = lpath->l_next; 1185 } 1186 mutex_exit(&oplmsu_uinst->l_lock); 1187 rw_exit(&oplmsu_uinst->lock); 1188 1189 if (lpath == NULL) { 1190 rval = EINVAL; 1191 } 1192 return (rval); 1193 } 1194 1195 int 1196 oplmsu_dr_attach(dev_info_t *dip) 1197 { 1198 ldi_ident_t msu_li = NULL; 1199 ldi_handle_t msu_lh = NULL; 1200 upath_t *upath; 1201 int len; 1202 int instance; 1203 int lnk_id = 0; 1204 int param = 0; 1205 int rval; 1206 1207 /* Get instance for serial */ 1208 instance = ddi_get_instance(dip); 1209 1210 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1211 mutex_enter(&oplmsu_uinst->u_lock); 1212 1213 /* Get current number of paths */ 1214 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1215 1216 /* Check specified upath_t */ 1217 upath = oplmsu_uinst->first_upath; 1218 while (upath) { 1219 if (instance == upath->path_no) { 1220 break; 1221 } 1222 upath = upath->u_next; 1223 } 1224 mutex_exit(&oplmsu_uinst->u_lock); 1225 rw_exit(&oplmsu_uinst->lock); 1226 1227 if (upath != NULL) { 1228 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1229 "Instance %d already exist", instance); 1230 return (EINVAL); 1231 } 1232 1233 /* Open oplmsu */ 1234 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh); 1235 if (rval != 0) { 1236 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1237 "msu open failed. errno = %d", rval); 1238 return (rval); 1239 } 1240 1241 /* Connect two streams */ 1242 rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id); 1243 if (rval != 0) { 1244 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1245 "i_plink failed. errno = %d", rval); 1246 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1247 ldi_ident_release(msu_li); 1248 return (rval); 1249 } 1250 1251 rval = oplmsu_set_lpathnum(lnk_id, instance); 1252 if (rval != 0) { 1253 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1254 "Link id %d is not found", lnk_id); 1255 /* Issue ioctl(I_PUNLINK) */ 1256 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL, 1257 kcred, ¶m); 1258 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1259 ldi_ident_release(msu_li); 1260 return (rval); 1261 } 1262 1263 /* Add the path */ 1264 rval = oplmsu_config_add(dip); 1265 if (rval != 0) { 1266 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1267 "Failed to add the path. errno = %d", rval); 1268 /* Issue ioctl(I_PUNLINK) */ 1269 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL, 1270 kcred, ¶m); 1271 1272 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1273 ldi_ident_release(msu_li); 1274 return (rval); 1275 } 1276 1277 /* Start to use the path */ 1278 rval = oplmsu_config_start(instance); 1279 if (rval != 0) { 1280 struct msu_path *mpath; 1281 struct msu_dev *mdev; 1282 1283 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1284 "Failed to start the path. errno = %d", rval); 1285 1286 len = sizeof (struct msu_path) + sizeof (struct msu_dev); 1287 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP); 1288 mpath->num = 1; 1289 mdev = (struct msu_dev *)(mpath + 1); 1290 mdev->dip = dip; 1291 1292 /* Delete the path */ 1293 if ((oplmsu_config_del(mpath)) == 0) { 1294 /* Issue ioctl(I_PUNLINK) */ 1295 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, 1296 FKIOCTL, kcred, ¶m); 1297 } 1298 kmem_free(mpath, (size_t)len); 1299 } 1300 1301 /* Close oplmsu */ 1302 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1303 ldi_ident_release(msu_li); 1304 return (rval); 1305 } 1306 1307 int 1308 oplmsu_dr_detach(dev_info_t *dip) 1309 { 1310 ldi_ident_t msu_li = NULL; 1311 ldi_handle_t msu_lh = NULL; 1312 struct msu_path *mpath; 1313 struct msu_dev *mdev; 1314 upath_t *upath; 1315 lpath_t *lpath; 1316 int len; 1317 int instance; 1318 int count = 0; 1319 int param = 0; 1320 int status; 1321 int rval; 1322 1323 /* Get instance for serial */ 1324 instance = ddi_get_instance(dip); 1325 1326 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1327 mutex_enter(&oplmsu_uinst->u_lock); 1328 1329 /* Get current number of paths */ 1330 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1331 1332 rval = FAILURE; 1333 1334 /* Check specified upath_t */ 1335 upath = oplmsu_uinst->first_upath; 1336 while (upath) { 1337 if (instance == upath->path_no) { 1338 /* Save status of specified path */ 1339 status = upath->status; 1340 rval = SUCCESS; 1341 } 1342 upath = upath->u_next; 1343 count += 1; 1344 } 1345 mutex_exit(&oplmsu_uinst->u_lock); 1346 rw_exit(&oplmsu_uinst->lock); 1347 1348 if (rval == FAILURE) { 1349 if (count <= 1) { 1350 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1351 "Instance %d is last path", instance); 1352 } else { 1353 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1354 "Instance %d doesn't find", instance); 1355 } 1356 return (EINVAL); 1357 } 1358 1359 /* Check status of specified path */ 1360 if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) { 1361 /* Stop to use the path */ 1362 rval = oplmsu_config_stop(instance); 1363 if (rval != 0) { 1364 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1365 "Failed to stop the path. errno = %d", rval); 1366 return (rval); 1367 } 1368 } 1369 1370 /* Prepare to unlink the path */ 1371 rval = oplmsu_config_disc(instance); 1372 if (rval != 0) { 1373 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1374 "Failed to disconnect the path. errno = %d", rval); 1375 return (rval); 1376 } 1377 1378 rw_enter(&oplmsu_uinst->lock, RW_READER); 1379 mutex_enter(&oplmsu_uinst->l_lock); 1380 lpath = oplmsu_uinst->first_lpath; 1381 while (lpath) { 1382 if (lpath->path_no == instance) { /* Get link ID */ 1383 break; 1384 } 1385 lpath = lpath->l_next; 1386 } 1387 mutex_exit(&oplmsu_uinst->l_lock); 1388 rw_exit(&oplmsu_uinst->lock); 1389 1390 if (lpath == NULL) { 1391 cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID"); 1392 return (EINVAL); 1393 } 1394 1395 /* Open oplmsu */ 1396 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh); 1397 if (rval != 0) { 1398 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1399 "msu open failed. errno = %d", rval); 1400 return (rval); 1401 } 1402 1403 /* Issue ioctl(I_PUNLINK) */ 1404 rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL, 1405 kcred, ¶m); 1406 if (rval != 0) { 1407 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1408 "ioctl(I_PUNLINK) failed. errno = %d", rval); 1409 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1410 ldi_ident_release(msu_li); 1411 return (rval); 1412 } 1413 1414 /* Close oplmsu(meta node) */ 1415 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1416 ldi_ident_release(msu_li); 1417 1418 len = sizeof (struct msu_path) + sizeof (struct msu_dev); 1419 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP); 1420 mpath->num = 1; 1421 mdev = (struct msu_dev *)(mpath + 1); 1422 mdev->dip = dip; 1423 1424 /* Delete the path */ 1425 rval = oplmsu_config_del(mpath); 1426 if (rval != 0) { 1427 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1428 "Failed to delete the path. errno = %d", rval); 1429 } 1430 1431 kmem_free(mpath, (size_t)len); 1432 return (rval); 1433 } 1434 1435 /* 1436 * The ebus and the serial device path under a given CMU_CH chip 1437 * is expected to be always at the same address. So, it is safe 1438 * to hard-code the pathnames as below. 1439 */ 1440 #define EBUS_PATH "ebus@1" 1441 #define SERIAL_PATH "serial@14,400000" 1442 #define EBUS_SERIAL_PATH ("/" EBUS_PATH "/" SERIAL_PATH) 1443 1444 /* 1445 * Given the CMU_CH dip, find the serial device dip. 1446 */ 1447 dev_info_t * 1448 oplmsu_find_ser_dip(dev_info_t *cmuch_dip) 1449 { 1450 dev_info_t *ebus_dip; 1451 dev_info_t *ser_dip = NULL; 1452 1453 ndi_devi_enter(cmuch_dip); 1454 ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH); 1455 1456 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: " 1457 "ebus_dip = %p", (void *)ebus_dip)); 1458 1459 if (ebus_dip != NULL) { 1460 ndi_devi_enter(ebus_dip); 1461 ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH); 1462 1463 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: " 1464 "ser_dip = %p", (void *)ser_dip)); 1465 ndi_devi_exit(ebus_dip); 1466 } 1467 ndi_devi_exit(cmuch_dip); 1468 return (ser_dip); 1469 } 1470 1471 /* 1472 * Find all console related serial devices. 1473 */ 1474 int 1475 oplmsu_find_serial(ser_devl_t **ser_dl) 1476 { 1477 dev_info_t *root_dip; 1478 dev_info_t *cmuch_dip; 1479 dev_info_t *dip; 1480 ser_devl_t *wrk_ser_dl; 1481 int count = 0; 1482 char pathname[MSU_PATHNAME_SIZE]; 1483 dev_t devt; 1484 char *namep; 1485 1486 root_dip = ddi_root_node(); 1487 ndi_devi_enter(root_dip); 1488 cmuch_dip = ddi_get_child(root_dip); 1489 1490 while (cmuch_dip != NULL) { 1491 namep = ddi_binding_name(cmuch_dip); /* Get binding name */ 1492 if (namep == NULL) { 1493 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1494 continue; 1495 } 1496 1497 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep)); 1498 1499 if ((strcmp(namep, MSU_CMUCH_FF) != 0) && 1500 (strcmp(namep, MSU_CMUCH_DC) != 0)) { 1501 #ifdef DEBUG 1502 if (strcmp(namep, MSU_CMUCH_DBG) != 0) { 1503 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1504 continue; 1505 } 1506 #else 1507 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1508 continue; 1509 #endif 1510 } 1511 1512 /* 1513 * Online the cmuch_dip so that its in the right state 1514 * to get the complete path, that is both name and address. 1515 */ 1516 (void) ndi_devi_online(cmuch_dip, 0); 1517 (void) ddi_pathname(cmuch_dip, pathname); 1518 DBG_PRINT((CE_NOTE, 1519 "oplmsu: find-serial: cmu-ch path => %s", pathname)); 1520 (void) strcat(pathname, EBUS_SERIAL_PATH); 1521 1522 /* 1523 * Call ddi_pathname_to_dev_t to forceload and attach 1524 * the required drivers. 1525 */ 1526 devt = ddi_pathname_to_dev_t(pathname); 1527 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device " 1528 "dev_t = %lx", devt)); 1529 if ((devt != NODEV) && 1530 ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) { 1531 wrk_ser_dl = (ser_devl_t *) 1532 kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP); 1533 wrk_ser_dl->dip = dip; 1534 count += 1; 1535 1536 if (*ser_dl != NULL) { 1537 wrk_ser_dl->next = *ser_dl; 1538 } 1539 *ser_dl = wrk_ser_dl; 1540 } 1541 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1542 } 1543 ndi_devi_exit(root_dip); 1544 return (count); 1545 } 1546 1547 /* Configure STREAM */ 1548 void 1549 oplmsu_conf_stream(uinst_t *msu_uinst) 1550 { 1551 ldi_ident_t msu_li = NULL; 1552 ldi_handle_t msu_lh = NULL; 1553 struct msu_path *mpath; 1554 struct msu_dev *mdev; 1555 ser_devl_t *ser_dl = NULL, *next_ser_dl; 1556 int *plink_id; 1557 int size; 1558 int i; 1559 int param; 1560 int connected = 0; 1561 int devcnt = 0; 1562 int rval; 1563 1564 DBG_PRINT((CE_NOTE, 1565 "oplmsu: conf-stream: stream configuration start!")); 1566 1567 /* Find serial devices */ 1568 devcnt = oplmsu_find_serial(&ser_dl); 1569 if ((devcnt == 0) || (ser_dl == NULL)) { 1570 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1571 "Discovered serial device = %d", devcnt); 1572 return; 1573 } 1574 1575 /* Open oplmsu */ 1576 rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh); 1577 if (rval != 0) { 1578 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1579 "msu open failed. errno = %d", rval); 1580 return; 1581 } 1582 1583 size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt)); 1584 mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP); 1585 plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP); 1586 1587 mdev = (struct msu_dev *)(mpath + 1); 1588 for (i = 0; i < devcnt; i++) { 1589 /* Connect two streams */ 1590 rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]); 1591 if (rval != 0) { 1592 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1593 "i_plink failed. errno = %d", rval); 1594 next_ser_dl = ser_dl->next; 1595 kmem_free(ser_dl, sizeof (ser_devl_t)); 1596 ser_dl = next_ser_dl; 1597 continue; 1598 } 1599 1600 rval = oplmsu_set_lpathnum(plink_id[i], 1601 ddi_get_instance(ser_dl->dip)); 1602 if (rval != 0) { 1603 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1604 "Link id %d is not found", plink_id[i]); 1605 /* Issue ioctl(I_PUNLINK) */ 1606 (void) ldi_ioctl(msu_lh, I_PUNLINK, 1607 (intptr_t)plink_id[i], FKIOCTL, kcred, ¶m); 1608 next_ser_dl = ser_dl->next; 1609 kmem_free(ser_dl, sizeof (ser_devl_t)); 1610 ser_dl = next_ser_dl; 1611 continue; 1612 } 1613 1614 mdev->dip = ser_dl->dip; 1615 next_ser_dl = ser_dl->next; 1616 kmem_free(ser_dl, sizeof (ser_devl_t)); 1617 ser_dl = next_ser_dl; 1618 1619 mdev++; 1620 connected++; 1621 } 1622 1623 if (connected == 0) { 1624 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1625 "Connected paths = %d", connected); 1626 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1627 ldi_ident_release(msu_li); 1628 kmem_free(plink_id, (sizeof (int) * devcnt)); 1629 kmem_free(mpath, size); 1630 return; 1631 } 1632 1633 /* Setup all structure */ 1634 mpath->num = connected; 1635 rval = oplmsu_config_new(mpath); 1636 if (rval != 0) { 1637 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1638 "Failed to create all paths. errno = %d", rval); 1639 oplmsu_unlinks(msu_lh, plink_id, devcnt); 1640 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1641 ldi_ident_release(msu_li); 1642 kmem_free(plink_id, (sizeof (int) * devcnt)); 1643 kmem_free(mpath, size); 1644 return; 1645 } 1646 1647 /* Start to use all paths */ 1648 rval = oplmsu_config_start(MSU_PATH_ALL); 1649 if (rval != 0) { 1650 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1651 "Failed to start all paths. errno = %d", rval); 1652 1653 /* Delete the path */ 1654 rval = oplmsu_config_del(mpath); 1655 if (rval == 0) { 1656 oplmsu_unlinks(msu_lh, plink_id, devcnt); 1657 } 1658 } 1659 1660 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1661 ldi_ident_release(msu_li); 1662 kmem_free(plink_id, (sizeof (int) * devcnt)); 1663 kmem_free(mpath, size); 1664 1665 DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!")); 1666 } 1667 1668 void 1669 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt) 1670 { 1671 int i; 1672 int param = 0; 1673 1674 for (i = 0; i < devcnt; i++) { 1675 if (plink_id[i] == 0) { 1676 continue; 1677 } 1678 1679 /* Issue ioctl(I_PUNLINK) */ 1680 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i], 1681 FKIOCTL, kcred, ¶m); 1682 } 1683 } 1684 1685 void 1686 oplmsu_setup(uinst_t *msu_uinst) 1687 { 1688 1689 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!")); 1690 1691 mutex_enter(&oplmsu_bthrd_excl); 1692 if (oplmsu_conf_st == MSU_CONFIGURING) { 1693 mutex_exit(&oplmsu_bthrd_excl); 1694 oplmsu_conf_stream(msu_uinst); /* Configure stream */ 1695 mutex_enter(&oplmsu_bthrd_excl); 1696 oplmsu_conf_st = MSU_CONFIGURED; 1697 cv_broadcast(&oplmsu_conf_cv); /* Wake up from cv_wait_sig() */ 1698 } 1699 1700 if (oplmsu_bthrd_id != NULL) { 1701 oplmsu_bthrd_id = NULL; 1702 } 1703 mutex_exit(&oplmsu_bthrd_excl); 1704 1705 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!")); 1706 1707 thread_exit(); 1708 } 1709 1710 int 1711 oplmsu_create_upath(dev_info_t *dip) 1712 { 1713 upath_t *upath; 1714 lpath_t *lpath; 1715 dev_info_t *cmuch_dip; 1716 int instance; 1717 int lsb; 1718 1719 cmuch_dip = ddi_get_parent(ddi_get_parent(dip)); 1720 lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP, 1721 FAILURE); 1722 if (lsb == FAILURE) { 1723 return (lsb); 1724 } 1725 1726 instance = ddi_get_instance(dip); 1727 1728 mutex_enter(&oplmsu_uinst->l_lock); 1729 lpath = oplmsu_uinst->first_lpath; 1730 while (lpath) { 1731 if (lpath->path_no == instance) { 1732 break; 1733 } 1734 lpath = lpath->l_next; 1735 } 1736 1737 if (lpath == NULL) { 1738 mutex_exit(&oplmsu_uinst->l_lock); 1739 return (ENODEV); 1740 } 1741 1742 upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP); 1743 1744 /* 1745 * Initialize members of upath_t 1746 */ 1747 1748 upath->path_no = instance; 1749 upath->lpath = lpath; 1750 upath->ser_devcb.dip = dip; 1751 upath->ser_devcb.lsb = lsb; 1752 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY, 1753 MSU_STOP); 1754 1755 lpath->src_upath = NULL; 1756 lpath->status = MSU_EXT_NOTUSED; 1757 mutex_exit(&oplmsu_uinst->l_lock); 1758 1759 oplmsu_link_upath(upath); 1760 return (SUCCESS); 1761 } 1762 1763 /* Setup new upper instance structure */ 1764 int 1765 oplmsu_config_new(struct msu_path *mpath) 1766 { 1767 struct msu_dev *mdev; 1768 int i; 1769 int rval = SUCCESS; 1770 1771 DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called")); 1772 ASSERT(mpath); 1773 1774 if (mpath->num == 0) { 1775 cmn_err(CE_WARN, "oplmsu: conf-new: " 1776 "Number of paths = %d", mpath->num); 1777 return (EINVAL); 1778 } 1779 1780 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1781 1782 mutex_enter(&oplmsu_uinst->l_lock); 1783 rval = oplmsu_check_lpath_usable(); 1784 mutex_exit(&oplmsu_uinst->l_lock); 1785 1786 if (rval == BUSY) { /* Check whether Lower path is usable */ 1787 rw_exit(&oplmsu_uinst->lock); 1788 cmn_err(CE_WARN, "oplmsu: conf-new: " 1789 "Other processing is using this device"); 1790 return (EBUSY); 1791 } 1792 1793 /* 1794 * Because the OPLMSU instance already exists when the upper path 1795 * table exists, the configure_new processing cannot be done. 1796 */ 1797 1798 mutex_enter(&oplmsu_uinst->u_lock); 1799 1800 if ((oplmsu_uinst->first_upath != NULL) || 1801 (oplmsu_uinst->last_upath != NULL)) { 1802 mutex_exit(&oplmsu_uinst->u_lock); 1803 rw_exit(&oplmsu_uinst->lock); 1804 cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist"); 1805 return (EINVAL); 1806 } 1807 1808 /* 1809 * Because the config_new processing has already been done 1810 * if oplmsu_uinst->path_num isn't -1, this processing cannot be 1811 * continued. 1812 */ 1813 1814 if (oplmsu_uinst->path_num != UNDEFINED) { 1815 mutex_exit(&oplmsu_uinst->u_lock); 1816 rw_exit(&oplmsu_uinst->lock); 1817 cmn_err(CE_WARN, "oplmsu: conf-new: " 1818 "conf-new processing has already been completed"); 1819 return (EINVAL); 1820 } 1821 1822 /* 1823 * Only the number of specified paths makes the upper path 1824 * information tables. 1825 */ 1826 1827 mdev = (struct msu_dev *)(mpath + 1); 1828 for (i = 0; i < mpath->num; i++) { 1829 /* 1830 * Associate upper path information table with lower path 1831 * information table. 1832 * 1833 * If the upper path information table and the lower path 1834 * information table cannot be associated, the link list of 1835 * the upper path information table is released. 1836 */ 1837 rval = oplmsu_create_upath(mdev->dip); 1838 if (rval != SUCCESS) { 1839 oplmsu_delete_upath_info(); 1840 mutex_exit(&oplmsu_uinst->u_lock); 1841 rw_exit(&oplmsu_uinst->lock); 1842 cmn_err(CE_WARN, "oplmsu: conf-new: " 1843 "Failed to create upath %d", rval); 1844 return (rval); 1845 } 1846 1847 mdev++; 1848 } 1849 1850 /* 1851 * Setup members of uinst_t 1852 */ 1853 1854 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1855 oplmsu_uinst->path_num = mpath->num; 1856 oplmsu_uinst->lower_queue = NULL; 1857 mutex_exit(&oplmsu_uinst->u_lock); 1858 rw_exit(&oplmsu_uinst->lock); 1859 return (SUCCESS); 1860 } 1861 1862 /* Add path information */ 1863 int 1864 oplmsu_config_add(dev_info_t *dip) 1865 { 1866 upath_t *upath; 1867 int instance; 1868 int rval = SUCCESS; 1869 1870 DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called")); 1871 ASSERT(dip); 1872 1873 instance = ddi_get_instance(dip); 1874 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1875 1876 if (oplmsu_uinst->path_num == UNDEFINED) { 1877 rw_exit(&oplmsu_uinst->lock); 1878 cmn_err(CE_WARN, "oplmsu: conf-add: " 1879 "conf-new processing has not been completed yet"); 1880 return (EINVAL); 1881 } 1882 1883 mutex_enter(&oplmsu_uinst->u_lock); 1884 upath = oplmsu_search_upath_info(instance); 1885 if (upath != NULL) { 1886 mutex_exit(&oplmsu_uinst->u_lock); 1887 rw_exit(&oplmsu_uinst->lock); 1888 cmn_err(CE_WARN, "oplmsu: conf-add: " 1889 "Proper upath_t doesn't find"); 1890 return (EINVAL); 1891 } 1892 1893 rval = oplmsu_create_upath(dip); 1894 if (rval != SUCCESS) { 1895 mutex_exit(&oplmsu_uinst->u_lock); 1896 rw_exit(&oplmsu_uinst->lock); 1897 cmn_err(CE_WARN, "oplmsu: conf-add: " 1898 "Failed to create upath %d", rval); 1899 return (rval); 1900 } 1901 1902 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1903 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1904 mutex_exit(&oplmsu_uinst->u_lock); 1905 rw_exit(&oplmsu_uinst->lock); 1906 return (SUCCESS); 1907 } 1908 1909 /* Delete each path information */ 1910 int 1911 oplmsu_config_del(struct msu_path *mpath) 1912 { 1913 struct msu_dev *mdev; 1914 upath_t *upath; 1915 lpath_t *lpath; 1916 int rval = SUCCESS; 1917 int use_flag; 1918 int i; 1919 1920 DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called")); 1921 ASSERT(mpath); 1922 1923 mdev = (struct msu_dev *)(mpath + 1); 1924 1925 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1926 mutex_enter(&oplmsu_uinst->u_lock); 1927 for (i = 0; i < mpath->num; i++) { 1928 upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip)); 1929 if (upath == NULL) { 1930 cmn_err(CE_WARN, "oplmsu: conf-del: " 1931 "Proper upath_t doesn't find"); 1932 rval = ENODEV; 1933 mdev++; 1934 continue; 1935 } 1936 1937 lpath = upath->lpath; 1938 if (lpath == NULL) { 1939 if ((upath->traditional_status == MSU_WSTP_ACK) || 1940 (upath->traditional_status == MSU_WSTR_ACK) || 1941 (upath->traditional_status == MSU_WPTH_CHG) || 1942 (upath->traditional_status == MSU_WTCS_ACK) || 1943 (upath->traditional_status == MSU_WTMS_ACK) || 1944 (upath->traditional_status == MSU_WPPS_ACK) || 1945 (upath->traditional_status == MSU_WWSZ_ACK) || 1946 (upath->traditional_status == MSU_WCAR_ACK)) { 1947 cmn_err(CE_WARN, "oplmsu: conf-del: " 1948 "Other processing is using this device"); 1949 rval = EBUSY; 1950 mdev++; 1951 continue; 1952 } 1953 1954 if ((upath->status != MSU_PSTAT_DISCON) || 1955 (upath->traditional_status != MSU_DISCON)) { 1956 cmn_err(CE_WARN, "oplmsu: conf-del: " 1957 "Status of path is improper"); 1958 rval = EINVAL; 1959 mdev++; 1960 continue; 1961 } 1962 } else { 1963 mutex_enter(&oplmsu_uinst->l_lock); 1964 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 1965 if (use_flag == BUSY) { 1966 mutex_exit(&oplmsu_uinst->l_lock); 1967 cmn_err(CE_WARN, "oplmsu: conf-del: " 1968 "Other processing is using lower path"); 1969 rval = EBUSY; 1970 mdev++; 1971 continue; 1972 } 1973 1974 if (((upath->status != MSU_PSTAT_STOP) || 1975 (upath->traditional_status != MSU_STOP)) && 1976 ((upath->status != MSU_PSTAT_FAIL) || 1977 (upath->traditional_status != MSU_FAIL))) { 1978 oplmsu_clear_ioctl_path(lpath); 1979 mutex_exit(&oplmsu_uinst->l_lock); 1980 cmn_err(CE_WARN, "oplmsu: conf-del: " 1981 "Status of path isn't 'Offline:stop/fail'"); 1982 rval = EINVAL; 1983 mdev++; 1984 continue; 1985 } 1986 lpath->src_upath = NULL; 1987 lpath->status = MSU_SETID_NU; 1988 oplmsu_clear_ioctl_path(lpath); 1989 mutex_exit(&oplmsu_uinst->l_lock); 1990 } 1991 oplmsu_unlink_upath(upath); /* Unlink upath_t */ 1992 kmem_free(upath, sizeof (upath_t)); 1993 mdev++; 1994 } 1995 1996 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1997 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1998 mutex_exit(&oplmsu_uinst->u_lock); 1999 rw_exit(&oplmsu_uinst->lock); 2000 return (rval); 2001 } 2002 2003 /* Stop to use the path */ 2004 int 2005 oplmsu_config_stop(int pathnum) 2006 { 2007 upath_t *upath, *altn_upath; 2008 lpath_t *lpath, *altn_lpath; 2009 queue_t *stp_queue = NULL; 2010 queue_t *dst_queue = NULL; 2011 mblk_t *nmp = NULL, *fmp = NULL; 2012 ctrl_t *ctrl; 2013 int term_ioctl, term_stat; 2014 int use_flag; 2015 2016 DBG_PRINT((CE_NOTE, 2017 "oplmsu: conf-stop: config_stop(%d) called", pathnum)); 2018 2019 if (pathnum == MSU_PATH_ALL) { 2020 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2021 "All path can't be transferred to the status of " 2022 "'Offline:stop'"); 2023 return (EINVAL); 2024 } 2025 2026 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2027 mutex_enter(&oplmsu_uinst->u_lock); 2028 2029 upath = oplmsu_search_upath_info(pathnum); /* Search upath_t */ 2030 if (upath == NULL) { 2031 mutex_exit(&oplmsu_uinst->u_lock); 2032 rw_exit(&oplmsu_uinst->lock); 2033 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2034 "Proper upath_t doesn't find"); 2035 return (ENODEV); 2036 } 2037 2038 lpath = upath->lpath; 2039 if (lpath == NULL) { 2040 mutex_exit(&oplmsu_uinst->u_lock); 2041 rw_exit(&oplmsu_uinst->lock); 2042 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2043 "Proper lpath_t doesn't exist"); 2044 return (ENODEV); 2045 } 2046 2047 mutex_enter(&oplmsu_uinst->l_lock); 2048 2049 /* Check status of lpath_t */ 2050 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2051 if (use_flag == BUSY) { 2052 mutex_exit(&oplmsu_uinst->l_lock); 2053 mutex_exit(&oplmsu_uinst->u_lock); 2054 rw_exit(&oplmsu_uinst->lock); 2055 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2056 "Other processing is using lower path"); 2057 return (EBUSY); 2058 } 2059 2060 if (upath->status == MSU_PSTAT_FAIL) { 2061 oplmsu_clear_ioctl_path(lpath); 2062 mutex_exit(&oplmsu_uinst->l_lock); 2063 mutex_exit(&oplmsu_uinst->u_lock); 2064 rw_exit(&oplmsu_uinst->lock); 2065 return (EIO); 2066 } else if ((upath->status == MSU_PSTAT_STOP) && 2067 (upath->traditional_status == MSU_STOP)) { 2068 oplmsu_clear_ioctl_path(lpath); 2069 mutex_exit(&oplmsu_uinst->l_lock); 2070 mutex_exit(&oplmsu_uinst->u_lock); 2071 rw_exit(&oplmsu_uinst->lock); 2072 return (SUCCESS); 2073 } else if ((upath->status == MSU_PSTAT_STANDBY) && 2074 (upath->traditional_status == MSU_STANDBY)) { 2075 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2076 upath->status, MSU_STOP); 2077 oplmsu_clear_ioctl_path(lpath); 2078 lpath->src_upath = NULL; 2079 lpath->status = MSU_EXT_NOTUSED; 2080 2081 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2082 mutex_exit(&oplmsu_uinst->l_lock); 2083 mutex_exit(&oplmsu_uinst->u_lock); 2084 rw_exit(&oplmsu_uinst->lock); 2085 return (SUCCESS); 2086 } else if ((upath->status == MSU_PSTAT_ACTIVE) && 2087 (upath->traditional_status == MSU_ACTIVE)) { 2088 altn_upath = oplmsu_search_standby(); 2089 if (altn_upath == NULL) { /* Alternate path doesn't exist */ 2090 DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: " 2091 "Alternate upper path doesn't find")); 2092 oplmsu_clear_ioctl_path(lpath); 2093 mutex_exit(&oplmsu_uinst->l_lock); 2094 mutex_exit(&oplmsu_uinst->u_lock); 2095 rw_exit(&oplmsu_uinst->lock); 2096 return (EINVAL); 2097 } 2098 2099 if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) { 2100 oplmsu_clear_ioctl_path(lpath); 2101 mutex_exit(&oplmsu_uinst->l_lock); 2102 mutex_exit(&oplmsu_uinst->u_lock); 2103 rw_exit(&oplmsu_uinst->lock); 2104 return (ENOSR); 2105 } 2106 2107 if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) != 2108 SUCCESS) { 2109 oplmsu_clear_ioctl_path(lpath); 2110 mutex_exit(&oplmsu_uinst->l_lock); 2111 mutex_exit(&oplmsu_uinst->u_lock); 2112 rw_exit(&oplmsu_uinst->lock); 2113 freeb(fmp); 2114 return (ENOSR); 2115 } 2116 2117 altn_lpath = altn_upath->lpath; 2118 use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL); 2119 if (use_flag == BUSY) { 2120 oplmsu_clear_ioctl_path(lpath); 2121 mutex_exit(&oplmsu_uinst->l_lock); 2122 mutex_exit(&oplmsu_uinst->u_lock); 2123 rw_exit(&oplmsu_uinst->lock); 2124 2125 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2126 "Other processing is using alternate lower path"); 2127 freeb(fmp); 2128 freemsg(nmp); 2129 return (EBUSY); 2130 } 2131 2132 dst_queue = WR(altn_lpath->lower_queue); 2133 2134 /* termios is not held. Change alternate path to MSU_ACTIVE */ 2135 if (nmp == NULL) { 2136 altn_upath->traditional_status = term_stat; 2137 altn_lpath->src_upath = upath; 2138 altn_lpath->status = MSU_EXT_VOID; 2139 2140 oplmsu_uinst->lower_queue = NULL; 2141 2142 ctrl = oplmsu_uinst->user_ctrl; 2143 if (ctrl != NULL) { 2144 mutex_enter(&oplmsu_uinst->c_lock); 2145 stp_queue = WR(ctrl->queue); 2146 mutex_exit(&oplmsu_uinst->c_lock); 2147 noenable(stp_queue); 2148 oplmsu_queue_flag = 1; 2149 } 2150 2151 /* Make M_FLUSH and send to alternate path */ 2152 oplmsu_cmn_set_mflush(fmp); 2153 (void) putq(dst_queue, fmp); 2154 2155 /* Change status of alternate path */ 2156 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2157 altn_upath->status, MSU_ACTIVE); 2158 2159 oplmsu_clear_ioctl_path(altn_lpath); 2160 altn_lpath->uinst = oplmsu_uinst; 2161 altn_lpath->src_upath = NULL; 2162 altn_lpath->status = MSU_EXT_NOTUSED; 2163 2164 /* Notify of the active path changing */ 2165 (void) prom_opl_switch_console( 2166 altn_upath->ser_devcb.lsb); 2167 2168 /* Send XON to notify active path */ 2169 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2170 2171 /* Send XOFF to notify all standby paths */ 2172 oplmsu_cmn_putxoff_standby(); 2173 2174 oplmsu_uinst->lower_queue = RD(dst_queue); 2175 ctrl = oplmsu_uinst->user_ctrl; 2176 2177 /* Switch active path of oplmsu */ 2178 if (ctrl != NULL) { 2179 queue_t *altn_queue; 2180 2181 mutex_enter(&oplmsu_uinst->c_lock); 2182 altn_queue = WR(ctrl->queue); 2183 mutex_exit(&oplmsu_uinst->c_lock); 2184 2185 /* Restart queuing of user access node */ 2186 enableok(altn_queue); 2187 2188 oplmsu_queue_flag = 0; 2189 mutex_exit(&oplmsu_uinst->l_lock); 2190 mutex_exit(&oplmsu_uinst->u_lock); 2191 oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER); 2192 mutex_enter(&oplmsu_uinst->u_lock); 2193 mutex_enter(&oplmsu_uinst->l_lock); 2194 } 2195 2196 /* Stop previous active path */ 2197 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2198 upath->status, MSU_STOP); 2199 2200 lpath->uinst = NULL; 2201 lpath->src_upath = NULL; 2202 lpath->status = MSU_EXT_NOTUSED; 2203 oplmsu_clear_ioctl_path(lpath); 2204 2205 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2206 mutex_exit(&oplmsu_uinst->l_lock); 2207 mutex_exit(&oplmsu_uinst->u_lock); 2208 rw_exit(&oplmsu_uinst->lock); 2209 return (SUCCESS); 2210 } 2211 2212 /* Send termios information to alternate path */ 2213 if (canput(dst_queue)) { 2214 altn_upath->traditional_status = term_stat; 2215 altn_lpath->src_upath = upath; 2216 altn_lpath->status = MSU_EXT_VOID; 2217 2218 upath->traditional_status = MSU_WSTP_ACK; 2219 lpath->uinst = NULL; 2220 2221 oplmsu_uinst->lower_queue = NULL; 2222 2223 ctrl = oplmsu_uinst->user_ctrl; 2224 if (ctrl != NULL) { 2225 mutex_enter(&oplmsu_uinst->c_lock); 2226 stp_queue = WR(ctrl->queue); 2227 mutex_exit(&oplmsu_uinst->c_lock); 2228 noenable(stp_queue); 2229 oplmsu_queue_flag = 1; 2230 } 2231 2232 mutex_exit(&oplmsu_uinst->l_lock); 2233 mutex_exit(&oplmsu_uinst->u_lock); 2234 rw_exit(&oplmsu_uinst->lock); 2235 oplmsu_cmn_set_mflush(fmp); 2236 (void) putq(dst_queue, fmp); 2237 (void) putq(dst_queue, nmp); 2238 2239 mutex_enter(&oplmsu_uinst->l_lock); 2240 lpath->sw_flag = 1; 2241 while (lpath->sw_flag != 0) { 2242 /* Wait for the completion of path switching */ 2243 cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock); 2244 } 2245 mutex_exit(&oplmsu_uinst->l_lock); 2246 return (SUCCESS); 2247 } else { 2248 oplmsu_clear_ioctl_path(altn_lpath); 2249 oplmsu_clear_ioctl_path(lpath); 2250 mutex_exit(&oplmsu_uinst->l_lock); 2251 mutex_exit(&oplmsu_uinst->u_lock); 2252 rw_exit(&oplmsu_uinst->lock); 2253 freeb(fmp); 2254 freemsg(nmp); 2255 return (FAILURE); 2256 } 2257 /* NOTREACHED */ 2258 } else { 2259 oplmsu_clear_ioctl_path(lpath); 2260 mutex_exit(&oplmsu_uinst->l_lock); 2261 mutex_exit(&oplmsu_uinst->u_lock); 2262 rw_exit(&oplmsu_uinst->lock); 2263 2264 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2265 "Status of path is improper"); 2266 return (EINVAL); 2267 } 2268 /* NOTREACHED */ 2269 } 2270 2271 /* Start to use path */ 2272 int 2273 oplmsu_config_start(int pathnum) 2274 { 2275 upath_t *upath = NULL; 2276 lpath_t *lpath = NULL; 2277 queue_t *dst_queue, *main_rq = NULL; 2278 int msu_tty_port; 2279 2280 DBG_PRINT((CE_NOTE, 2281 "oplmsu: conf-start: config_start(%d) called", pathnum)); 2282 2283 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2284 mutex_enter(&oplmsu_uinst->u_lock); 2285 2286 if (oplmsu_get_inst_status() == INST_STAT_BUSY) { 2287 mutex_exit(&oplmsu_uinst->u_lock); 2288 rw_exit(&oplmsu_uinst->lock); 2289 return (EBUSY); 2290 } 2291 2292 if (pathnum == MSU_PATH_ALL) { 2293 (void) oplmsu_search_min_stop_path(); 2294 } 2295 2296 for (upath = oplmsu_uinst->first_upath; upath; ) { 2297 if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) { 2298 upath = upath->u_next; 2299 continue; 2300 } 2301 2302 if (upath->path_no == pathnum) { 2303 lpath = upath->lpath; 2304 if (lpath == NULL) { 2305 mutex_exit(&oplmsu_uinst->u_lock); 2306 rw_exit(&oplmsu_uinst->lock); 2307 cmn_err(CE_WARN, "oplmsu: conf-start: " 2308 "Proper lpath_t doesn't exist"); 2309 return (EINVAL); 2310 } 2311 2312 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2313 upath->status, MSU_STANDBY); 2314 2315 mutex_enter(&oplmsu_uinst->l_lock); 2316 lpath->src_upath = NULL; 2317 lpath->status = MSU_EXT_NOTUSED; 2318 mutex_exit(&oplmsu_uinst->l_lock); 2319 mutex_exit(&oplmsu_uinst->u_lock); 2320 rw_exit(&oplmsu_uinst->lock); 2321 return (SUCCESS); 2322 } 2323 2324 /* 2325 * with PATH_ALL 2326 */ 2327 lpath = upath->lpath; 2328 if (lpath == NULL) { 2329 upath = upath->u_next; 2330 2331 DBG_PRINT((CE_WARN, "oplmsu: conf-start: " 2332 "Proper lpath_t doesn't exist")); 2333 continue; 2334 } 2335 2336 msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY, 2337 oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1); 2338 2339 if (upath->ser_devcb.lsb == msu_tty_port) { 2340 /* Notify of the active path changing */ 2341 (void) prom_opl_switch_console(upath->ser_devcb.lsb); 2342 2343 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE, 2344 upath->status, MSU_ACTIVE); 2345 2346 mutex_enter(&oplmsu_uinst->l_lock); 2347 main_rq = RD(lpath->lower_queue); 2348 dst_queue = WR(lpath->lower_queue); 2349 lpath->src_upath = NULL; 2350 lpath->status = MSU_EXT_NOTUSED; 2351 lpath->uinst = oplmsu_uinst; 2352 mutex_exit(&oplmsu_uinst->l_lock); 2353 2354 /* Send XON to notify active path */ 2355 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2356 } else { 2357 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2358 upath->status, MSU_STANDBY); 2359 2360 mutex_enter(&oplmsu_uinst->l_lock); 2361 lpath->src_upath = NULL; 2362 lpath->status = MSU_EXT_NOTUSED; 2363 mutex_exit(&oplmsu_uinst->l_lock); 2364 } 2365 upath = upath->u_next; 2366 } 2367 2368 if (main_rq == NULL) { 2369 upath_t *altn_upath; 2370 lpath_t *altn_lpath; 2371 2372 altn_upath = oplmsu_search_standby(); 2373 if (altn_upath) { 2374 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2375 altn_upath->status, MSU_ACTIVE); 2376 2377 /* Notify of the active path changing */ 2378 (void) prom_opl_switch_console( 2379 altn_upath->ser_devcb.lsb); 2380 2381 altn_lpath = altn_upath->lpath; 2382 if (altn_lpath) { 2383 mutex_enter(&oplmsu_uinst->l_lock); 2384 main_rq = RD(altn_lpath->lower_queue); 2385 dst_queue = WR(altn_lpath->lower_queue); 2386 altn_lpath->src_upath = NULL; 2387 altn_lpath->status = MSU_EXT_NOTUSED; 2388 altn_lpath->uinst = oplmsu_uinst; 2389 mutex_exit(&oplmsu_uinst->l_lock); 2390 2391 /* Send XON to notify active path */ 2392 (void) oplmsu_cmn_put_xoffxon(dst_queue, 2393 MSU_XON_4); 2394 } else { 2395 cmn_err(CE_WARN, "oplmsu: conf-start: " 2396 "Proper alternate lpath_t doesn't exist"); 2397 } 2398 } else { 2399 cmn_err(CE_WARN, "oplmsu: conf-start: " 2400 "Proper alternate upath_t doesn't exist"); 2401 } 2402 } 2403 2404 mutex_enter(&oplmsu_uinst->l_lock); 2405 2406 /* Send XOFF to notify all standby paths */ 2407 oplmsu_cmn_putxoff_standby(); 2408 2409 /* Change active path of oplmsu */ 2410 oplmsu_uinst->lower_queue = main_rq; 2411 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2412 mutex_exit(&oplmsu_uinst->l_lock); 2413 mutex_exit(&oplmsu_uinst->u_lock); 2414 rw_exit(&oplmsu_uinst->lock); 2415 return (SUCCESS); 2416 } 2417 2418 /* Prepare of unlink path */ 2419 int 2420 oplmsu_config_disc(int pathnum) 2421 { 2422 upath_t *upath; 2423 lpath_t *lpath; 2424 int use_flag; 2425 2426 DBG_PRINT((CE_NOTE, 2427 "oplmsu: conf-disc: config_disc(%d) called", pathnum)); 2428 2429 rw_enter(&oplmsu_uinst->lock, RW_READER); 2430 mutex_enter(&oplmsu_uinst->u_lock); 2431 2432 upath = oplmsu_search_upath_info(pathnum); 2433 if (upath == NULL) { 2434 mutex_exit(&oplmsu_uinst->u_lock); 2435 rw_exit(&oplmsu_uinst->lock); 2436 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2437 "Proper upath_t doesn't find"); 2438 return (EINVAL); 2439 } 2440 2441 if ((upath->status == MSU_PSTAT_DISCON) || 2442 (upath->traditional_status == MSU_DISCON)) { 2443 mutex_exit(&oplmsu_uinst->u_lock); 2444 rw_exit(&oplmsu_uinst->lock); 2445 return (SUCCESS); 2446 } else if (((upath->status != MSU_PSTAT_STOP) || 2447 (upath->traditional_status != MSU_STOP)) && 2448 ((upath->status != MSU_PSTAT_FAIL) || 2449 (upath->traditional_status != MSU_FAIL))) { 2450 mutex_exit(&oplmsu_uinst->u_lock); 2451 rw_exit(&oplmsu_uinst->lock); 2452 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2453 "Status of path is improper"); 2454 return (EINVAL); 2455 } 2456 2457 lpath = upath->lpath; 2458 if (lpath == NULL) { 2459 mutex_exit(&oplmsu_uinst->u_lock); 2460 rw_exit(&oplmsu_uinst->lock); 2461 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2462 "Proper lpath_t doesn't exist"); 2463 return (ENODEV); 2464 } 2465 2466 mutex_enter(&oplmsu_uinst->l_lock); 2467 2468 /* Check lower path status */ 2469 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2470 if (use_flag == BUSY) { 2471 mutex_exit(&oplmsu_uinst->l_lock); 2472 mutex_exit(&oplmsu_uinst->u_lock); 2473 rw_exit(&oplmsu_uinst->lock); 2474 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2475 "Other processing is using lower path"); 2476 return (EBUSY); 2477 } 2478 2479 upath->status = MSU_PSTAT_STOP; 2480 upath->traditional_status = MSU_SETID; 2481 2482 oplmsu_clear_ioctl_path(lpath); 2483 mutex_exit(&oplmsu_uinst->l_lock); 2484 mutex_exit(&oplmsu_uinst->u_lock); 2485 rw_exit(&oplmsu_uinst->lock); 2486 return (SUCCESS); 2487 } 2488