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  * nulldriver - null device driver
  28  *
  29  * The nulldriver is used to associate a solaris driver with a specific
  30  * device without enabling external device access.
  31  *
  32  * The driver can be used to:
  33  *
  34  * o Prevent external access to specific devices/hardware by associating a
  35  *   high-precedence 'compatible' binding, including a path-oriented alias,
  36  *   with nulldriver.
  37  *
  38  * o Enable a nexus bus_config implementation to perform dynamic child
  39  *   discovery by creating a child 'probe' devinfo node, bound to
  40  *   nulldriver, at the specific child @unit-addresses associated with
  41  *   discovery.  With a nulldriver bound 'probe' node, nexus driver
  42  *   bus_config discovery code can use the same devinfo node oriented
  43  *   transport services for both discovery and normal-operation: which
  44  *   is a significant simplification.  While nulldriver prevents external
  45  *   device access, a nexus driver can still internally use the transport
  46  *   services.
  47  *
  48  *   A scsi(4) example of this type of use is SCSA enumeration services
  49  *   issuing a scsi REPORT_LUN command to a lun-0 'probe' node bound to
  50  *   nulldriver in order to discover all luns supported by a target.
  51  */
  52 
  53 #include <sys/modctl.h>
  54 #include <sys/conf.h>
  55 #include <sys/ddi.h>
  56 #include <sys/sunddi.h>
  57 #include <sys/cmn_err.h>
  58 
  59 static int nulldriver_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
  60 static int nulldriver_probe(dev_info_t *);
  61 static int nulldriver_attach(dev_info_t *, ddi_attach_cmd_t);
  62 static int nulldriver_detach(dev_info_t *, ddi_detach_cmd_t);
  63 
  64 static struct cb_ops nulldriver_cb_ops = {
  65         nodev,                          /* open */
  66         nodev,                          /* close */
  67         nodev,                          /* strategy */
  68         nodev,                          /* print */
  69         nodev,                          /* dump */
  70         nodev,                          /* read */
  71         nodev,                          /* write */
  72         nodev,                          /* ioctl */
  73         nodev,                          /* devmap */
  74         nodev,                          /* mmap */
  75         nodev,                          /* segmap */
  76         nochpoll,                       /* poll */
  77         ddi_prop_op,                    /* cb_prop_op */
  78         0,                              /* streamtab  */
  79         D_MP | D_NEW | D_HOTPLUG        /* Driver compatibility flag */
  80 };
  81 
  82 static struct dev_ops nulldriver_dev_ops = {
  83         DEVO_REV,                       /* devo_rev, */
  84         0,                              /* refcnt  */
  85         nulldriver_getinfo,             /* info */
  86         nodev,                          /* identify */
  87         nulldriver_probe,               /* probe */
  88         nulldriver_attach,              /* attach */
  89         nulldriver_detach,              /* detach */
  90         nodev,                          /* reset */
  91         &nulldriver_cb_ops,         /* driver operations */
  92         (struct bus_ops *)0,            /* bus operations */
  93         NULL,                           /* power */
  94         ddi_quiesce_not_needed,         /* quiesce */
  95 };
  96 
  97 static struct modldrv modldrv = {
  98         &mod_driverops, "nulldriver 1.1", &nulldriver_dev_ops
  99 };
 100 
 101 static struct modlinkage modlinkage = {
 102         MODREV_1, &modldrv, NULL
 103 };
 104 
 105 int
 106 _init(void)
 107 {
 108         return (mod_install(&modlinkage));
 109 }
 110 
 111 int
 112 _fini(void)
 113 {
 114         return (mod_remove(&modlinkage));
 115 }
 116 
 117 int
 118 _info(struct modinfo *modinfop)
 119 {
 120         return (mod_info(&modlinkage, modinfop));
 121 }
 122 
 123 /*ARGSUSED*/
 124 static int
 125 nulldriver_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd,
 126     void *arg, void **result)
 127 {
 128         return (DDI_FAILURE);
 129 }
 130 
 131 /*ARGSUSED*/
 132 static int
 133 nulldriver_probe(dev_info_t *dip)
 134 {
 135         /*
 136          * We want to succeed probe so that the node gets assigned a unit
 137          * address "@addr".
 138          */
 139         if (ddi_dev_is_sid(dip) == DDI_SUCCESS)
 140                 return (DDI_PROBE_DONTCARE);
 141         return (DDI_PROBE_DONTCARE);
 142 }
 143 
 144 /*
 145  * nulldriver_attach()
 146  *      attach(9e) entrypoint.
 147  */
 148 /* ARGSUSED */
 149 static int
 150 nulldriver_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
 151 {
 152         switch (cmd) {
 153         case DDI_ATTACH:
 154         case DDI_RESUME:
 155                 return (DDI_SUCCESS);
 156 
 157         case DDI_PM_RESUME:
 158         default:
 159                 return (DDI_FAILURE);
 160         }
 161 }
 162 
 163 /*
 164  * nulldriver_detach()
 165  *      detach(9E) entrypoint
 166  */
 167 /* ARGSUSED */
 168 static int
 169 nulldriver_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
 170 {
 171         switch (cmd) {
 172         case DDI_DETACH:
 173         case DDI_SUSPEND:
 174                 return (DDI_SUCCESS);
 175 
 176         case DDI_PM_SUSPEND:
 177         default:
 178                 return (DDI_FAILURE);
 179         }
 180 }