summaryrefslogtreecommitdiffstats
path: root/private/nw/vwipxspx/dll/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/vwipxspx/dll/util.c')
-rw-r--r--private/nw/vwipxspx/dll/util.c2966
1 files changed, 2966 insertions, 0 deletions
diff --git a/private/nw/vwipxspx/dll/util.c b/private/nw/vwipxspx/dll/util.c
new file mode 100644
index 000000000..5e4aac78f
--- /dev/null
+++ b/private/nw/vwipxspx/dll/util.c
@@ -0,0 +1,2966 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ util.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains various utility routines
+
+ Contents:
+ GetInternetAddress
+ GetMaxPacketSize
+ RetrieveEcb
+ RetrieveXEcb
+ (AllocateXecb)
+ (DeallocateXecb)
+ ScheduleEvent
+ ScanTimerList
+ CancelTimerEvent
+ CancelTimedEvents
+ CancelAsyncEvent
+ CancelSocketEvent
+ CancelConnectionEvent
+ QueueEcb
+ DequeueEcb
+ CancelSocketQueue
+ CancelConnectionQueue
+ AbortQueue
+ AbortConnectionEvent
+ StartIpxSend
+ GetIoBuffer
+ (ReleaseIoBuffer)
+ GatherData
+ ScatterData
+ IpxReceiveFirst
+ IpxReceiveNext
+ (IpxSendFirst)
+ IpxSendNext
+ (QueueReceiveRequest)
+ (DequeueReceiveRequest)
+ (QueueSendRequest)
+ (DequeueSendRequest)
+ CompleteOrQueueIo
+ CompleteIo
+ CompleteOrQueueEcb
+ CompleteEcb
+ (QueueAsyncCompletion)
+ EsrCallback
+ VWinEsrCallback
+ FifoAddHead
+ FifoAdd
+ FifoRemove
+ FifoNext
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// private routine prototypes
+//
+
+PRIVATE
+LPXECB
+AllocateXecb(
+ VOID
+ );
+
+PRIVATE
+VOID
+DeallocateXecb(
+ IN LPXECB pXecb
+ );
+
+PRIVATE
+VOID
+ReleaseIoBuffer(
+ IN LPXECB pXecb
+ );
+
+PRIVATE
+VOID
+IpxSendFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+LPXECB
+DequeueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+LPXECB
+DequeueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueAsyncCompletion(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+//
+// private data
+//
+
+//
+// TimerList - singly-linked list of timed events, in order of duration
+//
+
+PRIVATE LPXECB TimerList = NULL;
+
+//
+// AsyncCompletionQueue - keeps list of completed ECBs awaiting removal via
+// ESR callback
+//
+
+PRIVATE FIFO AsyncCompletionQueue = {NULL, NULL};
+
+//
+// sort-of-private data (matches not-really-global data in other modules)
+//
+
+//
+// SerializationCritSec - grab this when manipulating SOCKET_INFO list
+//
+
+CRITICAL_SECTION SerializationCritSec;
+
+//
+// AsyncCritSec - grab this when manipulating AsyncCompletionQueue
+//
+
+CRITICAL_SECTION AsyncCritSec;
+
+//
+// functions
+//
+
+
+int
+GetInternetAddress(
+ IN OUT LPSOCKADDR_IPX InternetAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Gets the node and net numbers for this station
+
+Arguments:
+
+ InternetAddress - pointer to SOCKADDR_IPX structure to fill with internetwork
+ address for this station
+
+Return Value:
+
+ int
+ Success - 0
+ Failure - SOCKET_ERROR
+
+--*/
+
+{
+ SOCKET s;
+ int rc;
+ int structureLength = sizeof(*InternetAddress);
+
+ s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (s != INVALID_SOCKET) {
+
+ //
+ // make dynamic binding (socket number = 0)
+ //
+
+ ZeroMemory(InternetAddress, structureLength);
+ InternetAddress->sa_family = AF_IPX;
+ rc = bind(s, (LPSOCKADDR)InternetAddress, structureLength);
+ if (rc != SOCKET_ERROR) {
+ rc = getsockname(s, (LPSOCKADDR)InternetAddress, &structureLength);
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: getsockname() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: bind() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ closesocket(s);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: socket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ rc = SOCKET_ERROR;
+ }
+ return rc;
+}
+
+
+int
+GetMaxPacketSize(
+ OUT LPWORD MaxPacketSize
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the maximum packet allowed by the underlying transport
+
+Arguments:
+
+ MaxPacketSize - pointer to returned maximum packet size
+
+Return Value:
+
+ int
+ Success - 0
+ Failure - SOCKET_ERROR
+
+--*/
+
+{
+ SOCKET s;
+ int maxLen, maxLenSize = sizeof(maxLen);
+ int rc;
+ SOCKADDR_IPX ipxAddr;
+
+ s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (s != SOCKET_ERROR) {
+
+ //
+ // set socket to 0 - causes any applicable address to be bound
+ //
+
+ ZeroMemory(&ipxAddr, sizeof(ipxAddr));
+ ipxAddr.sa_family = AF_IPX;
+ rc = bind(s, (LPSOCKADDR)&ipxAddr, sizeof(ipxAddr));
+ if (rc != SOCKET_ERROR) {
+
+ rc = getsockopt(s,
+ NSPROTO_IPX,
+ IPX_MAXSIZE,
+ (char FAR*)&maxLen,
+ &maxLenSize
+ );
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // IPX_MAXSIZE always returns the amount of data that can be
+ // transmitted in a single frame. 16-bit IPX/SPX requires that
+ // the IPX header length be included in the data size
+ //
+
+ maxLen += IPX_HEADER_LENGTH;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: getsockopt() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: bind() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ closesocket(s);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: socket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ rc = SOCKET_ERROR;
+ }
+
+ //
+ // BUGBUG: since IPX and SPX can only deal in packets of max size 576 bytes,
+ // should we just return 576 if the indicated size is larger???
+ //
+
+ *MaxPacketSize = (rc != SOCKET_ERROR) ? maxLen : MAXIMUM_IPX_PACKET_LENGTH;
+
+ return rc;
+}
+
+
+LPXECB
+RetrieveEcb(
+ IN BYTE EcbType
+ )
+
+/*++
+
+Routine Description:
+
+ Returns pointer to 32-bit extended ECB structure which contains flat pointer
+ to IPX or AES ECB in VDM memory
+
+ We allocate the extended ECB for 3 reasons:
+
+ 1. Avoids 16-bit app scribbling over our control fields
+ 2. Don't have to make unaligned references to all fields (still need some)
+ 3. Don't have enough space in AES ECB to remember all the stuff we need
+
+ However, we do update the 16-bit ECB's LinkAddress field. We use this as a
+ pointer to the 32-bit XECB we allocate in this routine. This just saves us
+ having to traverse all the lists looking for the address of the 16-bit ECB
+ (which we could still do as a fall-back)
+
+Arguments:
+
+ EcbType - type of ECB - AES, IPX or SPX
+
+Return Value:
+
+ LPXECB - 32-bit pointer to extended ECB structure
+
+--*/
+
+{
+ WORD segment;
+ WORD offset;
+ LPECB pEcb;
+
+ segment = IPX_GET_ECB_SEGMENT();
+ offset = IPX_GET_ECB_OFFSET();
+ pEcb = (LPIPX_ECB)POINTER_FROM_WORDS(segment, offset, sizeof(IPX_ECB));
+
+ return RetrieveXEcb(EcbType, pEcb, (ECB_ADDRESS)MAKELONG(offset,segment));
+}
+
+
+LPXECB
+RetrieveXEcb(
+ IN BYTE EcbType,
+ LPECB pEcb,
+ ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ worker for RetrieveEcb, callable from windows functions (ex DOS parms)
+
+Arguments:
+
+ EcbType - type of ECB - AES, IPX or SPX
+ pEcb - pointer to the 16-bit ECB
+ EcbAddress - address (seg:off in DWORD) of 16-bit ECB
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ if (pEcb) {
+
+ //
+ // allocate and fill-in 32-bit extended ECB structure. If can't allocate
+ // then return NULL
+ //
+
+ pXecb = AllocateXecb();
+ if (pXecb) {
+ pXecb->Ecb = pEcb;
+ pXecb->EcbAddress = EcbAddress;
+ pXecb->EsrAddress = pEcb->EsrAddress;
+
+ //
+ // set flags - IPX/AES, SPX, protect-mode
+ //
+
+ pXecb->Flags |= (((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX))
+ ? XECB_FLAG_IPX
+ : XECB_FLAG_AES)
+ | ((EcbType == ECB_TYPE_SPX) ? XECB_FLAG_SPX : 0)
+ | ((getMSW() & MSW_PE) ? XECB_FLAG_PROTMODE : 0);
+
+ //
+ // this XECB is not yet on a queue
+ //
+
+ pXecb->QueueId = NO_QUEUE;
+
+ //
+ // mark the 16-bit ECB as being used. We use an undefined value to
+ // make sure it gets set/reset in the right places
+ //
+
+ pEcb->InUse = ECB_IU_TEMPORARY;
+
+ //
+ // use the LinkAddress field in the 16-bit ECB to point to the XECB.
+ // We use this when cancelling the ECB
+ //
+
+ pEcb->LinkAddress = pXecb;
+
+ //
+ // AES and IPX ECBs have different sizes and different layouts
+ //
+
+ if ((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX)) {
+ pXecb->SocketNumber = pEcb->SocketNumber;
+ }
+ }
+ } else {
+ pXecb = NULL;
+ }
+ return pXecb;
+}
+
+
+PRIVATE
+LPXECB
+AllocateXecb(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate an XECB; zero it; set the reference count to 1
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ pXecb = (LPXECB)LocalAlloc(LPTR, sizeof(*pXecb));
+ if (pXecb) {
+ pXecb->RefCount = 1;
+ }
+ return pXecb;
+}
+
+
+PRIVATE
+VOID
+DeallocateXecb(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ decrement the XECB reference count (while holding SerializationCritSec). If
+ goes to 0 then free the structure (else other thread is also holding pointer
+ to XECB)
+
+Arguments:
+
+ pXecb - XECB to deallocate
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RequestMutex();
+ --pXecb->RefCount;
+ if (!pXecb->RefCount) {
+
+#if DBG
+ FillMemory(pXecb, sizeof(*pXecb), 0xFF);
+#endif
+
+ FREE_OBJECT(pXecb);
+ }
+ ReleaseMutex();
+}
+
+
+VOID
+ScheduleEvent(
+ IN LPXECB pXecb,
+ IN WORD Ticks
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an ECB to the TimerList, ordered by Ticks. The value of Ticks cannot
+ be zero
+
+ Assumes 1. Ticks != 0
+ 2. pXecb->Next is already NULL (as result of LocalAlloc(LPTR,...)
+
+Arguments:
+
+ pXecb - pointer to XECB describing IPX or AES ECB to queue
+ Ticks - number of ticks to elapse before ECB is cooked
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ASSERT(Ticks);
+ ASSERT(pXecb->Next == NULL);
+
+ RequestMutex();
+ if (!TimerList) {
+ TimerList = pXecb;
+ } else {
+ if (TimerList->Ticks > Ticks) {
+ TimerList->Ticks -= Ticks;
+ pXecb->Next = TimerList;
+ TimerList = pXecb;
+ } else {
+
+ LPXECB previous = (LPXECB)TimerList;
+ LPXECB this = previous->Next;
+
+ Ticks -= TimerList->Ticks;
+ while (this && Ticks > this->Ticks) {
+ Ticks -= this->Ticks;
+ previous = this;
+ this = this->Next;
+ }
+ previous->Next = pXecb;
+ pXecb->Next = this;
+ }
+ }
+ pXecb->Ticks = Ticks;
+ pXecb->QueueId = TIMER_QUEUE;
+ ReleaseMutex();
+}
+
+
+VOID
+ScanTimerList(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Called once per tick. Decrements the tick count of the ECB at the head of
+ the list. If it goes to zero, completes the ECB and any subsequent ECBs
+ which whose tick count would go to zero
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ RequestMutex();
+ pXecb = TimerList;
+ if (pXecb) {
+
+ //
+ // Decrement if not already zero. Can be zero because the ECB at the
+ // front of the list could have been Cancelled. This makes sure we
+ // do not wrap around to 0xFFFF !!!
+ //
+
+ if (pXecb->Ticks != 0)
+ --pXecb->Ticks;
+
+ if (!pXecb->Ticks) {
+
+ //
+ // complete all ECBs that would go to 0 on this tick
+ //
+
+ while (pXecb->Ticks <= 1) {
+ TimerList = pXecb->Next;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "ScanTimerList: ECB %04x:%04x is done\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+ pXecb = TimerList;
+ if (!pXecb) {
+ break;
+ }
+ }
+ }
+ }
+
+ ReleaseMutex();
+}
+
+
+BYTE
+CancelTimerEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels a pending event on the timer list
+
+Arguments:
+
+ pXecb - pointer to XECB to cancel
+
+Return Value:
+
+ BYTE
+ Success - IPX_SUCCESS
+ Failure - IPX_ECB_NOT_IN_USE
+
+--*/
+
+{
+ LPXECB listptr;
+ LPXECB previous = (LPXECB)&TimerList;
+ BYTE status;
+
+ RequestMutex();
+ listptr = TimerList;
+ while (listptr && listptr != pXecb) {
+ previous = listptr;
+ listptr = listptr->Next;
+ }
+ if (listptr) {
+
+ //
+ // take the XECB out of the list and complete the ECB (in VDM memory).
+ // Does not generate a call-back to the ESR. When CompleteEcb returns,
+ // the XECB has been deallocated
+ //
+
+ previous->Next = listptr->Next;
+
+ ASSERT(pXecb->RefCount == 2);
+
+ --pXecb->RefCount;
+ CompleteEcb(pXecb, ECB_CC_CANCELLED);
+ status = IPX_SUCCESS;
+ } else {
+ status = IPX_ECB_NOT_IN_USE;
+ }
+ ReleaseMutex();
+ return status;
+}
+
+
+VOID
+CancelTimedEvents(
+ IN WORD SocketNumber,
+ IN WORD Owner,
+ IN DWORD TaskId
+ )
+
+/*++
+
+Routine Description:
+
+ traverses the TimerList cancelling any IPX or AES events owned by any of
+ SocketNumber, Owner or TaskId
+
+ Assumes valid SocketNumber, Owner or TaskId cannot be 0
+
+Arguments:
+
+ SocketNumber - owning socket of IPX events to cancel
+ Owner - owning DOS PDB
+ TaskID - owning Windows Task ID
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+ LPXECB prev = (LPXECB)&TimerList;
+ LPXECB next;
+
+ RequestMutex();
+ pXecb = TimerList;
+ while (pXecb) {
+
+ next = pXecb->Next;
+
+ if ((SocketNumber && (pXecb->SocketNumber == SocketNumber))
+ || (Owner && !(pXecb->Flags & XECB_FLAG_IPX) && (pXecb->Owner == Owner))
+ || (TaskId && (pXecb->TaskId == TaskId))) {
+
+ prev->Next = next;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CancelTimedEvents: cancelling ECB %08x (%04x:%04x)\n",
+ pXecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ CompleteEcb(pXecb, ECB_CC_CANCELLED);
+ }
+ else
+ {
+ prev = pXecb ;
+ }
+ pXecb = next;
+ }
+ ReleaseMutex();
+}
+
+
+BYTE
+CancelAsyncEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Called to cancel an event currently on the async completion list. We don't
+ cancel these events - just return 0xF9 (ECB cannot be cancelled). It is a
+ race to see who gets there first - us with the cancel, or the ESR callback.
+ In this case it is fairly immaterial
+
+Arguments:
+
+ pXecb - pointer to XECB to cancel (ignored)
+
+Return Value:
+
+ BYTE - IPX_CANNOT_CANCEL
+
+--*/
+
+{
+ //
+ // we call DeallocateXecb to reduce the reference count. If the other thread
+ // really tried to deallocate it in the short time we've been looking at it
+ // on the cancel path, the call will finish up what the other thread started
+ //
+
+ DeallocateXecb(pXecb);
+ return IPX_CANNOT_CANCEL;
+}
+
+
+BYTE
+CancelSocketEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Called to cancel a pending send or listen from a socket queue. Request can
+ be IPX or SPX. If IPX event, then the ECB is on either the SendQueue or
+ ListenQueue. If SPX, it may be on a CONNECTION_INFO ConnectQueue,
+ AcceptQueue, SendQueue or ListenQueue, or if it is an
+ SPXListenForSequencedPacket request that is still in the pool then it may
+ be on the owning SOCKET_INFO ListenQueue
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to cancel
+
+Return Value:
+
+ BYTE - IPX_SUCCESS
+
+--*/
+
+{
+ LPXECB ptr;
+ LPVOID pObject;
+
+ RequestMutex();
+ pObject = pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case SOCKET_LISTEN_QUEUE:
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->ListenQueue);
+ } else {
+ ptr = DequeueReceiveRequest(pXecb, (LPSOCKET_INFO)pObject);
+ }
+ break;
+
+ case SOCKET_SEND_QUEUE:
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->SendQueue);
+ } else {
+ ptr = DequeueSendRequest(pXecb, (LPSOCKET_INFO)pObject);
+ }
+ break;
+
+ case SOCKET_HEADER_QUEUE: // SPX only
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->HeaderQueue);
+ } else {
+ ASSERT(FALSE);
+ }
+ break;
+ }
+ ReleaseMutex();
+ if (ptr) {
+ CompleteIo(ptr, ECB_CC_CANCELLED);
+ }
+ return IPX_SUCCESS;
+}
+
+
+BYTE
+CancelConnectionEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels a pending SPXListenForConnection or SPXListenForSequencedPacket, the
+ only cancellable SPX requests
+
+Arguments:
+
+ pXecb - pointer to SPX XECB to cancel
+
+Return Value:
+
+ BYTE - IPX_SUCCESS
+
+--*/
+
+{
+ LPXECB ptr;
+ LPVOID pObject;
+ LPXECB_QUEUE pQueue;
+
+ RequestMutex();
+ pObject = pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case CONNECTION_ACCEPT_QUEUE:
+ pQueue = &((LPCONNECTION_INFO)pObject)->AcceptQueue;
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ pQueue = &((LPCONNECTION_INFO)pObject)->ListenQueue;
+ break;
+ }
+ ptr = DequeueEcb(pXecb, pQueue);
+ ReleaseMutex();
+ if (ptr) {
+ CompleteIo(ptr, ECB_CC_CANCELLED);
+ }
+ return IPX_SUCCESS;
+}
+
+
+VOID
+QueueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue,
+ IN QUEUE_ID QueueId
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an XECB to a queue and sets the queue identifier in the XECB.
+
+Arguments:
+
+ pXecb - pointer to XECB to queue
+ Queue - pointer to queue to add XECB to (at tail)
+ QueueId - identifies Queue
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPVOID owningObject = NULL;
+
+#define CONTAINER_STRUCTURE(p, t, f) (LPVOID)(((LPBYTE)(p)) - (DWORD)(&((t)0)->f))
+
+ pXecb->QueueId = QueueId;
+ switch (QueueId) {
+ case SOCKET_LISTEN_QUEUE:
+ if (Queue->Tail && (Queue->Tail->Length < pXecb->Length)) {
+ FifoAddHead((LPFIFO)Queue, (LPFIFO)pXecb);
+ } else {
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ }
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, ListenQueue);
+ break;
+
+ case SOCKET_SEND_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, SendQueue);
+ break;
+
+ case SOCKET_HEADER_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, HeaderQueue);
+ break;
+
+ case CONNECTION_CONNECT_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ConnectQueue);
+ break;
+
+ case CONNECTION_ACCEPT_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, AcceptQueue);
+ break;
+
+ case CONNECTION_SEND_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, SendQueue);
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ListenQueue);
+ break;
+ }
+ pXecb->OwningObject = owningObject;
+}
+
+
+LPXECB
+DequeueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue
+ )
+
+/*++
+
+Routine Description:
+
+ Removes pXecb from Queue and resets the XECB queue identifier (to NO_QUEUE)
+
+Arguments:
+
+ pXecb - pointer to XECB to remove
+ Queue - queue from which to remove pXecb
+
+Return Value:
+
+ LPXECB
+ pointer to removed XECB
+
+--*/
+
+{
+ LPXECB p;
+
+ p = (LPXECB)FifoRemove((LPFIFO)Queue, (LPFIFO)pXecb);
+ pXecb->QueueId = NO_QUEUE;
+ pXecb->OwningObject = NULL;
+ return pXecb;
+}
+
+
+VOID
+CancelSocketQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels all pending ECBs on a SOCKET_INFO queue
+
+Arguments:
+
+ pXecbQueue - pointer to (socket/connection) queue
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ CancelSocketEvent(ptr);
+ }
+}
+
+
+VOID
+CancelConnectionQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels all pending ECBs on a CONNECTION_INFO queue
+
+Arguments:
+
+ pXecbQueue - pointer to XECB queue on CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ CancelConnectionEvent(ptr);
+ }
+}
+
+
+VOID
+AbortQueue(
+ IN LPXECB_QUEUE pXecbQueue,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts or terminates an ECB queue from a CONNECTION_INFO structure
+
+Arguments:
+
+ pXecbQueue - pointer to queue
+ CompletionCode - to put in aborted/terminated ECBs
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ AbortConnectionEvent(ptr, CompletionCode);
+ }
+}
+
+
+VOID
+AbortConnectionEvent(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts a connection ECB
+
+Arguments:
+
+ pXecb - pointer to SPX XECB to cancel
+ CompletionCode - value to put in ECB
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+ LPCONNECTION_INFO pConnectionInfo;
+ LPXECB_QUEUE pQueue;
+
+ pConnectionInfo = (LPCONNECTION_INFO)pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case CONNECTION_CONNECT_QUEUE:
+ pQueue = &pConnectionInfo->ConnectQueue;
+ break;
+
+ case CONNECTION_ACCEPT_QUEUE:
+ pQueue = &pConnectionInfo->AcceptQueue;
+ break;
+
+ case CONNECTION_SEND_QUEUE:
+ pQueue = &pConnectionInfo->SendQueue;
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ pQueue = &pConnectionInfo->ListenQueue;
+ break;
+ }
+ ptr = DequeueEcb(pXecb, pQueue);
+ if (ptr) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AbortConnectionEvent: Aborting ECB %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ SPX_ECB_CONNECTION_ID(ptr->Ecb) = pConnectionInfo->ConnectionId;
+ CompleteOrQueueIo(ptr, CompletionCode);
+ }
+}
+
+
+VOID
+StartIpxSend(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Starts a send operation for IPXSendPacket(). Allocates a send buffer if
+ the ECB has >1 fragment else uses a pointer to the single fragment buffer
+ in 16-bit address space
+
+ Fills in various fields in the ECB and IPX header
+
+ Assumes: 1. By the time this function is called, we already know we have
+ a valid non-zero fragment count and the first fragment is
+ big enough to hold an IPX packet header
+
+ When this function terminates, the ECB is either completed or queued
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to use for sending
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL success;
+ int packetLength = 0;
+ LPFRAGMENT pFragment;
+ int fragmentCount;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "StartIpxSend: %d frag(s), 1: address=%x (%04x:%04x), len=%04x\n",
+ READ_WORD(&pEcb->FragmentCount),
+ GET_FAR_POINTER(&(ECB_FRAGMENT(pEcb, 0)->Address), IS_PROT_MODE(pXecb)),
+ GET_SELECTOR(&(ECB_FRAGMENT(pEcb, 0)->Address)),
+ GET_OFFSET(&(ECB_FRAGMENT(pEcb, 0)->Address)),
+ READ_WORD(&(ECB_FRAGMENT(pEcb, 0)->Length))
+ ));
+
+ //
+ // mark the ECB as being used by IPX (for send)
+ //
+
+ pEcb->InUse = ECB_IU_SENDING;
+
+ //
+ // the total send buffer size cannot exceed the maximum packet size
+ //
+
+ fragmentCount = (int)pEcb->FragmentCount;
+
+ ASSERT(fragmentCount);
+
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ while (fragmentCount--) {
+ packetLength += pFragment->Length;
+ ++pFragment;
+ }
+ if (packetLength <= MyMaxPacketSize) {
+ success = GetIoBuffer(pXecb, TRUE, IPX_HEADER_LENGTH);
+ if (success) {
+
+ LPIPX_PACKET pPacket = (LPIPX_PACKET)GET_FAR_POINTER(
+ &(ECB_FRAGMENT(pEcb, 0)->Address),
+ IS_PROT_MODE(pXecb)
+ );
+
+ //
+ // fill in the following fields in the IPX header:
+ //
+ // Checksum
+ // Length
+ // TransportControl
+ // Source (network, node, socket)
+ //
+ // BUGBUG: Does real IPX modify these fields in app memory?
+ // If so, does the app expect modified fields?
+ // If not, we need to always copy then modify memory,
+ // even if only 1 fragment
+ //
+
+ pPacket->Checksum = 0xFFFF;
+ pPacket->Length = L2BW((WORD)packetLength);
+ pPacket->TransportControl = 0;
+ CopyMemory((LPBYTE)&pPacket->Source,
+ &MyInternetAddress.sa_netnum,
+ sizeof(MyInternetAddress.sa_netnum)
+ + sizeof(MyInternetAddress.sa_nodenum)
+ );
+ pPacket->Source.Socket = pSocketInfo->SocketNumber;
+
+ //
+ // if we allocated a buffer then there is >1 fragment. Collect them
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ GatherData(pXecb, IPX_HEADER_LENGTH);
+ }
+
+ //
+ // initiate the send. IPX_ECB_BUFFER32(pEcb) points to the data to send,
+ // IPX_ECB_LENGTH32(pEcb) is the size of data to send
+ //
+
+ IpxSendFirst(pXecb, pSocketInfo);
+ } else {
+
+ //
+ // couldn't allocate a buffer? Comes under the heading of
+ // hardware error?
+ //
+
+ CompleteEcb(pXecb, ECB_CC_HARDWARE_ERROR);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // packet larger than MyMaxPacketSize
+ //
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_BAD_REQUEST);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+}
+
+
+BOOL
+GetIoBuffer(
+ IN OUT LPXECB pXecb,
+ IN BOOL Send,
+ IN WORD HeaderLength
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate a buffer based on the ECB fragment list. If there is only 1 fragment
+ we use the address of the buffer in the VDM. If >1 fragment, we allocate a
+ 32-bit buffer large enough to hold all the 16-bit fragments
+
+ We trim the buffer requirement for a send buffer: we do not send the IPX/SPX
+ header with the data: it will be provided by the transport
+
+ Assumes: 1. If called for a send buffer, the first fragment has already
+ been verified as >= HeaderLength
+
+Arguments:
+
+ pXecb - pointer to XECB which points to IPX_ECB containing fragment
+ list to allocate buffer for
+ Send - TRUE if this request is to get a send buffer
+ HeaderLength - length of the (untransmitted) header portion
+
+Return Value:
+
+ BOOL
+ TRUE - Buffer allocated, XECB updated with address, length and flags
+ FALSE - either ECB contains bad fragment descriptor list or we
+ couldn't allocate a buffer
+
+--*/
+
+{
+ WORD fragmentCount;
+ WORD bufferLength = 0;
+ LPBYTE bufferPointer = NULL;
+ WORD flags = 0;
+ int i;
+ int fragIndex = 0; // index of fragment address to use if no allocation required
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = READ_WORD(&pEcb->FragmentCount);
+
+ for (i = 0; i < (int)fragmentCount; ++i) {
+ bufferLength += ECB_FRAGMENT(pEcb, i)->Length;
+ }
+ if (bufferLength) {
+
+ //
+ // exclude the IPX header from send buffer. If the first send fragment
+ // contains only the IPX header, reduce the fragment count by 1
+ //
+
+ if (Send) {
+ bufferLength -= HeaderLength;
+ if (ECB_FRAGMENT(pEcb, 0)->Length == HeaderLength) {
+ --fragmentCount;
+ fragIndex = 1;
+ }
+ }
+ if (bufferLength) {
+ if (fragmentCount > 1) {
+ bufferPointer = AllocateBuffer(bufferLength);
+ if (bufferPointer) {
+ flags = XECB_FLAG_BUFFER_ALLOCATED;
+ } else {
+
+ //
+ // need a buffer; failed to allocate it
+ //
+
+ return FALSE;
+ }
+ } else {
+
+ //
+ // fragmentCount must be 1 (else bufferLength would be 0)
+ //
+
+ bufferPointer = GET_FAR_POINTER(
+ &ECB_FRAGMENT(pEcb, fragIndex)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ if (Send && !fragIndex) {
+
+ //
+ // if we are allocating a send buffer AND there is only 1
+ // fragment AND it is the first fragment then the one and
+ // only fragment must contain the IPX header and the data.
+ // Advance the data pointer past the IPX header
+ //
+
+ bufferPointer += HeaderLength;
+ }
+ }
+ } else {
+
+ //
+ // sending 0 bytes!!!
+ //
+
+ }
+ } else {
+
+ //
+ // fragments but no buffer length? Sounds like a malformed packet
+ //
+
+ return FALSE;
+ }
+
+ //
+ // bufferPointer is either the address of a buffer in 32-bit memory which
+ // must be gather/scattered when the I/O operation completes, or it is the
+ // address of a single fragment buffer in 16-bit memory. In the former case
+ // flags is ECB_ALLOCATE_32 and the latter 0
+ //
+
+ pXecb->Buffer = pXecb->Data = bufferPointer;
+ pXecb->Length = bufferLength;
+ pXecb->Flags |= flags;
+ return TRUE;
+}
+
+
+PRIVATE
+VOID
+ReleaseIoBuffer(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Deallocates I/O buffer attached to XECB and zaps associated XECB fields
+
+Arguments:
+
+ pXecb - pointer to XECB owning buffer to be released
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ DeallocateBuffer(pXecb->Buffer);
+ pXecb->Buffer = pXecb->Data = NULL;
+ pXecb->Flags &= ~XECB_FLAG_BUFFER_ALLOCATED;
+ }
+}
+
+
+VOID
+GatherData(
+ IN LPXECB pXecb,
+ IN WORD HeaderLength
+ )
+
+/*++
+
+Routine Description:
+
+ Copies data from fragmented 16-bit memory into single 32-bit memory buffer.
+ Used to send data. We exclude the IPX header: this information is supplied
+ by the transport
+
+ Assumes: 1. The fragment descriptor list has been verified: we know that
+ the first fragment contains at least the IPX header
+
+Arguments:
+
+ pXecb - pointer to XECB structure. The following IPX_ECB and XECB
+ fields must contain coherent values:
+
+ IPX_ECB.FragmentCount
+ XECB.Buffer
+
+ HeaderLength - length of the (untransmitted) header portion
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int fragmentCount;
+ WORD length;
+ ULPBYTE pData16;
+ ULPBYTE pData32;
+ LPFRAGMENT pFragment;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = (int)pEcb->FragmentCount;
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ pData32 = pXecb->Buffer;
+
+ //
+ // if the 1st fragment contains more than the IPX/SPX header, copy the data
+ // after the header
+ //
+
+ if (pFragment->Length > HeaderLength) {
+
+ LPBYTE fragAddr = GET_FAR_POINTER(&pFragment->Address,
+ IS_PROT_MODE(pXecb)
+ );
+
+ length = pFragment->Length - HeaderLength;
+ CopyMemory((LPVOID)pData32,
+ fragAddr + HeaderLength,
+ length
+ );
+ pData32 += length;
+ }
+
+ //
+ // copy subsequent fragments
+ //
+
+ ++pFragment;
+ while (--fragmentCount) {
+ pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
+ length = pFragment->Length;
+ CopyMemory((PVOID)pData32, (CONST VOID*)pData16, (ULONG)length);
+ pData32 += length;
+ ++pFragment;
+ }
+}
+
+
+VOID
+ScatterData(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Copies data from 32-bit memory to 16-bit. The data must be fragmented if
+ this function has been called (i.e. we determined there were >1 fragments
+ and allocated a single 32-bit buffer to cover them)
+
+Arguments:
+
+ pXecb - pointer to XECB containing 32-bit buffer info
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int fragmentCount;
+ int length;
+ WORD length16;
+ WORD length32;
+ ULPBYTE pData16;
+ ULPBYTE pData32;
+ LPFRAGMENT pFragment;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = (int)pEcb->FragmentCount;
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ pData32 = pXecb->Buffer;
+ length32 = pXecb->Length;
+ while (length32) {
+ pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
+ length16 = pFragment->Length;
+ length = min(length16, length32);
+ CopyMemory((PVOID)pData16, (CONST VOID*)pData32, (ULONG)length);
+ pData32 += length;
+ length32 -= length;
+ ++pFragment;
+ --fragmentCount;
+
+ ASSERT(fragmentCount >= 0);
+
+ }
+}
+
+
+VOID
+IpxReceiveFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Performs a receive against a non-blocking socket. This is the first
+ receive call for this ECB. If the receive completes immediately with data
+ or an error that isn't WSAEWOULDBLOCK then the ECB is completed. If the
+ receives completes with a WSAEWOULDBLOCK error then the request is queued
+ for deferred processing by the AES thread
+
+ Unlike send, receives are not serialized. If there are already receives
+ pending against the socket there could be a clash between this function
+ and IpxReceiveNext(), called from the AES thread. In this case, we expect
+ Winsock to do the right thing and serialize the callers
+
+Arguments:
+
+ pXecb - pointer to XECB describing receive ECB
+ pSocketInfo - pointer to socket structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SOCKADDR_IPX from;
+ int fromLen = sizeof(from);
+ int rc;
+ BYTE status;
+ BOOL error;
+
+ rc = recvfrom(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ (int)pXecb->Length,
+ 0, // flags
+ (LPSOCKADDR)&from,
+ &fromLen
+ );
+ if (rc != SOCKET_ERROR) {
+ error = FALSE;
+ status = ECB_CC_SUCCESS;
+ } else {
+ error = TRUE;
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+ RequestMutex();
+ QueueReceiveRequest(pXecb, pSocketInfo);
+ ReleaseMutex();
+ } else if (rc == WSAEMSGSIZE) {
+ error = FALSE;
+ status = ECB_CC_BAD_REQUEST;
+ rc = pXecb->Length;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxReceiveFirst: recvfrom() returns %d (buflen=%d)\n",
+ rc,
+ pXecb->Length
+ ));
+
+ //
+ // BUGBUG: map error
+ //
+
+ CompleteOrQueueIo(pXecb, ECB_CC_BAD_REQUEST);
+ }
+ }
+ if (!error) {
+
+ //
+ // rc = bytes received, or 0 = connection terminated (even for DGRAM?)
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxReceiveFirst: bytes received = %d (%x)\n",
+ rc,
+ rc
+ ));
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ //
+ // if the receive buffers are fragmented, copy the data to 16-bit memory
+ // (else single buffer: its already there (dude))
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+
+ //
+ // update the ECB_LENGTH32 field to reflect the amount of data received
+ //
+
+ pXecb->Length = (WORD)rc;
+ ScatterData(pXecb);
+
+ //
+ // we have finished with the 32-bit buffer: deallocate it
+ //
+
+ ReleaseIoBuffer(pXecb);
+ }
+
+ //
+ // update the ImmediateAddress field in the ECB with the node address
+ // of the sender
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress, from.sa_nodenum, sizeof(from.sa_nodenum));
+
+ //
+ // if this ECB has a non-NULL ESR then queue for asynchronous completion
+ // else complete immediately (app must poll InUse field)
+ //
+
+ CompleteOrQueueEcb(pXecb, status);
+ }
+}
+
+
+VOID
+IpxReceiveNext(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to complete an IPXListenForPacket request that has been deferred due
+ to the fact the socket was blocked.
+
+ The ECB containing all the receive information is at the head of the
+ ListenQueue on pSocketInfo
+
+ We can use any queued listen ECB, but it just so happens we use the one at
+ the head of the FIFO
+
+ Note: SerializationCritSec is held when this function is called.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+ SOCKADDR_IPX from;
+ int fromLen = sizeof(from);
+ int rc;
+ BYTE status;
+ BOOL error;
+
+ ASSERT(pSocketInfo);
+
+ pXecb = (LPXECB)pSocketInfo->ListenQueue.Head;
+
+ ASSERT(pXecb);
+
+ rc = recvfrom(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ (int)pXecb->Length,
+ 0, // flags
+ (LPSOCKADDR)&from,
+ &fromLen
+ );
+ if (rc != SOCKET_ERROR) {
+ error = FALSE;
+ status = ECB_CC_SUCCESS;
+ } else {
+ error = TRUE;
+ rc = WSAGetLastError();
+ if (rc == WSAEMSGSIZE) {
+ error = FALSE;
+ status = ECB_CC_BAD_REQUEST;
+ rc = pXecb->Length;
+ } else if (rc != WSAEWOULDBLOCK) {
+ DequeueReceiveRequest(pXecb, pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxReceiveNext: recvfrom() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: completion code?
+ //
+
+ CompleteOrQueueIo(pXecb, ECB_CC_CANCELLED);
+ }
+ }
+ if (!error) {
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ //
+ // data received. Remove ECB from queue
+ //
+
+ DequeueReceiveRequest(pXecb, pSocketInfo);
+
+ //
+ // rc = bytes received, or 0 = connection terminated (even for DGRAM?)
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxReceiveNext: ECB %04x:%04x bytes received = %d (%x)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ rc,
+ rc
+ ));
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ //
+ // if the receive buffers are fragmented, copy the data to 16-bit memory
+ // (else single buffer: its already there (dude))
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+
+ //
+ // update the IPX_ECB_LENGTH32 field to reflect the amount of data received
+ //
+
+ pXecb->Length = (WORD)rc;
+ ScatterData(pXecb);
+ ReleaseIoBuffer(pXecb);
+ }
+
+ //
+ // update the ImmediateAddress field in the ECB with the node address
+ // of the sender
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress,
+ from.sa_nodenum,
+ sizeof(from.sa_nodenum)
+ );
+
+ //
+ // if this ECB has a non-NULL ESR then queue for asynchronous completion
+ // else complete immediately (app must poll InUse field)
+ //
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+ }
+}
+
+
+PRIVATE
+VOID
+IpxSendFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Tries to send an IPX packet. This is the first attempt to send the packet
+ described in the ECB. If the send succeeds or fails with an error other
+ than WSAEWOULDBLOCK we complete the ECB. If the send attempt fails because
+ the transport can't accept the request at this time, we queue it for later
+ when the AES thread will attempt to send it.
+
+ If there is already a send being attempted then we just queue this request
+ and let AES handle it in IpxSendNext()
+
+Arguments:
+
+ pXecb - pointer to XECB
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RequestMutex();
+ if (pSocketInfo->Flags & SOCKET_FLAG_SENDING) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxSendFirst: queueing ECB %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ QueueSendRequest(pXecb, pSocketInfo);
+ } else {
+
+ SOCKADDR_IPX to;
+ LPIPX_PACKET pPacket;
+ int length;
+ int rc;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+ int type;
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ length = (int)pXecb->Length;
+
+ //
+ // the first fragment holds the destination address info
+ //
+
+ pPacket = (LPIPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pEcb, 0)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ to.sa_family = AF_IPX;
+
+ //
+ // copy the destination net number as a DWORD (4 bytes) from the
+ // destination network address structure in the IPX packet header
+ //
+
+ *(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
+ //
+ // copy the immediate (destination) node number as a DWORD (4 bytes) and
+ // a WORD (2 bytes) from the Destination network address structure in
+ // the IPX packet header. pPacket is an unaligned pointer, so we are
+ // safe
+ //
+
+ *(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
+
+ *(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
+
+ //
+ // copy the destination socket number from the IPX packet header as a
+ // WORD (2 bytes). Again, the aligned pointer will save us
+ //
+
+ to.sa_socket = pPacket->Destination.Socket;
+
+ type = (int)pPacket->PacketType;
+ rc = setsockopt(pSocketInfo->Socket,
+ NSPROTO_IPX,
+ IPX_PTYPE,
+ (char FAR*)&type,
+ sizeof(type)
+ );
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendFirst: setsockopt(IPX_PTYPE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ rc = sendto(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ length,
+ 0, // flags
+ (LPSOCKADDR)&to,
+ sizeof(to)
+ );
+ if (rc == length) {
+
+ //
+ // all data sent
+ //
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ CompleteOrQueueIo(pXecb, ECB_CC_SUCCESS);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ } else if (rc == SOCKET_ERROR) {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxSendFirst: queueing ECB %04x:%04x (after sendto)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ QueueSendRequest(pXecb, pSocketInfo);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendFirst: sendto() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: couple of possible completion codes here
+ //
+
+ CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // BUGBUG: Is 0 a valid value for rc?
+ //
+
+ //
+ // send should send all the data or return an error
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "IpxSendFirst: sendto() returns unexpected %d (length = %d)\n",
+ rc,
+ length
+ ));
+ }
+ }
+ ReleaseMutex();
+}
+
+
+VOID
+IpxSendNext(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to complete an IPXSendPacket request that has been deferred due
+ to the fact the socket was blocked.
+
+ The ECB containing all the send information is at the head of the SendQueue
+ on pSocketInfo
+
+ The SendQueue is serialized in FIFO order
+
+ Note: SerializationCritSec is held when this function is called.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SOCKADDR_IPX to;
+ LPIPX_PACKET pPacket;
+ int length;
+ int rc;
+ LPXECB pXecb;
+ LPIPX_ECB pEcb;
+ int type;
+
+ pXecb = (LPXECB)pSocketInfo->SendQueue.Head;
+ pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ ASSERT(pXecb);
+ ASSERT(pEcb);
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ length = (int)pXecb->Length;
+
+ //
+ // even though we have a 32-bit pointer to the IPX packet buffer which
+ // may be in 16- or 32-bit memory, we still need unaligned access
+ //
+
+ pPacket = (LPIPX_PACKET)pXecb->Buffer;
+ to.sa_family = AF_IPX;
+
+ //
+ // copy the destination net number as a DWORD (4 bytes) from the
+ // destination network address structure in the IPX packet header
+ //
+
+ *(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
+ //
+ // copy the immediate (destination) node number as a DWORD (4 bytes) and
+ // a WORD (2 bytes) from the Destination network address structure in
+ // the IPX packet header. pPacket is an unaligned pointer, so we are
+ // safe
+ //
+
+ *(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
+ *(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
+
+ //
+ // copy the destination socket number from the IPX packet header as a
+ // WORD (2 bytes). Again, the aligned pointer will save us
+ //
+
+ to.sa_socket = pPacket->Destination.Socket;
+
+ type = (int)pPacket->PacketType;
+ rc = setsockopt(pSocketInfo->Socket,
+ NSPROTO_IPX,
+ IPX_PTYPE,
+ (char FAR*)&type,
+ sizeof(type)
+ );
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendNext: setsockopt(IPX_PTYPE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ rc = sendto(pSocketInfo->Socket,
+ (char FAR*)pPacket,
+ length,
+ 0, // flags
+ (LPSOCKADDR)&to,
+ sizeof(to)
+ );
+ if (rc == length) {
+
+ //
+ // all data sent - dequeue it
+ //
+
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ DequeueEcb(pXecb, &pSocketInfo->SendQueue);
+ if (pXecb->EsrAddress) {
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ QueueAsyncCompletion(pXecb, ECB_CC_SUCCESS);
+ } else {
+ CompleteIo(pXecb, ECB_CC_SUCCESS);
+ }
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ } else if (rc == SOCKET_ERROR) {
+
+ //
+ // if the socket is still blocked, there's nothing to do - just leave
+ // the request hanging around till next time
+ //
+
+ rc = WSAGetLastError();
+ if (rc != WSAEWOULDBLOCK) {
+ DequeueSendRequest(pXecb, pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendNext: sendto() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: couple of possible completion codes here
+ //
+
+ CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // BUGBUG: Is 0 a valid value for rc?
+ //
+
+ //
+ // send should send all the data or return an error
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "IpxSendNext: sendto() returns unexpected %d (length = %d)\n",
+ rc,
+ length
+ ));
+ }
+}
+
+
+PRIVATE
+VOID
+QueueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Add a listen XECB to queue of listen XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to listen XECB to queue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ QueueEcb(pXecb, &pSocketInfo->ListenQueue, SOCKET_LISTEN_QUEUE);
+ ++pSocketInfo->PendingListens;
+ pSocketInfo->Flags |= SOCKET_FLAG_LISTENING;
+}
+
+
+PRIVATE
+LPXECB
+DequeueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove a listen XECB from queue of listen XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to listen XECB to dequeue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB ptr;
+
+ ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->ListenQueue);
+ if (ptr) {
+
+ ASSERT(ptr == pXecb);
+
+ --pSocketInfo->PendingListens;
+ if (!pSocketInfo->PendingListens) {
+ pSocketInfo->Flags &= ~SOCKET_FLAG_LISTENING;
+ }
+
+ //
+ // BUGBUG: Is this correct value?
+ //
+
+ pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
+ }
+ return ptr;
+}
+
+
+PRIVATE
+VOID
+QueueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Add a send XECB to queue of send XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to send XECB to queue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ QueueEcb(pXecb, &pSocketInfo->SendQueue, SOCKET_SEND_QUEUE);
+ ++pSocketInfo->PendingSends;
+ pSocketInfo->Flags |= SOCKET_FLAG_SENDING;
+ pXecb->Ecb->InUse = ECB_IU_SEND_QUEUED;
+}
+
+
+PRIVATE
+LPXECB
+DequeueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove a send XECB from queue of send XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to send XECB to dequeue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB ptr;
+
+ ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->SendQueue);
+ if (ptr) {
+
+ ASSERT(ptr == pXecb);
+
+ --pSocketInfo->PendingSends;
+ if (!pSocketInfo->PendingSends) {
+ pSocketInfo->Flags &= ~SOCKET_FLAG_SENDING;
+ }
+ pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
+ }
+ return ptr;
+}
+
+
+VOID
+CompleteOrQueueIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Returns any allocated buffer resource then completes or queues the ECB
+
+Arguments:
+
+ pXecb - pointer to XECB structure
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // if we allocated a buffer, free it
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ CompleteOrQueueEcb(pXecb, CompletionCode);
+}
+
+
+VOID
+CompleteIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a send/receive request by returning any allocated buffer resource
+ and setting the ECB InUse and CompletionCode fields
+
+Arguments:
+
+ pXecb - pointer to XECB structure
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // if we allocated a buffer, free it
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ CompleteEcb(pXecb, CompletionCode);
+}
+
+
+VOID
+CompleteOrQueueEcb(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Queues an XECB for completion by ESR or completes it now
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to complete
+ CompletionCode - value to put in ECB CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pXecb->EsrAddress) {
+ QueueAsyncCompletion(pXecb, CompletionCode);
+ } else {
+ CompleteIo(pXecb, CompletionCode);
+ }
+}
+
+
+VOID
+CompleteEcb(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Sets the CompletionCode field in the ECB and sets the InUse field to 0.
+ Deallocates the XECB structure
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB in 16-bit memory to update
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteEcb: completing ECB @%04x:%04x w/ %02x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ CompletionCode
+ ));
+
+ //
+ // if this is really an AES ECB then CompletionCode is actually the first
+ // byte of the AES workspace. It shouldn't matter that we write into this
+ // field - we are supposed to own it
+ //
+
+ pEcb->CompletionCode = CompletionCode;
+ pEcb->InUse = ECB_IU_NOT_IN_USE;
+
+ //
+ // reset the LinkAddress field. This means we have completed the ECB
+ //
+
+ pEcb->LinkAddress = NULL;
+
+ //
+ // finally, deallocate the XECB. This mustn't have any allocated resources
+ // (like a buffer)
+ //
+
+ DeallocateXecb(pXecb);
+}
+
+
+PRIVATE
+VOID
+QueueAsyncCompletion(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Add an XECB to the (serialized) async completion queue and raise a simulated
+ hardware interrupt in the VDM.
+
+ The interrupt will cause the VDM to start executing at the ISR in the TSR
+ which will call-back to find the address for the ESR, then execute it
+
+Arguments:
+
+ pXecb - pointer to XECB describing IPX or AES ECB to add to async
+ completion list
+ CompletionCode - the ECB in VDM memory will be updated with this completion
+ code
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "QueueAsyncCompletion: completing ECB @%04x:%04x w/ %02x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ CompletionCode
+ ));
+
+ pXecb->Ecb->CompletionCode = CompletionCode;
+ pXecb->QueueId = ASYNC_COMPLETION_QUEUE;
+ EnterCriticalSection(&AsyncCritSec);
+ FifoAdd(&AsyncCompletionQueue, (LPFIFO)pXecb);
+ LeaveCriticalSection(&AsyncCritSec);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "QueueAsyncCompletion: ECB @ %04x:%04x ESR @ %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ VDDSimulateInterrupt(Ica, IcaLine, 1);
+}
+
+
+VOID
+EsrCallback(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Callback function from within 16-bit TSR ESR function. Returns the address
+ of the next completed ECB in ES:SI
+
+ Any allocated resources (e.g. 32-bit buffer) must have been freed by the
+ time the ESR callback happens
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD segment = 0;
+ WORD offset = 0;
+ BYTE flags;
+
+ VWinEsrCallback( &segment, &offset, &flags );
+
+ setES(segment);
+ setSI(offset);
+ setAL(flags);
+}
+
+
+VOID
+VWinEsrCallback(
+ WORD *pSegment,
+ WORD *pOffset,
+ BYTE *pFlags
+ )
+
+/*++
+
+Routine Description:
+
+ Callback function from within 16-bit function. Returns the address
+ of the next completed ECB
+
+ Any allocated resources (e.g. 32-bit buffer) must have been freed by the
+ time the ESR callback happens
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ EnterCriticalSection(&AsyncCritSec);
+ pXecb = AsyncCompletionQueue.Head;
+ if (pXecb) {
+
+ WORD msw = getMSW();
+
+ if ((msw & MSW_PE) ^ IS_PROT_MODE(pXecb)) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "EsrCallback: ECB @ %04x:%04x NOT for this proc mode (%d)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ msw & MSW_PE
+ ));
+
+ pXecb = NULL;
+ } else {
+ pXecb = (LPXECB)FifoNext(&AsyncCompletionQueue);
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "EsrCallback: no ECBs on AsyncCompletionQueue!\n"
+ ));
+
+ }
+ LeaveCriticalSection(&AsyncCritSec);
+
+ if (pXecb) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "EsrCallback: ECB @ %04x:%04x ESR @ %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ *pSegment = HIWORD(pXecb->EcbAddress);
+ *pOffset = LOWORD(pXecb->EcbAddress);
+ pXecb->Ecb->LinkAddress = NULL;
+ pXecb->Ecb->InUse = ECB_IU_NOT_IN_USE;
+ *pFlags = (BYTE)((pXecb->Flags & XECB_FLAG_IPX) ? ECB_TYPE_IPX : ECB_TYPE_AES);
+ DeallocateXecb(pXecb);
+ setCF(0);
+ } else {
+ setCF(1);
+ }
+}
+
+
+VOID
+FifoAddHead(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an element to the head of a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to add to list
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (!pFifo->Head) {
+ pFifo->Head = pFifo->Tail = pElement;
+ pElement->Head = NULL;
+ } else {
+ pElement->Head = pFifo->Head;
+ pFifo->Head = pElement;
+ }
+}
+
+VOID
+FifoAdd(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an element to the tail of a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to add to list
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (!pFifo->Head) {
+ pFifo->Head = pFifo->Tail = pElement;
+ } else {
+ ((LPFIFO)pFifo->Tail)->Head = pElement;
+ }
+ pFifo->Tail = pElement;
+ pElement->Head = NULL;
+}
+
+
+LPFIFO
+FifoRemove(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Removes an element from a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to remove (single-linked)
+
+Return Value:
+
+ PFIFO
+ NULL - pElement not on list
+ !NULL - pElement removed from list
+
+--*/
+
+{
+ LPFIFO p;
+ LPFIFO prev = (LPFIFO)pFifo;
+
+ p = (LPFIFO)pFifo->Head;
+ while (p && (p != pElement)) {
+ prev = p;
+ p = p->Head;
+ }
+ if (p) {
+ prev->Head = p->Head;
+ if (pFifo->Head == NULL) {
+ pFifo->Tail = NULL;
+ } else if (pFifo->Tail == p) {
+ pFifo->Tail = prev;
+ }
+ }
+ return p;
+}
+
+
+LPFIFO
+FifoNext(
+ IN LPFIFO pFifo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove element at head of FIFO queue
+
+Arguments:
+
+ pFifo - pointer to FIFO
+
+Return Value:
+
+ LPFIFO
+ NULL - nothing on queue
+ !NULL - removed element
+
+--*/
+
+{
+ LPFIFO p;
+ LPFIFO prev = (LPFIFO)pFifo;
+
+ p = (LPFIFO)pFifo->Head;
+ if (p) {
+ pFifo->Head = p->Head;
+ if (!pFifo->Head) {
+ pFifo->Tail = NULL;
+ }
+ }
+ return p;
+}