Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/subr_locator.c
- This file was added.
/* | |||||
* Copyright (c) 2021 M. Warner Losh <imp@FreeBSD.org> | |||||
* | |||||
* SPDX-License-Identifier: BSD-2-Clause | |||||
*/ | |||||
#include <sys/param.h> | |||||
#include <sys/locator.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/sbuf.h> | |||||
static MALLOC_DEFINE(M_LOCATOR, "locator", "Bus locator structures"); | |||||
typedef TAILQ_HEAD(locator_list, locator) locator_list_t; | |||||
typedef TAILQ_HEAD(d2f_list, d2fmap) d2f_list_t; | |||||
static locator_list_t locators; | |||||
static struct mtx locator_lock; | |||||
MTX_SYSINIT(locator_lock, &locator_lock, "Locator locking routines", MTX_DEF); | |||||
struct d2fmap | |||||
{ | |||||
driver_t *driver; | |||||
locator_node_fn fn; | |||||
TAILQ_ENTRY(d2fmap) link; /**< list linkage */ | |||||
}; | |||||
struct locator | |||||
{ | |||||
const char *name; | |||||
d2f_list_t drivers; | |||||
locator_node_fn def_fmt_fn; | |||||
TAILQ_ENTRY(locator) link; | |||||
}; | |||||
static locator_t * | |||||
locator_find_int(const char *locator) | |||||
{ | |||||
struct locator *loc; | |||||
mtx_lock(&locator_lock); | |||||
TAILQ_FOREACH(loc, &locators, link) { | |||||
if (strcmp(locator, loc->name) == 0) | |||||
break; | |||||
} | |||||
mtx_unlock(&locator_lock); | |||||
return loc; | |||||
} | |||||
const locator_t * | |||||
locator_find(const char *locator) | |||||
{ | |||||
return (locator_find_int(locator)); | |||||
} | |||||
static struct d2fmap * | |||||
locator_find_driver(const locator_t *loc, driver_t *driver) | |||||
{ | |||||
struct d2fmap *d2f; | |||||
mtx_lock(&locator_lock); | |||||
TAILQ_FOREACH(d2f, &loc->drivers, link) { | |||||
if (d2f->driver == driver) | |||||
break; | |||||
} | |||||
mtx_unlock(&locator_lock); | |||||
return (d2f); | |||||
} | |||||
static void | |||||
locator_bind_driver_int(locator_t *loc, driver_t *driver, locator_node_fn fn) | |||||
{ | |||||
struct d2fmap *d2f; | |||||
mtx_assert(&locator_lock, MA_OWNED); | |||||
d2f = locator_find_driver(loc, driver); | |||||
KASSERT(d2f == NULL, | |||||
("Duplicate locator %s registered for driver %s", loc->name, driver->name)); | |||||
d2f = malloc(sizeof(*d2f), M_LOCATOR, M_WAITOK); | |||||
d2f->driver = driver; | |||||
d2f->fn = fn; | |||||
TAILQ_INSERT_TAIL(&loc->drivers, d2f, link); | |||||
} | |||||
/** | |||||
* sysinit callback to register that the driver knows how to render a mode for | |||||
* the given locator. | |||||
*/ | |||||
void | |||||
locator_bind_driver(void *xlgll) | |||||
{ | |||||
const struct locator_glue_ll *lgll = xlgll; | |||||
locator_t *loc; | |||||
loc = locator_find_int(lgll->loc); | |||||
KASSERT(loc != NULL, | |||||
("No locator registered for %s\n", lgll->loc)); | |||||
mtx_lock(&locator_lock); | |||||
locator_bind_driver_int(loc, lgll->driver, lgll->fnp); | |||||
mtx_unlock(&locator_lock); | |||||
} | |||||
static void | |||||
locator_unbind_driver_int(locator_t *loc, driver_t *driver, locator_node_fn fn) | |||||
{ | |||||
struct d2fmap *d2f; | |||||
mtx_assert(&locator_lock, MA_OWNED); | |||||
d2f = locator_find_driver(loc, driver); | |||||
KASSERT(d2f != NULL, | |||||
("Unregistering locator %s driver %s can't be found", loc->name, driver->name)); | |||||
TAILQ_REMOVE(&loc->drivers, d2f, link); | |||||
} | |||||
void | |||||
locator_unbind_driver(void *xlgll) | |||||
{ | |||||
const struct locator_glue_ll *lgll = xlgll; | |||||
locator_t *loc; | |||||
loc = locator_find_int(lgll->loc); | |||||
KASSERT(loc != NULL, | |||||
("No locator registered for %s\n", lgll->loc)); | |||||
mtx_lock(&locator_lock); | |||||
locator_unbind_driver_int(loc, lgll->driver, lgll->fnp); | |||||
mtx_unlock(&locator_lock); | |||||
} | |||||
static struct d2fmap * | |||||
locator_driver_find(const locator_t *loc, driver_t *driver) | |||||
{ | |||||
struct d2fmap *d2f; | |||||
driver_t **bc; | |||||
mtx_assert(&locator_lock, MA_OWNED); | |||||
d2f = locator_find_driver(loc, driver); | |||||
if (d2f != NULL) | |||||
return (d2f); | |||||
bc = driver->baseclasses; | |||||
while (*bc != NULL) { | |||||
d2f = locator_find_driver(loc, *bc); | |||||
if (d2f != NULL) | |||||
return (d2f); | |||||
bc++; | |||||
} | |||||
return (NULL); | |||||
} | |||||
int | |||||
locator_device_path(const locator_t *loc, device_t dev, struct sbuf *sb) | |||||
{ | |||||
locator_node_fn fn; | |||||
struct d2fmap *d2f; | |||||
int error; | |||||
/* | |||||
* Base of the recursion: we're done, start to unwind. | |||||
*/ | |||||
if (dev == NULL) | |||||
return (0); | |||||
mtx_lock(&locator_lock); | |||||
d2f = locator_driver_find(loc, device_get_driver(device_get_parent(dev))); | |||||
if (d2f == NULL) | |||||
fn = loc->def_fmt_fn; | |||||
else | |||||
fn = d2f->fn; | |||||
error = fn(loc, dev, sb); | |||||
mtx_unlock(&locator_lock); | |||||
return (error); | |||||
} | |||||
void | |||||
locator_register(void *xlr) | |||||
{ | |||||
const struct locator_reg *lr = xlr; | |||||
locator_t *loc; | |||||
loc = malloc(sizeof(*loc), M_LOCATOR, M_WAITOK); | |||||
loc->name = lr->name; | |||||
loc->def_fmt_fn = lr->def_fmt_fn; | |||||
TAILQ_INIT(&loc->drivers); | |||||
mtx_lock(&locator_lock); | |||||
TAILQ_INSERT_TAIL(&locators, loc, link); | |||||
mtx_unlock(&locator_lock); | |||||
} | |||||
static int | |||||
acpi_defname(const locator_t *loc, device_t child, struct sbuf *sb) | |||||
{ | |||||
return (0); | |||||
} | |||||
LOCATOR(acpi, acpi_defname); | |||||
static int | |||||
freebsd_defname(const locator_t *loc, device_t child, struct sbuf *sb) | |||||
{ | |||||
int rv; | |||||
rv = locator_device_path(loc, device_get_parent(child), sb); | |||||
if (rv == 0) { | |||||
sbuf_printf(sb, "/%s", device_get_nameunit(child)); | |||||
} | |||||
return (0); | |||||
} | |||||
LOCATOR(freebsd, freebsd_defname); | |||||
static int | |||||
uefi_defname(const locator_t *loc, device_t child, struct sbuf *sb) | |||||
{ | |||||
return (locator_device_path(loc, device_get_parent(child), sb)); | |||||
} | |||||
LOCATOR(uefi, uefi_defname); |