Files
StreamDevice/doc/businterface.html
2011-09-19 09:30:00 +00:00

646 lines
24 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Bus API</title>
<link rel="shortcut icon" href="favicon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<iframe src="nav.html" id="navleft"></iframe>
<h1>Bus API</h1>
<a name="class"></a>
<h2>Bus Interface Class</h2>
<p>
<em>StreamDevice</em> already comes with an interface to <a target="ex"
href="http://www.aps.anl.gov/epics/modules/soft/asyn/">
<em>asynDriver</em></a>.
You should first try to implement your bus driver compatible to
<em>asynDriver</em>.
Then it can be used by <em>StreamDevice</em> automatically.
Only if that does not work, write your own bus interface.
</p>
<p>
A bus interface is a C++ class that inherits from
<em>StreamBusInterface</em>.
Its purpose is to provide an interface to <em>StreamDevice</em> for a
low-level I/O bus driver.
<em>StreamDevice</em> acts as a client of the interface, calling interface
methods and receiving replies via callbacks.
Since the internal details of <em>StreamDevice</em> are not of
interest to a bus interface, I will reference it simply as
<em>client</em> in this chapter.
The interface class must be registered via a call to
<code><a href="#registration">
RegisterStreamBusInterface</a>()</code>
in the global context of the C++ file (not in a header file).
</p>
<p>
Interface methods called by the client must not block for arbitrary
long times.
That means the interface is allowed to take mutex semaphores to protect
its internal data structures but it must not take event semaphores to
wait for external I/O or similar.
</p>
<p>
It is assumed that the interface creates a separate thread to handle
blocking I/O and to call the callback methods in the context of that
thread when I/O has completed or timed out.
The callback methods don't block but may in turn call interface methods.
Much of the actual work will be done in the context of those callbacks,
i.e. in the interface thread, thus be generous with stack.
</p>
<h3>Example bus interface class declaration</h3>
<pre>
#include &lt;StreamBusInterface.h&gt;
class MyInterface : StreamBusInterface
{
// ... (internally used attributes and methods)
<a href="#create">MyInterface</a>(Client* client);
<a href="#create">~MyInterface</a>();
// StreamBusInterface virtual methods
bool <a href="#lock">lockRequest</a>(unsigned long lockTimeout_ms);
bool <a href="#lock">unlock</a>();
bool <a href="#write">writeRequest</a>(const void* output, size_t size,
unsigned long writeTimeout_ms);
bool <a href="#read">readRequest</a>(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms,
long expectedLength, bool async);
bool <a href="#read">supportsAsyncRead</a>();
bool <a href="#event">supportsEvent</a>();
bool <a href="#event">acceptEvent</a>(unsigned long mask,
unsigned long replytimeout_ms);
bool <a href="#connect">connectRequest</a>(unsigned long connecttimeout_ms);
bool <a href="#connect">disconnect</a>();
void <a href="#lock">finish</a>();
public:
// creator method
static StreamBusInterface* <a href="#create">getBusInterface</a>(
Client* client, const char* busname,
int addr, const char* param);
};
<a href="#registration">RegisterStreamBusInterface</a>(MyInterface);
// ... (implementation)
</pre>
<h3>Methods to implement</h3>
<p>
The interface class must implement a public static creator method:
</p>
<div class="indent"><code>
static StreamBusInterface*
<a href="#create">getBusInterface</a>(Client*&nbsp;client,
const&nbsp;char*&nbsp;busname, int&nbsp;addr,
const&nbsp;char*&nbsp;param);
</code></div>
<p>
And it must implement the following pure virtual methods:
</p>
<div class="indent"><code>
bool <a href="#lock">lockRequest</a>(unsigned&nbsp;long&nbsp;lockTimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#lock">unlock</a>();
</code></div>
<p>
It may implement additional virtual methods if the bus supports it:
</p>
<div class="indent"><code>
bool <a href="#write">writeRequest</a>(const&nbsp;void*&nbsp;output,
size_t&nbsp;size, unsigned&nbsp;long&nbsp;writeTimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#read">readRequest</a>(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
long&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
bool <a href="#read">supportsAsyncRead</a>();
</code></div>
<div class="indent"><code>
bool <a href="#event">supportsEvent</a>();
</code></div>
<div class="indent"><code>
bool <a href="#event">acceptEvent</a>(unsigned&nbsp;long&nbsp;mask,
unsigned&nbsp;long&nbsp;replytimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#connect">connectRequest</a>(unsigned&nbsp;long&nbsp;connecttimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#connect">disconnect</a>();
</code></div>
<div class="indent"><code>
void <a href="#lock">finish</a>();
</code></div>
<p>
It also may override the following virtual method:
</p>
<div class="indent"><code>
void <a href="#create">release</a>();
</code></div>
<h3>Callback methods provided</h3>
<p>
The base class <em>StreamBusInterface</em> implements a set of protected
callback methods which must be called in response to the above request
methods (most probably from another thread):
</p>
<div class="indent"><code>
void <a href="#lock">lockCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#write">writeCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
long <a href="#read">readCallback</a>(StreamIoStatus&nbsp;status,
const&nbsp;void*&nbsp;input&nbsp;=&nbsp;NULL,
long&nbsp;size&nbsp;=&nbsp;0);
</code></div>
<div class="indent"><code>
void <a href="#event">eventCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#connect">connectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#connect">disconnectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<h3>Other provided methods, attibutes, and types</h3>
<div class="indent"><code>
<a href="#create">StreamBusInterface</a>(Client*&nbsp;client);
</code></div>
<div class="indent"><code>
long <a href="#lock">priority</a>();
</code></div>
<div class="indent"><code>
const char* <a href="#create">clientName</a>();
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;<a href="#write">getOutTerminator</a>(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;<a href="#read">getInTerminator</a>(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
enum&nbsp;StreamIoStatus {StreamIoSuccess, StreamIoTimeout, StreamIoNoReply, StreamIoEnd, StreamIoFault};
</code></div>
<a name="theory"></a>
<h2>Theory of Operation</h2>
<a name="registration"></a>
<h3>Registration</h3>
<div class="indent"><code>
RegisterStreamBusInterface(<i>interfaceClass</i>);
</code></div>
<p>
During initialization, the macro <code>RegisterStreamBusInterface()</code>
registers the bus interface.
It must be called exactly once for each bus interface class in global
file context.
</p>
<a name="create"></a>
<h3>Creation and deletion</h3>
<div class="indent"><code>
static StreamBusInterface* getBusInterface(Client*&nbsp;client,
const&nbsp;char*&nbsp;busname, int&nbsp;addr,
const&nbsp;char*&nbsp;param);
</code></div>
<div class="indent"><code>
StreamBusInterface(Client*&nbsp;client);
</code></div>
<div class="indent"><code>
void release();
</code></div>
<div class="indent"><code>
const char* clientName();
</code></div>
<p>
During startup, each client instance searches for its bus interface
by name.
It does so by calling the static <code>getBusInterface()</code> method
of every registered interface class.
This method should check by <code>busname</code> if its interface class
is responsible for that bus.
If yes, it should check if the address <code>addr</code> is valid and
associate a <em>device</em> with <code>busname</code>/<code>addr</code>.
Some busses do not have addresses and allow only one device
(e.g. RS232).
Interfaces to such busses can ignore <code>addr</code>.
The bus interface may then try to connect to the device, but it should
allow it to be disconnected or switched off at that time.
If the bus interface requires additional parameters, parse the
<code>param</code> string.
Your constructor should pass <code>client</code> to the base class
constructor <code>StreamBusInterface(Client* client)</code>.
</p>
<p>
On success, <code>getBusInterface</code> should then return a pointer
to a bus interface instance.
Note that many client instances may want to connect to the same device.
Each needs its own bus interface instance.
The bus interface can get a string containing the name of the
client instance from <code>clientName()</code>.
This name is for use in error and log messages.
</p>
<p>
On failure, or if this interface class is not responsible for that bus,
<code>getBusInterface</code> should return <code>NULL</code>.
The client will then try other bus interface classes.
</p>
<p>
When the client does not need the interface any more, it calls
<code>release()</code>.
The default implementation of <code>release()</code> assumes that
<code>getBusInterface()</code> has allocated a new bus interface
and just calls <code>delete</code>.
You should change <code>release()</code> if that assumption is not
correct.
</p>
<a name="connect"></a>
<h3>Connecting and disconnecting</h3>
<div class="indent"><code>
bool connectRequest(unsigned&nbsp;long&nbsp;connecttimeout_ms);
</code></div>
<div class="indent"><code>
bool disconnect();
</code></div>
<div class="indent"><code>
void connectCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void disconnectCallback(IoStatus&nbsp;status);
</code></div>
<p>
Connection should be handled automatically.
If the device is disconnected, each attempt to access the
device should try to (re-)connect.
Normally, the interface should not try to disconnect unless
the device does so.
</p>
<p>
However, sometimes the client wants to connect or
disconnect explicitely.
To connect, the client calls <code>connectRequest()</code>.
This function should return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted or connection
handling is not supported.
The interface should call <code>connectCallback(StreamIoSuccess)</code>
once the bus could be connected.
If the bus cannot be connected within <code>connecttimeout_ms</code>
milliseconds, the bus interface should call
<code>connectCallback(StreamIoTimeout)</code>.
</p>
<p>
If a device cannot be connected, for example because there is
something wrong with the I/O hardware,
<code>connectCallback(StreamIoFault)</code> may be called.
</p>
<p>
To disconnect, the client calls <code>disconnectRequest()</code>;
This function should return <code>true</code> immediately or
<code>false</code> if the request cannot be accepted or connection
handling is not supported.
The interface should call <code>connectCallback(StreamIoSuccess)</code>
once the bus is disconnected. There is no timeout for this operation.
If disconnecting is impossible, the interface should call
<code>connectCallback(StreamIoFault)</code>.
</p>
<a name="lock"></a>
<h3>Bus locking</h3>
<div class="indent"><code>
bool lockRequest(unsigned&nbsp;long&nbsp;lockTimeout_ms);
</code></div>
<div class="indent"><code>
void lockCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
bool unlock();
</code></div>
<div class="indent"><code>
long priority();
</code></div>
<div class="indent"><code>
void finish();
</code></div>
<p>
Before doing output, the client calls <code>lockRequest()</code> to get
exclusive access to the device.
This function should return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
If the device is already locked, the bus interface should add itself to
a queue, sorted by <code>priority()</code>.
As soon as the device is available, the bus interface should call
<code>lockCallback(StreamIoSuccess)</code>.
If the bus cannot be locked within <code>lockTimeout_ms</code>
milliseconds, the bus interface should call
<code>lockCallback(StreamIoTimeout)</code>.
</p>
<p>
If a device cannot be locked, for example because there is
something wrong with the I/O hardware,
<code>lockCallback(StreamIoFault)</code> may be called.
</p>
<p>
Normally, it is not necessary to lock the complete bus but only one
device (i.e. one address).
Other clients should still be able to talk to other devices on the same bus.
</p>
<p>
The client may perform several read and write operations when it has
locked the device.
When the protocol ends and the device is locked, the client calls
<code>unlock()</code>.
If other bus interfaces are in the lock queue, the next one should
call <code>lockCallback(StreamIoSuccess)</code> now.
</p>
<p>
The client calls <code>finish()</code> when the protocol ends.
This allows the bus interface to clean up.
The bus interface should also cancel any outstanding requests of
this client.
</p>
<a name="write"></a>
<h3>Writing output</h3>
<div class="indent"><code>
bool writeRequest(const&nbsp;void*&nbsp;output,
size_t&nbsp;size, unsigned&nbsp;long&nbsp;writeTimeout_ms);
</code></div>
<div class="indent"><code>
void writeCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;getOutTerminator(size_t&&nbsp;length);
</code></div>
<p>
To start output, the client calls <code>writeRequest()</code>.
You can safely assume that the device has already been locked at this
time.
That means, no other client will call <code>writeRequest()</code>
for this device and no other output is currently active for this device
until it has been unlocked.
</p>
<p>
The function should arrange transmission of <code>size</code> bytes of
<code>output</code> but return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
It must not block until output has completed.
After all output has been successfully transmitted, but not earlier, the
interface should call <code>writeCallback(StreamIoSuccess)</code>.
</p>
<p>
If output blocks for <code>writeTimeout_ms</code> milliseconds,
the interface should abort the transmision and call
<code>writeCallback(StreamIoTimeout)</code>.
</p>
<p>
If output is impossible, for example because there is
something wrong with the I/O hardware,
<code>writeCallback(StreamIoFault)</code> may be called.
</p>
<p>
The interface must transmit excactly the <code>size</code> bytes
from <code>output</code>.
It must not change anything and it should not assume that
any bytes have a special meaning.
In particular, a null byte does not terminate <code>output</code>.
</p>
<p>
A call to <code>getOutTerminator</code> tells the interface which
terminator has already been added to the output.
If <code>NULL</code> was returned, the client is not aware of a
terminator (no outTerminator was defined in the protocol).
In this case, the interface may add a terminator which it knows from
other sources.
An interface is not required to support <code>NULL</code> results
and may not add any terminator in this case.
</p>
<p>
The buffer referenced by <code>output</code> stays valid until
<code>writeCallback()</code> is called.
</p>
<p>
The client may request more I/O or call <code>unlock()</code> after
<code>writeCallback()</code> has been called.
</p>
<a name="read"></a>
<h3>Reading input</h3>
<div class="indent"><code>
bool readRequest(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
long&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
long readCallback(IoStatus&nbsp;status,
const&nbsp;void*&nbsp;input&nbsp;=&nbsp;NULL,
long&nbsp;size&nbsp;=&nbsp;0);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;getInTerminator(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
bool supportsAsyncRead();
</code></div>
<p>
The client calls <code>readRequest()</code> to tell the bus interface
that it expects input.
Depending on the bus, this function might have to set the bus hardware
into receive mode.
If <code>expectedLength&gt;0</code>, the the bus interface should stop
input after this number of bytes have been received.
In opposite to writing, the device may be in a non-locked status when
<code>readRequest()</code> is called.
</p>
<p>
This function must not block until input is available.
Instead, it should arrange for
<code>readCallback(StreamIoSuccess, buffer, size)</code> to be called
when input has been received and return <code>true</code>
immediately or <code>false</code> if the request cannot be accepted.
</p>
<p>
Here, <code>buffer</code> is a pointer to
<code>size</code> input bytes.
The bus interface is responsible for the buffer.
The client copies its contents. It does not modify or free it.
</p>
<p>
It is not necessary to wait until all data has been received.
The bus interface can call <code>n=readCallback()</code> after
any amount of input has been received.
If the client needs more input, <code>readCallback()</code>
returns a non-zero value.
A positive <code>n</code> means, the client needs another
<code>n</code> bytes of input.
A negative <code>n</code> means, the client needs an unspecified
amount of additional input.
</p>
<p>
With some bus interfaces, <code>readRequest()</code> might not have to
do anything because the bus is always receiving.
It might also be that the bus has no local buffer associated to store
input before it is fetched with some <code>read()</code> call.
In this case, a race condition between device and client can occure.
To avoid loss of data, <code>readCallback(StreamIoSuccess, buffer, size)</code>
may be called in this case even before <code>readRequest()</code>.
If the client is expecting input in the next future, it will store it.
Otherwise the input is dropped.
</p>
<p>
The <code>replyTimeout_ms</code> parameter defines how many milliseconds
to wait for the first byte of a reply before the device is considered
offline.
If no input has been received after <code>replyTimeout_ms</code>
milliseconds, the bus interface should call
<code>readCallback(StreamIoNoReply)</code>.
</p>
<p>
The <code>readTimeout_ms</code> parameter is the maximum time to wait
for further input.
If input stops for longer than <code>readTimeout_ms</code> milliseconds
the bus interface should call
<code>readCallback(StreamIoTimeout,buffer,size)</code>.
The client decides if this timeout is an error or a legal termination.
Thus, pass all input received so far.
</p>
<p>
A call to <code>getInTerminator(length)</code> tells the interface which
terminator is expected for input and <code>length</code> is set to the
number of bytes of the terminator. The result is a hint to the bus
interface to recognize the end of an input.
Once the terminator string is found, the bus interface should stop
receiving input and call
<code>readCallback(StreamIoSuccess, buffer, size)</code>.
It is not necessary to remove the terminator string from the received input.
An empty terminator string (<code>length==0</code>) means:
Don't look for terminators.
</p>
<p>
If <code>NULL</code> was returned, the client is not aware of a
terminator (no inTerminator was defined in the protocol).
In this case, the interface may look for a terminator which it knows from
other sources, reduce size by the terminator length and call
<code>readCallback(StreamIoEnd, buffer, size)</code>.
A bus interface is not required to support <code>NULL</code> results and
may treat them as empty terminator (see above).
</p>
<p>
Some busses (e.g. GPIB) support special "end of message" signals.
If such a signal is received, the bus interface should call
<code>readCallback(StreamIoEnd, buffer, size)</code>.
Use it to indicate a special "end of message" signal which is not
visible in the normal byte data stream.
If <code>getInTerminator()</code> has not returned <code>NULL</code>
it it not necessary to remove a terminator which may come in
addition to the "end of message" signal.
</p>
<p>
If input is impossible, for example because there is
something wrong with the I/O hardware,
<code>readCallback(StreamIoFault)</code> may be called.
</p>
<p>
If the <code>async</code> flag is <code>true</code>, the client
wants to read input asyncronously without any timeout.
That means, the bus interface should call <code>readCallback()</code>
even if the input was requested by another client.
</p>
<p>
If a client wishes to receive asynchonous input, it first calls
<code>supportsAsyncRead()</code>.
The default implementation of this method always returns
<code>false</code>.
A bus interface may overwrite this method to return <code>true</code>
and eventually prepare for asynchonous input.
The client is then allowed to call <code>readRequest()</code> with
the <code>async==true</code>.
This means that the client is interested in any input.
It should receive a <code>readCallback()</code> of all input which came
in response to a synchonous (<code>async==false</code>) request from
another client (which of course should receive the input, too).
The interface should also receive asynchonous input when no
synchonous client is active at the moment.
Many asynchonous <code>readRequest()</code> calls from different clients
may be active at the same time.
All of them should receive the same input.
</p>
<p>
For asynchonous requests, <code>replyTimeout_ms</code> has a different
meaning: If the bus interface has to poll the bus for input, it may take
<code>replyTimeout_ms</code> as a hint for the poll period.
If many asynchonous requests are active at the same time, it should poll
with the shortest period of all clients.
An asynchonous request does not time out.
It stays active until the next input arrives.
The client may reissue the asynchronous <code>readRequest()</code>
from within the <code>readCallback()</code> if it wants to continue
receiving asynchonous input.
</p>
<p>
If the client calls <code>finish()</code> at any time, the bus
interface should cancel all outstanding requests, including
asynchonous read requests.
</p>
<a name="event"></a>
<h3>Handling events</h3>
<div class="indent"><code>
bool supportsEvent();
</code></div>
<div class="indent"><code>
bool acceptEvent(unsigned long mask, unsigned long replytimeout_ms);
</code></div>
<div class="indent"><code>
void eventCallback(StreamIoStatus status);
</code></div>
<p>
An event is a sort of input from a device which is not part of
the normal byte stream.
One example is the SRQ line of GPIB.
Not all bus types have events.
To support events, the bus interface must overwrite
<code>supportsEvent()</code> to return <code>true</code>.
The default implementation always returns <code>false</code>.
</p>
<p>
If <code>true</code> is returned, the client is allowed to call
<code>acceptEvent()</code>, where <code>mask</code> defines the
(bus dependent) type of event or events to wait for.
If <code>mask</code> is illegal, <code>acceptEvent()</code> should
return <code>false</code>.
The call to <code>acceptEvent()</code> must not block.
It should arrange to call <code>eventCallback(StreamIoSuccess)</code>
when the event matching <code>mask</code> arrives within
<code>replytimeout_ms</code> milliseconds.
If no such event arrives within this time, the bus interface
should call <code>eventCallback(StreamIoTimeout)</code>.
</p>
<p>
To avoid race conditions, the bus interface should buffer events and
also report a matching event which occured before the actual call
to <code>acceptEvent()</code> but after any previous call of any
other request method like <code>writeRequest()</code>.
</p>
<hr>
<p><small>Dirk Zimoch, 2007</small></p>
</body>
</html>