2016年6月1日 星期三

NDIS Protocol Drivers - II

由於篇幅關係,這裡介紹"Writing NDIS Protocol Drivers"

Initializing a Protocol Driver

系統在載入驅動以後呼叫驅動的 DriverEntry 例程。協議驅動程式載入系統服務。協議驅動可以在任何時間點載入,不論是在miniport驅動載入前,載入後或是載入期間。協議驅動配置驅動所需要的資源以及在 DriverEntry 註冊 ProtocolXxx 函式。協議驅動程式呼叫 NDIS 的「NdisRegisterProtocolDriver註冊 ProtocolXxx 函式。協議驅動程式提供如下:

NDIS 協議驅動提供以下的 ProtocolXxx 函式,用於操作網路數據的收發:
要允許一個協議驅動配置可選的服務,NDIS 呼叫ProtocolSetOptions更多有關可選服務的訊息請看Configuring Optional Protocol Driver Services要對NDIS進行反註冊,協議驅動在Unload例程中呼叫NdisDeregisterProtocolDriver 。要在協議驅動反安裝前執行清除的操作,協議驅動可以註冊「 ProtocolUninstall  函式。可以選擇是否註冊 ProtocolUninstall 函式。例如,中間層驅動的下界協議可能要求一個ProtocolUninstall 函式。中間層驅動可以在NDIS呼叫他的MiniportDriverUnload 」函式前,在ProtocolUninstall 內釋放他的協議資源。


Protocol Binding States and Operations

NDIS協議驅動必須支援以下的綁定狀態:

1. Unbound
  • Unbound 為綁定的初始狀態。在此狀態下,協議驅動會等待 NDIS 呼叫 ProtocolBindAdapterEx」。
2. Opening
  • Opening 狀態,協議驅動程式配置資源並且試圖去打開 adapter
3. Running
  • Running 狀態,一個協議驅動程式處理收發的工作。
4. Closing
  • Closing 狀態,協議驅動程式關閉綁定的adapter並且釋放綁定的資源。
5. Pausing
  • Pausing 狀態,一個協議驅動程式完成任何的操作,並且要求停止收發的工作。
6. Paused
  • Paused 狀態,協議驅動程式不會執行收發的工作。
7. Restarting
  • Restarting 狀態,一個協議驅動程式完成任何的操作,並且要求重新收發的操作。

在下表中,標頭顯示綁定的狀態,第一行列出了事件。表中剩餘的部分指示在一個狀態下發生事件以後的下一個狀態。空白部分表示無效的 event/state 組合。














Note 上表列出了 NDIS 協議綁定主要的事件。其他事件將被添加到該表中


主要綁定事件定義如下:

1.   ProtocolBindAdapterEx
2.   Bind failed
  • 如果協議驅動綁定adapter失敗,則回傳Unbound狀態。
3.   Bind is complete
  • 如果成功開adapter,則進入Paused狀態。驅動完成綁定操作。
4.   ProtocolUnbindAdapterEx
5.   Unbind is complete
  • 在驅動動完成 unbind 操作以後,則進入 Unbound 狀態。
6.   PnP pause
  •  NDIS 發送網路隨插即用(PnP)事件給協定驅動綁定進入 Pausing 狀態。更多訊息閱「Pausing a Binding
7.   Pause is complete
  • 當驅動已經完成所有的停止收發操作,則完成暫停操作並且進去Paused 狀態。
Note 驅動在暫停操作完成之前,必須等待所有發送請求都完成。

8.   PnP Restart
  • 當 NDIS 發送 PnP restart事件通知協議驅動,則進入 Restarting 狀態。更多訊息,閱「Restarting a Binding」。
9.   Restart is complete
  • 當驅動準備好去處理發送的工作,則完成 restart 操作並且進入 Running 狀態。
10.   Restart failed
  • 如果NDIS發送網路PnP restart事件通知協議驅動,並且restart失敗,則進入Paused狀態。
11.   Send and Receive Operations
12.   OID Requests
  • 協議驅動可以OID請求來設定或尋下層驅動的訊息。協議驅動可以OID請求在所有的狀態下,除了Unbound以及Opening

補充: 以下為我整理的狀態機圖。










每當有一個下層 adapter 變的可用,NDIS 可以調用協議驅動註冊的「ProtocolBindAdapterEx」來開一個下層 adapter 綁定。當 NDIS 調用ProtocolBindAdapterEx以後,則進入Opening綁定狀態。在Opening狀態下,協議驅動可以配置綁定的資源以及開啟一個 adpater

NDIS 傳遞給 ProtocolBindAdapterEx 綁定操作的NDIS上下文(NDIS context)以及一個指向NDIS_BIND_PARAMETERS結構的指標。這個結構包含有關adapter的資訊,如下:
  • Adapter的名稱
  • 此綁定參數的註冊表位置。
  • Adapter的實體裝置物件(physical device object)

要開一個adapter,協議驅動調用「NdisOpenAdapterEx」。協議驅動對NdisOpenAdapterEx傳遞如下:
  • 使用NdisRegisterProtocolDriverNDIS 會回傳NdisProtocolHandle 
  • 協議驅動綁定的上下文(context)
  • 指向「NDIS_OPEN_PARAMETERS」結構的指標。

NDIS_OPEN_PARAMETERS 」包含一些資訊,例如NdisOpenAdapterEx  要開 adapter 名稱。協議驅動所支援的媒體型態(medium type)陣列,以及可選擇的,驅動在這個綁定的 adapter 所接收到的偵型態(frame types)陣列。

如果一個協議驅動從ProtocolBindAdapterEx返回NDIS_STATUS_PENDING,則它必須在最終的狀態下調用「NdisCompleteBindAdapterEx 來完成綁定請求。如果NDIS   NdisOpenAdapterEx 返回 NDIS_STATUS_PENDING,則NDIS在稍後最終狀態下調用協議驅動的「ProtocolOpenAdapterCompleteEx ,當開的請求已經完成。在驅動開adapter綁定成功以後,綁定會進入 Paused 狀態。

協議驅動調用NdisCloseAdapterEx 來關閉adapter。驅動可以從ProtocolBindAdapterEx或是由ProtocolUnbindAdapterEx調用 NdisCloseAdapterEx。如果在開  adapter 之後,以及正在完成綁定請求之前,ProtocolBindAdapterEx 遇到了錯誤並且必須關閉adapter的綁定,它也可以調用 NdisCloseAdapterEx 更多訊息 Unbinding from an Adapter


Unbinding from an Adapter

NDIS 調用協議驅動的ProtocolUnbindAdapterEx 來解除來自於底層 adapter 的綁定。相較於ProtocolBindAdapterExNDIS 調用ProtocolUnbindAdapterEx 來關閉 adapter 的綁定以及釋放驅動對綁定所配置的資源。

ProtocolUnbindAdapterEx函式中,協議驅動程式調用 NdisCloseAdapterEx 來關閉下層adapter的綁定。協議驅動傳遞 NdisOpenAdapterEx 提供的 NdisBindingHandle  參數。這個句柄標識綁定,NDIS 應該要關閉。



協議驅動程式在ProtocolBindAdapterEx 函式或是在ProtocolUnbindAdapterEx 函式必須關閉一個 adapter

如果有一個協議驅動必須發起一個關閉綁定的操作,則驅動程式可以調用NdisUnbindAdapterNdisUnbindAdapter 會安排一個工作項目(work item),其結果為在 NDIS 調用ProtocolUnbindAdapterEx。這個工作項目可以在調用 NdisUnbindAdapter 回傳前運行。因此,驅動設計者必須假設在 NdisUnbindAdapter  回傳以後綁定句柄為無效的。


如果有一個協議驅動從ProtocolUnbindAdapterEx回傳了NDIS_STATUS_PENDIN,則它必須在最終狀態以後調用NdisCompleteUnbindAdapterEx 來完成綁定要求。

如果在NDIS內,NdisCloseAdapterEx函式回傳了回傳值NDIS_STATUS_PENDING,則 NDIS 在稍後會調用協議驅動的ProtocolCloseAdapterCompleteEx 。如果綁定狀態在Paused狀態,NDIS 可以調用 ProtocolUnbindAdapterEx 。在所有的解除綁定操作完成以後,綁定狀態進入Unbound狀態。
NDIS 可能會受到隨插即用(PnP)介入而暫停一個綁定並且停止數據流,舉例來說,在驅動堆疊中增加或是移除一個過濾模組,亦或增加一個新的綁定。有關如何變動一個運行中的驅動堆疊可以參考Modifying a Running Driver Stack

NDIS 從 Paused 階段開始一個綁定。綁定在綁定操作或是暫停操作完成以後進入Paused狀態。下列主題提供更多有關綁定的開始與暫停:

Paused下重一個綁定,NDIS 發給協議驅動一個網路隨插即用(PnP)事件通知。在協議驅動收到重通知以後,則受影響的綁定進入 Restarting 狀態。

當發送一個重通知,NDIS會調用協議驅動的「ProtocolNetPnPEvent」 
NET_PNP_EVENT_NOTIFICATION」結構為NDIS傳遞給ProtocolNetPnPEvent 的參數,當成員NetEvent NetEventRestart,則成員Buffer為包含指向「NDIS_PROTOCOL_RESTART_PARAMETERS」結構的指標。NDISNDIS_PROTOCOL_RESTART_PARAMETERS裡的RestartAttributes成員提供一個指向「NDIS_RESTART_ATTRIBUTES」結構的指標。

Note 當綁定在暫停狀態下,NDIS可以重新設定驅動堆疊。新的堆疊配置可以對底層配置器支援不同的功能。這些新功能可以影響協議驅動在綁定下如何通信。


協議驅動應利用NDIS_PROTOCOL_RESTART_PARAMETERS裡的資訊來避免不必要的OID請求。Restaring狀態下,協議驅動可以:
  • 使用OID請求來詢問驅動堆疊。舉例來說,驅動可以使用OID_GEN_RECEIVE_SCALE_CAPABILITIES明有關接收端的縮放支持。
  • 可以重新配置NET_BUFFER以及NET_BUFFER_LIST
  • 列舉底層的過濾模組
  • 利用OID請求來展示新的配置器功能。在驅動準備好重新收發工作,綁定進入Running狀態。

Pausing a binding
NDIS向協議驅動發送網路隨插即用(PnP)暫停事件通知以後,綁定進入Pausing狀態。

在通知協議驅動PnP暫停事件,NDIS調用ProtocolNetPnPEvent NET_PNP_EVENT_NOTIFICATION結構中的NetPnPEvent成員指向了NetEvent成員,並且NetEventNetEventPauseBuffer成員包含了一個 NDIS_PROTOCOL_PAUSE_PARAMETERS 的結構。

Pausing狀態下, 協議驅動:
  • 不應該發起任何的發送請求。
  • 必須等待外部的發送請求都完成。在NDIS調用ProtocolSendNetBufferListsComplete完成所有的外部發送請求前,暫停工作都不算完成。
  • 應該處理接收指示如常。在完成暫停工作前,下層miniport驅動等待外部接收數據返回。這保證miniport驅動在暫停以後不會有接收的工作正在進行。
  • 應該立即返回新的接收指示給NDIS。如果需要的話,驅動可以再返回前複製接收指示。

在協議驅動完成所有外部接收指示的返回則綁定進入Paused狀態,並且NDIS完成所有的外部發送要求。

Paused狀態,協議驅動:
  • 不可以建立任何的發送請求。
  • 應該立即返回接收指示給 NDIS。如果需要的話,驅動可以再返回前複製接收指示。