windows-nt/Source/XPSP1/NT/multimedia/media/dplayx/dplay/serial/serial.c
2020-09-26 16:20:57 +08:00

580 lines
14 KiB
C

/*==========================================================================
*
* 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
***************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include "dplaysp.h"
#include "comport.h"
#include "resource.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
GlobalFreePtr((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);
}