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:

  1. Allocate a struct net_device with associated private data using alloc_etherdev().
  2. Associate the driver functions with the net_device via netdev_init().
  3. Associate the net_device with the pci_device via pci_set_drvdata().
  4. Initialize private data.
  5. Ensure busmastering is enabled and check pci latency with adjust_pci_device().
  6. Reset the device.
  7. Initialize EEPROM.
  8. Read the MAC address from EEPROM.
  9. 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.
  10. Name the device and add it to the list of network devices via register_netdev().
  11. Possibly setup a non-volatile stored options block with nvo_init() & register_nvo().

remove

static void remove ( struct pci_device* )
This function is called last to remove the device. A typical driver will:

  1. Call unregister_nvo() for any registered non-volatile stored options.
  2. Call iounmap() for any addresses previously mapped with ioremap().
  3. Call unregister_netdev() for the device previously registered with register_netdev()
  4. Reset the device.
  5. Dissociate driver functions from net_device via netdev_nullify().
  6. Decrement reference count of net_device with netdev_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:

  1. Acknowledge interrupts.
  2. Disable irq, receives.
  3. Reset the device.
  4. 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:

  1. Program MAC address to device.
  2. Setup TX & RX rings.
  3. Perform other configuration (e.g. filters, bursts, interrupts).
  4. 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:

  1. Check for tx overflow.
  2. Save buffer pointer for later tx completion reference.
  3. Pad & align packet if necessary.
  4. Add packet to transmit ring.
  5. 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:

  1. Acknowledge interrupts.
  2. Check hardware and feed tx completions to netdev_tx_complete() or netdev_tx_complete_err().
  3. Add good received packets to receive queue with netdev_rx(), or report corrupted packets to netdev_rx_err()
  4. Check link state occasionally, and report changes with netdev_link_up() or netdev_link_down()

irq

static void irq ( struct net_device*, int enable )
In this function, a typical driver will:

  1. 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:
    1. The read_bit() and write_bit() function pointers are stored in a struct spi_bit_basher.
    2. The mode & endianness are also initialized.
    3. init_spi_bit_basher() is called.
    4. A struct spi_device is initialized (EEPROM-model dependent).
    5. Bus is copied: spidev.bus = &spibb.bus
    6. If non-volatile options will be utilized:
      1. Call nvo_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.
    7. Else, if non-volatile options will not be used:
      1. Initialize a struct nvo_block with nvob.nvs = &spidev.nvs
      2. Assign usable regions via nvob.fragments = nvof where nvof is an array of struct nvo_fragments 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.

QR Code
QR Code soc:2008:mdeck:notes:gpxe_driver_api (generated for current page)