summaryrefslogtreecommitdiffstats
path: root/cpukit/libdrvmgr/drvmgr_unregister.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libdrvmgr/drvmgr_unregister.c')
-rw-r--r--cpukit/libdrvmgr/drvmgr_unregister.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/cpukit/libdrvmgr/drvmgr_unregister.c b/cpukit/libdrvmgr/drvmgr_unregister.c
new file mode 100644
index 0000000000..ef5260a13c
--- /dev/null
+++ b/cpukit/libdrvmgr/drvmgr_unregister.c
@@ -0,0 +1,186 @@
+/* Driver Manager Device Unregister (removal) implementation
+ *
+ * COPYRIGHT (c) 2011.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#include <stdlib.h>
+
+#include <drvmgr/drvmgr.h>
+#include <drvmgr/drvmgr_list.h>
+#include "drvmgr_internal.h"
+
+/* Unregister all children on a bus.
+ *
+ * This function is called from the bus driver, from a "safe" state where
+ * devices will not be added or removed on this particular bus at this time
+ */
+int drvmgr_children_unregister(struct drvmgr_bus *bus)
+{
+ int err;
+
+ while (bus->children != NULL) {
+ err = drvmgr_dev_unregister(bus->children);
+ if (err != DRVMGR_OK) {
+ /* An error occured */
+ bus->children->error = err;
+ return err;
+ }
+ }
+
+ return DRVMGR_OK;
+}
+
+/* Unregister a BUS and all it's devices.
+ *
+ * It is up to the bus driver to remove all it's devices, either manually
+ * one by one calling drvmgr_dev_unregister(), or by letting the driver
+ * manager unregister all children by calling drvmgr_children_unregister().
+ */
+int drvmgr_bus_unregister(struct drvmgr_bus *bus)
+{
+ struct rtems_driver_manager *mgr = &drv_mgr;
+ struct drvmgr_list *list;
+
+ if (bus->ops->remove == NULL)
+ return DRVMGR_ENOSYS;
+
+ /* Call Bus driver to clean things up, it must remove all children */
+ bus->error = bus->ops->remove(bus);
+ if (bus->error != DRVMGR_OK)
+ return bus->error;
+ /* Check that bus driver has done its job and removed all children */
+ if (bus->children != NULL)
+ return DRVMGR_FAIL;
+ /* Remove References to bus */
+ bus->dev->bus = NULL;
+
+ DRVMGR_LOCK_WRITE();
+
+ /* Remove bus from bus-list */
+ if (bus->state & BUS_STATE_LIST_INACTIVE)
+ list = &mgr->buses_inactive;
+ else
+ list = &mgr->buses[bus->level];
+ drvmgr_list_remove(list, bus);
+
+ DRVMGR_UNLOCK();
+
+ /* All references to this bus has been removed at this point */
+ free(bus);
+
+ return DRVMGR_OK;
+}
+
+/* Separate Driver and Device from each other */
+int drvmgr_dev_drv_separate(struct drvmgr_dev *dev)
+{
+ struct rtems_driver_manager *mgr = &drv_mgr;
+ struct drvmgr_dev *subdev, **pprev;
+ int rc;
+
+ /* Remove children if this device exports a bus of devices. All
+ * children must be removed first as they depend upon the bus
+ * services this bridge provide.
+ */
+ if (dev->bus) {
+ rc = drvmgr_bus_unregister(dev->bus);
+ if (rc != DRVMGR_OK)
+ return rc;
+ }
+
+ if (dev->drv == NULL)
+ return DRVMGR_OK;
+
+ /* Remove device by letting assigned driver take care of hardware
+ * issues
+ */
+ if (!dev->drv->ops->remove) {
+ /* No remove function is considered severe when someone
+ * is trying to remove the device
+ */
+ return DRVMGR_ENOSYS;
+ }
+ dev->error = dev->drv->ops->remove(dev);
+ if (dev->error != DRVMGR_OK)
+ return DRVMGR_FAIL;
+
+ DRVMGR_LOCK_WRITE();
+
+ /* Delete device from driver's device list */
+ pprev = &dev->drv->dev;
+ subdev = dev->drv->dev;
+ while (subdev != dev) {
+ pprev = &subdev->next_in_drv;
+ subdev = subdev->next_in_drv;
+ }
+ *pprev = subdev->next_in_drv;
+ dev->drv->dev_cnt--;
+
+ /* Move device to inactive list */
+ drvmgr_list_remove(&mgr->devices[dev->level], dev);
+ dev->level = 0;
+ dev->state &= ~(DEV_STATE_UNITED|DEV_STATE_INIT_DONE);
+ dev->state |= DEV_STATE_LIST_INACTIVE;
+ drvmgr_list_add_tail(&mgr->devices_inactive, dev);
+
+ DRVMGR_UNLOCK();
+
+ /* Free Device Driver Private memory if allocated previously by
+ * Driver manager.
+ */
+ if (dev->drv->dev_priv_size && dev->priv) {
+ free(dev->priv);
+ dev->priv = NULL;
+ }
+ dev->drv = NULL;
+
+ return DRVMGR_OK;
+}
+
+/* Unregister device,
+ * - let assigned driver handle deletion
+ * - remove from device list
+ * - remove from driver list
+ * - remove from bus list
+ */
+int drvmgr_dev_unregister(struct drvmgr_dev *dev)
+{
+ struct rtems_driver_manager *mgr = &drv_mgr;
+ struct drvmgr_dev *subdev, **pprev;
+ int err;
+
+ /* Separate device from driver, if the device is united with a driver.
+ *
+ * If this device is a bridge all child buses/devices are also removed.
+ */
+ err = drvmgr_dev_drv_separate(dev);
+ if (err != DRVMGR_OK)
+ return err;
+
+ DRVMGR_LOCK_WRITE();
+
+ /* Remove it from inactive list */
+ drvmgr_list_remove(&mgr->devices_inactive, dev);
+
+ /* Remove device from parent bus list (no check if dev not in list) */
+ pprev = &dev->parent->children;
+ subdev = dev->parent->children;
+ while (subdev != dev) {
+ pprev = &subdev->next_in_bus;
+ subdev = subdev->next_in_bus;
+ }
+ *pprev = subdev->next_in_bus;
+ dev->parent->dev_cnt--;
+
+ DRVMGR_UNLOCK();
+
+ /* All references to this device has been removed at this point */
+ free(dev);
+
+ return DRVMGR_OK;
+}