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