StreamDevice already comes with an interface to asynDriver. You should first try to implement your bus driver compatible to asynDriver. Then it can be used by StreamDevice automatically. Only if that does not work, write your own bus interface.
A bus interface is a C++ class that inherits from
StreamBusInterface.
Its purpose is to provide an interface to StreamDevice for a
low-level I/O bus driver.
StreamDevice acts as a client of the interface, calling interface
methods and receiving replies via callbacks.
Since the internal details of StreamDevice are not of
interest to a bus interface, I will reference it simply as
client in this chapter.
The interface class must be registered via a call to
RegisterStreamBusInterface()
in the global context of the C++ file (not in a header file).
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.
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. 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.
#include <StreamBusInterface.h> class MyInterface : StreamBusInterface { // ... (internally used attributes and methods) MyInterface(Client* client); ~MyInterface(); // StreamBusInterface virtual methods bool lockRequest(unsigned long lockTimeout_ms); bool unlock(); bool writeRequest(const void* output, size_t size, unsigned long writeTimeout_ms); bool readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms, long expectedLength, bool async); bool acceptEvent(unsigned long mask, unsigned long replytimeout_ms); const char* busName(); void release(); bool supportsEvent(); bool supportsAsyncRead(); public: // creator method static StreamBusInterface* getBusInterface( Client* client, const char* busname, int addr, const char* param); }; // ... (implementation) RegisterStreamBusInterface(MyInterface);
The interface class must implement a public static creator method:
static StreamBusInterface*
getBusInterface(Client* client,
const char* busname, int addr,
const char* param);
And it must implement the following pure virtual methods:
bool lockRequest(unsigned long lockTimeout_ms);
bool unlock();
bool writeRequest(const void* output,
size_t size, unsigned long writeTimeout_ms);
bool readRequest(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms,
long expectedLength, bool async);
It may implement additional virtual methods if the bus supports events and/or asynchronous reads:
bool supportsAsyncRead();
bool supportsEvent();
bool acceptEvent(unsigned long mask,
unsigned long replytimeout_ms);
It also may override the following virtual methods:
bool setEos(const char* eos,
size_t eoslen);
void release();
The base class StreamBusInterface implements a set of protected callback methods which should be called in response to the above request methods:
void lockCallback(IoStatus status);
void writeCallback(IoStatus status);
long readCallback(IoStatus status,
const void* input = NULL,
long size = 0);
void eventCallback(IoStatus status);
StreamBusInterface(Client* client);
long priority();
const char* clientName();
const char* eos;
size_t eoslen;
enum IoStatus {ioSuccess, ioTimeout, ioNoReply, ioEnd, ioFault};
RegisterStreamBusInterface(interfaceClass);
During initialization, the macro RegisterStreamBusInterface()
registers the bus interface.
It must be called exactly once for each bus interface class in global
file context.
static StreamBusInterface* getBusInterface(Client* client,
const char* busname, int addr,
const char* param);
StreamBusInterface(Client* client);
void release();
const char* clientName();
During startup, each client instance searches for its bus interface
by name.
It does so by calling the static getBusInterface()
method
of every registered interface class.
This method should check by busname
if its interface class
is responsible for that bus.
If yes, it should check if the address addr
is valid and
associate a device with busname
/addr
.
Some busses do not have addresses and allow only one device
(e.g. RS232).
Interfaces to such busses can ignore addr
.
The bus interface may then try to connect to the device, but it should
allow it to be disconnected or switched off at the moment.
If the bus interface requires additional parameters, parse the
param
string.
Your constructor should pass client
to the base class
constructor StreamBusInterface(Client* client)
.
On success, getBusInterface
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 clientName()
.
This name is for use in error and log messages.
On failure, or if this interface class is not responsible for that bus,
getBusInterface should return NULL
.
The client will then try other bus interface classes.
When the client does not need the interface any more, it calls
release()
.
The default implementation of release()
assumes that
getBusInterface()
has allocated a new bus interface
and just calls delete
.
You should change release()
if that assumption is not
correct.
bool lockRequest(unsigned long lockTimeout_ms);
void lockCallback(IoStatus status);
bool unlock();
long priority();
Before doing output, the client calls lockRequest()
to get
exclusive access to the device.
This function should return immediately.
If the device is already locked, the bus interface should add itself to
a queue, sorted by priority()
.
As soon as the device is available, the bus interface should call
lockCallback(ioSuccess)
.
If the bus cannot be locked within lockTimeout_ms
milliseconds, the bus interface should call
lockCallback(ioTimeout)
.
If a device cannot be locked, for example because it is known to be
offline, lockCallback(ioFault)
may be called.
Normally, it is not necessary to lock the complete bus but only one devices (i.e. one address). Other clients should still be able to talk to other devices on the same bus.
The client may perform several read and write operations when it has
locked the device.
When the dialog with the device has ended, the client calls
unlock()
.
If other bus interfaces are in the lock queue, the next one should
call lockCallback(ioSuccess)
now.
bool writeRequest(const void* output,
size_t size, unsigned long writeTimeout_ms);
void writeCallback(IoStatus status);
To start output, the client calls writeRequest()
.
You can safely assume that the device has already been locked at this
time.
That means, no other client will call writeRequest()
for this device and no other output is currently active for this device
until it has been unlocked.
The function should arrange transmission of size
bytes of
output
but return immediately without blocking.
After all output has been successfully transmitted, but not earlier, the
interface should call writeCallback(ioSuccess)
.
If output blocks for writeTimeout_ms
milliseconds,
the interface should abort the transmision and call
writeCallback(ioTimeout)
.
If output is impossible, e.g. because the device is known to be offline,
the interface may call writeCallback(ioFault)
.
The interface must transmit excactly the size
bytes
from output
.
It must not append or change anything and it should not assume that
any bytes have a special meaning.
In particular, a null byte does not terminate output
and
no terminator must be added.
The buffer referenced by output
stays valid until
writeCallback()
is called.
unlock()
after
writeCallback()
has been called.
bool readRequest(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms,
long expectedLength, bool async);
long readCallback(IoStatus status,
const void* input = NULL,
long size = 0);
bool setEos(const char* eos,
size_t eoslen);
bool supportsAsyncRead();
To prepare for input, the client calls readRequest()
.
This can also happen on an unlocked device.
Dirk Zimoch, 2006