[802.11] Add rate control support; fix two bugs; remove high rate bits
[people/oremanj/gpxe.git] / src / net / net80211.c
index 88bdd55..63f357d 100644 (file)
@@ -20,7 +20,6 @@
 
 FILE_LICENCE ( GPL2_OR_LATER );
 
-#include <stdio.h>
 #include <string.h>
 #include <byteswap.h>
 #include <stdlib.h>
@@ -183,7 +182,6 @@ net80211_marshal_request_info ( struct net80211_device *dev,
 
 static void net80211_step_associate ( struct process *proc );
 static void net80211_set_rtscts_rate ( struct net80211_device *dev );
-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,
@@ -396,7 +394,7 @@ static inline int net80211_rate_is_erp ( u16 rate )
 static u16 net80211_duration ( struct net80211_device *dev, int bytes )
 {
        struct net80211_channel *chan = &dev->channels[dev->channel];
-       u16 rate = NET80211_RATE_VALUE ( dev->rates[dev->rate] );
+       u16 rate = dev->rates[dev->rate];
        u32 kbps = rate * 100;
 
        if ( chan->band == NET80211_BAND_5GHZ || net80211_rate_is_erp ( rate ) ) {
@@ -784,6 +782,7 @@ void net80211_unregister ( struct net80211_device *dev )
 void net80211_free ( struct net80211_device *dev )
 {
        free ( dev->hw );
+       free ( dev->rctl );
        netdev_nullify ( dev->netdev );
        netdev_put ( dev->netdev );
 }
@@ -932,7 +931,7 @@ static int net80211_process_capab ( struct net80211_device *dev,
 static int net80211_process_ie ( struct net80211_device *dev,
                                 union ieee80211_ie *ie, void *ie_end )
 {
-       u16 old_rate = NET80211_RATE_VALUE ( dev->rates[dev->rate] );
+       u16 old_rate = dev->rates[dev->rate];
        u16 old_phy = dev->phy_flags;
        int have_rates = 0, i;
        int ds_channel = 0;
@@ -1025,7 +1024,7 @@ static int net80211_process_ie ( struct net80211_device *dev,
                        int ok = 0;
                        for ( j = 0; j < dev->hw->nr_supported_rates; j++ ) {
                                if ( dev->hw->supported_rates[j] ==
-                                    NET80211_RATE_VALUE ( dev->rates[i] ) ) {
+                                    dev->rates[i] ) {
                                        ok = 1;
                                        break;
                                }
@@ -1042,6 +1041,16 @@ static int net80211_process_ie ( struct net80211_device *dev,
 
                dev->nr_rates -= delta;
 
+               /* Sort available rates - sorted subclumps tend to already
+                  exist, so insertion sort works well. */
+               for ( i = 1; i < dev->nr_rates; i++ ) {
+                       u16 rate = dev->rates[i];
+
+                       for ( j = i - 1; j >= 0 && dev->rates[j] >= rate; j-- )
+                               dev->rates[j + 1] = dev->rates[j];
+                       dev->rates[j + 1] = rate;
+               }
+
                net80211_set_rtscts_rate ( dev );
 
                if ( dev->rates[dev->rate] != old_rate )
@@ -1084,7 +1093,7 @@ net80211_marshal_request_info ( struct net80211_device *dev,
        ie->id = IEEE80211_IE_RATES;
        ie->len = dev->nr_rates;
        for ( i = 0; i < ie->len; i++ ) {
-               ie->rates[i] = NET80211_RATE_VALUE ( dev->rates[i] ) / 5;
+               ie->rates[i] = dev->rates[i] / 5;
                if ( dev->basic_rates & ( 1 << i ) )
                        ie->rates[i] |= 0x80;
        }
@@ -1542,6 +1551,12 @@ static void net80211_step_associate ( struct process *proc )
                        goto fail;
                }
 
+               /* If we probed using a broadcast SSID, record that
+                  fact for the settings applicator before we clobber
+                  it with the specific SSID we've chosen. */
+               if ( ! dev->essid[0] )
+                       dev->state |= NET80211_AUTO_SSID;
+
                DBGC ( dev, "802.11 %p found network %s (%s)\n", dev,
                       dev->associating->essid,
                       eth_ntoa ( dev->associating->bssid ) );
@@ -1626,6 +1641,8 @@ static void net80211_step_associate ( struct process *proc )
        net80211_free_wlan ( dev->associating );
        dev->associating = NULL;
 
+       dev->rctl = rc80211_init ( dev );
+
        process_del ( proc );
 
        DBGC ( dev, "802.11 %p associated with %s (%s)\n", dev,
@@ -1671,10 +1688,10 @@ int net80211_check_ssid_update ( void )
                                      IEEE80211_MAX_SSID_LEN );
                ssid[len] = 0;
 
-               if ( strcmp ( ssid, dev->essid ) != 0 ) {
+               if ( strcmp ( ssid, dev->essid ) != 0 &&
+                    ! ( ! ssid[0] && ( dev->state & NET80211_AUTO_SSID ) ) ) {
                        DBGC ( dev, "802.11 %p updating association: "
                               "%s -> %s\n", dev, dev->essid, ssid );
-                       net80211_set_state ( dev, NET80211_PROBED, 0, 0 );
                        net80211_autoassociate ( dev );
                }
        }
@@ -1707,7 +1724,7 @@ void net80211_autoassociate ( struct net80211_device *dev )
        dev->essid[len] = 0;
        dev->ctx.probe = NULL;
        dev->associating = NULL;
-       net80211_set_state ( dev, NET80211_ASSOCIATED, NET80211_WORKING, 0 );
+       net80211_set_state ( dev, NET80211_PROBED, NET80211_WORKING, 0 );
 }
 
 /**
@@ -1720,13 +1737,13 @@ void net80211_autoassociate ( struct net80211_device *dev )
  */
 static void net80211_set_rtscts_rate ( struct net80211_device *dev )
 {
-       u16 datarate = NET80211_RATE_VALUE ( dev->rates[dev->rate] );
+       u16 datarate = dev->rates[dev->rate];
        u16 rtsrate = 0;
        int rts_idx = -1;
        int i;
 
        for ( i = 0; i < dev->nr_rates; i++ ) {
-               u16 rate = NET80211_RATE_VALUE ( dev->rates[i] );
+               u16 rate = dev->rates[i];
 
                if ( ! ( dev->basic_rates & ( 1 << i ) ) || rate > datarate )
                        continue;
@@ -1746,42 +1763,22 @@ static void net80211_set_rtscts_rate ( struct net80211_device *dev )
 }
 
 /**
- * Pick TX rate from the rate list we have
+ * Set data transmission rate for 802.11 device
  *
  * @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.
+ * @v rate     Rate to set, as index into @c dev->rates array
  */
-static void net80211_set_rate_intelligently ( struct net80211_device *dev )
+void net80211_set_rate_idx ( struct net80211_device *dev, int rate )
 {
-       int i, oldrate = dev->rate;
-
-       if ( dev->nr_rates == 0 ) {
-               for ( i = 0; i < dev->hw->nr_supported_rates; i++ ) {
-                       u16 rate = dev->hw->supported_rates[i];
-                       dev->rates[dev->nr_rates++] = rate;
-               }
-               oldrate = -1;   /* always reconfigure */
-       }
-
-       /* 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 ( 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 */
+       if ( rate >= 0 && rate < dev->nr_rates && rate != dev->rate ) {
+               DBGC2 ( dev, "802.11 %p changing rate from %d->%d Mbps\n",
+                       dev, dev->rates[dev->rate] / 10,
+                       dev->rates[rate] / 10 );
 
-       if ( dev->rate != oldrate )
+               dev->rate = rate;
+               net80211_set_rtscts_rate ( dev );
                dev->op->config ( dev, NET80211_CFG_RATE );
+       }
 }
 
 /**
@@ -1848,11 +1845,15 @@ int net80211_prepare_probe ( struct net80211_device *dev, int band,
                                                NET80211_REG_TXPOWER );
        }
 
+       /* Use channel 1 for now */
        dev->channel = 0;
        dev->op->config ( dev, NET80211_CFG_CHANNEL );
 
-       dev->nr_rates = 0;
-       net80211_set_rate_intelligently ( dev );
+       /* Always do active probes at 1Mbps */
+       dev->rate = 0;
+       dev->nr_rates = 1;
+       dev->rates[0] = 10;
+       dev->op->config ( dev, NET80211_CFG_RATE );
 
        return 0;
 }
@@ -1892,7 +1893,9 @@ int net80211_prepare_assoc ( struct net80211_device *dev,
        if ( rc )
                return rc;
 
-       net80211_set_rate_intelligently ( dev );
+       /* Associate at the lowest rate so we know it'll get through */
+       dev->rate = 0;
+       dev->op->config ( dev, NET80211_CFG_RATE );
 
        return 0;
 }
@@ -2322,7 +2325,7 @@ static void net80211_rx_frag ( struct net80211_device *dev,
                                    net80211_accum_frags ( dev, i, fragnr,
                                                           size );
                                net80211_free_frags ( dev, i );
-                               net80211_rx ( dev, niob, signal );
+                               net80211_rx ( dev, niob, signal, 0 );
                        } else {
                                DBGC ( dev, "802.11 %p dropping fragmented "
                                       "packet due to out-of-order arrival, "
@@ -2340,9 +2343,12 @@ static void net80211_rx_frag ( struct net80211_device *dev,
  * @v dev      802.11 device
  * @v iob      I/O buffer
  * @v signal   Received signal strength
+ * @v rate     Bitrate at which frame was received, in 100 kbps units
+ *
+ * If the rate or signal is unknown, 0 should be passed.
  */
 void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
-                  int signal )
+                  int signal, u16 rate )
 {
        struct ieee80211_frame *hdr = iob->data;
        u16 type = hdr->fc & IEEE80211_FC_TYPE;
@@ -2393,14 +2399,11 @@ void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
        if ( ( hdr->fc & IEEE80211_FC_SUBTYPE ) != IEEE80211_STYPE_DATA )
                goto drop;      /* drop QoS, CFP, or null data packets */
 
-       if ( hdr->fc & IEEE80211_FC_RETRY ) {
-               DBGCP ( dev, "802.11 %p rx RETX packet %04x\n",
-                       dev, hdr->seq );
-       } else {
-               DBGCP ( dev, "802.11 %p rx ok   packet %04x\n",
-                       dev, hdr->seq );
-       }
+       /* Update rate-control algorithm */
+       if ( dev->rctl )
+               rc80211_update_rx ( dev, hdr->fc & IEEE80211_FC_RETRY, rate );
 
+       /* Pass packet onward */
        if ( netdev_link_ok ( dev->netdev ) ) {
                netdev_rx ( dev->netdev, iob );
                return;
@@ -2425,7 +2428,6 @@ void net80211_rx ( struct net80211_device *dev, struct io_buffer *iob,
 void net80211_rx_err ( struct net80211_device *dev,
                       struct io_buffer *iob, int rc )
 {
-       DBGCP ( dev, "802.11 %p rx FAIL\n", dev );
        netdev_rx_err ( dev->netdev, iob, rc );
 }
 
@@ -2437,7 +2439,8 @@ void net80211_rx_err ( struct net80211_device *dev,
  * @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.
+ * and removes and frees the I/O buffer from its TX queue. The
+ * provided retry information is used to tune our transmission rate.
  *
  * If the packet did not need to be retransmitted because it was
  * properly ACKed the first time, @a retries should be 0.
@@ -2445,20 +2448,10 @@ void net80211_rx_err ( struct net80211_device *dev,
 void net80211_tx_complete ( struct net80211_device *dev,
                            struct io_buffer *iob, int retries, int rc )
 {
-       if ( retries ) {
-               if ( rc ) {
-                       DBGCP ( dev, "802.11 %p tx FAIL after %d retx\n", dev,
-                               retries );
-               } else {
-                       DBGCP ( dev, "802.11 %p tx RETX %d times\n", dev,
-                               retries );
-               }
-       } else {
-               if ( rc ) {
-                       DBGCP ( dev, "802.11 %p tx FAIL outright\n", dev );
-               } else {
-                       DBGCP ( dev, "802.11 %p tx ok\n", dev );
-               }
-       }
+       /* Update rate-control algorithm */
+       if ( dev->rctl )
+               rc80211_update_tx ( dev, retries, rc );
+
+       /* Pass completion onward */
        netdev_tx_complete_err ( dev->netdev, iob, rc );
 }