Writing Device Drivers

Chapter 19 Drivers for Network Devices

To write a network driver for the Oracle Solaris OS, use the Solaris Generic LAN Driver (GLD) framework.

GLDv3 Network Device Driver Framework

The GLDv3 framework is a function calls-based interface of MAC plugins and MAC driver service routines and structures. The GLDv3 framework implements the necessary STREAMS entry points on behalf of GLDv3 compliant drivers and handles DLPI compatibility.

This section discusses the following topics:

GLDv3 MAC Registration

GLDv3 defines a driver API for drivers that register with a plugin type of MAC_PLUGIN_IDENT_ETHER.

GLDv3 MAC Registration Process

A GLDv3 device driver must perform the following steps to register with the MAC layer:

GLDv3 MAC Registration Functions

The GLDv3 interface includes driver entry points that are advertised during registration with the MAC layer and MAC entry points that are invoked by drivers.

The mac_init_ops() and mac_fini_ops() Functions

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

A GLDv3 device driver must invoke the mac_init_ops(9F) function in its _init(9E) entry point before calling mod_install(9F).

void mac_fini_ops(struct dev_ops *ops);

A GLDv3 device driver must invoke the mac_fini_ops(9F) function in its _fini(9E) entry point after calling mod_remove(9F).


Example 19–1 The mac_init_ops() and mac_fini_ops() Functions

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);
}

The mac_alloc() and mac_free() Functions

mac_register_t *mac_alloc(uint_t version);

The mac_alloc(9F) function allocates a new mac_register structure and returns a pointer to it. Initialize the structure members before you pass the new structure to mac_register(). MAC-private elements are initialized by the MAC layer before mac_alloc() returns. The value of version must be MAC_VERSION_V1.

void mac_free(mac_register_t *mregp);

The mac_free(9F) function frees a mac_register structure that was previously allocated by mac_alloc().

The mac_register() and mac_unregister() Functions

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

To register a new instance with the MAC layer, a GLDv3 driver must invoke the mac_register(9F) function in its attach(9E) entry point. The mregp argument is a pointer to a mac_register registration information structure. On success, the mhp argument is a pointer to a MAC handle for the new MAC instance. This handle is needed by other routines such as mac_tx_update(), mac_link_update(), and mac_rx().


Example 19–2 The mac_alloc(), mac_register(), and mac_free() Functions and mac_register Structure

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);

The mac_unregister(9F) function unregisters a MAC instance that was previously registered with mac_register(). The mh argument is the MAC handle that was allocated by mac_register(). Invoke mac_unregister() from the detach(9E) entry point.


Example 19–3 The mac_unregister() Function

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 Registration Data Structures

The structures described in this section are defined in the sys/mac_provider.h header file. Include the following three MAC header files in your GLDv3 driver: sys/mac.h, sys/mac_ether.h, and sys/mac_provider.h. Do not include any other MAC-related header file.

The mac_register(9S) data structure is the MAC registration information structure that is allocated by mac_alloc() and passed to mac_register(). Initialize the structure members before you pass the new structure to mac_register(). MAC-private elements are initialized by the MAC layer before mac_alloc() returns. The m_version structure member is the MAC version. Do not modify the MAC version. The m_type_ident structure member is the MAC type identifier. Set the MAC type identifier to MAC_PLUGIN_IDENT_ETHER. The m_callbacks member of the mac_register structure is a pointer to an instance of the mac_callbacks structure.

The mac_callbacks(9S) data structure is the structure that your device driver uses to expose its entry points to the MAC layer. These entry points are used by the MAC layer to control the driver. These entry points are used to do tasks such as start and stop the adapters, manage multicast addresses, set promiscuous mode, query the capabilities of the adapter, and get and set properties. See Table 19–1 for a complete list of required and optional GLDv3 entry points. Provide a pointer to your mac_callbacks structure in the m_callbacks field of the mac_register structure.

The mc_callbacks member of the mac_callbacks structure is a bit mask that is a combination of the following flags that specify which of the optional entry points are implemented by the driver. Other members of the mac_callbacks structure are pointers to each of the entry points of the driver.

MC_IOCTL

The mc_ioctl() entry point is present.

MC_GETCAPAB

The mc_getcapab() entry point is present.

MC_SETPROP

The mc_setprop() entry point is present.

MC_GETPROP

The mc_getprop() entry point is present.

MC_PROPINFO

The mc_propinfo() entry point is present.

MC_PROPERTIES

All properties entry points are present. Setting MC_PROPERTIES is equivalent to setting all three flags: MC_SETPROP, MC_GETPROP, and MC_PROPINFO.


Example 19–4 The mac_callbacks Structure

#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 Capabilities

GLDv3 implements a capability mechanism that allows the framework to query and enable capabilities that are supported by the GLDv3 driver. Use the mc_getcapab(9E)entry point to report capabilities. If a capability is supported by the driver, pass information about that capability, such as capability-specific entry points or flags through mc_getcapab(). Pass a pointer to the mc_getcapab() entry point in the mac_callback structure. See GLDv3 MAC Registration Data Structures for more information about the mac_callbacks structure.

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

The cap argument specifies the type of capability being queried. The value of cap can be either MAC_CAPAB_HCKSUM (hardware checksum offload) or MAC_CAPAB_LSO (large segment offload). Use the cap_data argument to return the capability data to the framework.

If the driver supports the cap capability, the mc_getcapab() entry point must return B_TRUE. If the driver does not support the cap capability, mc_getcapab() must return B_FALSE.


Example 19–5 The mc_getcapab() Entry Point

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);
}

The following sections describe the supported capabilities and the corresponding capability data to return.

Hardware Checksum Offload

To get data about support for hardware checksum offload, the framework sends MAC_CAPAB_HCKSUM in the cap argument. See Hardware Checksum Offload Capability Information.

To query checksum offload metadata and retrieve the per-packet hardware checksumming metadata when hardware checksumming is enabled, use mac_hcksum_get(9F). See The mac_hcksum_get() Function Flags.

To set checksum offload metadata, use mac_hcksum_set(9F). See The mac_hcksum_set() Function Flags.

See Hardware Checksumming: Hardware and Hardware Checksumming: MAC Layer for more information.

Hardware Checksum Offload Capability Information

To pass information about the MAC_CAPAB_HCKSUM capability to the framework, the driver must set a combination of the following flags in cap_data, which points to a uint32_t. These flags indicate the level of hardware checksum offload that the driver is capable of performing for outbound packets.

HCKSUM_INET_PARTIAL

Partial 1's complement checksum ability

HCKSUM_INET_FULL_V4

Full 1's complement checksum ability for IPv4 packets

HCKSUM_INET_FULL_V6

Full 1's complement checksum ability for IPv6 packets

HCKSUM_IPHDRCKSUM

IPv4 Header checksum offload capability

The mac_hcksum_get() Function Flags

The flags argument of mac_hcksum_get() is a combination of the following values:

HCK_FULLCKSUM

Compute the full checksum for this packet.

HCK_FULLCKSUM_OK

The full checksum was verified in hardware and is correct.

HCK_PARTIALCKSUM

Compute the partial 1's complement checksum based on other parameters passed to mac_hcksum_get(). HCK_PARTIALCKSUM is mutually exclusive with HCK_FULLCKSUM.

HCK_IPV4_HDRCKSUM

Compute the IP header checksum.

HCK_IPV4_HDRCKSUM_OK

The IP header checksum was verified in hardware and is correct.

The mac_hcksum_set() Function Flags

The flags argument of mac_hcksum_set() is a combination of the following values:

HCK_FULLCKSUM

The full checksum was computed and passed through the value argument.

HCK_FULLCKSUM_OK

The full checksum was verified in hardware and is correct.

HCK_PARTIALCKSUM

The partial checksum was computed and passed through the value argument. HCK_PARTIALCKSUM is mutually exclusive with HCK_FULLCKSUM.

HCK_IPV4_HDRCKSUM

The IP header checksum was computed and passed through the value argument.

HCK_IPV4_HDRCKSUM_OK

The IP header checksum was verified in hardware and is correct.

Large Segment (or Send) Offload

To query support for large segment (or send) offload, the framework sends MAC_CAPAB_LSO in the cap argument and expects the information back in cap_data, which points to a mac_capab_lso(9S) structure. The framework allocates the mac_capab_lso structure and passes a pointer to this structure in cap_data. The mac_capab_lso structure consists of an lso_basic_tcp_ipv4(9S) structure and an lso_flags member. If the driver instance supports LSO for TCP on IPv4, set the LSO_TX_BASIC_TCP_IPV4 flag in lso_flags and set the lso_max member of the lso_basic_tcp_ipv4 structure to the maximum payload size supported by the driver instance.

Use mac_lso_get(9F) to obtain per-packet LSO metadata. If LSO is enabled for this packet, the HW_LSO flag is set in the mac_lso_get() flags argument. The maximum segment size (MSS) to be used during segmentation of the large segment is returned through the location pointed to by the mss argument. See Large Segment Offload for more information.

GLDv3 Data Paths

Data-path entry points are comprised of the following components:

Transmit Data Path

The GLDv3 framework uses the transmit entry point, mc_tx(9E), to pass a chain of message blocks to the driver. Provide a pointer to the mc_tx() entry point in your mac_callbacks structure. See GLDv3 MAC Registration Data Structures for more information about the mac_callbacks structure.


Example 19–6 The mc_tx() Entry Point

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);
}

The following sections discuss topics related to transmitting data to the hardware.

Flow Control

If the driver cannot send the packets because of insufficient hardware resources, the driver returns the sub-chain of packets that could not be sent. When more descriptors become available at a later time, the driver must invoke mac_tx_update(9F) to notify the framework.

Hardware Checksumming: Hardware

If the driver specified hardware checksum support (see Hardware Checksum Offload), then the driver must do the following tasks:

Large Segment Offload

If the driver specified LSO capabilities (see Large Segment (or Send) Offload), then the driver must use mac_lso_get(9F) to query whether LSO must be performed on the packet.

Virtual LAN: Hardware

When the administrator configures VLANs, the MAC layer inserts the needed VLAN headers on the outbound packets before they are passed to the driver through the mc_tx() entry point.

Receive Data Path

Call the mac_rx(9F) function in your driver's interrupt handler to pass a chain of one or more packets up the stack to the MAC layer. Avoid holding mutex or other locks during the call to mac_rx(). In particular, do not hold locks that could be taken by a transmit thread during a call to mac_rx(). See mc_unicst(9E) for information about the packets that must be sent up to the MAC layer.

The following sections discuss topics related to sending data to the MAC layer.

Hardware Checksumming: MAC Layer

If the driver specified hardware checksum support (see Hardware Checksum Offload), then the driver must use the mac_hcksum_set(9F) function to associate hardware checksumming metadata with the packet.

Virtual LAN: MAC Layer

VLAN packets must be passed with their tags to the MAC layer. Do not strip the VLAN headers from the packets.

GLDv3 State Change Notifications

A driver can call the following functions to notify the network stack that the driver's state has changed.

void mac_tx_update(mac_handle_t mh);

The mac_tx_update(9F) function notifies the framework that more TX descriptors are available. If mc_tx() returns a non-empty chain of packets, then the driver must call mac_tx_update() as soon as possible after resources are available to inform the MAC layer to retry the packets that were returned as not sent. See Transmit Data Path for more information about the mc_tx() entry point.

void mac_link_update(mac_handle_t mh, link_state_t new_state);

The mac_link_update(9F) function notifies the MAC layer that the state of the media link has changed. The new_state argument must be one of the following values:

LINK_STATE_UP

The media link is up.

LINK_STATE_DOWN

The media link is down.

LINK_STATE_UNKNOWN

The media link is unknown.

GLDv3 Network Statistics

Device drivers maintain a set of statistics for the device instances they manage. The MAC layer queries these statistics through the mc_getstat(9E) entry point of the driver.

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

The GLDv3 framework uses stat to specify the statistic being queried. The driver uses stat_value to return the value of the statistic specified by stat. If the value of the statistic is returned, mc_getstat() must return 0. If the stat statistic is not supported by the driver, mc_getstat() must return ENOTSUP.

The GLDv3 statistics that are supported are the union of generic MAC statistics and Ethernet-specific statistics. See the mc_getstat(9E) man page for a complete list of supported statistics.


Example 19–7 The mc_getstat() Entry Point

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 Properties

Use the mc_propinfo(9E) entry point to return immutable attributes of a property. This information includes permissions, default values, and allowed value ranges. Use mc_setprop(9E) to set the value of a property for this particular driver instance. Use mc_getprop(9E) to return the current value of a property.

See the mc_propinfo(9E) man page for a complete list of properties and their types.

The mc_propinfo() entry point should invoke the mac_prop_info_set_perm(), mac_prop_info_set_default(), and mac_prop_info_set_range() functions to associate specific attributes of the property being queried, such as default values, permissions, or allowed value ranges.

The mac_prop_info_set_default_uint8(9F), mac_prop_info_set_default_str(9F), and mac_prop_info_set_default_link_flowctrl(9F) functions associate a default value with a specific property. The mac_prop_info_set_range_uint32(9F) function associates an allowed range of values for a specific property.

The mac_prop_info_set_perm(9F) function specifies the permission of the property. The permission can be one of the following values:

MAC_PROP_PERM_READ

The property is read-only

MAC_PROP_PERM_WRITE

The property is write-only

MAC_PROP_PERM_RW

The property can be read and written

If the mc_propinfo() entry point does not call mac_prop_info_set_perm() for a particular property, the GLDv3 framework assumes that the property has read and write permissions, corresponding to MAC_PROP_PERM_RW.

In addition to the properties listed in the mc_propinfo(9E) man page, drivers can also expose driver-private properties. Use the m_priv_props field of the mac_register structure to specify driver-private properties supported by the driver. The framework passes the MAC_PROP_PRIVATE property ID in mc_setprop(), mc_getprop(), or mc_propinfo(). See the mc_propinfo(9E) man page for more information.

Summary of GLDv3 Interfaces

The following table lists entry points, other DDI functions, and data structures that are part of the GLDv3 network device driver framework.

Table 19–1 GLDv3 Interfaces

Interface Name 

Description 

Required Entry Points

mc_getstat(9E)

Retrieve network statistics from the driver. See GLDv3 Network Statistics.

mc_start(9E)

Start a driver instance. The GLDv3 framework invokes the start entry point before any operation is attempted. 

mc_stop(9E)

Stop a driver instance. The MAC layer invokes the stop entry point before the device is detached. 

mc_setpromisc(9E)

Change the promiscuous mode of the device driver instance. 

mc_multicst(9E)

Add or remove a multicast address. 

mc_unicst(9E)

Set the primary unicast address. The device must start passing back through mac_rx() the packets with a destination MAC address that matches the new unicast address. See Receive Data Path for information about mac_rx().

mc_tx(9E)

Send one or more packets. See Transmit Data Path.

Optional Entry Points

mc_ioctl(9E)

Optional ioctl driver interface. This facility is intended to be used only for debugging purposes. 

mc_getcapab(9E)

Retrieve capabilities. See GLDv3 Capabilities.

mc_setprop(9E)

Set a property value. See GLDv3 Properties.

mc_getprop(9E)

Get a property value. See GLDv3 Properties.

mc_propinfo(9E)

Get information about a property. See GLDv3 Properties.

Data Structures

mac_register(9S)

Registration information. See GLDv3 MAC Registration Data Structures.

mac_callbacks(9S)

Driver callbacks. See GLDv3 MAC Registration Data Structures.

mac_capab_lso(9S)

LSO metadata. See Large Segment (or Send) Offload.

lso_basic_tcp_ipv4(9S)

LSO metadata for TCP/IPv4. See Large Segment (or Send) Offload.

MAC Registration Functions

mac_alloc(9F)

Allocate a new mac_register structure. See GLDv3 MAC Registration.

mac_free(9F)

Free a mac_register structure.

mac_register(9F)

Register with the MAC layer. 

mac_unregister(9F)

Unregister from the MAC layer. 

mac_init_ops(9F)

Initialize the driver's dev_ops(9S) structure.

mac_fini_ops(9F)

Release the driver's dev_ops structure.

Data Transfer Functions

mac_rx(9F)

Pass up received packets. See Receive Data Path.

mac_tx_update(9F)

TX resources are available. See GLDv3 State Change Notifications.

mac_link_update(9F)

Link state has changed. 

mac_hcksum_get(9F)

Retrieve hardware checksum information. See Hardware Checksum Offload and Transmit Data Path.

mac_hcksum_set(9F)

Attach hardware checksum information. See Hardware Checksum Offload and Receive Data Path.

mac_lso_get(9F)

Retrieve LSO information. See Large Segment (or Send) Offload.

Properties Functions

mac_prop_info_set_perm(9F)

Set the permission of a property. See GLDv3 Properties.

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

Set a property value. 

mac_prop_info_set_range_uint32(9F)

Set a property values range. 

GLDv2 Network Device Driver Framework

GLDv2 is a multi-threaded, clonable, loadable kernel module that provides support to device drivers for local area networks. Local area network (LAN) device drivers in the Oracle Solaris OS are STREAMS-based drivers that use the Data Link Provider Interface (DLPI) to communicate with network protocol stacks. These protocol stacks use the network drivers to send and receive packets on a LAN. The GLDv2 implements much of the STREAMS and DLPI functionality for an Oracle Solaris LAN driver. The GLDv2 provides common code that many network drivers can share. Using the GLDv2 reduces duplicate code and simplifies your network driver.

For more information about GLDv2, see the gld(7D) man page.

STREAMS drivers are documented in Part II, Kernel Interface, in STREAMS Programming Guide. Specifically, see Chapter 9, “STREAMS Drivers,” in the STREAMS guide. The STREAMS framework is a message-based framework. Interfaces that are unique to STREAMS drivers include STREAMS message queue processing entry points.

The DLPI specifies an interface to the Data Link Services (DLS) of the Data Link Layer of the OSI Reference Model. The DLPI enables a DLS user to access and use any of a variety of conforming DLS providers without special knowledge of the provider's protocol. The DLPI specifies access to the DLS provider in the form of M_PROTO and M_PCPROTO type STREAMS messages. A DLPI module uses STREAMS ioctl calls to link to the MAC sub-layer. For more information about the DLPI protocol, including Oracle Solaris-specific DPLI extensions, see the dlpi(7P) man page. For general information about DLPI, see the DLPI standard at http://www.opengroup.org/pubs/catalog/c811.htm.

An Oracle Solaris network driver that is implemented using GLDv2 has two distinct parts:

GLDv2 drivers must process fully formed MAC-layer packets and must not perform logical link control (LLC) handling.

This section discusses the following topics:

GLDv2 Device Support

The GLDv2 framework supports the following types of devices:

Ethernet V2 and ISO 8802-3 (IEEE 802.3)

For devices that are declared to be type DL_ETHER, GLDv2 provides support for both Ethernet V2 and ISO 8802-3 (IEEE 802.3) packet processing. Ethernet V2 enables a user to access a conforming provider of data link services without special knowledge of the provider's protocol. A service access point (SAP) is the point through which the user communicates with the service provider.

Streams bound to SAP values in the range [0-255] are treated as equivalent and denote that the user wants to use 8802-3 mode. If the SAP value of the DL_BIND_REQ is within this range, GLDv2 computes the length of each subsequent DL_UNITDATA_REQ message on that stream. The length does not include the 14-byte media access control (MAC) header. GLDv2 then transmits 8802-3 frames that have those lengths in the MAC frame header type fields. Such lengths do not exceed 1500.

Frames that have a type field in the range [0-1500] are assumed to be 8802-3 frames. These frames are routed up all open streams in 8802-3 mode. Those streams with SAP values in the [0-255] range are considered to be in 8802-3 mode. If more than one stream is in 8802-3 mode, the incoming frame is duplicated and routed up these streams.

Those streams that are bound to SAP values that are greater than 1500 are assumed to be in Ethernet V2 mode. These streams receive incoming packets whose Ethernet MAC header type value exactly matches the value of the SAP to which the stream is bound.

TPR and FDDI: SNAP Processing

For media types DL_TPR and DL_FDDI, GLDv2 implements minimal SNAP (Sub-Net Access Protocol) processing. This processing is for any stream that is bound to a SAP value that is greater than 255. SAP values in the range [0-255] are LLC SAP values. Such values are carried naturally by the media packet format. SAP values that are greater than 255 require a SNAP header, subordinate to the LLC header, to carry the 16-bit Ethernet V2-style SAP value.

SNAP headers are carried under LLC headers with destination SAP 0xAA. Outbound packets with SAP values that are greater than 255 require an LLC+SNAP header take the following form:

AA AA 03 00 00 00 XX XX

XX XX represents the 16-bit SAP, corresponding to the Ethernet V2 style type. This header is unique in supporting non-zero organizational unique identifier fields. LLC control fields other than 03 are considered to be LLC packets with SAP 0xAA. Clients that want to use SNAP formats other than this format must use LLC and bind to SAP 0xAA.

Incoming packets are checked for conformance with the above format. Packets that conform are matched to any streams that have been bound to the packet's 16-bit SNAP type. In addition, these packets are considered to match the LLC SNAP SAP 0xAA.

Packets received for any LLC SAP are passed up all streams that are bound to an LLC SAP, as described for media type DL_ETHER.

TPR: Source Routing

For type DL_TPR devices, GLDv2 implements minimal support for source routing.

Source routing support includes the following tasks:

Source routing adds routing information fields to the MAC headers of outgoing packets. In addition, this support recognizes such fields in incoming packets.

GLDv2 source routing support does not implement the full route determination entity (RDE) specified in Section 9 of ISO 8802-2 (IEEE 802.2). However, this support can interoperate with any RDE implementations that might exist in the same or a bridged network.

GLDv2 DLPI Providers

GLDv2 implements both Style 1 and Style 2 DLPI providers. A physical point of attachment (PPA) is the point at which a system attaches itself to a physical communication medium. All communication on that physical medium funnels through the PPA. The Style 1 provider attaches the streams to a particular PPA based on the major or minor device that has been opened. The Style 2 provider requires the DLS user to explicitly identify the desired PPA using DL_ATTACH_REQ. In this case, open(9E) creates a stream between the user and GLDv2, and DL_ATTACH_REQ subsequently associates a particular PPA with that stream. Style 2 is denoted by a minor number of zero. If a device node whose minor number is not zero is opened, Style 1 is indicated and the associated PPA is the minor number minus 1. In both Style 1 and Style 2 opens, the device is cloned.

GLDv2 DLPI Primitives

GLDv2 implements several DLPI primitives. The DL_INFO_REQ primitive requests information about the DLPI streams. The message consists of one M_PROTO message block. GLDv2 returns device-dependent values in the DL_INFO_ACK response to this request. These values are based on information that the GLDv2-based driver specified in the gld_mac_info(9S) structure that was passed to the gld_register(9F) function.

GLDv2 returns the following values on behalf of all GLDv2-based drivers:


Note –

Contrary to the DLPI specification, GLDv2 returns the correct address length and broadcast address of the device in DL_INFO_ACK even before the stream has been attached to a PPA.


The DL_ATTACH_REQ primitive is used to associate a PPA with a stream. This request is needed for Style 2 DLS providers to identify the physical medium over which the communication is sent. Upon completion, the state changes from DL_UNATTACHED to DL_UNBOUND. The message consists of one M_PROTO message block. This request is not allowed when Style 1 mode is used. Streams that are opened using Style 1 are already attached to a PPA by the time the open completes.

The DL_DETACH_REQ primitive requests to detach the PPA from the stream. This detachment is allowed only if the stream was opened using Style 2.

The DL_BIND_REQ and DL_UNBIND_REQ primitives bind and unbind a DLSAP (data link service access point) to the stream. The PPA that is associated with a stream completes initialization before the completion of the processing of the DL_BIND_REQ on that stream. You can bind multiple streams to the same SAP. Each stream in this case receives a copy of any packets that were received for that SAP.

The DL_ENABMULTI_REQ and DL_DISABMULTI_REQ primitives enable and disable reception of individual multicast group addresses. Through iterative use of these primitives, an application or other DLS user can create or modify a set of multicast addresses. The streams must be attached to a PPA for these primitives to be accepted.

The DL_PROMISCON_REQ and DL_PROMISCOFF_REQ primitives turn promiscuous mode on or off on a per-stream basis. These controls operate at either at a physical level or at the SAP level. The DL Provider routes all received messages on the media to the DLS user. Routing continues until a DL_DETACH_REQ is received, a DL_PROMISCOFF_REQ is received, or the stream is closed. You can specify physical level promiscuous reception of all packets on the medium or of multicast packets only.


Note –

The streams must be attached to a PPA for these promiscuous mode primitives to be accepted.


The DL_UNITDATA_REQ primitive is used to send data in a connectionless transfer. Because this service is not acknowledged, delivery is not guaranteed. The message consists of one M_PROTO message block followed by one or more M_DATA blocks containing at least one byte of data.

The DL_UNITDATA_IND type is used when a packet is to be passed on upstream. The packet is put into an M_PROTO message with the primitive set to DL_UNITDATA_IND.

The DL_PHYS_ADDR_REQ primitive requests the MAC address currently associated with the PPA attached to the streams. The address is returned by the DL_PHYS_ADDR_ACK primitive. When using Style 2, this primitive is only valid following a successful DL_ATTACH_REQ.

The DL_SET_PHYS_ADDR_REQ primitive changes the MAC address currently associated with the PPA attached to the streams. This primitive affects all other current and future streams attached to this device. Once changed, all streams currently or subsequently opened and attached to this device obtain this new physical address. The new physical address remains in effect until this primitive changes the physical address again or the driver is reloaded.


Note –

The superuser is allowed to change the physical address of a PPA while other streams are bound to the same PPA.


The DL_GET_STATISTICS_REQ primitive requests a DL_GET_STATISTICS_ACK response containing statistics information associated with the PPA attached to the stream. Style 2 Streams must be attached to a particular PPA using DL_ATTACH_REQ before this primitive can succeed.

GLDv2 I/O Control Functions

GLDv2 implements the ioctl ioc_cmd function described below. If GLDv2 receives an unrecognizable ioctl command, GLDv2 passes the command to the device-specific driver's gldm_ioctl() routine, as described in gld(9E).

The DLIOCRAW ioctl function is used by some DLPI applications, most notably the snoop(1M) command. The DLIOCRAW command puts the stream into a raw mode. In raw mode, the driver passes full MAC-level incoming packets upstream in M_DATA messages instead of transforming the packets into the DL_UNITDATA_IND form. The DL_UNITDATA_IND form is normally used for reporting incoming packets. Packet SAP filtering is still performed on streams that are in raw mode. If a stream user wants to receive all incoming packets, the user must also select the appropriate promiscuous modes. After successfully selecting raw mode, the application is also allowed to send fully formatted packets to the driver as M_DATA messages for transmission. DLIOCRAW takes no arguments. Once enabled, the stream remains in this mode until closed.

GLDv2 Driver Requirements

GLDv2-based drivers must include the header file <sys/gld.h>.

GLDv2-based drivers must be linked with the -N“misc/gld” option:

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

GLDv2 implements the following functions on behalf of the device-specific driver:

The mi_idname element of the module_info(9S) structure is a string that specifies the name of the driver. This string must exactly match the name of the driver module as defined in the file system.

The read-side qinit(9S) structure should specify the following elements:

qi_putp

NULL

qi_srvp

gld_rsrv

qi_qopen

gld_open

qi_qclose

gld_close

The write-side qinit(9S) structure should specify these elements:

qi_putp

gld_wput

qi_srvp

gld_wsrv

qi_qopen

NULL

qi_qclose

NULL

The devo_getinfo element of the dev_ops(9S) structure should specify gld_getinfo as the getinfo(9E) routine.

The driver's attach(9E) function associates the hardware-specific device driver with the GLDv2 facility. attach() then prepares the device and driver for use.

The attach(9E) function allocates a gld_mac_info(9S) structure using gld_mac_alloc(). The driver usually needs to save more information per device than is defined in the macinfo structure. The driver should allocate the additional required data structure and save a pointer to the structure in the gldm_private member of the gld_mac_info(9S) structure.

The attach(9E) routine must initialize the macinfo structure as described in the gld_mac_info(9S) man page. The attach() routine should then call gld_register() to link the driver with the GLDv2 module. The driver should map registers if necessary and be fully initialized and prepared to accept interrupts before calling gld_register(). The attach(9E) function should add interrupts but should not enable the device to generate these interrupts. The driver should reset the hardware before calling gld_register() to ensure the hardware is quiescent. A device must not be put into a state where the device might generate an interrupt before gld_register() is called. The device is started later when GLDv2 calls the driver's gldm_start() entry point, which is described in the gld(9E) man page. After gld_register() succeeds, the gld(9E) entry points might be called by GLDv2 at any time.

The attach(9E) routine should return DDI_SUCCESS if gld_register() succeeds. If gld_register() fails, DDI_FAILURE is returned. If a failure occurs, the attach(9E) routine should deallocate any resources that were allocated before gld_register() was called. The attach routine should then also return DDI_FAILURE. A failed macinfo structure should never be reused. Such a structure should be deallocated using gld_mac_free().

The detach(9E)function should attempt to unregister the driver from GLDv2 by calling gld_unregister(). For more information about gld_unregister(), see the gld(9F) man page. The detach(9E) routine can get a pointer to the needed gld_mac_info(9S) structure from the device's private data using ddi_get_driver_private(9F). gld_unregister() checks certain conditions that could require that the driver not be detached. If the checks fail, gld_unregister() returns DDI_FAILURE, in which case the driver's detach(9E) routine must leave the device operational and return DDI_FAILURE.

If the checks succeed, gld_unregister() ensures that the device interrupts are stopped. The driver's gldm_stop() routine is called if necessary. The driver is unlinked from the GLDv2 framework. gld_unregister() then returns DDI_SUCCESS. In this case, the detach(9E) routine should remove interrupts and use gld_mac_free() to deallocate any macinfo data structures that were allocated in the attach(9E) routine. The detach() routine should then return DDI_SUCCESS. The routine must remove the interrupt before calling gld_mac_free().

GLDv2 Network Statistics

Oracle Solaris network drivers must implement statistics variables. GLDv2 tallies some network statistics, but other statistics must be counted by each GLDv2-based driver. GLDv2 provides support for GLDv2-based drivers to report a standard set of network driver statistics. Statistics are reported by GLDv2 using the kstat(7D) and kstat(9S) mechanisms. The DL_GET_STATISTICS_REQ DLPI command can also be used to retrieve the current statistics counters. All statistics are maintained as unsigned. The statistics are 32 bits unless otherwise noted.

GLDv2 maintains and reports the following statistics.

rbytes64

Total bytes successfully received on the interface. Stores 64-bit statistics.

rbytes

Total bytes successfully received on the interface

obytes64

Total bytes that have requested transmission on the interface. Stores 64-bit statistics.

obytes

Total bytes that have requested transmission on the interface.

ipackets64

Total packets successfully received on the interface. Stores 64-bit statistics.

ipackets

Total packets successfully received on the interface.

opackets64

Total packets that have requested transmission on the interface. Stores 64-bit statistics.

opackets

Total packets that have requested transmission on the interface.

multircv

Multicast packets successfully received, including group and functional addresses (long).

multixmt

Multicast packets requested to be transmitted, including group and functional addresses (long).

brdcstrcv

Broadcast packets successfully received (long).

brdcstxmt

Broadcast packets that have requested transmission (long).

unknowns

Valid received packets not accepted by any stream (long).

noxmtbuf

Packets discarded on output because transmit buffer was busy, or no buffer could be allocated for transmit (long).

blocked

Number of times a received packet could not be put up a stream because the queue was flow-controlled (long).

xmtretry

Times transmit was retried after having been delayed due to lack of resources (long).

promisc

Current “promiscuous” state of the interface (string).

The device-dependent driver tracks the following statistics in a private per-instance structure. To report statistics, GLDv2 calls the driver's gldm_get_stats() entry point. gldm_get_stats() then updates device-specific statistics in the gld_stats(9S) structure. See the gldm_get_stats(9E) man page for more information. GLDv2 then reports the updated statistics using the named statistics variables that are shown below.

ifspeed

Current estimated bandwidth of the interface in bits per second. Stores 64-bit statistics.

media

Current media type in use by the device (string).

intr

Number of times that the interrupt handler was called, causing an interrupt (long).

norcvbuf

Number of times a valid incoming packet was known to have been discarded because no buffer could be allocated for receive (long).

ierrors

Total number of packets that were received but could not be processed due to errors (long).

oerrors

Total packets that were not successfully transmitted because of errors (long).

missed

Packets known to have been dropped by the hardware on receive (long).

uflo

Times FIFO underflowed on transmit (long).

oflo

Times receiver overflowed during receive (long).

The following group of statistics applies to networks of type DL_ETHER. These statistics are maintained by device-specific drivers of that type, as shown previously.

align_errors

Packets that were received with framing errors, that is, the packets did not contain an integral number of octets (long).

fcs_errors

Packets received with CRC errors (long).

duplex

Current duplex mode of the interface (string).

carrier_errors

Number of times carrier was lost or never detected on a transmission attempt (long).

collisions

Ethernet collisions during transmit (long).

ex_collisions

Frames where excess collisions occurred on transmit, causing transmit failure (long).

tx_late_collisions

Number of times a transmit collision occurred late, that is, after 512 bit times (long).

defer_xmts

Packets without collisions where first transmit attempt was delayed because the medium was busy (long).

first_collisions

Packets successfully transmitted with exactly one collision.

multi_collisions

Packets successfully transmitted with multiple collisions.

sqe_errors

Number of times that SQE test error was reported.

macxmt_errors

Packets encountering transmit MAC failures, except carrier and collision failures.

macrcv_errors

Packets received with MAC errors, except align_errors, fcs_errors, and toolong_errors.

toolong_errors

Packets received larger than the maximum allowed length.

runt_errors

Packets received smaller than the minimum allowed length (long).

The following group of statistics applies to networks of type DL_TPR. These statistics are maintained by device-specific drivers of that type, as shown above.

line_errors

Packets received with non-data bits or FCS errors.

burst_errors

Number of times an absence of transitions for five half-bit timers was detected.

signal_losses

Number of times loss of signal condition on the ring was detected.

ace_errors

Number of times that an AMP or SMP frame, in which A is equal to C is equal to 0, is followed by another SMP frame without an intervening AMP frame.

internal_errors

Number of times the station recognized an internal error.

lost_frame_errors

Number of times the TRR timer expired during transmit.

frame_copied_errors

Number of times a frame addressed to this station was received with the FS field `A' bit set to 1.

token_errors

Number of times the station acting as the active monitor recognized an error condition that needed a token transmitted.

freq_errors

Number of times the frequency of the incoming signal differed from the expected frequency.

The following group of statistics applies to networks of type DL_FDDI. These statistics are maintained by device-specific drivers of that type, as shown above.

mac_errors

Frames detected in error by this MAC that had not been detected in error by another MAC.

mac_lost_errors

Frames received with format errors such that the frame was stripped.

mac_tokens

Number of tokens that were received, that is, the total of non-restricted and restricted tokens.

mac_tvx_expired

Number of times that TVX has expired.

mac_late

Number of TRT expirations since either this MAC was reset or a token was received.

mac_ring_ops

Number of times the ring has entered the “Ring Operational” state from the “Ring Not Operational” state.

GLDv2 Declarations and Data Structures

This section describes the gld_mac_info(9S) and gld_stats structures.

gld_mac_info Structure

The GLDv2 MAC information (gld_mac_info) structure is the main data interface that links the device-specific driver with GLDv2. This structure contains data required by GLDv2 and a pointer to an optional additional driver-specific information structure.

Allocate the gld_mac_info structure using gld_mac_alloc(). Deallocate the structure using gld_mac_free(). Drivers must not make any assumptions about the length of this structure, which might vary in different releases of the Oracle Solaris OS, GLDv2, or both. Structure members private to GLDv2, not documented here, should neither be set nor be read by the device-specific driver.

The gld_mac_info(9S) structure contains the following fields.

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 */

The gldm_private structure member is visible to the device driver. gldm_private is also private to the device-specific driver. gldm_private is not used or modified by GLDv2. Conventionally, gldm_private is used as a pointer to private data, pointing to a per-instance data structure that is both defined and allocated by the driver.

The following group of structure members must be set by the driver before calling gld_register(), and should not thereafter be modified by the driver. Because gld_register() might use or cache the values of structure members, changes made by the driver after calling gld_register() might cause unpredictable results. For more information on these structures, see the gld(9E) man page.

gldm_reset

Pointer to driver entry point.

gldm_start

Pointer to driver entry point.

gldm_stop

Pointer to driver entry point.

gldm_set_mac_addr

Pointer to driver entry point.

gldm_set_multicast

Pointer to driver entry point.

gldm_set_promiscuous

Pointer to driver entry point.

gldm_send

Pointer to driver entry point.

gldm_intr

Pointer to driver entry point.

gldm_get_stats

Pointer to driver entry point.

gldm_ioctl

Pointer to driver entry point. This pointer is allowed to be null.

gldm_ident

Pointer to a string that contains a short description of the device. This pointer is used to identify the device in system messages.

gldm_type

Type of device the driver handles. GLDv2 currently supports the following values:

  • DL_ETHER (ISO 8802-3 (IEEE 802.3) and Ethernet Bus)

  • DL_TPR (IEEE 802.5 Token Passing Ring)

  • DL_FDDI (ISO 9314-2 Fibre Distributed Data Interface)

This structure member must be correctly set for GLDv2 to function properly.

gldm_minpkt

Minimum Service Data Unit size: the minimum packet size, not including the MAC header, that the device can transmit. This size is allowed to be zero if the device-specific driver handles any required padding.

gldm_maxpkt

Maximum Service Data Unit size: the maximum size of packet, not including the MAC header, that can be transmitted by the device. For Ethernet, this number is 1500.

gldm_addrlen

The length in bytes of physical addresses handled by the device. For Ethernet, Token Ring, and FDDI, the value of this structure member should be 6.

gldm_saplen

The length in bytes of the SAP address used by the driver. For GLDv2-based drivers, the length should always be set to -2. A length of -2 indicates that 2-byte SAP values are supported and that the SAP appears after the physical address in a DLSAP address. See Appendix A.2, “Message DL_INFO_ACK,” in the DLPI specification for more details.

gldm_broadcast_addr

Pointer to an array of bytes of length gldm_addrlen containing the broadcast address to be used for transmit. The driver must provide space to hold the broadcast address, fill the space with the appropriate value, and set gldm_broadcast_addr to point to the address. For Ethernet, Token Ring, and FDDI, the broadcast address is normally 0xFF-FF-FF-FF-FF-FF.

gldm_vendor_addr

Pointer to an array of bytes of length gldm_addrlen that contains the vendor-provided network physical address of the device. The driver must provide space to hold the address, fill the space with information from the device, and set gldm_vendor_addr to point to the address.

gldm_ppa

PPA number for this instance of the device. The PPA number should always be set to the instance number that is returned from ddi_get_instance(9F).

gldm_devinfo

Pointer to the dev_info node for this device.

gldm_cookie

Interrupt block cookie returned by one of the following routines:

This cookie must correspond to the device's receive-interrupt, from which gld_recv() is called.

gld_stats Structure

After calling gldm_get_stats(), a GLDv2-based driver uses the (gld_stats) structure to communicate statistics and state information to GLDv2. See the gld(9E) and gld(7D) man pages. The members of this structure, having been filled in by the GLDv2-based driver, are used when GLDv2 reports the statistics. In the tables below, the name of the statistics variable reported by GLDv2 is noted in the comments. See the gld(7D) man page for a more detailed description of the meaning of each statistic.

Drivers must not make any assumptions about the length of this structure. The structure length might vary in different releases of the Oracle Solaris OS, GLDv2, or both. Structure members private to GLDv2, which are not documented here, should not be set or be read by the device-specific driver.

The following structure members are defined for all media types:

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 */

The following structure members are defined for media type 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 */

The following structure members are defined for media type 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 */

The following structure members are defined for media type 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 */

Most of the above statistics variables are counters that denote the number of times that the particular event was observed. The following statistics do not represent the number of times:

glds_speed

Estimate of the interface's current bandwidth in bits per second. This object should contain the nominal bandwidth for those interfaces that do not vary in bandwidth or where an accurate estimate cannot be made.

glds_media

Type of media (wiring) or connector used by the hardware. The following media names are supported:

  • 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

Current duplex state of the interface. Supported values are GLD_DUPLEX_HALF and GLD_DUPLEX_FULL. GLD_DUPLEX_UNKNOWN is also allowed.

GLDv2 Function Arguments

The following arguments are used by the GLDv2 routines.

macinfo

Pointer to a gld_mac_info(9S) structure.

macaddr

Pointer to the beginning of a character array that contains a valid MAC address. The array is of the length specified by the driver in the gldm_addrlen element of the gld_mac_info(9S) structure.

multicastaddr

Pointer to the beginning of a character array that contains a multicast, group, or functional address. The array is of the length specified by the driver in the gldm_addrlen element of the gld_mac_info(9S) structure.

multiflag

Flag indicating whether to enable or disable reception of the multicast address. This argument is specified as GLD_MULTI_ENABLE or GLD_MULTI_DISABLE.

promiscflag

Flag indicating what type of promiscuous mode, if any, is to be enabled. This argument is specified as GLD_MAC_PROMISC_PHYS, GLD_MAC_PROMISC_MULTI, or GLD_MAC_PROMISC_NONE.

mp

gld_ioctl() uses mp as a pointer to a STREAMS message block containing the ioctl to be executed. gldm_send() uses mp as a pointer to a STREAMS message block containing the packet to be transmitted. gld_recv() uses mp as a pointer to a message block containing a received packet.

stats

Pointer to a gld_stats(9S) structure to be filled in with the current values of statistics counters.

q

Pointer to the queue(9S) structure to be used in the reply to the ioctl.

dip

Pointer to the device's dev_info structure.

name

Device interface name.

GLDv2 Entry Points

Entry points must be implemented by a device-specific network driver that has been designed to interface with GLDv2.

The gld_mac_info(9S) structure is the main structure for communication between the device-specific driver and the GLDv2 module. See the gld(7D) man page. Some elements in that structure are function pointers to the entry points that are described here. The device-specific driver must, in its attach(9E) routine, initialize these function pointers before calling gld_register().

gldm_reset() Entry Point

int prefix_reset(gld_mac_info_t *macinfo);

gldm_reset() resets the hardware to its initial state.

gldm_start() Entry Point

int prefix_start(gld_mac_info_t *macinfo);

gldm_start() enables the device to generate interrupts. gldm_start() also prepares the driver to call gld_recv() to deliver received data packets to GLDv2.

gldm_stop() Entry Point

int prefix_stop(gld_mac_info_t *macinfo);

gldm_stop() disables the device from generating any interrupts and stops the driver from calling gld_recv() for delivering data packets to GLDv2. GLDv2 depends on the gldm_stop() routine to ensure that the device will no longer interrupt. gldm_stop() must do so without fail. This function should always return GLD_SUCCESS.

gldm_set_mac_addr() Entry Point

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

gldm_set_mac_addr() sets the physical address that the hardware is to use for receiving data. This function enables the device to be programmed through the passed MAC address macaddr. If sufficient resources are currently not available to carry out the request, gldm_set_mac_add() should return GLD_NORESOURCES. If the requested function is not supported, gldm_set_mac_add() should return GLD_NOTSUPPORTED.

gldm_set_multicast() Entry Point

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

gldm_set_multicast() enables and disables device-level reception of specific multicast addresses. If the third argument multiflag is set to GLD_MULTI_ENABLE, then gldm_set_multicast() sets the interface to receive packets with the multicast address. gldm_set_multicast() uses the multicast address that is pointed to by the second argument. If multiflag is set to GLD_MULTI_DISABLE, the driver is allowed to disable reception of the specified multicast address.

This function is called whenever GLDv2 wants to enable or disable reception of a multicast, group, or functional address. GLDv2 makes no assumptions about how the device does multicast support and calls this function to enable or disable a specific multicast address. Some devices might use a hash algorithm and a bitmask to enable collections of multicast addresses. This procedure is allowed, and GLDv2 filters out any superfluous packets. If disabling an address could result in disabling more than one address at the device level, the device driver should keep any necessary information. This approach avoids disabling an address that GLDv2 has enabled but not disabled.

gldm_set_multicast() is not called to enable a particular multicast address that is already enabled. Similarly, gldm_set_multicast() is not called to disable an address that is not currently enabled. GLDv2 keeps track of multiple requests for the same multicast address. GLDv2 only calls the driver's entry point when the first request to enable, or the last request to disable, a particular multicast address is made. If sufficient resources are currently not available to carry out the request, the function should return GLD_NORESOURCES. The function should return GLD_NOTSUPPORTED if the requested function is not supported.

gldm_set_promiscuous() Entry Point

int prefix_set_promiscuous(gld_mac_info_t *macinfo, int promiscflag);

gldm_set_promiscuous() enables and disables promiscuous mode. This function is called whenever GLDv2 wants to enable or disable the reception of all packets on the medium. The function can also be limited to multicast packets on the medium. If the second argument promiscflag is set to the value of GLD_MAC_PROMISC_PHYS, then the function enables physical-level promiscuous mode. Physical-level promiscuous mode causes the reception of all packets on the medium. If promiscflag is set to GLD_MAC_PROMISC_MULTI, then reception of all multicast packets are enabled. If promiscflag is set to GLD_MAC_PROMISC_NONE, then promiscuous mode is disabled.

In promiscuous multicast mode, drivers for devices without multicast-only promiscuous mode must set the device to physical promiscuous mode. This approach ensures that all multicast packets are received. In this case, the routine should return GLD_SUCCESS. The GLDv2 software filters out any superfluous packets. If sufficient resources are currently not available to carry out the request, the function should return GLD_NORESOURCES. The gld_set_promiscuous() function should return GLD_NOTSUPPORTED if the requested function is not supported.

For forward compatibility, gldm_set_promiscuous() routines should treat any unrecognized values for promiscflag as though these values were GLD_MAC_PROMISC_PHYS.

gldm_send() Entry Point

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

gldm_send() queues a packet to the device for transmission. This routine is passed a STREAMS message containing the packet to be sent. The message might include multiple message blocks. The send() routine must traverse all the message blocks in the message to access the entire packet to be sent. The driver should be prepared to handle and skip over any zero-length message continuation blocks in the chain. The driver should also check that the packet does not exceed the maximum allowable packet size. The driver must pad the packet, if necessary, to the minimum allowable packet size. If the send routine successfully transmits or queues the packet, GLD_SUCCESS should be returned.

The send routine should return GLD_NORESOURCES if the packet for transmission cannot be immediately accepted. In this case, GLDv2 retries later. If gldm_send() ever returns GLD_NORESOURCES, the driver must call gld_sched() at a later time when resources have become available. This call to gld_sched() informs GLDv2 to retry packets that the driver previously failed to queue for transmission. (If the driver's gldm_stop() routine is called, the driver is absolved from this obligation until the driver returns GLD_NORESOURCES from the gldm_send() routine. However, extra calls to gld_sched() do not cause incorrect operation.)

If the driver's send routine returns GLD_SUCCESS, then the driver is responsible for freeing the message when the message is no longer needed. If the hardware uses DMA to read the data directly, the driver must not free the message until the hardware has completely read the data. In this case, the driver can free the message in the interrupt routine. Alternatively, the driver can reclaim the buffer at the start of a future send operation. If the send routine returns anything other than GLD_SUCCESS, then the driver must not free the message. Return GLD_NOLINK if gldm_send() is called when there is no physical connection to the network or link partner.

gldm_intr() Entry Point

int prefix_intr(gld_mac_info_t *macinfo);

gldm_intr() is called when the device might have interrupted. Because interrupts can be shared with other devices, the driver must check the device status to determine whether that device actually caused the interrupt. If the device that the driver controls did not cause the interrupt, then this routine must return DDI_INTR_UNCLAIMED. Otherwise, the driver must service the interrupt and return DDI_INTR_CLAIMED. If the interrupt was caused by successful receipt of a packet, this routine should put the received packet into a STREAMS message of type M_DATA and pass that message to gld_recv().

gld_recv() passes the inbound packet upstream to the appropriate next layer of the network protocol stack. The routine must correctly set the b_rptr and b_wptr members of the STREAMS message before calling gld_recv().

The driver should avoid holding mutex or other locks during the call to gld_recv(). In particular, locks that could be taken by a transmit thread must not be held during a call to gld_recv(). In some cases, the interrupt thread that calls gld_recv() sends an outgoing packet, which results in a call to the driver's gldm_send() routine. If gldm_send() tries to acquire a mutex that is held by gldm_intr() when gld_recv() is called, a panic occurs due to recursive mutex entry. If other driver entry points attempt to acquire a mutex that the driver holds across a call to gld_recv(), deadlock can result.

The interrupt code should increment statistics counters for any errors. Errors include the failure to allocate a buffer that is needed for the received data and any hardware-specific errors, such as CRC errors or framing errors.

gldm_get_stats() Entry Point

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

gldm_get_stats() gathers statistics from the hardware, driver private counters, or both, and updates the gld_stats(9S) structure pointed to by stats. This routine is called by GLDv2 for statistics requests. GLDv2 uses the gldm_get_stats() mechanism to acquire device-dependent statistics from the driver before GLDv2 composes the reply to the statistics request. See the gld_stats(9S), gld(7D), and qreply(9F) man pages for more information about defined statistics counters.

gldm_ioctl() Entry Point

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

gldm_ioctl() implements any device-specific ioctl commands. This element is allowed to be null if the driver does not implement any ioctl functions. The driver is responsible for converting the message block into an ioctl reply message and calling the qreply(9F) function before returning GLD_SUCCESS. This function should always return GLD_SUCCESS. The driver should report any errors as needed in a message to be passed to qreply(9F). If the gldm_ioctl element is specified as NULL, GLDv2 returns a message of type M_IOCNAK with an error of EINVAL.

GLDv2 Return Values

Some entry point functions in GLDv2 can return the following values, subject to the restrictions above:

GLD_BADARG

If the function detected an unsuitable argument, for example, a bad multicast address, a bad MAC address, or a bad packet

GLD_FAILURE

On hardware failure

GLD_SUCCESS

On success

GLDv2 Service Routines

This section provides the syntax and description for the GLDv2 service routines.

gld_mac_alloc() Function

gld_mac_info_t *gld_mac_alloc(dev_info_t *dip);

gld_mac_alloc() allocates a new gld_mac_info(9S) structure and returns a pointer to the structure. Some of the GLDv2-private elements of the structure might be initialized before gld_mac_alloc() returns. All other elements are initialized to zero. The device driver must initialize some structure members, as described in the gld_mac_info(9S) man page, before passing the pointer to the gld_mac_info structure to gld_register().

gld_mac_free() Function

void gld_mac_free(gld_mac_info_t *macinfo);

gld_mac_free() frees a gld_mac_info(9S) structure previously allocated by gld_mac_alloc().

gld_register() Function

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

gld_register() is called from the device driver's attach(9E) routine. gld_register() links the GLDv2-based device driver with the GLDv2 framework. Before calling gld_register(), the device driver's attach(9E) routine uses gld_mac_alloc() to allocate a gld_mac_info(9S) structure, and then initializes several structure elements. See gld_mac_info(9S) for more information. A successful call to gld_register() performs the following actions:

The device interface name passed to gld_register() must exactly match the name of the driver module as that name exists in the file system.

The driver's attach(9E) routine should return DDI_SUCCESS if gld_register() succeeds. If gld_register() does not return DDI_SUCCESS, the attach(9E) routine should deallocate any allocated resources before calling gld_register(), and then return DDI_FAILURE.

gld_unregister() Function

int gld_unregister(gld_mac_info_t *macinfo);

gld_unregister() is called by the device driver's detach(9E) function, and if successful, performs the following tasks:

If gld_unregister() returns DDI_SUCCESS, the detach(9E) routine should deallocate any data structures allocated in the attach(9E) routine, using gld_mac_free() to deallocate the macinfo structure, and return DDI_SUCCESS. If gld_unregister() does not return DDI_SUCCESS, the driver's detach(9E) routine must leave the device operational and return DDI_FAILURE.

gld_recv() Function

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

gld_recv() is called by the driver's interrupt handler to pass a received packet upstream. The driver must construct and pass a STREAMS M_DATA message containing the raw packet. gld_recv() determines which STREAMS queues should receive a copy of the packet, duplicating the packet if necessary. gld_recv() then formats a DL_UNITDATA_IND message, if required, and passes the data up all appropriate streams.

The driver should avoid holding mutex or other locks during the call to gld_recv(). In particular, locks that could be taken by a transmit thread must not be held during a call to gld_recv(). The interrupt thread that calls gld_recv() in some cases carries out processing that includes sending an outgoing packet. Transmission of the packet results in a call to the driver's gldm_send() routine. If gldm_send() tries to acquire a mutex that is held by gldm_intr() when gld_recv() is called, a panic occurs due to a recursive mutex entry. If other driver entry points attempt to acquire a mutex that the driver holds across a call to gld_recv(), deadlock can result.

gld_sched() Function

void gld_sched(gld_mac_info_t *macinfo);

gld_sched() is called by the device driver to reschedule stalled outbound packets. Whenever the driver's gldm_send() routine returns GLD_NORESOURCES, the driver must call gld_sched() to inform the GLDv2 framework to retry previously unsendable packets. gld_sched() should be called as soon as possible after resources become available so that GLDv2 resumes passing outbound packets to the driver's gldm_send() routine. (If the driver's gldm_stop() routine is called, the driver need not retry until GLD_NORESOURCES is returned from gldm_send(). However, extra calls to gld_sched() do not cause incorrect operation.)

gld_intr() Function

uint_t gld_intr(caddr_t);

gld_intr() is GLDv2's main interrupt handler. Normally, gld_intr() is specified as the interrupt routine in the device driver's call to ddi_add_intr(9F). The argument to the interrupt handler is specified as int_handler_arg in the call to ddi_add_intr(9F). This argument must be a pointer to the gld_mac_info(9S) structure. gld_intr(), when appropriate, calls the device driver's gldm_intr() function, passing that pointer to the gld_mac_info(9S) structure. However, to use a high-level interrupt, the driver must provide its own high-level interrupt handler and trigger a soft interrupt from within the handler. In this case, gld_intr() would normally be specified as the soft interrupt handler in the call to ddi_add_softintr(). gld_intr() returns a value that is appropriate for an interrupt handler.