white.gif (35 bytes) white.gif (35 bytes) white.gif (35 bytes)
white.gif (35 bytes) Arrow Vx/IP-OptoInterrupter (also MVIP-520)

sales@compware.com

Part Number Price Description
Vx/IPOptoIntr-S 995.00 Development/Source license for IP-OptoInterrupter VxWorks Driver
Vx/IPOptoIntr-RTU 135.00 Right-To-Use license for (1) IP-OptoInterrupter (MVIP-520)
Vx/IPOptoIntr-S-Maint 495.00 12-months Software Maintenance
 

Vx/IP-OptoIntr-S

VxWorks Driver for the GreenSpring IP-OptoInterrupter copyright (c) 1995 Compware Corp.

1. Overview

2. Features

3. Examples

4. Polarity, Disables and Transitions

5. A More Secure Approach

6. Test Code

Overview

This note describes the usage of the 1.1 release of the VxWorks driver for the IP-OptoInterrupter. The driver supports up to (4) IP-OptoInterrupter’s, which may be resident on the VME bus in IP ports of the 310 or 610 carrier, or in IP ports of the 4 IP slots of the Motorola MVME162 processor board.

The driver entry

ipOptoIntPackRegister probes either local or offboard space for a designated IP-OptoInterrupter. This routine checks the IP Prom Space and runs a small diagnostic selftest to confirm the functioning of registers in the IP I/O space. Following pack registration, devices- one per input channel- may be created at any time. A read directed to a (enabled) device completes when an input line transition to the designated polarity occurs, returning a byte representing that polarity. The polarity and disable/enable status of a device may be specified at device creation time and respecified by ioctls. If a device is disabled, polarity transitions are stored in hardware or software pending flags, and do not complete reads. When a device is enabled, a pend flag causes an immediate completion of any pending read. However a polarity respecification clears all pend flags, even if the polarity is unchanged. Ioctls exist for information, polarity/disable respecification, setting of "special" interrupts (see section 5) and rerun of the diagnostic selftest.

The product is primarily designed to catch a single transition of a critical input, not count up of multiple transitions. The software and hardware always cause 1 transition if the channel reaches the desired polarity for an adequate length of time. This is true in particular if the polarity is as desired when polarity for the device is set. Multiple transitions to and from the desired polarity cause 1 completion for each transition into the polarity, if the time period separating transitions is long enough.

In the case of particularly critical inputs, a multi-layered software approach, using a "special" interrupt and a system clock background poller, can provide significant protection against software and hardware failures. This strategy is outlined in section 5.

A test application is provided (ta2.c) which runs in conjunction with the Greenspring IP-OptoDriver. It generates a suitably slow pattern of polarity transitions on all eight input lines and counts up read completions. Both the Vx/IP-OptoInterrupter and Vx/IP-OptoDriver software are tested.

2. Features

2.1 Initialization

The driver itself is installed and initialized by

ipOptoIntDrv()

without arguments. It fails only if the driver cannot be installed in vxWorks, in that case returning "-1". Otherwise it returns "0". It also returns 0 if called after the driver is correctly installed, the call being then a no-op.

2.2 IP Registration

An Opto Interrupter Industry pack is registered by

ipOptoIntPackRegister(base, intLevel, localBus, verbose, vector)

which returns -1 if unsuccessful and otherwise a pack number (0-3) needed later for device creation.

"base" is the address of the I/O space base address for this IP. This will either be the VME short i/o address (0- 0xFFFF) or the local MVME 162 address. depending on the installation of the IP. If on the MVME162, a shorthand is available- set the base to a small number (0-3), which will be taken as the onboard slot # : 0 for A through 3 for D.

"localBus" is a BOOL, set TRUE(1) if the IP is onboard the MVME162, FALSE (0) if on a VME carrier.

"intLevel" and "vector" are the interrupt level and interrupt vector number (0-0xFF) to be used for this IP. The level and vector choices must be reviewed against the local software and hardware configuration. Note that an interrupt level used on the VME bus is a function of carrier slot and not arbitrary.

"verbose" is a BOOL, true if advisory and error messages are to be printed. If false, ipOptoIntPackRegister will fail or succeed silently.

IpOptoIntPackRegister fails if the IP is not found (bus error), has the wrong manufacturer, model number, or title ("IPAC") in the IP Prom ID space. It also fails if the selftest on the I/O space registers fails, if the memory allocation, interrupt connect, or interrupt enable O/S operations fail, or if the specified address parameters are impossible (e.g pointing to local RAM). If the IP has already been registered, the test succeeds as a no-op with an advisory message.

2.3 Device Creation

One device may be created for each of the 8 input lines on the IP-OptoInterrupter. By convention, these lines are numbered as channels 1 through 8.

ipOptoIntDevCreate ( name, pack, whichChan, Polarity, Disable ) : which returns -1 if unsuccessful, 0 otherwise.

"name" is a unique string such as "/ch1" to reference the device in open calls.

"pack" is the pack number, as returned by ipOptoIntPackRegister.

"whichChan" is the channel number, 1-8.

"polarity" and "disable" are BOOLs. Polarity TRUE/FALSE corresponds to 1/0 on the input lines, and gives the desired polarity transition. Disable FALSE allows completions (interrupts) to occur. Disable TRUE will pend desired transitions until such time as the device is enabled. An immediate completion (or pend) will occur if the polarity of the input line already matches the desired state.

IpOptoIntDevCreate fails if the driver or pack are not installed, the channel number is out of range, the channel is already attached to another device, or if the memory allocation or device installation O/S operations fail.

2.4 Read, Write, Open and Close

Read operations on opened OptoInterrupter devices:

read(fd, buffer, nbytes)

return 0 if they fail, or the byte count (always 1) if they succeed.

"fd" is the file designator returned by open.

"buffer" is the address to store read data.

"nbytes" is available buffer size. Nbytes must be 1.

A read takes a semaphore that is filled by a channel interrupt- a software event usually arising from a hardware interrupt. In other words, the read will not complete until a transition to the desired polarity occurs. A single byte is set in the read buffer, 0x00 or 0x01 according as the polarity is 0 or 1 (FALSE or TRUE). A read will fail immediately if the byte count argument is not 1.

Write operation is not available in this driver.

Open and close operations are conventional, except that in release 1.1, at least, a mutual exclusion semaphore is taken at open time and given at close time. This means only 1 process may hold a device open at a time. The file designator, however, may be shared with care among processes. Another mutual exclusion semaphore guards access to pack wide resources, such as the IP register set.

If a process tries to open an OptoInterrupter device, and it is already open, the process will hang until the previous file designator is closed. However, this close need not be done by the same process that opened it.

2.5 Ioctl Calls

The ioctls in table 1 are available in this driver. Symbols are defined in the include file ipOptoIntDrv.h.

status = ioctl (fd, func, data)

Function Data Code Type Purpose
FIO_OPT_INFO optoIntInfo * return information block
FIO_OPT_POLARITY BOOL set device polarity value
FIO_OPT_DISABLE BOOL set device disable value
FIO_OPT_SPECIAL makeSpecialInt * set special interrupt for device
FIO_OPT_SELFTEST void run self test diagnostic (for pack)

Table 1. OptoInterrupter Ioctls (release 1.1 of driver)

FIO_OPT_INFO returns a structure- optoIntInfo:

struct optoIntInfo {

BOOL Data; actual value just read from data input register

BOOL Polarity; internal device polarity setting (via DevCreate or ioctl)

BOOL Disable; internal device disable setting (via DevCreate or ioctl)

BOOL Pending; s _o _f _t _w _a _r _e pending flag

FUNCPTR specialInt; special interrupt routine address (via ioctl)

int softInt; software interrupt count for this device int hardInt; hardware interrupt count for this pack

BOOL semFull; not used };

Several comments pertain:

1. It is useful to check FIO_OPT_INFO after another process has posted ( and is blocking on) a read, to see that the "polarity" and "disable" settings are as they should be. "data" should be inverse to "polarity" unless a transition has already occurred.

2. "pending" is currently unuseful, since it ignores the pending flag in the hardware. See section D for elucidation of this point.

3. The "softInt" count corresponds to channel interrupts, which give the read semaphore. "hardInt" is actual hardware interrupts, each of which may pertain to several transitioning channels.

FIO_OPT_POLARITY

sets the polarity desired for the given channel. It does not change the disable status for this device. An immediate channel interrupt is guaranteed if the polarity already matches the desired and is held for an adequate time. However, all previous pended interrupts, in hardware or software, are cleared. These two statements are true even if the previous polarity is retained.

FIO_OPT_DISABLE

sets the disable status for the given channel. It does not change the polarity for this device. Previously pended interrupts (if device was disabled) cause 1 immediate channel interrupt if channel now enabled.

FIO_OPT_SPECIAL

passes a structure: makeSpecialInt:

struct makeSpecialInt { int password; FUNCPTR specialInt; void *specialContext; };

#define MS_PASSWORD 0x1234abcd

This allows a user interrupt routine to be inserted into the processing chain for every channel interrupt directed to this device. The presence of a special interrupt does not affect the normal semaphore (read completion) operation of the device. One can get a call to the user interrupt routine and a read completion from the same polarity transition.

The "password" field must be set to the value MS_PASSWORD for the call to succeed. This provides some protection against accidental invocation of FIO_OPT_SPECIAL. The "specialInt" is the entry address for the special routine, and "specialContext" is an argument passed with every call to specialInt. That call is of the form

specialInt(sc,iop)

where "sc" is the specialContext pointer value, and "iop" is an ipOptoIntDev structure pointer for the device. If the s" pecialInt" field in the makeSpecialInt structure is NULL, the existing special routine is cancelled. This is the state of things at device creation (no special routine). FIO_OPT_SPECIAL fails only if the "password" is bad. The specialInt routine used must conform to the rules for interrupt service routines (e.g. can't use printf) in similar fashion to user supplied vxWorks system clock interrupt routines.

FIO_OPT_SELFTEST

This call, without arguments, invokes one pass of the diagnostic selftest. This test, run once at device creation time, periodically toggles polarity to induce hardware interrupts from all 8 input channels. It will restore the original polarity and disable status of all channels. Reads posted to devices will not complete during the test. Transitions on input lines occurring during the test, which last less than 2.5 seconds, will in general be missed. However, if the input polarity at test completion matches the desired for a line, a channel interrupt (or pend flag if the channel is disabled) will immediately be generated. In other words, if a critical input goes true during the course of the self test, it will be ignored, but caught if still true when the self test completes.

In release 1.1 the selftest is stubbed. FIO_OPT_SELFTEST always returns good (0) status without any actual test being run. This is also true of the invocation at device creation time.

2.6 Display and Miscellaneous Routines

Two show routines exist for displaying IP-OptoInterrupter driver information.

ipOptoIntPackShow

without arguments displays information on each OptoInterrupter IP registered with ipOptoIntPackRegister. It shows IP addresses and residency, current setting of pack-wide polarity and disable registers, interrupt level and vector, count of hardware interrupts and status of semaphore controlling access to the pack. A list of the channels for each pack is given, indicating those with corresponding created devices.

ipOptoIntIntDevShow(pack,whichChan)

shows the device memory structure corresponding to the indicated "pack" and channel number w" hichChan". It shows device name, semaphore status, software channel interrupt count, special interrupt entry point, and current disable, polarity and (software) pend status of the channel. Obvious complaints are printed if p" ack"or "whichChan" correspond to unknown packs, channel numbers, or devices.

4. Polarity, Transitions and Disables

An OptoInterrupter device serves in effect to provide hardware independent access to the channel bits in the Interrupter register set. Four registers in particular are of interest: Data, Interrupt Polarity, Interrupt Disable and Pending Interrupt. The data register bits correspond to to the current state of the input lines, which are on the output side of a debouncing filter. The interrupt polarity register bits represent the target input polarity states required to set the corresponding bit in the Pending Interrupt register. The interrupt disable register bits are set for channels whose interrupts are currently disabled. When a Pending Interrupt register bit is set for a channel not disabled in the Disable register, an interrupt request is asserted. The interrupt condition, for that channel, is removed by clearing the bit in the Pending Interrupt register, which is ordinarily done in the interrupt service routine.

Software can modify bits in the Interrupt Polarity and Interrupt Disable registers. Clearing and setting disable bits has the obvious effect of enabling or masking interrupts on a channel by channel basis. Clearing or setting Polarity bits sets the target polarity for interrupts, with the additional special property: if the bit setting for a channel in the Polarity regiser is changed, and if the actual input polarity for that channel, as seen in the Data register, already matches the new setting of the Polarity register bit, a pending interrupt bit is set immediately for that channel. In this case, an actual polarity transition on the input is not required. An implication of this is that the interrupts can be tested without transitions on the input lines, by toggling the Polarity register bits on and off- without any input transitions, one interrupt will occur per 2 transition cycles on the Polarity register bit. It is also true that holding the Polarity register bits constant and toggling the input lines also produces 1 interrupt per 2 transitions. The diagnostic selftest is based on the first of these two facts.

The setting of the Polarity and Disable registers for device operation in the vxWorks driver is accomplished by the internal routine

ipOptoIntDevEnable (iop, ifchangePolarity)

which is used by DevCreate, and the 2 ioctl calls FIO_OPT_POLARITY and FIO_OPT_DISABLE (see section 2.5). "iop" is a device structure containing the desired values (as BOOLs) for both Polarity and Disable. When "ifChangePolarity" is true, both Polarity and Disable registers will be explicitly changed, even if the setting did not change. Otherwise, when "ifChangePolarity" is false, only the Disable register is affected. FIO_OPT_DISABLE invokes ipOptoIntDevEnable with "ifChangePolarity" FALSE- in other cases, it is TRUE.

The DevEnable routine primarily sets and clears the indicated bits in the Polarity and Disable registers. To manage this, the optoIntPack memory structure for the whole OptoInterrupter IP contains Polarity and Disable elements which are copies of the current hardware registers' contents. These elements are displayed by ipOptoIntPackShow. There is also a packSem taken, which guards the pack hardware registers and optoIntPack structure. Thus processes may access multiple devices in an orderly way. DevEnable does two other things. When changing polarity, interrupts already pending (from the previous polarity setting) are cleared. By setting polarity, even if no change occurs, the user indicates no further interest in previous channel context. Furthermore, when setting polarity, the driver ensures an additional channel interrupt, pending or otherwise, if the input polarity already matches that desired. This is a slight extension of the hardware functionality, in that the hardware will not generate an interrupt flag without a transition on either the input line or the bit in the Polarity register. For example, if the input line is held constant at logic "1", and FIO_OPT_POLARITY is called repeatedly, setting the polarity to TRUE (1), each ioctl call will generate another channel interrupt. The hardware itself will generate an interrupt only the first time, when the Polarity register bit goes from 0 to 1. The motive here is for the DevEnable to function independently of previous context. Of course, the finite time required for O/S scheduling will mitigate this- a channel interrupt occurring prior to DevEnable may not cause a waiting read process to unblock until after the transition.

DevEnable processing begins by taking the pack semaphore. If "ifPolarityChange" is FALSE, the new disable status is written into both the Disable element in the pack structure and the hardware Disable register. Processor interrupts are then locked. If the new disable status is FALSE (enabled) and the software interrupt pending flag (in the device structure) is set, and the hardware interrupt pending flag is not set, a call to ipOptoIntChanInt is made. The purpose is to generate a channel interrupt from a software pend flag whenever the device is enabled. If "ifPolarityChange" is TRUE, the new polarity is written into the Polarity element in the pack structure. Processor interrupts are then locked. The Polarity element in the pack structure is copied to the hardware Polarity register. The bit for this channel is unconditionally cleared in the hardware Pending register by writing a 1 in that position to the hardware Clear register. This will clear anything pending from the previous polarity, but may also clear a hardware interrupt for the new polarity. So we next read the hardware data register and set the software pending flag to TRUE if the data bit matches the desired polarity, FALSE otherwise. This has the side effect of clearing the value of software pend from the previous polarity. If the new disable status is FALSE (enabled) and the software interrupt pending flag is set, and the hardware interrupt pending flag is not set, a call to ipOptoIntChanInt is made. The purpose is to generate a channel interrupt from a software pend flag if the device is enabled, a software pend is set from the previous read of input data, and there is no hardware interrupt pending. Following this processing, DevEnable unlocks interrupts and gives back the pack semaphore.

The processing for hardware interrupts in this driver is straightforward. The hardware pending interrupt register is read, and copied to the hardware interrupt clear register, which clears all current interrupt sources. For each bit that was set in the hardware pending register, we check to see if a device has been created. If not, we continue. Otherwise, we see if the device is disabled. If so, the software pend flag for the device is set TRUE and we continue. Otherwise, a call is made to ipOptoIntChanInt for a channel interrupt. When all active channels are completed, we increment the hardware interrupt counter for the pack.

Channel interrupts (ipOptoIntChanInt) will invoke any special routine set by the FIO_OPT_SPECIAL_INT ioctl, give the read semaphore for the device, and clear the device's software pend flag. The software interrupt counter for the device is incremented.

5. Towards a More Secure Approach

Various hardware and software problems can arise to frustrate detection of transitions of Opto Interrupter input lines. In the case of especially critical inputs, it is worth putting design effort into protection against the obvious problems, and providing some alternate paths toward the desired goal. Presumably, the appearance of a critical transition requires taking of some action, which can be encapsulated into a single response procedure. By using special interrupts, system clock time polling and the normal read completion mechanism, this procedure can be reached by 3 different paths. Simple locking mechanisms, if required, can ensure that the response is executed only once. Through analysis, it is also possible to determine how long the transition need be held for detection by the IP Opto Interrupter, and quickly the response will be effected.

The major hardware problems:

a)- Miswiring

b)- Improper debouncing

c)- Inadequate testing

The major software problems:

a)- Massive vxWorks failure

b)- Massive driver failure

c)- Improper task priorities

d)- Improper setup of devices

e)- Inadequate testing

On the hardware side, the effect of the debouncing filters, as described on pp. 6 and 21 of the manual, should be evaluated. It may also be worthwhile to examine the real noise level on the line.

On the software side, problems a and b, massive O/S or driver failure, are difficult to overcome. In some cases, the effect of a failure is to reboot the O/S. Having a startup script, or modifying usrConfig.c, to set up input transition monitoring automatically, is valuable here. You inevitably lose 10-20 seconds of monitoring while the taget reboots- but you will respond eventually. This does depend on the host staying up, unless you have a Prom'd system. Use of a watchdog timer, with an unmaskable interrupt, can be used to convert many otherwise intractable failures into a simple reboot situation.

Improper task priorities (c) can prevent a task waiting on a transition via the read operation from ever getting scheduled in- such processes ought to have the greatest (smallest number) priority in the vxWorks system. Improper setup of the Interrupter device- leaving disabled, or the wrong polarity, or simply failing to post a read, will foil transition detection. Adequate testing helps here, as does examining the output of a FIO_OPT_INFO ioctl after device creation and setup.

To provide multiple paths to transition detection, you begin by spawning a process which waits on a read to the appropriate Interrupter device. After this is done, the parent process calls the ioctl FIO_OPT_INFO to see that the Disable and Polarity for the device is right, and the Data is opposite in sense to the Polarity ( unless a transition has just occurred). A special interrupt, which provides the correct transition response, is installed via FIO_OPT_SPECIAL. This interrupt is invoked regardless of vxWorks scheduling or the viability of the task waiting for the read. Finally, a user supplied routine may be declared to run at each system tick, via the BSP entry sysClkConnect. This routine polls the Opto Interrupter Data register periodically (usually 60 hz). If the polarity changes, the critical response is invoked. This mechanism is wholly independent of the driver or device setup.

If the critical response needs to be invoked once only, a locking mechanism may be used in the multiple paths approach. The response routine may use a counter, initialized to zero, for locking purposes. After locking interrupts, the response routine examines the counter- if non-zero, it increments the counter, unlocks interrupts, and returns. Otherwise, it increments the counter, unlocks interrupts and performs the operation for response.

If you choose the alternate paths approach, each path should be separately tested. If all of the above are in place, see that the locking counter (if used) increments to 3. It is also useful to poll the counter, to see that it stays zero, in the system clock user polling routine. If the counter is set non-zero, without polarity transition, the cause is presumably some errant code.