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