第 5 章 - Azure RTOS NetX Duo 网络驱动程序

本章介绍适用于 Azure RTOS NetX Duo 的网络驱动程序。 介绍的信息旨在帮助开发人员编写特定于应用程序的适用于 NetX Duo 的网络驱动程序。

驱动程序简介

NX_IP 结构包含用于管理单个 IP 实例的所有内容。 这包括常规 TCP/IP 协议信息,以及特定于应用程序的物理网络驱动程序的入口例程。 驱动程序的入口例程在 nx_ip_create 服务期间定义。 可以通过 nx_ip_interface_attach 服务向 IP 实例添加其他设备。

NetX Duo 与应用程序的网络驱动程序之间的通信是通过 NX_IP_DRIVER 请求结构来完成的。 此结构通常是在调用方的堆栈上本地定义的,因此在驱动程序和调用函数返回后发布。 此结构的定义如下所示。

typedef struct NX_IP_DRIVER_STRUCT
{
      UINT           nx_ip_driver_command;
      UINT           nx_ip_driver_status;
      ULONG          nx_ip_driver_physical_address_msw;
      ULONG          nx_ip_driver_physical_address_lsw;
      NX_PACKET      *nx_ip_driver_packet;
      ULONG          *nx_ip_driver_return_ptr;
      NX_IP          *nx_ip_driver_ptr;
      NX_INTERFACE   *nx_ip_driver_interface;01
} NX_IP_DRIVER;

驱动程序入口

NetX Duo 调用网络驱动程序入口函数以进行驱动程序初始化和发送数据包,以及进行各种控制和状态操作,包括初始化和启用网络设备。 通过设置 NX_IP_DRIVER 请求结构中的 nx_ip_driver_command 字段,NetX Duo 向网络驱动程序发出命令。 驱动程序入口函数的格式如下:

VOID my_driver_entry(NX_IP_DRIVER *request);

驱动程序请求

NetX Duo 使用特定命令创建驱动程序请求,并调用驱动程序入口函数以执行命令。 由于每个网络驱动程序都有单个入口函数,因此 NetX Duo 通过驱动程序请求数据结构发出所有请求。 驱动程序请求数据结构 (NX_IP_DRIVER) 的 nx_ip_driver_command 成员定义请求。 状态信息将报告回 nx_ip_driver_status 成员的调用方。 如果此字段为 NX_SUCCESS,则驱动程序请求已成功完成。

NetX Duo 串行化对驱动程序的所有访问。 因此,驱动程序无需处理多个异步调用入口函数的线程。 请注意,设备驱动程序函数在锁定了 IP 互斥的情况下执行。 因此,设备驱动程序内部函数不应自行阻止。

通常,设备驱动程序还会处理中断。 因此,所有驱动程序函数都需要是中断安全的。

驱动程序初始化

尽管实际驱动程序初始化处理特定于应用程序,但它通常由数据结构和物理硬件初始化组成。 NetX Duo 进行驱动程序初始化所需的信息是 IP 最大传输单元 (MTU),它是 IP 层有效负载可用的字节数,包括 IPv4 或 IPv6 标头以及物理接口是否需要逻辑到物理映射。 驱动程序通过调用 nx_ip_interface_mtu_set 来配置接口 MTU 值。

设备驱动程序需要调用 nx_ip_interface_address_mapping_configure 来通知 NetX Duo 是否需要接口地址映射。 如果需要地址映射,驱动程序将负责配置具有有效 MAC 地址的接口,并通过 nx_ip_interface_physical_address_set 向 NetX 提供 MAC 地址。

当网络驱动程序接收来自 NetX Duo 的 NX_LINK INITIALIZE 请求时,它会收到一个指向 IP 控制块的指针,作为上面所示的 NX_IP_DRIVER 请求控制块的一部分。

应用程序调用 nx_ip_create 后,IP 帮助程序线程会将命令设置为 NX_LINK_INITIALIZE 的驱动程序请求发送给驱动程序,以初始化其物理网络接口。 以下 NX_IP_DRIVER 成员用于初始化请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_INITIALIZE
nx_ip_driver_ptr 指向 IP 实例的指针。 此值应由驱动程序保存,以便驱动程序函数可以找到要操作的 IP 实例。
nx_ip_driver_interface 指向 IP 实例中的网络接口结构的指针。 此信息应由驱动程序保存。 接收数据包时,驱动程序应在堆栈上方发送数据包时使用接口结构信息。 可以通过读取此数据结构中的成员 nx_interface_index 获取接口索引(设备索引)。
nx_ip_driver_status 完成状态。 如果驱动程序无法初始化 IP 实例的指定接口,则会返回非零错误状态。

注意

驱动程序实际是从已为 IP 实例创建的 IP 帮助程序线程中调用的。 因此,驱动程序例程应避免执行阻止操作,否则 IP 帮助程序线程可能会停滞,从而导致依赖于该 IP 线程的应用程序出现无限延迟。

接下来,通过将驱动程序命令设置为驱动程序请求中的 NX_LINK_ENABLE 并将请求发送到网络驱动程序,IP 帮助程序线程会启用物理网络。 IP 帮助程序线程完成初始化请求后不久将发生这种情况。 启用链接可能与在接口实例中设置 nx_interface_link_up 字段一样简单。 但它可能还涉及物理硬件的操作。 以下 NX_IP_DRIVER 成员用于启用链接请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_ENABLE
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法启用指定的接口,则会返回非零错误状态。

此请求在 nx_ip_delete 服务删除 IP 实例的过程中由 NetX Duo 发出。 或者,为了暂时禁用链接以节能,应用程序可能会发出此命令。 此服务禁用 IP 实例上的物理网络接口。 为了禁用链接而进行的处理可能与在接口实例中清除 nx_interface_link_up 标志一样简单。 但它可能还涉及物理硬件的操作。 通常,它是“启用链接”操作的反向操作。 禁用链接后,应用程序可请求启用链接操作以启用接口

以下 NX_IP_DRIVER 成员用于禁用链接请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_DISABLE
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法禁用 IP 实例中的指定接口,则会返回非零错误状态。

此请求在 nx_ip_delete 服务删除 IP 实例的过程中由 NetX Duo 发出。 此请求取消初始化接口,并释放在初始化阶段创建的任何资源。 通常,它是“初始化链接”操作的反向操作。 取消初始化接口后,将无法使用设备,直到再次初始化该接口。

以下 NX_IP_DRIVER 成员用于禁用链接请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_UNINITIALZE
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法取消初始化 IP 实例的指定接口,则会返回非零错误状态。

数据包发送

此请求是在进行内部 IPv4 或 IPv6 发送处理期间发出的,所有 NetX Duo 协议都使用该过程传输数据包(ARP、RARP 除外)。 接收数据包发送命令后,nx_packet_prepend_ptr 会指向要发送的数据包的开头,即 IPv4 或 IPv6 标头的开头。 nx_packet_length 指示正在传输的数据的总大小(以字节为单位)。 如果 nx_packet_next 是有效的,则传出 IP 数据报将存储在多个数据包中,需要驱动程序才能跟踪链式数据包并传输整个帧。 请注意,每个链式数据包中的有效数据区域存储在 nx_packet_prepend_ptr 和 nx_packet_append_ptr 之间 。

驱动程序负责构造物理标头。 如果需要物理地址到 IP 地址的映射(如以太网),则 IP 层已解析 MAC 地址。 目标 MAC 地址是从存储在 nx_ip_driver_physical_address_msw 和 nx_ip_driver_physical_address_lsw 中的 IP 实例传递的。

添加物理标头后,数据包发送处理,然后调用驱动程序的输出函数以传输数据包。

以下 NX_IP_DRIVER 成员用于数据包发送请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_PACKET_SEND
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_packet 指向要发送的数据包的指针
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_physical_address_msw 物理地址的最高有效 32 位(仅当需要物理映射时)
nx_ip_driver_physical_address_lsw 物理地址的最低有效 32 位(仅当需要物理映射时)
nx_ip_driver_status 完成状态。 如果驱动程序无法发送数据包,则会返回非零错误状态。

数据包广播(仅 IPv4 数据包)

此请求与发送数据包请求几乎完全相同。 唯一的区别是,目标物理地址字段设置为以太网广播 MAC 地址。 以下 NX_IP_DRIVER 成员用于数据包广播请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_PACKET_BROADCAST
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_packet 指向要发送的数据包的指针
nx_ip_driver_physical_address_msw 0x0000FFFF(广播)
nx_ip_driver_physical_address_lsw 0xFFFFFFFF(广播)
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_status 完成状态。 如果驱动程序无法发送数据包,则会返回非零错误状态。

ARP 发送

此请求还类似于 IP 数据包发送请求。 唯一的区别是,以太网标头指定 ARP 数据包(而不是 IP 数据包),并且目标物理地址字段设置为 MAC 广播地址。 以下 NX_IP_DRIVER 成员用于 ARP 发送请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_ARP_SEND
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_packet 指向要发送的数据包的指针
nx_ip_driver_physical_address_msw 0x0000FFFF(广播)
nx_ip_driver_physical_address_lsw 0xFFFFFFFF(广播)
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_status 完成状态。 如果驱动程序无法发送 ARP 数据包,则会返回非零错误状态。

重要

如果不需要物理映射,则不需要实现此请求。

尽管已将 ARP 替换为 IPv6 中的邻居发现协议和路由器发现协议,但以太网网络驱动程序仍必须与 IPv4 对等机和路由器兼容。 因此,驱动程序仍必须处理 ARP 数据包。

ARP 响应发送

此请求与 ARP 发送数据包请求几乎完全相同。 唯一的区别是,目标物理地址字段是从 IP 实例传递的。 以下 NX_IP_DRIVER 成员用于 ARP 响应发送请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_ARP_RESPONSE_SEND
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_packet 指向要发送的数据包的指针
nx_ip_driver_physical_address_msw 物理地址的最高有效 32 位
nx_ip_driver_physical_address_lsw 物理地址的最低有效 32 位
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法发送 ARP 数据包,则会返回非零错误状态。

重要

如果不需要物理映射,则不需要实现此请求。

RARP 发送

此请求与 ARP 发送数据包请求几乎完全相同。 唯一的区别是,不需要数据包标头的类型和物理地址字段,因为物理目标始终是广播地址。

以下 NX_IP_DRIVER 成员用于 RARP 发送请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_RARP_SEND
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_packet 指向要发送的数据包的指针
nx_ip_driver_physical_address_msw 0x0000FFFF(广播)
nx_ip_driver_physical_address_lsw 0xFFFFFFFF(广播)
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_status 完成状态。 如果驱动程序无法发送 RARP 数据包,则会返回非零错误状态。

重要

需要 RARP 服务的应用程序必须实现此命令。

多播组联接

这个请求是通过 IPv4 中的 nx_igmp_multicast_interface join 和 nx_ipv4_multicast_interface_join 服务、IPv6 中的 nxd_ipv6_multicast_interface_join 服务以及 IPv6 所需的各种操作发出的。 网络驱动程序使用提供的多播组地址,并设置物理媒体以接受来自该多播组地址的传入数据包。 请注意,对于不支持多播筛选器的驱动程序,驱动程序接收逻辑可能必须处于混杂模式。 在这种情况下,驱动程序可能需要根据目标 MAC 地址筛选传入帧,从而减少传递到 IP 实例的流量。 以下 NX_IP_DRIVER 成员用于多播组联接请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_MULTICAST_JOIN
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_physical_address_msw 物理多播地址的最高有效 32 位
nx_ip_driver_physical_address_lsw 物理多播地址的最低有效 32 位
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法联接多播组,则会返回非零错误状态。

注意

IPv6 应用程序需要在基于 ICMPv6 的协议(如地址配置)的驱动程序中实现多播。 但对于 IPv4 应用程序,如果不需要多播功能,则不需要实现此请求。

重要

如果未启用 IPv6,并且 IPv4 不需要多播功能,则不需要实现此请求。

多播组离开

调用此请求是通过显示调用 IPv4 中的 nx_igmp_multicast_interface_leave 或 nx_ipv4_multicast_interface_leave 服务、IPv6 中的 nxd_ipv6_multicast_interface_leave 服务,或者通过 IPv6 所需的各种内部 NetX Duo 操作来完成的。 驱动程序从多播列表中删除提供的以太网多播地址。 主机离开多播组后,此 IP 实例将不再收到网络上带有此以太网多播地址的数据包。 以下 NX_IP_DRIVER 成员用于多播组离开请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_MULTICAST_LEAVE
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_physical_address_msw 物理多播地址的最高有效 32 位
nx_ip_driver_physical_address_lsw 物理多播地址的最低有效 32 位
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法离开多播组,则会返回非零错误状态。

重要

如果 IPv4 或 IPv6 不需要多播功能,则不需要实现此请求。

附加接口

此请求从 NetX Duo 调用到设备驱动程序,允许驱动程序将驱动程序实例与 IP 中的相应 IP 实例和物理接口实例相关联。 以下 NX_IP_DRIVER 成员用于附加接口请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_INTERFACE_ATTACH
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_status 完成状态。 如果驱动程序无法分离 IP 实例的指定接口,则会返回非零错误状态。

分离接口

此请求由 NetX Duo 调用到设备驱动程序,允许驱动程序将驱动程序实例与 IP 中的相应 IP 实例和物理接口实例解除关联。 以下 NX_IP_DRIVER 成员用于附加接口请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_INTERFACE_DETACH
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针。
nx_ip_driver_status 完成状态。 如果驱动程序无法附加 IP 实例的指定接口,则会返回非零错误状态。

应用程序可以使用 NetX Duo 服务 nx_ip_interface_status_check 服务为主机上的任何接口查询网络接口链接状态。 有关这些服务的更多详细信息,请参阅第 149 页上的第 4 章“NetX Duo 服务的说明”。

链接状态包含在 nx_ip_driver_interface 指针所指向的 NX_INTERFACE 结构的 nx_interface_link_up 字段中 。 以下 NX_IP_DRIVER 成员用于链接状态请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_STATUS
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向要放置状态的目标的指针。
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取特定状态,则会返回非零错误状态。

注意

nx_ip_status_check 仍可用于检查主接口的状态。 但是,建议应用程序开发人员使用特定于接口的服务:nx_ip_interface_status_check

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的线速度存储在提供的目标中。 以下 NX_IP_DRIVER 成员用于链接线速度请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_SPEED
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向要放置线速度的目标的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取速度信息,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

获取双工类型

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的双工类型存储在提供的目标中。 以下 NX_IP_DRIVER 成员用于双工类型请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_DUPLEX_TYPE
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向要放置双工类型的目标的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取双工信息,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

获取错误计数

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的错误计数存储在提供的目标中。 若要支持此功能,驱动程序需要跟踪操作错误。 以下 NX_IP_DRIVER 成员用于链接错误计数请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_ERROR_COUNT
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向要放置错误计数的目标的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取错误计数,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

获取接收数据包计数

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的接收数据包计数存储在提供的目标中。 若要支持此功能,驱动程序需要跟踪接收到的数据包数。 以下 NX_IP_DRIVER 成员用于链接接收数据包计数请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_RX_COUNT
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向放置接收数据包计数的目标的指针
nx_ip_driver_interface 指向物理网络接口的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取接收计数,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

获取传输数据包计数

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的传输数据包计数存储在提供的目标中。 若要支持此功能,驱动程序需要跟踪每个接口上传输的每个数据包。 以下 NX_IP_DRIVER 成员用于链接传输数据包计数请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_TX_COUNT
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向放置传输数据包计数的目标的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取传输计数,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

获取分配错误

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序将链接的数据包池分配错误计数存储在提供的目标中。 以下 NX_IP_DRIVER 成员用于链接分配错误计数请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_GET_ALLOC_ERRORS
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 指向要放置分配错误计数的目标的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法获取分配错误,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

驱动程序延迟处理

此请求是从 IP 帮助程序线程发出的,以响应从传输或接收 ISR 调用 _nx_ip_driver_deferred_processing 例程的驱动程序。 这允许驱动程序 ISR 将数据包接收和传输处理延迟到 IP 帮助程序线程,从而减少要在 ISR 中处理的数量。 nx_ip_driver_interface 所指向的 NX_INTERFACE 结构中的 nx_interface_additional_link_info 字段可由驱动程序用来存储有关来自 IP 帮助程序线程上下文的延迟处理事件的信息。 以下 NX_IP_DRIVER 成员用于延迟处理事件。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_DEFERRED_PROCESSING
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针

设置物理地址

此请求是从 nx_ip_interface_physical_address_set 服务中发出的。 此服务允许应用程序在运行时更改接口物理地址。 收到此命令后,要求驱动程序将网络接口的硬件地址重新配置为提供的物理地址。 由于 IP 实例已具有新地址,因此无需从此命令调用 nx_ip_interface_address_set 服务。

以下 NX_IP_DRIVER 成员用于用户命令请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_SET_PHYSICAL_ADDRESS
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_physical_ad dress_msw 新物理地址的最高有效 32 位
nx_ip_driver_physical_ad dress_lsw 新物理地址的最低有效 32 位
nx_ip_driver_status 完成状态。 如果驱动程序无法重新配置物理地址,则会返回非零错误状态。

用户命令

此请求是从 nx_ip_driver_direct_command 服务内发出的。 驱动程序处理特定于应用程序的用户命令。 以下 NX_IP_DRIVER 成员用于用户命令请求。

NX_IP_DRIVER 成员 含义
nx_ip_driver_command NX_LINK_USER_COMMAND
nx_ip_driver_ptr 指向 IP 实例的指针
nx_ip_driver_return_ptr 用户定义
nx_ip_driver_interface 指向接口实例的指针
nx_ip_driver_status 完成状态。 如果驱动程序无法执行用户命令,则会返回非零错误状态。

重要

此请求不是由 NetX Duo 在内部使用的,因此其实现是可选的。

未实现的命令

网络驱动程序未实现的命令必须将返回状态字段设置为 NX_UNHANDLED_COMMAND。

驱动程序功能

某些网络接口提供校验和卸载功能。 设备驱动程序可以利用硬件加速,将 CPU 时间从运行各种校验和计算中释放出来。

根据硬件的硬件校验和支持级别,设备驱动程序需要通知 IP 实例启用了哪个硬件功能。 这样 IP 实例就能识别硬件功能,尽可能多地将计算卸载到硬件上。 驱动程序应使用 API nx_ip_interface_capability_set 来设置物理接口能够处理的所有功能。

可以使用以下功能:

  • NX_INTERFACE_CAPABILITY_IPV4_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_IPV4_RX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_TCP_RX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_UDP_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_UDP_RX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_ICMPV4_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_ICMPV4_RX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_ICMPV6_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_ICMPV6_RX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_IGMP_TX_CHECKSUM
  • NX_INTERFACE_CAPABILITY_IGMP_RX_CHECKSUM

对于可以在硬件中执行的校验和计算,驱动程序必须正确设置硬件或缓冲区描述符,传出数据包的校验和才能够生成,并由硬件插入到标头中。 接收数据包时,硬件校验和逻辑应能够验证校验和值。 如果校验和值不正确,则应放弃接收的帧。

即使有了在硬件中执行校验和计算的功能,IP 实例仍会保留校验和功能。 在某些情况下(例如通过 IPsec 保护的 UDP 数据报),UDP 校验和必须先在软件中计算,然后再将 UDP 帧向下传递到堆栈。 大多数硬件校验和功能不支持 IPsec 保护的数据段的校验和计算。 对于需要分段的 UDP 帧或 ICMP 帧,UDP 或 ICMP 校验和需要在软件中计算。 大多数硬件校验和逻辑不处理将数据拆分为多个帧的情况。

驱动程序输出

前面提到的所有数据包传输请求都需要驱动程序中实现的输出函数。 特定传输逻辑特定于硬件,但它通常包含检查硬件容量以便立即发送数据包。 如果可能,数据包有效负载(以及数据包链中的其他有效负载)会加载到一个或多个硬件传输缓冲区中,并启动传输操作。 如果数据包不适合可用的传输缓冲区,则数据包应排队,并在传输缓冲区可用时进行传输。

建议的传输队列是单向链接列表,同时包含头指针和尾指针。 新数据包将添加到队列的末尾,并将最旧的数据包置于前端。 nx_packet_queue_next 字段用作队列中数据包的下一个链接。 驱动程序定义传输队列的头指针和尾指针。

注意

由于此队列是从驱动程序的线程和中断部分访问的,因此,必须围绕队列操作放置中断保护。

大多数物理硬件实现都会在数据包传输完成时生成中断。 当驱动程序收到此类中断时,它通常会释放与正在传输的数据包关联的资源。 如果传输逻辑直接从 NX_PACKET 缓冲区读取数据,则驱动程序应使用 nx_packet_transmit_release 服务将与传输完成中断关联的数据包释放回可用的数据包池。 接下来,驱动程序会检查传输队列中是否有其他等待发送的数据包。 适合硬件传输缓冲区的多个排队传输数据包将被取消排队并加载到缓冲区中。 然后会启动另一个发送操作。

一旦将 NX_PACKET 中的数据移动到发送器 FIFO 中(或者如果驱动程序支持零复制操作,则已传输 NX_PACKET 中的数据),则在调用 nx_packet_transmit_release 之前,驱动程序必须将 nx_packet_prepend_ptr 移动到 IP 标头的开头。请记住相应地调整 nx_packet_length 字段。 如果 IP 帧由多个数据包组成,则只需释放数据包链的头。

驱动程序输入

接收收到的数据包中断后,网络驱动程序将从物理硬件接收缓冲区检索数据包,并生成有效的 NetX Duo 数据包。 生成有效的 NetX Duo 数据包包括设置适当的长度字段,以及在传入数据包的大小大于单个数据包有效负载时将多个数据包链接在一起。 正确生成后,会将 prepend_ptr 移动到物理层标头之后,并将接收数据包调度到 NetX Duo。

NetX Duo 假定 IP(IPv4 和 IPv6)和 ARP 标头在 ULONG 边界上对齐。 因此,NetX Duo 驱动程序必须确保此对齐方式。 在以太网环境中,这是通过从数据包开头的两个字节开始使用以太网标头来完成的。 将 nx_packet_prepend_ptr 移动到以太网标头之外时,基础 IP(IPv4 和 IPv6)或 ARP 标头采用 4 字节对齐。

警告

有关 IPv6 和 IPv6 以太网标头之间的重要差异,请参阅下面的“以太网标头”部分。

NetX Duo 中提供了几个接收数据包函数。 如果接收到的数据包是 ARP 数据包,则会调用 _nx_arp_packet_deferred_receive。 如果接收到的数据包是 RARP 数据包,则会调用 nx_rarp_packet_deferred_receive。 有几个选项可用于处理传入 IP 数据包。 为了以最快的速度处理 IP 数据包,将调用 nx_ip_packet_receive。 此方法的开销最少,但在驱动程序的接收中断服务处理程序 (ISR) 中需要更多处理。 对于最小 ISR 处理,将调用 _nx_ip_packet_deferred_receive

正确生成新的接收数据包后,将设置物理硬件的接收缓冲区以接收更多数据。 这可能需要分配 NetX Duo 数据包并在硬件接收缓冲区中放置有效负载地址,或者只需更改硬件接收缓冲区中的设置。 若要最大限度地减少溢出可能性,请务必在收到数据包后,尽快获取硬件的可用接收缓冲区。

重要

在驱动程序初始化过程中设置初始接收缓冲区。

延迟接收数据包处理

驱动程序可能会将接收数据包处理延迟到 NetX Duo IP 帮助程序线程。 对于某些应用程序,这可能是最大限度地减少 ISR 处理和丢弃的数据包所必需的。

若要使用延迟数据包处理,必须先在定义了 NX_DRIVER_DEFERRED_PROCESSING 的情况下编译 NetX Duo 库。 这会将延迟数据包逻辑添加到 NetX Duo IP 帮助程序线程。 接下来,在接收数据包时,驱动程序必须调用 _nx_ip_packet_deferred_receive():

_nx_ip_packet_deferred_receive(ip_ptr, packet_ptr);

延迟接收函数将 packet_ptr 表示的接收数据包放置在 FIFO(链接列表)中,并通知 IP 帮助程序线程。 执行后,IP 帮助程序重复调用延迟处理函数来处理每个延迟数据包。 延迟处理程序处理通常包括删除数据包的物理层标头(通常为以太网),并将其调度到以下 NetX Duo 接收函数之一:

  • _nx_ip_packet_receive
  • _nx_arp_packet_deferred_receive
  • _nx_rarp_packet_deferred_receive

以太网标头

以太网标头的 IPv6 和 IPv4 之间最显著的区别之一是帧类型设置。 发送数据包时,以太网驱动程序负责设置传出数据包中的以太网帧类型。 就 IPv6 数据包而言,帧类型应为 0x86DD;而对于 IPv4 数据包,帧类型应为 0x0800。

以下代码段演示了此过程:

NX_PACKET *packet_ptr;
packet_ptr = driver_req_ptr -> nx_ip_driver_packet;
if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V4)
{

   /* Set Ethernet frame type to IPv4 /*
   ethernet_frame_ptr -> frame_type = 0x0800;

   /* Swap endian-ness for little endian targets.*/
   NX_CHANGE_USHORT_ENDIAN(ethernet_frame_ptr -> frame_type);
}
else if (packet_ptr -> nx_packet_ip_version == NX_IP_VERSION_V6)
{

   /* Set Ethernet frame type to IPv6. /*
   ethernet_frame_ptr -> frame_type = 0x86DD;

   /* Swap endian-ness for little endian targets.*/
   NX_CHANGE_USHORT_ENDIAN(ethernet_frame_ptr -> frame_type);
}
else
{

   /* Unknown IP version. Free the packet we will not send. */
   nx_packet_transmit_release(packet_ptr);
}

同样,对于传入数据包,以太网驱动程序应根据以太网帧类型确定数据包类型。 应该实现这一点以接受 IPv6 (0x86DD)、IPv4 (0x0800)、ARP (0x0806) 和 RARP (0x8035) 帧类型。

示例 RAM 以太网网络驱动程序

NetX Duo 演示系统附带了一个基于 RAM 的小型网络驱动程序,该驱动程序在文件 nx_ram_network_driver.c 中定义。此驱动程序假设 IP 实例全部都位于同一网络上,只需在创建虚拟硬件地址(MAC 地址)后将其分配给每个设备实例。 此文件提供了 NetX Duo 物理网络驱动程序的基本结构的典型示例。 用户可以使用此示例中提供的驱动程序框架开发自己的网络驱动程序。

网络驱动程序的入口函数为 _nx_ram_network_driver(),该函数传递到 IP 实例创建调用。 其他网络接口的入口函数可以传递到 nx_ip_interface_attach() 服务。 在 IP 实例开始运行后,将调用驱动程序入口函数以初始化和启用设备(请参阅 NX_LINK_INITIALIZE 和 NX_LINK_ENABLE) 。 发出 NX_LINK_ENABLE 命令后,设备应准备好传输和接收数据包。

IP 实例通过以下命令之一传输网络数据包:

Command 说明
NX_LINK_PACKET_SEND 正在传输 IPv4 或 IPv6 数据包,
NX_LINK_ARP_SEND 正在传输 ARP 请求或 ARP 响应数据包,
NX_LINK_ARP_RARP_SEND 正在传输反向 ARP 请求或响应数据包,

处理这些命令时,网络驱动程序需要预置适当的以太网框架标头,然后将其发送到基础硬件进行传输。 在传输过程中,网络驱动程序具有数据包缓冲区的独占所有权。 因此,在传输数据后(或在将数据复制到驱动程序内部传输缓冲区后),网络驱动程序负责先将超过以太网标头的预置指针移动到 IP 标头(并相应地调整数据包长度),然后调用 nx_packet_transmit_release() 服务释放数据包,从而释放数据包缓冲区。 在数据传输后不释放数据包将导致数据包泄露。

网络设备驱动程序还负责处理传入数据包。 在 RAM 驱动程序示例中,接收到的数据包由函数 _nx_ram_network_driver_receive() 处理。 设备收到以太网帧后,驱动程序就会负责将数据存储在 NX_PACKET 结构中。 请注意,NetX Duo 假设 IP 标头以 4 字节对齐地址开头。 由于以太网标头的长度为 14 字节,因此驱动程序需要在 2 字节对齐地址上存储以太网标头的开头,以保证 IP 标头以 4 字节对齐地址开头。

TCP/IP 卸载驱动程序指南

对于 TCP/IP 卸载功能,每个 IP 接口都需要一个驱动程序函数。 下面是网络驱动程序的其他任务列表。

  • 对于命令 NX_LINK_INITIALIZE
    • 创建用于处理 TCP/IP 卸载接收事件的驱动程序线程。
  • 对于命令 NX_LINK_INTERFACE_ATTACH
    • 设置驱动程序接口的功能。 请参见下面的示例代码。
driver_req_ptr -> nx_ip_driver_interface -> nx_interface_capability_flag = NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD;
  • 对于命令 NX_LINK_ENABLE
    • 启动驱动程序线程。
    • 将 TCP/IP 回调函数设置为驱动程序接口。 请参见下面的示例代码。
driver_req_ptr -> nx_ip_driver_interface -> nx_interface_tcpip_offload_handler = _nx_driver_tcpip_handler;
  • 对于命令 NX_LINK_DISABLE
    • 停止驱动程序线程
    • 清除驱动程序接口的 TCP/IP 回调函数。
  • 对于命令 NX_LINK_UNINITIALIZE
    • 删除驱动程序线程

TCP/IP 卸载驱动程序线程

驱动程序线程的用途是接收传入的 TCP 或 UDP 数据包。 在驱动程序线程中,通常会有一个 while 循环来检查是有传入的 TCP 或 UDP 数据包可用还是连接已建立。 如果数据可用,则将 TCP 或 UDP 数据包传递给 NetX Duo。 nx_packet_data_startnx_packet_prepend_ptr 之间的空间必须足够,以便插入 TCP/IP 标头。 对于 TCP 套接字,请分配类型为 NX_TCP_PACKET 的数据包。 对于 UDP 套接字,请分配类型为 NX_UDP_PACKET 的数据包。 填写从 nx_packet_append_ptrnx_packet_data_end 的传入数据。 nx_packet_append_ptr 中的数据只能包含 TCP 或 UDP 有效负载。 数据包中不得包含 TCP/IP 标头。 调整数据包长度并设置接收接口,然后针对 TCP 数据包调用 _nx_tcp_socket_driver_packet_receive,针对 UDP 数据包调用 _nx_udp_socket_driver_packet_receive。 如果 TCP 连接已关闭,请调用 _nx_tcp_socket_driver_packet_receive 并将数据包设置为 NULL。 如果已建立连接,请调用 _nx_tcp_socket_driver_establish

TCP/IP 卸载驱动程序处理程序

对于使用 TCP/IP 服务的网络接口,需要以下驱动程序命令。

  • 对于操作 NX_TCPIP_OFFLOAD_TCP_CLIENT_SOCKET_CONNECT
    • 根据需要分配资源。
    • 绑定到本地 TCP 端口并连接到服务器。
    • 建立连接后返回成功。 正在连接时,返回 NX_IN_PROGRESS。 否则返回失败。
  • 对于操作 NX_TCPIP_OFFLOAD_TCP_SERVER_SOCKET_LISTEN
    • 首先检查是否有重复的侦听。 它可以在同一端口上被多次调用。 第一次从 nx_tcp_server_socket_listen 调用,然后从 nx_tcp_server_socket_relisten 调用。
    • 根据需要分配资源。
    • 侦听本地 TCP 端口。
  • 对于操作 NX_TCPIP_OFFLOAD_TCP_SERVER_SOCKET_ACCEPT
    • 根据需要分配资源。
    • 接受连接。
  • 对于操作 NX_TCPIP_OFFLOAD_TCP_SERVER_SOCKET_UNLISTEN
    • 查找正在本地端口上侦听的 TCP 套接字。
    • 如果有正在侦听的套接字,请将其关闭。
  • 对于操作 NX_TCPIP_OFFLOAD_TCP_SOCKET_DISCONNECT
    • 关闭 TCP/IP 卸载连接。
    • 取消绑定本地 TCP 端口。
    • 清理在连接期间创建的资源。
  • 对于操作 NX_TCPIP_OFFLOAD_TCP_SOCKET_SEND
    • 通过 TCP/IP 卸载发送数据。 准备好处理大于 MSS 的数据包长度或数据包链情况。
  • 对于操作 NX_TCPIP_OFFLOAD_UDP_SOCKET_BIND
    • 根据需要分配资源。
    • 绑定到本地 UDP 端口。
  • 对于操作 NX_TCPIP_OFFLOAD_UDP_SOCKET_UNBIND
    • 取消绑定本地 UDP 端口。
    • 清理在绑定期间创建的资源。
  • 对于操作 NX_TCPIP_OFFLOAD_UDP_SOCKET_SEND
    • 通过 TCP/IP 卸载发送数据。 准备好处理大于 MTU 的数据包长度或数据包链情况。