Index: ieee80211_scan_sta.c =================================================================== --- ieee80211_scan_sta.c (revision 244051) +++ ieee80211_scan_sta.c (working copy) @@ -51,6 +51,7 @@ #ifdef IEEE80211_SUPPORT_MESH #include #endif +#include #include @@ -1567,6 +1568,7 @@ struct sta_table *st = ss->ss_priv; struct sta_entry *selbs; struct ieee80211_channel *chan; + struct ieee80211com *ic = vap->iv_ic; KASSERT(vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_AHDEMO || @@ -1612,15 +1614,13 @@ */ if (vap->iv_des_chan == IEEE80211_CHAN_ANYC || IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { - struct ieee80211com *ic = vap->iv_ic; - chan = adhoc_pick_channel(ss, 0); - if (chan != NULL) - chan = ieee80211_ht_adjust_channel(ic, - chan, vap->iv_flags_ht); } else chan = vap->iv_des_chan; if (chan != NULL) { + struct ieee80211com *ic = vap->iv_ic; + chan = ieee80211_ht_adjust_channel(ic, + chan, vap->iv_flags_ht); ieee80211_create_ibss(vap, chan); return 1; } @@ -1644,8 +1644,40 @@ chan = selbs->base.se_chan; if (selbs->se_flags & STA_DEMOTE11B) chan = demote11b(vap, chan); + chan = ieee80211_ht_adjust_channel(ic, + chan, vap->iv_flags_ht); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: calling sta_join\n", __func__); if (!ieee80211_sta_join(vap, chan, &selbs->base)) goto notfound; + + /* + * Setup HT state for this node; otherwise it won't get + * picked up when it creates the BSS. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: htinfo=%p, htcap=%p\n", + __func__, + selbs->base.se_ies.htinfo_ie, + selbs->base.se_ies.htcap_ie); + if (selbs->base.se_ies.htinfo_ie != NULL && + selbs->base.se_ies.htcap_ie != NULL && + vap->iv_flags_ht & IEEE80211_FHT_HT) { + ieee80211_ht_node_init(vap->iv_bss); + ieee80211_ht_updateparams(vap->iv_bss, + selbs->base.se_ies.htcap_ie, + selbs->base.se_ies.htinfo_ie); + ieee80211_setup_htrates(vap->iv_bss, + selbs->base.se_ies.htcap_ie, + IEEE80211_F_JOIN | IEEE80211_F_DOBRS); + ieee80211_setup_basic_htrates(vap->iv_bss, + selbs->base.se_ies.htinfo_ie); + ieee80211_node_setuptxparms(vap->iv_bss); + ieee80211_ratectl_node_init(vap->iv_bss); + } + /* XXX else check for ath FF? */ + /* XXX setup QoS */ + return 1; /* terminate scan */ } Index: ieee80211_proto.c =================================================================== --- ieee80211_proto.c (revision 244062) +++ ieee80211_proto.c (working copy) @@ -1787,6 +1787,13 @@ IF_LOCK(&vap->iv_ifp->if_snd); vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; IF_UNLOCK(&vap->iv_ifp->if_snd); + + /* + * This kick-starts the VAP TX queue if it's not already + * started. + * + * It likely should call a vap method to do this instead. + */ if_start(vap->iv_ifp); /* bring up any vaps waiting on us */ @@ -1801,7 +1808,7 @@ ieee80211_scan_flush(vap); /* XXX NB: cast for altq */ - ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); + vap->iv_flush(vap); } done: IEEE80211_UNLOCK(ic); Index: ieee80211_node.c =================================================================== --- ieee80211_node.c (revision 244051) +++ ieee80211_node.c (working copy) @@ -380,7 +380,10 @@ IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) ieee80211_dfs_cac_clear(ic, ic->ic_bsschan); ic->ic_bsschan = chan; - ieee80211_node_set_chan(ni, chan); + /* XXX is this really needed? */ + ieee80211_node_set_chan(ni, + ieee80211_ht_adjust_channel(ic, chan, + ieee80211_htchanflags(chan))); ic->ic_curmode = ieee80211_chan2mode(chan); /* * Do mode-specific setup. @@ -409,6 +412,8 @@ } } + /* XXX HT specific setup? */ + (void) ieee80211_sta_join1(ieee80211_ref_node(ni)); } @@ -771,6 +776,10 @@ /* XXX msg */ return 0; } + + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: mac<%s>\n", __func__, ether_sprintf(se->se_bssid)); + /* * Expand scan state into node's format. * XXX may not need all this stuff @@ -813,6 +822,12 @@ #endif } + printf("%s: ath_ie=%p, htcap=%p, htinfo=%p\n", + __func__, + ni->ni_ies.ath_ie, + ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); + vap->iv_dtim_period = se->se_dtimperiod; vap->iv_dtim_count = 0; @@ -821,9 +836,44 @@ IEEE80211_F_DOSORT); if (ieee80211_iserp_rateset(&ni->ni_rates)) ni->ni_flags |= IEEE80211_NODE_ERP; + + + /* + * Setup HT state for this node if it's available, otherwise + * non-STA modes won't pick this state up. + */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, + "%s: htinfo=%p, htcap=%p\n", + __func__, + ni->ni_ies.htinfo_ie, + ni->ni_ies.htcap_ie); + if (ni->ni_ies.htinfo_ie != NULL && + ni->ni_ies.htcap_ie != NULL && + vap->iv_flags_ht & IEEE80211_FHT_HT) { + ieee80211_ht_node_init(ni); + ieee80211_ht_updateparams(ni, + ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); + ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie, + IEEE80211_F_JOIN | IEEE80211_F_DOBRS); + ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie); + } + /* XXX else check for ath FF? */ + /* XXX setup QoS */ + + ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); + /* + * If we have HT information, set it up here. + * + * XXX this duplicates all of the other places where a node + * can get 'promoted' to be a HT node? + * + * XXX TODO? + */ + return ieee80211_sta_join1(ieee80211_ref_node(ni)); } @@ -938,6 +988,9 @@ case IEEE80211_ELEMID_HTCAP: ies->htcap_ie = ie; break; + case IEEE80211_ELEMID_HTINFO: + ies->htinfo_ie = ie; + break; #ifdef IEEE80211_SUPPORT_MESH case IEEE80211_ELEMID_MESHID: ies->meshid_ie = ie; @@ -1194,7 +1247,9 @@ IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); ieee80211_node_initref(ni); /* mark referenced */ /* NB: required by ieee80211_fix_rate */ - ieee80211_node_set_chan(ni, bss->ni_chan); + ieee80211_node_set_chan(ni, + ieee80211_ht_adjust_channel(ic, bss->ni_chan, + ieee80211_htchanflags(bss->ni_chan))); ieee80211_crypto_resetkey(vap, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE); ni->ni_txpower = bss->ni_txpower; @@ -1224,7 +1279,9 @@ */ copy_bss(ni, bss); IEEE80211_ADDR_COPY(ni->ni_bssid, bss->ni_bssid); - ieee80211_node_set_chan(ni, bss->ni_chan); + ieee80211_node_set_chan(ni, + ieee80211_ht_adjust_channel(ic, bss->ni_chan, + ieee80211_htchanflags(bss->ni_chan))); } return ni; } @@ -1253,6 +1310,9 @@ */ copy_bss(ni, vap->iv_bss); ieee80211_node_set_chan(ni, chan); + ieee80211_node_set_chan(ni, + ieee80211_ht_adjust_channel(ic, chan, + ieee80211_htchanflags(chan))); /* NB: propagate ssid so available to WPA supplicant */ ni->ni_esslen = vap->iv_des_ssid[0].len; memcpy(ni->ni_essid, vap->iv_des_ssid[0].ssid, ni->ni_esslen); @@ -1404,7 +1464,7 @@ { struct ieee80211_node *ni; - IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: mac<%s>\n", __func__, ether_sprintf(macaddr)); ni = ieee80211_dup_bss(vap, macaddr); if (ni != NULL) { @@ -1444,6 +1504,8 @@ const struct ieee80211_frame *wh, const struct ieee80211_scanparams *sp) { + int do_ht_setup = 0; + ni->ni_esslen = sp->ssid[1]; memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); @@ -1469,12 +1531,47 @@ if (ni->ni_ies.ath_ie != NULL) ieee80211_parse_ath(ni, ni->ni_ies.ath_ie); #endif + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, + "%s: wme_ie=%p, htcap_ie=%p, htinfo_ie=%p\n", __func__, + ni->ni_ies.wme_ie, + ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); + + if (ni->ni_ies.htcap_ie != NULL) + ieee80211_parse_htcap(ni, ni->ni_ies.htcap_ie); + if (ni->ni_ies.htinfo_ie != NULL) + ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie); + + if ((ni->ni_ies.htcap_ie != NULL) && + (ni->ni_ies.htinfo_ie != NULL) && + (ni->ni_vap->iv_flags_ht & IEEE80211_FHT_HT)) { + do_ht_setup = 1; + } } /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); + + if (do_ht_setup) { + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, + "%s: doing HT setup\n", __func__); + ieee80211_ht_node_init(ni); + ieee80211_ht_updateparams(ni, + ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); + ieee80211_setup_htrates(ni, + ni->ni_ies.htcap_ie, + IEEE80211_F_JOIN | IEEE80211_F_DOBRS); + ieee80211_setup_basic_htrates(ni, + ni->ni_ies.htinfo_ie); + ieee80211_node_setuptxparms(ni); + ieee80211_ratectl_node_init(ni); + } + + IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC, + "%s: done\n", __func__); } /* @@ -1490,7 +1587,7 @@ { struct ieee80211_node *ni; - IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, + IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, "%s: mac<%s>\n", __func__, ether_sprintf(wh->i_addr2)); ni = ieee80211_dup_bss(vap, wh->i_addr2);/* XXX alloc_node? */ if (ni != NULL) { Index: ieee80211_power.c =================================================================== --- ieee80211_power.c (revision 244051) +++ ieee80211_power.c (working copy) @@ -316,6 +316,10 @@ * Save an outbound packet for a node in power-save sleep state. * The new packet is placed on the node's saved queue, and the TIM * is changed, if necessary. + * + * Because of how fragments are currently stored, + * we can't just push fragments into the power save + * queue as there's no way to pull them out here. */ int ieee80211_pwrsave(struct ieee80211_node *ni, struct mbuf *m) @@ -326,6 +330,21 @@ struct ieee80211_psq_head *qhead; int qlen, age; + /* + * Don't allow fragments to be queued to the power save + * queue; they can't be pushed onto the queue in their + * current form (ie, using m_nextpkt to link things together.) + */ + if (m->m_nextpkt != NULL) { + if_printf(vap->iv_ifp, "%s: fragment queued!\n", + __func__); + /* + * XXX for now, just fall through after complaining. + * Later we should free the list and correctly handle + * freeing the fragments + the single node reference. + */ + } + IEEE80211_PSQ_LOCK(psq); if (psq->psq_len >= psq->psq_maxlen) { psq->psq_drops++; @@ -408,6 +427,9 @@ * and/or the driver's send queue; and kick the start * method for each, as appropriate. Note we're careful * to preserve packet ordering here. + * + * Packet transmission is done from outside of the + * power save queue lock. */ static void pwrsave_flushq(struct ieee80211_node *ni) @@ -416,31 +438,39 @@ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_psq_head *qhead; struct ifnet *parent, *ifp; + struct mbuf *m_parent = NULL, *m_vap = NULL, *mn; IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni, "flush ps queue, %u packets queued", psq->psq_len); IEEE80211_PSQ_LOCK(psq); + + /* + * Process frames pushed onto the power save queue + * from the driver. + */ qhead = &psq->psq_head[0]; /* 802.11 frames */ if (qhead->head != NULL) { /* XXX could dispatch through vap and check M_ENCAP */ parent = vap->iv_ic->ic_ifp; /* XXX need different driver interface */ /* XXX bypasses q max and OACTIVE */ - IF_PREPEND_LIST(&parent->if_snd, qhead->head, qhead->tail, - qhead->len); + m_parent = qhead->head; qhead->head = qhead->tail = NULL; qhead->len = 0; } else parent = NULL; + /* + * Process frames pushed onto the power save queue + * from the VAP queue. + */ qhead = &psq->psq_head[1]; /* 802.3 frames */ if (qhead->head != NULL) { ifp = vap->iv_ifp; /* XXX need different driver interface */ /* XXX bypasses q max and OACTIVE */ - IF_PREPEND_LIST(&ifp->if_snd, qhead->head, qhead->tail, - qhead->len); + m_vap = qhead->head; qhead->head = qhead->tail = NULL; qhead->len = 0; } else @@ -450,10 +480,24 @@ /* NB: do this outside the psq lock */ /* XXX packets might get reordered if parent is OACTIVE */ - if (parent != NULL) - if_start(parent); - if (ifp != NULL) - if_start(ifp); + + /* + * Send frames to the parent driver interface first, + * _then_ send the vap queued frames. + */ + while (m_parent != NULL) { + mn = m_parent->m_nextpkt; + m_parent->m_nextpkt = NULL; + parent->if_transmit(parent, m_parent); + m_parent = mn; + } + + while (m_vap != NULL) { + mn = m_vap->m_nextpkt; + m_vap->m_nextpkt = NULL; + ifp->if_transmit(ifp, m_vap); + m_vap = mn; + } } /* Index: ieee80211_var.h =================================================================== --- ieee80211_var.h (revision 244051) +++ ieee80211_var.h (working copy) @@ -477,6 +477,8 @@ int (*iv_send_mgmt)(struct ieee80211_node *, int, int); #endif + void (*iv_flush)(struct ieee80211vap *); + /* beacon miss processing */ void (*iv_bmiss)(struct ieee80211vap *); /* reset device state after 802.11 parameter/state change */ Index: ieee80211_hostap.c =================================================================== --- ieee80211_hostap.c (revision 244051) +++ ieee80211_hostap.c (working copy) @@ -2324,6 +2324,5 @@ ifp = vap->iv_ic->ic_ifp; else ifp = vap->iv_ifp; - IF_ENQUEUE(&ifp->if_snd, m); - if_start(ifp); + (void) ifp->if_transmit(ifp, m); } Index: ieee80211.c =================================================================== --- ieee80211.c (revision 244051) +++ ieee80211.c (working copy) @@ -404,6 +404,24 @@ } /* + * Default vap flush method. + * + * The flush method is called whenever a VAP is transitioning + * to an idle state. + * + * Drivers that bypass the traditional if_snd queue will need + * to override this and flush frames on the send queue for + * this given VAP whilst leaving the rest alone. + */ +static void +default_flush(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); +} + +/* * Prepare a vap for use. Drivers use this call to * setup net80211 state in new vap's prior attaching * them with ieee80211_vap_attach (below). @@ -509,6 +527,13 @@ */ vap->iv_reset = default_reset; + /* + * Install a default flush method; the driver can + * override this if it maintains its own queue + * management. + */ + vap->iv_flush = default_flush; + IEEE80211_ADDR_COPY(vap->iv_myaddr, macaddr); ieee80211_sysctl_vattach(vap); Index: ieee80211_adhoc.c =================================================================== --- ieee80211_adhoc.c (revision 244078) +++ ieee80211_adhoc.c (working copy) @@ -688,6 +688,7 @@ struct ieee80211_frame *wh; uint8_t *frm, *efrm, *sfrm; uint8_t *ssid, *rates, *xrates; + int ht_state_change = 0; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; @@ -748,10 +749,18 @@ memcpy(ni->ni_tstamp.data, scan.tstamp, sizeof(ni->ni_tstamp)); } + if (scan.htcap != NULL && scan.htinfo != NULL && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + if (ieee80211_ht_updateparams(ni, + scan.htcap, scan.htinfo)) + ht_state_change = 1; + } if (ni != NULL) { IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); ni->ni_noise = nf; } + if (ht_state_change) + ieee80211_update_chw(ic); } break; }