Part I Designing Device Drivers for the Solaris Platform
1. Overview of Solaris Device Drivers
2. Solaris Kernel and Device Tree
5. Managing Events and Queueing Tasks
7. Device Access: Programmed I/O
10. Mapping Device and Kernel Memory
14. Layered Driver Interface (LDI)
Part II Designing Specific Kinds of Device Drivers
15. Drivers for Character Devices
18. SCSI Host Bus Adapter Drivers
19. Drivers for Network Devices
GLDv3 Network Device Driver Framework
GLDv3 MAC Registration Process
GLDv3 MAC Registration Functions
GLDv3 MAC Registration Data Structures
Large Segment (or Send) Offload
GLDv2 Network Device Driver Framework
Ethernet V2 and ISO 8802-3 (IEEE 802.3)
GLDv2 Declarations and Data Structures
gldm_set_mac_addr() Entry Point
gldm_set_multicast() Entry Point
gldm_set_promiscuous() Entry Point
Part III Building a Device Driver
21. Compiling, Loading, Packaging, and Testing Drivers
22. Debugging, Testing, and Tuning Device Drivers
23. Recommended Coding Practices
B. Summary of Solaris DDI/DKI Services
C. Making a Device Driver 64-Bit Ready
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 defines a driver API for drivers that register with a plugin type of MAC_PLUGIN_IDENT_ETHER.
A GLDv3 device driver must perform the following steps to register with the MAC layer:
Include the following three MAC header files: sys/mac.h, sys/mac_ether.h, and sys/mac_provider.h. Do not include any other MAC-related header file in your driver.
Populate the mac_callbacks structure.
Invoke the mac_init_ops() function in its _init() entry point.
Invoke the mac_alloc() function in its attach() entry point to allocate a mac_register structure.
Populate the mac_register structure and invoke the mac_register() function in its attach() entry point.
Invoke the mac_unregister() function in its detach() entry point.
Invoke the mac_fini_ops() function in its _fini() entry point.
Link with a dependency on misc/mac:
# ld -N"misc/mac" xx.o -o xx
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.
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); }
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().
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); } /* ... */ }
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.
The mc_ioctl() entry point is present.
The mc_getcapab() entry point is present.
The mc_setprop() entry point is present.
The mc_getprop() entry point is present.
The mc_propinfo() entry point is present.
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 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.
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.
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.
Partial 1's complement checksum ability
Full 1's complement checksum ability for IPv4 packets
Full 1's complement checksum ability for IPv6 packets
IPv4 Header checksum offload capability
The flags argument of mac_hcksum_get() is a combination of the following values:
Compute the full checksum for this packet.
The full checksum was verified in hardware and is correct.
Compute the partial 1's complement checksum based on other parameters passed to mac_hcksum_get(). HCK_PARTIALCKSUM is mutually exclusive with HCK_FULLCKSUM.
Compute the IP header checksum.
The IP header checksum was verified in hardware and is correct.
The flags argument of mac_hcksum_set() is a combination of the following values:
The full checksum was computed and passed through the value argument.
The full checksum was verified in hardware and is correct.
The partial checksum was computed and passed through the value argument. HCK_PARTIALCKSUM is mutually exclusive with HCK_FULLCKSUM.
The IP header checksum was computed and passed through the value argument.
The IP header checksum was verified in hardware and is correct.
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.
Data-path entry points are comprised of the following components:
Callbacks exported by the driver and invoked by the GLDv3 framework for sending packets.
GLDv3 framework entry points called by the driver for transmit flow control and for receiving packets.
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.
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.
If the driver specified hardware checksum support (see Hardware Checksum Offload), then the driver must do the following tasks:
Use mac_hcksum_get(9F) to check every packet for hardware checksum metadata.
Program the hardware to perform the required checksum calculation.
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.
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.
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.
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.
VLAN packets must be passed with their tags to the MAC layer. Do not strip the VLAN headers from the packets.
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:
The media link is up.
The media link is down.
The media link is unknown.
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); }
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:
The property is read-only
The property is write-only
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.
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
|