DRVMGR: added driver manager to cpukit/libdrvmgr

This commit is contained in:
Daniel Hellstrom
2011-11-28 09:52:03 +01:00
parent 02550220c5
commit e7fade3ac4
37 changed files with 3724 additions and 3 deletions

12
aclocal/enable-drvmgr.m4 Normal file
View File

@@ -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])
])

View File

@@ -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])
])

View File

@@ -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 )
{
}

View File

@@ -57,6 +57,8 @@ void bsp_pretasking_hook(void);
void bsp_predriver_hook(void);
void bsp_driver_level_hook( int level );
void bsp_postdriver_hook(void);
void bsp_reset(void);

View File

@@ -20,6 +20,7 @@ RTEMS_ENABLE_MULTIPROCESSING
RTEMS_ENABLE_POSIX
RTEMS_ENABLE_NETWORKING
RTEMS_ENABLE_CXX
RTEMS_ENABLE_DRVMGR
RTEMS_ENV_RTEMSBSP

View File

@@ -28,6 +28,7 @@ RTEMS_ENABLE_RTEMS_DEBUG
RTEMS_ENABLE_RTEMSBSP
RTEMS_ENABLE_MULTILIB
RTEMS_ENABLE_PARAVIRT
RTEMS_ENABLE_DRVMGR
AC_ARG_ENABLE([docs],
[AS_HELP_STRING([--enable-docs],[enable building documentation

View File

@@ -8,6 +8,7 @@ SUBDIRS = . score rtems sapi posix
SUBDIRS += dev
SUBDIRS += libcrypt
SUBDIRS += libcsupport libblock libfs
SUBDIRS += libdrvmgr
SUBDIRS += libnetworking librpc
SUBDIRS += libpci
SUBDIRS += libi2c
@@ -238,6 +239,12 @@ include_rtems_HEADERS += libmisc/untar/untar.h
## fsmount
include_rtems_HEADERS += libmisc/fsmount/fsmount.h
## Driver manager
include_drvmgrdir = $(includedir)/drvmgr
include_drvmgr_HEADERS = libdrvmgr/drvmgr.h
include_drvmgr_HEADERS += libdrvmgr/drvmgr_confdefs.h
include_drvmgr_HEADERS += libdrvmgr/drvmgr_list.h
## HACK: doxygen filter.
EXTRA_DIST = doxy-filter

View File

@@ -0,0 +1,12 @@
dnl $Id: enable-drvmgr.m4,v 1.0
AC_DEFUN([RTEMS_ENABLE_DRVMGR],
[
AC_ARG_ENABLE(drvmgr,
AS_HELP_STRING(--enable-drvmgr,enable drvmgr 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])
])

View File

@@ -17,6 +17,7 @@ RTEMS_ENABLE_RTEMS_DEBUG
RTEMS_ENABLE_NETWORKING
RTEMS_ENABLE_PARAVIRT
RTEMS_ENABLE_PROFILING
RTEMS_ENABLE_DRVMGR
RTEMS_ENV_RTEMSCPU
RTEMS_CHECK_RTEMS_DEBUG
@@ -229,6 +230,11 @@ RTEMS_CPUOPT([RTEMS_NETWORKING],
[1],
[if networking is enabled])
RTEMS_CPUOPT([RTEMS_DRVMGR_STARTUP],
[test x"$enable_drvmgr" = xyes],
[1],
[if driver manager api is supported])
RTEMS_CPUOPT([RTEMS_VERSION],
[true],
["]_RTEMS_VERSION["],
@@ -453,6 +459,7 @@ score/cpu/v850/Makefile
score/cpu/no_cpu/Makefile
posix/Makefile
libblock/Makefile
libdrvmgr/Makefile
libfs/Makefile
libfs/src/nfsclient/Makefile
libgnat/Makefile

View File

@@ -0,0 +1,33 @@
##
## $Id: Makefile.am
##
include $(top_srcdir)/automake/compile.am
EXTRA_DIST=
noinst_LIBRARIES = libdrvmgr.a
libdrvmgr_a_SOURCES = drvmgr.c
libdrvmgr_a_SOURCES += drvmgr.h
libdrvmgr_a_SOURCES += drvmgr_by_name.c
libdrvmgr_a_SOURCES += drvmgr_by_id.c
libdrvmgr_a_SOURCES += drvmgr_dev_by_name.c
libdrvmgr_a_SOURCES += drvmgr_drvinf.c
libdrvmgr_a_SOURCES += drvmgr_init.c
libdrvmgr_a_SOURCES += drvmgr_confdefs.h
libdrvmgr_a_SOURCES += drvmgr_for_each_dev.c
libdrvmgr_a_SOURCES += drvmgr_for_each_list_dev.c
libdrvmgr_a_SOURCES += drvmgr_func.c
libdrvmgr_a_SOURCES += drvmgr_func_call.c
libdrvmgr_a_SOURCES += drvmgr_list.c
libdrvmgr_a_SOURCES += drvmgr_list.h
libdrvmgr_a_SOURCES += drvmgr_lock.c
libdrvmgr_a_SOURCES += drvmgr_print.c
libdrvmgr_a_SOURCES += drvmgr_res.c
libdrvmgr_a_SOURCES += drvmgr_rw.c
libdrvmgr_a_SOURCES += drvmgr_translate.c
libdrvmgr_a_SOURCES += drvmgr_translate_check.c
libdrvmgr_a_SOURCES += drvmgr_unregister.c
include $(top_srcdir)/automake/local.am

112
cpukit/libdrvmgr/README Normal file
View File

@@ -0,0 +1,112 @@
DRIVER MANAGER
==============
See documentation in Aeroflex Gaisler Driver manual.
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
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
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.
LEVEL0
------
The level of uninitialized devices that have been united with a
driver.
LEVEL1 - FIND/RESET/IRQ Clear
-----------------------------
The driver is for the first time informed of the presence of a
device. Only basic initialization.
- Find all hardware needed for IRQ, Console, Timer and hardware
that need to be reset.
- Reset hardware, so that interrupts are not generated by mistake
when enabled later on.
- Init low level non-interrupt (polling-mode) services needed by
drivers init LEVEL2 and onwards, such as
* Debug UART console for printk()
* Timer API (non-IRQ)
* GPIO (non-IRQ)
* Special non-main memory configuration, washing
- Register IRQ controller at BSP IRQ library
- Register Timer for system clock
- Register Console UART
During this intialization level interrupts may not be registered,
enabled or disabled at the IRQ controller. But, all IRQ sources
should be cleared to avoid spurious interrupts later on.
AFTER LEVEL1 - if initialized during startup
--------------------------------------------
The statically configured drivers are initialized as normally by RTEMS. The
hardware was found in LEVEL1.
CONFIGURE_BSP_PREREQUISITE_DRIVERS may initialize IRQ driver, or
IRQ lib initialized when IRQ controller was registered during LEVEL1.
LEVEL2
------
Initialize other device drivers than IRQ, Timer, console:
- ISR can be registered, enabled, disabled at IRQ controller
(IRQ is still masked by CPU interrupt level if initialized during
RTEMS startup)
- Timer API that does not require IRQ can be used
- printf() can be used
For standard peripherals this is the first initialization.
LEVEL3
------
Initialize drivers that require features/APIs provided by drivers
in LEVEL2.
Such features may involve services that require IRQ to be implemented.
LEVEL4
------
Unused extra level.
LEVEL INACTIVE - NOT ENABLED DEVICES
------------------------------------
List of devices that experienced:
- no driver found for device (not united)
- ignored (not united with a driver, forced by user)
- an error was reported by device driver during initialization

643
cpukit/libdrvmgr/drvmgr.c Normal file
View File

@@ -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.
*/
#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*/
#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
struct rtems_driver_manager drv_mgr = {
.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;
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;
}
/* 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 */
}
/* 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;
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;
}

965
cpukit/libdrvmgr/drvmgr.h Normal file
View File

@@ -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)
/*! 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);
/*! 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);
/* 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*);
/*! 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);
};
#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}
/*** 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
#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 */
};
/* 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 */
};
/*! 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)))
/*! 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);
};
/*** 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);
/*! 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);
/*! 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);
/*! Remove a device, and all its children devices if device is a bus device. The
* device driver will be requested to remove the device and once gone from bus,
* device and driver list the device is put into a inactive list for debugging
* (this is optional by using remove argument).
*
* Removing the Root Bus Device is not supported.
*
* \param remove If non-zero the device will be deallocated, and not put into
* the inacitve list.
*/
extern int drvmgr_dev_unregister(struct drvmgr_dev *dev);
/*! Register a bus */
extern int drvmgr_bus_register(struct drvmgr_bus *bus);
/*! Unregister a bus */
extern int drvmgr_bus_unregister(struct drvmgr_bus *bus);
/*! Unregister all child devices of 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
*/
extern int drvmgr_children_unregister(struct drvmgr_bus *bus);
/* Separate a device from the driver it has been united with */
extern int drvmgr_dev_drv_separate(struct drvmgr_dev *dev);
/*! Allocate a device structure, if no memory available
* rtems_error_fatal_occurred is called.
* The 'extra' argment tells how many bytes extra space is to be allocated after
* the device structure, this is typically used for "businfo" structures. The extra
* space is always aligned to a 4-byte boundary.
*/
extern int drvmgr_alloc_dev(struct drvmgr_dev **pdev, int extra);
/*! Allocate a bus structure, if no memory available rtems_error_fatal_occurred
* is called.
* The 'extra' argment tells how many bytes extra space is to be allocated after
* the device structure, this is typically used for "businfo" structures. The
* extra space is always aligned to a 4-byte boundary.
*/
extern int drvmgr_alloc_bus(struct drvmgr_bus **pbus, int extra);
/*** DRIVER RESOURCE FUNCTIONS ***/
/*! Add resources to a bus, typically used by a bus driver.
*
* \param bus The Bus to add the resources to.
* \param res An array with Driver resources, all together are called bus
* resources.
*/
extern void drvmgr_bus_res_add(struct drvmgr_bus *bus,
struct drvmgr_bus_res *bres);
/*! Find all the resource keys for a device among all driver resources on a
* bus. Typically used by a device driver to get configuration options.
*
* \param dev Device to find resources for
* \param key Location where the pointer to the driver resource array (drvmgr_drv_res->keys) is stored.
*/
extern int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys);
/*! Return the one key that matches key name from a driver keys array. The keys
* can be obtained using drvmgr_keys_get().
*
* \param keys An array of keys ended with KEY_EMPTY to search among.
* \param key_name Name of key to search for among the keys.
*/
extern struct drvmgr_key *drvmgr_key_get(struct drvmgr_key *keys, char *key_name);
/*! Extract key value from the key in the keys array matching name and type.
*
* This function calls drvmgr_keys_get to get the key requested (from key
* name), then determines if the type is correct. A pointer to the key value
* is returned.
*
* \param keys An array of keys ended with KEY_EMPTY to search among.
* \param key_name Name of key to search for among the keys.
* \param key_type Data Type of value. INTEGER, ADDRESS, STRING.
* \return Returns NULL if no value found matching Key Name and Key
* Type.
*/
extern union drvmgr_key_value *drvmgr_key_val_get(
struct drvmgr_key *keys,
char *key_name,
int key_type);
/*! Get key value from the bus resources matching [device, key name, key type]
* if no matching key is found NULL is returned.
*
* This is typically used by device drivers to find a particular device
* resource.
*
* \param dev The device to search resource for.
* \param key_name The key name to search for
* \param key_type The key type expected.
* \return Returns NULL if no value found matching Key Name and
* Key Type was found for device.
*/
extern union drvmgr_key_value *drvmgr_dev_key_get(
struct drvmgr_dev *dev,
char *key_name,
int key_type);
/*** DRIVER INTERACE USED TO REQUEST INFORMATION/SERVICES FROM BUS DRIVER ***/
/*! Get parent bus */
static inline struct drvmgr_bus *drvmgr_get_parent(struct drvmgr_dev *dev)
{
if (dev)
return dev->parent;
else
return NULL;
}
/*! Get Driver of device */
static inline struct drvmgr_drv *drvmgr_get_drv(struct drvmgr_dev *dev)
{
if (dev)
return dev->drv;
else
return NULL;
}
/*! Calls func() for every device found in the device tree, regardless of
* device state or if a driver is assigned. With the options argument the user
* can decide to do either a depth-first or a breadth-first search.
*
* If the function func() returns a non-zero value then for_each_dev will
* return imediatly with the same return value as func() returned.
*
* \param func Function called on each device
* \param arg Custom function argument
* \param options Search Options, see DRVMGR_FED_*
*
*/
#define DRVMGR_FED_BF 1 /* Breadth-first search */
#define DRVMGR_FED_DF 0 /* Depth first search */
extern int drvmgr_for_each_dev(
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg,
int options);
/*! Get Device pointer from Driver and Driver minor number
*
* \param drv Driver the device is united with.
* \param minor Driver minor number assigned to device.
* \param pdev Location where the Device point will be stored.
* \return Zero on success. -1 on failure, when device was not
* found in driver device list.
*/
extern int drvmgr_get_dev(
struct drvmgr_drv *drv,
int minor,
struct drvmgr_dev **pdev);
/*! Get Bus frequency in Hertz. Frequency is stored into address of freq_hz.
*
* \param dev The Device to get Bus frequency for.
* \param options Bus-type specific options
* \param freq_hz Location where Bus Frequency will be stored.
*/
extern int drvmgr_freq_get(
struct drvmgr_dev *dev,
int options,
unsigned int *freq_hz);
/*! Return 0 if dev is not located on the root bus, 1 if on root bus */
extern int drvmgr_on_rootbus(struct drvmgr_dev *dev);
/*! Get device name prefix, this name can be used to register a unique name in
* the bus->error filesystem or to get an idea where the device is located.
*
* \param dev The Device to get the device Prefix for.
* \param dev_prefix Location where the prefix will be stored.
*/
extern int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix);
/*! Register a shared interrupt handler. Since this service is shared among
* interrupt drivers/handlers the handler[arg] must be installed before the
* interrupt can be cleared or disabled. The handler is by default disabled
* after registration.
*
* \param index Index is used to identify the IRQ number if hardware has
* multiple IRQ sources. Normally Index is set to 0 to
* indicated the first and only IRQ source.
* A negative index is interpreted as a absolute bus IRQ
* number.
* \param isr Interrupt Service Routine.
* \param arg Optional ISR argument.
*/
extern int drvmgr_interrupt_register(
struct drvmgr_dev *dev,
int index,
const char *info,
drvmgr_isr isr,
void *arg);
/*! Unregister an interrupt handler. This also disables the interrupt before
* unregistering the interrupt handler.
* \param index Index is used to identify the IRQ number if hardware has
* multiple IRQ sources. Normally Index is set to 0 to
* indicated the first and only IRQ source.
* A negative index is interpreted as a absolute bus IRQ
* number.
* \param isr Interrupt Service Routine, previously registered.
* \param arg Optional ISR argument, previously registered.
*/
extern int drvmgr_interrupt_unregister(
struct drvmgr_dev *dev,
int index,
drvmgr_isr isr,
void *arg);
/*! Clear (ACK) pending interrupt
*
* \param dev Device to clear interrupt for.
* \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources.
* Normally Index is set to 0 to indicated the first and only IRQ source.
* A negative index is interpreted as a absolute bus IRQ number.
* \param isr Interrupt Service Routine, previously registered.
* \param arg Optional ISR argument, previously registered.
*/
extern int drvmgr_interrupt_clear(
struct drvmgr_dev *dev,
int index);
/*! Force unmasking/enableing an interrupt on the interrupt controller, this is not normally used,
* if used the caller has masked/disabled the interrupt just before.
*
* \param dev Device to clear interrupt for.
* \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources.
* Normally Index is set to 0 to indicated the first and only IRQ source.
* A negative index is interpreted as a absolute bus IRQ number.
* \param isr Interrupt Service Routine, previously registered.
* \param arg Optional ISR argument, previously registered.
*/
extern int drvmgr_interrupt_unmask(
struct drvmgr_dev *dev,
int index);
/*! Force masking/disable an interrupt on the interrupt controller, this is not normally performed
* since this will stop all other (shared) ISRs to be disabled until _unmask() is called.
*
* \param dev Device to mask interrupt for.
* \param index Index is used to identify the IRQ number if hardware has multiple IRQ sources.
* Normally Index is set to 0 to indicated the first and only IRQ source.
* A negative index is interpreted as a absolute bus IRQ number.
*/
extern int drvmgr_interrupt_mask(
struct drvmgr_dev *dev,
int index);
/*! drvmgr_translate() translation options */
enum drvmgr_tr_opts {
/* Translate CPU RAM Address (input) to DMA unit accessible address
* (output), this is an upstreams translation in reverse order.
*
* Typical Usage:
* It is common to translate a CPU accessible RAM address to an
* address that DMA units can access over bridges.
*/
CPUMEM_TO_DMA = 0x0,
/* Translate DMA Unit Accessible address mapped to CPU RAM (input) to
* CPU accessible address (output). This is an upstreams translation.
*
* Typical Usage (not often used):
* The DMA unit descriptors contain pointers to DMA buffers located at
* CPU RAM addresses that the DMA unit can access, the CPU processes
* the descriptors and want to access the data but a translation back
* to CPU address is required.
*/
CPUMEM_FROM_DMA = 0x1,
/* Translate DMA Memory Address (input) to CPU accessible address
* (output), this is a downstreams translation in reverse order.
*
* Typical Usage:
* A PCI network card puts packets into its memory not doing DMA over
* PCI, in order for the CPU to access them the PCI address must be
* translated.
*/
DMAMEM_TO_CPU = 0x2,
/* Translate CPU accessible address (input) mapped to DMA Memory Address
* to DMA Unit accessible address (output). This is a downstreams
* translation.
*/
DMAMEM_FROM_CPU = 0x3,
};
#define DRVMGR_TR_REVERSE 0x1 /* do reverse translation direction order */
#define DRVMGR_TR_PATH 0x2 /* 0x0=down-stream 0x2=up-stream address path */
/*! Translate an address on one bus to an address on another bus.
*
* The device determines source or destination bus, the root bus is always
* the other bus. It is assumed that the CPU is located on the root bus or
* that it can access it without address translation (mapped 1:1). The CPU
* is thus assumed to be located on level 0 top most in the bus hierarchy.
*
* If no map is present in the bus driver src_address is translated 1:1
* (just copied).
*
* Addresses are typically converted up-streams from the DMA unit towards the
* CPU (DMAMEM_TO_CPU) or down-streams towards DMA hardware from the CPU
* (CPUMEM_TO_DMA) over one or multiple bridges depending on bus architecture.
* See 'enum drvmgr_tr_opts' for other translation direction options.
* For example:
* Two common operations is to translate a CPU accessible RAM address to an
* address that DMA units can access (dev=DMA-unit, CPUMEM_TO_DMA,
* src_address=CPU-RAM-ADR) and to translate an address of a PCI resource for
* example RAM mapped into a PCI BAR to an CPU accessible address
* (dev=PCI-device, DMAMEM_TO_CPU, src_address=PCI-BAR-ADR).
*
* Source address is translated and the result is put into *dst_address, if
* the address is not accessible on the other bus -1 is returned.
*
* \param dev Device to translate addresses for
* \param options Tanslation direction options, see enum drvmgr_tr_opts
* \param src_address Address to translate
* \param dst_address Location where translated address is stored
*
* Returns 0 if unable to translate. The remaining length from the given
* address of the map is returned on success, for example if a map starts
* at 0x40000000 of size 0x100000 the result will be 0x40000 if the address
* was translated into 0x400C0000.
* If dev is on root-bus no translation is performed 0xffffffff is returned
* and src_address is stored in *dst_address.
*/
extern unsigned int drvmgr_translate(
struct drvmgr_dev *dev,
unsigned int options,
void *src_address,
void **dst_address);
/* Translate addresses between buses, used internally to implement
* drvmgr_translate. Function is not limited to translate from/to root bus
* where CPU is resident, however buses must be on a straight path relative
* to each other (parent of parent of parent and so on).
*
* \param from src_address is given for this bus
* \param to src_address is translated to this bus
* \param reverse Selects translation method, if map entries are used in
* the reverse order (map_up->to is used as map_up->from)
* \param src_address Address to be translated
* \param dst_address Translated address is stored here on success (return=0)
*
* Returns 0 if unable to translate. The remaining length from the given
* address of the map is returned on success and the result is stored into
* *dst_address. For example if a map starts at 0x40000000 of size 0x100000
* the result will be 0x40000 if the address was translated into 0x400C0000.
* If dev is on root-bus no translation is performed 0xffffffff is returned.
* and src_address is stored in *dst_address.
*/
extern unsigned int drvmgr_translate_bus(
struct drvmgr_bus *from,
struct drvmgr_bus *to,
int reverse,
void *src_address,
void **dst_address);
/* Calls drvmgr_translate() to translate an address range and checks the result,
* a printout is generated if the check fails. All parameters are passed on to
* drvmgr_translate() except for size, see paramters of drvmgr_translate().
*
* If size=0 only the starting address is not checked.
*
* If mapping failes a non-zero result is returned.
*/
extern int drvmgr_translate_check(
struct drvmgr_dev *dev,
unsigned int options,
void *src_address,
void **dst_address,
unsigned int size);
/*! Get function pointer from Device Driver or Bus Driver.
*
* Returns 0 if function is available
*/
extern int drvmgr_func_get(void *obj, int funcid, void **func);
/*! Lookup function and call it directly with the four optional arguments */
extern int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d);
/* Builds a Function ID.
*
* Used to request optional functions by a bus or device driver
*/
#define DRVMGR_FUNCID(major, minor) ((((major) & 0xfff) << 20) | ((minor) & 0xfffff))
#define DRVMGR_FUNCID_NONE 0
#define DRVMGR_FUNCID_END DRVMGR_FUNCID(DRVMGR_FUNCID_NONE, 0)
/* Major Function ID. Most significant 12-bits. */
enum {
FUNCID_NONE = 0x000,
FUNCID_RW = 0x001, /* Read/Write functions */
};
/* Select Sub-Function Read/Write function by ID */
#define RW_SIZE_1 0x00001 /* Access Size */
#define RW_SIZE_2 0x00002
#define RW_SIZE_4 0x00004
#define RW_SIZE_8 0x00008
#define RW_SIZE_ANY 0x00000
#define RW_SIZE(id) ((unsigned int)(id) & 0xf)
#define RW_DIR_ANY 0x00000 /* Access Direction */
#define RW_READ 0x00000 /* Read */
#define RW_WRITE 0x00010 /* Write */
#define RW_SET 0x00020 /* Write with same value (memset) */
#define RW_DIR(id) (((unsigned int)(id) >> 4) & 0x3)
#define RW_RAW 0x00000 /* Raw access - no swapping (machine default) */
#define RW_LITTLE 0x00040 /* Little Endian */
#define RW_BIG 0x00080 /* Big Endian */
#define RW_ENDIAN(id) (((unsigned int)(id) >> 6) & 0x3)
#define RW_TYPE_ANY 0x00000 /* Access type */
#define RW_REG 0x00100
#define RW_MEM 0x00200
#define RW_MEMREG 0x00300
#define RW_CFG 0x00400
#define RW_TYPE(id) (((unsigned int)(id) >> 8) & 0xf)
#define RW_ARG 0x01000 /* Optional Argument */
#define RW_ERR 0x02000 /* Optional Error Handler */
/* Build a Read/Write function ID */
#define DRVMGR_RWFUNC(minor) DRVMGR_FUNCID(FUNCID_RW, minor)
/* Argument to Read/Write functions, the "void *arg" pointer is returned by
* RW_ARG. If NULL is returned no argument is needed.
*/
struct drvmgr_rw_arg {
void *arg;
struct drvmgr_dev *dev;
};
/* Standard Read/Write function types */
typedef uint8_t (*drvmgr_r8)(uint8_t *srcadr);
typedef uint16_t (*drvmgr_r16)(uint16_t *srcadr);
typedef uint32_t (*drvmgr_r32)(uint32_t *srcadr);
typedef uint64_t (*drvmgr_r64)(uint64_t *srcadr);
typedef void (*drvmgr_w8)(uint8_t *dstadr, uint8_t data);
typedef void (*drvmgr_w16)(uint16_t *dstadr, uint16_t data);
typedef void (*drvmgr_w32)(uint32_t *dstadr, uint32_t data);
typedef void (*drvmgr_w64)(uint64_t *dstadr, uint64_t data);
/* READ/COPY a memory area located on bus into CPU memory.
* From 'src' (remote) to the destination 'dest' (local), n=number of bytes
*/
typedef int (*drvmgr_rmem)(void *dest, const void *src, int n);
/* WRITE/COPY a user buffer located in CPU memory to a location on the bus.
* From 'src' (local) to the destination 'dest' (remote), n=number of bytes
*/
typedef int (*drvmgr_wmem)(void *dest, const void *src, int n);
/* Set a memory area to the byte value given in c, see LIBC memset(). Memset is
* implemented by calling wmem() multiple times with a "large" buffer.
*/
typedef int (*drvmgr_memset)(void *dstadr, int c, size_t n);
/* Read/Write function types with additional argument */
typedef uint8_t (*drvmgr_r8_arg)(uint8_t *srcadr, void *a);
typedef uint16_t (*drvmgr_r16_arg)(uint16_t *srcadr, void *a);
typedef uint32_t (*drvmgr_r32_arg)(uint32_t *srcadr, void *a);
typedef uint64_t (*drvmgr_r64_arg)(uint64_t *srcadr, void *a);
typedef void (*drvmgr_w8_arg)(uint8_t *dstadr, uint8_t data, void *a);
typedef void (*drvmgr_w16_arg)(uint16_t *dstadr, uint16_t data, void *a);
typedef void (*drvmgr_w32_arg)(uint32_t *dstadr, uint32_t data, void *a);
typedef void (*drvmgr_w64_arg)(uint64_t *dstadr, uint64_t data, void *a);
typedef int (*drvmgr_rmem_arg)(void *dest, const void *src, int n, void *a);
typedef int (*drvmgr_wmem_arg)(void *dest, const void *src, int n, void *a);
typedef int (*drvmgr_memset_arg)(void *dstadr, int c, size_t n, void *a);
/* Report an error to the parent bus of the device */
typedef void (*drvmgr_rw_err)(struct drvmgr_rw_arg *a, struct drvmgr_bus *bus,
int funcid, void *adr);
/* Helper function for buses that implement the memset() over wmem() */
extern void drvmgr_rw_memset(
void *dstadr,
int c,
size_t n,
void *a,
drvmgr_wmem_arg wmem
);
/*** PRINT INFORMATION ABOUT DRIVER MANAGER ***/
/*! Calls func() for every device found matching the search requirements of
* set_mask and clr_mask. Each bit set in set_mask must be set in the
* device state bit mask (dev->state), and Each bit in the clr_mask must
* be cleared in the device state bit mask (dev->state). There are three
* special cases:
*
* 1. If state_set_mask and state_clr_mask are zero the state bits are
* ignored and all cores are treated as a match.
*
* 2. If state_set_mask is zero the function func will not be called due to
* a bit being set in the state mask.
*
* 3. If state_clr_mask is zero the function func will not be called due to
* a bit being cleared in the state mask.
*
* If the function func() returns a non-zero value then for_each_dev will
* return imediatly with the same return value as func() returned.
*
* \param devlist The list to iterate though searching for devices.
* \param state_set_mask Defines the bits that must be set in dev->state
* \param state_clr_mask Defines the bits that must be cleared in dev->state
* \param func Function called on each
*
*/
extern int drvmgr_for_each_listdev(
struct drvmgr_list *devlist,
unsigned int state_set_mask,
unsigned int state_clr_mask,
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg);
/* Print all devices */
#define PRINT_DEVS_FAILED 0x01 /* Failed during initialization */
#define PRINT_DEVS_ASSIGNED 0x02 /* Driver assigned */
#define PRINT_DEVS_UNASSIGNED 0x04 /* Driver not assigned */
#define PRINT_DEVS_IGNORED 0x08 /* Device ignored on user's request */
#define PRINT_DEVS_ALL (PRINT_DEVS_FAILED | \
PRINT_DEVS_ASSIGNED | \
PRINT_DEVS_UNASSIGNED |\
PRINT_DEVS_IGNORED)
/*! Print number of devices, buses and drivers */
extern void drvmgr_summary(void);
/*! Print devices with certain condictions met according to 'options' */
extern void drvmgr_print_devs(unsigned int options);
/*! Print device/bus topology */
extern void drvmgr_print_topo(void);
/*! Print the memory usage
* Only accounts for data structures. Not for the text size.
*/
extern void drvmgr_print_mem(void);
#define OPTION_DEV_GENINFO 0x00000001
#define OPTION_DEV_BUSINFO 0x00000002
#define OPTION_DEV_DRVINFO 0x00000004
#define OPTION_DRV_DEVS 0x00000100
#define OPTION_BUS_DEVS 0x00010000
#define OPTION_RECURSIVE 0x01000000
#define OPTION_INFO_ALL 0xffffffff
/*! Print information about a driver manager object (device, driver, bus) */
extern void drvmgr_info(void *id, unsigned int options);
/*! Get information about a device */
extern void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options);
/*! Get information about a bus */
extern void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options);
/*! Get information about a driver */
extern void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options);
/*! Get information about all devices on a bus */
extern void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options);
/*! Get information about all devices in the system (on all buses) */
extern void drvmgr_info_devs(unsigned int options);
/*! Get information about all drivers in the system */
extern void drvmgr_info_drvs(unsigned int options);
/*! Get information about all buses in the system */
extern void drvmgr_info_buses(unsigned int options);
/*! Get Driver by Driver ID */
extern struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id);
/*! Get Driver by Driver Name */
extern struct drvmgr_drv *drvmgr_drv_by_name(const char *name);
/*! Get Device by Device Name */
extern struct drvmgr_dev *drvmgr_dev_by_name(const char *name);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,33 @@
/* Find driver by driver-ID
*
* 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 <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
/* Get driver from driver name */
struct drvmgr_drv *drvmgr_drv_by_id(uint64_t id)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_drv *drv = NULL;
/* 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
*/
drv = DRV_LIST_HEAD(&mgr->drivers);
while (drv) {
if (drv->drv_id == id)
break;
drv = drv->next;
}
return drv;
}

View File

@@ -0,0 +1,37 @@
/* Find driver by driver-name
*
* 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 <string.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
/* Get driver from driver name */
struct drvmgr_drv *drvmgr_drv_by_name(const char *name)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_drv *drv = NULL;
if (!name)
return NULL;
/* 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
*/
drv = DRV_LIST_HEAD(&mgr->drivers);
while (drv) {
if (drv->name && (strcmp(drv->name, name) == 0))
break;
drv = drv->next;
}
return drv;
}

View File

@@ -0,0 +1,86 @@
/* Driver Manager Configuration file.
*
* 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.
*/
/*
* The configuration consist of an array with function pointers that
* register one or more drivers that will be used by the Driver Manger.
*
* The Functions are called in the order they are declared.
*
*/
#ifndef _DRIVER_MANAGER_CONFDEFS_H_
#define _DRIVER_MANAGER_CONFDEFS_H_
#include "drvmgr.h"
#ifdef __cplusplus
extern "C" {
#endif
extern struct drvmgr_drv_reg_func drvmgr_drivers[];
#ifdef CONFIGURE_INIT
#if 0 /* EXAMPLE: GPTIMER driver definition */
#define DRIVER_AMBAPP_GAISLER_GPTIMER_REG {gptimer_register_drv}
extern void gptimer_register_drv(void);
#endif
/* CONFIGURE DRIVER MANAGER */
struct drvmgr_drv_reg_func drvmgr_drivers[] = {
#if 0 /* EXAMPLE: GPTIMER Driver registration */
#ifdef CONFIGURE_DRIVER_AMBAPP_GAISLER_GPTIMER
DRIVER_AMBAPP_GAISLER_GPTIMER_REG,
#endif
#endif
/* Macros for adding custom drivers without needing to recompile
* kernel.
*/
#ifdef CONFIGURE_DRIVER_CUSTOM1
DRIVER_CUSTOM1_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM2
DRIVER_CUSTOM2_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM3
DRIVER_CUSTOM3_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM4
DRIVER_CUSTOM4_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM5
DRIVER_CUSTOM5_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM6
DRIVER_CUSTOM6_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM7
DRIVER_CUSTOM7_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM8
DRIVER_CUSTOM8_REG,
#endif
#ifdef CONFIGURE_DRIVER_CUSTOM9
DRIVER_CUSTOM9_REG,
#endif
/* End array with NULL */
{NULL}
};
#endif /* CONFIGURE_INIT */
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_MANAGER_CONFDEFS_H_ */

View File

@@ -0,0 +1,34 @@
/* Find device by device name
*
* 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 <string.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
static int dev_name_compare(struct drvmgr_dev *dev, void *arg)
{
const char *name = arg;
if (dev->name && (strcmp(dev->name, name) == 0))
return (int)dev;
else
return 0;
}
/* Get device by device name or bus name */
struct drvmgr_dev *drvmgr_dev_by_name(const char *name)
{
if (!name)
return NULL;
return (struct drvmgr_dev *)
drvmgr_for_each_dev(dev_name_compare, (void *)name, 0);
}

View File

@@ -0,0 +1,148 @@
/* Driver Manager Driver 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.
*
*/
/*
* This is the part the device driver API, the functions rely on that the
* parent bus driver has implemented the neccessary operations correctly.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
/* Get device pointer from knowing the Driver and the Driver minor
* that was assigned to it
*/
int drvmgr_get_dev(
struct drvmgr_drv *drv,
int minor,
struct drvmgr_dev **pdev)
{
struct drvmgr_dev *dev;
if (!drv)
return -1;
DRVMGR_LOCK_READ();
dev = drv->dev;
while (dev) {
if (dev->minor_drv == minor)
break;
dev = dev->next_in_drv;
}
DRVMGR_UNLOCK();
if (!dev)
return -1;
if (pdev)
*pdev = dev;
return 0;
}
/* Get Bus frequency in HZ from bus driver */
int drvmgr_freq_get(
struct drvmgr_dev *dev,
int options,
unsigned int *freq_hz)
{
if (!dev || !dev->parent || !dev->parent->ops->freq_get)
return -1;
return dev->parent->ops->freq_get(dev, options, freq_hz);
}
/* Get driver prefix */
int drvmgr_get_dev_prefix(struct drvmgr_dev *dev, char *dev_prefix)
{
struct drvmgr_bus_params params;
if (!dev || !dev->parent || !dev->parent->ops->get_params)
return -1;
dev->parent->ops->get_params(dev, &params);
if (!params.dev_prefix)
return -1;
if (dev_prefix)
strcpy(dev_prefix, params.dev_prefix);
return 0;
}
/* Register an interrupt */
int drvmgr_interrupt_register(
struct drvmgr_dev *dev,
int index,
const char *info,
drvmgr_isr isr,
void *arg)
{
if (!dev || !dev->parent || !dev->parent->ops->int_register)
return -1;
if (!isr)
return -1;
return dev->parent->ops->int_register(dev, index, info, isr, arg);
}
/* Unregister an interrupt */
int drvmgr_interrupt_unregister(
struct drvmgr_dev *dev,
int index,
drvmgr_isr isr,
void *arg)
{
if (!dev || !dev->parent || !dev->parent->ops->int_unregister)
return -1;
if (!isr)
return -1;
return dev->parent->ops->int_unregister(dev, index, isr, arg);
}
int drvmgr_interrupt_clear(
struct drvmgr_dev *dev,
int index)
{
if (!dev || !dev->parent || !dev->parent->ops->int_clear)
return -1;
return dev->parent->ops->int_clear(dev, index);
}
int drvmgr_interrupt_unmask(
struct drvmgr_dev *dev,
int index)
{
if (!dev || !dev->parent || !dev->parent->ops->int_unmask)
return -1;
return dev->parent->ops->int_unmask(dev, index);
}
int drvmgr_interrupt_mask(
struct drvmgr_dev *dev,
int index)
{
if (!dev || !dev->parent || !dev->parent->ops->int_mask)
return -1;
return dev->parent->ops->int_mask(dev, index);
}
int drvmgr_on_rootbus(struct drvmgr_dev *dev)
{
if (dev->parent && dev->parent->dev && dev->parent->dev->parent)
return 0;
else
return 1;
}

View File

@@ -0,0 +1,104 @@
/* Iterate over device tree topology, breadth or depth-first
*
* 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.
*/
#include <string.h>
#include <drvmgr/drvmgr.h>
#include <drvmgr/drvmgr_list.h>
#include "drvmgr_internal.h"
/* Traverse device tree breadth-first. Supports up to 31 buses */
static int drvmgr_for_each_dev_breadth(
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg
)
{
int ret = 0, i, pos;
struct drvmgr_bus *bus, *buses[32];
struct drvmgr_dev *dev;
pos = 0;
memset(&buses[0], 0, sizeof(buses));
buses[pos++] = drv_mgr.root_dev.bus; /* Get root bus */
for (i = 0, bus = buses[0]; buses[i]; i++, bus = buses[i]) {
dev = bus->children;
while (dev) {
ret = func(dev, arg);
if (ret != 0)
break;
if (dev->bus && pos < 31)
buses[pos++] = dev->bus;
dev = dev->next_in_bus;
}
}
return ret;
}
/* Traverse device tree depth-first. */
static int drvmgr_for_each_dev_depth(
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg
)
{
int ret = 0;
struct drvmgr_dev *dev;
/* Get first device */
dev = drv_mgr.root_dev.bus->children;
while (dev) {
ret = func(dev, arg);
if (ret != 0)
break;
if (dev->bus && dev->bus->children) {
dev = dev->bus->children;
} else {
next_dev:
if (dev->next_in_bus == NULL) {
/* Step up one level... back to parent bus */
dev = dev->parent->dev;
if (dev == &drv_mgr.root_dev)
break;
goto next_dev;
} else {
dev = dev->next_in_bus;
}
}
}
return ret;
}
/* Traverse device tree depth-first or breadth-first */
int drvmgr_for_each_dev(
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg,
int options
)
{
int ret;
DRVMGR_LOCK_READ();
/* Get Root Device */
if (drv_mgr.root_dev.bus->children != NULL) {
if (options & DRVMGR_FED_BF)
ret = drvmgr_for_each_dev_breadth(func, arg);
else
ret = drvmgr_for_each_dev_depth(func, arg);
} else
ret = 0;
DRVMGR_UNLOCK();
return ret;
}

View File

@@ -0,0 +1,44 @@
/* Iterate over one list of devices used internally by driver manager
*
* 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.
*/
#include <drvmgr/drvmgr.h>
#include <drvmgr/drvmgr_list.h>
#include "drvmgr_internal.h"
int drvmgr_for_each_listdev(
struct drvmgr_list *devlist,
unsigned int state_set_mask,
unsigned int state_clr_mask,
int (*func)(struct drvmgr_dev *dev, void *arg),
void *arg
)
{
struct drvmgr_dev *dev;
int ret = 0;
DRVMGR_LOCK_READ();
/* Get First Device */
dev = DEV_LIST_HEAD(devlist);
while (dev) {
if (((state_set_mask != 0) && ((dev->state & state_set_mask) == state_set_mask)) ||
((state_clr_mask != 0) && ((dev->state & state_clr_mask) == 0)) ||
((state_set_mask == 0) && (state_clr_mask == 0))) {
ret = func(dev, arg);
if (ret != 0)
break;
}
dev = dev->next;
}
DRVMGR_UNLOCK();
return ret;
}

View File

@@ -0,0 +1,42 @@
/* Driver Manager optional dynamic function interface
*
* 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 <drvmgr/drvmgr.h>
/* Get Function from Function ID */
int drvmgr_func_get(void *obj, int funcid, void **func)
{
int objtype;
struct drvmgr_func *f;
if (!obj)
return DRVMGR_FAIL;
objtype = *(int *)obj;
if (objtype == DRVMGR_OBJ_BUS)
f = ((struct drvmgr_bus *)obj)->funcs;
else if (objtype == DRVMGR_OBJ_DRV)
f = ((struct drvmgr_drv *)obj)->funcs;
else
return DRVMGR_FAIL;
if (f == NULL)
return DRVMGR_FAIL;
while (f->funcid != DRVMGR_FUNCID_NONE) {
if (f->funcid == funcid) {
*func = f->func;
return DRVMGR_OK;
}
f++;
}
return DRVMGR_FAIL;
}

View File

@@ -0,0 +1,21 @@
/* Driver Manager optional dynamic function interface
*
* 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 <drvmgr/drvmgr.h>
/* Lookup function from function ID and call it using given arguments */
int drvmgr_func_call(void *obj, int funcid, void *a, void *b, void *c, void *d)
{
int (*func)(void *arg1, void *arg2, void *arg3, void *arg4) = NULL;
if (drvmgr_func_get(obj, funcid, (void *)&func) != DRVMGR_OK)
return DRVMGR_FAIL;
return func(a, b, c, d);
}

View File

@@ -0,0 +1,26 @@
/* Driver Manager Initialization
*
* 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.
*/
#include <drvmgr/drvmgr.h>
/* Init driver manager - all in one go. Typically called from Init task when
* user wants to initilize driver manager after startup, otherwise not used.
*/
int drvmgr_init(void)
{
int level;
_DRV_Manager_initialization();
for (level = 1; level <= DRVMGR_LEVEL_MAX; level++)
_DRV_Manager_init_level(level);
return 0;
}

View File

@@ -0,0 +1,70 @@
/* Private driver manager declarations
*
* 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.
*/
/* Structure hold all information the driver manager needs to know of. Used
* internally by Driver Manager routines.
*/
struct rtems_driver_manager {
int level;
int initializing_objs;
/* Device tree Lock */
rtems_id lock;
/* The first device - The root device and it's driver */
struct drvmgr_drv *root_drv;
struct drvmgr_dev root_dev;
/*!< Linked list of all registered drivers */
struct drvmgr_list drivers;
/* Buses that reached a certain initialization level.
* Lists by Level:
* N=0 - Not intialized, just registered
* N=1..MAX-1 - Reached init level N
* N=MAX - Successfully initialized bus
*/
struct drvmgr_list buses[DRVMGR_LEVEL_MAX+1];
/* Buses failed to initialize or has been removed by not freed */
struct drvmgr_list buses_inactive;
/* Devices that reached a certain initialization level.
* Lists by Level:
* N=0 - Not intialized, just registered
* N=1..MAX-1 - Reached init level N
* N=MAX - Successfully initialized device
*/
struct drvmgr_list devices[DRVMGR_LEVEL_MAX+1];
/*!< Devices failed to initialize, removed, ignored, no driver */
struct drvmgr_list devices_inactive;
};
extern struct rtems_driver_manager drv_mgr;
extern void _DRV_Manager_Lock(void);
extern void _DRV_Manager_Unlock(void);
extern int _DRV_Manager_Init_Lock(void);
/* The best solution is to implement the locking with a RW lock, however there
* is no such API available. Care must be taken so that dead-lock isn't created
* for example in recursive functions.
*/
#if defined(DRVMGR_USE_LOCKS) && (DRVMGR_USE_LOCKS == 1)
#define DRVMGR_LOCK_INIT() _DRV_Manager_Init_Lock()
#define DRVMGR_LOCK_WRITE() _DRV_Manager_Lock()
#define DRVMGR_LOCK_READ() _DRV_Manager_Lock()
#define DRVMGR_UNLOCK() _DRV_Manager_Unlock()
#else
/* no locking */
#define DRVMGR_LOCK_INIT()
#define DRVMGR_LOCK_WRITE()
#define DRVMGR_LOCK_READ()
#define DRVMGR_UNLOCK()
#endif

View File

@@ -0,0 +1,67 @@
/* Driver Manager List 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.
*/
#include <stdlib.h>
#include <drvmgr/drvmgr_list.h>
/* LIST interface */
void drvmgr_list_init(struct drvmgr_list *list, int offset)
{
list->head = list->tail = NULL;
list->ofs = offset;
}
void drvmgr_list_empty(struct drvmgr_list *list)
{
list->head = list->tail = NULL;
}
void drvmgr_list_add_head(struct drvmgr_list *list, void *entry)
{
LIST_FIELD(list, entry) = list->head;
if (list->head == NULL)
list->tail = entry;
list->head = entry;
}
void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry)
{
if (list->tail == NULL)
list->head = entry;
else
LIST_FIELD(list, list->tail) = entry;
LIST_FIELD(list, entry) = NULL;
list->tail = entry;
}
void drvmgr_list_remove_head(struct drvmgr_list *list)
{
list->head = LIST_FIELD(list, list->head);
if (list->head == NULL)
list->tail = NULL;
}
void drvmgr_list_remove(struct drvmgr_list *list, void *entry)
{
void **prevptr = &list->head;
void *curr, *prev;
prev = NULL;
curr = list->head;
while (curr != entry) {
prev = curr;
prevptr = &LIST_FIELD(list, curr);
curr = LIST_FIELD(list, curr);
}
*prevptr = LIST_FIELD(list, entry);
if (list->tail == entry)
list->tail = prev;
}

View File

@@ -0,0 +1,79 @@
/* Linked list help functions used by driver manager.
*
* 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.
*/
/*
* Help functions for the Driver Manager. Implements a singly linked list
* with head and tail pointers for fast insertions/deletions to head and
* tail in list.
*/
#ifndef _DRVIVER_MANAGER_LIST_H_
#define _DRVIVER_MANAGER_LIST_H_
#ifdef __cplusplus
extern "C" {
#endif
/*! List description, Singly link list with head and tail pointers. */
struct drvmgr_list {
void *head; /*!< First entry in queue */
void *tail; /*!< Last entry in queue */
int ofs; /*!< Offset into head and tail to find next field */
};
/* Static initialization of list */
#define LIST_INITIALIZER(type, field) {NULL, NULL, offsetof(type, field)}
/* Return the first element in list */
#define LIST_HEAD(list, type) ((type *)(list)->head)
/* Return the last element in list */
#define LIST_TAIL(list, type) ((type *)(list)->tail)
/* Get the next pointer of an entry */
#define LIST_FIELD(list, entry) (*(void **)((char *)(entry) + (list)->ofs))
/* Return the next emlement in list */
#define LIST_NEXT(list, entry, type) ((type *)(LIST_FIELD(list, entry)))
/* Iterate through all entries in list */
#define LIST_FOR_EACH(list, entry, type) \
for (entry = LIST_HEAD(list, type); \
entry; \
entry = LIST_NEXT(list, entry, type))
/*! Initialize a list during runtime
*
* \param list The list to initialize
* \param offset The number of bytes into the entry structure the next pointer
* is found
*/
extern void drvmgr_list_init(struct drvmgr_list *list, int offset);
/*! Clear list */
extern void drvmgr_list_empty(struct drvmgr_list *list);
/*! Add entry to front of list */
extern void drvmgr_list_add_head(struct drvmgr_list *list, void *entry);
/*! Add entry to end of list */
extern void drvmgr_list_add_tail(struct drvmgr_list *list, void *entry);
/*! Remove entry from front of list */
extern void drvmgr_list_remove_head(struct drvmgr_list *list);
/*! Remove entry from anywhere in list */
extern void drvmgr_list_remove(struct drvmgr_list *list, void *entry);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,38 @@
/* Driver Manager Internal locking 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.
*/
#include <rtems.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
void _DRV_Manager_Lock(void)
{
rtems_semaphore_obtain(drv_mgr.lock, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
}
void _DRV_Manager_Unlock(void)
{
rtems_semaphore_release(drv_mgr.lock);
}
int _DRV_Manager_Init_Lock(void)
{
int rc;
rc = rtems_semaphore_create(
rtems_build_name('D', 'R', 'V', 'M'),
1,
RTEMS_DEFAULT_ATTRIBUTES,
0,
&drv_mgr.lock);
if (rc != RTEMS_SUCCESSFUL)
return -1;
return 0;
}

View File

@@ -0,0 +1,457 @@
/* Driver Manager Information printing 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.
*/
/*
* These functions print stuff about the driver manager, what devices were
* found and were united with a driver, the Bus topology, memory taken, etc.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
typedef void (*fun_ptr)(void);
static int print_dev_found(struct drvmgr_dev *dev, void *arg)
{
char **pparg = arg;
if (pparg && *pparg) {
printf(*pparg);
*pparg = NULL;
}
printf(" DEV %p %s on bus %p\n", dev,
dev->name ? dev->name : "NO_NAME", dev->parent);
return 0; /* Continue to next device */
}
void drvmgr_print_devs(unsigned int options)
{
struct rtems_driver_manager *mgr = &drv_mgr;
char *parg;
/* Print Drivers */
if (options & PRINT_DEVS_ASSIGNED) {
parg = " --- DEVICES ASSIGNED TO DRIVER ---\n";
drvmgr_for_each_listdev(&mgr->devices[DRVMGR_LEVEL_MAX],
DEV_STATE_UNITED, 0, print_dev_found, &parg);
if (parg != NULL)
printf("\n NO DEVICES WERE ASSIGNED A DRIVER\n");
}
if (options & PRINT_DEVS_UNASSIGNED) {
parg = "\n --- DEVICES WITHOUT DRIVER ---\n";
drvmgr_for_each_listdev(&mgr->devices_inactive, 0,
DEV_STATE_UNITED, print_dev_found, &parg);
if (parg != NULL)
printf("\n NO DEVICES WERE WITHOUT DRIVER\n");
}
if (options & PRINT_DEVS_FAILED) {
parg = "\n --- DEVICES FAILED TO INITIALIZE ---\n";
drvmgr_for_each_listdev(&mgr->devices_inactive,
DEV_STATE_INIT_FAILED, 0, print_dev_found, &parg);
if (parg != NULL)
printf("\n NO DEVICES FAILED TO INITIALIZE\n");
}
if (options & PRINT_DEVS_IGNORED) {
parg = "\n --- DEVICES IGNORED ---\n";
drvmgr_for_each_listdev(&mgr->devices_inactive,
DEV_STATE_IGNORED, 0, print_dev_found, &parg);
if (parg != NULL)
printf("\n NO DEVICES WERE IGNORED\n");
}
printf("\n\n");
}
static int drvmgr_topo_func(struct drvmgr_dev *dev, void *arg)
{
char prefix[32];
int depth = dev->parent->depth;
if (depth > 30)
return 0; /* depth more than 30 not supported */
memset(prefix, ' ', depth + 1);
prefix[depth + 1] = '\0';
printf(" %s|-> DEV %p %s\n", prefix, dev,
dev->name ? dev->name : "NO_NAME");
return 0;
}
void drvmgr_print_topo(void)
{
/* Print Bus topology */
printf(" --- BUS TOPOLOGY ---\n");
drvmgr_for_each_dev(drvmgr_topo_func, NULL, DRVMGR_FED_DF);
printf("\n\n");
}
/* Print the memory usage */
void drvmgr_print_mem(void)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_bus *bus;
struct drvmgr_dev *dev;
struct drvmgr_drv *drv;
struct drvmgr_bus_res *node;
struct drvmgr_drv_res *res;
struct drvmgr_key *key;
unsigned int busmem = 0;
unsigned int devmem = 0;
unsigned int drvmem = 0;
unsigned int resmem = 0;
unsigned int devprivmem = 0;
DRVMGR_LOCK_READ();
bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]);
while (bus) {
busmem += sizeof(struct drvmgr_bus);
/* Get size of resources on this bus */
node = bus->reslist;
while (node) {
resmem += sizeof(struct drvmgr_bus_res);
res = node->resource;
while (res->keys) {
resmem += sizeof(struct drvmgr_drv_res);
key = res->keys;
while (key->key_type != KEY_TYPE_NONE) {
resmem += sizeof
(struct drvmgr_key);
key++;
}
resmem += sizeof(struct drvmgr_key);
res++;
}
node = node->next;
}
bus = bus->next;
}
drv = DRV_LIST_HEAD(&mgr->drivers);
while (drv) {
drvmem += sizeof(struct drvmgr_drv);
drv = drv->next;
}
dev = DEV_LIST_HEAD(&mgr->devices[DRVMGR_LEVEL_MAX]);
while (dev) {
devmem += sizeof(struct drvmgr_dev);
if (dev->drv && dev->drv->dev_priv_size > 0)
devprivmem += dev->drv->dev_priv_size;
dev = dev->next;
}
DRVMGR_UNLOCK();
printf(" --- MEMORY USAGE ---\n");
printf(" BUS: %d bytes\n", busmem);
printf(" DRV: %d bytes\n", drvmem);
printf(" DEV: %d bytes\n", devmem);
printf(" DEV private: %d bytes\n", devprivmem);
printf(" RES: %d bytes\n", resmem);
printf(" TOTAL: %d bytes\n",
busmem + drvmem + devmem + devprivmem + resmem);
printf("\n\n");
}
/* Print the memory usage */
void drvmgr_summary(void)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_bus *bus;
struct drvmgr_dev *dev;
struct drvmgr_drv *drv;
int i, buscnt = 0, devcnt = 0, drvcnt = 0;
printf(" --- SUMMARY ---\n");
drv = DRV_LIST_HEAD(&mgr->drivers);
while (drv) {
drvcnt++;
drv = drv->next;
}
printf(" NUMBER OF DRIVERS: %d\n", drvcnt);
DRVMGR_LOCK_READ();
for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) {
buscnt = 0;
bus = BUS_LIST_HEAD(&mgr->buses[i]);
while (bus) {
buscnt++;
bus = bus->next;
}
if (buscnt > 0) {
printf(" NUMBER OF BUSES IN LEVEL[%d]: %d\n",
i, buscnt);
}
}
for (i = 0; i <= DRVMGR_LEVEL_MAX; i++) {
devcnt = 0;
dev = DEV_LIST_HEAD(&mgr->devices[i]);
while (dev) {
devcnt++;
dev = dev->next;
}
if (devcnt > 0) {
printf(" NUMBER OF DEVS IN LEVEL[%d]: %d\n",
i, devcnt);
}
}
DRVMGR_UNLOCK();
printf("\n\n");
}
static void print_info(void *p, char *str)
{
printf(" ");
puts(str);
}
void drvmgr_info_dev(struct drvmgr_dev *dev, unsigned int options)
{
if (!dev)
return;
printf(" -- DEVICE %p --\n", dev);
if (options & OPTION_DEV_GENINFO) {
printf(" PARENT BUS: %p\n", dev->parent);
printf(" NAME: %s\n", dev->name ? dev->name : "NO_NAME");
printf(" STATE: 0x%08x\n", dev->state);
if (dev->bus)
printf(" BRIDGE TO: %p\n", dev->bus);
printf(" INIT LEVEL: %d\n", dev->level);
printf(" ERROR: %d\n", dev->error);
printf(" MINOR BUS: %d\n", dev->minor_bus);
if (dev->drv) {
printf(" MINOR DRV: %d\n", dev->minor_drv);
printf(" DRIVER: %p (%s)\n", dev->drv,
dev->drv->name ? dev->drv->name : "NO_NAME");
printf(" PRIVATE: %p\n", dev->priv);
}
}
if (options & OPTION_DEV_BUSINFO) {
printf(" --- DEVICE INFO FROM BUS DRIVER ---\n");
if (!dev->parent)
printf(" !! device has no parent bus !!\n");
else if (dev->parent->ops->info_dev)
dev->parent->ops->info_dev(dev, print_info, NULL);
else
printf(" Bus doesn't implement info_dev func\n");
}
if (options & OPTION_DEV_DRVINFO) {
if (dev->drv) {
printf(" --- DEVICE INFO FROM DEVICE DRIVER ---\n");
if (dev->drv->ops->info)
dev->drv->ops->info(dev, print_info, NULL, 0, 0);
else
printf(" Driver doesn't implement info func\n");
}
}
}
static void drvmgr_info_bus_map(struct drvmgr_map_entry *map)
{
if (map == NULL)
printf(" Addresses mapped 1:1\n");
else if (map == DRVMGR_TRANSLATE_NO_BRIDGE)
printf(" No bridge in this direction\n");
else {
while (map->size != 0) {
printf(" 0x%08lx-0x%08lx => 0x%08lx-0x%08lx %s\n",
(unsigned long)map->from_adr,
(unsigned long)(map->from_adr + map->size - 1),
(unsigned long)map->to_adr,
(unsigned long)(map->to_adr + map->size - 1),
map->name ? map->name : "no label");
map++;
}
}
}
void drvmgr_info_bus(struct drvmgr_bus *bus, unsigned int options)
{
struct drvmgr_dev *dev;
/* Print Driver */
printf("-- BUS %p --\n", bus);
printf(" BUS TYPE: %d\n", bus->bus_type);
printf(" DEVICE: %p (%s)\n", bus->dev,
bus->dev->name ? bus->dev->name : "NO_NAME");
printf(" OPS: %p\n", bus->ops);
printf(" CHILDREN: %d devices\n", bus->dev_cnt);
printf(" LEVEL: %d\n", bus->level);
printf(" STATE: 0x%08x\n", bus->state);
printf(" ERROR: %d\n", bus->error);
/* Print address mappings up- (to parent) and down- (from parent to
* this bus) stream the bridge of this bus
*/
printf(" DOWN STREAMS BRIDGE MAPPINGS (from parent to this bus)\n");
drvmgr_info_bus_map(bus->maps_down);
printf(" UP STREAMS BRIDGE MAPPINGS (from this bus to parent)\n");
drvmgr_info_bus_map(bus->maps_up);
/* Print Devices on this bus? */
if (options & OPTION_BUS_DEVS) {
printf(" CHILDREN:\n");
DRVMGR_LOCK_READ();
dev = bus->children;
while (dev) {
printf(" |- DEV[%02d]: %p %s\n", dev->minor_bus,
dev, dev->name ? dev->name : "NO_NAME");
dev = dev->next_in_bus;
}
DRVMGR_UNLOCK();
}
}
char *drv_ops_names[DRV_OPS_NUM] = {
"init[1]:",
"init[2]:",
"init[3]:",
"init[4]:",
"remove: ",
"info: "
};
void drvmgr_info_drv(struct drvmgr_drv *drv, unsigned int options)
{
struct drvmgr_dev *dev;
fun_ptr *ppfunc;
int i;
/* Print Driver */
printf(" -- DRIVER %p --\n", drv);
printf(" DRIVER ID: 0x%llx\n", drv->drv_id);
printf(" NAME: %s\n", drv->name ? drv->name : "NO_NAME");
printf(" BUS TYPE: %d\n", drv->bus_type);
printf(" OPERATIONS:\n");
for (i = 0, ppfunc = (fun_ptr *)&drv->ops->init[0]; i<DRV_OPS_NUM; i++)
printf(" %s %p\n", drv_ops_names[i], ppfunc[i]);
printf(" NO. DEVICES: %d\n", drv->dev_cnt);
/* Print devices united with this driver? */
if (options & OPTION_DRV_DEVS) {
DRVMGR_LOCK_READ();
dev = drv->dev;
while (dev) {
printf(" DEV[%02d]: %p %s\n", dev->minor_drv,
dev, dev->name ? dev->name : "NO_NAME");
dev = dev->next_in_drv;
}
DRVMGR_UNLOCK();
}
}
void (*info_obj[3])(void *obj, unsigned int) = {
/* DRVMGR_OBJ_DRV */ (void (*)(void *, unsigned int))drvmgr_info_drv,
/* DRVMGR_OBJ_BUS */ (void (*)(void *, unsigned int))drvmgr_info_bus,
/* DRVMGR_OBJ_DEV */ (void (*)(void *, unsigned int))drvmgr_info_dev,
};
/* Get information about a device/bus/driver */
void drvmgr_info(void *id, unsigned int options)
{
int obj_type;
void (*func)(void *, unsigned int);
if (!id)
return;
obj_type = *(int *)id;
if ((obj_type < DRVMGR_OBJ_DRV) || (obj_type > DRVMGR_OBJ_DEV))
return;
func = info_obj[obj_type - 1];
func(id, options);
}
void drvmgr_info_devs_on_bus(struct drvmgr_bus *bus, unsigned int options)
{
struct drvmgr_dev *dev;
/* Print All Devices on Bus */
printf("\n\n -= All Devices on BUS %p =-\n\n", bus);
dev = bus->children;
while (dev) {
drvmgr_info_dev(dev, options);
puts("");
dev = dev->next_in_bus;
}
if ((options & OPTION_RECURSIVE) == 0)
return;
/* This device provides a bus, print the bus */
dev = bus->children;
while (dev) {
if (dev->bus)
drvmgr_info_devs_on_bus(dev->bus, options);
dev = dev->next_in_bus;
}
}
void drvmgr_info_devs(unsigned int options)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_dev *dev;
/* Print device information of all devices and their child devices */
dev = &mgr->root_dev;
drvmgr_info_devs_on_bus(dev->bus, options);
printf("\n\n");
}
void drvmgr_info_drvs(unsigned int options)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_drv *drv;
drv = DRV_LIST_HEAD(&mgr->drivers);
while (drv) {
drvmgr_info_drv(drv, options);
puts("\n");
drv = drv->next;
}
}
void drvmgr_info_buses(unsigned int options)
{
struct rtems_driver_manager *mgr = &drv_mgr;
struct drvmgr_bus *bus;
bus = BUS_LIST_HEAD(&mgr->buses[DRVMGR_LEVEL_MAX]);
while (bus) {
drvmgr_info_bus(bus, options);
puts("\n");
bus = bus->next;
}
}

View File

@@ -0,0 +1,102 @@
/* Driver Manager Driver Resource 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.
*/
#include <string.h>
#include <drvmgr/drvmgr.h>
/* Find all the resource keys for a device among all bus resources */
int drvmgr_keys_get(struct drvmgr_dev *dev, struct drvmgr_key **keys)
{
struct drvmgr_bus *bus;
struct drvmgr_bus_res *node;
struct drvmgr_drv_res *res;
uint64_t drv_id;
bus = dev->parent;
if (!bus || !dev->drv)
return -1;
drv_id = dev->drv->drv_id;
/* Loop all resource arrays */
node = bus->reslist;
while (node) {
/* Find driver ID in resource array */
res = &node->resource[0];
while (res->drv_id) {
if (res->drv_id == drv_id) {
/* Found resource matching driver, now check
* that this resource is for this device.
*/
if (dev->minor_bus == res->minor_bus) {
/* Matching driver and core number */
if (keys)
*keys = res->keys;
return 0;
}
}
res++;
}
node = node->next;
}
if (keys)
*keys = NULL;
return 1;
}
/* Return key that matches key name */
struct drvmgr_key *drvmgr_key_get(
struct drvmgr_key *keys,
char *key_name)
{
struct drvmgr_key *key;
if (!keys)
return NULL;
key = keys;
while (key->key_type != KEY_TYPE_NONE) {
if (strcmp(key_name, key->key_name) == 0)
return key;
key++;
}
return NULL;
}
union drvmgr_key_value *drvmgr_key_val_get(
struct drvmgr_key *keys,
char *key_name,
int key_type)
{
struct drvmgr_key *key_match;
key_match = drvmgr_key_get(keys, key_name);
if (key_match) {
/* Found key, put pointer to value into */
if ((key_type == -1) || (key_match->key_type == key_type))
return &key_match->key_value;
}
return NULL;
}
union drvmgr_key_value *drvmgr_dev_key_get(
struct drvmgr_dev *dev,
char *key_name,
int key_type)
{
struct drvmgr_key *keys = NULL;
/* Find first entry in key array for the device */
if (drvmgr_keys_get(dev, &keys))
return NULL;
/* Find a specific key among the device keys */
return drvmgr_key_val_get(keys, key_name, key_type);
}

View File

@@ -0,0 +1,52 @@
/* Driver Manager Read/Write 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.
*/
#include <string.h>
#include <drvmgr/drvmgr.h>
/* Set a range of memory in 128 byte chunks.
* This call will take 128 bytes for buffer on stack
*/
void drvmgr_rw_memset(
void *dstadr,
int c,
size_t n,
void *a,
drvmgr_wmem_arg wmem
)
{
unsigned long long buf[16+1]; /* Extra bytes after data are reserved
* for optimizations by write_mem */
int txlen;
char *adr;
if (n <= 0)
return;
if (n > sizeof(unsigned long long)*16)
txlen = sizeof(unsigned long long)*16;
else
txlen = n;
memset(buf, c, txlen);
adr = dstadr;
do {
wmem(adr, (const void *)&buf[0], txlen, a);
adr += txlen;
n -= txlen;
/* next length to transmitt */
if (n > 16*sizeof(unsigned long long))
txlen = 16*sizeof(unsigned long long);
else
txlen = n;
} while (n > 0);
}

View File

@@ -0,0 +1,149 @@
/* Driver Manager Driver Translate Interface Implementation
*
* COPYRIGHT (c) 2010.
* 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.
*/
/*
* Used by device drivers. The functions rely on that the parent bus driver
* has implemented the neccessary operations correctly.
*
* The translate functions are used to translate addresses between buses
* for DMA cores located on a "remote" bus, or for memory-mapped obtaining
* an address that can be used to access an remote bus.
*
* For example, PCI I/O might be memory-mapped at the PCI Host bridge,
* say address 0xfff10000-0xfff1ffff is mapped to the PCI I/O address
* of 0x00000000-0x0000ffff. The PCI Host bridge driver may then set up
* a map so that a driver that get PCI address 0x100 can translate that
* into 0xfff10100.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <drvmgr/drvmgr.h>
#include "drvmgr_internal.h"
unsigned int drvmgr_translate_bus(
struct drvmgr_bus *from,
struct drvmgr_bus *to,
int reverse,
void *src_address,
void **dst_address)
{
struct drvmgr_bus *path[16];
int dir, levels, i;
void *dst, *from_adr, *to_adr;
struct drvmgr_map_entry *map;
struct drvmgr_bus *bus;
unsigned int sz;
struct drvmgr_bus *bus_bot, *bus_top;
dst = src_address;
sz = 0xffffffff;
if (from == to) /* no need translating addresses when on same bus */
goto out;
/* Always find translation path from remote bus towards root bus. All
* buses have root bus has parent at some level
*/
if (from->depth > to->depth) {
bus_bot = from;
bus_top = to;
dir = 0;
} else {
bus_bot = to;
bus_top = from;
dir = 1;
}
levels = bus_bot->depth - bus_top->depth;
if (levels >= 16)
return 0; /* Does not support such a big depth */
i = 0;
while ((bus_bot != NULL) && bus_bot != bus_top) {
if (dir)
path[(levels - 1) - i] = bus_bot;
else
path[i] = bus_bot;
i++;
bus_bot = bus_bot->dev->parent;
}
if (bus_bot == NULL)
return 0; /* from -> to is not linearly connected */
for (i = 0; i < levels; i++) {
bus = path[i];
if ((dir && reverse) || (!dir && !reverse))
map = bus->maps_up;
else
map = bus->maps_down;
if (map == NULL)
continue; /* No translation needed - 1:1 mapping */
if (map == DRVMGR_TRANSLATE_NO_BRIDGE) {
sz = 0;
break; /* No bridge interface in this direction */
}
while (map->size != 0) {
if (reverse) {
/* Opposite direction */
from_adr = map->to_adr;
to_adr = map->from_adr;
} else {
from_adr = map->from_adr;
to_adr = map->to_adr;
}
if ((dst >= from_adr) &&
(dst <= (from_adr + (map->size - 1)))) {
if (((from_adr + (map->size - 1)) - dst) < sz)
sz = (from_adr + (map->size - 1)) - dst;
dst = (dst - from_adr) + to_adr;
break;
}
map++;
}
/* quit if no matching translation information */
if (map->size == 0) {
sz = 0;
break;
}
}
out:
if (dst_address)
*dst_address = dst;
return sz;
}
unsigned int drvmgr_translate(
struct drvmgr_dev *dev,
unsigned int options,
void *src_address,
void **dst_address)
{
struct drvmgr_bus *to, *from;
int rev = 0;
rev = (~options) & 1;
if ((options == CPUMEM_TO_DMA) || (options == DMAMEM_FROM_CPU)) {
from = drv_mgr.root_dev.bus;
to = dev->parent;
} else { /* CPUMEM_FROM_DMA || DMAMEM_TO_CPU */
from = dev->parent;
to = drv_mgr.root_dev.bus;
}
return drvmgr_translate_bus(from, to, rev, src_address, dst_address);
}

View File

@@ -0,0 +1,35 @@
/* Driver Manager Driver Translate Interface Implementation
*
* COPYRIGHT (c) 2010.
* 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 <drvmgr/drvmgr.h>
/* Calls drvmgr_translate() to translate an address range and check the result,
* a printout is generated if the check fails. See paramters of
* drvmgr_translate().
* If size=0 only the starting address is not checked.
*/
int drvmgr_translate_check(
struct drvmgr_dev *dev,
unsigned int options,
void *src_address,
void **dst_address,
unsigned int size)
{
unsigned int max;
max = drvmgr_translate(dev, options, src_address, dst_address);
if (max == 0 || (max < size && (size != 0))) {
printk(" ### dev %p (%s) failed mapping %p\n",
dev, dev->name ? dev->name : "unnamed", src_address);
return -1;
}
return 0;
}

View File

@@ -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;
}

View File

@@ -527,3 +527,20 @@ $(PROJECT_INCLUDE)/rtems/fsmount.h: libmisc/fsmount/fsmount.h $(PROJECT_INCLUDE)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/fsmount.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/fsmount.h
$(PROJECT_INCLUDE)/drvmgr/$(dirstamp):
@$(MKDIR_P) $(PROJECT_INCLUDE)/drvmgr
@: > $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
PREINSTALL_DIRS += $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
$(PROJECT_INCLUDE)/drvmgr/drvmgr.h: libdrvmgr/drvmgr.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr.h
$(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h: libdrvmgr/drvmgr_confdefs.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_confdefs.h
$(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h: libdrvmgr/drvmgr_list.h $(PROJECT_INCLUDE)/drvmgr/$(dirstamp)
$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h
PREINSTALL_FILES += $(PROJECT_INCLUDE)/drvmgr/drvmgr_list.h

View File

@@ -161,6 +161,15 @@ const rtems_libio_helper rtems_fs_init_helper =
*/
#define CONFIGURE_LIBIO_POSIX_KEYS 1
/**
* Driver Manager Configuration
*/
#ifdef RTEMS_DRVMGR_STARTUP
#define CONFIGURE_DRVMGR_SEMAPHORES 1
#else
#define CONFIGURE_DRVMGR_SEMAPHORES 0
#endif
#ifdef CONFIGURE_INIT
rtems_libio_t rtems_libio_iops[CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS];
@@ -2170,7 +2179,7 @@ const rtems_libio_helper rtems_fs_init_helper =
(CONFIGURE_MAXIMUM_SEMAPHORES + CONFIGURE_LIBIO_SEMAPHORES + \
CONFIGURE_TERMIOS_SEMAPHORES + CONFIGURE_LIBBLOCK_SEMAPHORES + \
CONFIGURE_SEMAPHORES_FOR_FILE_SYSTEMS + \
CONFIGURE_NETWORKING_SEMAPHORES)
CONFIGURE_NETWORKING_SEMAPHORES + CONFIGURE_DRVMGR_SEMAPHORES)
/**
* This macro is calculated to specify the memory required for

View File

@@ -56,6 +56,10 @@
#include <rtems/rtems/rtemsapi.h>
#include <rtems/posix/posixapi.h>
#ifdef RTEMS_DRVMGR_STARTUP
#include <drvmgr/drvmgr.h>
#endif
Objects_Information *_Internal_Objects[ OBJECTS_INTERNAL_CLASSES_LAST + 1 ];
void rtems_initialize_data_structures(void)
@@ -161,6 +165,9 @@ void rtems_initialize_data_structures(void)
void rtems_initialize_before_drivers(void)
{
#ifdef RTEMS_DRVMGR_STARTUP
_DRV_Manager_initialization();
#endif
#if defined(RTEMS_MULTIPROCESSING)
_MPCI_Create_server();
@@ -182,8 +189,64 @@ void rtems_initialize_device_drivers(void)
* NOTE: The MPCI may be build upon a device driver.
*/
#ifdef RTEMS_DRVMGR_STARTUP
/* BSPs has already registered their "root bus" driver in the
* bsp_predriver hook or so.
*
* Init Drivers to Level 1, constraints:
* - Interrupts and system clock timer does not work.
* - malloc() work, however other memory services may not
* have been initialized yet.
* - initializes most basic stuff
*
* Typical setup in Level 1:
* - Find most devices in system, do PCI scan and configuration.
* - Reset hardware if needed.
* - Install IRQ driver
* - Install Timer driver
* - Install console driver and debug printk()
* - Install extra memory.
*/
_DRV_Manager_init_level(1);
bsp_driver_level_hook(1);
#endif
/* Initialize I/O drivers.
*
* Driver Manager note:
* All drivers may not be registered yet. Drivers will dynamically
* be initialized when registered in level 2,3 and 4.
*/
_IO_Initialize_all_drivers();
#ifdef RTEMS_DRVMGR_STARTUP
/* Init Drivers to Level 2, constraints:
* - Interrupts can be registered and enabled.
* - System Clock is running
* - Console may be used.
*
* This is typically where drivers are initialized
* for the first time.
*/
_DRV_Manager_init_level(2);
bsp_driver_level_hook(2);
/* Init Drivers to Level 3
*
* This is typically where normal drivers are initialized
* for the second time, they may depend on other drivers
* API inited in level 2
*/
_DRV_Manager_init_level(3);
bsp_driver_level_hook(3);
/* Init Drivers to Level 4,
* Init drivers that depend on services initialized in Level 3
*/
_DRV_Manager_init_level(4);
bsp_driver_level_hook(4);
#endif
#if defined(RTEMS_MULTIPROCESSING)
if ( _System_state_Is_multiprocessing ) {
_MPCI_Initialization();

View File

@@ -63,10 +63,9 @@ TMP_LIBS += ../libmisc/libutf8proc.a
endif
TMP_LIBS += ../libmisc/libuuid.a
TMP_LIBS += ../libi2c/libi2c.a
TMP_LIBS += ../libpci/libpci.a
TMP_LIBS += ../libdrvmgr/libdrvmgr.a
if LIBNETWORKING
TMP_LIBS += ../libnetworking/libnetworking.a