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 (void) ddi_pathname(cmuch_dip, pathname); 1504 DBG_PRINT((CE_NOTE, 1505 "oplmsu: find-serial: cmu-ch path => %s", pathname)); 1506 (void) strcat(pathname, EBUS_SERIAL_PATH); 1507 1508 /* 1509 * Call ddi_pathname_to_dev_t to forceload and attach 1510 * the required drivers. 1511 */ 1512 devt = ddi_pathname_to_dev_t(pathname); 1513 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device " 1514 "dev_t = %lx", devt)); 1515 if ((devt != NODEV) && 1516 ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) { 1517 wrk_ser_dl = (ser_devl_t *) 1518 kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP); 1519 wrk_ser_dl->dip = dip; 1520 count += 1; 1521 1522 if (*ser_dl != NULL) { 1523 wrk_ser_dl->next = *ser_dl; 1524 } 1525 *ser_dl = wrk_ser_dl; 1526 } 1527 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1528 } 1529 ndi_devi_exit(root_dip, circ); 1530 return (count); 1531 } 1532 1533 /* Configure STREAM */ 1534 void 1535 oplmsu_conf_stream(uinst_t *msu_uinst) 1536 { 1537 ldi_ident_t msu_li = NULL; 1538 ldi_handle_t msu_lh = NULL; 1539 struct msu_path *mpath; 1540 struct msu_dev *mdev; 1541 ser_devl_t *ser_dl = NULL, *next_ser_dl; 1542 int *plink_id; 1543 int size; 1544 int i; 1545 int param; 1546 int connected = 0; 1547 int devcnt = 0; 1548 int rval; 1549 1550 DBG_PRINT((CE_NOTE, 1551 "oplmsu: conf-stream: stream configuration start!")); 1552 1553 /* Find serial devices */ 1554 devcnt = oplmsu_find_serial(&ser_dl); 1555 if ((devcnt == 0) || (ser_dl == NULL)) { 1556 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1557 "Discovered serial device = %d", devcnt); 1558 return; 1559 } 1560 1561 /* Open oplmsu */ 1562 rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh); 1563 if (rval != 0) { 1564 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1565 "msu open failed. errno = %d", rval); 1566 return; 1567 } 1568 1569 size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt)); 1570 mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP); 1571 plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP); 1572 1573 mdev = (struct msu_dev *)(mpath + 1); 1574 for (i = 0; i < devcnt; i++) { 1575 /* Connect two streams */ 1576 rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]); 1577 if (rval != 0) { 1578 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1579 "i_plink failed. errno = %d", rval); 1580 next_ser_dl = ser_dl->next; 1581 kmem_free(ser_dl, sizeof (ser_devl_t)); 1582 ser_dl = next_ser_dl; 1583 continue; 1584 } 1585 1586 rval = oplmsu_set_lpathnum(plink_id[i], 1587 ddi_get_instance(ser_dl->dip)); 1588 if (rval != 0) { 1589 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1590 "Link id %d is not found", plink_id[i]); 1591 /* Issue ioctl(I_PUNLINK) */ 1592 (void) ldi_ioctl(msu_lh, I_PUNLINK, 1593 (intptr_t)plink_id[i], FKIOCTL, kcred, ¶m); 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 mdev->dip = ser_dl->dip; 1601 next_ser_dl = ser_dl->next; 1602 kmem_free(ser_dl, sizeof (ser_devl_t)); 1603 ser_dl = next_ser_dl; 1604 1605 mdev++; 1606 connected++; 1607 } 1608 1609 if (connected == 0) { 1610 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1611 "Connected paths = %d", connected); 1612 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1613 ldi_ident_release(msu_li); 1614 kmem_free(plink_id, (sizeof (int) * devcnt)); 1615 kmem_free(mpath, size); 1616 return; 1617 } 1618 1619 /* Setup all structure */ 1620 mpath->num = connected; 1621 rval = oplmsu_config_new(mpath); 1622 if (rval != 0) { 1623 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1624 "Failed to create all paths. errno = %d", rval); 1625 oplmsu_unlinks(msu_lh, plink_id, devcnt); 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 /* Start to use all paths */ 1634 rval = oplmsu_config_start(MSU_PATH_ALL); 1635 if (rval != 0) { 1636 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1637 "Failed to start all paths. errno = %d", rval); 1638 1639 /* Delete the path */ 1640 rval = oplmsu_config_del(mpath); 1641 if (rval == 0) { 1642 oplmsu_unlinks(msu_lh, plink_id, devcnt); 1643 } 1644 } 1645 1646 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1647 ldi_ident_release(msu_li); 1648 kmem_free(plink_id, (sizeof (int) * devcnt)); 1649 kmem_free(mpath, size); 1650 1651 DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!")); 1652 } 1653 1654 void 1655 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt) 1656 { 1657 int i; 1658 int param = 0; 1659 1660 for (i = 0; i < devcnt; i++) { 1661 if (plink_id[i] == 0) { 1662 continue; 1663 } 1664 1665 /* Issue ioctl(I_PUNLINK) */ 1666 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i], 1667 FKIOCTL, kcred, ¶m); 1668 } 1669 } 1670 1671 void 1672 oplmsu_setup(uinst_t *msu_uinst) 1673 { 1674 1675 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!")); 1676 1677 mutex_enter(&oplmsu_bthrd_excl); 1678 if (oplmsu_conf_st == MSU_CONFIGURING) { 1679 mutex_exit(&oplmsu_bthrd_excl); 1680 oplmsu_conf_stream(msu_uinst); /* Configure stream */ 1681 mutex_enter(&oplmsu_bthrd_excl); 1682 oplmsu_conf_st = MSU_CONFIGURED; 1683 cv_broadcast(&oplmsu_conf_cv); /* Wake up from cv_wait_sig() */ 1684 } 1685 1686 if (oplmsu_bthrd_id != NULL) { 1687 oplmsu_bthrd_id = NULL; 1688 } 1689 mutex_exit(&oplmsu_bthrd_excl); 1690 1691 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!")); 1692 1693 thread_exit(); 1694 } 1695 1696 int 1697 oplmsu_create_upath(dev_info_t *dip) 1698 { 1699 upath_t *upath; 1700 lpath_t *lpath; 1701 dev_info_t *cmuch_dip; 1702 int instance; 1703 int lsb; 1704 1705 cmuch_dip = ddi_get_parent(ddi_get_parent(dip)); 1706 lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP, 1707 FAILURE); 1708 if (lsb == FAILURE) { 1709 return (lsb); 1710 } 1711 1712 instance = ddi_get_instance(dip); 1713 1714 mutex_enter(&oplmsu_uinst->l_lock); 1715 lpath = oplmsu_uinst->first_lpath; 1716 while (lpath) { 1717 if (lpath->path_no == instance) { 1718 break; 1719 } 1720 lpath = lpath->l_next; 1721 } 1722 1723 if (lpath == NULL) { 1724 mutex_exit(&oplmsu_uinst->l_lock); 1725 return (ENODEV); 1726 } 1727 1728 upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP); 1729 1730 /* 1731 * Initialize members of upath_t 1732 */ 1733 1734 upath->path_no = instance; 1735 upath->lpath = lpath; 1736 upath->ser_devcb.dip = dip; 1737 upath->ser_devcb.lsb = lsb; 1738 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY, 1739 MSU_STOP); 1740 1741 lpath->src_upath = NULL; 1742 lpath->status = MSU_EXT_NOTUSED; 1743 mutex_exit(&oplmsu_uinst->l_lock); 1744 1745 oplmsu_link_upath(upath); 1746 return (SUCCESS); 1747 } 1748 1749 /* Setup new upper instance structure */ 1750 int 1751 oplmsu_config_new(struct msu_path *mpath) 1752 { 1753 struct msu_dev *mdev; 1754 int i; 1755 int rval = SUCCESS; 1756 1757 DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called")); 1758 ASSERT(mpath); 1759 1760 if (mpath->num == 0) { 1761 cmn_err(CE_WARN, "oplmsu: conf-new: " 1762 "Number of paths = %d", mpath->num); 1763 return (EINVAL); 1764 } 1765 1766 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1767 1768 mutex_enter(&oplmsu_uinst->l_lock); 1769 rval = oplmsu_check_lpath_usable(); 1770 mutex_exit(&oplmsu_uinst->l_lock); 1771 1772 if (rval == BUSY) { /* Check whether Lower path is usable */ 1773 rw_exit(&oplmsu_uinst->lock); 1774 cmn_err(CE_WARN, "oplmsu: conf-new: " 1775 "Other processing is using this device"); 1776 return (EBUSY); 1777 } 1778 1779 /* 1780 * Because the OPLMSU instance already exists when the upper path 1781 * table exists, the configure_new processing cannot be done. 1782 */ 1783 1784 mutex_enter(&oplmsu_uinst->u_lock); 1785 1786 if ((oplmsu_uinst->first_upath != NULL) || 1787 (oplmsu_uinst->last_upath != NULL)) { 1788 mutex_exit(&oplmsu_uinst->u_lock); 1789 rw_exit(&oplmsu_uinst->lock); 1790 cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist"); 1791 return (EINVAL); 1792 } 1793 1794 /* 1795 * Because the config_new processing has already been done 1796 * if oplmsu_uinst->path_num isn't -1, this processing cannot be 1797 * continued. 1798 */ 1799 1800 if (oplmsu_uinst->path_num != UNDEFINED) { 1801 mutex_exit(&oplmsu_uinst->u_lock); 1802 rw_exit(&oplmsu_uinst->lock); 1803 cmn_err(CE_WARN, "oplmsu: conf-new: " 1804 "conf-new processing has already been completed"); 1805 return (EINVAL); 1806 } 1807 1808 /* 1809 * Only the number of specified paths makes the upper path 1810 * information tables. 1811 */ 1812 1813 mdev = (struct msu_dev *)(mpath + 1); 1814 for (i = 0; i < mpath->num; i++) { 1815 /* 1816 * Associate upper path information table with lower path 1817 * information table. 1818 * 1819 * If the upper path information table and the lower path 1820 * information table cannot be associated, the link list of 1821 * the upper path information table is released. 1822 */ 1823 rval = oplmsu_create_upath(mdev->dip); 1824 if (rval != SUCCESS) { 1825 oplmsu_delete_upath_info(); 1826 mutex_exit(&oplmsu_uinst->u_lock); 1827 rw_exit(&oplmsu_uinst->lock); 1828 cmn_err(CE_WARN, "oplmsu: conf-new: " 1829 "Failed to create upath %d", rval); 1830 return (rval); 1831 } 1832 1833 mdev++; 1834 } 1835 1836 /* 1837 * Setup members of uinst_t 1838 */ 1839 1840 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1841 oplmsu_uinst->path_num = mpath->num; 1842 oplmsu_uinst->lower_queue = NULL; 1843 mutex_exit(&oplmsu_uinst->u_lock); 1844 rw_exit(&oplmsu_uinst->lock); 1845 return (SUCCESS); 1846 } 1847 1848 /* Add path information */ 1849 int 1850 oplmsu_config_add(dev_info_t *dip) 1851 { 1852 upath_t *upath; 1853 int instance; 1854 int rval = SUCCESS; 1855 1856 DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called")); 1857 ASSERT(dip); 1858 1859 instance = ddi_get_instance(dip); 1860 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1861 1862 if (oplmsu_uinst->path_num == UNDEFINED) { 1863 rw_exit(&oplmsu_uinst->lock); 1864 cmn_err(CE_WARN, "oplmsu: conf-add: " 1865 "conf-new processing has not been completed yet"); 1866 return (EINVAL); 1867 } 1868 1869 mutex_enter(&oplmsu_uinst->u_lock); 1870 upath = oplmsu_search_upath_info(instance); 1871 if (upath != NULL) { 1872 mutex_exit(&oplmsu_uinst->u_lock); 1873 rw_exit(&oplmsu_uinst->lock); 1874 cmn_err(CE_WARN, "oplmsu: conf-add: " 1875 "Proper upath_t doesn't find"); 1876 return (EINVAL); 1877 } 1878 1879 rval = oplmsu_create_upath(dip); 1880 if (rval != SUCCESS) { 1881 mutex_exit(&oplmsu_uinst->u_lock); 1882 rw_exit(&oplmsu_uinst->lock); 1883 cmn_err(CE_WARN, "oplmsu: conf-add: " 1884 "Failed to create upath %d", rval); 1885 return (rval); 1886 } 1887 1888 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1889 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1890 mutex_exit(&oplmsu_uinst->u_lock); 1891 rw_exit(&oplmsu_uinst->lock); 1892 return (SUCCESS); 1893 } 1894 1895 /* Delete each path information */ 1896 int 1897 oplmsu_config_del(struct msu_path *mpath) 1898 { 1899 struct msu_dev *mdev; 1900 upath_t *upath; 1901 lpath_t *lpath; 1902 int rval = SUCCESS; 1903 int use_flag; 1904 int i; 1905 1906 DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called")); 1907 ASSERT(mpath); 1908 1909 mdev = (struct msu_dev *)(mpath + 1); 1910 1911 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1912 mutex_enter(&oplmsu_uinst->u_lock); 1913 for (i = 0; i < mpath->num; i++) { 1914 upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip)); 1915 if (upath == NULL) { 1916 cmn_err(CE_WARN, "oplmsu: conf-del: " 1917 "Proper upath_t doesn't find"); 1918 rval = ENODEV; 1919 mdev++; 1920 continue; 1921 } 1922 1923 lpath = upath->lpath; 1924 if (lpath == NULL) { 1925 if ((upath->traditional_status == MSU_WSTP_ACK) || 1926 (upath->traditional_status == MSU_WSTR_ACK) || 1927 (upath->traditional_status == MSU_WPTH_CHG) || 1928 (upath->traditional_status == MSU_WTCS_ACK) || 1929 (upath->traditional_status == MSU_WTMS_ACK) || 1930 (upath->traditional_status == MSU_WPPS_ACK) || 1931 (upath->traditional_status == MSU_WWSZ_ACK) || 1932 (upath->traditional_status == MSU_WCAR_ACK)) { 1933 cmn_err(CE_WARN, "oplmsu: conf-del: " 1934 "Other processing is using this device"); 1935 rval = EBUSY; 1936 mdev++; 1937 continue; 1938 } 1939 1940 if ((upath->status != MSU_PSTAT_DISCON) || 1941 (upath->traditional_status != MSU_DISCON)) { 1942 cmn_err(CE_WARN, "oplmsu: conf-del: " 1943 "Status of path is improper"); 1944 rval = EINVAL; 1945 mdev++; 1946 continue; 1947 } 1948 } else { 1949 mutex_enter(&oplmsu_uinst->l_lock); 1950 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 1951 if (use_flag == BUSY) { 1952 mutex_exit(&oplmsu_uinst->l_lock); 1953 cmn_err(CE_WARN, "oplmsu: conf-del: " 1954 "Other processing is using lower path"); 1955 rval = EBUSY; 1956 mdev++; 1957 continue; 1958 } 1959 1960 if (((upath->status != MSU_PSTAT_STOP) || 1961 (upath->traditional_status != MSU_STOP)) && 1962 ((upath->status != MSU_PSTAT_FAIL) || 1963 (upath->traditional_status != MSU_FAIL))) { 1964 oplmsu_clear_ioctl_path(lpath); 1965 mutex_exit(&oplmsu_uinst->l_lock); 1966 cmn_err(CE_WARN, "oplmsu: conf-del: " 1967 "Status of path isn't 'Offline:stop/fail'"); 1968 rval = EINVAL; 1969 mdev++; 1970 continue; 1971 } 1972 lpath->src_upath = NULL; 1973 lpath->status = MSU_SETID_NU; 1974 oplmsu_clear_ioctl_path(lpath); 1975 mutex_exit(&oplmsu_uinst->l_lock); 1976 } 1977 oplmsu_unlink_upath(upath); /* Unlink upath_t */ 1978 kmem_free(upath, sizeof (upath_t)); 1979 mdev++; 1980 } 1981 1982 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1983 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1984 mutex_exit(&oplmsu_uinst->u_lock); 1985 rw_exit(&oplmsu_uinst->lock); 1986 return (rval); 1987 } 1988 1989 /* Stop to use the path */ 1990 int 1991 oplmsu_config_stop(int pathnum) 1992 { 1993 upath_t *upath, *altn_upath; 1994 lpath_t *lpath, *altn_lpath; 1995 queue_t *stp_queue = NULL; 1996 queue_t *dst_queue = NULL; 1997 mblk_t *nmp = NULL, *fmp = NULL; 1998 ctrl_t *ctrl; 1999 int term_ioctl, term_stat; 2000 int use_flag; 2001 2002 DBG_PRINT((CE_NOTE, 2003 "oplmsu: conf-stop: config_stop(%d) called", pathnum)); 2004 2005 if (pathnum == MSU_PATH_ALL) { 2006 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2007 "All path can't be transferred to the status of " 2008 "'Offline:stop'"); 2009 return (EINVAL); 2010 } 2011 2012 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2013 mutex_enter(&oplmsu_uinst->u_lock); 2014 2015 upath = oplmsu_search_upath_info(pathnum); /* Search upath_t */ 2016 if (upath == NULL) { 2017 mutex_exit(&oplmsu_uinst->u_lock); 2018 rw_exit(&oplmsu_uinst->lock); 2019 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2020 "Proper upath_t doesn't find"); 2021 return (ENODEV); 2022 } 2023 2024 lpath = upath->lpath; 2025 if (lpath == NULL) { 2026 mutex_exit(&oplmsu_uinst->u_lock); 2027 rw_exit(&oplmsu_uinst->lock); 2028 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2029 "Proper lpath_t doesn't exist"); 2030 return (ENODEV); 2031 } 2032 2033 mutex_enter(&oplmsu_uinst->l_lock); 2034 2035 /* Check status of lpath_t */ 2036 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2037 if (use_flag == BUSY) { 2038 mutex_exit(&oplmsu_uinst->l_lock); 2039 mutex_exit(&oplmsu_uinst->u_lock); 2040 rw_exit(&oplmsu_uinst->lock); 2041 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2042 "Other processing is using lower path"); 2043 return (EBUSY); 2044 } 2045 2046 if (upath->status == MSU_PSTAT_FAIL) { 2047 oplmsu_clear_ioctl_path(lpath); 2048 mutex_exit(&oplmsu_uinst->l_lock); 2049 mutex_exit(&oplmsu_uinst->u_lock); 2050 rw_exit(&oplmsu_uinst->lock); 2051 return (EIO); 2052 } else if ((upath->status == MSU_PSTAT_STOP) && 2053 (upath->traditional_status == MSU_STOP)) { 2054 oplmsu_clear_ioctl_path(lpath); 2055 mutex_exit(&oplmsu_uinst->l_lock); 2056 mutex_exit(&oplmsu_uinst->u_lock); 2057 rw_exit(&oplmsu_uinst->lock); 2058 return (SUCCESS); 2059 } else if ((upath->status == MSU_PSTAT_STANDBY) && 2060 (upath->traditional_status == MSU_STANDBY)) { 2061 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2062 upath->status, MSU_STOP); 2063 oplmsu_clear_ioctl_path(lpath); 2064 lpath->src_upath = NULL; 2065 lpath->status = MSU_EXT_NOTUSED; 2066 2067 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2068 mutex_exit(&oplmsu_uinst->l_lock); 2069 mutex_exit(&oplmsu_uinst->u_lock); 2070 rw_exit(&oplmsu_uinst->lock); 2071 return (SUCCESS); 2072 } else if ((upath->status == MSU_PSTAT_ACTIVE) && 2073 (upath->traditional_status == MSU_ACTIVE)) { 2074 altn_upath = oplmsu_search_standby(); 2075 if (altn_upath == NULL) { /* Alternate path doesn't exist */ 2076 DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: " 2077 "Alternate upper path doesn't find")); 2078 oplmsu_clear_ioctl_path(lpath); 2079 mutex_exit(&oplmsu_uinst->l_lock); 2080 mutex_exit(&oplmsu_uinst->u_lock); 2081 rw_exit(&oplmsu_uinst->lock); 2082 return (EINVAL); 2083 } 2084 2085 if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) { 2086 oplmsu_clear_ioctl_path(lpath); 2087 mutex_exit(&oplmsu_uinst->l_lock); 2088 mutex_exit(&oplmsu_uinst->u_lock); 2089 rw_exit(&oplmsu_uinst->lock); 2090 return (ENOSR); 2091 } 2092 2093 if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) != 2094 SUCCESS) { 2095 oplmsu_clear_ioctl_path(lpath); 2096 mutex_exit(&oplmsu_uinst->l_lock); 2097 mutex_exit(&oplmsu_uinst->u_lock); 2098 rw_exit(&oplmsu_uinst->lock); 2099 freeb(fmp); 2100 return (ENOSR); 2101 } 2102 2103 altn_lpath = altn_upath->lpath; 2104 use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL); 2105 if (use_flag == BUSY) { 2106 oplmsu_clear_ioctl_path(lpath); 2107 mutex_exit(&oplmsu_uinst->l_lock); 2108 mutex_exit(&oplmsu_uinst->u_lock); 2109 rw_exit(&oplmsu_uinst->lock); 2110 2111 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2112 "Other processing is using alternate lower path"); 2113 freeb(fmp); 2114 freemsg(nmp); 2115 return (EBUSY); 2116 } 2117 2118 dst_queue = WR(altn_lpath->lower_queue); 2119 2120 /* termios is not held. Change alternate path to MSU_ACTIVE */ 2121 if (nmp == NULL) { 2122 altn_upath->traditional_status = term_stat; 2123 altn_lpath->src_upath = upath; 2124 altn_lpath->status = MSU_EXT_VOID; 2125 2126 oplmsu_uinst->lower_queue = NULL; 2127 2128 ctrl = oplmsu_uinst->user_ctrl; 2129 if (ctrl != NULL) { 2130 mutex_enter(&oplmsu_uinst->c_lock); 2131 stp_queue = WR(ctrl->queue); 2132 mutex_exit(&oplmsu_uinst->c_lock); 2133 noenable(stp_queue); 2134 oplmsu_queue_flag = 1; 2135 } 2136 2137 /* Make M_FLUSH and send to alternate path */ 2138 oplmsu_cmn_set_mflush(fmp); 2139 putq(dst_queue, fmp); 2140 2141 /* Change status of alternate path */ 2142 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2143 altn_upath->status, MSU_ACTIVE); 2144 2145 oplmsu_clear_ioctl_path(altn_lpath); 2146 altn_lpath->uinst = oplmsu_uinst; 2147 altn_lpath->src_upath = NULL; 2148 altn_lpath->status = MSU_EXT_NOTUSED; 2149 2150 /* Notify of the active path changing */ 2151 prom_opl_switch_console(altn_upath->ser_devcb.lsb); 2152 2153 /* Send XON to notify active path */ 2154 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2155 2156 /* Send XOFF to notify all standby paths */ 2157 oplmsu_cmn_putxoff_standby(); 2158 2159 oplmsu_uinst->lower_queue = RD(dst_queue); 2160 ctrl = oplmsu_uinst->user_ctrl; 2161 2162 /* Switch active path of oplmsu */ 2163 if (ctrl != NULL) { 2164 queue_t *altn_queue; 2165 2166 mutex_enter(&oplmsu_uinst->c_lock); 2167 altn_queue = WR(ctrl->queue); 2168 mutex_exit(&oplmsu_uinst->c_lock); 2169 2170 /* Restart queuing of user access node */ 2171 enableok(altn_queue); 2172 2173 oplmsu_queue_flag = 0; 2174 mutex_exit(&oplmsu_uinst->l_lock); 2175 mutex_exit(&oplmsu_uinst->u_lock); 2176 oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER); 2177 mutex_enter(&oplmsu_uinst->u_lock); 2178 mutex_enter(&oplmsu_uinst->l_lock); 2179 } 2180 2181 /* Stop previous active path */ 2182 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2183 upath->status, MSU_STOP); 2184 2185 lpath->uinst = NULL; 2186 lpath->src_upath = NULL; 2187 lpath->status = MSU_EXT_NOTUSED; 2188 oplmsu_clear_ioctl_path(lpath); 2189 2190 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2191 mutex_exit(&oplmsu_uinst->l_lock); 2192 mutex_exit(&oplmsu_uinst->u_lock); 2193 rw_exit(&oplmsu_uinst->lock); 2194 return (SUCCESS); 2195 } 2196 2197 /* Send termios information to alternate path */ 2198 if (canput(dst_queue)) { 2199 altn_upath->traditional_status = term_stat; 2200 altn_lpath->src_upath = upath; 2201 altn_lpath->status = MSU_EXT_VOID; 2202 2203 upath->traditional_status = MSU_WSTP_ACK; 2204 lpath->uinst = NULL; 2205 2206 oplmsu_uinst->lower_queue = NULL; 2207 2208 ctrl = oplmsu_uinst->user_ctrl; 2209 if (ctrl != NULL) { 2210 mutex_enter(&oplmsu_uinst->c_lock); 2211 stp_queue = WR(ctrl->queue); 2212 mutex_exit(&oplmsu_uinst->c_lock); 2213 noenable(stp_queue); 2214 oplmsu_queue_flag = 1; 2215 } 2216 2217 mutex_exit(&oplmsu_uinst->l_lock); 2218 mutex_exit(&oplmsu_uinst->u_lock); 2219 rw_exit(&oplmsu_uinst->lock); 2220 oplmsu_cmn_set_mflush(fmp); 2221 putq(dst_queue, fmp); 2222 putq(dst_queue, nmp); 2223 2224 mutex_enter(&oplmsu_uinst->l_lock); 2225 lpath->sw_flag = 1; 2226 while (lpath->sw_flag != 0) { 2227 /* Wait for the completion of path switching */ 2228 cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock); 2229 } 2230 mutex_exit(&oplmsu_uinst->l_lock); 2231 return (SUCCESS); 2232 } else { 2233 oplmsu_clear_ioctl_path(altn_lpath); 2234 oplmsu_clear_ioctl_path(lpath); 2235 mutex_exit(&oplmsu_uinst->l_lock); 2236 mutex_exit(&oplmsu_uinst->u_lock); 2237 rw_exit(&oplmsu_uinst->lock); 2238 freeb(fmp); 2239 freemsg(nmp); 2240 return (FAILURE); 2241 } 2242 /* NOTREACHED */ 2243 } else { 2244 oplmsu_clear_ioctl_path(lpath); 2245 mutex_exit(&oplmsu_uinst->l_lock); 2246 mutex_exit(&oplmsu_uinst->u_lock); 2247 rw_exit(&oplmsu_uinst->lock); 2248 2249 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2250 "Status of path is improper"); 2251 return (EINVAL); 2252 } 2253 /* NOTREACHED */ 2254 } 2255 2256 /* Start to use path */ 2257 int 2258 oplmsu_config_start(int pathnum) 2259 { 2260 upath_t *upath = NULL; 2261 lpath_t *lpath = NULL; 2262 queue_t *dst_queue, *main_rq = NULL; 2263 int msu_tty_port; 2264 2265 DBG_PRINT((CE_NOTE, 2266 "oplmsu: conf-start: config_start(%d) called", pathnum)); 2267 2268 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2269 mutex_enter(&oplmsu_uinst->u_lock); 2270 2271 if (oplmsu_get_inst_status() == INST_STAT_BUSY) { 2272 mutex_exit(&oplmsu_uinst->u_lock); 2273 rw_exit(&oplmsu_uinst->lock); 2274 return (EBUSY); 2275 } 2276 2277 if (pathnum == MSU_PATH_ALL) { 2278 (void) oplmsu_search_min_stop_path(); 2279 } 2280 2281 for (upath = oplmsu_uinst->first_upath; upath; ) { 2282 if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) { 2283 upath = upath->u_next; 2284 continue; 2285 } 2286 2287 if (upath->path_no == pathnum) { 2288 lpath = upath->lpath; 2289 if (lpath == NULL) { 2290 mutex_exit(&oplmsu_uinst->u_lock); 2291 rw_exit(&oplmsu_uinst->lock); 2292 cmn_err(CE_WARN, "oplmsu: conf-start: " 2293 "Proper lpath_t doesn't exist"); 2294 return (EINVAL); 2295 } 2296 2297 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2298 upath->status, MSU_STANDBY); 2299 2300 mutex_enter(&oplmsu_uinst->l_lock); 2301 lpath->src_upath = NULL; 2302 lpath->status = MSU_EXT_NOTUSED; 2303 mutex_exit(&oplmsu_uinst->l_lock); 2304 mutex_exit(&oplmsu_uinst->u_lock); 2305 rw_exit(&oplmsu_uinst->lock); 2306 return (SUCCESS); 2307 } 2308 2309 /* 2310 * with PATH_ALL 2311 */ 2312 lpath = upath->lpath; 2313 if (lpath == NULL) { 2314 upath = upath->u_next; 2315 2316 DBG_PRINT((CE_WARN, "oplmsu: conf-start: " 2317 "Proper lpath_t doesn't exist")); 2318 continue; 2319 } 2320 2321 msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY, 2322 oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1); 2323 2324 if (upath->ser_devcb.lsb == msu_tty_port) { 2325 /* Notify of the active path changing */ 2326 prom_opl_switch_console(upath->ser_devcb.lsb); 2327 2328 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE, 2329 upath->status, MSU_ACTIVE); 2330 2331 mutex_enter(&oplmsu_uinst->l_lock); 2332 main_rq = RD(lpath->lower_queue); 2333 dst_queue = WR(lpath->lower_queue); 2334 lpath->src_upath = NULL; 2335 lpath->status = MSU_EXT_NOTUSED; 2336 lpath->uinst = oplmsu_uinst; 2337 mutex_exit(&oplmsu_uinst->l_lock); 2338 2339 /* Send XON to notify active path */ 2340 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2341 } else { 2342 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2343 upath->status, MSU_STANDBY); 2344 2345 mutex_enter(&oplmsu_uinst->l_lock); 2346 lpath->src_upath = NULL; 2347 lpath->status = MSU_EXT_NOTUSED; 2348 mutex_exit(&oplmsu_uinst->l_lock); 2349 } 2350 upath = upath->u_next; 2351 } 2352 2353 if (main_rq == NULL) { 2354 upath_t *altn_upath; 2355 lpath_t *altn_lpath; 2356 2357 altn_upath = oplmsu_search_standby(); 2358 if (altn_upath) { 2359 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2360 altn_upath->status, MSU_ACTIVE); 2361 2362 /* Notify of the active path changing */ 2363 prom_opl_switch_console(altn_upath->ser_devcb.lsb); 2364 2365 altn_lpath = altn_upath->lpath; 2366 if (altn_lpath) { 2367 mutex_enter(&oplmsu_uinst->l_lock); 2368 main_rq = RD(altn_lpath->lower_queue); 2369 dst_queue = WR(altn_lpath->lower_queue); 2370 altn_lpath->src_upath = NULL; 2371 altn_lpath->status = MSU_EXT_NOTUSED; 2372 altn_lpath->uinst = oplmsu_uinst; 2373 mutex_exit(&oplmsu_uinst->l_lock); 2374 2375 /* Send XON to notify active path */ 2376 (void) oplmsu_cmn_put_xoffxon(dst_queue, 2377 MSU_XON_4); 2378 } else { 2379 cmn_err(CE_WARN, "oplmsu: conf-start: " 2380 "Proper alternate lpath_t doesn't exist"); 2381 } 2382 } else { 2383 cmn_err(CE_WARN, "oplmsu: conf-start: " 2384 "Proper alternate upath_t doesn't exist"); 2385 } 2386 } 2387 2388 mutex_enter(&oplmsu_uinst->l_lock); 2389 2390 /* Send XOFF to notify all standby paths */ 2391 oplmsu_cmn_putxoff_standby(); 2392 2393 /* Change active path of oplmsu */ 2394 oplmsu_uinst->lower_queue = main_rq; 2395 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2396 mutex_exit(&oplmsu_uinst->l_lock); 2397 mutex_exit(&oplmsu_uinst->u_lock); 2398 rw_exit(&oplmsu_uinst->lock); 2399 return (SUCCESS); 2400 } 2401 2402 /* Prepare of unlink path */ 2403 int 2404 oplmsu_config_disc(int pathnum) 2405 { 2406 upath_t *upath; 2407 lpath_t *lpath; 2408 int use_flag; 2409 2410 DBG_PRINT((CE_NOTE, 2411 "oplmsu: conf-disc: config_disc(%d) called", pathnum)); 2412 2413 rw_enter(&oplmsu_uinst->lock, RW_READER); 2414 mutex_enter(&oplmsu_uinst->u_lock); 2415 2416 upath = oplmsu_search_upath_info(pathnum); 2417 if (upath == NULL) { 2418 mutex_exit(&oplmsu_uinst->u_lock); 2419 rw_exit(&oplmsu_uinst->lock); 2420 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2421 "Proper upath_t doesn't find"); 2422 return (EINVAL); 2423 } 2424 2425 if ((upath->status == MSU_PSTAT_DISCON) || 2426 (upath->traditional_status == MSU_DISCON)) { 2427 mutex_exit(&oplmsu_uinst->u_lock); 2428 rw_exit(&oplmsu_uinst->lock); 2429 return (SUCCESS); 2430 } else if (((upath->status != MSU_PSTAT_STOP) || 2431 (upath->traditional_status != MSU_STOP)) && 2432 ((upath->status != MSU_PSTAT_FAIL) || 2433 (upath->traditional_status != MSU_FAIL))) { 2434 mutex_exit(&oplmsu_uinst->u_lock); 2435 rw_exit(&oplmsu_uinst->lock); 2436 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2437 "Status of path is improper"); 2438 return (EINVAL); 2439 } 2440 2441 lpath = upath->lpath; 2442 if (lpath == NULL) { 2443 mutex_exit(&oplmsu_uinst->u_lock); 2444 rw_exit(&oplmsu_uinst->lock); 2445 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2446 "Proper lpath_t doesn't exist"); 2447 return (ENODEV); 2448 } 2449 2450 mutex_enter(&oplmsu_uinst->l_lock); 2451 2452 /* Check lower path status */ 2453 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2454 if (use_flag == BUSY) { 2455 mutex_exit(&oplmsu_uinst->l_lock); 2456 mutex_exit(&oplmsu_uinst->u_lock); 2457 rw_exit(&oplmsu_uinst->lock); 2458 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2459 "Other processing is using lower path"); 2460 return (EBUSY); 2461 } 2462 2463 upath->status = MSU_PSTAT_STOP; 2464 upath->traditional_status = MSU_SETID; 2465 2466 oplmsu_clear_ioctl_path(lpath); 2467 mutex_exit(&oplmsu_uinst->l_lock); 2468 mutex_exit(&oplmsu_uinst->u_lock); 2469 rw_exit(&oplmsu_uinst->lock); 2470 return (SUCCESS); 2471 } 2472