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