编写设备驱动程序

GLDv2 网络设备驱动程序框架

GLDv2 是多线程、可克隆、可装入的核心模块,为局域网的设备驱动程序提供支持。Solaris OS 中的局域网 (Local area network, LAN) 设备驱动程序是基于 STREAMS 的驱动程序,使用数据链路提供者接口 (Data Link Provider Interface, DLPI) 与网络协议栈进行通信。这些协议栈使用网络驱动程序在 LAN 中发送和接收包。GLDv2 为 Solaris LAN 驱动程序实现 STREAMS 和 DLPI 的很多功能。GLDv2 提供可供许多网络驱动程序共享的通用代码。使用 GLDv2 可减少重复的代码,简化您的网络驱动程序。

有关 GLDv2 的更多信息,请参见gld(7D)手册页。

《STREAMS Programming Guide》中的第 II 部分, “Kernel Interface”中介绍了 STREAMS 驱动程序。具体请参见 STREAMS 指南中的第 9 章,“STREAMS 驱动程序”。STREAMS 框架是基于消息的框架。STREAMS 驱动程序特有的接口包括 STREAMS 消息队列处理入口点。

DLPI 指定 OSI 参考模型数据链路层的数据链路服务 (Data Link Services, DLS) 的接口。 DLPI 使 DLS 用户能够访问和使用多种符合规定的 DLS 提供者,而无需专门确认提供者的协议。DLPI 以 M_PROTO 和 M_PCPROTO 类型的·STREAMS 消息的形式指定对 DLS 提供者的访问。DLPI 模块使用 STREAMS ioctl 调用链接至 MAC 子层。有关 DLPI 协议的更多信息,包括特定于 Solaris 的 DPLI 扩展,请参见dlpi(7P)手册页。有关 DLPI 的一般信息,请参见 DLPI 标准:http://www.opengroup.org/pubs/catalog/c811.htm

使用 GLDv2 实现的 Solaris 网络驱动程序有两个独立的部分:

GLDv2 驱动程序必须处理格式完全的 MAC 层包,不能执行逻辑链路控制 (logical link control, LLC) 处理。

本节讨论以下主题:

GLDv2 设备支持

GLDv2 框架支持以下类型的设备:

Ethernet V2 和 ISO 8802-3 (IEEE 802.3)

对于声明为 DL_ETHER 类型的设备,GLDv2 提供 Ethernet V2 和 ISO 8802-3 (IEEE 802.3) 包处理支持。通过 Ethernet V2,用户可以访问遵循该标准的数据链路服务提供方,而无需了解有关此提供者协议的专门知识。服务访问点 (service access point, SAP) 是用户用于与服务提供者进行通信的点。

绑定到范围为 [0-255] 的 SAP 值的流均视为相同的流,并表示用户要使用 8802-3 模式。如果 DL_BIND_REQ 的 SAP 值在此范围内,则 GLDv2 将计算该流上的每条后续 DL_UNITDATA_REQ 消息的长度。长度不包括 14 字节的介质访问控制 (media access control, MAC) 头。然后,GLDv2 将传输 MAC 帧标题 type 字段中具有这些长度的 8802-3 帧。这样的长度不超过 1500。

拥有范围为 [0-1500] 的 type 字段的帧被视为 8802-3 帧。 这些帧以 8802-3 模式沿所有打开的流进行路由。那些具有范围为 [0-255] 的 SAP 值的流均被视为处于 8802-3 模式下。如果多个流处于 8802-3 模式,则传入帧会被复制并沿这些流进行路由。

那些绑定到大于 1500 的 SAP 值的流均被假定为处于 Ethernet V2 模式下。这些流将接收以太网 MAC 头 type 值与流绑定到的 SAP 值完全匹配的传入包。

TPR 和 FDDI:SNAP 处理

对于 DL_TPR 和·DL_FDDI 介质类型,GLDv2 会实现最低限度的 SNAP(Sub-Net Access Protocol,子网访问协议)处理。此处理用于任何绑定到大于 255 的 SAP 值的流。范围为 [0-255] 的 SAP 值是 LLC SAP 值。此类值自然地通过介质包格式传送。大于 255 的 SAP 值需要 SNAP 头(从属于 LLC 头)来传送 16 位 Ethernet V2 样式的 SAP 值。

SNAP 头位于 LLC 头后面,目标是 SAP 0xAA。SAP 值大于 255 的外发包要求 LLC+SNAP 头采用以下形式:

AA AA 03 00 00 00 XX XX

XX XX 表示对应于 Ethernet V2 样式 type 的 16 位 SAP。在支持非零的组织唯一标识符字段方面,此头是独一无二的。03 之外的 LLC 控制字段被视为具有 SAP 0xAA 的 LLC 包。要使用此格式之外的 SNAP 格式的客户机必须使用 LLC 并绑定到 SAP 0xAA

将检查传入包是否符合上述格式。将符合的包与任何已绑定到包的 16 位 SNAP 类型的流进行匹配。此外,还将这些包视为与 LLC SNAP SAP 0xAA 相匹配。

针对任何 LLC SAP 接收的包将沿所有绑定到 LLC SAP 的流传递。相关内容,请参见介质类型 DL_ETHER 的介绍。

TPR:源路由

对于类型 DL_TPR 设备,GLDv2 实现最低限度的源路由支持。

源路由支持包括以下任务:

源路由会将路由信息字段添加到传出包的 MAC 头中。此外,此支持还可在传入包中识别此类字段。

GLDv2 的源路由支持并未实现 ISO 8802-2 (IEEE 802.2) 的第 9 节中指定的完整路由确定实体 (route determination entity, RDE)。但是,此支持可以与任何可能存在于同一网络或桥接网络中的 RDE 实现进行交互操作。

GLDv2 DLPI 提供者

GLDv2 实现样式 1 和样式 2 的 DLPI 提供者。物理连接点 (physical point of attachment, PPA) 是系统将自身附加到物理通信介质的点。在此物理介质上进行的所有通信都通过 PPA。样式 1 提供者根据已打开的主设备或次要设备,将流附加到特定 PPA。样式 2 提供者需要 DLS 用户使用 DL_ATTACH_REQ 显式指定所需的 PPA。在这种情况下,open(9E) 会在用户与 GLDv2 之间创建流,并且 DL_ATTACH_REQ 随后会将特定的 PPA 与该流相关联。样式 2 由次要设备号 0 表示。如果打开了次要设备号不为 0 的设备节点,则会指示样式 1,并且关联的 PPA 为次要设备号减 1。在样式 1 和样式 2 启动中,都会克隆设备。

GLDv2 DLPI 原语

GLDv2 实现某些 DLPI 原语。DL_INFO_REQ 原语请求有关 DLPI 流的信息。消息包含一个 M_PROTO 消息块。GLDv2 将在对此请求的 DL_INFO_ACK 响应中返回与设备有关的值。这些值以基于 GLDv2 的驱动程序在传递给 gld_register(9F) 函数的 gld_mac_info(9S) 结构中指定的信息为基础。

GLDv2 代表所有基于 GLDv2 驱动程序返回以下值:


注 –

与 DLPI 规范相反,即使在将流附加到 PPA 之前,GLDv2 也会在 DL_INFO_ACK 中返回设备的正确地址长度和广播地址。


DL_ATTACH_REQ 原语用于将 PPA 与流相关联。样式 2 DLS 提供者标识用于发送通信的物理介质时需要此请求。完成时,状态会从 DL_UNATTACHED 更改为 DL_UNBOUND。消息包含一个 M_PROTO 消息块。使用样式 1 模式时,不允许此请求。使用样式 1 打开的流已在打开完成时附加到 PPA。

DL_DETACH_REQ 原语请求将 PPA 与流相分离。仅当流使用样式 2 打开时,才允许此分离。

DL_BIND_REQDL_UNBIND_REQ 原语用于将 DLSAP(data link service access point,数据链路服务访问点)与流绑定和解除绑定。 与流关联的 PPA 在完成处理该流上的 DL_BIND_REQ 之前完成初始化。可以将多个流绑定到同一 SAP。在这种情况下,每个流都接收针对此 SAP 接收的所有包的副本。

DL_ENABMULTI_REQDL_DISABMULTI_REQ 原语启用和禁用各多点传送组地址的接收。通过重复使用这些原语,应用程序或其他 DLS 用户可以创建或修改一组多点传送地址。必须将流附加到 PPA 才能接受这些原语。

DL_PROMISCON_REQ 和·DL_PROMISCOFF_REQ 原语以每个流为基础启动和关闭混杂模式。这些控制项既可在物理级别运行,也可在 SAP 级别运行。DL 提供者通过介质将所有已接收的消息路由到 DLS 用户。路由将继续进行,直到收到 DL_DETACH_REQ、收到 DL_PROMISCOFF_REQ 或关闭流为止。可以针对介质上的所有包或仅针对多点传送包指定物理级别混杂接收。


注 –

必须将流附加到 PPA 才能接受这些混杂模式原语。


DL_UNITDATA_REQ 原语用于在无连接传输中发送数据。由于此服务未得到确认,因此无法保证传送。消息包含一个 M_PROTO 消息块,以及一个或多个至少包含一个字节数据的 M_DATA 块。

在包向上游传递时,会使用 DL_UNITDATA_IND 类型。将包放入 M_PROTO 消息中,并将原语设置为 DL_UNITDATA_IND

DL_PHYS_ADDR_REQ 原语请求当前与附加到流的 PPA 相关联的 MAC 地址。此地址将通过 DL_PHYS_ADDR_ACK 原语返回。使用样式 2 时,仅当成功执行 DL_ATTACH_REQ 之后此原语才有效。

DL_SET_PHYS_ADDR_REQ 原语更改当前与附加到流的 PPA 相关联的 MAC 地址。 此原语会影响附加到此设备的所有其他当前流和将来流。更改之后,当前或随后打开并附加到此设备的所有流都将获取这一新的物理地址。在此原语再次更改物理地址或重新装入驱动程序之前,新的物理地址将一直有效。


注 –

允许超级用户在其他流绑定到 PPA 时更改同一 PPA 的物理地址。


DL_GET_STATISTICS_REQ 原语请求包含与附加到流的 PPA 相关的统计信息的 DL_GET_STATISTICS_ACK 响应。必须使用 DL_ATTACH_REQ 将样式 2 流附加到特定 PPA,此原语才能成功执行。

GLDv2 I/O 控制函数

GLDv2 可实现以下将介绍的 ioctl ioc_cmd 函数。如果 GLDv2 收到无法识别的 ioctl 命令,则 GLDv2 会将此命令传递给特定于设备的驱动程序的 gldm_ioctl() 例程,如 gld(9E) 中所述。

DLIOCRAW ioctl 函数可供某些 DLPI 应用程序(尤其是 snoop(1M) 命令)使用。DLIOCRAW 命令可将流置于原始模式。在原始模式下,驱动程序将在 M_DATA 消息中传递 MAC 级别的完整传入包,而不是将包转换为 DL_UNITDATA_IND 形式。DL_UNITDATA_IND 形式通常用于报告传入包。包 SAP 过滤仍将在处于原始模式下的流上执行。如果流用户要接收所有传入包,则此用户还必须选择相应的混杂模式。成功选择原始模式之后,还允许应用程序将完全格式化的包作为 M_DATA 消息发送到驱动程序以便传输。DLIOCRAW 不使用任何参数。启用之后,流将保持此模式直到关闭。

GLDv2 驱动程序需求

基于 GLDv2 的驱动程序必须包括头文件 <sys/gld.h>

基于 GLDv2 的驱动程序必须与 -N"misc/gld" 选项链接:

%ld -r -N"misc/gld" xx.o -o xx

GLDv2 代表特定于设备的驱动程序实现以下函数:

module_info(9S) 结构的 mi_idname 元素是用于指定驱动程序名称的字符串。此字符串必须与文件系统中定义的驱动程序模块的名称完全匹配。

读端 qinit(9S) 结构应该指定以下元素:

qi_putp

NULL

qi_srvp

gld_rsrv

qi_qopen

gld_open

qi_qclose

gld_close

写端 qinit(9S) 结构应该指定以下元素:

qi_putp

gld_wput

qi_srvp

gld_wsrv

qi_qopen

NULL

qi_qclose

NULL

dev_ops(9S) 结构的 devo_getinfo 元素应该将 gld_getinfo 指定为 getinfo(9E) 例程。

驱动程序的 attach(9E) 函数会将特定于硬件的设备驱动程序与 GLDv2 功能相关联。然后,attach() 将准备设备和驱动程序以供使用。

attach(9E) 函数使用 gld_mac_alloc() 分配 gld_mac_info(9S) 结构。通常,驱动程序需要针对每台设备保存的信息比在 macinfo 结构中定义的信息多。驱动程序应该分配其他必需的数据结构,并在 gld_mac_info(9S) 结构的 gldm_private 成员中保存指向该结构的指针。

attach(9E) 例程必须初始化 macinfo 结构,如 gld_mac_info(9S) 手册页中所述。然后,attach() 例程应该调用 gld_register() 以将驱动程序与 GLDv2 模块相链接。驱动程序应该在必要时映射寄存器,并在调用 gld_register() 之前完全初始化以准备接受中断。attach(9E) 函数应该添加中断,而不应该使设备生成这些中断。驱动程序应该在调用 gld_register() 之前重置硬件,以确保硬件处于停顿状态。不得将设备置于其可能会在调用 gld_register() 之前生成中断的状态。稍后会在 GLDv2 调用驱动程序的 gldm_start() 入口点时启动设备,相关内容在 gld(9E) 手册页中介绍。gld_register() 成功执行之后,GLDv2 可能会随时调用 gld(9E) 入口点。

如果 gld_register() 成功执行,则 attach(9E) 例程应该返回 DDI_SUCCESS。如果 gld_register() 失败,则会返回 DDI_FAILURE。如果出现故障,则 attach(9E) 例程应该解除分配在调用 gld_register() 之前分配的所有资源。然后,连接例程还应该返回 DDI_FAILURE。绝不能重新使用出现故障的 macinfo 结构。应该使用 gld_mac_free() 解除分配此类结构。

detach(9E) 函数应尝试通过调用 gld_unregister() 来尝试从 GLDv2 中取消注册驱动程序。有关 gld_unregister() 的更多信息,请参见 gld(9F) 手册页。detach(9E) 例程可以使用 ddi_get_driver_private(9F) 从设备的专用数据中获取指向所需 gld_mac_info(9S) 结构的指针。gld_unregister() 将检查可能要求不分离驱动程序的特定条件。如果检查失败,则 gld_unregister() 会返回 DDI_FAILURE,在这种情况下,驱动程序的 detach(9E) 例程必须保持设备处于运行状态并返回 DDI_FAILURE

如果检查成功,则 gld_unregister() 会确保停止设备中断。如有必要,则会调用驱动程序的 gldm_stop() 例程。驱动程序将与 GLDv2 框架解除链接。然后,gld_unregister() 会返回 DDI_SUCCESS。在这种情况下,detach(9E) 例程应该删除中断,并使用 gld_mac_free() 解除分配在 attach(9E) 例程中分配的所有 macinfo 数据结构。然后,detach() 例程应该返回 DDI_SUCCESS。此例程必须在调用 gld_mac_free() 之前删除中断。

GLDv2 网络统计信息

Solaris 网络驱动程序必须实现统计变量。GLDv2 可记录一些网络统计信息,但是其他统计信息必须由基于 GLDv2 的每个驱动程序进行计数。GLDv2 为基于 GLDv2 的驱动程序提供支持,以报告一组标准的网络驱动程序统计信息。GLDv2 使用 kstat(7D)kstat(9S) 机制报告统计信息。DL_GET_STATISTICS_REQ DLPI 命令还可用于检索当前统计计数器。所有统计信息均以无符号数据进行维护。除非另有说明,否则统计信息为 32 位。

GLDv2 维护并报告以下统计信息。

rbytes64

已在接口上成功接收的总字节数。将存储 64 位统计信息。

rbytes

已在接口上成功接收的总字节数。

obytes64

已请求在接口上传输的总字节数。将存储 64 位统计信息。

obytes

已请求在接口上传输的总字节数。

ipackets64

已在接口上成功接收的总包数。将存储 64 位统计信息。

ipackets

已在接口上成功接收的总包数。

opackets64

已请求在接口上传输的总包数。将存储 64 位统计信息。

opackets

已请求在接口上传输的总包数。

multircv

已成功接收的多点传送包,包括组和功能地址 (functional address) (long)。

multixmt

已请求传输的多点传送包,包括组和功能地址 (functional address) (long)。

brdcstrcv

已成功接收的广播包 (long)。

brdcstxmt

已请求传输的广播包 (long)。

unknowns

未由任何流接受的有效已接收包 (long)。

noxmtbuf

由于传输缓冲区繁忙或无法分配传输缓冲区而在输出中放弃的包 (long)。

blocked

由于队列受控于流而使已接收的包无法沿流放置的次数 (long)。

xmtretry

在由于资源不足而延迟之后重试传输的次数 (long)。

promisc

接口的当前“混杂”状态(字符串)。

与设备有关的驱动程序将在每个实例的专用结构中跟踪以下统计信息。为了报告统计信息,GLDv2 将调用驱动程序的 gldm_get_stats() 入口点。然后,gldm_get_stats() 会在 gld_stats(9S)有关更多信息,请参见 gldm_get_stats(9E) 手册页。然后,GLDv2 将使用如下所示的命名统计变量报告已更新的统计信息。

ifspeed

接口的当前估算带宽(以 bps 为单位)。将存储 64 位统计信息。

media

设备正在使用的当前介质类型(字符串)。

intr

调用中断处理程序从而导致中断的次数 (long)。

norcvbuf

由于无法分配接收缓冲区而放弃某个有效传入包的次数 (long)。

ierrors

已接收但由于错误而无法处理的总包数 (long)。

oerrors

由于错误而无法成功传输的总包数 (long)。

missed

硬件在接收时已丢弃的包数 (long)。

uflo

传输时 FIFO 下溢的次数 (long)。

oflo

接收期间接收器下溢的次数 (long)。

以下统计信息组适用于类型为 DL_ETHER 的网络。这些统计信息由此类型的特定于设备的驱动程序维护,如上所示。

align_errors

在出现帧错误时接收的包数,即这些包不包含整数个数的八位字节 (long)。

fcs_errors

在出现 CRC 错误时接收的包数 (long)。

duplex

接口的当前双工模式(字符串)。

carrier_errors

在尝试传输时丢失载体或从未检测到载体的次数 (long)。

collisions

传输期间的以太网冲突数 (long)。

ex_collisions

传输时出现过多的冲突而导致传输失败的帧数 (long)。

tx_late_collisions

在过了一段时间后(即过了 512 位时后)发生传输冲突的次数 (long)。

defer_xmts

没有发生由于介质繁忙而延迟首次传输尝试冲突的包数 (long)。

first_collisions

在仅发生一个冲突后成功传输的包数。

multi_collisions

在发生多个冲突后成功传输的包数。

sqe_errors

已报告 SQE 测试错误的次数。

macxmt_errors

遇到传输 MAC 故障(载体和冲突故障除外)的包数。

macrcv_errors

在出现 MAC 错误(align_errorsfcs_errorstoolong_errors 除外)时接收的包数。

toolong_errors

接收的大于最大允许长度的包数。

runt_errors

接收的小于最小允许长度的包数 (long)。

以下统计信息组适用于类型为 DL_TPR 的网络。这些统计信息由此类型的特定于设备的驱动程序维护,如上所示。

line_errors

在出现非数据位或 FCS 错误时接收的包数。

burst_errors

已针对五个半位计时器检测到不存在转换的次数。

signal_losses

在环上检测到信号丢失情况的次数。

ace_errors

AMP 或 SMP 帧(其中 A 等于 C 等于 0)后跟其他 SMP 帧而没有中间 AMP 帧的次数。

internal_errors

站识别到内部错误的次数。

lost_frame_errors

传输期间 TRR 计时器到期的次数。

frame_copied_errors

在 FS 字段 'A' 位设置为 1 时接收发往此站的帧的次数。

token_errors

用作活动监视器的站识别到需要已传输标记的错误情况的次数。

freq_errors

传入信号的频率不同于预期频率的次数。

以下统计信息组适用于类型为 DL_FDDI 的网络。这些统计信息由此类型的特定于设备的驱动程序维护,如上所示。

mac_errors

由该 MAC 检测到出现错误但是尚未由其他 MAC 检测到出现错误的帧数。

mac_lost_errors

在出现格式错误从而删除帧时接收的帧数。

mac_tokens

已接收的标记数,即不受限制和受限制的总标记数。

mac_tvx_expired

TVX 已到期的次数。

mac_late

自重置此 MAC 或接收标记以来的 TRT 到期次数。

mac_ring_ops

环从“环未运行”状态进入“环运行”状态的次数。

GLDv2 声明和数据结构

本节介绍 gld_mac_info(9S) 和 gld_stats 结构。

gld_mac_info 结构

GLDv2 MAC 信息 (gld_mac_info) 结构是用于链接特定于设备的驱动程序与 GLDv2 的主数据接口。此结构包含 GLDv2 所需的数据,以及指向可选的其他特定于驱动程序的信息结构的指针。

可使用 gld_mac_alloc 分配 gld_mac_info() 结构,可使用 gld_mac_free() 解除分配此结构。驱动程序不能做出有关此结构长度的任何假设,因为此长度在不同发行版的 Solaris OS 和/或 GLDv2 中可能会有所不同。专用于 GLDv2 的结构成员(未在此处介绍)既不应该由特定于设备的驱动程序设置,也不应该由其读取。

gld_mac_info(9S) 结构包含以下字段。

caddr_t              gldm_private;              /* Driver private data */
int                  (*gldm_reset)();           /* Reset device */
int                  (*gldm_start)();           /* Start device */
int                  (*gldm_stop)();            /* Stop device */
int                  (*gldm_set_mac_addr)();    /* Set device phys addr */
int                  (*gldm_set_multicast)();   /* Set/delete multicast addr */
int                  (*gldm_set_promiscuous)(); /* Set/reset promiscuous mode */
int                  (*gldm_send)();            /* Transmit routine */
uint_t               (*gldm_intr)();            /* Interrupt handler */
int                  (*gldm_get_stats)();       /* Get device statistics */
int                  (*gldm_ioctl)();           /* Driver-specific ioctls */
char                 *gldm_ident;               /* Driver identity string */
uint32_t             gldm_type;                 /* Device type */
uint32_t             gldm_minpkt;               /* Minimum packet size */
                                                /* accepted by driver */
uint32_t             gldm_maxpkt;               /* Maximum packet size */
                                                /* accepted by driver */
uint32_t             gldm_addrlen;              /* Physical address length */
int32_t              gldm_saplen;               /* SAP length for DL_INFO_ACK */
unsigned char        *gldm_broadcast_addr;      /* Physical broadcast addr */
unsigned char        *gldm_vendor_addr;         /* Factory MAC address */
t_uscalar_t          gldm_ppa;                  /* Physical Point of */
                                                /* Attachment (PPA) number */
dev_info_t           *gldm_devinfo;             /* Pointer to device's */
                                                /* dev_info node */
ddi_iblock_cookie_t  gldm_cookie;               /* Device's interrupt */
                                                /* block cookie */

gldm_private 结构成员对设备驱动程序可见。gldm_private 还专用于特定于设备的驱动程序。GLDv2 无法使用或修改 gldm_private。通常,gldm_private 用作指向专用数据的指针,指向同时由驱动程序定义和分配的每个实例的数据结构。

以下结构成员组必须由驱动程序在调用 gld_register() 之前设置,并且此后不应该由驱动程序进行修改。由于 gld_register() 可能会使用或高速缓存结构成员的值,因此,驱动程序在调用 gld_register() 之后进行的更改可能会导致不可预测的结果。有关这些结构的更多信息,请参见 gld(9E) 手册页。

gldm_reset

指向驱动程序入口点的指针。

gldm_start

指向驱动程序入口点的指针。

gldm_stop

指向驱动程序入口点的指针。

gldm_set_mac_addr

指向驱动程序入口点的指针。

gldm_set_multicast

指向驱动程序入口点的指针。

gldm_set_promiscuous

指向驱动程序入口点的指针。

gldm_send

指向驱动程序入口点的指针。

gldm_intr

指向驱动程序入口点的指针。

gldm_get_stats

指向驱动程序入口点的指针。

gldm_ioctl

指向驱动程序入口点的指针。允许此指针为 null。

gldm_ident

指向包含设备简短说明的字符串的指针。此指针用于在系统消息中标识设备。

gldm_type

驱动程序处理的设备的类型。GLDv2 目前支持以下值:

  • DL_ETHER(ISO 8802-3 (IEEE 802.3) 和以太网总线)

  • DL_TPR(IEEE 802.5 令牌传递环)

  • DL_FDDI(ISO 9314-2 光纤分布式数据接口)

必须正确设置此结构成员,GLDv2 才能正常运行。

gldm_minpkt

最小服务数据单元大小:设备可以传输的最小包大小(不包括 MAC 头)。如果特定于设备的驱动程序处理任何所需的填充,则允许此大小为 0。

gldm_maxpkt

最大服务数据单元大小:设备可以传输的最大包大小(不包括 MAC 头)。对于以太网,此数值为 1500。

gldm_addrlen

设备所处理的物理地址的长度(以字节为单位)。对于以太网、令牌环和 FDDI,此结构成员的值应该为 6。

gldm_saplen

驱动程序所使用的 SAP 地址的长度(以字节为单位)。对于基于 GLDv2 的驱动程序,应该始终将长度设置为 -2。长度为 -2 表示支持 2 个字节的 SAP 值,并且 SAP 出现在 DLSAP 地址中的物理地址之后。有关更多详细信息,请参见 DLPI 规范中的附录 A.2“消息 DL_INFO_ACK”。

gldm_broadcast_addr

指向长度 gldm_addrlen 字节数组的指针,该数组包含要用于传输的广播地址。驱动程序必须提供保存广播地址的空间,使用相应的值填充此空间,并将 gldm_broadcast_addr 设置为指向此地址。对于以太网、令牌环和 FDDI,广播地址通常为 0xFF-FF-FF-FF-FF-FF

gldm_vendor_addr

指向长度为 gldm_addrlen 字节的数组的指针,该数组包含供应商提供的设备网络物理地址。驱动程序必须提供保存地址的空间,使用来自设备的信息填充此空间,并将 gldm_vendor_addr 设置为指向此地址。

gldm_ppa

此设备实例的 PPA 编号。应该始终将 PPA 编号设置为从 ddi_get_instance(9F) 返回的实例编号。

gldm_devinfo

指向此设备的 dev_info 节点的指针。

gldm_cookie

以下例程之一返回的中断块 cookie:

此 cookie 必须对应于设备的接收中断,可从该中断中调用 gld_recv()

gld_stats 结构

调用 gldm_get_stats() 之后,基于 GLDv2 的驱动程序会使用 (gld_stats) 结构将统计信息和状态信息传递给 GLDv2。请参见 gld(9E) 和 gld(7D) 手册页。当 GLDv2 报告统计信息时,将使用已由基于 GLDv2 的驱动程序填充的此结构的成员。在下面各表的注释中说明了 GLDv2 所报告的统计变量的名称。有关每条统计信息含义的更详细说明,请参见 gld(7D) 手册页。

驱动程序不得做出有关此结构长度的任何假设。此结构长度在不同发行版的 Solaris OS 和/或 GLDv2 中可能会有所不同。专用于 GLDv2 的结构成员(未在此处介绍)既不应该由特定于设备的驱动程序设置,也不应该由其读取。

针对所有介质类型定义了以下结构成员:

uint64_t    glds_speed;                   /* ifspeed */
uint32_t    glds_media;                   /* media */
uint32_t    glds_intr;                    /* intr */
uint32_t    glds_norcvbuf;                /* norcvbuf */
uint32_t    glds_errrcv;                  /* ierrors */
uint32_t    glds_errxmt;                  /* oerrors */
uint32_t    glds_missed;                  /* missed */
uint32_t    glds_underflow;               /* uflo */
uint32_t    glds_overflow;                /* oflo */

针对介质类型 DL_ETHER 定义了以下结构成员:

uint32_t    glds_frame;                   /* align_errors */
uint32_t    glds_crc;                     /* fcs_errors */
uint32_t    glds_duplex;                  /* duplex */
uint32_t    glds_nocarrier;               /* carrier_errors */
uint32_t    glds_collisions;              /* collisions */
uint32_t    glds_excoll;                  /* ex_collisions */
uint32_t    glds_xmtlatecoll;             /* tx_late_collisions */
uint32_t    glds_defer;                   /* defer_xmts */
uint32_t    glds_dot3_first_coll;         /* first_collisions */
uint32_t    glds_dot3_multi_coll;         /* multi_collisions */
uint32_t    glds_dot3_sqe_error;          /* sqe_errors */
uint32_t    glds_dot3_mac_xmt_error;      /* macxmt_errors */
uint32_t    glds_dot3_mac_rcv_error;      /* macrcv_errors */
uint32_t    glds_dot3_frame_too_long;     /* toolong_errors */
uint32_t    glds_short;                   /* runt_errors */

针对介质类型 DL_TPR 定义了以下结构成员:

uint32_t    glds_dot5_line_error          /* line_errors */
uint32_t    glds_dot5_burst_error         /* burst_errors */
uint32_t    glds_dot5_signal_loss         /* signal_losses */
uint32_t    glds_dot5_ace_error           /* ace_errors */
uint32_t    glds_dot5_internal_error      /* internal_errors */
uint32_t    glds_dot5_lost_frame_error    /* lost_frame_errors */
uint32_t    glds_dot5_frame_copied_error  /* frame_copied_errors */
uint32_t    glds_dot5_token_error         /* token_errors */
uint32_t    glds_dot5_freq_error          /* freq_errors */

针对介质类型 DL_FDDI 定义了以下结构成员:

uint32_t    glds_fddi_mac_error;          /* mac_errors */
uint32_t    glds_fddi_mac_lost;           /* mac_lost_errors */
uint32_t    glds_fddi_mac_token;          /* mac_tokens */
uint32_t    glds_fddi_mac_tvx_expired;    /* mac_tvx_expired */
uint32_t    glds_fddi_mac_late;           /* mac_late */
uint32_t    glds_fddi_mac_ring_op;        /* mac_ring_ops */

上述大多数统计变量均为表示发现特定事件次数的计数器。以下统计信息不表示次数:

glds_speed

接口的当前带宽估算(以 bps 为单位)。此对象应该包含那些带宽不变或无法进行准确估算的接口的标称带宽。

glds_media

硬件所使用的介质(连线)或连接器的类型。支持以下介质名称:

  • GLDM_AUI

  • GLDM_BNC

  • GLDM_TP

  • GLDM_10BT

  • GLDM_100BT

  • GLDM_100BTX

  • GLDM_100BT4

  • GLDM_RING4

  • GLDM_RING16

  • GLDM_FIBER

  • GLDM_PHYMII

  • GLDM_UNKNOWN

glds_duplex

接口的当前双工状态。支持的值包括 GLD_DUPLEX_HALFGLD_DUPLEX_FULL。此外,还允许 GLD_DUPLEX_UNKNOWN

GLDv2 函数参数

GLDv2 例程使用以下参数。

macinfo

指向 gld_mac_info(9S) 结构的指针。

macaddr

指向包含有效 MAC 地址的字符数组的开头的指针。此数组的长度由驱动程序在 gld_mac_info(9S) 结构的 gldm_addrlen 元素中指定。

multicastaddr

指向包含多点传送、组或功能地址 (functional address) 的字符数组的起始地址的指针。此数组的长度由驱动程序在 gld_mac_info(9S) 结构的 gldm_addrlen 元素中指定。

multiflag

指示是启用还是禁用多点传送地址接收的标志。可将此参数指定为 GLD_MULTI_ENABLEGLD_MULTI_DISABLE

promiscflag

指示要启用何种类型的混杂模式(如果存在)的标志。可将此参数指定为 GLD_MAC_PROMISC_PHYSGLD_MAC_PROMISC_MULTIGLD_MAC_PROMISC_NONE

mp

gld_ioctl() 使用 mp 作为指向包含要执行的 ioctl 的 STREAMS 消息块的指针。gldm_send() 使用 mp 作为指向包含要传输的包的 STREAMS 消息块的指针。gld_recv() 使用 mp 作为指向包含已接收包的消息块的指针。

stats

指向要使用统计计数器的当前值填充的 gld_stats(9S) 结构的指针。

q

指向要用于 ioctl 回复的 queue(9S) 结构的指针。

dip

指向设备的 dev_info 结构的指针。

name

设备接口名称。

GLDv2 入口点

入口点必须通过针对使用 GLDv2 的接口设计的特定于设备的网络驱动程序实现。

gld_mac_info(9S) 结构是用于在特定于设备的驱动程序与 GLDv2 模块之间进行通信的主结构。请参见 gld(7D) 手册页。此结构中的某些元素是指向此处所述的入口点的函数指针。特定于设备的驱动程序必须在调用 gld_register() 之前在其 attach(9E) 例程中初始化这些函数指针。

gldm_reset() 入口点

int prefix_reset(gld_mac_info_t *macinfo);

gldm_reset() 可将硬件重置为初始状态。

gldm_start() 入口点

int prefix_start(gld_mac_info_t *macinfo);

gldm_start() 可使设备生成中断。gldm_start() 还可使驱动程序调用 gld_recv(),从而将已接收的数据包传送到 GLDv2。

gldm_stop() 入口点

int prefix_stop(gld_mac_info_t *macinfo);

gldm_stop() 禁止设备生成任何中断,并阻止驱动程序为将数据包传送到 GLDv2 而调用 gld_recv()。GLDv2 依赖 gldm_stop() 例程来确保设备不再中断。gldm_stop() 必须成功执行此操作。此函数应该始终返回 GLD_SUCCESS

gldm_set_mac_addr() 入口点

int prefix_set_mac_addr(gld_mac_info_t *macinfo, unsigned char *macaddr);

gldm_set_mac_addr() 可设置硬件用于接收数据的物理地址。利用此函数,可通过已传递的 MAC 地址 macaddr 对设备进行编程。如果当前没有足够的资源来执行请求,则 gldm_set_mac_add() 应该返回 GLD_NORESOURCES。如果不支持所请求的函数,则 gldm_set_mac_add() 应该返回 GLD_NOTSUPPORTED

gldm_set_multicast() 入口点

int prefix_set_multicast(gld_mac_info_t *macinfo, 
     unsigned char *multicastaddr, int multiflag);

gldm_set_multicast() 可启用和禁用设备级别的特定多点传送地址接收。如果将第三个参数 multiflag 设置为GLD_MULTI_ENABLE,则 gldm_set_multicast() 会将接口设置为使用多点传送地址接收包。gldm_set_multicast() 将使用第二个参数所指向的多点传送地址。如果将 multiflag 设置为 GLD_MULTI_DISABLE,则允许驱动程序禁用指定的多点传送地址接收。

当 GLDv2 要启用或禁用多点传送、组或功能地址 (functional address) 接收时,便会调用此函数。GLDv2 不会做出有关设备如何支持多点传送并调用此函数以启用或禁用特定多点传送地址的假设。某些设备可能会使用散列算法和位掩码来启用多点传送地址集合。将允许此过程,并且 GLDv2 会过滤出所有多余的包。如果在设备级别禁用一个地址会导致禁用多个地址,则设备驱动程序应该保留所有必要信息。此方法可避免禁用 GLDv2 已启用但未禁用的地址。

不能调用 gldm_set_multicast() 来启用已启用的特定多点传送地址。同样,也不能调用 gldm_set_multicast() 来禁用当前未启用的地址。GLDv2 将跟踪针对同一多点传送地址发出的多个请求。GLDv2 仅在第一次请求启用特定多点传送地址或最后一次请求禁用特定多点传送地址时才调用驱动程序的入口点。如果当前没有足够的资源来执行请求,则函数应该返回 GLD_NORESOURCES。如果不支持所请求的函数,则函数应该返回 GLD_NOTSUPPORTED

gldm_set_promiscuous() 入口点

int prefix_set_promiscuous(gld_mac_info_t *macinfo, int promiscflag);

gldm_set_promiscuous() 可启用和禁用混杂模式。当 GLDv2 要启用或禁用介质上的所有包接收时,便会调用此函数。还可以将此函数限制为针对介质上的多点传送包使用。如果将第二个参数 promiscflag 设置为 GLD_MAC_PROMISC_PHYS 的值,则函数会启用物理级别的混杂模式。物理级别的混杂模式会导致接收介质上的所有包。如果将 promiscflag 设置为 GLD_MAC_PROMISC_MULTI,则会启用所有多点传送包接收。如果将 promiscflag 设置为 GLD_MAC_PROMISC_NONE,则会禁用混杂模式。

在混杂多点传送模式下,无仅限多点传送混杂模式的设备的驱动程序必须将设备设置为物理混杂模式。此方法可确保接收所有多点传送包。在这种情况下,例程应该返回 GLD_SUCCESS。GLDv2 软件会过滤出所有多余的包。如果当前没有足够的资源来执行请求,则函数应该返回 GLD_NORESOURCES。如果不支持所请求的函数,gld_set_promiscuous() 应该返回 GLD_NOTSUPPORTED

为了向前兼容,gldm_set_promiscuous() 例程应该处理所有 promiscflag 无法识别的值,如同这些值为 GLD_MAC_PROMISC_PHYS 一样。

gldm_send() 入口点

int prefix_send(gld_mac_info_t *macinfo, mblk_t *mp);

gldm_send() 可将要发送到设备的包进行排队以进行传输。将向此例程传递包含要发送的包的 STREAMS 消息。此消息可能包括多个消息块。send() 例程必须遍历消息中的所有消息块,以访问要发送的整个包。应对驱动程序进行适当设置,以便处理并跳过链表中所有零长度的消息连续块。驱动程序还应该检查包是否未超过最大允许包大小。如有必要,驱动程序必须将包填充到最小允许包大小。如果发送例程成功传输包或对包排队,则应该返回 GLD_SUCCESS

如果无法立即接受要传输的包,则发送例程应该返回 GLD_NORESOURCES。在这种情况下,GLDv2 会稍后重试。如果 gldm_send() 曾经返回 GLD_NORESOURCES,则驱动程序必须在稍后资源变得可用时调用 gld_sched()。此 gld_sched() 调用会通知 GLDv2 重试驱动程序先前无法对其进行排队以进行传输的包。(如果调用驱动程序的 gldm_stop() 例程,则会为驱动程序免除这种职责,直到驱动程序从 gldm_send() 例程返回 GLD_NORESOURCES 为止。不过,再次调用 gld_sched() 也不会导致错误操作。)

如果驱动程序的发送例程返回 GLD_SUCCESS,则驱动程序会负责释放不再需要的消息。如果硬件使用 DMA 直接读取数据,则驱动程序在硬件完全读取数据之前不得释放消息。在这种情况下,驱动程序可以释放中断例程中的消息。或者,驱动程序可以在将来发送操作的开始回收缓冲区。如果发送例程返回 GLD_SUCCESS 之外的任何内容,则驱动程序不得释放消息。如果在不存在到网络或链路伙伴的物理连接时调用 gldm_send(),则会返回 GLD_NOLINK

gldm_intr() 入口点

int prefix_intr(gld_mac_info_t *macinfo);

当设备可能已中断时,便会调用 gldm_intr()。由于其他设备可以共享中断,因此,驱动程序必须检查设备状态以确定此设备是否实际导致了中断。如果驱动程序所控制的设备并未导致中断,则此例程必须返回 DDI_INTR_UNCLAIMED。否则,驱动程序必须修复中断并返回 DDI_INTR_CLAIMED。如果中断由成功接收包导致,则此例程应该将已接收的包放入类型为 M_DATA 的 STREAMS 消息中,并将此消息传递给 gld_recv()

gld_recv() 将传入包向上游传递到网络协议栈的相应下一层。在调用 gld_recv 之前,此例程必须正确设置 STREAMS 消息的 b_rptrb_wptr() 成员。

在调用 gld_recv() 期间,驱动程序应该避免持有互斥锁或其他锁。需要特别指出的是,在调用 gld_recv() 期间,不得持有传输线程可使用的锁。在某些情况下,调用 gld_recv() 的中断线程可发送传出包,这会导致调用驱动程序的 gldm_send() 例程。如果在调用 gld_recv()gldm_send() 尝试获取 gldm_intr() 持有的互斥锁,则会由于存在递归互斥锁入口操作而出现紧急情况。在调用 gld_recv() 时,如果其他驱动程序入口点尝试获取驱动程序所持有的互斥锁,则会导致死锁。

中断代码应该针对所有错误递增统计计数器。这些错误包括分配已接收数据所需的缓冲区时出现的故障,以及所有特定于硬件的错误(如 CRC 错误或帧错误)。

gldm_get_stats() 入口点

int prefix_get_stats(gld_mac_info_t *macinfo, struct gld_stats *stats);

gldm_get_stats() 可收集硬件和/或驱动程序专用计数器的统计信息,并更新 stats 所指向的 gld_stats(9S) 结构。GLDv2 会针对统计信息请求调用此例程。GLDv2 会使用 gldm_get_stats() 机制从驱动程序获取与设备有关的统计信息,然后再编写统计信息请求回复。有关已定义的统计计数器的更多信息,请参见 gld_stats(9S)gld(7D)qreply(9F) 手册页。

gldm_ioctl() 入口点

int prefix_ioctl(gld_mac_info_t *macinfo, queue_t *q, mblk_t *mp);

gldm_ioctl() 可实现所有特定于设备的 ioctl 命令。如果驱动程序未实现任何 ioctl 函数,则允许此元素为 null。驱动程序负责在返回 GLD_SUCCESS 之前将消息块转换为 ioctl 回复消息,并调用 qreply(9F) 函数。此函数应该始终返回 GLD_SUCCESS。驱动程序应该根据需要报告要传递给 qreply(9F) 的消息中的所有错误。如果将 gldm_ioctl 元素指定为 NULL,则 GLDv2 会返回类型为 M_IOCNAK 的消息以及 EINVAL 错误。

GLDv2 返回值

GLDv2 中的某些入口点函数可以返回以下值(具体视上述限制而定):

GLD_BADARG

函数检测到不适合的参数(如错误多点传送地址、错误 MAC 地址或错误包)时

GLD_FAILURE

出现硬件故障时

GLD_SUCCESS

成功时

GLDv2 服务例程

本节提供 GLDv2 服务例程的语法和说明。

gld_mac_alloc() 函数

gld_mac_info_t *gld_mac_alloc(dev_info_t *dip);

gld_mac_alloc() 可分配新的 gld_mac_info(9S) 结构并返回指向此结构的指针。可能会在 gld_mac_alloc() 返回之前初始化此结构的某些 GLDv2 专用元素。所有其他元素均初始化为 0。在将指向 gld_mac_info 结构的指针传递给 gld_register() 之前,设备驱动程序必须初始化某些结构成员,如 gld_mac_info(9S) 手册页中所述。

gld_mac_free() 函数

void gld_mac_free(gld_mac_info_t *macinfo);

gld_mac_free() 可释放先前由 gld_mac_alloc() 所分配的 gld_mac_info(9S) 结构。

gld_register() 函数

int gld_register(dev_info_t *dip, char *name, gld_mac_info_t *macinfo);

可通过设备驱动程序的 attach(9E) 例程调用 gld_register()gld_register() 将基于 GLDv2 的设备驱动程序与 GLDv2 框架相链接。在调用 gld_register() 之前,设备驱动程序的 attach(9E) 例程使用 gld_mac_alloc() 来分配 gld_mac_info(9S) 结构,然后初始化若干结构元素。有关更多信息,请参见 gld_mac_info(9S)。成功调用 gld_register() 可执行以下操作:

传递给 gld_register() 的设备接口名称必须与存在于文件系统中的驱动程序模块的名称完全匹配。

如果 gld_register() 成功执行,则驱动程序的 attach(9E) 例程应该返回 DDI_SUCCESS。如果 gld_register() 没有返回 DDI_SUCCESS,则 attach(9E) 例程应该在调用 gld_register() 之前解除分配所有已分配的资源,然后返回 DDI_FAILURE

gld_unregister() 函数

int gld_unregister(gld_mac_info_t *macinfo);

gld_unregister() 由设备驱动程序的 detach(9E) 函数进行调用,如果成功,会执行以下任务:

如果 gld_unregister() 返回 DDI_SUCCESS,则 detach(9E) 例程应该解除分配在 attach( 9E) 例程中分配的所有数据结构,使用 gld_mac_free() 解除分配 macinfo 结构,并返回 DDI_SUCCESS。如果 gld_unregister() 没有返回 DDI_SUCCESS,则驱动程序的 detach(9E) 例程必须保持设备处于运行状态并返回 DDI_FAILURE

gld_recv() 函数

void gld_recv(gld_mac_info_t *macinfo, mblk_t *mp);

驱动程序的中断处理程序可调用 gld_recv() 以向上游传递已接收的包。驱动程序必须构造并传递包含原始包的 STREAMS M_DATA 消息。gld_recv() 可确定哪些 STREAMS 队列应该接收包的副本,并在必要时复制包。然后,gld_recv() 会在需要时设置 DL_UNITDATA_IND 消息的格式,并沿所有相应的流向上传递数据。

在调用 gld_recv() 期间,驱动程序应该避免持有互斥锁或其他锁。需要特别指出的是,在调用 gld_recv() 期间,不得持有传输线程可使用的锁。在某些情况下调用 gld_recv() 的中断线程可执行包括发送传出包的处理。传输包会导致调用驱动程序的 gldm_send() 例程。如果在调用 gld_recv()gldm_send() 尝试获取 gldm_intr() 持有的互斥锁,则会由于存在递归互斥锁入口操作而出现紧急情况。在调用 gld_recv() 时,如果其他驱动程序入口点尝试获取驱动程序所持有的互斥锁,则会导致死锁。

gld_sched() 函数

void gld_sched(gld_mac_info_t *macinfo);

设备驱动程序可调用 gld_sched() 来重新安排已延迟的外发包。当驱动程序的 gldm_send() 例程返回 GLD_NORESOURCES 时,驱动程序必须调用 gld_sched() 以通知 GLDv2 框架重试先前无法发送的包。当资源变得可用之后,应该尽快调用 gld_sched(),以便 GLDv2 继续将外发包传递给驱动程序的 gldm_send() 例程。(如果调用了驱动程序的 gldm_stop() 例程,则在 gldm_send 返回 GLD_NORESOURCES() 之前,驱动程序不需要重试。不过,再次调用 gld_sched() 也不会导致错误操作。)

gld_intr() 函数

uint_t gld_intr(caddr_t);

gld_intr() 是 GLDv2 的主要中断处理程序。通常,将 gld_intr() 指定为设备驱动程序的 ddi_add_intr(9F) 调用中的中断例程。将中断处理程序的参数指定为 ddi_add_intr(9F) 调用中的 int_handler_arg。此参数必须是指向 gld_mac_info(9S) 结构的指针。gld_intr() 会在适当的情况下调用设备驱动程序的 gldm_intr() 函数,并将该指针传递给 gld_mac_info(9S) 结构。但是,要使用高级中断,驱动程序必须提供自身的高级中断处理程序,并在处理程序中触发软中断。在这种情况下,通常会将 gld_intr() 指定为 ddi_add_softintr() 调用中的软中断处理程序。gld_intr() 将返回适用于中断处理程序的值。