编写设备驱动程序

第 19 章 网络设备驱动程序

要为 Solaris OS 编写网络驱动程序,请使用 Solaris 通用 LAN 驱动程序 (Generic LAN Driver, GLD) 框架。

GLDv3 网络设备驱动程序框架

GLDv3 框架是 MAC 插件和 MAC 驱动程序服务例程与结构的基于函数调用的接口。GLDv3 框架代表符合 GLDv3 的驱动程序实现必要的 STREAMS 入口点,并处理 DLPI 兼容性。

本节讨论以下主题:

GLDv3 MAC 注册

GLDv3 为使用 MAC_PLUGIN_IDENT_ETHER 插件类型注册的驱动程序定义驱动程序 API。

GLDv3 MAC 注册过程

GLDv3 设备驱动程序必须执行以下步骤来注册 MAC 层:

GLDv3 MAC 注册函数

GLDv3 接口包括在使用 MAC 层注册过程中通告的驱动程序入口点和驱动程序调用的 MAC 入口点。

mac_init_ops() 和·mac_fini_ops() 函数

void mac_init_ops(struct dev_ops *ops, const char *name);

GLDv3 设备驱动程序必须在调用 mod_install(9F) 之前在其 _init(9E) 入口点中调用 mac_init_ops(9F) 函数。

void mac_fini_ops(struct dev_ops *ops);

GLDv3 设备驱动程序必须在调用 mod_remove(9F) 之后在其 _fini(9E) 入口点中调用 mac_fini_ops(9F) 函数。


示例 19–1 mac_init_ops() 和·mac_fini_ops() 函数

int
_init(void)
{
        int     rv;
        mac_init_ops(&xx_devops, "xx");
        if ((rv = mod_install(&xx_modlinkage)) != DDI_SUCCESS) {
                mac_fini_ops(&xx_devops);
        }
        return (rv);
}

int
_fini(void)
{
        int     rv;
        if ((rv = mod_remove(&xx_modlinkage)) == DDI_SUCCESS) {
                mac_fini_ops(&xx_devops);
        }
        return (rv);
}

mac_alloc()mac_free() 函数

mac_register_t *mac_alloc(uint_t version);

mac_alloc(9F) 函数分配一个新的 mac_register 结构,并返回此结构的指针。在将新结构传递给 mac_register() 之前初始化结构成员。在 mac_alloc() 返回之前,MAC 层会初始化 MAC 专用元素。 version 的值必须是 MAC_VERSION_V1

void mac_free(mac_register_t *mregp);

mac_free(9F) 函数释放此前由 mac_alloc() 分配的 mac_register 结构。

mac_register()mac_unregister () 函数

int mac_register(mac_register_t *mregp, mac_handle_t *mhp);

为了使用 MAC 层注册新实例,GLDv3 驱动程序必须在 attach(9E) 入口点中调用 mac_register(9F) 函数。mregp 参数是指向 mac_register 注册信息结构的指针。 在成功时,mhp 参数是指向新 MAC 实例的 MAC 句柄指针。mac_tx_update()mac_link_update ()mac_rx() 等其他例程需要此句柄。


示例 19–2 mac_alloc()mac_register ()mac_free() 函数及 mac_register 结构

int
xx_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
        mac_register_t        *macp;

/* ... */

        if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
                xx_error(dip, "mac_alloc failed");
                goto failed;
        }

        macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
        macp->m_driver = xxp;
        macp->m_dip = dip;
        macp->m_src_addr = xxp->xx_curraddr;
        macp->m_callbacks = &xx_m_callbacks;
        macp->m_min_sdu = 0;
        macp->m_max_sdu = ETHERMTU;
        macp->m_margin = VLAN_TAGSZ;

        if (mac_register(macp, &xxp->xx_mh) == DDI_SUCCESS) {
                mac_free(macp);
                return (DDI_SUCCESS);
        }

/* failed to register with MAC */
        mac_free(macp);
failed:
        /* ... */
}

int mac_unregister(mac_handle_t mh);

mac_unregister(9F) 函数取消注册此前通过 mac_register() 注册的 MAC 实例。 mh 参数是 mac_register() 分配的 MAC 句柄。 从 detach(9E) 入口点调用 mac_unregister()


示例 19–3 mac_unregister() 函数

int
xx_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
        xx_t        *xxp; /* driver soft state */

        /* ... */

        switch (cmd) {
        case DDI_DETACH:

                if (mac_unregister(xxp->xx_mh) != 0) {
                        return (DDI_FAILURE);
                }
        /* ... */
}

GLDv3 MAC 注册数据结构

本节介绍的结构是在 sys/mac_provider.h 头文件中定义的。 在 GLDv3 驱动程序中包含以下三个 MAC 头文件:sys/mac.hsys/mac_ether.hsys/mac_provider.h. 切勿包含其他任何于 MAC 相关的头文件。

mac_register(9S) 数据结构是 MAC 注册信息结构,由 mac_alloc() 分配,传递至 mac_register()。 ·在将新结构传递给 mac_register() 之前初始化结构成员。在 mac_alloc() 返回之前,MAC 层会初始化 MAC 专用元素。 m_version 结构成员是 MAC 版本。 切勿修改 MAC 版本。m_type_ident 结构成员是 MAC 类型标识符。 将 MAC 类型标识符设置为 MAC_PLUGIN_IDENT_ETHERmac_register 结构的 m_callbacks 成员是指向 mac_callbacks 结构的一个实例的指针。

mac_callbacks(9S) 数据结构是设备驱动程序用于向 MAC 层公开其入口点的结构。 MAC 层使用这些入口点来控制驱动程序。这些入口点用于完成启动和停止适配器、管理多点传送地址、设置混杂模式、查询适配器功能、获取和设置属性等任务。 有关必需和可选 GLDv3 入口点的完整列表,请参见表 19–1。在 mac_register 结构的 m_callbacks 字段中提供指向 mac_callbacks 结构的指针。

mac_callbacks 结构的 mc_callbacks 成员是一个位掩码,结合了指定驱动程序将实现哪些可选入口点的以下标志。mac_callbacks 结构的其他成员是驱动程序各入口点的指针。

MC_IOCTL

显示 mc_ioctl() 入口点。

MC_GETCAPAB

显示 mc_getcapab() 入口点。

MC_SETPROP

显示·mc_setprop() 入口点。

MC_GETPROP

显示 mc_getprop() 入口点。

MC_PROPINFO

显示 mc_propinfo() 入口点。

MC_PROPERTIES

显示所有属性入口点。设置·MC_PROPERTIES 等同于设置全部三个标志:MC_SETPROP MC_GETPROPMC_PROPINFO


示例 19–4 mac_callbacks 结构

#define XX_M_CALLBACK_FLAGS \
    (MC_IOCTL | MC_GETCAPAB | MC_PROPERTIES)

static mac_callbacks_t xx_m_callbacks = {
        XX_M_CALLBACK_FLAGS,
        xx_m_getstat,     /* mc_getstat() */
        xx_m_start,       /* mc_start() */
        xx_m_stop,        /* mc_stop() */
        xx_m_promisc,     /* mc_setpromisc() */
        xx_m_multicst,    /* mc_multicst() */
        xx_m_unicst,      /* mc_unicst() */
        xx_m_tx,          /* mc_tx() */
        NULL,             /* Reserved, do not use */
        xx_m_ioctl,       /* mc_ioctl() */
        xx_m_getcapab,    /* mc_getcapab() */
        NULL,             /* Reserved, do not use */
        NULL,             /* Reserved, do not use */
        xx_m_setprop,     /* mc_setprop() */
        xx_m_getprop,     /* mc_getprop() */
        xx_m_propinfo     /* mc_propinfo() */
};

GLDv3 功能

GLDv3 实现一种功能机制,允许框架查询和启用 GLDv3 驱动程序支持的功能。使用 mc_getcapab(9E) 入口点报告功能。如果驱动程序支持某种功能,请通过 mc_getcapab() 传递关于该功能的信息,例如特定于功能的入口点或标志。传递一个指向 mac_callback 结构中 mc_getcapab() 入口点的指针。有关 mac_callbacks 结构的更多信息,请参见GLDv3 MAC 注册数据结构

boolean_t mc_getcapab(void *driver_handle, mac_capab_t cap, void *cap_data);

cap 参数指定所查询的功能类型。 cap 的值可以是 MAC_CAPAB_HCKSUM(硬件校验和负载转移)或 MAC_CAPAB_LSO(大段负载转移)。使用 cap_data 参数将功能数据返回框架。

如果驱动程序支持 cap 功能,则 mc_getcapab() 入口点必须返回 B_TRUE。如果驱动程序不支持 cap 功能,则 mc_getcapab() 必须返回 B_FALSE


示例 19–5 mc_getcapab() 入口点

static boolean_t
xx_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
{
        switch (cap) {
        case MAC_CAPAB_HCKSUM: {
                uint32_t *txflags = cap_data;
                *txflags = HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM;
                break;
        }
        case MAC_CAPAB_LSO: {
                /* ... */
                break;
        }
        default:
                return (B_FALSE);
        }
        return (B_TRUE);
}

下面各节将介绍所支持的功能和需要返回的相应功能数据。

硬件校验和负载转移

为了获得关于硬件校验和负载转移支持的数据,框架将在 cap 参数中发送 MAC_CAPA _HCKSUM。 请参见硬件校验和负载转移功能信息

要在启用硬件校验和的情况下查询校验和负载转移元数据以及检索每个包的硬件校验和元数据,请使用 mac_hcksum_get(9F)。 请参见mac_hcksum_get()() 函数标志

要设置校验和负载转移元数据,请使用 mac_hcksum_set(9F)。请参见mac_hcksum_set()() 函数标志

有关更多信息,请参见硬件校验和:硬件硬件校验和:MAC 层

硬件校验和负载转移功能信息

要将关于 MAC_CAPAB_HCKSUM 功能的信息传递给框架,驱动程序必须在指向 uint32_tcap_data 中设置以下标志的组合。 这些标志指明驱动程序能够为外发包执行的硬件校验和负载转移的级别。

HCKSUM_INET_PARTIAL

1 的补码的部分校验和功能

HCKSUM_INET_FULL_V4

针对 IPv4 包的 1 的补码的完全校验和能力

HCKSUM_INET_FULL_V6

针对 IPv6 包的 1 的补码的完全校验和能力

HCKSUM_IPHDRCKSUM

IPv4 头校验和负载转移功能

mac_hcksum_get()() 函数标志

mac_hcksum_get()flags 参数是以下值的组合:

HCK_FULLCKSUM

计算此包的完整校验和。

HCK_FULLCKSUM_OK

完整校验和已在硬件中通过验证,证实是正确的。

HCK_PARTIALCKSUM

根据传递给 mac_hcksum_get() 的其他参数计算 1 的补码的部分校验和。HCK_PARTIALCKSUM HCK_FULLCKSUM 互斥。

HCK_IPV4_HDRCKSUM

计算 IP 报头校验和。

HCK_IPV4_HDRCKSUM_OK

IP 头校验和已在硬件中通过验证,证实是正确的。

mac_hcksum_set()() 函数标志

mac_hcksum_setflags() 参数是以下值的组合:

HCK_FULLCKSUM

通过 value 参数计算和传递完整校验和。

HCK_FULLCKSUM_OK

完整校验和已在硬件中通过验证,证实是正确的。

HCK_PARTIALCKSUM

通过 value 参数计算和传递部分校验和。HCK_PARTIALCKSUM HCK_FULLCKSUM 互斥。

HCK_IPV4_HDRCKSUM

通过 value 参数计算和传递 IP 头校验和。··

HCK_IPV4_HDRCKSUM_OK

IP 头校验和已在硬件中通过验证,证实是正确的。

大段(或发送)负载转移

为了查询大段(或发送)负载转移支持,框架将在 cap 参数中发送 MAC_CAPA _LSO,并等待接收在指向 mac_capab_lso(9S) 结构的 cap_data 中返回的信息。框架将分配 mac_capab_lso 结构,并在 cap_data 中传递指向此结构的指针。 mac_capab_lso 结构包含一个 lso_basic_tcp_ipv4(9S) 结构和一个 lso_flags 成员。 如果驱动程序实例为 IPv4 上的 TCP 支持 LSO,请设置 lso_flags 中的 LSO_TX_BASIC_TCP_IPV4 标志,并将 lso_basic_tcp_ipv4 结构的 lso_max 成员设置为驱动程序实例支持的最大有效载荷大小。

使用 mac_lso_get(9F) 获得每个包的 LSO 元数据。 如果为此包启用了 LSO,则将在 mac_lso_get () flags 参数中设置 HW_LSO 标志。在大段的分段过程中使用的最大段大小 (maximum segment size, MSS) 将通过 mss 参数指向的位置返回。 有关更多信息,请参见大段负载转移

GLDv3 数据路径

数据路径入口点包括以下组件:

传输数据路径

GLDv3 框架使用传输入口点 mc_tx(9E) 将消息块链传递至驱动程序。在您的 mac_callbacks 结构中提供指向 mc_tx() 入口点的指针。有关 GLDv3 MAC 注册数据结构 结构的更多信息,请参阅 GLDv3 MAC 注册数据结构


示例 19–6 mc_tx() 入口点

mblk_t *
xx_m_tx(void *arg, mblk_t *mp)
{
        xx_t    *xxp = arg;
        mblk_t   *nmp;

        mutex_enter(&xxp->xx_xmtlock);

        if (xxp->xx_flags & XX_SUSPENDED) {
                while ((nmp = mp) != NULL) {
                        xxp->xx_carrier_errors++;
                        mp = mp->b_next;
                        freemsg(nmp);
                }
                mutex_exit(&xxp->xx_xmtlock);
                return (NULL);
        }

        while (mp != NULL) {
                nmp = mp->b_next;
                mp->b_next = NULL;

                if (!xx_send(xxp, mp)) {
                        mp->b_next = nmp;
                        break;
                }
                mp = nmp;
        }
        mutex_exit(&xxp->xx_xmtlock);

        return (mp);
}

以下各节将讨论与将数据传输至硬件相关的主题。

流量控制

如果驱动程序因硬件资源不足而无法发送包,则驱动程序将返回无法发送的包的子链。此后,在更多描述符可用时,驱动程序必须调用 mac_tx_update(9F) 通知框架。

硬件校验和:硬件

如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须执行以下任务:

大段负载转移

如果驱动程序指定了 LSO 功能(请参见大段(或发送)负载转移),则驱动程序必须使用 mac_lso_get(9F) 来查询是否必须在包上执行 LSO。·

虚拟 LAN:硬件

管理员配置 VLAN 时,MAC 层将通过 mc_tx() 入口点,在外发包传递至驱动程序之前将所需的 VLAN 头添加到外发包中。

接收数据路径

在驱动程序的中断处理程序中调用 mac_rx(9F) 函数,将一个或多个包构成的链沿堆栈向上传递至 MAC 层。避免在调用 mac_rx() 的过程中保留互斥锁或其他锁。具体来说,不要保留可能会在 mac_rx() 调用过程中被传输获取的锁。有关必须向上发送至 MAC 层的包的信息,请参见mc_unicst(9E)

以下各节将讨论与将数据发送至 MAC 层相关的主题。

硬件校验和:MAC 层

如果驱动程序指定了硬件校验和支持(请参见硬件校验和负载转移),则驱动程序必须使用 mac_hcksum_set(9F) 函数将硬件校验和元数据与包关联。

虚拟 LAN:MAC 层

VLAN 包必须连同标签一起传递至 MAC 层。切勿分流包中的 VLAN 头。

GLDv3 状态更改通知

驱动程序可以调用以下函数来通知网络栈驱动程序的状态已更改。

void mac_tx_update(mac_handle_t mh);

mac_tx_update(9F) 函数通知框架有更多 TX 描述符可用。 如果 mc_tx() 返回非空包链,则驱动程序必须在资源可用时立即调用 mac_tx_update(),通知 MAC 层重试之前作为未发送的包返回的包。 有关 mc_tx() 入口点的更多信息,请参见传输数据路径

void mac_link_update(mac_handle_t mh, link_state_t new_state);

mac_link_update(9F) 函数通知 MAC 层介质链接的状态已更改。new_state 参数必须为以下值之一:

LINK_STATE_UP

介质链路为启动状态。

LINK_STATE_DOWN

介质链路为关闭状态。

LINK_STATE_UNKNOWN

介质链路为未知状态。

GLDv3 网络统计信息

设备驱动程序为其管理的设备实例维护一组统计信息。MAC 层通过驱动程序的 mc_getstat(9E) 入口点查询这些统计信息。

int mc_getstat(void *driver_handle, uint_t stat, uint64_t *stat_value);

GLDv3 框架使用 stat 指定所查询的统计信息。 驱动程序使用 stat_value 返回 stat 指定的统计信息的值。 如果返回了统计信息的值,则 mc_getstat() 必须返回 0。如果驱动程序不支持 stat 统计信息,mc_getstat() 必须返回 ENOTSUP

所支持的 GLDv3 统计信息是通用 MAC 统计信息和特定于以太网的统计信息的联合。有关所支持的统计信息的完整列表,请参见 mc_getstat(9E) 手册页。


示例 19–7 mc_getstat() 入口点

int
xx_m_getstat(void *arg, uint_t stat, uint64_t *val)
{
        xx_t    *xxp = arg;

        mutex_enter(&xxp->xx_xmtlock);
        if ((xxp->xx_flags & (XX_RUNNING|XX_SUSPENDED)) == XX_RUNNING)
                xx_reclaim(xxp);
        mutex_exit(&xxp->xx_xmtlock);

        switch (stat) {
        case MAC_STAT_MULTIRCV:
                *val = xxp->xx_multircv;
                break;
        /* ... */
        case ETHER_STAT_MACRCV_ERRORS:
                *val = xxp->xx_macrcv_errors;
                break;
        /* ... */
        default:
                return (ENOTSUP);
        }
        return (0);
}

GLDv3 属性

使用 mc_propinfo(9E) 入口点返回属性的不可变属性。此信息包括权限、缺省值和允许的值范围。使用 mc_setprop(9E) 为此特定驱动程序实例设置属性值。使用 mc_getprop(9E) 返回属性的当前值。

有关属性及其类型的完整列表,请参见 mc_propinfo(9E) 手册页。

mc_propinfo() 入口点应调用 mac_prop_info_set_perm()mac_prop_info_set_default()mac_prop_info_set_range() 函数来关联所查询属性的特定属性,例如缺省值、权限或允许的值范围等。

mac_prop_info_set_default_uint8(9F)mac_prop_info_set_default_str(9F)mac_prop_info_set_default_link_flowctrl(9F) 函数将缺省值与特定属性相关联。mac_prop_info_set_range_uint32(9F) 函数为特定属性关联值的允许范围。

mac_prop_info_set_perm(9F) 函数指定属性的权限。权限可以是以下值之一:

MAC_PROP_PERM_READ

属性为只读

MAC_PROP_PERM_WRITE

属性为只写

MAC_PROP_PERM_RW

属性可以是读取和写入

如果 mc_propinfo() 入口点未为特定属性调用 mac_prop_info_set_perm(),GLDv3 框架将假设该属性拥有对应于 MAC_PROP_PERM_RW 的读写权限。

除了 mc_propinfo(9E) 手册页中列出的属性之外,驱动程序还可公开驱动程序专用属性。使用 mac_register 结构的 m_priv_props 字段指定驱动程序支持的驱动程序专用属性。框架在 mc_setprop() mc_getprop()mc_propinfo() 中传递 MAC_PROP_PRIVATE 属性·ID。 有关更多信息,请参见 mc_propinfo (9E) 手册页。

GLDv3 接口汇总

下表列出了属于 GLDv3 网络设备驱动程序框架的入口点、其他 DDI 函数和数据结构。

表 19–1 GLDv3 接口

接口名称 

说明 

必需的入口点

mc_getstat(9E)

从驱动程序检索网络统计信息。请参见GLDv3 网络统计信息

mc_start(9E)

启动一个驱动程序实例。GLDv3 框架在尝试任何操作之前都会调用 start 入口点。 

mc_stop(9E)

停止一个驱动程序实例。MAC 层会在设备分离之前调用停止入口点。 

mc_setpromisc(9E)

更改设备驱动程序实例的混杂模式。 

mc_multicst(9E)

添加或删除多点传送地址。 

mc_unicst(9E)

设置主单点传送地址。设备必须通过 mac_rx() 开始回传带有与新单点传送地址匹配的目标 MAC 地址的包。 有关 mac_rx() 的信息,请参见接收数据路径

mc_tx(9E)

发送一个或多个包。请参见传输数据路径

可选入口点

mc_ioctl(9E)

可选 ioctl 驱动程序接口。此工具仅供调试之用。 

mc_getcapab(9E)

检索功能。请参见GLDv3 功能

mc_setprop(9E)

设置属性值。请参见GLDv3 属性

mc_getprop(9E)

获得属性值。请参见GLDv3 属性

mc_propinfo(9E)

获得关于属性的信息。请参见GLDv3 属性

数据结构

mac_register(9S)

注册信息。请参见GLDv3 MAC 注册数据结构

mac_callbacks(9S)

驱动程序回调。请参见GLDv3 MAC 注册数据结构

mac_capab_lso(9S)

LSO 元数据。请参见大段(或发送)负载转移

lso_basic_tcp_ipv4(9S)

TCP/IPv4 的 LSO 元数据。请参见大段(或发送)负载转移

MAC 注册函数

mac_alloc(9F)

分配新 mac_register 结构。 请参见GLDv3 MAC 注册

mac_free(9F)

释放 mac_register 结构。

mac_register(9F)

使用 MAC 层注册。 

mac_unregister(9F)

从 MAC 层取消注册。 

mac_init_ops(9F)

初始化驱动程序的 dev_ops(9S) 结构。

mac_fini_ops(9F)

发布驱动程序的 dev_ops 结构。

数据传输函数

mac_rx(9F)

向上传递接收到的包。请参见接收数据路径

mac_tx_update(9F)

TX 资源可用。请参见GLDv3 状态更改通知

mac_link_update(9F)

链接状态已更改。 

mac_hcksum_get(9F)

检索硬件校验和信息。请参见硬件校验和负载转移传输数据路径

mac_hcksum_set(9F)

附加硬件校验和信息。请参见硬件校验和负载转移接收数据路径

mac_lso_get(9F)

检索 LSO 信息。请参见大段(或发送)负载转移

属性函数

mac_prop_info_set_perm(9F)

设置属性权限。请参见GLDv3 属性

mac_prop_info_set_default_uint8(9F), mac_prop_info_set_default_str(9F), mac_prop_info_set_default_link_flowctrl(9F)

设置属性值。 

mac_prop_info_set_range_uint32(9F)

设置属性值范围。 

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() 将返回适用于中断处理程序的值。