NDIS 定义对象标识符 (OID) 值,以标识适配器参数,其中包括设备特征、可配置设置和统计信息等操作参数。 协议驱动程序可以查询或设置基础驱动程序的操作参数。
NDIS 还为 NDIS 6.1 及更高版本的协议驱动程序提供直接 OID 请求接口。 直接 OID 请求路径支持频繁查询或设置的 OID 请求。 例如,IPsec 卸载版本 2 (IPsecv2) 接口为直接 OID 请求提供 OID_TCP_TASK_IPSEC_OFFLOAD_V2_ADD_SA OID。 直接 OID 请求接口对于 NDIS 驱动程序是可选的。
从 NDIS 协议驱动程序生成 OID 请求
为了向基础驱动程序发出 OID 请求,协议调用 NdisOidRequest 函数。
下图演示了协议驱动程序发起的 OID 请求。
在协议驱动程序调用 NdisOidRequest 函数后,NDIS 将调用下一个基础驱动程序的请求函数。
若要同步完成, NdisOidRequest 将返回NDIS_STATUS_SUCCESS或错误状态。 为了异步完成, NdisOidRequest 返回NDIS_STATUS_PENDING。
如果 NdisOidRequest 返回NDIS_STATUS_PENDING,则 NDIS 在基础驱动程序完成 OID 请求后调用 ProtocolOidRequestComplete 函数。 在这种情况下,NDIS 在 ProtocolOidRequestComplete 的 OidRequest 参数中传递请求的结果。 NDIS 在 ProtocolOidRequestComplete 的 Status 参数中传递请求的最终状态。
如果 NdisOidRequest 返回NDIS_STATUS_SUCCESS,它将在 OidRequest 参数的 NDIS_OID_REQUEST 结构中返回查询请求的结果。 在这种情况下,NDIS 不调用 ProtocolOidRequestComplete 函数。
若要确定基础驱动程序成功处理了哪些信息,发出 OID 请求的协议驱动程序必须在 OID 请求返回后检查NDIS_OID_REQUEST结构中的 SupportedRevision 成员中的值。
如果基础驱动程序应将 OID 请求与后续状态指示相关联,则协议驱动程序应在NDIS_OID_REQUEST结构中设置 RequestId 成员。 当基础驱动程序发出状态指示时,它会将 NDIS_STATUS_INDICATION 结构中的 RequestId 成员设置为 OID 请求中提供的值。
当绑定处于“正在重启”、“正在运行”、“暂停”或“已暂停”状态时,驱动程序可以调用 NdisOidRequest。
协议驱动程序直接 OID 请求
为了支持直接 OID 请求路径,协议驱动程序在 NDIS_PROTOCOL_DRIVER_CHARACTERISTICS 结构中提供 ProtocolXxx 函数入口点,NDIS 为协议驱动程序提供 NdisXxx 函数。
直接 OID 请求接口类似于标准 OID 请求接口。 例如, NdisDirectOidRequest 和 ProtocolDirectOidRequestComplete 函数类似于 NdisOidRequest 和 ProtocolOidRequestComplete 函数。
注意 NDIS 6.1 及更高版本支持用于直接 OID 请求接口的特定 OID。 不支持在 NDIS 6.1 和某些 NDIS 6.1 OID 之前存在的 OID。
协议驱动程序同步 OID 请求
为了支持同步 OID 请求路径,协议驱动程序调用 NdisSynchronousOidRequest 函数来发出同步 OID。
对于协议驱动程序, 同步 OID 请求接口 不同于常规和直接 OID 请求接口,即协议驱动程序不必实现异步 完整 回调函数,这是因为路径的同步性质。
处理协议驱动程序中的状态指示
协议驱动程序必须提供当基础驱动程序报告状态时 NDIS 调用的 ProtocolStatusEx 函数。
在基础驱动程序 (NdisMIndicateStatus 或 NdisFIndicateStatus) 调用状态指示函数后,NDIS 调用协议驱动程序的 ProtocolStatusEx 函数。
如果状态指示与 OID 请求相关联,则基础驱动程序可以设置 DestinationHandle 和 RequestId 成员,以便 NDIS 可以为特定协议绑定提供状态指示。
处理协议驱动程序中的 PnP 事件通知
除了特定于 NDIS 6.0 及更高版本的事件通知外,NDIS 6.0 及更高版本的协议驱动程序还处理与 NDIS 5.x 驱动程序相同的即插即用 (PnP) 事件通知。 PnP 事件通知的处理特定于驱动程序。
为了通知协议驱动程序网络 PnP 事件,NDIS 调用驱动程序的 ProtocolNetPnPEvent 函数。 为了定义事件的类型和事件特征,NDIS 在 ProtocolNetPnPEvent 的 NetPnPEvent 事件参数中传递NET_PNP_EVENT_NOTIFICATION结构。
协议驱动程序应处理驱动程序堆栈更改。 不处理堆栈更改通知的协议驱动程序将从适配器取消绑定并反弹。 成功处理驱动程序堆栈通知的协议驱动程序绑定不受影响。
协议驱动程序中的可分页和可丢弃代码
驱动程序开发人员应尽可能将代码指定为可分页代码,为必须驻留在内存中的代码释放系统空间。 可以使用 NDIS_PAGEABLE_FUNCTION 宏将函数标记为可分页。 函数的 IRQL、资源管理功能和其他特征可能会禁止该函数可分页。
每个 ProtocolXxx 函数在 IRQL 上运行,范围从 PASSIVE_LEVEL 到 DISPATCH_LEVEL。 以 IRQL = PASSIVE_LEVEL 独占方式运行的函数应标记为可分页。
只要在 IRQL = PASSIVE_LEVEL 上运行的驱动程序函数既不调用也不由在 IRQL >= DISPATCH_LEVEL运行的任何函数(例如获取旋转锁的函数)调用,就可以使该函数可分页。 获取旋转锁会导致获取线程的 IRQL 提升为DISPATCH_LEVEL。 在 IRQL = PASSIVE_LEVEL运行的驱动程序函数(如 ProtocolBindAdapterEx)不得调用在 IRQL >= DISPATCH_LEVEL下运行的任何 NdisXxx 函数(如果该驱动程序函数被标记为可分页代码)。 有关每个 NdisXxx 函数的 IRQL 的详细信息,请参阅 NDIS 库函数。
应使用 NDIS_INIT_FUNCTION 宏将 NDIS 协议驱动程序的 DriverEntry 函数以及仅从 DriverEntry 调用 的代码 指定为仅初始化代码。 假定使用此宏标识的代码在系统初始化时只运行一次,因此,仅在该时间内映射代码。 在标记为“仅初始化”的函数返回后,该函数将被丢弃。
协议驱动程序重置操作
协议驱动程序无法在 NDIS 6.0 及更高版本中启动重置操作。
通常,基础微型端口驱动程序会重置 NIC,因为 NIC 在发送或请求操作期间超时。 此条件导致 NDIS 调用微型端口驱动程序的 MiniportCheckForHangEx 和随后 的 MiniportResetEx 函数。 或者,微型端口驱动程序确定 NIC 的接收功能功能失调。
如果重置由 NDIS 启动, 并且 MiniportResetEx 返回NDIS_STATUS_PENDING,则 NDIS 调用每个绑定协议驱动程序的 ProtocolStatusEx (或 ProtocolCoStatusEx) 函数,其状态为 NDIS_STATUS_RESET_START。 当微型端口驱动程序调用 NdisMResetComplete 时,NDIS 会再次调用 ProtocolStatusEx (或 ProtocolCoStatusEx) ,其状态为 NDIS_STATUS_RESET_END。
协议驱动程序必须处理在绑定到基础 NIC 上的未完成发送可以取消的可能性,因为 NIC 已重置。 如果绑定的协议驱动程序有任何挂起的传输请求,NDIS 将指示以适当的状态向协议驱动程序发送完成。 当重置操作完成后,协议驱动程序必须重新提交发送请求,前提是 NIC 再次正常运行。
当协议驱动程序收到NDIS_STATUS_RESET_START状态时,它应:
保留已准备好传输的任何网络数据 ,直到协议 Status 收到NDIS_STATUS_RESET_END通知。
不进行任何定向到基础微型端口驱动程序的 NDIS 调用,但返回资源(如使用 NdisReturnNetBufferLists 返回网络数据)的调用除外。
ProtocolStatusEx (或 ProtocolCoStatusEx) 收到NDIS_STATUS_RESET_END消息后,协议驱动程序可以继续发送网络数据和 OID 请求。
处理协议驱动程序中的 PnP 事件和电源管理事件
当操作系统向表示网络接口卡 (NIC) 的目标设备对象发出即插即用 (PnP) I/O 请求数据包 (IRP) 或电源管理 IRP 时,NDIS 会截获该 IRP。 NDIS 通过调用驱动程序的 ProtocolNetPnPEvent 函数,向每个绑定协议驱动程序和每个绑定中间驱动程序指示事件。 在对 ProtocolNetPnPEvent 的调用中,NDIS 传递指向包含NET_PNP_EVENT结构的 NET_PNP_EVENT_NOTIFICATION 的指针。 NET_PNP_EVENT结构描述要指示的 PnP 事件或电源管理事件。 有关协议驱动程序 PnP 接口的详细信息,请参阅 处理协议驱动程序中的 PnP 事件通知。
以下列表包含 PnP 和电源管理事件,如 NET_PNP_EVENT 结构中的 NetEvent 代码所示:
NetEventSetPower: 指示设置电源请求,该请求指定微型端口适配器应转换为特定电源状态。 电源管理感知协议驱动程序应始终通过返回NDIS_STATUS_SUCCESS成功此事件。 旧的协议驱动程序可以返回NDIS_STATUS_NOT_SUPPORTED,以指示 NDIS 应将其与微型端口适配器取消绑定。
发出设置电源请求后,如果微型端口适配器正在转换为低功耗状态,NDIS 将暂停驱动程序堆栈。 如果微型端口适配器正在转换为工作状态 (D0) ,则在设置电源请求之前,NDIS 会重启驱动程序堆栈。
如果微型端口适配器处于低功耗状态,则协议驱动程序无法发出任何 OID 请求。 此要求是额外的电源管理限制,在驱动程序堆栈处于“已暂停”状态时,将添加到其他限制中。
如果基础微型端口适配器无法识别电源管理,微型端口驱动程序会将NDIS_MINIPORT_ADAPTER_GENERAL_ATTRIBUTES的 PowerManagementCapabilities 成员设置为 NULL,NDIS 将 NDIS_BIND_PARAMETERS 的 PowerManagementCapabilities 成员设置为 NULL。
注意 从 NDIS 6.30 开始,在收到此事件的通知后,协议驱动程序必须停止生成新的 I/O 请求,并且不应等待调用 ProtocolNetPnPEvent 的上下文中任何挂起的 I/O 请求完成。
NetEventQueryPower:指示查询电源请求,该请求查询基础微型端口适配器是否可以转换到特定电源状态。 协议驱动程序应始终成功 NetEventQueryPower 。 建立活动连接后,协议驱动程序可以调用 PoRegisterSystemState 来注册连续忙状态。 只要状态注册生效,电源管理器就不会尝试使系统进入睡眠状态。 连接变为非活动状态后,协议驱动程序通过调用 PoUnregisterSystemState 取消状态注册。 协议驱动程序绝不应尝试通过使 NetEventQueryRemoveDevice 失败来阻止系统转换到睡眠状态。 请注意, NetEventQueryPower 始终后跟 NetEventSetPower。 设置设备当前电源状态的 NetEventSetPower 将取消 NetEventQueryPower。
注意 从 NDIS 6.30 开始,在收到此事件的通知后,协议驱动程序不应在调用 ProtocolNetPnPEvent 的上下文中等待任何挂起的 I/O 请求完成。
NetEventQueryRemoveDevice:指示查询删除设备请求,该请求查询是否可以在不中断操作的情况下删除 NIC。 例如,如果协议驱动程序无法释放设备 (因为设备正在使用) ,则必须通过返回NDIS_STATUS_FAILURE来使 NetEventQueryRemoveDevice 失败。
NetEventCancelRemoveDevice:指示取消删除设备请求,该请求取消删除基础 NIC。 协议驱动程序应始终通过返回NDIS_STATUS_SUCCESS成功此事件。
NetEventReconfigure:指示网络组件的配置已更改。 例如,如果用户更改 TCP/IP 的 IP 地址,NDIS 会使用 NetEventReconfigure 代码向 TCP/IP 协议指示此事件。 在极少数情况下,如果协议驱动程序无法应用指示的配置更改并且没有可用的默认值,则可能会返回失败代码。 分配内存的失败尝试是协议返回失败代码的一个示例。 返回错误代码可能会导致提示用户重启系统。协议应验证传递给其 ProtocolNetPnPEvent 函数的 NetEventReconfigure 相关数据。
NetEventBindList:向协议驱动程序指示其绑定列表处理顺序已重新配置。 此列表指示在处理时应用于协议绑定的相对顺序,例如,可能路由到多个绑定之一的用户请求。 随此事件一起传递的缓冲区包含以 NULL 结尾的 Unicode 字符串格式的设备名称列表。 每个设备名称的格式与传递给 ProtocolBindAdapterEx 调用的 DeviceName 参数相同。协议应验证传递给其 ProtocolNetPnPEvent 函数的 NetEventBindList 相关数据;协议应验证传递给其 ProtocolNetPnPEvent 函数的 NetEventBindList 相关数据。
NetEventBindsComplete:指示协议驱动程序已绑定到它可以绑定到的所有 NIC。 除非将 PnP NIC 插入系统,否则 NDIS 不会指示与协议驱动程序的更多绑定。
NetEventPnPCapabilities:指示用户启用或禁用基础适配器的唤醒功能。 NDIS 传递给 ProtocolNetPnPEvent 的 ProtocolBindingContext 参数指定绑定。
NetEventPause:指示指定的协议绑定应进入暂停状态。 NDIS 完成绑定的所有未完成发送请求后,绑定将进入“已暂停”状态。
NetEventRestart:指示指定的协议绑定已进入“正在重启”状态。 协议驱动程序准备好恢复绑定的发送和接收操作后,绑定将进入“正在运行”状态。
NetEventPortActivation: 指示激活与指定绑定关联的端口列表。
NetEventPortDeactivation:指示停用与指定绑定关联的端口列表。
NetEventIMReEnableDevice:指示已更改 NDIS 6.0 或更高版本中间驱动程序的虚拟微型端口的配置。 NetEventIMReEnableDevice 类似于 NetEventReconfigure 事件,不同之处在于中间驱动程序接收单个虚拟微型端口的此事件, NetEventReconfigure 事件应用于所有中间驱动程序的虚拟微型端口。 例如,当用户禁用然后从设备管理器或其他源启用单个虚拟微型端口时,中间驱动程序会收到 NetEventIMReEnableDevice 事件。
NET_PNP_EVENT 结构的 Buffer 成员指向包含特定于所指示事件的信息的缓冲区。
协议驱动程序可以使用 NdisCompleteNetPnPEvent 异步完成对 ProtocolNetPnPEvent 的调用。