[802.11] Comments and cleanup for 802.11 code
authorJoshua Oreman <oremanj@xenon.get-linux.org>
Fri, 12 Jun 2009 02:49:04 +0000 (19:49 -0700)
committerJoshua Oreman <oremanj@xenon.get-linux.org>
Fri, 12 Jun 2009 02:49:04 +0000 (19:49 -0700)
Completely documented the structures and functions for the core 802.11
code. Renamed "ERP parameters" to PHY parameters, because they apply
whether we're on an ERP network or not. Cleaned up fragment processing
code - it had been quite ugly because I assumed we had to handle
fragments received out of order, and we don't. Cleaned up output of
the association task, so it'll now look something like

  Waiting for link-up on net0... [802.11 associating... ok, <network>] ok.

src/include/gpxe/net80211.h
src/net/net80211.c

index 74c3d0d..8144463 100644 (file)
@@ -36,182 +36,581 @@ FILE_LICENCE ( GPL2_OR_LATER );
    net_device, with a link in its `priv' field to a net80211_device
    which we use to handle 802.11-specific details. */
 
-/* Constants for the (*config)() callback */
-#define NET80211_CFG_CHANNEL     (1 << 0) /* channel # or txpower */
-#define NET80211_CFG_RATE        (1 << 1)
-#define NET80211_CFG_ASSOC       (1 << 2)
-#define NET80211_CFG_ERP_PARAMS  (1 << 3)
 
-struct net80211_device;
+/** @defgroup net80211_band RF bands on which an 802.11 device can transmit */
+/** @{ */
 
-/** Operations that must be implemented by an 802.11 driver. */
-struct net80211_device_operations {
-       /* These have the same semantics as they do in net_device. */
+/** The 2.4 GHz ISM band, unlicensed in most countries */
+#define NET80211_BAND_2GHZ     (1 << 0)
+/** The band from 4.9 GHz to 5.7 GHz, which tends to be more restricted */
+#define NET80211_BAND_5GHZ     (1 << 1)
 
-       int ( * open )( struct net80211_device *dev );
-       void ( * close )( struct net80211_device *dev );
-       int ( * transmit )( struct net80211_device *dev, struct io_buffer *iobuf );
-       void ( * poll )( struct net80211_device *dev );
-       void ( * irq )( struct net80211_device *dev, int enable );
+/** @} */
 
-       /* And these are specific to 802.11. */
 
-       /** Update hardware state to match the state in
-           net80211_device with respect to `changed', a bitwise OR of
-           NET80211_CFG_* indicating what's changed. Only called when
-           the device is open. */
-       int ( * config )( struct net80211_device *dev, int changed );
-};
+/** @defgroup net80211_mode 802.11 operation modes supported by hardware */
+/** @{ */
 
-#define NET80211_BAND_2GHZ  (1 << 0)   /* the 2.4 GHz ISM band */
-#define NET80211_BAND_5GHZ  (1 << 1)   /* the band from 4.9 to 5.7 GHz */
+/** 802.11a: 54 Mbps operation using OFDM signaling on the 5GHz band */
+#define NET80211_MODE_A                (1 << 0)
 
-/** An 802.11 RF channel. */
-struct net80211_channel
-{
-       u8 band;                /* one of NET80211_BAND_* */
+/** 802.11b: 1-11 Mbps operation using DSSS/CCK signaling on the 2.4GHz band */
+#define NET80211_MODE_B                (1 << 1)
+
+/** 802.11g: 54 Mbps operation using ERP/OFDM signaling on the 2.4GHz band */
+#define NET80211_MODE_G                (1 << 2)
+
+/** 802.11n: High-rate operation using MIMO technology on 2.4GHz or 5GHz */
+#define NET80211_MODE_N                (1 << 3)
+
+/** @} */
+
+
+/** @defgroup net80211_cfg Constants for the net80211 config callback */
+/** @{ */
+
+/** Channel choice (@c dev->channel) or regulatory parameters have changed */
+#define NET80211_CFG_CHANNEL   (1 << 0)
+
+/** Requested transmission rate (@c dev->rate) has changed */
+#define NET80211_CFG_RATE      (1 << 1)
+
+/** Association has been established with a new BSS (@c dev->bssid) */
+#define NET80211_CFG_ASSOC     (1 << 2)
+
+/** Low-level link parameters (short preamble, protection, etc) have changed */
+#define NET80211_CFG_PHY_PARAMS        (1 << 3)
+
+/** @} */
+
+
+/** An 802.11 security handshaking protocol */
+enum net80211_security_proto {
+       /** No security handshaking
+        *
+        * This might be used with an open network, or with WEP, as
+        * WEP does not have a cryptographic handshaking phase.
+        */
+       NET80211_SECPROT_NONE = 0,
+
+       /** Pre-shared key handshaking
+        *
+        * This implements the "WPA Personal" handshake. 802.1X
+        * authentication is not performed -- the user supplies a
+        * pre-shared key directly -- but there is a 4-way handshake
+        * between client and AP to verify that both have the same key
+        * without revealing the contents of that key.
+        */
+       NET80211_SECPROT_PSK = 1,
 
-       u8 channel_nr;          /* a traditional channel number,
-                                  e.g. 1-11 for the 2.4GHz band */
+       /** Full EAP 802.1X handshaking
+        *
+        * This implements the "WPA Enterprise" handshake, connecting
+        * to an 802.1X authentication server to provide credentials
+        * and receive a pairwise master key (PMK), which is then used
+        * in the same 4-way handshake as the PSK method.
+        */
+       NET80211_SECPROT_EAP = 2,
+};
 
-       u16 center_freq;        /* MHz of channel center */
-       u8 maxpower;            /* maximum allowable EIRP in dBm */
 
-       /* There are more regulatory issues than this - bandwidth and
-          antenna gain are the main ones - but we can't do anything
-          about them based on the information in beacon frames, so we
-          assume 20 MHz bandwidth and a sane antenna setup by the user. */
+/** An 802.11 data encryption algorithm */
+enum net80211_crypto_alg {
+       /** No security, an "Open" network */
+       NET80211_CRYPT_NONE = 0,
+
+       /** Network protected with WEP (awful RC4-based system)
+        *
+        * WEP uses a naive application of RC4, with a monotonically
+        * increasing initialization vector that is prepended to the
+        * key to initialize the RC4 keystream. It is highly insecure
+        * and can be completely cracked or subverted using automated,
+        * robust, freely available tools (aircrack-ng) in minutes.
+        *
+        * 40-bit and 104-bit WEP are differentiated only by the size
+        * of the key. They may be advertised as 64-bit and 128-bit,
+        * counting the non-random IV as part of the key bits.
+        */
+       NET80211_CRYPT_WEP = 1,
+
+       /** Network protected with TKIP (better RC4-based system)
+        *
+        * Usually known by its trade name of WPA (Wi-Fi Protected
+        * Access), TKIP implements a message integrity code (MIC)
+        * called Michael, a timestamp counter for replay prevention,
+        * and a key mixing function that together remove almost all
+        * the security problems with WEP. Countermeasures are
+        * implemented to prevent high data-rate attacks.
+        *
+        * There exists one known attack on TKIP, that allows one to
+        * send between 7 and 15 arbitrary short data packets on a
+        * QoS-enabled network given about an hour of data
+        * gathering. Since gPXE does not support QoS for 802.11
+        * networks, this is not a threat to us. The only other method
+        * is a brute-force passphrase attack.
+        */
+       NET80211_CRYPT_TKIP = 2,
+
+       /** Network protected with CCMP (AES-based system)
+        *
+        * Often called WPA2 in commerce, or RSNA (Robust Security
+        * Network Architecture) in the 802.11 standard, CCMP is
+        * highly secure and does not have any known attack vectors.
+        * Since it is based on a block cipher, the statistical
+        * correlation and "chopchop" attacks used with great success
+        * against WEP and minor success against TKIP fail.
+        */
+       NET80211_CRYPT_CCMP = 3,
 };
 
-#define NET80211_MODE_A  (1 << 0)
-#define NET80211_MODE_B  (1 << 1)
-#define NET80211_MODE_G  (1 << 2)
-#define NET80211_MODE_N  (1 << 3)
 
-#define NET80211_MAX_RATES     16
-#define NET80211_MAX_CHANNELS  32
+/** @defgroup net80211_state Bits for the 802.11 association state field */
+/** @{ */
+
+/** An error code indicating the failure mode, or 0 if successful */
+#define NET80211_STATUS_MASK    0x7F
+
+/** Whether the error code provided is a "reason" code, not a "status" code */
+#define NET80211_IS_REASON     0x80
+
+/** Whether we have successfully authenticated with the network
+ *
+ * This usually has nothing to do with actual security; it is a
+ * holdover from older 802.11 implementation ideas.
+ */
+#define NET80211_AUTHENTICATED  (1 << 8)
+
+/** Whether we have successfully associated with the network */
+#define NET80211_ASSOCIATED     (1 << 9)
+
+/** Whether we have completed security handshaking with the network
+ *
+ * Once this is set, we can send data packets.
+ */
+#define NET80211_CRYPTO_SYNCED  (1 << 10)
+
+/** Whether the auto-association task is running */
+#define NET80211_WORKING        (1 << 11)
+
+/** Whether the auto-association task is waiting for a reply from the AP */
+#define NET80211_WAITING        (1 << 12)
+
+/** @} */
+
+
+/** @defgroup net80211_phy 802.11 physical layer flags */
+/** @{ */
+
+/** Whether to use RTS/CTS or CTS-to-self protection for transmissions
+ *
+ * Since the RTS or CTS is transmitted using 802.11b signaling, and
+ * includes a field indicating the amount of time that will be used by
+ * transmission of the following packet, this serves as an effective
+ * protection mechanism to avoid 802.11b clients interfering with
+ * 802.11g clients on mixed networks.
+ */
+#define NET80211_PHY_USE_PROTECTION      (1 << 1)
+
+/** Whether to use 802.11b short preamble operation
+ *
+ * Short-preamble operation can moderately increase throughput on
+ * 802.11b networks operating between 2Mbps and 11Mbps. It is
+ * irrelevant for 802.11g data rates, since they use a different
+ * modulation scheme.
+ */
+#define NET80211_PHY_USE_SHORT_PREAMBLE  (1 << 2)
+
+/** Whether to use 802.11g short slot operation
+ *
+ * This affects a low-level timing parameter of 802.11g transmissions.
+ */
+#define NET80211_PHY_USE_SHORT_SLOT      (1 << 3)
+
+/** @} */
+
+
+/** @defgroup net80211_ratedefs 802.11 layer rate flags */
+/** @{ */
+
+/** Get rate (in 100 kbps) from a ratecode */
+#define NET80211_RATE_VALUE(r) ((r) & 0x3fff)
+
+/** Rate flag 1 (reserved) */
+#define NET80211_RATE_FLAG1    0x8000
+
+/** Rate flag 2 (reserved) */
+#define NET80211_RATE_FLAG2    0x4000
+
+/** @} */
+
+
+/** The maximum number of TX rates we allow to be configured simultaneously */
+#define NET80211_MAX_RATES     16
+
+/** The maximum number of channels we allow to be configured simultaneously */
+#define NET80211_MAX_CHANNELS  32
+
+/** Seconds we'll wait to get all fragments of a packet */
+#define NET80211_FRAG_TIMEOUT  2
 
-/** Information on the capabilities of the hardware supported by a
-    driver; must be filled in by said driver. */
+/** The number of fragments we can receive at once
+ *
+ * The 802.11 standard requires that this be at least 3.
+ */
+#define NET80211_NR_CONCURRENT_FRAGS 3
+
+/** Maximum TX power to allow (dBm), if we don't get a regulatory hint */
+#define NET80211_REG_TXPOWER   20
+
+
+struct net80211_device;
+
+/** Operations that must be implemented by an 802.11 driver */
+struct net80211_device_operations {
+       /** Open 802.11 device
+        *
+        * @v dev       802.11 device
+        * @ret rc      Return status code
+        *
+        * This method should allocate RX I/O buffers and enable the
+        * hardware to start transmitting and receiving packets on the
+        * channels its net80211_register() call indicated it could
+        * handle. It does not need to tune the antenna to receive
+        * packets on any particular channel.
+        */
+       int ( * open ) ( struct net80211_device *dev );
+
+       /** Close 802.11 network device
+        *
+        * @v dev       802.11 device
+        *
+        * This method should stop the flow of packets, and call
+        * net80211_tx_complete() for any packets remaining in the
+        * device's TX queue.
+        */
+       void ( * close ) ( struct net80211_device *dev );
+
+       /** Transmit packet on 802.11 network device
+        *
+        * @v dev       802.11 device
+        * @v iobuf     I/O buffer
+        * @ret rc      Return status code
+        *
+        * This method should cause the hardware to initiate
+        * transmission of the I/O buffer, using the channel and rate
+        * most recently indicated by an appropriate call to the
+        * @c config callback. The 802.11 layer guarantees that said
+        * channel and rate will be the same as those currently
+        * reflected in the fields of @a dev.
+        *
+        * If this method returns success, the I/O buffer remains
+        * owned by the network layer's TX queue, and the driver must
+        * eventually call net80211_tx_complete() to free the buffer
+        * whether transmission succeeded or not. If this method
+        * returns failure, it will be interpreted as "failure to
+        * enqueue buffer" and the I/O buffer will be immediately
+        * released.
+        *
+        * This method is guaranteed to be called only when the device
+        * is open.
+        */
+       int ( * transmit ) ( struct net80211_device *dev,
+                            struct io_buffer *iobuf );
+
+       /** Poll for completed and received packets
+        *
+        * @v dev       802.11 device
+        *
+        * This method should cause the hardware to check for
+        * completed transmissions and received packets. Any received
+        * packets should be delivered via net80211_rx(), and
+        * completed transmissions should be indicated using
+        * net80211_tx_complete().
+        *
+        * This method is guaranteed to be called only when the device
+        * is open.
+        */
+       void ( * poll ) ( struct net80211_device *dev );
+
+       /** Enable or disable interrupts
+        *
+        * @v dev       802.11 device
+        * @v enable    If TRUE, interrupts should be enabled
+        */
+       void ( * irq ) ( struct net80211_device *dev, int enable );
+
+       /** Update hardware state to match 802.11 layer state
+        *
+        * @v dev       802.11 device
+        * @v changed   Set of flags indicating what may have changed
+        * @ret rc      Return status code
+        *
+        * This method should cause the hardware state to be
+        * reinitialized from the state indicated in fields of
+        * net80211_device, in the areas indicated by bits set in
+        * @a changed. If the hardware is unable to do so, this method
+        * may return an appropriate error indication.
+        *
+        * This method is guaranteed to be called only when the device
+        * is open.
+        */
+       int ( * config ) ( struct net80211_device *dev, int changed );
+};
+
+/** An 802.11 RF channel. */
+struct net80211_channel
+{
+       /** The band with which this channel is associated */
+       u8 band;
+
+       /** A channel number interpreted according to the band
+        *
+        * The 2.4GHz band uses channel numbers from 1-13 at 5MHz
+        * intervals such that channel 1 is 2407 MHz; channel 14,
+        * legal for use only in Japan, is defined separately as 2484
+        * MHz. Adjacent channels will overlap, since 802.11
+        * transmissions use a 20 MHz (4-channel) bandwidth. Most
+        * commonly, channels 1, 6, and 11 are used.
+        *
+        * The 5GHz band uses channel numbers derived directly from
+        * the frequency; channel 0 is 5000 MHz, and channels are
+        * always spaced 5 MHz apart. Channel numbers over 180 are
+        * relative to 4GHz instead of 5GHz, but these are rarely
+        * seen. Most channels are not legal for use.
+        */
+       u8 channel_nr;
+
+       /** The center frequency for this channel
+        *
+        * Currently a bandwidth of 20 MHz is assumed.
+        */
+       u16 center_freq;
+
+       /** Maximum allowable transmit power, in dBm
+        *
+        * This should be interpreted as EIRP, the power supplied to
+        * an ideal isotropic antenna in order to achieve the same
+        * average signal intensity as the real hardware at a
+        * particular distance.
+        *
+        * Currently no provision is made for directional antennas.
+        */
+       u8 maxpower;
+};
+
+/** Information on the capabilities of an 802.11 hardware device
+ *
+ * In its probe callback, an 802.11 driver must read hardware
+ * registers to determine the appropriate contents of this structure,
+ * fill it, and pass it to net80211_register() so that the 802.11
+ * layer knows how to treat the hardware and what to advertise as
+ * supported to access points.
+ */
 struct net80211_hw_info
 {
-       /** Hardware MAC address. */
+       /** Default hardware MAC address.
+        *
+        * The user may change this by setting the @c netX/mac setting
+        * before the driver's open function is called; in that case
+        * the driver must set the hardware MAC address to the address
+        * contained in the wrapping net_device's ll_addr field, or if
+        * that is impossible, set that ll_addr field back to the
+        * unchangeable hardware MAC address.
+        */
        u8 hwaddr[ETH_ALEN];
 
-       /** A bitwise OR of the 802.11x modes supported by this device. */
-       int modes;              /* e.g. NET80211_MODE_B | NET80211_MODE_G */
+       /** A bitwise OR of the 802.11x modes supported by this device */
+       int modes;
 
-       /** A bitwise OR of the bands on which this device can communicate. */
-       int bands;              /* e.g. NET80211_BAND_2GHZ */
+       /** A bitwise OR of the bands on which this device can communicate */
+       int bands;
 
+       /** A set of flags indicating peculiarities of this device. */
        enum {
                /** Received frames include a frame check sequence. */
                NET80211_HW_RX_HAS_FCS = (1 << 1),
 
-               /** Hardware doesn't support short preambles on the
-                   2.4GHz band (all 802.11g devices do). */
+               /** Hardware doesn't support 2.4GHz short preambles
+                *
+                * This is only relevant for 802.11b operation above
+                * 2Mbps. All 802.11g devices support short preambles.
+                */
                NET80211_HW_NO_SHORT_PREAMBLE = (1 << 2),
 
-               /** Hardware doesn't support short slot operation on
-                   the 2.4GHz band (only relevant for 802.11g). */
+               /** Hardware doesn't support 802.11g short slot operation */
                NET80211_HW_NO_SHORT_SLOT = (1 << 3),
        } flags;
 
-       /** Set according to the type of signal strength information
-           that can be provided. */
+       /** Signal strength information that can be provided by the device
+        *
+        * Signal strength is passed to net80211_rx(), primarily to
+        * allow determination of the closest access point for a
+        * multi-AP network. The units are provided for completeness
+        * of status displays.
+        */
        enum {
-               NET80211_SIGNAL_NONE = 0,  /**< no info at all */
-               NET80211_SIGNAL_ARBITRARY, /**< arbitrary units */
-               NET80211_SIGNAL_DB,        /**< dB relative to arbitrary base */
-               NET80211_SIGNAL_DBM,       /**< dB relative to 1mW */
+               /** No signal strength information supported */
+               NET80211_SIGNAL_NONE = 0,
+               /** Signal strength in arbitrary units */
+               NET80211_SIGNAL_ARBITRARY,
+               /** Signal strength in decibels relative to arbitrary base */
+               NET80211_SIGNAL_DB,
+               /** Signal strength in decibels relative to 1mW */
+               NET80211_SIGNAL_DBM,
        } signal_type;
        
-       /** List of transmit supported by the card, in 100kbps
-           increments. */
+       /** Maximum signal in arbitrary cases
+        *
+        * If signal_type is NET80211_SIGNAL_ARBITRARY or
+        * NET80211_SIGNAL_DB, the driver should report it on a scale
+        * from 0 to signal_max.
+        */
+       unsigned signal_max;
+
+       /** List of transmission rates supported by the card
+        *
+        * Rates should be in 100kbps increments (e.g. 11 Mbps would
+        * be represented as the number 110).
+        */
        u16 supported_rates[NET80211_MAX_RATES];
-       /** Number of supported rates. */
-       int nr_supported_rates;
 
-       /** If signal_type involves arbitrary units, it should be
-           reported on a scale from 0 to signal_max. */
-       unsigned signal_max;
+       /** Number of supported rates */
+       int nr_supported_rates;
 
-       /** Amount of time required to change channels, in
-           microseconds. */
+       /** Estimate of the time required to change channels, in microseconds
+        *
+        * If this is not known, a guess on the order of a few
+        * milliseconds (value of 1000-5000) is reasonable.
+        */
        unsigned channel_change_time;
 };
 
-/** Seconds we'll wait to get all fragments of a packet. If this
-    timer expires, we drop the fragments we've received so far. */
-#define NET80211_FRAG_TIMEOUT  2
-
-/** Keeps track of the fragments for a particular packet as we receive
-    them. We set up the frag_cache entry when we receive a fragment 0
-    with MOREFRAGS set; if all are unexpired we take over the one with
-    the most recent activity (and drop the frags it used to contain).
-    If we get a fragment after the zeroth with no cache entry for its
-    packet, we drop it. The standard says we're allowed to be cavalier
-    like this, as long as we have >= 3 cache entries. */
+/** Keeps track of received fragments for a packet
+ *
+ * We set up a fragment cache entry when we receive a packet marked as
+ * fragment 0 with the "more fragments" bit set in its frame control
+ * header. We are required by the 802.11 standard to track 3
+ * fragmented packets arriving simultaneously; if we receive more we
+ * may drop some. Upon receipt of a new fragment-0 packet, if no entry
+ * is available or expired, we take over the most @i recent entry for
+ * the new packet, since we don't want to starve old entries from ever
+ * finishing at all. If we get a fragment after the zeroth with no
+ * cache entry for its packet, we drop it.
+ */
 struct net80211_frag_cache
 {
-       /** State of this frag cache entry. */
-       u8 state;
-#define NET80211_FRAG_AVAIL    0 /* not in use */
-#define NET80211_FRAG_WAITING  1 /* waiting for the final fragment */
-#define NET80211_FRAG_FINISHING        2 /* waiting for holes to fill */
+       /** Whether this cache entry is in use */
+       u8 in_use;
 
-       /** Number of fragments to expect, if state is FINISHING. */
-       u8 nfrags;
-
-       /** Sequence number of this MSDU (packet). */
+       /** Sequence number of this MSDU (packet) */
        u16 seqnr;
 
-       /** Timestamp of when we started collecting these frags. */
+       /** Timestamp from point at which first fragment was collected */
        u32 start_ticks;
 
-       /** Buffers for each fragment (they can arrive out of order). */
+       /** Buffers for each fragment */
        struct io_buffer *iob[16];
 };
 
-/* 802.11 data security protocols: */
-#define NET80211_CRYPT_NONE   0        /* "Open" */
-#define NET80211_CRYPT_WEP40  2        /* "WEP 64-bit" (5-character key) */
-#define NET80211_CRYPT_WEP104 3 /* "WEP 128-bit" (13-character key) */
-#define NET80211_CRYPT_TKIP   4        /* "WPA Personal" */
-#define NET80211_CRYPT_CCMP   5        /* "WPA2 Personal" */
-#define NET80211_CRYPT_EAP    8        /* "WPA2 Enterprise" */
-
-/** Interface to a cryptographic algorithm. */
+/** Interface to an 802.11 cryptographic algorithm
+ *
+ * Cryptographic algorithms define a net80211_crypto structure
+ * statically, using a gPXE linker table to make it available to the
+ * 802.11 layer. When the algorithm needs to be used, the 802.11 code
+ * will allocate a copy of the static definition plus whatever space
+ * the algorithm has requested for private state, and point
+ * net80211_device::crypto at it.
+ */
 struct net80211_crypto
 {
-       /** Type, as one of the constants defined above. */
-       short type;
-
-       /** Initialize crypto algorithm using the given key. */
-       void ( * initialize )( struct net80211_device *dev, u8 *key,
+       /** The cryptographic algorithm implemented */
+       enum net80211_crypto_alg algorithm;
+
+       /** Initialize cryptographic algorithm using a given key
+        *
+        * @v crypto    802.11 cryptographic algorithm
+        * @v key       Pointer to key bytes
+        * @v keylen    Number of key bytes
+        * @ret rc      Return status code
+        *
+        * This method is passed the communication key provided by the
+        * security handshake handler, which will already be in the
+        * low-level form required.
+        */
+       int ( * initialize ) ( struct net80211_crypto *crypto, u8 *key,
                               int keylen );
 
-       /** Encrypt the given frame into a newly allocated io_buffer
-           with all frame headers intact. Returns NULL if out of
-           memory. */
-       struct io_buffer * ( * encrypt )( struct net80211_device *dev,
-                                         struct io_buffer *iob );
+       /** Encrypt a frame using the cryptographic algorithm
+        *
+        * @v crypto    802.11 cryptographic algorithm
+        * @v iob       I/O buffer
+        * @ret eiob    Newly allocated I/O buffer with encrypted packet
+        *
+        * This method is called to encrypt a single frame. It is
+        * guaranteed that initialize() will have completed
+        * successfully before this method is called.
+        *
+        * The frame passed already has an 802.11 header prepended,
+        * but the PROTECTED bit in the frame control field will not
+        * be set; this method is responsible for setting it. The
+        * returned I/O buffer should contain a complete copy of @a
+        * iob, including the 802.11 header, but with the PROTECTED
+        * bit set, the data encrypted, and whatever encryption
+        * headers/trailers are necessary added.
+        *
+        * This method should never free the passed I/O buffer.
+        *
+        * Return NULL if the packet could not be encrypted, due to
+        * memory limitations or otherwise.
+        */
+       struct io_buffer * ( * encrypt ) ( struct net80211_crypto *crypto,
+                                          struct io_buffer *iob );
        
-       /** Decrypt the given frame into a newly allocated io_buffer
-           with all frame headers intact. Returns NULL if decryption
-           fails. */
-       struct io_buffer * ( * decrypt )( struct net80211_device *dev,
-                                         struct io_buffer *iob );
-
-       /** Private data for the crypto algorithm to store key and
-           state information. */
-       void *priv;
 
-       /* More to come... */
+       /** Decrypt a frame using the cryptographic algorithm
+        *
+        * @v crypto    802.11 cryptographic algorithm
+        * @v eiob      Encrypted I/O buffer
+        * @ret iob     Newly allocated I/O buffer with decrypted packet
+        *
+        * This method is called to decrypt a single frame. It is
+        * guaranteed that initialize() will have completed
+        * successfully before this method is called.
+        *
+        * Decryption follows the reverse of the pattern used for
+        * encryption: this method must copy the 802.11 header into
+        * the returned packet, decrypt the data stream, remove any
+        * encryption header or trailer, and clear the PROTECTED bit
+        * in the frame control header.
+        *
+        * This method should never free the passed I/O buffer.
+        *
+        * Return NULL if memory was not available for decryption, if
+        * a consistency or integrity check on the decrypted frame
+        * failed, or if the decrypted frame should not be processed
+        * by the network stack for any other reason.
+        */
+       struct io_buffer * ( * decrypt ) ( struct net80211_crypto *crypto,
+                                          struct io_buffer *iob );
+
+       /** Length of private data requested to be allocated */
+       int priv_len;
+
+       /** Private data for the algorithm to store key and state info */
+       void *priv;
 };
 
-/** Structure encapsulating the complete state of an 802.11
-    device. Stored in the `priv' member of some net_device. */
+/** Structure encapsulating the complete state of an 802.11 device
+ *
+ * An 802.11 device is always wrapped by a network device, and this
+ * network device is always pointed to by the @a netdev field. In
+ * general, operations should never be performed by 802.11 code using
+ * netdev functions directly; for consistency, when such a direct call
+ * would be otherwise valid, an inline wrapper is provided in the
+ * net80211 namespace. It is generally the case that the 802.11 layer
+ * might need to do some processing or bookkeeping on top of what the
+ * netdevice code will do.
+ */
 struct net80211_device
 {
        /** The net_device that wraps us. */
@@ -220,120 +619,210 @@ struct net80211_device
        /** List of 802.11 devices. */
        struct list_head list;
 
-       /** Network device operations */
+       /** 802.11 device operations */
        struct net80211_device_operations *op;
 
        /** Driver private data */
        void *priv;
 
-       /** Information about the hardware, which the driver is
-           responsible for providing. */
+       /** Information about the hardware, provided to net80211_register() */
        struct net80211_hw_info *hw;
 
-       /** The asynchronous association process. (Successful
-           association is represented as "link up".) */
+       /** The asynchronous association process.
+        *
+        * When an 802.11 netdev is opened, or when the user changes
+        * the SSID setting on an open 802.11 device, an
+        * autoassociation task is started by net80211_autoassocate()
+        * to associate with the new best network. The association is
+        * generally asynchronous but may block for a few seconds
+        * during network probing. If it is successful, the wrapping
+        * net_device is set as "link up".
+        */
        struct process proc_assoc;
+
        /** Data structure for the association process. */
        struct net80211_wlan *associating;
 
-       /** A list of all possible channels, as computed from
-           regulatory information in the beacon frame. `channel' is
-           an index into this list. This should be NULL before
-           prepare() has been called. */
+       /** A list of all possible channels we might use */
        struct net80211_channel channels[NET80211_MAX_CHANNELS];
-       /** The number of channels in the `channels' list. */
+       /** The number of channels in the channels array */
        u8 nr_channels;
-       /** The channel currently in use, as an index into the
-           `channels' list. */
+       /** The channel currently in use, as an index into the channels array */
        u8 channel;
 
-       /** A list of all transmission rates supported by the AP we're
-           associated with, in units of 100 kbps. Max given flags is
-           16384 = 1.6 Gbps. */
+       /** A list of all possible TX rates we might use
+        *
+        * Rates are in units of 100 kbps. We reserve two bits for
+        * future flags (e.g. for 802.11n), meaning we can represent
+        * rates up to 1.6 Gbps.
+        */
        u16 rates[NET80211_MAX_RATES];
-#define NET80211_RATE_VALUE(r) ((r) & 0x3fff)
-#define NET80211_RATE_ERP      0x8000 /* an 802.11g extended rate (>11Mbps) */
-       /** The number of transmission rates in the `rates' array. */
-       u8 nr_rates;    
-       /** The rate currently in use, as an index into the `rates' array. */
+
+       /** The number of transmission rates in the rates array */
+       u8 nr_rates;
+
+       /** The rate currently in use, as an index into the rates array */
        u8 rate;
-       /** If basic_rates & (1 << i), then rates[i] is a basic rate. */
+
+       /** Bitmask of basic rates
+        *
+        * If bit N is set in this value, with the LSB bit 0, then
+        * rate N in the rates array is a "basic" rate.
+        *
+        * We don't decide which rates are "basic"; our AP does, and
+        * we respect its wishes. We need to be able to identify basic
+        * rates in order to calculate the duration of a CTS packet
+        * used for 802.11 g/b interoperability.
+        */
        u32 basic_rates;
 
-       /** State of our association to the network; bit OR of
-           NET80211_*ED and the most recent status returned by the
-           AP. */
-#define NET80211_STATUS_MASK    0xFF /**< Mask for IEEE status code */
-#define NET80211_AUTHENTICATED  (1 << 8)
-#define NET80211_ASSOCIATED     (1 << 9)
-#define NET80211_CRYPTO_SYNCED  (1 << 10)
-#define NET80211_WORKING        (1 << 11) /**< Association task is running */
-#define NET80211_WAITING        (1 << 12) /**< MAC layer awaiting AP reply */
+       /** State of our association to the network
+        *
+        * Since the association process happens asynchronously, it's
+        * necessary to have some channel of communication so the
+        * driver can say "I got an association reply and we're OK" or
+        * similar. This variable provides that link. It is a bitmask
+        * of any of NET80211_AUTHENTICATED, NET80211_ASSOCIATED,
+        * NET80211_CRYPTO_SYNCED to indicate how far along in
+        * associating we are; NET80211_WORKING if the association
+        * task is running; and NET80211_WAITING if a packet has been
+        * sent that we're waiting for a reply to. We can only be
+        * crypto-synced if we're associated, and we can only be
+        * associated if we're authenticated.
+        *
+        * If an association process fails (that is, we receive a
+        * packet with an error indication), the error code is copied
+        * into bits 6-0 of this variable and bit 7 is set to specify
+        * what type of error code it is. An AP can provide either a
+        * "status code" (0-51 are defined) explaining why it refused
+        * an association immediately, or a "reason code" (0-45 are
+        * defined) explaining why it canceled an association after it
+        * had originally OK'ed it. Status and reason codes serve
+        * similar functions, but they use separate error message
+        * tables.
+        *
+        * If the failure to associate is indicated by a status code,
+        * the NET80211_IS_REASON bit will be clear; if it is
+        * indicated by a reason code, the bit will be set. If we were
+        * successful, both zero status and zero reason mean success,
+        * so there is no ambiguity.
+        */
        short state;
-       /** Encryption method for the network we're associated to.
-           NULL for open. */
+
+       /** 802.11 cryptographic algorithm for our current network
+        *
+        * For an open network, this will be set to NULL.
+        */
        struct net80211_crypto *crypto;
-       /** Network to which we're associated, if `assoc' is set. */
+
+       /** MAC address of the access point most recently associated */
        u8 bssid[ETH_ALEN];
-       /** Name of the SSID we're connected to. */
+
+       /** SSID of the access point we are or will be associated with
+        *
+        * Although the SSID field in 802.11 packets is generally not
+        * NUL-terminated, here and in net80211_wlan we add a NUL for
+        * convenience.
+        */
        char essid[IEEE80211_MAX_SSID_LEN+1];
-       /** Association ID given to us by the AP. */
+
+       /** Association ID given to us by the AP */
        u16 aid;
-       /** ERP-related options, set dynamically based on the assoc
-           reply packet. Set even for non-ERP networks based on the
-           capabilities field. */
-       int erp_flags;
-#define NET80211_ERP_USE_PROTECTION      (1 << 1)
-#define NET80211_ERP_USE_SHORT_PREAMBLE  (1 << 2)
-#define NET80211_ERP_USE_SHORT_SLOT      (1 << 3)
-
-       /** Fragment reassembly state. IEEE standard requires >= 3. */
-#define NET80211_NR_CONCURRENT_FRAGS  3
+
+       /** Physical layer options
+        *
+        * These control the use of CTS protection, short preambles,
+        * and short-slot operation.
+        */
+       int phy_flags;
+
+       /** Fragment reassembly state */
        struct net80211_frag_cache frags[NET80211_NR_CONCURRENT_FRAGS];
 
-       /** The sequence number of the last packet we sent. */
+       /** The sequence number of the last packet we sent */
        u16 last_tx_seqnr;
 
-       /** Packet deduping state. We only are required to handle
-           immediate duplicates for each direct sender, and as we can
-           only have one direct sender (the AP), our job is easy. */
-       s16 last_rx_seq;        /* (frag << 12) | seqnr, as hdr->seq */
-
-       /** Sometimes we want to keep management packets we receive,
-           e.g. when we're scanning for networks. They will be stored
-           in this queue when keep_mgmt is true. */
+       /** Packet deduping state
+        *
+        * We are only required to handle immediate duplicates for
+        * each direct sender, and since we can only have one direct
+        * sender (the AP), we need only keep the sequence control
+        * field from the most recent packet we've received. Thus,
+        * this field stores the last sequence control field we've
+        * received for a packet from the AP.
+        */
+       s16 last_rx_seq;
+
+       /** RX management packet queue
+        *
+        * Sometimes we want to keep probe, beacon, and action packets
+        * that we receive, such as when we're scanning for networks.
+        * Ordinarily we drop them because they are sent at a large
+        * volume (ten beacons per second per AP, broadcast) and we
+        * have no need of them except when we're scanning.
+        *
+        * When keep_mgmt is true, received probe, beacon, and action
+        * management packets will be stored in this queue.
+        */
        struct list_head mgmt_queue;
 
-       /** RX packet info (signal strength) for packets in the
-           management queue. */
+       /** RX management packet info queue
+        *
+        * We need to keep track of the signal strength for management
+        * packets we're keeping, because that provides the only way
+        * to distinguish between multiple APs for the same network.
+        * Since we can't extend io_buffer to store it, this heads a
+        * linked list of "RX packet info" structures that contain
+        * that signal strength field. Its entries always parallel the
+        * entries in mgmt_queue, because the two queues are always
+        * added to or removed from in parallel.
+        */
        struct list_head mgmt_info_queue;
 
-       /** Store management packets only when TRUE. */
+       /** Whether to store management packets
+        *
+        * Received beacon, probe, and action packets will be added to
+        * mgmt_queue (and their signal strengths added to
+        * mgmt_info_queue) only when this variable is TRUE. It should
+        * be set by net80211_keep_mgmt() (which returns the old
+        * value) only when calling code is prepared to poll the
+        * management queue frequently, because packets will otherwise
+        * pile up and exhaust memory.
+        */
        int keep_mgmt;
 };
 
-/** Structure returned from the scanning functions and passed to
-    net80211_associate(). At least essid, bssid, channel, and beacon
-    must be filled in. */
+/** Structure representing a probed network.
+ *
+ * This is returned from net80211_probe() and passed to the low-level
+ * association functions. At least essid, bssid, channel, beacon, and
+ * security must be filled in if you want to build this structure
+ * manually.
+ */
 struct net80211_wlan
 {
-       /** The human-readable ESSID (network name). */
+       /** The human-readable ESSID (network name)
+        *
+        * Although the 802.11 SSID field is generally not
+        * NUL-terminated, the gPXE code adds an extra NUL (and
+        * expects one in this structure) for convenience.
+        */
        char essid[IEEE80211_MAX_SSID_LEN+1];
 
-       /** The MAC address of the "best" access point (strongest
-           signal) for this ESSID. */
+       /** MAC address of the strongest-signal access point for this ESSID */
        u8 bssid[ETH_ALEN];
 
-       /** The signal strength of communications from that access
-           point, measured in whatever units the hardware provides. */
+       /** Signal strength of beacon frame from that access point */
        int signal;
 
-       /** The channel on which that access point communicates;
-           pointer to an appropriate entry in dev->channels. */
-       struct net80211_channel *channel;
+       /** The channel on which that access point communicates
+        *
+        * This is an index into the channels array for the 802.11
+        * device that was used to run the probe.
+        */
+       int channel;
 
-       /** The complete beacon or probe-response frame from which
-           the network was identified. */
+       /** The complete beacon or probe-response frame received */
        struct io_buffer *beacon;
 
        /** One of the NET80211_CRYPT_* constants indicating the
@@ -348,138 +837,74 @@ struct net80211_wlan
        struct list_head list;
 };
 
-/* Regulatory hints when we don't get any beacons: */
-#define NET80211_REG_TXPOWER  20 /* conservative default, OK with
-                                   US/EU/JP at least */
-/* 2.4GHz channels 1-11 are safe to transmit on everywhere. */
 
+/** Indicate an error in receiving a packet
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer with received packet, or NULL
+ * @v rc       Error code
+ *
+ * This logs the error with the wrapping net_device, and frees iob if
+ * it is passed.
+ */
+static inline void net80211_rx_err ( struct net80211_device *dev,
+                                    struct io_buffer *iob, int rc )
+{
+       netdev_rx_err ( dev->netdev, iob, rc );
+}
 
-/* -- network scanning API: -- */
+/** Indicate the completed transmission of a packet
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer of transmitted packet
+ * @v rc       Error code, or 0 for success
+ *
+ * This logs an error with the wrapping net_device if one occurred,
+ * and removes and frees the I/O buffer from its TX queue.
+ */
+static inline void net80211_tx_complete ( struct net80211_device *dev,
+                                         struct io_buffer *iob, int rc )
+{
+       netdev_tx_complete_err ( dev->netdev, iob, rc );
+}
 
-/** Scans the area, returning a list of net80211_wlan structures
-    corresponding to each of the reachable wireless networks. */
-struct list_head * net80211_scan ( struct net80211_device *dev );
 
-/** Frees the list returned from a scan call. */
-void net80211_free_wlanlist ( struct list_head *netlist );
+/* Associate with the best or user-specified network: */
+void net80211_autoassociate ( struct net80211_device *dev );
 
-/** Scans for the given ESSID specifically. Can be used to associate
-    with "hidden" networks on the 2.4GHz band. */
+/* Find networks: */
 struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
                                        const char *essid, int active );
-
-/** Frees a single net80211_wlan structure. */
 void net80211_free_wlan ( struct net80211_wlan *wlan );
 
-/** Does scan/probe as appropriate given configured settings, and
-    associates. Operates as a background process; this function
-    returns immediately, before the association has been performed. */
-void net80211_autoassociate ( struct net80211_device *dev );
-
-/* -- general-use net80211 API: -- */
-
-/** If the passed net_device wraps a net80211_device, returns the
-    net80211_device. If not, returns NULL. */
+/* Get the 802.11 device from a wrapping net_device: */
 struct net80211_device * net80211_get ( struct net_device *netdev );
 
-/* - association steps: -   [normally performed by autoassociate] */
-
-/** Sets up the device's regulatory parameters to a sane default for
-    passive scanning or active 2.4GHz scanning. */
+/* Association steps: */
 int net80211_prepare_default ( struct net80211_device *dev, int band,
                               int active );
-
-/** Sets up the device's regulatory parameters as the provided beacon
-    or probe response frame tells us to. Must be called before
-    associating. */
 int net80211_prepare ( struct net80211_device *dev,
                       struct net80211_wlan *wlan );
-
-/** Performs an authentication sequence using the specified
-    method. Returns immediately, sets state variables. */
 int net80211_send_auth ( struct net80211_device *dev,
                         struct net80211_wlan *wlan, int method );
-
-/** Performs the actual association with the network described by
-    `wlan'. Requires authentication to have previously succeeded. */
 int net80211_send_assoc ( struct net80211_device *dev,
                          struct net80211_wlan *wlan );
 
-/* - management frame handling: - */
-
-/** Sets the state of the device keeping management packets. Only call
-    this with enable == 1 if you're planning to actually look at all
-    the packets using net80211_mgmt_dequeue(); otherwise they'll pile
-    up and waste memory. Keeping management packets is disabled by
-    default. Returns the setting of keeping management packets that
-    preceded the function call. */
+/* Management frame handling: */
 int net80211_keep_mgmt ( struct net80211_device *dev, int enable );
-
-/** Returns the next packet from the device's received management
-    packet queue, or NULL if the queue is empty. You must have called
-    net80211_set_management(dev, 1) previously. Ownership of the
-    returned io_buffer passes to the calling function. If `signal' is
-    non-NULL it is filled with the signal strength of the received
-    frame in device units. */
 struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev,
                                           int *signal );
+int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc,
+                      u8 bssid[ETH_ALEN], struct io_buffer *iob );
 
-/** Sends an 802.11 management packet. The passed io_buffer must have
-    at least IEEE80211_TYP_FRAME_HEADER_LEN = 24 bytes of headroom for
-    the 802.11 frame header to be prepended. The management frame is
-    sent to the AP of the network identified by `bssid'; it is not
-    possible to use this function to send management frames that are
-    not directed at APs. The frame is encrypted if fc & PROTECTED. */
-int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 bssid[6],
-                      struct io_buffer *iob );
-
-/* -- Driver API: -- */
-
-/* The 802.11 layer handles the things drivers usually do to the
-   wrapping net_device. Drivers should use the below functions and
-   *not* their net_device analogues on dev->netdev. */
-
-/** Allocates a new 802.11 device with priv_size bytes of
-    driver-private data behind its `priv' pointer. This also allocates
-    the gPXE net_device and sets up its operations. */
+/* Driver API: */
 struct net80211_device *net80211_alloc ( size_t priv_size );
-
-/** Set up an 802.11 device to use the provided operations and
-    hardware information, and register it with the network stack. The
-    hw info will be copied into a dynamically allocated structure that
-    the 802.11 layer takes ownership of; the ops are simply pointed
-    at, and should be defined statically. */
 int net80211_register ( struct net80211_device *dev,
                        struct net80211_device_operations *ops,
                        struct net80211_hw_info *hw );
-
-/** Indicate receipt of a raw packet from the network interface. Takes
-    ownership of the iob. `signal' is signal strength in device
-    units. */
 void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
                   int signal );
-
-/** Indicate an error in receiving a packet. Pass iob if you want it
-    freed. */
-static inline void net80211_rx_err ( struct net80211_device *dev,
-                                    struct io_buffer *iob, int rc )
-{
-       netdev_rx_err ( dev->netdev, iob, rc );
-}
-
-/** Indicate the completed transmission of a packet passed to
-    (*transmit)(), successfully if rc == 0 and unsuccessfully if
-    not. In either case this function takes ownership of the iob. */
-static inline void net80211_tx_complete ( struct net80211_device *dev,
-                                         struct io_buffer *iob, int rc )
-{
-       netdev_tx_complete_err ( dev->netdev, iob, rc );
-}
-
-/** Removes the registration of an 802.11 device. */
 void net80211_unregister ( struct net80211_device *dev );
-
-/** Frees all memory allocated by the 802.11 layer for a device. */
 void net80211_free ( struct net80211_device *dev );
 
 #endif
index e9a92f3..3f7bb6e 100644 (file)
@@ -64,35 +64,86 @@ struct setting net80211_active_setting __setting = {
 /** Set of device operations that does nothing */
 static struct net80211_device_operations net80211_null_ops;
 
-/** Single-step the association process. */
-void net80211_step_associate ( struct process *proc );
-
-/** Structure for keeping a queue of saved management packet signal
   strengths in parallel to the saved iobs. */
+/** Information associated with a received management packet
+ *
+ * This is used to keep beacon signal strengths in a parallel queue to
+ * the beacons themselves.
+ */
 struct net80211_rx_info {
        int signal;
        struct list_head list;
 };
 
+static u16 net80211_duration ( struct net80211_device *dev, int bytes );
+static int net80211_ll_push ( struct net_device *netdev,
+                             struct io_buffer *iobuf, const void *ll_dest,
+                             const void *ll_source, uint16_t net_proto );
+static int net80211_ll_pull ( struct net_device *netdev,
+                             struct io_buffer *iobuf, const void **ll_dest,
+                             const void **ll_source, uint16_t * net_proto );
+static int net80211_ll_mc_hash ( unsigned int af, const void *net_addr,
+                                void *ll_addr );
+
+static void net80211_add_channels ( struct net80211_device *dev, int start,
+                                   int len, int txpower );
+static int net80211_process_capab ( struct net80211_device *dev,
+                                   u16 capab );
+static int net80211_process_ie ( struct net80211_device *dev,
+                                struct ieee80211_ie *ie, int len );
+static struct ieee80211_ie *
+net80211_marshal_request_info ( struct net80211_device *dev,
+                               struct ieee80211_ie *ie );
+
+static void net80211_step_associate ( struct process *proc );
+static void net80211_set_rate_intelligently ( struct net80211_device *dev );
+static void net80211_handle_auth ( struct net80211_device *dev,
+                                  struct io_buffer *iob );
+static void net80211_handle_assoc_reply ( struct net80211_device *dev,
+                                         struct io_buffer *iob );
+static void net80211_handle_mgmt ( struct net80211_device *dev,
+                                  struct io_buffer *iob, int signal );
+
+static void net80211_free_frags ( struct net80211_device *dev, int fcid );
+static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev,
+                                               int fcid, int nfrags, int size );
+static void net80211_rx_frag ( struct net80211_device *dev,
+                              struct io_buffer *iob, int signal );
+
 /* ---------- net_device wrapper ---------- */
 
+/**
+ * Open 802.11 device and start association
+ *
+ * @v netdev   Wrapping network device
+ * @ret rc     Return status code
+ *
+ * This sets up a default conservative set of channels for probing,
+ * and starts the auto-association task.
+ */
 int net80211_netdev_open ( struct net_device *netdev )
 {
        struct net80211_device *dev = netdev->priv;
        int rc = 0;
 
-       if ( dev->op->open )
-               rc = dev->op->open ( dev );
-
        /* In case someone tries to transmit before we set link-up, we
           need to at least be in a consistent enough state not to
           crash. */
        net80211_prepare_default ( dev, dev->hw->bands, 0 );
 
+       if ( dev->op->open )
+               rc = dev->op->open ( dev );
+
        net80211_autoassociate ( dev );
        return 0;
 }
 
+/**
+ * Close 802.11 device
+ *
+ * @v netdev   Wrapping network device.
+ *
+ * If the association task is running, this will stop it.
+ */
 void net80211_netdev_close ( struct net_device *netdev )
 {
        struct net80211_device *dev = netdev->priv;
@@ -104,6 +155,16 @@ void net80211_netdev_close ( struct net_device *netdev )
                dev->op->close ( dev );
 }
 
+/**
+ * Transmit packet on 802.11 device
+ *
+ * @v netdev   Wrapping network device
+ * @v iobuf    I/O buffer
+ * @ret rc     Return status code
+ *
+ * If encryption is enabled for the currently associated network, the
+ * packet will be encrypted prior to transmission.
+ */
 int net80211_netdev_transmit ( struct net_device *netdev,
                               struct io_buffer *iobuf )
 {
@@ -111,7 +172,8 @@ int net80211_netdev_transmit ( struct net_device *netdev,
        int rc = -ENOSYS;
 
        if ( dev->crypto ) {
-               struct io_buffer *niob = dev->crypto->encrypt ( dev, iobuf );
+               struct io_buffer *niob = dev->crypto->encrypt ( dev->crypto,
+                                                               iobuf );
                if ( ! niob )
                        return -ENOMEM; /* only reason encryption could fail */
 
@@ -125,6 +187,11 @@ int net80211_netdev_transmit ( struct net_device *netdev,
        return rc;
 }
 
+/**
+ * Poll 802.11 device for received packets and completed transmissions
+ *
+ * @v netdev   Wrapping network device
+ */
 void net80211_netdev_poll ( struct net_device *netdev )
 {
        struct net80211_device *dev = netdev->priv;
@@ -133,6 +200,12 @@ void net80211_netdev_poll ( struct net_device *netdev )
                dev->op->poll ( dev );
 }
 
+/**
+ * Enable or disable interrupts for 802.11 device
+ *
+ * @v netdev   Wrapping network device
+ * @v enable   Whether to enable interrupts
+ */
 void net80211_netdev_irq ( struct net_device *netdev, int enable )
 {
        struct net80211_device *dev = netdev->priv;
@@ -141,6 +214,7 @@ void net80211_netdev_irq ( struct net_device *netdev, int enable )
                dev->op->irq ( dev, enable );
 }
 
+/** Network device operations for a wrapped 802.11 device */
 struct net_device_operations net80211_netdev_ops = {
        .open = net80211_netdev_open,
        .close = net80211_netdev_close,
@@ -151,35 +225,99 @@ struct net_device_operations net80211_netdev_ops = {
 
 /* ---------- 802.11 link-layer protocol ---------- */
 
+/** 802.11 broadcast MAC address */
 static u8 net80211_ll_broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
 
-/* Returns the time taken to transmit a packet of length `bytes' using
-   the current PHY parameters of `dev', measured in microseconds
-   starting immediately after reception of the previous packet is
-   complete. */
+/**
+ * Determine whether a transmission rate uses ERP/OFDM
+ *
+ * @v rate     Rate in 100 kbps units
+ * @ret is_erp TRUE if the rate is an ERP/OFDM rate
+ *
+ * 802.11b supports rates of 1.0, 2.0, 5.5, and 11.0 Mbps; any other
+ * rate than these on the 2.4GHz spectrum is an ERP (802.11g) rate.
+ */
+static inline int net80211_rate_is_erp ( u16 rate ) 
+{
+       if ( rate == 10 || rate == 20 || rate == 55 || rate == 110 )
+               return 1;
+       return 0;
+}
+
+
+/**
+ * Calculate one frame's contribution to 802.11 duration field
+ *
+ * @v dev      802.11 device
+ * @v bytes    Amount of data to calculate duration for
+ * @ret dur    Duration field in microseconds
+ *
+ * To avoid multiple stations attempting to transmit at once, 802.11
+ * provides that every packet shall include a duration field
+ * specifying a length of time for which the wireless medium will be
+ * reserved after it is transmitted. The duration is measured in
+ * microseconds and is calculated with respect to the current
+ * physical-layer parameters of the 802.11 device.
+ *
+ * For an unfragmented data or management frame, or the last fragment
+ * of a fragmented frame, the duration captures only the 10 data bytes
+ * of one ACK; call once with bytes = 10.
+ *
+ * For a fragment of a data or management rame that will be followed
+ * by more fragments, the duration captures an ACK, the following
+ * fragment, and its ACK; add the results of three calls, two with
+ * bytes = 10 and one with bytes set to the next fragment's size.
+ *
+ * For an RTS control frame, the duration captures the responding CTS,
+ * the frame being sent, and its ACK; add the results of three calls,
+ * two with bytes = 10 and one with bytes set to the next frame's size
+ * (assuming unfragmented).
+ *
+ * For a CTS-to-self control frame, the duration captures the frame
+ * being protected and its ACK; add the results of two calls, one with
+ * bytes = 10 and one with bytes set to the next frame's size.
+ *
+ * No other frame types are currently supported by gPXE.
+ */
 static u16 net80211_duration ( struct net80211_device *dev, int bytes )
 {
        struct net80211_channel *chan = &dev->channels[dev->channel];
-       u16 ratecode = dev->rates[dev->rate];
-       u32 kbps = NET80211_RATE_VALUE ( ratecode ) * 100;
+       u16 rate = NET80211_RATE_VALUE ( dev->rates[dev->rate] );
+       u32 kbps = rate * 100;
 
-       if ( chan->band == NET80211_BAND_5GHZ || ratecode & NET80211_RATE_ERP ) {
+       if ( chan->band == NET80211_BAND_5GHZ || net80211_rate_is_erp ( rate ) ) {
                /* OFDM encoding (802.11a/g) */
                int bits_per_symbol = ( kbps * 4 ) / 1000;      /* 4us/symbol */
                int bits = 22 + ( bytes << 3 ); /* 22-bit PLCP */
                int symbols = ( bits + bits_per_symbol - 1 ) / bits_per_symbol;
+
                return 16 + 20 + ( symbols * 4 ); /* 16us SIFS, 20us preamble */
        } else {
                /* CCK encoding (802.11b) */
                int phy_time = 144 + 48;        /* preamble + PLCP */
-               if ( dev->erp_flags & NET80211_ERP_USE_SHORT_PREAMBLE )
-                       phy_time >>= 1;
                int bits = bytes << 3;
-               int data_time = ( bits * 1000 ) / kbps;
+               int data_time = ( bits * 1000 + kbps - 1 ) / kbps;
+
+               if ( dev->phy_flags & NET80211_PHY_USE_SHORT_PREAMBLE )
+                       phy_time >>= 1;
+
                return 10 + phy_time + data_time; /* 10us SIFS */
        }
 }
 
+/**
+ * Add 802.11 link-layer header
+ *
+ * @v netdev           Wrapping network device
+ * @v iobuf            I/O buffer
+ * @v ll_dest          Link-layer destination address
+ * @v ll_source                Link-layer source address
+ * @v net_proto                Network-layer protocol, in network byte order
+ * @ret rc             Return status code
+ *
+ * This adds both the 802.11 frame header and the 802.3 LLC/SNAP
+ * header used on data packets.
+ */
 static int net80211_ll_push ( struct net_device *netdev,
                              struct io_buffer *iobuf, const void *ll_dest,
                              const void *ll_source, uint16_t net_proto )
@@ -213,6 +351,19 @@ static int net80211_ll_push ( struct net_device *netdev,
        return 0;
 }
 
+/**
+ * Remove 802.11 link-layer header
+ *
+ * @v netdev           Wrapping network device
+ * @v iobuf            I/O buffer
+ * @ret ll_dest                Link-layer destination address
+ * @ret ll_source      Link-layer source 
+ * @ret net_proto      Network-layer protocol, in network byte order
+ * @ret rc             Return status code
+ *
+ * This expects and removes both the 802.11 frame header and the 802.3
+ * LLC/SNAP header that are used on data packets.
+ */
 static int net80211_ll_pull ( struct net_device *netdev __unused,
                              struct io_buffer *iobuf,
                              const void **ll_dest, const void **ll_source,
@@ -266,6 +417,16 @@ static int net80211_ll_pull ( struct net_device *netdev __unused,
        return 0;
 }
 
+/**
+ * Hash 802.11 multicast address
+ *
+ * @v af       Address family
+ * @v net_addr Network-layer address
+ * @ret ll_addr        Filled link-layer address
+ * @ret rc     Return status code
+ *
+ * Currently unimplemented.
+ */
 static int net80211_ll_mc_hash ( unsigned int af __unused,
                                 const void *net_addr __unused,
                                 void *ll_addr __unused )
@@ -273,6 +434,7 @@ static int net80211_ll_mc_hash ( unsigned int af __unused,
        return -ENOTSUP;
 }
 
+/** 802.11 link-layer protocol */
 static struct ll_protocol net80211_ll_protocol __ll_protocol = {
        .name = "802.11",
        .push = net80211_ll_push,
@@ -288,6 +450,14 @@ static struct ll_protocol net80211_ll_protocol __ll_protocol = {
 
 /* ---------- 802.11 network management API ---------- */
 
+/**
+ * Get 802.11 device from wrapping network device
+ *
+ * @v netdev   Wrapping network device
+ * @ret dev    802.11 device wrapped by network device, or NULL
+ *
+ * Returns NULL if the network device does not wrap an 802.11 device.
+ */
 struct net80211_device * net80211_get ( struct net_device *netdev )
 {
        struct net80211_device *dev;
@@ -300,6 +470,16 @@ struct net80211_device * net80211_get ( struct net_device *netdev )
        return NULL;
 }
 
+/**
+ * Set state of 802.11 device keeping management frames
+ *
+ * @v dev      802.11 device
+ * @v enable   Whether to keep management frames
+ * @ret oldenab        Whether management frames were enabled before this call
+ *
+ * If enable is TRUE, beacon, probe, and action frames will be kept
+ * and may be retrieved by calling net80211_mgmt_dequeue().
+ */
 int net80211_keep_mgmt ( struct net80211_device *dev, int enable )
 {
        int oldenab = dev->keep_mgmt;
@@ -308,6 +488,19 @@ int net80211_keep_mgmt ( struct net80211_device *dev, int enable )
        return oldenab;
 }
 
+/**
+ * Get 802.11 management frame
+ *
+ * @v dev      802.11 device
+ * @ret signal Signal strength of returned management frame
+ * @ret iob    I/O buffer
+ *
+ * Frames will only be returned by this function if
+ * net80211_keep_mgmt() has been previously called with enable set to
+ * TRUE.
+ *
+ * The calling function takes ownership of the returned I/O buffer.
+ */
 struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev,
                                           int *signal )
 {
@@ -328,6 +521,23 @@ struct io_buffer * net80211_mgmt_dequeue ( struct net80211_device *dev,
        return NULL;
 }
 
+/**
+ * Transmit 802.11 management frame
+ *
+ * @v dev      802.11 device
+ * @v fc       Frame Control flags for management frame
+ * @v dest     Destination access point
+ * @v iob      I/O buffer
+ * @ret rc     Return status code
+ *
+ * The fc argument must contain at least an IEEE 802.11 management
+ * subtype number (e.g. IEEE80211_STYPE_PROBE_REQ). If it contains
+ * IEEE80211_FC_PROTECTED, the frame will be encrypted prior to
+ * transmission.
+ *
+ * It is required that @a iob have at least 24 bytes of headroom
+ * reserved before its data start.
+ */
 int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6],
                       struct io_buffer *iob )
 {
@@ -347,7 +557,8 @@ int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6],
                if ( ! dev->crypto )
                        return -EINVAL;
 
-               struct io_buffer *eiob = dev->crypto->encrypt ( dev, iob );
+               struct io_buffer *eiob = dev->crypto->encrypt ( dev->crypto,
+                                                               iob );
                free_iob ( iob );
                iob = eiob;
        }
@@ -358,6 +569,18 @@ int net80211_tx_mgmt ( struct net80211_device *dev, u16 fc, u8 dest[6],
 
 /* ---------- driver API ---------- */
 
+/**
+ * Allocate 802.11 device
+ *
+ * @v priv_size                Size of driver-private allocation area
+ * @ret dev            Newly allocated 802.11 device
+ *
+ * This function allocates a net_device with space in its private area
+ * for both the net80211_device it will wrap and the driver-private
+ * data space requested. It initializes the link-layer-specific parts
+ * of the net_device, and links the net80211_device to the net_device
+ * appropriately.
+ */
 struct net80211_device * net80211_alloc ( size_t priv_size )
 {
        struct net80211_device *dev;
@@ -384,6 +607,16 @@ struct net80211_device * net80211_alloc ( size_t priv_size )
        return dev;
 }
 
+/**
+ * Register 802.11 device with network stack
+ *
+ * @v dev      802.11 device
+ * @v ops      802.11 device operations
+ * @v hw       802.11 hardware information
+ *
+ * This also registers the wrapping net_device with the higher network
+ * layers.
+ */
 int net80211_register ( struct net80211_device *dev,
                        struct net80211_device_operations *ops,
                        struct net80211_hw_info *hw )
@@ -400,6 +633,14 @@ int net80211_register ( struct net80211_device *dev,
        return register_netdev ( dev->netdev );
 }
 
+/**
+ * Unregister 802.11 device from network stack
+ *
+ * @v dev      802.11 device
+ *
+ * After this call, the device operations are cleared so that they
+ * will not be called.
+ */
 void net80211_unregister ( struct net80211_device *dev )
 {
        unregister_netdev ( dev->netdev );
@@ -407,6 +648,13 @@ void net80211_unregister ( struct net80211_device *dev )
        dev->op = &net80211_null_ops;
 }
 
+/**
+ * Free 802.11 device
+ *
+ * @v dev      802.11 device
+ *
+ * The device should be unregistered before this function is called.
+ */
 void net80211_free ( struct net80211_device *dev )
 {
        free ( dev->hw );
@@ -414,28 +662,42 @@ void net80211_free ( struct net80211_device *dev )
        netdev_put ( dev->netdev );
 }
 
+
 /* ---------- 802.11 network management workhorse code ---------- */
 
-/* Clear the flags in `clear', set those in `set', and set status to
-   `status'. Clears are done before sets. The LINK_UP bit is cleared
-   when association is cleared, but not set when association is set -
-   that's left to the association task to do after it checks
-   everything's OK. */
+/**
+ * Set state of 802.11 device
+ *
+ * @v dev      802.11 device
+ * @v clear    Bitmask of flags to clear
+ * @v set      Bitmask of flags to set
+ * @v status   Status or reason code for most recent operation
+ *
+ * If @a status represents a reason code, it should be OR'ed with
+ * NET80211_IS_REASON.
+ *
+ * Clearing authentication also clears association; clearing
+ * association also clears security handshaking state. Clearing
+ * association removes the link-up flag from the wrapping net_device,
+ * but setting it does not automatically set the flag; that is left to
+ * the judgment of higher-level code.
+ */
 static inline void net80211_set_state ( struct net80211_device *dev,
                                        short clear, short set,
                                        u16 status )
 {
        /* The conditions in this function are deliberately formulated
           to be decidable at compile-time. */
+       const int statmsk = NET80211_STATUS_MASK | NET80211_IS_REASON;
 
        if ( clear & NET80211_AUTHENTICATED )
                clear |= NET80211_ASSOCIATED;
+
        if ( clear & NET80211_ASSOCIATED )
                clear |= NET80211_CRYPTO_SYNCED;
 
        dev->state = ( dev->state & ~clear ) | set;
-       dev->state = ( dev->state & ~NET80211_STATUS_MASK ) |
-               ( status & NET80211_STATUS_MASK );
+       dev->state = ( dev->state & ~statmsk ) | ( status & statmsk );
 
        if ( clear & NET80211_ASSOCIATED )
                dev->netdev->state &= ~NETDEV_LINK_UP;
@@ -444,6 +706,18 @@ static inline void net80211_set_state ( struct net80211_device *dev,
                dev->op->config ( dev, NET80211_CFG_ASSOC );
 }
 
+/**
+ * Add channels to 802.11 device
+ *
+ * @v dev      802.11 device
+ * @v start    First channel number to add
+ * @v len      Number of channels to add
+ * @v txpower  TX power (dBm) to allow on added channels
+ *
+ * To effectively replace the current list of channels, simply set the
+ * nr_channels field of the 802.11 device to 0 before calling this
+ * function.
+ */
 static void net80211_add_channels ( struct net80211_device *dev, int start,
                                    int len, int txpower )
 {
@@ -471,10 +745,17 @@ static void net80211_add_channels ( struct net80211_device *dev, int start,
        dev->nr_channels = i;
 }
 
+/**
+ * Update 802.11 device state to reflect received capabilities field
+ *
+ * @v dev      802.11 device
+ * @v capab    Capabilities field in beacon, probe, or association frame
+ * @ret rc     Return status code
+ */
 static int net80211_process_capab ( struct net80211_device *dev,
                                    u16 capab )
 {
-       u16 old_erp = dev->erp_flags;
+       u16 old_phy = dev->phy_flags;
 
        if ( ( capab & ( IEEE80211_CAPAB_MANAGED | IEEE80211_CAPAB_ADHOC ) ) !=
             IEEE80211_CAPAB_MANAGED ) {
@@ -487,28 +768,36 @@ static int net80211_process_capab ( struct net80211_device *dev,
                return -ENOSYS;
        }
 
-       dev->erp_flags &= ~( NET80211_ERP_USE_SHORT_PREAMBLE |
-                            NET80211_ERP_USE_SHORT_SLOT );
+       dev->phy_flags &= ~( NET80211_PHY_USE_SHORT_PREAMBLE |
+                            NET80211_PHY_USE_SHORT_SLOT );
 
        if ( capab & IEEE80211_CAPAB_SHORT_PMBL )
-               dev->erp_flags |= NET80211_ERP_USE_SHORT_PREAMBLE;
+               dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE;
 
        if ( capab & IEEE80211_CAPAB_SHORT_SLOT )
-               dev->erp_flags |= NET80211_ERP_USE_SHORT_SLOT;
+               dev->phy_flags |= NET80211_PHY_USE_SHORT_SLOT;
 
-       if ( old_erp != dev->erp_flags )
-               dev->op->config ( dev, NET80211_CFG_ERP_PARAMS );
+       if ( old_phy != dev->phy_flags )
+               dev->op->config ( dev, NET80211_CFG_PHY_PARAMS );
 
        return 0;
 }
 
+/**
+ * Update 802.11 device state to reflect received information elements
+ *
+ * @v dev      802.11 device
+ * @v ie       Pointer to first information element
+ * @v len      Total length of all information elements
+ * @ret rc     Return status code
+ */
 static int net80211_process_ie ( struct net80211_device *dev,
                                 struct ieee80211_ie *ie, int len )
 {
        void *ie_byte = ie;
        void *ie_byte_end = ie_byte + len;
        u16 old_rate = NET80211_RATE_VALUE ( dev->rates[dev->rate] );
-       u16 old_erp = dev->erp_flags;
+       u16 old_phy = dev->phy_flags;
        int have_rates = 0, i;
        int ds_channel = 0;
        int changed = 0;
@@ -539,10 +828,6 @@ static int net80211_process_ie ( struct net80211_device *dev,
                                        dev->basic_rates |=
                                                ( 1 << dev->nr_rates );
 
-                               if ( rate != 10 && rate != 20 && rate != 55 &&
-                                    rate != 110 )      /* 802.11b rates */
-                                       rate |= NET80211_RATE_ERP;
-
                                dev->rates[dev->nr_rates++] = rate;
                        }
 
@@ -562,8 +847,7 @@ static int net80211_process_ie ( struct net80211_device *dev,
                        DBG ( "802.11 setting country regulations for %c%c\n",
                              ie->country.name[0], ie->country.name[1] );
                        for ( i = 0; i < ( ie->len - 3 ) / 3; i++ ) {
-                               if ( ie->country.triplet[i].ext.
-                                    reg_ext_id > 200 ) {
+                               if ( ie->country.triplet[i].ext.reg_ext_id > 200 ) {
                                        DBG ( "802.11 don't know how to parse "
                                              "regulatory extension information\n" );
                                } else {
@@ -576,12 +860,12 @@ static int net80211_process_ie ( struct net80211_device *dev,
                        break;
 
                case IEEE80211_IE_ERP_INFO:
-                       dev->erp_flags &= ~( NET80211_ERP_USE_PROTECTION |
-                                            NET80211_ERP_USE_SHORT_PREAMBLE );
+                       dev->phy_flags &= ~( NET80211_PHY_USE_PROTECTION |
+                                            NET80211_PHY_USE_SHORT_PREAMBLE );
                        if ( ie->erp_info & IEEE80211_ERP_USE_PROTECTION )
-                               dev->erp_flags |= NET80211_ERP_USE_PROTECTION;
+                               dev->phy_flags |= NET80211_PHY_USE_PROTECTION;
                        if ( ! ( ie->erp_info & IEEE80211_ERP_BARKER_LONG ) )
-                               dev->erp_flags |= NET80211_ERP_USE_SHORT_PREAMBLE;
+                               dev->phy_flags |= NET80211_PHY_USE_SHORT_PREAMBLE;
                        break;
 
                case IEEE80211_IE_RSN:
@@ -631,12 +915,12 @@ static int net80211_process_ie ( struct net80211_device *dev,
        }
 
        if ( dev->hw->flags & NET80211_HW_NO_SHORT_PREAMBLE )
-               dev->erp_flags &= ~NET80211_ERP_USE_SHORT_PREAMBLE;
+               dev->phy_flags &= ~NET80211_PHY_USE_SHORT_PREAMBLE;
        if ( dev->hw->flags & NET80211_HW_NO_SHORT_SLOT )
-               dev->erp_flags &= ~NET80211_ERP_USE_SHORT_SLOT;
+               dev->phy_flags &= ~NET80211_PHY_USE_SHORT_SLOT;
 
-       if ( old_erp != dev->erp_flags )
-               changed |= NET80211_CFG_ERP_PARAMS;
+       if ( old_phy != dev->phy_flags )
+               changed |= NET80211_CFG_PHY_PARAMS;
 
        if ( changed )
                dev->op->config ( dev, changed );
@@ -644,9 +928,16 @@ static int net80211_process_ie ( struct net80211_device *dev,
        return 0;
 }
 
-struct ieee80211_ie *
+/**
+ * Create information elements for outgoing probe or association packet
+ *
+ * @v dev              802.11 device
+ * @v ie               Pointer to start of information element area
+ * @ret next_ie                Pointer to first byte after added information elements
+ */
+static struct ieee80211_ie *
 net80211_marshal_request_info ( struct net80211_device *dev,
-                               struct ieee80211_ie *ie ) 
+                               struct ieee80211_ie *ie )
 {
        void *ie_byte = ie;
        int i;
@@ -683,9 +974,26 @@ net80211_marshal_request_info ( struct net80211_device *dev,
        return ie;
 }
 
-#define NET80211_PROBE_GATHER    4 /* seconds to always take */
-#define NET80211_PROBE_TIMEOUT   6 /* seconds to take at maximum */
+/** Seconds to always take when probing, to gather better signal strengths */
+#define NET80211_PROBE_GATHER    4
 
+/** Seconds to allow a probe to take, if no usable AP has yet been found */
+#define NET80211_PROBE_TIMEOUT   6
+
+/**
+ * Probe 802.11 networks
+ *
+ * @v dev      802.11 device
+ * @v essid    SSID to probe for, or "" to accept any (may not be NULL)
+ * @v active   Whether to use active scanning
+ * @ret wlan   WLAN structure for best detected network
+ *
+ * Active scanning may only be used on channels 1-11 in the 2.4GHz
+ * band, due to gPXE's lack of a complete regulatory database. If
+ * active scanning is used, probe packets will be sent on each
+ * channel; this can allow association with hidden-SSID networks if
+ * the SSID is properly specified.
+ */
 struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
                                        const char *essid, int active )
 {
@@ -704,7 +1012,7 @@ struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
        void *ie_byte;
        int rc;
 
-       /* Channels on the 2.4GHz overlap, and the most commonly used
+       /* Channels on 2.4GHz overlap, and the most commonly used
           are 1, 6, and 11. We'll get a result faster if we check
           every 5 channels, but in order to hit all of them the
           number of channels must be relatively prime to 5. If it's
@@ -822,6 +1130,7 @@ struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
                wlan->signal = signal;
                wlan->beacon = iob;
                wlan->security = 0; /* XXX implement */
+               wlan->channel = dev->channel;
                DBG ( "802.11 probe: new best signal %d for AP %s on '%s'\n",
                      wlan->signal, eth_ntoa ( wlan->bssid ), wlan->essid );
 
@@ -848,6 +1157,12 @@ struct net80211_wlan * net80211_probe ( struct net80211_device *dev,
        return NULL;
 }
 
+
+/**
+ * Free WLAN structure
+ *
+ * @v wlan     WLAN structure to free
+ */
 void net80211_free_wlan ( struct net80211_wlan *wlan )
 {
        if ( wlan ) {
@@ -856,7 +1171,12 @@ void net80211_free_wlan ( struct net80211_wlan *wlan )
        }
 }
 
-void net80211_step_associate ( struct process *proc )
+/**
+ * Step 802.11 association process
+ *
+ * @v proc     Association process
+ */
+static void net80211_step_associate ( struct process *proc )
 {
        struct net80211_device *dev =
            container_of ( proc, struct net80211_device, proc_assoc );
@@ -948,7 +1268,7 @@ void net80211_step_associate ( struct process *proc )
        /* state: done! */
        dev->netdev->state |= NETDEV_LINK_UP;
        dev->state &= ~NET80211_WORKING;
-       printf ( "[802.11 associated with %s]\n", dev->essid );
+       printf ( " ok, %s]\n", dev->essid );
        process_del ( proc );
        return;
 
@@ -956,14 +1276,23 @@ void net80211_step_associate ( struct process *proc )
        dev->state &= ~NET80211_WORKING;
        DBG ( "802.11 association process failed with state=%04x "
              "rc=%08x\n", dev->state, rc );
-       printf ( "[802.11 association error: %s]\n", strerror ( rc ) );
+       printf ( " error: %s]\n", strerror ( rc ) );
        process_del ( proc );
 }
 
+/**
+ * Start 802.11 association process
+ *
+ * @v dev      802.11 device
+ *
+ * If the association process is running, it will be restarted.
+ */
 void net80211_autoassociate ( struct net80211_device *dev )
 {
        int len;
 
+       printf ( " [802.11 associating...");
+
        if ( ! ( dev->state & NET80211_WORKING ) ) {
                DBG2 ( "802.11 spawning association process\n" );
                process_add ( &dev->proc_assoc );
@@ -980,35 +1309,49 @@ void net80211_autoassociate ( struct net80211_device *dev )
        dev->associating = NULL;
 }
 
-void net80211_set_rate_intelligently ( struct net80211_device *dev ) 
+/**
+ * Pick TX rate from the rate list we have
+ *
+ * @v dev      802.11 device
+ *
+ * This needs to be expanded into an algorithm that adapts to large
+ * numbers of dropped packets by lowering the rate, and tries raising
+ * the rate if we've been running well for a while at a lower one.
+ */
+static void net80211_set_rate_intelligently ( struct net80211_device *dev )
 {
-       if ( dev->nr_rates == 0 ) {
-               int i;
+       int i;
 
+       if ( dev->nr_rates == 0 ) {
                for ( i = 0; i < dev->hw->nr_supported_rates; i++ ) {
                        u16 rate = dev->hw->supported_rates[i];
-                       if ( rate != 10 && rate != 20 && rate != 55 &&
-                            rate != 110 )
-                               rate |= NET80211_RATE_ERP;
                        dev->rates[dev->nr_rates++] = rate;
                }
+       }
 
-               /* For initial probe, where we know nothing of the
-                  type of network we're on, use a safe default. */
+       /* For now, stick with something safe: the last (probably
+          fastest) 802.11b-compatible rate. */
 
-               dev->rate = dev->nr_rates;
-               for ( i = 0; i < dev->nr_rates; i++ ) {
-                       if ( dev->rates[i] & NET80211_RATE_ERP )
-                               continue;
-                       dev->rate = i;
-                       break;
-               }
-
-               if ( dev->rate == dev->nr_rates ) /* no non-ERP rates */
-                       dev->rate = 0;
+       dev->rate = dev->nr_rates;
+       for ( i = 0; i < dev->nr_rates; i++ ) {
+               if ( net80211_rate_is_erp ( dev->rates[i] ) )
+                       continue;
+               dev->rate = i;
+               break;
        }
+
+       if ( dev->rate == dev->nr_rates ) /* no non-ERP rates */
+               dev->rate = 0;  /* first ERP rate */
 }
 
+/**
+ * Prepare 802.11 device channel and rate set for scanning
+ *
+ * @v dev      802.11 device
+ * @v band     RF band(s) on which to prepare for scanning
+ * @v active   Whether the scanning will be active
+ * @ret rc     Return status code
+ */
 int net80211_prepare_default ( struct net80211_device *dev, int band,
                               int active )
 {
@@ -1044,6 +1387,13 @@ int net80211_prepare_default ( struct net80211_device *dev, int band,
        return 0;
 }
 
+/**
+ * Prepare 802.11 device channel and rate set for communication
+ *
+ * @v dev      802.11 device
+ * @v wlan     WLAN to prepare for communication with
+ * @ret rc     Return status code
+ */
 int net80211_prepare ( struct net80211_device *dev,
                       struct net80211_wlan *wlan )
 {
@@ -1058,6 +1408,11 @@ int net80211_prepare ( struct net80211_device *dev,
 
        /* do crypto setup here */
 
+       /* Barring an IE that tells us the channel outright, assume
+          the channel we heard this AP best on is the channel it's
+          communicating on. */
+       dev->channel = wlan->channel;
+
        rc = net80211_process_capab ( dev, beacon->capability );
        if ( rc )
                return rc;
@@ -1073,6 +1428,19 @@ int net80211_prepare ( struct net80211_device *dev,
        return 0;
 }
 
+/**
+ * Send 802.11 initial authentication frame
+ *
+ * @v dev      802.11 device
+ * @v wlan     WLAN to authenticate with
+ * @v method   Authentication method
+ * @ret rc     Return status code
+ *
+ * @a method may be 0 for Open System authentication or 1 for Shared
+ * Key authentication. Open System provides no security in association
+ * whatsoever, relying on encryption for confidentiality, but Shared
+ * Key actively introduces security problems and is very rarely used.
+ */
 int net80211_send_auth ( struct net80211_device *dev,
                         struct net80211_wlan *wlan, int method )
 {
@@ -1089,6 +1457,17 @@ int net80211_send_auth ( struct net80211_device *dev,
        return net80211_tx_mgmt ( dev, IEEE80211_STYPE_AUTH, wlan->bssid, iob );
 }
 
+/**
+ * Handle receipt of 802.11 authentication frame
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer
+ *
+ * If the authentication method being used is Shared Key, and the
+ * frame that was received included challenge text, the frame is
+ * encrypted using the cryptographic algorithm currently in effect and
+ * sent back to the AP to complete the authentication.
+ */
 static void net80211_handle_auth ( struct net80211_device *dev,
                                   struct io_buffer *iob )
 {
@@ -1132,7 +1511,7 @@ static void net80211_handle_auth ( struct net80211_device *dev,
                memcpy ( hdr->addr1, hdr->addr3, ETH_ALEN );
 
                netdev_tx ( dev->netdev,
-                           dev->crypto->encrypt ( dev, iob ) );
+                           dev->crypto->encrypt ( dev->crypto, iob ) );
                return;
        }
 
@@ -1142,6 +1521,13 @@ static void net80211_handle_auth ( struct net80211_device *dev,
        return;
 }
 
+/**
+ * Send 802.11 association frame
+ *
+ * @v dev      802.11 device
+ * @v wlan     WLAN to associate with
+ * @ret rc     Return status code
+ */
 int net80211_send_assoc ( struct net80211_device *dev,
                          struct net80211_wlan *wlan )
 {
@@ -1176,6 +1562,12 @@ int net80211_send_assoc ( struct net80211_device *dev,
                                  wlan->bssid, iob );
 }
 
+/**
+ * Handle receipt of 802.11 association reply frame
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer
+ */
 static void net80211_handle_assoc_reply ( struct net80211_device *dev,
                                          struct io_buffer *iob )
 {
@@ -1203,6 +1595,13 @@ static void net80211_handle_assoc_reply ( struct net80211_device *dev,
                             IEEE80211_STATUS_SUCCESS );
 }
 
+/**
+ * Handle receipt of 802.11 management frame
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer
+ * @v signal   Signal strength of received frame
+ */
 static void net80211_handle_mgmt ( struct net80211_device *dev,
                                   struct io_buffer *iob, int signal )
 {
@@ -1287,7 +1686,14 @@ static void net80211_handle_mgmt ( struct net80211_device *dev,
 
 /* ---------- Packet handling functions ---------- */
 
-/* Free the iob's for frag cache entry fcid and mark it unused. */
+/**
+ * Free buffers used by 802.11 fragment cache entry
+ *
+ * @v dev      802.11 device
+ * @v fcid     Fragment cache entry index
+ *
+ * After this function, the referenced entry will be marked unused.
+ */
 static void net80211_free_frags ( struct net80211_device *dev, int fcid )
 {
        int j;
@@ -1301,19 +1707,26 @@ static void net80211_free_frags ( struct net80211_device *dev, int fcid )
        }
 
        frag->seqnr = 0;
-       frag->nfrags = 0;
        frag->start_ticks = 0;
-       frag->state = NET80211_FRAG_AVAIL;
+       frag->in_use = 0;
 }
 
-/* Accumulate fragments from frag cache entry fcid, of total size
-   `size' (headers included), into one iob and return it. */
-static struct io_buffer *net80211_accum_frags ( struct net80211_device
-                                               *dev, int fcid, int size )
+/**
+ * Accumulate 802.11 fragments into one I/O buffer
+ *
+ * @v dev      802.11 device
+ * @v fcid     Fragment cache entry index
+ * @v nfrags   Number of fragments received
+ * @v size     Sum of sizes of all fragments, including headers
+ * @ret iob    I/O buffer containing reassembled packet
+ *
+ * This function does not free the fragment buffers.
+ */
+static struct io_buffer *net80211_accum_frags ( struct net80211_device *dev,
+                                               int fcid, int nfrags, int size )
 {
        struct net80211_frag_cache *frag = &dev->frags[fcid];
        int hdrsize = IEEE80211_TYP_FRAME_HEADER_LEN;
-       int nfrags = frag->nfrags;
        int nsize = size - hdrsize * ( nfrags - 1 );
        int i;
 
@@ -1324,7 +1737,7 @@ static struct io_buffer *net80211_accum_frags ( struct net80211_device
        memcpy ( iob_put ( niob, hdrsize ), frag->iob[0]->data, hdrsize );
 
        /* ... and all the data from all of them. */
-       for ( i = 0; i < frag->nfrags; i++ ) {
+       for ( i = 0; i < nfrags; i++ ) {
                int len = iob_len ( frag->iob[i] ) - hdrsize;
                memcpy ( iob_put ( niob, len ),
                         frag->iob[i]->data + hdrsize, len );
@@ -1337,20 +1750,27 @@ static struct io_buffer *net80211_accum_frags ( struct net80211_device
        return niob;
 }
 
+/**
+ * Handle receipt of 802.11 fragment
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer containing fragment
+ * @v signal   Signal strength with which fragment was received
+ */
 static void net80211_rx_frag ( struct net80211_device *dev,
                               struct io_buffer *iob, int signal )
 {
        struct ieee80211_frame *hdr = iob->data;
+       int fragnr = IEEE80211_FRAG ( hdr->seq );
 
-       if ( IEEE80211_FRAG ( hdr->seq ) == 0 &&
-            ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
+       if ( fragnr == 0 && ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
                /* start a frag cache entry */
                int i, newest = -1;
                u32 curr_ticks = currticks(), newest_ticks = 0;
                u32 timeout = ticks_per_sec() * NET80211_FRAG_TIMEOUT;
 
                for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) {
-                       if ( dev->frags[i].state == NET80211_FRAG_AVAIL )
+                       if ( dev->frags[i].in_use == 0 )
                                break;
 
                        if ( dev->frags[i].start_ticks + timeout >=
@@ -1367,13 +1787,13 @@ static void net80211_rx_frag ( struct net80211_device *dev,
 
                /* If we're being sent more concurrent fragmented
                   packets than we can handle, drop the newest so the
-                  oldest has time to complete. */
+                  older ones have time to complete. */
                if ( i == NET80211_NR_CONCURRENT_FRAGS ) {
                        i = newest;
                        net80211_free_frags ( dev, i );
                }
 
-               dev->frags[i].state = NET80211_FRAG_WAITING;
+               dev->frags[i].in_use = 1;
                dev->frags[i].seqnr = IEEE80211_SEQNR ( hdr->seq );
                dev->frags[i].start_ticks = currticks();
                dev->frags[i].iob[0] = iob;
@@ -1381,8 +1801,8 @@ static void net80211_rx_frag ( struct net80211_device *dev,
        } else {
                int i;
                for ( i = 0; i < NET80211_NR_CONCURRENT_FRAGS; i++ ) {
-                       if ( dev->frags[i].state != NET80211_FRAG_AVAIL &&
-                            dev->frags[i].seqnr == IEEE80211_SEQNR ( hdr->seq ) )
+                       if ( dev->frags[i].in_use && dev->frags[i].seqnr ==
+                            IEEE80211_SEQNR ( hdr->seq ) )
                                break;
                }
                if ( i == NET80211_NR_CONCURRENT_FRAGS ) {
@@ -1393,33 +1813,39 @@ static void net80211_rx_frag ( struct net80211_device *dev,
                        return;
                }
 
-               dev->frags[i].iob[IEEE80211_FRAG ( hdr->seq )] = iob;
-
-               if ( dev->frags[i].state == NET80211_FRAG_WAITING &&
-                    ! ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
-                       dev->frags[i].state = NET80211_FRAG_FINISHING;
-                       dev->frags[i].nfrags =
-                           IEEE80211_FRAG ( hdr->seq ) + 1;
-               }
+               dev->frags[i].iob[fragnr] = iob;
 
-               if ( dev->frags[i].state == NET80211_FRAG_FINISHING ) {
+               if ( ! ( hdr->fc & IEEE80211_FC_MORE_FRAG ) ) {
                        int j, size = 0;
-                       for ( j = 0; j < dev->frags[i].nfrags; j++ ) {
+                       for ( j = 0; j < fragnr; j++ ) {
                                size += iob_len ( dev->frags[i].iob[j] );
                                if ( dev->frags[i].iob[j] == NULL )
                                        break;
                        }
-                       if ( j == dev->frags[i].nfrags ) {
+                       if ( j == fragnr ) {
                                /* we've got everything! */
                                struct io_buffer *niob =
-                                   net80211_accum_frags ( dev, i, size );
+                                   net80211_accum_frags ( dev, i, fragnr,
+                                                          size );
                                net80211_free_frags ( dev, i );
                                net80211_rx ( dev, niob, signal );
+                       } else {
+                               DBG ( "802.11 dropping fragmented packet due "
+                                     "to out-of-order arrival, fc=%04x "
+                                     "seq=%04x\n", hdr->fc, hdr->seq );
+                               net80211_free_frags ( dev, i );
                        }
                }
        }
 }
 
+/**
+ * Handle receipt of 802.11 frame
+ *
+ * @v dev      802.11 device
+ * @v iob      I/O buffer
+ * @v signal   Received signal strength
+ */
 void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
                   int signal )
 {
@@ -1446,7 +1872,7 @@ void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
                if ( ! dev->crypto )
                        goto drop;      /* can't decrypt packets on an open network */
 
-               niob = dev->crypto->decrypt ( dev, iob );
+               niob = dev->crypto->decrypt ( dev->crypto, iob );
                if ( ! niob )
                        goto drop;      /* drop failed decryption */
                free_iob ( iob );