/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: serial.c * Content: Routines for serial I/O * History: * Date By Reason * ==== == ====== * 6/10/96 kipo created it * 6/22/96 kipo added support for EnumConnectionData() * 6/25/96 kipo updated for DPADDRESS * 7/13/96 kipo added GetSerialAddress() * 7/16/96 kipo changed address types to be GUIDs instead of 4CC * 8/21/96 kipo move comport address into dplobby.h * 1/06/97 kipo updated for objects * 2/11/97 kipo pass player flags to GetAddress() * 2/18/97 kipo allow multiple instances of service provider * 3/17/97 kipo deal with errors returned by DialogBoxParam() * 5/07/97 kipo added support for modem choice list * 12/22/00 aarono #190380 - use process heap for memory allocation ***************************************************************************/ #include #include #include "dplaysp.h" #include "comport.h" #include "resource.h" #include "macros.h" // constants typedef enum { ASCII_XON = 0x11, ASCII_XOFF = 0x13 }; // serial object typedef struct { DPCOMPORT comPort; // base object globals BOOL bHaveSettings; // set to TRUE after settings dialog has been displayed DPCOMPORTADDRESS settings; // settings to use } DPSERIAL, *LPDPSERIAL; // dialog choices for serial port settings static DWORD gComPorts[] = { 1, 2, 3, 4 }; static DWORD gBaudRates[] = { 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 }; static DWORD gStopBits[] = { ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS }; static DWORD gParities[] = { NOPARITY, EVENPARITY, ODDPARITY, MARKPARITY }; static DWORD gFlowControls[] = { DPCPA_NOFLOW, DPCPA_XONXOFFFLOW, DPCPA_RTSFLOW, DPCPA_DTRFLOW, DPCPA_RTSDTRFLOW }; // globals // this is defined in dllmain.c extern HINSTANCE ghInstance; // this is defined in dpserial.c extern GUID DPSERIAL_GUID; // prototypes static HRESULT DisposeSerial(LPDPCOMPORT baseObject); static HRESULT ConnectSerial(LPDPCOMPORT baseObject, BOOL bWaitForConnection, BOOL bReturnStatus); static HRESULT DisconnectSerial(LPDPCOMPORT baseObject); static HRESULT GetSerialAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags, LPVOID lpAddress, LPDWORD lpdwAddressSize); static HRESULT GetSerialAddressChoices(LPDPCOMPORT baseObject, LPVOID lpAddress, LPDWORD lpdwAddressSize); static BOOL SetupConnection(HANDLE hCom, LPDPCOMPORTADDRESS portSettings); static BOOL FAR PASCAL EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize, LPCVOID lpData, LPVOID lpContext); static BOOL GetSerialSettings(HINSTANCE hInstance, HWND hWndParent, LPDPSERIAL globals); static UINT_PTR CALLBACK SettingsDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); static void InitDialog(HWND hDlg, LPDPCOMPORTADDRESS settings); static void GetSettingsFromDialog(HWND hDlg, LPDPCOMPORTADDRESS settings); static int ValueToIndex(LPDWORD buf, int bufLen, DWORD value); static void FillComboBox(HWND hDlg, int dlgItem, int startStr, int stopStr); /* * NewSerial * * Create new serial port object. */ HRESULT NewSerial(LPVOID lpConnectionData, DWORD dwConnectionDataSize, LPDIRECTPLAYSP lpDPlay, LPREADROUTINE lpReadRoutine, LPDPCOMPORT *storage) { LPDPCOMPORT baseObject; LPDPSERIAL globals; HRESULT hr; // create base object with enough space for our globals hr = NewComPort(sizeof(DPSERIAL), lpDPlay, lpReadRoutine, &baseObject); if FAILED(hr) return (hr); // fill in methods we implement baseObject->Dispose = DisposeSerial; baseObject->Connect = ConnectSerial; baseObject->Disconnect = DisconnectSerial; baseObject->GetAddress = GetSerialAddress; baseObject->GetAddressChoices = GetSerialAddressChoices; // setup default settings globals = (LPDPSERIAL) baseObject; globals->settings.dwComPort = 1; // COM port to use (1-4) globals->settings.dwBaudRate = CBR_57600; // baud rate (100-256k) globals->settings.dwStopBits = ONESTOPBIT; // no. stop bits (1-2) globals->settings.dwParity = NOPARITY; // parity (none, odd, even, mark) globals->settings.dwFlowControl = DPCPA_RTSDTRFLOW; // flow control (none, xon/xoff, rts, dtr) // check for valid connection data if (lpConnectionData) { baseObject->lpDPlay->lpVtbl->EnumAddress(baseObject->lpDPlay, EnumAddressData, lpConnectionData, dwConnectionDataSize, globals); } // return object pointer *storage = baseObject; return (DP_OK); } /* * EnumConnectionData * * Search for valid connection data */ static BOOL FAR PASCAL EnumAddressData(REFGUID lpguidDataType, DWORD dwDataSize, LPCVOID lpData, LPVOID lpContext) { LPDPSERIAL globals = (LPDPSERIAL) lpContext; LPDPCOMPORTADDRESS settings = (LPDPCOMPORTADDRESS) lpData; // this is a com port chunk if ( IsEqualGUID(lpguidDataType, &DPAID_ComPort) && (dwDataSize == sizeof(DPCOMPORTADDRESS)) ) { // make sure it's valid! if ((ValueToIndex(gComPorts, sizeof(gComPorts), settings->dwComPort) >= 0) && (ValueToIndex(gBaudRates, sizeof(gBaudRates), settings->dwBaudRate) >= 0) && (ValueToIndex(gStopBits, sizeof(gStopBits), settings->dwStopBits) >= 0) && (ValueToIndex(gParities, sizeof(gParities), settings->dwParity) >= 0) && (ValueToIndex(gFlowControls, sizeof(gFlowControls), settings->dwFlowControl) >= 0)) { globals->settings = *settings; // copy the data globals->bHaveSettings = TRUE; // we have valid settings } } return (TRUE); } /* * DisposeSerial * * Dispose serial port object. */ static HRESULT DisposeSerial(LPDPCOMPORT baseObject) { LPDPSERIAL globals = (LPDPSERIAL) baseObject; // make sure we are disconnected DisconnectSerial(baseObject); // free object SP_MemFree((HGLOBAL) baseObject); return (DP_OK); } /* * ConnectSerial * * Open serial port and configure based on user settings. */ static HRESULT ConnectSerial(LPDPCOMPORT baseObject, BOOL bWaitForConnection, BOOL bReturnStatus) { LPDPSERIAL globals = (LPDPSERIAL) baseObject; HANDLE hCom; TCHAR portName[10]; HRESULT hr; // see if com port is already connected hCom = baseObject->GetHandle(baseObject); if (hCom) return (DP_OK); // ask user for settings if we have not already if (!globals->bHaveSettings) { if (!GetSerialSettings(ghInstance, GetForegroundWindow(), globals)) { hr = DPERR_USERCANCEL; goto Failure; } globals->bHaveSettings = TRUE; } // open specified com port CopyMemory(portName, "COM0", 5); portName[3] += (BYTE) globals->settings.dwComPort; hCom = CreateFile( portName, GENERIC_READ | GENERIC_WRITE, 0, /* comm devices must be opened w/exclusive-access */ NULL, /* no security attrs */ OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // overlapped I/O NULL /* hTemplate must be NULL for comm devices */ ); if (hCom == INVALID_HANDLE_VALUE) { hCom = NULL; hr = HRESULT_FROM_WIN32(GetLastError()); goto Failure; } // configure com port to proper settings if (!SetupConnection(hCom, &globals->settings)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Failure; } // setup com port hr = baseObject->Setup(baseObject, hCom); if FAILED(hr) goto Failure; return (DP_OK); Failure: if (hCom) CloseHandle(hCom); return (hr); } /* * DisconnectSerial * * Close serial port. */ static HRESULT DisconnectSerial(LPDPCOMPORT baseObject) { HANDLE hCom; HRESULT hr; hCom = baseObject->GetHandle(baseObject); // com port is already disconnected if (hCom == NULL) return (DP_OK); // shut down com port hr = baseObject->Shutdown(baseObject); // close com port CloseHandle(hCom); return (hr); } /* * SetupConnection * * Configure serial port with specified settings. */ static BOOL SetupConnection(HANDLE hCom, LPDPCOMPORTADDRESS portSettings) { DCB dcb; dcb.DCBlength = sizeof(DCB); if (!GetCommState(hCom, &dcb)) return (FALSE); // setup various port settings dcb.fBinary = TRUE; dcb.BaudRate = portSettings->dwBaudRate; dcb.ByteSize = 8; dcb.StopBits = (BYTE) portSettings->dwStopBits; dcb.Parity = (BYTE) portSettings->dwParity; if (portSettings->dwParity == NOPARITY) dcb.fParity = FALSE; else dcb.fParity = TRUE; // setup hardware flow control if ((portSettings->dwFlowControl == DPCPA_DTRFLOW) || (portSettings->dwFlowControl == DPCPA_RTSDTRFLOW)) { dcb.fOutxDsrFlow = TRUE; dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; } else { dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_ENABLE; } if ((portSettings->dwFlowControl == DPCPA_RTSFLOW) || (portSettings->dwFlowControl == DPCPA_RTSDTRFLOW)) { dcb.fOutxCtsFlow = TRUE; dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; } else { dcb.fOutxCtsFlow = FALSE; dcb.fRtsControl = RTS_CONTROL_ENABLE; } // setup software flow control if (portSettings->dwFlowControl == DPCPA_XONXOFFFLOW) { dcb.fInX = TRUE; dcb.fOutX = TRUE; } else { dcb.fInX = FALSE; dcb.fOutX = FALSE; } dcb.XonChar = ASCII_XON; dcb.XoffChar = ASCII_XOFF; dcb.XonLim = 100; dcb.XoffLim = 100; if (!SetCommState( hCom, &dcb )) return (FALSE); return (TRUE); } /* * GetSerialAddress * * Return current serial port info if available. */ static HRESULT GetSerialAddress(LPDPCOMPORT baseObject, DWORD dwPlayerFlags, LPVOID lpAddress, LPDWORD lpdwAddressSize) { LPDPSERIAL globals = (LPDPSERIAL) baseObject; HRESULT hResult; // no settings yet if (!globals->bHaveSettings) return (DPERR_UNAVAILABLE); hResult = baseObject->lpDPlay->lpVtbl->CreateAddress(baseObject->lpDPlay, &DPSERIAL_GUID, &DPAID_ComPort, &globals->settings, sizeof(DPCOMPORTADDRESS), lpAddress, lpdwAddressSize); return (hResult); } /* * GetSerialAddressChoices * * Return current serial address choices */ static HRESULT GetSerialAddressChoices(LPDPCOMPORT baseObject, LPVOID lpAddress, LPDWORD lpdwAddressSize) { LPDPSERIAL globals = (LPDPSERIAL) baseObject; // currently the serial provider does not support any choices return (E_NOTIMPL); } /* * GetComPortSettings * * Displays a dialog to gather and return the COM port settings. */ static BOOL GetSerialSettings(HINSTANCE hInstance, HWND hWndParent, LPDPSERIAL globals) { INT_PTR iResult; iResult = (INT_PTR)DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_SETTINGSDIALOG), hWndParent, SettingsDialog, (LPARAM) globals); return (iResult > 0); } /* * SettingsDialog * * The dialog callback routine to display and edit the COM port settings. */ static UINT_PTR CALLBACK SettingsDialog(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { LPDPSERIAL globals = (LPDPSERIAL) GetWindowLongPtr(hDlg, DWLP_USER); HWND hWndCtl; BOOL msgHandled = FALSE; switch (msg) { case WM_INITDIALOG: // serial info pointer passed in lParam globals = (LPDPSERIAL) lParam; // save the globals with the window SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) globals); hWndCtl = GetDlgItem(hDlg, IDC_COMPORT); // make sure our dialog item is there if (hWndCtl == NULL) { EndDialog(hDlg, FALSE); msgHandled = TRUE; } else { InitDialog(hDlg, &globals->settings); // setup our dialog SetFocus(hWndCtl); // focus on com port combo box msgHandled = FALSE; // keep windows from setting input focus for us } break; case WM_COMMAND: if (HIWORD(wParam) == 0) { switch (LOWORD(wParam)) { case IDOK: // return settings GetSettingsFromDialog(hDlg, &globals->settings); EndDialog(hDlg, TRUE); msgHandled = TRUE; break; case IDCANCEL: // cancel EndDialog(hDlg, FALSE); msgHandled = TRUE; break; } } break; } return (msgHandled); } /* * InitDialog * * Initialize the dialog controls to display the given COM port settings. */ static void InitDialog(HWND hDlg, LPDPCOMPORTADDRESS settings) { // fill dialog combo boxes with items from string table FillComboBox(hDlg, IDC_COMPORT, IDS_COM1, IDS_COM4); FillComboBox(hDlg, IDC_BAUDRATE, IDS_BAUD1, IDS_BAUD15); FillComboBox(hDlg, IDC_STOPBITS, IDS_STOPBIT1, IDS_STOPBIT3); FillComboBox(hDlg, IDC_PARITY, IDS_PARITY1, IDS_PARITY4); FillComboBox(hDlg, IDC_FLOW, IDS_FLOW1, IDS_FLOW5); // select default values in combo boxes SendDlgItemMessage(hDlg, IDC_COMPORT, CB_SETCURSEL, ValueToIndex(gComPorts, sizeof(gComPorts), settings->dwComPort), 0); SendDlgItemMessage(hDlg, IDC_BAUDRATE, CB_SETCURSEL, ValueToIndex(gBaudRates, sizeof(gBaudRates), settings->dwBaudRate), 0); SendDlgItemMessage(hDlg, IDC_STOPBITS, CB_SETCURSEL, ValueToIndex(gStopBits, sizeof(gStopBits), settings->dwStopBits), 0); SendDlgItemMessage(hDlg, IDC_PARITY, CB_SETCURSEL, ValueToIndex(gParities, sizeof(gParities), settings->dwParity), 0); SendDlgItemMessage(hDlg, IDC_FLOW, CB_SETCURSEL, ValueToIndex(gFlowControls, sizeof(gFlowControls), settings->dwFlowControl), 0); } /* * GetSettingsFromDialog * * Get the COM port settings from the dialog controls. */ static void GetSettingsFromDialog(HWND hDlg, LPDPCOMPORTADDRESS settings) { INT_PTR index; index = SendDlgItemMessage(hDlg, IDC_COMPORT, CB_GETCURSEL, 0, 0); if (index == CB_ERR) return; settings->dwComPort = gComPorts[index]; index = SendDlgItemMessage(hDlg, IDC_BAUDRATE, CB_GETCURSEL, 0, 0); if (index == CB_ERR) return; settings->dwBaudRate = gBaudRates[index]; index = SendDlgItemMessage(hDlg, IDC_STOPBITS, CB_GETCURSEL, 0, 0); if (index == CB_ERR) return; settings->dwStopBits = gStopBits[index]; index = SendDlgItemMessage(hDlg, IDC_PARITY, CB_GETCURSEL, 0, 0); if (index == CB_ERR) return; settings->dwParity = gParities[index]; index = SendDlgItemMessage(hDlg, IDC_FLOW, CB_GETCURSEL, 0, 0); if (index == CB_ERR) return; settings->dwFlowControl = gFlowControls[index]; } /* * FillComboBox * * Add the specified strings to the combo box. */ #define MAXSTRINGSIZE 200 static void FillComboBox(HWND hDlg, int dlgItem, int startStr, int stopStr) { int i; TCHAR str[MAXSTRINGSIZE]; for (i = startStr; i <= stopStr; i++) { if (LoadString(ghInstance, i, str, MAXSTRINGSIZE)) SendDlgItemMessage(hDlg, dlgItem, CB_ADDSTRING, (WPARAM) 0, (LPARAM) str); } } /* * ValueToIndex * * Convert a settings value to a combo box selection index. */ static int ValueToIndex(LPDWORD buf, int bufLen, DWORD value) { int i; bufLen /= sizeof(DWORD); for (i = 0; i < bufLen; i++) if (buf[i] == value) return (i); return (-1); }