//***************************************************************************** // // serial.cpp : Implementation of simple serial port data communications. // // Author: Reed Bement 04/09/2004 // // Notes: // ---- // // Simple polling based serial data communications with no event or // handshaking. // //***************************************************************************** #include #include #include "serial.h" //***************************************************************************** // // Global Variables // //***************************************************************************** DWORD g_dwValidBaudRates[] = { CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000 }; //***************************************************************************** // // MapComErrorToString - . // // Paramaters: // // pszErr - Pointer to string to receive error string. // dwSize - Size of receiving string buffer in characters. // dwErr - Error mask from ClearCommError. // // Returns - Nothing. // //***************************************************************************** void MapComErrorToString(PSZ pszErr, DWORD dwSize, DWORD dwErr) { #define MAX_COM_ERR_STR 16 pszErr[0] = '\0'; if (dwSize > MAX_COM_ERR_STR) { if (dwErr & CE_BREAK) strcpy(pszErr, "Break "); if (dwErr & CE_DNS) strcat(pszErr, "Parallel DNS "); if (dwErr & CE_FRAME) strcat(pszErr, "Frame "); if (dwErr & CE_IOE) strcat(pszErr, "I/O Error "); if (dwErr & CE_MODE) strcat(pszErr, "Mode "); if (dwErr & CE_OOP) strcat(pszErr, "Paper "); if (dwErr & CE_OVERRUN) strcat(pszErr, "Buffer Overrun "); if (dwErr & CE_PTO) strcat(pszErr, "Parallel TO "); if (dwErr & CE_RXOVER) strcat(pszErr, "Receive Overrun "); if (dwErr & CE_RXPARITY) strcat(pszErr, "Parity "); if (dwErr & CE_TXFULL) strcat(pszErr, "Transmit Full "); } else { strncpy(pszErr, "pszErr too small", dwSize); pszErr[dwSize-1] = '\0'; } } //***************************************************************************** // // SetSerialError - Set an error in the serial context. // // Paramaters: // // psc - Pointer to the ser comm. context. // dwError - Error number to set. // // Returns - Nothing. // //***************************************************************************** void SetSerialError(PSERIAL_CONTEXT psc, DWORD dwError, PSZ pszFuncName, DWORD dwParam) { char pszErr[64]; psc->dwError = dwError; switch (dwError) { case BAD_PORT_NUM: sprintf(psc->szError, "Bad COM port requested: %d", psc->dwComPort); break; case BAD_BAUD_RATE: sprintf(psc->szError, "Bad baud rate requested: %d", psc->dwBaudRate); break; case W32_COMM_ERR: { DWORD dwLastErr = GetLastError(); sprintf(psc->szError, "Error calling %s on COM%d:, LastError: %d, 0x%X", pszFuncName, psc->dwComPort, dwLastErr, dwLastErr); break; } case COMM_ERR: { MapComErrorToString(pszErr, 64, dwParam); sprintf(psc->szError, "Comm error: %s detected during: %s", pszErr, pszFuncName); break; } case SERIAL_UNINIT: sprintf(psc->szError, "Serial context not initialized during: %s", pszFuncName); break; case READ_SIZE_ERR: { sprintf(psc->szError, "ReadFile bytes read doesn't match requested: %d", dwParam); break; } case BLOCK_TOO_BIG: { sprintf(psc->szError, "Request block larger than driver buffer: %d", dwParam); break; } default: sprintf(psc->szError, "Unknown serial error: %d", dwError); } } //***************************************************************************** // // CheckCommStatus - Check the status of the comm port. // // Paramaters: // // psc - Pointer to the ser comm. context. // pdwRxCount - Pointer to DWORD to receive buffer count. // pdwTxCount - Pointer to DWORD to transmit buffer count. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL CheckCommStatus(PSERIAL_CONTEXT psc, PDWORD pdwRxCount, PDWORD pdwTxCount) { if (pdwRxCount) *pdwRxCount = 0; if (pdwTxCount) *pdwTxCount = 0; if (psc->hCom != INVALID_HANDLE_VALUE) { DWORD dwErrCode; COMSTAT cs; if (ClearCommError(psc->hCom, &dwErrCode, &cs)) { if (!dwErrCode) { if (pdwRxCount) *pdwRxCount = cs.cbInQue; if (pdwTxCount) *pdwTxCount = cs.cbOutQue; return TRUE; } else { PurgeComm(psc->hCom, PURGE_RXCLEAR | PURGE_TXCLEAR); SetSerialError(psc, COMM_ERR, "CheckCommStatus", dwErrCode); } } else { SetSerialError(psc, W32_COMM_ERR, "ClearCommError", 0); } } else { SetSerialError(psc, SERIAL_UNINIT, "CheckCommStatus", 0); } return FALSE; } //***************************************************************************** // // InitSerial - Initialize context and open a serial port. // // Paramaters: // // psc - Pointer to the ser comm. context. // dwComPort - Port number up to MAX_PORTS. // dwBaudRate - May any one of the CBR_* constant baud rate values. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL InitSerial(PSERIAL_CONTEXT psc, DWORD dwComPort, DWORD dwBaudRate) { // Clear context. memset(psc, 0, sizeof(PSERIAL_CONTEXT)); psc->dwSize = sizeof(PSERIAL_CONTEXT); psc->hCom = INVALID_HANDLE_VALUE; psc->dwComPort = dwComPort; psc->dwBaudRate = dwBaudRate; // Parameter validation if ((dwComPort > 0) && (dwComPort <= MAX_PORTS)) { BOOL bFound = FALSE; for (int i = 0; i < (sizeof(g_dwValidBaudRates)/sizeof(DWORD)); i++) { if (g_dwValidBaudRates[i] == dwBaudRate) { bFound = TRUE; break; } } if (!bFound) { SetSerialError(psc, BAD_BAUD_RATE, 0, 0); return FALSE; } } else { SetSerialError(psc, BAD_PORT_NUM, 0, 0); return FALSE; } sprintf(psc->szPortName, "COM%d", dwComPort); psc->hCom = CreateFile(psc->szPortName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (psc->hCom != INVALID_HANDLE_VALUE) { // Omit the call to SetupComm to use the default queue sizes. // Get the current configuration. if (GetCommState(psc->hCom, &psc->dcb)) { // Fill in the DCB. psc->dcb.BaudRate = psc->dwBaudRate; psc->dcb.ByteSize = 8; psc->dcb.Parity = NOPARITY; psc->dcb.StopBits = ONESTOPBIT; psc->dcb.fAbortOnError = 1; psc->dcb.fDsrSensitivity = 0; psc->dcb.fDtrControl = 0; psc->dcb.fOutxCtsFlow = 0; psc->dcb.fOutxDsrFlow = 0; psc->dcb.fRtsControl = 0; if (SetCommState(psc->hCom, &psc->dcb)) { // Set timeouts if (GetCommTimeouts(psc->hCom, &psc->cto)) { psc->cto.ReadIntervalTimeout = 2000; psc->cto.ReadTotalTimeoutConstant = 2000; psc->cto.ReadTotalTimeoutMultiplier = 10; if (SetCommTimeouts(psc->hCom, &psc->cto)) { if (SetCommMask(psc->hCom, 0)) { COMMPROP cp; if (GetCommProperties(psc->hCom, &cp)) { psc->dwMaxBuffer = cp.dwCurrentRxQueue; return TRUE; } else { SetSerialError(psc, W32_COMM_ERR, "GetCommProperties", 0); } } else { SetSerialError(psc, W32_COMM_ERR, "SetCommMask", 0); } } else { SetSerialError(psc, W32_COMM_ERR, "SetCommTimeouts", 0); } } else { SetSerialError(psc, W32_COMM_ERR, "GetCommTimeouts", 0); } } else { SetSerialError(psc, W32_COMM_ERR, "SetCommState", 0); } } else { SetSerialError(psc, W32_COMM_ERR, "GetCommState", 0); } CloseHandle(psc->hCom); } else { SetSerialError(psc, W32_COMM_ERR, "CreateFile", 0); } return FALSE; } //***************************************************************************** // // CloseSerial - Close the serial port and clear the context. // // Paramaters: // // psc - Pointer to the ser comm. context. // // Returns - Nothing. // //***************************************************************************** void CloseSerial(PSERIAL_CONTEXT psc) { if (psc->hCom != INVALID_HANDLE_VALUE) { CloseHandle(psc->hCom); } // Clear context. memset(psc, 0, sizeof(PSERIAL_CONTEXT)); psc->dwSize = sizeof(PSERIAL_CONTEXT); psc->hCom = INVALID_HANDLE_VALUE; } //***************************************************************************** // // SendSerialByte - Send a byte out the serial port. Blocks. // // Paramaters: // // psc - Pointer to the ser comm. context. // b - Byte to be sent. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL SendSerialByte(PSERIAL_CONTEXT psc, BYTE b) { return SendSerialBlock(psc, &b, 1); } //***************************************************************************** // // RecvSerialByte - Receive a byte from the serial port. Blocks. // // Paramaters: // // psc - Pointer to the ser comm. context. // pb - Pointer to location for received byte. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL RecvSerialByte(PSERIAL_CONTEXT psc, PBYTE pb) { return RecvSerialBlock(psc, pb, 1); } //***************************************************************************** // // SendSerialBlock - Send a block of bytes to the serial port. Blocks. // // Paramaters: // // psc - Pointer to the ser comm. context. // pb - Pointer to block. // dwByteCount - Number of bytes to send. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL SendSerialBlock(PSERIAL_CONTEXT psc, PBYTE pb, DWORD dwByteCount) { if (psc->hCom != INVALID_HANDLE_VALUE) { if (dwByteCount <= psc->dwMaxBuffer) { DWORD dwWritten; if (WriteFile(psc->hCom, pb, dwByteCount, &dwWritten, NULL)) { return TRUE; } else { // See if there's a Comm error, CheckCommStatus will SetSerialError if so. if (CheckCommStatus(psc, NULL, NULL)) { SetSerialError(psc, W32_COMM_ERR, "WriteFile", 0); } } } else { SetSerialError(psc, BLOCK_TOO_BIG, "RecvSerialBlock", psc->dwMaxBuffer); } } else { SetSerialError(psc, SERIAL_UNINIT, "SendSerialBlock", 0); } return FALSE; } //***************************************************************************** // // RecvSerialBlock - Receive a block of bytes from the serial port. Blocks. // // Paramaters: // // psc - Pointer to the ser comm. context. // pb - Pointer to block. // dwByteCount - Number of bytes to send. // // Returns - TRUE on success, FALSE on failure. // //***************************************************************************** BOOL RecvSerialBlock(PSERIAL_CONTEXT psc, PBYTE pb, DWORD dwByteCount) { if (psc->hCom != INVALID_HANDLE_VALUE) { if (dwByteCount <= psc->dwMaxBuffer) { DWORD dwRecvCount; // Check to see if there is any data waiting in the buffer. while (CheckCommStatus(psc, &dwRecvCount, NULL)) { if (dwRecvCount) { DWORD dwRead; if (ReadFile(psc->hCom, pb, dwByteCount, &dwRead, NULL)) { if (dwByteCount == dwRead) { return TRUE; } else { SetSerialError(psc, READ_SIZE_ERR, "RecvSerialBlock", dwByteCount); } } else { SetSerialError(psc, W32_COMM_ERR, "ReadFile", 0); } break; } else { // No data ready, wait 200ms. Sleep(200); } } } else { SetSerialError(psc, BLOCK_TOO_BIG, "RecvSerialBlock", psc->dwMaxBuffer); } } else { SetSerialError(psc, SERIAL_UNINIT, "RecvSerialBlock", 0); } return FALSE; }