/* 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * 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. * 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. * Sep 18, 2002 6.22 MD Fixed bugs in ODComWaitEvent for non-blocking sockets. * Aug 10, 2003 6.23 SH *nix support */ #define BUILDING_OPENDOORS #include #include #include #include #include #include #include "OpenDoor.h" #ifdef ODPLAT_NIX #include #include #include #include #include #include #include #include #endif #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 */ /* Serial I/O mechanisms supported inder *nix version */ #ifdef ODPLAT_NIX #define INCLUDE_STDIO_COM #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 #endif /* ODPLAT_NIX */ /* Include "windows.h" for Win32-API based serial I/O. */ #ifdef INCLUDE_WIN32_COM #include "windows.h" #endif /* INCLUDE_WIN32_COM */ /* terminal variables */ #ifdef INCLUDE_STDIO_COM struct termios tio_default; /* Initial term settings */ #endif #if defined(_WIN32) && defined(INCLUDE_SOCKET_COM) #include 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; int old_delay; #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 */ #ifdef INCLUDE_STDIO_COM struct termios tio_raw; #endif 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 */ #ifdef INCLUDE_STDIO_COM if(pPortInfo->Method == kComMethodStdIO || pPortInfo->Method == kComMethodUnspecified) { if (isatty(STDIN_FILENO)) { tcgetattr(STDIN_FILENO,&tio_default); tio_raw = tio_default; cfmakeraw(&tio_raw); tcsetattr(STDIN_FILENO,TCSANOW,&tio_raw); setvbuf(stdout, NULL, _IONBF, 0); } /* 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 */ /* 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) { socklen_t delay=FALSE; pPortInfo->socket = dwExistingHandle; getsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &(pPortInfo->old_delay), &delay); delay=FALSE; setsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &delay, sizeof(delay)); 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: setsockopt(pPortInfo->socket, IPPROTO_TCP, TCP_NODELAY, &(pPortInfo->old_delay), sizeof(pPortInfo->old_delay)); closesocket(pPortInfo->socket); break; #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: if(isatty(STDIN_FILENO)) tcsetattr(STDIN_FILENO,TCSANOW,&tio_default); break; #endif 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) { #ifdef ODPLAT_NIX sigset_t sigs; #endif 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 || (i==1 && recv(pPortInfo->socket,&ch,1,MSG_PEEK)==1)) *pbIsCarrier = TRUE; else *pbIsCarrier = FALSE; break; } #endif #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: { sigpending(&sigs); if(sigismember(&sigs,SIGHUP)) *pbIsCarrier = FALSE; else *pbIsCarrier = TRUE; break; } #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComSetDTR() * * Raises or lowers the DTR signal on the port. * * Parameters: hPort - Handle to a serial port object. * * bHigh - TRUE to raise DTR, FALSE to lower it. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComSetDTR(tPortHandle hPort, BOOL bHigh) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: ASM cmp byte ptr bHigh, 0 ASM je lower ASM mov al, 1 ASM jmp set_dtr lower: ASM xor al, al set_dtr: ASM mov ah, 6 ASM mov dx, nPort ASM int 20 #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: if(bHigh) { ASM cli ASM mov dx, nModemCtrlRegAddr ASM in al, dx ASM or al, DTR ASM out dx, al ASM sti } else { ASM cli ASM mov dx, nModemCtrlRegAddr ASM in al, dx ASM and al, NOT_DTR ASM out dx, al ASM sti } break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: /* Set DTR line appropriately. */ if(!EscapeCommFunction(pPortInfo->hCommDev, bHigh ? SETDTR : CLRDTR)) { return(kODRCGeneralFailure); } break; #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: return(kODRCUnsupported); #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: if(bHigh) return(kODRCUnsupported); closesocket(pPortInfo->socket); break; #endif /* INCLUDE_SOCKET_CO */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: return(kODRCUnsupported); #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComOutbound() * * Determines the number of bytes waiting in the serial port outbound buffer. * * Parameters: hPort - Handle to a serial port object. * * pnOutboundWaiting - Location where result the number of bytes * waiting in the outbound buffer should be * stored. Under some I/O methods we can * determine whether data is still in the * buffer, but not the number of bytes in the * buffer. In this situation, this may be set * to SIZE_NON_ZERO. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComOutbound(tPortHandle hPort, int *pnOutboundWaiting) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pnOutboundWaiting != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: ASM mov ah, 0x03 ASM mov dx, nPort ASM int 20 ASM and ah, 0x40 ASM jz still_sending *pnOutboundWaiting = 0; break; still_sending: *pnOutboundWaiting = SIZE_NON_ZERO; break; #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: *pnOutboundWaiting = (int)nTXChars; break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwErrors; COMSTAT ComStat; /* Use ClearCommError() to obtain device status. */ if(!ClearCommError(pPortInfo->hCommDev, &dwErrors, &ComStat)) { return(kODRCGeneralFailure); } /* Set pbIsInbound to TRUE if any bytes are in outbound queue. */ *pnOutboundWaiting = (int)ComStat.cbOutQue; break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: /* Door32 doesn't currently support this functionality, so we */ /* assume that all sent data is transmitted immediately. */ *pnOutboundWaiting = 0; return(kODRCUnsupported); #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: *pnOutboundWaiting = 0; return(kODRCUnsupported); #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: *pnOutboundWaiting = 0; return(kODRCUnsupported); #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComClearOutbound() * * Removes the current contents of the serial port outbound buffer. * * Parameters: hPort - Handle to a serial port object. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComClearOutbound(tPortHandle hPort) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: ASM mov ah, 9 ASM mov dx, nPort ASM int 20 #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: ODComInternalResetTX(); break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: if(!PurgeComm(pPortInfo->hCommDev, PURGE_TXCLEAR)) { return(kODRCGeneralFailure); } break; #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: return(kODRCUnsupported); #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: return(kODRCUnsupported); #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: return(kODRCUnsupported); #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } /* Return with success. */ return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComClearInbound() * * Removes the current contents of the serial port inbound buffer. * * Parameters: hPort - Handle to a serial port object. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComClearInbound(tPortHandle hPort) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: ASM mov ah, 10 ASM mov dx, nPort ASM int 20 #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: ODComInternalResetRX(); break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: if(!PurgeComm(pPortInfo->hCommDev, PURGE_RXCLEAR)) { return(kODRCGeneralFailure); } break; #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: return(kODRCUnsupported); #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: return(kODRCUnsupported); #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: return(kODRCUnsupported); #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } /* Return with success. */ return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComInbound() * * Determines the number of bytes waiting in the serial port inbound buffer. * * Parameters: hPort - Handle to a serial port object. * * pnInboundWaiting - Location in which to store number of bytes * waiting in the inbound buffer. Under some * I/O methods (e.g. FOSSIL driver), we can * determine whether data is still in the * buffer, but not the number of bytes in the * buffer. In this situation, this may be set * to SIZE_NON_ZERO. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComInbound(tPortHandle hPort, int *pnInboundWaiting) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pnInboundWaiting != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: { BOOL bDataInBuffer = FALSE; ASM mov ah, 3 ASM mov dx, nPort ASM push si ASM push di ASM int 20 ASM pop di ASM pop si ASM and ah, 1 ASM mov bDataInBuffer, ah *pnInboundWaiting = bDataInBuffer ? SIZE_NON_ZERO : 0; break; } #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: *pnInboundWaiting = (int)nRXChars; break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwErrors; COMSTAT ComStat; /* Use ClearCommError() to obtain device status. */ if(!ClearCommError(pPortInfo->hCommDev, &dwErrors, &ComStat)) { return(kODRCGeneralFailure); } /* Set pbIsInbound to TRUE if there are any bytes in inbound queue. */ *pnInboundWaiting = (int)ComStat.cbInQue; break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: ASSERT(pPortInfo->pfDoorGetAvailableEventHandle != NULL); if(WaitForSingleObject( (*pPortInfo->pfDoorGetAvailableEventHandle)(), 0) == WAIT_OBJECT_0) { *pnInboundWaiting = SIZE_NON_ZERO; } else { *pnInboundWaiting = 0; } break; #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: if(ioctlsocket(pPortInfo->socket,FIONREAD,pnInboundWaiting) != 0) *pnInboundWaiting = 0; break; #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: if(ioctl(0,FIONREAD,pnInboundWaiting) == -1) *pnInboundWaiting = 0; break; #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComGetByte() * * Returns a single inbound byte. If there are characters waiting in the * inbound buffer, the next character is returned immediately. If bWait is TRUE * and no characters are waiting, this function will wait until a character is * received (possibly forever, if no characters are ever received). * * Parameters: hPort - Handle to a serial port object. * * pbtNext - Location to store retrieved byte. * * bWait - If TRUE, function will only return after a character * has been received. If FALSE, this function will return * kODRCNothingWaiting if no characters are waiting. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComGetByte(tPortHandle hPort, char *pbtNext, BOOL bWait) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pbtNext != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: { BYTE btToReturn; int nInboundSize; /* If we should not wait for characters if inbound queue is empty. */ if(!bWait) { /* Determine whether there are any inbound characterse waiting. */ ODComInbound(hPort, &nInboundSize); /* If there are no inbound characters waiting, then return */ /* without obtaining any characters. */ if(nInboundSize == 0) return(kODRCNothingWaiting); } ASM mov ah, 2 ASM mov dx, nPort ASM push si ASM push di ASM int 20 ASM pop di ASM pop si ASM mov btToReturn, al *pbtNext = btToReturn; break; } #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: /* If we should not wait for characters if inbound queue is empty. */ if(!bWait) { /* If there are no inbound characters waiting, then return */ /* without obtaining any characters. */ if(!nRXChars) return(kODRCNothingWaiting); } /* Loop, calling idle function, until next character arrives. */ while(!nRXChars) { if(pPortInfo->pfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } } /* Disable interrupts. */ ASM cli /* Get next character from receive queue. */ *pbtNext = pbtRXQueue[nRXOutIndex++]; /* Wrap queue index if needed. */ if (nRXOutIndex == nRXQueueSize) { nRXOutIndex = 0; } /* Decrement count of total character in the receive queue. */ nRXChars--; /* Re-enable interrupts. */ ASM sti /* If receive buffer is below low water mark. */ if(nRXChars <= nRXLowWaterMark) { /* If we are using flow control, then stop sender from */ /* sending. */ if(btFlowControl & FLOW_RTSCTS) { /* If using RTS/CTS flow control, then raise RTS line. */ ASM mov dx, nModemCtrlRegAddr ASM in al, dx ASM or al, RTS ASM out dx, al } } break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwBytesRead; DWORD dwErrors; /* Ensure read timeout state is set appropriately for bWait value. */ if(bWait) { ODComWin32SetReadTimeouts(pPortInfo, kBlocking); } else { ODComWin32SetReadTimeouts(pPortInfo, kNonBlocking); } /* Perform read operation. */ if(!ReadFile(pPortInfo->hCommDev, pbtNext, 1, &dwBytesRead, NULL)) { ClearCommError(pPortInfo->hCommDev, &dwErrors, NULL); return(kODRCGeneralFailure); } /* Determine whether or not a byte was read. */ if(dwBytesRead == 0) { /* If no bytes where read, then this is a general error if bWait */ /* is TRUE. If bWait is FALSE, then we should return */ /* waiting kODRCNothingWaiting. */ return(bWait ? kODRCGeneralFailure : kODRCNothingWaiting); } break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: if(WaitForSingleObject((*pPortInfo->pfDoorGetAvailableEventHandle)(), bWait ? INFINITE : 0) == WAIT_OBJECT_0) { (*pPortInfo->pfDoorRead)(pbtNext, 1); break; } return(bWait ? kODRCGeneralFailure : kODRCNothingWaiting); break; #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: { fd_set socket_set; struct timeval tv; int select_ret, recv_ret; FD_ZERO(&socket_set); FD_SET(pPortInfo->socket,&socket_set); tv.tv_sec=0; tv.tv_usec=100; select_ret = select(pPortInfo->socket+1, &socket_set, NULL, NULL, bWait ? NULL : &tv); if (select_ret == SOCKET_ERROR) return (kODRCGeneralFailure); if (select_ret == 0) return (kODRCNothingWaiting); do { recv_ret = recv(pPortInfo->socket, pbtNext, 1, 0); if(recv_ret != SOCKET_ERROR) break; if(WSAGetLastError() != WSAEWOULDBLOCK) return (kODRCGeneralFailure); od_sleep(50); } while (bWait); if (recv_ret == 0) return (kODRCNothingWaiting); break; } #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: { fd_set socket_set; struct timeval tv; int select_ret=-1; int recv_ret; while(select_ret==-1) { FD_ZERO(&socket_set); FD_SET(STDIN_FILENO,&socket_set); tv.tv_sec=0; tv.tv_usec=100; select_ret = select(STDIN_FILENO+1, &socket_set, NULL, NULL, bWait ? NULL : &tv); if (select_ret == -1) { if(errno==EINTR) continue; return (kODRCGeneralFailure); } if (select_ret == 0) return (kODRCNothingWaiting); } recv_ret = read(STDIN_FILENO, pbtNext, 1); if(recv_ret == 1) break; return (kODRCGeneralFailure); break; } #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } return(0); } /* ---------------------------------------------------------------------------- * ODComSendByte() * * Sends a single byte to the serial port outbound buffer. * * Parameters: hPort - Handle to a serial port object. * * btToSend - The byte to transmit. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComSendByte(tPortHandle hPort, BYTE btToSend) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: try_again: ASM mov ah, 0x0b ASM mov dx, nPort ASM mov al, btToSend ASM int 20 ASM cmp ax, 0 ASM jne keep_going /* Call idle function, if any. */ if(pPortInfo->pfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } goto try_again; keep_going: break; #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: /* Loop, calling idle function, until characters are waiting in */ /* the transmit buffer. */ while(!ODComInternalTXReady()) { /* Call idle function, if any. */ if(pPortInfo->pfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } } /* Disable interrupts. */ ASM cli /* Place the character in the queue. */ pbtTXQueue[nTXInIndex++] = btToSend; /* Wrap transmit queue index, if needed. */ if (nTXInIndex == nTXQueueSize) { nTXInIndex = 0; } /* Increment count of total characters in the queue. */ nTXChars++; /* Enable transmit interrupt on the UART. */ ASM mov dx, nIntEnableRegAddr ASM in al, dx ASM or al, THRE ASM out dx, al ASM sti break; #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwErrors; DWORD dwBytesWritten; /* Attempt to perform write operation. */ if(!WriteFile(pPortInfo->hCommDev, &btToSend, 1, &dwBytesWritten, NULL) || dwBytesWritten != 1) { ClearCommError(pPortInfo->hCommDev, &dwErrors, NULL); return(kODRCGeneralFailure); } break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: ASSERT(pPortInfo->pfDoorWrite != NULL); if(!(*pPortInfo->pfDoorWrite)(&btToSend, 1)) { return(kODRCGeneralFailure); } break; #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: { fd_set socket_set; struct timeval tv; int send_ret; FD_ZERO(&socket_set); FD_SET(pPortInfo->socket,&socket_set); tv.tv_sec=1; tv.tv_usec=0; if(select(pPortInfo->socket+1,NULL,&socket_set,NULL,&tv) != 1) return(kODRCGeneralFailure); do { send_ret = send(pPortInfo->socket, &btToSend, 1, 0); if (send_ret != 1) od_sleep(50); } while ((send_ret == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK)); if (send_ret == SOCKET_ERROR) return (kODRCGeneralFailure); break; } #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: { fd_set fdset; struct timeval tv; int retval=-1; int loopcount=0; while(retval==-1 && loopcount < 10) { FD_ZERO(&fdset); FD_SET(STDOUT_FILENO,&fdset); tv.tv_sec=1; tv.tv_usec=0; retval=select(STDOUT_FILENO+1,NULL,&fdset,NULL,&tv); if(retval!=1) { if(retval==0) { retval=-1; loopcount++; continue; } if(retval==-1 && errno==EINTR) continue; return(kODRCGeneralFailure); } } if(fwrite(&btToSend,1,1,stdout)!=1) return(kODRCGeneralFailure); break; } #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } /* Return with success. */ return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComGetBuffer() * * Retreives received data into a buffer, filling the buffer with as much data * as possible that has been received, returning immediately. * * Parameters: hPort - Handle to a serial port object. * * pbtBuffer - Pointer to a contiguous array of bytes. * * nSize - Size of buffer, in bytes. This is the maximum * number of characters that will be returned. * * pnBytesRead - Pointer to an int where function will store the * number of bytes actually read. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComGetBuffer(tPortHandle hPort, BYTE *pbtBuffer, int nSize, int *pnBytesRead) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); int nPort; VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pbtBuffer != NULL); VERIFY_CALL(nSize > 0); VERIFY_CALL(pnBytesRead != NULL); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: { int nReceived; ASM push di ASM mov cx, nSize ASM mov dx, nPort #ifdef LARGEDATA ASM les di, pbtBuffer #else ASM mov ax, ds ASM mov es, ax ASM mov di, pbtBuffer #endif ASM mov ah, 0x18 ASM int 20 ASM pop di ASM mov nReceived, ax *pnBytesRead = nReceived; break; } #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: { int nTransferSize; int nFirstHalfSize; int nSecondHalfSize; char *pbtSource; /* Disable interrupts. */ ASM cli /* Number of bytes to transfer is minimum of buffer size, and */ /* number of bytes in receive queue. */ nTransferSize = MIN(nRXChars, nSize); /* First half of transfer is minimum of number of bytes from here */ /* to the end of the buffer, and the total transfer size. */ nFirstHalfSize = nRXQueueSize - nRXOutIndex; nFirstHalfSize = MIN(nFirstHalfSize, nTransferSize); /* Second half of transfer is remaining bytes, if any. */ nSecondHalfSize = nTransferSize - nFirstHalfSize; /* Perform first half of transfer. */ pbtSource = pbtRXQueue + nRXOutIndex; while(nFirstHalfSize--) { *pbtBuffer++ = *pbtSource++; } /* If there is a second half to transfer. */ if(nSecondHalfSize) { /* Copy source will begin at beginning of queue. */ pbtSource = pbtRXQueue; /* Set final queue out index. */ nRXOutIndex = nSecondHalfSize; /* Perform second half of transfer. */ while(nSecondHalfSize--) { *pbtBuffer++ = *pbtSource++; } } /* If entire transfer was performed in first half. */ else { /* Set final queue out index. */ nRXOutIndex += nTransferSize; /* Wrap queue out index, if needed. */ if(nRXOutIndex == nRXQueueSize) nRXOutIndex = 0; } /* Subtract number of bytes retrieved from number of bytes in */ /* receive queue. */ nRXChars -= nTransferSize; /* Return number of bytes copied into buffer. */ *pnBytesRead = nTransferSize; /* Re-enable interrupts. */ ASM sti break; } #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwBytesRead; DWORD dwErrors; /* Ensure read timeout state is set for non-blocking read */ ODComWin32SetReadTimeouts(pPortInfo, kNonBlocking); /* Perform read operation. */ if(!ReadFile(pPortInfo->hCommDev, pbtBuffer, nSize, &dwBytesRead, NULL)) { ClearCommError(pPortInfo->hCommDev, &dwErrors, NULL); return(kODRCGeneralFailure); } /* Pass number of bytes read back to caller. */ *pnBytesRead = (int)dwBytesRead; break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: ASSERT(pPortInfo->pfDoorRead != NULL); *pnBytesRead = (int)((*pPortInfo->pfDoorRead)(pbtBuffer, nSize)); break; #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: { fd_set socket_set; struct timeval tv; FD_ZERO(&socket_set); FD_SET(pPortInfo->socket,&socket_set); tv.tv_sec=0; tv.tv_usec=100; if(select(pPortInfo->socket+1,&socket_set,NULL,NULL,&tv) != 1) { *pnBytesRead = 0; break; } *pnBytesRead = recv(pPortInfo->socket,pbtBuffer,nSize,0); break; } #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: { for(*pnBytesRead=0; *pnBytesRead= 0); VERIFY_CALL(pPortInfo->bIsOpen); nPort = pPortInfo->btPort; /* If there are no characters to transmit, then there is no need to */ /* proceed further. */ if(nSize == 0) { return(kODRCSuccess); } switch(pPortInfo->Method) { #ifdef INCLUDE_FOSSIL_COM case kComMethodFOSSIL: { int nCount; try_again: ASM push di ASM mov cx, nSize ASM mov dx, nPort #ifdef LARGEDATA ASM les di, pbtBuffer #else ASM mov ax, ds ASM mov es, ax ASM mov di, pbtBuffer #endif ASM mov ah, 0x19 ASM int 20 ASM pop di ASM mov nCount, ax if(nCountpfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } nSize-=nCount; pbtBuffer+=nCount; goto try_again; } break; } #endif /* INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_UART_COM case kComMethodUART: { int nTransferSize; int nFirstHalfSize; int nSecondHalfSize; char *pbtDest; /* Loop, copying as much of buffer to transmit queue as possible, */ /* then waiting for some characters to be transmitted, and copy */ /* more of buffer to transmit queue, until entire buffer has been */ /* transferred. */ for(;;) { /* Disable interrupts. */ ASM cli /* Try to transfer all of buffer if possible. */ nTransferSize = nSize; /* Adjust number of character to transfer down if there isn't */ /* enough space in transmit queue. */ if(nTransferSize > (nTXQueueSize - nTXChars)) { nTransferSize = (nTXQueueSize - nTXChars); } /* Block transfer is divided into two segments - everything from */ /* current in index to end of queue, and everything from */ /* beginning of queue to end of free space in queue. */ /* Calculate size of first half of transfer. */ nFirstHalfSize = nTXQueueSize - nTXInIndex; if(nFirstHalfSize > nTransferSize) nFirstHalfSize = nTransferSize; /* Calculate size of second half of transfer. */ nSecondHalfSize = nTransferSize - nFirstHalfSize; /* Transfer characters at current queue in index. */ pbtDest = pbtTXQueue + nTXInIndex; while(nFirstHalfSize--) { *pbtDest++ = *pbtBuffer++; } /* If there is a second half to transfer. */ if(nSecondHalfSize) { /* Copy destination will begin at beginning of queue. */ pbtDest = pbtTXQueue; /* Set final queue in index. */ nTXInIndex = nSecondHalfSize; /* Perform second half of transfer. */ while(nSecondHalfSize--) { *pbtDest++ = *pbtBuffer++; } } /* If entire transfer was performed in first half. */ else { /* Set final queue in index. */ nTXInIndex += nTransferSize; /* Wrap queue in index if we just happened to fill characters */ /* up to end of physical queue. If there was one less */ /* character transferred, no wrap would be necessary, and if */ /* there was one more character to be transferred, transfer */ /* would have to be performed in two halves. */ if(nTXInIndex == nTXQueueSize) nTXInIndex = 0; } /* Update count of total characters in the queue. */ nTXChars += nTransferSize; /* Enable transmit interrupt on the UART. */ ASM mov dx, nIntEnableRegAddr ASM in al, dx ASM or al, THRE ASM out dx, al /* Re-enable interrupts. */ ASM sti /* Adjust count of characters left to transfer down by number of */ /* characters transferred. */ nSize -= nTransferSize; /* If there are no characters left to transfer, then we are */ /* done. */ if(nSize == 0) break; /* Call idle function, if any. */ if(pPortInfo->pfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } } break; } #endif /* INCLUDE_UART_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwErrors; DWORD dwBytesWritten; /* Attempt to perform write operation. */ if(!WriteFile(pPortInfo->hCommDev, pbtBuffer, nSize, &dwBytesWritten, NULL) || dwBytesWritten != (DWORD)nSize) { ClearCommError(pPortInfo->hCommDev, &dwErrors, NULL); return(kODRCGeneralFailure); } break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: ASSERT(pPortInfo->pfDoorWrite != NULL); if(!(*pPortInfo->pfDoorWrite)(pbtBuffer, nSize)) { return(kODRCGeneralFailure); } break; return(kODRCUnsupported); #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: { fd_set socket_set; struct timeval tv; int send_ret; FD_ZERO(&socket_set); FD_SET(pPortInfo->socket,&socket_set); tv.tv_sec=1; tv.tv_usec=0; if(select(pPortInfo->socket+1,NULL,&socket_set,NULL,&tv) != 1) return(kODRCGeneralFailure); do { send_ret = send(pPortInfo->socket, pbtBuffer, nSize, 0); if (send_ret != SOCKET_ERROR) break; od_sleep(25); } while (WSAGetLastError() == WSAEWOULDBLOCK); if (send_ret != nSize) return (kODRCGeneralFailure); break; } #endif /* INCLUDE_SOCKET_COM */ #ifdef INCLUDE_STDIO_COM case kComMethodStdIO: { int pos=0; fd_set fdset; struct timeval tv; int retval; int loopcount=0; while(pos10) return(kODRCGeneralFailure); continue; } if(retval==-1 && errno==EINTR) continue; return(kODRCGeneralFailure); } retval=fwrite(pbtBuffer+pos,1,nSize-pos,stdout); if(retval!=nSize-pos) { od_sleep(1); } pos+=retval; } break; } #endif default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } /* Return with success. */ return(kODRCSuccess); } /* ---------------------------------------------------------------------------- * ODComWaitEvent() * * Blocks until the specified serial I/O event occurs, or an error condition * is encountered. * * Parameters: hPort - Handle to an open port. * * Event - Event type to wait for. * * Return: kODRCSuccess on success, or an error code on failure. */ tODResult ODComWaitEvent(tPortHandle hPort, tComEvent Event) { tPortInfo *pPortInfo = ODHANDLE2PTR(hPort, tPortInfo); VERIFY_CALL(pPortInfo != NULL); VERIFY_CALL(pPortInfo->bIsOpen); switch(pPortInfo->Method) { #if defined(INCLUDE_UART_COM) || defined(INCLUDE_FOSSIL_COM) || defined(INCLUDE_STDIO_COM) case kComMethodFOSSIL: case kComMethodUART: case kComMethodStdIO: switch(Event) { case kNoCarrier: { BOOL bCarrier; for(;;) { ODComCarrier(hPort, &bCarrier); if(!bCarrier) break; if(pPortInfo->pfIdleCallback != NULL) { (*pPortInfo->pfIdleCallback)(); } } break; } default: VERIFY_CALL(FALSE); } break; #endif /* INCLUDE_UART_COM || INCLUDE_FOSSIL_COM */ #ifdef INCLUDE_WIN32_COM case kComMethodWin32: { DWORD dwEvtMask; /* Obtain current event mask. */ if(!GetCommMask(pPortInfo->hCommDev, &dwEvtMask)) { return(kODRCGeneralFailure); } /* Turn on event to be waited for. */ switch(Event) { case kNoCarrier: dwEvtMask |= EV_RLSD; break; default: VERIFY_CALL(FALSE); } /* Write new event mask. */ if(!SetCommMask(pPortInfo->hCommDev, dwEvtMask)) { return(kODRCGeneralFailure); } /* Wait until event occurs. */ for(;;) { /* Block until some event occurs. */ if(!WaitCommEvent(pPortInfo->hCommDev, &dwEvtMask, NULL)) { return(kODRCGeneralFailure); } /* Determine whether this is what we are waiting for. */ switch(Event) { case kNoCarrier: if(dwEvtMask | EV_RLSD) { BOOL bCarrier; ODComCarrier(hPort, &bCarrier); if(!bCarrier) { return(kODRCSuccess); } } break; } /* If we get here, the event we are waiting for hasn't occurred */ /* yet, so loop and block waiting for next event. */ } break; } #endif /* INCLUDE_WIN32_COM */ #ifdef INCLUDE_DOOR32_COM case kComMethodDoor32: switch(Event) { case kNoCarrier: ASSERT(pPortInfo->pfDoorGetOfflineEventHandle != NULL); WaitForSingleObject( (*pPortInfo->pfDoorGetOfflineEventHandle)(), INFINITE); break; default: VERIFY_CALL(FALSE); } break; #endif /* INCLUDE_DOOR32_COM */ #ifdef INCLUDE_SOCKET_COM case kComMethodSocket: { if(Event == kNoCarrier) { /* Wait for socket disconnect */ fd_set socket_set; char ch; int recv_ret; while(1) { FD_ZERO(&socket_set); FD_SET(pPortInfo->socket,&socket_set); if(select(pPortInfo->socket+1,&socket_set,NULL,NULL,NULL) ==SOCKET_ERROR) break; recv_ret = recv(pPortInfo->socket, &ch, 1, MSG_PEEK); if(recv_ret == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK) continue; if (recv_ret != 1) break; } } else { VERIFY_CALL(FALSE); } break; } #endif /* INCLUDE_SOCKET_COM */ default: /* If we get here, then the current serial I/O method is not */ /* handled by this function. */ ASSERT(FALSE); } /* Return with success. */ return(kODRCSuccess); }