Serial Support
Message Passing Facility

Release 1-3

Mark Rivers andMarty Kraimer
July 2000

Contents

Requires
Introduction
Using the support in applications
Getting Started
serialPortSniff
Serial Support
OctalUART Support

Requires

Introduction

Serial support consists of the following:
In addition the user will often use devStringMpf, which is described in the MPF documentation.

Using the support in applications

In <top>/config
 
In RELEASE add the line
MPF_SERIAL=<full path name>
If the base 3.13.2 or later config rules are used then just type
gnumake
otherwise add the following to  CONFIG_APP
ifdef MPF_SERIAL
MPF_SERIAL_BIN = $(MPF_SERIAL)/bin/$(T_A)
endif
The following database definition file must be included:
include "devMpfString.dbd"
In your source directory  Makefile.Vx: The following commands must appear in the st.cmd file for the server
...
initIpacCarrier("carrierName", 0)
initOctalUART("moduleName","carrierName:,...)
initOctalUARTPort(...
initSerialServer(...
See below for details. Looking at the <top>/iocBoot/ioc* shows a complete set of commands for the st.cmd files.

Getting Started

The iocBoot directory has sub directories for running a serial test. In order to use the tests a greensprings octal uart ip module is required. The tests assume that each of the 8 ports echos each character written to it, i.e. just wire pin2 to pin 3. The test assumes the ip module is installed in site IP_b. The test can be run either on a single processor (ioclocal) or on separate processors for client and server. If you run the two processor test change the inet addresses of the tcpMessageRouterClientStart and the tcpMessageRouterServerStart commands. To run the single processor test The test consists of the following: Studying this example should give a good idea of how serial support works. In particular look at the following files because they give examples of things developers are likely to do.

The example also contains a test that uses circular buffer mode.

serialPortSniff("portName",seconds)

This command show every character transmitted and received by the specified port for the number of seconds specified. For example
 
serialPortSniff "UART[5]",1
value = 0 = 0x0
iocrstserver>
T31 T34 R31 T35 R34 T00 R35 R00 T31 T34 14154.5.14
R31 T36 R34 T00 R36 R00 T34 T31 R34 T32 164.6.4142
R31 T00 R32 R00 T31 T34 R31 T37 R34 T00 1.2.14174.
R37 R00 T31 T34 R31 T38 R34 T00 R38 R00 7.14184.8.
T31 T34 R31 T39 R34 T00 R39 R00 T31 T35 14194.9.15
R31 T30 R35 T00 R30 R00 T31 T35 R31 T31 105.0.1511
R35 T00 R31 R00 T34 T31 R34 T33 R31 T00 5.1.41431.
R33 R00 T31 T35 R31 T32 R35 T00 R32 R00 3.15125.2.
T31 T35 R31 T33 R35 T00 R33 R00 T31 T35 15135.3.15
R31 T34 R35 T00 R34 R00 145.4.
serialPortSniff done
The output displays 10 characters per line. It first shows the hex value preceeded by T (transmit) or R (receive). It then shows the ascii values. Non-printable ascii values are shown as ".".

Serial Support

Serial Support consists of the following components:

SerialPort

An abstract base class for a serial port. It allows access to a serial port independent of the specific serial interface.
OctalSerial
Implements SerialPort for the Green Springs Quad/Octal IP carrier.
serialServer
A generic MPF server for SerialPort. It accepts Char8Array messages.
devStringMpf
Provides EPICS device support to send  string to a MPF server and receive a reply string.
SerialPort and OctalSerial can be used independent of MPF. SerialServer is the "glue" between MPF and SerialPort. DevStringMpf provides EPICS device support to send and receive string messages. It can be used with MPF servers other than serialServer. For example the MPF provides serverChar8Array, which just echoes whatever messages it is sent.

Because devices attached to serial ports have private protocols, serialServer and devStringMpf may not be usable. In such cases new device support and/or a new server  must be supplied. Usually only new device support is necessary, because serialServer is sufficiently generic.  For special devices a new server may also be needed. SerialServer and devStringMpf provide a model of how to write special support. This section provides details about SerialPort so the the code for serialServer is easier to follow.

SerialPort is independent of MPF and Industry Pack. It can be used as the base class for any type of serial port, i.e. VME, IP, etc. The code for OctalSerial provides a model of what needs to be done to derive from SerialPort.

SerialPort

enum byteHandlerRC {byteHandlerOK,byteHandlerEndRead,byteHandlerError};
enum serialPortStatus
    {serialPortSuccess, serialPortTimeout, serialPortFailure};
typedef byteHandlerRC (*BYTE_HANDLER)(void* parm,unsigned char byte);
typedef void (*PEEK_HANDLER)
    (void* parm,bool isReceive, unsigned char byte);
// If registered PEEK_HANDLER is called for every byte transmitted or received
class SerialPort
{
public:
    SerialPort();

public:
    SerialPort();
    int registerName(const char *portName);
    static SerialPort *find(const char *portName);
    static SerialPort *bind(const char *portName,void* bhparm,BYTE_HANDLER bh);
    void release();
    static bool setSniffHandler(
        const char *portName,void* phParm,PEEK_HANDLER ph);
    static void freeSniffHandler(const char *portName);
    serialPortStatus getStatus() { return(ioStatus);}
    virtual serialPortStatus write(
        unsigned char *data, int nbytes, int timeoutSeconds) = 0;
    virtual serialPortStatus write(
        unsigned char *data, int nbytes, double timeoutSeconds) = 0;
    virtual serialPortStatus read(int timeoutSeconds) =0;
    virtual serialPortStatus read(double timeoutSeconds) =0;
    virtual serialPortStatus writeRead(
        unsigned char *data, int nbytes, int timeoutSeconds) =0;
    virtual serialPortStatus writeRead(
        unsigned char *data, int nbytes, double timeoutSeconds) =0;
    virtual bool config( int baud, int stopBits, int bitsPerChar,
        char parity, char flowControl) = 0;
    void clientStartIoTimeout(int seconds);
    void clientStartIoTimeout(double seconds);
protected:
    void startIoTimeout(int seconds);
    void startIoTimeout(double seconds);
    void waitIo();
    requestType request;
    serialPortStatus ioStatus;
    BYTE_HANDLER bh;
    void* bhParm;
    PEEK_HANDLER ph;
    void* phParm;
    SEM_ID ioComplete;
private:
    ...
};
 
Method Implementor Usage Description
find SerialPort Called by servers Given the name of a port, find and return SerialPort.
bind SerialPort Called by servers Bind the caller to the specified port and if successful return the address of the SerialPort. Only one caller can be bound to a particular port. After a successful bind the caller may call write, read, writeRead, and release.
setSniffHandler SerialPort Called by serialPortSniff or other diagnostics SerialPort locates the serial port and sets the field ph. The Derived Class is expected to call the peek handler for every character trasmitted and received if ph has a value.
freeSniffHandler SerialPort Called by serialPortSniff or other diagnostics SerialPort locates the serial port and clears the field ph.
release SerialPort Called by servers Release the caller that is bound to the port.
getStatus SerialPort Called by servers Return the status of the last I/O request.
write Derived Class Called by servers. Send data to the serial port.
read Derived Class Called by servers Read data from the serial port.
writeRead Derived Class Called by servers Send data to the serial port while simultaneously reading.
registerName SerialPort Called by derived class Register the serial port.
clientStartIoTimeout SerialPort Called by servers Starts a timeout BEFORE a call to read, write, or writeRead.
startIoTimeout SerialPort Called by derived class Start a time out request. If server calls clientStartIoTimeout before calling write, read, or writeRead then startIoTimout does nothing. NOTE that this does a semTake with NO_WAIT before starting a watchdog. If a server doesnt desire this action it must call clientStartIoTimeout.
waitIo SerialPort Called by derived class Wait for I/O to complete or timeout.

The bind call must supply a byte handler which is called for every input character received by the serial port. The byte handler returns one of the following values.
 
Byte Handler Return Codes
byteHandlerOK If any read request is outstanding, just continue.
byteHandlerEndRead If any read request is outstanding terminate it with success.
byteHandlerError If any read request is outstanding terminate it with failure.

OctalUART Support

This is support for the GreenSprings Octal and Quad serial IP modules. Before initializing the OctalUART the IndustryPack Carrier must be initialized. The following command initializes an OctalUart module.
 
initOctalUART("moduleName","carrierName","carrierSite",nports,intVec)
parameter value
moduleName The IP Module name to be registered.
carrierName The name of the IP Carrier.
carrierSite The mezzanine slot location on a carrier. Eg: IP_a, IP_b, IP_c, IP_d
nports The number of ports. Must be 4 or 8.
intVec First interrupt vector to be used.

After the OctalUart is initialized each port can be initialized via the command.
 

initOctalUARTPort("portName","moduleName",port,baud,"parity",
    stop_bits,bits_char,"flowType")
parameter value
portName portName which is registered for port.
moduleName The IP module name for identification.
port port number. Must be between 0 and 7.
baud 1200, 2400, 4800, 9600, 19200, 38400
parity O, E, N for (Odd, Even, None)
stop_bits 1,2
bits_char 5,6,7,8
flowType H, N means (? , ?)

serialServer

This is a MPF server for SerialPort. It accepts SerialConfigMessage sand Char8Array messages. Client code must include file "serialServer.h", which contains definitions for commands, options, and for return status.

When serialServer receives a SerialConfigMessage it calls SerialPort::config and returns an Int32Message. A status of 0 means success.

When serialServer receives a Char8Array message it returns  a Char8Array message. The return message contains any characters read and a status value which has one of the following values:
 
serialServerStatusOK Request operation OK
serialServerStatusTimeout Operation timeout.
serialServerStatusOverflow Input buffer overflow
serialServerStatusError Other error

The cmd field of the Char8Array message can contain a command and also options. The commands are:
 
cmdWrite Write value to the port. A Char8Array message is returned. Only the status field is of interest.
cmdRead Read from from the serial port. A Char8Array message is returned. The value contains the input. Status should always be checked. Input is read until the first of the following occurs: 1) the end of message string is read, 2) extra is non zero and extra bytes are read, 3) A read buffer overflow occurs, or 4) a timeout occurs.
cmdWriteRead The output string is sent to the serial port and a input string read. The port is enabled for input BEFORE any bytes are sent to the port.

The cmd field can also contain the following options:
 
cmdFlush Flush the input buffer before starting I/O. This is useful when circularBuffer mode is enabled.
cmdStartCircularBuffer Start circular buffer mode. When circular buffer mode is enabled, input is accepted even when no read request is present.
cmdStopCircularBuffrer Stop circularBuffer mode, i.e. ignore any input characters unless an input request is active.
cmdSetEom Set end of message string. If the option is set then the fields eomLen and eomString determine the new end of message string.

The following additional fields of the Char8ArrayMessage are also honored by the serialServer:
 
timeout The timeout value in seconds. This field must be set.
numberRetrys Number of times to retry the request if the initial request fails.
extra If extra <= 0 then it is ignored. If extra is > 0 then it is the maximum number of bytes to read before ending the read. Disabling the end of message string and specifying extra allows serialServer to read binary data.
eomLen If cmdSetEom is set, this field determines the length of the end of message string. It can have the value 0, 1, or 2.
A zero value disables the end of message string. If the value is 1 or 2 the string is determined by eomString
eomString If cmdSetEom is set and eomLen is 1 or 2, this is the end of message string.

The following command initializes a serial server:

initSerialServer("serverName","portName",bufSize,queuesize,"eomstr",circularBufferMode)
 
serverName The MPF name for this server.
portName The name of the serial port to which the server is attached.
bufSize The size for the input buffer.  Note that this can not be changed after initialization
queuesize The maximum number of messages that can be on the servers queue.
eomstr The initial input terminator. It must be one or two characters.
circularBufferMode The initial state for circularBuffer. If (0,1) then circularBufferMode is (disabled, enabled)

Discussion of Circular Buffer Mode

Circular Buffer mode is provided for devices that respond with multiple lines of output in response to a single command request. Although it is possible to run with circular buffer mode always enabled this is not recommended because, if any errors occur, it is possible for writes and reads to get "out of sync". A better way to handle multiline requests is the following:

Circular buffer mode can also be used to send a command to a device with an output record and  read the response with an input record.  This method is discouraged because the output and input are not an atomic operation. If other records access the same port, it may be possible for one of the other records to send a message to the server between the output and input requests.

devStringMpf

Three versions of device support are supplied with MPF, corresponding to the three commands that can be given to serialServer. The first to types are associated with a stringin record and the third with a stringout record. File devStringMpf.dbd provides the device definitions and devStringMpf.cc provides the device support.

A record which uses MPFwriteRead has DTYP and INP defined as follows:

record(stringin,"<pvname>")
{
    field(DTYP,"MPFwriteRead")
    field(INP, "#C<location> S0 @<server>,<inputRecord>")
}
 
pvname Record name
location Location of message server.
server Name of the message server
inputRecord Name of field from which a serial output string is obtained.

When the record is processed the following happens:

Please note the following restrictions: Unless these restrictions are acceptable specialized device support will have to be written.

A record which uses MPFread has DTYP and INP defined as follows:

record(stringin,"<pvname>")
{
    field(DTYP,"MPFread")
    field(INP, "#C<location> S0 @<server>")
}
A record which uses MPFwrite has DTYP and OUT defined as follows:
record(stringout,"<pvname>")
{
    field(DTYP,"MPFwrite")
    field(OUT, "#C<location> S0 @<server>")
}

DISCUSSION

What about a serialPortRecord?  Is it necessary? How does it relate to Mark River's generic serial record?

Also what do we do about null terminators for strings? serialServer does not add or remove null terminator nor should it. What about device or record support?  If we have serialPortRecord we could have additional options to handle this. What about flow control? How do we handle this in a generic way? What else am I missing?