diff options
author | Renato Botelho <renato@netgate.com> | 2015-08-17 13:55:50 -0300 |
---|---|---|
committer | Renato Botelho <renato@netgate.com> | 2015-08-17 13:55:50 -0300 |
commit | 6ee75bdd7bf7c20359dd6e38c243586cb062edea (patch) | |
tree | 2a1f5febde659ebdcabbb46159fce1457b3dc98a /sys/net80211 | |
parent | 924a927559577e9cea5abf4a725e679acad834bf (diff) | |
download | FreeBSD-src-6ee75bdd7bf7c20359dd6e38c243586cb062edea.zip FreeBSD-src-6ee75bdd7bf7c20359dd6e38c243586cb062edea.tar.gz |
Importing pfSense patches net80211HEAD.tgz and conf.file.ieee80211.diff
Diffstat (limited to 'sys/net80211')
40 files changed, 1564 insertions, 843 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index db505ab..2fa7e9a 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -237,7 +238,7 @@ static int null_transmit(struct ifnet *ifp, struct mbuf *m) { m_freem(m); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return EACCES; /* XXX EIO/EPERM? */ } @@ -569,15 +570,9 @@ ieee80211_vap_attach(struct ieee80211vap *vap, ifp->if_baudrate = IF_Mbps(maxrate); ether_ifattach(ifp, vap->iv_myaddr); - if (vap->iv_opmode == IEEE80211_M_MONITOR) { - /* NB: disallow transmit */ - ifp->if_transmit = null_transmit; - ifp->if_output = null_output; - } else { - /* hook output method setup by ether_ifattach */ - vap->iv_output = ifp->if_output; - ifp->if_output = ieee80211_output; - } + /* hook output method setup by ether_ifattach */ + vap->iv_output = ifp->if_output; + ifp->if_output = ieee80211_output; /* NB: if_mtu set by ether_ifattach to ETHERMTU */ IEEE80211_LOCK(ic); @@ -1407,7 +1402,8 @@ ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) * rate only when running; otherwise we may have a mismatch * in which case the rate will not be convertible. */ - if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_state == IEEE80211_S_RUN || + vap->iv_state == IEEE80211_S_SLEEP) { imr->ifm_status |= IFM_ACTIVE; mode = ieee80211_chan2mode(ic->ic_curchan); } else @@ -1751,3 +1747,23 @@ ieee80211_mac_hash(const struct ieee80211com *ic, return c; } #undef mix + +char +ieee80211_channel_type_char(const struct ieee80211_channel *c) +{ + if (IEEE80211_IS_CHAN_ST(c)) + return 'S'; + if (IEEE80211_IS_CHAN_108A(c)) + return 'T'; + if (IEEE80211_IS_CHAN_108G(c)) + return 'G'; + if (IEEE80211_IS_CHAN_HT(c)) + return 'n'; + if (IEEE80211_IS_CHAN_A(c)) + return 'a'; + if (IEEE80211_IS_CHAN_ANYG(c)) + return 'g'; + if (IEEE80211_IS_CHAN_B(c)) + return 'b'; + return 'f'; +} diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index 9b005f3..8e586bd 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -169,6 +169,11 @@ struct ieee80211_qosframe_addr4 { #define IEEE80211_FC1_PROTECTED 0x40 #define IEEE80211_FC1_ORDER 0x80 +#define IEEE80211_HAS_SEQ(type, subtype) \ + ((type) != IEEE80211_FC0_TYPE_CTL && \ + !((type) == IEEE80211_FC0_TYPE_DATA && \ + ((subtype) & IEEE80211_FC0_SUBTYPE_QOS_NULL) == \ + IEEE80211_FC0_SUBTYPE_QOS_NULL)) #define IEEE80211_SEQ_FRAG_MASK 0x000f #define IEEE80211_SEQ_FRAG_SHIFT 0 #define IEEE80211_SEQ_SEQ_MASK 0xfff0 diff --git a/sys/net80211/ieee80211_action.c b/sys/net80211/ieee80211_action.c index a36df7c..e37863e 100644 --- a/sys/net80211/ieee80211_action.c +++ b/sys/net80211/ieee80211_action.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index e9e0d30..0d761c4 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_llc.h> #include <net/ethernet.h> @@ -228,6 +229,8 @@ adhoc_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } #endif break; + case IEEE80211_S_RUN: /* IBSS merge */ + break; default: goto invalid; } @@ -288,7 +291,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#define HAS_SEQ(type) ((type & 0x4) == 0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; @@ -368,7 +370,10 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) /* * Validate the bssid. */ - if (!IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && + if (!(type == IEEE80211_FC0_TYPE_MGT && + (subtype == IEEE80211_FC0_SUBTYPE_BEACON || + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) && + !IEEE80211_ADDR_EQ(bssid, vap->iv_bss->ni_bssid) && !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) { /* not interested in */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT, @@ -408,7 +413,8 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) } IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = nf; - if (HAS_SEQ(type)) { + if (IEEE80211_HAS_SEQ(type, subtype) && + IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) { uint8_t tid = ieee80211_gettid(wh); if (IEEE80211_QOS_HAS_SEQ(wh) && TID_TO_WME_AC(tid) >= WME_AC_VI) @@ -652,7 +658,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) break; } err: - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); out: if (m != NULL) { if (need_tap && ieee80211_radiotap_active_vap(vap)) @@ -729,7 +735,8 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } - ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf); + ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, + subtype, rssi, nf); return; } if (scan.capinfo & IEEE80211_CAPINFO_IBSS) { diff --git a/sys/net80211/ieee80211_ageq.c b/sys/net80211/ieee80211_ageq.c index 018ddc2..b650136 100644 --- a/sys/net80211/ieee80211_ageq.c +++ b/sys/net80211/ieee80211_ageq.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> diff --git a/sys/net80211/ieee80211_alq.c b/sys/net80211/ieee80211_alq.c index e651574..a52103a 100644 --- a/sys/net80211/ieee80211_alq.c +++ b/sys/net80211/ieee80211_alq.c @@ -53,10 +53,8 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_var.h> -#include <net/if_dl.h> -#include <net/if_clone.h> #include <net/if_media.h> -#include <net/if_types.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_freebsd.h> diff --git a/sys/net80211/ieee80211_amrr.c b/sys/net80211/ieee80211_amrr.c index 003b4bc..b1d92c8 100644 --- a/sys/net80211/ieee80211_amrr.c +++ b/sys/net80211/ieee80211_amrr.c @@ -38,7 +38,9 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> +#include <net/ethernet.h> #ifdef INET #include <netinet/in.h> @@ -129,6 +131,12 @@ amrr_deinit(struct ieee80211vap *vap) free(vap->iv_rs, M_80211_RATECTL); } +/* + * Return whether 11n rates are possible. + * + * Some 11n devices may return HT information but no HT rates. + * Thus, we shouldn't treat them as an 11n node. + */ static int amrr_node_is_11n(struct ieee80211_node *ni) { @@ -137,6 +145,8 @@ amrr_node_is_11n(struct ieee80211_node *ni) return (0); if (ni->ni_chan == IEEE80211_CHAN_ANYC) return (0); + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates == 0) + return (0); return (IEEE80211_IS_CHAN_HT(ni->ni_chan)); } @@ -185,17 +195,18 @@ amrr_node_init(struct ieee80211_node *ni) rate &= IEEE80211_RATE_VAL; /* pick initial rate from the rateset - HT or otherwise */ + /* Pick something low that's likely to succeed */ for (amn->amn_rix = rs->rs_nrates - 1; amn->amn_rix > 0; amn->amn_rix--) { /* legacy - anything < 36mbit, stop searching */ - /* 11n - stop at MCS4 / MCS12 / MCS28 */ - if (amrr_node_is_11n(ni) && - (rs->rs_rates[amn->amn_rix] & 0x7) < 4) - break; - else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) + /* 11n - stop at MCS4 */ + if (amrr_node_is_11n(ni)) { + if ((rs->rs_rates[amn->amn_rix] & 0x1f) < 4) + break; + } else if ((rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL) <= 72) break; - rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; } + rate = rs->rs_rates[amn->amn_rix] & IEEE80211_RATE_VAL; /* if the rate is an 11n rate, ensure the MCS bit is set */ if (amrr_node_is_11n(ni)) diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c index 6f7dabf..74e82e5 100644 --- a/sys/net80211/ieee80211_ddb.c +++ b/sys/net80211/ieee80211_ddb.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> diff --git a/sys/net80211/ieee80211_dfs.c b/sys/net80211/ieee80211_dfs.c index 82e525d..5fa9ba4 100644 --- a/sys/net80211/ieee80211_dfs.c +++ b/sys/net80211/ieee80211_dfs.c @@ -48,7 +48,9 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> diff --git a/sys/net80211/ieee80211_freebsd.c b/sys/net80211/ieee80211_freebsd.c index 8c97f1e..0822058 100644 --- a/sys/net80211/ieee80211_freebsd.c +++ b/sys/net80211/ieee80211_freebsd.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_clone.h> #include <net/if_media.h> @@ -419,7 +420,7 @@ ieee80211_getmgtframe(uint8_t **frm, int headroom, int pktlen) * frames which all fit in MHLEN. */ if (m != NULL) - MH_ALIGN(m, len); + M_ALIGN(m, len); } else { m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m != NULL) @@ -872,14 +873,8 @@ wlan_modevent(module_t mod, int type, void *unused) printf("wlan: <802.11 Link Layer>\n"); wlan_bpfevent = EVENTHANDLER_REGISTER(bpf_track, bpf_track, 0, EVENTHANDLER_PRI_ANY); - if (wlan_bpfevent == NULL) - return ENOMEM; wlan_ifllevent = EVENTHANDLER_REGISTER(iflladdr_event, wlan_iflladdr, NULL, EVENTHANDLER_PRI_ANY); - if (wlan_ifllevent == NULL) { - EVENTHANDLER_DEREGISTER(bpf_track, wlan_bpfevent); - return ENOMEM; - } #if __FreeBSD_version >= 1000020 wlan_cloner = if_clone_simple(wlanname, wlan_clone_create, wlan_clone_destroy, 0); diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h index 711bac3..aa21f3b 100644 --- a/sys/net80211/ieee80211_freebsd.h +++ b/sys/net80211/ieee80211_freebsd.h @@ -355,8 +355,8 @@ wlan_##name##_modevent(module_t mod, int type, void *unused) \ case MOD_UNLOAD: \ case MOD_QUIESCE: \ if (nrefs) { \ - printf("wlan_##name: still in use (%u dynamic refs)\n",\ - nrefs); \ + printf("wlan_" #name ": still in use " \ + "(%u dynamic refs)\n", nrefs); \ return EBUSY; \ } \ if (type == MOD_UNLOAD) { \ diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 832a8b0..a625b4e 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_llc.h> #include <net/ethernet.h> @@ -367,7 +368,7 @@ hostap_deliver_data(struct ieee80211vap *vap, /* * Do accounting. */ - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); IEEE80211_NODE_STAT(ni, rx_data); IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { @@ -383,7 +384,7 @@ hostap_deliver_data(struct ieee80211vap *vap, if (m->m_flags & M_MCAST) { mcopy = m_dup(m, M_NOWAIT); if (mcopy == NULL) - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); else mcopy->m_flags |= M_MCAST; } else { @@ -421,7 +422,7 @@ hostap_deliver_data(struct ieee80211vap *vap, if (err) { /* NB: IFQ_HANDOFF reclaims mcopy */ } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } } } @@ -477,7 +478,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#define HAS_SEQ(type) ((type & 0x4) == 0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; @@ -570,7 +570,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = nf; - if (HAS_SEQ(type)) { + if (IEEE80211_HAS_SEQ(type, subtype)) { uint8_t tid = ieee80211_gettid(wh); if (IEEE80211_QOS_HAS_SEQ(wh) && TID_TO_WME_AC(tid) >= WME_AC_VI) @@ -908,7 +908,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) break; } err: - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); out: if (m != NULL) { if (need_tap && ieee80211_radiotap_active_vap(vap)) @@ -1736,7 +1736,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } - ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf); + ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, + subtype, rssi, nf); return; } /* diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 16135e1..618252f 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> @@ -1046,6 +1047,7 @@ ieee80211_ht_node_init(struct ieee80211_node *ni) tap = &ni->ni_tx_ampdu[tid]; tap->txa_tid = tid; tap->txa_ni = ni; + tap->txa_lastsample = ticks; /* NB: further initialization deferred */ } ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; @@ -1215,6 +1217,7 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni) for (tid = 0; tid < WME_NUM_TID; tid++) { tap = &ni->ni_tx_ampdu[tid]; tap->txa_tid = tid; + tap->txa_lastsample = ticks; } /* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */ ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU; @@ -1690,6 +1693,7 @@ ampdu_tx_setup(struct ieee80211_tx_ampdu *tap) { callout_init(&tap->txa_timer, CALLOUT_MPSAFE); tap->txa_flags |= IEEE80211_AGGR_SETUP; + tap->txa_lastsample = ticks; } static void @@ -1717,8 +1721,12 @@ ampdu_tx_stop(struct ieee80211_tx_ampdu *tap) */ bar_stop_timer(tap); - tap->txa_lastsample = 0; + /* + * Reset packet estimate. + */ + tap->txa_lastsample = ticks; tap->txa_avgpps = 0; + /* NB: clearing NAK means we may re-send ADDBA */ tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); } @@ -2654,10 +2662,24 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; - /* use advertised setting (XXX locally constraint) */ + + /* Start by using the advertised settings */ rxmax = MS(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); density = MS(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); + /* Cap at VAP rxmax */ + if (rxmax > vap->iv_ampdu_rxmax) + rxmax = vap->iv_ampdu_rxmax; + + /* + * If the VAP ampdu density value greater, use that. + * + * (Larger density value == larger minimum gap between A-MPDU + * subframes.) + */ + if (vap->iv_ampdu_density > density) + density = vap->iv_ampdu_density; + /* * NB: Hardware might support HT40 on some but not all * channels. We can't determine this earlier because only @@ -2674,9 +2696,12 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; + + /* XXX TODO should it start by using advertised settings? */ rxmax = vap->iv_ampdu_rxmax; density = vap->iv_ampdu_density; } + /* adjust short GI based on channel and config */ if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 5e95646..a0f737d 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <net/ethernet.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_llc.h> #include <net/if_media.h> #include <net/if_vlan_var.h> @@ -260,7 +261,7 @@ ieee80211_deliver_data(struct ieee80211vap *vap, /* * Do accounting. */ - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); IEEE80211_NODE_STAT(ni, rx_data); IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { diff --git a/sys/net80211/ieee80211_input.h b/sys/net80211/ieee80211_input.h index b90f46a..f3d569e 100644 --- a/sys/net80211/ieee80211_input.h +++ b/sys/net80211/ieee80211_input.h @@ -168,19 +168,22 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh) { #define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0) #define SEQ_EQ(a,b) ((int)((a)-(b)) == 0) -#define HAS_SEQ(type) ((type & 0x4) == 0) #define SEQNO(a) ((a) >> IEEE80211_SEQ_SEQ_SHIFT) #define FRAGNO(a) ((a) & IEEE80211_SEQ_FRAG_MASK) uint16_t rxseq; - uint8_t type; + uint8_t type, subtype; uint8_t tid; struct ieee80211_rx_ampdu *rap; rxseq = le16toh(*(uint16_t *)wh->i_seq); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - /* Types with no sequence number are always treated valid */ - if (! HAS_SEQ(type)) + /* + * Types with no sequence number (or QoS (+)Null frames) + * are always treated valid. + */ + if (! IEEE80211_HAS_SEQ(type, subtype)) return 1; tid = ieee80211_gettid(wh); @@ -235,7 +238,6 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh) return 1; #undef SEQ_LEQ #undef SEQ_EQ -#undef HAS_SEQ #undef SEQNO #undef FRAGNO } diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 6b668cb..2798d80 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -32,7 +32,6 @@ __FBSDID("$FreeBSD$"); */ #include "opt_inet.h" -#include "opt_ipx.h" #include "opt_wlan.h" #include <sys/endian.h> @@ -44,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/ethernet.h> @@ -53,11 +53,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/if_ether.h> #endif -#ifdef IPX -#include <netipx/ipx.h> -#include <netipx/ipx_if.h> -#endif - #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ioctl.h> #include <net80211/ieee80211_regdomain.h> @@ -607,7 +602,7 @@ ieee80211_ioctl_getcurchan(struct ieee80211vap *vap, struct ieee80211req *ireq) * in use. When in RUN state report the vap-specific channel. * Otherwise return curchan. */ - if (vap->iv_state == IEEE80211_S_RUN) + if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) c = vap->iv_bss->ni_chan; else c = ic->ic_curchan; @@ -925,7 +920,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, case IEEE80211_IOC_BSSID: if (ireq->i_len != IEEE80211_ADDR_LEN) return EINVAL; - if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) { error = copyout(vap->iv_opmode == IEEE80211_M_WDS ? vap->iv_bss->ni_macaddr : vap->iv_bss->ni_bssid, ireq->i_data, ireq->i_len); @@ -1031,7 +1026,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, case IEEE80211_IOC_AMPDU_LIMIT: if (vap->iv_opmode == IEEE80211_M_HOSTAP) ireq->i_val = vap->iv_ampdu_rxmax; - else if (vap->iv_state == IEEE80211_S_RUN) + else if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) ireq->i_val = MS(vap->iv_bss->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); else @@ -1039,7 +1034,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_AMPDU_DENSITY: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) ireq->i_val = MS(vap->iv_bss->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); else @@ -1113,7 +1108,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_SMPS: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) { + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_RTS) ireq->i_val = IEEE80211_HTCAP_SMPS_DYNAMIC; else if (vap->iv_bss->ni_flags & IEEE80211_NODE_MIMO_PS) @@ -1125,7 +1120,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, break; case IEEE80211_IOC_RIFS: if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) ireq->i_val = (vap->iv_bss->ni_flags & IEEE80211_NODE_RIFS) != 0; else @@ -1954,7 +1949,7 @@ setcurchan(struct ieee80211vap *vap, struct ieee80211_channel *c) if (IEEE80211_IS_CHAN_NOADHOC(c)) return EINVAL; } - if (vap->iv_state == IEEE80211_S_RUN && + if ((vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) && vap->iv_bss->ni_chan == c) return 0; /* NB: nothing to do */ } @@ -3419,24 +3414,6 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) arp_ifinit(ifp, ifa); break; #endif -#ifdef IPX - /* - * XXX - This code is probably wrong, - * but has been copied many times. - */ - case AF_IPX: { - struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); - - if (ipx_nullhost(*ina)) - ina->x_host = *(union ipx_host *) - IF_LLADDR(ifp); - else - bcopy((caddr_t) ina->x_host.c_host, - (caddr_t) IF_LLADDR(ifp), - ETHER_ADDR_LEN); - /* fall thru... */ - } -#endif default: if ((ifp->if_flags & IFF_UP) == 0) { ifp->if_flags |= IFF_UP; diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c index a06b241..24969a2 100644 --- a/sys/net80211/ieee80211_mesh.c +++ b/sys/net80211/ieee80211_mesh.c @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_llc.h> #include <net/ethernet.h> @@ -1046,7 +1047,7 @@ mesh_transmit_to_gate(struct ieee80211vap *vap, struct mbuf *m, ni = ieee80211_mesh_find_txnode(vap, rt_gate->rt_dest); if (ni == NULL) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); return; } @@ -1182,7 +1183,7 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, "%s", "frame not fwd'd, cannot dup"); vap->iv_stats.is_mesh_fwd_nobuf++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } mcopy = m_pullup(mcopy, ieee80211_hdrspace(ic, wh) + @@ -1191,7 +1192,7 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, IEEE80211_NOTE_FRAME(vap, IEEE80211_MSG_MESH, wh, "%s", "frame not fwd'd, too short"); vap->iv_stats.is_mesh_fwd_tooshort++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(mcopy); return; } @@ -1249,7 +1250,7 @@ mesh_forward(struct ieee80211vap *vap, struct mbuf *m, /* NB: IFQ_HANDOFF reclaims mbuf */ ieee80211_free_node(ni); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } } @@ -1843,7 +1844,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) break; } err: - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); out: if (m != NULL) { if (need_tap && ieee80211_radiotap_active_vap(vap)) @@ -1905,7 +1906,7 @@ mesh_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } - ieee80211_add_scan(vap, &scan, wh, + ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, subtype, rssi, nf); return; } @@ -2692,7 +2693,7 @@ mesh_send_action(struct ieee80211_node *ni, return EIO; /* XXX */ } - M_PREPEND(m, sizeof(struct ieee80211_frame), M_DONTWAIT); + M_PREPEND(m, sizeof(struct ieee80211_frame), M_NOWAIT); if (m == NULL) { ieee80211_free_node(ni); return ENOMEM; @@ -3333,7 +3334,7 @@ mesh_airtime_calc(struct ieee80211_node *ni) /* Error rate in percentage */ /* XXX assuming small failures are ok */ errrate = (((ifp->if_oerrors + - ifp->if_ierrors) / 100) << M_BITS) / 100; + ifp->if_ierrors) / 100) << M_BITS) / 100; res = (overhead + (nbits / rate)) * ((1 << S_FACTOR) / ((1 << M_BITS) - errrate)); diff --git a/sys/net80211/ieee80211_monitor.c b/sys/net80211/ieee80211_monitor.c index e324081..8909339 100644 --- a/sys/net80211/ieee80211_monitor.c +++ b/sys/net80211/ieee80211_monitor.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_llc.h> #include <net/ethernet.h> @@ -129,7 +130,7 @@ monitor_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) struct ieee80211vap *vap = ni->ni_vap; struct ifnet *ifp = vap->iv_ifp; - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if (ieee80211_radiotap_active_vap(vap)) ieee80211_radiotap_rx(vap, m); diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 4169255..c84f9a8 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> @@ -92,6 +93,8 @@ static void node_getmimoinfo(const struct ieee80211_node *, static void _ieee80211_free_node(struct ieee80211_node *); +static void node_reclaim(struct ieee80211_node_table *nt, + struct ieee80211_node *ni); static void ieee80211_node_table_init(struct ieee80211com *ic, struct ieee80211_node_table *nt, const char *name, int inact, int keymaxix); @@ -718,9 +721,15 @@ ieee80211_sta_join1(struct ieee80211_node *selbs) IEEE80211_ADDR_EQ(obss->ni_macaddr, selbs->ni_macaddr)); vap->iv_bss = selbs; /* NB: caller assumed to bump refcnt */ if (obss != NULL) { + struct ieee80211_node_table *nt = obss->ni_table; + copy_bss(selbs, obss); ieee80211_node_decref(obss); /* iv_bss reference */ - ieee80211_free_node(obss); /* station table reference */ + + IEEE80211_NODE_LOCK(nt); + node_reclaim(nt, obss); /* station table reference */ + IEEE80211_NODE_UNLOCK(nt); + obss = NULL; /* NB: guard against later use */ } @@ -870,7 +879,7 @@ ieee80211_sta_leave(struct ieee80211_node *ni) void ieee80211_node_deauth(struct ieee80211_node *ni, int reason) { - /* NB: bump the refcnt to be sure temporay nodes are not reclaimed */ + /* NB: bump the refcnt to be sure temporary nodes are not reclaimed */ ieee80211_ref_node(ni); if (ni->ni_associd != 0) IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, reason); @@ -1748,6 +1757,28 @@ _ieee80211_free_node(struct ieee80211_node *ni) ni->ni_ic->ic_node_free(ni); } +/* + * Clear any entry in the unicast key mapping table. + */ +static int +node_clear_keyixmap(struct ieee80211_node_table *nt, struct ieee80211_node *ni) +{ + ieee80211_keyix keyix; + + keyix = ni->ni_ucastkey.wk_rxkeyix; + if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax && + nt->nt_keyixmap[keyix] == ni) { + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, + "%s: %p<%s> clear key map entry %u\n", + __func__, ni, ether_sprintf(ni->ni_macaddr), keyix); + nt->nt_keyixmap[keyix] = NULL; + ieee80211_node_decref(ni); + return 1; + } + + return 0; +} + void #ifdef IEEE80211_DEBUG_REFCNT ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line) @@ -1769,24 +1800,9 @@ ieee80211_free_node(struct ieee80211_node *ni) * Last reference, reclaim state. */ _ieee80211_free_node(ni); - } else if (ieee80211_node_refcnt(ni) == 1 && - nt->nt_keyixmap != NULL) { - ieee80211_keyix keyix; - /* - * Check for a last reference in the key mapping table. - */ - keyix = ni->ni_ucastkey.wk_rxkeyix; - if (keyix < nt->nt_keyixmax && - nt->nt_keyixmap[keyix] == ni) { - IEEE80211_DPRINTF(ni->ni_vap, - IEEE80211_MSG_NODE, - "%s: %p<%s> clear key map entry", __func__, - ni, ether_sprintf(ni->ni_macaddr)); - nt->nt_keyixmap[keyix] = NULL; - ieee80211_node_decref(ni); /* XXX needed? */ + } else if (ieee80211_node_refcnt(ni) == 1) + if (node_clear_keyixmap(nt, ni)) _ieee80211_free_node(ni); - } - } IEEE80211_NODE_UNLOCK(nt); } else { if (ieee80211_node_dectestref(ni)) @@ -1854,7 +1870,6 @@ ieee80211_node_delucastkey(struct ieee80211_node *ni) static void node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) { - ieee80211_keyix keyix; IEEE80211_NODE_LOCK_ASSERT(nt); @@ -1869,15 +1884,7 @@ node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni) * table. We cannot depend on the mapping table entry * being cleared because the node may not be free'd. */ - keyix = ni->ni_ucastkey.wk_rxkeyix; - if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax && - nt->nt_keyixmap[keyix] == ni) { - IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, - "%s: %p<%s> clear key map entry %u\n", - __func__, ni, ether_sprintf(ni->ni_macaddr), keyix); - nt->nt_keyixmap[keyix] = NULL; - ieee80211_node_decref(ni); /* NB: don't need free */ - } + (void)node_clear_keyixmap(nt, ni); if (!ieee80211_node_dectestref(ni)) { /* * Other references are present, just remove the diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 9247e0e..2158bee 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/ethernet.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_llc.h> #include <net/if_media.h> #include <net/if_vlan_var.h> @@ -157,7 +158,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, ni->ni_macaddr, NULL, "%s", "classification failure"); vap->iv_stats.is_tx_classify++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); ieee80211_free_node(ni); @@ -254,7 +255,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m, ieee80211_free_node(ni); if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OMCASTS, mcast); if_inc_counter(ifp, IFCOUNTER_OBYTES, len); } @@ -304,7 +305,7 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "discard frame, %s\n", "m_pullup failed"); vap->iv_stats.is_tx_nobuf++; /* XXX */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENOBUFS); } eh = mtod(m, struct ether_header *); @@ -338,7 +339,7 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) ni = ieee80211_find_txnode(vap, eh->ether_dhost); if (ni == NULL) { /* NB: ieee80211_find_txnode does stat+msg */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); /* XXX better status? */ return (ENOBUFS); @@ -350,7 +351,7 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) "sta not associated (type 0x%04x)", htons(eh->ether_type)); vap->iv_stats.is_tx_notassoc++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); ieee80211_free_node(ni); /* XXX better status? */ @@ -369,7 +370,7 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) eh->ether_dhost, NULL, "%s", "proxy not enabled"); vap->iv_stats.is_mesh_notproxy++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(m); /* XXX better status? */ return (ENOBUFS); @@ -386,7 +387,7 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) * NB: ieee80211_mesh_discover holds/disposes * frame (e.g. queueing on path discovery). */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* XXX better status? */ return (ENOBUFS); } @@ -396,6 +397,20 @@ ieee80211_start_pkt(struct ieee80211vap *vap, struct mbuf *m) /* * We've resolved the sender, so attempt to transmit it. */ + + if (vap->iv_state == IEEE80211_S_SLEEP) { + /* + * In power save; queue frame and then wakeup device + * for transmit. + */ + ic->ic_lastdata = ticks; + if (ieee80211_pwrsave(ni, m) != 0) + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ieee80211_free_node(ni); + ieee80211_new_state(vap, IEEE80211_S_RUN, 0); + return (0); + } + if (ieee80211_vap_pkt_send_dest(vap, m, ni) != 0) return (ENOBUFS); return (0); @@ -426,24 +441,19 @@ ieee80211_vap_transmit(struct ifnet *ifp, struct mbuf *m) if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return (ENETDOWN); } - if (vap->iv_state == IEEE80211_S_SLEEP) { - /* - * In power save, wakeup device for transmit. - */ - ieee80211_new_state(vap, IEEE80211_S_RUN, 0); - m_freem(m); - return (0); - } + /* * No data frames go out unless we're running. * Note in particular this covers CAC and CSA * states (though maybe we should check muting * for CSA). */ - if (vap->iv_state != IEEE80211_S_RUN) { + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_LOCK(ic); /* re-check under the com lock to avoid races */ - if (vap->iv_state != IEEE80211_S_RUN) { + if (vap->iv_state != IEEE80211_S_RUN && + vap->iv_state != IEEE80211_S_SLEEP) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT, "%s: ignore queue, in %s state\n", __func__, ieee80211_state_name[vap->iv_state]); @@ -484,6 +494,13 @@ ieee80211_vap_qflush(struct ifnet *ifp) /* * 802.11 raw output routine. + * + * XXX TODO: this (and other send routines) should correctly + * XXX keep the pwr mgmt bit set if it decides to call into the + * XXX driver to send a frame whilst the state is SLEEP. + * + * Otherwise the peer may decide that we're awake and flood us + * with traffic we are still too asleep to receive! */ int ieee80211_raw_output(struct ieee80211vap *vap, struct ieee80211_node *ni, @@ -600,7 +617,7 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m, if (ieee80211_classify(ni, m)) senderr(EIO); /* XXX */ - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); IEEE80211_NODE_STAT(ni, tx_data); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { IEEE80211_NODE_STAT(ni, tx_mcast); @@ -628,7 +645,7 @@ bad: m_freem(m); if (ni != NULL) ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return error; #undef senderr } @@ -722,7 +739,12 @@ ieee80211_send_setup( if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) m->m_flags |= M_AMPDU_MPDU; else { - seqno = ni->ni_txseqs[tid]++; + if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK, + type & IEEE80211_FC0_SUBTYPE_MASK)) + seqno = ni->ni_txseqs[tid]++; + else + seqno = 0; + *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); M_SEQNO_SET(m, seqno); @@ -1694,7 +1716,7 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs) /* * Add an ssid element to a frame. */ -static uint8_t * +uint8_t * ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) { *frm++ = IEEE80211_ELEMID_SSID; @@ -2314,18 +2336,33 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) ic->ic_curchan); frm = ieee80211_add_supportedchannels(frm, ic); } + + /* + * Check the channel - we may be using an 11n NIC with an + * 11n capable station, but we're configured to be an 11b + * channel. + */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && - ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) { frm = ieee80211_add_htcap(frm, ni); + } frm = ieee80211_add_wpa(frm, vap); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + + /* + * Same deal - only send HT info if we're on an 11n + * capable channel. + */ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) && + IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_ies.htcap_ie != NULL && - ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) + ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_VENDOR) { frm = ieee80211_add_htcap_vendor(frm, ni); + } #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) { frm = ieee80211_add_ath(frm, diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c index 923266c..4242ac0 100644 --- a/sys/net80211/ieee80211_phy.c +++ b/sys/net80211/ieee80211_phy.c @@ -35,12 +35,16 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/socket.h> #include <net/if.h> #include <net/if_media.h> +#include <net/ethernet.h> +#include <net/route.h> + #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_phy.h> diff --git a/sys/net80211/ieee80211_power.c b/sys/net80211/ieee80211_power.c index 4542ec5..812cd70 100644 --- a/sys/net80211/ieee80211_power.c +++ b/sys/net80211/ieee80211_power.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> @@ -554,3 +555,108 @@ ieee80211_sta_pwrsave(struct ieee80211vap *vap, int enable) ieee80211_send_nulldata(ieee80211_ref_node(ni)); } } + +/* + * Handle being notified that we have data available for us in a TIM/ATIM. + * + * This may schedule a transition from _SLEEP -> _RUN if it's appropriate. + * + * In STA mode, we may have put to sleep during scan and need to be dragged + * back out of powersave mode. + */ +void +ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set) +{ + struct ieee80211com *ic = vap->iv_ic; + + /* + * Schedule the driver state change. It'll happen at some point soon. + * Since the hardware shouldn't know that we're running just yet + * (and thus tell the peer that we're awake before we actually wake + * up said hardware), we leave the actual node state transition + * up to the transition to RUN. + * + * XXX TODO: verify that the transition to RUN will wake up the + * BSS node! + */ + IEEE80211_LOCK(vap->iv_ic); + if (set == 1 && vap->iv_state == IEEE80211_S_SLEEP) { + ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: TIM=%d; wakeup\n", __func__, set); + } else if ((set == 1) && (ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN)) { + /* + * XXX only do this if we're in RUN state? + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: wake up from bgscan vap sleep\n", + __func__); + /* + * We may be in BGSCAN mode - this means the VAP is is in STA + * mode powersave. If it is, we need to wake it up so we + * can process outbound traffic. + */ + vap->iv_sta_ps(vap, 0); + } + IEEE80211_UNLOCK(vap->iv_ic); +} + +/* + * Timer check on whether the VAP has had any transmit activity. + * + * This may schedule a transition from _RUN -> _SLEEP if it's appropriate. + */ +void +ieee80211_sta_ps_timer_check(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + /* XXX lock assert */ + + /* For no, only do this in STA mode */ + if (! (vap->iv_caps & IEEE80211_C_SWSLEEP)) + goto out; + + if (vap->iv_opmode != IEEE80211_M_STA) + goto out; + + /* If we're not at run state, bail */ + if (vap->iv_state != IEEE80211_S_RUN) + goto out; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: lastdata=%llu, ticks=%llu\n", + __func__, (unsigned long long) ic->ic_lastdata, + (unsigned long long) ticks); + + /* If powersave is disabled on the VAP, don't bother */ + if (! (vap->iv_flags & IEEE80211_F_PMGTON)) + goto out; + + /* If we've done any data within our idle interval, bail */ + /* XXX hard-coded to one second for now, ew! */ + if (time_after(ic->ic_lastdata + 500, ticks)) + goto out; + + /* + * Signify we're going into power save and transition the + * node to powersave. + */ + if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) + vap->iv_sta_ps(vap, 1); + + /* + * XXX The driver has to handle the fact that we're going + * to sleep but frames may still be transmitted; + * hopefully it and/or us will do the right thing and mark any + * transmitted frames with PWRMGT set to 1. + */ + ieee80211_new_state_locked(vap, IEEE80211_S_SLEEP, 0); + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_POWER, + "%s: time delta=%d msec\n", __func__, + (int) ticks_to_msecs(ticks - ic->ic_lastdata)); + +out: + return; +} diff --git a/sys/net80211/ieee80211_power.h b/sys/net80211/ieee80211_power.h index 55270d2..d9bbaa5 100644 --- a/sys/net80211/ieee80211_power.h +++ b/sys/net80211/ieee80211_power.h @@ -79,6 +79,9 @@ int ieee80211_node_psq_age(struct ieee80211_node *); int ieee80211_pwrsave(struct ieee80211_node *, struct mbuf *); void ieee80211_node_pwrsave(struct ieee80211_node *, int enable); void ieee80211_sta_pwrsave(struct ieee80211vap *, int enable); +void ieee80211_sta_tim_notify(struct ieee80211vap *vap, int set); +void ieee80211_sta_ps_timer_check(struct ieee80211vap *vap); +/* XXX what's this? */ void ieee80211_power_poll(struct ieee80211com *); #endif /* _NET80211_IEEE80211_POWER_H_ */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 6f75130..2f84769 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sockio.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 8df5116..3b46ac4 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -149,6 +149,7 @@ struct mbuf *ieee80211_alloc_cts(struct ieee80211com *, uint8_t *ieee80211_add_rates(uint8_t *, const struct ieee80211_rateset *); uint8_t *ieee80211_add_xrates(uint8_t *, const struct ieee80211_rateset *); +uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); uint8_t *ieee80211_add_wpa(uint8_t *, const struct ieee80211vap *); uint8_t *ieee80211_add_rsn(uint8_t *, const struct ieee80211vap *); uint8_t *ieee80211_add_qos(uint8_t *, const struct ieee80211_node *); diff --git a/sys/net80211/ieee80211_radiotap.c b/sys/net80211/ieee80211_radiotap.c index f06f7e0..5638f52 100644 --- a/sys/net80211/ieee80211_radiotap.c +++ b/sys/net80211/ieee80211_radiotap.c @@ -42,8 +42,10 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_llc.h> #include <net/if_media.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> diff --git a/sys/net80211/ieee80211_ratectl.c b/sys/net80211/ieee80211_ratectl.c index 0ad46bd3..3eff898 100644 --- a/sys/net80211/ieee80211_ratectl.c +++ b/sys/net80211/ieee80211_ratectl.c @@ -30,9 +30,12 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/systm.h> #include <sys/socket.h> +#include <sys/malloc.h> #include <net/if.h> #include <net/if_media.h> +#include <net/ethernet.h> +#include <net/route.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_ratectl.h> diff --git a/sys/net80211/ieee80211_ratectl.h b/sys/net80211/ieee80211_ratectl.h index be81781..5603509 100644 --- a/sys/net80211/ieee80211_ratectl.h +++ b/sys/net80211/ieee80211_ratectl.h @@ -62,13 +62,13 @@ void ieee80211_ratectl_set(struct ieee80211vap *, int); MALLOC_DECLARE(M_80211_RATECTL); -static void __inline +static __inline void ieee80211_ratectl_deinit(struct ieee80211vap *vap) { vap->iv_rate->ir_deinit(vap); } -static void __inline +static __inline void ieee80211_ratectl_node_init(struct ieee80211_node *ni) { const struct ieee80211vap *vap = ni->ni_vap; @@ -76,7 +76,7 @@ ieee80211_ratectl_node_init(struct ieee80211_node *ni) vap->iv_rate->ir_node_init(ni); } -static void __inline +static __inline void ieee80211_ratectl_node_deinit(struct ieee80211_node *ni) { const struct ieee80211vap *vap = ni->ni_vap; @@ -92,14 +92,14 @@ ieee80211_ratectl_rate(struct ieee80211_node *ni, void *arg, uint32_t iarg) return vap->iv_rate->ir_rate(ni, arg, iarg); } -static void __inline +static __inline void ieee80211_ratectl_tx_complete(const struct ieee80211vap *vap, const struct ieee80211_node *ni, int status, void *arg1, void *arg2) { vap->iv_rate->ir_tx_complete(vap, ni, status, arg1, arg2); } -static void __inline +static __inline void ieee80211_ratectl_tx_update(const struct ieee80211vap *vap, const struct ieee80211_node *ni, void *arg1, void *arg2, void *arg3) { @@ -108,7 +108,7 @@ ieee80211_ratectl_tx_update(const struct ieee80211vap *vap, vap->iv_rate->ir_tx_update(vap, ni, arg1, arg2, arg3); } -static void __inline +static __inline void ieee80211_ratectl_setinterval(const struct ieee80211vap *vap, int msecs) { if (vap->iv_rate->ir_setinterval == NULL) diff --git a/sys/net80211/ieee80211_ratectl_none.c b/sys/net80211/ieee80211_ratectl_none.c index 0edec44..a0056f3 100644 --- a/sys/net80211/ieee80211_ratectl_none.c +++ b/sys/net80211/ieee80211_ratectl_none.c @@ -29,13 +29,16 @@ __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include <sys/param.h> +#include <sys/systm.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/module.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <net/if.h> #include <net/if_media.h> +#include <net/ethernet.h> #ifdef INET #include <netinet/in.h> diff --git a/sys/net80211/ieee80211_regdomain.c b/sys/net80211/ieee80211_regdomain.c index 6bc5e0d..ed7f422 100644 --- a/sys/net80211/ieee80211_regdomain.c +++ b/sys/net80211/ieee80211_regdomain.c @@ -34,11 +34,14 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_regdomain.h> diff --git a/sys/net80211/ieee80211_rssadapt.c b/sys/net80211/ieee80211_rssadapt.c index aaf4057..f230f60 100644 --- a/sys/net80211/ieee80211_rssadapt.c +++ b/sys/net80211/ieee80211_rssadapt.c @@ -33,13 +33,17 @@ #include "opt_wlan.h" #include <sys/param.h> +#include <sys/systm.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/module.h> #include <sys/socket.h> #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_rssadapt.h> diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c index 3a8c24d..6c950d6 100644 --- a/sys/net80211/ieee80211_scan.c +++ b/sys/net80211/ieee80211_scan.c @@ -40,40 +40,16 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> #include <net80211/ieee80211_var.h> -#include <net/bpf.h> - -struct scan_state { - struct ieee80211_scan_state base; /* public state */ - - u_int ss_iflags; /* flags used internally */ -#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ -#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ -#define ISCAN_CANCEL 0x0004 /* cancel current scan */ -#define ISCAN_ABORT 0x0008 /* end the scan immediately */ - unsigned long ss_chanmindwell; /* min dwell on curchan */ - unsigned long ss_scanend; /* time scan must stop */ - u_int ss_duration; /* duration for next scan */ - struct task ss_scan_task; /* scan execution */ - struct cv ss_scan_cv; /* scan signal */ - struct callout ss_scan_timer; /* scan timer */ -}; -#define SCAN_PRIVATE(ss) ((struct scan_state *) ss) +/* XXX until it's implemented as attach ops */ +#include <net80211/ieee80211_scan_sw.h> -/* - * Amount of time to go off-channel during a background - * scan. This value should be large enough to catch most - * ap's but short enough that we can return on-channel - * before our listen interval expires. - * - * XXX tunable - * XXX check against configured listen interval - */ -#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) +#include <net/bpf.h> /* * Roaming-related defaults. RSSI thresholds are as returned by the @@ -92,55 +68,32 @@ struct scan_state { #define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */ #define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */ -static void scan_curchan(struct ieee80211_scan_state *, unsigned long); -static void scan_mindwell(struct ieee80211_scan_state *); -static void scan_signal(void *); -static void scan_task(void *, int); - -MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); - void ieee80211_scan_attach(struct ieee80211com *ic) { - struct scan_state *ss; - ss = (struct scan_state *) malloc(sizeof(struct scan_state), - M_80211_SCAN, M_NOWAIT | M_ZERO); - if (ss == NULL) { - ic->ic_scan = NULL; - return; - } - callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); - cv_init(&ss->ss_scan_cv, "scan"); - TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss); - ic->ic_scan = &ss->base; - ss->base.ss_ic = ic; - - ic->ic_scan_curchan = scan_curchan; - ic->ic_scan_mindwell = scan_mindwell; + /* + * For now, the swscan module does both the + * allocation (so it can pad it) and sets up the net80211 + * bits. + * + * I'll split this stuff later. + */ + ieee80211_swscan_attach(ic); } void ieee80211_scan_detach(struct ieee80211com *ic) { - struct ieee80211_scan_state *ss = ic->ic_scan; - if (ss != NULL) { - IEEE80211_LOCK(ic); - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; - scan_signal(ss); - IEEE80211_UNLOCK(ic); - ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); - callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); - KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, - ("scan still running")); - if (ss->ss_ops != NULL) { - ss->ss_ops->scan_detach(ss); - ss->ss_ops = NULL; - } - ic->ic_scan = NULL; - free(SCAN_PRIVATE(ss), M_80211_SCAN); - } + /* + * Ideally we'd do the ss_ops detach call here; + * but then ieee80211_swscan_detach would need + * to be split in two. + * + * I'll do that later. + */ + ieee80211_swscan_detach(ic); } static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = { @@ -175,6 +128,8 @@ ieee80211_scan_vattach(struct ieee80211vap *vap) vap->iv_roaming = IEEE80211_ROAMING_AUTO; memcpy(vap->iv_roamparms, defroam, sizeof(defroam)); + + ieee80211_swscan_vattach(vap); } void @@ -185,11 +140,10 @@ ieee80211_scan_vdetach(struct ieee80211vap *vap) IEEE80211_LOCK(ic); ss = ic->ic_scan; + + ieee80211_swscan_vdetach(vap); + if (ss != NULL && ss->ss_vap == vap) { - if (ic->ic_flags & IEEE80211_F_SCAN) { - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; - scan_signal(ss); - } if (ss->ss_ops != NULL) { ss->ss_ops->scan_detach(ss); ss->ss_ops = NULL; @@ -260,8 +214,8 @@ ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) * ensure later callbacks find ss_ops set to properly * reflect current operating mode. */ -static void -scan_update_locked(struct ieee80211vap *vap, +void +ieee80211_scan_update_locked(struct ieee80211vap *vap, const struct ieee80211_scanner *scan) { struct ieee80211com *ic = vap->iv_ic; @@ -306,26 +260,6 @@ scan_update_locked(struct ieee80211vap *vap, } } -static char -channel_type(const struct ieee80211_channel *c) -{ - if (IEEE80211_IS_CHAN_ST(c)) - return 'S'; - if (IEEE80211_IS_CHAN_108A(c)) - return 'T'; - if (IEEE80211_IS_CHAN_108G(c)) - return 'G'; - if (IEEE80211_IS_CHAN_HT(c)) - return 'n'; - if (IEEE80211_IS_CHAN_A(c)) - return 'a'; - if (IEEE80211_IS_CHAN_ANYG(c)) - return 'g'; - if (IEEE80211_IS_CHAN_B(c)) - return 'b'; - return 'f'; -} - void ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) { @@ -338,14 +272,14 @@ ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) const struct ieee80211_channel *c = ss->ss_chans[i]; printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), - channel_type(c)); + ieee80211_channel_type_char(c)); sep = ", "; } } #ifdef IEEE80211_DEBUG -static void -scan_dump(struct ieee80211_scan_state *ss) +void +ieee80211_scan_dump(struct ieee80211_scan_state *ss) { struct ieee80211vap *vap = ss->ss_vap; @@ -356,8 +290,8 @@ scan_dump(struct ieee80211_scan_state *ss) } #endif /* IEEE80211_DEBUG */ -static void -copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, +void +ieee80211_scan_copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, int nssid, const struct ieee80211_scan_ssid ssids[]) { if (nssid > IEEE80211_SCAN_MAX_SSID) { @@ -374,86 +308,12 @@ copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, /* * Start a scan unless one is already going. */ -static int -start_scan_locked(const struct ieee80211_scanner *scan, - struct ieee80211vap *vap, int flags, u_int duration, - u_int mindwell, u_int maxdwell, - u_int nssid, const struct ieee80211_scan_ssid ssids[]) -{ - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - - IEEE80211_LOCK_ASSERT(ic); - - if (ic->ic_flags & IEEE80211_F_CSAPENDING) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: scan inhibited by pending channel change\n", __func__); - } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" - , __func__ - , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" - , duration, mindwell, maxdwell - , ieee80211_phymode_name[vap->iv_des_mode] - , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" - , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" - , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" - , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" - , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" - , flags & IEEE80211_SCAN_ONCE ? ", once" : "" - ); - - scan_update_locked(vap, scan); - if (ss->ss_ops != NULL) { - if ((flags & IEEE80211_SCAN_NOSSID) == 0) - copy_ssid(vap, ss, nssid, ssids); - - /* NB: top 4 bits for internal use */ - ss->ss_flags = flags & 0xfff; - if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - vap->iv_stats.is_scan_active++; - else - vap->iv_stats.is_scan_passive++; - if (flags & IEEE80211_SCAN_FLUSH) - ss->ss_ops->scan_flush(ss); - if (flags & IEEE80211_SCAN_BGSCAN) - ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; - - /* NB: flush frames rx'd before 1st channel change */ - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; - SCAN_PRIVATE(ss)->ss_duration = duration; - ss->ss_next = 0; - ss->ss_mindwell = mindwell; - ss->ss_maxdwell = maxdwell; - /* NB: scan_start must be before the scan runtask */ - ss->ss_ops->scan_start(ss, vap); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(vap)) - scan_dump(ss); -#endif /* IEEE80211_DEBUG */ - ic->ic_flags |= IEEE80211_F_SCAN; - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); - } - return 1; - } else { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: %s scan already in progress\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); - } - return 0; -} - -/* - * Start a scan unless one is already going. - */ int ieee80211_start_scan(struct ieee80211vap *vap, int flags, u_int duration, u_int mindwell, u_int maxdwell, u_int nssid, const struct ieee80211_scan_ssid ssids[]) { - struct ieee80211com *ic = vap->iv_ic; const struct ieee80211_scanner *scan; - int result; scan = ieee80211_scanner_get(vap->iv_opmode); if (scan == NULL) { @@ -464,12 +324,9 @@ ieee80211_start_scan(struct ieee80211vap *vap, int flags, return 0; } - IEEE80211_LOCK(ic); - result = start_scan_locked(scan, vap, flags, duration, + /* XXX ops */ + return ieee80211_swscan_start_scan(scan, vap, flags, duration, mindwell, maxdwell, nssid, ssids); - IEEE80211_UNLOCK(ic); - - return result; } /* @@ -516,49 +373,19 @@ ieee80211_check_scan(struct ieee80211vap *vap, int flags, /* XXX re-use cache contents? e.g. adhoc<->sta */ flags |= IEEE80211_SCAN_FLUSH; } - scan_update_locked(vap, scan); - if (ss->ss_ops != NULL) { - /* XXX verify ss_ops matches vap->iv_opmode */ - if ((flags & IEEE80211_SCAN_NOSSID) == 0) { - /* - * Update the ssid list and mark flags so if - * we call start_scan it doesn't duplicate work. - */ - copy_ssid(vap, ss, nssid, ssids); - flags |= IEEE80211_SCAN_NOSSID; - } - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && - (flags & IEEE80211_SCAN_FLUSH) == 0 && - time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { - /* - * We're not currently scanning and the cache is - * deemed hot enough to consult. Lock out others - * by marking IEEE80211_F_SCAN while we decide if - * something is already in the scan cache we can - * use. Also discard any frames that might come - * in while temporarily marked as scanning. - */ - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; - ic->ic_flags |= IEEE80211_F_SCAN; - - /* NB: need to use supplied flags in check */ - ss->ss_flags = flags & 0xff; - result = ss->ss_ops->scan_end(ss, vap); - - ic->ic_flags &= ~IEEE80211_F_SCAN; - SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; - if (result) { - ieee80211_notify_scan_done(vap); - IEEE80211_UNLOCK(ic); - return 1; - } - } - } - result = start_scan_locked(scan, vap, flags, duration, + + /* + * XXX TODO: separate things out a bit better. + * XXX TODO: ops + */ + ieee80211_scan_update_locked(vap, scan); + + result = ieee80211_swscan_check_scan(scan, vap, flags, duration, mindwell, maxdwell, nssid, ssids); + IEEE80211_UNLOCK(ic); - return result; + return (result); } /* @@ -581,10 +408,10 @@ ieee80211_check_scan_current(struct ieee80211vap *vap) int ieee80211_bg_scan(struct ieee80211vap *vap, int flags) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; const struct ieee80211_scanner *scan; + // IEEE80211_UNLOCK_ASSERT(sc); + scan = ieee80211_scanner_get(vap->iv_opmode); if (scan == NULL) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, @@ -594,84 +421,14 @@ ieee80211_bg_scan(struct ieee80211vap *vap, int flags) return 0; } - IEEE80211_LOCK(ic); - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - u_int duration; - /* - * Go off-channel for a fixed interval that is large - * enough to catch most ap's but short enough that - * we can return on-channel before our listen interval - * expires. - */ - duration = IEEE80211_SCAN_OFFCHANNEL; - - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: %s scan, ticks %u duration %lu\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", - ticks, duration); - - scan_update_locked(vap, scan); - if (ss->ss_ops != NULL) { - ss->ss_vap = vap; - /* - * A background scan does not select a new sta; it - * just refreshes the scan cache. Also, indicate - * the scan logic should follow the beacon schedule: - * we go off-channel and scan for a while, then - * return to the bss channel to receive a beacon, - * then go off-channel again. All during this time - * we notify the ap we're in power save mode. When - * the scan is complete we leave power save mode. - * If any beacon indicates there are frames pending - * for us then we drop out of power save mode - * (and background scan) automatically by way of the - * usual sta power save logic. - */ - ss->ss_flags |= IEEE80211_SCAN_NOPICK - | IEEE80211_SCAN_BGSCAN - | flags - ; - /* if previous scan completed, restart */ - if (ss->ss_next >= ss->ss_last) { - if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - vap->iv_stats.is_scan_active++; - else - vap->iv_stats.is_scan_passive++; - /* - * NB: beware of the scan cache being flushed; - * if the channel list is empty use the - * scan_start method to populate it. - */ - ss->ss_next = 0; - if (ss->ss_last != 0) - ss->ss_ops->scan_restart(ss, vap); - else { - ss->ss_ops->scan_start(ss, vap); -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(vap)) - scan_dump(ss); -#endif /* IEEE80211_DEBUG */ - } - } - /* NB: flush frames rx'd before 1st channel change */ - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; - SCAN_PRIVATE(ss)->ss_duration = duration; - ss->ss_maxdwell = duration; - ic->ic_flags |= IEEE80211_F_SCAN; - ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); - } else { - /* XXX msg+stat */ - } - } else { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: %s scan already in progress\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); - } - IEEE80211_UNLOCK(ic); - - /* NB: racey, does it matter? */ - return (ic->ic_flags & IEEE80211_F_SCAN); + /* + * XXX TODO: pull apart the bgscan logic into whatever + * belongs here and whatever belongs in the software + * scanner. + * + * XXX TODO: ops + */ + return (ieee80211_swscan_bg_scan(scan, vap, flags)); } /* @@ -680,25 +437,9 @@ ieee80211_bg_scan(struct ieee80211vap *vap, int flags) void ieee80211_cancel_scan(struct ieee80211vap *vap) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - IEEE80211_LOCK(ic); - if ((ic->ic_flags & IEEE80211_F_SCAN) && - ss->ss_vap == vap && - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: cancel %s scan\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? - "active" : "passive"); - - /* clear bg scan NOPICK and mark cancel request */ - ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; - /* wake up the scan task */ - scan_signal(ss); - } - IEEE80211_UNLOCK(ic); + /* XXX TODO: ops */ + ieee80211_swscan_cancel_scan(vap); } /* @@ -707,24 +448,9 @@ ieee80211_cancel_scan(struct ieee80211vap *vap) void ieee80211_cancel_anyscan(struct ieee80211vap *vap) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - IEEE80211_LOCK(ic); - if ((ic->ic_flags & IEEE80211_F_SCAN) && - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: cancel %s scan\n", __func__, - ss->ss_flags & IEEE80211_SCAN_ACTIVE ? - "active" : "passive"); - - /* clear bg scan NOPICK and mark cancel request */ - ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; - /* wake up the scan task */ - scan_signal(ss); - } - IEEE80211_UNLOCK(ic); + /* XXX TODO: ops */ + ieee80211_swscan_cancel_anyscan(vap); } /* @@ -734,13 +460,9 @@ ieee80211_cancel_anyscan(struct ieee80211vap *vap) void ieee80211_scan_next(struct ieee80211vap *vap) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - /* wake up the scan task */ - IEEE80211_LOCK(ic); - scan_signal(ss); - IEEE80211_UNLOCK(ic); + /* XXX TODO: ops */ + ieee80211_swscan_scan_next(vap); } /* @@ -753,10 +475,15 @@ ieee80211_scan_done(struct ieee80211vap *vap) struct ieee80211com *ic = vap->iv_ic; struct ieee80211_scan_state *ss; + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); + IEEE80211_LOCK(ic); ss = ic->ic_scan; ss->ss_next = ss->ss_last; /* all channels are complete */ - scan_signal(ss); + + /* XXX TODO: ops */ + ieee80211_swscan_scan_done(vap); + IEEE80211_UNLOCK(ic); } @@ -771,293 +498,14 @@ void ieee80211_probe_curchan(struct ieee80211vap *vap, int force) { struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - struct ifnet *ifp = vap->iv_ifp; - int i; if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) { ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; return; } - /* - * Send directed probe requests followed by any - * broadcast probe request. - * XXX remove dependence on ic/vap->iv_bss - */ - for (i = 0; i < ss->ss_nssid; i++) - ieee80211_send_probereq(vap->iv_bss, - vap->iv_myaddr, ifp->if_broadcastaddr, - ifp->if_broadcastaddr, - ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); - if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) - ieee80211_send_probereq(vap->iv_bss, - vap->iv_myaddr, ifp->if_broadcastaddr, - ifp->if_broadcastaddr, - "", 0); -} - -/* - * Scan curchan. If this is an active scan and the channel - * is not marked passive then send probe request frame(s). - * Arrange for the channel change after maxdwell ticks. - */ -static void -scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) -{ - struct ieee80211vap *vap = ss->ss_vap; - - IEEE80211_LOCK(vap->iv_ic); - if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - ieee80211_probe_curchan(vap, 0); - callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, - maxdwell, scan_signal, ss); - IEEE80211_UNLOCK(vap->iv_ic); -} - -static void -scan_signal(void *arg) -{ - struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; - - IEEE80211_LOCK_ASSERT(ss->ss_ic); - - cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv); -} - -/* - * Handle mindwell requirements completed; initiate a channel - * change to the next channel asap. - */ -static void -scan_mindwell(struct ieee80211_scan_state *ss) -{ - struct ieee80211com *ic = ss->ss_ic; - - IEEE80211_LOCK(ic); - scan_signal(ss); - IEEE80211_UNLOCK(ic); -} - -static void -scan_task(void *arg, int pending) -{ -#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) - struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; - struct ieee80211vap *vap = ss->ss_vap; - struct ieee80211com *ic = ss->ss_ic; - struct ieee80211_channel *chan; - unsigned long maxdwell, scanend; - int scandone = 0; - - IEEE80211_LOCK(ic); - if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { - /* Cancelled before we started */ - goto done; - } - - if (ss->ss_next == ss->ss_last) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: no channels to scan\n", __func__); - scandone = 1; - goto done; - } - - if (vap->iv_opmode == IEEE80211_M_STA && - vap->iv_state == IEEE80211_S_RUN) { - if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { - /* Enable station power save mode */ - vap->iv_sta_ps(vap, 1); - /* - * Use an 1ms delay so the null data frame has a chance - * to go out. - * XXX Should use M_TXCB mechanism to eliminate this. - */ - cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, - IEEE80211_LOCK_OBJ(ic), hz / 1000); - if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) - goto done; - } - } - - scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; - IEEE80211_UNLOCK(ic); - ic->ic_scan_start(ic); /* notify driver */ - IEEE80211_LOCK(ic); - - for (;;) { - scandone = (ss->ss_next >= ss->ss_last) || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; - if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || - (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || - time_after(ticks + ss->ss_mindwell, scanend)) - break; - - chan = ss->ss_chans[ss->ss_next++]; - - /* - * Watch for truncation due to the scan end time. - */ - if (time_after(ticks + ss->ss_maxdwell, scanend)) - maxdwell = scanend - ticks; - else - maxdwell = ss->ss_maxdwell; - - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", - __func__, - ieee80211_chan2ieee(ic, ic->ic_curchan), - channel_type(ic->ic_curchan), - ieee80211_chan2ieee(ic, chan), channel_type(chan), - (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && - (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? - "active" : "passive", - ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); - - /* - * Potentially change channel and phy mode. - */ - ic->ic_curchan = chan; - ic->ic_rt = ieee80211_get_ratetable(chan); - IEEE80211_UNLOCK(ic); - /* - * Perform the channel change and scan unlocked so the driver - * may sleep. Once set_channel returns the hardware has - * completed the channel change. - */ - ic->ic_set_channel(ic); - ieee80211_radiotap_chan_change(ic); - - /* - * Scan curchan. Drivers for "intelligent hardware" - * override ic_scan_curchan to tell the device to do - * the work. Otherwise we manage the work outselves; - * sending a probe request (as needed), and arming the - * timeout to switch channels after maxdwell ticks. - * - * scan_curchan should only pause for the time required to - * prepare/initiate the hardware for the scan (if at all), the - * below condvar is used to sleep for the channels dwell time - * and allows it to be signalled for abort. - */ - ic->ic_scan_curchan(ss, maxdwell); - IEEE80211_LOCK(ic); - - SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; - /* clear mindwell lock and initial channel change flush */ - SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; - - if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) - continue; - - /* Wait to be signalled to scan the next channel */ - cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); - } - if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) - goto done; - - IEEE80211_UNLOCK(ic); - ic->ic_scan_end(ic); /* notify driver */ - IEEE80211_LOCK(ic); - - /* - * Since a cancellation may have occured during one of the - * driver calls (whilst unlocked), update scandone. - */ - if (scandone == 0 && - ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) { - /* XXX printf? */ - if_printf(vap->iv_ifp, - "%s: OOPS! scan cancelled during driver call!\n", - __func__); - } - scandone |= ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0); - - /* - * Record scan complete time. Note that we also do - * this when canceled so any background scan will - * not be restarted for a while. - */ - if (scandone) - ic->ic_lastscan = ticks; - /* return to the bss channel */ - if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && - ic->ic_curchan != ic->ic_bsschan) { - ieee80211_setupcurchan(ic, ic->ic_bsschan); - IEEE80211_UNLOCK(ic); - ic->ic_set_channel(ic); - ieee80211_radiotap_chan_change(ic); - IEEE80211_LOCK(ic); - } - /* clear internal flags and any indication of a pick */ - SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; - ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; - - /* - * If not canceled and scan completed, do post-processing. - * If the callback function returns 0, then it wants to - * continue/restart scanning. Unfortunately we needed to - * notify the driver to end the scan above to avoid having - * rx frames alter the scan candidate list. - */ - if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && - !ss->ss_ops->scan_end(ss, vap) && - (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && - time_before(ticks + ss->ss_mindwell, scanend)) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: done, restart " - "[ticks %u, dwell min %lu scanend %lu]\n", - __func__, - ticks, ss->ss_mindwell, scanend); - ss->ss_next = 0; /* reset to begining */ - if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) - vap->iv_stats.is_scan_active++; - else - vap->iv_stats.is_scan_passive++; - - ss->ss_ops->scan_restart(ss, vap); /* XXX? */ - ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); - IEEE80211_UNLOCK(ic); - return; - } - - /* past here, scandone is ``true'' if not in bg mode */ - if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) - scandone = 1; - - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", - __func__, scandone ? "done" : "stopped", - ticks, ss->ss_mindwell, scanend); - /* - * Clear the SCAN bit first in case frames are - * pending on the station power save queue. If - * we defer this then the dispatch of the frames - * may generate a request to cancel scanning. - */ -done: - ic->ic_flags &= ~IEEE80211_F_SCAN; - /* - * Drop out of power save mode when a scan has - * completed. If this scan was prematurely terminated - * because it is a background scan then don't notify - * the ap; we'll either return to scanning after we - * receive the beacon frame or we'll drop out of power - * save mode because the beacon indicates we have frames - * waiting for us. - */ - if (scandone) { - vap->iv_sta_ps(vap, 0); - if (ss->ss_next >= ss->ss_last) { - ieee80211_notify_scan_done(vap); - ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; - } - } - SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); - ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); - IEEE80211_UNLOCK(ic); -#undef ISCAN_REP + /* XXX TODO: ops */ + ieee80211_swscan_probe_curchan(vap, force); } #ifdef IEEE80211_DEBUG @@ -1086,8 +534,8 @@ dump_country(const uint8_t *ie) printf("]"); } -static void -dump_probe_beacon(uint8_t subtype, int isnew, +void +ieee80211_scan_dump_probe_beacon(uint8_t subtype, int isnew, const uint8_t mac[IEEE80211_ADDR_LEN], const struct ieee80211_scanparams *sp, int rssi) { @@ -1114,49 +562,14 @@ dump_probe_beacon(uint8_t subtype, int isnew, */ void ieee80211_add_scan(struct ieee80211vap *vap, + struct ieee80211_channel *curchan, const struct ieee80211_scanparams *sp, const struct ieee80211_frame *wh, int subtype, int rssi, int noise) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_scan_state *ss = ic->ic_scan; - /* XXX locking */ - /* - * Frames received during startup are discarded to avoid - * using scan state setup on the initial entry to the timer - * callback. This can occur because the device may enable - * rx prior to our doing the initial channel change in the - * timer routine. - */ - if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) - return; -#ifdef IEEE80211_DEBUG - if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) - dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); -#endif - if (ss->ss_ops != NULL && - ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise)) { - /* - * If we've reached the min dwell time terminate - * the timer so we'll switch to the next channel. - */ - if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && - time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { - IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: chan %3d%c min dwell met (%u > %lu)\n", - __func__, - ieee80211_chan2ieee(ic, ic->ic_curchan), - channel_type(ic->ic_curchan), - ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); - SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; - /* - * NB: trigger at next clock tick or wait for the - * hardware. - */ - ic->ic_scan_mindwell(ss); - } - } + return (ieee80211_swscan_add_scan(vap, curchan, sp, wh, subtype, + rssi, noise)); } /* diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h index 3f13e17..8b54186 100644 --- a/sys/net80211/ieee80211_scan.h +++ b/sys/net80211/ieee80211_scan.h @@ -148,6 +148,7 @@ struct ieee80211_channel *ieee80211_scan_pickchannel(struct ieee80211com *, int) struct ieee80211_scanparams; void ieee80211_add_scan(struct ieee80211vap *, + struct ieee80211_channel *, const struct ieee80211_scanparams *, const struct ieee80211_frame *, int subtype, int rssi, int noise); @@ -273,6 +274,7 @@ struct ieee80211_scanner { struct ieee80211_scan_state *, int); /* add an entry to the cache */ int (*scan_add)(struct ieee80211_scan_state *, + struct ieee80211_channel *, const struct ieee80211_scanparams *, const struct ieee80211_frame *, int subtype, int rssi, int noise); @@ -299,4 +301,14 @@ void ieee80211_scanner_unregister(enum ieee80211_opmode, const struct ieee80211_scanner *); void ieee80211_scanner_unregister_all(const struct ieee80211_scanner *); const struct ieee80211_scanner *ieee80211_scanner_get(enum ieee80211_opmode); +void ieee80211_scan_update_locked(struct ieee80211vap *vap, + const struct ieee80211_scanner *scan); +void ieee80211_scan_copy_ssid(struct ieee80211vap *vap, + struct ieee80211_scan_state *ss, + int nssid, const struct ieee80211_scan_ssid ssids[]); +void ieee80211_scan_dump_probe_beacon(uint8_t subtype, int isnew, + const uint8_t mac[IEEE80211_ADDR_LEN], + const struct ieee80211_scanparams *sp, int rssi); +void ieee80211_scan_dump(struct ieee80211_scan_state *ss); + #endif /* _NET80211_IEEE80211_SCAN_H_ */ diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index c791ea1..1fb45b0 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/ethernet.h> @@ -227,6 +228,7 @@ sta_flush_table(struct sta_table *st) */ static int sta_add(struct ieee80211_scan_state *ss, + struct ieee80211_channel *curchan, const struct ieee80211_scanparams *sp, const struct ieee80211_frame *wh, int subtype, int rssi, int noise) @@ -309,15 +311,15 @@ found: * IEEE80211_BPARSE_OFFCHAN. */ c = ieee80211_find_channel_byieee(ic, sp->chan, - ic->ic_curchan->ic_flags); + curchan->ic_flags); if (c != NULL) { ise->se_chan = c; } else if (ise->se_chan == NULL) { /* should not happen, pick something */ - ise->se_chan = ic->ic_curchan; + ise->se_chan = curchan; } } else - ise->se_chan = ic->ic_curchan; + ise->se_chan = curchan; if (IEEE80211_IS_CHAN_HT(ise->se_chan) && sp->htcap == NULL) { /* Demote legacy networks to a non-HT channel. */ c = ieee80211_find_channel(ic, ise->se_chan->ic_freq, @@ -599,10 +601,12 @@ makescanlist(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, * so if the desired mode is 11g, then use * the 11b channel list but upgrade the mode. */ - if (vap->iv_des_mode != IEEE80211_MODE_11G || - mode != IEEE80211_MODE_11B) - continue; - mode = IEEE80211_MODE_11G; /* upgrade */ + if (vap->iv_des_mode == IEEE80211_MODE_11G) { + if (mode == IEEE80211_MODE_11G) /* Skip the G check */ + continue; + else if (mode == IEEE80211_MODE_11B) + mode = IEEE80211_MODE_11G; /* upgrade */ + } } } else { /* @@ -732,7 +736,7 @@ sta_cancel(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) return 0; } -/* unalligned little endian access */ +/* unaligned little endian access */ #define LE_READ_2(p) \ ((uint16_t) \ ((((const uint8_t *)(p))[0] ) | \ diff --git a/sys/net80211/ieee80211_scan_sw.c b/sys/net80211/ieee80211_scan_sw.c new file mode 100644 index 0000000..70b4673 --- /dev/null +++ b/sys/net80211/ieee80211_scan_sw.c @@ -0,0 +1,948 @@ +/*- + * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +/* + * IEEE 802.11 scanning support. + */ +#include "opt_wlan.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/condvar.h> + +#include <sys/socket.h> + +#include <net/if.h> +#include <net/if_var.h> +#include <net/if_media.h> +#include <net/ethernet.h> + +#include <net80211/ieee80211_var.h> + +#include <net80211/ieee80211_scan_sw.h> + +#include <net/bpf.h> + +struct scan_state { + struct ieee80211_scan_state base; /* public state */ + + u_int ss_iflags; /* flags used internally */ +#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ +#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ +#define ISCAN_CANCEL 0x0004 /* cancel current scan */ +#define ISCAN_ABORT 0x0008 /* end the scan immediately */ + unsigned long ss_chanmindwell; /* min dwell on curchan */ + unsigned long ss_scanend; /* time scan must stop */ + u_int ss_duration; /* duration for next scan */ + struct task ss_scan_task; /* scan execution */ + struct cv ss_scan_cv; /* scan signal */ + struct callout ss_scan_timer; /* scan timer */ +}; +#define SCAN_PRIVATE(ss) ((struct scan_state *) ss) + +/* + * Amount of time to go off-channel during a background + * scan. This value should be large enough to catch most + * ap's but short enough that we can return on-channel + * before our listen interval expires. + * + * XXX tunable + * XXX check against configured listen interval + */ +#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) + +/* + * Roaming-related defaults. RSSI thresholds are as returned by the + * driver (.5dBm). Transmit rate thresholds are IEEE rate codes (i.e + * .5M units) or MCS. + */ +/* rssi thresholds */ +#define ROAM_RSSI_11A_DEFAULT 14 /* 11a bss */ +#define ROAM_RSSI_11B_DEFAULT 14 /* 11b bss */ +#define ROAM_RSSI_11BONLY_DEFAULT 14 /* 11b-only bss */ +/* transmit rate thresholds */ +#define ROAM_RATE_11A_DEFAULT 2*12 /* 11a bss */ +#define ROAM_RATE_11B_DEFAULT 2*5 /* 11b bss */ +#define ROAM_RATE_11BONLY_DEFAULT 2*1 /* 11b-only bss */ +#define ROAM_RATE_HALF_DEFAULT 2*6 /* half-width 11a/g bss */ +#define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */ +#define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */ + +static void scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void scan_mindwell(struct ieee80211_scan_state *); +static void scan_signal(void *); +static void scan_task(void *, int); + +MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); + +void +ieee80211_swscan_attach(struct ieee80211com *ic) +{ + struct scan_state *ss; + + ss = (struct scan_state *) malloc(sizeof(struct scan_state), + M_80211_SCAN, M_NOWAIT | M_ZERO); + if (ss == NULL) { + ic->ic_scan = NULL; + return; + } + callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); + cv_init(&ss->ss_scan_cv, "scan"); + TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss); + + ic->ic_scan = &ss->base; + ss->base.ss_ic = ic; + + ic->ic_scan_curchan = scan_curchan; + ic->ic_scan_mindwell = scan_mindwell; + + /* + * TODO: all of the non-vap scan calls should be methods! + */ +} + +void +ieee80211_swscan_detach(struct ieee80211com *ic) +{ + struct ieee80211_scan_state *ss = ic->ic_scan; + + if (ss != NULL) { + IEEE80211_LOCK(ic); + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; + scan_signal(ss); + IEEE80211_UNLOCK(ic); + ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); + KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, + ("scan still running")); + + /* + * For now, do the ss_ops detach here rather + * than ieee80211_scan_detach(). + * + * I'll figure out how to cleanly split things up + * at a later date. + */ + if (ss->ss_ops != NULL) { + ss->ss_ops->scan_detach(ss); + ss->ss_ops = NULL; + } + ic->ic_scan = NULL; + free(SCAN_PRIVATE(ss), M_80211_SCAN); + } +} + +void +ieee80211_swscan_vattach(struct ieee80211vap *vap) +{ + /* nothing to do for now */ + /* + * TODO: all of the vap scan calls should be methods! + */ + +} + +void +ieee80211_swscan_vdetach(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + + IEEE80211_LOCK_ASSERT(ic); + ss = ic->ic_scan; + if (ss != NULL && ss->ss_vap == vap) { + if (ic->ic_flags & IEEE80211_F_SCAN) { + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; + scan_signal(ss); + } + } +} + +void +ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + /* NB: flush frames rx'd before 1st channel change */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + SCAN_PRIVATE(ss)->ss_duration = duration; +} + +void +ieee80211_swscan_run_scan_task(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); +} + +/* + * Start a scan unless one is already going. + */ +static int +ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, u_int duration, + u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK_ASSERT(ic); + + if (ic->ic_flags & IEEE80211_F_CSAPENDING) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: scan inhibited by pending channel change\n", __func__); + } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" + , __func__ + , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" + , duration, mindwell, maxdwell + , ieee80211_phymode_name[vap->iv_des_mode] + , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" + , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" + , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" + , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" + , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" + , flags & IEEE80211_SCAN_ONCE ? ", once" : "" + ); + + ieee80211_scan_update_locked(vap, scan); + if (ss->ss_ops != NULL) { + if ((flags & IEEE80211_SCAN_NOSSID) == 0) + ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); + + /* NB: top 4 bits for internal use */ + ss->ss_flags = flags & 0xfff; + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + if (flags & IEEE80211_SCAN_FLUSH) + ss->ss_ops->scan_flush(ss); + if (flags & IEEE80211_SCAN_BGSCAN) + ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; + + /* Set duration for this particular scan */ + ieee80211_swscan_set_scan_duration(vap, duration); + + ss->ss_next = 0; + ss->ss_mindwell = mindwell; + ss->ss_maxdwell = maxdwell; + /* NB: scan_start must be before the scan runtask */ + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + ieee80211_scan_dump(ss); +#endif /* IEEE80211_DEBUG */ + ic->ic_flags |= IEEE80211_F_SCAN; + + /* Start scan task */ + ieee80211_swscan_run_scan_task(vap); + } + return 1; + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + return 0; +} + + +/* + * Start a scan unless one is already going. + * + * Called without the comlock held; grab the comlock as appropriate. + */ +int +ieee80211_swscan_start_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + int result; + + IEEE80211_UNLOCK_ASSERT(ic); + + IEEE80211_LOCK(ic); + result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); + IEEE80211_UNLOCK(ic); + + return result; +} + +/* + * Check the scan cache for an ap/channel to use; if that + * fails then kick off a new scan. + * + * Called with the comlock held. + * + * XXX TODO: split out! + */ +int +ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + int result; + + IEEE80211_LOCK_ASSERT(ic); + + if (ss->ss_ops != NULL) { + /* XXX verify ss_ops matches vap->iv_opmode */ + if ((flags & IEEE80211_SCAN_NOSSID) == 0) { + /* + * Update the ssid list and mark flags so if + * we call start_scan it doesn't duplicate work. + */ + ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); + flags |= IEEE80211_SCAN_NOSSID; + } + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && + (flags & IEEE80211_SCAN_FLUSH) == 0 && + time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { + /* + * We're not currently scanning and the cache is + * deemed hot enough to consult. Lock out others + * by marking IEEE80211_F_SCAN while we decide if + * something is already in the scan cache we can + * use. Also discard any frames that might come + * in while temporarily marked as scanning. + */ + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; + ic->ic_flags |= IEEE80211_F_SCAN; + + /* NB: need to use supplied flags in check */ + ss->ss_flags = flags & 0xff; + result = ss->ss_ops->scan_end(ss, vap); + + ic->ic_flags &= ~IEEE80211_F_SCAN; + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; + if (result) { + ieee80211_notify_scan_done(vap); + return 1; + } + } + } + result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, + mindwell, maxdwell, nssid, ssids); + + return result; +} + +/* + * Restart a previous scan. If the previous scan completed + * then we start again using the existing channel list. + */ +int +ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + /* XXX assert unlocked? */ + // IEEE80211_UNLOCK_ASSERT(ic); + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { + u_int duration; + /* + * Go off-channel for a fixed interval that is large + * enough to catch most ap's but short enough that + * we can return on-channel before our listen interval + * expires. + */ + duration = IEEE80211_SCAN_OFFCHANNEL; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan, ticks %u duration %lu\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", + ticks, duration); + + ieee80211_scan_update_locked(vap, scan); + if (ss->ss_ops != NULL) { + ss->ss_vap = vap; + /* + * A background scan does not select a new sta; it + * just refreshes the scan cache. Also, indicate + * the scan logic should follow the beacon schedule: + * we go off-channel and scan for a while, then + * return to the bss channel to receive a beacon, + * then go off-channel again. All during this time + * we notify the ap we're in power save mode. When + * the scan is complete we leave power save mode. + * If any beacon indicates there are frames pending + * for us then we drop out of power save mode + * (and background scan) automatically by way of the + * usual sta power save logic. + */ + ss->ss_flags |= IEEE80211_SCAN_NOPICK + | IEEE80211_SCAN_BGSCAN + | flags + ; + /* if previous scan completed, restart */ + if (ss->ss_next >= ss->ss_last) { + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + /* + * NB: beware of the scan cache being flushed; + * if the channel list is empty use the + * scan_start method to populate it. + */ + ss->ss_next = 0; + if (ss->ss_last != 0) + ss->ss_ops->scan_restart(ss, vap); + else { + ss->ss_ops->scan_start(ss, vap); +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap)) + ieee80211_scan_dump(ss); +#endif /* IEEE80211_DEBUG */ + } + } + ieee80211_swscan_set_scan_duration(vap, duration); + ss->ss_maxdwell = duration; + ic->ic_flags |= IEEE80211_F_SCAN; + ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + } else { + /* XXX msg+stat */ + } + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s scan already in progress\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); + } + IEEE80211_UNLOCK(ic); + + /* NB: racey, does it matter? */ + return (ic->ic_flags & IEEE80211_F_SCAN); +} + +/* + * Cancel any scan currently going on for the specified vap. + */ +void +ieee80211_swscan_cancel_scan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + ss->ss_vap == vap && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* wake up the scan task */ + scan_signal(ss); + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n", + __func__, + !! (ic->ic_flags & IEEE80211_F_SCAN), + (ss->ss_vap == vap ? "match" : "nomatch"), + !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL)); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Cancel any scan currently going on. + */ +void +ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_LOCK(ic); + if ((ic->ic_flags & IEEE80211_F_SCAN) && + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: cancel %s scan\n", __func__, + ss->ss_flags & IEEE80211_SCAN_ACTIVE ? + "active" : "passive"); + + /* clear bg scan NOPICK and mark cancel request */ + ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; + /* wake up the scan task */ + scan_signal(ss); + } else { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n", + __func__, + !! (ic->ic_flags & IEEE80211_F_SCAN), + (ss->ss_vap == vap ? "match" : "nomatch"), + !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL)); + } + IEEE80211_UNLOCK(ic); +} + +/* + * Public access to scan_next for drivers that manage + * scanning themselves (e.g. for firmware-based devices). + */ +void +ieee80211_swscan_scan_next(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); + + /* wake up the scan task */ + IEEE80211_LOCK(ic); + scan_signal(ss); + IEEE80211_UNLOCK(ic); +} + +/* + * Public access to scan_next for drivers that are not able to scan single + * channels (e.g. for firmware-based devices). + */ +void +ieee80211_swscan_scan_done(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss; + + IEEE80211_LOCK_ASSERT(ic); + + ss = ic->ic_scan; + scan_signal(ss); +} + +/* + * Probe the curent channel, if allowed, while scanning. + * If the channel is not marked passive-only then send + * a probe request immediately. Otherwise mark state and + * listen for beacons on the channel; if we receive something + * then we'll transmit a probe request. + */ +void +ieee80211_swscan_probe_curchan(struct ieee80211vap *vap, int force) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + struct ifnet *ifp = vap->iv_ifp; + int i; + + /* + * Send directed probe requests followed by any + * broadcast probe request. + * XXX remove dependence on ic/vap->iv_bss + */ + for (i = 0; i < ss->ss_nssid; i++) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); + if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) + ieee80211_send_probereq(vap->iv_bss, + vap->iv_myaddr, ifp->if_broadcastaddr, + ifp->if_broadcastaddr, + "", 0); +} + +/* + * Scan curchan. If this is an active scan and the channel + * is not marked passive then send probe request frame(s). + * Arrange for the channel change after maxdwell ticks. + */ +static void +scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) +{ + struct ieee80211vap *vap = ss->ss_vap; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: calling; maxdwell=%lu\n", + __func__, + maxdwell); + IEEE80211_LOCK(vap->iv_ic); + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + ieee80211_probe_curchan(vap, 0); + callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, + maxdwell, scan_signal, ss); + IEEE80211_UNLOCK(vap->iv_ic); +} + +static void +scan_signal(void *arg) +{ + struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + + IEEE80211_LOCK_ASSERT(ss->ss_ic); + cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv); +} + +/* + * Handle mindwell requirements completed; initiate a channel + * change to the next channel asap. + */ +static void +scan_mindwell(struct ieee80211_scan_state *ss) +{ + struct ieee80211com *ic = ss->ss_ic; + + IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); + + IEEE80211_LOCK(ic); + scan_signal(ss); + IEEE80211_UNLOCK(ic); +} + +static void +scan_task(void *arg, int pending) +{ +#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) + struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; + struct ieee80211vap *vap = ss->ss_vap; + struct ieee80211com *ic = ss->ss_ic; + struct ieee80211_channel *chan; + unsigned long maxdwell, scanend; + int scandone = 0; + + IEEE80211_LOCK(ic); + if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { + /* Cancelled before we started */ + goto done; + } + + if (ss->ss_next == ss->ss_last) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: no channels to scan\n", __func__); + scandone = 1; + goto done; + } + + if (vap->iv_opmode == IEEE80211_M_STA && + vap->iv_state == IEEE80211_S_RUN) { + if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { + /* Enable station power save mode */ + vap->iv_sta_ps(vap, 1); + /* + * Use an 1ms delay so the null data frame has a chance + * to go out. + * XXX Should use M_TXCB mechanism to eliminate this. + */ + cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, + IEEE80211_LOCK_OBJ(ic), hz / 1000); + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) + goto done; + } + } + + scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; + + /* XXX scan state can change! Re-validate scan state! */ + + IEEE80211_UNLOCK(ic); + ic->ic_scan_start(ic); /* notify driver */ + IEEE80211_LOCK(ic); + + for (;;) { + + scandone = (ss->ss_next >= ss->ss_last) || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: loop start; scandone=%d\n", + __func__, + scandone); + + if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || + (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || + time_after(ticks + ss->ss_mindwell, scanend)) + break; + + chan = ss->ss_chans[ss->ss_next++]; + + /* + * Watch for truncation due to the scan end time. + */ + if (time_after(ticks + ss->ss_maxdwell, scanend)) + maxdwell = scanend - ticks; + else + maxdwell = ss->ss_maxdwell; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + ieee80211_channel_type_char(ic->ic_curchan), + ieee80211_chan2ieee(ic, chan), + ieee80211_channel_type_char(chan), + (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && + (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? + "active" : "passive", + ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); + + /* + * Potentially change channel and phy mode. + */ + ic->ic_curchan = chan; + ic->ic_rt = ieee80211_get_ratetable(chan); + IEEE80211_UNLOCK(ic); + /* + * Perform the channel change and scan unlocked so the driver + * may sleep. Once set_channel returns the hardware has + * completed the channel change. + */ + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); + + /* + * Scan curchan. Drivers for "intelligent hardware" + * override ic_scan_curchan to tell the device to do + * the work. Otherwise we manage the work outselves; + * sending a probe request (as needed), and arming the + * timeout to switch channels after maxdwell ticks. + * + * scan_curchan should only pause for the time required to + * prepare/initiate the hardware for the scan (if at all), the + * below condvar is used to sleep for the channels dwell time + * and allows it to be signalled for abort. + */ + ic->ic_scan_curchan(ss, maxdwell); + IEEE80211_LOCK(ic); + + /* XXX scan state can change! Re-validate scan state! */ + + SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; + /* clear mindwell lock and initial channel change flush */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + + if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) + continue; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: waiting\n", __func__); + /* Wait to be signalled to scan the next channel */ + cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); + } + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__); + + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) + goto done; + + IEEE80211_UNLOCK(ic); + ic->ic_scan_end(ic); /* notify driver */ + IEEE80211_LOCK(ic); + /* XXX scan state can change! Re-validate scan state! */ + + /* + * Since a cancellation may have occured during one of the + * driver calls (whilst unlocked), update scandone. + */ + if (scandone == 0 && + ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) { + /* XXX printf? */ + if_printf(vap->iv_ifp, + "%s: OOPS! scan cancelled during driver call (1)!\n", + __func__); + scandone = 1; + } + + /* + * Record scan complete time. Note that we also do + * this when canceled so any background scan will + * not be restarted for a while. + */ + if (scandone) + ic->ic_lastscan = ticks; + /* return to the bss channel */ + if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && + ic->ic_curchan != ic->ic_bsschan) { + ieee80211_setupcurchan(ic, ic->ic_bsschan); + IEEE80211_UNLOCK(ic); + ic->ic_set_channel(ic); + ieee80211_radiotap_chan_change(ic); + IEEE80211_LOCK(ic); + } + /* clear internal flags and any indication of a pick */ + SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; + ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; + + /* + * If not canceled and scan completed, do post-processing. + * If the callback function returns 0, then it wants to + * continue/restart scanning. Unfortunately we needed to + * notify the driver to end the scan above to avoid having + * rx frames alter the scan candidate list. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && + !ss->ss_ops->scan_end(ss, vap) && + (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && + time_before(ticks + ss->ss_mindwell, scanend)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: done, restart " + "[ticks %u, dwell min %lu scanend %lu]\n", + __func__, + ticks, ss->ss_mindwell, scanend); + ss->ss_next = 0; /* reset to begining */ + if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) + vap->iv_stats.is_scan_active++; + else + vap->iv_stats.is_scan_passive++; + + ss->ss_ops->scan_restart(ss, vap); /* XXX? */ + ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); + IEEE80211_UNLOCK(ic); + return; + } + + /* past here, scandone is ``true'' if not in bg mode */ + if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) + scandone = 1; + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", + __func__, scandone ? "done" : "stopped", + ticks, ss->ss_mindwell, scanend); + + /* + * Since a cancellation may have occured during one of the + * driver calls (whilst unlocked), update scandone. + */ + if (scandone == 0 && + ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) { + /* XXX printf? */ + if_printf(vap->iv_ifp, + "%s: OOPS! scan cancelled during driver call (2)!\n", + __func__); + scandone = 1; + } + + /* + * Clear the SCAN bit first in case frames are + * pending on the station power save queue. If + * we defer this then the dispatch of the frames + * may generate a request to cancel scanning. + */ +done: + ic->ic_flags &= ~IEEE80211_F_SCAN; + /* + * Drop out of power save mode when a scan has + * completed. If this scan was prematurely terminated + * because it is a background scan then don't notify + * the ap; we'll either return to scanning after we + * receive the beacon frame or we'll drop out of power + * save mode because the beacon indicates we have frames + * waiting for us. + */ + if (scandone) { + vap->iv_sta_ps(vap, 0); + if (ss->ss_next >= ss->ss_last) { + ieee80211_notify_scan_done(vap); + ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; + } + } + SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); + ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); + IEEE80211_UNLOCK(ic); +#undef ISCAN_REP +} + +/* + * Process a beacon or probe response frame. + */ +void +ieee80211_swscan_add_scan(struct ieee80211vap *vap, + struct ieee80211_channel *curchan, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_scan_state *ss = ic->ic_scan; + + /* XXX locking */ + /* + * Frames received during startup are discarded to avoid + * using scan state setup on the initial entry to the timer + * callback. This can occur because the device may enable + * rx prior to our doing the initial channel change in the + * timer routine. + */ + if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) + return; +#ifdef IEEE80211_DEBUG + if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) + ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); +#endif + if (ss->ss_ops != NULL && + ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) { + /* + * If we've reached the min dwell time terminate + * the timer so we'll switch to the next channel. + */ + if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && + time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, + "%s: chan %3d%c min dwell met (%u > %lu)\n", + __func__, + ieee80211_chan2ieee(ic, ic->ic_curchan), + ieee80211_channel_type_char(ic->ic_curchan), + ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); + SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; + /* + * NB: trigger at next clock tick or wait for the + * hardware. + */ + ic->ic_scan_mindwell(ss); + } + } +} + diff --git a/sys/net80211/ieee80211_scan_sw.h b/sys/net80211/ieee80211_scan_sw.h new file mode 100644 index 0000000..7bb0cc3 --- /dev/null +++ b/sys/net80211/ieee80211_scan_sw.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2015 Adrian Chadd <adrian@FreeBSD.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef __NET80211_IEEE80211_SCAN_SW_H__ +#define __NET80211_IEEE80211_SCAN_SW_H__ + +extern void ieee80211_swscan_attach(struct ieee80211com *ic); +extern void ieee80211_swscan_detach(struct ieee80211com *ic); + +extern void ieee80211_swscan_vattach(struct ieee80211vap *vap); +extern void ieee80211_swscan_vdetach(struct ieee80211vap *vap); + +extern int ieee80211_swscan_start_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]); +extern void ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, + u_int duration); +extern void ieee80211_swscan_run_scan_task(struct ieee80211vap *vap); +extern int ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags, + u_int duration, u_int mindwell, u_int maxdwell, + u_int nssid, const struct ieee80211_scan_ssid ssids[]); +extern int ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan, + struct ieee80211vap *vap, int flags); +extern void ieee80211_swscan_cancel_scan(struct ieee80211vap *vap); +extern void ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap); +extern void ieee80211_swscan_scan_next(struct ieee80211vap *vap); +extern void ieee80211_swscan_scan_done(struct ieee80211vap *vap); +extern void ieee80211_swscan_probe_curchan(struct ieee80211vap *vap, + int force); +extern void ieee80211_swscan_add_scan(struct ieee80211vap *vap, + struct ieee80211_channel *curchan, + const struct ieee80211_scanparams *sp, + const struct ieee80211_frame *wh, + int subtype, int rssi, int noise); + +#endif /* __NET80211_IEEE80211_SCAN_SW_H__ */ diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 8685e4b..267ad11 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -234,6 +234,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) switch (ostate) { case IEEE80211_S_SLEEP: /* XXX wakeup */ + /* XXX driver hook to wakeup the hardware? */ case IEEE80211_S_RUN: IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DISASSOC, @@ -299,12 +300,18 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan_current(vap); break; + case IEEE80211_S_SLEEP: /* beacon miss */ + /* + * XXX if in sleep we need to wakeup the hardware. + */ + /* FALLTHROUGH */ case IEEE80211_S_RUN: /* beacon miss */ /* * Beacon miss. Notify user space and if not * under control of a user application (roaming * manual) kick off a scan to re-connect. */ + ieee80211_sta_leave(ni); if (vap->iv_roaming == IEEE80211_ROAMING_AUTO) ieee80211_check_scan_current(vap); @@ -403,6 +410,7 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); break; case IEEE80211_S_SLEEP: + /* Wake up from sleep */ vap->iv_sta_ps(vap, 0); break; default: @@ -430,9 +438,11 @@ sta_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) ieee80211_node_authorize(ni); /* * Fake association when joining an existing bss. + * + * Don't do this if we're doing SLEEP->RUN. */ - if (ic->ic_newassoc != NULL) - ic->ic_newassoc(vap->iv_bss, ostate != IEEE80211_S_RUN); + if (ic->ic_newassoc != NULL && ostate != IEEE80211_S_SLEEP) + ic->ic_newassoc(vap->iv_bss, (ostate != IEEE80211_S_RUN)); break; case IEEE80211_S_CSA: if (ostate != IEEE80211_S_RUN) @@ -517,7 +527,6 @@ doprint(struct ieee80211vap *vap, int subtype) static int sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#define HAS_SEQ(type) ((type & 0x4) == 0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; @@ -613,7 +622,8 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = nf; - if (HAS_SEQ(type) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { + if ( IEEE80211_HAS_SEQ(type, subtype) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) { uint8_t tid = ieee80211_gettid(wh); if (IEEE80211_QOS_HAS_SEQ(wh) && TID_TO_WME_AC(tid) >= WME_AC_VI) @@ -928,7 +938,7 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) break; } err: - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); out: if (m != NULL) { if (need_tap && ieee80211_radiotap_active_vap(vap)) @@ -1312,6 +1322,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, vap->iv_stats.is_beacon_bad++; return; } + /* * Count frame now that we know it's to be processed. */ @@ -1381,25 +1392,66 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } if (scan.quiet) ic->ic_set_quiet(ni, scan.quiet); + if (scan.tim != NULL) { struct ieee80211_tim_ie *tim = (struct ieee80211_tim_ie *) scan.tim; -#if 0 + /* + * XXX Check/debug this code; see if it's about + * the right time to force the VAP awake if we + * receive a frame destined for us? + */ int aid = IEEE80211_AID(ni->ni_associd); int ix = aid / NBBY; int min = tim->tim_bitctl &~ 1; int max = tim->tim_len + min - 4; - if ((tim->tim_bitctl&1) || - (min <= ix && ix <= max && - isset(tim->tim_bitmap - min, aid))) { - /* - * XXX Do not let bg scan kick off - * we are expecting data. + int tim_ucast = 0, tim_mcast = 0; + + /* + * Only do this for unicast traffic in the TIM + * The multicast traffic notification for + * the scan notification stuff should occur + * differently. + */ + if (min <= ix && ix <= max && + isset(tim->tim_bitmap - min, aid)) { + tim_ucast = 1; + } + + /* + * Do a separate notification + * for the multicast bit being set. + */ + if (tim->tim_bitctl & 1) { + tim_mcast = 1; + } + + /* + * If the TIM indicates there's traffic for + * us then get us out of STA mode powersave. + */ + if (tim_ucast == 1) { + + /* + * Wake us out of SLEEP state if we're + * in it; and if we're doing bgscan + * then wake us out of STA powersave. + */ + ieee80211_sta_tim_notify(vap, 1); + + /* + * This is preventing us from + * continuing a bgscan; because it + * tricks the contbgscan() + * routine to think there's always + * traffic for us. + * + * I think we need both an RX and + * TX ic_lastdata field. */ ic->ic_lastdata = ticks; - vap->iv_sta_ps(vap, 0); } -#endif + ni->ni_dtim_count = tim->tim_count; ni->ni_dtim_period = tim->tim_period; } @@ -1432,8 +1484,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, * our ap. */ if (ic->ic_flags & IEEE80211_F_SCAN) { - ieee80211_add_scan(vap, &scan, wh, - subtype, rssi, nf); + ieee80211_add_scan(vap, ic->ic_curchan, + &scan, wh, subtype, rssi, nf); } else if (contbgscan(vap)) { ieee80211_bg_scan(vap, 0); } else if (startbgscan(vap)) { @@ -1446,6 +1498,14 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, } /* + * Put the station to sleep if we haven't seen + * traffic in a while. + */ + IEEE80211_LOCK(ic); + ieee80211_sta_ps_timer_check(vap); + IEEE80211_UNLOCK(ic); + + /* * If we've had a channel width change (eg HT20<->HT40) * then schedule a delayed driver notification. */ @@ -1469,7 +1529,8 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, ieee80211_probe_curchan(vap, 1); ic->ic_flags_ext &= ~IEEE80211_FEXT_PROBECHAN; } - ieee80211_add_scan(vap, &scan, wh, subtype, rssi, nf); + ieee80211_add_scan(vap, ic->ic_curchan, &scan, wh, + subtype, rssi, nf); return; } break; diff --git a/sys/net80211/ieee80211_superg.c b/sys/net80211/ieee80211_superg.c index 461b5a1..81a4b43 100644 --- a/sys/net80211/ieee80211_superg.c +++ b/sys/net80211/ieee80211_superg.c @@ -38,11 +38,12 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> -#include <net/bpf.h> -#include <net/ethernet.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_llc.h> #include <net/if_media.h> +#include <net/bpf.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_input.h> @@ -479,7 +480,7 @@ ff_transmit(struct ieee80211_node *ni, struct mbuf *m) /* NB: IFQ_HANDOFF reclaims mbuf */ ieee80211_free_node(ni); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } } else ieee80211_free_node(ni); diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 174fabc..4710697 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -629,6 +629,7 @@ MALLOC_DECLARE(M_80211_VAP); #define IEEE80211_C_MONITOR 0x00010000 /* CAPABILITY: monitor mode */ #define IEEE80211_C_DFS 0x00020000 /* CAPABILITY: DFS/radar avail*/ #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ +#define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ @@ -710,6 +711,7 @@ int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *); uint32_t ieee80211_mac_hash(const struct ieee80211com *, const uint8_t addr[IEEE80211_ADDR_LEN]); +char ieee80211_channel_type_char(const struct ieee80211_channel *c); void ieee80211_radiotap_attach(struct ieee80211com *, struct ieee80211_radiotap_header *th, int tlen, diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index d95b45f..fc77259 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_llc.h> #include <net/ethernet.h> @@ -257,14 +258,14 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) */ mcopy = m_copypacket(m, M_NOWAIT); if (mcopy == NULL) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* XXX stat + msg */ continue; } ni = ieee80211_find_txnode(vap, eh->ether_dhost); if (ni == NULL) { /* NB: ieee80211_find_txnode does stat+msg */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(mcopy); continue; } @@ -275,7 +276,7 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) eh->ether_dhost, NULL, "%s", "classification failure"); vap->iv_stats.is_tx_classify++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); m_freem(mcopy); ieee80211_free_node(ni); continue; @@ -298,10 +299,10 @@ ieee80211_dwds_mcast(struct ieee80211vap *vap0, struct mbuf *m) err = ieee80211_parent_xmitpkt(ic, mcopy); if (err) { /* NB: IFQ_HANDOFF reclaims mbuf */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); if_inc_counter(ifp, IFCOUNTER_OBYTES, m->m_pkthdr.len); @@ -409,7 +410,6 @@ wds_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) static int wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) { -#define HAS_SEQ(type) ((type & 0x4) == 0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ifnet *ifp = vap->iv_ifp; @@ -491,7 +491,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) } IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = nf; - if (HAS_SEQ(type)) { + if (IEEE80211_HAS_SEQ(type, subtype)) { uint8_t tid = ieee80211_gettid(wh); if (IEEE80211_QOS_HAS_SEQ(wh) && TID_TO_WME_AC(tid) >= WME_AC_VI) @@ -733,7 +733,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, int rssi, int nf) break; } err: - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); out: if (m != NULL) { if (need_tap && ieee80211_radiotap_active_vap(vap)) |