Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

ODCom.c 107 KB
Newer Older
rswindell's avatar
rswindell committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* OpenDoors Online Software Programming Toolkit
 * (C) Copyright 1991 - 1999 by Brian Pirie.
 *
 * Oct-2001 door32.sys/socket modifications by Rob Swindell (www.synchro.net)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
Deucе's avatar
Deucе committed
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
rswindell's avatar
rswindell committed
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 *
 *
 *        File: ODCom.c
 *
 * Description: Generic serial I/O routines, provide a single interface to
 *              serial ports on any platform.
 *
 *   Revisions: Date          Ver   Who  Change
 *              ---------------------------------------------------------------
 *              Oct 13, 1994  6.00  BP   New file header format.
 *              Oct 20, 1994  6.00  BP   Handle BIOS missing port addrs.
 *              Oct 20, 1994  6.00  BP   Standardized coding style.
 *              Oct 21, 1994  6.00  BP   Further isolated com routines.
 *              Dec 07, 1994  6.00  BP   Support for RTS/CTS flow control.
 *              Dec 10, 1994  6.00  BP   Allow word frmt setting for intern I/O
 *              Dec 13, 1994  6.00  BP   Remove include of dir.h.
 *              Dec 31, 1994  6.00  BP   Remove #ifndef USEINLINE DOS code.
 *              Jan 01, 1995  6.00  BP   Integrate in Win32 code.
 *              Jan 01, 1995  6.00  BP   Add FLOW_DEFAULT setting.
 *              Jan 01, 1995  6.00  BP   Added ODComWaitEvent().
 *              Nov 16, 1995  6.00  BP   Removed oddoor.h, added odcore.h.
 *              Nov 21, 1995  6.00  BP   Ported to Win32.
 *              Dec 21, 1995  6.00  BP   Add ability to use already open port.
 *              Jan 09, 1996  6.00  BP   Supply actual in/out buffer size used.
 *              Feb 19, 1996  6.00  BP   Changed version number to 6.00.
 *              Mar 03, 1996  6.10  BP   Begin version 6.10.
 *              Mar 06, 1996  6.10  BP   Initial support for Door32 interface.
 *              Mar 19, 1996  6.10  BP   MSVC15 source-level compatibility.
 *              Jan 13, 1997  6.10  BP   Fixes for Door32 support.
 *              Oct 19, 2001  6.20  RS   Added TCP/IP socket (telnet) support.
 *              Oct 22, 2001  6.21  RS   Fixed disconnected socket detection.
50 51
 *              Aug 22, 2002  6.22  RS   Fixed bugs in ODComCarrier and ODComWaitEvent
 *              Aug 22, 2002  6.22  MD   Modified socket functions for non-blocking use.
52
 *              Sep 18, 2002  6.22  MD   Fixed bugs in ODComWaitEvent for non-blocking sockets.
53
 *              Aug 10, 2003  6.23  SH   *nix support
rswindell's avatar
rswindell committed
54 55 56 57 58 59 60 61 62 63 64 65
 */

#define BUILDING_OPENDOORS

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "OpenDoor.h"
deuce's avatar
deuce committed
66 67
#ifdef ODPLAT_NIX
#include <sys/ioctl.h>
68 69
#include <signal.h>
#include <termios.h>
deuce's avatar
deuce committed
70
#include <errno.h>
71
#include <unistd.h>
72
#include <sys/socket.h>
73 74
#include <netinet/in.h>
#include <netinet/tcp.h>
deuce's avatar
deuce committed
75
#endif
rswindell's avatar
rswindell committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
#include "ODCore.h"
#include "ODGen.h"
#include "ODPlat.h"
#include "ODCom.h"
#include "ODUtil.h"


/* The following define determines whether serial port function should */
/* ASSERT or return an error code on programmer erorrs (e.g. invalid   */
/* parameters.                                                         */
#define ASSERT_ON_INVALID_CALLS

/* The following code defines the VERIFY_CALL() macro, which maps to an */
/* ASSERT if ASSERT_ON_INVALID_CALLS is defined. Otherwise, this macro  */
/* maps to a test which will return an error code to the caller.        */
#ifdef ASSERT_ON_INVALID_CALLS
#define VERIFY_CALL(x) ASSERT(x)
#else /* !ASSERT_ON_INVALID_CALLS */
#define VERIFY_CALL(x) if(x) return(kODRCInvalidCall)
#endif /* !ASSERT_ON_INVALID_CALLS */


/* The following defines determine which serial I/O mechanisms should be */
/* supported.                                                            */

/* Serial I/O mechanisms supported under MS-DOS version. */
#ifdef ODPLAT_DOS
#define INCLUDE_FOSSIL_COM                      /* INT 14h FOSSIL-based I/O. */
#define INCLUDE_UART_COM                   /* Internal interrupt driven I/O. */
#endif /* ODPLAT_DOS */

/* Serial I/O mechanisms supported under Win32 version. */
#ifdef ODPLAT_WIN32
#define INCLUDE_WIN32_COM                           /* Win32 API serial I/O. */
#define INCLUDE_DOOR32_COM                          /* Door32 I/O interface. */
#define INCLUDE_SOCKET_COM                          /* TCP/IP socket I/O.    */
#endif /* ODPLAT_WIN32 */

deuce's avatar
deuce committed
114 115 116
/* Serial I/O mechanisms supported inder *nix version */
#ifdef ODPLAT_NIX
#define INCLUDE_STDIO_COM
117 118 119 120 121 122 123 124 125
#define INCLUDE_SOCKET_COM                          /* TCP/IP socket I/O.    */

/* Win32 Compat. Stuff */
#define SOCKET	int
#define WSAEWOULDBLOCK	EAGAIN
#define SOCKET_ERROR -1
#define WSAGetLastError() errno
#define ioctlsocket	ioctl
#define closesocket	close
deuce's avatar
deuce committed
126
#endif /* ODPLAT_NIX */
rswindell's avatar
rswindell committed
127 128 129 130 131 132

/* Include "windows.h" for Win32-API based serial I/O. */
#ifdef INCLUDE_WIN32_COM
#include "windows.h"
#endif /* INCLUDE_WIN32_COM */

133 134 135 136 137 138
/* terminal variables */
#ifdef INCLUDE_STDIO_COM
struct termios tio_default;				/* Initial term settings */
#endif


rswindell's avatar
rswindell committed
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
#if defined(_WIN32) && defined(INCLUDE_SOCKET_COM)
	#include <winsock.h>
	static WSADATA WSAData;		/* WinSock data */
#endif

/* ========================================================================= */
/* Serial port object structure.                                             */
/* ========================================================================= */

/* Win32-API serial I/O implementation requires current timeout setting */
/* status variable in serial port object structure.                     */
#ifdef INCLUDE_WIN32_COM
typedef enum
{
   kNotSet,
   kBlocking,
   kNonBlocking
} tReadTimeoutState;
#endif /* INCLUDE_WIN32_COM */

/* Structure associated with each serial port handle. */
typedef struct
{
   BOOL bIsOpen;
   BOOL bUsingClientsHandle;
   BYTE btFlowControlSetting;
   long lSpeed;
   BYTE btPort;
   int nPortAddress;
   BYTE btIRQLevel;
   BYTE btWordFormat;
   int nReceiveBufferSize;
   int nTransmitBufferSize;
   BYTE btFIFOSetting;
   tComMethod Method;
   void (*pfIdleCallback)(void);
#ifdef INCLUDE_WIN32_COM
   HANDLE hCommDev;
   tReadTimeoutState ReadTimeoutState;
#endif /* INCLUDE_WIN32_COM */
#ifdef INCLUDE_DOOR32_COM
   HINSTANCE hinstDoor32DLL;
   BOOL (WINAPI *pfDoorInitialize)(void);
   BOOL (WINAPI *pfDoorShutdown)(void);
   BOOL (WINAPI *pfDoorWrite)(const BYTE *pbData, DWORD dwSize);
   DWORD (WINAPI *pfDoorRead)(BYTE *pbData, DWORD dwSize);
   HANDLE (WINAPI *pfDoorGetAvailableEventHandle)(void);
   HANDLE (WINAPI *pfDoorGetOfflineEventHandle)(void);
#endif /* INCLUDE_DOOR32_COM */
#ifdef INCLUDE_SOCKET_COM
	SOCKET	socket;
190
	int	old_delay;
rswindell's avatar
rswindell committed
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220
#endif
} tPortInfo;

/* ========================================================================= */
/* Internal interrupt-driven serial I/O specific defintions & functions.     */
/* ========================================================================= */

#ifdef INCLUDE_UART_COM

/* Private function prototypes, used by internal UART async serial I/O. */
static void ODComSetVect(BYTE btVector, void (INTERRUPT far *pfISR)(void));
static void (INTERRUPT far *ODComGetVect(BYTE btVector))(void);
static void INTERRUPT ODComInternalISR();
static BOOL ODComInternalTXReady(void);
static void ODComInternalResetRX(void);
static void ODComInternalResetTX(void);


/* Offsets of UART registers. */
#define TXBUFF  0                       /* Transmit buffer register. */
#define RXBUFF  0                       /* Receive buffer register. */
#define DLLSB   0                       /* Divisor latch LS byte. */
#define DLMSB   1                       /* Divisor latch MS byte. */
#define IER     1                       /* Interrupt enable register. */
#define IIR     2                       /* Interrupt ID register. */
#define LCR     3                       /* Line control register. */
#define MCR     4                       /* Modem control register. */
#define LSR     5                       /* Line status register. */
#define MSR     6                       /* Modem status register. */

/* FIFO control register bits. */
#define FE      0x01                    /* FIFO enable. */
#define RR      0x02                    /* FIFO receive buffer reset. */
#define TR      0x04                    /* FIFO transmit buffer reset. */
#define FTS_1   0x00                    /* FIFO trigger size 1 byte. */
#define FTS_4   0x40                    /* FIFO trigger size 4 bytes. */
#define FTS_8   0x80                    /* FIFO trigger size 8 bytes. */
#define FTS_14  0xc0                    /* FIFO trigger size 14 bytes. */

/* Modem control register (MCR) bits. */
#define DTR     0x01                    /* Data terminal ready. */
#define NOT_DTR 0xfe                    /* All bits other than DTR. */
#define RTS     0x02                    /* Request to send. */
#define NOT_RTS 0xfd                    /* All bits other than RTS. */
#define OUT1    0x04                    /* Output #1. */
#define OUT2    0x08                    /* Output #2. */
#define LPBK    0x10                    /* Loopback mode bit. */

/* Modem status register (MSR) bits. */
#define DCTS    0x01                    /* Delta clear to send. */
#define DDSR    0x02                    /* Delta data set ready. */
#define TERI    0x04                    /* Trailing edge ring indicator. */
#define DRLSD   0x08                    /* Delta Rx line signal detect. */
#define CTS     0x10                    /* Clear to send. */
#define DSR     0x20                    /* Data set ready. */
#define RI      0x40                    /* Ring indicator. */
#define RLSD    0x80                    /* Receive line signal detect. */

/* Line control register (LCR) bits. */
#define DATA5   0x00                    /* 5 Data bits. */
#define DATA6   0x01                    /* 6 Data bits. */
#define DATA7   0x02                    /* 7 Data bits. */
#define DATA8   0x03                    /* 8 Data bits. */

#define STOP1   0x00                    /* 1 Stop bit. */
#define STOP2   0x04                    /* 2 Stop bits. */

#define NOPAR   0x00                    /* No parity. */
#define ODDPAR  0x08                    /* Odd parity. */
#define EVNPAR  0x18                    /* Even parity. */
#define STKPAR  0x28                    /* Sticky parity. */
#define ZROPAR  0x38                    /* Zero parity. */

#define DLATCH  0x80                    /* Baud rate divisor latch. */
#define NOT_DL  0x7f                    /* Turns off divisor latch. */

/* Line status register (LSR) bits. */
#define RDR     0x01                    /* Receive data ready. */
#define ERRS    0x1E                    /* All the error bits. */
#define TXR     0x20                    /* Transmitter ready. */

/* Interrupt enable register (IER) bits. */
#define DR      0x01                    /* Data ready. */
#define THRE    0x02                    /* Transmit holding register empty. */
#define RLS     0x04                    /* Receive line status. */
#define MS      0x08                    /* Modem status. */

/* Flow control receive buffer limits. */
#define RECEIVE_LOW_NUM     1           /* Numerator for low water mark. */
#define RECEIVE_LOW_DENOM   4           /* Denominator for low water mark. */
#define RECEIVE_HIGH_NUM    3           /* Numerator for high water mark. */
#define RECEIVE_HIGH_DENOM  4           /* Denominator for high water mark. */


/* Built-in async serial I/O global variables. */

/* These variabes are shared throughout the functions that handle the      */
/* built-in UART-base serial I/O, including the interrupt service routine. */
/* Since only one copy of these variables exist, the built-in serial I/O   */
/* routines may only be used to access one port at a time.                 */

/* Default port addresses. */
/* First 4 addresses are standard addresses used for PC/AT COM1 thru COM4. */
/* Second 4 addresses are PS/2 standard addresses used for COM5 thru COM8. */
static int anDefaultPortAddr[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8,
                                  0x4220, 0x4228, 0x5220, 0x5228};

/* UART address variables. */
static int nDataRegAddr;                /* Data register address. */
static int nIntEnableRegAddr;           /* Interrupt enable register. */
static int nIntIDRegAddr;               /* Interrupt ID register address. */
static int nLineCtrlRegAddr;            /* Line control register address. */
static int nModemCtrlRegAddr;           /* Modem control register address. */
static int nLineStatusRegAddr;          /* Line status register address. */
static int nModemStatusRegAddr;         /* Modem status register address. */

/* General variables. */
static BYTE btIntVector;                /* Interrupt vector number for port. */
static char btI8259Bit;                 /* 8259 bit mask. */
static char btI8259Mask;                /* Copy as it was before open. */
static int nI8259MaskRegAddr;           /* Address of i8259 mask register. */
static int nI8259EndOfIntRegAddr;       /* Address of i8259 EOI register. */
static int nI8259MasterEndOfIntRegAddr; /* Address of master PIC EOI reg. */
static char btOldIntEnableReg;          /* Original IER contents. */
static char btOldModemCtrlReg;          /* Original MCR contents. */
static void (INTERRUPT far *pfOldISR)();/* Original ISR routine for IRQ. */
static char bUsingFIFO = FALSE;         /* Are we using 16550 FIFOs? */
static unsigned char btBaseFIFOCtrl;    /* FIFO control register byte. */


/* Transmit queue variables. */
static int nTXQueueSize;                /* Actual size of transmit queue. */
static char *pbtTXQueue;                /* Pointer to transmit queue. */
static int nTXInIndex;                  /* Location to store next byte. */
static int nTXOutIndex;                 /* Location to get next byte. */
static int nTXChars;                    /* Count of characters in queue. */

/*  Receive queue variables. */
static int nRXQueueSize;                /* Actual size of receive queue. */
static char *pbtRXQueue;                /* Pointer to receive queue. */
static int nRXInIndex;                  /* Location to store next byte. */
static int nRXOutIndex;                 /* Location to retrieve next byte. */
static int nRXChars;                    /* Count of characters in queue. */

/* Flow control variables. */
static int nRXHighWaterMark;            /* High water mark for queue size. */
static int nRXLowWaterMark;             /* Low water mark for queue size. */
static BYTE btFlowControl;              /* Flow control method. */
static BOOL bStopTrans;                 /* Flag set to stop transmitting. */

/* ----------------------------------------------------------------------------
 * ODComSetVect()                                      *** PRIVATE FUNCTION ***
 *
 * Sets the function to be called for the specified interrupt level.
 *
 * Parameters: btVector - Interrupt vector level, a value from 0 to 255.
 *
 *             pfISR    - Pointer to the ISR function to be called.
 *
 *     Return: void
 */
static void ODComSetVect(BYTE btVector, void (INTERRUPT far *pfISR)(void))
{
   ASM   push ds
   ASM   mov ah, 0x25
   ASM   mov al, btVector
   ASM   lds dx, pfISR
   ASM   int 0x21
   ASM   pop ds
}


/* ----------------------------------------------------------------------------
 * ODComGetVect()                                      *** PRIVATE FUNCTION ***
 *
 * Returns the address of the function that is currently called for the
 * specified interrupt level.
 *
 * Parameters: btVector - Interrupt vector level, a value from 0 to 255.
 *
 *     Return: A pointer to the code that is currently executed on an interrupt
 *             of the speceified level.
 */
static void (INTERRUPT far *ODComGetVect(BYTE btVector))(void)
{
   void (INTERRUPT far *pfISR)(void);

   ASM   push es
   ASM   mov ah, 0x35
   ASM   mov al, btVector
   ASM   int 0x21
   ASM   mov word ptr pfISR, bx
   ASM   mov word ptr [pfISR+2], bx
   ASM   pop es

   return(pfISR);
}


/* ----------------------------------------------------------------------------
 * ODComInternalTXReady()                              *** PRIVATE FUNCTION ***
 *
 * Returns TRUE if the internal serial I/O transmit buffer is not full.
 *
 * Parameters: none
 *
 *     Return: void
 */
static BOOL ODComInternalTXReady(void)
{
   /* Return TRUE if tx_chars is less than total tx buffer size. */
   return(nTXChars < nTXQueueSize);
}


/* ----------------------------------------------------------------------------
 * ODComInternalResetTX()                              *** PRIVATE FUNCTION ***
 *
 * Clears transmit buffer used by internal serial I/O routines.
 *
 * Parameters: none
 *
 *     Return: void
 */
static void ODComInternalResetTX(void)
{
   /* Disable interrupts. */
   ASM cli

   /* If we are using 16550A FIFO buffers, then clear the FIFO transmit */
   /* buffer.                                                           */
   if(bUsingFIFO)
   {
      ASM mov al, btBaseFIFOCtrl
      ASM or al, TR
      ASM mov dx, nIntIDRegAddr
      ASM out dx, al
   }

   /* Reset start, end and total count of characters in buffer      */
   /* If buffer is still empty on next transmit interrupt, transmit */
   /* interrupts will be turned off.                                */
   nTXChars = nTXInIndex = nTXOutIndex = 0;

   /* Re-enable interrupts. */
   ASM sti
}


/* ----------------------------------------------------------------------------
 * ODComInternalResetRX()                              *** PRIVATE FUNCTION ***
 *
 * Clears receive buffer used by internal serial I/O routines.
 *
 * Parameters: none
 *
 *     Return: void
 */
static void ODComInternalResetRX(void)
{
   /* Disable interrupts. */
   ASM cli

   /* If we are using 16550A FIFO buffers, then clear the FIFO receive */
   /* buffer.                                                          */
   if(bUsingFIFO)
   {
      ASM mov al, btBaseFIFOCtrl
      ASM or al, RR
      ASM mov dx, nIntIDRegAddr
      ASM out dx, al
   }

   /* Reset start, end and total count of characters in buffer           */
   /* On the next receive interrupt, data will be added at the beginning */
   /* of the buffer.                                                     */
   nRXChars = nRXInIndex = nRXOutIndex = 0;

   /* Re-enable interrupts. */
   ASM sti
}


/* ----------------------------------------------------------------------------
 * ODComInternalISR()                                  *** PRIVATE FUNCTION ***
 *
 * Interrupt service routine for internal UART-based serial I/O.
 *
 * Parameters: none
 *
 *     Return: void
 */
static void INTERRUPT ODComInternalISR()
{
   char btIIR;
   BYTE btTemp;

   /* Loop until there are no more pending operations to perform with the */
   /* UART. */
   for(;;)
   {
      /* While bit 0 of the UART IIR is 0, there remains pending operations. */
      /* Read IIR. */
      ASM mov dx, nIntIDRegAddr
      ASM in al, dx
      ASM mov btIIR, al

      /* If IIR bit 0 is set, then UART processing is finished.             */
      if(btIIR & 0x01) break;

      /* Bits 1 and 2 of the IIR register identify the type of operation */
      /* to be performed with the UART.                                  */

      /* Switch on bits 1 and 2 of IIR register. */
      switch(btIIR & 0x06)
      {
         case 0x00:
            /* Operation: modem status has changed. */

            /* Read modem status register. */
            ASM mov dx, nModemStatusRegAddr
            ASM in al, dx
            ASM mov btTemp, al

            /* We only care about the modem status register if we are */
            /* using RTS/CTS flow control, and the CTS register has  */
            /* changed.                                               */
            if((btFlowControl & FLOW_RTSCTS) && (btTemp & DCTS))
            {
               if(btTemp & CTS)
               {
                  /* If CTS has gone high, then re-enable transmission. */
                  bStopTrans = FALSE;

                  /* Restart transmission if there is anything in the */
                  /* transmit buffer.                                 */
                  if(nTXChars > 0)
                  {
                     /* Enable transmit interrupt. */
                     ASM mov dx, nIntEnableRegAddr
                     ASM in al, dx
                     ASM or al, THRE
                     ASM out dx, al
                  }
               }
               else
               {
                  /* If CTS has gone low, then stop transmitting. */
                  bStopTrans = TRUE;
               }
            }

            break;

         case 0x02:
            /* Operation: room in transmit register/FIFO. */
            /* Check whether we can send further characters to transmit. */
            if(nTXChars <= 0 || bStopTrans)
            {
               /* If we cannot send more characters, then turn off */
               /* transmit interrupts.                             */
               ASM mov dx, nIntEnableRegAddr
               ASM in al, dx
               ASM and al, 0xfd
               ASM out dx, al
            }
            else
            {
               /* If we still have characters to transmit ... */

               /* Check line status register to determine whether transmit  */
               /* register/FIFO truly has room. Some UARTs trigger transmit */
               /* interrupts before the character has been tranmistted,     */
               /* causing transmitted characters to be lost.                */
               ASM mov dx, nLineStatusRegAddr
               ASM in al, dx
               ASM mov btTemp, al

               if(btTemp & TXR)
               {
                  /* There is room in the transmit register/FIFO. */

                  /* Get next character to transmit. */
                  btTemp = pbtTXQueue[nTXOutIndex++];

                  /* Write character to UART data register. */
                  ASM mov dx, nDataRegAddr
                  ASM mov al, btTemp
                  ASM out dx, al

                  /* Wrap-around transmit buffer pointer, if needed. */
                  if (nTXOutIndex == nTXQueueSize)
                  {
                     nTXOutIndex = 0;
                  }

                  /* Decrease count of characters in transmit buffer. */
                  nTXChars--;
               }
            }
            break;

         case 0x04:
            /* Operation: Receive Data. */

            /* Get character from receive buffer ASAP. */
            ASM mov dx, nDataRegAddr
            ASM in al, dx
            ASM mov btTemp, al

            /* If receive buffer is above high water mark. */
            if(nRXChars >= nRXHighWaterMark)
            {
               /* If we are using flow control, then stop sender from */
               /* sending.                                            */
               if(btFlowControl & FLOW_RTSCTS)
               {
                  /* If using RTS/CTS flow control, then lower RTS line. */
                  ASM mov dx, nModemCtrlRegAddr
                  ASM in al, dx
                  ASM and al, NOT_RTS
                  ASM out dx, al
               }
            }

            /* If there is room in receive buffer. */
            if(nRXChars < nRXQueueSize)
            {
               /* Store the new character in the receive buffer. */
               pbtRXQueue[nRXInIndex++] = btTemp;

               /* Wrap-around buffer index, if needed. */
               if (nRXInIndex == nRXQueueSize)
                  nRXInIndex = 0;

               /* Increment count of characters in the buffer. */
               nRXChars++;
            }
            break;

         case 0x06:
            /* Operation: Change in line status register. */

            /* We just read the register to move on to further operations. */
            ASM mov dx, nLineStatusRegAddr
            ASM in al, dx
            break;
      }
   }

   /* Send end of interrupt to interrupt controller(s). */
   ASM mov dx, nI8259EndOfIntRegAddr
   ASM mov al, 0x20
   ASM out dx, al

   if(nI8259MasterEndOfIntRegAddr != 0)
   {
      ASM mov dx, nI8259MasterEndOfIntRegAddr
      ASM mov al, 0x20
      ASM out dx, al
   }
}
#endif /* INCLUDE_UART_COM */



/* ========================================================================= */
/* Win32-API base serial I/O specific functions.                             */
/* ========================================================================= */

#ifdef INCLUDE_WIN32_COM

/* Function prototypes. */
static tODResult ODComWin32SetReadTimeouts(tPortInfo *pPortInfo,
   tReadTimeoutState RequiredTimeoutState);


/* ----------------------------------------------------------------------------
 * ODComWin32SetReadTimeouts()                         *** PRIVATE FUNCTION ***
 *
 * Ensures that read timeout state is set appropriately.
 *
 * Parameters: pPortInfo            - Pointer to serial port handle structure.
 *
 *             RequiredTimeoutState - Timeout state that should be set.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
static tODResult ODComWin32SetReadTimeouts(tPortInfo *pPortInfo,
   tReadTimeoutState RequiredTimeoutState)
{
   ASSERT(pPortInfo != NULL);

   /* If timeout state must be changed ... */
   if(RequiredTimeoutState != pPortInfo->ReadTimeoutState)
   {
      COMMTIMEOUTS CommTimeouts;

      /* Obtain current timeout settings. */
      if(!GetCommTimeouts(pPortInfo->hCommDev, &CommTimeouts))
      {
         return(kODRCGeneralFailure);
      }

      /* Setup timeout setting structure appropriately. */
      switch(RequiredTimeoutState)
      {
         case kBlocking:
            CommTimeouts.ReadIntervalTimeout = 0;
            CommTimeouts.ReadTotalTimeoutMultiplier = 0;
            CommTimeouts.ReadTotalTimeoutConstant = 0;
            break;
         case kNonBlocking:
            CommTimeouts.ReadIntervalTimeout = INFINITE;
            CommTimeouts.ReadTotalTimeoutMultiplier = 0;
            CommTimeouts.ReadTotalTimeoutConstant = 0;
            break;
         default:
            ASSERT(FALSE);
      }

      /* Write settings. */
      if(!SetCommTimeouts(pPortInfo->hCommDev, &CommTimeouts))
      {
         return(kODRCGeneralFailure);
      }

      /* Record current read timeout setting state for subsequent */
      /* calls to this function.                                  */
      pPortInfo->ReadTimeoutState = RequiredTimeoutState;
   }

   return(kODRCSuccess);
}

#endif /* INCLUDE_WIN32_COM */



/* ========================================================================= */
/* Implementation of generic serial I/O functions.                           */
/* ========================================================================= */

/* ----------------------------------------------------------------------------
 * ODComAlloc()
 *
 * Allocates a serial port handle, which can be passed to other ODCom...()
 * functions.
 *
 * Parameters: phPort - Pointer to serial port handle.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComAlloc(tPortHandle *phPort)
{
   tPortInfo *pPortInfo;

   VERIFY_CALL(phPort != NULL);

   /* Attempt to allocate a serial port information structure. */
   pPortInfo = malloc(sizeof(tPortInfo));

   /* If memory allocation failed, return with failure. */
   if(pPortInfo == NULL)
   {
      *phPort = ODPTR2HANDLE(NULL, tPortInfo);
      return(kODRCNoMemory);
   }

   /* Initialize serial port information structure. */
   pPortInfo->bIsOpen = FALSE;
   pPortInfo->bUsingClientsHandle = FALSE;
   pPortInfo->btFlowControlSetting = FLOW_DEFAULT;
   pPortInfo->lSpeed = SPEED_UNSPECIFIED;
   pPortInfo->btWordFormat = ODPARITY_NONE | DATABITS_EIGHT | STOP_ONE;
   pPortInfo->nReceiveBufferSize = 1024;
   pPortInfo->nTransmitBufferSize = 1024;
   pPortInfo->btFIFOSetting = FIFO_ENABLE | FIFO_TRIGGER_8;
   pPortInfo->Method = kComMethodUnspecified;
   pPortInfo->pfIdleCallback = NULL;

   /* Convert serial port information structure pointer to a handle. */
   *phPort = ODPTR2HANDLE(pPortInfo, tPortInfo);

   /* Set default port number. */
   ODComSetPort(*phPort, 0);

#if defined(INCLUDE_SOCKET_COM) && defined(_WINSOCKAPI_)
	WSAStartup(MAKEWORD(1,1), &WSAData);
#endif

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComFree()
 *
 * Deallocates a serial port handle that is no longer required.
 *
 * Parameters: hPort - Handle to a serial port object.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComFree(tPortHandle hPort)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   /* Deallocate port information structure. */
   free(pPortInfo);

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetIdleFunction()
 *
 * Sets function to call when serial I/O module is idle, or NULL for none.
 *
 * Parameters: hPort      - Handle to a serial port object.
 *
 *             pfCallback - Pointer to function to call when idle.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetIdleFunction(tPortHandle hPort,
   void (*pfCallback)(void))
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->pfIdleCallback = pfCallback;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetFlowControl()
 *
 * Sets the flow control method(s) to use. If this function is not called,
 * RTS/CTS flow control is used by default. This function should not be
 * called while the port is open.
 *
 * Parameters: hPort                - Handle to a serial port object.
 *
 *             btFlowControlSetting - One or more FLOW_* settings, joined
 *                                    by bitwise-or (|) operators. If
 *                                    FLOW_DEFAULT is included, all other
 *                                    settings are ignored, and the default
 *                                    settings for this serial I/O method
 *                                    are used.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetFlowControl(tPortHandle hPort, BYTE btFlowControlSetting)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->btFlowControlSetting = btFlowControlSetting;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetSpeed()
 *
 * Sets the serial port BPS (baud) rate to use. Depending upon the serial I/O
 * method being used, this setting may be controlled by the user's system
 * configuration, in which case the value passed to this function wil have
 * no effect. A setting of SPEED_UNSPECIFIED, indicates that the serial port
 * speed should not be changed, if it is possible not to do so with the serial
 * I/O method being used. This function cannot be called while the port is
 * open.
 *
 * Parameters: hPort - Handle to a serial port object.
 *
 *             lSpeed - A valid BPS rate, or SPEED_UNSPECIFIED.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetSpeed(tPortHandle hPort, long lSpeed)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->lSpeed = lSpeed;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetPort()
 *
 * Sets the serial port number to be associated with this port handle. This
 * function cannot be called while the port is open. Calling this function
 * also sets the IRQ line number and serial port address to their defaults
 * for this port number, if this values can be set for the serial I/O method
 * being used.
 *
 * Parameters: hPort  - Handle to a serial port object.
 *
 *             btPort - Serial port identification, where 0 typically
 *                      corresponds to COM1, 1 to COM2, and so on.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetPort(tPortHandle hPort, BYTE btPort)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   /* Store port number in port information structure. */
   pPortInfo->btPort = btPort;


#ifdef INCLUDE_UART_COM
   /* Get default address for this port number, if possible. */
   pPortInfo->nPortAddress = 0;

   if(btPort < 4)
   {
      /* Get port address from BIOS data area. */
      pPortInfo->nPortAddress = *(((int far *)0x400) + btPort);
   }

   /* If port address is still unknown, and we know the default */
   /* address, then use that address. */
   if(pPortInfo->nPortAddress == 0
      && btPort < DIM(anDefaultPortAddr))
   {
      pPortInfo->nPortAddress = anDefaultPortAddr[btPort];
   }


   /* Set default IRQ number for this port number. */

   /* Ports 0 and 2 (COM1:, COM3:) default to IRQ 4, all others */
   /* default to IRQ 3. */
   if(btPort == 0 || btPort == 2)
   {
      pPortInfo->btIRQLevel = 4;
   }
   else
   {
      pPortInfo->btIRQLevel = 3;
   }
#endif /* INCLUDE_UART_COM */

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetPortAddress()
 *
 * Sets address of the serial port, if it can be set for the serial I/O method
 * being used. This function cannot be called when the port is open.
 *
 * Parameters: hPort        - Handle to a serial port object.
 *
 *             nPortAddress - Address of serial port.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetPortAddress(tPortHandle hPort, int nPortAddress)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->nPortAddress = nPortAddress;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetIRQ()
 *
 * Sets the IRQ line associated with this serial port, if applicable for the
 * serial I/O method being used. This function cannot be called while the port
 * is open.
 *
 * Parameters: hPort      - Handle to a serial port object.
 *
 *             btIRQLevel - A number from 1 to 15, specifying the IRQ line that
 *                          the serial port is wired to.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetIRQ(tPortHandle hPort, BYTE btIRQLevel)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->btIRQLevel = btIRQLevel;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetWordFormat()
 *
 * Determine the word format (number of data bits, stop bits and parity bits)
 * to use, if it can be set for the serial I/O method being used. If this
 * function is not called, N81 word format is used. This function can only
 * be called when the port is not open.
 *
 * Parameters: hPort        - Handle to a serial port object.
 *
 *             btWordFormat - Bitwise-or (|) of PARITY_*, STOP_* and DATABITS_*
 *                            settings which determine the word format to use.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetWordFormat(tPortHandle hPort, BYTE btWordFormat)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->btWordFormat = btWordFormat;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetRXBuf()
 *
 * Sets the desired size of the receive buffer, if possible for the
 * serial I/O method being used. Note that for some serial I/O methods, this
 * buffer size is fixed, controlled by the user's system configuration.
 * No error is generated when this function is called when such serial I/O
 * methods will be used - in this case this setting will simply have no effect.
 * This function cannot be called while the port is open.
 *
 * Parameters: hPort              - Handle to a serial port object.
 *
 *             nReceiveBufferSize - Number of bytes in the receive buffer.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetRXBuf(tPortHandle hPort, int nReceiveBufferSize)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->nReceiveBufferSize = nReceiveBufferSize;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetTXBuf()
 *
 * Sets the desired size of the transmit buffer, if possible for the
 * serial I/O method being used. Note that for some serial I/O methods, this
 * buffer size is fixed, controlled by the user's system configuration.
 * No error is generated when this function is called when such serial I/O
 * methods will be used - in this case this setting will simply have no effect.
 * This function cannot be called while the port is open.
 *
 * Parameters: hPort               - Handle to a serial port object.
 *
 *             nTransmitBufferSize - Number of bytes in the transmit buffer.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetTXBuf(tPortHandle hPort, int nTransmitBufferSize)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->nTransmitBufferSize = nTransmitBufferSize;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetFIFO()
 *
 * Enables or disables use of the UART FIFO buffers (if applicable), and also
 * sets the FIFO trigger level. This function cannot be called while the port
 * is open.
 *
 * Parameters: hPort         - Handle to a serial port object.
 *
 *             btFIFOSetting - UART FIFO setting, including FIFO_ENABLE or
 *                             FIDO_DISABLE, and a FIFO_TRIGGER_* setting,
 *                             joined by bitwise-or (|) operators.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetFIFO(tPortHandle hPort, BYTE btFIFOSetting)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->btFIFOSetting = btFIFOSetting;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComSetPreferredMethod()
 *
 * Sets the method to be used to perform serial I/O.
 *
 * Parameters: hPort  - Handle to a serial port object.
 *
 *             Method - The method to be used for peforming serial I/O,
 *                      or kComMethodUnspecified to have the serial I/O
 *                      routines to automatically choose the method to use.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComSetPreferredMethod(tPortHandle hPort, tComMethod Method)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

   pPortInfo->Method = Method;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComGetMethod()
 *
 * Returns the method being used to perform serial I/O, if this has been
 * determined. You can only assume that this value will be set after
 * ODComOpen() has been called.
 *
 * Parameters: hPort   - Handle to a serial port object.
 *
 *             pMethod - Pointer to a tComMethod, in which function will
 *                       store the method of serial I/O being used, if this
 *                       has been determined.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComGetMethod(tPortHandle hPort, tComMethod *pMethod)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);
   VERIFY_CALL(pMethod != NULL);

   *pMethod = pPortInfo->Method;

   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComOpen()
 *
 * Initializes serial I/O for appropriate serial I/O mechanism (e.g. FOSSIL
 * driver, internal async I/O, etc.)
 *
 * Parameters: hPort - Handle to a serial port object.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComOpen(tPortHandle hPort)
{
#if defined(INCLUDE_FOSSIL_COM) || defined(INCLUDE_UART_COM)
   unsigned int uDivisor;
   unsigned long ulQuotient, ulRemainder;
   BYTE btTemp;
#endif /* INCLUDE_FOSSIL_COM || INCLUDE_UART_COM */
1221 1222 1223
#ifdef INCLUDE_STDIO_COM
	struct termios tio_raw;
#endif
rswindell's avatar
rswindell committed
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);
   int nPort;

   VERIFY_CALL(pPortInfo != NULL);

   nPort = (int)pPortInfo->btPort;

   /* Ensure that port is not already open. */
   VERIFY_CALL(!pPortInfo->bIsOpen);

   /* The following code is used to handle FOSSIL-based serial I/O open */
   /* operations.                                                       */
#ifdef INCLUDE_FOSSIL_COM
   /* If use of FOSSIL driver has not been disabled, then first attempt to */
   /* use it.                                                              */
   if(pPortInfo->Method == kComMethodFOSSIL ||
      pPortInfo->Method == kComMethodUnspecified)
   {
      /* Attempt to open port with FOSSIL DRIVER. */
      ASM    push si
      ASM    push di
      ASM    mov ah, 4
      ASM    mov dx, nPort
      ASM    mov bx, 0
      ASM    int 20
      ASM    pop di
      ASM    pop si
      ASM    cmp ax, 6484
      ASM    je fossil
      goto no_fossil;

fossil:
      pPortInfo->Method = kComMethodFOSSIL;

      /* Enable flow control, if applicable. */

      /* Generate flow control setting. All bits in high nibble of flow   */
      /* control are set to 1, because some FOSSIL driver implementations */
      /* use the high nibble as a control mask.                           */
      if(pPortInfo->btFlowControlSetting & FLOW_DEFAULT)
      {
         btTemp = FLOW_RTSCTS | 0xf0;
      }
      else
      {
         btTemp = pPortInfo->btFlowControlSetting | 0xf0;
      }

      ASM    push si
      ASM    push di
      ASM    mov ah, 0x0f
      ASM    mov al, btTemp
      ASM    mov dx, nPort
      ASM    int 20
      ASM    pop di
      ASM    pop si

      /* If serial port speed is not to be set, then return now. */
      if(pPortInfo->lSpeed == SPEED_UNSPECIFIED)
      {
         /* Set port state to open. */
         pPortInfo->bIsOpen = TRUE;

         /* Return with success. */
         return(kODRCSuccess);
      }

      /* Set to current baud rate. */
      switch(pPortInfo->lSpeed)
      {
         case 300L:
            btTemp = 0x40;
            break;
         case 600L:
            btTemp = 0x60;
            break;
         case 1200L:
            btTemp = 0x80;
            break;
         case 2400L:
            btTemp = 0xa0;
            break;
         case 4800L:
            btTemp = 0xc0;
            break;
         case 9600L:
            btTemp = 0xe0;
            break;
         case 19200L:
            btTemp = 0x00;
            break;
         case 38400L:
            btTemp = 0x20;
            break;
         default:
            /* If invalid bps rate, don't change current bps setting. */
            /* Set port state to open. */
            pPortInfo->bIsOpen = TRUE;

            /* Return with success. */
            return(kODRCSuccess);
      }

      /* Add desired word format parameters to data to be passed to fossil. */
      btTemp |= pPortInfo->btWordFormat;

      /* Initialize fossil driver. */
      ASM    push si
      ASM    push di
      ASM    mov al, btTemp
      ASM    mov ah, 0
      ASM    mov dx, nPort
      ASM    int 20
      ASM    pop di
      ASM    pop si

      /* Set port state to open. */
      pPortInfo->bIsOpen = TRUE;

      /* Return with success. */
      return(kODRCSuccess);
   }

no_fossil:
#endif /* INCLUDE_FOSSIL_COM */

   /* The following code is used to carry out the serial port I/O open */
   /* operations if built-in UART-based serial I/O is being used.      */
#ifdef INCLUDE_UART_COM
   if(pPortInfo->Method == kComMethodUART ||
      pPortInfo->Method == kComMethodUnspecified)
   {
      /* Set internal serial I/O flow control variable from pre-set */
      /* flow control options.                                      */
      if(pPortInfo->btFlowControlSetting & FLOW_DEFAULT)
      {
         btFlowControl = FLOW_RTSCTS;
      }
      else
      {
         btFlowControl = pPortInfo->btFlowControlSetting;
      }

      /* Store serial I/O method being used. */
      pPortInfo->Method = kComMethodUART;

      /* Calculate receive buffer high and low water marks for use with */
      /* flow control. */
      nRXHighWaterMark = (pPortInfo->nReceiveBufferSize * RECEIVE_HIGH_NUM)
         / RECEIVE_HIGH_DENOM;
      nRXLowWaterMark = (pPortInfo->nReceiveBufferSize * RECEIVE_LOW_NUM)
         / RECEIVE_LOW_DENOM;

      /* Allocate transmit and receive buffers */
      pbtTXQueue = malloc(nTXQueueSize = pPortInfo->nTransmitBufferSize);
      pbtRXQueue = malloc(nRXQueueSize = pPortInfo->nReceiveBufferSize);

      if(pbtTXQueue == NULL || pbtRXQueue == NULL)
      {
         return(kODRCNoMemory);
      }

      /* If serial port address is unknown. */
      if(pPortInfo->nPortAddress == 0)
      {
         return(kODRCNoPortAddress);
      }

      /* Initialize table of UART register port addresses. */
      nDataRegAddr = pPortInfo->nPortAddress;
      nIntEnableRegAddr = nDataRegAddr + IER;
      nIntIDRegAddr = nDataRegAddr + IIR;
      nLineCtrlRegAddr = nDataRegAddr + LCR;
      nModemCtrlRegAddr = nDataRegAddr + MCR;
      nLineStatusRegAddr = nDataRegAddr + LSR;
      nModemStatusRegAddr = nDataRegAddr + MSR;


      /* Store interrupt vector number and PIC interrupt information for */
      /* the specified IRQ line.                                         */
      if(pPortInfo->btIRQLevel <= 7)
      {
         btIntVector = 0x08 + (pPortInfo->btIRQLevel);
         btI8259Bit = 1 << (pPortInfo->btIRQLevel);
         nI8259MaskRegAddr = 0x21;
         nI8259EndOfIntRegAddr = 0x20;
         nI8259MasterEndOfIntRegAddr = 0x00;
      }
      else
      {
         btIntVector = 0x68 + (pPortInfo->btIRQLevel);
         btI8259Bit = 1 << (pPortInfo->btIRQLevel - 8);
         nI8259MaskRegAddr = 0xA1;
         nI8259EndOfIntRegAddr = 0xA0;
         nI8259MasterEndOfIntRegAddr = 0x20;
      }

      /* Save original state of UART IER register. */
      ASM mov dx, nIntEnableRegAddr
      ASM in al, dx
      ASM mov btOldIntEnableReg, al

      /* Test that a UART is indeed installed at this port address. */
      ASM mov dx, nIntEnableRegAddr
      ASM mov al, 0
      ASM out dx, al

      ASM mov dx, nIntEnableRegAddr
      ASM in al, dx
      ASM mov btTemp, al

      if (btTemp != 0)
      {
         return(kODRCNoUART);
      }

      /* Setup for RTS/CTS flow control, if it is to be used. */
      if(btFlowControl & FLOW_RTSCTS)
      {
         /* Read modem status register. */
         ASM mov dx, nModemStatusRegAddr
         ASM in al, dx
         ASM mov btTemp, al

         /* Enable transmission only if CTS is high. */
         bStopTrans = !(btTemp & CTS);
      }

      /* Save original PIC interrupt settings, and temporarily disable */
      /* interrupts on this IRQ line while we perform initialization.  */
      ASM cli

      ASM mov dx, nI8259MaskRegAddr
      ASM in al, dx
      ASM mov btI8259Mask, al
      ASM or  al, btI8259Bit
      ASM out dx, al

      /* Initialize transmit and recieve buffers. */
      ODComInternalResetTX();
      ODComInternalResetRX();

      /* Re-enable interrupts. */
      ASM sti

      /* Save original interrupt vector. */
      pfOldISR = ODComGetVect(btIntVector);

      /* Set interrupt vector to point to our ISR. */
#ifdef _MSC_VER
      ODComSetVect(btIntVector, (void far *)ODComInternalISR);
#else /* !_MSC_VER */
      ODComSetVect(btIntVector, ODComInternalISR);
#endif /* !_MSC_VER */

      /* Set line control register to 8 data bits, no parity bits, 1 stop */
      /* bit. */
      btTemp = pPortInfo->btWordFormat;
      ASM mov dx, nLineCtrlRegAddr
      ASM mov al, btTemp
      ASM out dx, al

      /* Save original modem control register. */
      ASM cli

      ASM mov dx, nModemCtrlRegAddr
      ASM in al, dx
      ASM mov btOldModemCtrlReg, al

      /* Keep current DTR setting, and activate RTS. */
      btTemp = (btOldModemCtrlReg & DTR) | (OUT2 + RTS);
      ASM mov dx, nModemCtrlRegAddr
      ASM mov al, btTemp
      ASM out dx, al

      /* Enable use of 16550A FIFOs, if available. */
      if(pPortInfo->btFIFOSetting & FIFO_ENABLE)
      {
         /* Set FIFO enable bit and trigger size. */
         btBaseFIFOCtrl = pPortInfo->btFIFOSetting;

         /* Attempt to enable use of FIFO buffers. */
         ASM mov al, btBaseFIFOCtrl
         ASM mov dx, nIntIDRegAddr
         ASM out dx, al

         /* Check whether a 16550A UART is actually present by reading */
         /* state of FIFO buffer. */
         ASM mov dx, nIntIDRegAddr
         ASM in al, dx
         ASM mov btTemp, al

         bUsingFIFO = btTemp & 0xc0;
      }

      ASM sti

      /* Enable receive and modem status interrupts on the UART. */
      ASM mov dx, nIntEnableRegAddr
      ASM mov al, DR + MS
      ASM out dx, al

      ASM cli

      ASM mov dx, nI8259MaskRegAddr
      ASM in al, dx
      ASM mov ah, btI8259Bit
      ASM not ah
      ASM and al, ah
      ASM out dx, al

      ASM sti

      /* Set baud rate, if possible. */

      /* Calculate baud rate divisor. */
      if(pPortInfo->lSpeed != SPEED_UNSPECIFIED)
      {
         ODDWordDivide(&ulQuotient, &ulRemainder, 115200UL, pPortInfo->lSpeed);

         /* If division results in a remainder, then this is an invalid     */
         /* baud rate. We only change the UART baud rate if we have a valid */
         /* rate to set it to. Otherwise, we cross our fingers and proceed  */
         /* with the currently set UART baud rate.                          */
         if(ulRemainder == 0L)
         {
            uDivisor = (unsigned int)ulQuotient;

            /* Disable interrupts. */
            ASM cli

            /* Set baud rate divisor latch. */
            /* The data register now becomes the lower byte of the baud rate */
            /* divisor, and the interrupt enable register becomes the upper  */
            /* byte of the divisor.                                          */
            ASM mov dx, nLineCtrlRegAddr
            ASM in al, dx
            ASM or al, DLATCH
            ASM out dx, al

            /* Write lower byte of baud rate divisor. */
            ASM mov dx, nDataRegAddr
            ASM mov ax, uDivisor
            ASM out dx, al

            /* Write upper byte of baud rate divisor. */
            ASM mov dx, nIntEnableRegAddr
            ASM mov al, ah
            ASM out dx, al

            /* Reset baud rate divisor latch. */
            ASM mov dx, nLineCtrlRegAddr
            ASM in al, dx
            ASM and al, NOT_DL
            ASM out dx, al

            /* Re-enable interrupts. */
            ASM sti
         }
      }

      /* Remember the serial I/O method that we are using. */
      pPortInfo->Method = kComMethodUART;

      /* Store port state as open. */
      pPortInfo->bIsOpen = TRUE;

      /* Return with success. */
      return(kODRCSuccess);
   }
#endif /* INCLUDE_UART_COM */

   /* The following code is used to handle I/O using the Door32 interface. */
#ifdef INCLUDE_DOOR32_COM
   if(pPortInfo->Method == kComMethodDoor32 ||
      pPortInfo->Method == kComMethodUnspecified)
   {
      /* Attempt to load the Door32 DLL. */
      pPortInfo->hinstDoor32DLL = LoadLibrary("DOOR32.DLL");
      if(pPortInfo->hinstDoor32DLL != NULL)
      {
         /* Obtain pointers to required Door32 API function entry points. */
         pPortInfo->pfDoorInitialize = (BOOL (WINAPI *)(void))
            GetProcAddress(pPortInfo->hinstDoor32DLL, "DoorInitialize");
         pPortInfo->pfDoorShutdown = (BOOL (WINAPI *)(void))
            GetProcAddress(pPortInfo->hinstDoor32DLL, "DoorShutdown");
         pPortInfo->pfDoorWrite = (BOOL (WINAPI *)(const BYTE *, DWORD))
            GetProcAddress(pPortInfo->hinstDoor32DLL, "DoorWrite");
         pPortInfo->pfDoorRead = (DWORD (WINAPI *)(BYTE *, DWORD))
            GetProcAddress(pPortInfo->hinstDoor32DLL, "DoorRead");
         pPortInfo->pfDoorGetAvailableEventHandle = (HANDLE (WINAPI *)(void))
            GetProcAddress(pPortInfo->hinstDoor32DLL,
            "DoorGetAvailableEventHandle");
         pPortInfo->pfDoorGetOfflineEventHandle = (HANDLE (WINAPI *)(void))
            GetProcAddress(pPortInfo->hinstDoor32DLL,
            "DoorGetOfflineEventHandle");

         /* Check whether we have successfully been able to obtain all the */
         /* required function entry points.                                */
         if(pPortInfo->pfDoorInitialize != NULL
            && pPortInfo->pfDoorShutdown != NULL
            && pPortInfo->pfDoorWrite != NULL
            && pPortInfo->pfDoorRead != NULL
            && pPortInfo->pfDoorGetAvailableEventHandle != NULL
            && pPortInfo->pfDoorGetOfflineEventHandle != NULL)
         {
            if((*pPortInfo->pfDoorInitialize)())
            {
               /* Set port state as open. */
               pPortInfo->bIsOpen = TRUE;

               /* Set serial I/O method. */
               pPortInfo->Method = kComMethodDoor32;

               /* Return with success. */
               return(kODRCSuccess);
            }
         }

         /* On failure to obtain all Door32 function entry points, unload */
         /* the Door32 DLL.                                               */
         FreeLibrary(pPortInfo->hinstDoor32DLL);
      }

      /* If our attempt to use the Door32 interface failed for any reason, */
      /* then proceed, attempting to use the Win32 serial I/O interface.   */
   }
#endif /* INCLUDE_DOOR32_COM */

   /* The following code is used to handle Win32 API-base serial I/O */
   /* open operations.                                               */
#ifdef INCLUDE_WIN32_COM
   if(pPortInfo->Method == kComMethodWin32 ||
      pPortInfo->Method == kComMethodUnspecified)
   {
      char szDevName[7];
      DCB dcb;

      /* Generate device name. */
      sprintf(szDevName, "COM%u", (unsigned)pPortInfo->btPort + 1);

      /* Attempt to create handle for device. */
      pPortInfo->hCommDev = CreateFile(szDevName, GENERIC_READ | GENERIC_WRITE,
         0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

      /* On open failure, return with an error code. */
      if(pPortInfo->hCommDev == INVALID_HANDLE_VALUE)
      {
         return(kODRCGeneralFailure);
      }

      /* Note that read timeout settings have not been set. */
      pPortInfo->ReadTimeoutState = kNotSet;
      
      /* Call SetupComm() to set queue sizes. */
      if(!SetupComm(pPortInfo->hCommDev, pPortInfo->nReceiveBufferSize,
         pPortInfo->nTransmitBufferSize))
      {
         CloseHandle(pPortInfo->hCommDev);
         return(kODRCGeneralFailure);
      }

      /* Get current port state. */
      if(!GetCommState(pPortInfo->hCommDev, &dcb))
      {
         CloseHandle(pPortInfo->hCommDev);
         return(kODRCGeneralFailure);
      }

      /* Fill device control block. */

      /* Set bps rate, if appropriate. */
      if(pPortInfo->lSpeed != SPEED_UNSPECIFIED)
      {
         dcb.BaudRate = pPortInfo->lSpeed;
      }

      /* Set flow control, if appropriate. */
      if(!(pPortInfo->btFlowControlSetting & FLOW_DEFAULT))
      {
         if(pPortInfo->btFlowControlSetting & FLOW_RTSCTS)
         {
            dcb.fOutxCtsFlow = 1;
            dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
         }
         else
         {
            dcb.fOutxCtsFlow = 0;
            dcb.fRtsControl = RTS_CONTROL_ENABLE;
         }
      }

      /* Set word size. */
      if((pPortInfo->btWordFormat & DATABITS_MASK) == DATABITS_FIVE)
      {
         dcb.ByteSize = 5;
      }
      else if((pPortInfo->btWordFormat & DATABITS_MASK) == DATABITS_SIX)
      {
         dcb.ByteSize = 6;
      }
      else if((pPortInfo->btWordFormat & DATABITS_MASK) == DATABITS_SEVEN)
      {
         dcb.ByteSize = 7;
      }
      else if((pPortInfo->btWordFormat & DATABITS_MASK) == DATABITS_EIGHT)
      {
         dcb.ByteSize = 8;
      }

      /* Set parity. */
      if((pPortInfo->btWordFormat & ODPARITY_MASK) == ODPARITY_NONE)
      {
         dcb.Parity = NOPARITY;
      }
      else if((pPortInfo->btWordFormat & ODPARITY_MASK) == ODPARITY_ODD)
      {
         dcb.Parity = ODDPARITY;
      }
      else if((pPortInfo->btWordFormat & ODPARITY_MASK) == ODPARITY_EVEN)
      {
         dcb.Parity = EVENPARITY;
      }

      /* Enable DTR control. */
      dcb.fDtrControl = DTR_CONTROL_ENABLE;

      /* Set number of stop bits. */
      if((pPortInfo->btWordFormat & STOP_MASK) == STOP_ONE)
      {
         dcb.StopBits = ONESTOPBIT;
      }
      else if((pPortInfo->btWordFormat & STOP_MASK) == STOP_ONE_POINT_FIVE)
      {
         dcb.StopBits = ONE5STOPBITS;
      }
      else if((pPortInfo->btWordFormat & STOP_MASK) == STOP_TWO)
      {
         dcb.StopBits = TWOSTOPBITS;
      }

      /* Set comm state from device control block. */
      if(!SetCommState(pPortInfo->hCommDev, &dcb))
      {
         CloseHandle(pPortInfo->hCommDev);
         return(kODRCGeneralFailure);
      }

      /* Store port state as open. */
      pPortInfo->bIsOpen = TRUE;

      /* Set serial I/O method. */
      pPortInfo->Method = kComMethodWin32;

      /* Return with success. */
      return(kODRCSuccess);
   }
#endif /* INCLUDE_WIN32_COM */

deuce's avatar
deuce committed
1783 1784 1785 1786
#ifdef INCLUDE_STDIO_COM
   if(pPortInfo->Method == kComMethodStdIO ||
      pPortInfo->Method == kComMethodUnspecified)
   {
deuce's avatar
deuce committed
1787 1788
		if (isatty(STDIN_FILENO))  {
			tcgetattr(STDIN_FILENO,&tio_default);
1789 1790
			tio_raw = tio_default;
			cfmakeraw(&tio_raw);
deuce's avatar
deuce committed
1791
			tcsetattr(STDIN_FILENO,TCSANOW,&tio_raw);
1792
			setvbuf(stdout, NULL, _IONBF, 0);
1793 1794
		}

deuce's avatar
deuce committed
1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806
      /* Set port state as open. */
      pPortInfo->bIsOpen = TRUE;

      /* Set serial I/O method. */
      pPortInfo->Method = kComMethodStdIO;

      /* Return with success. */
      return(kODRCSuccess);

   }
#endif /* INCLUDE_STDIO_COM */

rswindell's avatar
rswindell committed
1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836
   /* If we get to this point, then no form of serial I/O could be */
   /* initialized.                                                 */
   return(kODRCGeneralFailure);
}


/* ----------------------------------------------------------------------------
 * ODComOpenFromExistingHandle()
 *
 * Initializes serial I/O using a serial port handle natvie to the current
 * operating system, which has already been opened by another application.
 *
 * Parameters: hPort            - Handle to a serial port object.
 *
 *             dwExistingHandle - Native operating system's handle to an
 *                                already open serial port.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComOpenFromExistingHandle(tPortHandle hPort,
   DWORD dwExistingHandle)
{
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(!pPortInfo->bIsOpen);

#ifdef INCLUDE_SOCKET_COM
	if(pPortInfo->Method == kComMethodSocket) {
1837
		socklen_t delay=FALSE;
rswindell's avatar
rswindell committed
1838 1839 1840

		pPortInfo->socket = dwExistingHandle;

1841 1842 1843 1844
		getsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &(pPortInfo->old_delay), &delay);
		delay=FALSE;
		setsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &delay, sizeof(delay));

rswindell's avatar
rswindell committed
1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975
        pPortInfo->bIsOpen = TRUE;

		return(kODRCSuccess);
	}
#endif /* INCLUDE_SOCKET_COM */

#ifdef INCLUDE_WIN32_COM

   /* Store handle to the Win32 handle to the serial port. */
   pPortInfo->hCommDev = (HANDLE)dwExistingHandle;

   /* Remember that read timeout settings have not been set. */
   pPortInfo->ReadTimeoutState = kNotSet;

   /* Remember that we are using a handle provided by the client, rather  */
   /* than one that we opened ourself. This flag prevents the handle from */
   /* being closed by a call to ODComClose().                             */
   pPortInfo->bUsingClientsHandle = TRUE;

   /* Remember that the serial port is now open. */
   pPortInfo->bIsOpen = TRUE;

   return(kODRCSuccess);

#else /* !INCLUDE_WIN32_COM */
   UNUSED(dwExistingHandle);
   UNUSED(pPortInfo);

   /* If no form of serial I/O included in this build can use this handle, */
   /* then return with a failure.                                          */
   return(kODRCInvalidCall);

#endif /* !INCLUDE_WIN32_COM */
}


/* ----------------------------------------------------------------------------
 * ODComClose()
 *
 * Closes currently open serial port.
 *
 * Parameters: hPort - Handle to a serial port object.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComClose(tPortHandle hPort)
{
#ifdef INCLUDE_UART_COM
   BYTE btTemp;
#endif /* INCLUDE_UART_COM */
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);
   int nPort;

   VERIFY_CALL(pPortInfo != NULL);

   VERIFY_CALL(pPortInfo->bIsOpen);

   /* If we are using the client's handle, then we should not close it. */
   if(pPortInfo->bUsingClientsHandle)
   {
      pPortInfo->bIsOpen = FALSE;
      return(kODRCSuccess);
   }

   nPort = (int)pPortInfo->btPort;

   switch(pPortInfo->Method)
   {
#ifdef INCLUDE_FOSSIL_COM
      case kComMethodFOSSIL:
         ASM    mov ah, 5
         ASM    mov dx, nPort
         ASM    int 20
         break;
#endif /* INCLUDE_FOSSIL_COM */

#ifdef INCLUDE_UART_COM
      case kComMethodUART:
         /* Reset UART registers to their original values. */
         ASM mov dx, nModemCtrlRegAddr
         ASM mov al, btOldModemCtrlReg
         ASM out dx, al
         ASM mov dx, nIntEnableRegAddr
         ASM mov al, btOldIntEnableReg
         ASM out dx, al

         /* Disable interrupts. */
         ASM cli

         /* Reset this line's interrupt enable status on the PIC to its */
         /* original state.                                             */
         ASM mov dx, nI8259MaskRegAddr
         ASM in al, dx
         ASM mov btTemp, al

         btTemp = (btTemp  & ~btI8259Bit) | (btI8259Mask &  btI8259Bit);

         ASM mov dx, nI8259MaskRegAddr
         ASM mov al, btTemp
         ASM out dx, al

         /* Re-enable interrupts. */
         ASM sti

         /* Reset vector to original interrupt handler. */
#ifdef _MSC_VER
         ODComSetVect(btIntVector, (void far *)pfOldISR);
#else /* !_MSC_VER */
         ODComSetVect(btIntVector, pfOldISR);
#endif /* !_MSC_VER */

         break;
#endif /* INCLUDE_UART_COM */

#ifdef INCLUDE_WIN32_COM
      case kComMethodWin32:
         CloseHandle(pPortInfo->hCommDev);
         break;
#endif /* INCLUDE_WIN32_COM */

#ifdef INCLUDE_DOOR32_COM
      case kComMethodDoor32:
         ASSERT(pPortInfo->pfDoorShutdown != NULL);
         (*pPortInfo->pfDoorShutdown)();
         ASSERT(pPortInfo->hinstDoor32DLL != NULL);
         FreeLibrary(pPortInfo->hinstDoor32DLL);
         break;
#endif /* INCLUDE_DOOR32_COM */

#ifdef INCLUDE_SOCKET_COM
      case kComMethodSocket:
1976
		 setsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &(pPortInfo->old_delay), sizeof(pPortInfo->old_delay));
rswindell's avatar
rswindell committed
1977 1978 1979 1980
         closesocket(pPortInfo->socket);
         break;
#endif /* INCLUDE_SOCKET_COM */

deuce's avatar
deuce committed
1981 1982
#ifdef INCLUDE_STDIO_COM
	  case kComMethodStdIO:
deuce's avatar
deuce committed
1983 1984
	     if(isatty(STDIN_FILENO))
		    tcsetattr(STDIN_FILENO,TCSANOW,&tio_default);
deuce's avatar
deuce committed
1985 1986 1987
	     break;
#endif

rswindell's avatar
rswindell committed
1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015
      default:
         /* If we get here, then the current serial I/O method is not */
         /* handled by this function.                                 */
         ASSERT(FALSE);
   }

   /* Store the fact that the port is now closed. */
   pPortInfo->bIsOpen = FALSE;

   /* Return with success. */
   return(kODRCSuccess);
}


/* ----------------------------------------------------------------------------
 * ODComCarrier()
 *
 * Determines whether or not the carrier detect signal is present.
 *
 * Parameters: hPort       - Handle to a serial port object.
 *
 *             pbIsCarrier - Location to store result. Set to TRUE if carrier
 *                           detect signal is high, FALSE if it is low.
 *
 *     Return: kODRCSuccess on success, or an error code on failure.
 */
tODResult ODComCarrier(tPortHandle hPort, BOOL *pbIsCarrier)
{
deuce's avatar
deuce committed
2016 2017 2018
#ifdef ODPLAT_NIX
   sigset_t	  sigs;
#endif
rswindell's avatar
rswindell committed
2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102
   tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo);
   int nPort;

   VERIFY_CALL(pPortInfo != NULL);
   VERIFY_CALL(pbIsCarrier != NULL);

   VERIFY_CALL(pPortInfo->bIsOpen);

   nPort = pPortInfo->btPort;

   switch(pPortInfo->Method)
   {
#ifdef INCLUDE_FOSSIL_COM
      case kComMethodFOSSIL:
      {
         int to_return;

         ASM    mov ah, 3
         ASM    mov dx, nPort
         ASM    int 20
         ASM    and ax, 128
         ASM    mov to_return, ax

         *pbIsCarrier = to_return;

         break;
      }
#endif /* INCLUDE_FOSSIL_COM */

#ifdef INCLUDE_UART_COM
      case kComMethodUART:
      {
         BYTE btMSR;

         ASM mov dx, nModemStatusRegAddr
         ASM in al, dx
         ASM mov btMSR, al

         *pbIsCarrier = btMSR & RLSD;
         break;
      }
#endif /* INCLUDE_UART_COM */

#ifdef INCLUDE_WIN32_COM
      case kComMethodWin32:
      {
         DWORD dwModemStats;

         /* Get modem status settings. */
         if(!GetCommModemStatus(pPortInfo->hCommDev, &dwModemStats))
         {
            return(kODRCGeneralFailure);
         }

         *pbIsCarrier = (dwModemStats & MS_RLSD_ON) ? TRUE : FALSE;

         break;
      }
#endif /* INCLUDE_WIN32_COM */

#ifdef INCLUDE_DOOR32_COM
      case kComMethodDoor32:
         ASSERT(pPortInfo->pfDoorGetOfflineEventHandle != NULL);
         *pbIsCarrier = (WaitForSingleObject(
            (*pPortInfo->pfDoorGetOfflineEventHandle)(),
            0) != WAIT_OBJECT_0);
         break;
#endif /* INCLUDE_DOOR32_COM */

#ifdef INCLUDE_SOCKET_COM
      case kComMethodSocket:
		{
			int		i;
			char		ch;
			fd_set	socket_set;
			struct	timeval tv;

			FD_ZERO(&socket_set);
			FD_SET(pPortInfo->socket,&socket_set);

			tv.tv_sec=0;
			tv.tv_usec=0;
			i=select(pPortInfo->socket+1,&socket_set,NULL,NULL,&tv);
			if(i==0 
2103
				|| (i==1 && recv(pPortInfo->socket,&ch,1,MSG_PEEK)==1))
rswindell's avatar
rswindell committed
2104 2105 2106 2107 2108 2109
				*pbIsCarrier = TRUE;
			else
				*pbIsCarrier = FALSE;
			break;
		}
#endif
2110 2111

#ifdef INCLUDE_STDIO_COM
deuce's avatar
deuce committed
2112 2113 2114 2115 2116 2117 2118 2119 2120
	  case kComMethodStdIO:
	    {
			sigpending(&sigs);
			if(sigismember(&sigs,SIGHUP))
				*pbIsCarrier = FALSE;
			else
				*pbIsCarrier = TRUE;
			break;
		}
2121
#endif
rswindell's avatar
rswindell committed
2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160