From e7fade3ac4559214ab0508dc54a71a3d1f522afb Mon Sep 17 00:00:00 2001 From: Daniel Hellstrom Date: Mon, 28 Nov 2011 09:52:03 +0100 Subject: DRVMGR: added driver manager to cpukit/libdrvmgr --- aclocal/enable-drvmgr.m4 | 12 + c/src/aclocal/enable-drvmgr.m4 | 12 + c/src/lib/libbsp/shared/bspdriverlevelhook.c | 16 + c/src/lib/libbsp/shared/include/bootcard.h | 2 + c/src/make/configure.ac | 1 + configure.ac | 1 + cpukit/Makefile.am | 7 + cpukit/aclocal/enable-drvmgr.m4 | 12 + cpukit/configure.ac | 7 + cpukit/libdrvmgr/Makefile.am | 33 + cpukit/libdrvmgr/README | 112 ++++ cpukit/libdrvmgr/drvmgr.c | 643 ++++++++++++++++++ cpukit/libdrvmgr/drvmgr.h | 965 +++++++++++++++++++++++++++ cpukit/libdrvmgr/drvmgr_by_id.c | 33 + cpukit/libdrvmgr/drvmgr_by_name.c | 37 + cpukit/libdrvmgr/drvmgr_confdefs.h | 86 +++ cpukit/libdrvmgr/drvmgr_dev_by_name.c | 34 + cpukit/libdrvmgr/drvmgr_drvinf.c | 148 ++++ cpukit/libdrvmgr/drvmgr_for_each_dev.c | 104 +++ cpukit/libdrvmgr/drvmgr_for_each_list_dev.c | 44 ++ cpukit/libdrvmgr/drvmgr_func.c | 42 ++ cpukit/libdrvmgr/drvmgr_func_call.c | 21 + cpukit/libdrvmgr/drvmgr_init.c | 26 + cpukit/libdrvmgr/drvmgr_internal.h | 70 ++ cpukit/libdrvmgr/drvmgr_list.c | 67 ++ cpukit/libdrvmgr/drvmgr_list.h | 79 +++ cpukit/libdrvmgr/drvmgr_lock.c | 38 ++ cpukit/libdrvmgr/drvmgr_print.c | 457 +++++++++++++ cpukit/libdrvmgr/drvmgr_res.c | 102 +++ cpukit/libdrvmgr/drvmgr_rw.c | 52 ++ cpukit/libdrvmgr/drvmgr_translate.c | 149 +++++ cpukit/libdrvmgr/drvmgr_translate_check.c | 35 + cpukit/libdrvmgr/drvmgr_unregister.c | 186 ++++++ cpukit/preinstall.am | 17 + cpukit/sapi/include/confdefs.h | 11 +- cpukit/sapi/src/exinit.c | 63 ++ cpukit/wrapup/Makefile.am | 3 +- 37 files changed, 3724 insertions(+), 3 deletions(-) create mode 100644 aclocal/enable-drvmgr.m4 create mode 100644 c/src/aclocal/enable-drvmgr.m4 create mode 100644 c/src/lib/libbsp/shared/bspdriverlevelhook.c create mode 100644 cpukit/aclocal/enable-drvmgr.m4 create mode 100644 cpukit/libdrvmgr/Makefile.am create mode 100644 cpukit/libdrvmgr/README create mode 100644 cpukit/libdrvmgr/drvmgr.c create mode 100644 cpukit/libdrvmgr/drvmgr.h create mode 100644 cpukit/libdrvmgr/drvmgr_by_id.c create mode 100644 cpukit/libdrvmgr/drvmgr_by_name.c create mode 100644 cpukit/libdrvmgr/drvmgr_confdefs.h create mode 100644 cpukit/libdrvmgr/drvmgr_dev_by_name.c create mode 100644 cpukit/libdrvmgr/drvmgr_drvinf.c create mode 100644 cpukit/libdrvmgr/drvmgr_for_each_dev.c create mode 100644 cpukit/libdrvmgr/drvmgr_for_each_list_dev.c create mode 100644 cpukit/libdrvmgr/drvmgr_func.c create mode 100644 cpukit/libdrvmgr/drvmgr_func_call.c create mode 100644 cpukit/libdrvmgr/drvmgr_init.c create mode 100644 cpukit/libdrvmgr/drvmgr_internal.h create mode 100644 cpukit/libdrvmgr/drvmgr_list.c create mode 100644 cpukit/libdrvmgr/drvmgr_list.h create mode 100644 cpukit/libdrvmgr/drvmgr_lock.c create mode 100644 cpukit/libdrvmgr/drvmgr_print.c create mode 100644 cpukit/libdrvmgr/drvmgr_res.c create mode 100644 cpukit/libdrvmgr/drvmgr_rw.c create mode 100644 cpukit/libdrvmgr/drvmgr_translate.c create mode 100644 cpukit/libdrvmgr/drvmgr_translate_check.c create mode 100644 cpukit/libdrvmgr/drvmgr_unregister.c diff --git a/aclocal/enable-drvmgr.m4 b/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..489f60e75f --- /dev/null +++ b/aclocal/enable-drvmgr.m4 @@ -0,0 +1,12 @@ +AC_DEFUN([RTEMS_ENABLE_DRVMGR], +[ +## AC_BEFORE([$0], [RTEMS_CHECK_DRVMGR_STARTUP])dnl + +AC_ARG_ENABLE(drvmgr, +[AS_HELP_STRING([--enable-drvmgr],[enable Driver Manager at Startup])], +[case "${enableval}" in + yes) RTEMS_DRVMGR_STARTUP=yes ;; + no) RTEMS_DRVMGR_STARTUP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; +esac],[RTEMS_DRVMGR_STARTUP=yes]) +]) diff --git a/c/src/aclocal/enable-drvmgr.m4 b/c/src/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..489f60e75f --- /dev/null +++ b/c/src/aclocal/enable-drvmgr.m4 @@ -0,0 +1,12 @@ +AC_DEFUN([RTEMS_ENABLE_DRVMGR], +[ +## AC_BEFORE([$0], [RTEMS_CHECK_DRVMGR_STARTUP])dnl + +AC_ARG_ENABLE(drvmgr, +[AS_HELP_STRING([--enable-drvmgr],[enable Driver Manager at Startup])], +[case "${enableval}" in + yes) RTEMS_DRVMGR_STARTUP=yes ;; + no) RTEMS_DRVMGR_STARTUP=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for enable-drvmgr option) ;; +esac],[RTEMS_DRVMGR_STARTUP=yes]) +]) diff --git a/c/src/lib/libbsp/shared/bspdriverlevelhook.c b/c/src/lib/libbsp/shared/bspdriverlevelhook.c new file mode 100644 index 0000000000..93406f9b45 --- /dev/null +++ b/c/src/lib/libbsp/shared/bspdriverlevelhook.c @@ -0,0 +1,16 @@ +/* + * This is a dummy bsp_driver_level_hook routine. + * + * COPYRIGHT (c) 2015. + * Cobham Gaisler. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include + +void bsp_driver_level_hook( int level ) +{ +} diff --git a/c/src/lib/libbsp/shared/include/bootcard.h b/c/src/lib/libbsp/shared/include/bootcard.h index 5e31fcfb97..c39460292d 100644 --- a/c/src/lib/libbsp/shared/include/bootcard.h +++ b/c/src/lib/libbsp/shared/include/bootcard.h @@ -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); diff --git a/c/src/make/configure.ac b/c/src/make/configure.ac index 08f8c8be74..10c47a06d8 100644 --- a/c/src/make/configure.ac +++ b/c/src/make/configure.ac @@ -20,6 +20,7 @@ RTEMS_ENABLE_MULTIPROCESSING RTEMS_ENABLE_POSIX RTEMS_ENABLE_NETWORKING RTEMS_ENABLE_CXX +RTEMS_ENABLE_DRVMGR RTEMS_ENV_RTEMSBSP diff --git a/configure.ac b/configure.ac index 4d6cd564b5..61e0eda9e3 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am index d68f0576d2..faae9b26f5 100644 --- a/cpukit/Makefile.am +++ b/cpukit/Makefile.am @@ -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 diff --git a/cpukit/aclocal/enable-drvmgr.m4 b/cpukit/aclocal/enable-drvmgr.m4 new file mode 100644 index 0000000000..a9da288b11 --- /dev/null +++ b/cpukit/aclocal/enable-drvmgr.m4 @@ -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]) +]) diff --git a/cpukit/configure.ac b/cpukit/configure.ac index d04ce87efd..69319ad65f 100644 --- a/cpukit/configure.ac +++ b/cpukit/configure.ac @@ -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 diff --git a/cpukit/libdrvmgr/Makefile.am b/cpukit/libdrvmgr/Makefile.am new file mode 100644 index 0000000000..d93854a6c4 --- /dev/null +++ b/cpukit/libdrvmgr/Makefile.am @@ -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 diff --git a/cpukit/libdrvmgr/README b/cpukit/libdrvmgr/README new file mode 100644 index 0000000000..6e55370fb0 --- /dev/null +++ b/cpukit/libdrvmgr/README @@ -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 diff --git a/cpukit/libdrvmgr/drvmgr.c b/cpukit/libdrvmgr/drvmgr.c new file mode 100644 index 0000000000..0471865178 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr.c @@ -0,0 +1,643 @@ +/* Driver Manager Interface Implementation. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include +#include +#include + +#include +#include + +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr.h b/cpukit/libdrvmgr/drvmgr.h new file mode 100644 index 0000000000..f091728e49 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr.h @@ -0,0 +1,965 @@ +/* Driver Manager Interface. + * + * COPYRIGHT (c) 2009. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#ifndef _DRIVER_MANAGER_H_ +#define _DRIVER_MANAGER_H_ + +#include +#include +#include + +#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 diff --git a/cpukit/libdrvmgr/drvmgr_by_id.c b/cpukit/libdrvmgr/drvmgr_by_id.c new file mode 100644 index 0000000000..7a0ea42f3e --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_by_id.c @@ -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 +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr_by_name.c b/cpukit/libdrvmgr/drvmgr_by_name.c new file mode 100644 index 0000000000..2ae527aef1 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_by_name.c @@ -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 +#include +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr_confdefs.h b/cpukit/libdrvmgr/drvmgr_confdefs.h new file mode 100644 index 0000000000..23affe9263 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_confdefs.h @@ -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_ */ diff --git a/cpukit/libdrvmgr/drvmgr_dev_by_name.c b/cpukit/libdrvmgr/drvmgr_dev_by_name.c new file mode 100644 index 0000000000..f5a99ee696 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_dev_by_name.c @@ -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 +#include +#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); +} diff --git a/cpukit/libdrvmgr/drvmgr_drvinf.c b/cpukit/libdrvmgr/drvmgr_drvinf.c new file mode 100644 index 0000000000..9f7c7a5713 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_drvinf.c @@ -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 +#include +#include + +#include +#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, ¶ms); + 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; +} diff --git a/cpukit/libdrvmgr/drvmgr_for_each_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_dev.c new file mode 100644 index 0000000000..a3e63ecef9 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_for_each_dev.c @@ -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 +#include +#include +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c new file mode 100644 index 0000000000..9e1b95b8ff --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_for_each_list_dev.c @@ -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 +#include +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr_func.c b/cpukit/libdrvmgr/drvmgr_func.c new file mode 100644 index 0000000000..e02a4446cb --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_func.c @@ -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 + +/* 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; +} diff --git a/cpukit/libdrvmgr/drvmgr_func_call.c b/cpukit/libdrvmgr/drvmgr_func_call.c new file mode 100644 index 0000000000..75836c31a0 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_func_call.c @@ -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 + +/* 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); +} diff --git a/cpukit/libdrvmgr/drvmgr_init.c b/cpukit/libdrvmgr/drvmgr_init.c new file mode 100644 index 0000000000..b8a738fee2 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_init.c @@ -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 + +/* 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; +} diff --git a/cpukit/libdrvmgr/drvmgr_internal.h b/cpukit/libdrvmgr/drvmgr_internal.h new file mode 100644 index 0000000000..b4594555d5 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_internal.h @@ -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 diff --git a/cpukit/libdrvmgr/drvmgr_list.c b/cpukit/libdrvmgr/drvmgr_list.c new file mode 100644 index 0000000000..dc1665f645 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_list.c @@ -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 +#include + +/* 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; +} diff --git a/cpukit/libdrvmgr/drvmgr_list.h b/cpukit/libdrvmgr/drvmgr_list.h new file mode 100644 index 0000000000..6f4b98e29b --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_list.h @@ -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 diff --git a/cpukit/libdrvmgr/drvmgr_lock.c b/cpukit/libdrvmgr/drvmgr_lock.c new file mode 100644 index 0000000000..d6601d35c9 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_lock.c @@ -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 +#include +#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; +} diff --git a/cpukit/libdrvmgr/drvmgr_print.c b/cpukit/libdrvmgr/drvmgr_print.c new file mode 100644 index 0000000000..c36abfe527 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_print.c @@ -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 +#include +#include + +#include +#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]; idev_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; + } +} diff --git a/cpukit/libdrvmgr/drvmgr_res.c b/cpukit/libdrvmgr/drvmgr_res.c new file mode 100644 index 0000000000..5f8598b7d8 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_res.c @@ -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 +#include + +/* 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); +} diff --git a/cpukit/libdrvmgr/drvmgr_rw.c b/cpukit/libdrvmgr/drvmgr_rw.c new file mode 100644 index 0000000000..6530cddc6d --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_rw.c @@ -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 +#include + +/* 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); +} diff --git a/cpukit/libdrvmgr/drvmgr_translate.c b/cpukit/libdrvmgr/drvmgr_translate.c new file mode 100644 index 0000000000..7febf77d60 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_translate.c @@ -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 +#include +#include + +#include +#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); +} diff --git a/cpukit/libdrvmgr/drvmgr_translate_check.c b/cpukit/libdrvmgr/drvmgr_translate_check.c new file mode 100644 index 0000000000..c882b88f27 --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_translate_check.c @@ -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 + +/* 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; +} diff --git a/cpukit/libdrvmgr/drvmgr_unregister.c b/cpukit/libdrvmgr/drvmgr_unregister.c new file mode 100644 index 0000000000..ef5260a13c --- /dev/null +++ b/cpukit/libdrvmgr/drvmgr_unregister.c @@ -0,0 +1,186 @@ +/* Driver Manager Device Unregister (removal) implementation + * + * COPYRIGHT (c) 2011. + * Cobham Gaisler AB. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ + +#include + +#include +#include +#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; +} diff --git a/cpukit/preinstall.am b/cpukit/preinstall.am index 640a17a335..95d61ae781 100644 --- a/cpukit/preinstall.am +++ b/cpukit/preinstall.am @@ -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 + diff --git a/cpukit/sapi/include/confdefs.h b/cpukit/sapi/include/confdefs.h index daeda0be3c..9f3640b4a6 100644 --- a/cpukit/sapi/include/confdefs.h +++ b/cpukit/sapi/include/confdefs.h @@ -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 diff --git a/cpukit/sapi/src/exinit.c b/cpukit/sapi/src/exinit.c index 235ba77c41..ba71ec465b 100644 --- a/cpukit/sapi/src/exinit.c +++ b/cpukit/sapi/src/exinit.c @@ -56,6 +56,10 @@ #include #include +#ifdef RTEMS_DRVMGR_STARTUP + #include +#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(); diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am index 5dc23a6a32..8ac5a728d0 100644 --- a/cpukit/wrapup/Makefile.am +++ b/cpukit/wrapup/Makefile.am @@ -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 -- cgit v1.2.3