subr_device.c revision 1.10 1 /* $NetBSD: subr_device.c,v 1.10 2022/01/21 15:55:36 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 2006, 2021 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: subr_device.c,v 1.10 2022/01/21 15:55:36 thorpej Exp $");
31
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/systm.h>
35
36 #include <sys/device_calls.h>
37
38 /* Root device. */
39 device_t root_device;
40
41 /*
42 * devhandle_t accessors / mutators.
43 */
44
45 static bool
46 devhandle_is_valid_internal(const devhandle_t * const handlep)
47 {
48 if (handlep->impl == NULL) {
49 return false;
50 }
51 return handlep->impl->type != DEVHANDLE_TYPE_INVALID;
52 }
53
54 bool
55 devhandle_is_valid(devhandle_t handle)
56 {
57 return devhandle_is_valid_internal(&handle);
58 }
59
60 devhandle_t
61 devhandle_invalid(void)
62 {
63 static const devhandle_t invalid_devhandle = {
64 .impl = NULL,
65 .uintptr = 0,
66 };
67 return invalid_devhandle;
68 }
69
70 devhandle_type_t
71 devhandle_type(devhandle_t handle)
72 {
73 if (!devhandle_is_valid_internal(&handle)) {
74 return DEVHANDLE_TYPE_INVALID;
75 }
76
77 return handle.impl->type;
78 }
79
80 device_call_t
81 devhandle_lookup_device_call(devhandle_t handle, const char *name,
82 devhandle_t *call_handlep)
83 {
84 const struct devhandle_impl *impl;
85 device_call_t call;
86
87 /*
88 * The back-end can override the handle to use for the call,
89 * if needed.
90 */
91 *call_handlep = handle;
92
93 for (impl = handle.impl; impl != NULL; impl = impl->super) {
94 if (impl->lookup_device_call != NULL) {
95 call = impl->lookup_device_call(handle, name,
96 call_handlep);
97 if (call != NULL) {
98 return call;
99 }
100 }
101 }
102 return NULL;
103 }
104
105 void
106 devhandle_impl_inherit(struct devhandle_impl *impl,
107 const struct devhandle_impl *super)
108 {
109 memcpy(impl, super, sizeof(*impl));
110 impl->super = super;
111 }
112
113 /*
114 * Accessor functions for the device_t type.
115 */
116
117 devclass_t
118 device_class(device_t dev)
119 {
120
121 return dev->dv_class;
122 }
123
124 cfdata_t
125 device_cfdata(device_t dev)
126 {
127
128 return dev->dv_cfdata;
129 }
130
131 cfdriver_t
132 device_cfdriver(device_t dev)
133 {
134
135 return dev->dv_cfdriver;
136 }
137
138 cfattach_t
139 device_cfattach(device_t dev)
140 {
141
142 return dev->dv_cfattach;
143 }
144
145 int
146 device_unit(device_t dev)
147 {
148
149 return dev->dv_unit;
150 }
151
152 const char *
153 device_xname(device_t dev)
154 {
155
156 return dev->dv_xname;
157 }
158
159 device_t
160 device_parent(device_t dev)
161 {
162
163 return dev->dv_parent;
164 }
165
166 bool
167 device_activation(device_t dev, devact_level_t level)
168 {
169 int active_flags;
170
171 active_flags = DVF_ACTIVE;
172 switch (level) {
173 case DEVACT_LEVEL_FULL:
174 active_flags |= DVF_CLASS_SUSPENDED;
175 /*FALLTHROUGH*/
176 case DEVACT_LEVEL_DRIVER:
177 active_flags |= DVF_DRIVER_SUSPENDED;
178 /*FALLTHROUGH*/
179 case DEVACT_LEVEL_BUS:
180 active_flags |= DVF_BUS_SUSPENDED;
181 break;
182 }
183
184 return (dev->dv_flags & active_flags) == DVF_ACTIVE;
185 }
186
187 bool
188 device_is_active(device_t dev)
189 {
190 int active_flags;
191
192 active_flags = DVF_ACTIVE;
193 active_flags |= DVF_CLASS_SUSPENDED;
194 active_flags |= DVF_DRIVER_SUSPENDED;
195 active_flags |= DVF_BUS_SUSPENDED;
196
197 return (dev->dv_flags & active_flags) == DVF_ACTIVE;
198 }
199
200 bool
201 device_is_enabled(device_t dev)
202 {
203 return (dev->dv_flags & DVF_ACTIVE) == DVF_ACTIVE;
204 }
205
206 bool
207 device_has_power(device_t dev)
208 {
209 int active_flags;
210
211 active_flags = DVF_ACTIVE | DVF_BUS_SUSPENDED;
212
213 return (dev->dv_flags & active_flags) == DVF_ACTIVE;
214 }
215
216 int
217 device_locator(device_t dev, u_int locnum)
218 {
219
220 KASSERT(dev->dv_locators != NULL);
221 return dev->dv_locators[locnum];
222 }
223
224 void *
225 device_private(device_t dev)
226 {
227
228 /*
229 * The reason why device_private(NULL) is allowed is to simplify the
230 * work of a lot of userspace request handlers (i.e., c/bdev
231 * handlers) which grab cfdriver_t->cd_units[n].
232 * It avoids having them test for it to be NULL and only then calling
233 * device_private.
234 */
235 return dev == NULL ? NULL : dev->dv_private;
236 }
237
238 prop_dictionary_t
239 device_properties(device_t dev)
240 {
241
242 return dev->dv_properties;
243 }
244
245 /*
246 * device_is_a:
247 *
248 * Returns true if the device is an instance of the specified
249 * driver.
250 */
251 bool
252 device_is_a(device_t dev, const char *dname)
253 {
254 if (dev == NULL || dev->dv_cfdriver == NULL) {
255 return false;
256 }
257
258 return strcmp(dev->dv_cfdriver->cd_name, dname) == 0;
259 }
260
261 /*
262 * device_attached_to_iattr:
263 *
264 * Returns true if the device attached to the specified interface
265 * attribute.
266 */
267 bool
268 device_attached_to_iattr(device_t dev, const char *iattr)
269 {
270 cfdata_t cfdata = device_cfdata(dev);
271 const struct cfparent *pspec;
272
273 if (cfdata == NULL || (pspec = cfdata->cf_pspec) == NULL) {
274 return false;
275 }
276
277 return strcmp(pspec->cfp_iattr, iattr) == 0;
278 }
279
280 void
281 device_set_handle(device_t dev, devhandle_t handle)
282 {
283 dev->dv_handle = handle;
284 }
285
286 devhandle_t
287 device_handle(device_t dev)
288 {
289 return dev->dv_handle;
290 }
291
292 int
293 device_call_generic(device_t dev, const struct device_call_generic *gen)
294 {
295 devhandle_t handle = device_handle(dev);
296 device_call_t call;
297 devhandle_t call_handle;
298
299 call = devhandle_lookup_device_call(handle, gen->name, &call_handle);
300 if (call == NULL) {
301 return ENOTSUP;
302 }
303 return call(dev, call_handle, gen->args);
304 }
305
306 int
307 device_enumerate_children(device_t dev,
308 bool (*callback)(device_t, devhandle_t, void *),
309 void *callback_arg)
310 {
311 struct device_enumerate_children_args args = {
312 .callback = callback,
313 .callback_arg = callback_arg,
314 };
315
316 return device_call(dev, DEVICE_ENUMERATE_CHILDREN(&args));
317 }
318