共用方式為


使用網路環接收網路數據

當架構叫用其 EvtPacketQueueAdvance 接收佇列的回呼函式時,NetAdapterCx 用戶端驅動程式會收到網路數據。 在此回呼期間,客戶端驅動程式會將接收到的片段和封包清空至 OS,表示已接收,然後將新的緩衝區發佈至硬體。

接收(Rx)發送與排放作業概觀

下列動畫說明簡單 PCI 網路適配器 (NIC) 的用戶端驅動程式如何執行接收 (Rx) 佇列的張貼和清空作業。 此範例狀況中的片段緩衝區由作業系統配置,並附加至片段環。 此範例假設硬體接收佇列與 OS 接收佇列之間的一對一關聯性。

動畫,說明PCI網路適配器接收的網環張貼和清空作業。

在此動畫中,用戶端驅動程式所擁有的封包會以淺藍色和深藍色標示,而用戶端驅動程式所擁有的片段會以黃色和橙色標示。 較淺的色彩代表驅動程式擁有之元素的 清空 子區段,而較深的色彩則代表驅動程式擁有之元素的 子區段。

依序接收數據

以下是依序接收數據的驅動程式一般順序,每個封包有一個片段。

  1. 呼叫 NetRxQueueGetRingCollection 以取得接收佇列的環形集合結構。 您可以將此內容儲存在佇列的內容空間中,以減少驅動程式的呼叫。 使用環集合來擷取接收佇列的片段環和封包環的耗盡迭代器。
  2. 藉由清空網路環,以向操作系統指出已接收的數據:
    1. 配置 UINT32 變數來追蹤片段通道的目前索引和封包通道的目前索引。 將這些變數設定為其各自網環的 BeginIndex,這是環的排水子區段的起始點。 將一個 UINT32 變數分配給片段環的排放區段結尾,並將其設置為片段環的 NextIndex
    2. 在循環中執行下列動作:
      1. 檢查硬體是否已收到片段。 如果沒有,中斷迴圈。
      2. 呼叫 NetRingGetFragmentAtIndex 以取得片段。
      3. 請根據與片段相符的硬體描述元,填寫片段的資訊,例如其 ValidLength
      4. 呼叫 netRingGetPacketAtIndex 來取得此片段的封包。
      5. 藉由將封包的 FragmentIndex 設定為片段環中的目前索引,並適當地設定片段數目,將片段系結至封包(在此範例中,它會設定為 1)。
      6. 選擇性地填入任何其他封包資訊,例如檢查碼。
      7. 呼叫 NetRingIncrementIndex以推進片段索引。
      8. 利用 呼叫 NetRingIncrementIndex來推進封包索引。
    3. 將片段通道的 BeginIndex 更新為目前的片段索引變數,並將封包通道的 BeginIndex 更新為目前的封包索引,以完成指出已接收封包及其片段至 OS。
  3. 將片段緩衝區提交到硬體以進行下一個接收:
    1. 將目前的片段索引設定為片段環的 NextIndex,這是環中後續小節的開頭。 將片段結束索引設定為片段環的 EndIndex
    2. 在循環中執行下列動作:
      1. 將片段的信息張貼至相符的硬體描述元。
      2. 呼叫 NetRingIncrementIndex以推進片段索引。
    3. 將片段環的 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 接收佇列,您必須同步管理對接收緩衝區的存取。 這樣做的範圍不包含在本主題中,且取決於您的硬體設計。