** This patch only applies on HEAD ** Port rssadapt for wi from NetBSD, along with a couple of other fixes: UNKN: 1.142, 1.146? TODO: 1.114, 1.127, 1.138?, 1.140/1.141(pwrsave), DONE: 1.135, 1.139, 1.145, 1.147 --- sys/conf/files.orig Thu Jul 21 15:36:22 2005 +++ sys/conf/files Mon Jul 25 15:11:28 2005 @@ -1405,6 +1405,7 @@ net80211/ieee80211_node.c optional wlan net80211/ieee80211_output.c optional wlan net80211/ieee80211_proto.c optional wlan net80211/ieee80211_xauth.c optional wlan_xauth +net80211/ieee80211_rssadapt.c optional wlan netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk --- sys/dev/wi/if_wavelan_ieee.h.orig Mon Jan 10 17:13:48 2005 +++ sys/dev/wi/if_wavelan_ieee.h Mon Jul 25 15:11:28 2005 @@ -237,7 +237,9 @@ struct wi_counters { #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_CNF_DBM_ADJUST 0xFC46 #define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */ #define WI_RID_BASIC_RATE 0xFCB3 --- sys/dev/wi/if_wi.c.orig Sun Jul 24 13:29:07 2005 +++ sys/dev/wi/if_wi.c Mon Jul 25 16:00:17 2005 @@ -101,6 +101,7 @@ __FBSDID("$FreeBSD: src/sys/dev/wi/if_wi #include #include #include +#include #include #include @@ -122,13 +123,15 @@ static int wi_media_change(struct ifnet static void wi_media_status(struct ifnet *, struct ifmediareq *); static void wi_rx_intr(struct wi_softc *); +static void wi_txalloc_intr(struct wi_softc *); static void wi_tx_intr(struct wi_softc *); static void wi_tx_ex_intr(struct wi_softc *); static void wi_info_intr(struct wi_softc *); 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 *); @@ -144,6 +147,7 @@ static int wi_read_rid(struct wi_softc static int wi_write_rid(struct wi_softc *, int, void *, int); static int wi_newstate(struct ieee80211com *, enum ieee80211_state, int); +static void wi_set_tim(struct ieee80211_node *, int); static int wi_scan_ap(struct wi_softc *, u_int16_t, u_int16_t); static void wi_scan_result(struct wi_softc *, int, int); @@ -153,6 +157,19 @@ static void wi_dump_pkt(struct wi_frame static int wi_get_debug(struct wi_softc *, struct wi_req *); static int wi_set_debug(struct wi_softc *, struct wi_req *); +static struct ieee80211_node *wi_node_alloc(struct ieee80211_node_table *); +static void wi_node_free(struct ieee80211_node *); + +static void wi_raise_rate(struct ieee80211com *, struct ieee80211_rssdesc *); +static void wi_lower_rate(struct ieee80211com *, struct ieee80211_rssdesc *); +static int wi_choose_rate(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_frame *, u_int); +static void wi_rssadapt_updatestats_cb(void *, struct ieee80211_node *); +static void wi_rssadapt_updatestats(void *); +static void wi_rssdescs_init(struct wi_rssdesc (*)[], wi_rssdescq_t *); +static void wi_rssdescs_reset(struct ieee80211com *, struct wi_rssdesc (*)[], + wi_rssdescq_t *, u_int8_t (*)[]); + #if __FreeBSD_version >= 500000 /* support to download firmware for symbol CF card */ static int wi_symbol_write_firm(struct wi_softc *, const void *, int, @@ -192,7 +209,8 @@ SYSCTL_INT(_hw_wi, OID_AUTO, debug, CTLF #define IFF_DUMPPKTS(_ifp) 0 #endif -#define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO) +#define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO | \ + WI_EV_TX | WI_EV_TX_EXC) struct wi_card_ident wi_card_ident[] = { /* CARD_ID CARD_NAME FIRM_TYPE */ @@ -318,9 +336,7 @@ wi_attach(device_t dev) ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_STA; ic->ic_state = IEEE80211_S_INIT; - ic->ic_caps = IEEE80211_C_PMGT - | IEEE80211_C_WEP /* everyone supports WEP */ - ; + ic->ic_caps = IEEE80211_C_AHDEMO; ic->ic_max_aid = WI_MAX_AID; /* @@ -362,6 +378,8 @@ wi_attach(device_t dev) ic->ic_ibss_chan = &ic->ic_channels[0]; } + sc->sc_flags |= WI_FLAGS_RSSADAPTSTA; + /* * Set flags based on firmware version. */ @@ -381,6 +399,7 @@ wi_attach(device_t dev) ic->ic_caps |= IEEE80211_C_IBSS; ic->ic_caps |= IEEE80211_C_MONITOR; } + ic->ic_caps |= IEEE80211_C_PMGT; sc->sc_ibss_port = htole16(1); sc->sc_min_rssi = WI_LUCENT_MIN_RSSI; @@ -411,7 +430,9 @@ wi_attach(device_t dev) */ if (sc->sc_sta_firmware_ver >= 803) ic->ic_caps |= IEEE80211_C_HOSTAP; + ic->ic_caps |= IEEE80211_C_PMGT; sc->sc_ibss_port = htole16(0); + sc->sc_alt_retry = 2; sc->sc_min_rssi = WI_PRISM_MIN_RSSI; sc->sc_max_rssi = WI_PRISM_MAX_RSSI; @@ -466,6 +487,8 @@ wi_attach(device_t dev) sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN; sc->sc_roaming_mode = 1; + callout_init(&sc->sc_rssadapt_ch, 0); + sc->sc_portnum = WI_DEFAULT_PORT; sc->sc_authtype = WI_DEFAULT_AUTHTYPE; @@ -483,7 +506,13 @@ wi_attach(device_t dev) ieee80211_ifattach(ic); /* override state transition method */ sc->sc_newstate = ic->ic_newstate; + sc->sc_set_tim = ic->ic_set_tim; + sc->sc_node_free = ic->ic_node_free; ic->ic_newstate = wi_newstate; + ic->ic_node_alloc = wi_node_alloc; + ic->ic_node_free = wi_node_free; + ic->ic_set_tim = wi_set_tim; + ieee80211_media_init(ic, wi_media_change, wi_media_status); #if NBPFILTER > 0 @@ -619,19 +648,28 @@ wi_intr(void *arg) CSR_WRITE_2(sc, WI_INT_EN, 0); status = CSR_READ_2(sc, WI_EVENT_STAT); + sc->sc_status = status; + if (status & WI_EV_RX) wi_rx_intr(sc); if (status & WI_EV_ALLOC) + wi_txalloc_intr(sc); + if (status & WI_EV_TX) wi_tx_intr(sc); if (status & WI_EV_TX_EXC) wi_tx_ex_intr(sc); if (status & WI_EV_INFO) wi_info_intr(sc); + + CSR_WRITE_2(sc, WI_EVENT_ACK, sc->sc_status); + if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0 && (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0 && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) wi_start(ifp); + sc->sc_status = 0; + /* Re-enable interrupts. */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); @@ -640,6 +678,39 @@ wi_intr(void *arg) return; } +#define arraylen(a) (sizeof(a) / sizeof((a)[0])) + +static void +wi_rssdescs_init(struct wi_rssdesc (*rssd)[WI_NTXRSS], wi_rssdescq_t *rssdfree) +{ + int i; + + SLIST_INIT(rssdfree); + for (i = 0; i < arraylen(*rssd); i++) + SLIST_INSERT_HEAD(rssdfree, &(*rssd)[i], rd_next); +} + +static void +wi_rssdescs_reset(struct ieee80211com *ic, struct wi_rssdesc (*rssd)[WI_NTXRSS], + wi_rssdescq_t *rssdfree, u_int8_t (*txpending)[IEEE80211_RATE_MAXSIZE]) +{ + struct ieee80211_node *ni; + int i; + + for (i = 0; i < arraylen(*rssd); i++) { + ni = (*rssd)[i].rd_desc.id_node; + (*rssd)[i].rd_desc.id_node = NULL; + if (ni != NULL && (ic->ic_ifp->if_flags & IFF_DEBUG) != 0) + printf("%s: cleaning outstanding rssadapt " + "descriptor for %s\n", ic->ic_ifp->if_xname, + ether_sprintf(ni->ni_macaddr)); + if (ni != NULL) + ieee80211_free_node(ni); + } + memset(*txpending, 0, sizeof(*txpending)); + wi_rssdescs_init(rssd, rssdfree); +} + void wi_init(void *arg) { @@ -732,21 +803,51 @@ wi_init(void *arg) 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 && sc->sc_firmware_type == WI_INTERSIL) { wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_bintval); - wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */ - wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */ wi_write_val(sc, WI_RID_DTIM_PERIOD, 1); } + if (sc->sc_firmware_type == WI_INTERSIL) { + struct ieee80211_rateset *rs = + &ic->ic_sup_rates[IEEE80211_MODE_11B]; + u_int16_t basic = 0, supported = 0, rate; + + for (i = 0; i < rs->rs_nrates; i++) { + switch (rs->rs_rates[i] & IEEE80211_RATE_VAL) { + case 2: + rate = 1; + break; + case 4: + rate = 2; + break; + case 11: + rate = 4; + break; + case 22: + rate = 8; + break; + default: + rate = 0; + break; + } + if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) + basic |= rate; + supported |= rate; + } + wi_write_val(sc, WI_RID_BASIC_RATE, basic); + wi_write_val(sc, WI_RID_SUPPORT_RATE, supported); + wi_write_val(sc, WI_RID_ALT_RETRY_COUNT, sc->sc_alt_retry); + } + /* * Initialize promisc mode. - * Being in the Host-AP mode causes a great - * deal of pain if primisc mode is set. + * Being in Host-AP mode causes a great + * deal of pain if promiscuous mode is set. * Therefore we avoid confusing the firmware * and always reset promisc mode in Host-AP * mode. Host-AP sees all the packets anyway. @@ -786,6 +887,8 @@ wi_init(void *arg) } sc->sc_txcur = sc->sc_txnext = 0; + wi_rssdescs_init(&sc->sc_rssd, &sc->sc_rssdfree); + /* Enable desired port */ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0); @@ -858,7 +961,10 @@ wi_stop(struct ifnet *ifp, int disable) sc->sc_enabled = 0; } } else if (sc->wi_gone && disable) /* gone --> not enabled */ - sc->sc_enabled = 0; + sc->sc_enabled = 0; + + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); sc->sc_tx_timer = 0; sc->sc_scan_timer = 0; @@ -870,6 +976,85 @@ wi_stop(struct ifnet *ifp, int disable) WI_UNLOCK(sc); } +/* + * Choose a data rate for a packet len bytes long that suits the packet + * type and the wireless conditions. + * + * TBD Adapt fragmentation threshold. + */ +static int +wi_choose_rate(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_frame *wh, u_int len) +{ + struct wi_softc *sc = ic->ic_ifp->if_softc; + struct wi_node *wn = (void *)ni; + struct ieee80211_rssadapt *ra = &wn->wn_rssadapt; + int do_not_adapt, i, rateidx; + WI_LOCK_DECL(); + + WI_LOCK(sc); + + do_not_adapt = (ic->ic_opmode != IEEE80211_M_HOSTAP) && + (sc->sc_flags & WI_FLAGS_RSSADAPTSTA) == 0; + + rateidx = ieee80211_rssadapt_choose(ra, &ni->ni_rates, wh, len, + ic->ic_fixed_rate, + ((ic->ic_ifp->if_flags & IFF_DEBUG) == 0) ? NULL : + ic->ic_ifp->if_xname, do_not_adapt); + + ni->ni_txrate = rateidx; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP) { + /* + * choose the slowest pending rate so that we don't + * accidentally send a packet on the MAC's queue + * too fast. TBD find out if the MAC labels Tx + * packets w/ rate when enqueued or dequeued. + */ + for (i = 0; i < rateidx && sc->sc_txpending[i] == 0; i++) + ; + rateidx = i; + } + + WI_UNLOCK(sc); + return (rateidx); +} + +/* XXX: locking? */ +static void +wi_raise_rate(struct ieee80211com *ic, struct ieee80211_rssdesc *id) +{ + struct wi_node *wn; + + if (id->id_node == NULL) + return; + + wn = (void *)id->id_node; + ieee80211_rssadapt_raise_rate(ic, &wn->wn_rssadapt, id); +} + +static void +wi_lower_rate(struct ieee80211com *ic, struct ieee80211_rssdesc *id) +{ + struct ieee80211_node *ni; + struct wi_node *wn; + struct wi_softc *sc = ic->ic_ifp->if_softc; + WI_LOCK_DECL() + + WI_LOCK(sc); + + if ((ni = id->id_node) == NULL) { + DPRINTF(("wi_lower_rate: missing node\n")); + goto out; + } + + wn = (void *)ni; + + ieee80211_rssadapt_lower_rate(ic, ni, &wn->wn_rssadapt, id); +out: + WI_UNLOCK(sc); +} + static void wi_start(struct ifnet *ifp) { @@ -877,10 +1062,13 @@ wi_start(struct ifnet *ifp) struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct ieee80211_frame *wh; + struct ieee80211_rateset *rs; struct ether_header *eh; + struct wi_rssdesc *rd; + struct ieee80211_rssdesc *id; struct mbuf *m0; struct wi_frame frmhdr; - int cur, fid, off, error; + int cur, fid, off, error, rateidx; WI_LOCK_DECL(); WI_LOCK(sc); @@ -897,6 +1085,10 @@ wi_start(struct ifnet *ifp) memset(&frmhdr, 0, sizeof(frmhdr)); cur = sc->sc_txnext; for (;;) { + if (SLIST_EMPTY(&sc->sc_rssdfree)) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } IF_POLL(&ic->ic_mgtq, m0); if (m0 != NULL) { if (sc->sc_txd[cur].d_len != 0) { @@ -919,7 +1111,7 @@ wi_start(struct ifnet *ifp) m_copydata(m0, 4, ETHER_ADDR_LEN * 2, (caddr_t)&frmhdr.wi_ehdr); frmhdr.wi_ehdr.ether_type = 0; - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m0, struct ieee80211_frame *); } else { if (ic->ic_state != IEEE80211_S_RUN) break; @@ -955,13 +1147,16 @@ wi_start(struct ifnet *ifp) ieee80211_free_node(ni); continue; } - wh = mtod(m0, struct ieee80211_frame *); + wh = mtod(m0, struct ieee80211_frame *); } #if NBPFILTER > 0 if (ic->ic_rawbpf) bpf_mtap(ic->ic_rawbpf, m0); #endif - frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); + frmhdr.wi_tx_ctl = + htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX|WI_TXCNTL_TX_OK); + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_ALTRTRY); /* XXX check key for SWCRYPT instead of using operating mode */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && (wh->i_fc[1] & IEEE80211_FC1_WEP)) { @@ -976,14 +1171,37 @@ wi_start(struct ifnet *ifp) } frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); } + + rateidx = wi_choose_rate(ic, ni, wh, m0->m_pkthdr.len); + rs = &ni->ni_rates; + #if NBPFILTER > 0 if (sc->sc_drvbpf) { - sc->sc_tx_th.wt_rate = - ni->ni_rates.rs_rates[ni->ni_txrate]; + struct wi_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_rate = rs->rs_rates[rateidx]; + tap->wt_chan_freq = + htole16(ic->ic_bss->ni_chan->ic_freq); + tap->wt_chan_flags = + htole16(ic->ic_bss->ni_chan->ic_flags); + bpf_mtap2(sc->sc_drvbpf, &sc->sc_tx_th, sc->sc_tx_th_len, m0); } #endif + rd = SLIST_FIRST(&sc->sc_rssdfree); + id = &rd->rd_desc; + id->id_len = m0->m_pkthdr.len; + id->id_rateidx = ni->ni_txrate; + id->id_rssi = ni->ni_rssi; + + frmhdr.wi_tx_idx = rd - sc->sc_rssd; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + frmhdr.wi_tx_rate = 5 * (rs->rs_rates[rateidx] & + IEEE80211_RATE_VAL); + else if (sc->sc_flags & WI_FLAGS_RSSADAPTSTA) + (void)wi_write_txrate(sc, rs->rs_rates[rateidx]); m_copydata(m0, 0, sizeof(struct ieee80211_frame), (caddr_t)&frmhdr.wi_whdr); m_adj(m0, sizeof(struct ieee80211_frame)); @@ -995,8 +1213,6 @@ wi_start(struct ifnet *ifp) error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0 || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0; m_freem(m0); - if (ni != NULL) - ieee80211_free_node(ni); if (error) { ifp->if_oerrors++; continue; @@ -1012,6 +1228,8 @@ wi_start(struct ifnet *ifp) ifp->if_timer = 1; } sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; + SLIST_REMOVE_HEAD(&sc->sc_rssdfree, rd_next); + id->id_node = ni; } WI_UNLOCK(sc); @@ -1025,7 +1243,7 @@ wi_reset(struct wi_softc *sc) int i; int error = 0; int tries; - + /* Symbol firmware cannot be initialized more than once */ if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_reset) return (0); @@ -1304,6 +1522,30 @@ wi_media_status(struct ifnet *ifp, struc } } +static struct ieee80211_node * +wi_node_alloc(struct ieee80211_node_table *nt) +{ + struct wi_node *wn; + + wn = malloc(sizeof(struct wi_node), M_80211_NODE, M_NOWAIT | M_ZERO); + + return (wn != NULL) ? &wn->wn_node : NULL; +} + +static void +wi_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_softc *sc = ic->ic_ifp->if_softc; + int i; + + for (i = 0; i < WI_NTXRSS; i++) { + if (sc->sc_rssd[i].rd_desc.id_node == ni) + sc->sc_rssd[i].rd_desc.id_node = NULL; + } + sc->sc_node_free(ni); +} + static void wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) { @@ -1419,6 +1661,21 @@ done: m_freem(m); } +static __inline void +wi_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_frame *wh, int rssi) +{ + struct wi_node *wn; + + if (ni == NULL) { + printf("%s: null node", __func__); + return; + } + + wn = (void *)ni; + ieee80211_rssadapt_input(ic, ni, &wn->wn_rssadapt, rssi); +} + static void wi_rx_intr(struct wi_softc *sc) { @@ -1441,13 +1698,11 @@ wi_rx_intr(struct wi_softc *sc) * read the data from the device. */ wi_rx_monitor(sc, fid); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); return; } /* First read in the frame header */ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: read fid %x failed\n", fid)); return; @@ -1461,7 +1716,6 @@ wi_rx_intr(struct wi_softc *sc) */ status = le16toh(frmhdr.wi_status); if (status & WI_STAT_ERRSTAT) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status)); return; @@ -1479,7 +1733,6 @@ wi_rx_intr(struct wi_softc *sc) */ if (off + len > MCLBYTES) { if (ic->ic_opmode != IEEE80211_M_MONITOR) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: oversized packet\n")); return; @@ -1489,7 +1742,6 @@ wi_rx_intr(struct wi_softc *sc) MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: MGET failed\n")); return; @@ -1497,7 +1749,6 @@ wi_rx_intr(struct wi_softc *sc) if (off + len > MHLEN) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); m_freem(m); ifp->if_ierrors++; DPRINTF(("wi_rx_intr: MCLGET failed\n")); @@ -1512,8 +1763,6 @@ wi_rx_intr(struct wi_softc *sc) m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len; m->m_pkthdr.rcvif = ifp; - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); - wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_WEP) { /* @@ -1555,6 +1804,9 @@ wi_rx_intr(struct wi_softc *sc) * Send frame up for processing. */ ieee80211_input(ic, m, ni, rssi, rstamp); + + wi_rssadapt_input(ic, ni, wh, rssi); + /* * The frame may have caused the node to be marked for * reclamation (e.g. in response to a DEAUTH message) @@ -1566,63 +1818,96 @@ wi_rx_intr(struct wi_softc *sc) static void wi_tx_ex_intr(struct wi_softc *sc) { + struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni = NULL; /* to silence gcc */ struct wi_frame frmhdr; + struct ieee80211_rssdesc *id; + struct wi_rssdesc *rssd = NULL; /* to silence gcc */ 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) { + printf("%s: %s read fid %x failed\n", ic->ic_ifp->if_xname, + __func__, fid); + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); + 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)) { - if_printf(ifp, "tx failed"); - 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 %6D)", - le16toh(frmhdr.wi_dat_len), - 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 */ - } - } else - DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid)); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); + if (frmhdr.wi_tx_idx >= WI_NTXRSS) { + printf("%s: %s bad idx %02x\n", + ic->ic_ifp->if_xname, __func__, frmhdr.wi_tx_idx); + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); + goto out; + } + + 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 (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) { + printf("%s: tx failed", ic->ic_ifp->if_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++; + rssd = &sc->sc_rssd[frmhdr.wi_tx_idx]; + id = &rssd->rd_desc; + if ((status & WI_TXSTAT_RET_ERR) != 0) + wi_lower_rate(ic, id); + + ni = id->id_node; + id->id_node = NULL; + + if (ni == NULL) { + printf("%s: %s null node, rssdesc %02x\n", + ic->ic_ifp->if_xname, __func__, frmhdr.wi_tx_idx); + goto out; + } + + if (sc->sc_txpending[id->id_rateidx]-- == 0) { + printf("%s: %s txpending[%i] wraparound\n", + ic->ic_ifp->if_xname, __func__, id->id_rateidx); + sc->sc_txpending[id->id_rateidx] = 0; + } + if (ni != NULL) + ieee80211_free_node(ni); + SLIST_INSERT_HEAD(&sc->sc_rssdfree, rssd, rd_next); +out: + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } static void -wi_tx_intr(struct wi_softc *sc) +wi_txalloc_intr(struct wi_softc *sc) { + struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = sc->sc_ifp; int fid, cur; - if (sc->wi_gone) - return; - fid = CSR_READ_2(sc, WI_ALLOC_FID); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); - cur = sc->sc_txcur; if (sc->sc_txd[cur].d_fid != fid) { - if_printf(ifp, "bad alloc %x != %x, cur %d nxt %d\n", - fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext); + printf("%s: bad alloc %x != %x, cur %d nxt %d\n", + ic->ic_ifp->if_xname, fid, sc->sc_txd[cur].d_fid, cur, + sc->sc_txnext); return; } sc->sc_tx_timer = 0; @@ -1633,7 +1918,7 @@ wi_tx_intr(struct wi_softc *sc) else { if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid, 0, 0)) { - if_printf(ifp, "xmit failed\n"); + printf("%s: xmit failed\n", ic->ic_ifp->if_xname); sc->sc_txd[cur].d_len = 0; } else { sc->sc_tx_timer = 5; @@ -1643,6 +1928,61 @@ wi_tx_intr(struct wi_softc *sc) } static void +wi_tx_intr(struct wi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + struct ieee80211_rssdesc *id; + struct wi_rssdesc *rssd; + struct wi_frame frmhdr; + int fid; + + fid = CSR_READ_2(sc, WI_TX_CMP_FID); + /* Read in the frame header */ + if (wi_read_bap(sc, fid, offsetof(struct wi_frame, wi_tx_swsup2), + &frmhdr.wi_tx_swsup2, 2) != 0) { + printf("%s: %s read fid %x failed\n", ic->ic_ifp->if_xname, + __func__, fid); + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); + goto out; + } + + if (frmhdr.wi_tx_idx >= WI_NTXRSS) { + printf("%s: %s bad idx %02x\n", + ic->ic_ifp->if_xname, __func__, frmhdr.wi_tx_idx); + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); + goto out; + } + + rssd = &sc->sc_rssd[frmhdr.wi_tx_idx]; + id = &rssd->rd_desc; + wi_raise_rate(ic, id); + + ni = id->id_node; + id->id_node = NULL; + + if (ni == NULL) { + printf("%s: %s null node, rssdesc %02x\n", + ic->ic_ifp->if_xname, __func__, frmhdr.wi_tx_idx); + goto out; + } + + if (sc->sc_txpending[id->id_rateidx]-- == 0) { + printf("%s: %s txpending[%i] wraparound\n", + ic->ic_ifp->if_xname, __func__, id->id_rateidx); + sc->sc_txpending[id->id_rateidx] = 0; + } + if (ni != NULL) + ieee80211_free_node(ni); + SLIST_INSERT_HEAD(&sc->sc_rssdfree, rssd, rd_next); +out: + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +} + +static void wi_info_intr(struct wi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; @@ -1720,7 +2060,6 @@ wi_info_intr(struct wi_softc *sc) le16toh(ltbuf[1]), le16toh(ltbuf[0]))); break; } - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); } static int @@ -2182,7 +2521,7 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd ic->ic_fixed_rate = i; } if (sc->sc_enabled) - error = wi_write_txrate(sc); + error = wi_cfg_txrate(sc); WI_UNLOCK(sc); break; @@ -2265,59 +2604,86 @@ wi_set_cfg(struct ifnet *ifp, u_long cmd return error; } +/* + * 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) +wi_write_txrate(struct wi_softc *sc, int rate) { - struct ieee80211com *ic = &sc->sc_ic; - int i; - u_int16_t rate; - - if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) - rate = 0; /* auto */ - else - rate = (ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[ic->ic_fixed_rate] & - IEEE80211_RATE_VAL) / 2; - - /* rate: 0, 1, 2, 5, 11 */ + u_int16_t hwrate; + /* rate: 0, 2, 4, 11, 22 */ switch (sc->sc_firmware_type) { case WI_LUCENT: - switch (rate) { - case 0: /* auto == 11mbps auto */ - rate = 3; - break; - /* case 1, 2 map to 1, 2*/ - case 5: /* 5.5Mbps -> 4 */ - rate = 4; + switch (rate & IEEE80211_RATE_VAL) { + case 2: + hwrate = 1; break; - case 11: /* 11mbps -> 5 */ - rate = 5; + case 4: + hwrate = 2; break; default: + hwrate = 3; /* auto */ + break; + case 11: + hwrate = 4; + break; + case 22: + hwrate = 5; break; } break; default: - /* Choose a bit according to this table. - * - * bit | data rate - * ----+------------------- - * 0 | 1Mbps - * 1 | 2Mbps - * 2 | 5.5Mbps - * 3 | 11Mbps - */ - for (i = 8; i > 0; i >>= 1) { - if (rate >= i) - break; + switch (rate & IEEE80211_RATE_VAL) { + case 2: + hwrate = 1; + break; + case 4: + hwrate = 2; + break; + case 11: + hwrate = 4; + break; + case 22: + hwrate = 8; + break; + default: + hwrate = 15; /* auto */ + break; } - if (i == 0) - rate = 0xf; /* auto */ - else - rate = i; break; } - return wi_write_val(sc, WI_RID_TX_RATE, rate); + + if (sc->sc_tx_rate == hwrate) + return (0); + + if (sc->sc_ic.ic_ifp->if_flags & IFF_DEBUG) + printf("%s: tx rate %d -> %d (%d)\n", __func__, sc->sc_tx_rate, + hwrate, rate); + + sc->sc_tx_rate = hwrate; + + return wi_write_val(sc, WI_RID_TX_RATE, sc->sc_tx_rate); +} + +static int +wi_cfg_txrate(struct wi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_rateset *rs; + int rate; + + rs = &ic->ic_sup_rates[IEEE80211_MODE_11B]; + + sc->sc_tx_rate = 0; /* force write to RID */ + + if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) + rate = 0; /* auto */ + else + rate = rs->rs_rates[ic->ic_fixed_rate]; + + return wi_write_txrate(sc, rate); } static int @@ -2361,7 +2727,7 @@ wi_write_wep(struct wi_softc *sc) * If promiscuous mode disable, Prism2 chip * does not work with WEP . * It is under investigation for details. - * (ichiro@netbsd.org) + * (ichiro@NetBSD.org) */ if (sc->sc_firmware_type == WI_INTERSIL && sc->sc_sta_firmware_ver < 802 ) { @@ -2423,7 +2789,7 @@ wi_cmd(struct wi_softc *sc, int cmd, int { int i, s = 0; static volatile int count = 0; - + if (sc->wi_gone) return (ENODEV); @@ -2659,7 +3025,7 @@ wi_read_rid(struct wi_softc *sc, int rid rid, le16toh(ltbuf[1])); return EIO; } - len = (le16toh(ltbuf[0]) - 1) * 2; /* already got rid */ + len = (le16toh(ltbuf[0]) - 1) * 2; /* already got rid */ if (*buflenp < len) { device_printf(sc->sc_dev, "record buffer is too small, " "rid=%x, size=%d, len=%d\n", @@ -2676,7 +3042,7 @@ wi_write_rid(struct wi_softc *sc, int ri int error; u_int16_t ltbuf[2]; - ltbuf[0] = htole16((buflen + 1) / 2 + 1); /* includes rid */ + ltbuf[0] = htole16((buflen + 1) / 2 + 1); /* includes rid */ ltbuf[1] = htole16(rid); error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); @@ -2689,6 +3055,27 @@ wi_write_rid(struct wi_softc *sc, int ri return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0); } +static void +wi_rssadapt_updatestats_cb(void *arg, struct ieee80211_node *ni) +{ + struct wi_node *wn = (void *)ni; + + ieee80211_rssadapt_updatestats(&wn->wn_rssadapt); +} + +static void +wi_rssadapt_updatestats(void *arg) +{ + struct wi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + + ieee80211_iterate_nodes(&ic->ic_scan, wi_rssadapt_updatestats_cb, arg); + if (ic->ic_opmode != IEEE80211_M_MONITOR && + ic->ic_state == IEEE80211_S_RUN) + callout_reset(&sc->sc_rssadapt_ch, hz / 10, + wi_rssadapt_updatestats, arg); +} + static int wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { @@ -2712,6 +3099,9 @@ wi_newstate(struct ieee80211com *ic, enu */ switch (nstate) { case IEEE80211_S_INIT: + if (ic->ic_opmode != IEEE80211_M_MONITOR) + callout_stop(&sc->sc_rssadapt_ch); + ic->ic_flags &= ~IEEE80211_F_SIBSS; sc->sc_flags &= ~WI_FLAGS_OUTRANGE; return (*sc->sc_newstate)(ic, nstate, arg); @@ -2759,12 +3149,33 @@ wi_newstate(struct ieee80211com *ic, enu if (ni->ni_esslen > IEEE80211_NWID_LEN) ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/ memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen); + ni->ni_rates = ic->ic_sup_rates[ + ieee80211_chan2mode(ic, ni->ni_chan)]; /*XXX*/ } + if (ic->ic_opmode != IEEE80211_M_MONITOR) + callout_reset(&sc->sc_rssadapt_ch, hz / 10, + wi_rssadapt_updatestats, sc); return (*sc->sc_newstate)(ic, nstate, arg); } return 0; } +static void +wi_set_tim(struct ieee80211_node *ni, int set) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wi_softc *sc = ic->ic_ifp->if_softc; + + (*sc->sc_set_tim)(ni, set); + + if ((ic->ic_flags & IEEE80211_F_TIMUPDATE) == 0) + return; + + ic->ic_flags &= ~IEEE80211_F_TIMUPDATE; + wi_write_val(sc, WI_RID_SET_TIM, + IEEE80211_AID(ni->ni_associd) | (set ? 0x8000 : 0)); +} + static int wi_scan_ap(struct wi_softc *sc, u_int16_t chanmask, u_int16_t txrate) { @@ -3187,7 +3598,7 @@ wi_symbol_write_firm(struct wi_softc *sc (const uint16_t *)p, len / 2); p += len; } - + /* * PDR: id[4], address[4], length[4]; */ --- sys/dev/wi/if_wi_pccard.c.orig Sun Jul 10 19:45:16 2005 +++ sys/dev/wi/if_wi_pccard.c Mon Jul 25 15:11:28 2005 @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD: src/sys/dev/wi/if_wi #include #include +#include #define PCCARD_API_LEVEL 6 #include --- sys/dev/wi/if_wi_pci.c.orig Wed Mar 2 16:32:47 2005 +++ sys/dev/wi/if_wi_pci.c Mon Jul 25 15:11:28 2005 @@ -63,6 +63,7 @@ #include #include +#include #include #include --- sys/dev/wi/if_wireg.h.orig Mon Jan 10 17:13:48 2005 +++ sys/dev/wi/if_wireg.h Mon Jul 25 15:11:28 2005 @@ -642,6 +642,12 @@ struct wi_frame { 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 --- sys/dev/wi/if_wivar.h.orig Thu Jun 16 09:33:16 2005 +++ sys/dev/wi/if_wivar.h Mon Jul 25 16:00:27 2005 @@ -31,7 +31,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/wi/if_wivar.h,v 1.25 2005/06/10 16:49:17 brooks Exp $ + * $FreeBSD: src/sys/dev/wi/if_wivar.h,v 1.24 2005/01/06 01:43:33 imp Exp $ */ #if 0 @@ -61,11 +61,20 @@ #define WI_MAX_AID 256 /* max stations for ap operation */ +struct wi_rssdesc { + struct ieee80211_rssdesc rd_desc; + SLIST_ENTRY(wi_rssdesc) rd_next; +}; + +typedef SLIST_HEAD(,wi_rssdesc) wi_rssdescq_t; + struct wi_softc { struct ifnet *sc_ifp; struct ieee80211com sc_ic; int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + void (*sc_node_free)(struct ieee80211_node *); + void (*sc_set_tim)(struct ieee80211_node *, int); device_t sc_dev; #if __FreeBSD_version >= 500000 struct mtx sc_mtx; @@ -117,6 +126,7 @@ struct wi_softc { u_int16_t sc_max_datalen; u_int16_t sc_system_scale; + u_int16_t sc_tx_rate; u_int16_t sc_cnfauthmode; u_int16_t sc_roaming_mode; u_int16_t sc_microwave_oven; @@ -129,6 +139,7 @@ struct wi_softc { int sc_buflen; /* TX buffer size */ int sc_ntxbuf; #define WI_NTXBUF 3 +#define WI_NTXRSS 10 struct { int d_fid; int d_len; @@ -138,6 +149,11 @@ struct wi_softc { int sc_tx_timer; int sc_scan_timer; + int sc_status; + + struct wi_rssdesc sc_rssd[WI_NTXRSS]; + wi_rssdescq_t sc_rssdfree; + struct wi_counters sc_stats; u_int16_t sc_ibss_port; @@ -164,6 +180,7 @@ struct wi_softc { struct timeval sc_last_syn; int sc_false_syns; + int sc_alt_retry; u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2]; @@ -177,10 +194,29 @@ struct wi_softc { u_int8_t pad[64]; } u_rx_rt; int sc_rx_th_len; + union { + struct wi_rx_radiotap_header tap; + u_int8_t pad[64]; + } sc_rxtapu; + union { + struct wi_tx_radiotap_header tap; + u_int8_t pad[64]; + } sc_txtapu; + /* number of transmissions pending at each data rate */ + u_int8_t sc_txpending[IEEE80211_RATE_MAXSIZE]; + struct callout sc_rssadapt_ch; }; #define sc_tx_th u_tx_rt.th #define sc_rx_th u_rx_rt.th +#define sc_rxtap sc_rxtapu.tap +#define sc_txtap sc_txtapu.tap + +struct wi_node { + struct ieee80211_node wn_node; + struct ieee80211_rssadapt wn_rssadapt; +}; + /* maximum consecutive false change-of-BSSID indications */ #define WI_MAX_FALSE_SYNS 10 @@ -190,6 +226,7 @@ struct wi_softc { #define WI_FLAGS_ATTACHED 0x0001 #define WI_FLAGS_INITIALIZED 0x0002 #define WI_FLAGS_OUTRANGE 0x0004 +#define WI_FLAGS_RSSADAPTSTA 0x0008 #define WI_FLAGS_HAS_MOR 0x0010 #define WI_FLAGS_HAS_ROAMING 0x0020 #define WI_FLAGS_HAS_DIVERSITY 0x0040 --- sys/modules/wlan/Makefile.orig Fri Mar 11 09:48:02 2005 +++ sys/modules/wlan/Makefile Mon Jul 25 15:11:28 2005 @@ -5,7 +5,8 @@ KMOD= wlan SRCS= ieee80211.c ieee80211_crypto.c ieee80211_crypto_none.c \ ieee80211_freebsd.c ieee80211_input.c ieee80211_ioctl.c \ - ieee80211_node.c ieee80211_output.c ieee80211_proto.c + ieee80211_node.c ieee80211_output.c ieee80211_proto.c \ + ieee80211_rssadapt.c SRCS+= bus_if.h device_if.h opt_inet.h opt_ipx.h opt_inet.h: --- sys/net80211/ieee80211_rssadapt.c.orig Mon Jul 25 15:11:28 2005 +++ sys/net80211/ieee80211_rssadapt.c Mon Jul 25 15:11:28 2005 @@ -0,0 +1,387 @@ +/* $NetBSD: ieee80211_rssadapt.c,v 1.8 2004/07/23 06:44:56 mycroft Exp $ */ +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +#include +#include +#include /* for hz */ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef interpolate +#undef interpolate +#endif +#define interpolate(parm, old, new) ((parm##_old * (old) + \ + (parm##_denom - parm##_old) * (new)) / \ + parm##_denom) + +#ifdef IEEE80211_DEBUG +static struct timeval lastrateadapt; /* time of last rate adaptation msg */ +static int currssadaptps = 0; /* rate-adaptation msgs this second */ +static int ieee80211_adaptrate = 4; /* rate-adaptation max msgs/sec */ + +#define RSSADAPT_DO_PRINT() \ + ((ieee80211_rssadapt_debug > 0) && \ + ppsratecheck(&lastrateadapt, &currssadaptps, ieee80211_adaptrate)) +#define RSSADAPT_PRINTF(X) \ + if (RSSADAPT_DO_PRINT()) \ + printf X + +int ieee80211_rssadapt_debug = 0; + +#else +#define RSSADAPT_DO_PRINT() (0) +#define RSSADAPT_PRINTF(X) +#endif + +static struct ieee80211_rssadapt_expavgctl master_expavgctl = { + rc_decay_denom : 16, + rc_decay_old : 15, + rc_thresh_denom : 8, + rc_thresh_old : 4, + rc_avgrssi_denom : 8, + rc_avgrssi_old : 4 +}; + +#ifdef __NetBSD__ +#ifdef IEEE80211_DEBUG +/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ +static int +sysctl_ieee80211_rssadapt_debug(SYSCTLFN_ARGS) +{ + int error, t; + struct sysctlnode node; + + node = *rnode; + t = *(int*)rnode->sysctl_data; + node.sysctl_data = &t; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + if (t < 0 || t > 2) + return (EINVAL); + *(int*)rnode->sysctl_data = t; + + return (0); +} +#endif /* IEEE80211_DEBUG */ + +/* TBD factor with sysctl_ath_verify, sysctl_ieee80211_verify. */ +static int +sysctl_ieee80211_rssadapt_expavgctl(SYSCTLFN_ARGS) +{ + struct ieee80211_rssadapt_expavgctl rc; + int error; + struct sysctlnode node; + + node = *rnode; + rc = *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data; + node.sysctl_data = &rc; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return (error); + + if (rc.rc_decay_old < 0 || + rc.rc_decay_denom < rc.rc_decay_old) + return (EINVAL); + + if (rc.rc_thresh_old < 0 || + rc.rc_thresh_denom < rc.rc_thresh_old) + return (EINVAL); + + if (rc.rc_avgrssi_old < 0 || + rc.rc_avgrssi_denom < rc.rc_avgrssi_old) + return (EINVAL); + + *(struct ieee80211_rssadapt_expavgctl *)rnode->sysctl_data = rc; + + return (0); +} + +/* + * Setup sysctl(3) MIB, net.ieee80211.* + * + * TBD condition CTLFLAG_PERMANENT on being an LKM or not + */ +SYSCTL_SETUP(sysctl_ieee80211_rssadapt, + "sysctl ieee80211 rssadapt subtree setup") +{ + int rc; + struct sysctlnode *node; + + if ((rc = sysctl_createv(clog, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "net", NULL, + NULL, 0, NULL, 0, CTL_NET, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "link", NULL, + NULL, 0, NULL, 0, PF_LINK, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "ieee80211", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + + if ((rc = sysctl_createv(clog, 0, &node, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "rssadapt", + SYSCTL_DESCR("Received Signal Strength adaptation controls"), + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0) + goto err; + +#ifdef IEEE80211_DEBUG + /* control debugging printfs */ + if ((rc = sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "debug", + SYSCTL_DESCR("Enable RSS adaptation debugging output"), + sysctl_ieee80211_rssadapt_debug, 0, &ieee80211_rssadapt_debug, 0, + CTL_CREATE, CTL_EOL)) != 0) + goto err; +#endif /* IEEE80211_DEBUG */ + + /* control rate of decay for exponential averages */ + if ((rc = sysctl_createv(clog, 0, &node, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_STRUCT, + "expavgctl", SYSCTL_DESCR("RSS exponential averaging control"), + sysctl_ieee80211_rssadapt_expavgctl, 0, + &master_expavgctl, sizeof(master_expavgctl), CTL_CREATE, + CTL_EOL)) != 0) + goto err; + + return; +err: + printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc); +} +#endif /* __NetBSD__ */ + +int +ieee80211_rssadapt_choose(struct ieee80211_rssadapt *ra, + struct ieee80211_rateset *rs, struct ieee80211_frame *wh, u_int len, + int fixed_rate, const char *dvname, int do_not_adapt) +{ + u_int16_t (*thrs)[IEEE80211_RATE_SIZE]; + int flags = 0, i, rateidx = 0, thridx, top; + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) + flags |= IEEE80211_RATE_BASIC; + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thridx = i; + if (len <= top) + break; + } + + thrs = &ra->ra_rate_thresh[thridx]; + + if (fixed_rate != IEEE80211_FIXED_RATE_NONE) { + if ((rs->rs_rates[fixed_rate] & flags) == flags) { + rateidx = fixed_rate; + goto out; + } + flags |= IEEE80211_RATE_BASIC; + i = fixed_rate; + } else + i = rs->rs_nrates; + + while (--i >= 0) { + rateidx = i; + if ((rs->rs_rates[i] & flags) != flags) + continue; + if (do_not_adapt) + break; + if ((*thrs)[i] < ra->ra_avg_rssi) + break; + } + +out: +#ifdef IEEE80211_DEBUG + if (ieee80211_rssadapt_debug && dvname != NULL) { + printf("%s: dst %s threshold[%d, %d.%d] %d < %d\n", + dvname, ether_sprintf(wh->i_addr1), len, + (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) / 2, + (rs->rs_rates[rateidx] & IEEE80211_RATE_VAL) * 5 % 10, + (*thrs)[rateidx], ra->ra_avg_rssi); + } +#endif /* IEEE80211_DEBUG */ + return rateidx; +} + +void +ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *ra) +{ + long interval; + + ra->ra_pktrate = + (ra->ra_pktrate + 10 * (ra->ra_nfail + ra->ra_nok)) / 2; + ra->ra_nfail = ra->ra_nok = 0; + + /* a node is eligible for its rate to be raised every 1/10 to 10 + * seconds, more eligible in proportion to recent packet rates. + */ + interval = MAX(100000, 10000000 / MAX(1, 10 * ra->ra_pktrate)); + ra->ra_raise_interval.tv_sec = interval / (1000 * 1000); + ra->ra_raise_interval.tv_usec = interval % (1000 * 1000); +} + +void +ieee80211_rssadapt_input(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_rssadapt *ra, int rssi) +{ +#ifdef IEEE80211_DEBUG + int last_avg_rssi = ra->ra_avg_rssi; +#endif + + ra->ra_avg_rssi = interpolate(master_expavgctl.rc_avgrssi, + ra->ra_avg_rssi, (rssi << 8)); + + RSSADAPT_PRINTF(("%s: src %s rssi %d avg %d -> %d\n", + ic->ic_ifp->if_xname, ether_sprintf(ni->ni_macaddr), + rssi, last_avg_rssi, ra->ra_avg_rssi)); +} + +/* + * Adapt the data rate to suit the conditions. When a transmitted + * packet is dropped after IEEE80211_RSSADAPT_RETRY_LIMIT retransmissions, + * raise the RSS threshold for transmitting packets of similar length at + * the same data rate. + */ +void +ieee80211_rssadapt_lower_rate(struct ieee80211com *ic, + struct ieee80211_node *ni, struct ieee80211_rssadapt *ra, + struct ieee80211_rssdesc *id) +{ + struct ieee80211_rateset *rs = &ni->ni_rates; + u_int16_t last_thr; + u_int i, thridx, top; + + ra->ra_nfail++; + + if (id->id_rateidx >= rs->rs_nrates) { + RSSADAPT_PRINTF(("ieee80211_rssadapt_lower_rate: " + "%s rate #%d > #%d out of bounds\n", + ether_sprintf(ni->ni_macaddr), id->id_rateidx, + rs->rs_nrates - 1)); + return; + } + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thridx = i; + if (id->id_len <= top) + break; + } + + last_thr = ra->ra_rate_thresh[thridx][id->id_rateidx]; + ra->ra_rate_thresh[thridx][id->id_rateidx] = + interpolate(master_expavgctl.rc_thresh, last_thr, + (id->id_rssi << 8)); + + RSSADAPT_PRINTF(("%s: dst %s rssi %d threshold[%d, %d.%d] %d -> %d\n", + ic->ic_ifp->if_xname, ether_sprintf(ni->ni_macaddr), + id->id_rssi, id->id_len, + (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) / 2, + (rs->rs_rates[id->id_rateidx] & IEEE80211_RATE_VAL) * 5 % 10, + last_thr, ra->ra_rate_thresh[thridx][id->id_rateidx])); +} + +void +ieee80211_rssadapt_raise_rate(struct ieee80211com *ic, + struct ieee80211_rssadapt *ra, struct ieee80211_rssdesc *id) +{ + u_int16_t (*thrs)[IEEE80211_RATE_SIZE], newthr, oldthr; + struct ieee80211_node *ni = id->id_node; + struct ieee80211_rateset *rs = &ni->ni_rates; + int i, rate, top; +#ifdef IEEE80211_DEBUG + int j; +#endif + + ra->ra_nok++; + + if (!ratecheck(&ra->ra_last_raise, &ra->ra_raise_interval)) + return; + + for (i = 0, top = IEEE80211_RSSADAPT_BKT0; + i < IEEE80211_RSSADAPT_BKTS; + i++, top <<= IEEE80211_RSSADAPT_BKTPOWER) { + thrs = &ra->ra_rate_thresh[i]; + if (id->id_len <= top) + break; + } + + if (id->id_rateidx + 1 < rs->rs_nrates && + (*thrs)[id->id_rateidx + 1] > (*thrs)[id->id_rateidx]) { + rate = (rs->rs_rates[id->id_rateidx + 1] & IEEE80211_RATE_VAL); + + RSSADAPT_PRINTF(("%s: threshold[%d, %d.%d] decay %d ", + ic->ic_ifp->if_xname, + IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER* i), + rate / 2, rate * 5 % 10, (*thrs)[id->id_rateidx + 1])); + oldthr = (*thrs)[id->id_rateidx + 1]; + if ((*thrs)[id->id_rateidx] == 0) + newthr = ra->ra_avg_rssi; + else + newthr = (*thrs)[id->id_rateidx]; + (*thrs)[id->id_rateidx + 1] = + interpolate(master_expavgctl.rc_decay, oldthr, newthr); + + RSSADAPT_PRINTF(("-> %d\n", (*thrs)[id->id_rateidx + 1])); + } + +#ifdef IEEE80211_DEBUG + if (RSSADAPT_DO_PRINT()) { + printf("%s: dst %s thresholds\n", ic->ic_ifp->if_xname, + ether_sprintf(ni->ni_macaddr)); + for (i = 0; i < IEEE80211_RSSADAPT_BKTS; i++) { + printf("%d-byte", IEEE80211_RSSADAPT_BKT0 << (IEEE80211_RSSADAPT_BKTPOWER * i)); + for (j = 0; j < rs->rs_nrates; j++) { + rate = (rs->rs_rates[j] & IEEE80211_RATE_VAL); + printf(", T[%d.%d] = %d", rate / 2, + rate * 5 % 10, ra->ra_rate_thresh[i][j]); + } + printf("\n"); + } + } +#endif /* IEEE80211_DEBUG */ +} --- sys/net80211/ieee80211_rssadapt.h.orig Mon Jul 25 15:11:28 2005 +++ sys/net80211/ieee80211_rssadapt.h Mon Jul 25 15:11:28 2005 @@ -0,0 +1,100 @@ +/* $NetBSD: ieee80211_rssadapt.h,v 1.3 2004/05/06 03:03:20 dyoung Exp $ */ +/*- + * Copyright (c) 2003, 2004 David Young. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* Data-rate adaptation loosely based on "Link Adaptation Strategy + * for IEEE 802.11 WLAN via Received Signal Strength Measurement" + * by Javier del Prado Pavon and Sunghyun Choi. + */ + +/* Buckets for frames 0-128 bytes long, 129-1024, 1025-maximum. */ +#define IEEE80211_RSSADAPT_BKTS 3 +#define IEEE80211_RSSADAPT_BKT0 128 +#define IEEE80211_RSSADAPT_BKTPOWER 3 /* 2**_BKTPOWER */ + +#define ieee80211_rssadapt_thresh_new \ + (ieee80211_rssadapt_thresh_denom - ieee80211_rssadapt_thresh_old) +#define ieee80211_rssadapt_decay_new \ + (ieee80211_rssadapt_decay_denom - ieee80211_rssadapt_decay_old) +#define ieee80211_rssadapt_avgrssi_new \ + (ieee80211_rssadapt_avgrssi_denom - ieee80211_rssadapt_avgrssi_old) + +struct ieee80211_rssadapt_expavgctl { + /* RSS threshold decay. */ + u_int rc_decay_denom; + u_int rc_decay_old; + /* RSS threshold update. */ + u_int rc_thresh_denom; + u_int rc_thresh_old; + /* RSS average update. */ + u_int rc_avgrssi_denom; + u_int rc_avgrssi_old; +}; + +struct ieee80211_rssadapt { + /* exponential average RSSI << 8 */ + u_int16_t ra_avg_rssi; + /* Tx failures in this update interval */ + u_int32_t ra_nfail; + /* Tx successes in this update interval */ + u_int32_t ra_nok; + /* exponential average packets/second */ + u_int32_t ra_pktrate; + /* RSSI threshold for each Tx rate */ + u_int16_t ra_rate_thresh[IEEE80211_RSSADAPT_BKTS] + [IEEE80211_RATE_SIZE]; + struct timeval ra_last_raise; + struct timeval ra_raise_interval; +}; + +/* Properties of a Tx packet, for link adaptation. */ +struct ieee80211_rssdesc { + u_int id_len; /* Tx packet length */ + u_int id_rateidx; /* index into ni->ni_rates */ + struct ieee80211_node *id_node; /* destination STA MAC */ + u_int8_t id_rssi; /* destination STA avg RSS @ + * Tx time + */ +}; + +void ieee80211_rssadapt_updatestats(struct ieee80211_rssadapt *); +void ieee80211_rssadapt_input(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_rssadapt *, int); +void ieee80211_rssadapt_lower_rate(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_rssadapt *, + struct ieee80211_rssdesc *); +void ieee80211_rssadapt_raise_rate(struct ieee80211com *, + struct ieee80211_rssadapt *, struct ieee80211_rssdesc *); +int ieee80211_rssadapt_choose(struct ieee80211_rssadapt *, + struct ieee80211_rateset *, struct ieee80211_frame *, u_int, int, + const char *, int); +#ifdef IEEE80211_DEBUG +extern int ieee80211_rssadapt_debug; +#endif /* IEEE80211_DEBUG */