You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
18 KiB
411 lines
18 KiB
/****************************************************************************/
|
|
// wtdapi.c
|
|
//
|
|
// Transport driver - Windows specific API
|
|
//
|
|
// Copyright(C) 1997-1999 Microsoft Corporation
|
|
/****************************************************************************/
|
|
|
|
#include <adcg.h>
|
|
extern "C" {
|
|
#define TRC_FILE "wtdapi"
|
|
#define TRC_GROUP TRC_GROUP_NETWORK
|
|
#include <atrcapi.h>
|
|
#include <adcgfsm.h>
|
|
}
|
|
|
|
#include "autil.h"
|
|
#include "td.h"
|
|
#include "nl.h"
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TD_Recv */
|
|
/* */
|
|
/* Purpose: Called to receive X bytes from WinSock and store them in the */
|
|
/* buffer pointed to by pData. */
|
|
/* */
|
|
/* Returns: The number of bytes received. */
|
|
/* */
|
|
/* Params: IN pData - pointer to buffer to receive the data. */
|
|
/* IN size - number of bytes to receive. */
|
|
/****************************************************************************/
|
|
DCUINT DCAPI CTD::TD_Recv(PDCUINT8 pData,
|
|
DCUINT size)
|
|
{
|
|
unsigned bytesToRecv;
|
|
unsigned bytesCopied;
|
|
unsigned BytesRecv;
|
|
unsigned BytesToCopy;
|
|
|
|
DC_BEGIN_FN("TD_Recv");
|
|
|
|
/************************************************************************/
|
|
/* Check that we're being asked to receive some data, that the pointer */
|
|
/* is not NULL, that the memory range to receive the data into is */
|
|
/* writable by us and that there is data available. */
|
|
/************************************************************************/
|
|
TRC_ASSERT((pData != NULL), (TB, _T("Data pointer is NULL")));
|
|
TRC_ASSERT((size != 0), (TB, _T("No data to receive")));
|
|
|
|
TRC_ASSERT((0 == IsBadWritePtr(pData, size)),
|
|
(TB, _T("Don't have write access to memory %p (size %u)"),
|
|
pData,
|
|
size));
|
|
TRC_ASSERT((_TD.dataInTD), (TB, _T("TD_Recv called when dataInTD is FALSE")));
|
|
TRC_DBG((TB, _T("Request for %u bytes"), size));
|
|
|
|
/************************************************************************/
|
|
/* TD has a recv buffer into which it attempts to receive sufficient */
|
|
/* data to fill the buffer. */
|
|
/* Initially this buffer is empty. On a call to TD_Recv, the following */
|
|
/* sequence occurs. */
|
|
/* Data is copied from the recv buffer to the caller's buffer. If this */
|
|
/* satisfies the caller's request, no further action is needed. */
|
|
/* If this does not satisfy the caller, ie the recv buffer was empty or */
|
|
/* had less data then requested (so is now empty), another call is made */
|
|
/* to WinSock. */
|
|
/* The buffer used for this recv is the recv buffer is the caller */
|
|
/* requires fewer bytes than the recv buffer holds, the caller's buffer */
|
|
/* otherwise. */
|
|
/* Whenever the recv buffer is used, WinSock is asked for as many bytes */
|
|
/* as the buffer holds (rather than the number of bytes the caller */
|
|
/* wants). This means there may be some data left in the recv buffer */
|
|
/* ready for the next call to TD_Recv. */
|
|
/************************************************************************/
|
|
|
|
bytesToRecv = size;
|
|
|
|
/************************************************************************/
|
|
/* Copy as much data as possible from the recv buffer. */
|
|
/************************************************************************/
|
|
// If the recv buffer contains data then copy up to bytesToCopy to the
|
|
// caller's buffer, otherwise just quit.
|
|
if (_TD.recvBuffer.dataLength == 0) {
|
|
// The recv buffer is empty, so zero bytes copied.
|
|
TRC_DBG((TB, _T("recv buffer is empty, need to go to WinSock")));
|
|
bytesCopied = 0;
|
|
}
|
|
else {
|
|
// Copy the lesser of the number of bytes requested and the number of
|
|
// bytes in the buffer.
|
|
bytesCopied = DC_MIN(bytesToRecv, _TD.recvBuffer.dataLength);
|
|
TRC_ASSERT(((bytesCopied + _TD.recvBuffer.dataStart) <=
|
|
_TD.recvBuffer.size),
|
|
(TB, _T("Want %u bytes from buffer, but start %u, size %u"),
|
|
bytesCopied,
|
|
_TD.recvBuffer.dataStart,
|
|
_TD.recvBuffer.size));
|
|
|
|
memcpy(pData, &_TD.recvBuffer.pData[_TD.recvBuffer.dataStart],
|
|
bytesCopied);
|
|
TRC_DBG((TB, _T("Copied %u bytes from recv buffer"), bytesCopied));
|
|
|
|
// Update the recv buffer to take account of the data copied.
|
|
_TD.recvBuffer.dataLength -= bytesCopied;
|
|
if (0 == _TD.recvBuffer.dataLength)
|
|
// Used all the data from the recv buffer so reset the start pos.
|
|
_TD.recvBuffer.dataStart = 0;
|
|
else
|
|
// Still some data left in recv buffer.
|
|
_TD.recvBuffer.dataStart += bytesCopied;
|
|
|
|
TRC_DBG((TB, _T("recv buffer now has %u bytes starting at %u"),
|
|
_TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart));
|
|
|
|
// Update the number of bytes still to receive.
|
|
bytesToRecv -= bytesCopied;
|
|
if (0 == bytesToRecv) {
|
|
TRC_DBG((TB, _T("Received all necessary data")));
|
|
DC_QUIT;
|
|
}
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Now try to get any data which may still be required by recv'ing from */
|
|
/* WinSock. Offset the address of the caller's buffer by the amount of */
|
|
/* data copied from the recv buffer. */
|
|
/************************************************************************/
|
|
TRC_ASSERT(((_TD.recvBuffer.dataStart == 0) &&
|
|
(_TD.recvBuffer.dataLength == 0)),
|
|
(TB, _T("About to recv into buffer, but existing recv ")
|
|
_T("length %u, start %u"), _TD.recvBuffer.dataStart,
|
|
_TD.recvBuffer.dataLength));
|
|
|
|
// Select the buffer into which we recv the data. This is the recv
|
|
// buffer if all the data required fits into it, the caller's buffer
|
|
// otherwise.
|
|
if (bytesToRecv < _TD.recvBuffer.size) {
|
|
// Caller requires less than the recv buffer size, so attempt to
|
|
// have Winsock fill the recv buffer and copy to the caller's
|
|
// buffer.
|
|
BytesRecv = TDDoWinsockRecv(_TD.recvBuffer.pData, _TD.recvBuffer.size);
|
|
if (BytesRecv != 0) {
|
|
// Successful WinSock recv. Copy data from the recv buffer to the
|
|
// caller's buffer (offset by bytesCopied, the end of the recv
|
|
// buffer copy).
|
|
BytesToCopy = DC_MIN(bytesToRecv, BytesRecv);
|
|
memcpy(pData + bytesCopied, _TD.recvBuffer.pData, BytesToCopy);
|
|
bytesCopied = BytesToCopy;
|
|
|
|
// If we copied less than we recv'ed then there is some data left
|
|
// in the recv buffer for next time.
|
|
if (BytesRecv > bytesCopied) {
|
|
_TD.recvBuffer.dataLength = BytesRecv - bytesCopied;
|
|
_TD.recvBuffer.dataStart = bytesCopied;
|
|
TRC_DBG((TB, _T("recv buffer now has %u bytes starting %u"),
|
|
_TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart));
|
|
}
|
|
|
|
TRC_DBG((TB, _T("%u bytes read to recv buffer, %u copied to caller ")
|
|
_T("still need %u"), BytesRecv, bytesCopied,
|
|
bytesToRecv - bytesCopied));
|
|
}
|
|
else {
|
|
TRC_DBG((TB, _T("Didn't receive any data")));
|
|
bytesCopied = 0;
|
|
}
|
|
|
|
TRC_DBG((TB, _T("%u bytes in recv buffer starting %u"),
|
|
_TD.recvBuffer.dataLength, _TD.recvBuffer.dataStart));
|
|
}
|
|
else {
|
|
// Caller requires more than the recv buffer size, so attempt to
|
|
// have Winsock fill the caller's buffer.
|
|
bytesCopied = TDDoWinsockRecv(pData + bytesCopied, bytesToRecv);
|
|
TRC_DBG((TB, _T("Read %u bytes to caller's buffer, still need %u"),
|
|
bytesCopied, bytesToRecv - bytesCopied));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Update the number of bytes to receive. */
|
|
/************************************************************************/
|
|
bytesToRecv -= bytesCopied;
|
|
|
|
DC_EXIT_POINT:
|
|
/************************************************************************/
|
|
/* If we have received more than the maximum we allow without resetting */
|
|
/* the data available flag OR we didn't get all the bytes we requested */
|
|
/* then we can't allow TD to continue reporting data available. */
|
|
/************************************************************************/
|
|
if (bytesToRecv != 0 || _TD.recvByteCount >= TD_MAX_UNINTERRUPTED_RECV) {
|
|
// We didn't get all the bytes that we wanted, or we have received
|
|
// more than TD_MAX_UNINTERRUPTED_RECV so need to get back to the
|
|
// message loop. So, update our global data available flag and
|
|
// reset the per-FD_READ byte count.
|
|
TRC_DBG((TB, _T("Only got %u bytes of %u requested, total %u"),
|
|
size - bytesToRecv, size, _TD.recvByteCount));
|
|
|
|
_TD.dataInTD = FALSE;
|
|
_TD.recvByteCount = 0;
|
|
}
|
|
|
|
DC_END_FN();
|
|
return (size - bytesToRecv);
|
|
} /* TD_Recv */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TDDoWinsockRecv */
|
|
/* */
|
|
/* Purpose: Wrapper round the WinSock recv function which handles any */
|
|
/* errors returned. */
|
|
/* */
|
|
/* Returns: The number of bytes copied. */
|
|
/* */
|
|
/* Params: IN pData - pointer to buffer to receive the data. */
|
|
/* IN bytesToRecv - number of bytes to receive. */
|
|
/****************************************************************************/
|
|
unsigned DCINTERNAL CTD::TDDoWinsockRecv(BYTE FAR *pData, unsigned bytesToRecv)
|
|
{
|
|
unsigned bytesReceived;
|
|
int WSAErr;
|
|
|
|
DC_BEGIN_FN("TDDoWinsockRecv");
|
|
|
|
// Check that we are requesting some bytes. This will work if we ask
|
|
// for zero bytes, but it implies there is a flaw in the logic.
|
|
TRC_ASSERT((bytesToRecv != 0), (TB, _T("Requesting recv of 0 bytes")));
|
|
|
|
// In the debug build we can constrain the amount of data received,
|
|
// to simulate low-bandwidth scenarios.
|
|
#ifdef DC_NLTEST
|
|
#pragma message("NL Test code compiled in")
|
|
bytesToRecv = 1;
|
|
|
|
#elif DC_DEBUG
|
|
// Calculate how many bytes we can receive and then decrement the count
|
|
// of bytes left to send in this period.
|
|
if (0 != _TD.hThroughputTimer) {
|
|
bytesToRecv = (unsigned)DC_MIN(bytesToRecv, _TD.periodRecvBytesLeft);
|
|
_TD.periodRecvBytesLeft -= bytesToRecv;
|
|
|
|
if (0 == bytesToRecv) {
|
|
// We won't recv any data, but still need to make the call to
|
|
// ensure the FD_READ messages keep flowing.
|
|
TRC_ALT((TB, _T("constrained READ bytes")));
|
|
}
|
|
|
|
TRC_DBG((TB, _T("periodRecvBytesLeft:%u"), _TD.periodRecvBytesLeft));
|
|
}
|
|
#endif
|
|
|
|
#ifdef OS_WINCE
|
|
// Check to see we already called WinSock recv() for this FD_READ.
|
|
if (_TD.enableWSRecv) {
|
|
// set enableWSRecv to FALSE to indicate that we performed a recv()
|
|
// call for this FD_READ.
|
|
_TD.enableWSRecv = FALSE;
|
|
#endif // OS_WINCE
|
|
|
|
//
|
|
// Try to get bytesToRecv bytes from WinSock.
|
|
//
|
|
bytesReceived = (unsigned)recv(_TD.hSocket, (char *)pData,
|
|
(int)bytesToRecv, 0);
|
|
|
|
// Do any necessary error handling. We are OK if no error or if the
|
|
// error is WOULDBLOCK (or INPROGRESS on CE).
|
|
if (bytesReceived != SOCKET_ERROR) {
|
|
// Successful WinSock recv.
|
|
TRC_DBG((TB, _T("Requested %d bytes, got %d"),
|
|
bytesToRecv, bytesReceived));
|
|
|
|
// Update the performance counter.
|
|
PRF_ADD_COUNTER(PERF_BYTES_RECV, bytesReceived);
|
|
|
|
// Add this lot of data to the total amount received since last
|
|
// resetting the counter, ie since we last returned to the
|
|
// message loop.
|
|
_TD.recvByteCount += bytesReceived;
|
|
}
|
|
else {
|
|
WSAErr = WSAGetLastError();
|
|
|
|
#ifndef OS_WINCE
|
|
if (WSAErr == WSAEWOULDBLOCK) {
|
|
#else
|
|
if (WSAErr == WSAEWOULDBLOCK || WSAErr == WSAEINPROGRESS) {
|
|
#endif
|
|
|
|
// On a blocking call, we simply set received length to zero and
|
|
// continue.
|
|
bytesReceived = 0;
|
|
}
|
|
else {
|
|
// Zero bytes received on error.
|
|
bytesReceived = 0;
|
|
|
|
// Call the FSM to begin disconnect processing.
|
|
TRC_ERR((TB, _T("Error on call to recv, rc:%d"), WSAErr));
|
|
TDConnectFSMProc(TD_EVT_ERROR,
|
|
NL_MAKE_DISCONNECT_ERR(NL_ERR_TDONCALLTORECV));
|
|
|
|
TRC_ALT((TB, _T("WinSock recv error")));
|
|
}
|
|
}
|
|
|
|
#ifdef OS_WINCE
|
|
}
|
|
else {
|
|
// recv is called once for this FD_READ.
|
|
TRC_DBG((TB, _T("recv() already called.")));
|
|
bytesReceived = 0;
|
|
}
|
|
#endif // OS_WINCE
|
|
|
|
DC_END_FN();
|
|
return bytesReceived;
|
|
}
|
|
|
|
|
|
#ifdef DC_DEBUG
|
|
/****************************************************************************/
|
|
/* Name: TD_GetNetworkThroughput */
|
|
/* */
|
|
/* Purpose: Get the current network throughput setting. */
|
|
/* */
|
|
/* Returns: Current network throughput. */
|
|
/****************************************************************************/
|
|
DCUINT32 DCAPI CTD::TD_GetNetworkThroughput(DCVOID)
|
|
{
|
|
DCUINT32 retVal;
|
|
|
|
DC_BEGIN_FN("TD_GetNetworkThroughput");
|
|
|
|
/************************************************************************/
|
|
/* Calculate the actual throughput. This is the */
|
|
/************************************************************************/
|
|
retVal = _TD.currentThroughput * (1000 / TD_THROUGHPUTINTERVAL);
|
|
|
|
TRC_NRM((TB, _T("Returning network throughput of:%lu"), retVal));
|
|
|
|
DC_END_FN();
|
|
return(retVal);
|
|
} /* TD_GetNetworkThroughput */
|
|
|
|
|
|
/****************************************************************************/
|
|
/* Name: TD_SetNetworkThroughput */
|
|
/* */
|
|
/* Purpose: Sets the network throughput. This is the number of bytes */
|
|
/* that can be passed into or out of the network layer per */
|
|
/* second. For example setting this to 3000 is roughly */
|
|
/* equivalent to a 24000bps modem connection. */
|
|
/* */
|
|
/* Params: throughput - the number of bytes to be allowed into and out */
|
|
/* of the network layer per second. */
|
|
/****************************************************************************/
|
|
DCVOID DCAPI CTD::TD_SetNetworkThroughput(DCUINT32 throughput)
|
|
{
|
|
DC_BEGIN_FN("TD_SetNetworkThroughput");
|
|
|
|
/************************************************************************/
|
|
/* Check to determine if the throughput throttling has been enabled */
|
|
/* or disabled. */
|
|
/************************************************************************/
|
|
if (0 == throughput)
|
|
{
|
|
/********************************************************************/
|
|
/* Throughput throttling has been disabled so kill the throughput */
|
|
/* timer. */
|
|
/********************************************************************/
|
|
TRC_ALT((TB, _T("Throughput throttling disabled")));
|
|
|
|
if (_TD.hThroughputTimer != 0)
|
|
{
|
|
TRC_NRM((TB, _T("Kill throttling timer")));
|
|
KillTimer(_TD.hWnd, TD_THROUGHPUTTIMERID);
|
|
_TD.hThroughputTimer = 0;
|
|
}
|
|
|
|
_TD.currentThroughput = 0;
|
|
}
|
|
else
|
|
{
|
|
/********************************************************************/
|
|
/* Throughput throttling has been enabled so update the throughput */
|
|
/* byte counts and set the timer. */
|
|
/********************************************************************/
|
|
_TD.currentThroughput = (throughput * TD_THROUGHPUTINTERVAL) / 1000;
|
|
_TD.periodSendBytesLeft = _TD.currentThroughput;
|
|
_TD.periodRecvBytesLeft = _TD.currentThroughput;
|
|
|
|
_TD.hThroughputTimer = SetTimer(_TD.hWnd,
|
|
TD_THROUGHPUTTIMERID,
|
|
TD_THROUGHPUTINTERVAL,
|
|
NULL);
|
|
|
|
TRC_ALT((TB, _T("Throughput throttling enabled interval:%u"),
|
|
throughput));
|
|
}
|
|
|
|
DC_END_FN();
|
|
} /* TD_SetNetworkThroughput */
|
|
|
|
#endif /* DC_DEBUG */
|
|
|
|
|
|
|
|
|
|
|