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