This is an old revision of the document!
====== Writing wireless card device drivers ====== ===== Basic stuff ===== gPXE implements wireless support using a wrapper around a ''net_device'', following much the same concept as the Infiniband subsystem. Since 802.11 uses Ethernet-style addressing, it is not necessary to devise a separate means for handling "IP over 802.11"; there only needs to be an association between a generic ''net_device'' for a particular wireless card and the ''net80211_device'' representing its 802.11-specific attributes. For simplicity of design I chose to have the ''net_device'' wrap the ''net80211_device'', instead of vice versa; that way we don't have to worry about queueing packets or tracking TX completions ourselves. As much as possible, I have tried to keep the wireless API analogous to that for wired NICs except where additional functionality in the wireless realm forces differences. Because a wireless device is encapsulated in a ''net_device'', though, almost all gPXE code need know nothing about wireless. Calling wireless-specific functions is necessary only for wireless drivers and perhaps in the future some wireless-specific code in other areas. Such calls should generally be avoided, because they introduce a link-time dependency on the rather large 802.11 stack; when the file introducing them can be compiled in or out due to a configuration option, the ''REQUIRE_OBJECT()'' calls should be placed in ''net/80211/config_80211.c'' instead of ''core/config.c'' to avoid ever pulling in the objects without a wireless stack present to make them useful. When a wireless network card's driver ''probe()'' function is called, it acts much the same as a wired NIC, setting up device fundamentals and registering itself with the network stack. An 802.11 device is allocated using ''net80211_alloc()'' and registered using ''net80211_register()''; the driver must additionally pass some hardware-specific information about the wireless capabilities of the device it is registering. A wrapping ''net_device'' is created and registered behind the scenes in this call, and it can later be accessed by the ''netdev'' field in the ''net80211_device'' structure in use. In all of the following, ''netdev'' denotes a pointer to a ''net_device'' structure and ''dev'' denotes a pointer to a ''net80211_device'' structure. ===== Use in a wireless driver ===== In general, in an 802.11 driver, ''net80211_device'' simply takes the place of ''net_device''. Some of the fields in ''net_device'' must continue to be accessed through the ''dev->netdev'' wrapper structure pointer, while others have analogues in the ''net80211_device'' structure directly. Specifically: * For a wired NIC, the proper MAC address to use is always ''netdev->ll_addr''. For a wireless NIC, there are two available: ''dev->netdev->ll_addr'' is the MAC address most recently configured by the user, and ''dev->hw->hwaddr'' is the MAC address burned into the card. Usually (e.g. for setting the RX filter) the former should be used. * Any access to ''netdev->name'' and ''netdev->ll_protocol'' should continue to use the wrapping ''net_device''. * The ''netdev->state'' field retains its validity, but for wireless it does not provide much status detail. The ''netdev->link_rc'' field is useful for presenting a user-visible error, and it keeps its value until a different error or a success. For programmatic status information, ''dev->state'' contains the most recent 802.11 status code and several bits indicating how network association is progressing. * The ''netdev->priv'' now points to the wrapped ''net80211_device'' structure. Driver-private data must be accessed using ''dev->priv'' (the ''priv'' field in the ''net80211_device'' itself). There are also //many// more fields in ''net80211_device'' than in ''net_device'', owing to the significant added complexity of wireless. Those that a driver writer needs to know about are: * ''dev->hw'' contains a pointer to the ''net80211_hw_info'' structure passed to ''net80211_register()''. * ''dev->channels'' is a list of 802.11 channels that might be used, each identified by a structure indicating center frequency, standard channel number, transmission power, etc. ''dev->nr_channels'' contains a count of channels, and ''dev->channel'' is the index of the channel currently in use. * In the same vein, ''dev->rates'' is an array of transmission rates counted by ''dev->nr_rates'' and indexed by ''dev->rate''. For simplicity, rates are not represented using a structure, but instead simply as an integral multiple of 100,000 bits per second. * ''dev->rtscts_rate'' is the index of the rate that should be used for RTS and CTS (request-to-send and clear-to-send) frames, if their use is necessary. * ''dev->bssid'' is the MAC address of the Access Point with which the card is associated; generally this is used in setting the RX filter. * ''dev->phy_flags'' is a bitmask of physical-layer flags (whether or not to use short preamble, short slot time, or CTS protection) that the driver must communicate appropriately to the card. To manage these additional wireless-specific issues, a new API function is added to ''net80211_device_operations'': ''config()'', passed a pointer to the device and a bitmask of what (from the set of channel, TX rate, association, and PHY parameters) has changed. In all cases the changes are not passed directly; the changed parameters are updated in the ''net80211_device'' structure before the call, and the driver is expected to update the card's understanding to match them. It is possible for a bit to be set in the ''changed'' argument without the underlying parameter having actually changed, but this is expected to be rare. The operations structure is otherwise perfectly analogous to ''net_device_operations'', with the obvious substitution of ''net80211_device'' structures for ''net_device'' structures in arguments. The creation and registration process for an 802.11 device is fairly similar to that for a regular network device: ''net80211_alloc()'' passing the size of a driver-private data area to allocate, followed by field-filling and ''net80211_register()''. Differences lie in the fact that ''net80211_register()'' expects both the device operations structure and an 802.11-specific hardware information structure. There is no analogue to ''netdev_init()''; initialisation is performed by either the allocation or the registration function as appropriate. Before freeing an 802.11 device, it must be unregistered using ''net80211_unregister()''. 802.11 devices are not reference-counted, so they should be freed using ''net80211_free()'' rather than a combination of nullify and put. As in the wired case, drivers must supply notification to the network layer of received and transmitted packets: void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob, int signal, u16 rate ); Called with a received packet in ''iob'', which the network layer takes ownership of. In ''signal'', pass a hardware-level signal strength indication; it will be interpreted according to the information specified in the hardware info structure passed to ''net80211_register''. In ''rate'', pass the bitrate used to receive the packet, as usual in units of 100 kbps. This information is used to adaptively set the best TX rate. void net80211_rx_err ( struct net80211_device *dev, struct io_buffer *iob, int rc ); Called when it is known that reception of a packet failed (e.g. due to a reported CRC error). If ''iob'' is non-NULL, it is freed, and an error of type ''rc'' is recorded in the ''net_device'' rx statistics. void net80211_tx_complete ( struct net80211_device *dev, struct io_buffer *iob, int retries, int rc ); Called when a transmission completes, whether successfully or no. (There is no separate net80211_tx_complete_err() function for error conditions.) ''rc'' must be set to 0 if the transmission was ultimately successful, or an error code if it failed. If the transmission had to be retried before it succeeded, this should be reflected in a nonzero ''retries'' value, measuring the number of failed transmissions before the successful one.