HOWTO- handle serial ports in QNX (and probably unix in general)

1.0 Introduction

Under QNX, the kernel only handles message routing between processes. Dev is the device manager and oversees various device drivers. Dev.ser drives the serial ports on PC architectures. The port parameters (baud rate, parity,...) can be set by Dev.ser in /etc/sysinit, or they can be altered in a shell by stty or within a user process by tcsetattr(). Processes can detect the state of the serial port with tcgetattr(). At the shell level, use "stty < /dev/ser". Most of this was figured out by trial and error with John Staren and by following examples in qtalk 1.0 by Quantum Software Systems.

2.0 C code

2.1 Define the basic data type

The examples are b_port.h and b_port.c. Within the include file, we define a structure,

typedef struct {
*FILE ifp,ofp; /*stream pointers to the serial port*/
int ifd,ofd; /*file descriptors for the stream pointers*/
char obuf[BUFLEN],ibuf[BUFLEN]; /*buffers for handling the streams*/
} PORT;

where we have separated the input and output channels. This is easiest, since then you can be sloppy about the handshaking INSIDE the code. You still have to be careful with the hardware. The buffers are merely conveniences.

2.2 Initialize

A sample initial code is:

PORT *InitPort(PORT *port,char *portlocation) {
}

Here portlocation is the string of the pathname to the serial port, such as "/dev/ser1".

By using pointers to PORTs, one can define them with

PORT *serport;

This is a convenient method since it can become an array of ports by allocating memory BEFORE initializing. For example:

serport = (PORT *)calloc(NUMBEROPORTS,sizeof(PORT));

which allocates enough contiguous memory for NUMBEROPORTS serial port structures. Then they can be referred by serport, serport+1, ... serport+NUMBEROPORTS. This may be too abstract and one may wish to define aliases in the preprocessor, i.e. #define COMPORT2 serport+1 or #define SERCONSOLE serport+4.

2.3 Reading and Writing

One can now write to a port with fprintf(). For example,

void swritep(PORT *sp, char *buffer) { 
}

Reading is a bit more problematic because we have to decide when the port has failed to read anything or when the reception is finished. Under QNX one can conveniently use dev_read() which contains timers to to quit the read when the transmission stops or becomes slow. Example:

int sreadp(PORT *sp) { 

See the Watcom library manuals for a description of dev_read. dev_*() functions require the file descriptor, which is why we add it to the PORT structure.

In these examples we simply pass strings back and forth. Other ways of doing it might be to pass a union if the device you were talking to was going to hand back bytes only.

Error checking has to be performed above the sreadp(), swritep() level.