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:
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:
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:
struct net_device
with associated private data using alloc_etherdev()
.net_device
via netdev_init()
.net_device
with the pci_device
via pci_set_drvdata()
.adjust_pci_device()
.netdev_link_up()
if connected. Many drivers don't yet handle the link state and simply assume the link is up.register_netdev()
.nvo_init()
& register_nvo()
.
static void remove ( struct pci_device* )
This function is called last to remove the device. A typical driver will:
unregister_nvo()
for any registered non-volatile stored options.iounmap()
for any addresses previously mapped with ioremap()
.unregister_netdev()
for the device previously registered with register_netdev()
net_device
via netdev_nullify()
.net_device
with netdev_put()
.
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:
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:
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:
static int transmit ( struct net_device*, struct io_buffer* )
A data transmission is actuated with this routine. A typical driver might:
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:
netdev_tx_complete()
or netdev_tx_complete_err()
.netdev_rx()
, or report corrupted packets to netdev_rx_err()
netdev_link_up()
or netdev_link_down()
static void irq ( struct net_device*, int enable )
In this function, a typical driver will:
Note the force interrupt behavior from Etherboot is deprecated.
The nvs API may be used to access non-volatile storage that conforms to a number of supported SPI variants.
struct spi_bit_basher
.init_spi_bit_basher()
is called.struct spi_device
is initialized (EEPROM-model dependent).spidev.bus = &spibb.bus
nvo_init(&nvob, &spidev.nvs, nvof, ..)
where nvof
is an array of struct nvo_fragment
s that specify the usable regions. This will also initialize a settings block. struct nvo_block
with nvob.nvs = &spidev.nvs
nvob.fragments = nvof
where nvof
is an array of struct nvo_fragment
s that specify the usable regions.nvs_read()
to perform a serial read @ a specific address.nvs_write()
to perform a serial write @ a specific address.