Serial Support
Message Passing Facility
Operating System Independent Version

Release 2-6

Marty Kraimer and Mark Rivers
April 2004

License Agreement

MPF is available via the open source license described at the end of this document.

Contents

Introduction
Serial Test
Building applications
Initialization Commands
Serial Support Theory
devStringMpf
serialServer
License Agreement

Introduction

mpfSerial provides support for EPICS records to communicate with serial devices, i.e. RS232, RS485, etc. Since mpfSerial uses asynDriver for communication with low level drivers it will work with any driver that supports asynOctet. This includes most gpib and serial drivers.

The basic concept is:

The support is structured so that special requirements can be satisfied by writing device support and/or a serial server. As long as this support satisfies the conventions defined in serialserver.h then the low level drivers should still work.

mpfSerial consists of the folowing:

The major difference between the OSI (Operating System Independent) version of mpfSerial and the previous version is that the low lever driver support now uses asynDriver rather than developing specialized support. This approach allows mpfSerial to be used with anything supported by asynDriver.

NOTE: The only serial device support discussed in this manual is devStringMpf. Most mpfSerial users use the genericBus/serialMpfApp support that comes with SYNAPPS.

Serial Test

The iocBoot directory has sub directories for running serial tests. The tests use a combination of standard serial ports, SBS OctalUART ports, and MOXA ports. The tests assume that a port echos each character written to it, i.e. pin2 is connected to pin 3. Tests are provided for vxWorks (iocserialvxWorks) and for Unix/Linux (iocserialhost). Each provides two versions of the test: a version that uses writeRead requests, and a version that uses separate write and reads.

To use the the version that uses writeRead requests on Unix/Linux do the following:

cd iocBoot/iocserialhost
../../bin/<arch>/testSerial st.cmd

NOTE: The st.cmd file starts ports for the MOXA box in my office. Just comment out all references to port2,...port5.

The test starts a serialServer and creates records for each port. The following is the record template for a port.
record(calc,"serial$(port):calc$(ind)")
{
    field(SCAN, "Event")
    field(EVNT, "1")
    field(CALC,"a=1000?0:a+1")
    field(FLNK,"serial$(port):StringOut$(ind)")
    field(INPA,"serial$(port):calc$(ind)")
}
record(stringout,"serial$(port):StringOut$(ind)")
{
    field(DOL,"serial$(port):calc$(ind)")
    field(OMSL,"closed_loop")
    field(FLNK,"serial$(port):StringIn$(ind)")
}
record(stringin,"serial$(port):StringIn$(ind)")
{
    field(DTYP,"MPFwriteRead")
    field(INP, "#C$(card) S0 @serial$(port),serial$(port):StringOut$(ind)")
}
The calc record  counts from 0 to 1000 and links to a stringout record which reads the value of the calc record and forward links to a  stringin record.  The stringin record reads the value from the stringout record and sends it to serverChar8array. The reply from serverChar8array is put into the val field. Studying this example should give a good idea of how serial support works.

Building applications

Since mpfSerial is bundled with mpfosi the only requirement is specif the correct include statements on your xxxInclude.dbd file.

In the directory where the application is built. NOTE: xxx is replaced by the name of the application you are building add the following to xxxInclude.db

    include "mpf.dbd"
    include "devStringMpf.dbd"
    include "mpfSerial.dbd"
or for vxWorks only
    include "mpfSerialVx.dbd"

Initialization Commands

This section describes initialization commands that appear in a st.cmd file. A simple example may make this section easier to understand. The following commands initialize everything needed for accessing a single serial port on an x86 Linux based ioc.

localMessageRouterStart(1)
drvGenericSerialConfigure("port0","/dev/ttyS0",0,0)
initSerialServer("serial0","port0",0,1000,2,"")

The following is an example record that connects to this port.

record(stringin,"name")
{
    field(DTYP,"MPFread")
    field(INP, "#C1 S0 @serial0")
}

NOTE: #C1 means DevMpf location 1, which is the argument given to localMessageRouterStart.

mpfSerial initialization Commands

Consult the asynDriver documentation for how to configure port drivers. For example look at the documentation for drvGenericSerialConfigure. The only configuration command provided by mpfSerial is:

int initSerialServer(const char *serverName,const char *portName,int addr,
    int bufsize, int queueSize, const char *eomstr);

The arguments are:

serverName The server name which is specified in INP or OUT fields
portName The port name. This must match a port name from an asynDriver configuration command.
addr The address on the port. For serial ports just specify 0. For a gpib port spscify the address of the device.
buffersize Input buffer size. Both the server and low level drivers need a buffer.
queueSize The number of message a server is willing to buffer.
eomString End of message string for input messages. The initial value can be overridden by device support. The eomString can be any of the following:
  • null string - server doesnt look for end of message. Device support must specify exact length for input messages.
  • "", i.e. zero length string. A character with binary value 0 signifies the end on message.
  • a one or two character string that appears at the end of input messages.

Other Initialization Commands

In addition the following initialization commands may be needed.

Example st.cmd file for a Linux x86 IOC

The following example starts two serial ports on the ioc and four serial ports on a MOXA ethernet serial server that resides at address :164.54.9.90 and responds to TCP connect requests at TCP ports 4001,...,4004.

localMessageRouterStart(1)
drvAsynSerialPortConfigure("port0","/dev/ttyS0",0,0,0)
drvAsynSerialPortConfigure("port1","/dev/ttyS1",0,0,0)
drvAsynTCPPortConfigure("port2","164.54.9.90:4001",0,0)
drvAsynTCPPortConfigure("port3","164.54.9.90:4002",0,0)
drvAsynTCPPortConfigure("port4","164.54.9.90:4003",0,0)
drvAsynTCPPortConfigure("port5","164.54.9.90:4004",0,0)
initSerialServer("serial0","port0",0,1000,2,"")
initSerialServer("serial1","port1",0,1000,2,"")
initSerialServer("serial2","port2",0,1000,2,"")
initSerialServer("serial3","port3",0,1000,2,"")
initSerialServer("serial4","port4",0,1000,2,"")
initSerialServer("serial5","port5",0,1000,2,"")

Example st.cmd for a vxWorks IOC

The vxWorks support uses the standard vxWorks serial ports and/or drvIpac.. The following example shows how to configure support for an mv162 . See the drvIpac documentation for how to configure other carriers. Note that the ipacAddCarrier command must be on a single line. The example starts one mv162 serial port and eight serial ports for a Green Springs OctalUART Industry Pack module.

...
localMessageRouterStart(1)

#The following starts code that uses tyGSOctal.c
ipacAddMVME162("A:l=3,3 m=0xe0000000,64;B:l=3,3 m=e0010000,64")
tyGSOctalDrv(1)
tyGSOctalModuleInit("RS232", 0x80, 0, 1)

tyGSOctalDevCreate("/tyGS/0/0",0,0,1000,1000)
drvAsynSerialPortConfigure("tyGS00","/tyGS/0/0",0,0,0)
# example of all port options
asynSetOption("tyGS00",0,"baud","38400")
asynSetOption("tyGS00",0,"bits","8")
asynSetOption("tyGS00",0,"parity","none")
asynSetOption("tyGS00",0,"stop","1")
asynSetOption("tyGS00",0,"clocal","Y")
asynSetOption("tyGS00",0,"crtscts","N")
initSerialServer("serial0","tyGS00",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/1",0,1,1000,1000)
drvAsynSerialPortConfigure("tyGS01","/tyGS/0/1",0,0,0)
asynSetOption("tyGS01",0,"baud","38400")
initSerialServer("serial1","tyGS01",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/2",0,2,1000,1000)
drvAsynSerialPortConfigure("tyGS02","/tyGS/0/2",0,0,0)
asynSetOption("tyGS02",0,"baud","38400")
initSerialServer("serial2","tyGS02",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/3",0,3,1000,1000)
drvAsynSerialPortConfigure("tyGS03","/tyGS/0/3",0,0,0)
asynSetOption("tyGS03",0,"baud","38400")
initSerialServer("serial3","tyGS03",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/4",0,4,1000,1000)
drvAsynSerialPortConfigure("tyGS04","/tyGS/0/4",0,0,0)
asynSetOption("tyGS04",0,"baud","38400")
initSerialServer("serial4","tyGS04",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/5",0,5,1000,1000)
drvAsynSerialPortConfigure("tyGS05","/tyGS/0/5",0,0,0)
asynSetOption("tyGS05",0,"baud","38400")
initSerialServer("serial5","tyGS05",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/6",0,6,1000,1000)
drvAsynSerialPortConfigure("tyGS06","/tyGS/0/6",0,0,0)
asynSetOption("tyGS06",0,"baud","38400")
initSerialServer("serial6","tyGS06",0,100,2,"")

tyGSOctalDevCreate("/tyGS/0/7",0,7,1000,1000)
drvAsynSerialPortConfigure("tyGS07","/tyGS/0/7",0,0,0)
asynSetOption("tyGS07",0,"baud","38400")
initSerialServer("serial7","tyGS07",0,100,2,"")
...

Serial Support Theory

mpfSerial support consists of three levels:

mpfSerial provides code for all the first two levels and uses asynDriver for the lowest level. It is designed so that additional code can be written at either bthe Device Support or Serial server levels in order to support special requirements.

The mpfSerial support interface is described in serialServer.h

serialServer.h

The interface between device support and the serial server. The definitions use fields of messages that are defined by MPF.

#define cmdWrite        0x1
#define cmdRead         0x2
#define cmdWriteRead    (cmdWrite|cmdRead)
//Following are additional options
#define cmdFlush        0x4
#define cmdSetEom       0x8
//Following are for serialGetConfig
#define cmdGetConfig    0x1
/*
 * cmdFlush - flush before starting write and/or read
 * cmdSetEom - change current eom
 *
 * for example the following could be specified
 * message->cmd = cmdWriteRead|cmdFlush;
*/
/* Generic serial server messages

SerialConfigMessage.
   This just calls SerialPort::config.
   It returns an Int32Message. status is 0 for success.

Int32Message.
       cmdGetConfig
              Returns a SerialConfigMessage with current settings
Char8ArrayMessage
       cmdWrite        : write request
       cmdRead         : read request
       cmdWriteRead    : writeRead request

       other fields from input message

       timeoutUnits     timeout units.
       timeout          The timeout value in timeoutUnits
       extra            If >0 the maximum number of chars to read

       If cmdSetEom is set

       eomSize          If 0 dont look for eom; otherwise must be 1 or 2
       eomString        1 or 2 character eomString

       Return values
                        A Char8ArrayMessage message, which
                        contains any input characters read

                        status is one of the serialStatus values
*/

serialServer.h is the interface between device suport and a serial server.

Some serial devices have a protocal that is not compatible with serialServer and/or devStringMpf. In such cases new device support and/or a new server must be supplied. SerialServer and devStringMpf provide a model of how to write special support. If a special application requires BOTH new device support and a new server than serialServer.h may not be applicable. If, however, either serialserver or devStringMpf is used then the conventions described in serialServer.h must be followed.

devStringMpf

NOTE: Perhaps NO current mpfSerial users use this for device support, because it provides no nice way of handling termination characters.

devStringMpf implements three versions of device support, corresponding to the three commands that can be given to serialServer. The first two 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, i.e. the location specified via the initXXXMessageRouter commands for MPF.
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:

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>")
}

serialServer

serialServer is a generic server which implements the serialSever.h interface and communicates with low level drivers via the SerialPort interface. It accepts SerialConfig, Int32, and 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. The status field in Int32Message is one of the serialStatus values. Low level drivers may only support a subset of the config options.

If an Int32Message is sent to serialServer, the only valid command is cmdGetConfig. It returns a SerialConfigMessage with the current configuration. Low level drivers may only support a subset of the config options.

When serialServer receives a Char8Array message it returns  a Char8Array message. The return message contains any characters read and a serialStatus value.
 

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 exactly 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 cmd field can also contain the following options:
 

cmdFlush Flush before starting I/O.
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. This field must be set. The field timeoutUnits determines if the units are milliSeconds or seconds.
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.

License Agreement

Copyright (c) 2002 University of Chicago. All rights reserved.

MPF is distributed subject to the following license conditions:

 SOFTWARE LICENSE AGREEMENT
 Software: MPF

 1. The "Software", below, refers to mpf (in either source code, or
    binary form and accompanying documentation). Each licensee is
    addressed as "you" or "Licensee."

 2. The copyright holders shown above and their third-party licensors
    hereby grant Licensee a royalty-free nonexclusive license, subject to
    the limitations stated herein and U.S. Government license rights.

 3. You may modify and make a copy or copies of the Software for use
    within your organization, if you meet the following conditions:
      a. Copies in source code must include the copyright notice and this
         Software License Agreement.
      b. Copies in binary form must include the copyright notice and this
         Software License Agreement in the documentation and/or other
         materials provided with the copy.

 4. You may modify a copy or copies of the Software or any portion of it,
    thus forming a work based on the Software, and distribute copies of
    such work outside your organization, if you meet all of the following
    conditions:
      a. Copies in source code must include the copyright notice and this
         Software License Agreement;
      b. Copies in binary form must include the copyright notice and this
         Software License Agreement in the documentation and/or other
         materials provided with the copy;
      c. Modified copies and works based on the Software must carry
         prominent notices stating that you changed specified portions of
         the Software.

 5. Portions of the Software resulted from work developed under a U.S.
    Government contract and are subject to the following license: the
    Government is granted for itself and others acting on its behalf a
    paid-up, nonexclusive, irrevocable worldwide license in this computer
    software to reproduce, prepare derivative works, and perform publicly
    and display publicly.

 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY
    OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE
    UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR
    EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
    BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
    FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME
    ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS,
    OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE
    SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT
    THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE
    OR THAT ANY ERRORS WILL BE CORRECTED.

 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR
    THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT
    OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE,
    INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY
    REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF
    CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR
    OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE
    POSSIBILITY OF SUCH LOSS OR DAMAGES.