When a network interface is brought up—for example, using the ifconfig command—the driver’s enable function is invoked. This function is responsible for preparing the driver to transmit and receive packets. Here are a few of the operations typically performed in enable:
Opening the driver’s provider nub (if the nub was closed in the start function).
Creating any resources needed by the driver for operation, such as hardware-specific transmit and receive buffers, memory cursors for managing scatter/gather lists, and event sources for the work loop.
Resetting the hardware so that it’s ready to transmit and receive packets.
Starting I/O by enabling hardware interrupts as well as interrupt event sources and setting timer event sources, restarting the output queue (if the driver uses one), and then starting the transmit and receive engines on the hardware.
The disable function must reverse this process, disabling what was enabled, shutting down what was started, disposing of resources that were created, and, if necessary, closing the provider nub. The driver should also reset the hardware when disabling, to leave it in a known state.
You invoke the enable and disable functions within a synchronized context through the driver’s superclass, using an IOCommandGate on the driver’s work loop. These functions are intended to be overridden, and not directly invoked.
Opening and Closing the Provider Nub
Creating and Destroying Resources
Starting and Stopping I/O
Both enable and disable are invoked with the provider nub the driver was originally started with. The enable function is responsible for invoking open on the nub, returning a failure result if it can’t open the nub. Similarly, disable must invoke close on the nub. Because network driver code can be running in multiple threads, however, the driver should implement a mutex. The lock should ensure that the enable function is allowed to run only once at a time, and that the disable function can run only after the enable function. The sample driver uses a Boolean variable, fNetifEnabled, as the lock.
In order to preserve kernel resources, a driver should never consume more system memory than necessary. This means that the driver should delay creating resources until they’re actually needed, as when the driver becomes active, and should dispose of those resources when it becomes inactive. Such resources typically include any hardware-specific transmit and receive buffers, the BSD mbuf_t structures used to pass packets up and down, memory cursor objects used to manage scatter/gather lists based on the mbuf_t structures, and any event sources needed for operation.
Hardware-specific resources are necessarily outside the scope of this document. Whatever the hardware-specific resources are, however, they will typically include a receive buffer that contains mbuf_t structures. Your driver can use its network interface object to allocate and free these structures through the allocatePacket and freePacket functions defined by IONetworkController.
Memory cursors, represented by the IOMBufMemoryCursor group of classes, manage the translation between mbuf_t structures and scatter/gather lists used by the hardware. Specific classes are available to handle data in big-endian, little-endian, or CPU-native byte order, and to handle data used with DBDMA engines. Your driver will typically create one or more memory cursors, depending on whether it’s single- or multithreaded and whether the transmit and receive buffers used by hardware are similar or different.
To start I/O, the enable function should enable the interrupt and timer event sources and then enable any hardware-specific interrupts. Following this, it should start up the output queue (if it has one) using IOOutputQueue’s setCapacity and start functions. Finally, it should start any I/O engines on the hardware.
The disable function stops I/O by roughly reversing this process. It should disable hardware-specific interrupts, disable the interrupt event source and cancel any pending timeout. It should then stop the I/O engines and reset the hardware if necessary. Then, it must stop and flush the output queue by invoking its stop function, setting its capacity to zero with setCapacity, and invoking flush.
Last updated: 2008-03-11