Index: sys/net80211/ieee80211_adhoc.c =================================================================== --- sys/net80211/ieee80211_adhoc.c (revision 298757) +++ sys/net80211/ieee80211_adhoc.c (working copy) @@ -400,8 +400,12 @@ goto err; } /* - * Fake up a node for this newly - * discovered member of the IBSS. + * Fake up a node for this newly discovered member + * of the IBSS. + * + * Note: This doesn't "upgrade" the node to 11n; + * that will happen after a probe request/response + * exchange. */ ni = ieee80211_fakeup_adhoc_node(vap, wh->i_addr2); if (ni == NULL) { @@ -733,6 +737,18 @@ * Create a new entry in the neighbor table. */ ni = ieee80211_add_neighbor(vap, wh, &scan); + + /* + * Send a probe request so we announce 11n + * capabilities. + */ + ieee80211_send_probereq(ni, /* node */ + vap->iv_myaddr, /* SA */ + ni->ni_macaddr, /* DA */ + vap->iv_bss->ni_bssid, /* BSSID */ + vap->iv_bss->ni_essid, + vap->iv_bss->ni_esslen); /* SSID */ + } else if (ni->ni_capinfo == 0) { /* * Update faked node created on transmit. @@ -739,6 +755,17 @@ * Note this also updates the tsf. */ ieee80211_init_neighbor(ni, wh, &scan); + + /* + * Send a probe request so we announce 11n + * capabilities. + */ + ieee80211_send_probereq(ni, /* node */ + vap->iv_myaddr, /* SA */ + ni->ni_macaddr, /* DA */ + vap->iv_bss->ni_bssid, /* BSSID */ + vap->iv_bss->ni_essid, + vap->iv_bss->ni_esslen); /* SSID */ } else { /* * Record tsf for potential resync. @@ -850,6 +877,12 @@ */ ieee80211_send_proberesp(vap, wh->i_addr2, is11bclient(rates, xrates) ? IEEE80211_SEND_LEGACY_11B : 0); + + /* + * Note: we don't benefit from stashing the probe request + * IEs away to use for IBSS negotiation, because we + * typically don't get all of the IEs. + */ break; case IEEE80211_FC0_SUBTYPE_ACTION: Index: sys/net80211/ieee80211_ht.c =================================================================== --- sys/net80211/ieee80211_ht.c (revision 298757) +++ sys/net80211/ieee80211_ht.c (working copy) @@ -2838,6 +2838,98 @@ } /* + * Non-associated probe request - add HT capabilities based on + * the current channel configuration. + * + */ +static uint8_t * +ieee80211_add_htcap_body_ch(uint8_t *frm, struct ieee80211vap *vap, + struct ieee80211_channel *c) +{ +#define ADDSHORT(frm, v) do { \ + frm[0] = (v) & 0xff; \ + frm[1] = (v) >> 8; \ + frm += 2; \ +} while (0) + struct ieee80211com *ic = vap->iv_ic; + uint16_t caps, extcaps; + int rxmax, density; + + /* HT capabilities */ + caps = vap->iv_htcaps & 0xffff; + + /* + * We don't use this in STA mode; only in IBSS mode. + * So in IBSS mode we base our HTCAP flags on the + * given channel. + */ + if (1) { + /* override 20/40 use based on current channel */ + if (IEEE80211_IS_CHAN_HT40(c)) + caps |= IEEE80211_HTCAP_CHWIDTH40; + else + caps &= ~IEEE80211_HTCAP_CHWIDTH40; + + /* Use the currently configured values */ + 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; + if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 || + (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) + caps &= ~IEEE80211_HTCAP_SHORTGI40; + ADDSHORT(frm, caps); + + /* HT parameters */ + *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) + | SM(density, IEEE80211_HTCAP_MPDUDENSITY) + ; + frm++; + + /* pre-zero remainder of ie */ + memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); + + /* supported MCS set */ + /* + * XXX: For sta mode the rate set should be restricted based + * on the AP's capabilities, but ni_htrates isn't setup when + * we're called to form an AssocReq frame so for now we're + * restricted to the device capabilities. + */ + ieee80211_set_mcsset(ic, frm); + + frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) - + __offsetof(struct ieee80211_ie_htcap, hc_mcsset); + + /* HT extended capabilities */ + extcaps = vap->iv_htextcaps & 0xffff; + + ADDSHORT(frm, extcaps); + + frm += sizeof(struct ieee80211_ie_htcap) - + __offsetof(struct ieee80211_ie_htcap, hc_txbf); + + return frm; +#undef ADDSHORT +} + +/* + * Add 802.11n HT capabilities information element + */ +uint8_t * +ieee80211_add_htcap_ch(uint8_t *frm, struct ieee80211vap *vap, + struct ieee80211_channel *c) +{ + frm[0] = IEEE80211_ELEMID_HTCAP; + frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; + return ieee80211_add_htcap_body_ch(frm + 2, vap, c); +} + +/* * Add Broadcom OUI wrapped standard HTCAP ie; this is * used for compatibility w/ pre-draft implementations. */ Index: sys/net80211/ieee80211_ht.h =================================================================== --- sys/net80211/ieee80211_ht.h (revision 298757) +++ sys/net80211/ieee80211_ht.h (working copy) @@ -210,6 +210,8 @@ int ieee80211_send_bar(struct ieee80211_node *, struct ieee80211_tx_ampdu *, ieee80211_seq); uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *); +uint8_t *ieee80211_add_htcap_ch(uint8_t *, struct ieee80211vap *, + struct ieee80211_channel *); uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *); uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *); Index: sys/net80211/ieee80211_node.c =================================================================== --- sys/net80211/ieee80211_node.c (revision 298757) +++ sys/net80211/ieee80211_node.c (working copy) @@ -330,10 +330,11 @@ struct ieee80211_node *ni; IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, - "%s: creating %s on channel %u%c\n", __func__, + "%s: creating %s on channel %u%c flags 0x%08x\n", __func__, ieee80211_opmode_name[vap->iv_opmode], ieee80211_chan2ieee(ic, chan), - ieee80211_channel_type_char(chan)); + ieee80211_channel_type_char(chan), + chan->ic_flags); ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); if (ni == NULL) { @@ -414,6 +415,15 @@ } } + /* XXX TODO: other bits and pieces - eg fast-frames? */ + + /* If we're an 11n channel then initialise the 11n bits */ + if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + printf("%s: TODO: setup HT state?\n", __func__); + /* XXX what else? */ + ieee80211_ht_node_init(ni); + } + (void) ieee80211_sta_join1(ieee80211_ref_node(ni)); } @@ -1473,6 +1483,9 @@ * so we can do interesting things (e.g. use * WME to disable ACK's). */ + /* + * XXX TODO: 11n? + */ if (vap->iv_flags & IEEE80211_F_WME) ni->ni_flags |= IEEE80211_NODE_QOS; #ifdef IEEE80211_SUPPORT_SUPERG @@ -1482,8 +1495,44 @@ } ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); + + /* + * XXX TODO: 11n? At least 20MHz, at least A-MPDU RX, + * not A-MPDU TX; not 11n rates, etc. We'll cycle + * that after we hear that we can indeed do 11n + * (either by a beacon frame or by a probe response.) + */ + + /* + * This is the first time we see the node. + */ if (ic->ic_newassoc != NULL) ic->ic_newassoc(ni, 1); + + /* + * Kick off a probe request to the given node; + * we will then use the probe response to update + * 11n/etc configuration state. + * + * XXX TODO: this isn't guaranteed, and until we get + * a probe response, we won't be able to actually + * do anything 802.11n related to the node. + * So if this does indeed work, maybe we should hold + * off on sending responses until we get the probe + * response, or just default to some sensible subset + * of 802.11n behaviour (eg always allow aggregation + * negotiation TO us, but not FROM us, etc) so we + * aren't entirely busted. + */ + if (vap->iv_opmode == IEEE80211_M_IBSS) { + ieee80211_send_probereq(ni, /* node */ + vap->iv_myaddr, /* SA */ + ni->ni_macaddr, /* DA */ + vap->iv_bss->ni_bssid, /* BSSID */ + vap->iv_bss->ni_essid, + vap->iv_bss->ni_esslen); /* SSID */ + } + /* XXX not right for 802.1x/WPA */ ieee80211_node_authorize(ni); } @@ -1556,6 +1605,21 @@ ni->ni_ies.htinfo_ie); ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); + + /* Reassociate; we're now 11n */ + /* + * XXX TODO: this is the wrong thing to do - + * we're calling it with isnew=1 so the ath(4) + * driver reinitialises the rate tables. + * This "mostly" works for ath(4), but it won't + * be right for firmware devices which allocate + * node states. + * + * So, do we just create a new node and delete + * the old one? Or? + */ + if (ni->ni_ic->ic_newassoc) + ni->ni_ic->ic_newassoc(ni, 1); } } @@ -1733,6 +1797,10 @@ * caller to be consistent with * ieee80211_find_node_locked. */ + /* + * XXX TODO: this doesn't fake up 11n state; we need + * to find another way to get it upgraded. + */ ni = ieee80211_fakeup_adhoc_node(vap, macaddr); if (ni != NULL) (void) ieee80211_ref_node(ni); Index: sys/net80211/ieee80211_output.c =================================================================== --- sys/net80211/ieee80211_output.c (revision 298757) +++ sys/net80211/ieee80211_output.c (working copy) @@ -2080,6 +2080,7 @@ { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; + struct ieee80211_node *bss; const struct ieee80211_txparam *tp; struct ieee80211_bpf_params params; struct ieee80211_frame *wh; @@ -2088,10 +2089,13 @@ uint8_t *frm; int ret; + bss = ieee80211_ref_node(vap->iv_bss); + if (vap->iv_state == IEEE80211_S_CAC) { IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, "block %s frame in CAC state", "probe request"); vap->iv_stats.is_tx_badstate++; + ieee80211_free_node(bss); return EIO; /* XXX */ } @@ -2113,6 +2117,7 @@ * [tlv] supported rates * [tlv] RSN (optional) * [tlv] extended supported rates + * [tlv] HT cap (optional) * [tlv] WPA (optional) * [tlv] user-specified ie's */ @@ -2120,6 +2125,8 @@ ic->ic_headroom + sizeof(struct ieee80211_frame), 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_htinfo) + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) @@ -2129,6 +2136,7 @@ if (m == NULL) { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); + ieee80211_free_node(bss); return ENOMEM; } @@ -2137,6 +2145,27 @@ frm = ieee80211_add_rates(frm, rs); frm = ieee80211_add_rsn(frm, vap); frm = ieee80211_add_xrates(frm, rs); + + /* + * Note: we can't use bss; we don't have one yet. + * + * So, we should announce our capabilities + * in this channel mode (2g/5g), not the + * channel details itself. + */ + if ((vap->iv_opmode == IEEE80211_M_IBSS) && + (vap->iv_flags_ht & IEEE80211_FHT_HT)) { + struct ieee80211_channel *c; + + /* + * Get the HT channel that we should try upgrading to. + * If we can do 40MHz then this'll upgrade it appropriately. + */ + c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + vap->iv_flags_ht); + frm = ieee80211_add_htcap_ch(frm, vap, c); + } + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_appie_probereq != NULL) frm = add_appie(frm, vap->iv_appie_probereq); @@ -2148,6 +2177,7 @@ if (m == NULL) { /* NB: cannot happen */ ieee80211_free_node(ni); + ieee80211_free_node(bss); return ENOMEM; } @@ -2165,8 +2195,11 @@ IEEE80211_NODE_STAT(ni, tx_mgmt); IEEE80211_DPRINTF(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS, - "send probe req on channel %u bssid %s ssid \"%.*s\"\n", - ieee80211_chan2ieee(ic, ic->ic_curchan), ether_sprintf(bssid), + "send probe req on channel %u bssid %s sa %6D da %6D ssid \"%.*s\"\n", + ieee80211_chan2ieee(ic, ic->ic_curchan), + ether_sprintf(bssid), + sa, ":", + da, ":", ssidlen, ssid); memset(¶ms, 0, sizeof(params)); @@ -2181,6 +2214,7 @@ params.ibp_power = ni->ni_txpower; ret = ieee80211_raw_output(vap, ni, m, ¶ms); IEEE80211_TX_UNLOCK(ic); + ieee80211_free_node(bss); return (ret); }