diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h index 956a6ca79..23ffd7d09 100644 --- a/modules/database/src/ioc/db/dbChannel.h +++ b/modules/database/src/ioc/db/dbChannel.h @@ -13,10 +13,16 @@ #define INC_dbChannel_H /** \file dbChannel.h - * \brief Declarations for the \ref dbChannel "dbChannel" record type * - * Author: Andrew Johnson - * Ralph Lange + * \author Andrew Johnson (ANL) + * \author Ralph Lange (BESSY) + * + * \brief The dbChannel API gives access to record fields. + * + * The dbChannel API is used internally by the IOC and by link types, device + * support and IOC servers (RSRV and QSRV) to access record fields, either + * directly or through one or more server-side filters as specified in the + * channel name used when creating the channel. */ #include "dbDefs.h" @@ -36,198 +42,527 @@ extern "C" { * event subscription */ typedef struct evSubscrip { - ELLNODE node; - struct dbChannel *chan; - EVENTFUNC *user_sub; - void *user_arg; - struct event_que *ev_que; - db_field_log **pLastLog; - unsigned long npend; /** \brief n times this event is on the queue */ - unsigned long nreplace; /** \brief n times replacing event on the queue */ - unsigned char select; - char useValque; - char callBackInProgress; - char enabled; + ELLNODE node; + struct dbChannel * chan; + EVENTFUNC * user_sub; + void * user_arg; + struct event_que * ev_que; + db_field_log ** pLastLog; + unsigned long npend; /**< n times this event is on the queue */ + unsigned long nreplace; /**< n times replacing event on the queue */ + unsigned char select; + char useValque; + char callBackInProgress; + char enabled; } evSubscrip; typedef struct chFilter chFilter; -/** A dbChannel points to a record field, and can have multiple filters */ +/** \brief A Database Channel object + * + * A dbChannel is created from a user-supplied channel name, and holds + * pointers to the record & field and information about any filters that + * were specified with it. The dbChannel macros defined in this header + * file should always be used to read data from a dbChannel object, the + * internal implementation may change without notice. + */ typedef struct dbChannel { - const char *name; - dbAddr addr; /** \brief address structure for record/field */ - long final_no_elements; /** \brief final number of elements (arrays) */ - short final_field_size; /** \brief final size of element */ - short final_type; /** \brief final type of database field */ - ELLLIST filters; /** \brief list of filters as created from JSON */ - ELLLIST pre_chain; /** \brief list of filters to be called pre-event-queue */ - ELLLIST post_chain; /** \brief list of filters to be called post-event-queue */ + const char *name; /**< Channel name */ + dbAddr addr; /**< Pointers to record & field */ + long final_no_elements; /**< Final number of array elements */ + short final_field_size; /**< Final size of each element */ + short final_type; /**< Final type of database field */ + ELLLIST filters; /**< Filters used by dbChannel */ + ELLLIST pre_chain; /**< Filters on pre-event-queue chain */ + ELLLIST post_chain; /**< Filters on post-event-queue chain */ } dbChannel; -/** - * Prototype for the channel event function that is called in filter stacks +/** \brief Event filter function type * - * When invoked the scan lock for the record associated with 'chan' _may_ be locked. - * Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data, - * as this indicates the data is still owned by the record. + * Prototype for channel event filter functions. * - * This function has ownership of the field log pLog, if it wishes to discard - * this update it should free the field log with db_delete_field_log() and - * then return NULL. + * When these functions are called the scan lock for the record associated + * with \p chan _may_ already be locked, but they must use dbfl_has_copy() + * to determine whether the data in \p pLog belongs to the record. If that + * returns 0 the function must call dbScanLock() before accessing the data. + * + * A filter function owns the field log \p pLog when called. To discard an + * update it should free the field log using db_delete_field_log() and + * return NULL. */ typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog); -/** Return values from chFilterIf->parse_*- routines: */ +/** \brief Result returned by chFilterIf parse routines. + * + * The parsing functions from a chFilterIf must return either \p parse_stop + * (in event of an error) or \p parse_continue. + */ typedef enum { parse_stop, parse_continue } parse_result; -/** These routines must be implemented by each filter plug-in */ -typedef struct chFilterIf { -/** - * cleanup pointer passed to ::dbRegisterFilter (). - * Called during DB shutdown +/** \brief Channel Filter Interface + * + * Routines to be implemented by each Channel Filter. */ +typedef struct chFilterIf { + /** \brief Release private filter data. + * + * Called during database shutdown to release resources allocated by + * the filter. + * \param puser The user-pointer passed into dbRegisterFilter(). + */ void (* priv_free)(void *puser); - /** Parsing event handlers: */ - parse_result (* parse_start)(chFilter *filter); - /** If parse_start() returns parse_continue for a filter, one of + + /** \name Parsing event handlers + * + * A filter that doesn't accept a particular JSON value type may use a + * \p NULL pointer to the parsing handler for that value type, which is + * equivalent to a routine that always returns \p parse_stop. + */ + + /** \brief Create new filter instance. + * + * Called when a new filter instance is requested. Filter may allocate + * resources for this instance and store in \p filter->puser. + * If parse_start() returns \p parse_continue for a filter, one of * parse_abort() or parse_end() will later be called for that same * filter. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_start)(chFilter *filter); + + /** \brief Parsing of filter instance is being cancelled. + * + * This function should release any memory allocated for the given + * \p filter instance; no further parsing handlers will be called for it. + * \param filter Pointer to instance data. */ void (* parse_abort)(chFilter *filter); - /** If parse_abort() is called it should release any memory allocated - * for this filter; no further parse_...() calls will be made; + + /** \brief Parsing of filter instance has completed successfully. + * + * The parser has reached the end of this instance and no further parsing + * handlers will be called for it. The filter must check the instance + * data and indicate whether it was complete or not. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue */ parse_result (* parse_end)(chFilter *filter); - /** If parse_end() returns parse_stop it should have released any - * memory allocated for this filter; no further parse_...() calls will - * be made in this case. + + /** \brief Parser saw \p null value. + * + * Optional. + * Null values are rarely accepted by channel filters. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue */ - parse_result (* parse_null)(chFilter *filter); - parse_result (* parse_boolean)(chFilter *filter, int boolVal); - parse_result (* parse_integer)(chFilter *filter, long integerVal); - parse_result (* parse_double)(chFilter *filter, double doubleVal); - parse_result (* parse_string)(chFilter *filter, const char *stringVal, - size_t stringLen); /** \brief NB: stringVal is not zero-terminated: */ + /** \brief Parser saw boolean value. + * + * Optional. + * \param filter Pointer to instance data. + * \param boolVal true/false Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_boolean)(chFilter *filter, int boolVal); + + /** \brief Parser saw integer value. + * + * Optional. + * \param filter Pointer to instance data. + * \param integerVal Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_integer)(chFilter *filter, long integerVal); + + /** \brief Parser saw double value. + * + * Optional. + * \param filter Pointer to instance data. + * \param doubleVal Value. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_double)(chFilter *filter, double doubleVal); + + /** \brief Parser saw string value. + * + * Optional. + * \param filter Pointer to instance data. + * \param stringVal Value, not zero-terminated. + * \param stringLen Number of chars in \p stringVal. + * \returns \p parse_stop on error, or \p parse_continue + */ + parse_result (* parse_string)(chFilter *filter, const char *stringVal, + size_t stringLen); + + /** \brief Parser saw start of a JSON map value. + * + * Optional. + * Inside a JSON map all data consists of key/value pairs. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_start_map)(chFilter *filter); + + /** \brief Parser saw a JSON map key. + * + * Optional. + * \param filter Pointer to instance data. + * \param key Value not zero-terminated. + * \param stringLen Number of chars in \p key + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_map_key)(chFilter *filter, const char *key, - size_t stringLen); /** \brief NB: key is not zero-terminated: */ + size_t stringLen); + + /** \brief Parser saw end of a JSON map value. + * + * Optional. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_end_map)(chFilter *filter); + /** \brief Parser saw start of a JSON array value. + * + * Optional. + * Data inside a JSON array doesn't have to be all of the same type. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_start_array)(chFilter *filter); + + /** \brief Parser saw end of a JSON array value. + * + * Optional. + * \param filter Pointer to instance data. + * \returns \p parse_stop on error, or \p parse_continue + */ parse_result (* parse_end_array)(chFilter *filter); - /** Channel operations: */ + /** \name Channel operations */ + + /** \brief Open filter on channel. + * + * Optional, initialize instance. + * \param filter Pointer to instance data. + * \returns 0, or an error status value. + */ long (* channel_open)(chFilter *filter); - void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); - void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); - void (* channel_report)(chFilter *filter, int level, const unsigned short indent); + + /** \brief Get pre-chain filter function. + * + * Optional. + * Returns pre-chain filter function and context. + * \param[in] filter Pointer to instance data. + * \param[out] cb_out Write filter function pointer here. + * \param[out] arg_out Write private data pointer here. + * \param[in,out] probe db_field_log with metadata for adjusting. + */ + void (* channel_register_pre) (chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + + /** \brief Get post-chain filter function. + * + * Optional, return post-chain filter function and context. + * \param[in] filter Pointer to instance data. + * \param[out] cb_out Write filter function pointer here. + * \param[out] arg_out Write private data pointer here. + * \param[in,out] probe db_field_log with metadata for adjusting. + */ + void (* channel_register_post)(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + + /** \brief Print information about filter to stdout. + * + * Optional. + * \param filter Pointer to instance data. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ + void (* channel_report)(chFilter *filter, + int level, const unsigned short indent); + + /** \brief Close filter. + * + * Optional, releases resources allocated for this instance. + * \param filter Pointer to instance data. + */ void (* channel_close)(chFilter *filter); } chFilterIf; -/** A chFilterPlugin holds data for a filter plugin */ +/** \brief Filter plugin data + * + * A chFilterPlugin object holds data about a filter plugin. + */ typedef struct chFilterPlugin { - ELLNODE node; - const char *name; - const chFilterIf *fif; - void *puser; + ELLNODE node; /**< \brief List node (dbBase->filterList) */ + const char *name; /**< \brief Filter name */ + const chFilterIf *fif; /**< \brief Filter interface routines */ + void *puser; /**< \brief For use by the plugin */ } chFilterPlugin; -/** A chFilter holds data for a single filter instance */ +/** \brief Filter instance data + * + * A chFilter holds data about a single filter instance. + */ struct chFilter { - ELLNODE list_node; - ELLNODE pre_node; - ELLNODE post_node; - dbChannel *chan; - const chFilterPlugin *plug; - chPostEventFunc *pre_func; - void *pre_arg; - chPostEventFunc *post_func; - void *post_arg; - void *puser; + ELLNODE list_node; /**< \brief List node (dbChannel->filters) */ + ELLNODE pre_node; /**< \brief List node (dbChannel->pre_chain) */ + ELLNODE post_node; /**< \brief List node (dbChannel->post_chain) */ + dbChannel *chan; /**< \brief The dbChannel we belong to */ + const chFilterPlugin *plug; /**< \brief The plugin that created us */ + chPostEventFunc *pre_func; /**< \brief pre-chain filter function */ + void *pre_arg; /**< \brief pre-chain context pointer */ + chPostEventFunc *post_func; /**< \brief post-chain filter function */ + void *post_arg; /**< \brief post-chain context pointer */ + void *puser; /**< \brief For use by the plugin */ }; struct dbCommon; struct dbFldDes; -DBCORE_API void dbChannelInit (void); +/** \brief Initialize the dbChannel subsystem. */ +DBCORE_API void dbChannelInit(void); + +/** \brief Cleanup the dbChannel subsystem. */ DBCORE_API void dbChannelExit(void); + +/** \brief Test the given PV name for existance. + * + * This routine looks up the given record and field name, but does not check + * whether any field modifiers given after the field name are correct. + * This is sufficient for the correct server to quickly direct searches to the + * IOC that owns that PV name. Field modifiers will be checked when + * dbChannelCreate() is later called with the same name. + * \param name Channel name. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelTest(const char *name); + +/** \brief Create a dbChannel object for the given PV name. + * + * \param name Channel name. + * \return Pointer to dbChannel object, or NULL if invalid. + */ DBCORE_API dbChannel * dbChannelCreate(const char *name); + +/** \brief Open a dbChannel for doing I/O. + * + * \param chan Pointer to the dbChannel object. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelOpen(dbChannel *chan); -/**Following is also defined in db_convert.h*/ +/** \brief Request (DBR) type conversion array. + * + * This converter array is declared in db_convert.h but redeclared + * here as it is needed by the dbChannel...CAType macros defined here. + */ DBCORE_API extern unsigned short dbDBRnewToDBRold[]; -/** In the following macros pChan is dbChannel* */ +/** \name dbChannel Inspection Macros */ -/** evaluates to const char* */ +/** \brief Name that defined the channel. + * + * \param pChan Pointer to the dbChannel object. + * \returns const char* + */ #define dbChannelName(pChan) ((pChan)->name) -/** evaluates to struct dbCommon* */ +/** \brief Record the channel connects to. + * + * \param pChan Pointer to the dbChannel object. + * \returns struct dbCommon* + */ #define dbChannelRecord(pChan) ((pChan)->addr.precord) -/** evaluates to struct dbFldDes* */ +/** \brief Field descriptor for the field pointed to. + * + * \param pChan Pointer to the dbChannel object. + * \returns struct dbFldDes* + */ #define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes) -/** evaluates to long */ +/** \brief Number of array elements in the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns long + */ #define dbChannelElements(pChan) ((pChan)->addr.no_elements) -/** evaluates to short */ +/** \brief Data type (DBF type) of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFieldType(pChan) ((pChan)->addr.field_type) -/** evaluates to short */ +/** \brief Request type (DBR type) of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type) -/** evaluates to short */ +/** \brief CA data type of the field. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)]) -/** evaluates to short */ +/** \brief Field (element if array) size in bytes. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFieldSize(pChan) ((pChan)->addr.field_size) -/** evaluates to long */ +/** \brief Array length after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns long + */ #define dbChannelFinalElements(pChan) ((pChan)->final_no_elements) -/** evaluates to short */ +/** \brief Data type after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFinalFieldType(pChan) ((pChan)->final_type) -/** evaluates to short */ +/** \brief Channel CA data type after filtering. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type]) -/** evaluates to short */ +/** \brief Field/element size after filtering, in bytes. + * + * \param pChan Pointer to the dbChannel object. + * \returns short */ #define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size) -/** evaluates to short */ +/** \brief Field special attribute. + * + * \param pChan Pointer to the dbChannel object. + * \returns short + */ #define dbChannelSpecial(pChan) ((pChan)->addr.special) -/** Channel filters do not get to interpose here since there are many +/** \brief Pointer to the record field. + * + * Channel filters do not get to interpose here since there are many * places where the field pointer is compared with the address of a * specific record field, so they can't modify the pointer value. + * \param pChan Pointer to the dbChannel object. + * \returns void * */ -/** evaluates to void* */ #define dbChannelField(pChan) ((pChan)->addr.pfield) +/** \name dbChannel Operation Functions */ + +/** \brief dbGet() through a dbChannel. + * + * Calls dbGet() for the field that \p chan refers to. + * Only call this routine if the record is already locked. + * \param[in] chan Pointer to the dbChannel object. + * \param[in] type Request type from dbFldTypes.h. + * \param[out] pbuffer Pointer to data buffer. + * \param[in,out] options Request options from dbAccessDefs.h. + * \param[in,out] nRequest Pointer to the element count. + * \param[in] pfl Pointer to a db_field_log or NULL. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelGet(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); + +/** \brief dbGetField() through a dbChannel. + * + * Get values from a PV through a channel. + * This routine locks the record, calls + * dbChannelGet(), then unlocks the record again. + * \param[in] chan Pointer to the dbChannel object. + * \param[in] type Request type from dbFldTypes.h. + * \param[out] pbuffer Pointer to data buffer. + * \param[in,out] options Request options from dbAccessDefs.h. + * \param[in,out] nRequest Pointer to the element count. + * \param[in] pfl Pointer to a db_field_log or NULL. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); + +/** \brief dbPut() through a dbChannel. + * + * Put values to a PV through a channel. Only call this routine if the + * record is already locked. + * Calls dbPut() for the field that \p chan refers to. + * \param chan[in] Pointer to the dbChannel object. + * \param type[in] Request type from dbFldTypes.h. + * \param pbuffer[in] Pointer to data buffer. + * \param nRequest[in] Number of elements in pbuffer. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); + +/** \brief dbPutField() through a dbChannel. + * + * Put values to a PV through a channel. + * This routine calls dbPutField() for the field that \p chan refers to. + * \param chan[in] Pointer to the dbChannel object. + * \param type[in] Request type from dbFldTypes.h. + * \param pbuffer[in] Pointer to data buffer. + * \param nRequest[in] Number of elements in pbuffer. + * \returns 0, or an error status value. + */ DBCORE_API long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); + +/** \brief Print report on a channel. + * + * Print information about the channel to stdout. + * \param chan Pointer to the dbChannel object. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ DBCORE_API void dbChannelShow(dbChannel *chan, int level, const unsigned short indent); + +/** \brief Print report on a channel's filters. + * + * Print information about the channel's filters to stdout. + * \param chan Pointer to the dbChannel object. + * \param level Higher levels may provide more detail. + * \param indent Indent all lines by this many spaces. + */ DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent); + +/** \brief Delete a channel. + * + * Releases resources owned by this channel and its filters. + * \param chan Pointer to the dbChannel object. + */ DBCORE_API void dbChannelDelete(dbChannel *chan); -DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); -DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); -DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); + +/** \name Other routines */ + +DBCORE_API void dbRegisterFilter(const char *key, + const chFilterIf *fif, void *puser); +DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, + db_field_log *pLogIn); +DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, + db_field_log *pLogIn); DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len); DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan, void **pfield, long *no_elements, long *offset); diff --git a/modules/libcom/src/iocsh/initHooks.h b/modules/libcom/src/iocsh/initHooks.h index 76d9fed4e..15faf1384 100644 --- a/modules/libcom/src/iocsh/initHooks.h +++ b/modules/libcom/src/iocsh/initHooks.h @@ -7,14 +7,46 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/** \file initHooks.h - * \brief Facility to call functions during ioc init + +/** + * \file initHooks.h * - * Authors: Benjamin Franksen (BESY) and Marty Kraimer - * Date: 06-01-91 - * major Revision: 07JuL97 + * \author Benjamin Franksen (BESSY) + * \author Marty Kraimer (ANL) + * + * \brief Facility to call functions during iocInit() + * + * The initHooks facility allows application functions to be called at various + * stages/states during IOC initialization, pausing, restart and shutdown. + * + * All registered application functions will be called whenever the IOC + * initialization, pause/resume or shutdown process reaches a new state. + * + * The following C++ example shows how to use this facility: + * + * \code{.cpp} + * static void myHookFunction(initHookState state) + * { + * switch (state) { + * case initHookAfterInitRecSup: + * ... + * break; + * case initHookAfterDatabaseRunning: + * ... + * break; + * default: + * break; + * } + * } + * + * // A static constructor registers hook function at startup: + * static int myHookStatus = initHookRegister(myHookFunction); + * \endcode + * + * An arbitrary number of functions can be registered. */ + #ifndef INC_initHooks_H #define INC_initHooks_H @@ -24,84 +56,90 @@ extern "C" { #endif -/** -* The inithooks facility allows application functions to be called at various states during ioc initialization. -* This enum must agree with the array of names defined in initHookName() \n -* - * Deprecated states, provided for backwards compatibility. - * These states are announced at the same point they were before, - * but will not be repeated if the IOC gets paused and restarted. +/** \brief Initialization stages + * + * The enum states must agree with the names in the initHookName() function. + * New states may be added in the future if extra facilities get incorporated + * into the IOC. The numerical value of any state enum may change between + * EPICS releases; states are not guaranteed to appear in numerical order. + * + * Some states were deprecated when iocPause() and iocRun() were added, but + * are still provided for backwards compatibility. These deprecated states + * are announced at the same point they were before, but will not be repeated + * if the IOC is later paused and restarted. */ typedef enum { - initHookAtIocBuild = 0, /**< \brief Start of iocBuild/iocInit commands */ - initHookAtBeginning, - initHookAfterCallbackInit, - initHookAfterCaLinkInit, - initHookAfterInitDrvSup, - initHookAfterInitRecSup, - initHookAfterInitDevSup, - initHookAfterInitDatabase, - initHookAfterFinishDevSup, - initHookAfterScanInit, - initHookAfterInitialProcess, - initHookAfterCaServerInit, - initHookAfterIocBuilt, /**< \brief End of iocBuild command */ + initHookAtIocBuild = 0, /**< Start of iocBuild() / iocInit() */ + initHookAtBeginning, /**< Database sanity checks passed */ + initHookAfterCallbackInit, /**< Callbacks, generalTime & taskwd init */ + initHookAfterCaLinkInit, /**< CA links init */ + initHookAfterInitDrvSup, /**< Driver support init */ + initHookAfterInitRecSup, /**< Record support init */ + initHookAfterInitDevSup, /**< Device support init pass 0 */ + initHookAfterInitDatabase, /**< Records and locksets init */ + initHookAfterFinishDevSup, /**< Device support init pass 1 */ + initHookAfterScanInit, /**< Scan, AS, ProcessNotify init */ + initHookAfterInitialProcess, /**< Records with PINI = YES processsed */ + initHookAfterCaServerInit, /**< RSRV init */ + initHookAfterIocBuilt, /**< End of iocBuild() */ - initHookAtIocRun, /**< \brief Start of iocRun command */ - initHookAfterDatabaseRunning, - initHookAfterCaServerRunning, - initHookAfterIocRunning, /**< \brief End of iocRun/iocInit commands */ + initHookAtIocRun, /**< Start of iocRun() */ + initHookAfterDatabaseRunning, /**< Scan tasks and CA links running */ + initHookAfterCaServerRunning, /**< RSRV running */ + initHookAfterIocRunning, /**< End of iocRun() / iocInit() */ - initHookAtIocPause, /**< \brief Start of iocPause command */ - initHookAfterCaServerPaused, - initHookAfterDatabasePaused, - initHookAfterIocPaused, /**< \brief End of iocPause command */ + initHookAtIocPause, /**< Start of iocPause() */ + initHookAfterCaServerPaused, /**< RSRV paused */ + initHookAfterDatabasePaused, /**< CA links and scan tasks paused */ + initHookAfterIocPaused, /**< End of iocPause() */ - initHookAtShutdown, /**< \brief Start of iocShutdown commands */ - initHookAfterCloseLinks, - initHookAfterStopScan, /**< \brief triggered only by unittest code. testIocShutdownOk() */ - initHookAfterStopCallback, /**< \brief triggered only by unittest code. testIocShutdownOk() */ - initHookAfterStopLinks, - initHookBeforeFree, /**< \brief triggered only by unittest code. testIocShutdownOk() */ - initHookAfterShutdown, /**< \brief End of iocShutdown commands */ - initHookAfterInterruptAccept, /**< \brief After initHookAfterDatabaseRunning */ - initHookAtEnd, /**< \brief Before initHookAfterIocRunning */ + initHookAtShutdown, /**< Start of iocShutdown() (unit tests only) */ + initHookAfterCloseLinks, /**< Links disabled/deleted */ + initHookAfterStopScan, /**< Scan tasks stopped */ + initHookAfterStopCallback, /**< Callback tasks stopped */ + initHookAfterStopLinks, /**< CA links stopped */ + initHookBeforeFree, /**< Resource cleanup about to happen */ + initHookAfterShutdown, /**< End of iocShutdown() */ + + /* Deprecated states: */ + initHookAfterInterruptAccept, /**< After initHookAfterDatabaseRunning */ + initHookAtEnd, /**< Before initHookAfterIocRunning */ } initHookState; -/** - * Any functions that are registered before iocInit reaches the desired state will be called when it reaches that state. - * The initHookName function returns a static string representation of the state passed into it which is intended for printing. The following skeleton code shows how to use this facility: +/** \brief Type for application callback functions * - * static initHookFunction myHookFunction; - * - * \code{.cpp} - * int myHookInit(void) - * { - * return(initHookRegister(myHookFunction)); - *} - * - * static void myHookFunction(initHookState state) - * { - * switch(state) { - * case initHookAfterInitRecSup: - * ... - * break; - * case initHookAfterInterruptAccept: - * ... - * break; - * default: - * break; - * } - * } - * \endcode - * An arbitrary number of functions can be registered. + * Application callback functions must match this typdef. + * \param state initHook enumeration value */ - - typedef void (*initHookFunction)(initHookState state); + +/** \brief Register a function for initHook notifications + * + * Registers \p func for initHook notifications + * \param func Pointer to application's notification function. + * \return 0 if Ok, -1 on error (memory allocation failure). + */ LIBCOM_API int initHookRegister(initHookFunction func); + +/** \brief Routine called by iocInit() to trigger notifications. + * + * Calls registered callbacks announcing \p state + * \param state initHook enumeration value + */ LIBCOM_API void initHookAnnounce(initHookState state); + +/** \brief Returns printable representation of \p state + * + * Static string representation of \p state for printing + * \param state enum value of an initHook + * \return Pointer to name string + */ LIBCOM_API const char *initHookName(int state); + +/** \brief Forget all registered application functions + * + * This cleanup routine is called by unit test programs between IOC runs. + */ LIBCOM_API void initHookFree(void); #ifdef __cplusplus