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