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