diff options
-rw-r--r-- | src/include/builtins.h | 5 | ||||
-rw-r--r-- | src/include/usr/devicefw/driverif.H | 250 | ||||
-rw-r--r-- | src/include/usr/devicefw/userif.H | 108 | ||||
-rw-r--r-- | src/lib/stdlib.C | 2 | ||||
-rw-r--r-- | src/makefile | 4 | ||||
-rw-r--r-- | src/usr/devicefw/assoccontain.C | 52 | ||||
-rw-r--r-- | src/usr/devicefw/assoccontain.H | 93 | ||||
-rw-r--r-- | src/usr/devicefw/associator.C | 205 | ||||
-rw-r--r-- | src/usr/devicefw/associator.H | 72 | ||||
-rw-r--r-- | src/usr/devicefw/driverif.C | 157 | ||||
-rw-r--r-- | src/usr/devicefw/makefile | 8 | ||||
-rw-r--r-- | src/usr/devicefw/test/assoccontaintest.H | 102 | ||||
-rw-r--r-- | src/usr/devicefw/test/associatortest.H | 174 | ||||
-rw-r--r-- | src/usr/devicefw/test/makefile | 6 | ||||
-rw-r--r-- | src/usr/devicefw/userif.C | 46 | ||||
-rw-r--r-- | src/usr/makefile | 2 |
16 files changed, 1282 insertions, 4 deletions
diff --git a/src/include/builtins.h b/src/include/builtins.h index 5126b6114..07b185697 100644 --- a/src/include/builtins.h +++ b/src/include/builtins.h @@ -40,6 +40,11 @@ extern "C" #define SYMB_SECTION(x) __attribute__((section(#x))) /** + * Use of this macro will ensure a data structure is not padded + */ +#define PACKED __attribute__((packed)) + +/** * Get the value of the link register * * @return the value of the link register diff --git a/src/include/usr/devicefw/driverif.H b/src/include/usr/devicefw/driverif.H new file mode 100644 index 000000000..ceff91c59 --- /dev/null +++ b/src/include/usr/devicefw/driverif.H @@ -0,0 +1,250 @@ +/** @file driverif.H + * @brief Provides the device driver interfaces for performing device access + * and enabling routing registration. + * + * @note These interfaces should only be used by device drivers. User code + * wanting to perform device operations should use userif.H instead. + */ +#ifndef __DEVICEFW_DRIVERIF +#define __DEVICEFW_DRIVERIF + +#include <devicefw/userif.H> +#include <stdarg.h> +#include <builtins.h> + +namespace DeviceFW +{ + /** TODO: Update these to real target handle type once that piece of + * code is complete */ + enum TargetType_t + { + PROCESSOR = 0, + + LAST_TARGET_TYPE + }; + + /** @enum AccessType_DriverOnly + * @brief Access types to be used internally by drivers for routing + * requests to other drivers. + */ + enum AccessType_DriverOnly + { + XSCOM = LAST_ACCESS_TYPE, + FSISCOM, + + LAST_DRIVER_ACCESS_TYPE + }; + + /** @enum OperationType + * @brief Set of operations which can be registered for. + */ + enum OperationType + { + READ = 0, + WRITE, + + LAST_OP_TYPE + + }; + + /** @enum DriverSpecial + * @brief Special Wildcard enum that can be used for drivers to do + * routing registrations. + */ + enum DriverSpecial + { + WILDCARD = -1, + }; + + /** @class InvalidParameterType + * @brief Unused type to cause compiler fails for invalid template types. + * + * Forward Declaration of type that is never actually used anywhere. + * + * Assists in making more developer friendly compiler fails when a + * template function is called for which there is no specialization and + * the default template function should never be used. This is used for + * allowing function calls that take multiple enum types but still provide + * type-safety above a int-parameter. + */ + class InvalidParameterType; + + /** @typedef deviceOp_t + * @brief Function prototype for registered device-driver operations. + */ + typedef ErrorHandle_t(*deviceOp_t)(OperationType, + TargetHandle_t, + void*, size_t&, + int64_t, va_list); + + /** + * @brief Register a device driver routing function with the framework. + * + * @param[in] i_opType - Enumeration specifying the operation this + * driver performs. (Read, Write, Wildcard) + * @param[in] i_accessType - Enumeration specifying the access type this + * driver performs. (SCOM, XSCOM, PNOR, etc.) + * @param[in] i_targetType - Enumeration specifying the target type this + * driver performs. (Proc, MC, Wildcard, etc.) + * @param[in] i_regRoute - The function being registered. + * + * This function call be called to register a device driver routing + * function with the framework. If it is desired to always register a + * device driver when the module associated with that driver is loaded, + * the DEVICE_REGISTER_ROUTE macro can be used. + * + * <PRE> + * Example usage: + * // High-level address manipulation routing function. + * deviceRegisterRoute(WILDCARD, + * SCOM, + * MEMORY_CONTROLLER, + * &scomAdjustMCAddresses); + * + * // Low-level (internal) XSCOM read operation. + * deviceRegisterRoute(READ, + * XSCOM, + * PROCESSOR, + * &xscomPerformRead); + * </PRE> + * + * @note Valid OpType are OperatorType enum or WILDCARD. + * @note Valid TargType are TargetType enum or WILDCARD. + * @note Valid AccType are AccessType or AccessType_DriverOnly; WILDCARD is + * not permitted. + * + * @note Any unsupported enumeration type will result in a compile error + * referencing a InvalidParameterType class. + */ + template <typename OpType, typename AccType, typename TargType> + void deviceRegisterRoute(OpType i_opType, + AccType i_accessType, + TargType i_targetType, + deviceOp_t i_regRoute) + { + return InvalidParameterType(); // Cause a compile fail if not one of + // the explicit template specializations. + } + + /** Assistance macro for stringification. */ + #define __DEVICE_REGISTER_ROUTE_XY(X,Y) X##Y + /** Assistance macro for stringification. */ + #define __DEVICE_REGISTER_ROUTE_MAKENAME(X,Y) __DEVICE_REGISTER_ROUTE_XY(X,Y) + + /** + * @brief Create a static constructed registration of a device driver + * function when a module is loaded. + * + * Parameters are the same as DeviceFW::deviceRegisterRoute, except the + * route function should be passed by name as opposed to pointer. + * + * If the route function is in a namespace, then this definition must + * also be placed into that namespace. + */ + #define DEVICE_REGISTER_ROUTE(i_opType, i_accessType, \ + i_targetType, i_regRoute) \ + class __DEVICE_REGISTER_ROUTE_MAKENAME(DeviceRouteRegistrator_, \ + i_regRoute) \ + { \ + public: \ + __DEVICE_REGISTER_ROUTE_MAKENAME(DeviceRouteRegistrator_, \ + i_regRoute)() \ + { \ + DeviceFW::deviceRegisterRoute(i_opType, i_accessType, \ + i_targetType, &i_regRoute); \ + } \ + }; \ + __DEVICE_REGISTER_ROUTE_MAKENAME(DeviceRouteRegistrator_, i_regRoute) \ + __DEVICE_REGISTER_ROUTE_MAKENAME(DeviceRouteRegistrator_instance_, \ + i_regRoute); + + /** + * @brief Perform a device operation by routing through the framework and + * calling the appropriate registered operation. + * + * @param[in] i_opType - Operation request (READ vs WRITE). + * @param[in] i_target - Target to perform operation on. + * @param[in,out] io_buffer - Data buffer for operation. + * @param[in,out] io_buflen - Length of buffer / result size. + * @param[in] i_accessType - Type of hardware access method to perform. + * + * This function has similar behavior as the user-visible deviceRead and + * deviceWrite functions and is meant as a method for device drivers to + * perform accesses which may be only visible to internal drivers. + */ + template <typename AccType> + ErrorHandle_t deviceOp(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + AccType i_accessType, ...) + { + return InvalidParameterType(); // Cause a compile fail if not one of + // the explicit template specializations. + } + + // --- Below are template specializations to aid in type-safety. --- + + // deviceRegisterRoute: + // OpType - OperationType or WILDCARD + // TargType - TargetType or WILDCARD + // AccType - AccessType, AccessType_DriverOnly (no WILDCARD). + + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType_DriverOnly i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType_DriverOnly i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType_DriverOnly i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute); + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType_DriverOnly i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute); + + // deviceOp: + // OpType - OperationType only. + // TargType - TargetType only. + // AccType - AccessType, AccessType_DriverOnly (no WILDCARD). + template <> + ErrorHandle_t deviceOp<>(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + AccessType i_accessType, ...); + template <> + ErrorHandle_t deviceOp<>(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + AccessType_DriverOnly i_accessType, ...); + +}; + +#endif diff --git a/src/include/usr/devicefw/userif.H b/src/include/usr/devicefw/userif.H new file mode 100644 index 000000000..dc4cbd4c7 --- /dev/null +++ b/src/include/usr/devicefw/userif.H @@ -0,0 +1,108 @@ +/** @file userif.H + * @brief Provides the user application interfaces for performing device + * access. + * + * @note These interfaces should not be used directly by device drivers. + * Use driverif.H instead. + */ + +#ifndef __DEVICEFW_USERIF +#define __DEVICEFW_USERIF + +#include <stdint.h> + +namespace DeviceFW +{ + /* TODO: Update these to real error log / target handle type once those + * pieces of code are complete. */ + typedef void* ErrorHandle_t; + typedef int TargetHandle_t; + + /** @enum AccessType + * @brief Access types for accessing a hardware device. + */ + enum AccessType + { + SCOM = 0, + PNOR, + MAILBOX, + PRESENT, + + LAST_ACCESS_TYPE, + }; + + /** Construct the device addressing parameters for SCOM device ops. + * @param[in] i_address - Scom address to operate on. + */ + #define DEVICE_SCOM_ADDRESS(i_address) \ + DeviceFW::SCOM, static_cast<uint64_t>((i_address)) + + /** Construct the device addressing parameters for the PRESENT device ops. + */ + #define DEVICE_PRESENT_ADDRESS() \ + DeviceFW::PRESENT + + /** + * @brief Perform a hardware read operation. + * + * @param[in] i_target Device target to operate on. + * @param[out] o_buffer Buffer to put result data into. + * @param[in,out] io_buflen Length of the buffer on input, length of + * data on output (in bytes). + * @param[in] i_accessType Operation to perform on target. + * @param[in] ... Operation specific addressing parameters. + * + * @return NULL - No error. + * @return Non-NULL - An error handle when error has occured, typically + * passed directly from device driver but potentially + * created by the device framework as in the case of + * not finding an installed driver for the desired + * operation. + * + * It is expected that the callers will use operation specific macros to + * assist in the AccessType parameter and variable arguments. + * + * <PRE> + * Example usage: + * errl = deviceRead(chip, buf, bufsize, DEVICE_SCOM_ADDRESS(0x42)); + * </PRE> + * + */ + ErrorHandle_t deviceRead(TargetHandle_t i_target, + void* o_buffer, size_t& io_buflen, + AccessType i_accessType, ...); + + /** + * @brief Perform a hardware write operation. + * + * @param[in] i_target Device target to operate on. + * @param[in] i_buffer Buffer to get write data from. + * @param[in,out] io_buflen Length of the buffer on input, length of + * data on output (in bytes). + * @param[in] i_accessType Operation to perform on target. + * @param[in] ... Operation specific addressing parameters. + * + * @return NULL - No error. + * @return Non-NULL - An error handle when error has occured, typically + * passed directly from device driver but potentially + * created by the device framework as in the case of + * not finding an installed driver for the desired + * operation. + * + * It is expected that the callers will use operation specific macros to + * assist in the AccessType parameter and variable arguments. + * + * <PRE> + * Example usage: + * errl = deviceWrite(chip, buf, bufsize, DEVICE_SCOM_ADDRESS(0x42)); + * </PRE> + * + */ + ErrorHandle_t deviceWrite(TargetHandle_t i_target, + void* i_buffer, size_t& io_buflen, + AccessType i_accessType, ...); + +}; + + +#endif diff --git a/src/lib/stdlib.C b/src/lib/stdlib.C index 8aa6b36eb..1614bdd8f 100644 --- a/src/lib/stdlib.C +++ b/src/lib/stdlib.C @@ -52,7 +52,7 @@ void* realloc(void* p, size_t s) cur_size = (1 << (*len + 4)) - 8; } - if (s < cur_size) + if (s <= cur_size) return p; void* new_p = malloc(s); diff --git a/src/makefile b/src/makefile index 5f65cb74e..4a1987678 100644 --- a/src/makefile +++ b/src/makefile @@ -15,10 +15,10 @@ DIRECT_BOOT_OBJECTS = start.o kernel.o taskmgr.o cpumgr.o syscall.o \ RUNTIME_OBJECTS = -BASE_MODULES = trace errl +BASE_MODULES = trace errl devicefw DIRECT_BOOT_MODULES = example RUNTIME_MODULES = -TESTCASE_MODULES = cxxtest testexample testtrace testerrl +TESTCASE_MODULES = cxxtest testerrl testdevicefw RELOCATABLE_IMAGE_LDFLAGS = -pie --export-dynamic diff --git a/src/usr/devicefw/assoccontain.C b/src/usr/devicefw/assoccontain.C new file mode 100644 index 000000000..d15eec223 --- /dev/null +++ b/src/usr/devicefw/assoccontain.C @@ -0,0 +1,52 @@ +#include "assoccontain.H" +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +namespace DeviceFW +{ + + AssociationContainer::AssociationContainer() + : iv_data(NULL), iv_size(0) + { + } + + AssociationContainer::~AssociationContainer() + { + if (NULL != iv_data) + { + free(iv_data); + } + } + + AssociationData* AssociationContainer::operator[](size_t i_pos) const + { + if (i_pos >= iv_size) + { + return NULL; + } + + return &iv_data[i_pos]; + } + + size_t AssociationContainer::allocate(size_t i_size) + { + size_t cur_pos = iv_size; + + // Resize buffer to have space for request. + iv_size += i_size; + iv_data = + static_cast<AssociationData*>( + realloc(iv_data, + sizeof(AssociationData) * (iv_size) + ) + ); + + // Clear out newly allocated space. + memset(this->operator[](cur_pos), '\0', + sizeof(AssociationData) * i_size); + + return cur_pos; + } +}; + diff --git a/src/usr/devicefw/assoccontain.H b/src/usr/devicefw/assoccontain.H new file mode 100644 index 000000000..9786168e0 --- /dev/null +++ b/src/usr/devicefw/assoccontain.H @@ -0,0 +1,93 @@ +/** @file assoccontain.H + * @brief Provides a memory pool container for allocating blocks of + * associations for the device driver framework. + * + * This class exists because storing the information in a normal map or + * vector would be exceptionally big for the expected sparseness of the + * device driver associations. + */ + +#ifndef __DEVICEFW_ASSOCCONTAIN_H +#define __DEVICEFW_ASSOCCONTAIN_H + +#include <stdint.h> +#include <builtins.h> + +namespace DeviceFW +{ + /** @struct AssociationData + * @brief Data about a particular association. + * + * Typically this is a offset (pointer) to another association and an + * optional flag for the associatior to use. This allows a user to + * construct a light-weight multi-dimensional sparse array. + * + * The purpose of the flag field is left to the user. + */ + struct AssociationData + { + bool flag:1; + size_t offset:15; + + AssociationData() : flag(false), offset(0) { }; + + AssociationData(bool i_flag, size_t i_off) + : flag(i_flag), offset(i_off) { }; + + } PACKED; + + /** @class AssociationContainer + * @brief The memory pool container of AssociationData blocks. + * + * The typical usage of this will be to create a multi-dimensional sparse + * associative array (map). The first block allocated will be the first + * dimension of the array. Each AssociationData entry in the block is the + * offset of the block containing the AssociationData for the next + * dimension. At the lowest level this offset might be used as an offset + * into another container, such as an std::vector. + */ + class AssociationContainer + { + public: + /** + * @brief Constructor for AssociationContainer. + * + * Initializes internal members to clean values. + */ + AssociationContainer(); + + /** + * @brief Destructor for AssociationContainer. + * + * Releases memory associated with container. After this call + * all pointers previously obtained through operator[] are invalid. + */ + ~AssociationContainer(); + + /** + * @brief Look up a particular association entry by offset. + * @return A pointer to the requested association block. + * + * @note Will return a NULL pointer if an invalid offset is given. + */ + AssociationData* operator[](size_t) const; + + /** + * @brief Allocate a new block into the pool. + * @return A offset to the newly allocated block. + * + * The blocks allocated will all be erased to 0's. + * + * @note This function can change the pointers previously + * returned by the operator[]. Treat this function + * as though all previous pointers are invalidated. + */ + size_t allocate(size_t); + + private: + AssociationData* iv_data; + size_t iv_size; + }; +}; + +#endif diff --git a/src/usr/devicefw/associator.C b/src/usr/devicefw/associator.C new file mode 100644 index 000000000..95c8ef7a0 --- /dev/null +++ b/src/usr/devicefw/associator.C @@ -0,0 +1,205 @@ +#include <algorithm> +#include "associator.H" + +#include <trace/interface.H> + +namespace DeviceFW +{ + trace_desc_t* g_traceBuffer = NULL; + TRAC_INIT(&g_traceBuffer, "DevFW", 4096); + + Associator::Associator() : iv_mutex(mutex_create()) + { + TRACFCOMP(g_traceBuffer, ENTER_MRK "Associator::Associator"); + + // Allocate first level of map (access types). + iv_routeMap = iv_associations.allocate(LAST_DRIVER_ACCESS_TYPE); + } + + Associator::~Associator() + { + mutex_destroy(iv_mutex); + + TRACFCOMP(g_traceBuffer, EXIT_MRK "Associator::~Associator"); + } + + void Associator::registerRoute(int64_t i_opType, + int64_t i_accType, + int64_t i_targetType, + deviceOp_t i_regRoute) + { + TRACFCOMP(g_traceBuffer, "Device route registered for (%d, %d, %d)", + i_opType, i_accType, i_targetType); + + // The ranges of the parameters should all be verified by the + // compiler due to the template specializations in driverif.H. + // No assert-checks will be done here. + + mutex_lock(iv_mutex); + + size_t ops = 0; + AssociationData targets = AssociationData(); + + // Look up second level of map (op-type) or allocate fresh block. + ops = iv_associations[iv_routeMap][i_accType].offset; + if (0 == ops) + { + // space for LAST_OP_TYPE plus WILDCARD(-1). + ops = iv_associations.allocate(LAST_OP_TYPE + 1) + 1; + iv_associations[iv_routeMap][i_accType].offset = ops; + } + + // Look up third level of map (access-type) or allocate fresh block. + targets = iv_associations[ops][i_opType]; + if (0 == targets.offset) + { + // To conserve space only allocate 1 block for WILDCARD. + if (WILDCARD == i_targetType) + { + targets.offset = iv_associations.allocate(1); + targets.flag = true; // True flag indicates WILDCARD. + } + else + { + // Allocate full number of spaces. + targets.offset = iv_associations.allocate(LAST_TARGET_TYPE); + } + iv_associations[ops][i_opType] = targets; + } + // Ensure the right block size was allocated previously for this + // target-type (wildcard vs non-wildcard). + if(((targets.flag) && (i_targetType != WILDCARD)) || + ((!targets.flag) && (i_targetType == WILDCARD))) + { + // TODO: Create error log for invalid target type? + bool l_invalidTargetType = false; + assert(l_invalidTargetType); + } + + // Index offset to proper target type. This is now lowest level of map. + targets.offset += (i_targetType == WILDCARD ? 0 : i_targetType); + + + // Search function vector for entry. + opVector_t::iterator opLocation = std::find(iv_operations.begin(), + iv_operations.end(), + i_regRoute); + // Insert function into vector if not found. + if (iv_operations.end() == opLocation) + { + iv_operations.push_back(i_regRoute); + opLocation = iv_operations.end() - 1; + } + + // TODO: Implement std::distance algorithm and change to: + // std::distance(iv_operations.begin(), opLocation); + size_t opLoc = opLocation - iv_operations.begin(); + + // Set function offset into map. True flag indicates valid. + (*iv_associations[targets.offset]) = AssociationData(true, opLoc); + + mutex_unlock(iv_mutex); + } + + ErrorHandle_t Associator::performOp(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + int64_t i_accessType, va_list i_addr) + const + { + TRACDCOMP(g_traceBuffer, "Device op requested for (%d, %d, %d)", + i_opType, i_accessType, /*TODO: i_target->type*/PROCESSOR); + + // The ranges of the parameters should all be verified by the + // compiler due to the template specializations in driverif.H. + // No assert-checks will be done here. + + ErrorHandle_t l_errl = NULL; + + mutex_lock(iv_mutex); + + // Function pointer found for this route request. + deviceOp_t l_devRoute = NULL; + // Pointer to root of the map. + const AssociationData* routeMap = iv_associations[iv_routeMap]; + + do + { + // Follow first level (access type), verify. + if (0 == routeMap[i_accessType].offset) + { + break; + } + + const AssociationData* ops = + iv_associations[routeMap[i_accessType].offset]; + + // Check op type = WILDCARD registrations. + if (0 != ops[WILDCARD].offset) + { + // Check access type = WILDCARD registrations. + if (ops[WILDCARD].flag) + { + l_devRoute = + iv_operations[ + iv_associations[ops[WILDCARD].offset]->offset]; + break; + } + + // Check access type = i_target->type registrations. + const AssociationData* targets = + iv_associations[ops[WILDCARD].offset]; + if (targets[/*TODO: i_target->type*/PROCESSOR].flag) + { + l_devRoute = + iv_operations[ + targets[/*TODO: i_target->type*/PROCESSOR].offset]; + break; + } + } + + // Check op type = i_opType registrations. + if (0 != ops[i_opType].offset) + { + // Check access type = WILDCARD registrations. + if(ops[i_opType].flag) + { + l_devRoute = + iv_operations[ + iv_associations[ops[i_opType].offset]->offset]; + break; + } + + // Check access type = i_target->type registrations. + const AssociationData* targets = + iv_associations[ops[i_opType].offset]; + if (targets[/*TODO: i_target->type*/PROCESSOR].flag) + { + l_devRoute = + iv_operations[ + targets[/*TODO: i_target->type*/PROCESSOR].offset]; + break; + } + } + } while(0); + + mutex_unlock(iv_mutex); + + // Call function if one was found, create error otherwise. + if (NULL == l_devRoute) + { + // TODO: create error log. + assert(NULL != l_devRoute); + } + else + { + l_errl = (*l_devRoute)(i_opType, i_target, + io_buffer, io_buflen, + i_accessType, i_addr); + } + + return l_errl; + } +} + + diff --git a/src/usr/devicefw/associator.H b/src/usr/devicefw/associator.H new file mode 100644 index 000000000..67b35e31b --- /dev/null +++ b/src/usr/devicefw/associator.H @@ -0,0 +1,72 @@ +/** @file associator.H + * Define the internal routing mechanism (DriverAssociator) for the framework. + */ +#ifndef __DEVICEFW_ASSOCIATOR_H +#define __DEVICEFW_ASSOCIATOR_H + +#include <devicefw/driverif.H> +#include <sys/mutex.h> +#include <vector> + +#include "assoccontain.H" + +namespace DeviceFW +{ + /** @class Associator + * @brief Class which performs the routing. + * + * Acts as a map of {op, access method, target type} -> deviceOp_t. + * + * This class is not implemented using a stl::map (or similar container) + * because space required to store the map, due to the number of pointers + * required to iterate to 3 levels of depth, would be much larger than + * needed. + * + * This class instead uses the AssociationContainer as a map on 16bit + * offsets to compact the storage requirements. Since we expect the + * same driver function may be registered multiple times (for different + * op / access method / target types), the class only stores the function + * pointers once and keeps an index of the function pointer in multiple + * locations in the map. + * + * The map is stored in the AssociationContainer as: + * iv_associations[AccessType][OpType][TargetType]. + */ + class Associator + { + public: + /** @brief Default constructor for Associator. */ + Associator(); + + /** @brief Destructor for Associator. + * Removes all existing routing registrations. + */ + ~Associator(); + + /** Register routing interface. See deviceRegisterRoute. */ + void registerRoute(int64_t i_opType, + int64_t i_accType, + int64_t i_targetType, + deviceOp_t i_regRoute); + + /** Perform routing. See deviceOp. */ + ErrorHandle_t performOp(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + int64_t i_accessType, va_list i_addr) const; + private: + typedef std::vector<deviceOp_t> opVector_t; + + /** Mutex to provide thread-safety. (could be rw-lock) */ + mutex_t iv_mutex; + /** Vector of deviceOp_t functions registered. */ + opVector_t iv_operations; + /** Compacted offset map. */ + AssociationContainer iv_associations; + /** Index in map of the first level of the associations. */ + size_t iv_routeMap; + + }; +} + +#endif diff --git a/src/usr/devicefw/driverif.C b/src/usr/devicefw/driverif.C new file mode 100644 index 000000000..f6a2b9321 --- /dev/null +++ b/src/usr/devicefw/driverif.C @@ -0,0 +1,157 @@ +/** @file driverif.C + * Implement the template specializations of functions from driverif.H. + */ +#include <devicefw/driverif.H> +#include <util/singleton.H> + +#include "associator.H" + +namespace DeviceFW +{ + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType_DriverOnly i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(OperationType i_opType, + AccessType_DriverOnly i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType_DriverOnly i_accessType, + TargetType_t i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + template <> + void deviceRegisterRoute<>(DriverSpecial i_opType, + AccessType_DriverOnly i_accessType, + DriverSpecial i_targetType, + deviceOp_t i_regRoute) + { + Singleton<Associator>::instance().registerRoute( + static_cast<int64_t>(i_opType), + static_cast<int64_t>(i_accessType), + static_cast<int64_t>(i_targetType), + i_regRoute); + } + + // deviceOp: + // OpType - OperationType only. + // TargType - TargetType only. + // AccType - AccessType, AccessType_DriverOnly (no WILDCARD). + template <> + ErrorHandle_t deviceOp<>(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + AccessType i_accessType, ...) + { + va_list args; + ErrorHandle_t errl; + + va_start(args, i_accessType); + + errl = Singleton<Associator>::instance().performOp( + i_opType, i_target, io_buffer, io_buflen, + i_accessType, args); + + va_end(args); + return errl; + } + + template <> + ErrorHandle_t deviceOp<>(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + AccessType_DriverOnly i_accessType, ...) + { + va_list args; + ErrorHandle_t errl; + + va_start(args, i_accessType); + + errl = Singleton<Associator>::instance().performOp( + i_opType, i_target, io_buffer, io_buflen, + i_accessType, args); + + va_end(args); + return errl; + } + +}; diff --git a/src/usr/devicefw/makefile b/src/usr/devicefw/makefile new file mode 100644 index 000000000..27d73465c --- /dev/null +++ b/src/usr/devicefw/makefile @@ -0,0 +1,8 @@ +ROOTPATH = ../../.. +MODULE = devicefw + +OBJS = driverif.o userif.o assoccontain.o associator.o + +SUBDIRS = test.d + +include ${ROOTPATH}/config.mk diff --git a/src/usr/devicefw/test/assoccontaintest.H b/src/usr/devicefw/test/assoccontaintest.H new file mode 100644 index 000000000..160a8c2f9 --- /dev/null +++ b/src/usr/devicefw/test/assoccontaintest.H @@ -0,0 +1,102 @@ +#ifndef __TEST_ASSOCCONTAINTEST_H +#define __TEST_ASSOCCONTAINTEST_H + +#include <cxxtest/TestSuite.H> +#include "../assoccontain.H" + +using namespace DeviceFW; + +class AssocContainTest: public CxxTest::TestSuite +{ +public: + + /** + * @test Ensure AssociationContainer can be constructed. + */ + void testConstruct() + { + AssociationContainer* ac = new AssociationContainer(); + if (NULL == ac) + { + TS_FAIL("NULL pointer returned for 'new AssociationContainer'."); + } + delete ac; + } + + /** + * @test Verify allocate operation. + */ + void testAllocate() + { + AssociationContainer ac; + + // Allocation of a single block. + size_t root = ac.allocate(5); + + // Index operation on block. + AssociationData* root_ptr = ac[root]; + if (NULL == root_ptr) + { + TS_FAIL("NULL pointer returned for newly allocated block."); + } + + // Freshly allocated block is empty. + for(int i = 0; i < 5; i++) + { + if (root_ptr[i].flag) + { + TS_FAIL("Wildcard flag set on newly allocated block."); + } + if (0 != root_ptr[i].offset) + { + TS_FAIL("Non-zero value set in newly allocated block."); + } + } + + // Second allocated block is outside original. + size_t second_block = ac.allocate(2); + if (((second_block + 2) > root) && + (second_block < (root + 5))) + { + TS_FAIL("Newly allocated block is inside previous block."); + } + } + + /** + * @test Verify operator[]. + */ + void testIndex() + { + AssociationContainer ac; + + AssociationData root; + AssociationData second; + + root.offset = ac.allocate(5); + second.offset = ac.allocate(2); + second.flag = true; + + ac[second.offset][1].offset = 0x42; + + AssociationData* root_ptr = ac[root.offset]; + root_ptr[2] = second; + + // Check initial level block index. + if ((ac[root.offset + 2]->flag != second.flag) || + (ac[root.offset + 2]->offset != second.offset)) + { + TS_FAIL("Index operator points to incorrect block."); + } + + // Check second level indirection block index. + if (ac[ac[root.offset + 2]->offset + 1]->offset != 0x42) + { + TS_FAIL("Index operator points to incorrect block."); + } + } + + +}; + +#endif + diff --git a/src/usr/devicefw/test/associatortest.H b/src/usr/devicefw/test/associatortest.H new file mode 100644 index 000000000..792b6c3ef --- /dev/null +++ b/src/usr/devicefw/test/associatortest.H @@ -0,0 +1,174 @@ +#ifndef __TEST_ASSOCIATORTEST_H +#define __TEST_ASSOCIATORTEST_H + +#include <cxxtest/TestSuite.H> +#include "../associator.H" + +using namespace DeviceFW; + +class AssociatorTest; +typedef void (AssociatorTest::*test_fn)(); + +static test_fn g_associatorTest_value; +static test_fn g_associatorTest_result; +static OperationType g_associatorTest_opType; +static int64_t g_associatorTest_accessType; + +class AssociatorTest: public CxxTest::TestSuite +{ +public: + + /** + * @test Ensure AssociationContainer can be constructed. + */ + void testConstruct() + { + Associator* as = new Associator(); + if (NULL == as) + { + TS_FAIL("Unable to construct Associator."); + } + delete as; + } + + // Used for registration testing. + static + ErrorHandle_t performOperation(OperationType i_opType, + TargetHandle_t i_target, + void* io_buffer, size_t& io_buflen, + int64_t i_accessType, va_list i_addr) + { + g_associatorTest_result = g_associatorTest_value; + g_associatorTest_opType = i_opType; + g_associatorTest_accessType = i_accessType; + + return NULL; + } + + /** + * @test Verify simple registration. + */ + void testSimpleRegistration() + { + void* buf = NULL; + size_t bufsize = 0; + + g_associatorTest_value = &AssociatorTest::testSimpleRegistration; + + Associator as; + as.registerRoute(READ, + SCOM, + PROCESSOR, + &performOperation); + + ErrorHandle_t l_errl = as.performOp(READ, TargetHandle_t(), + buf, bufsize, + SCOM, va_list()); + + if (l_errl) + { + TS_FAIL("Error received from performOp."); + } + + if (g_associatorTest_result != g_associatorTest_value) + { + TS_FAIL("PerformOperation does not appear to have been called."); + } + + if (g_associatorTest_opType != READ) + { + TS_FAIL("Invalid operation type."); + } + + if (g_associatorTest_accessType != SCOM) + { + TS_FAIL("Invalid access type."); + } + } + + /** + * @test Verify registration with an operator as a WILDCARD. + */ + void testOpWildcard() + { + void* buf = NULL; + size_t bufsize = 0; + + g_associatorTest_value = &AssociatorTest::testOpWildcard; + + Associator as; + as.registerRoute(WILDCARD, + SCOM, + PROCESSOR, + &performOperation); + + ErrorHandle_t l_errl = as.performOp(WRITE, TargetHandle_t(), + buf, bufsize, + SCOM, va_list()); + + if (l_errl) + { + TS_FAIL("Error received from performOp."); + } + + if (g_associatorTest_result != g_associatorTest_value) + { + TS_FAIL("PerformOperation does not appear to have been called."); + } + + if (g_associatorTest_opType != WRITE) + { + TS_FAIL("Invalid operation type."); + } + + if (g_associatorTest_accessType != SCOM) + { + TS_FAIL("Invalid access type."); + } + } + + /** + * @test Verify registration with a target type as a WILDCARD. + */ + void testTargTypeWildcard() + { + void* buf = NULL; + size_t bufsize = 0; + + g_associatorTest_value = &AssociatorTest::testTargTypeWildcard; + + Associator as; + as.registerRoute(READ, + SCOM, + WILDCARD, + &performOperation); + + ErrorHandle_t l_errl = as.performOp(READ, TargetHandle_t(), + buf, bufsize, + SCOM, va_list()); + + if (l_errl) + { + TS_FAIL("Error received from performOp."); + } + + if (g_associatorTest_result != g_associatorTest_value) + { + TS_FAIL("PerformOperation does not appear to have been called."); + } + + if (g_associatorTest_opType != READ) + { + TS_FAIL("Invalid operation type."); + } + + if (g_associatorTest_accessType != SCOM) + { + TS_FAIL("Invalid access type."); + } + } + +}; + +#endif + diff --git a/src/usr/devicefw/test/makefile b/src/usr/devicefw/test/makefile new file mode 100644 index 000000000..b3571ae96 --- /dev/null +++ b/src/usr/devicefw/test/makefile @@ -0,0 +1,6 @@ +ROOTPATH = ../../../.. + +MODULE = testdevicefw +TESTS = *.H + +include ${ROOTPATH}/config.mk diff --git a/src/usr/devicefw/userif.C b/src/usr/devicefw/userif.C new file mode 100644 index 000000000..3b1f237f8 --- /dev/null +++ b/src/usr/devicefw/userif.C @@ -0,0 +1,46 @@ +/** @file driverif.C + * Implement the functions from userif.H. + */ + +#include <devicefw/userif.H> +#include <util/singleton.H> + +#include "associator.H" + +namespace DeviceFW +{ + ErrorHandle_t deviceRead(TargetHandle_t i_target, + void* o_buffer, size_t& io_buflen, + AccessType i_accessType, ...) + { + va_list args; + ErrorHandle_t errl; + + va_start(args, i_accessType); + + errl = Singleton<Associator>::instance().performOp( + READ, i_target, o_buffer, io_buflen, + i_accessType, args); + + va_end(args); + return errl; + } + + ErrorHandle_t deviceWrite(TargetHandle_t i_target, + void* i_buffer, size_t& io_buflen, + AccessType i_accessType, ...) + { + va_list args; + ErrorHandle_t errl; + + va_start(args, i_accessType); + + errl = Singleton<Associator>::instance().performOp( + WRITE, i_target, i_buffer, io_buflen, + i_accessType, args); + + va_end(args); + return errl; + } + +}; diff --git a/src/usr/makefile b/src/usr/makefile index aa24cf6cd..cd0daeba7 100644 --- a/src/usr/makefile +++ b/src/usr/makefile @@ -1,6 +1,6 @@ ROOTPATH = ../.. OBJS = module_init.o -SUBDIRS = example.d trace.d cxxtest.d errl.d +SUBDIRS = example.d trace.d cxxtest.d errl.d devicefw.d include ${ROOTPATH}/config.mk |