Q) Does anyone have any C sample code showing how to use the serial port under OS/2 using DosOpen() and DosDevIoCtl()?
A) As a matter of fact, yes. :-)
-=-= extracted from a silly & specialized program =-=-=-=-==-=-=
/* dtp.c -- D-dial Terminal Program: the first hack */
//-- an annoying detail
#define INCL_BASE
#define INCL_DOSDEVIOCTL /* docs lie, this is NOT included by BASE */
#include <os2.h>
//-- initialization (in main() as written)
HFILE portFd;
ULONG action;
if (DosOpen("COM1", &portFd, &action, 0, FILE_NORMAL, FILE_OPEN,
OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE, 0) != 0) {
fprintf(stderr, "Open of COM1 failed\n");
goto error0;
}
{ DCBINFO di;
ULONG dummy;
dummy = sizeof(di);
if (DosDevIOCtl(portFd, IOCTL_ASYNC, ASYNC_GETDCBINFO, 0, 0, 0,
&di, sizeof(di), &dummy) != 0)
fprintf(stderr, "DosDevIOCtl failed\n");
else {
fprintf(stderr, "Timeouts: read = %u, write = %u\n",
di.usWriteTimeout, di.usReadTimeout);
fprintf(stderr, "Flag bytes: %02x, %02x, %02x\n",
di.fbCtlHndShake, di.fbFlowReplace, di.fbTimeout);
}
di.fbTimeout = (di.fbTimeout & ~(3 << 1)) | (2 << 1);
/* rcv = wait-for-something */
di.usReadTimeout = 250;
dummy = sizeof(di);
if (DosDevIOCtl(portFd, IOCTL_ASYNC, ASYNC_SETDCBINFO,
&di, sizeof(di), &dummy, 0, 0, 0) != 0)
fprintf(stderr, "DosDevIOCtl failed to set parameters!\a\n");
}
if (initSerialOutput(portFd) != 0)
goto error1;
//-- the "running" variable is sort of a relic, I think
running = 1;
if (_beginthread(serialInputThread, 0, 8192, (void *)portFd) < 0) {
fprintf(stderr, "can't start serial input thread\n");
goto error2;
}
if (_beginthread(serialOutputThread, 0, 8192, (void *)portFd) < 0) {
fprintf(stderr, "can't start serial output thread\n");
goto error2;
}
//-- wrapup code
shutdownSerialOutput();
DosClose(portFd);
//-- the rest of this lives outside of main()...
//-- input side: gory details omitted
//-- the port has been setup in "wait for something" mode, so we can request
//-- more than one character at a time without blocking until the buffer is
//-- full. At least, I *think* that's working now: this is used with 300 baud
//-- systems, so it's hard to tell <g>. At least it isn't blocking until the
//-- buffer is filled...
/* serialInputThread -- reads port, writes to text window
*
* arg is the port's handle for reading
*/
void serialInputThread(void *arg)
{
HFILE inFd = (long)arg;
FILE *logFile;
UCHAR buf[10];
ParserState ps = {0};
logFile = fopen("dtp.log", "ab");
for ( ; ; )
{
ULONG n;
if (DosRead(inFd, buf, 10, &n) == 0)
{
ULONG i;
for (i = 0; i <n; ++i)
{
if (logFile != 0)
putc(buf[i], logFile);
if (runParser(&ps, buf[i]) != 0)
postChar(buf[i]);
}
}
}
}
/* output side: I rather like this arrangement using queues
except that I'd prefer an anonymous queue.
For this, having the queue named in the
filesystem's name space is at best a minor annoyance. */
/* * * * SerialOutput subsystem
*/
#define MAX_CHUNK_SIZE 50
typedef struct
{
USHORT nUsed;
UCHAR buf[MAX_CHUNK_SIZE];
} SO_CHUNK;
#define NUM_SO_CHUNKS 6
HQUEUE soQueue, freeQueue;
int initSerialOutput (HFILE outFd)
{
(void) outFd; /* reserved for more general version */
if (DosCreateQueue
(&soQueue, QUE_FIFO, "\\queues\\dtp\\soQueue") != 0)
{
fprintf(stderr, "Failed to create serial output queue\n");
goto error0;
}
if (DosCreateQueue
(&freeQueue, QUE_FIFO, "\\queues\\dtp\\freeQueue") != 0)
{
fprintf(stderr, "Failed to create serial free queue\n");
goto error1;
}
{ SO_CHUNK *p = malloc(sizeof(SO_CHUNK) * NUM_SO_CHUNKS);
int i;
if (p == 0)
{
fprintf(stderr,
"Failed to allocate memory for serial chunks\n");
goto error1;
}
for (i = NUM_SO_CHUNKS; 0 < i; --i)
if (DosWriteQueue(freeQueue, 0, sizeof(SO_CHUNK), p++, 0)
!= 0)
{
fprintf(stderr, "Failed to initialize free queue\n");
goto error1;
}
}
return 0;
error1:
DosCloseQueue(soQueue);
error0:
return -1;
}
void shutdownSerialOutput(void)
{
DosCloseQueue(freeQueue);
DosCloseQueue(soQueue);
}
void writeSerial(UCHAR const *buf, USHORT n)
{
while (0 < n)
{
REQUESTDATA rd;
ULONG dataLength;
PVOID data;
BYTE priority;
if (DosReadQueue(freeQueue, &rd, &dataLength, &data,
0, DCWW_WAIT, &priority, 0) == 0)
{
SO_CHUNK *sc = data;
USHORT m = MAX_CHUNK_SIZE;
if (n < m)
m = n;
memcpy(sc->buf, buf, m);
sc->nUsed = m;
DosWriteQueue(soQueue, 0, sizeof(SO_CHUNK), sc, 0);
buf += m;
n -= m;
}
}
}
void writeSerialString(UCHAR const *buf)
{
writeSerial(buf, strlen(buf));
}
void serialOutputThread(void *arg)
{
HFILE outFd = (long)arg;
REQUESTDATA rd;
ULONG dataLength;
PVOID data;
BYTE priority;
for ( ; ; )
{
if (DosReadQueue(soQueue, &rd, &dataLength, &data,
0, DCWW_WAIT, &priority, 0) == 0)
{
if (rd.ulData == 0) { /* simple data block */
ULONG dummy;
SO_CHUNK *sc = data;
DosWrite(outFd, sc->buf, sc->nUsed, &dummy);
DosWriteQueue(freeQueue, 0, sizeof(SO_CHUNK), sc, 0);
}
else
; /* anything else is a test, ignore it */
}
}
}
The intention was that control messages could be posted to the queue using null data packets (passing the actual message in the REQUESTDATA.ulData field); these would allow for controlling the port's baud rate and other settings. This seems to work under 2.0, and even appears to be intended to work (Deitel & Kogan's description), but I haven't done anything with it yet, as you can see. Haven't needed the facility yet...
Credit: Martin Maney