Leaked source code of windows server 2003
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.
 
 
 
 
 
 

595 lines
21 KiB

/*
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
(C) Copyright 1998
All rights reserved.
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
Portions of this software are:
(C) Copyright 1995, 1999 TriplePoint, Inc. -- http://www.TriplePoint.com
License to use this software is granted under the terms outlined in
the TriplePoint Software Services Agreement.
(C) Copyright 1992 Microsoft Corp. -- http://www.Microsoft.com
License to use this software is granted under the terms outlined in
the Microsoft Windows Device Driver Development Kit.
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@doc INTERNAL Transmit Transmit_c
@module Transmit.c |
This module implements the Miniport packet Transmit routines. This module is
very dependent on the hardware/firmware interface and should be looked at
whenever changes to these interfaces occur.
@head3 Contents |
@index class,mfunc,func,msg,mdata,struct,enum | Transmit_c
@end
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
*/
/* @doc EXTERNAL INTERNAL
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@topic 3.3 Sending Packets |
<f MiniportWanSend> transmits a packet through the adapter
onto the network.
Ownership of both the packet descriptor and the packet data
is transferred to the WAN NIC driver until this request is
completed, either synchronously or asynchronously. If the
WAN miniport returns NDIS_STATUS_PENDING, it must later
indicate completion of the request by calling NdisMWanSendComplete.
If the WAN miniport returns a status other than NDIS_STATUS_PENDING,
the request is considered to be complete, and ownership of the packet
immediately reverts to the caller.
Unlike LAN miniports, the WAN driver cannot return a status of
NDIS_STATUS_RESOURCES to indicate that it does not have enough
resources currently available to process the transmit. Instead,
the WAN miniport should queue the send internally for a later
time and perhaps lower the SendWindow value on the line by
making a line-up indication. The NDISWAN driver will insure
that the WAN miniport driver never has more than SendWindow
packets outstanding. If a WAN miniport makes a line-up indication
for a particular line, and sets the SendWindow to zero, NDISWAN
reverts to using the default value of the transmit window passed
as the MaxTransmit value provided to an earlier OID_WAN_GET_INFO
request.
It is also an error for the WAN miniport NIC driver to
call NdisMSendResourcesAvailable.
The packet passed to <f MiniportWanSend> will contain simple HDLC
PPP framing if PPP framing is set. For SLIP or RAS framing, the
packet contains only the data portion with no framing whatsoever.
Simple HDLC PPP framing is discussed later in more detail.
A WAN NIC driver must not attempt to provide software loopback or
promiscuous mode loopback. Both of these are fully supported in
the NDISWAN driver.
The MacReservedx members as well as the WanPacketQueue member of
the <t NDIS_WAN_PACKET> is fully available for use by the WAN miniport.
The available header padding is simply CurrentBuffer-StartBuffer.
The available tail padding is EndBuffer-(CurrentBuffer+CurrentLength).
The header and tail padding is guaranteed to be at least the amount
requested, but it can be more.
See <t NDIS_WAN_PACKET> in the Network Driver Reference for details of
the WAN packet descriptor structure.
A WAN miniport calls NdisMWanSendComplete to indicate that it has
completed a previous transmit operation for which it returned
NDIS_STATUS_PENDING. This does not necessarily imply that the
packet has been transmitted, although, with the exception of
intelligent adapters, it generally has. It does however, mean
the miniport is ready to release ownership of the packet.
When a WAN miniport calls NdisMWanSendComplete, it passes back
the original packet to indicate which send operation was completed.
If <f MiniportWanSend> returns a status other than NDIS_STATUS_PENDING,
it does not call NdisMWanSendComplete for that packet.
@end
*/
#define __FILEID__ TRANSMIT_OBJECT_TYPE
// Unique file ID for error logging
#include "Miniport.h" // Defines all the miniport objects
#if defined(NDIS_LCODE)
# pragma NDIS_LCODE // Windows 95 wants this code locked down!
# pragma NDIS_LDATA
#endif
/* @doc INTERNAL Transmit Transmit_c TransmitAddToQueue
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@func
<f TransmitAddToQueue> places the packet on the transmit queue. If the
queue was empty to begin with, TRUE is returned so the caller can kick
start the transmiter.
@rdesc
<f TransmitAddToQueue> returns TRUE if this is the only entry in the
list, FALSE otherwise.
*/
DBG_STATIC BOOLEAN TransmitAddToQueue(
IN PMINIPORT_ADAPTER_OBJECT pAdapter, // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
IN PBCHANNEL_OBJECT pBChannel, // @parm
// A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.
IN PNDIS_WAN_PACKET pWanPacket // @parm
// A pointer to the associated NDIS packet structure <t NDIS_WAN_PACKET>.
)
{
DBG_FUNC("TransmitAddToQueue")
/*
// Note if the list is empty to begin with.
*/
BOOLEAN ListWasEmpty;
DBG_ENTER(pAdapter);
/*
// Place the packet on the TransmitPendingList.
*/
NdisAcquireSpinLock(&pAdapter->TransmitLock);
ListWasEmpty = IsListEmpty(&pAdapter->TransmitPendingList);
InsertTailList(&pAdapter->TransmitPendingList, &pWanPacket->WanPacketQueue);
NdisReleaseSpinLock(&pAdapter->TransmitLock);
DBG_RETURN(pAdapter, ListWasEmpty);
return (ListWasEmpty);
}
/* @doc INTERNAL Transmit Transmit_c TransmitPacketHandler
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@func
<f TransmitPacketHandler> removes an entry from the TransmitPendingList
and places the packet on the appropriate B-channel and starts the
transmission. The packet is then placed on the <t TransmitBusyList> to
await a transmit complete event processed by <f TransmitCompleteHandler>.
@comm
The packets go out in a FIFO order for the entire driver, independent of
the channel on which it goes out. This means that a slow link, or one
that is backed up can hold up all other channels. There is no good way
to get around this because we must to deliver packets in the order they
are given to the Miniport, regardless of the link they are on.
*/
DBG_STATIC VOID TransmitPacketHandler(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("TransmitPacketHandler")
PNDIS_WAN_PACKET pWanPacket;
// Holds the packet being transmitted.
USHORT BytesToSend;
// Tells us how many bytes are to be transmitted.
PBCHANNEL_OBJECT pBChannel;
// A pointer to one of our <t BCHANNEL_OBJECT>'s.
DBG_ENTER(pAdapter);
/*
// MUTEX to protect against async EventHandler access at the same time.
*/
NdisAcquireSpinLock(&pAdapter->TransmitLock);
#if DBG
{ // Sanity check!
PLIST_ENTRY pList = &pAdapter->TransmitPendingList;
ASSERT(pList->Flink && pList->Flink->Blink == pList);
ASSERT(pList->Blink && pList->Blink->Flink == pList);
}
#endif // DBG
/*
// This might be called when no packets are queued!
*/
while (!IsListEmpty(&pAdapter->TransmitPendingList))
{
/*
// Remove the packet from the TransmitPendingList.
*/
pWanPacket = (PNDIS_WAN_PACKET)RemoveHeadList(&pAdapter->TransmitPendingList);
/*
// Release MUTEX
*/
NdisReleaseSpinLock(&pAdapter->TransmitLock);
/*
// Retrieve the information we saved in the packet reserved fields.
*/
pBChannel = (PBCHANNEL_OBJECT) pWanPacket->MacReserved1;
/*
// Make sure the link is still up and can accept transmits.
*/
if (pBChannel->CallState != LINECALLSTATE_CONNECTED)
{
/*
// Indicate send complete failure to the NDIS wrapper.
*/
DBG_WARNING(pAdapter,("Flushing send on link#%d (Packet=0x%X)\n",
pBChannel->BChannelIndex, pWanPacket));
if (pBChannel->NdisLinkContext)
{
NdisMWanSendComplete(pAdapter->MiniportAdapterHandle,
pWanPacket, NDIS_STATUS_FAILURE);
}
/*
// Reacquire MUTEX
*/
NdisAcquireSpinLock(&pAdapter->TransmitLock);
}
else
{
BytesToSend = (USHORT) pWanPacket->CurrentLength;
pAdapter->TotalTxBytes += BytesToSend;
pAdapter->TotalTxPackets++;
/*
// Attempt to place the packet on the NIC for transmission.
*/
if (!CardTransmitPacket(pAdapter->pCard, pBChannel, pWanPacket))
{
/*
// ReQueue the packet on the TransmitPendingList and leave.
// Reacquire MUTEX
*/
NdisAcquireSpinLock(&pAdapter->TransmitLock);
InsertHeadList(&pAdapter->TransmitPendingList, &pWanPacket->WanPacketQueue);
break;
}
DBG_TX(pAdapter, pBChannel->BChannelIndex,
BytesToSend, pWanPacket->CurrentBuffer);
/*
// Reacquire MUTEX
*/
NdisAcquireSpinLock(&pAdapter->TransmitLock);
}
}
/*
// Release MUTEX
*/
NdisReleaseSpinLock(&pAdapter->TransmitLock);
DBG_LEAVE(pAdapter);
}
/* @doc INTERNAL Transmit Transmit_c TransmitCompleteHandler
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@func
<f TransmitCompleteHandler> is called by <f MiniportTimer> to handle a
transmit complete event. We walk the <t TransmitCompleteList> to find
all the packets that have been sent out on the wire, and then tell the
protocol stack that we're done with the packet, and it can be re-used.
*/
VOID TransmitCompleteHandler(
IN PMINIPORT_ADAPTER_OBJECT pAdapter // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
)
{
DBG_FUNC("TransmitCompleteHandler")
PNDIS_WAN_PACKET pWanPacket;
// Holds the packet that's just been transmitted.
PBCHANNEL_OBJECT pBChannel;
// A pointer to one of our <t BCHANNEL_OBJECT>'s.
DBG_ENTER(pAdapter);
/*
// I find it useful to do this nest check, just so I can make sure
// I handle it correctly when it happens.
*/
if (++(pAdapter->NestedDataHandler) > 1)
{
DBG_ERROR(pAdapter,("NestedDataHandler=%d > 1\n",
pAdapter->NestedDataHandler));
}
/*
// MUTEX to protect against async EventHandler access at the same time.
*/
NdisDprAcquireSpinLock(&pAdapter->TransmitLock);
#if DBG
{ // Sanity check!
PLIST_ENTRY pList = &pAdapter->TransmitCompleteList;
ASSERT(pList->Flink && pList->Flink->Blink == pList);
ASSERT(pList->Blink && pList->Blink->Flink == pList);
}
#endif // DBG
while (!IsListEmpty(&pAdapter->TransmitCompleteList))
{
/*
// Remove the packet from the TransmitCompleteList.
*/
pWanPacket = (PNDIS_WAN_PACKET)RemoveHeadList(&pAdapter->TransmitCompleteList);
/*
// Release MUTEX
*/
NdisDprReleaseSpinLock(&pAdapter->TransmitLock);
/*
// Retrieve the information we saved in the packet reserved fields.
*/
pBChannel = (PBCHANNEL_OBJECT) pWanPacket->MacReserved1;
/*
// Indicate send complete to the NDIS wrapper.
*/
DBG_TXC(pAdapter, pBChannel->BChannelIndex);
NdisMWanSendComplete(pAdapter->MiniportAdapterHandle,
pWanPacket, NDIS_STATUS_SUCCESS);
/*
// Reacquire MUTEX
*/
NdisDprAcquireSpinLock(&pAdapter->TransmitLock);
}
/*
// Release MUTEX
*/
NdisDprReleaseSpinLock(&pAdapter->TransmitLock);
/*
// Start any other pending transmits.
*/
TransmitPacketHandler(pAdapter);
/*
// I find it useful to do this nest check, just so I can make sure
// I handle it correctly when it happens.
*/
if (--(pAdapter->NestedDataHandler) < 0)
{
DBG_ERROR(pAdapter,("NestedDataHandler=%d < 0\n",
pAdapter->NestedDataHandler));
}
DBG_LEAVE(pAdapter);
}
/* @doc INTERNAL Transmit Transmit_c MiniportWanSend
ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ
@func
<f MiniportWanSend> instructs a WAN driver to transmit a packet through
the adapter onto the medium.
@iex
typedef struct _NDIS_WAN_PACKET
{
LIST_ENTRY WanPacketQueue;
PUCHAR CurrentBuffer;
ULONG CurrentLength;
PUCHAR StartBuffer;
PUCHAR EndBuffer;
PVOID ProtocolReserved1;
PVOID ProtocolReserved2;
PVOID ProtocolReserved3;
PVOID ProtocolReserved4;
PVOID MacReserved1; // pBChannel
PVOID MacReserved2;
PVOID MacReserved3;
PVOID MacReserved4;
} NDIS_WAN_PACKET, *PNDIS_WAN_PACKET;
@comm
When <f MiniportWanSend> is called, ownership of both the packet descriptor and
the packet data is transferred to the driver until it completes the given
packet, either synchronously or asynchronously. If <f MiniportWanSend>
returns a status other than NDIS_STATUS_PENDING, the request is
considered complete and ownership of the packet immediately reverts
to the initiator of the send request. If MiniportWanSend returns
NDIS_STATUS_PENDING, the miniport subsequently must call
NdisMWanSendComplete with the packet to indicate completion
of the transmit request.
MiniportWanSend can use both the <t MacReservedX> and <t WanPacketQueue>
areas within the <t NDIS_WAN_PACKET> structure. However, the miniport
cannot access the ProtocolReservedx members.
Any NDIS intermediate driver that binds itself to an underlying
WAN miniport is responsible for providing a fresh <t NDIS_WAN_PACKET>
structure to the underlying driver's <f MiniportWanSend> function. Before
such an intermediate driver calls NdisSend, it must repackage the send
packet given to its MiniportWanSend function so the underlying driver
will have MacReservedx and WanPacketQueue areas of its own to use.
The available header padding within a given packet can be calculated
as (CurrentBuffer - StartBuffer), the available tail padding as
(EndBuffer - (CurrentBuffer + CurrentLength)). The header and
tail padding is guaranteed to be at least the length that the
miniport requested in response to a preceding OID_WAN_GET_INFO
query. The header and/or tail padding for any packet given to
<f MiniportWanSend> can be more than the length that was requested.
<f MiniportWanSend> can neither return NDIS_STATUS_RESOURCES for an
input packet nor call NdisMSendResourcesAvailable. Instead, the
miniport must queue incoming send packets internally for subsequent
transmission. The miniport controls how many packets NDIS will
submit to MiniportWanSend when the NIC driver sets the SendWindow
value in the driver's NDIS_MAC_LINE_UP indication to establish the
given link. NDISWAN uses this value as an upper bound on uncompleted
sends submitted to <f MiniportWanSend>, so the miniport's internal queue
cannot be overrun, and the miniport can adjust the SendWindow
dynamically with subsequent line-up indications for established
links. If the miniport set the SendWindow to zero when it called
NdisMIndicateStatus with a particular line-up indication, NDISWAN
uses the MaxTransmit value that the driver originally set in response
to the OID_WAN_GET_INFO query as its limit on submitted but uncompleted
send packets.
Each packet passed to <f MiniportWanSend> is set up according to one of
the flags that the miniport set in the FramingBits member in response
to the OID_WAN_GET_INFO query. It will contain simple HDLC PPP framing
if the driver claimed to support PPP. For SLIP or RAS framing, such
a packet contains only the data portion with no framing whatsoever.
For more information about system-defined WAN and TAPI OIDs, see Part 2.
<f Note>: A WAN driver cannot manage software loopback or promiscuous mode
loopback internally. NDISWAN supplies this loopback support for
WAN drivers.
<f Note>: <f MiniportWanSend> can be pre-empted by an interrupt.
By default, <f MiniportWanSend> runs at IRQL DISPATCH_LEVEL.
@rdesc
<f MiniportWanSend> can return one of the following:
@flag NDIS_STATUS_SUCCESS |
The driver (or its NIC) has accepted the packet data for
transmission, so <f MiniportWanSend> is returning the packet.<nl>
<f Note>: A non-zero return value indicates one of the following
error codes:
@iex
NDIS_STATUS_INVALID_DATA
NDIS_STATUS_INVALID_LENGTH
NDIS_STATUS_INVALID_OID
NDIS_STATUS_NOT_ACCEPTED
NDIS_STATUS_NOT_SUPPORTED
NDIS_STATUS_PENDING
NDIS_STATUS_FAILURE
*/
NDIS_STATUS MiniportWanSend(
IN PMINIPORT_ADAPTER_OBJECT pAdapter, // @parm
// A pointer to the <t MINIPORT_ADAPTER_OBJECT> instance.
IN PBCHANNEL_OBJECT pBChannel, // @parm
// A pointer to the <t BCHANNEL_OBJECT> returned by <f BChannelCreate>.
IN PNDIS_WAN_PACKET pWanPacket // @parm
// A pointer to the associated NDIS packet structure <t NDIS_WAN_PACKET>.
// The structure contains a pointer to a contiguous buffer with guaranteed
// padding at the beginning and end. The driver may manipulate the buffer
// in any way.
)
{
DBG_FUNC("MiniportWanSend")
UINT BytesToSend;
// Tells us how many bytes are to be transmitted.
NDIS_STATUS Result;
// Holds the result code returned by this function.
ASSERT(pAdapter && pAdapter->ObjectType == MINIPORT_ADAPTER_OBJECT_TYPE);
ASSERT(pBChannel && pBChannel->ObjectType == BCHANNEL_OBJECT_TYPE);
ASSERT(pAdapter == pBChannel->pAdapter);
DBG_ENTER(pAdapter);
/*
// Make sure the packet size is something we can deal with.
*/
BytesToSend = pWanPacket->CurrentLength;
if ((BytesToSend == 0) || (BytesToSend > pAdapter->pCard->BufferSize))
{
DBG_ERROR(pAdapter,("Bad packet size = %d\n",BytesToSend));
Result = NDIS_STATUS_FAILURE;
}
/*
// Return if line has been closed.
*/
else if (pBChannel->CallClosing)
{
DBG_ERROR(pAdapter,("BChannel Closed\n"));
Result = NDIS_STATUS_FAILURE;
}
else
{
/*
// We have to accept the frame if possible, I just want to know
// if somebody has lied to us...
*/
if (BytesToSend > pBChannel->WanLinkInfo.MaxSendFrameSize)
{
DBG_NOTICE(pAdapter,("Line=%d Packet size=%d > %d\n",
pBChannel->BChannelIndex, BytesToSend,
pBChannel->WanLinkInfo.MaxSendFrameSize));
}
/*
// We'll need to use these when the transmit completes.
*/
pWanPacket->MacReserved1 = (PVOID) pBChannel;
/*
// Place the packet in the transmit list.
*/
if (TransmitAddToQueue(pAdapter, pBChannel, pWanPacket) &&
pAdapter->NestedDataHandler < 1)
{
/*
// The queue was empty so we've gotta kick start it.
// Once it's going, it runs off the DPC.
//
// No kick start is necessary if we're already running the the
// TransmitCompleteHandler -- In fact, it will screw things up if
// we call TransmitPacketHandler while TransmitCompleteHandler is
// running.
*/
TransmitPacketHandler(pAdapter);
}
Result = NDIS_STATUS_PENDING;
}
DBG_RETURN(pAdapter, Result);
return (Result);
}