當架構叫用其 EvtPacketQueueAdvance 接收佇列的回呼函式時,NetAdapterCx 用戶端驅動程式會收到網路數據。 在此回呼期間,客戶端驅動程式會將接收到的片段和封包清空至 OS,表示已接收,然後將新的緩衝區發佈至硬體。
接收(Rx)發送與排放作業概觀
下列動畫說明簡單 PCI 網路適配器 (NIC) 的用戶端驅動程式如何執行接收 (Rx) 佇列的張貼和清空作業。 此範例狀況中的片段緩衝區由作業系統配置,並附加至片段環。 此範例假設硬體接收佇列與 OS 接收佇列之間的一對一關聯性。
在此動畫中,用戶端驅動程式所擁有的封包會以淺藍色和深藍色標示,而用戶端驅動程式所擁有的片段會以黃色和橙色標示。 較淺的色彩代表驅動程式擁有之元素的 清空 子區段,而較深的色彩則代表驅動程式擁有之元素的 后 子區段。
依序接收數據
以下是依序接收數據的驅動程式一般順序,每個封包有一個片段。
- 呼叫 NetRxQueueGetRingCollection 以取得接收佇列的環形集合結構。 您可以將此內容儲存在佇列的內容空間中,以減少驅動程式的呼叫。 使用環集合來擷取接收佇列的片段環和封包環的耗盡迭代器。
- 藉由清空網路環,以向操作系統指出已接收的數據:
- 配置 UINT32 變數來追蹤片段通道的目前索引和封包通道的目前索引。 將這些變數設定為其各自網環的 BeginIndex,這是環的排水子區段的起始點。 將一個 UINT32 變數分配給片段環的排放區段結尾,並將其設置為片段環的 NextIndex。
- 在循環中執行下列動作:
- 檢查硬體是否已收到片段。 如果沒有,中斷迴圈。
- 呼叫 NetRingGetFragmentAtIndex 以取得片段。
- 請根據與片段相符的硬體描述元,填寫片段的資訊,例如其 ValidLength。
- 呼叫 netRingGetPacketAtIndex 來取得此片段的封包。
- 藉由將封包的 FragmentIndex 設定為片段環中的目前索引,並適當地設定片段數目,將片段系結至封包(在此範例中,它會設定為 1)。
- 選擇性地填入任何其他封包資訊,例如檢查碼。
- 呼叫 NetRingIncrementIndex以推進片段索引。
- 利用 呼叫 NetRingIncrementIndex來推進封包索引。
- 將片段通道的 BeginIndex 更新為目前的片段索引變數,並將封包通道的 BeginIndex 更新為目前的封包索引,以完成指出已接收封包及其片段至 OS。
- 將片段緩衝區提交到硬體以進行下一個接收:
- 將目前的片段索引設定為片段環的 NextIndex,這是環中後續小節的開頭。 將片段結束索引設定為片段環的 EndIndex。
- 在循環中執行下列動作:
- 將片段的信息張貼至相符的硬體描述元。
- 呼叫 NetRingIncrementIndex以推進片段索引。
- 將片段環的 NextIndex 更新為目前的片段索引變數,以完成將片段提交至硬體。
這些步驟在程式代碼中看起來可能像這樣:
void
MyEvtRxQueueAdvance(
NETPACKETQUEUE RxQueue
)
{
//
// Retrieve the receive queue's ring collection and net rings.
// This example stores the Rx queue's ring collection in its queue context space.
//
PMY_RX_QUEUE_CONTEXT rxQueueContext = MyGetRxQueueContext(RxQueue);
NET_RING_COLLECTION const * ringCollection = rxQueueContext->RingCollection;
NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
UINT32 currentPacketIndex = 0;
UINT32 currentFragmentIndex = 0;
UINT32 fragmentEndIndex = 0;
//
// Indicate receives by draining the rings
//
currentPacketIndex = packetRing->BeginIndex;
currentFragmentIndex = fragmentRing->BeginIndex;
fragmentEndIndex = fragmentRing->NextIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Test for fragment reception. Break if fragment has not been received.
...
//
NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);
fragment->ValidLength = ... ;
NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
packet->FragmentIndex = currentFragmentIndex;
packet->FragmentCount = 1;
if(rxQueueContext->IsChecksumExtensionEnabled)
{
// Fill in checksum info
...
//
}
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
}
fragmentRing->BeginIndex = currentFragmentIndex;
packetRing->BeginIndex = currentPacketIndex;
//
// Post fragment buffers to hardware
//
currentFragmentIndex = fragmentRing->NextIndex;
fragmentEndIndex = fragmentRing->EndIndex;
while(currentFragmentIndex != fragmentEndIndex)
{
// Post fragment information to hardware descriptor
...
//
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
}
fragmentRing->NextIndex = currentFragmentIndex;
}
接收到數據的順序錯亂
與 Tx 佇列不同,通常情況下,如果客戶端驅動程式為每個硬體接收佇列配置一個 OS 接收佇列,則數據不會無序接收。 不論客戶端驅動程式的 NIC 類型為何,都是如此。 無論裝置是以PCI為基礎,且OS會配置並擁有接收緩衝區,或裝置是否以USB為基礎,且USB堆疊擁有接收緩衝區,客戶端驅動程式會針對收到的每個片段初始化封包,並將它指示給OS。 在此情況下,順序並不重要。
如果您的硬體支援每個硬體接收佇列包含多個 OS 接收佇列,您必須同步管理對接收緩衝區的存取。 這樣做的範圍不包含在本主題中,且取決於您的硬體設計。