diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 90ceb77e4..366721455 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -19,6 +19,26 @@
-->
+
Type-safe Device and Driver Support Tables
+
+Type-safe versions of the device and driver support structures dset
+and drvet have been added to the devSup.h and drvSup.h headers
+respectively. The original structure definitions have not been changed so
+existing support modules will still build normally, but older modules can be
+modified and new code written to be compatible with both.
+
+The old structure definitions will be replaced by the new ones if the macros
+USE_TYPED_DSET and/or USE_TYPED_DRVET are defined when the
+appropriate header is included. The best place to define these is in the
+Makefile, as with the USE_TYPED_RSET macro that was introduced in
+Base-3.16.1 and described below. See the comments in devSup.h for a brief usage
+example, or look at
+this commit to the ipac module to see a module conversion.
+
+A helper function DBLINK* dbGetDevLink(dbCommon *prec) has also been
+added to devSup.h which fetches a pointer to the INP or OUT field of the
+record.
+
RTEMS build configuration update, running tests under QEMU
This release includes the ability to run the EPICS unit tests built for a
diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c
index e255fff55..143c427da 100644
--- a/src/ioc/db/dbAccess.c
+++ b/src/ioc/db/dbAccess.c
@@ -702,6 +702,18 @@ void dbInitEntryFromRecord(struct dbCommon *prec, DBENTRY *pdbentry)
pdbentry->precnode = ppvt->recnode;
}
+struct link* dbGetDevLink(struct dbCommon* prec)
+{
+ DBLINK *plink = 0;
+ DBENTRY entry;
+ dbInitEntryFromRecord(prec, &entry);
+ if(dbFindField(&entry, "INP")==0 || dbFindField(&entry, "OUT")==0) {
+ plink = (DBLINK*)entry.pfield;
+ }
+ dbFinishEntry(&entry);
+ return plink;
+}
+
long dbValueSize(short dbr_type)
{
/* sizes for value associated with each DBR request type */
diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h
index 028d09ec8..d483a0c30 100644
--- a/src/ioc/db/dbScan.h
+++ b/src/ioc/db/dbScan.h
@@ -19,6 +19,7 @@
#include "menuScan.h"
#include "shareLib.h"
#include "compilerDependencies.h"
+#include "devSup.h"
#ifdef __cplusplus
extern "C" {
@@ -33,9 +34,7 @@ extern "C" {
#define MIN_PHASE SHRT_MIN
/*definitions for I/O Interrupt Scanning */
-struct ioscan_head;
-
-typedef struct ioscan_head *IOSCANPVT;
+/* IOSCANPVT now defined in devSup.h */
typedef struct event_list *EVENTPVT;
struct dbCommon;
diff --git a/src/ioc/dbStatic/devSup.h b/src/ioc/dbStatic/devSup.h
index bd900cae4..73dba198c 100644
--- a/src/ioc/dbStatic/devSup.h
+++ b/src/ioc/dbStatic/devSup.h
@@ -6,7 +6,11 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* devSup.h Device Support */
+/** @file devSup.h
+ *
+ * @brief Device support routines
+ */
+
/*
* Author: Marty Kraimer
* Date: 6-1-90
@@ -21,6 +25,111 @@
/* structures defined elsewhere */
struct dbCommon;
struct devSup;
+typedef struct ioscan_head *IOSCANPVT;
+struct link; /* aka DBLINK */
+
+/** Type safe version of 'struct dset'
+ *
+ * Recommended usage:
+ *
+ * In Makefile:
+ @code
+ USR_CFLAGS += -DUSE_TYPED_RSET -DUSE_TYPED_DSET
+ @endcode
+ *
+ * In C source file:
+ @code
+ #include
+ #include // For IOCSCANPVT
+ ...
+ #include // defines epicsExportSharedSymbols
+ ...
+ static long init_record(dbCommon *prec);
+ static long get_iointr_info(int detach, dbCommon *prec, IOCSCANPVT* pscan);
+ static long longin_read(longinRecord *prec);
+
+ const struct {
+ dset common;
+ long (*read)(longinRecord *prec);
+ } devLiDevName = {
+ {
+ 5, // 4 from dset + 1 from longinRecord
+ NULL,
+ NULL,
+ &init_record,
+ &get_iointr_info
+ },
+ &longin_read
+ };
+ epicsExportAddress(dset, devLiDevName);
+ @endcode
+ */
+typedef struct typed_dset {
+ /** Number of function pointers which follow.
+ * The value depends on the recordtype, but must be >=4 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called twice during iocInit().
+ * First with @a after = 0 before init_record() or array field allocation.
+ * Again with @a after = 1 after init_record() has finished.
+ */
+ long (*init)(int after);
+ /** Called once per record instance */
+ long (*init_record)(struct dbCommon *prec);
+ /** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
+ *
+ * Caller must assign the third arguement (IOCSCANPVT*). eg.
+ @code
+ struct mpvt {
+ IOSCANPVT drvlist;
+ };
+ ...
+ // init_record() routine calls
+ scanIoInit(&pvt->drvlist);
+ ...
+ static long get_ioint_info(int detach, struct dbCommon *prec, IOCSCANPVT* pscan) {
+ if(prec->dpvt)
+ *pscan = &((mypvt*)prec->dpvt)->drvlist;
+ @endcode
+ *
+ * When a particular record instance can/will only used a single scan list,
+ * the @a detach argument can be ignored.
+ *
+ * If this is not the case, then the following should be noted.
+ * + get_ioint_info() is called with @a detach = 0 to fetch the scan list to
+ * which this record will be added.
+ * + get_ioint_info() is called later with @a detach = 1 to fetch the scan
+ * list from which this record should be removed.
+ * + Calls will be balanced, so a call with @a detach = 0 will be followed
+ * by one with @a detach = 1.
+ *
+ * @note get_ioint_info() will be called during IOC shutdown if the
+ * dsxt::del_record() extended callback is defined. (from 3.15.0.1)
+ */
+ long (*get_ioint_info)(int detach, struct dbCommon *prec, IOSCANPVT* pscan);
+ /* Any further functions are specified by the record type. */
+} typed_dset;
+
+/** Device support extension table.
+ *
+ * Optional routines to allow run-time address modifications to be communicated
+ * to device support, which must register a struct dsxt by calling devExtend()
+ * from its init() routine.
+ */
+typedef struct dsxt {
+ /** Optional, called to offer device support a new record to control.
+ *
+ * Routine may return a non-zero error code to refuse record.
+ */
+ long (*add_record)(struct dbCommon *precord);
+ /** Optional, called to remove record from device support control.
+ *
+ * Routine return a non-zero error code to refuse record removal.
+ */
+ long (*del_record)(struct dbCommon *precord);
+ /* Only future Base releases may extend this table. */
+} dsxt;
#ifdef __cplusplus
extern "C" {
@@ -29,6 +138,8 @@ extern "C" {
typedef long (*DEVSUPFUN)(); /* ptr to device support function*/
#endif
+#ifndef USE_TYPED_DSET
+
typedef struct dset { /* device support entry table */
long number; /*number of support routines*/
DEVSUPFUN report; /*print report*/
@@ -38,11 +149,15 @@ typedef struct dset { /* device support entry table */
/*other functions are record dependent*/
} dset;
-typedef struct dsxt { /* device support extension table */
- long (*add_record)(struct dbCommon *precord);
- long (*del_record)(struct dbCommon *precord);
- /* Recordtypes are *not* allowed to extend this table */
-} dsxt;
+#else
+typedef typed_dset dset;
+#endif /* USE_TYPED_DSET */
+
+/** Fetch INP or OUT link (or NULL if record type has neither).
+ *
+ * Recommended for use in device support init_record()
+ */
+epicsShareFunc struct link* dbGetDevLink(struct dbCommon* prec);
epicsShareExtern dsxt devSoft_DSXT; /* Allow anything table */
diff --git a/src/ioc/dbStatic/drvSup.h b/src/ioc/dbStatic/drvSup.h
index 5778038e7..193d57482 100644
--- a/src/ioc/dbStatic/drvSup.h
+++ b/src/ioc/dbStatic/drvSup.h
@@ -6,7 +6,10 @@
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/* drvSup.h Driver Support */
+/** @file drvSup.h
+ *
+ * @brief Driver support routines.
+ */
/*
* Author: Marty Kraimer
@@ -18,16 +21,38 @@
#include "errMdef.h"
-typedef long (*DRVSUPFUN) (); /* ptr to driver support function*/
+/** Driver entry table */
+typedef struct typed_drvet {
+ /** Number of function pointers which follow. Must be >=2 */
+ long number;
+ /** Called from dbior() */
+ long (*report)(int lvl);
+ /** Called during iocInit() */
+ long (*init)(void);
+ /* Any further functions are driver-specific */
+} typed_drvet;
+
+#ifdef USE_TYPED_DRVET
+
+typedef typed_drvet drvet;
+
+#else
+
+/* These interfaces may eventually get deprecated */
+
+typedef long (*DRVSUPFUN) (); /* ptr to driver support function */
+
+typedef struct drvet { /* driver entry table */
+ long number; /* number of support routines */
+ DRVSUPFUN report; /* print report */
+ DRVSUPFUN init; /* init support */
+ /* Any further functions are driver-specific */
+} drvet;
-typedef struct drvet { /* driver entry table */
- long number; /*number of support routines*/
- DRVSUPFUN report; /*print report*/
- DRVSUPFUN init; /*init support*/
- /*other functions are device dependent*/
-}drvet;
#define DRVETNUMBER ( (sizeof(struct drvet) -sizeof(long))/sizeof(DRVSUPFUN) )
+#endif /* USE_TYPED_DRVET */
+
#define S_drv_noDrvSup (M_drvSup| 1) /*SDR_DRVSUP: Driver support missing*/
#define S_drv_noDrvet (M_drvSup| 3) /*Missing driver support entry table*/
diff --git a/src/std/rec/test/linkInitTest.c b/src/std/rec/test/linkInitTest.c
index 7225beb1c..09ff40942 100644
--- a/src/std/rec/test/linkInitTest.c
+++ b/src/std/rec/test/linkInitTest.c
@@ -7,11 +7,14 @@
#include
#include "dbAccess.h"
+#include "devSup.h"
#include "alarm.h"
#include "dbUnitTest.h"
#include "errlog.h"
#include "epicsThread.h"
+#include "longinRecord.h"
+
#include "testMain.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
@@ -28,12 +31,21 @@ static void startTestIoc(const char *dbfile)
eltc(1);
}
+/* testing here instead of ioc/db/test as xRecord has no INP/OUT */
+static void testdbGetDevLink(void)
+{
+ longinRecord *rec = (longinRecord*)testdbRecordPtr("li1");
+ testOk1(dbGetDevLink((dbCommon*)rec) == &rec->inp);
+}
+
static void testLongStringInit()
{
testDiag("testLongStringInit");
startTestIoc("linkInitTest.db");
+ testdbGetDevLink();
+
{
const char buf[] = "!----------------------------------------------!";
testdbGetArrFieldEqual("longstr1.VAL$", DBF_CHAR, NELEMENTS(buf)+2, NELEMENTS(buf), buf);
@@ -230,7 +242,7 @@ void testInt64Inputs(void)
MAIN(linkInitTest)
{
- testPlan(77);
+ testPlan(78);
testLongStringInit();
testCalcInit();