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
_init(void)106 _init(void)
107 {
108 return (mod_install(&modlinkage));
109 }
110
111 int
_fini(void)112 _fini(void)
113 {
114 return (mod_remove(&modlinkage));
115 }
116
117 int
_info(struct modinfo * modinfop)118 _info(struct modinfo *modinfop)
119 {
120 return (mod_info(&modlinkage, modinfop));
121 }
122
123 /*ARGSUSED*/
124 static int
nulldriver_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
nulldriver_probe(dev_info_t * dip)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
nulldriver_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
nulldriver_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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 }
181