本部分介绍如何移植现有过滤器,以便其可以使用 RTX64 NAL。关于如何创建一个使用 NAL 的新应用,参阅创建使用 NAL 的实时应用程序(Creating a Real-Time Application that Uses the NAL)。
如果设备支持单个接收或发送队列,则必须要使用 RT-TCP/IP 栈过滤器驱动。此外,该设备必须在第 2 层(Layer 2)协议和 RT-TCP/IP 栈之间共享。这种情况下,无法使用 NAL 应用程序替换过滤器驱动。过滤器驱动程序无需更改即可使用。
如果第 2 层协议有指定的网络接口(或一些接收和发送队列),则该协议可以实现为 NAL 客户端应用程序。如果网络接口被指定为第 2 层协议,并且不需要运行 RT-TCP/IP 栈,则该接口必须配置为不支持 TCP/IP。
如果网络接口用于运行第 2 层协议以及 RT-TCP/IP 栈,则栈将在相同编号的默认接收队列和发送队列上运行。所有其他队列均可用于第 2 层协议。只有 RtNalIGB 驱动程序允许重新分配默认接收队列。所有其他驱动程序都使用默认接收队列 0。使用多个设备队列会影响单个队列的性能,因为它们共享同一物理线路。
注意:NAL 不包含专用的过滤器驱动程序。移植现有过滤器驱动程序后,该驱动的功能将与连接到 NAL 的其他应用程序类似。
初始化
RTX64 控制面板允许用户通过创建 RT-TCP/IP 栈可用的接口,来关联实时网络设备驱动程序和过滤器驱动程序。过滤器在栈配置其网络设备并将设备指针传递给 RtndConfigureFilter 后启动。用户可以通过调用 RtnGetDeviceName 来检索设备名称。
NAL 应用程序必须知道连接到设备接收和发送队列的接口名称。首先,应用程序必须调用 RtNalInit。这会将应用程序与 NAL 关联起来,因此在应用程序退出之前 NAL 不会退出。然后,如果应用程序不知道 NAL 提供的可用队列,则可以调用 RtNalGetNumberOfQueues 并使用 RtNalGetQueueInfoByIndex 迭代所有 NAL 队列。
为了使用设备队列,应用程序必须通过调用 RtNalAcquireQueue 获取队列的所有权,然后通过 RtNalConfigureQueue 配置它。必须将接口名称传递给 RtNalAcquireQueue,它返回队列句柄。可以在下面所有 NAL API 调用中使用此队列句柄。
从 RtNalAcquireQueue 成功返回后,您现在可以独占使用设备队列。
应用程序需要获取两个单独的队列来发送和接收数据:接收队列和发送队列。 应用程序可能只使用一个队列,要么接收数据,要么发送数据。为了开始发送或接收数据,您必须使用队列句柄调用 RtNalConfigureQueue。RtNalConfigureQueue 的其他参数是应用程序上下文指针,和指向功能结构体 RTNAL_QUEUE_CAPABILITIES 的指针。在这个结构中,可以为发送和接收数据包等操作设置回调(callbacks)。必须编写两个回调函数 fpRtnGetPacket 和 fpRtnDecodePacket 才能使用 RtNalReceive。必须编写 fpRtnDecodePacket 回调才能使用 RtNalTransmit。 用于解码接收和发送队列的数据包回调可能不同。如果应用程序使用 RtNalReceiveWithCallback 或 RtNalEnableReceivePolling,则必须编写接收数据包回调 fpRtnReceiveCallback。
应用程序上下文指针,通常是指向与该设备队列关联的应用程序结构体的指针。该结构体包含了设备名称,物理地址,数据包缓冲区和所有其他必要信息。该上下文指针被传递给应用程序回调。
接收数据包
过滤器应用程序通过 RT-TCP/IP 栈上运行的设备接收线程调用 RtndReceiveFilter 或 RtndReceiveFilterEx 的回调接收数据。
NAL 不运行接收线程。NAL 应用程序必须运行自己的接收线程,或使用轮询定时器。通常情况下,该线程应当在调用 RtNalConfigureQueue 之前启动。
如果 NAL 应用程序运行接收线程,则该线程应等待接收通知事件句柄,该句柄在 RtNalAcquireQueue 所需的结构体 RTNAL_QUEUE_EVENTS 中返回。接收线程应调用 RtNalGetReceivePacketCount,然后根据 RtNalGetReceivePacketCount 的第二个参数返回的确切次数调用 RtNalReceive 或 RtNalReceiveWithCallback。或者,应用程序可以选择不使用 RtNalGetReceivePacketCount,而是调用 RtNalReceive,直到失败并出现错误代码 ERROR_NO_DATA。
如果设备收到有效的以太网数据包,RtNalReceive 会调用 fpRtnGetPacket 和 fpRtnDecodePacket 回调。作为将应用程序移植到 NAL 的一部分,如果您使用 RtNalReceive,则必须编写这些回调并将其指针传递给 RtNalConfigureQueue。
NAL 应用程序可以按照其自有格式定义数据包结构。使用队列上下文指针和请求的数据包长度作为参数调用 fpRtnGetPacket 函数,将返回指向该结构体的指针。fpRtnDecodePacket 函数和应用程序数据包指针一起调用,并提供上下文指针,指向数据包数据区域以及数据包数据长度。
与 RtndReceiveFilter 不同,fpRtnGetPacket 中请求的长度包括以太网标头的长度。该长度应大于等于最大数据包大小 + 4。
RtNalReceive 成功返回后,应用程序数据包结构体(由 fpRtnGetPacket 返回)包含接收到的数据包,数据区域(fpRtnDecodePacket 提供的指针指向)包含数据包数据。
在 NAL 提供的 NalDataStream 示例中,这些称为 AppRtnGetPacket 和 AppRtnDecodePacket。
注意:当驱动程序接收例程持有驱动程序的 DMA 环锁时,将直接调用 fpRtnGetPacket 和 fpRtnDecodePacket 函数。
接收数据包回调
注意:RtNalE1000 驱动程序不支持 RtndReceiveWithCallback。
应用程序接收数据包回调,可以在传递给 RtNalConfigureQueue 的功能结构体中的 fpRtnReceiveCallback 中,传递给驱动程序。
fpRtnReceiveCallback 函数直接从驱动程序中的 RtndReceive 或 RtndReceiveWithCallback 调用。
如果应用程序调用 RtNalReceiveWithCallback,则必须在接收数据包回调中处理该数据包,但它不需要 fpRtnGetPacket 和 fpRtnDecodePacket 回调。NalMultiplePacketTx 示例使用 RtNalReceiveWithCallback API 和接收数据包回调。
如果应用程序调用 RtNalReceive,它还可以在接收数据包回调中处理数据包,该回调将返回 TRUE。fpRtnGetPacket 和 fpRtnDecodePacket 回调函数不会被调用。或者,它可以使用回调来决定接受或拒绝数据包。在这种情况下,数据包回调返回 TRUE 以拒绝数据包。RtNalReceive返回后,数据包回调返回 FALSE 以传递要处理的数据包。
注意:当持有驱动程序的 DMA 环锁时,fpRtnReceiveCallback 函数(与 fpRtnGetPacket 和 fpRtnDecodePacket 相同)直接从驱动程序接收例程调用。
如果应用程序使用轮询定时器,则它必须获取没有接收通知的接收队列。它需要一个接收数据包回调 fpRtnReceiveCallback。不需要 FpRtnGetPacket 和 FpRtnDecodePacket 回调。
发送数据
要使用 RtNalTransmit 发送数据,必须执行 fpRtnDecodePacket 回调,该回调对传递给 RtNalTransmit 的应用程序数据包结构体进行解码,让驱动程序获知数据区域指针和帧长度(包括以太网标头的大小)。RtNalTransmit 在 NIC 驱动程序中调用 RtNalTransmit ,将数据复制到 DMA 缓冲区。
RtNalTransmitEx 在单次调用中发送多个以太网帧。帧必须由 RtNalAllocateFrame 预先分配,它返回一个指向 RTNAL_FRAME 结构体的指针,其中包含以太网帧缓冲区的虚拟和物理地址。指向 RTNAL_FRAME 结构体的指针数组被传递给 RtNalTransmitEx。应用程序还可以提供回调,对发送完成线程中的每个或某些帧进行调用。指向发送完成回调的指针是 RTNAL_FRAME 结构体的一个字段。
要使用发送完成回调,必须启用此队列的发送通知。当使用发送通知获取队列时,NAL 在应用程序上下文中为每个队列运行一个额外的发送完成线程。该线程在理想处理器上运行,并具有为接口配置的优先级。即使发送完成事件句柄是从 RtNalAcquireQueue 的结构体 RTNAL_QUEUE_EVENTS 中返回的,应用程序也不应该直接使用它。
NalMultiplePacketEx 示例使用 RtNalTransmitEx API 和传输完成回调。
注意:RtNalE1000 驱动程序不支持 RtndTransmitEx(由 RtNalTransmitEx 使用)。
链接状态变化
链接变化通知事件句柄在 RtNalAcquireQueue 的 RTNAL_QUEUE_EVENTS 结构体中返回。当应用程序检测到此事件时,应该使用 ENIOLINKSTATUS 调用 RtNalIoctl 来确定链路是打开还是关闭。该 IOCTL 可以随时进行。由于设备驱动程序不会重新协商链接,在 RtNalAcquireQueue 或 RtNalConfigureQueue (执行)期间将没有链接通知。
提供的 NalDataStream 示例在 GetLinkStatus 函数中显示了这一点。
其他注意事项
- 当应用程序不再需要设备队列时,应该调用 RtNalReleaseQueue,以便其他使用 NAL 的应用程序获取资源。否则,队列将在应用程序退出或由 RtssKill 终止时由 NAL 释放;
- 队列有一个停止通知事件提供给应用程序。这是一个手动重置事件。NAL 进程在释放队列时设置它。应用程序还可以设置它来通知其线程,但 NAL 不会监控它;
- 在多线程应用程序中,当在线程中释放队列时,所有其他引用该队列句柄的线程,对 NAL API 调用将失败,并返回错误 ERROR_INVALID_HANDLE;
- 不得使用来自 DLL_PROCESS_DETACH 的任何 NAL API 调用;
- 借助 RT-TCP/IP 栈,可以在部署期间通过将过滤器驱动与设备接口相关联来分配网络接口卡。这使得开发人员无需提前了解目标系统上可用的驱动程序或 NIC 数量即可编写应用程序。