编写设备驱动程序

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)

设置属性值范围。