共用方式為


使用 net 通道接收網路資料

當架構針對接收佇列叫用 其 EvtPacketQueueAdvance 回呼函式時,NetAdapterCx 用戶端驅動程式會收到網路資料。 在此回呼期間,用戶端驅動程式會清空接收的片段和封包至 OS,然後將新的緩衝區張貼至硬體,以指出接收。

接收 (Rx) 後清空作業概觀

下列動畫說明簡單 PCI 網路介面卡的用戶端驅動程式 (NIC) 如何執行接收 (Rx) 佇列的後置和清空作業。 此範例案例中的片段緩衝區會由 OS 配置並附加至片段通道。 此範例假設硬體接收佇列與 OS 接收佇列之間的一對一關聯性。

說明 PCI 網路介面卡接收之網路環形張貼和清空作業的動畫。

在此動畫中,用戶端驅動程式所擁有的封包會以淺藍色和深藍色反白顯示,而用戶端驅動程式所擁有的片段會以黃色和橙色醒目提示。 較淺的色彩代表驅動程式擁有之專案的 清空 子區段,而較深的色彩則代表驅動程式擁有之專案的 置子區段。

依序接收資料

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

  1. 呼叫 NetRxQueueGetRingCollection 以擷取接收佇列的通道集合結構。 您可以將此值儲存在佇列的內容空間中,以減少驅動程式的呼叫。 使用通道集合來擷取接收佇列片段通道和封包通道的清空反覆運算器。
  2. 清空網路通道,以向 OS 指出已接收的資料:
    1. 配置 UINT32 變數來追蹤片段通道的目前索引和封包通道的目前索引。 將這些變數設定為其各自 net 環形的 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 接收佇列,您必須同步存取接收緩衝區。 這樣做的範圍不在本主題之外,而且取決於您的硬體設計。