使用網路通道傳送網路資料
當架構針對傳輸佇列叫用 其 EvtPacketQueueAdvance 回 呼函式時,NetAdapterCx 用戶端驅動程式會傳送網路資料。 在此回呼期間,用戶端驅動程式會將緩衝區從佇列的片段通道張貼到硬體,然後清空已完成的封包和片段回到 OS。
傳輸 (Tx) 後和清空作業概觀
下列動畫說明簡單 PCI 網路介面卡的用戶端驅動程式 (NIC) 如何針對傳輸 (Tx) 佇列執行後續和清空作業。
在此動畫中,用戶端驅動程式所擁有的封包會以淺藍色和深藍色反白顯示,而用戶端驅動程式所擁有的片段會以黃色和橙色醒目提示。 較淺的色彩代表驅動程式擁有之元素的 清空 子區段,而較深的色彩則代表驅動程式擁有之元素的 後 置子區段。
依序傳送資料
以下是驅動程式的一般後置和清空順序,其裝置會依序傳輸資料,例如簡單的 PCI NIC。
- 呼叫 NetTxQueueGetRingCollection 以擷取傳輸佇列的通道集合結構。 您可以將此值儲存在佇列的內容空間中,以減少驅動程式的呼叫。 使用通道集合來擷取傳輸佇列的封包通道。
- 將資料張貼到硬體:
- 為封包索引配置 UINT32 變數,並將其設定為封包通道的 NextIndex,這是通道後小節的開頭。
- 在 迴圈中執行下列動作:
- 使用封包索引呼叫 NetRingGetPacketAtIndex 來取得封包。
- 檢查是否應該忽略此封包。 如果應該忽略它,請跳至此迴圈的步驟 6。 如果沒有,請繼續。
- 取得此封包的片段。 從通道集合擷取傳輸佇列的片段通道、從封包的FragmentIndex成員擷取封包片段的開頭,然後使用封包的FragmentCount呼叫NetRingIncrementIndex來擷取封包片段的結尾。
- 在迴圈中執行下列動作:
- 呼叫 NetRingGetFragmentAtIndex 以取得片段。
- 將 NET_FRAGMENT 描述元轉譯成相關聯的硬體片段描述元。
- 藉由呼叫 NetRingIncrementIndex來前進片段索引。
- 更新片段通道的 Next 索引,以符合片段反覆運算器的目前 Index,這表示張貼到硬體已完成。
- 藉由呼叫 NetRingIncrementIndex來前進封包索引。
- 將封包通道的 NextIndex 更新為封包索引,以完成將封包張貼至硬體。
- 清空已完成的封包會傳送至 OS:
- 將封包索引設定為封包通道的 BeginIndex,這是環形清空子區段的開頭。
- 在 迴圈中執行下列動作:
- 使用封包索引呼叫 NetRingGetPacketAtIndex 來取得封包。
- 檢查封包是否已完成傳輸。 如果沒有,請中斷迴圈。
- 藉由呼叫 NetRingIncrementIndex來前進封包索引。
- 將封包通道的 BeginIndex 更新為封包索引,以完成將封包張貼至硬體。
這些步驟在程式碼中看起來可能像這樣。 請注意,硬體特定詳細資料,例如如何將描述項張貼至硬體或排清成功的後續交易,以保持清楚起見。
void
MyEvtTxQueueAdvance(
NETPACKETQUEUE TxQueue
)
{
//
// Retrieve the transmit queue's ring collection and packet ring.
// This example stores the Tx queue's ring collection in its queue context space.
//
PMY_TX_QUEUE_CONTEXT txQueueContext = MyGetTxQueueContext(TxQueue);
NET_RING_COLLECTION const * ringCollection = txQueueContext->RingCollection;
NET_RING * packetRing = ringCollection->Rings[NET_RING_TYPE_PACKET];
UINT32 currentPacketIndex = 0;
//
// Post data to hardware
//
currentPacketIndex = packetRing->NextIndex;
while(currentPacketIndex != packetRing->EndIndex)
{
NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
if(!packet->Ignore)
{
NET_RING * fragmentRing = ringCollection->Rings[NET_RING_TYPE_FRAGMENT];
UINT32 currentFragmentIndex = packet->FragmentIndex;
UINT32 fragmentEndIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex + packet->FragmentCount - 1);
for(txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors = 0;
currentFragmentIndex != fragmentEndIndex;
txQueueContext->PacketTransmitControlBlocks[packetIndex]->numTxDescriptors++)
{
NET_FRAGMENT * fragment = NetRingGetFragmentAtIndex(fragmentRing, currentFragmentIndex);
// Post fragment descriptor to hardware
...
//
currentFragmentIndex = NetRingIncrementIndex(fragmentRing, currentFragmentIndex);
}
//
// Update the fragment ring's Next index to indicate that posting is complete and prepare for draining
//
fragmentRing->NextIndex = currentFragmentIndex;
}
currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
}
packetRing->NextIndex = currentPacketIndex;
//
// Drain packets if completed
//
currentPacketIndex = packetRing->BeginIndex;
while(currentPacketIndex != packetRing->NextIndex)
{
NET_PACKET * packet = NetRingGetPacketAtIndex(packetRing, currentPacketIndex);
// Test packet for transmit completion by checking hardware ownership flags in the packet's last fragment
// Break if transmit is not complete
...
//
currentPacketIndex = NetRingIncrementIndex(packetRing, currentPacketIndex);
}
packetRing->BeginIndex = currentPacketIndex;
}
依序傳送資料
對於裝置可能依序完成傳輸的驅動程式,順序與順序裝置的主要差異在於配置傳輸緩衝區的人員,以及驅動程式如何處理傳輸完成的測試。 針對具有支援 DMA 之 PCI NIC 的依序傳輸,OS 通常會配置、連結,最後擁有片段緩衝區。 然後,用戶端驅動程式可以在 EvtPacketQueueAdvance期間測試每個片段的對應硬體擁有權旗標。
相較于此模型,請考慮一般的 USB 型 NIC。 在此情況下,USB 堆疊擁有用於傳輸的記憶體緩衝區,而這些緩衝區可能位於系統記憶體的其他地方。 USB 堆疊表示用戶端驅動程式的完成順序不足,因此用戶端驅動程式必須在完成回呼常式期間個別記錄封包的完成狀態。 若要這樣做,用戶端驅動程式可以使用封包的 [臨時 ] 欄位,也可以使用其他方法,例如將資訊儲存在其佇列內容空間中。 然後,在 對 EvtPacketQueueAdvance的呼叫中,用戶端驅動程式會檢查此資訊以進行封包完成測試。