Passive: Passively scanned.
Event: Event Scanned. The field EVNT specifies event number
I/O Intr: I/O Event scanned.
10 Second: Periodically scanned - Every 10 seconds
...
.1 Second: Periodically scanned - Every .1 seconds
In general it is not a good idea to rely on PHAS to enforce processing order. It is better to use database links.
GBL_SCAN "Passive" GBL_SCAN "Event" GBL_SCAN "I/O Intr" GBL_SCAN "10 second" ... GBL_SCAN ".1 second"The first three definitions must appear first and in the order shown. The remaining definitions are for the periodic scan rates, which must appear in order of decreasing rate. At IOC initialization, the menu values are read by scan initialization. The number of periodic scan rates and the value of each rate is determined from the menu values. Thus periodic scan rates can be changed by changing choiceGbl.ascii and running the makeSdr utility. The only requirement is that each periodic definition must begin with the value and the value must be in units of seconds.
The most important definitions in this file are:
/* Note that these must match the first four definitions in choiceGbl.ascii*/The first set of definitions defines the various scan types. The next two definitions (IOSCANPVT and interruptAccept) are for interfacing with the I/O event scanner. The remaining definitions define the public scan access routines. These are described in the following subsections.
#define SCAN_PASSIVE 0 #define SCAN_EVENT 1 #define SCAN_IO_EVENT 2 #define SCAN_1ST_PERIODIC 3 /*definitions for SCAN_IO_EVENT */ typedef void * IOSCANPVT; extern int interruptAccept; long scanInit(void); void post_event(int event); void scanAdd(struct dbCommon *); void scanDelete(struct dbCommon *); void scanOnce(void *precord); int scanppl(void); /*print periodic lists*/ int scanpel(void); /*print event lists*/ int scanpiol(void); /*print io_event list*/ void scanIoInit(IOSCANPVT *); void scanIoRequest(IOSCANPVT);
scanInit(void);The routine scanInit is called by iocInit. It initializes the scanning system.
scanAdd(struct dbCommon *); scanDelete(struct dbCommon *);These routines are called by scanInit at IOC initialization time in order to enter all records created via DCT into the correct scan list. The routine dbPut calls scanDelete and scanAdd each time a scan related field is changed (each scan related field is declared to be SPC_SCAN in dbCommon.ascii). scanDelete is called before the field is modified and scanAdd after the field is modified.
post_event(event)This can be called by virtually any IOC software component. For example sequence programs can call it. The record support module for eventRecord calls it.
static IOSCANPVT ioscanpvt;
scanIoInit(&ioscanpvt);
long get_ioint_info( int cmd, struct dbCommon *precord, IOSCANPVT *ppvt);This routine is called each time the record pointed to by precord is added or deleted from an I/O event scan list. cmd has the value (0,1) if the record is being (added to, deleted from) an I/O event list. This routine must give a value to *ppvt.
scanIoRequest(ioscanpvt)This routine can be called from interrupt level. The request is actually directed to one of the standard callback tasks. The actual one is determined by the PRIO field of dbCommon.
#include <vxWorks.h> #include <types.h> #include <stdioLib.h> #include <intLib.h> #include <dbDefs.h> #include <dbAccess.h> #include <dbScan.h> #include <recSup.h> #include <devSup.h> #include <eventRecord.h> /* Create the dset for devEventXXX */ long init(); long get_ioint_info(); struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_event; }devEventTestIoEvent={ 5, NULL, init, NULL, get_ioint_info, NULL}; static IOSCANPVT ioscanpvt; static void int_service(IOSCANPVT ioscanpvt) { scanIoRequest(ioscanpvt); } static long init() { scanIoInit(&ioscanpvt); intConnect(<vector>,(FUNCPTR)int_service,ioscanpvt); return(0); } static long get_ioint_info( int cmd, struct eventRecord *pr, IOSCANPVT *ppvt) { *ppvt = ioscanpvt; return(0); }
struct scan_list { FAST_LOCK lock; ELLLIST list; short modified; long ticks; /*used only for periodic scan sets*/ }; struct scan_element{ ELLNODE node; struct scan_list *pscan_list; struct dbCommon *precord; }Later we will see how scan_lists are determined. For now just realize that scan_list.list is the head of a list of records that belong to the same scan set (for example, all records that are periodically scanned at a 1 second rate are in the same scan set). The node field in scan_element contain the list links. The normal vxWorks lstLib routines are used to access the list. Each record that appears in some scan list has an associated scan_element. The SPVT field which appears in dbCommon holds the address of the associated scan_element.
The lock, modified, and pscan_list fields allow scan_elements, i.e. records, to be dynamically removed and added to scan lists. If scanList, the routine which actually processes a scan list, is studied it can be seen that these fields allow the list to be scanned very efficiently if no modifications are made to the list while it is being scanned. This is, of course, the normal case.
The dbScan.c module contains several private routines. The following access a single scan set:
dbScanLock(precord); dbProcess(precord); dbScanUnlock(precord);It also has code to recognize when a scan list is modified while the scan set is being processed.
#define MAX_EVENTS 256 #define EVENT_QUEUE_SIZE 1000 static struct scan_list *papEvent[MAX_EVENTS]; static SEM_ID eventSem; static RING_ID eventQ; static int eventTaskId;papEvent is an array of pointers to scan_lists. Note that the array has 256 elements, i.e. one for each possible event number. In other words, each event number has its own scan list. No scan_list is actually created until the first request to add an element for that event number. The event scan lists have the memory layout illustrated in Figure 7-1.
Figure 7-1: Scan List Memory Layout
At iocInit time a task "eventTask" is spawned. It waits on semaphore eventSem. When post_event is called it puts the event number on the ring buffer eventQ and issues a semGive for eventSem. This wakes up eventTask which calls scanList for the appropriate scan_list.
struct io_scan_list { CALLBACK callback; struct scan_list scan_list; struct io_scan_list *next; } static struct io_scan_list *iosl_head [NUM_CALLBACK_PRIORITIES] = {NULL,NULL,NULL};The array iosl_head and the field next are only kept so that scanpiol can be implemented and will not be discussed further. I/O event scanning uses the general purpose callback tasks to perform record processing, i.e. no task is spawned for I/O event. The callback field of io_scan_list is used to communicate with the callback tasks.
The following routines implement I/O event scanning:
scanIoInit (IOSCANPVT *ppioscanpvt)This routine is called by device or driver support. It is called once for each interrupt source. scanIoInit allocates and initializes an array of io_scan_list structures; one for each callback priority and puts the address in pioscanpvt. Remember that three callback priorities are supported (low, medium, and high). Thus for each interrupt source the structures are illustrated in Figure 7-2:
Figure 7-2: Interrupt Source Structure
When scanAdd or scanDelete are called, they call the device support routine get_ioint_info which returns pioscanpvt. The scan_element is added or deleted from the correct scan list.
scanIoRequest (IOSCANPVT pioscanpvt)This routine is called to request I/O event scanning. It can be called from interrupt level. It looks at each io_scan_list referenced by pioscanpvt (one for each callback priority) and if any elements are present in the scan_list a callbackRequest is issued. The appropriate callback task calls routine ioeventCallback, which just calls scanList.
static int nPeriodic; static struct scan_list **papPeriodic; static int *periodicTaskId;nPeriodic, which is determined at iocInit time, is the number of periodic rates. papPeriodic is a pointer to an array of pointers to scan_lists. There is an array element for each scan rate. Thus the structure illustrated in Figure 7-3 exists after iocInit.
Figure 7-3: Structure after iocInit
A periodic scan task is created for each scan rate. The following routines implement periodic scanning:
initPeriodic()This routine first determines the scan rates. It does this by accessing the SCAN field of the first record it finds. It issues a call to dbGetField with a DBR_ENUM request. This returns the menu choices for SCAN. From this the periodic rates are determined. The array of pointers referenced by papPeriodic is allocated. For each scan rate a scan_list is allocated and a periodicTask is spawned.
periodicTask (struct scan_list *psl)This task just performs an infinite loop of calling scanList and then calling taskDelay to wait until the beginning of the next time interval.
void scanOnce (void *precord)A task onceTask waits for requests to issue a dbProcess request. The routine scanOnce puts the address of the record to be processed in a ring buffer and wakes up onceTask.