diff --git a/fsw/modules/iodriver/CMakeLists.txt b/fsw/modules/iodriver/CMakeLists.txt new file mode 100644 index 00000000..83bd9869 --- /dev/null +++ b/fsw/modules/iodriver/CMakeLists.txt @@ -0,0 +1,9 @@ + +# Generic I/O device driver interface module +add_psp_module(iodriver src/iodriver.c) + +target_include_directories(iodriver PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc) + +if (ENABLE_UNIT_TESTS) + add_subdirectory(ut-stubs) +endif (ENABLE_UNIT_TESTS) diff --git a/fsw/modules/iodriver/inc/iodriver_analog_io.h b/fsw/modules/iodriver/inc/iodriver_analog_io.h new file mode 100644 index 00000000..8c8d41a8 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_analog_io.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_analog_io.h + * + * Created on: Sep 30, 2015 + * Created by: joseph.p.hickey@nasa.gov + * + */ + +#ifndef CFE_PSP_IODriver_ANALOG_IO_H_ +#define CFE_PSP_IODriver_ANALOG_IO_H_ + +/* Include all base definitions */ +#include "iodriver_base.h" + +/** + * Standardized width of ADC/DAC codes. + * + * This should reflect the highest-precision ADC that the system is expected to use. ADC inputs + * that are less precise than this will be bit-expanded in software such that all processing + * in the upper layers receives consistent data no matter what the actual hardware implements. + * This permits easier swapping between different phsyical hardware types, including those with + * potentially less ADC/DAC precision, while presenting similar values to application code. + */ +#define CFE_PSP_IODriver_ADC_BITWIDTH 24 + +/** + * Type abstraction for expressing analog ADC codes. + * + * This type is an integer type of at least CFE_PSP_IODriver_ADC_BITWIDTH in length. It is used + * as a parameter for the Read/Write opcodes on ADC/DAC channels. Normalized (fixed-width) ADC + * codes are used at this layer rather than floating point due to the fact that floats involve a + * lot of extra overhead and some CPUs do not have FP units at all. + * + * If desired on CPUs that are capable of good-performance floating point operations, another + * module/CFS application can convert the ADC codes to real-word floats. This would be done + * outside the I/O driver layer. + */ +typedef int32 CFE_PSP_IODriver_AdcCode_t; + +/** + * Complete API container for analog read/write commands. + * This allows reading/writing multiple channels at once with a single entry into the API. + * As each entry into the API needs to acquire a mutex for serialization, this can be much + * more efficient to read channels through this means rather than single channel read/write. + * Set NumChannels to 1 to perform single channel read/write + */ +typedef struct +{ + uint16 NumChannels; /**< Number of channels in the i/o structure (length of "samples" array) */ + CFE_PSP_IODriver_AdcCode_t *Samples; /**< Array for ADC/DAC samples */ +} CFE_PSP_IODriver_AnalogRdWr_t; + +/** + * Opcodes specific to analog io (ADC/DAC) devices + */ +enum +{ + CFE_PSP_IODriver_ANALOG_IO_NOOP = CFE_PSP_IODriver_ANALOG_IO_CLASS_BASE, + + CFE_PSP_IODriver_ANALOG_IO_READ_CHANNELS, /**< CFE_PSP_IODriver_AnalogRdWr_t argument */ + CFE_PSP_IODriver_ANALOG_IO_WRITE_CHANNELS, /**< CFE_PSP_IODriver_AnalogRdWr_t argument */ + + CFE_PSP_IODriver_ANALOG_IO_MAX +}; + +#endif /* CFE_PSP_IODriver_ANALOG_IO_H_ */ diff --git a/fsw/modules/iodriver/inc/iodriver_base.h b/fsw/modules/iodriver/inc/iodriver_base.h new file mode 100644 index 00000000..dee86b36 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_base.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file + * + * I/O Driver base header file + */ + +#ifndef IODRIVER_BASE_H +#define IODRIVER_BASE_H + +#include + +/** + * Physical channel location descriptor. + * + * See the CFE_PSP_IODriver_LOOKUP_SUBCHANNEL opcode to determine channel number to set in here, + * as each board may have their own unique channel naming conventions. The integer value that + * goes in this structure may or may not correlate to the physical device labeling. + */ +typedef struct +{ + uint32 PspModuleId; /**< Device selection */ + uint16 SubsystemId; /**< Instance or subsystem number */ + uint16 SubchannelId; /**< Subchannel number - optional, set to 0 for devices that do not have multiple channels */ +} CFE_PSP_IODriver_Location_t; + +/** + * Wrapper for constant arguments, to avoid a compiler warning + * about arguments differing in const-ness. Use the inline functions to + * pass in an immediate/constant value. + */ +typedef union +{ + void * Vptr; + const void *ConstVptr; + const char *ConstStr; + uint32 U32; +} CFE_PSP_IODriver_Arg_t; + +static inline CFE_PSP_IODriver_Arg_t CFE_PSP_IODriver_VPARG(void *x) +{ + CFE_PSP_IODriver_Arg_t a; + a.Vptr = x; + return a; +} +static inline CFE_PSP_IODriver_Arg_t CFE_PSP_IODriver_CONST_VPARG(const void *x) +{ + CFE_PSP_IODriver_Arg_t a; + a.ConstVptr = x; + return a; +} +static inline CFE_PSP_IODriver_Arg_t CFE_PSP_IODriver_CONST_STR(const char *x) +{ + CFE_PSP_IODriver_Arg_t a; + a.ConstStr = x; + return a; +} +static inline CFE_PSP_IODriver_Arg_t CFE_PSP_IODriver_U32ARG(uint32 x) +{ + CFE_PSP_IODriver_Arg_t a; + a.U32 = x; + return a; +} + +/** + * Standardized concept of directionality for any device + * + * Some code may use these enumeration values as a bitmask - + * use care when updating to ensure that the values may be used as bitmasks. + * Specific hardware drivers may or may not implement all modes depending on capabilities. + */ +typedef enum +{ + CFE_PSP_IODriver_Direction_DISABLED = 0, /**< Disabled (inactive, tri-state if possible) */ + CFE_PSP_IODriver_Direction_INPUT_ONLY = 0x01, /**< Device/channel is configured for input */ + CFE_PSP_IODriver_Direction_OUTPUT_ONLY = 0x02, /**< Device/channel is configured for output */ + CFE_PSP_IODriver_Direction_INPUT_OUTPUT = 0x03 /**< Input/Output (some HW supports this) */ +} CFE_PSP_IODriver_Direction_t; + +/** + * Some common values for the device command codes + * These are some VERY basic ops that many devices may support in some way. + * Any opcode that is not implemented should return CFE_PSP_ERROR_NOT_IMPLEMENTED + * + * Negative return values indicate an error of some type, while return values >= 0 indicate success + */ +enum +{ + CFE_PSP_IODriver_NOOP = 0, /**< Reserved, do nothing */ + + /* Start/stop opcodes */ + CFE_PSP_IODriver_SET_RUNNING, /**< uint32 argument, 0=stop 1=start device */ + CFE_PSP_IODriver_GET_RUNNING, /**< no argument, returns positive nonzero (true) if running and zero (false) if + stopped, negative on error */ + + /* Configuration opcodes */ + CFE_PSP_IODriver_SET_CONFIGURATION, /**< const string argument (device-dependent content) */ + CFE_PSP_IODriver_GET_CONFIGURATION, /**< void * argument (device-dependent content) */ + + /* Sub-channel configuration/mapping opcodes */ + CFE_PSP_IODriver_LOOKUP_SUBSYSTEM, /**< const char * argument, looks up ChannelName and returns positive value for + subsystem ID, negative value for error */ + CFE_PSP_IODriver_LOOKUP_SUBCHANNEL, /**< const char * argument, looks up ChannelName and returns positive value for + subchannel ID, negative value for error */ + CFE_PSP_IODriver_SET_DIRECTION, /**< U32 (CFE_PSP_IODriver_Direction_t) argument as input */ + CFE_PSP_IODriver_QUERY_DIRECTION, /**< U32 (CFE_PSP_IODriver_Direction_t) argument as output */ + + /* + * Placeholders for opcodes that could be implemented across a class of devices. + * For instance, all ADC/DAC devices should implement a common set of read/write opcodes + * so that devices can be interchanged without affecting higher-level software + */ + CFE_PSP_IODriver_ANALOG_IO_CLASS_BASE = 0x00010000, /**< Opcodes for typical adc/dac devices */ + CFE_PSP_IODriver_DISCRETE_IO_CLASS_BASE = 0x00020000, /**< Opcodes for discrete IO (digital logic) devices */ + CFE_PSP_IODriver_PACKET_IO_CLASS_BASE = 0x00030000, /**< Opcodes for packet/datagram-oriented devices */ + CFE_PSP_IODriver_MEMORY_IO_CLASS_BASE = 0x00040000, /**< Opcodes for memory/register oriented devices */ + CFE_PSP_IODriver_STREAM_IO_CLASS_BASE = 0x00050000, /**< Opcodes for data stream oriented devices */ + + /** + * Placeholder for extended opcodes that may be very specific to a single device/device type. + * This allows the same API call (CFE_PSP_DeviceCommandFunc_t) but + */ + CFE_PSP_IODriver_EXTENDED_BASE = 0x7FFF0000 + +}; + +/* ------------------------------------------------------------- */ +/** + * @brief Find an IO device module ID by name + * + * @param DriverName the device name to find + * @param PspModuleId location to store the module ID, if found + * + * @retval #CFE_PSP_SUCCESS if found, or error code if not found + */ +int32 CFE_PSP_IODriver_FindByName(const char *DriverName, uint32 *PspModuleId); + +/* ------------------------------------------------------------- */ +/** + * @brief Issue a request to an IO device module + * + * @param Location Aggregate location identifier + * @param CommandCode Request identifier + * @param Arg Request Argument + * + * @retval #CFE_PSP_SUCCESS if successful, or error code if not successful + */ +int32 CFE_PSP_IODriver_Command(const CFE_PSP_IODriver_Location_t *Location, uint32 CommandCode, + CFE_PSP_IODriver_Arg_t Arg); + +#endif /* IODRIVER_BASE_H */ diff --git a/fsw/modules/iodriver/inc/iodriver_digital_gpio.h b/fsw/modules/iodriver/inc/iodriver_digital_gpio.h new file mode 100644 index 00000000..a5aa4a53 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_digital_gpio.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_digital_gpio.h + * + * Created on: Sep 30, 2015 + * Created by: joseph.p.hickey@nasa.gov + * + */ + +#ifndef CFE_PSP_IODriver_DISCRETE_GPIO_H_ +#define CFE_PSP_IODriver_DISCRETE_GPIO_H_ + +/** + * Type abstraction for expressing digital logic levels. + * + * This value will be filled starting with the LSB. A typical GPIO logic channel is 1 bit, so + * only the LSB is signficiant and the other bits are not used. + * + * This allows single channels up to 8 bits wide, but multiple "channels" could be concatenated + * using a multi-read/write opcode to allow atomic access to any number of bits. + */ +typedef uint8 CFE_PSP_IODriver_GpioLevel_t; + +/** + * Enumerated names for typical digital 1-bit logic channel states. + * + * For convenience / code readability. + */ +enum +{ + CFE_PSP_IODriver_GPIO_LOGIC_LOW = 0, + CFE_PSP_IODriver_GPIO_LOGIC_HIGH = 1 +}; + +#ifdef JPHFIX +/** + * Overall Configuration structure for channel configuration commands. + * + * This describes both ADC/DAC (analog) and GPIO (digital logic) + * channels. It is returned from the "Get Status" command. + */ +typedef struct +{ + CFE_PSP_IODriver_ChannelType_t ChannelType; /**< Define the type of channel */ + CFE_PSP_IODriver_Direction_t Direction; /**< Define whether GPIO is input or output */ +} CFE_PSP_IODriver_ChannelStatus_t; + +/** + * Sample container for multi-channel read/write commands. + * + * This is just a pointer to an array, but the array could be ADC codes + * or digital logic levels. This allows the same API to be used for both. + */ +typedef union +{ + void * VoidPtr; + CFE_PSP_IODriver_GpioLevel_t *GpioLev; +} CFE_PSP_IODriver_Sample_t; + +#endif + +/** + * Complete API container for gpio read/write commands. + * This allows reading/writing multiple channels at once with a single entry into the API. + * As each entry into the API needs to acquire a mutex for serialization, this can be much + * more efficient to read channels through this means rather than single channel read/write. + */ +typedef struct +{ + uint16 NumChannels; /**< Number of channels in the i/o structure (length of "samples" array) */ + CFE_PSP_IODriver_GpioLevel_t *Samples; /**< Array for digital logic levels */ +} CFE_PSP_IODriver_GpioRdWr_t; + +/** + * Opcodes specific to digital GPIO devices + */ +enum +{ + CFE_PSP_IODriver_DISCRETE_IO_NOOP = CFE_PSP_IODriver_DISCRETE_IO_CLASS_BASE, + + CFE_PSP_IODriver_DISCRETE_IO_READ_CHANNELS, /**< CFE_PSP_IODriver_GpioRdWr_t argument */ + CFE_PSP_IODriver_DISCRETE_IO_WRITE_CHANNELS, /**< CFE_PSP_IODriver_GpioRdWr_t argument */ + + CFE_PSP_IODriver_DISCRETE_IO_MAX +}; + +#endif /* CFE_PSP_IODriver_DISCRETE_GPIO_H_ */ diff --git a/fsw/modules/iodriver/inc/iodriver_impl.h b/fsw/modules/iodriver/inc/iodriver_impl.h new file mode 100644 index 00000000..ed537c94 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_impl.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_impl.h + * + * Created on: Oct 5, 2015 + * Created by: joseph.p.hickey@nasa.gov + * + */ + +#ifndef IODRIVER_IMPL_H +#define IODRIVER_IMPL_H + +#ifndef _CFE_PSP_MODULE_ +#error "Do not include this file from outside the PSP" +#endif + +#include "cfe_psp_module.h" +#include "iodriver_base.h" + +/** + * Macro to declare the global object for an IO device driver + */ +#define CFE_PSP_MODULE_DECLARE_IODEVICEDRIVER(name) \ + static void name##_Init(uint32 PspModuleId); \ + CFE_PSP_ModuleApi_t CFE_PSP_##name##_API = { \ + .ModuleType = CFE_PSP_MODULE_TYPE_DEVICEDRIVER, \ + .OperationFlags = 0, \ + .Init = name##_Init, \ + .ExtendedApi = &name##_DevApi, \ + } + +/** + * Prototype for a basic device command function + * Implemented as a single API call with an extendible command code for device-specific ops. This allows + * a common API to be used while still allowing full freedom to handle many different device types. + */ +typedef int32 (*CFE_PSP_IODriver_ApiFunc_t)(uint32 CommandCode, uint16 Instance, uint16 SubChannel, + CFE_PSP_IODriver_Arg_t arg); + +typedef const struct +{ + CFE_PSP_IODriver_ApiFunc_t DeviceCommand; + CFE_PSP_IODriver_ApiFunc_t DeviceMutex; +} CFE_PSP_IODriver_API_t; + +osal_id_t CFE_PSP_IODriver_GetMutex(uint32 PspModuleId, int32 DeviceHash); +int32 CFE_PSP_IODriver_HashMutex(int32 StartHash, int32 Datum); + +#endif /* IODRIVER_IMPL_H */ diff --git a/fsw/modules/iodriver/inc/iodriver_memory_io.h b/fsw/modules/iodriver/inc/iodriver_memory_io.h new file mode 100644 index 00000000..d5f3065c --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_memory_io.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_memory_io.h + * + * \date May 31, 2016 + * \author joseph.p.hickey@nasa.gov + * + */ + +#ifndef CFE_PSP_IODriver_MEMORY_IO_H_ +#define CFE_PSP_IODriver_MEMORY_IO_H_ + +/* Include all base definitions */ +#include "iodriver_base.h" + +/** + * API container for memory write commands. + * + * Associates a device address, buffer pointer and a buffer size. + */ +typedef struct +{ + uint32 DeviceAddress; + uint32 BufferSize; + const void *BufferMem; +} CFE_PSP_IODriver_WriteMemoryBuffer_t; + +/** + * API container for memory write commands. + * + * Associates a device address, buffer pointer and a buffer size. + */ +typedef struct +{ + uint32 DeviceAddress; + uint32 BufferSize; + void * BufferMem; +} CFE_PSP_IODriver_ReadMemoryBuffer_t; + +/** + * Opcodes specific to memory devices or other direct register-oriented interfaces + */ +enum +{ + CFE_PSP_IODriver_MEMORY_IO_NOOP = CFE_PSP_IODriver_MEMORY_IO_CLASS_BASE, + + CFE_PSP_IODriver_MEMORY_IO_READ_32, /**< CFE_PSP_IODriver_ReadMemoryBuffer_t argument, use 32 bit access */ + CFE_PSP_IODriver_MEMORY_IO_WRITE_32, /**< CFE_PSP_IODriver_WriteMemoryBuffer_t argument, use 32 bit access */ + CFE_PSP_IODriver_MEMORY_IO_READ_16, /**< CFE_PSP_IODriver_ReadMemoryBuffer_t argument, use 16 bit access */ + CFE_PSP_IODriver_MEMORY_IO_WRITE_16, /**< CFE_PSP_IODriver_WriteMemoryBuffer_t argument, use 16 bit access */ + CFE_PSP_IODriver_MEMORY_IO_READ_8, /**< CFE_PSP_IODriver_ReadMemoryBuffer_t argument, use 8 bit access */ + CFE_PSP_IODriver_MEMORY_IO_WRITE_8, /**< CFE_PSP_IODriver_WriteMemoryBuffer_t argument, use 8 bit access */ + CFE_PSP_IODriver_MEMORY_IO_READ_BLOCK, /**< CFE_PSP_IODriver_ReadMemoryBuffer_t argument, use any appropriate access + cycle (generic) */ + CFE_PSP_IODriver_MEMORY_IO_WRITE_BLOCK, /**< CFE_PSP_IODriver_WriteMemoryBuffer_t argument, use any appropriate + access cycle (generic) */ + + CFE_PSP_IODriver_MEMORY_IO_MAX +}; + +#endif /* CFE_PSP_IODriver_MEMORY_IO_H_ */ diff --git a/fsw/modules/iodriver/inc/iodriver_packet_io.h b/fsw/modules/iodriver/inc/iodriver_packet_io.h new file mode 100644 index 00000000..a67f1be5 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_packet_io.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_packet_io.h + * + * \date Mar 4, 2016 + * \author joseph.p.hickey@nasa.gov + * + */ + +#ifndef CFE_PSP_IODriver_PACKET_IO_H_ +#define CFE_PSP_IODriver_PACKET_IO_H_ + +/* Include all base definitions */ +#include "iodriver_base.h" + +/** + * API container for packet read/write commands. + * + * Associates a buffer pointer and a buffer size. + * For "write" operations the size reflects the actual size of the packet, + * and the buffer memory should not be modified by the driver (const). + */ +typedef struct +{ + uint32 OutputSize; /**< Number of channels in the i/o structure (length of "samples" array) */ + const void *BufferMem; +} CFE_PSP_IODriver_WritePacketBuffer_t; + +/** + * API container for packet read/write commands. + * + * Associates a buffer pointer and a buffer size. + * For "read" operations the size reflects maximum (allocated) size. It + * must be adjusted to the actual size of the packet recieved. + */ +typedef struct +{ + uint32 BufferSize; /**< Number of channels in the i/o structure (length of "samples" array) */ + void * BufferMem; +} CFE_PSP_IODriver_ReadPacketBuffer_t; + +/** + * Opcodes specific to packet oriented interfaces + */ +enum +{ + CFE_PSP_IODriver_PACKET_IO_NOOP = CFE_PSP_IODriver_PACKET_IO_CLASS_BASE, + + CFE_PSP_IODriver_PACKET_IO_READ, /**< CFE_PSP_IODriver_ReadPacketBuffer_t argument */ + CFE_PSP_IODriver_PACKET_IO_WRITE, /**< CFE_PSP_IODriver_WritePacketBuffer_t argument */ + + CFE_PSP_IODriver_PACKET_IO_MAX +}; + +/** + * Additional error codes specific to Packet I/O + * + * These are based from the CFE_PSP_IODriver_PACKET_IO_CLASS_BASE so as to not conflict with other classes of I/O + */ +enum +{ + CFE_PSP_IODriver_PACKET_ERROR_BASE = -(CFE_PSP_IODriver_PACKET_IO_CLASS_BASE + 0xFFFF), + CFE_PSP_IODriver_PACKET_LENGTH_ERROR, + CFE_PSP_IODriver_PACKET_CRC_ERROR +}; + +#endif /* CFE_PSP_IODriver_PACKET_IO_H_ */ diff --git a/fsw/modules/iodriver/inc/iodriver_stream_io.h b/fsw/modules/iodriver/inc/iodriver_stream_io.h new file mode 100644 index 00000000..323c4af3 --- /dev/null +++ b/fsw/modules/iodriver/inc/iodriver_stream_io.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file iodriver_stream_io.h + * + * \date Apr 4, 2017 + * \author lisa.b.vanderaar@nasa.gov + * + * \description Modification of the iodriver_packet_io.h to be stream specific + * + */ + +#ifndef CFE_PSP_IODriver_STREAM_IO_H_ +#define CFE_PSP_IODriver_STREAM_IO_H_ + +/* Include all base definitions */ +#include "iodriver_base.h" + +/** + * API container for stream write commands. + * -- Associates a buffer pointer and a buffer size. + */ +typedef struct +{ + uint32 BufferSize; /**< Size of data buffer */ + const void *BufferMem; /**< Pointer to data buffer to write */ +} CFE_PSP_IODriver_WriteStreamBuffer_t; + +/** + * API container for stream read commands. + * -- Associates a buffer pointer and a buffer size. + */ +typedef struct +{ + uint32 BufferSize; /**< Size of data buffer */ + void * BufferMem; /**< Pointer to data buffer to store read data */ +} CFE_PSP_IODriver_ReadStreamBuffer_t; + +/** + * Opcodes specific to stream oriented interfaces + * FIX: Right now these are the same as the packet interface and need to be changed. + */ +enum +{ + CFE_PSP_IODriver_STREAM_IO_NOOP = CFE_PSP_IODriver_STREAM_IO_CLASS_BASE, + + CFE_PSP_IODriver_STREAM_IO_READ, /**< CFE_PSP_IODriver_ReadStreamBuffer_t argument */ + CFE_PSP_IODriver_STREAM_IO_WRITE, /**< CFE_PSP_IODriver_WriteStreamBuffer_t argument */ + + CFE_PSP_IODriver_STREAM_IO_MAX +}; + +/** + * Additional error codes specific to Stream I/O + * + * These are based from the CFE_PSP_IODriver_STREAM_IO_CLASS_BASE so as to not conflict with other classes of I/O + */ +enum +{ + CFE_PSP_IODriver_STREAM_ERROR_BASE = -(CFE_PSP_IODriver_STREAM_IO_CLASS_BASE + 0xFFFF), + CFE_PSP_IODriver_STREAM_LENGTH_ERROR, + CFE_PSP_IODriver_STREAM_CRC_ERROR +}; + +#endif /* CFE_PSP_IODriver_STREAM_IO_H_ */ diff --git a/fsw/modules/iodriver/src/iodriver.c b/fsw/modules/iodriver/src/iodriver.c new file mode 100644 index 00000000..1ef8939e --- /dev/null +++ b/fsw/modules/iodriver/src/iodriver.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * \file cfe_psp_iodriver.c + * + * Created on: Sep 29, 2015 + * Created by: joseph.p.hickey@nasa.gov + * + */ + +#include "cfe_psp_module.h" +#include "iodriver_base.h" +#include "iodriver_impl.h" + +#define CFE_PSP_IODriver_LOCK_TABLE_SIZE 7 + +CFE_PSP_MODULE_DECLARE_SIMPLE(iodriver); + +static osal_id_t CFE_PSP_IODriver_Mutex_Table[CFE_PSP_IODriver_LOCK_TABLE_SIZE]; + +const CFE_PSP_IODriver_API_t CFE_PSP_IODriver_DEFAULT_API = {.DeviceCommand = NULL, .DeviceMutex = NULL}; + +void iodriver_Init(uint32 PspModuleId) +{ + uint32 i; + char TempName[OS_MAX_PATH_LEN]; + + for (i = 0; i < CFE_PSP_IODriver_LOCK_TABLE_SIZE; ++i) + { + snprintf(TempName, sizeof(TempName), "DriverMutex-%02u", (unsigned int)(i + 1)); + OS_MutSemCreate(&CFE_PSP_IODriver_Mutex_Table[i], TempName, 0); + } +} + +CFE_PSP_IODriver_API_t *CFE_PSP_IODriver_GetAPI(uint32 PspModuleId) +{ + int32 Result; + CFE_PSP_ModuleApi_t * API; + CFE_PSP_IODriver_API_t *CFE_PSP_IODriver_API; + + Result = CFE_PSP_Module_GetAPIEntry(PspModuleId, &API); + if (Result == CFE_PSP_SUCCESS && API->ModuleType == CFE_PSP_MODULE_TYPE_DEVICEDRIVER) + { + CFE_PSP_IODriver_API = (const CFE_PSP_IODriver_API_t *)API->ExtendedApi; + } + else + { + CFE_PSP_IODriver_API = &CFE_PSP_IODriver_DEFAULT_API; + } + + return CFE_PSP_IODriver_API; +} + +/** + * Based on a board type and instance, determine which mutex needs to be acquired. + * Allows some degree of parallelization by (most likely) not blocking requests to a different boards + * that come in concurrently, but also ensuring that requests to the _same_ board will be serialized. + * The computation here seemed to produce a decent-enough spread across the mutex table without + * overlaps (at least with the current set of hardware) + */ +osal_id_t CFE_PSP_IODriver_GetMutex(uint32 PspModuleId, int32 DeviceHash) +{ + uint32 LookupId; + osal_id_t ResultId; + + if (DeviceHash < 0) + { + /* No locking required */ + ResultId = OS_OBJECT_ID_UNDEFINED; + } + else + { + LookupId = (uint32)DeviceHash; + if (PspModuleId != 0) + { + LookupId ^= PspModuleId; + } + ResultId = CFE_PSP_IODriver_Mutex_Table[LookupId % CFE_PSP_IODriver_LOCK_TABLE_SIZE]; + } + + return ResultId; +} + +int32 CFE_PSP_IODriver_HashMutex(int32 StartHash, int32 Datum) +{ + Datum *= 0x10AB; + return ((StartHash + Datum) & 0x7FFFFFFF); +} + +int32 CFE_PSP_IODriver_Command(const CFE_PSP_IODriver_Location_t *Location, uint32 CommandCode, + CFE_PSP_IODriver_Arg_t Arg) +{ + int32 Result; + osal_id_t MutexId; + CFE_PSP_IODriver_API_t *API; + + API = CFE_PSP_IODriver_GetAPI(Location->PspModuleId); + if (API->DeviceCommand != NULL) + { + if (API->DeviceMutex != NULL) + { + MutexId = + CFE_PSP_IODriver_GetMutex(Location->PspModuleId, API->DeviceMutex(CommandCode, Location->SubsystemId, + Location->SubchannelId, Arg)); + } + else + { + MutexId = OS_OBJECT_ID_UNDEFINED; + } + if (OS_ObjectIdDefined(MutexId)) + { + OS_MutSemTake(MutexId); + } + Result = API->DeviceCommand(CommandCode, Location->SubsystemId, Location->SubchannelId, Arg); + if (OS_ObjectIdDefined(MutexId)) + { + OS_MutSemGive(MutexId); + } + } + else + { + /* No command function defined - this is a driver implementation error */ + Result = CFE_PSP_ERROR_NOT_IMPLEMENTED; + } + + return Result; +} + +int32 CFE_PSP_IODriver_FindByName(const char *DriverName, uint32 *PspModuleId) +{ + int32 Result; + CFE_PSP_ModuleApi_t *API; + + Result = CFE_PSP_Module_FindByName(DriverName, PspModuleId); + if (Result == CFE_PSP_SUCCESS) + { + /* + * Check that the module is actually registered as a device driver, + * if not then return CFE_PSP_INVALID_MODULE_NAME instead of SUCCESS + */ + Result = CFE_PSP_Module_GetAPIEntry(*PspModuleId, &API); + if (Result == CFE_PSP_SUCCESS && API->ModuleType != CFE_PSP_MODULE_TYPE_DEVICEDRIVER) + { + Result = CFE_PSP_INVALID_MODULE_NAME; + } + } + + return Result; +} diff --git a/fsw/modules/iodriver/ut-stubs/CMakeLists.txt b/fsw/modules/iodriver/ut-stubs/CMakeLists.txt new file mode 100644 index 00000000..a21deacb --- /dev/null +++ b/fsw/modules/iodriver/ut-stubs/CMakeLists.txt @@ -0,0 +1,15 @@ +add_cfe_coverage_stubs(iodriver + iodriver_base_stubs.c + iodriver_impl_stubs.c +) + +#target_compile_definitions(coverage-iodriver-stubs PRIVATE +# _CFE_PSP_MODULE_ +#) +#target_include_directories(coverage-iodriver-stubs PUBLIC +# $ +#) +#target_link_libraries(coverage-iodriver-stubs PRIVATE +# psp_module_api +# ut_assert +#) diff --git a/fsw/modules/iodriver/ut-stubs/iodriver_base_stubs.c b/fsw/modules/iodriver/ut-stubs/iodriver_base_stubs.c new file mode 100644 index 00000000..80c4e51d --- /dev/null +++ b/fsw/modules/iodriver/ut-stubs/iodriver_base_stubs.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * @file + * + * Auto-Generated stub implementations for functions defined in iodriver_base header + */ + +#include "iodriver_base.h" +#include "utgenstub.h" + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_PSP_IODriver_Command() + * ---------------------------------------------------- + */ +int32 CFE_PSP_IODriver_Command(const CFE_PSP_IODriver_Location_t *Location, uint32 CommandCode, + CFE_PSP_IODriver_Arg_t Arg) +{ + UT_GenStub_SetupReturnBuffer(CFE_PSP_IODriver_Command, int32); + + UT_GenStub_AddParam(CFE_PSP_IODriver_Command, const CFE_PSP_IODriver_Location_t *, Location); + UT_GenStub_AddParam(CFE_PSP_IODriver_Command, uint32, CommandCode); + UT_GenStub_AddParam(CFE_PSP_IODriver_Command, CFE_PSP_IODriver_Arg_t, Arg); + + UT_GenStub_Execute(CFE_PSP_IODriver_Command, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_PSP_IODriver_Command, int32); +} + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_PSP_IODriver_FindByName() + * ---------------------------------------------------- + */ +int32 CFE_PSP_IODriver_FindByName(const char *DriverName, uint32 *PspModuleId) +{ + UT_GenStub_SetupReturnBuffer(CFE_PSP_IODriver_FindByName, int32); + + UT_GenStub_AddParam(CFE_PSP_IODriver_FindByName, const char *, DriverName); + UT_GenStub_AddParam(CFE_PSP_IODriver_FindByName, uint32 *, PspModuleId); + + UT_GenStub_Execute(CFE_PSP_IODriver_FindByName, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_PSP_IODriver_FindByName, int32); +} diff --git a/fsw/modules/iodriver/ut-stubs/iodriver_impl_stubs.c b/fsw/modules/iodriver/ut-stubs/iodriver_impl_stubs.c new file mode 100644 index 00000000..89100aa1 --- /dev/null +++ b/fsw/modules/iodriver/ut-stubs/iodriver_impl_stubs.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015, United States government as represented by the + * administrator of the National Aeronautics Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + */ + +/** + * @file + * + * Auto-Generated stub implementations for functions defined in iodriver_impl header + */ + +#include "iodriver_impl.h" +#include "utgenstub.h" + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_PSP_IODriver_GetMutex() + * ---------------------------------------------------- + */ +osal_id_t CFE_PSP_IODriver_GetMutex(uint32 PspModuleId, int32 DeviceHash) +{ + UT_GenStub_SetupReturnBuffer(CFE_PSP_IODriver_GetMutex, osal_id_t); + + UT_GenStub_AddParam(CFE_PSP_IODriver_GetMutex, uint32, PspModuleId); + UT_GenStub_AddParam(CFE_PSP_IODriver_GetMutex, int32, DeviceHash); + + UT_GenStub_Execute(CFE_PSP_IODriver_GetMutex, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_PSP_IODriver_GetMutex, osal_id_t); +} + +/* + * ---------------------------------------------------- + * Generated stub function for CFE_PSP_IODriver_HashMutex() + * ---------------------------------------------------- + */ +int32 CFE_PSP_IODriver_HashMutex(int32 StartHash, int32 Datum) +{ + UT_GenStub_SetupReturnBuffer(CFE_PSP_IODriver_HashMutex, int32); + + UT_GenStub_AddParam(CFE_PSP_IODriver_HashMutex, int32, StartHash); + UT_GenStub_AddParam(CFE_PSP_IODriver_HashMutex, int32, Datum); + + UT_GenStub_Execute(CFE_PSP_IODriver_HashMutex, Basic, NULL); + + return UT_GenStub_GetReturnValue(CFE_PSP_IODriver_HashMutex, int32); +} diff --git a/fsw/modules/linux_sysmon/CMakeLists.txt b/fsw/modules/linux_sysmon/CMakeLists.txt new file mode 100644 index 00000000..c3ddd4a5 --- /dev/null +++ b/fsw/modules/linux_sysmon/CMakeLists.txt @@ -0,0 +1,4 @@ + +# Pseudo-terminal interface module +add_psp_module(linux_sysmon linux_sysmon.c) +target_include_directories(linux_sysmon PRIVATE $) diff --git a/fsw/modules/linux_sysmon/linux_sysmon.c b/fsw/modules/linux_sysmon/linux_sysmon.c new file mode 100644 index 00000000..05c2246d --- /dev/null +++ b/fsw/modules/linux_sysmon/linux_sysmon.c @@ -0,0 +1,590 @@ +/*********************************************************************** + * Copyright (c) 2017, United States government as represented by the + * administrator of the National Aeronautics and Space Administration. + * All rights reserved. This software was created at NASA Glenn + * Research Center pursuant to government contracts. + * + * \file linux_sysmon.c + * + ***********************************************************************/ + +/* + * NOTE: This relies on the Linux Kernel sched stats via the /proc filesystem. + * Documented here: https://docs.kernel.org/scheduler/sched-stats.html + */ + +/************************************************************************ + * Includes + ************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cfe_psp.h" +#include "cfe_psp_module.h" +#include "osapi-clock.h" + +#include "iodriver_impl.h" +#include "iodriver_analog_io.h" + +/******************************************************************** + * Local Defines + ********************************************************************/ + +#define LINUX_SYSMON_AGGREGATE_SUBSYS 0 +#define LINUX_SYSMON_CPULOAD_SUBSYS 1 +#define LINUX_SYSMON_AGGR_CPULOAD_SUBCH 0 +#define LINUX_SYSMON_MAX_CPUS 128 +#define LINUX_SYSMON_SAMPLE_DELAY 30 + +#define LINUX_SYSMON_DEBUG(...) OS_printf(__VA_ARGS__) + +/******************************************************************** + * Local Type Definitions + ********************************************************************/ + +typedef struct linux_sysmon_cpuload_core +{ + CFE_PSP_IODriver_AdcCode_t avg_load; + unsigned long last_run_time; +} linux_sysmon_cpuload_core_t; + +typedef struct linux_sysmon_cpuload_state +{ + volatile bool is_running; + volatile bool should_run; + + uint8_t num_cpus; + pthread_t task_id; + int dev_fd; + uint32_t num_samples; + uint64_t last_sample_time; + + linux_sysmon_cpuload_core_t per_core[LINUX_SYSMON_MAX_CPUS]; +} linux_sysmon_cpuload_state_t; + +typedef struct linux_sysmon_state +{ + uint32_t local_module_id; + linux_sysmon_cpuload_state_t cpu_load; +} linux_sysmon_state_t; + +/******************************************************************** + * Local Function Prototypes + ********************************************************************/ + +static void * linux_sysmon_Task(void *arg); +static int32_t linux_sysmon_Start(linux_sysmon_cpuload_state_t *state); +static int32_t linux_sysmon_Stop(linux_sysmon_cpuload_state_t *state); +static void linux_sysmon_Init(uint32_t local_module_id); + +/* Function that starts up linux_sysmon driver. */ +static int32_t linux_sysmon_DevCmd(uint32_t CommandCode, uint16_t SubsystemId, uint16_t SubchannelId, + CFE_PSP_IODriver_Arg_t Arg); + +/******************************************************************** + * Global Data + ********************************************************************/ + +/* linux_sysmon device command that is called by iodriver to start up linux_sysmon */ +CFE_PSP_IODriver_API_t linux_sysmon_DevApi = {.DeviceCommand = linux_sysmon_DevCmd}; + +CFE_PSP_MODULE_DECLARE_IODEVICEDRIVER(linux_sysmon); + +static linux_sysmon_state_t linux_sysmon_global; + +static const char *linux_sysmon_subsystem_names[] = { "aggregate", "per-cpu", NULL }; +static const char *linux_sysmon_subchannel_names[] = { "cpu-load", NULL }; + + + +/*********************************************************************** + * Global Functions + ********************************************************************/ + +void linux_sysmon_Init(uint32_t local_module_id) +{ + memset(&linux_sysmon_global, 0, sizeof(linux_sysmon_global)); + + linux_sysmon_global.local_module_id = local_module_id; +} + +void linux_sysmon_read_cpuuse_line(const char *line_data, unsigned int *cpu_num, unsigned long *run_time) +{ + unsigned long value; + const char * val_end; + int val_count; + + /* each "cpu" line contains the cpu number followed by 9 values */ + for (val_count = 0; val_count < 10 && *line_data != 0; ++val_count) + { + while (isspace((unsigned char)*line_data)) + { + ++line_data; + } + value = strtoul(line_data, (char **)&val_end, 10); + if (val_end == line_data) + { + /* not a number, something went wrong */ + break; + } + + line_data = val_end; + + switch (val_count) + { + case 0: /* this is the cpu number */ + *cpu_num = value; + break; + + case 7: /* this is the number of nanoseconds spent executing instructions on this CPU */ + *run_time = value; + break; + + default: /* don't care about this one */ + break; + } + } +} + +void linux_sysmon_update_schedstat(linux_sysmon_cpuload_state_t *state, int elapsed_ms) +{ + unsigned int cpu_time_ms; + unsigned int cpu_num; + unsigned int highest_cpu_num; + unsigned long run_time; + char line_data[256]; + size_t line_size; + ssize_t line_rdsz; + char * eol_p; + + linux_sysmon_cpuload_core_t *core_p; + + line_size = 0; + highest_cpu_num = 0; + + /* Reset to beginning of file to re-read it */ + lseek(state->dev_fd, 0, SEEK_SET); + + while (true) + { + line_rdsz = read(state->dev_fd, &line_data[line_size], sizeof(line_data) - line_size); + if (line_rdsz <= 0) + { + /* error (not expected) or EOF, stop reading */ + break; + } + + /* check for newline char */ + eol_p = memchr(&line_data[line_size], '\n', line_rdsz); + line_size += line_rdsz; + + while (eol_p != NULL) + { + *eol_p = 0; + ++eol_p; + + if (strncmp("cpu", line_data, 3) == 0) + { + linux_sysmon_read_cpuuse_line(&line_data[3], &cpu_num, &run_time); + + if (cpu_num < LINUX_SYSMON_MAX_CPUS) + { + core_p = &state->per_core[cpu_num]; + if (cpu_num > highest_cpu_num) + { + highest_cpu_num = cpu_num; + } + } + else + { + core_p = NULL; + } + + if (core_p != NULL) + { + cpu_time_ms = + OS_TimeGetTotalMilliseconds(OS_TimeFromTotalNanoseconds(run_time - core_p->last_run_time)); + core_p->last_run_time = run_time; + if (cpu_time_ms >= elapsed_ms) + { + core_p->avg_load = 0xFFFFFF; /* max */ + } + else if (elapsed_ms == 0) + { + core_p->avg_load = 0; + } + else + { + core_p->avg_load = (0x1000 * cpu_time_ms) / elapsed_ms; + core_p->avg_load |= (core_p->avg_load << 12); /* Expand from 12->24 bit */ + } + LINUX_SYSMON_DEBUG("CFE_PSP(linux_sysmon): CPU%u time_ms=%u ms, load=%06x\n", cpu_num, cpu_time_ms, + (unsigned int)core_p->avg_load); + } + } + + line_rdsz = eol_p - &line_data[0]; + if (line_rdsz < line_size) + { + memmove(line_data, eol_p, line_size - line_rdsz); + line_size -= line_rdsz; + } + else + { + line_size = 0; + } + + eol_p = memchr(line_data, '\n', line_size); + } + + if (line_size >= sizeof(line_data)) + { + /* not supposed to happen, drop data */ + OS_printf("CFE_PSP(linux_sysmon): malformed data from /proc/schedstat\n"); + break; + } + + } + + state->num_cpus = 1 + highest_cpu_num; +} + +void *linux_sysmon_Task(void *arg) +{ + linux_sysmon_cpuload_state_t *state = arg; + + OS_time_t last_sample; + OS_time_t curr_sample; + OS_time_t next_sample; + int msec_diff; + struct pollfd pfd; + + CFE_PSP_GetTime(&next_sample); + curr_sample = next_sample; + memset(&pfd, 0, sizeof(pfd)); + + linux_sysmon_update_schedstat(state, 0); + + while (state->should_run) + { + next_sample = OS_TimeAdd(next_sample, OS_TimeFromTotalSeconds(LINUX_SYSMON_SAMPLE_DELAY)); + msec_diff = OS_TimeGetTotalMilliseconds(OS_TimeSubtract(next_sample, curr_sample)); + if (msec_diff > 0) + { + poll(&pfd, 0, msec_diff); + } + + last_sample = curr_sample; + CFE_PSP_GetTime(&curr_sample); + msec_diff = OS_TimeGetTotalMilliseconds(OS_TimeSubtract(curr_sample, last_sample)); + linux_sysmon_update_schedstat(state, msec_diff); + } + + return NULL; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * linux_sysmon_Start() + * ------------------------------------------------------ + * Starts the cpu load watcher function + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +int32_t linux_sysmon_Start(linux_sysmon_cpuload_state_t *state) +{ + int32_t StatusCode; + int32_t DelayCount; + + DelayCount = 0; + if (state->is_running) + { + /* already running, nothing to do */ + StatusCode = CFE_PSP_SUCCESS; + } + else + { + /* start clean */ + memset(state, 0, sizeof(*state)); + StatusCode = CFE_PSP_ERROR; + + state->dev_fd = open("/proc/schedstat", O_RDONLY); + if (state->dev_fd < 0) + { + perror("open(/proc/schedstat)"); + } + else + { + state->should_run = true; + if (pthread_create(&state->task_id, NULL, linux_sysmon_Task, state) < 0) + { + perror("pthread_create()"); + + /* Clean up */ + state->should_run = false; + close(state->dev_fd); + } + else + { + /* wait for the "num_cpus" to become nonzero, this should be + * initialized in the first sample taken by the worker task */ + while (state->num_cpus == 0 && DelayCount < 100000000 /*jphfix*/) + { + ++DelayCount; + OS_TaskDelay(10); + } + + if (state->num_cpus == 0) + { + OS_printf("CFE_PSP(Linux_SysMon): Failed to detect number of CPUs\n"); + + /* Clean up */ + state->should_run = false; + pthread_cancel(state->task_id); + pthread_join(state->task_id, NULL); + close(state->dev_fd); + } + else + { + OS_printf("CFE_PSP(Linux_SysMon): Started CPU utilization monitoring on %u CPU(s)\n", (unsigned int)state->num_cpus); + + StatusCode = CFE_PSP_SUCCESS; + state->is_running = true; + } + } + } + } + + return StatusCode; +} + +int32_t linux_sysmon_Stop(linux_sysmon_cpuload_state_t *state) +{ + if (state->is_running) + { + state->should_run = false; + state->is_running = false; + pthread_cancel(state->task_id); + pthread_join(state->task_id, NULL); + close(state->dev_fd); + } + + return CFE_PSP_SUCCESS; +} + +int32_t linux_sysmon_calc_aggregate_cpu(linux_sysmon_cpuload_state_t *state, CFE_PSP_IODriver_AdcCode_t *Val) +{ + uint8_t cpu; + uint32_t sum; + + sum = 0; + for (cpu=0; cpu < state->num_cpus; ++cpu) + { + sum += state->per_core[cpu].avg_load; + } + + if (cpu == 0) + { + *Val = 0; + return CFE_PSP_ERROR; + } + + /* average of all cpus */ + sum /= cpu; + LINUX_SYSMON_DEBUG("CFE_PSP(linux_sysmon): Aggregate CPU load=%06x\n", (unsigned int)sum); + *Val = sum; + + return CFE_PSP_SUCCESS; +} + +int32_t linux_sysmon_aggregate_dispatch(uint32_t CommandCode, uint16_t Subchannel, CFE_PSP_IODriver_Arg_t Arg) +{ + int32_t StatusCode; + linux_sysmon_cpuload_state_t *state; + + /* There is just one global cpuload object */ + state = &linux_sysmon_global.cpu_load; + switch (CommandCode) + { + case CFE_PSP_IODriver_NOOP: + case CFE_PSP_IODriver_ANALOG_IO_NOOP: + { + /* NO-OP should return success - + * This is a required opcode as "generic" clients may use it to + * determine if a certain set of opcodes are supported or not + */ + StatusCode = CFE_PSP_SUCCESS; + break; + } + /* Start/stop opcodes */ + case CFE_PSP_IODriver_SET_RUNNING: /**< int32_t argument, 0=stop 1=start device */ + { + if (Arg.U32) + { + StatusCode = linux_sysmon_Start(state); + } + else + { + StatusCode = linux_sysmon_Stop(state); + } + break; + } + case CFE_PSP_IODriver_GET_RUNNING: /**< no argument, returns positive nonzero (true) if running and zero (false) + if stopped, negative on error */ + { + StatusCode = state->is_running; + break; + } + case CFE_PSP_IODriver_SET_CONFIGURATION: /**< const string argument (device-dependent content) */ + case CFE_PSP_IODriver_GET_CONFIGURATION: /**< void * argument (device-dependent content) */ + { + /* not implemented for now */ + break; + } + case CFE_PSP_IODriver_LOOKUP_SUBSYSTEM: /**< const char * argument, looks up name and returns positive + value for subsystem number, negative value for error */ + { + uint16_t i; + + for (i=0; linux_sysmon_subsystem_names[i] != NULL; ++i) + { + if (strcmp(Arg.ConstStr, linux_sysmon_subsystem_names[i]) == 0) + { + StatusCode = i; + break; + } + } + + break; + } + case CFE_PSP_IODriver_LOOKUP_SUBCHANNEL: /**< const char * argument, looks up name and returns positive + value for channel number, negative value for error */ + { + uint16_t i; + + for (i=0; linux_sysmon_subchannel_names[i] != NULL; ++i) + { + if (strcmp(Arg.ConstStr, linux_sysmon_subchannel_names[i]) == 0) + { + StatusCode = i; + break; + } + } + + break; + } + case CFE_PSP_IODriver_QUERY_DIRECTION: /**< CFE_PSP_IODriver_Direction_t argument */ + { + CFE_PSP_IODriver_Direction_t *DirPtr = (CFE_PSP_IODriver_Direction_t *)Arg.Vptr; + if (DirPtr != NULL) + { + *DirPtr = CFE_PSP_IODriver_Direction_INPUT_ONLY; + StatusCode = CFE_PSP_SUCCESS; + } + break; + } + case CFE_PSP_IODriver_ANALOG_IO_READ_CHANNELS: + { + CFE_PSP_IODriver_AnalogRdWr_t *RdWr = Arg.Vptr; + + if (RdWr->NumChannels == 1 && Subchannel == LINUX_SYSMON_AGGR_CPULOAD_SUBCH) + { + StatusCode = linux_sysmon_calc_aggregate_cpu(state, RdWr->Samples); + } + break; + } + default: + break; + } + + return StatusCode; +} + +int32_t linux_sysmon_cpu_load_dispatch(uint32_t CommandCode, uint16_t Subchannel, CFE_PSP_IODriver_Arg_t Arg) +{ + int32_t StatusCode; + linux_sysmon_cpuload_state_t *state; + + /* There is just one global cpuload object */ + state = &linux_sysmon_global.cpu_load; + switch (CommandCode) + { + case CFE_PSP_IODriver_NOOP: + case CFE_PSP_IODriver_ANALOG_IO_NOOP: + { + /* NO-OP should return success - + * This is a required opcode as "generic" clients may use it to + * determine if a certain set of opcodes are supported or not + */ + StatusCode = CFE_PSP_SUCCESS; + break; + } + case CFE_PSP_IODriver_ANALOG_IO_READ_CHANNELS: + { + CFE_PSP_IODriver_AnalogRdWr_t *RdWr = Arg.Vptr; + uint16_t ch; + + if (Subchannel < state->num_cpus && (Subchannel + RdWr->NumChannels) <= state->num_cpus) + { + for (ch = Subchannel; ch < (Subchannel + RdWr->NumChannels); ++ch) + { + RdWr->Samples[ch] = state->per_core[ch].avg_load; + } + } + break; + } + default: + break; + } + + return StatusCode; +} + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* linux_sysmonDevCmd() */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/** + ** \brief This function is called by IO_Driver to run the linux_sysmon module. + ** + ** \par Description + ** This function is called by IO_Driver to run the linux_sysmon module. + ** \par Assumptions, External Events, and Notes: + ** None + ** + ** \param[in] CommandCode The CFE_PSP_IODriver_xxx command. + ** \param[in] Instance Board instance. Set to 1 if there is only one serial card. + ** \param[in] SubChannel Subchannel number. Set to 0 for devices that do not have multiple channels. + ** \param[in] Arg The arguments for the corresponding command. + ** + ** \returns + ** \retcode #CFE_SUCCESS \retdesc \copydoc CFE_SUCCESS \endcode + ** \retcode #CFE_ES_APP_ERROR \retdesc \copydoc CFE_ES_APP_ERROR \endcode + ** \endreturns + ** + **/ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +int32_t linux_sysmon_DevCmd(uint32_t CommandCode, uint16_t SubsystemId, uint16_t SubchannelId, CFE_PSP_IODriver_Arg_t Arg) +{ + int32_t StatusCode; + + switch(SubsystemId) + { + case LINUX_SYSMON_AGGREGATE_SUBSYS: + StatusCode = linux_sysmon_aggregate_dispatch(CommandCode, SubchannelId, Arg); + break; + case LINUX_SYSMON_CPULOAD_SUBSYS: + StatusCode = linux_sysmon_cpu_load_dispatch(CommandCode, SubchannelId, Arg); + break; + default: + /* not implemented */ + StatusCode = CFE_PSP_ERROR_NOT_IMPLEMENTED; + break; + } + + return StatusCode; +} diff --git a/fsw/pc-linux/psp_module_list.cmake b/fsw/pc-linux/psp_module_list.cmake index ad98b339..557e2886 100644 --- a/fsw/pc-linux/psp_module_list.cmake +++ b/fsw/pc-linux/psp_module_list.cmake @@ -6,3 +6,5 @@ timebase_posix_clock eeprom_mmap_file ram_notimpl port_notimpl +iodriver +linux_sysmon diff --git a/fsw/shared/inc/cfe_psp_module.h b/fsw/shared/inc/cfe_psp_module.h index 581f465d..55968354 100644 --- a/fsw/shared/inc/cfe_psp_module.h +++ b/fsw/shared/inc/cfe_psp_module.h @@ -34,7 +34,8 @@ typedef enum { CFE_PSP_MODULE_TYPE_INVALID = 0, - CFE_PSP_MODULE_TYPE_SIMPLE + CFE_PSP_MODULE_TYPE_SIMPLE, + CFE_PSP_MODULE_TYPE_DEVICEDRIVER, /* May be extended in the future */ } CFE_PSP_ModuleType_t; @@ -52,6 +53,7 @@ typedef const struct uint32 OperationFlags; CFE_PSP_ModuleInitFunc_t Init; /* More API calls may be added for other module types */ + const void *ExtendedApi; } CFE_PSP_ModuleApi_t; /**