Previous sections have described specific actions an intermediate driver must follow to perform correctly, summarized as follows:
If any internal driver resources are shared between an intermediate driver’s send function and any other MiniportXxx function, that resource must be protected by a spin lock. The only exception to this is in the MiniportReset path, which is serialized by NDIS with respect to the MiniportSend or MiniportSendPackets function and all other MiniportXxx functions.
As for any full-duplex miniport driver, an intermediate driver that organizes its per-binding context area into discrete receive-specific, send-specific, and shared ranges, with only the shared resource(s) protected by spin lock(s), will exhibit far better performance than a driver that must overprotect its context area because send-specific, receive-specific, and shared variables are scattered throughout.
NDIS synchronizes the execution of MiniportXxx functions as already explained in Part 2. To perform this same synchronization for an intermediate driver so that it can act as a miniport, any ProtocolXxx function in the intermediate driver must call NdisIMSwitchToMiniport before the driver calls an NdisMXxx that indicates up to the higher level driver. For instance, before ProtocolReceive calls NdisMIndicateReceivePacket or NdisMXxxIndicateReceive to pass a packet to the next higher driver, it must call NDIS to get into the context of a miniport.
An intermediate driver first calls NdisIMSwitchToMiniport. If NdisIMSwitchToMiniport returns TRUE, ProtocolReceive(Packets) can make its NdisMXxx call followed by a call to NdisIMRevertBack when its miniport-only operations are complete. NdisIMSwitchToMiniport acquires a spin lock that prevents other MiniportXxx functions in the same driver from running while the spin lock is held. Therefore, it is important that the intermediate driver call NdisIMRevertBack as quickly as possible.
If NdisIMSwitchToMiniport returns FALSE, ProtocolReceive(Packets) must call NdisIMQueueMiniportCallback to schedule a callback function in which to forward the receive indication. NDIS calls the driver-supplied callback function when it obtains the necessary spin lock, thereby allowing the callback function to perform the same actions the intermediate driver would have performed if the preceding call to NdisIMSwitchToMiniport had been successful.
When NdisIMQueueMiniportCallback returns, ProtocolXxx can resume protocol operations. If NdisIMQueueMiniportCallback returns NDIS_STATUS_SUCCESS, the miniport-only operations have been completed in the MiniportCallback function before the return.
Note that the general restriction of switching to a miniport context applies to protocol actions other than just indicating up received packets. For instance, an intermediate driver's ProtocolStatus function also must make a context switch to being a miniport before the driver calls NdisMIndicateStatus to forward a status indication from a lower level driver to a higher level driver.
NdisIMSwitchToMiniport, NdisIMRevertBack and NdisIMQueueMiniportCallback must not be called from any MiniportXxx function. It is a fatal error to make such a call.