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