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.
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:
STATUS_PENDING
STATUS_INSUFFICIENT_RESOURCES
STATUS_INVALID_CONNECTION
STATUS_BUFFER_OVERFLOW
This client already established an endpoint-to-endpoint connection with a
remote-node peer on this connection endpoint.
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:
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:
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:
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.
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:
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.
ClientEventChainedReceive, ClientEventChainedReceiveExpedited, ClientEventReceive, ClientEventReceiveExpedited, NdisGetFirstBufferFromPacket, NDIS_PACKET, ProtocolReceive, ProtocolReceivePacket, TdiBuildReceive, TdiCopyLookaheadData, TdiDispatchInternalDeviceControl, TdiReturnChainedReceives