Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
soc:2008:mdeck:notes:gpxe_driver_api [2008/05/28 14:18] mdeck |
soc:2008:mdeck:notes:gpxe_driver_api [2009/11/03 08:58] (current) meteger |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| ====== gPXE Driver API Documentation ====== | ====== gPXE Driver API Documentation ====== | ||
| - | :!: Under Construction | + | A gPXE network driver may incorporate elements of the following: |
| - | ===== Driver Exported Functions ===== | + | * [[#gpxe_pci_device_driver_api|gPXE PCI Device Driver API]] |
| - | The following functions should be defined in any network driver: | + | * [[#gpxe_network_driver_api|gPXE Network Driver API]] |
| + | * [[#non-volatile_storage_api|Non-Volatile Storage API]] | ||
| + | |||
| + | Note the [[:dev:netdriverapi|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: | ||
| + | <code 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, | ||
| + | }; | ||
| + | </code> | ||
| + | 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* )]]'' | * ''[[#probe|static int probe ( struct pci_device* , const struct pci_device_id* )]]'' | ||
| * ''[[#remove|static void remove ( struct pci_device* )]]'' | * ''[[#remove|static void remove ( struct pci_device* )]]'' | ||
| - | * ''[[#reset|static void reset ( struct net_device* )]]'' | ||
| - | * ''[[#open|static int open ( struct net_device* )]]'' | ||
| - | * ''[[#close|static void close ( struct net_device* )]]'' | ||
| - | * ''[[#transmit|static int transmit ( struct net_device*, struct io_buffer* )]]'' | ||
| - | * ''[[#poll|static void poll ( struct net_device* )]]'' | ||
| - | * ''[[#irq|static void irq ( struct net_device*, int enable )]]'' | ||
| - | In addition, your driver should define a ''struct pci_driver'' where | ||
| - | >''.ids = '' an array of ''struct pci_device_id''s | ||
| - | >''.id_count = '' array count | ||
| - | >''.probe = '' the driver ''probe'' function | ||
| - | >''.remove = '' the driver ''remove'' function | ||
| ==== probe ==== | ==== probe ==== | ||
| ''static int probe ( struct pci_device* , const struct pci_device_id* )''\\ | ''static int probe ( struct pci_device* , const struct pci_device_id* )''\\ | ||
| - | This function is called first to initialize the card. Here a typical driver will: | + | This function is called [[:soc:2008:mdeck:notes:initialization|first]] to initialize the card. Here a typical network driver will: |
| - Allocate a ''struct net_device'' with associated private data using ''alloc_etherdev()''. | - Allocate a ''struct net_device'' with associated private data using ''alloc_etherdev()''. | ||
| - Associate the driver functions with the ''net_device'' via ''netdev_init()''. | - Associate the driver functions with the ''net_device'' via ''netdev_init()''. | ||
| Line 28: | Line 51: | ||
| - Initialize [[#EEPROM|EEPROM]]. | - Initialize [[#EEPROM|EEPROM]]. | ||
| - Read the MAC address from EEPROM. | - Read the MAC address from EEPROM. | ||
| - | - Mark the ''net_device'' as having a link up with ''netdev_link_up()'', as we don't yet handle the link state. | + | - 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()''. | - 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()'' & ''register_nvo()''. | - Possibly setup a non-volatile stored options block with ''nvo_init()'' & ''register_nvo()''. | ||
| Line 42: | Line 65: | ||
| - Decrement reference count of ''net_device'' with ''netdev_put()''. | - Decrement reference count of ''net_device'' with ''netdev_put()''. | ||
| - | ==== reset ==== | ||
| - | ''static void reset ( struct net_device* )''\\ | ||
| - | This function issues a hardware reset and waits for completion. A typical driver might: | ||
| - | - Mask off interrupts. | ||
| - | - Clear pending transmits. | ||
| - | - Trigger hardware reset. | ||
| - | - Wait for completion. | ||
| - | ==== open ==== | + | |
| - | ''static int open ( struct net_device* )''\\ | + | ===== gPXE Network Driver API ===== |
| - | In this function, a driver might: | + | A network driver in gPXE provides its API routines to the system via a ''struct net_device_operations'' during the initial ''probe()'' call |
| - | - Program MAC address to device. | + | (see [[:soc:2008:mdeck:notes:initialization|initialization of a network driver]]). For example, in natsemi.c: |
| - | - Setup TX & RX rings. | + | <code c> |
| - | - Perform other configuration (e.g. filters, bursts, interrupts). | + | static struct net_device_operations natsemi_operations = { |
| - | - Enable RX and TX. | + | .open = natsemi_open, |
| + | .close = natsemi_close, | ||
| + | .transmit = natsemi_transmit, | ||
| + | .poll = natsemi_poll, | ||
| + | .irq = natsemi_irq, | ||
| + | }; | ||
| + | </code> | ||
| + | Here, natsemi_open/close/etc are driver implementations of the required network driver API functions: | ||
| + | * ''[[#close|static void close ( struct net_device* )]]'' | ||
| + | * ''[[#open|static int open ( struct net_device* )]]'' | ||
| + | * ''[[#transmit|static int transmit ( struct net_device*, struct io_buffer* )]]'' | ||
| + | * ''[[#poll|static void poll ( struct net_device* )]]'' | ||
| + | * ''[[#irq|static void irq ( struct net_device*, int enable )]]'' | ||
| ==== close ==== | ==== close ==== | ||
| ''static void close ( struct net_device* )''\\ | ''static void close ( struct net_device* )''\\ | ||
| - | In this function, a typical driver might: | + | This function is called if the device is open in ''autoboot()'' during [[:soc:2008:mdeck:notes:initialization|initialization]], after a failed or successful attempt to boot the network device. In this routine, a typical driver might: |
| - Acknowledge interrupts. | - Acknowledge interrupts. | ||
| - Disable irq, receives. | - Disable irq, receives. | ||
| - Reset the device. | - Reset the device. | ||
| - Free any used resources (e.g. rx/tx rings, dma buffers, etc). | - 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 [[:soc:2008:mdeck:notes:initialization|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 ==== | ==== transmit ==== | ||
| ''static int transmit ( struct net_device*, struct io_buffer* )''\\ | ''static int transmit ( struct net_device*, struct io_buffer* )''\\ | ||
| - | In this function, a typical driver might: | + | A data transmission is actuated with this routine. A typical driver might: |
| - Check for tx overflow. | - Check for tx overflow. | ||
| - Save buffer pointer for later tx completion reference. | - Save buffer pointer for later tx completion reference. | ||
| Line 77: | Line 113: | ||
| ==== poll ==== | ==== poll ==== | ||
| ''static void poll ( struct net_device* )''\\ | ''static void poll ( struct net_device* )''\\ | ||
| - | In this function, a typical driver might: | + | This function is called periodically by the network stack to process tx completions and rx packets. A typical driver would: |
| - Acknowledge interrupts. | - Acknowledge interrupts. | ||
| - | - Feed tx completions to ''netdev_tx_complete()'' or ''netdev_tx_complete_err()''. | + | - Check hardware and feed tx completions to ''netdev_tx_complete()'' or ''netdev_tx_complete_err()''. |
| - | - Add good received packets to receive queue with ''netdev_rx()'', or feed corrupted packets to ''netdev_rx_err()'' | + | - Add good received packets to receive queue with ''netdev_rx()'', or report corrupted packets to ''netdev_rx_err()'' |
| + | - Check link state occasionally, and report changes with ''netdev_link_up()'' or ''netdev_link_down()'' | ||
| ==== irq ==== | ==== irq ==== | ||
| Line 86: | Line 123: | ||
| In this function, a typical driver will: | In this function, a typical driver will: | ||
| - Enable interrupts if the int parameter is non-zero | - Enable interrupts if the int parameter is non-zero | ||
| + | Note the //force interrupt// behavior from Etherboot is deprecated. | ||
| + | |||
| + | |||
| - | ===== EEPROM ===== | + | ===== Non-Volatile Storage API ===== |
| - | The EEPROM can be accessed via direct port access or using nvs functions. | + | The nvs API may be used to access non-volatile storage that conforms to a number of supported SPI variants. |
| - | ==== Non-volatile storage (nvs) ==== | + | |
| - | The nvs functions may be used to access non-volatile storage that conforms to a number of supported SPI protocols. | + | |
| * To initialize nvs support: | * To initialize nvs support: | ||
| - The read_bit() and write_bit() function pointers are stored in a ''struct spi_bit_basher''. | - The read_bit() and write_bit() function pointers are stored in a ''struct spi_bit_basher''. | ||
| Line 97: | Line 135: | ||
| - A ''struct spi_device'' is initialized (EEPROM-model dependent). | - A ''struct spi_device'' is initialized (EEPROM-model dependent). | ||
| - Bus is copied: ''spidev.bus = &spibb.bus'' | - Bus is copied: ''spidev.bus = &spibb.bus'' | ||
| - | - EITHER | + | - If non-volatile options will be utilized: |
| + | - Call ''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. | ||
| + | - Else, if non-volatile options will //not// be used: | ||
| - Initialize a ''struct nvo_block'' with ''nvob.nvs = &spidev.nvs'' | - Initialize a ''struct nvo_block'' with ''nvob.nvs = &spidev.nvs'' | ||
| - Assign usable regions via ''nvob.fragments = nvof'' where ''nvof'' is an array of ''struct nvo_fragment''s that specify the usable regions. | - Assign usable regions via ''nvob.fragments = nvof'' where ''nvof'' is an array of ''struct nvo_fragment''s that specify the usable regions. | ||
| - | - OR | ||
| - | - Call ''nvo_init(&nvob, &spidev.nvs, nvof, ..)'' where ''nvof'' is an array of ''struct nvo_fragment''s that specify the usable regions. | ||
| * Use ''nvs_read()'' to perform a serial read @ a specific address. | * Use ''nvs_read()'' to perform a serial read @ a specific address. | ||
| * Use ''nvs_write()'' to perform a serial write @ a specific address. | * Use ''nvs_write()'' to perform a serial write @ a specific address. | ||