** This patch only applies on HEAD ** Porting rssadapt for wi from NetBSD, along with a couple of other fixes: MERGED: 1.135, 1.139, 1.141, 1.145, 1.147, 1.148, 1.149, 1.150, 1.151, 1.152, 1.153, 1.154, 1.155, 1.162, 1.167, 1.169, 1.170, 1.171, 1.173, 1.174, 1.175, 1.176, 1.177, 1.182, 1.189, 1.191, 1.192(wi_cmd_wait feedback appreciated), 1.193, 1.196 SKIPPED: 1.140, 1.142, 1.143, 1.144, 1.150, 1.156, 1.157, 1.158, 1.159, 1.160, 1.161, 1.164, 1.165, 1.166, 1.168, 1.172, 1.178, 1.183, 1.184, 1.186, 1.187, 1.190, 1.194, 1.197, 1.200, 1.203, 1.204, 1.206, 1.207, 1.208 PARTIAL: 1.146(endian fix feedback appreciated), 1.198(power saving feedback appreciated) UNKNOWN: 1.114(broken firmware, not tested), 1.127(ejection panic fix, sc_gone/sc_invalid), 1.138(alignment), 1.179, 1.180(copyright), 1.185, 1.188, 1.199, 1.201, 1.202, 1.205(??), 1.209(wicontrol deprecated) --- sys/conf/files.orig Mon Sep 12 16:09:52 2005 +++ sys/conf/files Mon Sep 12 21:48:17 2005 @@ -1475,6 +1475,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 Sep 12 21:48:17 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 Tue Sep 6 20:41:07 2005 +++ sys/dev/wi/if_wi.c Fri Sep 23 18:42:47 2005 @@ -48,7 +48,7 @@ * without an NDA (if at all). What they do release is an API library * called the HCF (Hardware Control Functions) which is supposed to * do the device-specific operations of a device driver for you. The - * publically available version of the HCF library (the 'HCF Light') is + * publically available version of the HCF library (the 'HCF Light') is * a) extremely gross, b) lacks certain features, particularly support * for 802.11 frames, and c) is contaminated by the GNU Public License. * @@ -101,6 +101,7 @@ __FBSDID("$FreeBSD: src/sys/dev/wi/if_wi #include #include #include +#include #include #include @@ -122,13 +123,17 @@ 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_cmd_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 void wi_push_packet(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 *); @@ -136,6 +141,8 @@ static void wi_read_nicid(struct wi_soft static int wi_write_ssid(struct wi_softc *, int, u_int8_t *, int); static int wi_cmd(struct wi_softc *, int, int, int, int); +static int wi_cmd_start(struct wi_softc *, int, int, int, int); +static int wi_cmd_wait(struct wi_softc *, int, int); static int wi_seek_bap(struct wi_softc *, int, int); static int wi_read_bap(struct wi_softc *, int, int, void *, int); static int wi_write_bap(struct wi_softc *, int, int, void *, int); @@ -144,6 +151,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 +161,20 @@ 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_softc *, 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 +214,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 | WI_EV_CMD) struct wi_card_ident wi_card_ident[] = { /* CARD_ID CARD_NAME FIRM_TYPE */ @@ -320,9 +343,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; /* @@ -364,6 +385,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. */ @@ -383,6 +406,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; @@ -413,7 +437,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; @@ -468,6 +494,8 @@ wi_attach(device_t dev) sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN; sc->sc_roaming_mode = 1; + callout_init_mtx(&sc->sc_rssadapt_ch, &sc->sc_mtx, 0); + sc->sc_portnum = WI_DEFAULT_PORT; sc->sc_authtype = WI_DEFAULT_AUTHTYPE; @@ -485,7 +513,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 @@ -621,19 +655,35 @@ 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 (sc->sc_status & WI_EV_CMD) + wi_cmd_intr(sc); + 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); @@ -642,6 +692,47 @@ wi_intr(void *arg) return; } +#define arraylen(a) (sizeof(a) / sizeof((a)[0])) + +static void +wi_rssdescs_init(struct wi_softc *sc, + struct wi_rssdesc (*rssd)[WI_NTXRSS], wi_rssdescq_t *rssdfree) +{ + int i; + + WI_LOCK_ASSERT(sc); + + 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 ifnet *ifp = ic->ic_ifp; + struct wi_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + int i; + + WI_LOCK_ASSERT(sc); + + for (i = 0; i < arraylen(*rssd); i++) { + ni = (*rssd)[i].rd_desc.id_node; + (*rssd)[i].rd_desc.id_node = NULL; + if (ni != NULL) { + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "cleaning outstanding rssadapt " + "descriptor for %s\n", + ether_sprintf(ni->ni_macaddr)); + ieee80211_free_node(ni); + } + } + memset(*txpending, 0, sizeof(*txpending)); + wi_rssdescs_init(sc, rssd, rssdfree); +} + void wi_init(void *arg) { @@ -734,21 +825,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. @@ -769,6 +890,13 @@ wi_init(void *arg) /* Set multicast filter. */ wi_write_multi(sc); + sc->sc_txalloc = 0; + sc->sc_txalloced = 0; + sc->sc_txqueue = 0; + sc->sc_txqueued = 0; + sc->sc_txstart = 0; + sc->sc_txstarted = 0; + /* Allocate fids for the card */ if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) { sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame); @@ -783,10 +911,13 @@ wi_init(void *arg) error); goto out; } - sc->sc_txd[i].d_len = 0; + DPRINTF2(("wi_init: txbuf %d allocated %x\n", i, + sc->sc_txd[i].d_fid)); + ++sc->sc_txalloced; } } - sc->sc_txcur = sc->sc_txnext = 0; + + wi_rssdescs_init(sc, &sc->sc_rssd, &sc->sc_rssdfree); /* Enable desired port */ wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0); @@ -837,6 +968,18 @@ out: return; } +static void +wi_txcmd_wait(struct wi_softc *sc) +{ + KASSERT(sc->sc_txcmds == 1, ("%d tx cmds pending", sc->sc_txcmds)); + + if (sc->sc_status & WI_EV_CMD) { + sc->sc_status &= ~WI_EV_CMD; + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); + } else + (void)wi_cmd_wait(sc, WI_CMD_TX | WI_RECLAIM, 0); +} + void wi_stop(struct ifnet *ifp, int disable) { @@ -849,6 +992,13 @@ wi_stop(struct ifnet *ifp, int disable) DELAY(100000); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + /* wait for tx command completion (deassoc, deauth) */ + while (sc->sc_txcmds > 0) { + wi_txcmd_wait(sc); + wi_cmd_intr(sc); + } + if (sc->sc_enabled && !sc->wi_gone) { CSR_WRITE_2(sc, WI_INT_EN, 0); wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0); @@ -860,7 +1010,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; @@ -872,6 +1025,83 @@ 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_ASSERT(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; + } + + return (rateidx); +} + +/* XXX: locking? */ +static void +wi_raise_rate(struct ieee80211com *ic, struct ieee80211_rssdesc *id) +{ +#ifdef INVARIANTS + struct wi_softc *sc = ic->ic_ifp->if_softc; +#endif + struct wi_node *wn; + + WI_LOCK_ASSERT(sc); + + wn = WI_NODE(id->id_node); + if (wn != NULL) + ieee80211_rssadapt_raise_rate(ic, &wn->wn_rssadapt, id); +} + +static void +wi_lower_rate(struct ieee80211com *ic, struct ieee80211_rssdesc *id) +{ +#ifdef INVARIANTS + struct wi_softc *sc = ic->ic_ifp->if_softc; +#endif + struct wi_node *wn; + + WI_LOCK_ASSERT(sc); + + wn = WI_NODE(id->id_node); + if (wn != NULL) { + ieee80211_rssadapt_lower_rate(ic, id->id_node, + &wn->wn_rssadapt, id); + } else { + DPRINTF(("wi_lower_rate: missing node\n")); + } +} + static void wi_start(struct ifnet *ifp) { @@ -879,10 +1109,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,14 +1130,14 @@ wi_start(struct ifnet *ifp) } memset(&frmhdr, 0, sizeof(frmhdr)); - cur = sc->sc_txnext; + cur = sc->sc_txqueue; for (;;) { + if (sc->sc_txalloced == 0 || 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) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } IF_DEQUEUE(&ic->ic_mgtq, m0); /* * Hack! The referenced node pointer is in the @@ -921,18 +1154,13 @@ 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; IFQ_DRV_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; - if (sc->sc_txd[cur].d_len != 0) { - IFQ_DRV_PREPEND(&ifp->if_snd, m0); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } if (m0->m_len < sizeof(struct ether_header) && (m0 = m_pullup(m0, sizeof(struct ether_header))) == NULL) { ifp->if_oerrors++; @@ -944,26 +1172,31 @@ wi_start(struct ifnet *ifp) m_freem(m0); continue; } + if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) && + (m0->m_flags & M_PWR_SAV) == 0) { + ieee80211_pwrsave(ic, ni, m0); + goto reclaim; + } ifp->if_opackets++; - m_copydata(m0, 0, ETHER_HDR_LEN, + m_copydata(m0, 0, ETHER_HDR_LEN, (caddr_t)&frmhdr.wi_ehdr); #if NBPFILTER > 0 BPF_MTAP(ifp, m0); #endif m0 = ieee80211_encap(ic, m0, ni); - if (m0 == NULL) { - ifp->if_oerrors++; - ieee80211_free_node(ni); - continue; - } - wh = mtod(m0, struct ieee80211_frame *); + if (m0 == NULL) + goto bad; + 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)) { @@ -971,21 +1204,42 @@ wi_start(struct ifnet *ifp) k = ieee80211_crypto_encap(ic, ni, m0); if (k == NULL) { - if (ni != NULL) - ieee80211_free_node(ni); m_freem(m0); - continue; + goto reclaim; } 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)); @@ -997,23 +1251,35 @@ 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) { + if_printf(ifp, "%s write fid %x failed\n", __func__, + fid); +bad: ifp->if_oerrors++; +reclaim: + if (ni != NULL) + ieee80211_free_node(ni); continue; } - sc->sc_txd[cur].d_len = off; - if (sc->sc_txcur == cur) { - if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) { - if_printf(ifp, "xmit failed\n"); - sc->sc_txd[cur].d_len = 0; - continue; - } - sc->sc_tx_timer = 5; - ifp->if_timer = 1; + sc->sc_txpending[ni->ni_txrate]++; + --sc->sc_txalloced; + if (sc->sc_txqueued++ == 0) { +#ifdef DIAGNOSTIC + if (cur != sc->sc_txstart) + if_printf(ifp, "ring is desynchronized\n"); +#endif + wi_push_packet(sc); + } else { +#ifdef WI_RING_DEBUG + if_printf(ifp, "queue %04x, alloc %d queue %d start %d alloced %d queued %d started %d\n", + fid, + sc->sc_txalloc, sc->sc_txqueue, sc->sc_txstart, + sc->sc_txalloced, sc->sc_txqueued, sc->sc_txstarted); +#endif } - sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; + sc->sc_txqueue = cur = (cur + 1) % sc->sc_ntxbuf; + SLIST_REMOVE_HEAD(&sc->sc_rssdfree, rd_next); + id->id_node = ni; } WI_UNLOCK(sc); @@ -1027,7 +1293,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); @@ -1311,6 +1577,31 @@ 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; + + /* XXX if this can happen then it leaks node references */ + 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]) { @@ -1426,6 +1717,18 @@ 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; + + KASSERT(ni != NULL, ("no node")); + + wn = WI_NODE(ni); + ieee80211_rssadapt_input(ic, ni, &wn->wn_rssadapt, rssi); +} + static void wi_rx_intr(struct wi_softc *sc) { @@ -1448,13 +1751,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; @@ -1468,7 +1769,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; @@ -1486,7 +1786,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; @@ -1496,7 +1795,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; @@ -1504,7 +1802,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")); @@ -1519,8 +1816,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) { /* @@ -1562,6 +1857,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) @@ -1573,80 +1871,222 @@ 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) { + if_printf(ifp, "%s read fid %x failed\n", __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) { + if_printf(ifp, "%s bad idx %02x\n", __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)) { + 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 %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) { + if_printf(ifp, "%s null node, rssdesc %02x\n", + __func__, frmhdr.wi_tx_idx); + goto out; + } + + if (sc->sc_txpending[id->id_rateidx]-- == 0) { + if_printf(ifp, "%s txpending[%i] wraparound\n", + __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) { +#if defined(DIAGNOSTIC) || defined(WI_RING_DEBUG) struct ifnet *ifp = sc->sc_ifp; +#endif 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); + cur = sc->sc_txalloc; +#ifdef DIAGNOSTIC + if (sc->sc_txstarted == 0) { + if_printf(ifp, "spurious alloc %x != %x, alloc %d queue %d start %d alloced %d queued %d started %d\n", + fid, sc->sc_txd[cur].d_fid, cur, + sc->sc_txqueue, sc->sc_txstart, sc->sc_txalloced, sc->sc_txqueued, sc->sc_txstarted); return; } - sc->sc_tx_timer = 0; - sc->sc_txd[cur].d_len = 0; - sc->sc_txcur = cur = (cur + 1) % sc->sc_ntxbuf; - if (sc->sc_txd[cur].d_len == 0) +#endif + --sc->sc_txstarted; + ++sc->sc_txalloced; + sc->sc_txd[cur].d_fid = fid; + sc->sc_txalloc = (cur + 1) % sc->sc_ntxbuf; +#ifdef WI_RING_DEBUG + if_printf(ifp, "alloc %04x, alloc %d queue %d start %d alloced %d queued %d started %d\n", + fid, + sc->sc_txalloc, sc->sc_txqueue, sc->sc_txstart, + sc->sc_txalloced, sc->sc_txqueued, sc->sc_txstarted); +#endif +} + +static void +wi_cmd_intr(struct wi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + + DPRINTF2(("%s: %d txcmds outstanding\n", __func__, sc->sc_txcmds)); + + KASSERT(sc->sc_txcmds > 0, ("no tx cmd")); + + --sc->sc_txcmds; + + if (--sc->sc_txqueued == 0) { + sc->sc_tx_timer = 0; ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - else { - if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid, - 0, 0)) { - if_printf(ifp, "xmit failed\n"); - sc->sc_txd[cur].d_len = 0; - } else { - sc->sc_tx_timer = 5; - ifp->if_timer = 1; - } +#ifdef WI_RING_DEBUG + if_printf(ifp, "cmd , alloc %d queue %d start %d alloced %d queued %d started %d\n", + sc->sc_txalloc, sc->sc_txqueue, sc->sc_txstart, + sc->sc_txalloced, sc->sc_txqueued, sc->sc_txstarted); +#endif + } else + wi_push_packet(sc); +} + +static void +wi_push_packet(struct wi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + int cur, fid; + + cur = sc->sc_txstart; + fid = sc->sc_txd[cur].d_fid; + + KASSERT(sc->sc_txcmds == 0, ("%d tx cmds pending", sc->sc_txcmds)); + + if (wi_cmd_start(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) { + if_printf(ifp, "xmit failed\n"); + /* XXX ring might have a hole */ } + if (sc->sc_txcmds++ > 0) + if_printf(ifp, "%s: %d tx cmds pending!!!\n", + __func__, sc->sc_txcmds); + + ++sc->sc_txstarted; +#ifdef DIAGNOSTIC + if (sc->sc_txstarted > WI_NTXBUF) + if_printf(ifp, "%s: too many buffers started\n", __func__); +#endif + sc->sc_txstart = (cur + 1) % sc->sc_ntxbuf; + sc->sc_tx_timer = 5; + ifp->if_timer = 1; +#ifdef WI_RING_DEBUG + if_printf(ifp, "%s: push %04x, alloc %d queue %d start %d alloced %d queued %d started %d\n", + __func__, fid, sc->sc_txalloc, sc->sc_txqueue, sc->sc_txstart, + sc->sc_txalloced, sc->sc_txqueued, sc->sc_txstarted); +#endif +} + +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) { + if_printf(ifp, "%s read fid %x failed\n", __func__, fid); + wi_rssdescs_reset(ic, &sc->sc_rssd, &sc->sc_rssdfree, + &sc->sc_txpending); + goto out; + } + + if (frmhdr.wi_tx_idx >= WI_NTXRSS) { + if_printf(ifp, "%s bad idx %02x\n", __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) { + if_printf(ifp, "%s null node, rssdesc %02x\n", __func__, + frmhdr.wi_tx_idx); + goto out; + } + + if (sc->sc_txpending[id->id_rateidx]-- == 0) { + if_printf(ifp, "%s txpending[%i] wraparound\n", __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 @@ -1706,6 +2146,7 @@ wi_info_intr(struct wi_softc *sc) off = sizeof(ltbuf); for (i = 0; i < len; i++, off += 2, ptr++) { wi_read_bap(sc, fid, off, &stat, sizeof(stat)); + stat = le16toh(stat); #ifdef WI_HERMES_STATS_WAR if (stat & 0xf000) stat = ~stat; @@ -1727,7 +2168,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 @@ -2191,7 +2631,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; @@ -2274,59 +2714,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 @@ -2370,7 +2837,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 ) { @@ -2427,11 +2894,12 @@ wi_write_wep(struct wi_softc *sc) return error; } +/* Must be called at proper protection level! */ static int -wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) +wi_cmd_start(struct wi_softc *sc, int cmd, int val0, int val1, int val2) { - int i, s = 0; - + int i; + if (sc->wi_gone) return (ENODEV); @@ -2452,27 +2920,58 @@ wi_cmd(struct wi_softc *sc, int cmd, int CSR_WRITE_2(sc, WI_PARAM2, val2); CSR_WRITE_2(sc, WI_COMMAND, cmd); + return (0); +} + +static int +wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) +{ + int rc; + + DPRINTF(("%s: [enter] %d txcmds outstanding\n", __func__, + sc->sc_txcmds)); + + if (sc->sc_txcmds > 0) + wi_txcmd_wait(sc); + + if ((rc = wi_cmd_start(sc, cmd, val0, val1, val2)) != 0) + return (rc); + if (cmd == WI_CMD_INI) { /* XXX: should sleep here. */ DELAY(100*1000); /* 100ms delay for init */ } + + rc = wi_cmd_wait(sc, cmd, val0); + + DPRINTF(("%s: [ ] %d txcmds outstanding\n", __func__, + sc->sc_txcmds)); + + if (sc->sc_txcmds > 0) + wi_cmd_intr(sc); + + DPRINTF(("%s: [leave] %d txcmds outstanding\n", __func__, + sc->sc_txcmds)); + + return (rc); +} + +static int +wi_cmd_wait(struct wi_softc *sc, int cmd, int val0) +{ + int i, s = 0; + + DPRINTF2(("%s: cmd=%#x, arg=%#x\n", __func__, cmd, val0)); + + /* wait for the cmd completed bit */ for (i = 0; i < WI_TIMEOUT; i++) { - /* - * Wait for 'command complete' bit to be - * set in the event status register. - */ - s = CSR_READ_2(sc, WI_EVENT_STAT); - if (s & WI_EV_CMD) { - /* Ack the event and read result code. */ - s = CSR_READ_2(sc, WI_STATUS); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); - if (s & WI_STAT_CMD_RESULT) { - return(EIO); - } + if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD) break; - } + if (sc->wi_gone) + return (ENXIO); DELAY(WI_DELAY); } + s = CSR_READ_2(sc, WI_STATUS); if (i == WI_TIMEOUT) { device_printf(sc->sc_dev, @@ -2481,6 +2980,12 @@ wi_cmd(struct wi_softc *sc, int cmd, int sc->wi_gone = 1; return(ETIMEDOUT); } + + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); + + if (s & WI_STAT_CMD_RESULT) + return (EIO); + return (0); } @@ -2660,7 +3165,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", @@ -2677,7 +3182,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)); @@ -2690,6 +3195,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) { @@ -2713,6 +3239,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); @@ -2743,13 +3272,13 @@ wi_newstate(struct ieee80211com *ic, enu #endif if (ic->ic_opmode != IEEE80211_M_HOSTAP) { /* - * XXX hack; unceremoniously clear + * XXX hack; unceremoniously clear * IEEE80211_F_DROPUNENC when operating with * wep enabled so we don't drop unencoded frames * at the 802.11 layer. This is necessary because * we must strip the WEP bit from the 802.11 header * before passing frames to ieee80211_input because - * the card has already stripped the WEP crypto + * the card has already stripped the WEP crypto * header from the packet. */ if (ic->ic_flags & IEEE80211_F_PRIVACY) @@ -2761,12 +3290,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) { @@ -2788,7 +3338,7 @@ wi_scan_ap(struct wi_softc *sc, u_int16_ /* * XXX only supported on 3.x ? */ - val[0] = BSCAN_BCAST | BSCAN_ONETIME; + val[0] = htole16(BSCAN_BCAST | BSCAN_ONETIME); error = wi_write_rid(sc, WI_RID_BCAST_SCAN_REQ, val, sizeof(val[0])); break; @@ -2797,7 +3347,7 @@ wi_scan_ap(struct wi_softc *sc, u_int16_ sc->sc_scan_timer = WI_SCAN_WAIT; sc->sc_ifp->if_timer = 1; DPRINTF(("wi_scan_ap: start scanning, " - "chamask 0x%x txrate 0x%x\n", chanmask, txrate)); + "chanmask 0x%x txrate 0x%x\n", chanmask, txrate)); } return error; } @@ -3189,7 +3739,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 Tue Sep 20 22:36:54 2005 @@ -63,6 +63,7 @@ __FBSDID("$FreeBSD: src/sys/dev/wi/if_wi #include #include +#include #define PCCARD_API_LEVEL 6 #include @@ -106,6 +107,7 @@ static driver_t wi_pccard_driver = { DRIVER_MODULE(wi, pccard, wi_pccard_driver, wi_devclass, 0, 0); MODULE_DEPEND(wi, wlan, 1, 1, 1); +MODULE_DEPEND(wi, wlan_rssadapt, 1, 1, 1); static const struct pccard_product wi_pccard_products[] = { PCMCIA_CARD(3COM, 3CRWE737A), --- sys/dev/wi/if_wi_pci.c.orig Fri Aug 12 16:19:19 2005 +++ sys/dev/wi/if_wi_pci.c Tue Sep 20 22:36:55 2005 @@ -63,6 +63,7 @@ #include #include +#include #include #include @@ -114,6 +115,7 @@ static struct { DRIVER_MODULE(wi, pci, wi_pci_driver, wi_devclass, 0, 0); MODULE_DEPEND(wi, pci, 1, 1, 1); MODULE_DEPEND(wi, wlan, 1, 1, 1); +MODULE_DEPEND(wi, wlan_rssadapt, 1, 1, 1); static int wi_pci_probe(dev) --- sys/dev/wi/if_wireg.h.orig Mon Jan 10 17:13:48 2005 +++ sys/dev/wi/if_wireg.h Thu Sep 15 17:03:37 2005 @@ -640,7 +640,13 @@ struct wi_frame { struct ieee80211_frame_addr4 wi_whdr; /* 0x0e */ u_int16_t wi_dat_len; /* 0x2c */ struct ether_header wi_ehdr; /* 0x2e */ -} __attribute__((__packed__)); +} __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 --- sys/dev/wi/if_wivar.h.orig Thu Jun 16 09:33:16 2005 +++ sys/dev/wi/if_wivar.h Fri Sep 23 18:39:50 2005 @@ -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,15 +139,25 @@ 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; } sc_txd[WI_NTXBUF]; /* TX buffers */ - int sc_txnext; /* index of next TX */ - int sc_txcur; /* index of current TX*/ + int sc_txalloc; /* next FID to allocate */ + int sc_txalloced; /* FIDs currently allocated */ + int sc_txqueue; /* next FID to queue */ + int sc_txqueued; /* FIDs currently queued */ + int sc_txstart; /* next FID to start */ + int sc_txstarted; /* FIDs currently started */ + int sc_txcmds; 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 +184,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 +198,32 @@ 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; +}; + +#define WI_NODE(ni) ((struct wi_node *)(ni)) +#define WI_NODE_CONST(ni) ((const struct wi_node *)(ni)) + /* maximum consecutive false change-of-BSSID indications */ #define WI_MAX_FALSE_SYNS 10 @@ -190,6 +233,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 @@ -224,10 +268,12 @@ struct wi_card_ident { #define ifaddr_byindex(idx) ifnet_addrs[(idx) - 1]; #define WI_LOCK_DECL() int s #define WI_LOCK(_sc) s = splimp() +#define WI_LOCK_ASSERT(_sc) #define WI_UNLOCK(_sc) splx(s) #else #define WI_LOCK_DECL() #define WI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define WI_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define WI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #endif --- sys/modules/wlan_rssadapt/Makefile.orig Thu Sep 22 22:44:47 2005 +++ sys/modules/wlan_rssadapt/Makefile Tue Sep 20 22:34:25 2005 @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../net80211 + +KMOD= wlan_rssadapt +SRCS= ieee80211_rssadapt.c + +.include --- sys/modules/Makefile.orig Tue Sep 20 22:35:45 2005 +++ sys/modules/Makefile Tue Sep 20 22:36:02 2005 @@ -272,6 +272,7 @@ SUBDIR= ${_3dfx} \ wlan \ wlan_acl \ wlan_ccmp \ + wlan_rssadapt \ wlan_tkip \ wlan_wep \ wlan_xauth \ --- sys/net80211/ieee80211_rssadapt.c.orig Mon Sep 12 21:48:20 2005 +++ sys/net80211/ieee80211_rssadapt.c Tue Sep 20 22:42:54 2005 @@ -0,0 +1,412 @@ +/* $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 +#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 */ +} + +/* + * Module glue. + */ +static int +rssadapt_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + return 0; + case MOD_UNLOAD: + return 0; + } + return EINVAL; +} + +static moduledata_t rssadapt_mod = { + "wlan_rssadapt", + rssadapt_modevent, + 0 +}; +DECLARE_MODULE(wlan_rssadapt, rssadapt_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(wlan_rssadapt, 1); +MODULE_DEPEND(wlan_rssadapt, wlan, 1, 1, 1); --- sys/net80211/ieee80211_rssadapt.h.orig Mon Sep 12 21:48:20 2005 +++ sys/net80211/ieee80211_rssadapt.h Mon Sep 12 21:48:20 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 */