Subject: rate adaptation for BSD
To: None <tech-net@netbsd.org, bsd-wireless@lists.bawug.org>
From: David Young <dyoung@pobox.com>
List: tech-net
Date: 06/26/2003 22:18:18
--PNTmBPCT7hxwcZjr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
I have added rate adaptation for 802.11b APs and clients to wi.
The performance of host APs will be especially improved. I have attached
patches in the hope of getting a some review and testing.
This will definitely improve the performance of wi access points. I have
high hopes that it will improve the performance for clients, too.
It is desirable for the host to do rate adaptation because in AP mode,
the Prism firmware will not do it, and because in client mode, the
algorithm which Lucent & Prism use is not very sophisticated.
I have drawn the basic ideas for the algorithm from the paper "Link
Adaptation Strategy for IEEE 802.11 WLAN via Received Signal Strength
Measurement" by Javier del Prado Pavon and Sunghyun Choi. I have also
had some personal correspondence with the authors. It is not possible
to implement their algorithm precisely as they envision it because of
hardware limitations, but I think I got about as close as I could hope.
Caveat:
Sometimes an AP will behave differently than I expect, sending long
packets at higher rates than it sends short ones to the same client.
I think that this has something to do with Prism sending packets
"louder" or "softer" depending on length. I don't understand it.
I have not treated ad hoc mode, yet, since Prism complicates that
with their limited API.
I have not tested and debugged the algorithm with clients, yet,
but it does what I expect for an AP.
Dave
--
David Young OJC Technologies
dyoung@ojctech.com Urbana, IL * (217) 278-3933
--PNTmBPCT7hxwcZjr
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=ratediffs
Index: sys/net/if_ieee80211.h
===================================================================
RCS file: /cvsroot/src/sys/net/if_ieee80211.h,v
retrieving revision 1.32
diff -u -r1.32 if_ieee80211.h
--- sys/net/if_ieee80211.h 2003/05/16 01:26:17 1.32
+++ sys/net/if_ieee80211.h 2003/06/27 02:19:46
@@ -405,8 +405,25 @@
#define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT)
/*
+ * Link adaptation.
+ */
+
+/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */
+#define IEEE80211_LADAPT_BUCKETS 3
+#define IEEE80211_LADAPT_BUCKET0 128
+
+#define IEEE80211_LADAPT_RETRY_LIMIT 3
+
+#define IEEE80211_LADAPT_THRESH_NEW \
+ (ieee80211_ladapt_thresh_denom - ieee80211_ladapt_thresh_old)
+#define IEEE80211_LADAPT_DECAY_NEW \
+ (ieee80211_ladapt_decay_denom - ieee80211_ladapt_decay_old)
+#define IEEE80211_LADAPT_AVGRSSI_NEW \
+ (ieee80211_ladapt_avgrssi_denom - ieee80211_ladapt_avgrssi_old)
+
+/*
* Structure for IEEE 802.11 drivers.
- */
+ */
#define IEEE80211_CHAN_MAX 255
#define IEEE80211_RATE_SIZE 12
@@ -448,6 +465,8 @@
/* hardware */
u_int8_t ni_rssi;
+ /* exponential average RSSI << 8 */
+ u_int16_t ni_avg_rssi;
u_int32_t ni_rstamp;
/* header */
@@ -478,8 +497,24 @@
int ni_fails; /* failure count to associate */
int ni_inact; /* inactivity mark count */
int ni_txrate; /* index to ni_rates[] */
+ /* RSSI threshold for each Tx rate */
+ u_int16_t ni_rate_thresh[IEEE80211_LADAPT_BUCKETS]
+ [IEEE80211_RATE_SIZE];
+ u_char ni_rate_used[IEEE80211_LADAPT_BUCKETS]
+ [howmany(IEEE80211_RATE_SIZE,NBBY)];
+ u_char ni_rate_fail[IEEE80211_LADAPT_BUCKETS]
+ [howmany(IEEE80211_RATE_SIZE,NBBY)];
+
void *ni_private; /* driver private */
};
+
+/* Properties of a Tx packet, for link adaptation. */
+struct ieee80211_txinfo {
+ u_int i_len; /* Tx packet length */
+ u_int i_rateidx; /* index into ni->ni_rates */
+ u_int8_t i_dstaddr[IEEE80211_ADDR_LEN]; /* destination STA MAC */
+ u_int8_t i_rssi; /* destination STA avg RSS @ Tx time */
+};
/* ni_chan encoding for FH phy */
#define IEEE80211_FH_CHANMOD 80
@@ -534,6 +569,7 @@
int ic_mgt_timer; /* mgmt timeout */
int ic_scan_timer; /* scant wait */
int ic_inact_timer; /* inactivity timer wait */
+ int ic_ladapt_timer;/* link adaptation debug timer*/
int ic_des_esslen;
u_int8_t ic_des_essid[IEEE80211_NWID_LEN];
int ic_des_chan; /* desired channel */
@@ -544,6 +580,7 @@
u_int32_t ic_iv; /* initial vector for wep */
u_int32_t ic_aid_bitmap[IEEE80211_MAX_AID / 32 + 1];
u_int16_t ic_max_aid;
+ struct callout ic_ladapt_ch; /* decay of RSS thresholds */
};
#ifdef __NetBSD__
#define ic_if ic_ec.ec_if
@@ -615,6 +652,11 @@
void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
struct mbuf *);
+
+int ieee80211_choose_rate(struct ieee80211com *, struct ieee80211_frame *,
+ u_int);
+void ieee80211_lower_rate(struct ieee80211com *, struct ieee80211_txinfo *);
+void ieee80211_raise_rate(void *);
#endif /* _KERNEL */
Index: sys/net/if_ieee80211subr.c
===================================================================
RCS file: /cvsroot/src/sys/net/if_ieee80211subr.c,v
retrieving revision 1.34
diff -u -r1.34 if_ieee80211subr.c
--- sys/net/if_ieee80211subr.c 2003/05/31 19:37:15 1.34
+++ sys/net/if_ieee80211subr.c 2003/06/27 02:19:48
@@ -82,9 +82,28 @@
#ifdef IEEE80211_DEBUG
int ieee80211_debug = 0;
+
+static struct timeval lastratechoice; /* time of last rate choice msg */
+static int curchoiceps = 0; /* current rate-choice msgs/sec */
+static int ieee80211_choicerate = 2; /* rate-choice max msgs/sec */
+
+static struct timeval lastrateadapt; /* time of last rate adaptation msg */
+static int curladaptps = 0; /* current rate-adaptation msgs/sec */
+static int ieee80211_adaptrate = 4; /* rate-adaptation max msgs/sec */
+
+#define RCHOICE_PRINTF(__ic, X) \
+ if ((__ic->ic_if.if_flags & IFF_DEBUG) && \
+ ppsratecheck(&lastratechoice, &curchoiceps, ieee80211_choicerate)) \
+ printf X
+#define LADAPT_PRINTF(X) \
+ if ((ieee80211_debug > 0) && \
+ ppsratecheck(&lastrateadapt, &curladaptps, ieee80211_adaptrate)) \
+ printf X
#define DPRINTF(X) if (ieee80211_debug) printf X
#define DPRINTF2(X) if (ieee80211_debug>1) printf X
#else
+#define RCHOICE_PRINTF(__ic, X)
+#define LADAPT_PRINTF(X)
#define DPRINTF(X)
#define DPRINTF2(X)
#endif
@@ -125,6 +144,9 @@
static void ieee80211_crc_init(void);
static u_int32_t ieee80211_crc_update(u_int32_t, u_int8_t *, int);
+static void ieee80211_node_raise_rate(struct ieee80211com *,
+ struct ieee80211_node *);
+
static const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
@@ -132,6 +154,16 @@
"deauth", "reserved#13", "reserved#14", "reserved#15"
};
+/* RSS threshold decay. */
+u_int ieee80211_ladapt_decay_denom = 8;
+u_int ieee80211_ladapt_decay_old = 4;
+/* RSS threshold update. */
+u_int ieee80211_ladapt_thresh_denom = 8;
+u_int ieee80211_ladapt_thresh_old = 4;
+/* RSS average update. */
+u_int ieee80211_ladapt_avgrssi_denom = 8;
+u_int ieee80211_ladapt_avgrssi_old = 4;
+
void
ieee80211_ifattach(struct ifnet *ifp)
{
@@ -172,6 +204,9 @@
if (ic->ic_max_aid == 0)
ic->ic_max_aid = IEEE80211_MAX_AID;
+ /* rate threshold decay */
+ callout_init(&ic->ic_ladapt_ch);
+
/* initialize management frame handlers */
ic->ic_recv_mgmt[IEEE80211_FC0_SUBTYPE_PROBE_RESP
>> IEEE80211_FC0_SUBTYPE_SHIFT] = ieee80211_recv_beacon;
@@ -227,6 +262,7 @@
free(ic->ic_wep_ctx, M_DEVBUF);
ic->ic_wep_ctx = NULL;
}
+ callout_stop(&ic->ic_ladapt_ch);
ieee80211_free_allnodes(ic);
ether_ifdetach(ifp);
splx(s);
@@ -241,7 +277,7 @@
struct ether_header *eh;
void (*rh)(struct ieee80211com *, struct mbuf *, int, u_int);
struct mbuf *m1;
- int error, len;
+ int error, len, last_avg;
u_int8_t dir, subtype;
u_int8_t *bssid;
u_int16_t rxseq;
@@ -302,6 +338,14 @@
goto out;
}
ni->ni_rssi = rssi;
+ last_avg = ni->ni_avg_rssi;
+ ni->ni_avg_rssi =
+ (ieee80211_ladapt_avgrssi_old * ni->ni_avg_rssi +
+ IEEE80211_LADAPT_AVGRSSI_NEW * (ni->ni_rssi << 8)) /
+ ieee80211_ladapt_avgrssi_denom;
+ LADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n",
+ ic->ic_if.if_xname, ether_sprintf(wh->i_addr2),
+ ni->ni_rssi, last_avg, ni->ni_avg_rssi));
ni->ni_rstamp = rstamp;
rxseq = ni->ni_rxseq;
ni->ni_rxseq =
@@ -337,7 +381,7 @@
if (ifp->if_flags & IFF_DEBUG)
printf("%s: power save mode off for %s\n",
- ifp->if_xname, ether_sprintf(wh->i_addr1));
+ ifp->if_xname, ether_sprintf(wh->i_addr2));
while (!IF_IS_EMPTY(&ni->ni_savedq)) {
struct mbuf *m;
@@ -2153,6 +2197,8 @@
IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_BASIC_RATE);
return;
}
+ /* use highest rate (TBD rate selection) */
+ ni->ni_txrate = ni->ni_nrate - 1;
ni->ni_rssi = rssi;
ni->ni_rstamp = rstamp;
ni->ni_intval = bintval;
@@ -2408,7 +2454,6 @@
(*ifp->if_start)(ifp);
}
-
int
ieee80211_new_state(struct ifnet *ifp, enum ieee80211_state nstate, int mgt)
{
@@ -2436,6 +2481,7 @@
ic->ic_state = nstate;
switch (nstate) {
case IEEE80211_S_INIT:
+ callout_stop(&ic->ic_ladapt_ch);
switch (ostate) {
case IEEE80211_S_INIT:
break;
@@ -2492,6 +2538,7 @@
}
break;
case IEEE80211_S_SCAN:
+ ieee80211_raise_rate((void*)ic);
ic->ic_flags &= ~IEEE80211_F_SIBSS;
ni = &ic->ic_bss;
/* initialize bss for probe request */
@@ -2608,6 +2655,7 @@
}
break;
case IEEE80211_S_RUN:
+ ieee80211_raise_rate((void*)ic);
switch (ostate) {
case IEEE80211_S_INIT:
case IEEE80211_S_AUTH:
@@ -3534,4 +3582,195 @@
break;
}
return error;
+}
+
+/*
+ * If we transmitted an l-length packet at rate n in this decay period,
+ * then the RSS threshold T[l, n+1] decays, approaching T[l, n].
+ */
+void
+ieee80211_raise_rate(void *xic)
+{
+ struct ieee80211com *ic = (struct ieee80211com *)xic;
+ struct ieee80211_node *ni;
+ int s;
+
+ s = splnet();
+
+ ++ic->ic_ladapt_timer;
+
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
+ ieee80211_node_raise_rate(ic, ni);
+ } else {
+ ieee80211_node_raise_rate(ic, &ic->ic_bss);
+ }
+
+ callout_reset(&ic->ic_ladapt_ch, hz, ieee80211_raise_rate, xic);
+
+ splx(s);
+}
+
+static void
+ieee80211_node_raise_rate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int i, j, rate;
+
+ for (i = 0; i < IEEE80211_LADAPT_BUCKETS; i++) {
+ thrs = &ni->ni_rate_thresh[i];
+ for (j = ni->ni_nrate - 1; --j >= 0; ) {
+ if (isclr(ni->ni_rate_used[i], j) ||
+ isset(ni->ni_rate_fail[i], j) ||
+ (*thrs)[j + 1] <= (*thrs)[j])
+ continue;
+
+ rate = (ni->ni_rates[j + 1] & IEEE80211_RATE_VAL);
+
+ LADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ",
+ ic->ic_if.if_xname,
+ IEEE80211_LADAPT_BUCKET0 << (3 * i),
+ rate / 2, rate * 5 % 10, (*thrs)[j + 1]));
+
+ (*thrs)[j + 1] =
+ (ieee80211_ladapt_decay_old * (*thrs)[j + 1] +
+ IEEE80211_LADAPT_DECAY_NEW * (*thrs)[j]) /
+ ieee80211_ladapt_decay_denom;
+
+ LADAPT_PRINTF(("-> %d\n", (*thrs)[j + 1]));
+ }
+ }
+ memset(ni->ni_rate_used, 0, sizeof(ni->ni_rate_used));
+ memset(ni->ni_rate_fail, 0, sizeof(ni->ni_rate_fail));
+
+#ifdef IEEE80211_DEBUG
+ if ((ieee80211_debug > 0) && (ic->ic_ladapt_timer % 5 == 0)) {
+ printf("%s: dst %s thresholds\n", ic->ic_if.if_xname,
+ ether_sprintf(ni->ni_macaddr));
+ for (i = 0; i < IEEE80211_LADAPT_BUCKETS; i++) {
+ printf("%d-byte", IEEE80211_LADAPT_BUCKET0 << (3 * i));
+ for (j = 0; j < ni->ni_nrate; j++) {
+ rate = (ni->ni_rates[j] & IEEE80211_RATE_VAL);
+ printf(", T[%d.%d] = %d", rate / 2,
+ rate * 5 % 10, ni->ni_rate_thresh[i][j]);
+ }
+ printf("\n");
+ }
+ }
+#endif /* IEEE80211_DEBUG */
+}
+
+/*
+ * Adapt the data rate to suit the conditions. When a transmitted
+ * packet is dropped after IEEE80211_LADAPT_RETRY_LIMIT retransmissions,
+ * raise the RSS threshold for transmitting packets of similar length at
+ * the same data rate.
+ */
+void
+ieee80211_lower_rate(struct ieee80211com *ic, struct ieee80211_txinfo *txi)
+{
+ struct ieee80211_node *ni;
+ u_int16_t last_thr;
+ u_int i, thridx, top;
+ int s;
+
+ s = splnet();
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+ ni = &ic->ic_bss;
+ } else if ((ni = ieee80211_find_node(ic, txi->i_dstaddr)) == NULL) {
+ DPRINTF(("ieee80211_lower_rate: missing node %s\n",
+ ether_sprintf(txi->i_dstaddr)));
+ splx(s);
+ return;
+ }
+
+ if (txi->i_rateidx >= ni->ni_nrate) {
+ DPRINTF(("ieee80211_lower_rate: "
+ "%s rate #%d > #%d out of bounds\n",
+ ether_sprintf(txi->i_dstaddr), txi->i_rateidx,
+ ni->ni_nrate - 1));
+ splx(s);
+ return;
+ }
+
+ for (i = 0, top = IEEE80211_LADAPT_BUCKET0;
+ i < IEEE80211_LADAPT_BUCKETS; i++, top <<= 3) {
+ thridx = i;
+ if (txi->i_len <= top)
+ break;
+ }
+
+ last_thr = ni->ni_rate_thresh[thridx][txi->i_rateidx];
+ ni->ni_rate_thresh[thridx][txi->i_rateidx] =
+ (ieee80211_ladapt_thresh_old * last_thr +
+ IEEE80211_LADAPT_THRESH_NEW * (txi->i_rssi << 8)) /
+ ieee80211_ladapt_thresh_denom;
+
+ setbit(ni->ni_rate_fail[thridx], txi->i_rateidx);
+
+ LADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n",
+ ic->ic_if.if_xname, ether_sprintf(txi->i_dstaddr),
+ txi->i_rssi, txi->i_len,
+ (ni->ni_rates[txi->i_rateidx] & IEEE80211_RATE_VAL) / 2,
+ (ni->ni_rates[txi->i_rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ last_thr, ni->ni_rate_thresh[thridx][txi->i_rateidx]));
+ splx(s);
+}
+
+/*
+ * Choose a data rate for a packet len bytes long that suits the wireless
+ * conditions.
+ *
+ * TBD Adapt fragmentation threshold.
+ */
+int
+ieee80211_choose_rate(struct ieee80211com *ic, struct ieee80211_frame *wh,
+ u_int len)
+{
+ struct ieee80211_node *ni;
+ u_int16_t (*thrs)[IEEE80211_RATE_SIZE];
+ int flags = 0, i, rateidx = 0, thridx, s, top;
+
+ s = splnet();
+
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ flags |= IEEE80211_RATE_BASIC;
+ ni = &ic->ic_bss; /* TBD choose STA w/ lowest rate */
+ } else if (ic->ic_opmode == IEEE80211_M_STA)
+ ni = &ic->ic_bss;
+ else if ((ni = ieee80211_find_node(ic, wh->i_addr1)) == NULL)
+ goto out;
+
+ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+ flags |= IEEE80211_RATE_BASIC;
+
+ for (i = 0, top = IEEE80211_LADAPT_BUCKET0;
+ i < IEEE80211_LADAPT_BUCKETS; i++, top <<= 3) {
+ thridx = i;
+ if (len <= top)
+ break;
+ }
+
+ thrs = &ni->ni_rate_thresh[thridx];
+
+ /* Choose the highest rate with all the flags set. */
+ for (i = ni->ni_nrate; --i >= 0; ) {
+ rateidx = i;
+ if ((ni->ni_rates[i] & flags) != flags)
+ continue;
+ if (i == ic->ic_fixed_rate || (*thrs)[i] < ni->ni_avg_rssi)
+ break;
+ }
+
+ setbit(ni->ni_rate_used[thridx], rateidx);
+
+ RCHOICE_PRINTF(ic, ("%s: dst %s threshold[%d, %d.%d] %d < %d\n",
+ ic->ic_if.if_xname, ether_sprintf(wh->i_addr1), len,
+ (ni->ni_rates[rateidx] & IEEE80211_RATE_VAL) / 2,
+ (ni->ni_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10,
+ (*thrs)[rateidx], ni->ni_avg_rssi));
+out:
+ splx(s);
+ return rateidx;
}
Index: sys/dev/ic/wi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wi.c,v
retrieving revision 1.130
diff -u -r1.130 wi.c
--- sys/dev/ic/wi.c 2003/06/19 06:16:36 1.130
+++ sys/dev/ic/wi.c 2003/06/27 02:19:49
@@ -121,7 +121,8 @@
static int wi_get_cfg(struct ifnet *, u_long, caddr_t);
static int wi_set_cfg(struct ifnet *, u_long, caddr_t);
-static int wi_write_txrate(struct wi_softc *);
+static int wi_cfg_txrate(struct wi_softc *);
+static int wi_write_txrate(struct wi_softc *, int);
static int wi_write_wep(struct wi_softc *);
static int wi_write_multi(struct wi_softc *);
static int wi_alloc_fid(struct wi_softc *, int, int *);
@@ -272,12 +273,20 @@
setbit(ic->ic_chan_avail, i + 1);
}
- sc->sc_dbm_adjust = 100; /* default */
+ if (sc->sc_firmware_type == WI_LUCENT) {
+ sc->sc_min_rssi = WI_LUCENT_MIN_RSSI;
+ sc->sc_max_rssi = WI_LUCENT_MAX_RSSI;
+ sc->sc_dbm_offset = WI_LUCENT_DBM_OFFSET;
+ } else {
+ sc->sc_min_rssi = WI_PRISM_MIN_RSSI;
+ sc->sc_max_rssi = WI_PRISM_MAX_RSSI;
- buflen = sizeof(val);
- if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) &&
- wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) {
- sc->sc_dbm_adjust = le16toh(val);
+ buflen = sizeof(val);
+ if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) &&
+ wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0)
+ sc->sc_dbm_offset = le16toh(val);
+ else
+ sc->sc_dbm_offset = WI_PRISM_DBM_OFFSET;
}
/* Find default IBSS channel */
@@ -627,7 +636,7 @@
wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode);
if (sc->sc_flags & WI_FLAGS_HAS_MOR)
wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven);
- wi_write_txrate(sc);
+ wi_cfg_txrate(sc);
wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen);
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
@@ -638,6 +647,12 @@
wi_write_val(sc, WI_RID_DTIM_PERIOD, 1);
}
+ if (sc->sc_firmware_type == WI_INTERSIL &&
+ (ic->ic_opmode == IEEE80211_M_STA ||
+ ic->ic_opmode == IEEE80211_M_HOSTAP))
+ wi_write_val(sc, WI_RID_ALT_RETRY_COUNT,
+ IEEE80211_LADAPT_RETRY_LIMIT);
+
/*
* Initialize promisc mode.
* Being in the Host-AP mode causes a great
@@ -677,7 +692,8 @@
sc->sc_txd[i].d_len = 0;
}
}
- sc->sc_txcur = sc->sc_txnext = 0;
+ memset(sc->sc_txinfo, 0, sizeof(sc->sc_txinfo));
+ sc->sc_txcur = sc->sc_txnext = sc->sc_txinfonext = 0;
/* Enable desired port */
wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0);
@@ -865,6 +881,42 @@
}
frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
}
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_STA) {
+ struct ieee80211_txinfo *txi;
+
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ ni = &ic->ic_bss;
+ ni->ni_txrate = ieee80211_choose_rate(ic, wh,
+ m0->m_pkthdr.len);
+ wi_write_txrate(sc,
+ ni->ni_rates[ni->ni_txrate]);
+ } else {
+ ni = ieee80211_find_node(ic, wh->i_addr1);
+ if (ni == NULL) {
+ m_freem(m0);
+ ifp->if_oerrors++;
+ continue;
+ }
+ ni->ni_txrate = ieee80211_choose_rate(ic, wh,
+ m0->m_pkthdr.len);
+ frmhdr.wi_tx_rate =
+ (ni->ni_rates[ni->ni_txrate] &
+ IEEE80211_RATE_VAL) * 5;
+ }
+
+ frmhdr.wi_tx_idx = sc->sc_txinfonext;
+ sc->sc_txinfonext = (sc->sc_txinfonext + 1) % WI_NTXBUF;
+
+ txi = &sc->sc_txinfo[frmhdr.wi_tx_idx];
+
+ txi->i_rateidx = ni->ni_txrate;
+ txi->i_rssi = ni->ni_rssi;
+ txi->i_len = m0->m_pkthdr.len;
+
+ IEEE80211_ADDR_COPY(txi->i_dstaddr, wh->i_addr1);
+ frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY);
+ }
m_copydata(m0, 0, sizeof(struct ieee80211_frame),
(caddr_t)&frmhdr.wi_whdr);
m_adj(m0, sizeof(struct ieee80211_frame));
@@ -1285,8 +1337,8 @@
M_COPY_PKTHDR(&mb, m);
mb.m_data = (caddr_t)&frmhdr;
- frmhdr.wi_rx_signal -= sc->sc_dbm_adjust;
- frmhdr.wi_rx_silence -= sc->sc_dbm_adjust;
+ frmhdr.wi_rx_signal = WI_RSSI_TO_DBM(sc, frmhdr.wi_rx_signal);
+ frmhdr.wi_rx_silence = WI_RSSI_TO_DBM(sc, frmhdr.wi_rx_silence);
mb.m_len = (char *)&frmhdr.wi_whdr - (char *)&frmhdr;
mb.m_next = m;
mb.m_pkthdr.len += mb.m_len;
@@ -1317,42 +1369,54 @@
struct ifnet *ifp = &ic->ic_if;
struct wi_frame frmhdr;
int fid;
+ u_int16_t status;
fid = CSR_READ_2(sc, WI_TX_CMP_FID);
/* Read in the frame header */
- if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) {
- u_int16_t status = le16toh(frmhdr.wi_status);
+ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0) {
+ DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
+ goto out;
+ }
- /*
- * Spontaneous station disconnects appear as xmit
- * errors. Don't announce them and/or count them
- * as an output error.
- */
- if ((status & WI_TXSTAT_DISCONNECT) == 0) {
- if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
- curtxeps = 0;
- printf("%s: tx failed", sc->sc_dev.dv_xname);
- if (status & WI_TXSTAT_RET_ERR)
- printf(", retry limit exceeded");
- if (status & WI_TXSTAT_AGED_ERR)
- printf(", max transmit lifetime exceeded");
- if (status & WI_TXSTAT_DISCONNECT)
- printf(", port disconnected");
- if (status & WI_TXSTAT_FORM_ERR)
- printf(", invalid format (data len %u src %s)",
- le16toh(frmhdr.wi_dat_len),
- ether_sprintf(frmhdr.wi_ehdr.ether_shost));
- if (status & ~0xf)
- printf(", status=0x%x", status);
- printf("\n");
- }
- ifp->if_oerrors++;
- } else {
- DPRINTF(("port disconnected\n"));
- ifp->if_collisions++; /* XXX */
+ status = le16toh(frmhdr.wi_status);
+
+ /*
+ * Spontaneous station disconnects appear as xmit
+ * errors. Don't announce them and/or count them
+ * as an output error.
+ */
+ if ((status & WI_TXSTAT_DISCONNECT) != 0) {
+ DPRINTF(("port disconnected\n"));
+ ifp->if_collisions++; /* XXX */
+ goto out;
+ }
+ if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) {
+ curtxeps = 0;
+ printf("%s: tx failed", sc->sc_dev.dv_xname);
+ if (status & WI_TXSTAT_RET_ERR)
+ printf(", retry limit exceeded");
+ if (status & WI_TXSTAT_AGED_ERR)
+ printf(", max transmit lifetime exceeded");
+ if (status & WI_TXSTAT_DISCONNECT)
+ printf(", port disconnected");
+ if (status & WI_TXSTAT_FORM_ERR)
+ printf(", invalid format (data len %u src %s)",
+ le16toh(frmhdr.wi_dat_len),
+ ether_sprintf(frmhdr.wi_ehdr.ether_shost));
+ if (status & ~0xf)
+ printf(", status=0x%x", status);
+ printf("\n");
+ }
+ ifp->if_oerrors++;
+ if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_STA) &&
+ (status & WI_TXSTAT_RET_ERR) != 0) {
+ if (frmhdr.wi_tx_idx >= WI_NTXBUF) {
+ goto out;
}
- } else
- DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid));
+ ieee80211_lower_rate(ic, &sc->sc_txinfo[frmhdr.wi_tx_idx]);
+ }
+out:
CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC);
}
@@ -1648,7 +1712,7 @@
&len);
break;
}
- wreq.wi_val[0] = htole16(sc->sc_dbm_adjust);
+ wreq.wi_val[0] = htole16(sc->sc_dbm_offset);
len = sizeof(u_int16_t);
break;
@@ -1847,7 +1911,7 @@
ic->ic_fixed_rate = i;
}
if (sc->sc_enabled)
- error = wi_write_txrate(sc);
+ error = wi_cfg_txrate(sc);
break;
case WI_RID_SCAN_APS:
@@ -1887,24 +1951,45 @@
}
static int
-wi_write_txrate(struct wi_softc *sc)
+wi_cfg_txrate(struct wi_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
- int i;
- u_int16_t rate;
+ int rate;
+
+ sc->sc_tx_rate = 0; /* force write to RID */
if (ic->ic_fixed_rate < 0)
rate = 0; /* auto */
else
- rate = (ic->ic_sup_rates[ic->ic_fixed_rate] &
- IEEE80211_RATE_VAL) / 2;
+ rate = ic->ic_sup_rates[ic->ic_fixed_rate];
- /* rate: 0, 1, 2, 5, 11 */
+ return wi_write_txrate(sc, rate);
+}
+/* Rate is 0 for hardware auto-select, otherwise rate is
+ * 2, 4, 11, or 22 (units of 500Kbps).
+ */
+static int
+wi_write_txrate(struct wi_softc *sc, int rate)
+{
+ u_int16_t hwrate;
+ int i;
+
+ rate = (rate & IEEE80211_RATE_VAL) / 2;
+
+ /* rate: 0, 1, 2, 5, 11 */
switch (sc->sc_firmware_type) {
case WI_LUCENT:
- if (rate == 0)
- rate = 3; /* auto */
+ switch (rate) {
+ case 0:
+ hwrate = 3; /* auto */
+ case 5:
+ hwrate = 4;
+ case 11:
+ hwrate = 5;
+ default:
+ hwrate = rate;
+ }
break;
default:
/* Choose a bit according to this table.
@@ -1921,12 +2006,18 @@
break;
}
if (i == 0)
- rate = 0xf; /* auto */
+ hwrate = 0xf; /* auto */
else
- rate = i;
+ hwrate = i;
break;
}
- return wi_write_val(sc, WI_RID_TX_RATE, rate);
+
+ if (sc->sc_tx_rate == hwrate)
+ return 0;
+
+ sc->sc_tx_rate = hwrate;
+
+ return wi_write_val(sc, WI_RID_TX_RATE, sc->sc_tx_rate);
}
static int
@@ -2331,9 +2422,11 @@
ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/
memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
}
+ ieee80211_raise_rate((void*)ic);
break;
case IEEE80211_S_SCAN:
+ ieee80211_raise_rate((void*)ic);
case IEEE80211_S_AUTH:
case IEEE80211_S_ASSOC:
break;
Index: sys/dev/ic/wi_ieee.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wi_ieee.h,v
retrieving revision 1.20
diff -u -r1.20 wi_ieee.h
--- sys/dev/ic/wi_ieee.h 2003/04/08 04:31:25 1.20
+++ sys/dev/ic/wi_ieee.h 2003/06/27 02:19:49
@@ -236,6 +236,7 @@
#define WI_RID_WEP_MAPTABLE 0xFC29
#define WI_RID_CNFAUTHMODE 0xFC2A
#define WI_RID_ROAMING_MODE 0xFC2D
+#define WI_RID_ALT_RETRY_COUNT 0xFC32 /* retry count if WI_TXCNTL_ALTRTRY */
#define WI_RID_OWN_BEACON_INT 0xFC33 /* beacon xmit time for BSS creation */
#define WI_RID_SET_TIM 0xFC40
#define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */
Index: sys/dev/ic/wireg.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wireg.h,v
retrieving revision 1.45
diff -u -r1.45 wireg.h
--- sys/dev/ic/wireg.h 2003/05/13 08:35:58 1.45
+++ sys/dev/ic/wireg.h 2003/06/27 02:19:49
@@ -544,6 +544,12 @@
struct ether_header wi_ehdr; /* 0x2e */
} __attribute__((__packed__));
+/* Software support fields are returned untouched by TxOK, TxExc events. */
+#define wi_tx_swsup0 wi_rx_silence
+#define wi_tx_swsup1 wi_rx_signal
+#define wi_tx_swsup2 wi_rx_rate
+#define wi_tx_idx wi_rx_flow
+
/* Tx Status Field */
#define WI_TXSTAT_RET_ERR 0x0001
#define WI_TXSTAT_AGED_ERR 0x0002
Index: sys/dev/ic/wivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wivar.h,v
retrieving revision 1.34
diff -u -r1.34 wivar.h
--- sys/dev/ic/wivar.h 2003/05/20 01:29:35 1.34
+++ sys/dev/ic/wivar.h 2003/06/27 02:19:49
@@ -67,7 +67,10 @@
u_int16_t sc_portnum;
- u_int16_t sc_dbm_adjust;
+ /* RSSI interpretation */
+ u_int16_t sc_min_rssi; /* clamp sc_min_rssi < RSSI */
+ u_int16_t sc_max_rssi; /* clamp RSSI < sc_max_rssi */
+ u_int16_t sc_dbm_offset; /* dBm ~ RSSI - sc_dbm_offset */
u_int16_t sc_max_datalen;
u_int16_t sc_frag_thresh;
u_int16_t sc_rts_thresh;
@@ -88,6 +91,8 @@
} sc_txd[WI_NTXBUF];
int sc_txnext;
int sc_txcur;
+ struct ieee80211_txinfo sc_txinfo[WI_NTXBUF];
+ int sc_txinfonext;
int sc_tx_timer;
int sc_scan_timer;
int sc_syn_timer;
@@ -107,6 +112,17 @@
/* maximum consecutive false change-of-BSSID indications */
#define WI_MAX_FALSE_SYNS 10
+
+#define WI_PRISM_MIN_RSSI 0x1b
+#define WI_PRISM_MAX_RSSI 0x9a
+#define WI_PRISM_DBM_OFFSET 100 /* XXX */
+
+#define WI_LUCENT_MIN_RSSI 47
+#define WI_LUCENT_MAX_RSSI 138
+#define WI_LUCENT_DBM_OFFSET 149
+
+#define WI_RSSI_TO_DBM(sc, rssi) (MIN((sc)->sc_max_rssi, \
+ MAX((sc)->sc_min_rssi, (rssi))) - (sc)->sc_dbm_offset)
#define WI_SCAN_INQWAIT 3 /* wait sec before inquire */
#define WI_SCAN_WAIT 5 /* maximum scan wait */
--PNTmBPCT7hxwcZjr--