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.busnvo_init(&nvob, &spidev.nvs, nvof, ..) where nvof is an array of struct nvo_fragments that specify the usable regions. This will also initialize a settings block. struct nvo_block with nvob.nvs = &spidev.nvsnvob.fragments = nvof where nvof is an array of struct nvo_fragments 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.