1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 24 */ 25 26 /* 27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 32 #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 (void) 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 (void) 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 (void) 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 (void) 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 (void) 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 (void) 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 (void) ddi_pathname(dip, wrkbuf); 1121 *(wrkbuf + strlen(wrkbuf)) = '\0'; 1122 (void) sprintf(pathname, "/devices%s:%c", wrkbuf, 1123 'a'+ ddi_get_instance(dip)); 1124 1125 /* Allocate LDI identifier */ 1126 rval = ldi_ident_from_dip(dip, &li); 1127 if (rval != 0) { 1128 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1129 "%s ldi_ident_from_dip failed. errno = %d", pathname, rval); 1130 return (rval); 1131 } 1132 1133 /* Open serial */ 1134 rval = ldi_open_by_name(pathname, (FREAD|FWRITE|FEXCL), kcred, &lh, li); 1135 if (rval != 0) { 1136 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1137 "%s open failed. errno = %d", pathname, rval); 1138 ldi_ident_release(li); 1139 return (rval); 1140 } 1141 1142 /* Try to remove the top module from the stream */ 1143 param = 0; 1144 while ((ldi_ioctl(lh, I_POP, (intptr_t)0, FKIOCTL, kcred, ¶m)) 1145 == 0) { 1146 continue; 1147 } 1148 1149 /* Issue ioctl(I_PLINK) */ 1150 param = 0; 1151 rval = ldi_ioctl(msu_lh, I_PLINK, (intptr_t)lh, FKIOCTL, kcred, ¶m); 1152 if (rval != 0) { 1153 cmn_err(CE_WARN, "oplmsu: plink-serial: " 1154 "%s ioctl(I_PLINK) failed. errno = %d", pathname, rval); 1155 } 1156 1157 (void) ldi_close(lh, (FREAD|FWRITE|FEXCL), kcred); 1158 ldi_ident_release(li); 1159 1160 *id = param; /* Save link-id */ 1161 return (rval); 1162 } 1163 1164 int 1165 oplmsu_set_lpathnum(int lnk_id, int instance) 1166 { 1167 lpath_t *lpath; 1168 int rval = SUCCESS; 1169 1170 rw_enter(&oplmsu_uinst->lock, RW_READER); 1171 mutex_enter(&oplmsu_uinst->l_lock); 1172 lpath = oplmsu_uinst->first_lpath; 1173 while (lpath) { 1174 if ((lpath->path_no == UNDEFINED) && 1175 (lpath->link_id == lnk_id)) { 1176 lpath->path_no = instance; /* Set instance number */ 1177 lpath->src_upath = NULL; 1178 lpath->status = MSU_SETID_NU; 1179 break; 1180 } 1181 lpath = lpath->l_next; 1182 } 1183 mutex_exit(&oplmsu_uinst->l_lock); 1184 rw_exit(&oplmsu_uinst->lock); 1185 1186 if (lpath == NULL) { 1187 rval = EINVAL; 1188 } 1189 return (rval); 1190 } 1191 1192 int 1193 oplmsu_dr_attach(dev_info_t *dip) 1194 { 1195 ldi_ident_t msu_li = NULL; 1196 ldi_handle_t msu_lh = NULL; 1197 upath_t *upath; 1198 int len; 1199 int instance; 1200 int lnk_id = 0; 1201 int param = 0; 1202 int rval; 1203 1204 /* Get instance for serial */ 1205 instance = ddi_get_instance(dip); 1206 1207 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1208 mutex_enter(&oplmsu_uinst->u_lock); 1209 1210 /* Get current number of paths */ 1211 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1212 1213 /* Check specified upath_t */ 1214 upath = oplmsu_uinst->first_upath; 1215 while (upath) { 1216 if (instance == upath->path_no) { 1217 break; 1218 } 1219 upath = upath->u_next; 1220 } 1221 mutex_exit(&oplmsu_uinst->u_lock); 1222 rw_exit(&oplmsu_uinst->lock); 1223 1224 if (upath != NULL) { 1225 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1226 "Instance %d already exist", instance); 1227 return (EINVAL); 1228 } 1229 1230 /* Open oplmsu */ 1231 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh); 1232 if (rval != 0) { 1233 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1234 "msu open failed. errno = %d", rval); 1235 return (rval); 1236 } 1237 1238 /* Connect two streams */ 1239 rval = oplmsu_plink_serial(dip, msu_lh, &lnk_id); 1240 if (rval != 0) { 1241 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1242 "i_plink failed. errno = %d", rval); 1243 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1244 ldi_ident_release(msu_li); 1245 return (rval); 1246 } 1247 1248 rval = oplmsu_set_lpathnum(lnk_id, instance); 1249 if (rval != 0) { 1250 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1251 "Link id %d is not found", lnk_id); 1252 /* Issue ioctl(I_PUNLINK) */ 1253 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL, 1254 kcred, ¶m); 1255 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1256 ldi_ident_release(msu_li); 1257 return (rval); 1258 } 1259 1260 /* Add the path */ 1261 rval = oplmsu_config_add(dip); 1262 if (rval != 0) { 1263 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1264 "Failed to add the path. errno = %d", rval); 1265 /* Issue ioctl(I_PUNLINK) */ 1266 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, FKIOCTL, 1267 kcred, ¶m); 1268 1269 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1270 ldi_ident_release(msu_li); 1271 return (rval); 1272 } 1273 1274 /* Start to use the path */ 1275 rval = oplmsu_config_start(instance); 1276 if (rval != 0) { 1277 struct msu_path *mpath; 1278 struct msu_dev *mdev; 1279 1280 cmn_err(CE_WARN, "oplmsu: attach(dr): " 1281 "Failed to start the path. errno = %d", rval); 1282 1283 len = sizeof (struct msu_path) + sizeof (struct msu_dev); 1284 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP); 1285 mpath->num = 1; 1286 mdev = (struct msu_dev *)(mpath + 1); 1287 mdev->dip = dip; 1288 1289 /* Delete the path */ 1290 if ((oplmsu_config_del(mpath)) == 0) { 1291 /* Issue ioctl(I_PUNLINK) */ 1292 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lnk_id, 1293 FKIOCTL, kcred, ¶m); 1294 } 1295 kmem_free(mpath, (size_t)len); 1296 } 1297 1298 /* Close oplmsu */ 1299 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1300 ldi_ident_release(msu_li); 1301 return (rval); 1302 } 1303 1304 int 1305 oplmsu_dr_detach(dev_info_t *dip) 1306 { 1307 ldi_ident_t msu_li = NULL; 1308 ldi_handle_t msu_lh = NULL; 1309 struct msu_path *mpath; 1310 struct msu_dev *mdev; 1311 upath_t *upath; 1312 lpath_t *lpath; 1313 int len; 1314 int instance; 1315 int count = 0; 1316 int param = 0; 1317 int status; 1318 int rval; 1319 1320 /* Get instance for serial */ 1321 instance = ddi_get_instance(dip); 1322 1323 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1324 mutex_enter(&oplmsu_uinst->u_lock); 1325 1326 /* Get current number of paths */ 1327 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1328 1329 rval = FAILURE; 1330 1331 /* Check specified upath_t */ 1332 upath = oplmsu_uinst->first_upath; 1333 while (upath) { 1334 if (instance == upath->path_no) { 1335 /* Save status of specified path */ 1336 status = upath->status; 1337 rval = SUCCESS; 1338 } 1339 upath = upath->u_next; 1340 count += 1; 1341 } 1342 mutex_exit(&oplmsu_uinst->u_lock); 1343 rw_exit(&oplmsu_uinst->lock); 1344 1345 if (rval == FAILURE) { 1346 if (count <= 1) { 1347 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1348 "Instance %d is last path", instance); 1349 } else { 1350 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1351 "Instance %d doesn't find", instance); 1352 } 1353 return (EINVAL); 1354 } 1355 1356 /* Check status of specified path */ 1357 if ((status == MSU_PSTAT_ACTIVE) || (status == MSU_PSTAT_STANDBY)) { 1358 /* Stop to use the path */ 1359 rval = oplmsu_config_stop(instance); 1360 if (rval != 0) { 1361 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1362 "Failed to stop the path. errno = %d", rval); 1363 return (rval); 1364 } 1365 } 1366 1367 /* Prepare to unlink the path */ 1368 rval = oplmsu_config_disc(instance); 1369 if (rval != 0) { 1370 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1371 "Failed to disconnect the path. errno = %d", rval); 1372 return (rval); 1373 } 1374 1375 rw_enter(&oplmsu_uinst->lock, RW_READER); 1376 mutex_enter(&oplmsu_uinst->l_lock); 1377 lpath = oplmsu_uinst->first_lpath; 1378 while (lpath) { 1379 if (lpath->path_no == instance) { /* Get link ID */ 1380 break; 1381 } 1382 lpath = lpath->l_next; 1383 } 1384 mutex_exit(&oplmsu_uinst->l_lock); 1385 rw_exit(&oplmsu_uinst->lock); 1386 1387 if (lpath == NULL) { 1388 cmn_err(CE_WARN, "oplmsu: detach(dr): Can not find link ID"); 1389 return (EINVAL); 1390 } 1391 1392 /* Open oplmsu */ 1393 rval = oplmsu_open_msu(oplmsu_uinst->msu_dip, &msu_li, &msu_lh); 1394 if (rval != 0) { 1395 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1396 "msu open failed. errno = %d", rval); 1397 return (rval); 1398 } 1399 1400 /* Issue ioctl(I_PUNLINK) */ 1401 rval = ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)lpath->link_id, FKIOCTL, 1402 kcred, ¶m); 1403 if (rval != 0) { 1404 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1405 "ioctl(I_PUNLINK) failed. errno = %d", rval); 1406 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1407 ldi_ident_release(msu_li); 1408 return (rval); 1409 } 1410 1411 /* Close oplmsu(meta node) */ 1412 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1413 ldi_ident_release(msu_li); 1414 1415 len = sizeof (struct msu_path) + sizeof (struct msu_dev); 1416 mpath = (struct msu_path *)kmem_zalloc((size_t)len, KM_SLEEP); 1417 mpath->num = 1; 1418 mdev = (struct msu_dev *)(mpath + 1); 1419 mdev->dip = dip; 1420 1421 /* Delete the path */ 1422 rval = oplmsu_config_del(mpath); 1423 if (rval != 0) { 1424 cmn_err(CE_WARN, "oplmsu: detach(dr): " 1425 "Failed to delete the path. errno = %d", rval); 1426 } 1427 1428 kmem_free(mpath, (size_t)len); 1429 return (rval); 1430 } 1431 1432 /* 1433 * The ebus and the serial device path under a given CMU_CH chip 1434 * is expected to be always at the same address. So, it is safe 1435 * to hard-code the pathnames as below. 1436 */ 1437 #define EBUS_PATH "ebus@1" 1438 #define SERIAL_PATH "serial@14,400000" 1439 #define EBUS_SERIAL_PATH ("/" EBUS_PATH "/" SERIAL_PATH) 1440 1441 /* 1442 * Given the CMU_CH dip, find the serial device dip. 1443 */ 1444 dev_info_t * 1445 oplmsu_find_ser_dip(dev_info_t *cmuch_dip) 1446 { 1447 int circ1, circ2; 1448 dev_info_t *ebus_dip; 1449 dev_info_t *ser_dip = NULL; 1450 1451 ndi_devi_enter(cmuch_dip, &circ1); 1452 ebus_dip = ndi_devi_findchild(cmuch_dip, EBUS_PATH); 1453 1454 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: " 1455 "ebus_dip = %p", (void *)ebus_dip)); 1456 1457 if (ebus_dip != NULL) { 1458 ndi_devi_enter(ebus_dip, &circ2); 1459 ser_dip = ndi_devi_findchild(ebus_dip, SERIAL_PATH); 1460 1461 DBG_PRINT((CE_NOTE, "oplmsu: find-serial-dip: " 1462 "ser_dip = %p", (void *)ser_dip)); 1463 ndi_devi_exit(ebus_dip, circ2); 1464 } 1465 ndi_devi_exit(cmuch_dip, circ1); 1466 return (ser_dip); 1467 } 1468 1469 /* 1470 * Find all console related serial devices. 1471 */ 1472 int 1473 oplmsu_find_serial(ser_devl_t **ser_dl) 1474 { 1475 dev_info_t *root_dip; 1476 dev_info_t *cmuch_dip; 1477 dev_info_t *dip; 1478 ser_devl_t *wrk_ser_dl; 1479 int circ; 1480 int count = 0; 1481 char pathname[MSU_PATHNAME_SIZE]; 1482 dev_t devt; 1483 char *namep; 1484 1485 root_dip = ddi_root_node(); 1486 ndi_devi_enter(root_dip, &circ); 1487 cmuch_dip = ddi_get_child(root_dip); 1488 1489 while (cmuch_dip != NULL) { 1490 namep = ddi_binding_name(cmuch_dip); /* Get binding name */ 1491 if (namep == NULL) { 1492 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1493 continue; 1494 } 1495 1496 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: name => %s", namep)); 1497 1498 if ((strcmp(namep, MSU_CMUCH_FF) != 0) && 1499 (strcmp(namep, MSU_CMUCH_DC) != 0)) { 1500 #ifdef DEBUG 1501 if (strcmp(namep, MSU_CMUCH_DBG) != 0) { 1502 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1503 continue; 1504 } 1505 #else 1506 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1507 continue; 1508 #endif 1509 } 1510 1511 /* 1512 * Online the cmuch_dip so that its in the right state 1513 * to get the complete path, that is both name and address. 1514 */ 1515 (void) ndi_devi_online(cmuch_dip, 0); 1516 (void) ddi_pathname(cmuch_dip, pathname); 1517 DBG_PRINT((CE_NOTE, 1518 "oplmsu: find-serial: cmu-ch path => %s", pathname)); 1519 (void) strcat(pathname, EBUS_SERIAL_PATH); 1520 1521 /* 1522 * Call ddi_pathname_to_dev_t to forceload and attach 1523 * the required drivers. 1524 */ 1525 devt = ddi_pathname_to_dev_t(pathname); 1526 DBG_PRINT((CE_NOTE, "oplmsu: find-serial: serial device " 1527 "dev_t = %lx", devt)); 1528 if ((devt != NODEV) && 1529 ((dip = oplmsu_find_ser_dip(cmuch_dip)) != NULL)) { 1530 wrk_ser_dl = (ser_devl_t *) 1531 kmem_zalloc(sizeof (ser_devl_t), KM_SLEEP); 1532 wrk_ser_dl->dip = dip; 1533 count += 1; 1534 1535 if (*ser_dl != NULL) { 1536 wrk_ser_dl->next = *ser_dl; 1537 } 1538 *ser_dl = wrk_ser_dl; 1539 } 1540 cmuch_dip = ddi_get_next_sibling(cmuch_dip); 1541 } 1542 ndi_devi_exit(root_dip, circ); 1543 return (count); 1544 } 1545 1546 /* Configure STREAM */ 1547 void 1548 oplmsu_conf_stream(uinst_t *msu_uinst) 1549 { 1550 ldi_ident_t msu_li = NULL; 1551 ldi_handle_t msu_lh = NULL; 1552 struct msu_path *mpath; 1553 struct msu_dev *mdev; 1554 ser_devl_t *ser_dl = NULL, *next_ser_dl; 1555 int *plink_id; 1556 int size; 1557 int i; 1558 int param; 1559 int connected = 0; 1560 int devcnt = 0; 1561 int rval; 1562 1563 DBG_PRINT((CE_NOTE, 1564 "oplmsu: conf-stream: stream configuration start!")); 1565 1566 /* Find serial devices */ 1567 devcnt = oplmsu_find_serial(&ser_dl); 1568 if ((devcnt == 0) || (ser_dl == NULL)) { 1569 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1570 "Discovered serial device = %d", devcnt); 1571 return; 1572 } 1573 1574 /* Open oplmsu */ 1575 rval = oplmsu_open_msu(msu_uinst->msu_dip, &msu_li, &msu_lh); 1576 if (rval != 0) { 1577 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1578 "msu open failed. errno = %d", rval); 1579 return; 1580 } 1581 1582 size = (sizeof (struct msu_path) + (sizeof (struct msu_dev) * devcnt)); 1583 mpath = (struct msu_path *)kmem_zalloc((size_t)size, KM_SLEEP); 1584 plink_id = (int *)kmem_zalloc((sizeof (int) * devcnt), KM_SLEEP); 1585 1586 mdev = (struct msu_dev *)(mpath + 1); 1587 for (i = 0; i < devcnt; i++) { 1588 /* Connect two streams */ 1589 rval = oplmsu_plink_serial(ser_dl->dip, msu_lh, &plink_id[i]); 1590 if (rval != 0) { 1591 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1592 "i_plink failed. errno = %d", rval); 1593 next_ser_dl = ser_dl->next; 1594 kmem_free(ser_dl, sizeof (ser_devl_t)); 1595 ser_dl = next_ser_dl; 1596 continue; 1597 } 1598 1599 rval = oplmsu_set_lpathnum(plink_id[i], 1600 ddi_get_instance(ser_dl->dip)); 1601 if (rval != 0) { 1602 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1603 "Link id %d is not found", plink_id[i]); 1604 /* Issue ioctl(I_PUNLINK) */ 1605 (void) ldi_ioctl(msu_lh, I_PUNLINK, 1606 (intptr_t)plink_id[i], FKIOCTL, kcred, ¶m); 1607 next_ser_dl = ser_dl->next; 1608 kmem_free(ser_dl, sizeof (ser_devl_t)); 1609 ser_dl = next_ser_dl; 1610 continue; 1611 } 1612 1613 mdev->dip = ser_dl->dip; 1614 next_ser_dl = ser_dl->next; 1615 kmem_free(ser_dl, sizeof (ser_devl_t)); 1616 ser_dl = next_ser_dl; 1617 1618 mdev++; 1619 connected++; 1620 } 1621 1622 if (connected == 0) { 1623 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1624 "Connected paths = %d", connected); 1625 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1626 ldi_ident_release(msu_li); 1627 kmem_free(plink_id, (sizeof (int) * devcnt)); 1628 kmem_free(mpath, size); 1629 return; 1630 } 1631 1632 /* Setup all structure */ 1633 mpath->num = connected; 1634 rval = oplmsu_config_new(mpath); 1635 if (rval != 0) { 1636 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1637 "Failed to create all paths. errno = %d", rval); 1638 oplmsu_unlinks(msu_lh, plink_id, devcnt); 1639 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1640 ldi_ident_release(msu_li); 1641 kmem_free(plink_id, (sizeof (int) * devcnt)); 1642 kmem_free(mpath, size); 1643 return; 1644 } 1645 1646 /* Start to use all paths */ 1647 rval = oplmsu_config_start(MSU_PATH_ALL); 1648 if (rval != 0) { 1649 cmn_err(CE_WARN, "oplmsu: conf-stream: " 1650 "Failed to start all paths. errno = %d", rval); 1651 1652 /* Delete the path */ 1653 rval = oplmsu_config_del(mpath); 1654 if (rval == 0) { 1655 oplmsu_unlinks(msu_lh, plink_id, devcnt); 1656 } 1657 } 1658 1659 (void) ldi_close(msu_lh, (FREAD|FWRITE), kcred); 1660 ldi_ident_release(msu_li); 1661 kmem_free(plink_id, (sizeof (int) * devcnt)); 1662 kmem_free(mpath, size); 1663 1664 DBG_PRINT((CE_NOTE, "oplmsu: conf-stream: stream configuration end!")); 1665 } 1666 1667 void 1668 oplmsu_unlinks(ldi_handle_t msu_lh, int *plink_id, int devcnt) 1669 { 1670 int i; 1671 int param = 0; 1672 1673 for (i = 0; i < devcnt; i++) { 1674 if (plink_id[i] == 0) { 1675 continue; 1676 } 1677 1678 /* Issue ioctl(I_PUNLINK) */ 1679 (void) ldi_ioctl(msu_lh, I_PUNLINK, (intptr_t)plink_id[i], 1680 FKIOCTL, kcred, ¶m); 1681 } 1682 } 1683 1684 void 1685 oplmsu_setup(uinst_t *msu_uinst) 1686 { 1687 1688 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread start!")); 1689 1690 mutex_enter(&oplmsu_bthrd_excl); 1691 if (oplmsu_conf_st == MSU_CONFIGURING) { 1692 mutex_exit(&oplmsu_bthrd_excl); 1693 oplmsu_conf_stream(msu_uinst); /* Configure stream */ 1694 mutex_enter(&oplmsu_bthrd_excl); 1695 oplmsu_conf_st = MSU_CONFIGURED; 1696 cv_broadcast(&oplmsu_conf_cv); /* Wake up from cv_wait_sig() */ 1697 } 1698 1699 if (oplmsu_bthrd_id != NULL) { 1700 oplmsu_bthrd_id = NULL; 1701 } 1702 mutex_exit(&oplmsu_bthrd_excl); 1703 1704 DBG_PRINT((CE_NOTE, "oplmsu: setup: Background thread end!")); 1705 1706 thread_exit(); 1707 } 1708 1709 int 1710 oplmsu_create_upath(dev_info_t *dip) 1711 { 1712 upath_t *upath; 1713 lpath_t *lpath; 1714 dev_info_t *cmuch_dip; 1715 int instance; 1716 int lsb; 1717 1718 cmuch_dip = ddi_get_parent(ddi_get_parent(dip)); 1719 lsb = ddi_prop_get_int(DDI_DEV_T_ANY, cmuch_dip, 0, MSU_BOARD_PROP, 1720 FAILURE); 1721 if (lsb == FAILURE) { 1722 return (lsb); 1723 } 1724 1725 instance = ddi_get_instance(dip); 1726 1727 mutex_enter(&oplmsu_uinst->l_lock); 1728 lpath = oplmsu_uinst->first_lpath; 1729 while (lpath) { 1730 if (lpath->path_no == instance) { 1731 break; 1732 } 1733 lpath = lpath->l_next; 1734 } 1735 1736 if (lpath == NULL) { 1737 mutex_exit(&oplmsu_uinst->l_lock); 1738 return (ENODEV); 1739 } 1740 1741 upath = (upath_t *)kmem_zalloc(sizeof (upath_t), KM_SLEEP); 1742 1743 /* 1744 * Initialize members of upath_t 1745 */ 1746 1747 upath->path_no = instance; 1748 upath->lpath = lpath; 1749 upath->ser_devcb.dip = dip; 1750 upath->ser_devcb.lsb = lsb; 1751 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, MSU_PSTAT_EMPTY, 1752 MSU_STOP); 1753 1754 lpath->src_upath = NULL; 1755 lpath->status = MSU_EXT_NOTUSED; 1756 mutex_exit(&oplmsu_uinst->l_lock); 1757 1758 oplmsu_link_upath(upath); 1759 return (SUCCESS); 1760 } 1761 1762 /* Setup new upper instance structure */ 1763 int 1764 oplmsu_config_new(struct msu_path *mpath) 1765 { 1766 struct msu_dev *mdev; 1767 int i; 1768 int rval = SUCCESS; 1769 1770 DBG_PRINT((CE_NOTE, "oplmsu: conf-new: config_new() called")); 1771 ASSERT(mpath); 1772 1773 if (mpath->num == 0) { 1774 cmn_err(CE_WARN, "oplmsu: conf-new: " 1775 "Number of paths = %d", mpath->num); 1776 return (EINVAL); 1777 } 1778 1779 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1780 1781 mutex_enter(&oplmsu_uinst->l_lock); 1782 rval = oplmsu_check_lpath_usable(); 1783 mutex_exit(&oplmsu_uinst->l_lock); 1784 1785 if (rval == BUSY) { /* Check whether Lower path is usable */ 1786 rw_exit(&oplmsu_uinst->lock); 1787 cmn_err(CE_WARN, "oplmsu: conf-new: " 1788 "Other processing is using this device"); 1789 return (EBUSY); 1790 } 1791 1792 /* 1793 * Because the OPLMSU instance already exists when the upper path 1794 * table exists, the configure_new processing cannot be done. 1795 */ 1796 1797 mutex_enter(&oplmsu_uinst->u_lock); 1798 1799 if ((oplmsu_uinst->first_upath != NULL) || 1800 (oplmsu_uinst->last_upath != NULL)) { 1801 mutex_exit(&oplmsu_uinst->u_lock); 1802 rw_exit(&oplmsu_uinst->lock); 1803 cmn_err(CE_WARN, "oplmsu: conf-new: upath_t already exist"); 1804 return (EINVAL); 1805 } 1806 1807 /* 1808 * Because the config_new processing has already been done 1809 * if oplmsu_uinst->path_num isn't -1, this processing cannot be 1810 * continued. 1811 */ 1812 1813 if (oplmsu_uinst->path_num != UNDEFINED) { 1814 mutex_exit(&oplmsu_uinst->u_lock); 1815 rw_exit(&oplmsu_uinst->lock); 1816 cmn_err(CE_WARN, "oplmsu: conf-new: " 1817 "conf-new processing has already been completed"); 1818 return (EINVAL); 1819 } 1820 1821 /* 1822 * Only the number of specified paths makes the upper path 1823 * information tables. 1824 */ 1825 1826 mdev = (struct msu_dev *)(mpath + 1); 1827 for (i = 0; i < mpath->num; i++) { 1828 /* 1829 * Associate upper path information table with lower path 1830 * information table. 1831 * 1832 * If the upper path information table and the lower path 1833 * information table cannot be associated, the link list of 1834 * the upper path information table is released. 1835 */ 1836 rval = oplmsu_create_upath(mdev->dip); 1837 if (rval != SUCCESS) { 1838 oplmsu_delete_upath_info(); 1839 mutex_exit(&oplmsu_uinst->u_lock); 1840 rw_exit(&oplmsu_uinst->lock); 1841 cmn_err(CE_WARN, "oplmsu: conf-new: " 1842 "Failed to create upath %d", rval); 1843 return (rval); 1844 } 1845 1846 mdev++; 1847 } 1848 1849 /* 1850 * Setup members of uinst_t 1851 */ 1852 1853 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1854 oplmsu_uinst->path_num = mpath->num; 1855 oplmsu_uinst->lower_queue = NULL; 1856 mutex_exit(&oplmsu_uinst->u_lock); 1857 rw_exit(&oplmsu_uinst->lock); 1858 return (SUCCESS); 1859 } 1860 1861 /* Add path information */ 1862 int 1863 oplmsu_config_add(dev_info_t *dip) 1864 { 1865 upath_t *upath; 1866 int instance; 1867 int rval = SUCCESS; 1868 1869 DBG_PRINT((CE_NOTE, "oplmsu: conf-add: config_add() called")); 1870 ASSERT(dip); 1871 1872 instance = ddi_get_instance(dip); 1873 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1874 1875 if (oplmsu_uinst->path_num == UNDEFINED) { 1876 rw_exit(&oplmsu_uinst->lock); 1877 cmn_err(CE_WARN, "oplmsu: conf-add: " 1878 "conf-new processing has not been completed yet"); 1879 return (EINVAL); 1880 } 1881 1882 mutex_enter(&oplmsu_uinst->u_lock); 1883 upath = oplmsu_search_upath_info(instance); 1884 if (upath != NULL) { 1885 mutex_exit(&oplmsu_uinst->u_lock); 1886 rw_exit(&oplmsu_uinst->lock); 1887 cmn_err(CE_WARN, "oplmsu: conf-add: " 1888 "Proper upath_t doesn't find"); 1889 return (EINVAL); 1890 } 1891 1892 rval = oplmsu_create_upath(dip); 1893 if (rval != SUCCESS) { 1894 mutex_exit(&oplmsu_uinst->u_lock); 1895 rw_exit(&oplmsu_uinst->lock); 1896 cmn_err(CE_WARN, "oplmsu: conf-add: " 1897 "Failed to create upath %d", rval); 1898 return (rval); 1899 } 1900 1901 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1902 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1903 mutex_exit(&oplmsu_uinst->u_lock); 1904 rw_exit(&oplmsu_uinst->lock); 1905 return (SUCCESS); 1906 } 1907 1908 /* Delete each path information */ 1909 int 1910 oplmsu_config_del(struct msu_path *mpath) 1911 { 1912 struct msu_dev *mdev; 1913 upath_t *upath; 1914 lpath_t *lpath; 1915 int rval = SUCCESS; 1916 int use_flag; 1917 int i; 1918 1919 DBG_PRINT((CE_NOTE, "oplmsu: conf-del: config_del() called")); 1920 ASSERT(mpath); 1921 1922 mdev = (struct msu_dev *)(mpath + 1); 1923 1924 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 1925 mutex_enter(&oplmsu_uinst->u_lock); 1926 for (i = 0; i < mpath->num; i++) { 1927 upath = oplmsu_search_upath_info(ddi_get_instance(mdev->dip)); 1928 if (upath == NULL) { 1929 cmn_err(CE_WARN, "oplmsu: conf-del: " 1930 "Proper upath_t doesn't find"); 1931 rval = ENODEV; 1932 mdev++; 1933 continue; 1934 } 1935 1936 lpath = upath->lpath; 1937 if (lpath == NULL) { 1938 if ((upath->traditional_status == MSU_WSTP_ACK) || 1939 (upath->traditional_status == MSU_WSTR_ACK) || 1940 (upath->traditional_status == MSU_WPTH_CHG) || 1941 (upath->traditional_status == MSU_WTCS_ACK) || 1942 (upath->traditional_status == MSU_WTMS_ACK) || 1943 (upath->traditional_status == MSU_WPPS_ACK) || 1944 (upath->traditional_status == MSU_WWSZ_ACK) || 1945 (upath->traditional_status == MSU_WCAR_ACK)) { 1946 cmn_err(CE_WARN, "oplmsu: conf-del: " 1947 "Other processing is using this device"); 1948 rval = EBUSY; 1949 mdev++; 1950 continue; 1951 } 1952 1953 if ((upath->status != MSU_PSTAT_DISCON) || 1954 (upath->traditional_status != MSU_DISCON)) { 1955 cmn_err(CE_WARN, "oplmsu: conf-del: " 1956 "Status of path is improper"); 1957 rval = EINVAL; 1958 mdev++; 1959 continue; 1960 } 1961 } else { 1962 mutex_enter(&oplmsu_uinst->l_lock); 1963 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 1964 if (use_flag == BUSY) { 1965 mutex_exit(&oplmsu_uinst->l_lock); 1966 cmn_err(CE_WARN, "oplmsu: conf-del: " 1967 "Other processing is using lower path"); 1968 rval = EBUSY; 1969 mdev++; 1970 continue; 1971 } 1972 1973 if (((upath->status != MSU_PSTAT_STOP) || 1974 (upath->traditional_status != MSU_STOP)) && 1975 ((upath->status != MSU_PSTAT_FAIL) || 1976 (upath->traditional_status != MSU_FAIL))) { 1977 oplmsu_clear_ioctl_path(lpath); 1978 mutex_exit(&oplmsu_uinst->l_lock); 1979 cmn_err(CE_WARN, "oplmsu: conf-del: " 1980 "Status of path isn't 'Offline:stop/fail'"); 1981 rval = EINVAL; 1982 mdev++; 1983 continue; 1984 } 1985 lpath->src_upath = NULL; 1986 lpath->status = MSU_SETID_NU; 1987 oplmsu_clear_ioctl_path(lpath); 1988 mutex_exit(&oplmsu_uinst->l_lock); 1989 } 1990 oplmsu_unlink_upath(upath); /* Unlink upath_t */ 1991 kmem_free(upath, sizeof (upath_t)); 1992 mdev++; 1993 } 1994 1995 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 1996 oplmsu_uinst->path_num = oplmsu_get_pathnum(); 1997 mutex_exit(&oplmsu_uinst->u_lock); 1998 rw_exit(&oplmsu_uinst->lock); 1999 return (rval); 2000 } 2001 2002 /* Stop to use the path */ 2003 int 2004 oplmsu_config_stop(int pathnum) 2005 { 2006 upath_t *upath, *altn_upath; 2007 lpath_t *lpath, *altn_lpath; 2008 queue_t *stp_queue = NULL; 2009 queue_t *dst_queue = NULL; 2010 mblk_t *nmp = NULL, *fmp = NULL; 2011 ctrl_t *ctrl; 2012 int term_ioctl, term_stat; 2013 int use_flag; 2014 2015 DBG_PRINT((CE_NOTE, 2016 "oplmsu: conf-stop: config_stop(%d) called", pathnum)); 2017 2018 if (pathnum == MSU_PATH_ALL) { 2019 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2020 "All path can't be transferred to the status of " 2021 "'Offline:stop'"); 2022 return (EINVAL); 2023 } 2024 2025 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2026 mutex_enter(&oplmsu_uinst->u_lock); 2027 2028 upath = oplmsu_search_upath_info(pathnum); /* Search upath_t */ 2029 if (upath == NULL) { 2030 mutex_exit(&oplmsu_uinst->u_lock); 2031 rw_exit(&oplmsu_uinst->lock); 2032 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2033 "Proper upath_t doesn't find"); 2034 return (ENODEV); 2035 } 2036 2037 lpath = upath->lpath; 2038 if (lpath == NULL) { 2039 mutex_exit(&oplmsu_uinst->u_lock); 2040 rw_exit(&oplmsu_uinst->lock); 2041 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2042 "Proper lpath_t doesn't exist"); 2043 return (ENODEV); 2044 } 2045 2046 mutex_enter(&oplmsu_uinst->l_lock); 2047 2048 /* Check status of lpath_t */ 2049 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2050 if (use_flag == BUSY) { 2051 mutex_exit(&oplmsu_uinst->l_lock); 2052 mutex_exit(&oplmsu_uinst->u_lock); 2053 rw_exit(&oplmsu_uinst->lock); 2054 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2055 "Other processing is using lower path"); 2056 return (EBUSY); 2057 } 2058 2059 if (upath->status == MSU_PSTAT_FAIL) { 2060 oplmsu_clear_ioctl_path(lpath); 2061 mutex_exit(&oplmsu_uinst->l_lock); 2062 mutex_exit(&oplmsu_uinst->u_lock); 2063 rw_exit(&oplmsu_uinst->lock); 2064 return (EIO); 2065 } else if ((upath->status == MSU_PSTAT_STOP) && 2066 (upath->traditional_status == MSU_STOP)) { 2067 oplmsu_clear_ioctl_path(lpath); 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_STANDBY) && 2073 (upath->traditional_status == MSU_STANDBY)) { 2074 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2075 upath->status, MSU_STOP); 2076 oplmsu_clear_ioctl_path(lpath); 2077 lpath->src_upath = NULL; 2078 lpath->status = MSU_EXT_NOTUSED; 2079 2080 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2081 mutex_exit(&oplmsu_uinst->l_lock); 2082 mutex_exit(&oplmsu_uinst->u_lock); 2083 rw_exit(&oplmsu_uinst->lock); 2084 return (SUCCESS); 2085 } else if ((upath->status == MSU_PSTAT_ACTIVE) && 2086 (upath->traditional_status == MSU_ACTIVE)) { 2087 altn_upath = oplmsu_search_standby(); 2088 if (altn_upath == NULL) { /* Alternate path doesn't exist */ 2089 DBG_PRINT((CE_NOTE, "oplmsu: conf-stop: " 2090 "Alternate upper path doesn't find")); 2091 oplmsu_clear_ioctl_path(lpath); 2092 mutex_exit(&oplmsu_uinst->l_lock); 2093 mutex_exit(&oplmsu_uinst->u_lock); 2094 rw_exit(&oplmsu_uinst->lock); 2095 return (EINVAL); 2096 } 2097 2098 if ((fmp = allocb(sizeof (char), BPRI_LO)) == NULL) { 2099 oplmsu_clear_ioctl_path(lpath); 2100 mutex_exit(&oplmsu_uinst->l_lock); 2101 mutex_exit(&oplmsu_uinst->u_lock); 2102 rw_exit(&oplmsu_uinst->lock); 2103 return (ENOSR); 2104 } 2105 2106 if (oplmsu_stop_prechg(&nmp, &term_ioctl, &term_stat) != 2107 SUCCESS) { 2108 oplmsu_clear_ioctl_path(lpath); 2109 mutex_exit(&oplmsu_uinst->l_lock); 2110 mutex_exit(&oplmsu_uinst->u_lock); 2111 rw_exit(&oplmsu_uinst->lock); 2112 freeb(fmp); 2113 return (ENOSR); 2114 } 2115 2116 altn_lpath = altn_upath->lpath; 2117 use_flag = oplmsu_set_ioctl_path(altn_lpath, NULL, NULL); 2118 if (use_flag == BUSY) { 2119 oplmsu_clear_ioctl_path(lpath); 2120 mutex_exit(&oplmsu_uinst->l_lock); 2121 mutex_exit(&oplmsu_uinst->u_lock); 2122 rw_exit(&oplmsu_uinst->lock); 2123 2124 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2125 "Other processing is using alternate lower path"); 2126 freeb(fmp); 2127 freemsg(nmp); 2128 return (EBUSY); 2129 } 2130 2131 dst_queue = WR(altn_lpath->lower_queue); 2132 2133 /* termios is not held. Change alternate path to MSU_ACTIVE */ 2134 if (nmp == NULL) { 2135 altn_upath->traditional_status = term_stat; 2136 altn_lpath->src_upath = upath; 2137 altn_lpath->status = MSU_EXT_VOID; 2138 2139 oplmsu_uinst->lower_queue = NULL; 2140 2141 ctrl = oplmsu_uinst->user_ctrl; 2142 if (ctrl != NULL) { 2143 mutex_enter(&oplmsu_uinst->c_lock); 2144 stp_queue = WR(ctrl->queue); 2145 mutex_exit(&oplmsu_uinst->c_lock); 2146 noenable(stp_queue); 2147 oplmsu_queue_flag = 1; 2148 } 2149 2150 /* Make M_FLUSH and send to alternate path */ 2151 oplmsu_cmn_set_mflush(fmp); 2152 (void) putq(dst_queue, fmp); 2153 2154 /* Change status of alternate path */ 2155 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2156 altn_upath->status, MSU_ACTIVE); 2157 2158 oplmsu_clear_ioctl_path(altn_lpath); 2159 altn_lpath->uinst = oplmsu_uinst; 2160 altn_lpath->src_upath = NULL; 2161 altn_lpath->status = MSU_EXT_NOTUSED; 2162 2163 /* Notify of the active path changing */ 2164 (void) prom_opl_switch_console( 2165 altn_upath->ser_devcb.lsb); 2166 2167 /* Send XON to notify active path */ 2168 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2169 2170 /* Send XOFF to notify all standby paths */ 2171 oplmsu_cmn_putxoff_standby(); 2172 2173 oplmsu_uinst->lower_queue = RD(dst_queue); 2174 ctrl = oplmsu_uinst->user_ctrl; 2175 2176 /* Switch active path of oplmsu */ 2177 if (ctrl != NULL) { 2178 queue_t *altn_queue; 2179 2180 mutex_enter(&oplmsu_uinst->c_lock); 2181 altn_queue = WR(ctrl->queue); 2182 mutex_exit(&oplmsu_uinst->c_lock); 2183 2184 /* Restart queuing of user access node */ 2185 enableok(altn_queue); 2186 2187 oplmsu_queue_flag = 0; 2188 mutex_exit(&oplmsu_uinst->l_lock); 2189 mutex_exit(&oplmsu_uinst->u_lock); 2190 oplmsu_wcmn_high_qenable(altn_queue, RW_WRITER); 2191 mutex_enter(&oplmsu_uinst->u_lock); 2192 mutex_enter(&oplmsu_uinst->l_lock); 2193 } 2194 2195 /* Stop previous active path */ 2196 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STOP, 2197 upath->status, MSU_STOP); 2198 2199 lpath->uinst = NULL; 2200 lpath->src_upath = NULL; 2201 lpath->status = MSU_EXT_NOTUSED; 2202 oplmsu_clear_ioctl_path(lpath); 2203 2204 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2205 mutex_exit(&oplmsu_uinst->l_lock); 2206 mutex_exit(&oplmsu_uinst->u_lock); 2207 rw_exit(&oplmsu_uinst->lock); 2208 return (SUCCESS); 2209 } 2210 2211 /* Send termios information to alternate path */ 2212 if (canput(dst_queue)) { 2213 altn_upath->traditional_status = term_stat; 2214 altn_lpath->src_upath = upath; 2215 altn_lpath->status = MSU_EXT_VOID; 2216 2217 upath->traditional_status = MSU_WSTP_ACK; 2218 lpath->uinst = NULL; 2219 2220 oplmsu_uinst->lower_queue = NULL; 2221 2222 ctrl = oplmsu_uinst->user_ctrl; 2223 if (ctrl != NULL) { 2224 mutex_enter(&oplmsu_uinst->c_lock); 2225 stp_queue = WR(ctrl->queue); 2226 mutex_exit(&oplmsu_uinst->c_lock); 2227 noenable(stp_queue); 2228 oplmsu_queue_flag = 1; 2229 } 2230 2231 mutex_exit(&oplmsu_uinst->l_lock); 2232 mutex_exit(&oplmsu_uinst->u_lock); 2233 rw_exit(&oplmsu_uinst->lock); 2234 oplmsu_cmn_set_mflush(fmp); 2235 (void) putq(dst_queue, fmp); 2236 (void) putq(dst_queue, nmp); 2237 2238 mutex_enter(&oplmsu_uinst->l_lock); 2239 lpath->sw_flag = 1; 2240 while (lpath->sw_flag != 0) { 2241 /* Wait for the completion of path switching */ 2242 cv_wait(&lpath->sw_cv, &oplmsu_uinst->l_lock); 2243 } 2244 mutex_exit(&oplmsu_uinst->l_lock); 2245 return (SUCCESS); 2246 } else { 2247 oplmsu_clear_ioctl_path(altn_lpath); 2248 oplmsu_clear_ioctl_path(lpath); 2249 mutex_exit(&oplmsu_uinst->l_lock); 2250 mutex_exit(&oplmsu_uinst->u_lock); 2251 rw_exit(&oplmsu_uinst->lock); 2252 freeb(fmp); 2253 freemsg(nmp); 2254 return (FAILURE); 2255 } 2256 /* NOTREACHED */ 2257 } else { 2258 oplmsu_clear_ioctl_path(lpath); 2259 mutex_exit(&oplmsu_uinst->l_lock); 2260 mutex_exit(&oplmsu_uinst->u_lock); 2261 rw_exit(&oplmsu_uinst->lock); 2262 2263 cmn_err(CE_WARN, "oplmsu: conf-stop: " 2264 "Status of path is improper"); 2265 return (EINVAL); 2266 } 2267 /* NOTREACHED */ 2268 } 2269 2270 /* Start to use path */ 2271 int 2272 oplmsu_config_start(int pathnum) 2273 { 2274 upath_t *upath = NULL; 2275 lpath_t *lpath = NULL; 2276 queue_t *dst_queue, *main_rq = NULL; 2277 int msu_tty_port; 2278 2279 DBG_PRINT((CE_NOTE, 2280 "oplmsu: conf-start: config_start(%d) called", pathnum)); 2281 2282 rw_enter(&oplmsu_uinst->lock, RW_WRITER); 2283 mutex_enter(&oplmsu_uinst->u_lock); 2284 2285 if (oplmsu_get_inst_status() == INST_STAT_BUSY) { 2286 mutex_exit(&oplmsu_uinst->u_lock); 2287 rw_exit(&oplmsu_uinst->lock); 2288 return (EBUSY); 2289 } 2290 2291 if (pathnum == MSU_PATH_ALL) { 2292 (void) oplmsu_search_min_stop_path(); 2293 } 2294 2295 for (upath = oplmsu_uinst->first_upath; upath; ) { 2296 if ((pathnum != MSU_PATH_ALL) && (upath->path_no != pathnum)) { 2297 upath = upath->u_next; 2298 continue; 2299 } 2300 2301 if (upath->path_no == pathnum) { 2302 lpath = upath->lpath; 2303 if (lpath == NULL) { 2304 mutex_exit(&oplmsu_uinst->u_lock); 2305 rw_exit(&oplmsu_uinst->lock); 2306 cmn_err(CE_WARN, "oplmsu: conf-start: " 2307 "Proper lpath_t doesn't exist"); 2308 return (EINVAL); 2309 } 2310 2311 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2312 upath->status, MSU_STANDBY); 2313 2314 mutex_enter(&oplmsu_uinst->l_lock); 2315 lpath->src_upath = NULL; 2316 lpath->status = MSU_EXT_NOTUSED; 2317 mutex_exit(&oplmsu_uinst->l_lock); 2318 mutex_exit(&oplmsu_uinst->u_lock); 2319 rw_exit(&oplmsu_uinst->lock); 2320 return (SUCCESS); 2321 } 2322 2323 /* 2324 * with PATH_ALL 2325 */ 2326 lpath = upath->lpath; 2327 if (lpath == NULL) { 2328 upath = upath->u_next; 2329 2330 DBG_PRINT((CE_WARN, "oplmsu: conf-start: " 2331 "Proper lpath_t doesn't exist")); 2332 continue; 2333 } 2334 2335 msu_tty_port = ddi_prop_get_int(DDI_DEV_T_ANY, 2336 oplmsu_uinst->msu_dip, 0, MSU_TTY_PORT_PROP, -1); 2337 2338 if (upath->ser_devcb.lsb == msu_tty_port) { 2339 /* Notify of the active path changing */ 2340 (void) prom_opl_switch_console(upath->ser_devcb.lsb); 2341 2342 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_ACTIVE, 2343 upath->status, MSU_ACTIVE); 2344 2345 mutex_enter(&oplmsu_uinst->l_lock); 2346 main_rq = RD(lpath->lower_queue); 2347 dst_queue = WR(lpath->lower_queue); 2348 lpath->src_upath = NULL; 2349 lpath->status = MSU_EXT_NOTUSED; 2350 lpath->uinst = oplmsu_uinst; 2351 mutex_exit(&oplmsu_uinst->l_lock); 2352 2353 /* Send XON to notify active path */ 2354 (void) oplmsu_cmn_put_xoffxon(dst_queue, MSU_XON_4); 2355 } else { 2356 oplmsu_cmn_set_upath_sts(upath, MSU_PSTAT_STANDBY, 2357 upath->status, MSU_STANDBY); 2358 2359 mutex_enter(&oplmsu_uinst->l_lock); 2360 lpath->src_upath = NULL; 2361 lpath->status = MSU_EXT_NOTUSED; 2362 mutex_exit(&oplmsu_uinst->l_lock); 2363 } 2364 upath = upath->u_next; 2365 } 2366 2367 if (main_rq == NULL) { 2368 upath_t *altn_upath; 2369 lpath_t *altn_lpath; 2370 2371 altn_upath = oplmsu_search_standby(); 2372 if (altn_upath) { 2373 oplmsu_cmn_set_upath_sts(altn_upath, MSU_PSTAT_ACTIVE, 2374 altn_upath->status, MSU_ACTIVE); 2375 2376 /* Notify of the active path changing */ 2377 (void) prom_opl_switch_console( 2378 altn_upath->ser_devcb.lsb); 2379 2380 altn_lpath = altn_upath->lpath; 2381 if (altn_lpath) { 2382 mutex_enter(&oplmsu_uinst->l_lock); 2383 main_rq = RD(altn_lpath->lower_queue); 2384 dst_queue = WR(altn_lpath->lower_queue); 2385 altn_lpath->src_upath = NULL; 2386 altn_lpath->status = MSU_EXT_NOTUSED; 2387 altn_lpath->uinst = oplmsu_uinst; 2388 mutex_exit(&oplmsu_uinst->l_lock); 2389 2390 /* Send XON to notify active path */ 2391 (void) oplmsu_cmn_put_xoffxon(dst_queue, 2392 MSU_XON_4); 2393 } else { 2394 cmn_err(CE_WARN, "oplmsu: conf-start: " 2395 "Proper alternate lpath_t doesn't exist"); 2396 } 2397 } else { 2398 cmn_err(CE_WARN, "oplmsu: conf-start: " 2399 "Proper alternate upath_t doesn't exist"); 2400 } 2401 } 2402 2403 mutex_enter(&oplmsu_uinst->l_lock); 2404 2405 /* Send XOFF to notify all standby paths */ 2406 oplmsu_cmn_putxoff_standby(); 2407 2408 /* Change active path of oplmsu */ 2409 oplmsu_uinst->lower_queue = main_rq; 2410 oplmsu_uinst->inst_status = oplmsu_get_inst_status(); 2411 mutex_exit(&oplmsu_uinst->l_lock); 2412 mutex_exit(&oplmsu_uinst->u_lock); 2413 rw_exit(&oplmsu_uinst->lock); 2414 return (SUCCESS); 2415 } 2416 2417 /* Prepare of unlink path */ 2418 int 2419 oplmsu_config_disc(int pathnum) 2420 { 2421 upath_t *upath; 2422 lpath_t *lpath; 2423 int use_flag; 2424 2425 DBG_PRINT((CE_NOTE, 2426 "oplmsu: conf-disc: config_disc(%d) called", pathnum)); 2427 2428 rw_enter(&oplmsu_uinst->lock, RW_READER); 2429 mutex_enter(&oplmsu_uinst->u_lock); 2430 2431 upath = oplmsu_search_upath_info(pathnum); 2432 if (upath == NULL) { 2433 mutex_exit(&oplmsu_uinst->u_lock); 2434 rw_exit(&oplmsu_uinst->lock); 2435 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2436 "Proper upath_t doesn't find"); 2437 return (EINVAL); 2438 } 2439 2440 if ((upath->status == MSU_PSTAT_DISCON) || 2441 (upath->traditional_status == MSU_DISCON)) { 2442 mutex_exit(&oplmsu_uinst->u_lock); 2443 rw_exit(&oplmsu_uinst->lock); 2444 return (SUCCESS); 2445 } else if (((upath->status != MSU_PSTAT_STOP) || 2446 (upath->traditional_status != MSU_STOP)) && 2447 ((upath->status != MSU_PSTAT_FAIL) || 2448 (upath->traditional_status != MSU_FAIL))) { 2449 mutex_exit(&oplmsu_uinst->u_lock); 2450 rw_exit(&oplmsu_uinst->lock); 2451 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2452 "Status of path is improper"); 2453 return (EINVAL); 2454 } 2455 2456 lpath = upath->lpath; 2457 if (lpath == NULL) { 2458 mutex_exit(&oplmsu_uinst->u_lock); 2459 rw_exit(&oplmsu_uinst->lock); 2460 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2461 "Proper lpath_t doesn't exist"); 2462 return (ENODEV); 2463 } 2464 2465 mutex_enter(&oplmsu_uinst->l_lock); 2466 2467 /* Check lower path status */ 2468 use_flag = oplmsu_set_ioctl_path(lpath, NULL, NULL); 2469 if (use_flag == BUSY) { 2470 mutex_exit(&oplmsu_uinst->l_lock); 2471 mutex_exit(&oplmsu_uinst->u_lock); 2472 rw_exit(&oplmsu_uinst->lock); 2473 cmn_err(CE_WARN, "oplmsu: conf-disc: " 2474 "Other processing is using lower path"); 2475 return (EBUSY); 2476 } 2477 2478 upath->status = MSU_PSTAT_STOP; 2479 upath->traditional_status = MSU_SETID; 2480 2481 oplmsu_clear_ioctl_path(lpath); 2482 mutex_exit(&oplmsu_uinst->l_lock); 2483 mutex_exit(&oplmsu_uinst->u_lock); 2484 rw_exit(&oplmsu_uinst->lock); 2485 return (SUCCESS); 2486 } 2487