On Thu, Feb 26, 2015 at 11:38 AM, Daniel Hellstrom <dan...@gaisler.com> wrote: [...] > diff --git a/c/src/aclocal/enable-drvmgr.m4 b/c/src/aclocal/enable-drvmgr.m4 > new file mode 100644 > index 0000000..489f60e > --- /dev/null > +++ b/c/src/aclocal/enable-drvmgr.m4 > @@ -0,0 +1,12 @@ > +AC_DEFUN([RTEMS_ENABLE_DRVMGR], > +[ > +## AC_BEFORE([$0], [RTEMS_CHECK_DRVMGR_STARTUP])dnl > + > +AC_ARG_ENABLE(drvmgr, > +[AS_HELP_STRING([--enable-drvmgr],[enable Driver Manager at Startup])], > +[case "${enableval}" in > + yes) RTEMS_DRVMGR_STARTUP=yes ;; > + no) RTEMS_DRVMGR_STARTUP=no ;; > + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; > +esac],[RTEMS_DRVMGR_STARTUP=yes]) > +]) Is a configure-time option the best solution for this? The more 'switches' we have, the harder it is to test RTEMS.
> diff --git a/c/src/lib/libbsp/shared/bspdriverlevelhook.c > b/c/src/lib/libbsp/shared/bspdriverlevelhook.c > new file mode 100644 > index 0000000..93406f9 > --- /dev/null > +++ b/c/src/lib/libbsp/shared/bspdriverlevelhook.c > @@ -0,0 +1,16 @@ > +/* > + * This is a dummy bsp_driver_level_hook routine. > + * > + * COPYRIGHT (c) 2015. > + * Cobham Gaisler. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.org/license/LICENSE. > + */ > + > +#include <bsp/bootcard.h> > + > +void bsp_driver_level_hook( int level ) > +{ > +} We'll need some documentation added to the BSP guide on this. [...] > diff --git a/cpukit/libdrvmgr/README b/cpukit/libdrvmgr/README > new file mode 100644 > index 0000000..6e55370 > --- /dev/null > +++ b/cpukit/libdrvmgr/README > @@ -0,0 +1,112 @@ > +DRIVER MANAGER > +============== > + > +See documentation in Aeroflex Gaisler Driver manual. Should either have a link to a hosted version somewhere or we should provide the document in our manuals. > + > + > +INITIALIZATION > +============== > +The Driver Manager can be intialized in two different ways: > + 1. during RTEMS startup > + 2. started by user, typically in the Init task > + > +The driver manager is initalized during RTEMS startup in the > +rtems_initialize_device_drivers() function when RTEMS is > +configured with driver manager support. > + > +When RTEMS is not configured with the driver manager, the manager > +may still be initialized by the user after system startup, typically > +from the Init() task. > + > +The main difference between the two ways is when interrupt > +is enabled. Interrupt is enabled for the first time by RTEMS when > +the Init task is started. This means, for the first case, that > +drivers can not use interrupt services until after the > +initialization phase is over and the user request services from > +the drivers. For the second case of initialization, this means > +that driver must take extra care during initalization when interrupt s/initalization/initialization > +is enabled so that spurious interrupts are not generated and that the > +system does not hang in an infinite IRQ loop. > + > +Most of the problems above are solved for the two methods by > +specifying in which initialization levels IRQ handling is done. > +See Level 1 and Level 2 below. > + > +Other differences is that IRQ, System Clock Timer, debug Console > +and Console can be initalized by the help of the driver manager s/initalized/initialized > +when initialized during start up. Between Level0 and Level1 the > +RTEMS I/O Manager drivers are initialized. The LEON3 BSP has > +therefore two different versions of the basic drivers. > + What is the advantage to having the two different approaches? I don't clearly see, and it obviously complicated the initialization code, and seems to have led to two versions of the same drivers. [...] > diff --git a/cpukit/libdrvmgr/drvmgr.c b/cpukit/libdrvmgr/drvmgr.c > new file mode 100644 > index 0000000..0471865 > --- /dev/null > +++ b/cpukit/libdrvmgr/drvmgr.c > @@ -0,0 +1,643 @@ > +/* Driver Manager Interface Implementation. > + * > + * COPYRIGHT (c) 2009. > + * 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. use rtems.org for the url. (it redirects there now.) > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include <drvmgr/drvmgr.h> > +#include <drvmgr/drvmgr_confdefs.h> > + > +#include "drvmgr_internal.h" > + > +/* Enable debugging */ > +/*#define DEBUG 1*/ Consider inheriting the debug macro from --enable-rtems-debug > + > +#ifdef DEBUG > +#define DBG(x...) printk(x) > +#else > +#define DBG(x...) > +#endif > + > +struct rtems_driver_manager drv_mgr = { This struct type (and name) should probably be part of the "drvmgr" namespace? If only used in this file, the global var should be declared 'static'. > + .level = 0, > + .initializing_objs = 0, > + .lock = 0, > + .root_dev = {0}, > + .root_drv = NULL, > + > + .drivers = LIST_INITIALIZER(struct drvmgr_drv, next), > + > + .buses = { > + LIST_INITIALIZER(struct drvmgr_bus, next), > + LIST_INITIALIZER(struct drvmgr_bus, next), > + LIST_INITIALIZER(struct drvmgr_bus, next), > + LIST_INITIALIZER(struct drvmgr_bus, next), > + LIST_INITIALIZER(struct drvmgr_bus, next), > + }, > + .buses_inactive = LIST_INITIALIZER(struct drvmgr_bus, next), > + > + .devices = { > + LIST_INITIALIZER(struct drvmgr_dev, next), > + LIST_INITIALIZER(struct drvmgr_dev, next), > + LIST_INITIALIZER(struct drvmgr_dev, next), > + LIST_INITIALIZER(struct drvmgr_dev, next), > + LIST_INITIALIZER(struct drvmgr_dev, next), > + }, > + .devices_inactive = LIST_INITIALIZER(struct drvmgr_dev, next), > +}; > + > +static int do_bus_init( > + struct rtems_driver_manager *mgr, > + struct drvmgr_bus *bus, > + int level); > +static int do_dev_init( > + struct rtems_driver_manager *mgr, > + struct drvmgr_dev *dev, > + int level); > + > +/* DRIVER MANAGER */ > + > +void _DRV_Manager_init_level(int level) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + > + if (mgr->level >= level) > + return; > + > + /* Set new Level */ > + mgr->level = level; > + > + /* Initialize buses and devices into this new level */ > + drvmgr_init_update(); > +} > + > +/* Initialize Data structures of the driver manager and call driver > + * register functions configured by the user. > + */ > +void _DRV_Manager_initialization(void) > +{ > + struct drvmgr_drv_reg_func *drvreg; > + > + /* drv_mgr is already initialized statically by compiler except > + * the lock > + */ > + DRVMGR_LOCK_INIT(); > + > + /* Call driver register functions. */ > + drvreg = &drvmgr_drivers[0]; > + while (drvreg->drv_reg) { > + /* Make driver register */ > + drvreg->drv_reg(); > + drvreg++; > + } > +} > + > +/* Take ready devices and buses into the correct init level step by step. > + * Once a bus or a device has been registered there is no turning > + * back - they are taken to the level of the driver manager. > + */ > +void drvmgr_init_update(void) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + struct drvmgr_bus *bus; > + struct drvmgr_dev *dev; > + int bus_might_been_registered; > + int level; > + > + /* "Lock" to make sure we don't use up the stack and that the lists > + * remain consistent. > + */ > + DRVMGR_LOCK_WRITE(); > + if (mgr->initializing_objs || (mgr->level == 0)) > + goto out; I'd rather see return than goto. > + mgr->initializing_objs = 1; > + > +init_registered_buses: > + /* Take all buses and devices ready into the same stage > + * as the driver manager global level. > + */ > + for (level = 0; level < mgr->level; level++) { > + > + bus_might_been_registered = 0; > + > + /* Take buses into next level */ > + > + while ((bus = BUS_LIST_HEAD(&mgr->buses[level])) != NULL) { > + > + /* Remove first in the list (will be inserted in > + * appropriate list by do_bus_init()) > + */ > + drvmgr_list_remove_head(&mgr->buses[level]); > + > + DRVMGR_UNLOCK(); > + > + /* Initialize Bus, this will register devices on > + * the bus. Take bus into next level. > + */ > + do_bus_init(mgr, bus, level+1); > + > + DRVMGR_LOCK_WRITE(); > + } > + > + /* Take devices into next level */ > + while ((dev = DEV_LIST_HEAD(&mgr->devices[level])) != NULL) { > + > + /* Always process first in list */ > + dev = DEV_LIST_HEAD(&mgr->devices[level]); > + > + /* Remove first in the list (will be inserted in > + * appropriate list by do_dev_init()) > + */ > + drvmgr_list_remove_head(&mgr->devices[level]); > + > + DRVMGR_UNLOCK(); > + > + /* Initialize Device, this may register a new bus */ > + do_dev_init(mgr, dev, level+1); > + > + DRVMGR_LOCK_WRITE(); > + > + bus_might_been_registered = 1; > + } > + > + /* Make sure all buses registered and ready are taken at > + * the same time into init level N. > + */ > + if (bus_might_been_registered) > + goto init_registered_buses; how about level = 0 to restart the loop instead of a backward goto? > + } > + > + /* Release bus/device initialization "Lock" */ > + mgr->initializing_objs = 0; > + > +out: > + DRVMGR_UNLOCK(); > +} > + > +/* Take bus into next level */ > +static int do_bus_init( > + struct rtems_driver_manager *mgr, > + struct drvmgr_bus *bus, > + int level) > +{ > + int (*init)(struct drvmgr_bus *); > + > + /* If bridge device has failed during initialization, the bus is not > + * initialized further. > + */ > + if (bus->dev->state & DEV_STATE_INIT_FAILED) { > + bus->state |= BUS_STATE_DEPEND_FAILED; > + goto inactivate_out; > + } > + > + if (bus->ops && (init = bus->ops->init[level-1])) { > + /* Note: This init1 function may register new devices */ > + bus->error = init(bus); > + if (bus->error != DRVMGR_OK) { > + /* An error of some kind during bus initialization. > + * > + * Child devices and their buses are not inactived > + * directly here, instead they will all be catched by > + * do_dev_init() and do_bus_init() by checking if > + * parent or bridge-device failed. We know that > + * initialization will happen later for those devices. > + */ > + goto inactivate_out; > + } > + } > + > + DRVMGR_LOCK_WRITE(); > + > + /* Bus taken into the new level */ > + bus->level = level; > + > + /* Put bus into list of buses reached level 'level'. > + * Put at end of bus list so that init[N+1]() calls comes > + * in the same order as init[N]() > + */ > + drvmgr_list_add_tail(&mgr->buses[level], bus); > + > + DRVMGR_UNLOCK(); > + > + return 0; > + > +inactivate_out: > + DRVMGR_LOCK_WRITE(); > + bus->state |= BUS_STATE_INIT_FAILED; > + bus->state |= BUS_STATE_LIST_INACTIVE; > + drvmgr_list_add_head(&mgr->buses_inactive, bus); > + DRVMGR_UNLOCK(); > + > + DBG("do_bus_init(%d): (DEV: %s) failed\n", level, bus->dev->name); > + > + return 1; > +} > + > +/* Take device to initialization level 1 */ > +static int do_dev_init( > + struct rtems_driver_manager *mgr, > + struct drvmgr_dev *dev, > + int level) > +{ > + int (*init)(struct drvmgr_dev *); > + > + /* Try to allocate Private Device Structure for driver if driver > + * requests for this feature. > + */ > + if (dev->drv && dev->drv->dev_priv_size && !dev->priv) { > + dev->priv = malloc(dev->drv->dev_priv_size); > + memset(dev->priv, 0, dev->drv->dev_priv_size); > + } > + > + /* If parent bus has failed during initialization, > + * the device is not initialized further. > + */ > + if (dev->parent && (dev->parent->state & BUS_STATE_INIT_FAILED)) { > + dev->state |= DEV_STATE_DEPEND_FAILED; > + goto inactivate_out; > + } > + > + /* Call Driver's Init Routine */ > + if (dev->drv && (init = dev->drv->ops->init[level-1])) { > + /* Note: This init function may register new devices */ > + dev->error = init(dev); > + if (dev->error != DRVMGR_OK) { > + /* An error of some kind has occured in the > + * driver/device, the failed device is put into the > + * inactive list, this way Init2,3 and/or 4 will not > + * be called for this device. > + * > + * The device is not removed from the bus (not > + * unregistered). The driver can be used to find > + * device information and debugging for example even > + * if device initialization failed. > + * > + * Child buses and their devices are not inactived > + * directly here, instead they will all be catched by > + * do_dev_init() and do_bus_init() by checking if > + * parent or bridge-device failed. We know that > + * initialization will happen later for those devices. > + */ > + goto inactivate_out; > + } > + } > + > + DRVMGR_LOCK_WRITE(); > + /* Dev taken into new level */ > + dev->level = level; > + > + /* Put at end of device list so that init[N+1]() calls comes > + * in the same order as init[N]() > + */ > + drvmgr_list_add_tail(&mgr->devices[level], dev); > + DRVMGR_UNLOCK(); > + > + return 0; > + > +inactivate_out: > + DRVMGR_LOCK_WRITE(); > + dev->state |= DEV_STATE_INIT_FAILED; > + dev->state |= DEV_STATE_LIST_INACTIVE; > + drvmgr_list_add_head(&mgr->devices_inactive, dev); > + DRVMGR_UNLOCK(); > + > + DBG("do_dev_init(%d): DRV: %s (DEV: %s) failed\n", > + level, dev->drv->name, dev->name); > + > + return 1; /* Failed to take device into requested level */ > +} These functions look terribly similar. > + > +/* Register Root device driver */ > +int drvmgr_root_drv_register(struct drvmgr_drv *drv) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + struct drvmgr_dev *root = &mgr->root_dev; > + > + if (mgr->root_drv) { > + /* Only possible to register root device once */ > + return DRVMGR_FAIL; > + } > + > + /* Set root device driver */ > + drv->next = NULL; > + mgr->root_drv = drv; > + > + /* Init root device non-NULL fields */ > + root->minor_drv = -1; > + root->minor_bus = 0; > + root->businfo = mgr; > + root->name = "root bus"; > + /* Custom Driver association */ > + root->drv = mgr->root_drv; > + > + /* This registers the root device and a bus */ > + drvmgr_dev_register(root); > + > + return DRVMGR_OK; > +} > + > +/* Register a driver */ > +int drvmgr_drv_register(struct drvmgr_drv *drv) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + > + /* All drivers must have been registered before start of init, > + * because the manager does not scan all existing devices to find > + * suitable hardware for this driver, and it is not protected with > + * a lock therefore. > + */ > + if (mgr->level > 0) > + return -1; > + > + drv->obj_type = DRVMGR_OBJ_DRV; > + > + /* Put driver into list of registered drivers */ > + drvmgr_list_add_head(&mgr->drivers, drv); > + > + /* TODO: we could scan for devices that this new driver has support > + * for. However, at this stage we assume that all drivers are > + * registered before devices are registered. > + * > + * LOCK: From the same assumsion locking the driver list is not needed > + * either. > + */ > + > + return 0; > +} > + > +/* Insert a device into a driver's device list and assign a driver minor > number > + * to the device. > + * > + * The devices are ordered by their minor number (sorted linked list of > devices) > + * the minor number is found by looking for a gap or at the end. > + */ > +static void drvmgr_insert_dev_into_drv( > + struct drvmgr_drv *drv, > + struct drvmgr_dev *dev) > +{ > + struct drvmgr_dev *curr, **pprevnext; > + int minor; > + > + minor = 0; > + pprevnext = &drv->dev; > + curr = drv->dev; > + > + while (curr) { > + if (minor < curr->minor_drv) { > + /* Found a gap. Insert new device between prev > + * and curr. */ > + break; > + } > + minor++; > + pprevnext = &curr->next_in_drv; > + curr = curr->next_in_drv; > + } > + dev->next_in_drv = curr; > + *pprevnext = dev; > + > + /* Set minor */ > + dev->minor_drv = minor; > + drv->dev_cnt++; > +} > + > +/* Insert a device into a bus device list and assign a bus minor number to > the > + * device. > + * > + * The devices are ordered by their minor number (sorted linked list of > devices) > + * and by their registeration order if not using the same driver. > + * > + * The minor number is found by looking for a gap or at the end. > + */ > +static void drvmgr_insert_dev_into_bus( > + struct drvmgr_bus *bus, > + struct drvmgr_dev *dev) > +{ > + struct drvmgr_dev *curr, **pprevnext; > + int minor; > + > + minor = 0; > + pprevnext = &bus->children; > + curr = bus->children; > + > + while (curr) { > + if (dev->drv && (dev->drv == curr->drv)) { > + if (minor < curr->minor_bus) { > + /* Found a gap. Insert new device between prev > + * and curr. */ > + break; > + } > + minor++; > + } > + pprevnext = &curr->next_in_bus; > + curr = curr->next_in_bus; > + } > + dev->next_in_bus = curr; > + *pprevnext = dev; > + > + /* Set minor. Devices without driver are given -1 */ > + if (dev->drv == NULL) > + minor = -1; > + dev->minor_bus = minor; > + bus->dev_cnt++; > +} > + > +/* Try to find a driver for a device (unite a device with driver). > + * a device with a driver > + */ > +static struct drvmgr_drv *drvmgr_dev_find_drv( > + struct drvmgr_dev *dev) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + struct drvmgr_drv *drv; > + > + /* NOTE: No locking is needed here since Driver list is supposed to be > + * initialized once during startup, we treat it as a static > + * read-only list > + */ > + > + /* Try to find a driver that can handle this device */ > + for (drv = DRV_LIST_HEAD(&mgr->drivers); drv; drv = drv->next) > + if (dev->parent->ops->unite(drv, dev) == 1) > + break; > + > + return drv; > +} > + > +/* Register a device */ > +int drvmgr_dev_register(struct drvmgr_dev *dev) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + struct drvmgr_drv *drv; > + struct drvmgr_bus *bus = dev->parent; > + struct drvmgr_key *keys; > + struct drvmgr_list *init_list = &mgr->devices_inactive; > + > + DBG("DEV_REG: %s at bus \"%s\"\n", dev->name, > + bus && bus->dev && bus->dev->name ? bus->dev->name : > "UNKNOWN"); > + > + /* Custom driver assocation? */ > + if (dev->drv) { > + drv = dev->drv; > + DBG("CUSTOM ASSOCIATION (%s to %s)\n", dev->name, drv->name); > + } else { > + /* Try to find a driver that can handle this device */ > + dev->drv = drv = drvmgr_dev_find_drv(dev); > + } > + > + DRVMGR_LOCK_WRITE(); > + > + /* Assign Bus Minor number and put into bus device list > + * unless root device. > + */ > + if (bus) > + drvmgr_insert_dev_into_bus(bus, dev); > + > + if (!drv) { > + /* No driver found that can handle this device, put into > + * inactive list > + */ > + dev->minor_drv = -1; > + dev->state |= DEV_STATE_LIST_INACTIVE; > + } else { > + /* United device with driver. > + * Put the device on the registered device list > + */ > + dev->state |= DEV_STATE_UNITED; > + > + /* Check if user want to skip this core. This is not a > + * normal request, however in a multi-processor system > + * the two(or more) RTEMS instances must not use the same > + * devices in a system, not reporting a device to > + * it's driver will effectively accomplish this. In a > + * non Plug & Play system one can easily avoid this > + * problem by not report the core, but in a Plug & Play > + * system the bus driver will report all found cores. > + * > + * To stop the two RTEMS instances from using the same > + * device the user can simply define a resource entry > + * for a certain device but set the keys field to NULL. > + */ > + if (drvmgr_keys_get(dev, &keys) == 0 && keys == NULL) { > + /* Found Driver resource entry point > + * for this device, it was NULL, this > + * indicates to skip the core. > + * > + * We put it into the inactive list > + * marking it as ignored. > + */ > + dev->state |= DEV_STATE_IGNORED; > + } else { > + /* Assign Driver Minor number and put into driver's > + * device list > + */ > + drvmgr_insert_dev_into_drv(drv, dev); > + > + /* Just register device, it will be initialized > + * later together with bus. > + * > + * At the end of the list (breadth first search) > + */ > + init_list = &mgr->devices[0]; > + > + DBG("Registered %s (DRV: %s) on %s\n", > + dev->name, drv->name, > + bus ? bus->dev->name : "NO PARENT"); > + } > + } > + > + drvmgr_list_add_tail(init_list, dev); > + > + DRVMGR_UNLOCK(); > + > + /* Trigger Device initialization if not root device and > + * has a driver > + */ > + if (bus && dev->drv) > + drvmgr_init_update(); > + > + return 0; > +} > + > +/* Register a bus */ > +int drvmgr_bus_register(struct drvmgr_bus *bus) > +{ > + struct rtems_driver_manager *mgr = &drv_mgr; > + struct drvmgr_bus *bus_up; > + > + /* Get bus architecture depth - the distance from root bus */ > + bus->depth = 0; > + bus_up = bus->dev->parent; > + while (bus_up) { > + bus->depth++; > + bus_up = bus_up->dev->parent; > + } > + > + DRVMGR_LOCK_WRITE(); > + > + /* Put driver into list of found buses */ > + drvmgr_list_add_tail(&mgr->buses[0], bus); > + > + DRVMGR_UNLOCK(); > + > + /* Take bus into level1 and so on */ > + drvmgr_init_update(); > + > + return 0; > +} > + > +/* Allocate memory for a Device structure */ > +int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra) > +{ > + struct drvmgr_dev *dev; > + int size; > + > + size = ((sizeof(struct drvmgr_dev) + 3) & ~0x3) + extra; I have no idea what is happening here. > + dev = (struct drvmgr_dev *)calloc(size, 1); > + if (!dev) { > + /* Failed to allocate device structure - critical error */ > + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); > + } > + *pdev = dev; > + dev->obj_type = DRVMGR_OBJ_DEV; > + > + return 0; > +} > + > +/* Allocate memory for a Bus structure */ > +int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra) > +{ > + struct drvmgr_bus *bus; > + int size; > + > + size = ((sizeof(struct drvmgr_bus) + 3) & ~0x3) + extra; > + bus = (struct drvmgr_bus *)calloc(size, 1); > + if (!bus) { > + /* Failed to allocate device structure - critical error */ > + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); > + } > + *pbus = bus; > + bus->obj_type = DRVMGR_OBJ_BUS; > + > + return 0; > +} > + > +/* Add driver resources to a bus instance */ > +void drvmgr_bus_res_add(struct drvmgr_bus *bus, > + struct drvmgr_bus_res *bres) > +{ > + /* insert first in bus resource list. Locking isn't needed since > + * resources can only be added before resource requests are made. > + * When bus has been registered resources are considered a read-only > + * tree. > + */ > + bres->next = bus->reslist; > + bus->reslist = bres; > +} > diff --git a/cpukit/libdrvmgr/drvmgr.h b/cpukit/libdrvmgr/drvmgr.h > new file mode 100644 > index 0000000..f091728 > --- /dev/null > +++ b/cpukit/libdrvmgr/drvmgr.h > @@ -0,0 +1,965 @@ > +/* Driver Manager Interface. > + * > + * COPYRIGHT (c) 2009. > + * 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. > + */ > + > +#ifndef _DRIVER_MANAGER_H_ > +#define _DRIVER_MANAGER_H_ > + > +#include <rtems.h> > +#include <drvmgr/drvmgr_list.h> > +#include <stdint.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +/*** Configure Driver manager ***/ > + > +/* Define the number of initialization levels of device drivers */ > +#define DRVMGR_LEVEL_MAX 4 > + > +/* Default to use semahpores for protection. Initialization works without > + * locks and after initialization too if devices are not removed. > + */ > +#ifndef DRVMGR_USE_LOCKS > +#define DRVMGR_USE_LOCKS 1 > +#endif > + > +struct drvmgr_dev; /* Device */ > +struct drvmgr_bus; /* Bus */ > +struct drvmgr_drv; /* Driver */ > + > +/*** List Interface shortcuts ***/ > +#define BUS_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_bus) > +#define BUS_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_bus) > +#define DEV_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_dev) > +#define DEV_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_dev) > +#define DRV_LIST_HEAD(list) LIST_HEAD(list, struct drvmgr_drv) > +#define DRV_LIST_TAIL(list) LIST_TAIL(list, struct drvmgr_drv) > + > +/*** Bus indentification ***/ > +#define DRVMGR_BUS_TYPE_NONE 0 /* Not a valid bus */ > +#define DRVMGR_BUS_TYPE_ROOT 1 /* Hard coded bus */ > +#define DRVMGR_BUS_TYPE_PCI 2 /* PCI bus */ > +#define DRVMGR_BUS_TYPE_AMBAPP 3 /* AMBA Plug & Play bus */ > +#define DRVMGR_BUS_TYPE_LEON2_AMBA 4 /* LEON2 hardcoded bus */ > +#define DRVMGR_BUS_TYPE_AMBAPP_DIST 5 /* Distibuted AMBA Plug & Play bus > accessed using a communication interface */ > +#define DRVMGR_BUS_TYPE_SPW_RMAP 6 /* SpaceWire Network bus */ > +#define DRVMGR_BUS_TYPE_AMBAPP_RMAP 7 /* SpaceWire RMAP accessed AMBA Plug > & Play bus */ > + > +enum { > + DRVMGR_OBJ_NONE = 0, > + DRVMGR_OBJ_DRV = 1, > + DRVMGR_OBJ_BUS = 2, > + DRVMGR_OBJ_DEV = 3, > +}; > + > +/*** Driver indentification *** > + * > + * 64-bit identification integer definition > + * * Bus ID 8-bit [7..0] > + * * Reserved 8-bit field [63..56] > + * * Device ID specific for bus type 48-bit [55..8] (Different buses have > + * different unique identifications for hardware/driver.) > + * > + * ID Rules > + * * A root bus driver must always have device ID set to 0. There can only > by > + * one root bus driver for a certain bus type. > + * * A Driver ID must identify a unique hardware core > + * > + */ > + > +/* Bus ID Mask */ > +#define DRIVER_ID_BUS_MASK 0x00000000000000FFULL > + > +/* Reserved Mask for future use */ > +#define DRIVER_ID_RSV_MASK 0xFF00000000000000ULL > + > +/* Reserved Mask for future use */ > +#define DRIVER_ID_DEV_MASK 0x00FFFFFFFFFFFF00ULL > + > +/* Set Bus ID Mask. */ > +#define DRIVER_ID(busid, devid) ((unsigned long long) \ > + ((((unsigned long long)(devid) << 8) & DRIVER_ID_DEV_MASK) | \ > + ((unsigned long long)(busid) & DRIVER_ID_BUS_MASK))) > + > +/* Get IDs */ > +#define DRIVER_BUSID_GET(id) ((unsigned long long)(id) & > DRIVER_ID_BUS_MASK) > +#define DRIVER_DEVID_GET(id) (((unsigned long long)(id) & > DRIVER_ID_DEV_MASK) >> 8) > + > +#define DRIVER_ROOTBUS_ID(bus_type) DRIVER_ID(bus_type, 0) > + > +/*** Root Bus drivers ***/ > + > +/* Generic Hard coded Root bus: Driver ID */ > +#define DRIVER_ROOT_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_ROOT) > + > +/* PCI Plug & Play bus: Driver ID */ > +#define DRIVER_PCIBUS_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_PCI) > + > +/* AMBA Plug & Play bus: Driver ID */ > +#define DRIVER_GRLIB_AMBAPP_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_AMBAPP) > + > +/* AMBA Hard coded bus: Driver ID */ > +#define DRIVER_LEON2_AMBA_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_LEON2_AMBA) > + > +/* Distributed AMBA Plug & Play bus: Driver ID */ > +#define DRIVER_AMBAPP_DIST_ID DRIVER_ROOTBUS_ID(DRVMGR_BUS_TYPE_AMBAPP_DIST) > + It would be good to put all these macros in the same 'macro' namespace for consistency purposes. > +/*! Bus parameters used by driver interface functions to aquire information > + * about bus. All Bus drivers should implement the operation 'get_params' so > + * that the driver interface routines can access bus dependent information in > + * an non-dependent way. > + */ > +struct drvmgr_bus_params { > + char *dev_prefix; /*!< Optional name prefix */ > +}; > + > +/* Interrupt Service Routine (ISR) */ > +typedef void (*drvmgr_isr)(void *arg); Can you use the existing typedef for ISRs? > + > +/*! Bus operations */ > +struct drvmgr_bus_ops { > + /* Functions used internally within driver manager */ > + int (*init[DRVMGR_LEVEL_MAX])(struct drvmgr_bus *); > + int (*remove)(struct drvmgr_bus *); > + int (*unite)(struct drvmgr_drv *, struct drvmgr_dev *); /*!< > Unite Hardware Device with Driver */ > + > + /* Functions called indirectly from drivers */ > + int (*int_register)(struct drvmgr_dev *, int index, const char > *info, drvmgr_isr isr, void *arg); > + int (*int_unregister)(struct drvmgr_dev *, int index, drvmgr_isr > isr, void *arg); > + int (*int_clear)(struct drvmgr_dev *, int index); > + int (*int_mask)(struct drvmgr_dev *, int index); > + int (*int_unmask)(struct drvmgr_dev *, int index); why are these all prefixed by int_? > + > + /* Get Parameters */ > + int (*get_params)(struct drvmgr_dev *, struct drvmgr_bus_params > *); > + /* Get Frequency of Bus */ > + int (*freq_get)(struct drvmgr_dev*, int, unsigned int*); try to be consistent about using verb_object, e.g. get_params(), get_frequency(). > + /*! Function called to request information about a device. The bus > + * driver interpret the bus-specific information about the device. > + */ > + void (*info_dev)(struct drvmgr_dev *, void (*print)(void *p, char > *str), void *p); maybe get_dev_info() ? > +}; I'm a little confused why drvmgr_bus_ops contains a number of functions without a drvmgr_bus* as an argument. > +#define BUS_OPS_NUM (sizeof(struct drvmgr_bus_ops)/sizeof(void (*)(void))) > + > +struct drvmgr_func { > + int funcid; > + void *func; > +}; > +#define DRVMGR_FUNC(_ID_, _FUNC_) {(int)(_ID_), (void *)(_FUNC_)} > +#define DRVMGR_FUNC_END {0, NULL} Are these macros used anywhere? > + > +/*** Resource definitions *** > + * > + * Overview of structures: > + * All bus resources entries (_bus_res) are linked together per bus > + * (bus_info->reslist). One bus resource entry has a pointer to an array of > + * driver resources (_drv_res). One driver resouces is made out of an array > + * of keys (drvmgr_key). All keys belongs to the same driver and harwdare > + * device. Each key has a Name, Type ID and Data interpreted differently > + * depending on the Type ID (union drvmgr_key_value). > + * > + */ > + > +/* Key Data Types */ > +#define KEY_TYPE_NONE 0 > +#define KEY_TYPE_INT 1 > +#define KEY_TYPE_STRING 2 > +#define KEY_TYPE_POINTER 3 > + You might consider an enum type instead. And again use a "namespace" especially here in public headers. > +#define KEY_EMPTY {NULL, KEY_TYPE_NONE, {0}} > +#define RES_EMPTY {0, 0, NULL} > +#define MMAP_EMPTY {0, 0, 0} > + > +/*! Union of different values */ > +union drvmgr_key_value { > + unsigned int i; /*!< Key data type UNSIGNED INTEGER */ > + char *str; /*!< Key data type STRING */ > + void *ptr; /*!< Key data type ADDRESS/POINTER */ > +}; You may like to use uintptr_t instead of unsigned int in here. > + > +/* One key. One Value. Holding information relevant to the driver. */ > +struct drvmgr_key { > + char *key_name; /* Name of key */ > + int key_type; /* How to interpret key_value > */ > + union drvmgr_key_value key_value; /* The value or pointer to > value */ > +}; > + > +/*! Driver resource entry, Driver resources for a certain device instance, > + * containing a number of keys where each key hold the data of interest. > + */ > +struct drvmgr_drv_res { > + uint64_t drv_id; /*!< Identifies the driver > this resource is aiming */ > + int minor_bus; /*!< Indentifies a specfic > device */ > + struct drvmgr_key *keys; /*!< First key in key array, > ended with KEY_EMPTY */ > +}; > + > +/*! Bus resource list node */ > +struct drvmgr_bus_res { > + struct drvmgr_bus_res *next; /*!< Next resource node in > list */ > + struct drvmgr_drv_res resource[]; /*!< Array of resources, one > per device instance */ > +}; I think you want to use resource[RTEMS_ZERO_LENGTH_ARRAY] here. > + > +/*! MAP entry. Describes an linear address space translation. Untranslated > + * Start, Translated Start and length. > + * > + * Used by bus drivers to describe the address translation needed for > + * the translation driver interface. > + */ > +struct drvmgr_map_entry { > + char *name; /*!< Map Name */ > + unsigned int size; /*!< Size of map window */ > + char *from_adr; /*!< Start address of access window > used > + * to reach into remote bus */ > + char *to_adr; /*!< Start address of remote system > + * address range */ > +}; > +#define DRVMGR_TRANSLATE_ONE2ONE NULL > +#define DRVMGR_TRANSLATE_NO_BRIDGE ((void *)1) /* No bridge, error */ > + > +/*! Bus information. Describes a bus. */ > +struct drvmgr_bus { > + int obj_type; /*!< DRVMGR_OBJ_BUS */ > + unsigned char bus_type; /*!< Type of bus */ > + unsigned char depth; /*!< Bus level distance from > root bus */ > + struct drvmgr_bus *next; /*!< Next Bus */ > + struct drvmgr_dev *dev; /*!< Bus device, the > hardware... */ > + void *priv; /*!< Private data structure > used by BUS driver */ > + struct drvmgr_dev *children; /*!< Hardware devices on this > bus */ > + struct drvmgr_bus_ops *ops; /*!< Bus operations supported > by this bus driver */ > + struct drvmgr_func *funcs; /*!< Extra operations */ > + int dev_cnt; /*!< Number of devices this > bus has */ > + struct drvmgr_bus_res *reslist; /*!< Bus resources, head of a > linked list of resources. */ > + struct drvmgr_map_entry *maps_up; /*!< Map Translation, array > of address spaces upstreams to CPU */ > + struct drvmgr_map_entry *maps_down; /*!< Map Translation, array > of address spaces downstreams to Hardware */ > + > + /* Bus status */ > + int level; /*!< Initialization Level of > Bus */ > + int state; /*!< Init State of Bus, > BUS_STATE_* */ > + int error; /*!< Return code from > bus->ops->initN() */ > +}; > + > +/* States of a bus */ > +#define BUS_STATE_INIT_FAILED 0x00000001 /* Initialization Failed */ > +#define BUS_STATE_LIST_INACTIVE 0x00001000 /* In inactive bus > list */ > +#define BUS_STATE_DEPEND_FAILED 0x00000004 /* Device init failed > */ > + > +/* States of a device */ > +#define DEV_STATE_INIT_FAILED 0x00000001 /* Initialization Failed */ > +#define DEV_STATE_INIT_DONE 0x00000002 /* All init levels completed > */ > +#define DEV_STATE_DEPEND_FAILED 0x00000004 /* Parent Bus init > failed */ > +#define DEV_STATE_UNITED 0x00000100 /* Device United with Device > Driver */ > +#define DEV_STATE_REMOVED 0x00000200 /* Device has been removed > (unregistered) */ > +#define DEV_STATE_IGNORED 0x00000400 /* Device was ignored > according to user's request, the device > + * was never reported to it's > driver (as expected). > + */ > +#define DEV_STATE_LIST_INACTIVE 0x00001000 /* In inactive device > list */ > + > +/*! Device information */ > +struct drvmgr_dev { > + int obj_type; /*!< DRVMGR_OBJ_DEV */ > + struct drvmgr_dev *next; /*!< Next device */ > + struct drvmgr_dev *next_in_bus; /*!< Next device on the same > bus */ > + struct drvmgr_dev *next_in_drv; /*!< Next device using the > same driver */ > + > + struct drvmgr_drv *drv; /*!< The driver owning this > device */ > + struct drvmgr_bus *parent; /*!< Bus that this device > resides on */ > + short minor_drv; /*!< Device number within > driver */ > + short minor_bus; /*!< Device number on bus > (for device separation) */ > + char *name; /*!< Name of Device Hardware > */ > + void *priv; /*!< Pointer to driver > private device structure */ > + void *businfo; /*!< Host bus specific > information */ > + struct drvmgr_bus *bus; /*!< Pointer to bus, set only > if this is a bridge */ > + > + /* Device Status */ > + unsigned int state; /*!< State of device, see > DEV_STATE_* */ > + int level; /*!< Init Level */ > + int error; /*!< Error state returned by > driver */ > +}; > + > +/*! Driver operations, function pointers. */ > +struct drvmgr_drv_ops { > + int (*init[DRVMGR_LEVEL_MAX])(struct drvmgr_dev *); /*! Function > doing Init Stage 1 of a hardware device */ > + int (*remove)(struct drvmgr_dev *); /*! Function called when > device instance is to be removed */ > + int (*info)(struct drvmgr_dev *, void (*print)(void *p, char > *str), void *p, int, char *argv[]);/*! Function called to request information > about a device or driver */ > +}; > +#define DRV_OPS_NUM (sizeof(struct drvmgr_drv_ops)/sizeof(void (*)(void))) > + Function names in these ops tables could be made consistent when they have teh same purpose. Also, the macro get the num could be shared, e.g. #define DRVMGR_OPS_NUM(x) (sizeof(x)/sizeof(void (*)(void)) where x is an ops table would work. > +/*! Device driver description */ > +struct drvmgr_drv { > + int obj_type; /*!< DRVMGR_OBJ_DRV */ > + struct drvmgr_drv *next; /*!< Next Driver */ > + struct drvmgr_dev *dev; /*!< Devices using this > driver */ > + > + uint64_t drv_id; /*!< Unique Driver ID */ > + char *name; /*!< Name of Driver */ > + int bus_type; /*!< Type of Bus this driver > supports */ > + struct drvmgr_drv_ops *ops; /*!< Driver operations */ > + struct drvmgr_func *funcs; /*!< Extra Operations */ > + unsigned int dev_cnt; /*!< Number of devices in dev > */ > + unsigned int dev_priv_size; /*!< If non-zero DRVMGR will > allocate memory for dev->priv */ > +}; > + > +/*! Structure defines a function pointer called when driver manager is ready > + * for drivers to register themselfs. Used to select drivers available to > the > + * driver manager. > + */ > +struct drvmgr_drv_reg_func { > + void (*drv_reg)(void); > +}; In RTEMS kernel code we try to avoid abbreviations. I've been letting it slip, but I'm having second thoughts that many of these public-facing struct and function definitions should be re-written to use fewer abbreviations. so this would be like struct drvmgr_driver_register_function. Also, in this case it is just one function, why not just use a typedef for it? > + > +/*** DRIVER | DEVICE | BUS FUNCTIONS ***/ > + > +/* Return Codes */ > +enum { > + DRVMGR_OK = 0, /* Sucess */ > + DRVMGR_NOMEM = 1, /* Memory allocation error */ > + DRVMGR_EIO = 2, /* I/O error */ > + DRVMGR_EINVAL = 3, /* Invalid parameter */ > + DRVMGR_ENOSYS = 4, > + DRVMGR_TIMEDOUT = 5, /* Operation timeout error */ > + DRVMGR_EBUSY = 6, > + DRVMGR_ENORES = 7, /* Not enough resources */ > + DRVMGR_FAIL = -1 /* Unspecified failure */ > +}; > + > +/*! Initialize data structures of the driver management system. > + * Calls predefined register driver functions so that drivers can > + * register themselves. > + */ > +extern void _DRV_Manager_initialization(void); > + Should pick a consistent "namespace" for this, for example Drvmgr would be consistent with drvmgr. Then this function might either be _Drvmgr_Manager_initialization() or _Drvmgr_Initialization() depending on how you view it. > +/*! Take all devices into init level 'level', all devices registered later > + * will directly be taken into this level as well, ensuring that all > + * registerd devices has been taken into the level. > + * > + */ > +extern void _DRV_Manager_init_level(int level); > + > +/*! This function must be defined by the BSP when the driver manager is > enabled > + * and initialized during BSP initialization. The function is called after a > + * init level is reached the first time by the driver manager. > + */ > +extern void bsp_driver_level_hook(int level); It seems wrong to have a declaration for a bsp-layer function here. It is probably also wrong to be calling it directly below. > + > +/*! Init driver manager all in one go, will call > _DRV_Manager_initialization(), > + * then _DRV_Manager_init_level([1..DRVMGR_LEVEL_MAX]). > + * Typically called from Init task when user wants to initilize driver > + * manager after startup, otherwise not used. > + */ > +extern int drvmgr_init(void); > + > +/* Take registered buses and devices into the correct init level, > + * this function is called from _init_level() so normally > + * we don't need to call it directly. > + */ > +extern void drvmgr_init_update(void); > + > +/*! Register Root Bus device driver */ > +extern int drvmgr_root_drv_register(struct drvmgr_drv *drv); > + > +/*! Register a driver */ > +extern int drvmgr_drv_register(struct drvmgr_drv *drv); > + > +/*! Register a device */ > +extern int drvmgr_dev_register(struct drvmgr_dev *dev); Tired eyes have trouble distinguishing drvmgr_drv and drvmgr_dev. Another point in favor of reducing the abbreviations in the drvmgr code base.. This is all the further I got for now. Will finish later. Gedare _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel