1 / 30

Device Support

Device Support. Andy Foster Observatory Sciences Limited. Outline. What is device support? The .dbd file entry The Device Support Entry Table DSET – report DSET – init DSET – init_record DSET – read_write Hardware device addresses Useful EPICS Facilities Interrupts

dominy
Télécharger la présentation

Device Support

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Device Support Andy Foster Observatory Sciences Limited

  2. Outline • What is device support? • The .dbd file entry • The Device Support Entry Table • DSET – report • DSET – init • DSET – init_record • DSET – read_write • Hardware device addresses • Useful EPICS Facilities • Interrupts • EPICS and Interrupts • The callback system • scanIoInit • DSET – get_ioint_info • scanIoRequest • Asynchronous I/O • The optional driver layer

  3. What is Device Support? • Interface between record and hardware • Provides an API for record support to call • Record type determines routines needed • Performs record I/O on request • Determines whether a record is to be synchronous or asynchronous • Provides I/O Interrupt support • Determines whether I/O Interrupts are allowed CHANNEL ACCESS DATABASE ACCESS RECORD SUPPORT DEVICE SUPPORT DRIVER SUPPORT

  4. Why use it • Why not make record types for each hardware interface, with fields to allow full control over the facilities it provides? • Users don't have to learn about a new record type for each I/O board they want to use • Changing the I/O hardware is much easier than writing new records! • Record types provided are sufficient for most hardware interfacing tasks • Device support is simpler than record support • Device support isolates the hardware interface code from changes to the record API • Bug-fixes or enhancements to record support only need to happen in one place • Modularity (good software engineering practice)

  5. The .dbd file entry • The IOC discovers what device supports are present from entries in the .dbd file device(recType,addrType,dset,"name") • addrType is one of AB_IO BITBUS_IO BBGPIB_IO CAMAC_IO GPIB_IO INST_IO RF_IO VME_IO VXI_IO • dset is the ‘C’ symbol name for the Device Support Entry Table (DSET) • By convention the dset name indicates the record type and hardware interface • This is how record support and the database code call device support routines • For example device(bi,INST_IO, devBiXy2440,“XYCOM-2440") device(bo,VME_IO, devBoXy240, “XYCOM-240")

  6. The Device Support Entry Table • … is a ‘C’ struct containing function pointers, the content of which can vary by record type • Each device support layer defines a named DSET with pointers to its own routines • All DSET structure declarations start struct xxxdset { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_write; }; • number gives the number of function pointers in the DSET, usually 5 for standard types • A NULL pointer is given when an optional routine is not implemented

  7. How does a record find its device support? Through .dbd ‘device’ statements:

  8. DSET: report long report(int type); • Optional function. Called by dbior shell command • Normally omitted because the driver also has a report function which prints out information about the current state of the hardware. • The value of type defines how much information to print.

  9. DSET: init long init(int pass); • Initializes the device support layer • Used for one-time startup operations, e.g. • Starting background tasks • Creating semaphores • Optional routine, not always needed • Routine called twice by iocInit: • pass=0 — Before record initialization • At this stage we don’t know what I/O is to be used by the database, therefore, do not access hardware. • pass=1 — After all record initialization • Can be used as a final startup step, all devices addressed by database are known by this point

  10. DSET: init_record long init_record( struct xxxRecord *prec ); • Called from xxxRecord’s init_record routine once for each record. This occurs at iocInit. • Important routine, normally required. • init_record should: • Parse the hardware address contained in the INP or OUT field • Check if addressed hardware is present • Allocate any private storage required • Every record type has a void *dpvt field for device support to use as it wishes • Program device registers • Set record-specific fields needed for conversion to/from engineering units

  11. DSET: read_write long read_write( struct xxxRecord *prec ); • Important routine. • Called from xxxRecord’s process routine and implements the I/O operation (or calls the driver to do this) which occurs when the record is processed. • Precise action depends on the record type and whether synchronous or asynchronous processing. • Generally, synchronous input support • Reads hardware value into prec->rval • Returns 0 • and synchronous output support • Copies value in prec->rval to hardware • Returns 0

  12. Hardware Device Addresses • Device support .dbd entry was device(recType,addrType,dset,"name") • addrType tells database software what type to use for the address link device(bo,VME_IO,devBoXy240,"XYCOM-240”) implies that: • pbo->out.type = VME_IO • If we look in base/include/link.h we see that struct vmeio { short card; short signal; char *parm; } • We can obtain card, signal, parm as follows: pvmeio = (struct vmeio *)&(pbo->out.value) • The most flexible link type is INST_IO which provides a string the user can parse.

  13. A simple example #include <recGbl.h> #include <devSup.h> #include <devLib.h> #include <biRecord.h> #include <epicsExport.h> long init_record(biRecord *prec) { char *pbyte, dummy; prec->pact = 1; if ((prec->inp.type != VME_IO) || (prec->inp.value.vmeio.signal < 0) || (prec->inp.value.vmeio.signal > 7)) { recGblRecordError(S_dev_badInpType, (void *)prec, "devBiFirst: Bad INP address"); return S_dev_badInpType; } if( devRegisterAddress("devBiFirst", atVMEA16, prec->inp.value.vmeio.card, 0x1, &pbyte) != 0) { recGblRecordError(S_dev_badCard, (void *)prec, "devBiFirst: Bad VME address"); return -1; } if( devReadProbe(1, pbyte, &dummy) < 0 ) { recGblRecordError(S_dev_badCard, (void *)prec, "devBiFirst: Nothing there!"); return -1; } prec->dpvt = pbyte; prec->pact = 0; return OK; }

  14. A simple example long read_bi(biRecord *prec) { char *pbyte = (char *)prec->dpvt; prec->rval = *pbyte; return OK; } struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_write; } devBiFirst = { 5, NULL, NULL, init_record, NULL, read_bi }; epicsExportAddress(dset, devBiFirst);

  15. A simple example – device support .dbd file The .dbd file for the device support routines shown on the preceding pages might be: device(bi, VME_IO, devBiFirst, “simpleInput”) An application .db file using the device support routines shown might contain: record(bi, "$(P):statusBit") { field(DESC, "Simple example binary input") field(DTYP, "simpleInput") field(INP, "#C$(C) S$(S)") }

  16. A simple example – application startup script An application startup script (st.cmd) using the device support routines shown on the preceding pages might contain: dbLoadRecords("db/example.db", "P=test,C=0x1E0,S=0") which would expand the .db file into record(bi, "test:statusBit") { field(DESC, "Simple example binary input") field(DTYP, "simpleInput") field(INP, "#C0x1E0 S0") }

  17. Useful facilities • ANSI C routines (EPICS headers fill in vendor holes) • epicsStdio.h – printf, sscanf, epicsSnprintf • epicsString.h – strcpy, memcpy, epicsStrDup • epicsStdlib.h – getenv, abs, epicsScanDouble • OS-independent hardware access (devLib.h) • Bus address  Local address conversion • Interrupt control • Bus probing • EPICS routines • epicsEvent.h – process synchronization semaphore • epicsMutex.h – mutual-exclusion semaphore • epicsThread.h – multithreading support • recGbl.h – record error and alarm reporting

  18. Interrupts • vxWorks ISRs can be written in ‘C’ • On VME two parameters are needed: • Interrupt Level — prioritized 1-7 • Often set with I/O board DIP switches or jumpers • Level 7 is non-maskable and can crash vxWorks • Interrupt Number • 0-255 has to be unique within this CPU • Interrupt numbers < 64 are reserved by MC680x0 • Use EPICS veclist on IOC to see vectors in use However, not supported by some PPC BSP’s! • OS initialization takes two calls Connect interrupt handler to vector: devConnectInterruptVME( unsigned vectorNumber, void (*pFunction)(void *), void *parameter); Enable interrupt from VME level: devEnableInterruptLevelVME (unsigned level);

  19. EPICS and Interrupts The callback system • How do you make an EPICS record process when an interrupt occurs? • Use the SCAN setting of “I/O Intr” in the record • Since an ISR cannot call a record’s process routine directly, I/O interrupt scanning is implemented using the EPICS callback system. • The callback system uses ring buffers to queue callback requests made from interrupt level. • One of 3 prioritized callback tasks executes the registered callback function which in turn calls the record’s process routine. • See callback.h for a definition of the EPICS callback structure. • The choice of callback task is determined by the PRIO field in the record (LOW, MEDIUM, HIGH). • From the device/driver layer, we support I/O interrupt scanning by using the following routines: • scanIoInit • get_ioint_info • scanIoRequest

  20. EPICS and Interrupts scanIoInit • scanIoInit must be called for each possible interrupt “source”. Device support can be written to allow any number of “sources”. Ultimately, the granularity of interrupts depends on the hardware. • One “source” per card • One “source” per bit • scanIoInit is typically called from the driver initialize routine to initialize a pointer void scanIoInit(IOSCANPVT *ppvt) int xy2440Initialise( void ) { struct config2440 *plist = ptrXy2440First; #ifndef NO_EPICS { int i; for( i=0; i<MAXPORTS*MAXBITS; i++ ) { scanIoInit( &plist->biScan[i] ); scanIoInit( &plist->mbbiScan[i] ); } } #endif ... }

  21. EPICS and Interrupts scanIoInit – contd struct config2440 { ... #ifndef NO_EPICS IOSCANPVT biScan[MAXPORTS*MAXBITS]; IOSCANPVT mbbiScan[MAXPORTS*MAXBITS]; #endif } • scanIoInit allocates an array of 3 “scan lists”, one for each callback priority (PRIO field). IOSCANPVT will hold the address of this array.

  22. EPICS and Interruptsget_ioint_info • The DSET must have a get_ioint_info routine. This is called at iocInit for all records with “SCAN = I/O Intr”. • Device support copies the IOSCANPVT value for this record (determined by which bit) into *ppvt typedef struct { int port; int bit; } xipIo_t; static long init_bi( struct biRecord *pbi ) { xipIo_t *pxip; switch(pbi->inp.type) { case(INST_IO): pxip = (xipIo_t *)malloc(sizeof(xipIo_t)); status = xipIoParse(pbi->inp.value.instio.string, pxip); pbi->dpvt = pxip; ret = 0; break;default: printf("Suitable error message\n"); ret = 1; break; } return(ret); }

  23. EPICS and Interruptsget_ioint_info – contd static long bi_ioinfo( int cmd, struct biRecord *pbi, IOSCANPVT *ppvt ) { struct config2440 *plist = ptrXy2440First; xipIo_t *pxip; pxip = (xipIo_t *)pbi->dpvt; /* "channel" is calculated from pxip->port, pxip->bit */ *ppvt = plist->biScan[channel]; return(0); } • Return 0 if OK, else non-zero when scan will be reset to Passive • Can usually ignore the cmd value: • At iocInit this routine is called with cmd=0 for all records with SCAN=I/O Intr . • If SCAN is later changed by a caPut or dbPut, this routine will be called again with cmd=1.We do not have to worry about this case. • get_ioint_info adds this record to the appropriate scan list, depending on PRIO.

  24. EPICS and InterruptsscanIoRequest • In the ISR, determine which channel on the card caused the interrupt and then call:scanIoRequest(plist->biScan[channel]); • This causes all records in all 3 scan lists associated with “plist->biScan[channel]” to process!

  25. Asynchronous I/O • It is never permissible for Device Support to wait for slow I/O • If hardware read/write operations take “a long time,” (>100µsec) use asynchronous record processing • If the device does not provide a suitable completion interrupt, a background task can poll it periodically • An asynchronous read_write routine looks at precord->pact, and if FALSE, it: • Starts the I/O operation • Sets precord->pact = TRUE and returns

  26. Asynchronous I/Ocontinued • When the operation completes, Device Support must run the following code at task level, not in the ISR: struct rset *prset = (struct rset *)precord->rset; dbScanLock(precord); (*prset->process)(precord); dbScanUnlock(precord); • The record’s process routine will call the Device Support read_write routine again. Now pact is TRUE, the read_write routine will: • Complete the I/O by setting the rval field etc. • Return 0

  27. Asynchronous example #include <vxWorks.h> #include <stdlib.h> #include <stdio.h> #include <wdLib.h> #include <recSup.h> #include <devSup.h> #include <aiRecord.h> static long init_record(); static long read_ai(); static void myCallback( CALLBACK * ); typedef struct {long number;DEVSUPFUN report;DEVSUPFUN init;DEVSUPFUN init_record;DEVSUPFUN get_ioint_info;DEVSUPFUN read_ai;DEVSUPFUN special_linconv; } ANALOGDSET; ANALOGDSET devAiTestAsyn = { 6, NULL, NULL, init_record, NULL, read_ai, NULL }; /* Device Private Structure */ struct devPrivate {CALLBACK callback; /* EPICS CALLBACK structure */WDOG_ID wdId; };

  28. Asynchronous example - continued static long init_record( struct aiRecord *pai ) { struct devPrivate *pdevp; pai->udf = FALSE; pdevp = (struct devPrivate *)(calloc(1,sizeof(struct devPrivate))); pai->dpvt = (void *)pdevp; callbackSetCallback( myCallback, &pdevp->callback ); callbackSetPriority( pai->prio, &pdevp->callback ); callbackSetUser( pai, &pdevp->callback ); pdevp->wdId = wdCreate(); return(0); } static long read_ai(struct aiRecord *pai) { struct devPrivate *pdevp = (struct devPrivate *)(pai->dpvt); int waitTime; if( pai->pact == FALSE ) { waitTime = (int)(pai->val * vxTicksPerSecond); printf("(%s) Starting Async. Processing...\n", pai->name);wdStart( pdevp->wdId, waitTime, (FUNCPTR)callbackRequest, (int)&pdevp->callback );pai->pact = TRUE;return(0); } else { printf("(%s) Completed Async. Processing...\n", pai->name);return(2); /* No linear conversion in record */ } }

  29. Asynchronous example - continued static void myCallback( CALLBACK *pCallback ) { struct dbCommon *precord; struct rset *prset; callbackGetUser( precord, pCallback ); prset = (struct rset *)precord->rset; dbScanLock(precord); (*prset->process)(precord); dbScanUnlock(precord); }

  30. The optional Driver layer • Some reasons to include a driver layer: • Share code between similar device support layers for different record types • Most digital I/O interfaces support record types bi, bo, mbbi, mbbo, mbbiDirect and mbboDirect • Initialize a software module before individual device layers using it are initialized • The order in which device support initialization is called is not guaranteed • Provide a common interface to an I/O bus such as GPIB, Bitbus, Allen-Bradley etc. • I/O devices placed on this bus need their own device support, but share the interface hardware • Provide a wrapper around commercial device drivers or other software libraries

More Related