906 lines
22 KiB
C
906 lines
22 KiB
C
|
/*==========================================================================
|
||
|
*
|
||
|
* Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved.
|
||
|
*
|
||
|
* File: dpserial.c
|
||
|
* Content: Implementation of serial port service provider
|
||
|
* History:
|
||
|
* Date By Reason
|
||
|
* ==== == ======
|
||
|
* 4/10/96 kipo created it
|
||
|
* 4/12/96 kipo updated for new interfaces
|
||
|
* 4/15/96 kipo added msinternal
|
||
|
* 5/22/96 kipo updated for new interfaces
|
||
|
* 6/10/96 kipo updated for new interfaces
|
||
|
* 6/10/96 kipo added modem support
|
||
|
* 6/18/96 kipo use guid to choose serial/modem connection
|
||
|
* 6/20/96 kipo updated for new interfaces
|
||
|
* 6/21/96 kipo Bug #2078. Changed modem service provider GUID so it's not the
|
||
|
* same as the DPlay 1.0 GUID, so games that are checking won't
|
||
|
* put up their loopy modem-specific UI.
|
||
|
* 6/21/96 kipo updated for latest interfaces; return error if message size is too big.
|
||
|
* 6/22/96 kipo updated for latest interfaces; use connection data; return version
|
||
|
* 6/23/96 kipo updated for latest service provider interfaces.
|
||
|
* 6/24/96 kipo divide baud rate by 100 to conform to DPlay 1.0 usage.
|
||
|
* 6/25/96 kipo added WINAPI prototypes and updated for DPADDRESS
|
||
|
* 7/13/96 kipo added support for GetAddress() method.
|
||
|
* 7/13/96 kipo don't print as many errors for invalid messages.
|
||
|
* 8/10/96 kipo return DPERR_SESSIONLOST on write failures
|
||
|
* 8/13/96 kipo added CRC
|
||
|
* 8/21/96 kipo return a value for dwHeaderLength in caps
|
||
|
* 9/07/96 kip changed latency and timeout values
|
||
|
* 1/06/97 kipo updated for objects
|
||
|
* 2/11/97 kipo pass player flags to GetAddress()
|
||
|
* 2/11/97 kipo SPInit was needlessly clearing the dwFlags field of the
|
||
|
* callback table.
|
||
|
* 2/18/97 kipo allow multiple instances of service provider
|
||
|
* 3/04/97 kipo updated debug output; make sure we linke with dplayx.dll
|
||
|
* 4/08/97 kipo added support for separate modem and serial baud rates
|
||
|
* 5/07/97 kipo added support for modem choice list
|
||
|
* 5/23/97 kipo added support return status codes
|
||
|
* 5/15/98 a-peterz When Write fails, return DPERR_NOCONNECTION (#23745)
|
||
|
***************************************************************************/
|
||
|
|
||
|
#define INITGUID
|
||
|
#include <windows.h>
|
||
|
#include <windowsx.h>
|
||
|
|
||
|
#include <objbase.h>
|
||
|
#include <initguid.h>
|
||
|
|
||
|
#include "dpf.h"
|
||
|
#include "dplaysp.h"
|
||
|
#include "comport.h"
|
||
|
|
||
|
// macros
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define DPF_ERRVAL(a, b) DPF( 0, DPF_MODNAME ": " a, b );
|
||
|
#else
|
||
|
#define DPF_ERRVAL(a, b)
|
||
|
#endif
|
||
|
|
||
|
// constants
|
||
|
|
||
|
#define SPMINORVERSION 0x0000 // service provider-specific version number
|
||
|
#define VERSIONNUMBER (DPSP_MAJORVERSION | SPMINORVERSION) // version number for service provider
|
||
|
|
||
|
#define MESSAGETOKEN 0x2BAD // token to signify start of message
|
||
|
#define MESSAGEHEADERLEN sizeof(MESSAGEHEADER) // size of message header
|
||
|
#define MESSAGEMAXSIZEINT 0x0000FFFF // maximum size of an internal message
|
||
|
#define MESSAGEMAXSIZEEXT (MESSAGEMAXSIZEINT - MESSAGEHEADERLEN) // maximum size of an external message
|
||
|
|
||
|
typedef enum {
|
||
|
NEWMESSAGESTATE = 0, // start reading a new message
|
||
|
READHEADERSTATE, // read the message header
|
||
|
READDATASTATE, // read the message data
|
||
|
SKIPDATASTATE // skip the message data
|
||
|
} MESSAGESTATE;
|
||
|
|
||
|
// structures
|
||
|
|
||
|
// message header
|
||
|
typedef struct {
|
||
|
WORD wToken; // message token
|
||
|
WORD wMessageSize; // length of message
|
||
|
WORD wMessageCRC; // CRC checksum value for message body
|
||
|
WORD wHeaderCRC; // CRC checksum value for header
|
||
|
} MESSAGEHEADER, *LPMESSAGEHEADER;
|
||
|
|
||
|
// service provider context
|
||
|
typedef struct {
|
||
|
LPDPCOMPORT lpComPort; // pointer to com port data structure
|
||
|
MESSAGESTATE msReadState; // current read state
|
||
|
BYTE lpReadHeader[MESSAGEHEADERLEN]; // buffer for message header
|
||
|
LPBYTE lpReadBuffer; // buffer for message data
|
||
|
DWORD dwReadBufferSize; // size of message buffer in bytes
|
||
|
DWORD dwReadCount; // no. bytes read into message buffer
|
||
|
DWORD dwReadTotal; // no. total bytes to read into message buffer
|
||
|
DWORD dwSkipCount; // no. bytes skipped to find message header
|
||
|
LPDIRECTPLAYSP lpDPlay; // pointer to IDirectPlaySP needed to call back into DPlay
|
||
|
} SPCONTEXT, *LPSPCONTEXT;
|
||
|
|
||
|
// {0F1D6860-88D9-11cf-9C4E-00A0C905425E}
|
||
|
DEFINE_GUID(DPSERIAL_GUID, // GUID for serial service provider
|
||
|
0xf1d6860, 0x88d9, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e);
|
||
|
|
||
|
// {44EAA760-CB68-11cf-9C4E-00A0C905425E}
|
||
|
DEFINE_GUID(DPMODEM_GUID, // GUID for modem service provider
|
||
|
0x44eaa760, 0xcb68, 0x11cf, 0x9c, 0x4e, 0x0, 0xa0, 0xc9, 0x5, 0x42, 0x5e);
|
||
|
|
||
|
/*
|
||
|
* GetSPContext
|
||
|
*
|
||
|
* Get service provider context from DirectPlay.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "GetSPContext"
|
||
|
|
||
|
LPSPCONTEXT GetSPContext(LPDIRECTPLAYSP lpDPlay)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext = NULL;
|
||
|
DWORD dwContextSize = 0;
|
||
|
HRESULT hr;
|
||
|
|
||
|
// no dplay interface?
|
||
|
if (lpDPlay == NULL)
|
||
|
{
|
||
|
DPF_ERR("DPlaySP interface is NULL!");
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// get pointer to context from DPlay
|
||
|
hr = lpDPlay->lpVtbl->GetSPData(lpDPlay, &lpContext, &dwContextSize, DPGET_LOCAL);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERRVAL("could not get context: 0x%08X", hr);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// make sure size is correct
|
||
|
if (dwContextSize != sizeof(SPCONTEXT))
|
||
|
{
|
||
|
DPF_ERR("invalid context size!");
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
return (lpContext);
|
||
|
|
||
|
FAILURE:
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* SetupMessageHeader
|
||
|
*
|
||
|
* Initialize the service provider-specific header put
|
||
|
* in front of every message.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SetupMessageHeader"
|
||
|
|
||
|
HRESULT SetupMessageHeader(LPVOID pvMessage, DWORD dwMessageSize)
|
||
|
{
|
||
|
LPMESSAGEHEADER pMessageHeader = (LPMESSAGEHEADER) pvMessage;
|
||
|
|
||
|
// make sure message will fit in header
|
||
|
if (dwMessageSize > MESSAGEMAXSIZEINT)
|
||
|
return (DPERR_SENDTOOBIG);
|
||
|
|
||
|
// set message header
|
||
|
pMessageHeader->wToken = (WORD) MESSAGETOKEN;
|
||
|
|
||
|
// set message size
|
||
|
pMessageHeader->wMessageSize = (WORD) dwMessageSize;
|
||
|
|
||
|
// generate CRC for message body
|
||
|
pMessageHeader->wMessageCRC = (WORD) GenerateCRC(((LPBYTE) pvMessage) + MESSAGEHEADERLEN,
|
||
|
dwMessageSize - MESSAGEHEADERLEN);
|
||
|
|
||
|
// generate CRC for message header
|
||
|
pMessageHeader->wHeaderCRC = (WORD) GenerateCRC(pvMessage, MESSAGEHEADERLEN - sizeof(pMessageHeader->wHeaderCRC));
|
||
|
|
||
|
return (DP_OK);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* GetMessageLength
|
||
|
*
|
||
|
* Check for valid message header and return length of message.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "GetMessageLength"
|
||
|
|
||
|
DWORD GetMessageLength(LPBYTE header)
|
||
|
{
|
||
|
LPMESSAGEHEADER pMessageHeader = (LPMESSAGEHEADER) header;
|
||
|
DWORD byteCount;
|
||
|
|
||
|
// check for token we put in front of every message
|
||
|
if (pMessageHeader->wToken != MESSAGETOKEN)
|
||
|
goto FAILURE;
|
||
|
|
||
|
// check CRC for message header
|
||
|
if (pMessageHeader->wHeaderCRC != (WORD) GenerateCRC(header, MESSAGEHEADERLEN - sizeof(pMessageHeader->wHeaderCRC)))
|
||
|
goto FAILURE;
|
||
|
|
||
|
// get length of message
|
||
|
byteCount = pMessageHeader->wMessageSize;
|
||
|
if (byteCount <= MESSAGEHEADERLEN)
|
||
|
{
|
||
|
DPF_ERRVAL("bad message size: %d", byteCount);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
return (byteCount);
|
||
|
|
||
|
FAILURE:
|
||
|
return (0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* SetupToReadMessage
|
||
|
*
|
||
|
* Create/resize buffer to fit length of message and initialize header.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SetupToReadMessage"
|
||
|
|
||
|
BOOL SetupToReadMessage(LPSPCONTEXT lpContext)
|
||
|
{
|
||
|
// no buffer, so create one
|
||
|
if (lpContext->lpReadBuffer == NULL)
|
||
|
{
|
||
|
lpContext->lpReadBuffer = GlobalAllocPtr(GHND, lpContext->dwReadTotal);
|
||
|
if (lpContext->lpReadBuffer == NULL)
|
||
|
{
|
||
|
DPF_ERRVAL("could not create message buffer: %d", GetLastError());
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
lpContext->dwReadBufferSize = lpContext->dwReadTotal;
|
||
|
}
|
||
|
|
||
|
// existing buffer not big enough, so resize
|
||
|
else if (lpContext->dwReadBufferSize < lpContext->dwReadTotal)
|
||
|
{
|
||
|
HANDLE h;
|
||
|
h = GlobalReAllocPtr(lpContext->lpReadBuffer, lpContext->dwReadTotal, 0);
|
||
|
if (h == NULL)
|
||
|
{
|
||
|
DPF_ERRVAL("could not reallocate message buffer: %d", GetLastError());
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
lpContext->lpReadBuffer = h;
|
||
|
lpContext->dwReadBufferSize = lpContext->dwReadTotal;
|
||
|
}
|
||
|
|
||
|
// copy message header to buffer
|
||
|
CopyMemory(lpContext->lpReadBuffer, lpContext->lpReadHeader, lpContext->dwReadCount);
|
||
|
|
||
|
return (TRUE);
|
||
|
|
||
|
FAILURE:
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ReadRoutine
|
||
|
*
|
||
|
* Read bytes from COM port using a state machine to assemble a message.
|
||
|
* When message is assembled, call back to DirectPlay to deliver it.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "ReadRoutine"
|
||
|
|
||
|
void ReadRoutine(LPDIRECTPLAYSP lpDPlay)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
DWORD byteCount;
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(lpDPlay);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
switch (lpContext->msReadState)
|
||
|
{
|
||
|
// start reading a new message
|
||
|
case NEWMESSAGESTATE:
|
||
|
lpContext->dwReadCount = 0;
|
||
|
lpContext->dwReadTotal = MESSAGEHEADERLEN;
|
||
|
lpContext->msReadState = READHEADERSTATE;
|
||
|
lpContext->dwSkipCount = 0;
|
||
|
break;
|
||
|
|
||
|
// read message header
|
||
|
case READHEADERSTATE:
|
||
|
byteCount = lpContext->lpComPort->Read(lpContext->lpComPort,
|
||
|
&lpContext->lpReadHeader[lpContext->dwReadCount],
|
||
|
lpContext->dwReadTotal - lpContext->dwReadCount);
|
||
|
if (byteCount == 0)
|
||
|
return;
|
||
|
|
||
|
lpContext->dwReadCount += byteCount;
|
||
|
if (lpContext->dwReadCount == lpContext->dwReadTotal) // got enough for a header
|
||
|
{
|
||
|
lpContext->dwReadTotal = GetMessageLength(lpContext->lpReadHeader); // see if it's real
|
||
|
if (lpContext->dwReadTotal)
|
||
|
{
|
||
|
if (lpContext->dwSkipCount)
|
||
|
DPF_ERRVAL("%d bytes skipped", lpContext->dwSkipCount);
|
||
|
|
||
|
if (SetupToReadMessage(lpContext)) // prepare to read message
|
||
|
lpContext->msReadState = READDATASTATE;
|
||
|
else
|
||
|
lpContext->msReadState = SKIPDATASTATE;
|
||
|
}
|
||
|
else // bad message header - reset
|
||
|
{
|
||
|
DWORD i;
|
||
|
|
||
|
if (lpContext->dwSkipCount == 0)
|
||
|
DPF_ERR("invalid message header - skipping bytes");
|
||
|
|
||
|
lpContext->dwReadCount = MESSAGEHEADERLEN - 1; // throw away first byte and try again
|
||
|
lpContext->dwReadTotal = MESSAGEHEADERLEN;
|
||
|
lpContext->dwSkipCount += 1;
|
||
|
|
||
|
for (i = 0; i < lpContext->dwReadCount; i++) // shuffle down one byte
|
||
|
lpContext->lpReadHeader[i] = lpContext->lpReadHeader[i + 1];
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// read message data
|
||
|
case READDATASTATE:
|
||
|
byteCount = lpContext->lpComPort->Read(lpContext->lpComPort,
|
||
|
&lpContext->lpReadBuffer[lpContext->dwReadCount],
|
||
|
lpContext->dwReadTotal - lpContext->dwReadCount);
|
||
|
if (byteCount == 0)
|
||
|
return;
|
||
|
|
||
|
lpContext->dwReadCount += byteCount;
|
||
|
if (lpContext->dwReadCount == lpContext->dwReadTotal) // have read entire message
|
||
|
{
|
||
|
LPMESSAGEHEADER pMessageHeader;
|
||
|
|
||
|
// check for CRC errors
|
||
|
pMessageHeader = (LPMESSAGEHEADER) lpContext->lpReadBuffer;
|
||
|
if (pMessageHeader->wMessageCRC != (WORD) GenerateCRC(lpContext->lpReadBuffer + MESSAGEHEADERLEN, lpContext->dwReadTotal - MESSAGEHEADERLEN))
|
||
|
{
|
||
|
DPF_ERR("Message dropped - CRC did not match!");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPF(5, "%d byte message received", lpContext->dwReadTotal);
|
||
|
|
||
|
// deliver message to DirectPlay
|
||
|
lpContext->lpDPlay->lpVtbl->HandleMessage(lpContext->lpDPlay, // DirectPlay instance
|
||
|
lpContext->lpReadBuffer + MESSAGEHEADERLEN, // pointer to message data
|
||
|
lpContext->dwReadTotal - MESSAGEHEADERLEN, // length of message data
|
||
|
NULL); // pointer to header (unused here)
|
||
|
}
|
||
|
lpContext->msReadState = NEWMESSAGESTATE; // go read next message
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// skip message data
|
||
|
case SKIPDATASTATE:
|
||
|
DPF_ERR("Skipping data!");
|
||
|
while (lpContext->lpComPort->Read(lpContext->lpComPort, &lpContext->lpReadHeader[0], 1)) // spin until entire message discarded
|
||
|
{
|
||
|
lpContext->dwReadCount += 1;
|
||
|
if (lpContext->dwReadCount == lpContext->dwReadTotal)
|
||
|
{
|
||
|
lpContext->msReadState = NEWMESSAGESTATE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
DPF_ERRVAL("bad read state: %d", lpContext->msReadState);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* SP_EnumSessions
|
||
|
*
|
||
|
* Broadcast a message to the network.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_EnumSessions"
|
||
|
|
||
|
HRESULT WINAPI SP_EnumSessions(LPDPSP_ENUMSESSIONSDATA ped)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
DWORD byteCount;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering SP_EnumSessions");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(ped->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// make connection
|
||
|
hr = lpContext->lpComPort->Connect(lpContext->lpComPort, FALSE, ped->bReturnStatus);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
if (hr != DPERR_CONNECTING)
|
||
|
DPF_ERRVAL("error making connection: 0x%08X", hr);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// see if connection has been lost
|
||
|
if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
|
||
|
{
|
||
|
DPF_ERR("connection lost!");
|
||
|
hr = DPERR_SESSIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// setup the message
|
||
|
hr = SetupMessageHeader(ped->lpMessage, ped->dwMessageSize);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERR("message too large!");
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// send message
|
||
|
byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, ped->lpMessage, ped->dwMessageSize, TRUE);
|
||
|
if (byteCount != ped->dwMessageSize)
|
||
|
{
|
||
|
DPF(0, "error writing message: %d requested, %d actual", ped->dwMessageSize, byteCount);
|
||
|
hr = DPERR_CONNECTIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
DPF(5, "%d byte enum sessions message sent", byteCount);
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // EnumSessions
|
||
|
|
||
|
/*
|
||
|
* SP_Send
|
||
|
*
|
||
|
* Send a message to a particular player or group.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_Send"
|
||
|
|
||
|
HRESULT WINAPI SP_Send(LPDPSP_SENDDATA psd)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
DWORD byteCount;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering SP_Send");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(psd->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// see if connection has been lost
|
||
|
if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
|
||
|
{
|
||
|
DPF_ERR("connection lost!");
|
||
|
hr = DPERR_SESSIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// setup the message
|
||
|
hr = SetupMessageHeader(psd->lpMessage, psd->dwMessageSize);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERR("message too large!");
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// send message
|
||
|
byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, psd->lpMessage, psd->dwMessageSize, TRUE);
|
||
|
if (byteCount != psd->dwMessageSize)
|
||
|
{
|
||
|
DPF(0, "error writing message: %d requested, %d actual", psd->dwMessageSize, byteCount);
|
||
|
hr = DPERR_CONNECTIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
DPF(5, "%d byte message sent", byteCount);
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // Send
|
||
|
|
||
|
/*
|
||
|
* SP_Reply
|
||
|
*
|
||
|
* Send a reply to a message.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_Reply"
|
||
|
|
||
|
HRESULT WINAPI SP_Reply(LPDPSP_REPLYDATA prd)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
DWORD byteCount;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering Reply");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(prd->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// see if connection has been lost
|
||
|
if (lpContext->lpComPort->GetHandle(lpContext->lpComPort) == NULL)
|
||
|
{
|
||
|
DPF_ERR("connection lost!");
|
||
|
hr = DPERR_SESSIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// setup the message
|
||
|
hr = SetupMessageHeader(prd->lpMessage, prd->dwMessageSize);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERR("message too large!");
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// send message
|
||
|
byteCount = lpContext->lpComPort->Write(lpContext->lpComPort, prd->lpMessage, prd->dwMessageSize, TRUE);
|
||
|
if (byteCount != prd->dwMessageSize)
|
||
|
{
|
||
|
DPF(0, "error writing message: %d requested, %d actual", prd->dwMessageSize, byteCount);
|
||
|
hr = DPERR_CONNECTIONLOST;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
DPF(5, "%d byte reply message sent", byteCount);
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // Reply
|
||
|
|
||
|
/*
|
||
|
* SP_Open
|
||
|
*
|
||
|
* Open the service provider.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_Open"
|
||
|
|
||
|
HRESULT WINAPI SP_Open(LPDPSP_OPENDATA pod)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering Open");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(pod->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// make connection
|
||
|
hr = lpContext->lpComPort->Connect(lpContext->lpComPort, pod->bCreate, pod->bReturnStatus);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERRVAL("error making connection: 0x%08X", hr);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // Open
|
||
|
|
||
|
/*
|
||
|
* SP_GetCaps
|
||
|
*
|
||
|
* Return capabilities of service provider.
|
||
|
*
|
||
|
* Only the fields that matter to this service provider have
|
||
|
* to be set here, since all the fields are preset to
|
||
|
* default values.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_GetCaps"
|
||
|
|
||
|
HRESULT WINAPI SP_GetCaps(LPDPSP_GETCAPSDATA pcd)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
LPDPCAPS lpCaps;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering GetCaps");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(pcd->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// make sure caps buffer is large enough
|
||
|
lpCaps = pcd->lpCaps;
|
||
|
if (lpCaps->dwSize < sizeof(DPCAPS))
|
||
|
{
|
||
|
DPF_ERR("caps buffer too small");
|
||
|
hr = DPERR_BUFFERTOOSMALL;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// don't zero out caps as DPlay has pre-initialized some default caps for us
|
||
|
lpCaps->dwSize = sizeof(DPCAPS);
|
||
|
lpCaps->dwMaxBufferSize = MESSAGEMAXSIZEEXT; // return maximum external message size
|
||
|
lpCaps->dwHeaderLength = MESSAGEHEADERLEN; // return size of message header
|
||
|
lpCaps->dwFlags = 0; // have DPlay do the keep-alives
|
||
|
lpCaps->dwLatency = 250; // todo - base these on baud rate ACK!!!
|
||
|
lpCaps->dwTimeout = 2500;
|
||
|
|
||
|
// if we have connected we can get the baud rate
|
||
|
if (lpContext->lpComPort->GetHandle(lpContext->lpComPort))
|
||
|
{
|
||
|
DWORD dwBaudRate;
|
||
|
|
||
|
// try to get baud rate
|
||
|
hr = lpContext->lpComPort->GetBaudRate(lpContext->lpComPort, &dwBaudRate);
|
||
|
if SUCCEEDED(hr)
|
||
|
{
|
||
|
lpCaps->dwHundredBaud = dwBaudRate / 100; // return baud rate in hundreds of baud
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // GetCaps
|
||
|
|
||
|
/*
|
||
|
* SP_GetAddress
|
||
|
*
|
||
|
* Return network address of a given player.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_GetAddress"
|
||
|
|
||
|
HRESULT WINAPI SP_GetAddress(LPDPSP_GETADDRESSDATA pga)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering GetAddress");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(pga->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
hr = lpContext->lpComPort->GetAddress(lpContext->lpComPort, pga->dwFlags, pga->lpAddress, pga->lpdwAddressSize);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // GetAddress
|
||
|
|
||
|
/*
|
||
|
* SP_GetAddressChoices
|
||
|
*
|
||
|
* Return address choices for this service provider
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_GetAddressChoices"
|
||
|
|
||
|
HRESULT WINAPI SP_GetAddressChoices(LPDPSP_GETADDRESSCHOICESDATA pga)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering GetAddressChoices");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(pga->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
hr = lpContext->lpComPort->GetAddressChoices(lpContext->lpComPort, pga->lpAddress, pga->lpdwAddressSize);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // GetAddressChoices
|
||
|
|
||
|
/*
|
||
|
* SP_Shutdown
|
||
|
*
|
||
|
* Turn off all I/O on service provider and release all allocated
|
||
|
* memory and resources.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SP_Shutdown"
|
||
|
|
||
|
HRESULT WINAPI SP_ShutdownEx(LPDPSP_SHUTDOWNDATA psd)
|
||
|
{
|
||
|
LPSPCONTEXT lpContext;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering Shutdown");
|
||
|
|
||
|
// get service provider context
|
||
|
lpContext = GetSPContext(psd->lpISP);
|
||
|
if (lpContext == NULL)
|
||
|
{
|
||
|
DPF_ERR("invalid context!");
|
||
|
hr = DPERR_NOINTERFACE;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
if (lpContext->lpComPort)
|
||
|
{
|
||
|
lpContext->lpComPort->Dispose(lpContext->lpComPort);
|
||
|
lpContext->lpComPort = NULL;
|
||
|
}
|
||
|
|
||
|
if (lpContext->lpReadBuffer)
|
||
|
{
|
||
|
GlobalFreePtr(lpContext->lpReadBuffer);
|
||
|
lpContext->lpReadBuffer = NULL;
|
||
|
}
|
||
|
|
||
|
lpContext->lpDPlay = NULL;
|
||
|
|
||
|
// OK to release DPLAYX.DLL
|
||
|
gdwDPlaySPRefCount++;
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // Shutdown
|
||
|
|
||
|
/*
|
||
|
* SPInit
|
||
|
*
|
||
|
* This is the main entry point for the service provider. This should be
|
||
|
* the only entry point exported from the DLL.
|
||
|
*
|
||
|
* Allocate any needed resources and return the supported callbacks.
|
||
|
*/
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "SPInit"
|
||
|
|
||
|
HRESULT WINAPI SPInit(LPSPINITDATA pid)
|
||
|
{
|
||
|
SPCONTEXT context;
|
||
|
LPSPCONTEXT lpContext;
|
||
|
LPDPSP_SPCALLBACKS lpcbTable;
|
||
|
HRESULT hr;
|
||
|
|
||
|
DPF(5,"entering SPInit");
|
||
|
|
||
|
// check to make sure table is big enough
|
||
|
lpcbTable = pid->lpCB;
|
||
|
if (lpcbTable->dwSize < sizeof(DPSP_SPCALLBACKS)) // table not big enough
|
||
|
{
|
||
|
DPF_ERR("callback table too small");
|
||
|
hr = DPERR_BUFFERTOOSMALL;
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// initialize context
|
||
|
ZeroMemory(&context, sizeof(SPCONTEXT));
|
||
|
lpContext = &context;
|
||
|
lpContext->msReadState = NEWMESSAGESTATE;
|
||
|
lpContext->lpDPlay = pid->lpISP; // save pointer to IDPlaySP so we can pass it back later
|
||
|
|
||
|
// check for correct GUID
|
||
|
if (IsEqualGUID(pid->lpGuid, &DPSERIAL_GUID))
|
||
|
{
|
||
|
hr = NewSerial(pid->lpAddress, pid->dwAddressSize,
|
||
|
lpContext->lpDPlay, ReadRoutine,
|
||
|
&lpContext->lpComPort);
|
||
|
}
|
||
|
else if (IsEqualGUID(pid->lpGuid, &DPMODEM_GUID))
|
||
|
{
|
||
|
hr = NewModem(pid->lpAddress, pid->dwAddressSize,
|
||
|
lpContext->lpDPlay, ReadRoutine,
|
||
|
&lpContext->lpComPort);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPF_ERR("unknown service provider GUID");
|
||
|
hr = DPERR_INVALIDPARAM;
|
||
|
}
|
||
|
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERRVAL("error opening com port: 0x%08X", hr);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// return size of header we need on every message so
|
||
|
// DirectPlay will leave room for it.
|
||
|
pid->dwSPHeaderSize = MESSAGEHEADERLEN;
|
||
|
|
||
|
// return version number so DirectPlay will treat us with respect
|
||
|
pid->dwSPVersion = VERSIONNUMBER;
|
||
|
|
||
|
// set up callbacks
|
||
|
lpcbTable->dwSize = sizeof(DPSP_SPCALLBACKS); // MUST set the return size of the table
|
||
|
lpcbTable->Send = SP_Send;
|
||
|
lpcbTable->EnumSessions = SP_EnumSessions;
|
||
|
lpcbTable->Reply = SP_Reply;
|
||
|
lpcbTable->GetCaps = SP_GetCaps;
|
||
|
lpcbTable->GetAddress = SP_GetAddress;
|
||
|
lpcbTable->GetAddressChoices = SP_GetAddressChoices;
|
||
|
lpcbTable->Open = SP_Open;
|
||
|
lpcbTable->ShutdownEx = SP_ShutdownEx;
|
||
|
|
||
|
// save context with DPlay so we can get it later
|
||
|
hr = lpContext->lpDPlay->lpVtbl->SetSPData(lpContext->lpDPlay, lpContext, sizeof(SPCONTEXT), DPSET_LOCAL);
|
||
|
if FAILED(hr)
|
||
|
{
|
||
|
DPF_ERRVAL("could not store context: 0x%08X", hr);
|
||
|
goto FAILURE;
|
||
|
}
|
||
|
|
||
|
// make sure DPLAYX.DLL sticks around
|
||
|
gdwDPlaySPRefCount++;
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
FAILURE:
|
||
|
return (hr);
|
||
|
|
||
|
} // SPInit
|