From owner-svn-src-head@freebsd.org Fri Jan 13 06:53:57 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 6D5B9CAC669; Fri, 13 Jan 2017 06:53:57 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 365A811B8; Fri, 13 Jan 2017 06:53:57 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v0D6ruqu007085; Fri, 13 Jan 2017 06:53:56 GMT (envelope-from adrian@FreeBSD.org) Received: (from adrian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v0D6ruDn007084; Fri, 13 Jan 2017 06:53:56 GMT (envelope-from adrian@FreeBSD.org) Message-Id: <201701130653.v0D6ruDn007084@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: adrian set sender to adrian@FreeBSD.org using -f From: Adrian Chadd Date: Fri, 13 Jan 2017 06:53:56 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r312014 - head/sys/net80211 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 13 Jan 2017 06:53:57 -0000 Author: adrian Date: Fri Jan 13 06:53:56 2017 New Revision: 312014 URL: https://svnweb.freebsd.org/changeset/base/312014 Log: [net80211] initial, somewhat incomplete VHT channel setup code and attach path. This sets up: * vht capabilities in vaps; * calls vht_announce to announce VHT capabilities if any; * sets up vht20, vht40 and vht80 channels, assuming the regulatory code does the right thing with 80MHz available ranges; * adds support to the ieee80211_add_channel_list_5ghz() code to populate VHT channels, as this is the API my ath10k driver is using; * add support for the freq1/freq2 field population and lookup that VHT channels require. The VHT80 code assumes that the regulatory domain already has limited VHT80 bands to, well, 80MHz wide chunks. Modified: head/sys/net80211/ieee80211.c Modified: head/sys/net80211/ieee80211.c ============================================================================== --- head/sys/net80211/ieee80211.c Fri Jan 13 06:22:49 2017 (r312013) +++ head/sys/net80211/ieee80211.c Fri Jan 13 06:53:56 2017 (r312014) @@ -54,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#include #include @@ -119,6 +120,8 @@ static const struct ieee80211_rateset ie { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B +static int set_vht_extchan(struct ieee80211_channel *c); + /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick @@ -150,10 +153,23 @@ ieee80211_chan_init(struct ieee80211com */ if (c->ic_ieee == 0) c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); + + /* + * Setup the HT40/VHT40 upper/lower bits. + * The VHT80 math is done elsewhere. + */ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), c->ic_flags); + + /* Update VHT math */ + /* + * XXX VHT again, note that this assumes VHT80 channels + * are legit already + */ + set_vht_extchan(c); + /* default max tx power to max regulatory */ if (c->ic_maxpower == 0) c->ic_maxpower = 2*c->ic_maxregpower; @@ -343,6 +359,7 @@ ieee80211_ifattach(struct ieee80211com * ieee80211_superg_attach(ic); #endif ieee80211_ht_attach(ic); + ieee80211_vht_attach(ic); ieee80211_scan_attach(ic); ieee80211_regdomain_attach(ic); ieee80211_dfs_attach(ic); @@ -386,6 +403,7 @@ ieee80211_ifdetach(struct ieee80211com * #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_detach(ic); #endif + ieee80211_vht_detach(ic); ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ ieee80211_proto_detach(ic); @@ -515,8 +533,15 @@ ieee80211_vap_setup(struct ieee80211com vap->iv_flags_ext = ic->ic_flags_ext; vap->iv_flags_ven = ic->ic_flags_ven; vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; + + /* 11n capabilities - XXX methodize */ vap->iv_htcaps = ic->ic_htcaps; vap->iv_htextcaps = ic->ic_htextcaps; + + /* 11ac capabilities - XXX methodize */ + vap->iv_vhtcaps = ic->ic_vhtcaps; + vap->iv_vhtextcaps = ic->ic_vhtextcaps; + vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr); @@ -601,6 +626,7 @@ ieee80211_vap_setup(struct ieee80211com ieee80211_superg_vattach(vap); #endif ieee80211_ht_vattach(vap); + ieee80211_vht_vattach(vap); ieee80211_scan_vattach(vap); ieee80211_regdomain_vattach(vap); ieee80211_radiotap_vattach(vap); @@ -737,6 +763,7 @@ ieee80211_vap_detach(struct ieee80211vap #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_vdetach(vap); #endif + ieee80211_vht_vdetach(vap); ieee80211_ht_vdetach(vap); /* NB: must be before ieee80211_node_vdetach */ ieee80211_proto_vdetach(vap); @@ -1081,6 +1108,110 @@ set_extchan(struct ieee80211_channel *c) c->ic_extieee = 0; } +/* + * Populate the freq1/freq2 fields as appropriate for VHT channels. + * + * This for now uses a hard-coded list of 80MHz wide channels. + * + * For HT20/HT40, freq1 just is the centre frequency of the 40MHz + * wide channel we've already decided upon. + * + * For VHT80 and VHT160, there are only a small number of fixed + * 80/160MHz wide channels, so we just use those. + * + * This is all likely very very wrong - both the regulatory code + * and this code needs to ensure that all four channels are + * available and valid before the VHT80 (and eight for VHT160) channel + * is created. + */ + +struct vht_chan_range { + uint16_t freq_start; + uint16_t freq_end; +}; + +struct vht_chan_range vht80_chan_ranges[] = { + { 5170, 5250 }, + { 5250, 5330 }, + { 5490, 5570 }, + { 5570, 5650 }, + { 5650, 5730 }, + { 5735, 5815 }, + { 0, 0, } +}; + +static int +set_vht_extchan(struct ieee80211_channel *c) +{ + int i; + + if (! IEEE80211_IS_CHAN_VHT(c)) { + return (0); + } + + if (IEEE80211_IS_CHAN_VHT20(c)) { + c->ic_vht_ch_freq1 = c->ic_ieee; + return (1); + } + + if (IEEE80211_IS_CHAN_VHT40(c)) { + if (IEEE80211_IS_CHAN_HT40U(c)) + c->ic_vht_ch_freq1 = c->ic_ieee + 2; + else if (IEEE80211_IS_CHAN_HT40D(c)) + c->ic_vht_ch_freq1 = c->ic_ieee - 2; + else + return (0); + return (1); + } + + if (IEEE80211_IS_CHAN_VHT80(c)) { + for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) { + if (c->ic_freq >= vht80_chan_ranges[i].freq_start && + c->ic_freq < vht80_chan_ranges[i].freq_end) { + int midpoint; + + midpoint = vht80_chan_ranges[i].freq_start + 40; + c->ic_vht_ch_freq1 = + ieee80211_mhz2ieee(midpoint, c->ic_flags); + c->ic_vht_ch_freq2 = 0; +#if 0 + printf("%s: %d, freq=%d, midpoint=%d, freq1=%d, freq2=%d\n", + __func__, c->ic_ieee, c->ic_freq, midpoint, + c->ic_vht_ch_freq1, c->ic_vht_ch_freq2); +#endif + return (1); + } + } + return (0); + } + + printf("%s: unknown VHT channel type (ieee=%d, flags=0x%08x)\n", + __func__, + c->ic_ieee, + c->ic_flags); + + return (0); +} + +/* + * Return whether the current channel could possibly be a part of + * a VHT80 channel. + * + * This doesn't check that the whole range is in the allowed list + * according to regulatory. + */ +static int +is_vht80_valid_freq(uint16_t freq) +{ + int i; + for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) { + if (freq >= vht80_chan_ranges[i].freq_start && + freq < vht80_chan_ranges[i].freq_end) + return (1); + } + return (0); +} + static int addchan(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags) @@ -1090,13 +1221,25 @@ addchan(struct ieee80211_channel chans[] if (*nchans >= maxchans) return (ENOBUFS); +#if 0 + printf("%s: %d: ieee=%d, freq=%d, flags=0x%08x\n", + __func__, + *nchans, + ieee, + freq, + flags); +#endif + c = &chans[(*nchans)++]; c->ic_ieee = ieee; c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags); c->ic_maxregpower = maxregpower; c->ic_maxpower = 2 * maxregpower; c->ic_flags = flags; + c->ic_vht_ch_freq1 = 0; + c->ic_vht_ch_freq2 = 0; set_extchan(c); + set_vht_extchan(c); return (0); } @@ -1112,14 +1255,27 @@ copychan_prev(struct ieee80211_channel c if (*nchans >= maxchans) return (ENOBUFS); +#if 0 + printf("%s: %d: flags=0x%08x\n", + __func__, + *nchans, + flags); +#endif + c = &chans[(*nchans)++]; c[0] = c[-1]; c->ic_flags = flags; + c->ic_vht_ch_freq1 = 0; + c->ic_vht_ch_freq2 = 0; set_extchan(c); + set_vht_extchan(c); return (0); } +/* + * XXX VHT-2GHz + */ static void getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40) { @@ -1140,35 +1296,73 @@ getflags_2ghz(const uint8_t bands[], uin } static void -getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40) +getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40, int vht80) { int nmodes; + /* + * the addchan_list function seems to expect the flags array to + * be in channel width order, so the VHT bits are interspersed + * as appropriate to maintain said order. + * + * It also assumes HT40U is before HT40D. + */ nmodes = 0; + + /* 20MHz */ if (isset(bands, IEEE80211_MODE_11A)) flags[nmodes++] = IEEE80211_CHAN_A; if (isset(bands, IEEE80211_MODE_11NA)) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; + if (isset(bands, IEEE80211_MODE_VHT_5GHZ)) { + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 | + IEEE80211_CHAN_VHT20; + + /* 40MHz */ if (ht40) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U; + } + if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U + | IEEE80211_CHAN_VHT40U; + } + if (ht40) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D; } + if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { + flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D + | IEEE80211_CHAN_VHT40D; + } + + /* 80MHz */ + if (vht80 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { + flags[nmodes++] = IEEE80211_CHAN_A | + IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT80; + flags[nmodes++] = IEEE80211_CHAN_A | + IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT80; + } + } + + /* XXX VHT80+80 */ + /* XXX VHT160 */ flags[nmodes] = 0; } static void -getflags(const uint8_t bands[], uint32_t flags[], int ht40) +getflags(const uint8_t bands[], uint32_t flags[], int ht40, int vht80) { flags[0] = 0; if (isset(bands, IEEE80211_MODE_11A) || - isset(bands, IEEE80211_MODE_11NA)) { + isset(bands, IEEE80211_MODE_11NA) || + isset(bands, IEEE80211_MODE_VHT_5GHZ)) { if (isset(bands, IEEE80211_MODE_11B) || isset(bands, IEEE80211_MODE_11G) || - isset(bands, IEEE80211_MODE_11NG)) + isset(bands, IEEE80211_MODE_11NG) || + isset(bands, IEEE80211_MODE_VHT_2GHZ)) return; - getflags_5ghz(bands, flags, ht40); + getflags_5ghz(bands, flags, ht40, vht80); } else getflags_2ghz(bands, flags, ht40); } @@ -1176,6 +1370,7 @@ getflags(const uint8_t bands[], uint32_t /* * Add one 20 MHz channel into specified channel list. */ +/* XXX VHT */ int ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, @@ -1184,7 +1379,7 @@ ieee80211_add_channel(struct ieee80211_c uint32_t flags[IEEE80211_MODE_MAX]; int i, error; - getflags(bands, flags, 0); + getflags(bands, flags, 0, 0); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower, @@ -1218,6 +1413,7 @@ findchannel(struct ieee80211_channel cha /* * Add 40 MHz channel pair into specified channel list. */ +/* XXX VHT */ int ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags) @@ -1275,11 +1471,17 @@ ieee80211_get_channel_center_freq(const * For 80+80MHz channels this will be the centre of the primary * 80MHz channel; the secondary 80MHz channel will be center_freq2(). */ - uint32_t ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c) { + /* + * VHT - use the pre-calculated centre frequency + * of the given channel. + */ + if (IEEE80211_IS_CHAN_VHT(c)) + return (ieee80211_ieee2mhz(c->ic_vht_ch_freq1, c->ic_flags)); + if (IEEE80211_IS_CHAN_HT40U(c)) { return (c->ic_freq + 10); } @@ -1291,12 +1493,15 @@ ieee80211_get_channel_center_freq1(const } /* - * For now, no 80+80 support; this is zero. + * For now, no 80+80 support; it will likely always return 0. */ uint32_t ieee80211_get_channel_center_freq2(const struct ieee80211_channel *c) { + if (IEEE80211_IS_CHAN_VHT(c) && (c->ic_vht_ch_freq2 != 0)) + return (ieee80211_ieee2mhz(c->ic_vht_ch_freq2, c->ic_flags)); + return (0); } @@ -1310,16 +1515,70 @@ add_chanlist(struct ieee80211_channel ch { uint16_t freq; int i, j, error; + int is_vht; for (i = 0; i < nieee; i++) { freq = ieee80211_ieee2mhz(ieee[i], flags[0]); for (j = 0; flags[j] != 0; j++) { + /* + * Notes: + * + HT40 and VHT40 channels occur together, so + * we need to be careful that we actually allow that. + * + VHT80, VHT160 will coexist with HT40/VHT40, so + * make sure it's not skipped because of the overlap + * check used for (V)HT40. + */ + is_vht = !! (flags[j] & IEEE80211_CHAN_VHT); + + /* + * Test for VHT80. + * XXX This is all very broken right now. + * What we /should/ do is: + * + * + check that the frequency is in the list of + * allowed VHT80 ranges; and + * + the other 3 channels in the list are actually + * also available. + */ + if (is_vht && flags[j] & IEEE80211_CHAN_VHT80) + if (! is_vht80_valid_freq(freq)) + continue; + + /* + * Test for (V)HT40. + * + * This is also a fall through from VHT80; as we only + * allow a VHT80 channel if the VHT40 combination is + * also valid. If the VHT40 form is not valid then + * we certainly can't do VHT80.. + */ if (flags[j] & IEEE80211_CHAN_HT40D) + /* + * Can't have a "lower" channel if we are the + * first channel. + * + * Can't have a "lower" channel if it's below/ + * within 20MHz of the first channel. + * + * Can't have a "lower" channel if the channel + * below it is not 20MHz away. + */ if (i == 0 || ieee[i] < ieee[0] + 4 || freq - 20 != ieee80211_ieee2mhz(ieee[i] - 4, flags[j])) continue; if (flags[j] & IEEE80211_CHAN_HT40U) + /* + * Can't have an "upper" channel if we are + * the last channel. + * + * Can't have an "upper" channel be above the + * last channel in the list. + * + * Can't have an "upper" channel if the next + * channel according to the math isn't 20MHz + * away. (Likely for channel 13/14.) + */ if (i == nieee - 1 || ieee[i] + 4 > ieee[nieee - 1] || freq + 20 != @@ -1348,6 +1607,7 @@ ieee80211_add_channel_list_2ghz(struct i { uint32_t flags[IEEE80211_MODE_MAX]; + /* XXX no VHT for now */ getflags_2ghz(bands, flags, ht40); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); @@ -1360,8 +1620,15 @@ ieee80211_add_channel_list_5ghz(struct i int ht40) { uint32_t flags[IEEE80211_MODE_MAX]; + int vht80 = 0; + + /* + * For now, assume VHT == VHT80 support as a minimum. + */ + if (isset(bands, IEEE80211_MODE_VHT_5GHZ)) + vht80 = 1; - getflags_5ghz(bands, flags, ht40); + getflags_5ghz(bands, flags, ht40, vht80); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); @@ -1662,6 +1929,7 @@ ieee80211_announce(struct ieee80211com * printf("\n"); } ieee80211_ht_announce(ic); + ieee80211_vht_announce(ic); } void