Table of Contents

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 config_net80211.c instead of
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:

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.

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.