summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/kern/subr_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/kern/subr_bus.c')
-rw-r--r--freebsd/sys/kern/subr_bus.c251
1 files changed, 198 insertions, 53 deletions
diff --git a/freebsd/sys/kern/subr_bus.c b/freebsd/sys/kern/subr_bus.c
index 51717b49..78a803e0 100644
--- a/freebsd/sys/kern/subr_bus.c
+++ b/freebsd/sys/kern/subr_bus.c
@@ -430,8 +430,8 @@ static void
devinit(void)
{
#ifndef __rtems__
- devctl_dev = make_dev(&dev_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
- "devctl");
+ devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL,
+ UID_ROOT, GID_WHEEL, 0600, "devctl");
mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF);
cv_init(&devsoftc.cv, "dev cv");
TAILQ_INIT(&devsoftc.devq);
@@ -1058,10 +1058,12 @@ devclass_find(const char *classname)
* is called by devclass_add_driver to accomplish the recursive
* notification of all the children classes of dc, as well as dc.
* Each layer will have BUS_DRIVER_ADDED() called for all instances of
- * the devclass. We do a full search here of the devclass list at
- * each iteration level to save storing children-lists in the devclass
- * structure. If we ever move beyond a few dozen devices doing this,
- * we may need to reevaluate...
+ * the devclass.
+ *
+ * We do a full search here of the devclass list at each iteration
+ * level to save storing children-lists in the devclass structure. If
+ * we ever move beyond a few dozen devices doing this, we may need to
+ * reevaluate...
*
* @param dc the devclass to edit
* @param driver the driver that was just added
@@ -1156,6 +1158,77 @@ devclass_add_driver(devclass_t dc, driver_t *driver, int pass, devclass_t *dcp)
}
/**
+ * @brief Register that a device driver has been deleted from a devclass
+ *
+ * Register that a device driver has been removed from a devclass.
+ * This is called by devclass_delete_driver to accomplish the
+ * recursive notification of all the children classes of busclass, as
+ * well as busclass. Each layer will attempt to detach the driver
+ * from any devices that are children of the bus's devclass. The function
+ * will return an error if a device fails to detach.
+ *
+ * We do a full search here of the devclass list at each iteration
+ * level to save storing children-lists in the devclass structure. If
+ * we ever move beyond a few dozen devices doing this, we may need to
+ * reevaluate...
+ *
+ * @param busclass the devclass of the parent bus
+ * @param dc the devclass of the driver being deleted
+ * @param driver the driver being deleted
+ */
+static int
+devclass_driver_deleted(devclass_t busclass, devclass_t dc, driver_t *driver)
+{
+ devclass_t parent;
+ device_t dev;
+ int error, i;
+
+ /*
+ * Disassociate from any devices. We iterate through all the
+ * devices in the devclass of the driver and detach any which are
+ * using the driver and which have a parent in the devclass which
+ * we are deleting from.
+ *
+ * Note that since a driver can be in multiple devclasses, we
+ * should not detach devices which are not children of devices in
+ * the affected devclass.
+ */
+ for (i = 0; i < dc->maxunit; i++) {
+ if (dc->devices[i]) {
+ dev = dc->devices[i];
+ if (dev->driver == driver && dev->parent &&
+ dev->parent->devclass == busclass) {
+ if ((error = device_detach(dev)) != 0)
+ return (error);
+ BUS_PROBE_NOMATCH(dev->parent, dev);
+ devnomatch(dev);
+ dev->flags |= DF_DONENOMATCH;
+ }
+ }
+ }
+
+ /*
+ * Walk through the children classes. Since we only keep a
+ * single parent pointer around, we walk the entire list of
+ * devclasses looking for children. We set the
+ * DC_HAS_CHILDREN flag when a child devclass is created on
+ * the parent, so we only walk the list for those devclasses
+ * that have children.
+ */
+ if (!(busclass->flags & DC_HAS_CHILDREN))
+ return (0);
+ parent = busclass;
+ TAILQ_FOREACH(busclass, &devclasses, link) {
+ if (busclass->parent == parent) {
+ error = devclass_driver_deleted(busclass, dc, driver);
+ if (error)
+ return (error);
+ }
+ }
+ return (0);
+}
+
+/**
* @brief Delete a device driver from a device class
*
* Delete a device driver from a devclass. This is normally called
@@ -1174,8 +1247,6 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver)
{
devclass_t dc = devclass_find(driver->name);
driverlink_t dl;
- device_t dev;
- int i;
int error;
PDEBUG(("%s from devclass %s", driver->name, DEVCLANAME(busclass)));
@@ -1197,27 +1268,9 @@ devclass_delete_driver(devclass_t busclass, driver_t *driver)
return (ENOENT);
}
- /*
- * Disassociate from any devices. We iterate through all the
- * devices in the devclass of the driver and detach any which are
- * using the driver and which have a parent in the devclass which
- * we are deleting from.
- *
- * Note that since a driver can be in multiple devclasses, we
- * should not detach devices which are not children of devices in
- * the affected devclass.
- */
- for (i = 0; i < dc->maxunit; i++) {
- if (dc->devices[i]) {
- dev = dc->devices[i];
- if (dev->driver == driver && dev->parent &&
- dev->parent->devclass == busclass) {
- if ((error = device_detach(dev)) != 0)
- return (error);
- device_set_driver(dev, NULL);
- }
- }
- }
+ error = devclass_driver_deleted(busclass, dc, driver);
+ if (error != 0)
+ return (error);
TAILQ_REMOVE(&busclass->drivers, dl, link);
free(dl, M_BUS);
@@ -1889,7 +1942,7 @@ device_delete_child(device_t dev, device_t child)
PDEBUG(("%s from %s", DEVICENAME(child), DEVICENAME(dev)));
/* remove children first */
- while ( (grandchild = TAILQ_FIRST(&child->children)) ) {
+ while ((grandchild = TAILQ_FIRST(&child->children)) != NULL) {
error = device_delete_child(child, grandchild);
if (error)
return (error);
@@ -1908,6 +1961,39 @@ device_delete_child(device_t dev, device_t child)
}
/**
+ * @brief Delete all children devices of the given device, if any.
+ *
+ * This function deletes all children devices of the given device, if
+ * any, using the device_delete_child() function for each device it
+ * finds. If a child device cannot be deleted, this function will
+ * return an error code.
+ *
+ * @param dev the parent device
+ *
+ * @retval 0 success
+ * @retval non-zero a device would not detach
+ */
+int
+device_delete_children(device_t dev)
+{
+ device_t child;
+ int error;
+
+ PDEBUG(("Deleting all children of %s", DEVICENAME(dev)));
+
+ error = 0;
+
+ while ((child = TAILQ_FIRST(&dev->children)) != NULL) {
+ error = device_delete_child(dev, child);
+ if (error) {
+ PDEBUG(("Failed deleting %s", DEVICENAME(child)));
+ break;
+ }
+ }
+ return (error);
+}
+
+/**
* @brief Find a device given a unit number
*
* This is similar to devclass_get_devices() but only searches for
@@ -2001,19 +2087,23 @@ device_probe_child(device_t dev, device_t child)
for (dl = first_matching_driver(dc, child);
dl;
dl = next_matching_driver(dc, child, dl)) {
-
/* If this driver's pass is too high, then ignore it. */
if (dl->pass > bus_current_pass)
continue;
PDEBUG(("Trying %s", DRIVERNAME(dl->driver)));
- device_set_driver(child, dl->driver);
+ result = device_set_driver(child, dl->driver);
+ if (result == ENOMEM)
+ return (result);
+ else if (result != 0)
+ continue;
if (!hasclass) {
- if (device_set_devclass(child, dl->driver->name)) {
- printf("driver bug: Unable to set devclass (devname: %s)\n",
- (child ? device_get_name(child) :
- "no device"));
- device_set_driver(child, NULL);
+ if (device_set_devclass(child,
+ dl->driver->name) != 0) {
+ printf("driver bug: Unable to set "
+ "devclass (devname: %s)\n",
+ device_get_name(child));
+ (void)device_set_driver(child, NULL);
continue;
}
}
@@ -2029,7 +2119,7 @@ device_probe_child(device_t dev, device_t child)
/* Reset flags and devclass before the next probe. */
child->devflags = 0;
if (!hasclass)
- device_set_devclass(child, NULL);
+ (void)device_set_devclass(child, NULL);
/*
* If the driver returns SUCCESS, there can be
@@ -2046,7 +2136,7 @@ device_probe_child(device_t dev, device_t child)
* certainly doesn't match.
*/
if (result > 0) {
- device_set_driver(child, NULL);
+ (void)device_set_driver(child, NULL);
continue;
}
@@ -2083,7 +2173,7 @@ device_probe_child(device_t dev, device_t child)
/* XXX What happens if we rebid and got no best? */
if (best) {
/*
- * If this device was atached, and we were asked to
+ * If this device was attached, and we were asked to
* rescan, and it is a different driver, then we have
* to detach the old driver and reattach this new one.
* Note, we don't have to check for DF_REBID here
@@ -2109,7 +2199,9 @@ device_probe_child(device_t dev, device_t child)
if (result != 0)
return (result);
}
- device_set_driver(child, best->driver);
+ result = device_set_driver(child, best->driver);
+ if (result != 0)
+ return (result);
#ifndef __rtems__
resource_int_value(best->driver->name, child->unit,
"flags", &child->devflags);
@@ -2171,6 +2263,11 @@ device_get_children(device_t dev, device_t **devlistp, int *devcountp)
TAILQ_FOREACH(child, &dev->children, link) {
count++;
}
+ if (count == 0) {
+ *devlistp = NULL;
+ *devcountp = 0;
+ return (0);
+ }
#ifdef __rtems__
/* malloc(0) may return NULL */
@@ -2471,12 +2568,13 @@ device_disable(device_t dev)
void
device_busy(device_t dev)
{
- if (dev->state < DS_ATTACHED)
+ if (dev->state < DS_ATTACHING)
panic("device_busy: called for unattached device");
if (dev->busy == 0 && dev->parent)
device_busy(dev->parent);
dev->busy++;
- dev->state = DS_BUSY;
+ if (dev->state == DS_ATTACHED)
+ dev->state = DS_BUSY;
}
/**
@@ -2485,14 +2583,16 @@ device_busy(device_t dev)
void
device_unbusy(device_t dev)
{
- if (dev->state != DS_BUSY)
+ if (dev->busy != 0 && dev->state != DS_BUSY &&
+ dev->state != DS_ATTACHING)
panic("device_unbusy: called for non-busy device %s",
device_get_nameunit(dev));
dev->busy--;
if (dev->busy == 0) {
if (dev->parent)
device_unbusy(dev->parent);
- dev->state = DS_ATTACHED;
+ if (dev->state == DS_BUSY)
+ dev->state = DS_ATTACHED;
}
}
@@ -2602,6 +2702,7 @@ device_set_driver(device_t dev, driver_t *driver)
free(dev->softc, M_BUS_SC);
dev->softc = NULL;
}
+ device_set_desc(dev, NULL);
kobj_delete((kobj_t) dev, NULL);
dev->driver = driver;
if (driver) {
@@ -2724,22 +2825,36 @@ device_attach(device_t dev)
{
int error;
+#ifndef __rtems__
+ if (resource_disabled(dev->driver->name, dev->unit)) {
+ device_disable(dev);
+ if (bootverbose)
+ device_printf(dev, "disabled via hints entry\n");
+ return (ENXIO);
+ }
+#endif /* __rtems__ */
+
device_sysctl_init(dev);
if (!device_is_quiet(dev))
device_print_child(dev->parent, dev);
+ dev->state = DS_ATTACHING;
if ((error = DEVICE_ATTACH(dev)) != 0) {
printf("device_attach: %s%d attach returned %d\n",
dev->driver->name, dev->unit, error);
- /* Unset the class; set in device_probe_child */
- if (dev->devclass == NULL)
- device_set_devclass(dev, NULL);
- device_set_driver(dev, NULL);
+ if (!(dev->flags & DF_FIXEDCLASS))
+ devclass_delete_device(dev->devclass, dev);
+ (void)device_set_driver(dev, NULL);
device_sysctl_fini(dev);
+ KASSERT(dev->busy == 0, ("attach failed but busy"));
dev->state = DS_NOTPRESENT;
return (error);
}
device_sysctl_update(dev);
- dev->state = DS_ATTACHED;
+ if (dev->busy)
+ dev->state = DS_BUSY;
+ else
+ dev->state = DS_ATTACHED;
+ dev->flags &= ~DF_DONENOMATCH;
devadded(dev);
return (0);
}
@@ -2785,8 +2900,7 @@ device_detach(device_t dev)
devclass_delete_device(dev->devclass, dev);
dev->state = DS_NOTPRESENT;
- device_set_driver(dev, NULL);
- device_set_desc(dev, NULL);
+ (void)device_set_driver(dev, NULL);
device_sysctl_fini(dev);
return (0);
@@ -3517,6 +3631,23 @@ bus_generic_teardown_intr(device_t dev, device_t child, struct resource *irq,
}
/**
+ * @brief Helper function for implementing BUS_ADJUST_RESOURCE().
+ *
+ * This simple implementation of BUS_ADJUST_RESOURCE() simply calls the
+ * BUS_ADJUST_RESOURCE() method of the parent of @p dev.
+ */
+int
+bus_generic_adjust_resource(device_t dev, device_t child, int type,
+ struct resource *r, u_long start, u_long end)
+{
+ /* Propagate up the bus hierarchy until someone handles it. */
+ if (dev->parent)
+ return (BUS_ADJUST_RESOURCE(dev->parent, child, type, r, start,
+ end));
+ return (EINVAL);
+}
+
+/**
* @brief Helper function for implementing BUS_ALLOC_RESOURCE().
*
* This simple implementation of BUS_ALLOC_RESOURCE() simply calls the
@@ -3839,6 +3970,21 @@ bus_alloc_resource(device_t dev, int type, int *rid, u_long start, u_long end,
}
/**
+ * @brief Wrapper function for BUS_ADJUST_RESOURCE().
+ *
+ * This function simply calls the BUS_ADJUST_RESOURCE() method of the
+ * parent of @p dev.
+ */
+int
+bus_adjust_resource(device_t dev, int type, struct resource *r, u_long start,
+ u_long end)
+{
+ if (dev->parent == NULL)
+ return (EINVAL);
+ return (BUS_ADJUST_RESOURCE(dev->parent, dev, type, r, start, end));
+}
+
+/**
* @brief Wrapper function for BUS_ACTIVATE_RESOURCE().
*
* This function simply calls the BUS_ACTIVATE_RESOURCE() method of the
@@ -4417,7 +4563,6 @@ print_driver(driver_t *driver, int indent)
print_driver_short(driver, indent);
}
-
static void
print_driver_list(driver_list_t drivers, int indent)
{