When a kernel-mode client makes a TDI_RECEIVE request, it asks the underlying TDI transport driver to indicate a normal or expedited TSDU on an established endpoint-to-endpoint connection.
IRP
The transport calls IoGetCurrentIrpStackLocation with the given Irp to get a pointer to its own I/O stack location in the IRP, shown in the following list as IrpSp. IRP members relevant to this request include the following:
IoStatus.Status
Specifies the final status of the receive request. The transport sets this member before it completes the IRP, possibly to one of the following:
STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_CONNECTION
STATUS_BUFFER_OVERFLOW
IoStatus.Information
Specifies the number of bytes of received data the transport is returning in the client-supplied buffer.
IrpSp->MajorFunction
Specifies IRP_MJ_INTERNAL_DEVICE_CONTROL. The transport can ignore this member if it exports a TdiDispatchInternalDeviceControl routine that handles only TDI_XXX requests.
IrpSp->MinorFunction
Specifies TDI_RECEIVE.
IrpSp->FileObject
Points to an open file object representing the connection endpoint. The transport uses the FsContext and, possibly, FsContext2 fields to access the state it maintains about this connection.
This client already established an endpoint-to-endpoint connection with a remote-node peer on this connection endpoint.
IrpSp->Parameters
Points to a TDI_REQUEST_KERNEL_RECEIVE structure, defined as follows:
struct _TDI_REQUEST_KERNEL_RECEIVE {
ULONG ReceiveLength;
ULONG ReceiveFlags;
} TDI_REQUEST_KERNEL_RECEIVE, *PTDI_REQUEST_KERNEL_RECEIVE;
The transport uses the members of this structure as follows:
ReceiveLength
Specifies the size in bytes of the client-supplied buffer mapped at Irp->MdlAddress.
ReceiveFlags
Specifies the type of receive this client is requesting, as described later (see Comments).
MdlAddress
Points to an MDL, possibly the initial MDL in a chain, mapping a client-supplied buffer in which the transport returns the received data.
Comments
When a client calls TdiBuildReceive to set up this IRP, it can specify the type of receive it wants. The transport finds this information at IrpSp->Parameters in the ReceiveFlags member. On input, ReceiveFlags can be zero or set with any combination (ORed) of the following flags:
TDI_RECEIVE_NORMAL
The transport should return a normal TSDU to this client.
TDI_RECEIVE_EXPEDITED
The transport should return an expedited TSDU to this client.
TDI_RECEIVE_PEEK
The transport should return any available received data that it has buffered internally at once, even if it is only a partial TSDU, to this client.
If ReceiveFlags is zero or set with both (ORed) TDI_RECEIVE_NORMAL and TDI_RECEIVE_EXPEDITED on input, either normal or expedited data is acceptable to the client. If only one of these flags is set, the transport should return only the type of TSDU requested. If TDI_RECEIVE_PEEK is set, the transport should return any received data it has buffered internally and retain the data in its internal buffer awaiting a subsequent receive request for the remainder of the TSDU from this client.
Typically, a transport receives normal data until it fills the receive buffer or it receives an end-of-record indication from the remote node. However, if the transport is receiving normal data and expedited data arrives, the transport must pre-empt its normal receive operation, completing the IRP for the normal receive immediately with any normal data it has already transferred into the client's buffer. Then, the transport must satisfy the client's receive request(s) for the expedited data. When the transport has indicated all the expedited data to the client, the transport resumes indicating normal receives when the next receive request is submitted by that client.
If the client has registered any ClientEventReceive, ClientEventReceiveExpedited, ClientEventChainedReceive, and/or ClientEventChainedReceiveExpedited handler(s) for receives on the given connection endpoint, the transport stops indicating receives to the ClientEvent(Chained)Receive(Expedited) routine(s) until the transport has satisfied the receive IRP.
When it calls ClientEventReceive or ClientEventReceiveExpedited, the transport supplies information about the type of receive event it is indicating, which the client can use to set up the ReceiveFlags for a subsequent TDI_RECEIVE request if it is not given the full TSDU. The transport supplies this information to its client by setting the ReceiveFlags parameter to ClientEventReceive(Expedited) with one or more (ORed) of the following flags:
TDI_RECEIVE_NORMAL
The transport is receiving a normal TSDU from the remote node.
TDI_RECEIVE_EXPEDITED
The transport is receiving an expedited TSDU from the remote node.
TDI_RECEIVE_ENTIRE_MESSAGE
If this flag is set, the transport already has a full TSDU available for receipt by the client. When this flag is set, ClientEventReceive is expected to copy the full net packet into an internal buffer and return control without postprocessing the received data, deferring any necessary postprocessing until later.
If this flag remains clear, it is equivalent to setting the (obsolete, but possibly still set by legacy transports) TDI_RECEIVE_PARTIAL flag to indicate that only the initial part of a TSDU is currently available from the transport.
TDI_RECEIVE_COPY_LOOKAHEAD
The transport has received only the lookahead buffer for an indication from the underlying NIC driver, so it sets BytesAvailable to something less than BytesIndicated when the transport calls ClientEventReceive(Expedited) with these parameters. The client should copy the BytesAvailable into an internal buffer (see TdiCopyLookaheadData) and, then, submit one or more receive requests to obtain the remaining data in the TSDU.
TDI_RECEIVE_PEEK
The transport has buffered some receive data internally, but not yet the full TSDU. The client handles such an indication in a similar manner to one in which TDI_RECEIVE_COPY_LOOKAHEAD is set.
TDI_RECEIVE_TRUNCATED
The transport received a TSDU that was truncated.
TDI_RECEIVE_FRAGMENT
The transport received a TSDU that is fragmented.
TDI_RECEIVE_AT_DISPATCH_LEVEL
The transport received the TSDU at IRQL DISPATCH_LEVEL. This flag restricts the set of support routines that the client can call in processing the receive indication. For example, the client cannot call TdiBuildInternalDeviceControlIrp, which can be called only at IRQL PASSIVE_LEVEL, in the context of this receive indication.
When the transport ORs TDI_RECEIVE_ENTIRE_MESSAGE with TDI_RECEIVE_NORMAL and calls the ClientEventChainedReceive handler, the client is given direct read-only access to a full TSDU in the underlying NIC's receive buffer(s) until that client calls TdiReturnChainedReceives with the input TsduDescriptor. For such an indication, the client has no need to issue a TDI_RECEIVE request. In effect, such a transport forwards receive indications from the underlying NDIS driver directly to its client with only the minimal overhead of setting up the parameters to ClientEventChainedReceive, as follows:
·The transport uses the NDIS_PACKET-type descriptor input to its ProtocolReceivePacket function as the TsduDescriptor passed to ClientEventChainedReceive. For each client to which the transport forwards the receive indication, the transport augments the value to be returned by ProtocolReceivePacket by one.
·The transport retrieves the buffer containing the indicated data from the packet descriptor supplied by the underlying NDIS driver, possibly by calling NdisGetFirstBufferFromPacket. The transport uses the pointer to this NDIS_BUFFER-type (equivalent to the MDL type) chain as the Tsdu for ClientEventChainedReceive.
·From the full range of receive data indicated by the underlying NDIS driver, the transport selects the subrange of interest to its client. The driver uses this subrange as the StartingOffset and ReceiveLength for ClientEventChainedReceive.
In a similar manner, such a transport gives ClientEventChainedReceiveExpedited direct read-only access to a full TSDU when TDI_RECEIVE_ENTIRE_MESSAGE is ORed with TDI_RECEIVE_EXPEDITED in the input RequestFlags.
For a TSDU indicated to a ClientEventChainedReceive(Expedited) handler, the client retains control of all resources for the receive indication until it calls TdiReturnChainedReceives with the TsduDescriptor. As each client that received the indication calls TdiReturnChainedReceives, the NDIS library decrements the counter it maintains for the packet descriptor, which was set originally to the value returned by the transport's ProtocolReceivePacket function. When all clients that received the indication have called TdiReturnChainedReceives, NDIS returns control of all resources specified by that packet descriptor to the NDIS driver that allocated these resources.
When such a transport is called at its ProtocolReceive function with a full-packet indication made by the underlying NDIS driver, the transport must not call a registered ClientEventChainedReceive or ClientEventChainedReceiveExpedited handler with the indication. Instead, it should copy the indicated data for its client into a transport-allocated buffer and call the ClientEventReceive(Expedited) handler. An NDIS call to the transport's ProtocolReceive function implies that the underlying driver expects all higher level drivers to copy the receive data it is indicating and to return its packet descriptor (and all the lower driver's resources that it describes) as quickly as possible because the lowest level NDIS driver is short on NIC receive buffer space.
See Also
ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, NdisGetFirstBufferFromPacket, NDIS_PACKET, ProtocolReceive, ProtocolReceivePacket, TdiBuildReceive, TdiCopyLookaheadData, TdiDispatchInternalDeviceControl, TdiReturnChainedReceives