xref: /netbsd-src/sys/kern/subr_device.c (revision 53b02e147d4ed531c0d2a5ca9b3e8026ba3e99b5)
1 /*	$NetBSD: subr_device.c,v 1.9 2021/09/15 17:33:08 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.9 2021/09/15 17:33:08 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 void
61 devhandle_invalidate(devhandle_t * const handlep)
62 {
63 	handlep->impl = NULL;
64 	handlep->uintptr = 0;
65 }
66 
67 devhandle_type_t
68 devhandle_type(devhandle_t handle)
69 {
70 	if (!devhandle_is_valid_internal(&handle)) {
71 		return DEVHANDLE_TYPE_INVALID;
72 	}
73 
74 	return handle.impl->type;
75 }
76 
77 device_call_t
78 devhandle_lookup_device_call(devhandle_t handle, const char *name,
79     devhandle_t *call_handlep)
80 {
81 	const struct devhandle_impl *impl;
82 	device_call_t call;
83 
84 	/*
85 	 * The back-end can override the handle to use for the call,
86 	 * if needed.
87 	 */
88 	*call_handlep = handle;
89 
90 	for (impl = handle.impl; impl != NULL; impl = impl->super) {
91 		if (impl->lookup_device_call != NULL) {
92 			call = impl->lookup_device_call(handle, name,
93 			    call_handlep);
94 			if (call != NULL) {
95 				return call;
96 			}
97 		}
98 	}
99 	return NULL;
100 }
101 
102 void
103 devhandle_impl_inherit(struct devhandle_impl *impl,
104     const struct devhandle_impl *super)
105 {
106 	memcpy(impl, super, sizeof(*impl));
107 	impl->super = super;
108 }
109 
110 /*
111  * Accessor functions for the device_t type.
112  */
113 
114 devclass_t
115 device_class(device_t dev)
116 {
117 
118 	return dev->dv_class;
119 }
120 
121 cfdata_t
122 device_cfdata(device_t dev)
123 {
124 
125 	return dev->dv_cfdata;
126 }
127 
128 cfdriver_t
129 device_cfdriver(device_t dev)
130 {
131 
132 	return dev->dv_cfdriver;
133 }
134 
135 cfattach_t
136 device_cfattach(device_t dev)
137 {
138 
139 	return dev->dv_cfattach;
140 }
141 
142 int
143 device_unit(device_t dev)
144 {
145 
146 	return dev->dv_unit;
147 }
148 
149 const char *
150 device_xname(device_t dev)
151 {
152 
153 	return dev->dv_xname;
154 }
155 
156 device_t
157 device_parent(device_t dev)
158 {
159 
160 	return dev->dv_parent;
161 }
162 
163 bool
164 device_activation(device_t dev, devact_level_t level)
165 {
166 	int active_flags;
167 
168 	active_flags = DVF_ACTIVE;
169 	switch (level) {
170 	case DEVACT_LEVEL_FULL:
171 		active_flags |= DVF_CLASS_SUSPENDED;
172 		/*FALLTHROUGH*/
173 	case DEVACT_LEVEL_DRIVER:
174 		active_flags |= DVF_DRIVER_SUSPENDED;
175 		/*FALLTHROUGH*/
176 	case DEVACT_LEVEL_BUS:
177 		active_flags |= DVF_BUS_SUSPENDED;
178 		break;
179 	}
180 
181 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
182 }
183 
184 bool
185 device_is_active(device_t dev)
186 {
187 	int active_flags;
188 
189 	active_flags = DVF_ACTIVE;
190 	active_flags |= DVF_CLASS_SUSPENDED;
191 	active_flags |= DVF_DRIVER_SUSPENDED;
192 	active_flags |= DVF_BUS_SUSPENDED;
193 
194 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
195 }
196 
197 bool
198 device_is_enabled(device_t dev)
199 {
200 	return (dev->dv_flags & DVF_ACTIVE) == DVF_ACTIVE;
201 }
202 
203 bool
204 device_has_power(device_t dev)
205 {
206 	int active_flags;
207 
208 	active_flags = DVF_ACTIVE | DVF_BUS_SUSPENDED;
209 
210 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
211 }
212 
213 int
214 device_locator(device_t dev, u_int locnum)
215 {
216 
217 	KASSERT(dev->dv_locators != NULL);
218 	return dev->dv_locators[locnum];
219 }
220 
221 void *
222 device_private(device_t dev)
223 {
224 
225 	/*
226 	 * The reason why device_private(NULL) is allowed is to simplify the
227 	 * work of a lot of userspace request handlers (i.e., c/bdev
228 	 * handlers) which grab cfdriver_t->cd_units[n].
229 	 * It avoids having them test for it to be NULL and only then calling
230 	 * device_private.
231 	 */
232 	return dev == NULL ? NULL : dev->dv_private;
233 }
234 
235 prop_dictionary_t
236 device_properties(device_t dev)
237 {
238 
239 	return dev->dv_properties;
240 }
241 
242 /*
243  * device_is_a:
244  *
245  *	Returns true if the device is an instance of the specified
246  *	driver.
247  */
248 bool
249 device_is_a(device_t dev, const char *dname)
250 {
251 	if (dev == NULL || dev->dv_cfdriver == NULL) {
252 		return false;
253 	}
254 
255 	return strcmp(dev->dv_cfdriver->cd_name, dname) == 0;
256 }
257 
258 /*
259  * device_attached_to_iattr:
260  *
261  *	Returns true if the device attached to the specified interface
262  *	attribute.
263  */
264 bool
265 device_attached_to_iattr(device_t dev, const char *iattr)
266 {
267 	cfdata_t cfdata = device_cfdata(dev);
268 	const struct cfparent *pspec;
269 
270 	if (cfdata == NULL || (pspec = cfdata->cf_pspec) == NULL) {
271 		return false;
272 	}
273 
274 	return strcmp(pspec->cfp_iattr, iattr) == 0;
275 }
276 
277 void
278 device_set_handle(device_t dev, devhandle_t handle)
279 {
280 	dev->dv_handle = handle;
281 }
282 
283 devhandle_t
284 device_handle(device_t dev)
285 {
286 	return dev->dv_handle;
287 }
288 
289 int
290 device_call_generic(device_t dev, const struct device_call_generic *gen)
291 {
292 	devhandle_t handle = device_handle(dev);
293 	device_call_t call;
294 	devhandle_t call_handle;
295 
296 	call = devhandle_lookup_device_call(handle, gen->name, &call_handle);
297 	if (call == NULL) {
298 		return ENOTSUP;
299 	}
300 	return call(dev, call_handle, gen->args);
301 }
302 
303 int
304 device_enumerate_children(device_t dev,
305     bool (*callback)(device_t, devhandle_t, void *),
306     void *callback_arg)
307 {
308 	struct device_enumerate_children_args args = {
309 		.callback = callback,
310 		.callback_arg = callback_arg,
311 	};
312 
313 	return device_call(dev, DEVICE_ENUMERATE_CHILDREN(&args));
314 }
315