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