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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 /* 28 * The tphci driver can be used to exercise the mpxio framework together 29 * with tvhci/tclient. 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/file.h> 34 #include <sys/open.h> 35 #include <sys/stat.h> 36 #include <sys/modctl.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/sunmdi.h> 41 #include <sys/disp.h> 42 43 /* cb_ops entry points */ 44 static int tphci_open(dev_t *, int, int, cred_t *); 45 static int tphci_close(dev_t, int, int, cred_t *); 46 static int tphci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 47 static int tphci_attach(dev_info_t *, ddi_attach_cmd_t); 48 static int tphci_detach(dev_info_t *, ddi_detach_cmd_t); 49 static int tphci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 50 51 /* bus_ops entry points */ 52 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 53 void *); 54 static int tphci_initchild(dev_info_t *, dev_info_t *); 55 static int tphci_uninitchild(dev_info_t *, dev_info_t *); 56 static int tphci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, 57 dev_info_t **); 58 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t, 59 void *); 60 static int tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, 61 ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result); 62 63 64 static void *tphci_state; 65 struct tphci_state { 66 dev_info_t *dip; 67 }; 68 69 static struct cb_ops tphci_cb_ops = { 70 tphci_open, /* open */ 71 tphci_close, /* close */ 72 nodev, /* strategy */ 73 nodev, /* print */ 74 nodev, /* dump */ 75 nodev, /* read */ 76 nodev, /* write */ 77 tphci_ioctl, /* ioctl */ 78 nodev, /* devmap */ 79 nodev, /* mmap */ 80 nodev, /* segmap */ 81 nochpoll, /* chpoll */ 82 ddi_prop_op, /* cb_prop_op */ 83 0, /* streamtab */ 84 D_NEW | D_MP, /* cb_flag */ 85 CB_REV, /* rev */ 86 nodev, /* aread */ 87 nodev /* awrite */ 88 }; 89 90 static struct bus_ops tphci_bus_ops = { 91 BUSO_REV, /* busops_rev */ 92 nullbusmap, /* bus_map */ 93 NULL, /* bus_get_intrspec */ 94 NULL, /* bus_add_interspec */ 95 NULL, /* bus_remove_interspec */ 96 i_ddi_map_fault, /* bus_map_fault */ 97 ddi_no_dma_map, /* bus_dma_map */ 98 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 99 NULL, /* bus_dma_freehdl */ 100 NULL, /* bus_dma_bindhdl */ 101 NULL, /* bus_dma_unbindhdl */ 102 NULL, /* bus_dma_flush */ 103 NULL, /* bus_dma_win */ 104 NULL, /* bus_dma_ctl */ 105 tphci_ctl, /* bus_ctl */ 106 ddi_bus_prop_op, /* bus_prop_op */ 107 NULL, /* bus_get_eventcookie */ 108 NULL, /* bus_add_eventcall */ 109 NULL, /* bus_remove_event */ 110 NULL, /* bus_post_event */ 111 NULL, /* bus_intr_ctl */ 112 tphci_bus_config, /* bus_config */ 113 tphci_bus_unconfig, /* bus_unconfig */ 114 NULL, /* bus_fm_init */ 115 NULL, /* bus_fm_fini */ 116 NULL, /* bus_fm_access_enter */ 117 NULL, /* bus_fm_access_exit */ 118 NULL, /* bus_power */ 119 tphci_intr_op /* bus_intr_op */ 120 }; 121 122 static struct dev_ops tphci_ops = { 123 DEVO_REV, 124 0, 125 tphci_getinfo, 126 nulldev, /* identify */ 127 nulldev, /* probe */ 128 tphci_attach, /* attach and detach are mandatory */ 129 tphci_detach, 130 nodev, /* reset */ 131 &tphci_cb_ops, /* cb_ops */ 132 &tphci_bus_ops, /* bus_ops */ 133 NULL, /* power */ 134 ddi_quiesce_not_needed, /* quiesce */ 135 }; 136 137 extern struct mod_ops mod_driverops; 138 139 static struct modldrv modldrv = { 140 &mod_driverops, 141 "test phci driver", 142 &tphci_ops 143 }; 144 145 static struct modlinkage modlinkage = { 146 MODREV_1, 147 { &modldrv, NULL } 148 }; 149 150 int 151 _init(void) 152 { 153 int rval; 154 155 if ((rval = ddi_soft_state_init(&tphci_state, 156 sizeof (struct tphci_state), 2)) != 0) { 157 return (rval); 158 } 159 160 if ((rval = mod_install(&modlinkage)) != 0) { 161 ddi_soft_state_fini(&tphci_state); 162 } 163 return (rval); 164 } 165 166 167 int 168 _fini(void) 169 { 170 int rval; 171 172 /* 173 * don't start cleaning up until we know that the module remove 174 * has worked -- if this works, then we know that each instance 175 * has successfully been detached 176 */ 177 if ((rval = mod_remove(&modlinkage)) != 0) { 178 return (rval); 179 } 180 181 ddi_soft_state_fini(&tphci_state); 182 183 return (rval); 184 } 185 186 int 187 _info(struct modinfo *modinfop) 188 { 189 return (mod_info(&modlinkage, modinfop)); 190 } 191 192 /* ARGSUSED */ 193 static int 194 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp) 195 { 196 struct tphci_state *phci; 197 198 if (otype != OTYP_CHR) { 199 return (EINVAL); 200 } 201 202 phci = ddi_get_soft_state(tphci_state, getminor(*devp)); 203 if (phci == NULL) { 204 return (ENXIO); 205 } 206 207 return (0); 208 } 209 210 211 /* ARGSUSED */ 212 static int 213 tphci_close(dev_t dev, int flag, int otype, cred_t *credp) 214 { 215 struct tphci_state *phci; 216 if (otype != OTYP_CHR) { 217 return (EINVAL); 218 } 219 220 phci = ddi_get_soft_state(tphci_state, getminor(dev)); 221 if (phci == NULL) { 222 return (ENXIO); 223 } 224 225 return (0); 226 } 227 228 /* ARGSUSED */ 229 static int 230 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 231 cred_t *credp, int *rval) 232 { 233 return (0); 234 } 235 236 /* 237 * attach the module 238 */ 239 static int 240 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 241 { 242 char *vclass; 243 int instance, phci_regis = 0; 244 struct tphci_state *phci = NULL; 245 246 instance = ddi_get_instance(dip); 247 248 switch (cmd) { 249 case DDI_ATTACH: 250 break; 251 252 case DDI_RESUME: 253 case DDI_PM_RESUME: 254 return (0); /* nothing to do */ 255 256 default: 257 return (DDI_FAILURE); 258 } 259 260 /* 261 * Allocate phci data structure. 262 */ 263 if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) { 264 return (DDI_FAILURE); 265 } 266 267 phci = ddi_get_soft_state(tphci_state, instance); 268 ASSERT(phci != NULL); 269 phci->dip = dip; 270 271 /* bus_addr has the form #,<vhci_class> */ 272 vclass = strchr(ddi_get_name_addr(dip), ','); 273 if (vclass == NULL || vclass[1] == '\0') { 274 cmn_err(CE_NOTE, "tphci invalid bus_addr %s", 275 ddi_get_name_addr(dip)); 276 goto attach_fail; 277 } 278 279 /* 280 * Attach this instance with the mpxio framework 281 */ 282 if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) { 283 cmn_err(CE_WARN, "%s mdi_phci_register failed", 284 ddi_node_name(dip)); 285 goto attach_fail; 286 } 287 phci_regis++; 288 289 if (ddi_create_minor_node(dip, "devctl", S_IFCHR, 290 instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) { 291 cmn_err(CE_NOTE, "%s ddi_create_minor_node failed", 292 ddi_node_name(dip)); 293 goto attach_fail; 294 } 295 296 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1); 297 ddi_report_dev(dip); 298 return (DDI_SUCCESS); 299 300 attach_fail: 301 if (phci_regis) 302 (void) mdi_phci_unregister(dip, 0); 303 304 ddi_soft_state_free(tphci_state, instance); 305 return (DDI_FAILURE); 306 } 307 308 309 /*ARGSUSED*/ 310 static int 311 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 312 { 313 int instance = ddi_get_instance(dip); 314 315 switch (cmd) { 316 case DDI_DETACH: 317 break; 318 319 case DDI_SUSPEND: 320 case DDI_PM_SUSPEND: 321 return (0); /* nothing to do */ 322 323 default: 324 return (DDI_FAILURE); 325 } 326 327 if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS) 328 return (DDI_FAILURE); 329 330 ddi_remove_minor_node(dip, NULL); 331 ddi_soft_state_free(tphci_state, instance); 332 333 return (DDI_SUCCESS); 334 } 335 336 /* 337 * tphci_getinfo() 338 * Given the device number, return the devinfo pointer or the 339 * instance number. 340 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach. 341 */ 342 343 /*ARGSUSED*/ 344 static int 345 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 346 { 347 struct tphci_state *phci; 348 int instance = getminor((dev_t)arg); 349 350 switch (cmd) { 351 case DDI_INFO_DEVT2DEVINFO: 352 phci = ddi_get_soft_state(tphci_state, instance); 353 if (phci != NULL) 354 *result = phci->dip; 355 else { 356 *result = NULL; 357 return (DDI_FAILURE); 358 } 359 break; 360 361 case DDI_INFO_DEVT2INSTANCE: 362 *result = (void *)(uintptr_t)instance; 363 break; 364 365 default: 366 return (DDI_FAILURE); 367 } 368 369 return (DDI_SUCCESS); 370 } 371 372 /* 373 * Interrupt stuff. NO OP for pseudo drivers. 374 */ 375 /*ARGSUSED*/ 376 static int 377 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 378 ddi_intr_handle_impl_t *hdlp, void *result) 379 { 380 return (DDI_FAILURE); 381 } 382 383 static int 384 tphci_ctl(dev_info_t *dip, dev_info_t *rdip, 385 ddi_ctl_enum_t ctlop, void *arg, void *result) 386 { 387 switch (ctlop) { 388 case DDI_CTLOPS_REPORTDEV: 389 if (rdip == (dev_info_t *)0) 390 return (DDI_FAILURE); 391 cmn_err(CE_CONT, "?tphci-device: %s%d\n", 392 ddi_get_name(rdip), ddi_get_instance(rdip)); 393 return (DDI_SUCCESS); 394 395 case DDI_CTLOPS_INITCHILD: 396 { 397 dev_info_t *child = (dev_info_t *)arg; 398 return (tphci_initchild(dip, child)); 399 } 400 401 case DDI_CTLOPS_UNINITCHILD: 402 { 403 dev_info_t *child = (dev_info_t *)arg; 404 return (tphci_uninitchild(dip, child)); 405 } 406 407 case DDI_CTLOPS_DMAPMAPC: 408 case DDI_CTLOPS_REPORTINT: 409 case DDI_CTLOPS_REGSIZE: 410 case DDI_CTLOPS_NREGS: 411 case DDI_CTLOPS_SIDDEV: 412 case DDI_CTLOPS_SLAVEONLY: 413 case DDI_CTLOPS_AFFINITY: 414 case DDI_CTLOPS_POKE: 415 case DDI_CTLOPS_PEEK: 416 /* 417 * These ops correspond to functions that "shouldn't" be called 418 * by a pseudo driver. So we whine when we're called. 419 */ 420 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 421 ddi_get_name(dip), ddi_get_instance(dip), 422 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 423 return (DDI_FAILURE); 424 425 case DDI_CTLOPS_ATTACH: 426 case DDI_CTLOPS_BTOP: 427 case DDI_CTLOPS_BTOPR: 428 case DDI_CTLOPS_DETACH: 429 case DDI_CTLOPS_DVMAPAGESIZE: 430 case DDI_CTLOPS_IOMIN: 431 case DDI_CTLOPS_POWER: 432 case DDI_CTLOPS_PTOB: 433 default: 434 /* 435 * The ops that we pass up (default). We pass up memory 436 * allocation oriented ops that we receive - these may be 437 * associated with pseudo HBA drivers below us with target 438 * drivers below them that use ddi memory allocation 439 * interfaces like scsi_alloc_consistent_buf. 440 */ 441 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 442 } 443 } 444 445 static int 446 tphci_initchild(dev_info_t *dip, dev_info_t *child) 447 { 448 _NOTE(ARGUNUSED(dip)) 449 ddi_set_name_addr(child, "0"); 450 return (DDI_SUCCESS); 451 } 452 453 /*ARGSUSED*/ 454 static int 455 tphci_uninitchild(dev_info_t *dip, dev_info_t *child) 456 { 457 ddi_set_name_addr(child, NULL); 458 return (DDI_SUCCESS); 459 } 460 461 static int 462 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid) 463 { 464 char *tmp; 465 466 i_ddi_parse_name(devnm, cname, paddr, NULL); 467 if ((strcmp(*cname, "tclient") != 0) && 468 (strcmp(*cname, "tphci") != 0) || *paddr == NULL) 469 return (-1); 470 471 tmp = strchr(*paddr, ','); 472 if (tmp == NULL || tmp[1] == '\0') 473 return (-1); 474 475 *guid = tmp + 1; 476 return (0); 477 } 478 479 static int 480 tphci_bus_config(dev_info_t *parent, uint_t flags, 481 ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 482 { 483 _NOTE(ARGUNUSED(flags)) 484 char *cname, *paddr, *guid, *devnm; 485 mdi_pathinfo_t *pip; 486 int len, circ, rval; 487 488 switch (op) { 489 case BUS_CONFIG_ONE: 490 break; 491 case BUS_CONFIG_DRIVER: /* no direct children to configure */ 492 case BUS_CONFIG_ALL: 493 return (NDI_SUCCESS); 494 default: 495 return (NDI_FAILURE); 496 } 497 498 /* only implement BUS_CONFIG_ONE */ 499 devnm = i_ddi_strdup((char *)arg, KM_SLEEP); 500 len = strlen(devnm) + 1; 501 502 /* caddr is hardcoded in the form *,<guid> */ 503 if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) { 504 cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s", 505 (char *)arg); 506 kmem_free(devnm, len); 507 return (NDI_FAILURE); 508 } 509 510 mdi_devi_enter(parent, &circ); 511 rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip); 512 kmem_free(devnm, len); 513 if (rval != MDI_SUCCESS) { 514 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed"); 515 mdi_devi_exit(parent, circ); 516 return (NDI_FAILURE); 517 } 518 519 /* 520 * Hold the path and exit the pHCI while calling mdi_pi_online 521 * to avoid deadlock with power management of pHCI. 522 */ 523 mdi_hold_path(pip); 524 mdi_devi_exit_phci(parent, circ); 525 rval = mdi_pi_online(pip, 0); 526 mdi_devi_enter_phci(parent, &circ); 527 mdi_rele_path(pip); 528 529 if (rval != MDI_SUCCESS) { 530 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed"); 531 (void) mdi_pi_free(pip, 0); 532 mdi_devi_exit(parent, circ); 533 return (NDI_FAILURE); 534 } 535 536 if (childp) { 537 *childp = mdi_pi_get_client(pip); 538 ndi_hold_devi(*childp); 539 } 540 mdi_devi_exit(parent, circ); 541 542 return (NDI_SUCCESS); 543 } 544 545 static int 546 tphci_bus_unconfig(dev_info_t *parent, uint_t flags, 547 ddi_bus_config_op_t op, void *arg) 548 { 549 int rval = MDI_SUCCESS; 550 int circ; 551 mdi_pathinfo_t *pip, *next; 552 char *devnm, *cname, *caddr; 553 554 switch (op) { 555 case BUS_UNCONFIG_ONE: 556 devnm = (char *)arg; 557 i_ddi_parse_name(devnm, &cname, &caddr, NULL); 558 if (strcmp(cname, "tclient") != 0) 559 return (NDI_SUCCESS); /* no such device */ 560 561 mdi_devi_enter(parent, &circ); 562 pip = mdi_pi_find(parent, NULL, caddr); 563 if (pip) { 564 mdi_hold_path(pip); 565 mdi_devi_exit_phci(parent, circ); 566 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 567 mdi_devi_enter_phci(parent, &circ); 568 mdi_rele_path(pip); 569 570 if (rval == MDI_SUCCESS) 571 (void) mdi_pi_free(pip, 0); 572 } 573 mdi_devi_exit(parent, circ); 574 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 575 576 case BUS_UNCONFIG_ALL: 577 if (flags & NDI_AUTODETACH) 578 return (NDI_FAILURE); 579 580 mdi_devi_enter(parent, &circ); 581 next = mdi_get_next_client_path(parent, NULL); 582 while ((pip = next) != NULL) { 583 next = mdi_get_next_client_path(parent, pip); 584 585 mdi_hold_path(pip); 586 mdi_devi_exit_phci(parent, circ); 587 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 588 mdi_devi_enter_phci(parent, &circ); 589 mdi_rele_path(pip); 590 591 if (rval != MDI_SUCCESS) 592 break; 593 (void) mdi_pi_free(pip, 0); 594 } 595 mdi_devi_exit(parent, circ); 596 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 597 598 case BUS_UNCONFIG_DRIVER: /* nothing to do */ 599 return (NDI_SUCCESS); 600 601 default: 602 return (NDI_FAILURE); 603 } 604 /*NOTREACHED*/ 605 }