diff options
author | dfr <dfr@FreeBSD.org> | 1998-06-10 10:57:29 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 1998-06-10 10:57:29 +0000 |
commit | 224577d6cf4d0daf37dddd81b9f9c646ad2be083 (patch) | |
tree | 345e0ea224736af311f2e28c0acb268d809bff9c /sys/kern/subr_bus.c | |
parent | 2e6fba7d51b32033eec1fc27efaa0f8e840825fe (diff) | |
download | FreeBSD-src-224577d6cf4d0daf37dddd81b9f9c646ad2be083.zip FreeBSD-src-224577d6cf4d0daf37dddd81b9f9c646ad2be083.tar.gz |
Add initial support for the FreeBSD/alpha kernel. This is very much a
work in progress and has never booted a real machine. Initial
development and testing was done using SimOS (see
http://simos.stanford.edu for details). On the SimOS simulator, this
port successfully reaches single-user mode and has been tested with
loads as high as one copy of /bin/ls :-).
Obtained from: partly from NetBSD/alpha
Diffstat (limited to 'sys/kern/subr_bus.c')
-rw-r--r-- | sys/kern/subr_bus.c | 854 |
1 files changed, 854 insertions, 0 deletions
diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c new file mode 100644 index 0000000..883666d --- /dev/null +++ b/sys/kern/subr_bus.c @@ -0,0 +1,854 @@ +/*- + * Copyright (c) 1997 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus_private.h> +#include <sys/systm.h> + +device_t +bus_get_device(bus_t bus) +{ + return bus->dev; +} + +void +bus_print_device(bus_t bus, device_t dev) +{ + printf("%s%d", device_get_name(dev), device_get_unit(dev)); + if (device_is_alive(dev)) { + if (device_get_desc(dev)) + printf(": <%s>", device_get_desc(dev)); + bus->ops->print_device(bus, dev); + } else + printf(" not found"); + printf("\n"); +} + +int +bus_read_ivar(bus_t bus, device_t dev, + int index, u_long *result) +{ + return bus->ops->read_ivar(bus, dev, index, result); +} + +int +bus_write_ivar(bus_t bus, device_t dev, + int index, u_long value) +{ + return bus->ops->write_ivar(bus, dev, index, value); +} + +int +bus_map_intr(bus_t bus, device_t dev, driver_intr_t *intr, void *arg) +{ + return bus->ops->map_intr(bus, dev, intr, arg); +} + +static devclass_list_t devclasses; + +static void +devclass_init(void) +{ + TAILQ_INIT(&devclasses); +} + +static devclass_t +devclass_find_internal(const char *classname, int create) +{ + devclass_t dc; + + for (dc = TAILQ_FIRST(&devclasses); dc; dc = TAILQ_NEXT(dc, link)) + if (!strcmp(dc->name, classname)) + return dc; + + if (create) { + dc = malloc(sizeof(struct devclass) + strlen(classname) + 1, + M_DEVBUF, M_NOWAIT); + if (!dc) + return NULL; + dc->name = (char*) (dc + 1); + strcpy(dc->name, classname); + dc->devices = NULL; + dc->maxunit = 0; + dc->nextunit = 0; + TAILQ_INIT(&dc->drivers); + TAILQ_INSERT_TAIL(&devclasses, dc, link); + } + + return dc; +} + +devclass_t +devclass_find(const char *classname) +{ + return devclass_find_internal(classname, FALSE); +} + +int +devclass_add_driver(devclass_t dc, driver_t *driver) +{ + /* + * Make sure the devclass which the driver is implementing exists. + */ + devclass_find_internal(driver->name, TRUE); + + TAILQ_INSERT_TAIL(&dc->drivers, driver, link); + + return 0; +} + +int +devclass_delete_driver(devclass_t dc, driver_t *driver) +{ + bus_t bus; + device_t dev; + int i; + int error; + + /* + * Disassociate from any devices. We iterate through all the + * devices attached to any bus in this class. + */ + for (i = 0; i < dc->maxunit; i++) { + if (dc->devices[i]) { + bus = dc->devices[i]->softc; + for (dev = TAILQ_FIRST(&bus->devices); dev; + dev = TAILQ_NEXT(dev, link)) + if (dev->driver == driver) { + if (error = device_detach(dev)) + return error; + device_set_driver(dev, NULL); + } + } + } + + TAILQ_REMOVE(&dc->drivers, driver, link); + return 0; +} + +driver_t * +devclass_find_driver(devclass_t dc, const char *classname) +{ + driver_t *driver; + + for (driver = TAILQ_FIRST(&dc->drivers); driver; + driver = TAILQ_NEXT(driver, link)) + if (!strcmp(driver->name, classname)) + return driver; + + return NULL; +} + +const char * +devclass_get_name(devclass_t dc) +{ + return dc->name; +} + +device_t +devclass_get_device(devclass_t dc, int unit) +{ + if (unit < 0 || unit >= dc->maxunit) + return NULL; + return dc->devices[unit]; +} + +void * +devclass_get_softc(devclass_t dc, int unit) +{ + device_t dev; + + if (unit < 0 || unit >= dc->maxunit) + return NULL; + dev = dc->devices[unit]; + if (!dev || dev->state < DS_ATTACHED) + return NULL; + return dev->softc; +} + +int +devclass_get_devices(devclass_t dc, device_t **devlistp, int *devcountp) +{ + int i; + int count; + device_t dev; + device_t *list; + + count = 0; + for (i = 0; i < dc->maxunit; i++) + if (dc->devices[i]) + count++; + + list = malloc(count * sizeof(device_t), M_TEMP, M_NOWAIT); + if (!list) + return ENOMEM; + + count = 0; + for (i = 0; i < dc->maxunit; i++) + if (dc->devices[i]) { + list[count] = dc->devices[i]; + count++; + } + + *devlistp = list; + *devcountp = count; + + return 0; +} + +int +devclass_get_maxunit(devclass_t dc) +{ + return dc->maxunit; +} + +static int +devclass_alloc_unit(devclass_t dc, int *unitp) +{ + int unit = *unitp; + + /* + * If we have been given a wired unit number, check for existing + * device. + */ + if (unit != -1) { + device_t dev; + dev = devclass_get_device(dc, unit); + if (dev) { + printf("devclass_alloc_unit: %s%d already exists, using next available unit number\n", dc->name, unit); + unit = -1; + } + } + + if (unit == -1) { + unit = dc->nextunit; + dc->nextunit++; + } else if (dc->nextunit <= unit) + dc->nextunit = unit + 1; + + if (unit >= dc->maxunit) { + device_t *newlist; + int newsize; + + newsize = (dc->maxunit ? 2 * dc->maxunit + : MINALLOCSIZE / sizeof(device_t)); + newlist = malloc(sizeof(device_t) * newsize, M_DEVBUF, M_NOWAIT); + if (!newlist) + return ENOMEM; + bcopy(dc->devices, newlist, sizeof(device_t) * dc->maxunit); + bzero(newlist + dc->maxunit, + sizeof(device_t) * (newsize - dc->maxunit)); + if (dc->devices) + free(dc->devices, M_DEVBUF); + dc->devices = newlist; + dc->maxunit = newsize; + } + + *unitp = unit; + return 0; +} + +static int +devclass_add_device(devclass_t dc, device_t dev) +{ + int error; + + if (error = devclass_alloc_unit(dc, &dev->unit)) + return error; + dc->devices[dev->unit] = dev; + dev->devclass = dc; + return 0; +} + +static int +devclass_delete_device(devclass_t dc, device_t dev) +{ + if (dev->devclass != dc + || dc->devices[dev->unit] != dev) + panic("devclass_delete_device: inconsistent device class"); + dc->devices[dev->unit] = NULL; + if (dev->flags & DF_WILDCARD) + dev->unit = -1; + dev->devclass = NULL; + while (dc->nextunit > 0 && dc->devices[dc->nextunit - 1] == NULL) + dc->nextunit--; + return 0; +} + +static device_t +make_device(bus_t bus, const char *name, + int unit, void *ivars) +{ + driver_t *driver; + device_t dev; + devclass_t dc; + int error; + + if (name) { + dc = devclass_find_internal(name, TRUE); + if (!dc) { + printf("make_device: can't find device class %s\n", name); + return NULL; + } + + if (error = devclass_alloc_unit(dc, &unit)) + return NULL; + } else + dc = NULL; + + dev = malloc(sizeof(struct device), M_DEVBUF, M_NOWAIT); + if (!dev) + return 0; + + dev->parent = bus; + dev->driver = NULL; + dev->devclass = dc; + dev->unit = unit; + dev->desc = NULL; + dev->busy = 0; + dev->flags = DF_ENABLED; + if (unit == -1) + dev->flags |= DF_WILDCARD; + if (name) + dev->flags |= DF_FIXEDCLASS; + dev->ivars = ivars; + dev->softc = NULL; + + if (dc) + dc->devices[unit] = dev; + + dev->state = DS_NOTPRESENT; + + return dev; +} + +void +bus_init(bus_t bus, device_t dev, bus_ops_t *ops) +{ + bus->ops = ops; + bus->dev = dev; + TAILQ_INIT(&bus->devices); +} + +device_t +bus_add_device(bus_t bus, const char *name, int unit, void *ivars) +{ + device_t dev; + + dev = make_device(bus, name, unit, ivars); + + TAILQ_INSERT_TAIL(&bus->devices, dev, link); + + return dev; +} + +device_t +bus_add_device_after(bus_t bus, device_t place, const char *name, + int unit, void *ivars) +{ + device_t dev; + + dev = make_device(bus, name, unit, ivars); + + if (place) { + TAILQ_INSERT_AFTER(&bus->devices, place, dev, link); + } else { + TAILQ_INSERT_HEAD(&bus->devices, dev, link); + } + + return dev; +} + +int +bus_delete_device(bus_t bus, device_t dev) +{ + int error; + + if (error = device_detach(dev)) + return error; + if (dev->devclass) + devclass_delete_device(dev->devclass, dev); + TAILQ_REMOVE(&bus->devices, dev, link); + free(dev, M_DEVBUF); + + return 0; +} + +/* + * Find only devices attached to this bus. + */ +device_t +bus_find_device(bus_t bus, const char *classname, int unit) +{ + devclass_t dc; + device_t dev; + + dc = devclass_find(classname); + if (!dc) + return NULL; + + dev = devclass_get_device(dc, unit); + if (dev && dev->parent == bus) + return dev; + return NULL; +} + +static driver_t * +first_matching_driver(devclass_t dc, device_t dev) +{ + if (dev->devclass) + return devclass_find_driver(dc, dev->devclass->name); + else + return TAILQ_FIRST(&dc->drivers); +} + +static driver_t * +next_matching_driver(devclass_t dc, device_t dev, driver_t *last) +{ + if (dev->devclass) { + driver_t *driver; + for (driver = TAILQ_NEXT(last, link); driver; + driver = TAILQ_NEXT(driver, link)) + if (!strcmp(dev->devclass->name, driver->name)) + return driver; + return NULL; + } else + return TAILQ_NEXT(last, link); +} + +static int +bus_probe_device(bus_t bus, device_t dev) +{ + devclass_t dc; + driver_t *driver; + void *softc; + + dc = bus->dev->devclass; + if (dc == NULL) + panic("bus_probe_device: bus' device has no devclass"); + + if (dev->state == DS_ALIVE) + return 0; + + for (driver = first_matching_driver(dc, dev); + driver; + driver = next_matching_driver(dc, dev, driver)) { + device_set_driver(dev, driver); + if (driver->probe(bus, dev) == 0) { + if (!dev->devclass) + device_set_devclass(dev, driver->name); + dev->state = DS_ALIVE; + return 0; + } + } + + return ENXIO; +} + +int +bus_generic_attach(bus_t parent, device_t busdev) +{ + bus_t bus = busdev->softc; + device_t dev; + int error; + + for (dev = TAILQ_FIRST(&bus->devices); + dev; dev = TAILQ_NEXT(dev, link)) + device_probe_and_attach(dev); + + return 0; +} + +int +bus_generic_detach(bus_t parent, device_t busdev) +{ + bus_t bus = busdev->softc; + device_t dev; + int error; + + if (busdev->state != DS_ATTACHED) + return EBUSY; + + for (dev = TAILQ_FIRST(&bus->devices); + dev; dev = TAILQ_NEXT(dev, link)) + device_detach(dev); + + return 0; +} + +int +bus_generic_shutdown(bus_t parent, device_t busdev) +{ + bus_t bus = busdev->softc; + device_t dev; + + for (dev = TAILQ_FIRST(&bus->devices); + dev; dev = TAILQ_NEXT(dev, link)) + device_shutdown(dev); + + return 0; +} + +bus_t +device_get_parent(device_t dev) +{ + return dev->parent; +} + +driver_t * +device_get_driver(device_t dev) +{ + return dev->driver; +} + +devclass_t +device_get_devclass(device_t dev) +{ + return dev->devclass; +} + +const char * +device_get_name(device_t dev) +{ + if (dev->devclass) + return devclass_get_name(dev->devclass); + return NULL; +} + +int +device_get_unit(device_t dev) +{ + return dev->unit; +} + +const char * +device_get_desc(device_t dev) +{ + return dev->desc; +} + +void +device_set_desc(device_t dev, const char* desc) +{ + dev->desc = desc; +} + +void * +device_get_softc(device_t dev) +{ + return dev->softc; +} + +void * +device_get_ivars(device_t dev) +{ + return dev->ivars; +} + +device_state_t +device_get_state(device_t dev) +{ + return dev->state; +} + +void +device_enable(device_t dev) +{ + dev->flags |= DF_ENABLED; +} + +void +device_disable(device_t dev) +{ + dev->flags &= ~DF_ENABLED; +} + +void +device_busy(device_t dev) +{ + if (dev->state < DS_ATTACHED) + panic("device_busy: called for unattached device"); + if (dev->busy == 0 && dev->parent) + device_busy(dev->parent->dev); + dev->busy++; + dev->state = DS_BUSY; +} + +void +device_unbusy(device_t dev) +{ + if (dev->state != DS_BUSY) + panic("device_unbusy: called for non-busy device"); + dev->busy--; + if (dev->busy == 0) { + if (dev->parent->dev) + device_unbusy(dev->parent->dev); + dev->state = DS_ATTACHED; + } +} + +int +device_is_enabled(device_t dev) +{ + return (dev->flags & DF_ENABLED) != 0; +} + +int +device_is_alive(device_t dev) +{ + return dev->state >= DS_ALIVE; +} + +int +device_set_devclass(device_t dev, const char *classname) +{ + devclass_t dc; + + if (dev->devclass) { + printf("device_set_devclass: device class already set\n"); + return EINVAL; + } + + dc = devclass_find_internal(classname, TRUE); + if (!dc) + return ENOMEM; + + return devclass_add_device(dc, dev); +} + +int +device_set_driver(device_t dev, driver_t *driver) +{ + if (dev->state >= DS_ATTACHED) + return EBUSY; + + if (dev->driver == driver) + return 0; + + if (dev->softc) { + free(dev->softc, M_DEVBUF); + dev->softc = NULL; + } + dev->driver = driver; + if (driver) { + dev->softc = malloc(driver->softc, M_DEVBUF, M_NOWAIT); + bzero(dev->softc, driver->softc); + } + return 0; +} + +int +device_probe_and_attach(device_t dev) +{ + bus_t bus = dev->parent; + int error; + + if (dev->state >= DS_ALIVE) + return 0; + + if (dev->flags & DF_ENABLED) { + bus_probe_device(bus, dev); + bus_print_device(bus, dev); + if (dev->state == DS_ALIVE) { + error = dev->driver->attach(bus, dev); + if (!error) + dev->state = DS_ATTACHED; + else { + printf("device_probe_and_attach: %s%n attach returned %d\n", + dev->driver->name, dev->unit, error); + device_set_driver(dev, NULL); + dev->state = DS_NOTPRESENT; + } + } + } else + printf("%s%d: disabled, not probed.\n", + dev->devclass->name, dev->unit); + + return 0; +} + +int +device_detach(device_t dev) +{ + int error; + + if (dev->state == DS_BUSY) + return EBUSY; + if (dev->state != DS_ATTACHED) + return 0; + + if (dev->driver->detach) { + if (error = dev->driver->detach(dev->parent, dev)) + return error; + } else + return EBUSY; + + if (!(dev->flags & DF_FIXEDCLASS)) + devclass_delete_device(dev->devclass, dev); + + dev->state = DS_NOTPRESENT; + device_set_driver(dev, NULL); + + return 0; +} + +int +device_shutdown(device_t dev) +{ + if (dev->state < DS_ATTACHED) + return 0; + if (dev->driver->shutdown) + return dev->driver->shutdown(dev->parent, dev); + else + return 0; +} + +void +null_print_device(bus_t bus, device_t dev) +{ +} + +int +null_read_ivar(bus_t bus, device_t dev, int index, u_long* result) +{ + return ENOENT; +} + +int +null_write_ivar(bus_t bus, device_t dev, int index, u_long value) +{ + return ENOENT; +} + +int +null_map_intr(bus_t bus, device_t dev, driver_intr_t *intr, void *arg) +{ + /* Propagate up the bus hierarchy until someone handles it. */ + if (bus->dev) + return bus_map_intr(bus->dev->parent, bus->dev, intr, arg); + else + return EINVAL; +} + +bus_ops_t null_bus_ops = { + null_print_device, + null_read_ivar, + null_write_ivar, + null_map_intr, +}; + +static void +root_bus_print_device(bus_t bus, device_t dev) +{ +} + +static int root_bus_map_intr(bus_t bus, device_t dev, + driver_intr_t *intr, void *arg) +{ + return EINVAL; +} + +static bus_ops_t root_bus_ops = { + root_bus_print_device, + null_read_ivar, + null_write_ivar, + root_bus_map_intr, +}; + +static struct bus the_root_bus; +bus_t root_bus = &the_root_bus; +device_t root_device; +devclass_t root_devclass; + +static int +root_bus_module_handler(module_t mod, modeventtype_t what, void* arg) +{ + switch (what) { + case MOD_LOAD: + devclass_init(); + root_device = make_device(NULL, "root", 0, NULL); + root_device->state = DS_ATTACHED; + bus_init(root_bus, root_device, &root_bus_ops); + root_devclass = devclass_find("root"); + return 0; + } + + return 0; +} + +static moduledata_t root_bus_mod = { + "rootbus", + root_bus_module_handler, + 0 +}; +DECLARE_MODULE(rootbus, root_bus_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); + +void +root_bus_configure() +{ + device_t dev; + int error; + + for (dev = TAILQ_FIRST(&root_bus->devices); dev; + dev = TAILQ_NEXT(dev, link)) { + device_probe_and_attach(dev); + } +} + +int +driver_module_handler(module_t mod, modeventtype_t what, void* arg) +{ + struct driver_module_data* data = (struct driver_module_data*) arg; + devclass_t bus_devclass = devclass_find_internal(data->busname, TRUE); + int error; + + switch (what) { + case MOD_LOAD: + if (error = devclass_add_driver(bus_devclass, + data->driver)) + return error; + *data->devclass = + devclass_find_internal(data->driver->name, TRUE); + break; + + case MOD_UNLOAD: + if (error = devclass_delete_driver(bus_devclass, + data->driver)) + return error; + break; + } + + if (data->chainevh) + return data->chainevh(mod, what, data->chainarg); + else + return 0; +} |