Table of Contents
gPXE Driver API Documentation
A gPXE network driver may incorporate elements of the following:
Note the previous driver model of Etherboot is deprecated. Existing Etherboot PCI drivers are temporarily supported via the compatibility layer in src/drivers/net/legacy.c Drivers currently conforming to the gPXE Network Driver API are:
- 3c90x
- ath5k
- atl1e
- b44
- e1000
- etherfabric
- mtnic
- natsemi
- phantom
- pnic
- r8169
- rtl8139
- rtl818x
- sis190
- sky2
gPXE PCI Device Driver API
A PCI driver provides its API routines to gPXE via a struct pci_driver
. For example, in natsemi.c:
struct pci_driver natsemi_driver __pci_driver = { .ids = natsemi_nics, .id_count = (sizeof (natsemi_nics) / sizeof (natsemi_nics[0])), .probe = natsemi_probe, .remove = natsemi_remove, };
The .ids
and .id_count
members list the vendor & device IDs of supported devices.
The functions natsemi_probe & natsemi_remove are driver implementations of the required PCI device driver API functions:
probe
static int probe ( struct pci_device* , const struct pci_device_id* )
This function is called first to initialize the card. Here a typical network driver will:
- Allocate a
struct net_device
with associated private data usingalloc_etherdev()
. - Associate the driver functions with the
net_device
vianetdev_init()
. - Associate the
net_device
with thepci_device
viapci_set_drvdata()
. - Initialize private data.
- Ensure busmastering is enabled and check pci latency with
adjust_pci_device()
. - Reset the device.
- Initialize EEPROM.
- Read the MAC address from EEPROM.
- Check the link state and report
netdev_link_up()
if connected. Many drivers don't yet handle the link state and simply assume the link is up. - Name the device and add it to the list of network devices via
register_netdev()
. - Possibly setup a non-volatile stored options block with
nvo_init()
®ister_nvo()
.
remove
static void remove ( struct pci_device* )
This function is called last to remove the device. A typical driver will:
- Call
unregister_nvo()
for any registered non-volatile stored options. - Call
iounmap()
for any addresses previously mapped withioremap()
. - Call
unregister_netdev()
for the device previously registered withregister_netdev()
- Reset the device.
- Dissociate driver functions from
net_device
vianetdev_nullify()
. - Decrement reference count of
net_device
withnetdev_put()
.
gPXE Network Driver API
A network driver in gPXE provides its API routines to the system via a struct net_device_operations
during the initial probe()
call
(see initialization of a network driver). For example, in natsemi.c:
static struct net_device_operations natsemi_operations = { .open = natsemi_open, .close = natsemi_close, .transmit = natsemi_transmit, .poll = natsemi_poll, .irq = natsemi_irq, };
Here, natsemi_open/close/etc are driver implementations of the required network driver API functions:
close
static void close ( struct net_device* )
This function is called if the device is open in autoboot()
during initialization, after a failed or successful attempt to boot the network device. In this routine, a typical driver might:
- Acknowledge interrupts.
- Disable irq, receives.
- Reset the device.
- Free any used resources (e.g. rx/tx rings, dma buffers, etc).
open
static int open ( struct net_device* )
This function is first called in netboot()
when attempting a device boot during initialization, after close()
of any previous device attempt is called. A driver would:
- Program MAC address to device.
- Setup TX & RX rings.
- Perform other configuration (e.g. filters, bursts, interrupts).
- Enable RX and TX.
transmit
static int transmit ( struct net_device*, struct io_buffer* )
A data transmission is actuated with this routine. A typical driver might:
- Check for tx overflow.
- Save buffer pointer for later tx completion reference.
- Pad & align packet if necessary.
- Add packet to transmit ring.
- Possibly ensure transmit is on.
poll
static void poll ( struct net_device* )
This function is called periodically by the network stack to process tx completions and rx packets. A typical driver would:
- Acknowledge interrupts.
- Check hardware and feed tx completions to
netdev_tx_complete()
ornetdev_tx_complete_err()
. - Add good received packets to receive queue with
netdev_rx()
, or report corrupted packets tonetdev_rx_err()
- Check link state occasionally, and report changes with
netdev_link_up()
ornetdev_link_down()
irq
static void irq ( struct net_device*, int enable )
In this function, a typical driver will:
- Enable interrupts if the int parameter is non-zero
Note the force interrupt behavior from Etherboot is deprecated.
Non-Volatile Storage API
The nvs API may be used to access non-volatile storage that conforms to a number of supported SPI variants.
- To initialize nvs support:
- The read_bit() and write_bit() function pointers are stored in a
struct spi_bit_basher
. - The mode & endianness are also initialized.
init_spi_bit_basher()
is called.- A
struct spi_device
is initialized (EEPROM-model dependent). - Bus is copied:
spidev.bus = &spibb.bus
- If non-volatile options will be utilized:
- Call
nvo_init(&nvob, &spidev.nvs, nvof, ..)
wherenvof
is an array ofstruct nvo_fragment
s that specify the usable regions. This will also initialize a settings block.
- Else, if non-volatile options will not be used:
- Initialize a
struct nvo_block
withnvob.nvs = &spidev.nvs
- Assign usable regions via
nvob.fragments = nvof
wherenvof
is an array ofstruct nvo_fragment
s that specify the usable regions.
- Use
nvs_read()
to perform a serial read @ a specific address. - Use
nvs_write()
to perform a serial write @ a specific address.