xref: /netbsd-src/sys/kern/subr_device.c (revision 310d5db64af6aa27640cad060591b336d705eb91)
1*310d5db6Sthorpej /*	$NetBSD: subr_device.c,v 1.14 2024/08/27 13:44:55 thorpej Exp $	*/
20f9bb09eSpooka 
30f9bb09eSpooka /*
45e189a3dSthorpej  * Copyright (c) 2006, 2021 The NetBSD Foundation, Inc.
50f9bb09eSpooka  * All rights reserved.
60f9bb09eSpooka  *
70f9bb09eSpooka  * Redistribution and use in source and binary forms, with or without
80f9bb09eSpooka  * modification, are permitted provided that the following conditions
90f9bb09eSpooka  * are met:
100f9bb09eSpooka  * 1. Redistributions of source code must retain the above copyright
110f9bb09eSpooka  *    notice, this list of conditions and the following disclaimer.
120f9bb09eSpooka  * 2. Redistributions in binary form must reproduce the above copyright
130f9bb09eSpooka  *    notice, this list of conditions and the following disclaimer in the
140f9bb09eSpooka  *    documentation and/or other materials provided with the distribution.
150f9bb09eSpooka  *
160f9bb09eSpooka  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
170f9bb09eSpooka  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
180f9bb09eSpooka  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
190f9bb09eSpooka  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
200f9bb09eSpooka  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
210f9bb09eSpooka  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
220f9bb09eSpooka  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
230f9bb09eSpooka  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
240f9bb09eSpooka  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
250f9bb09eSpooka  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
260f9bb09eSpooka  * POSSIBILITY OF SUCH DAMAGE.
270f9bb09eSpooka  */
280f9bb09eSpooka 
290f9bb09eSpooka #include <sys/cdefs.h>
30*310d5db6Sthorpej __KERNEL_RCSID(0, "$NetBSD: subr_device.c,v 1.14 2024/08/27 13:44:55 thorpej Exp $");
310f9bb09eSpooka 
320f9bb09eSpooka #include <sys/param.h>
330f9bb09eSpooka #include <sys/device.h>
34b6c8c37fSriastradh #include <sys/device_impl.h>
350f9bb09eSpooka #include <sys/systm.h>
360f9bb09eSpooka 
374ecce2b2Sthorpej #include <sys/device_calls.h>
384ecce2b2Sthorpej 
39915977c5Spooka /* Root device. */
40915977c5Spooka device_t			root_device;
41915977c5Spooka 
420f9bb09eSpooka /*
433e6ba253Sthorpej  * devhandle_t accessors / mutators.
44105da683Sthorpej  */
45105da683Sthorpej 
46105da683Sthorpej static bool
47105da683Sthorpej devhandle_is_valid_internal(const devhandle_t * const handlep)
48105da683Sthorpej {
49105da683Sthorpej 	if (handlep->impl == NULL) {
50105da683Sthorpej 		return false;
51105da683Sthorpej 	}
52105da683Sthorpej 	return handlep->impl->type != DEVHANDLE_TYPE_INVALID;
53105da683Sthorpej }
54105da683Sthorpej 
55105da683Sthorpej bool
56105da683Sthorpej devhandle_is_valid(devhandle_t handle)
57105da683Sthorpej {
58105da683Sthorpej 	return devhandle_is_valid_internal(&handle);
59105da683Sthorpej }
60105da683Sthorpej 
6146457800Sthorpej devhandle_t
6246457800Sthorpej devhandle_invalid(void)
63105da683Sthorpej {
6446457800Sthorpej 	static const devhandle_t invalid_devhandle = {
6546457800Sthorpej 		.impl = NULL,
6646457800Sthorpej 		.uintptr = 0,
6746457800Sthorpej 	};
6846457800Sthorpej 	return invalid_devhandle;
69105da683Sthorpej }
70105da683Sthorpej 
71105da683Sthorpej devhandle_type_t
72105da683Sthorpej devhandle_type(devhandle_t handle)
73105da683Sthorpej {
74105da683Sthorpej 	if (!devhandle_is_valid_internal(&handle)) {
75105da683Sthorpej 		return DEVHANDLE_TYPE_INVALID;
76105da683Sthorpej 	}
77105da683Sthorpej 
78105da683Sthorpej 	return handle.impl->type;
79105da683Sthorpej }
80105da683Sthorpej 
8138bae68aSthorpej int
8238bae68aSthorpej devhandle_compare(devhandle_t handle1, devhandle_t handle2)
8338bae68aSthorpej {
8438bae68aSthorpej 	devhandle_type_t type1 = devhandle_type(handle1);
8538bae68aSthorpej 	devhandle_type_t type2 = devhandle_type(handle2);
8638bae68aSthorpej 
8738bae68aSthorpej 	if (type1 == DEVHANDLE_TYPE_INVALID) {
8838bae68aSthorpej 		return -1;
8938bae68aSthorpej 	}
9038bae68aSthorpej 	if (type2 == DEVHANDLE_TYPE_INVALID) {
9138bae68aSthorpej 		return 1;
9238bae68aSthorpej 	}
9338bae68aSthorpej 
9438bae68aSthorpej 	if (type1 < type2) {
9538bae68aSthorpej 		return -1;
9638bae68aSthorpej 	}
9738bae68aSthorpej 	if (type1 > type2) {
9838bae68aSthorpej 		return 1;
9938bae68aSthorpej 	}
10038bae68aSthorpej 
10138bae68aSthorpej 	/* For private handles, we also compare the impl pointers. */
10238bae68aSthorpej 	if (type1 == DEVHANDLE_TYPE_PRIVATE) {
10338bae68aSthorpej 		intptr_t impl1 = (intptr_t)handle1.impl;
10438bae68aSthorpej 		intptr_t impl2 = (intptr_t)handle2.impl;
10538bae68aSthorpej 
10638bae68aSthorpej 		if (impl1 < impl2) {
10738bae68aSthorpej 			return -1;
10838bae68aSthorpej 		}
10938bae68aSthorpej 		if (impl1 > impl2) {
11038bae68aSthorpej 			return 1;
11138bae68aSthorpej 		}
11238bae68aSthorpej 	}
11338bae68aSthorpej 
11438bae68aSthorpej 	if (handle1.integer < handle2.integer) {
11538bae68aSthorpej 		return -1;
11638bae68aSthorpej 	}
11738bae68aSthorpej 	if (handle1.integer > handle2.integer) {
11838bae68aSthorpej 		return 1;
11938bae68aSthorpej 	}
12038bae68aSthorpej 
12138bae68aSthorpej 	return 0;
12238bae68aSthorpej }
12338bae68aSthorpej 
12468a52352Sthorpej device_call_t
125105da683Sthorpej devhandle_lookup_device_call(devhandle_t handle, const char *name,
126105da683Sthorpej     devhandle_t *call_handlep)
127105da683Sthorpej {
128105da683Sthorpej 	const struct devhandle_impl *impl;
129105da683Sthorpej 	device_call_t call;
130105da683Sthorpej 
131105da683Sthorpej 	/*
132105da683Sthorpej 	 * The back-end can override the handle to use for the call,
133105da683Sthorpej 	 * if needed.
134105da683Sthorpej 	 */
135105da683Sthorpej 	*call_handlep = handle;
136105da683Sthorpej 
137105da683Sthorpej 	for (impl = handle.impl; impl != NULL; impl = impl->super) {
138105da683Sthorpej 		if (impl->lookup_device_call != NULL) {
139105da683Sthorpej 			call = impl->lookup_device_call(handle, name,
140105da683Sthorpej 			    call_handlep);
141105da683Sthorpej 			if (call != NULL) {
142105da683Sthorpej 				return call;
143105da683Sthorpej 			}
144105da683Sthorpej 		}
145105da683Sthorpej 	}
146105da683Sthorpej 	return NULL;
147105da683Sthorpej }
148105da683Sthorpej 
149105da683Sthorpej void
150*310d5db6Sthorpej devhandle_impl_subclass(struct devhandle_impl *new_impl,
151*310d5db6Sthorpej     const struct devhandle_impl *super,
152*310d5db6Sthorpej     device_call_t (*new_lookup)(devhandle_t, const char *, devhandle_t *))
153105da683Sthorpej {
154*310d5db6Sthorpej 	new_impl->type = super->type;
155*310d5db6Sthorpej 	new_impl->super = super;
156*310d5db6Sthorpej 	new_impl->lookup_device_call = new_lookup;
157*310d5db6Sthorpej }
158*310d5db6Sthorpej 
159*310d5db6Sthorpej /*
160*310d5db6Sthorpej  * Helper function that provides a short-hand method of the common
161*310d5db6Sthorpej  * "subclass a device handle" flow.
162*310d5db6Sthorpej  */
163*310d5db6Sthorpej devhandle_t
164*310d5db6Sthorpej devhandle_subclass(devhandle_t handle,
165*310d5db6Sthorpej     struct devhandle_impl *new_impl,
166*310d5db6Sthorpej     device_call_t (*new_lookup)(devhandle_t, const char *, devhandle_t *))
167*310d5db6Sthorpej {
168*310d5db6Sthorpej 	devhandle_impl_subclass(new_impl, handle.impl, new_lookup);
169*310d5db6Sthorpej 	handle.impl = new_impl;
170*310d5db6Sthorpej 
171*310d5db6Sthorpej 	return handle;
172105da683Sthorpej }
173105da683Sthorpej 
174105da683Sthorpej /*
1750f9bb09eSpooka  * Accessor functions for the device_t type.
1760f9bb09eSpooka  */
1775e189a3dSthorpej 
1780f9bb09eSpooka devclass_t
1790f9bb09eSpooka device_class(device_t dev)
1800f9bb09eSpooka {
1810f9bb09eSpooka 
1820f9bb09eSpooka 	return dev->dv_class;
1830f9bb09eSpooka }
1840f9bb09eSpooka 
1850f9bb09eSpooka cfdata_t
1860f9bb09eSpooka device_cfdata(device_t dev)
1870f9bb09eSpooka {
1880f9bb09eSpooka 
1890f9bb09eSpooka 	return dev->dv_cfdata;
1900f9bb09eSpooka }
1910f9bb09eSpooka 
1920f9bb09eSpooka cfdriver_t
1930f9bb09eSpooka device_cfdriver(device_t dev)
1940f9bb09eSpooka {
1950f9bb09eSpooka 
1960f9bb09eSpooka 	return dev->dv_cfdriver;
1970f9bb09eSpooka }
1980f9bb09eSpooka 
1990f9bb09eSpooka cfattach_t
2000f9bb09eSpooka device_cfattach(device_t dev)
2010f9bb09eSpooka {
2020f9bb09eSpooka 
2030f9bb09eSpooka 	return dev->dv_cfattach;
2040f9bb09eSpooka }
2050f9bb09eSpooka 
2060f9bb09eSpooka int
2070f9bb09eSpooka device_unit(device_t dev)
2080f9bb09eSpooka {
2090f9bb09eSpooka 
2100f9bb09eSpooka 	return dev->dv_unit;
2110f9bb09eSpooka }
2120f9bb09eSpooka 
2130f9bb09eSpooka const char *
2140f9bb09eSpooka device_xname(device_t dev)
2150f9bb09eSpooka {
2160f9bb09eSpooka 
2170f9bb09eSpooka 	return dev->dv_xname;
2180f9bb09eSpooka }
2190f9bb09eSpooka 
2200f9bb09eSpooka device_t
2210f9bb09eSpooka device_parent(device_t dev)
2220f9bb09eSpooka {
2230f9bb09eSpooka 
2240f9bb09eSpooka 	return dev->dv_parent;
2250f9bb09eSpooka }
2260f9bb09eSpooka 
2270f9bb09eSpooka bool
2280f9bb09eSpooka device_activation(device_t dev, devact_level_t level)
2290f9bb09eSpooka {
2300f9bb09eSpooka 	int active_flags;
2310f9bb09eSpooka 
2320f9bb09eSpooka 	active_flags = DVF_ACTIVE;
2330f9bb09eSpooka 	switch (level) {
2340f9bb09eSpooka 	case DEVACT_LEVEL_FULL:
2350f9bb09eSpooka 		active_flags |= DVF_CLASS_SUSPENDED;
2360f9bb09eSpooka 		/*FALLTHROUGH*/
2370f9bb09eSpooka 	case DEVACT_LEVEL_DRIVER:
2380f9bb09eSpooka 		active_flags |= DVF_DRIVER_SUSPENDED;
2390f9bb09eSpooka 		/*FALLTHROUGH*/
2400f9bb09eSpooka 	case DEVACT_LEVEL_BUS:
2410f9bb09eSpooka 		active_flags |= DVF_BUS_SUSPENDED;
2420f9bb09eSpooka 		break;
2430f9bb09eSpooka 	}
2440f9bb09eSpooka 
2450f9bb09eSpooka 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
2460f9bb09eSpooka }
2470f9bb09eSpooka 
2480f9bb09eSpooka bool
2490f9bb09eSpooka device_is_active(device_t dev)
2500f9bb09eSpooka {
2510f9bb09eSpooka 	int active_flags;
2520f9bb09eSpooka 
2530f9bb09eSpooka 	active_flags = DVF_ACTIVE;
2540f9bb09eSpooka 	active_flags |= DVF_CLASS_SUSPENDED;
2550f9bb09eSpooka 	active_flags |= DVF_DRIVER_SUSPENDED;
2560f9bb09eSpooka 	active_flags |= DVF_BUS_SUSPENDED;
2570f9bb09eSpooka 
2580f9bb09eSpooka 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
2590f9bb09eSpooka }
2600f9bb09eSpooka 
2610f9bb09eSpooka bool
2620f9bb09eSpooka device_is_enabled(device_t dev)
2630f9bb09eSpooka {
2640f9bb09eSpooka 	return (dev->dv_flags & DVF_ACTIVE) == DVF_ACTIVE;
2650f9bb09eSpooka }
2660f9bb09eSpooka 
2670f9bb09eSpooka bool
2680f9bb09eSpooka device_has_power(device_t dev)
2690f9bb09eSpooka {
2700f9bb09eSpooka 	int active_flags;
2710f9bb09eSpooka 
2720f9bb09eSpooka 	active_flags = DVF_ACTIVE | DVF_BUS_SUSPENDED;
2730f9bb09eSpooka 
2740f9bb09eSpooka 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
2750f9bb09eSpooka }
2760f9bb09eSpooka 
2770f9bb09eSpooka int
2780f9bb09eSpooka device_locator(device_t dev, u_int locnum)
2790f9bb09eSpooka {
2800f9bb09eSpooka 
2810f9bb09eSpooka 	KASSERT(dev->dv_locators != NULL);
2820f9bb09eSpooka 	return dev->dv_locators[locnum];
2830f9bb09eSpooka }
2840f9bb09eSpooka 
2850f9bb09eSpooka void *
2860f9bb09eSpooka device_private(device_t dev)
2870f9bb09eSpooka {
2880f9bb09eSpooka 
2890f9bb09eSpooka 	/*
2900f9bb09eSpooka 	 * The reason why device_private(NULL) is allowed is to simplify the
2910f9bb09eSpooka 	 * work of a lot of userspace request handlers (i.e., c/bdev
2920f9bb09eSpooka 	 * handlers) which grab cfdriver_t->cd_units[n].
2930f9bb09eSpooka 	 * It avoids having them test for it to be NULL and only then calling
2940f9bb09eSpooka 	 * device_private.
2950f9bb09eSpooka 	 */
2960f9bb09eSpooka 	return dev == NULL ? NULL : dev->dv_private;
2970f9bb09eSpooka }
2980f9bb09eSpooka 
299930df277Sriastradh void
300930df277Sriastradh device_set_private(device_t dev, void *private)
301930df277Sriastradh {
302930df277Sriastradh 
303930df277Sriastradh 	KASSERTMSG(dev->dv_private == NULL, "device_set_private(%p, %p):"
304930df277Sriastradh 	    " device %s already has private set to %p",
305930df277Sriastradh 	    dev, private, device_xname(dev), device_private(dev));
306930df277Sriastradh 	KASSERT(private != NULL);
307930df277Sriastradh 	dev->dv_private = private;
308930df277Sriastradh }
309930df277Sriastradh 
3100f9bb09eSpooka prop_dictionary_t
3110f9bb09eSpooka device_properties(device_t dev)
3120f9bb09eSpooka {
3130f9bb09eSpooka 
3140f9bb09eSpooka 	return dev->dv_properties;
3150f9bb09eSpooka }
3160f9bb09eSpooka 
3170f9bb09eSpooka /*
3180f9bb09eSpooka  * device_is_a:
3190f9bb09eSpooka  *
3200f9bb09eSpooka  *	Returns true if the device is an instance of the specified
3210f9bb09eSpooka  *	driver.
3220f9bb09eSpooka  */
3230f9bb09eSpooka bool
3240f9bb09eSpooka device_is_a(device_t dev, const char *dname)
3250f9bb09eSpooka {
32637a8d677Sthorpej 	if (dev == NULL || dev->dv_cfdriver == NULL) {
32737a8d677Sthorpej 		return false;
32837a8d677Sthorpej 	}
3290f9bb09eSpooka 
3300f9bb09eSpooka 	return strcmp(dev->dv_cfdriver->cd_name, dname) == 0;
3310f9bb09eSpooka }
3325e189a3dSthorpej 
3335e189a3dSthorpej /*
3345e189a3dSthorpej  * device_attached_to_iattr:
3355e189a3dSthorpej  *
3365e189a3dSthorpej  *	Returns true if the device attached to the specified interface
3375e189a3dSthorpej  *	attribute.
3385e189a3dSthorpej  */
3395e189a3dSthorpej bool
3405e189a3dSthorpej device_attached_to_iattr(device_t dev, const char *iattr)
3415e189a3dSthorpej {
3425e189a3dSthorpej 	cfdata_t cfdata = device_cfdata(dev);
3435e189a3dSthorpej 	const struct cfparent *pspec;
3445e189a3dSthorpej 
3455e189a3dSthorpej 	if (cfdata == NULL || (pspec = cfdata->cf_pspec) == NULL) {
3465e189a3dSthorpej 		return false;
3475e189a3dSthorpej 	}
3485e189a3dSthorpej 
3495e189a3dSthorpej 	return strcmp(pspec->cfp_iattr, iattr) == 0;
3505e189a3dSthorpej }
351105da683Sthorpej 
352105da683Sthorpej void
353105da683Sthorpej device_set_handle(device_t dev, devhandle_t handle)
354105da683Sthorpej {
355105da683Sthorpej 	dev->dv_handle = handle;
356105da683Sthorpej }
357105da683Sthorpej 
358105da683Sthorpej devhandle_t
359105da683Sthorpej device_handle(device_t dev)
360105da683Sthorpej {
361105da683Sthorpej 	return dev->dv_handle;
362105da683Sthorpej }
363105da683Sthorpej 
364105da683Sthorpej int
365*310d5db6Sthorpej device_call_generic(device_t dev, devhandle_t handle,
366*310d5db6Sthorpej     const struct device_call_generic *gen)
367105da683Sthorpej {
368105da683Sthorpej 	device_call_t call;
369105da683Sthorpej 	devhandle_t call_handle;
370105da683Sthorpej 
3714ecce2b2Sthorpej 	call = devhandle_lookup_device_call(handle, gen->name, &call_handle);
372105da683Sthorpej 	if (call == NULL) {
373105da683Sthorpej 		return ENOTSUP;
374105da683Sthorpej 	}
3754ecce2b2Sthorpej 	return call(dev, call_handle, gen->args);
376105da683Sthorpej }
377105da683Sthorpej 
378105da683Sthorpej int
379105da683Sthorpej device_enumerate_children(device_t dev,
380105da683Sthorpej     bool (*callback)(device_t, devhandle_t, void *),
381105da683Sthorpej     void *callback_arg)
382105da683Sthorpej {
383105da683Sthorpej 	struct device_enumerate_children_args args = {
384105da683Sthorpej 		.callback = callback,
385105da683Sthorpej 		.callback_arg = callback_arg,
386105da683Sthorpej 	};
387105da683Sthorpej 
3884ecce2b2Sthorpej 	return device_call(dev, DEVICE_ENUMERATE_CHILDREN(&args));
389105da683Sthorpej }
390