Differences

This shows you the differences between two versions of the page.

Link to this comparison view

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 network ​driver]]).  For example, in natsemi.c
-  - Setup TX & RX rings+<code c> 
-  - Perform other configuration (e.gfiltersburstsinterrupts). +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.

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