Home | History | Annotate | Line # | Download | only in kern
      1 /*	$NetBSD: subr_device.c,v 1.14 2024/08/27 13:44:55 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.14 2024/08/27 13:44:55 thorpej Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/device.h>
     34 #include <sys/device_impl.h>
     35 #include <sys/systm.h>
     36 
     37 #include <sys/device_calls.h>
     38 
     39 /* Root device. */
     40 device_t			root_device;
     41 
     42 /*
     43  * devhandle_t accessors / mutators.
     44  */
     45 
     46 static bool
     47 devhandle_is_valid_internal(const devhandle_t * const handlep)
     48 {
     49 	if (handlep->impl == NULL) {
     50 		return false;
     51 	}
     52 	return handlep->impl->type != DEVHANDLE_TYPE_INVALID;
     53 }
     54 
     55 bool
     56 devhandle_is_valid(devhandle_t handle)
     57 {
     58 	return devhandle_is_valid_internal(&handle);
     59 }
     60 
     61 devhandle_t
     62 devhandle_invalid(void)
     63 {
     64 	static const devhandle_t invalid_devhandle = {
     65 		.impl = NULL,
     66 		.uintptr = 0,
     67 	};
     68 	return invalid_devhandle;
     69 }
     70 
     71 devhandle_type_t
     72 devhandle_type(devhandle_t handle)
     73 {
     74 	if (!devhandle_is_valid_internal(&handle)) {
     75 		return DEVHANDLE_TYPE_INVALID;
     76 	}
     77 
     78 	return handle.impl->type;
     79 }
     80 
     81 int
     82 devhandle_compare(devhandle_t handle1, devhandle_t handle2)
     83 {
     84 	devhandle_type_t type1 = devhandle_type(handle1);
     85 	devhandle_type_t type2 = devhandle_type(handle2);
     86 
     87 	if (type1 == DEVHANDLE_TYPE_INVALID) {
     88 		return -1;
     89 	}
     90 	if (type2 == DEVHANDLE_TYPE_INVALID) {
     91 		return 1;
     92 	}
     93 
     94 	if (type1 < type2) {
     95 		return -1;
     96 	}
     97 	if (type1 > type2) {
     98 		return 1;
     99 	}
    100 
    101 	/* For private handles, we also compare the impl pointers. */
    102 	if (type1 == DEVHANDLE_TYPE_PRIVATE) {
    103 		intptr_t impl1 = (intptr_t)handle1.impl;
    104 		intptr_t impl2 = (intptr_t)handle2.impl;
    105 
    106 		if (impl1 < impl2) {
    107 			return -1;
    108 		}
    109 		if (impl1 > impl2) {
    110 			return 1;
    111 		}
    112 	}
    113 
    114 	if (handle1.integer < handle2.integer) {
    115 		return -1;
    116 	}
    117 	if (handle1.integer > handle2.integer) {
    118 		return 1;
    119 	}
    120 
    121 	return 0;
    122 }
    123 
    124 device_call_t
    125 devhandle_lookup_device_call(devhandle_t handle, const char *name,
    126     devhandle_t *call_handlep)
    127 {
    128 	const struct devhandle_impl *impl;
    129 	device_call_t call;
    130 
    131 	/*
    132 	 * The back-end can override the handle to use for the call,
    133 	 * if needed.
    134 	 */
    135 	*call_handlep = handle;
    136 
    137 	for (impl = handle.impl; impl != NULL; impl = impl->super) {
    138 		if (impl->lookup_device_call != NULL) {
    139 			call = impl->lookup_device_call(handle, name,
    140 			    call_handlep);
    141 			if (call != NULL) {
    142 				return call;
    143 			}
    144 		}
    145 	}
    146 	return NULL;
    147 }
    148 
    149 void
    150 devhandle_impl_subclass(struct devhandle_impl *new_impl,
    151     const struct devhandle_impl *super,
    152     device_call_t (*new_lookup)(devhandle_t, const char *, devhandle_t *))
    153 {
    154 	new_impl->type = super->type;
    155 	new_impl->super = super;
    156 	new_impl->lookup_device_call = new_lookup;
    157 }
    158 
    159 /*
    160  * Helper function that provides a short-hand method of the common
    161  * "subclass a device handle" flow.
    162  */
    163 devhandle_t
    164 devhandle_subclass(devhandle_t handle,
    165     struct devhandle_impl *new_impl,
    166     device_call_t (*new_lookup)(devhandle_t, const char *, devhandle_t *))
    167 {
    168 	devhandle_impl_subclass(new_impl, handle.impl, new_lookup);
    169 	handle.impl = new_impl;
    170 
    171 	return handle;
    172 }
    173 
    174 /*
    175  * Accessor functions for the device_t type.
    176  */
    177 
    178 devclass_t
    179 device_class(device_t dev)
    180 {
    181 
    182 	return dev->dv_class;
    183 }
    184 
    185 cfdata_t
    186 device_cfdata(device_t dev)
    187 {
    188 
    189 	return dev->dv_cfdata;
    190 }
    191 
    192 cfdriver_t
    193 device_cfdriver(device_t dev)
    194 {
    195 
    196 	return dev->dv_cfdriver;
    197 }
    198 
    199 cfattach_t
    200 device_cfattach(device_t dev)
    201 {
    202 
    203 	return dev->dv_cfattach;
    204 }
    205 
    206 int
    207 device_unit(device_t dev)
    208 {
    209 
    210 	return dev->dv_unit;
    211 }
    212 
    213 const char *
    214 device_xname(device_t dev)
    215 {
    216 
    217 	return dev->dv_xname;
    218 }
    219 
    220 device_t
    221 device_parent(device_t dev)
    222 {
    223 
    224 	return dev->dv_parent;
    225 }
    226 
    227 bool
    228 device_activation(device_t dev, devact_level_t level)
    229 {
    230 	int active_flags;
    231 
    232 	active_flags = DVF_ACTIVE;
    233 	switch (level) {
    234 	case DEVACT_LEVEL_FULL:
    235 		active_flags |= DVF_CLASS_SUSPENDED;
    236 		/*FALLTHROUGH*/
    237 	case DEVACT_LEVEL_DRIVER:
    238 		active_flags |= DVF_DRIVER_SUSPENDED;
    239 		/*FALLTHROUGH*/
    240 	case DEVACT_LEVEL_BUS:
    241 		active_flags |= DVF_BUS_SUSPENDED;
    242 		break;
    243 	}
    244 
    245 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
    246 }
    247 
    248 bool
    249 device_is_active(device_t dev)
    250 {
    251 	int active_flags;
    252 
    253 	active_flags = DVF_ACTIVE;
    254 	active_flags |= DVF_CLASS_SUSPENDED;
    255 	active_flags |= DVF_DRIVER_SUSPENDED;
    256 	active_flags |= DVF_BUS_SUSPENDED;
    257 
    258 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
    259 }
    260 
    261 bool
    262 device_is_enabled(device_t dev)
    263 {
    264 	return (dev->dv_flags & DVF_ACTIVE) == DVF_ACTIVE;
    265 }
    266 
    267 bool
    268 device_has_power(device_t dev)
    269 {
    270 	int active_flags;
    271 
    272 	active_flags = DVF_ACTIVE | DVF_BUS_SUSPENDED;
    273 
    274 	return (dev->dv_flags & active_flags) == DVF_ACTIVE;
    275 }
    276 
    277 int
    278 device_locator(device_t dev, u_int locnum)
    279 {
    280 
    281 	KASSERT(dev->dv_locators != NULL);
    282 	return dev->dv_locators[locnum];
    283 }
    284 
    285 void *
    286 device_private(device_t dev)
    287 {
    288 
    289 	/*
    290 	 * The reason why device_private(NULL) is allowed is to simplify the
    291 	 * work of a lot of userspace request handlers (i.e., c/bdev
    292 	 * handlers) which grab cfdriver_t->cd_units[n].
    293 	 * It avoids having them test for it to be NULL and only then calling
    294 	 * device_private.
    295 	 */
    296 	return dev == NULL ? NULL : dev->dv_private;
    297 }
    298 
    299 void
    300 device_set_private(device_t dev, void *private)
    301 {
    302 
    303 	KASSERTMSG(dev->dv_private == NULL, "device_set_private(%p, %p):"
    304 	    " device %s already has private set to %p",
    305 	    dev, private, device_xname(dev), device_private(dev));
    306 	KASSERT(private != NULL);
    307 	dev->dv_private = private;
    308 }
    309 
    310 prop_dictionary_t
    311 device_properties(device_t dev)
    312 {
    313 
    314 	return dev->dv_properties;
    315 }
    316 
    317 /*
    318  * device_is_a:
    319  *
    320  *	Returns true if the device is an instance of the specified
    321  *	driver.
    322  */
    323 bool
    324 device_is_a(device_t dev, const char *dname)
    325 {
    326 	if (dev == NULL || dev->dv_cfdriver == NULL) {
    327 		return false;
    328 	}
    329 
    330 	return strcmp(dev->dv_cfdriver->cd_name, dname) == 0;
    331 }
    332 
    333 /*
    334  * device_attached_to_iattr:
    335  *
    336  *	Returns true if the device attached to the specified interface
    337  *	attribute.
    338  */
    339 bool
    340 device_attached_to_iattr(device_t dev, const char *iattr)
    341 {
    342 	cfdata_t cfdata = device_cfdata(dev);
    343 	const struct cfparent *pspec;
    344 
    345 	if (cfdata == NULL || (pspec = cfdata->cf_pspec) == NULL) {
    346 		return false;
    347 	}
    348 
    349 	return strcmp(pspec->cfp_iattr, iattr) == 0;
    350 }
    351 
    352 void
    353 device_set_handle(device_t dev, devhandle_t handle)
    354 {
    355 	dev->dv_handle = handle;
    356 }
    357 
    358 devhandle_t
    359 device_handle(device_t dev)
    360 {
    361 	return dev->dv_handle;
    362 }
    363 
    364 int
    365 device_call_generic(device_t dev, devhandle_t handle,
    366     const struct device_call_generic *gen)
    367 {
    368 	device_call_t call;
    369 	devhandle_t call_handle;
    370 
    371 	call = devhandle_lookup_device_call(handle, gen->name, &call_handle);
    372 	if (call == NULL) {
    373 		return ENOTSUP;
    374 	}
    375 	return call(dev, call_handle, gen->args);
    376 }
    377 
    378 int
    379 device_enumerate_children(device_t dev,
    380     bool (*callback)(device_t, devhandle_t, void *),
    381     void *callback_arg)
    382 {
    383 	struct device_enumerate_children_args args = {
    384 		.callback = callback,
    385 		.callback_arg = callback_arg,
    386 	};
    387 
    388 	return device_call(dev, DEVICE_ENUMERATE_CHILDREN(&args));
    389 }
    390