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,
 148         NULL
 149 };
 150 
 151 int
 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 
 168 int
 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 
 187 int
 188 _info(struct modinfo *modinfop)
 189 {
 190         return (mod_info(&modlinkage, modinfop));
 191 }
 192 
 193 /* ARGSUSED */
 194 static int
 195 tphci_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 */
 213 static int
 214 tphci_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 */
 230 static int
 231 tphci_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  */
 240 static int
 241 tphci_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 
 301 attach_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*/
 311 static int
 312 tphci_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*/
 345 static int
 346 tphci_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*/
 377 static int
 378 tphci_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 
 384 static int
 385 tphci_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 
 446 static int
 447 tphci_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*/
 455 static int
 456 tphci_uninitchild(dev_info_t *dip, dev_info_t *child)
 457 {
 458         ddi_set_name_addr(child, NULL);
 459         return (DDI_SUCCESS);
 460 }
 461 
 462 static int
 463 tp_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 
 480 static int
 481 tphci_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 
 546 static int
 547 tphci_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 }