NDIS_MINIPORT_CHARACTERISTICS
這個資料結構只可以用在 NDIS 5.x 之前,之後已經被 NDIS 6.0 禁用了,不過 NDIS 6.0 是換湯不換藥啦,所以瞭解這個並無不妥。typedef { struct _NDIS_MINIPORT_CHARACTERISTICSUCHAR MajorNdisVersion;UCHAR MinorNdisVersion;UINT Reserved;W_CHECK_FOR_HANG_HANDLER CheckForHangHandler;W_DISABLE_INTERRUPT_HANDLER DisableInterruptHandler;W_ENABLE_INTERRUPT_HANDLER EnableInterruptHandler;W_HALT_HANDLER HaltHandler ;W_HANDLE_INTERRUPT_HANDLER HandleInterruptHandler ;W_INITIALIZE_HANDLER InitializeHandler ;W_ISR_HANDLER ISRHandler ;W_QUERY_INFORMATION_HANDLER QueryInformationHandler ;W_RECONFIGURE_HANDLER ReconfigureHandler;W_RESET_HANDLER ResetHandler ;W_SEND_HANDLER SendHandler ;W_SET_INFORMATION_HANDLER SetInformationHandler ;W_TRANSFER_DATA_HANDLER TransferDataHandler;// // Version used is V4.0 or V5.0 // with following members // W_RETURN_PACKET_HANDLER ReturnPacketHandler;W_SEND_PACKETS_HANDLER SendPacketsHandler;W_ALLOCATE_COMPLETE_HANDLER AllocateCompleteHandler;// // Version used is V5.0 with the following members // W_CO_CREATE_VC_HANDLER CoCreateVcHandler;W_CO_DELETE_VC_HANDLER CoDeleteVcHandler;W_CO_ACTIVATE_VC_HANDLER CoActivateVcHandler;W_CO_DEACTIVATE_VC_HANDLER CoDeactivateVcHandler;W_CO_SEND_PACKETS_HANDLER CoSendPacketsHandler;W_CO_REQUEST_HANDLER CoRequestHandler;// // Version used is V5.1 with the following members // W_CANCEL_SEND_PACKETS_HANDLER CancelSendPacketsHandler;W_PNP_EVENT_NOTIFY_HANDLER PnPEventNotifyHandler;W_MINIPORT_SHUTDOWN_HANDLER AdapterShutdownHandler; }NDIS_MINIPORT_CHARACTERISTICS , *PNDIS_MINIPORT_CHARACTERISTICS ;
基本上, W_CO_XXX 不用理會,直接給
需要實做的大概只有紅色的函式,如果 NIC 不會產生 interrupt 的話。ISR 相關的東西在下一集說明。
CheckForHangHandler
系統預設每兩秒會呼叫這個函式,如果回傳 TRUE,表示 NIC 已經不正常了,接著系統會呼叫 MiniportReset。 你可以在呼叫 NdisMSetAttributesEx() 的時候,修改系統呼叫這個函式的間隔。BOOLEAN MiniportCheckForHang ( INNDIS_HANDLE MiniportAdapterContext );
MiniportCheckForHang runs at IRQL = DISPATCH_LEVEL.
DisableInterruptHandler
系統呼叫這個函式,命令你的 Driver 關掉 NIC 的 interrutp。如果 NIC 支援動態開關 interrupt 但不共享 IRQ的話,Driver 可以提供這個函式。這個函式只是讓系統有機會開關你的 NIC 的 interrupt,換句話說,你也可以不提供。VOID MiniportDisableInterrupt ( INNDIS_HANDLE MiniportAdapterContext );
DIRQL
EnableInterruptHandler
系統呼叫這個函式,命令你的 Driver 打開 NIC 的 interrupt。如果 NIC 支援動態開關 interrupt 但不共享 IRQ的話,Driver 可以提供這個函式。這個函式只是讓系統有機會開關你的 NIC 的 interrupt,換句話說,你也可以不提供。VOID MiniportEnableInterrupt ( INNDIS_HANDLE MiniportAdapterContext );
DIRQL
HaltHandler
NDIS 會呼叫 MiniportHalt 的時機:- NDIS 無法設定 Multicast Address 或 MAC_OPTION。
- Unbind 所有 bind 到 Miniport Driver 的 protocol drivers 之後。
- 在 Driver Unload 的時候。
- 系統關機的時候。
Driver 如果有收到來自 NIC 的封包,並且已經往上層回報,但是 NDIS 還沒呼叫 MinoportReturnPacket() 的話, MiniportHalt 必須等到所有封包的 MinoportReturnPacket() 都已經被呼叫過,才可以返回。
VOID MiniportHalt (INNDIS_HANDLE MiniportAdapterContext);
MiniportHalt runs at IRQL = PASSIVE_LEVEL.
HandleInterruptHandler
DSR(Deferred Service Routine)。如果 NIC 共享 IRQ 的話,只有當 MiniportISR() 把 QueueMiniportHandleInterrupt 設為 TRUE 的時候,系統之後才會呼叫 MiniportHandleInterrupt()。 通常在這裡面會做的事情有:- 1. 取得 NIC 的 Interrupt Event。
- 2. 根據 NIC 的 Interrupt Event 採取對應的動作。大部分的動作都是去取得 NIC 上的封包。
- 3. 清掉 NIC 的 Interrupt。
- 4. Enable Interrupt Mask。因為這時候 Interrupt Mask 通常是關掉的。
VOID MiniportHandleInterrupt ( INNDIS_HANDLE MiniportAdapterContext );
MiniportHandleInterrupt runs at IRQL = DISPATCH_LEVEL.
InitializeHandler
MiniportInitialize() 是 Driver 的 DriverEntry() 離開之後會被系統呼叫的函式。它要做的事情很多,包括檢查 Medium Type 是不是符合(通常是 NdisMedium802_3)、初始化所有的資源(如 Timer 、SpinLock、Memory...等等)、讀取 Registry、初始化 NIC。MiniportInitialize() 的傳入值 MiniportAdapterHandle 跟 WrapperConfigurationContext,要把它們記起來,以後常常會用到。 如果 NIC 是屬於 PCI 之類的介面的話,Driver 需要呼叫 NdisMQueryAdapterResources(),取得 NIC 的硬體資源。一般的硬體資源不外乎 Interrupt、DMA、Port(IoAddress)。
VOID NdisMQueryAdapterResources ( OUTPNDIS_STATUS Status, INNDIS_HANDLE WrapperConfigurationContext, OUTPNDIS_RESOURCE_LIST ResourceList, INOUT PUINT BufferSize );
typedefstruct _CM_PARTIAL_RESOURCE_LIST {USHORT Version;USHORT Revision;ULONG Count;CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; }CM_PARTIAL_RESOURCE_LIST , *PCM_PARTIAL_RESOURCE_LIST ;
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {UCHAR Type;UCHAR ShareDisposition;USHORT Flags;union {struct {PHYSICAL_ADDRESS Start;ULONG Length; } Generic;struct {PHYSICAL_ADDRESS Start;ULONG Length; } Port;struct {ULONG Level;ULONG Vector;KAFFINITY Affinity; } Interrupt;struct {PHYSICAL_ADDRESS Start;ULONG Length; } Memory;struct {ULONG Channel; ;ULONG Reserved1; }Dma ;struct {ULONG Data[3]; }DevicePrivate ;struct {ULONG Start;ULONG Length;ULONG Reserved; }BusNumber ;struct {ULONG DataSize;ULONG Reserved1;ULONG Reserved2; }DeviceSpecificData ; }u ; }CM_PARTIAL_RESOURCE_DESCRIPTOR , *PCM_PARTIAL_RESOURCE_DESCRIPTOR ;
Driver 需要做的事情,就是把這些硬體資訊記起來,以後會用到。 如果 NIC 支援 Port 資源的話,Driver 需要呼叫 NdisMRegisterIoPortRange(),之後才可以使用NdisRawReadPortXxx() 以及 NdisRawWritePortXxx()。
NDIS_STATUS NdisMRegisterIoPortRange ( OUTPVOID *PortOffset, INNDIS_HANDLE MiniportAdapterHandle, INUINT InitialPort, INUINT NumberOfPorts );
PCMCIA 卡可能會有所謂的 Atrribute Memory,Driver 可以使用 NdisReadPcmciaAttributeMemory() 來讀取:
ULONG NdisReadPcmciaAttributeMemory ( INNDIS_HANDLE NdisAdapterHandle, INULONG Offset, INPVOID Buffer, INULONG Length );
接下來便是讀取 Registry。首先 Driver 先呼叫 NdisOpenConfiguration(),取得 Configuration Handle:
VOID NdisOpenConfiguration ( OUTPNDIS_STATUS Status, OUTPNDIS_HANDLE ConfigurationHandle, INNDIS_HANDLE WrapperConfigurationContext );
接下來 Driver 便可以快快樂樂用這個 Configuration Handle 來讀取或寫入 Registry:
VOID NdisReadConfiguration ( OUTPNDIS_STATUS Status, OUTPNDIS_CONFIGURATION_PARAMETER *ParameterValue, INNDIS_HANDLE ConfigurationHandle, INPNDIS_STRING Keyword, INNDIS_PARAMETER_TYPE ParameterType );
VOID NdisWriteConfiguration ( OUTPNDIS_STATUS Status, INNDIS_HANDLE ConfigurationHandle, INPNDIS_STRING Keyword, INPNDIS_CONFIGURATION_PARAMETER ParameterValue );
最後,再把這個 Configuration Handle 還給系統:
VOID NdisCloseConfiguration ( INNDIS_HANDLE ConfigurationHandle );
在 MiniportInitialize() 裡面,Driver 一定要呼叫 NdisMSetAttributesEx()。其函式原型如下:
VOID NdisMSetAttributesEx ( INNDIS_HANDLE MiniportAdapterHandle, INNDIS_HANDLE MiniportAdapterContext, INUINT CheckForHangTimeInSeconds OPTIONAL, INULONG AttributeFlags, INNDIS_INTERFACE_TYPE AdapterType );
MiniportAdapterContext 很重要。要知道在 NDIS 或是 WDM 裡面,Driver 永遠都只有被呼叫的份,那表示 Driver 得維護自己的狀態才行,不然系統隨便呼叫 Driver 的函式,Driver 自己又不知道自己的狀態,那不是很容易就 BSOD (Blue Screen Of Death)了嗎。所以 MiniportAdapterContext 就是讓 Driver 提供一個地方,讓系統每次呼叫 Driver 的函式時,都會把這個地方的位址傳給 Driver,如此 Driver 就可以記住跟知道它需要的資訊。
CheckForHangTimeInSeconds 是告訴系統,多久來呼叫 Driver 的 MiniportCheckForHang()。
AttributeFlags 很重要。NDIS_ATTRIBUTE_BUS_MASTER、NDIS_ATTRIBUTE_DESERIALIZE、 NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND、NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 值得注意。
- NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 表示這隻 Driver 可以處理 NIC 突然被移除,並且不需要通知使用者。這樣的 Driver 必須實做 MiniportPnPEventNotify ()。
- NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND 表示系統轉移到低耗電的狀態(Sleeping)之前,NDIS 並不需要呼叫這隻 Driver 的 MiniportHalt()。
typedefenum _NDIS_INTERFACE_TYPE { NdisInterfaceInternal = Internal, NdisInterfaceIsa = Isa, NdisInterfaceEisa = Eisa, NdisInterfaceMca = MicroChannel, NdisInterfaceTurboChannel = TurboChannel, NdisInterfacePci = PCIBus, NdisInterfacePcMcia = PCMCIABus }NDIS_INTERFACE_TYPE , *PNDIS_INTERFACE_TYPE ;
只有 ISA、EISA、PCI、PCMCIA,那 USB 跟 SDIO 勒?請使用
呼叫完 NdisMSetAttributesEx() 之後,如果 Driver 需要處理 Interrupt,請呼叫 NdisMRegisterInterrupt():
NDIS_STATUS NdisMRegisterInterrupt ( OUTPNDIS_MINIPORT_INTERRUPT Interrupt, INNDIS_HANDLE MiniportAdapterHandle, INUINT InterruptVector, INUINT InterruptLevel, INBOOLEAN RequestIsr, INBOOLEAN SharedInterrupt, INNDIS_INTERRUPT_MODE InterruptMode );
最後,在 NDIS5.0 之前的版本,Driver 還必須 呼叫 NdisMRegisterAdapterShutdownHandler() ,註冊系統關機時要呼叫的函式。
VOID NdisMRegisterAdapterShutdownHandler ( INNDIS_HANDLE MiniportHandle, INPVOID ShutdownContext, INADAPTER_SHUTDOWN_HANDLER ShutdownHandler );
NDIS_STATUS MiniportInitialize ( OUTPNDIS_STATUS OpenErrorStatus, OUTPUINT SelectedMediumIndex, INPNDIS_MEDIUM MediumArray, INUINT MediumArraySize, INNDIS_HANDLE MiniportAdapterHandle, INNDIS_HANDLE WrapperConfigurationContext );
MiniportInitialize runs at IRQL = PASSIVE_LEVEL.
ISRHandler
如果要系統呼叫這個函式的話,Driver 必須在 MiniportInitialize() 內呼叫 NdisMRegisterInterrupt()。VOID MiniportISR ( OUTPBOOLEAN InterruptRecognized, OUTPBOOLEAN QueueMiniportHandleInterrupt, INNDIS_HANDLE MiniportAdapterContext );
MiniportISR runs at DIRQL.
QueryInformationHandler
系統會透過 MiniportQueryInformation() 取得 NIC 的資訊,如 MAC address、TX/RX 統計數字、連線速度等等。NDIS_STATUS MiniportQueryInformation ( INNDIS_HANDLE MiniportAdapterContext, INNDIS_OID Oid, INPVOID InformationBuffer, INULONG InformationBufferLength, OUTPULONG BytesWritten, OUTPULONG BytesNeeded );
MiniportQueryInformation runs at IRQL = DISPATCH_LEVEL. ReconfigureHandler NDIS 不會呼叫 MiniportReconfigure() ,但是 Driver 可以在 MiniportInitialize() 內呼叫它。
NDIS_STATUS MiniportReconfigure ( OUTPNDIS_STATUS OpenErrorStatus, INNDIS_HANDLE MiniportAdapterContext, INNDIS_HANDLE WrapperConfigurationContext );
MiniportReconfigure runs at the same IRQL as MiniportIntialize.
ResetHandler
NDIS 會呼叫 MiniportReset 的時機:- MiniportCheckForHang() 回傳 TRUE。
- NDIS 偵測到 Pending Sending Packet(for serialized miniport drivers only)。
- NDIS 偵測到在某段時間內 Pending Sending Packet 一直無法被 complete。
Deserialized Miniport Driver 必須 Complete 任何尚未送到 NIC 的 Ndis Packets。
Miniport Driver 不需要呼叫 NdisMIndicateStatus() 或 NdisMCoIndicateStatus()。
如果 Driver 有支援 MiniportCheckForHang() 的話,系統會定期呼叫 MiniportCheckForHang(),再決定是否要呼叫 MiniportReset()。如果 Driver 不支援 MiniportCheckForHang() 的話,系統會在呼叫 MiniportQueryInformation()、MiniportSetInformation()、MiniportSendPackets()、MiniportSend() 或 MiniportWanSend 逾時之後再呼叫 MiniportReset()。
NDIS_STATUS MiniportReset ( OUTPBOOLEAN AddressingReset, INNDIS_HANDLE MiniportAdapterContext );
MiniportReset runs at IRQL = DISPATCH_LEVEL.
SendHandler
如果 Driver 不支援 MiniportSendPackets()、MiniportWanSend()、或 MiniportCoSendPackets() 的話,Driver 必須支援 SendHandler()。如果 Driver 支援 MiniportSendPackets() 跟 MiniportSend() 的話,系統將不會呼叫 MiniportSend()。如果是 Deserialized Miniport Driver 的話,Driver 通常會把 Packet 放到 software queue 中,然後回傳 NDIS_STATUS_PENDING。等到 Driver 把 software queue 內的 Packet 送到 NIC 之後,再呼叫 NdisMSendComplete(),如此才算是完成整個 TX。
NDIS_STATUS MiniportSend ( INNDIS_HANDLE MiniportAdapterContext, INPNDIS_PACKET Packet, INUINT Flags );
typedefstruct _NDIS_PACKET {NDIS_PACKET_PRIVATE Private; union {struct {UCHAR MiniportReserved[2*sizeof(PVOID)];UCHAR WrapperReserved[2*sizeof(PVOID)]; };struct {UCHAR MiniportReservedEx[3*sizeof(PVOID)];UCHAR WrapperReservedEx[sizeof(PVOID)]; };struct {UCHAR MacReserved[4*sizeof(PVOID)]; }; };ULONG_PTR Reserved[2];UCHAR ProtocolReserved[1]; }NDIS_PACKET , *PNDIS_PACKET , **PPNDIS_PACKET ;
typedefstruct _NDIS_PACKET_PRIVATE {UINT PhysicalCount; // number of physical pages in packet.UINT TotalLength; // Total amount of data in the packet.PNDIS_BUFFER Head; // first buffer in the chainPNDIS_BUFFER Tail; // last buffer in the chain// if Head is NULL the chain is empty; Tail doesn't have to be NULL also PNDIS_PACKET_POOL Pool; // so we know where to free it back toUINT Count;ULONG Flags;BOOLEAN ValidCounts;UCHAR NdisPacketFlags; // See fPACKET_xxx bits belowUSHORT NdisPacketOobOffset; }NDIS_PACKET_PRIVATE , *PNDIS_PACKET_PRIVATE ;
VOID NdisQueryPacket ( INPNDIS_PACKET _Packet, OUTPUINT _PhysicalBufferCount OPTIONAL, OUTPUINT _BufferCount OPTIONAL, OUTPNDIS_BUFFER * _FirstBuffer OPTIONAL, OUTPUINT _TotalPacketLength OPTIONAL ) { if ((_FirstBuffer) != NULL) {PNDIS_BUFFER * __FirstBuffer = (_FirstBuffer); *(__FirstBuffer) = (_Packet)->Private.Head; } if ((_TotalPacketLength != NULL) || (_BufferCount != NULL) || (_PhysicalBufferCount != NULL)) { if (!(_Packet)->Private.ValidCounts) {PNDIS_BUFFER TmpBuffer = (_Packet)->Private.Head;UINT PTotalLength = 0, PPhysicalCount = 0, PAddedCount = 0;UINT PacketLength, Offset; while (TmpBuffer != (PNDIS_BUFFER)NULL) {NdisQueryBufferOffset (TmpBuffer, &Offset, &PacketLength); PTotalLength += PacketLength; PPhysicalCount += (UINT)NDIS_BUFFER_TO_SPAN_PAGES (TmpBuffer); ++PAddedCount; TmpBuffer = TmpBuffer->Next; } (_Packet)->Private.Count = PAddedCount; (_Packet)->Private.TotalLength = PTotalLength; (_Packet)->Private.PhysicalCount = PPhysicalCount; (_Packet)->Private.ValidCounts = TRUE; } if (_PhysicalBufferCount != NULL) {PUINT __PhysicalBufferCount = (_PhysicalBufferCount); *(__PhysicalBufferCount) = (_Packet)->Private.PhysicalCount; } if (_BufferCount != NULL) {PUINT __BufferCount = (_BufferCount); *(__BufferCount) = (_Packet)->Private.Count; } if (_TotalPacketLength != NULL) {PUINT __TotalPacketLength = (_TotalPacketLength); *(__TotalPacketLength) = (_Packet)->Private.TotalLength; } } }
直接看程式碼,NDIS_PACKET 的秘密可說破解一半,同時也把 MDL 的資料結構給搞清楚。NdisQueryPacket() 會找出它所包含的所有 MDL,然後透過 NdisQueryBufferOffset() 查詢每個 MDL 的大小,接著把這些資訊紀錄在 NDIS_PACKET_PRIVATE 內。
請注意,NDIS_BUFFER 是一個 MDL(Memory Description List),也因為它是個 List,這表示它有可能是由數個記憶體區塊串起來而形成的,因此 Driver 是不可以直接存取它的。所以 Driver 必須透過 NdisQueryBufferSafe() 來取得真正的記憶體位址,並且用 NdisGetNextBuffer() 取得下個 MDL。如此便可以把整個 MDL 的內容複製到一塊連續的記憶體中,整個送給 NIC。有的 Bus Type 可能不需要這個動作,因為它們的 Bus Driver 支援 MDL 的寫入方式。
VOID NdisGetNextBuffer (INPNDIS_BUFFER CurrentBuffer, OUTPNDIS_BUFFER *NextBuffer) { *NextBuffer = CurrentBuffer->Next; }
VOID NdisQueryBufferSafe ( INPNDIS_BUFFER Buffer, OUTPVOID *VirtualAddress OPTIONAL, OUTPUINT Length, INMM_PAGE_PRIORITY Priority ) { if ((CHAR *)VirtualAddress != (CHAR *)NULL) { *(PVOID *)(_VirtualAddress) = MmGetSystemAddressForMdlSafe(Buffer, Priority); } *_Length = MmGetMdlByteCount(Buffer); }
除此之外,Driver 可以透過 NDIS_PER_PACKET_INFO_FROM_PACKET() 取得 Packet 的額外資訊。Driver 可以拿它來判斷 Packet 是否支援 802.1Q(這個我也不知道是什麼,網路的東西太多了,學不完啊)。
PVOID NDIS_PER_PACKET_INFO_FROM_PACKET( IN/OUT PNDIS_PACKET Packet, IN NDIS_PER_PACKET_INFO InfoType );
typedef enum _NDIS_PER_PACKET_INFO { TcpIpChecksumPacketInfo, IpSecPacketInfo, TcpLargeSendPacketInfo, ClassificationHandlePacketInfo, HeaderIndexInfo, // Internal NDIS use only ScatterGatherListPacketInfo, Ieee8021pPriority, OriginalPacketInfo, NdisInternalExtension1, // Internal NDIS use only NdisInternalExtension2, // Internal NDIS use only NdisInternalExtension3, // Internal NDIS use only NdisInternalPktDebug, // Internal NDIS use only MaxPerPacketInfo } NDIS_PER_PACKET_INFO, *PNDIS_PER_PACKET_INFO;
typedefstruct _NDIS_PACKET_8021Q_INFO { union {struct {UINT32 UserPriority:3;UINT32 CanonicalFormatId:1;UINT32 VlanId:12;UINT32 Reserved:16; }TagHeader ;PVOID Value; }; }NDIS_PACKET_8021Q_INFO , *PNDIS_PACKET_8021Q_INFO ;
The MiniportSend function of a serialized miniport driver runs at IRQL = DISPATCH_LEVEL.
SetInformationHandler
系統會透過 MiniportSetInformation() 設定 NIC 的資訊,如 Packet Filter、Protocol Option 等等。NDIS_STATUS MiniportSetInformation ( INNDIS_HANDLE MiniportAdapterContext, INNDIS_OID Oid, INPVOID InformationBuffer, INULONG InformationBufferLength, OUTPULONG BytesRead, OUTPULONG BytesNeeded );
MiniportSetInformation runs at IRQL = DISPATCH_LEVEL.
TransferDataHandler
NDIS_STATUS MiniportTransferData ( OUTPNDIS_PACKET Packet, OUTPUINT BytesTransferred, INNDIS_HANDLE MiniportAdapterContext, INNDIS_HANDLE MiniportReceiveContext, INUINT ByteOffset, INUINT BytesToTransfer );
ReturnPacketHandler
當 Driver 透過 NdisMIndicateReceivePacket() 或 NdisMCoIndicateReceivePacket() 告訴 NDIS 有封包由 NIC 收進來之後,因為 RX 的 Ndis Packet 是由 Driver 所配置,所以當 NDIS 把封包丟給 Application 之後,會呼叫 MinoportReturnPacket() ,讓 Driver 把這些 Ndis Packet 釋放或再利用。Driver 必須在 MinoportReturnPacket() 裡面呼叫 NdisUnchainBufferAtXxx(),然後利用 NdisFreeBuffer() 釋放掉取得的 NDIS_BUFFER。最後如果你不要重新利用這個 NDIS_PACKET 的話,再呼叫 NdisFreePacket() 把這個 Packet 釋放掉。如果 Driver 要重新利用這個 NDIS_PACKET 的話,呼叫NdisReinitializePacket()。
這樣說明似乎有點模糊,這牽涉到 NDIS_PACKET 的配置。首先,NDIS_PACKET 的定義如下:
typedefstruct _NDIS_PACKET {NDIS_PACKET_PRIVATE Private;union {struct {UCHAR MiniportReserved[2*sizeof(PVOID)];UCHAR WrapperReserved[2*sizeof(PVOID)]; };struct {UCHAR MiniportReservedEx[3*sizeof(PVOID)];UCHAR WrapperReservedEx[sizeof(PVOID)]; };struct {UCHAR MacReserved[4*sizeof(PVOID)]; }; };ULONG_PTR Reserved[2];UCHAR ProtocolReserved[1]; }NDIS_PACKET , *PNDIS_PACKET , **PPNDIS_PACKET ;
當 Driver 由 NIC 收到封包時,Driver 需要呼叫 NdisAllocatePacket() 配置一個 NDIS_PACKET。
VOID NdisAllocatePacket ( OUTPNDIS_STATUS Status, OUTPNDIS_PACKET *Packet, INNDIS_HANDLE PoolHandle );
VOID NdisAllocatePacketPool ( OUTPNDIS_STATUS Status, OUTPNDIS_HANDLE PoolHandle, INUINT NumberOfDescriptors, INUINT ProtocolReservedLength );
VOID NdisAllocateBuffer ( OUTPNDIS_STATUS Status, OUTPNDIS_BUFFER *Buffer, INNDIS_HANDLE PoolHandle, INPVOID VirtualAddress, INUINT Length );
VOID NdisAllocateBufferPool ( OUTPNDIS_STATUS Status, OUTPNDIS_HANDLE PoolHandle, INUINT NumberOfDescriptors );
所以事情比較清楚了。因為要把封包丟給 NDIS 之前,Driver 必須先配置 NDIS_PACKET,而 NDIS_PACKET 又包含一個以上的 NDIS_BUFFER,所以在 MinoportReturnPacket() 裡面,就算你要重新利用 NDIS_PACKET,你還是得先把裡面的 NDIS_BUFFER全部移除並釋放。因為 MDL映射到的 Virtual Address 有可能已經都不對了。
VOID MinoportReturnPacket ( INNDIS_HANDLE MiniportAdapterContext, INPNDIS_PACKET Packet );
MiniportReturnPacket runs at IRQL = DISPATCH_LEVEL.
SendPacketsHandler
跟 MiniportSend() 相同,只不過它可以一次處理多個 NDIS_PACKET。VOID MiniportSendPackets ( INNDIS_HANDLE MiniportAdapterContext, INPPNDIS_PACKET PacketArray, INUINT NumberOfPackets );
AllocateCompleteHandler
Bus-Master DMA NIC 的 Driver 在呼叫 NdisMAllocateSharedMemoryAsync() 才需要的函式。因為我也沒寫過這類 Driver,所以也無從解釋。VOID MiniportAllocateComplete ( INNDIS_HANDLE MiniportAdapterContext, INPVOID VirtualAddress, INPNDIS_PHYSICAL_ADDRESS PhysicalAddress, INULONG Length, INPVOID Context );
MiniportAllocateComplete runs at IRQL = DISPATCH_LEVEL.
CoCreateVcHandler
Ignore.CoDeleteVcHandler
Ignore.CoActivateVcHandler
Ignore.CoDeactivateVcHandler
Ignore.CoSendPacketsHandler
Ignore.CoRequestHandler
Ignore.CancelSendPacketsHandler
取消某個特定群組的封包傳送。 作法如下:- 在儲存 NDIS_PACKET 的 software queue 內搜尋,利用 NDIS_GET_PACKET_CANCEL_ID() 一個一個檢視封包的 ID 是否跟 CancelId 相同,如果相同的話,Driver 必須把這些 NDIS_PACKET 由 software queue 內移除,並呼叫 NdisMSendComplete()、NdisMSendWanComplete() 或 NdisMCoSendComplete(),並且把 Status 設為 NDIS_STATUS_ABORTED。
VOID MiniportCancelSendPackets ( INNDIS_HANDLE MiniportAdapterContext, INPVOID CancelId );
System support for MiniportCancelSendPackets is available in Windows XP and later operating systems.
MiniportCancelSendPackets runs at IRQL <= DISPATCH_LEVEL.
PnPEventNotifyHandler
- 在處理 surprise removal 部份,所有在 Windows XP 版本之後,不會直接 access 硬體的 miniport driver,都應該在呼叫 NdisMSetAttributesEx() 的時候,傳入 NDIS_ATTRIBUTE_SURPRISE_REMOVE_OK 這個 flag。如果有設定這個 flag 的話,當使用者突然移除 NIC 時,就不會顯示出警告訊息。Driver 應該 completes pending Ndis Packets。 除此之外,在 MiniportPnPEventNotify() 之外的任何動作,都應該確認卡是否已經被移除(可以透過 register write/read 的方式,確認 register 是否可以被寫入)。如果不做這個確認的話,Driver 有可能會一直嘗試讀取已被移除的 NIC 的 register,這樣會導致 NDIS 不會呼叫到 MiniportPnPEventNotify() 。
- 在處理 power source 部份,miniport 可以利用 MiniportPnPEventNotify() 傳入的參數,調整 NIC 的 power comsuption。例如,如果收到 NdisPowerProfileBattery 的話,就調低 power comsuption。,如果收到 NdisPowerProfileAcOnline 的話,便調高 power comsuption。
VOID MiniportPnPEventNotify ( INNDIS_HANDLE MiniportAdapterContext, INNDIS_DEVICE_PNP_EVENT PnPEvent, INPVOID InformationBuffer, INULONG InformationBufferLength, );
System support for MiniportPnPEventNotify is available in Windows XP and later operating systems.
MiniportPnPEventNotify runs synchronously at IRQL = PASSIVE_LEVEL and in the context of a system thread.
AdapterShutdownHandler
當系統關機的時候(不管是使用者選擇或是系統發生無法回覆的錯誤),MiniportShutdown() 會把 NIC 重置成初始的狀態。每個 Miniport Driver 都必須提供 MiniportShutdown(),不管是透過 NdisMRegisterAdapterShutdownHandler() (~NDIS5.0) 還是 NdisMRegisterMiniport() (NDIS5.1~)。MiniportShutdown() 不用釋放任何配置過的資源,它只是 Stop the NIC。
VOID MiniportShutdown ( INPVOID ShutdownContext );
MiniportShutdown should call no NdisXxx functions.