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, anddev→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
andnetdev→ll_protocol
should continue to use the wrappingnet_device
. - The
netdev→state
field retains its validity, but for wireless it does not provide much status detail. Thenetdev→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 wrappednet80211_device
structure. Driver-private data must be accessed usingdev→priv
(thepriv
field in thenet80211_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 thenet80211_hw_info
structure passed tonet80211_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, anddev→channel
is the index of the channel currently in use.- In the same vein,
dev→rates
is an array of transmission rates counted bydev→nr_rates
and indexed bydev→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.