| 1 | UNIT SerialIO;
|
|---|
| 2 |
|
|---|
| 3 | INTERFACE
|
|---|
| 4 |
|
|---|
| 5 | {**************************************************************************
|
|---|
| 6 | * *
|
|---|
| 7 | * Written for Speed Pascal/2 *
|
|---|
| 8 | * Interface to Async communications under OS/2 *
|
|---|
| 9 | * Author: Alex T. Vermeulen (a.vermeulen@ecn.nl, atverm@xs4all.nl)*
|
|---|
| 10 | * Date: 17-4-95 *
|
|---|
| 11 | **************************************************************************}
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 | TYPE
|
|---|
| 16 | parityt = (par_none,par_odd,par_even,par_mark,par_space);
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 | FUNCTION initport (port_num:integer;
|
|---|
| 21 | parity:parityt;
|
|---|
| 22 | databits,stopbits:byte;
|
|---|
| 23 | RTS_CTS,XON_XOFF:BOOLEAN):INTEGER;
|
|---|
| 24 |
|
|---|
| 25 | { This function initializes the com buffer, setting up the interrupt,
|
|---|
| 26 | and com parameters, returns 0 ik ok else an error number}
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | PROCEDURE closeport;
|
|---|
| 30 |
|
|---|
| 31 | { This function closes the com port, removing the interrupt routine,
|
|---|
| 32 | etc. }
|
|---|
| 33 |
|
|---|
| 34 |
|
|---|
| 35 | PROCEDURE outcomch (ch: char);
|
|---|
| 36 |
|
|---|
| 37 | { This function outputs one character to the com port }
|
|---|
| 38 |
|
|---|
| 39 |
|
|---|
| 40 | FUNCTION peek1char:char;
|
|---|
| 41 |
|
|---|
| 42 | { return next char in receive buffer, or 0 for none available }
|
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 | FUNCTION get1char:char;
|
|---|
| 46 |
|
|---|
| 47 | { This function returns one character from the com port, or a zero if
|
|---|
| 48 | no character is waiting }
|
|---|
| 49 |
|
|---|
| 50 |
|
|---|
| 51 |
|
|---|
| 52 | FUNCTION comhit:boolean;
|
|---|
| 53 |
|
|---|
| 54 | { This returns a value telling if there is a character waiting in the com
|
|---|
| 55 | buffer. }
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 | PROCEDURE dump;
|
|---|
| 59 |
|
|---|
| 60 | { This function clears the com buffer }
|
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 | PROCEDURE set_baud (baud:longint);
|
|---|
| 64 |
|
|---|
| 65 | { This function sets the com speed to that passed }
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 | PROCEDURE setdtr(i:boolean);
|
|---|
| 69 |
|
|---|
| 70 | { This function sets the DTR pin to the status given }
|
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 | PROCEDURE setrts(i:boolean);
|
|---|
| 74 |
|
|---|
| 75 | { This function sets the RTS pin to the status given }
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 | FUNCTION carrierdetect:boolean;
|
|---|
| 79 |
|
|---|
| 80 | { This returns the status of the carrier detect lead from the modem }
|
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 | IMPLEMENTATION
|
|---|
| 84 |
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 | USES crt,bsedev,bsedos,os2def;
|
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 | CONST
|
|---|
| 92 |
|
|---|
| 93 | BAUD_RATE : WORD =38400;
|
|---|
| 94 | COMM_BUFFER_SIZE =16384;
|
|---|
| 95 |
|
|---|
| 96 | VAR
|
|---|
| 97 | head, { index to the last char in buffer }
|
|---|
| 98 | tail : INTEGER; { index to first char in buffer }
|
|---|
| 99 | buffer : ARRAY [0..COMM_BUFFER_SIZE] OF CHAR; { incoming character buffer }
|
|---|
| 100 | PortHandle : HFILE; { OS/2 file handle for COM port }
|
|---|
| 101 | RecvThreadID : TID; { Thread ID of receive-character thread }
|
|---|
| 102 |
|
|---|
| 103 | {
|
|---|
| 104 | * our receive-character thread; all it does is wait for a
|
|---|
| 105 | * character to come in on the com port. when one does, it
|
|---|
| 106 | * suspends the current process with DosEnterCritSec() and
|
|---|
| 107 | * places the character in the buffer.
|
|---|
| 108 | *
|
|---|
| 109 | * Purists will note that using DosEnterCritSec() instead of
|
|---|
| 110 | * semaphores is not "clean" or "true" multi-threading, but I chose
|
|---|
| 111 | * this method because it gave the largest performance boost.
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | PROCEDURE async_isr (ulThreadArg:ULONG); CDECL;
|
|---|
| 115 | VAR
|
|---|
| 116 | BytesRead : ULONG; { num. bytes read from last DosRead() call }
|
|---|
| 117 | ch : CHAR; { char read in from last DosRead() call }
|
|---|
| 118 | res : APIRET;
|
|---|
| 119 | BEGIN
|
|---|
| 120 | { endless loop }
|
|---|
| 121 | while true do
|
|---|
| 122 | begin
|
|---|
| 123 | { read character; this will block until a char is available }
|
|---|
| 124 | res:=DosRead (PortHandle, ch, 1, BytesRead);
|
|---|
| 125 |
|
|---|
| 126 | { if a character was actually read in... }
|
|---|
| 127 | if (BytesRead=1) then
|
|---|
| 128 | begin
|
|---|
| 129 | { suspend all other processing }
|
|---|
| 130 | DosEnterCritSec;
|
|---|
| 131 |
|
|---|
| 132 | { put char in buffer and adjust indices }
|
|---|
| 133 | buffer[head] := ch;
|
|---|
| 134 | inc(head);
|
|---|
| 135 | if (head = COMM_BUFFER_SIZE) then head := 0;
|
|---|
| 136 |
|
|---|
| 137 | { release suspended processes }
|
|---|
| 138 | DosExitCritSec;
|
|---|
| 139 | end;
|
|---|
| 140 | end;
|
|---|
| 141 | END;
|
|---|
| 142 |
|
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 | { This function outputs one character to the com port }
|
|---|
| 146 |
|
|---|
| 147 | PROCEDURE outcomch (ch: char);
|
|---|
| 148 |
|
|---|
| 149 | VAR
|
|---|
| 150 |
|
|---|
| 151 | rc : APIRET;
|
|---|
| 152 | BytesWritten : ULONG; { unless but required parameter }
|
|---|
| 153 | BEGIN
|
|---|
| 154 | rc:=DosWrite (PortHandle, ch, 1, BytesWritten);
|
|---|
| 155 | END;
|
|---|
| 156 |
|
|---|
| 157 |
|
|---|
| 158 |
|
|---|
| 159 | { return next char in receive buffer, or 0 for none available }
|
|---|
| 160 |
|
|---|
| 161 | FUNCTION peek1char:char;
|
|---|
| 162 | begin
|
|---|
| 163 | if head<>tail then peek1char:=buffer[tail]
|
|---|
| 164 | else peek1char:=#0;
|
|---|
| 165 | end;
|
|---|
| 166 |
|
|---|
| 167 |
|
|---|
| 168 |
|
|---|
| 169 | { This function returns one character from the com port, or a zero if
|
|---|
| 170 |
|
|---|
| 171 | * no character is waiting }
|
|---|
| 172 |
|
|---|
| 173 | FUNCTION get1char:char;
|
|---|
| 174 | var
|
|---|
| 175 | { temp var to hold char for returning if one is available }
|
|---|
| 176 | c1 : char;
|
|---|
| 177 | begin
|
|---|
| 178 | if (head <>tail) then
|
|---|
| 179 | begin
|
|---|
| 180 | c1 := buffer[tail];
|
|---|
| 181 | inc(tail);
|
|---|
| 182 | if (tail = COMM_BUFFER_SIZE) then tail := 0;
|
|---|
| 183 | get1char:=c1;
|
|---|
| 184 | end
|
|---|
| 185 | else get1char:=#0;
|
|---|
| 186 | end;
|
|---|
| 187 |
|
|---|
| 188 |
|
|---|
| 189 |
|
|---|
| 190 | { This returns a value telling if there is a character waiting in the com
|
|---|
| 191 | * buffer.
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | FUNCTION comhit:boolean;
|
|---|
| 195 | begin
|
|---|
| 196 | comhit:=(head<>tail);
|
|---|
| 197 | end;
|
|---|
| 198 |
|
|---|
| 199 |
|
|---|
| 200 |
|
|---|
| 201 | { This function clears the com buffer }
|
|---|
| 202 | PROCEDURE dump;
|
|---|
| 203 | begin
|
|---|
| 204 | head:=0;tail:=0;
|
|---|
| 205 | end;
|
|---|
| 206 |
|
|---|
| 207 |
|
|---|
| 208 |
|
|---|
| 209 | CONST
|
|---|
| 210 | ASYNC_EXTSETBAUDRATE = $43;
|
|---|
| 211 |
|
|---|
| 212 |
|
|---|
| 213 |
|
|---|
| 214 | { This function sets the com speed to that passed }
|
|---|
| 215 |
|
|---|
| 216 | PROCEDURE set_baud (baud:longint);
|
|---|
| 217 | var
|
|---|
| 218 | par : RECORD
|
|---|
| 219 | rate : ULONG;
|
|---|
| 220 | fraction : UCHAR;
|
|---|
| 221 | END;
|
|---|
| 222 |
|
|---|
| 223 | res : APIRET;
|
|---|
| 224 | begin
|
|---|
| 225 | {
|
|---|
| 226 | * OS/2 2.11+ standard COM drivers support up to 345600 bps !
|
|---|
| 227 | }
|
|---|
| 228 |
|
|---|
| 229 | par.rate:=baud;
|
|---|
| 230 | par.fraction:=0;
|
|---|
| 231 | if ((par.rate <= 345600) and (par.rate >= 10)) then
|
|---|
| 232 | res:=DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE,
|
|---|
| 233 | par, sizeof (par), NIL, NIL, 0, NIL);
|
|---|
| 234 | end;
|
|---|
| 235 |
|
|---|
| 236 |
|
|---|
| 237 |
|
|---|
| 238 | { This function sets the DTR pin to the status given }
|
|---|
| 239 |
|
|---|
| 240 | PROCEDURE setdtr(i:boolean);
|
|---|
| 241 | var
|
|---|
| 242 | ms : MODEMSTATUS;
|
|---|
| 243 | data : UINT;
|
|---|
| 244 | res : APIRET;
|
|---|
| 245 | begin
|
|---|
| 246 | ms.fbModemOn:=0;ms.fbModemOff:=0;
|
|---|
| 247 |
|
|---|
| 248 | if i then ms.fbModemOn := DTR_ON
|
|---|
| 249 | else ms.fbModemOff := DTR_OFF;
|
|---|
| 250 |
|
|---|
| 251 | res:=DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, ms,
|
|---|
| 252 | sizeof (ms), NIL, data, sizeof (data), NIL);
|
|---|
| 253 | end;
|
|---|
| 254 |
|
|---|
| 255 |
|
|---|
| 256 | { This function sets the RTS pin to the status given }
|
|---|
| 257 |
|
|---|
| 258 | PROCEDURE setrts(i:boolean);
|
|---|
| 259 | var
|
|---|
| 260 | ms : MODEMSTATUS;
|
|---|
| 261 | data : UINT;
|
|---|
| 262 | res : APIRET;
|
|---|
| 263 | begin
|
|---|
| 264 | ms.fbModemOn:=0;ms.fbModemOff:=0;
|
|---|
| 265 | if i then ms.fbModemOn := RTS_ON
|
|---|
| 266 | else ms.fbModemOff := RTS_OFF;
|
|---|
| 267 |
|
|---|
| 268 | res:=DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, ms,
|
|---|
| 269 | sizeof (ms), NIL, data, sizeof (data), NIL);
|
|---|
| 270 | end;
|
|---|
| 271 |
|
|---|
| 272 |
|
|---|
| 273 |
|
|---|
| 274 | { This function initializes the com buffer, setting up the interrupt,
|
|---|
| 275 | * and com parameters }
|
|---|
| 276 |
|
|---|
| 277 | FUNCTION initport (port_num:integer;
|
|---|
| 278 | parity:parityt;
|
|---|
| 279 | databits,stopbits:byte;
|
|---|
| 280 | RTS_CTS,XON_XOFF:BOOLEAN):integer;
|
|---|
| 281 | var
|
|---|
| 282 | rc : APIRET;
|
|---|
| 283 | action : ULONG;
|
|---|
| 284 | lctl : LINECONTROL;
|
|---|
| 285 | dcb : DCBINFO;
|
|---|
| 286 | portname : Cstring;
|
|---|
| 287 | begin
|
|---|
| 288 | { open com port }
|
|---|
| 289 | initport:=0;
|
|---|
| 290 |
|
|---|
| 291 | portname:= 'COM'+CHR(port_num + ORD('0'));
|
|---|
| 292 |
|
|---|
| 293 | if DosOpen (portname, PortHandle, action, 0, 0, 1, $42, NIL)<>0 then
|
|---|
| 294 | begin
|
|---|
| 295 | initport:=1;
|
|---|
| 296 | Exit;
|
|---|
| 297 | end;
|
|---|
| 298 |
|
|---|
| 299 | { set line }
|
|---|
| 300 | lctl.bParity := ord(parity);
|
|---|
| 301 | lctl.bDataBits := databits;
|
|---|
| 302 | if stopbits=1 then lctl.bStopBits := 0 else lctl.bStopBits:=2;
|
|---|
| 303 | lctl.fTransBreak := 0;
|
|---|
| 304 | if DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_SETLINECTRL,
|
|---|
| 305 | lctl, sizeof (LINECONTROL), NIL, NIL, 0, NIL)<>0 then
|
|---|
| 306 | begin
|
|---|
| 307 | DosClose (PortHandle);
|
|---|
| 308 | initport:=2;
|
|---|
| 309 | exit;
|
|---|
| 310 | end;
|
|---|
| 311 |
|
|---|
| 312 | { set device control block info }
|
|---|
| 313 | dcb.usWriteTimeout := 0;
|
|---|
| 314 | dcb.usReadTimeout := 0;
|
|---|
| 315 | dcb.fbCtlHndShake := MODE_DTR_CONTROL;
|
|---|
| 316 |
|
|---|
| 317 | IF RTS_CTS THEN
|
|---|
| 318 | BEGIN
|
|---|
| 319 | dcb.fbFlowReplace := MODE_RTS_HANDSHAKE;
|
|---|
| 320 | dcb.fbCtlHndShake := dcb.fbCtlHndShake + MODE_CTS_HANDSHAKE;
|
|---|
| 321 | END
|
|---|
| 322 | ELSE dcb.fbFlowReplace := MODE_RTS_CONTROL;
|
|---|
| 323 |
|
|---|
| 324 | IF XON_XOFF THEN
|
|---|
| 325 | dcb.fbFlowReplace := dcb.fbFlowReplace + MODE_AUTO_RECEIVE + MODE_AUTO_TRANSMIT;
|
|---|
| 326 |
|
|---|
| 327 | dcb.fbTimeout := MODE_NO_WRITE_TIMEOUT + MODE_WAIT_READ_TIMEOUT;
|
|---|
| 328 | dcb.bErrorReplacementChar := 0;
|
|---|
| 329 | dcb.bBreakReplacementChar := 0;
|
|---|
| 330 | dcb.bXONChar := $11;
|
|---|
| 331 | dcb.bXOFFChar := $13;
|
|---|
| 332 | if DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_SETDCBINFO, dcb,
|
|---|
| 333 | sizeof (DCBINFO), NIL, NIL, 0, NIL)<>0 then
|
|---|
| 334 | begin
|
|---|
| 335 | initport:=3;
|
|---|
| 336 | DosClose (PortHandle);
|
|---|
| 337 | exit;
|
|---|
| 338 | end;
|
|---|
| 339 |
|
|---|
| 340 | { indicate receive buffer is currently empty }
|
|---|
| 341 |
|
|---|
| 342 | head :=0; tail := 0;
|
|---|
| 343 |
|
|---|
| 344 | { spawn receive thread }
|
|---|
| 345 | if DosCreateThread (RecvThreadID, @async_isr, NIL, 0, 4096)<>0 then
|
|---|
| 346 | begin
|
|---|
| 347 | initport:=4;
|
|---|
| 348 | DosClose (PortHandle);
|
|---|
| 349 | exit
|
|---|
| 350 | end;
|
|---|
| 351 |
|
|---|
| 352 | setdtr(true);
|
|---|
| 353 | end;
|
|---|
| 354 |
|
|---|
| 355 |
|
|---|
| 356 |
|
|---|
| 357 | { This function closes out the com port, removing the interrupt routine,
|
|---|
| 358 |
|
|---|
| 359 | * etc. }
|
|---|
| 360 |
|
|---|
| 361 | PROCEDURE closeport;
|
|---|
| 362 | begin
|
|---|
| 363 | { kill receive thread and wait for it to close }
|
|---|
| 364 | DosKillThread (RecvThreadID);
|
|---|
| 365 |
|
|---|
| 366 | DosWaitThread (RecvThreadID, DCWW_WAIT);
|
|---|
| 367 |
|
|---|
| 368 | { close COM port handle }
|
|---|
| 369 | DosClose (PortHandle);
|
|---|
| 370 | end;
|
|---|
| 371 |
|
|---|
| 372 |
|
|---|
| 373 |
|
|---|
| 374 | { This returns the status of the carrier detect lead from the modem }
|
|---|
| 375 | FUNCTION carrierdetect:boolean;
|
|---|
| 376 | var
|
|---|
| 377 | instat : BYTE;
|
|---|
| 378 | begin
|
|---|
| 379 | { if DosDevIOCtl() returns an error, return 0 }
|
|---|
| 380 | if DosDevIOCtl (PortHandle, IOCTL_ASYNC, ASYNC_GETMODEMINPUT,
|
|---|
| 381 | NIL, 0, NIL, instat, sizeof (instat), NIL)<>0 then
|
|---|
| 382 | begin
|
|---|
| 383 | carrierdetect:=false;
|
|---|
| 384 | exit;
|
|---|
| 385 | end;
|
|---|
| 386 |
|
|---|
| 387 | { otherwise return carrier detect status }
|
|---|
| 388 | carrierdetect:=(instat and DCD_ON)<>0;
|
|---|
| 389 | end;
|
|---|
| 390 |
|
|---|
| 391 |
|
|---|
| 392 | end.
|
|---|
| 393 |
|
|---|
| 394 |
|
|---|