diff mbox series

[4/4] mac80211: add AX support

Message ID 20210215101921.3371799-4-john@phrozen.org
State Superseded
Delegated to: John Crispin
Headers show
Series [1/4] hostapd: add rts_threshold support | expand

Commit Message

John Crispin Feb. 15, 2021, 10:19 a.m. UTC
Signed-off-by: John Crispin <john@phrozen.org>
---
 .../files/lib/netifd/wireless/mac80211.sh     | 193 +++++++++++++++++-
 .../mac80211/files/lib/wifi/mac80211.sh       |  19 +-
 2 files changed, 195 insertions(+), 17 deletions(-)

Comments

Felix Fietkau Feb. 15, 2021, 3:50 p.m. UTC | #1
On 2021-02-15 11:19, John Crispin wrote:
> Signed-off-by: John Crispin <john@phrozen.org>
> ---
>  .../files/lib/netifd/wireless/mac80211.sh     | 193 +++++++++++++++++-
>  .../mac80211/files/lib/wifi/mac80211.sh       |  19 +-
>  2 files changed, 195 insertions(+), 17 deletions(-)
> 
> diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> index 92c56afd24..b717770d5e 100644
> --- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> +++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> @@ -56,6 +56,13 @@ drv_mac80211_init_device_config() {
[...]
> +mac80211_get_seg0() {
> +	local ht_mode="$1"
> +	local seg0=0
> +
> +	case "$ht_mode" in
> +		40)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$(( ($channel / 4) % 2 ))" in
> +					1) seg0=$(($channel - 2));;
> +					0) seg0=$(($channel + 2));;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$(( ($channel / 4) % 2 ))" in
> +					1) seg0=$(($channel + 2));;
> +					0) seg0=$(($channel - 2));;
> +				esac
> +			fi
> +			;;
> +		80)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$(( ($channel / 4) % 4 ))" in
> +					0) seg0=$(($channel + 6));;
> +					1) seg0=$(($channel + 2));;
> +					2) seg0=$(($channel - 2));;
> +					3) seg0=$(($channel - 6));;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$(( ($channel / 4) % 4 ))" in
> +					1) seg0=$(($channel + 6));;
> +					2) seg0=$(($channel + 2));;
> +					3) seg0=$(($channel - 2));;
> +					0) seg0=$(($channel - 6));;
> +				esac
> +			fi
> +			;;
> +		160)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$channel" in
> +					1|5|9|13|17|21|25|29) seg0=15;;
> +					33|37|41|45|49|53|57|61) seg0=47;;
> +					65|69|73|77|81|85|89|93) seg0=79;;
> +					97|101|105|109|113|117|121|125) seg0=111;;
> +					129|133|137|141|145|149|153|157) seg0=143;;
> +					161|165|169|173|177|181|185|189) seg0=175;;
> +					193|197|201|205|209|213|217|221) seg0=207;;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$channel" in
> +					36|40|44|48|52|56|60|64) seg0=50;;
> +					100|104|108|112|116|120|124|128) seg0=114;;
> +				esac
> +			fi
> +			;;
> +	esac
> +	printf "$seg0"
> +}
This function duplicates the seg0 that's already done for VHT+HE and
simply adds some extra conditions for 6 GHz. Those extra conditions
should be folded into the existing code.

> +
>  mac80211_hostapd_setup_base() {
>  	local phy="$1"
>  
> @@ -333,20 +412,105 @@ mac80211_hostapd_setup_base() {
>  	# 802.11ax
>  	enable_ax=0
>  	case "$htmode" in
> -		HE*) enable_ax=1 ;;
> +		HE20)   enable_ax=1
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				append base_cfg "op_class=131" "$N"
> +			fi
> +			;;
> +		HE40)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "40")"
> +			if [ $freq -ge 5180 ] && [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=132" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=0" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +			;;
> +		HE80)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "80")"
> +			if [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=133" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=1" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +			;;
> +		HE160)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "160")"
> +			if [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=134" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=2" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +		;;
>  	esac
Please remove this chunk entirely and replace it with a simple extra
case/esac for adding op_class.
he_oper_chwidth and he_oper_centr_freq_seg0_idx is already handled
elsewhere.

[...]
> @@ -401,6 +565,7 @@ mac80211_get_addr() {
>  
>  mac80211_generate_mac() {
>  	local phy="$1"
> +	local multiple_bssid="$2"
>  	local id="${macidx:-0}"
>  
>  	local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
> @@ -425,6 +590,11 @@ mac80211_generate_mac() {
>  
>  	local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
>  
> +	[ "$multiple_bssid" -eq 1 ] && {
> +		printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx
This looks wrong to me. first byte should be:
b0 = orig_b0 | 0x2
b5 = orig_b5 & ~mac_mask | mac_idx
mac_mask should be set based on the number of mbssid entries to leave
enough room for macidx.

- Felix
Philip Prindeville Feb. 15, 2021, 7:30 p.m. UTC | #2
AX support. Cool.

Is there any UCI defaults stuff that needs to be touched for first boot and discovery?

Comments:



> On Feb 15, 2021, at 3:19 AM, John Crispin <john@phrozen.org> wrote:
> 
> Signed-off-by: John Crispin <john@phrozen.org>
> ---
> .../files/lib/netifd/wireless/mac80211.sh     | 193 +++++++++++++++++-
> .../mac80211/files/lib/wifi/mac80211.sh       |  19 +-
> 2 files changed, 195 insertions(+), 17 deletions(-)
> 
> diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> index 92c56afd24..b717770d5e 100644
> --- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> +++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
> @@ -56,6 +56,13 @@ drv_mac80211_init_device_config() {
> 		short_gi_40 \
> 		max_amsdu \
> 		dsss_cck_40
> +	config_add_int \
> +		he_su_beamformer \
> +		he_su_beamformee \
> +		he_mu_beamformer \
> +		he_bss_color \
> +		he_spr_sr_control \
> +		he_spr_non_srg_obss_pd_max_offset
> }
> 
> drv_mac80211_init_iface_config() {
> @@ -69,6 +76,7 @@ drv_mac80211_init_iface_config() {
> 	config_add_int max_listen_int
> 	config_add_int dtim_period
> 	config_add_int start_disabled
> +	config_add_int he_twt_required he_spr_sr_control
> 
> 	# mesh
> 	config_add_string mesh_id
> @@ -96,6 +104,77 @@ mac80211_add_capabilities() {
> 	export -n -- "$__var=$__out"
> }
> 
> +mac80211_add_he_capabilities() {
> +	local __out= oifs
> +
> +	oifs="$IFS"
> +	IFS=:

Can't we just do:

local IFS=:

and get rid of oifs, etc.


% cat /tmp/foo
#!/bin/sh

func() {
  local IFS="b"

  echo "IFS=$IFS"
}

IFS="a"

echo "IFS=$IFS"
func

echo "IFS=$IFS"


% /tmp/foo
IFS=a
IFS=b
IFS=a
%




> +	for capab in "$@"; do
> +		set -- $capab
> +		[ "$(($4))" -gt 0 ] || continue
> +		[ "$(((0x$2) & $3))" -gt 0 ] || continue
> +		append base_cfg "$1=1" "$N"
> +	done
> +	IFS="$oifs"
> +}
> +
> +mac80211_get_seg0() {
> +	local ht_mode="$1"
> +	local seg0=0
> +
> +	case "$ht_mode" in
> +		40)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$(( ($channel / 4) % 2 ))" in
> +					1) seg0=$(($channel - 2));;
> +					0) seg0=$(($channel + 2));;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$(( ($channel / 4) % 2 ))" in
> +					1) seg0=$(($channel + 2));;
> +					0) seg0=$(($channel - 2));;
> +				esac
> +			fi
> +			;;
> +		80)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$(( ($channel / 4) % 4 ))" in
> +					0) seg0=$(($channel + 6));;
> +					1) seg0=$(($channel + 2));;
> +					2) seg0=$(($channel - 2));;
> +					3) seg0=$(($channel - 6));;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$(( ($channel / 4) % 4 ))" in
> +					1) seg0=$(($channel + 6));;
> +					2) seg0=$(($channel + 2));;
> +					3) seg0=$(($channel - 2));;
> +					0) seg0=$(($channel - 6));;
> +				esac
> +			fi
> +			;;
> +		160)
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				case "$channel" in
> +					1|5|9|13|17|21|25|29) seg0=15;;
> +					33|37|41|45|49|53|57|61) seg0=47;;
> +					65|69|73|77|81|85|89|93) seg0=79;;
> +					97|101|105|109|113|117|121|125) seg0=111;;
> +					129|133|137|141|145|149|153|157) seg0=143;;
> +					161|165|169|173|177|181|185|189) seg0=175;;
> +					193|197|201|205|209|213|217|221) seg0=207;;
> +				esac
> +			elif [ $freq != 5935 ]; then
> +				case "$channel" in
> +					36|40|44|48|52|56|60|64) seg0=50;;
> +					100|104|108|112|116|120|124|128) seg0=114;;
> +				esac
> +			fi
> +			;;
> +	esac
> +	printf "$seg0"
> +}
> +
> mac80211_hostapd_setup_base() {
> 	local phy="$1"
> 
> @@ -333,20 +412,105 @@ mac80211_hostapd_setup_base() {
> 	# 802.11ax
> 	enable_ax=0
> 	case "$htmode" in
> -		HE*) enable_ax=1 ;;
> +		HE20)   enable_ax=1
> +			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +				append base_cfg "op_class=131" "$N"
> +			fi
> +			;;
> +		HE40)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "40")"
> +			if [ $freq -ge 5180 ] && [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=132" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=0" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +			;;
> +		HE80)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "80")"
> +			if [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=133" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=1" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +			;;
> +		HE160)
> +			enable_ax=1
> +			idx="$(mac80211_get_seg0 "160")"
> +			if [ $freq != 5935 ]; then
> +				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
> +					append base_cfg "op_class=134" "$N"
> +				fi
> +				append base_cfg "he_oper_chwidth=2" "$N"
> +				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
> +			fi
> +		;;
> 	esac
> 
> 	if [ "$enable_ax" != "0" ]; then
> +		json_get_vars \
> +			he_su_beamformer:1 \
> +			he_su_beamformee:0 \
> +			he_mu_beamformer:1 \
> +			he_twt_required:0 \
> +			he_spr_sr_control:0 \
> +			he_spr_non_srg_obss_pd_max_offset:1 \
> +			he_bss_color:64
> +	
> 		append base_cfg "ieee80211ax=1" "$N"
> +		append base_cfg "he_bss_color=$he_bss_color" "$N"
> 		[ "$hwmode" = "a" ] && {
> 			append base_cfg "he_oper_chwidth=$vht_oper_chwidth" "$N"
> 			append base_cfg "he_oper_centr_freq_seg0_idx=$vht_center_seg0" "$N"
> 		}
> +		he_phy_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE PHY Capabilities/ { print $2 }' | head -1)
> +		he_phy_cap=${he_phy_cap:2}
> +		he_mac_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE MAC Capabilities/ { print $2 }' | head -1)
> +		he_mac_cap=${he_mac_cap:2}
> +
> +		mac80211_add_he_capabilities \
> +			he_su_beamformer:${he_phy_cap:6:2}:0x80:$he_su_beamformer \
> +			he_su_beamformee:${he_phy_cap:8:2}:0x1:$he_su_beamformee \
> +			he_mu_beamformer:${he_phy_cap:8:2}:0x2:$he_mu_beamformer \
> +			he_spr_sr_control:${he_phy_cap:14:2}:0x1:$he_spr_sr_control \
> +			he_twt_required:${he_mac_cap:0:2}:0x6:$he_twt_required
> +
> +		[ "$he_spr_sr_control" != "0" ] && {
> +			append base_cfg "he_spr_sr_control=$he_spr_sr_control" "$N"
> +			append base_cfg "he_spr_non_srg_obss_pd_max_offset=$he_spr_non_srg_obss_pd_max_offset" "$N"
> +		}
> +		
> 		append base_cfg "he_default_pe_duration=4" "$N"
> 		append base_cfg "he_rts_threshold=1023" "$N"
> -		append base_cfg "he_su_beamformer=1" "$N"
> -		append base_cfg "he_su_beamformee=1" "$N"
> -		append base_cfg "he_mu_beamformer=1" "$N"
> +		append base_cfg "he_mu_edca_qos_info_param_count=0" "$N"
> +		append base_cfg "he_mu_edca_qos_info_q_ack=0" "$N"
> +		append base_cfg "he_mu_edca_qos_info_queue_request=0" "$N"
> +		append base_cfg "he_mu_edca_qos_info_txop_request=0" "$N"
> +		append base_cfg "he_mu_edca_ac_be_aifsn=8" "$N"
> +		append base_cfg "he_mu_edca_ac_be_aci=0" "$N"
> +		append base_cfg "he_mu_edca_ac_be_ecwmin=9" "$N"
> +		append base_cfg "he_mu_edca_ac_be_ecwmax=10" "$N"
> +		append base_cfg "he_mu_edca_ac_be_timer=255" "$N"
> +		append base_cfg "he_mu_edca_ac_bk_aifsn=15" "$N"
> +		append base_cfg "he_mu_edca_ac_bk_aci=1" "$N"
> +		append base_cfg "he_mu_edca_ac_bk_ecwmin=9" "$N"
> +		append base_cfg "he_mu_edca_ac_bk_ecwmax=10" "$N"
> +		append base_cfg "he_mu_edca_ac_bk_timer=255" "$N"
> +		append base_cfg "he_mu_edca_ac_vi_ecwmin=5" "$N"
> +		append base_cfg "he_mu_edca_ac_vi_ecwmax=7" "$N"
> +		append base_cfg "he_mu_edca_ac_vi_aifsn=5" "$N"
> +		append base_cfg "he_mu_edca_ac_vi_aci=2" "$N"
> +		append base_cfg "he_mu_edca_ac_vi_timer=255" "$N"
> +		append base_cfg "he_mu_edca_ac_vo_aifsn=5" "$N"
> +		append base_cfg "he_mu_edca_ac_vo_aci=3" "$N"
> +		append base_cfg "he_mu_edca_ac_vo_ecwmin=5" "$N"
> +		append base_cfg "he_mu_edca_ac_vo_ecwmax=7" "$N"
> +		append base_cfg "he_mu_edca_ac_vo_timer=255" "$N"
> 	fi
> 
> 	hostapd_prepare_device_config "$hostapd_conf_file" nl80211
> @@ -401,6 +565,7 @@ mac80211_get_addr() {
> 
> mac80211_generate_mac() {
> 	local phy="$1"
> +	local multiple_bssid="$2"
> 	local id="${macidx:-0}"
> 
> 	local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
> @@ -425,6 +590,11 @@ mac80211_generate_mac() {
> 
> 	local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
> 
> +	[ "$multiple_bssid" -eq 1 ] && {
> +		printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx


Should that be "%02x" and not "02"?  There are more arguments than than formatters...

-Philip



> +		return
> +	}
> +
> 	macidx=$(($id + 1))
> 	[ "$((0x$mask1))" -gt 0 ] && {
> 		b1="0x$1"
> @@ -533,6 +703,7 @@ mac80211_iw_interface_add() {
> }
> 
> mac80211_prepare_vif() {
> +	local multiple_bssid=$1
> 	json_select config
> 
> 	json_get_vars ifname mode ssid wds powersave macaddr enable wpa_psk_file vlan_file
> @@ -546,7 +717,7 @@ mac80211_prepare_vif() {
> 	json_select ..
> 
> 	[ -n "$macaddr" ] || {
> -		macaddr="$(mac80211_generate_mac $phy)"
> +		macaddr="$(mac80211_generate_mac $phy $multiple_bssid)"
> 		macidx="$(($macidx + 1))"
> 	}
> 
> @@ -687,8 +858,9 @@ mac80211_setup_supplicant_noctl() {
> 
> mac80211_prepare_iw_htmode() {
> 	case "$htmode" in
> -		VHT20|HT20) iw_htmode=HT20;;
> -		HT40*|VHT40|VHT160)
> +		VHT20|HT20|HE20) iw_htmode=HT20;;
> +		HT40*|VHT40|VHT160|\
> +		HE40|HE40|HE160)
> 			case "$hwmode" in
> 				a)
> 					case "$(( ($channel / 4) % 2 ))" in
> @@ -712,7 +884,7 @@ mac80211_prepare_iw_htmode() {
> 			esac
> 			[ "$auto_channel" -gt 0 ] && iw_htmode="HT40+"
> 		;;
> -		VHT80)
> +		VHT80|HE80)
> 			iw_htmode="80MHZ"
> 		;;
> 		NONE|NOHT)
> @@ -896,7 +1068,8 @@ drv_mac80211_setup() {
> 		country chanbw distance \
> 		txpower antenna_gain \
> 		rxantenna txantenna \
> -		frag rts beacon_int:100 htmode
> +		frag rts beacon_int:100 htmode \
> +		multiple_bssid:0
> 	json_get_values basic_rate_list basic_rate
> 	json_get_values scan_list scan_list
> 	json_select ..
> @@ -991,7 +1164,7 @@ drv_mac80211_setup() {
> 	mac80211_prepare_iw_htmode
> 	for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
> 	NEWAPLIST=
> -	for_each_interface "ap" mac80211_prepare_vif
> +	for_each_interface "ap" mac80211_prepare_vif ${multiple_bssid}
> 	NEW_MD5=$(test -e "${hostapd_conf_file}" && md5sum ${hostapd_conf_file})
> 	OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
> 	if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
> diff --git a/package/kernel/mac80211/files/lib/wifi/mac80211.sh b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
> index 3e99f06693..e7b3aa82c3 100644
> --- a/package/kernel/mac80211/files/lib/wifi/mac80211.sh
> +++ b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
> @@ -80,18 +80,23 @@ detect_mac80211() {
> 		htmode=""
> 		ht_capab=""
> 
> -		iw phy "$dev" info | grep -q 'Capabilities:' && htmode=HT20
> +		iw phy "$dev" info | grep -q 'Capabilities:' && {
> +			htmode=HT20
> +			iw phy "$dev" info | grep -q 'HE.*Capabilities' && htmode="HE20"
> +		}
> 
> -		iw phy "$dev" info | grep -q '\* 5... MHz \[' && {
> +		iw phy "$dev" info | grep -q '5180 MHz' && {
> 			mode_band="a"
> -			channel=$(iw phy "$dev" info | grep '\* 5... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\].*//g')
> +			channel="36"
> +			iw phy "$dev" info | grep -q '5180 MHz.*disabled' && channel=149
> 			iw phy "$dev" info | grep -q 'VHT Capabilities' && htmode="VHT80"
> +			iw phy "$dev" info | grep -q 'HE.*Capabilities' && htmode="HE80"
> 		}
> 
> -		iw phy "$dev" info | grep -q '\* 5.... MHz \[' && {
> -			mode_band="ad"
> -			channel=$(iw phy "$dev" info | grep '\* 5.... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\|\].*//g')
> -			iw phy "$dev" info | grep -q 'Capabilities:' && htmode="HT20"
> +		iw phy "$dev" info | grep -q '5955 MHz' && {
> +			mode_band="a"
> +			channel="1"
> +			htmode="HE80"
> 		}
> 
> 		[ -n "$htmode" ] && ht_capab="set wireless.radio${devidx}.htmode=$htmode"
diff mbox series

Patch

diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
index 92c56afd24..b717770d5e 100644
--- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
+++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
@@ -56,6 +56,13 @@  drv_mac80211_init_device_config() {
 		short_gi_40 \
 		max_amsdu \
 		dsss_cck_40
+	config_add_int \
+		he_su_beamformer \
+		he_su_beamformee \
+		he_mu_beamformer \
+		he_bss_color \
+		he_spr_sr_control \
+		he_spr_non_srg_obss_pd_max_offset
 }
 
 drv_mac80211_init_iface_config() {
@@ -69,6 +76,7 @@  drv_mac80211_init_iface_config() {
 	config_add_int max_listen_int
 	config_add_int dtim_period
 	config_add_int start_disabled
+	config_add_int he_twt_required he_spr_sr_control
 
 	# mesh
 	config_add_string mesh_id
@@ -96,6 +104,77 @@  mac80211_add_capabilities() {
 	export -n -- "$__var=$__out"
 }
 
+mac80211_add_he_capabilities() {
+	local __out= oifs
+
+	oifs="$IFS"
+	IFS=:
+	for capab in "$@"; do
+		set -- $capab
+		[ "$(($4))" -gt 0 ] || continue
+		[ "$(((0x$2) & $3))" -gt 0 ] || continue
+		append base_cfg "$1=1" "$N"
+	done
+	IFS="$oifs"
+}
+
+mac80211_get_seg0() {
+	local ht_mode="$1"
+	local seg0=0
+
+	case "$ht_mode" in
+		40)
+			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+				case "$(( ($channel / 4) % 2 ))" in
+					1) seg0=$(($channel - 2));;
+					0) seg0=$(($channel + 2));;
+				esac
+			elif [ $freq != 5935 ]; then
+				case "$(( ($channel / 4) % 2 ))" in
+					1) seg0=$(($channel + 2));;
+					0) seg0=$(($channel - 2));;
+				esac
+			fi
+			;;
+		80)
+			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+				case "$(( ($channel / 4) % 4 ))" in
+					0) seg0=$(($channel + 6));;
+					1) seg0=$(($channel + 2));;
+					2) seg0=$(($channel - 2));;
+					3) seg0=$(($channel - 6));;
+				esac
+			elif [ $freq != 5935 ]; then
+				case "$(( ($channel / 4) % 4 ))" in
+					1) seg0=$(($channel + 6));;
+					2) seg0=$(($channel + 2));;
+					3) seg0=$(($channel - 2));;
+					0) seg0=$(($channel - 6));;
+				esac
+			fi
+			;;
+		160)
+			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+				case "$channel" in
+					1|5|9|13|17|21|25|29) seg0=15;;
+					33|37|41|45|49|53|57|61) seg0=47;;
+					65|69|73|77|81|85|89|93) seg0=79;;
+					97|101|105|109|113|117|121|125) seg0=111;;
+					129|133|137|141|145|149|153|157) seg0=143;;
+					161|165|169|173|177|181|185|189) seg0=175;;
+					193|197|201|205|209|213|217|221) seg0=207;;
+				esac
+			elif [ $freq != 5935 ]; then
+				case "$channel" in
+					36|40|44|48|52|56|60|64) seg0=50;;
+					100|104|108|112|116|120|124|128) seg0=114;;
+				esac
+			fi
+			;;
+	esac
+	printf "$seg0"
+}
+
 mac80211_hostapd_setup_base() {
 	local phy="$1"
 
@@ -333,20 +412,105 @@  mac80211_hostapd_setup_base() {
 	# 802.11ax
 	enable_ax=0
 	case "$htmode" in
-		HE*) enable_ax=1 ;;
+		HE20)   enable_ax=1
+			if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+				append base_cfg "op_class=131" "$N"
+			fi
+			;;
+		HE40)
+			enable_ax=1
+			idx="$(mac80211_get_seg0 "40")"
+			if [ $freq -ge 5180 ] && [ $freq != 5935 ]; then
+				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+					append base_cfg "op_class=132" "$N"
+				fi
+				append base_cfg "he_oper_chwidth=0" "$N"
+				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
+			fi
+			;;
+		HE80)
+			enable_ax=1
+			idx="$(mac80211_get_seg0 "80")"
+			if [ $freq != 5935 ]; then
+				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+					append base_cfg "op_class=133" "$N"
+				fi
+				append base_cfg "he_oper_chwidth=1" "$N"
+				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
+			fi
+			;;
+		HE160)
+			enable_ax=1
+			idx="$(mac80211_get_seg0 "160")"
+			if [ $freq != 5935 ]; then
+				if [ $freq -gt 5950 ] && [ $freq -le 7115 ]; then
+					append base_cfg "op_class=134" "$N"
+				fi
+				append base_cfg "he_oper_chwidth=2" "$N"
+				append base_cfg "he_oper_centr_freq_seg0_idx=$idx" "$N"
+			fi
+		;;
 	esac
 
 	if [ "$enable_ax" != "0" ]; then
+		json_get_vars \
+			he_su_beamformer:1 \
+			he_su_beamformee:0 \
+			he_mu_beamformer:1 \
+			he_twt_required:0 \
+			he_spr_sr_control:0 \
+			he_spr_non_srg_obss_pd_max_offset:1 \
+			he_bss_color:64
+	
 		append base_cfg "ieee80211ax=1" "$N"
+		append base_cfg "he_bss_color=$he_bss_color" "$N"
 		[ "$hwmode" = "a" ] && {
 			append base_cfg "he_oper_chwidth=$vht_oper_chwidth" "$N"
 			append base_cfg "he_oper_centr_freq_seg0_idx=$vht_center_seg0" "$N"
 		}
+		he_phy_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE PHY Capabilities/ { print $2 }' | head -1)
+		he_phy_cap=${he_phy_cap:2}
+		he_mac_cap=$(iw phy "$phy" info | awk -F "[()]" '/HE MAC Capabilities/ { print $2 }' | head -1)
+		he_mac_cap=${he_mac_cap:2}
+
+		mac80211_add_he_capabilities \
+			he_su_beamformer:${he_phy_cap:6:2}:0x80:$he_su_beamformer \
+			he_su_beamformee:${he_phy_cap:8:2}:0x1:$he_su_beamformee \
+			he_mu_beamformer:${he_phy_cap:8:2}:0x2:$he_mu_beamformer \
+			he_spr_sr_control:${he_phy_cap:14:2}:0x1:$he_spr_sr_control \
+			he_twt_required:${he_mac_cap:0:2}:0x6:$he_twt_required
+
+		[ "$he_spr_sr_control" != "0" ] && {
+			append base_cfg "he_spr_sr_control=$he_spr_sr_control" "$N"
+			append base_cfg "he_spr_non_srg_obss_pd_max_offset=$he_spr_non_srg_obss_pd_max_offset" "$N"
+		}
+		
 		append base_cfg "he_default_pe_duration=4" "$N"
 		append base_cfg "he_rts_threshold=1023" "$N"
-		append base_cfg "he_su_beamformer=1" "$N"
-		append base_cfg "he_su_beamformee=1" "$N"
-		append base_cfg "he_mu_beamformer=1" "$N"
+		append base_cfg "he_mu_edca_qos_info_param_count=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_q_ack=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_queue_request=0" "$N"
+		append base_cfg "he_mu_edca_qos_info_txop_request=0" "$N"
+		append base_cfg "he_mu_edca_ac_be_aifsn=8" "$N"
+		append base_cfg "he_mu_edca_ac_be_aci=0" "$N"
+		append base_cfg "he_mu_edca_ac_be_ecwmin=9" "$N"
+		append base_cfg "he_mu_edca_ac_be_ecwmax=10" "$N"
+		append base_cfg "he_mu_edca_ac_be_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_bk_aifsn=15" "$N"
+		append base_cfg "he_mu_edca_ac_bk_aci=1" "$N"
+		append base_cfg "he_mu_edca_ac_bk_ecwmin=9" "$N"
+		append base_cfg "he_mu_edca_ac_bk_ecwmax=10" "$N"
+		append base_cfg "he_mu_edca_ac_bk_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_vi_ecwmin=5" "$N"
+		append base_cfg "he_mu_edca_ac_vi_ecwmax=7" "$N"
+		append base_cfg "he_mu_edca_ac_vi_aifsn=5" "$N"
+		append base_cfg "he_mu_edca_ac_vi_aci=2" "$N"
+		append base_cfg "he_mu_edca_ac_vi_timer=255" "$N"
+		append base_cfg "he_mu_edca_ac_vo_aifsn=5" "$N"
+		append base_cfg "he_mu_edca_ac_vo_aci=3" "$N"
+		append base_cfg "he_mu_edca_ac_vo_ecwmin=5" "$N"
+		append base_cfg "he_mu_edca_ac_vo_ecwmax=7" "$N"
+		append base_cfg "he_mu_edca_ac_vo_timer=255" "$N"
 	fi
 
 	hostapd_prepare_device_config "$hostapd_conf_file" nl80211
@@ -401,6 +565,7 @@  mac80211_get_addr() {
 
 mac80211_generate_mac() {
 	local phy="$1"
+	local multiple_bssid="$2"
 	local id="${macidx:-0}"
 
 	local ref="$(cat /sys/class/ieee80211/${phy}/macaddress)"
@@ -425,6 +590,11 @@  mac80211_generate_mac() {
 
 	local oIFS="$IFS"; IFS=":"; set -- $ref; IFS="$oIFS"
 
+	[ "$multiple_bssid" -eq 1 ] && {
+		printf "02:%s:%s:%s:%s:%02x" $b1 $2 $3 $4 $5 $macidx
+		return
+	}
+
 	macidx=$(($id + 1))
 	[ "$((0x$mask1))" -gt 0 ] && {
 		b1="0x$1"
@@ -533,6 +703,7 @@  mac80211_iw_interface_add() {
 }
 
 mac80211_prepare_vif() {
+	local multiple_bssid=$1
 	json_select config
 
 	json_get_vars ifname mode ssid wds powersave macaddr enable wpa_psk_file vlan_file
@@ -546,7 +717,7 @@  mac80211_prepare_vif() {
 	json_select ..
 
 	[ -n "$macaddr" ] || {
-		macaddr="$(mac80211_generate_mac $phy)"
+		macaddr="$(mac80211_generate_mac $phy $multiple_bssid)"
 		macidx="$(($macidx + 1))"
 	}
 
@@ -687,8 +858,9 @@  mac80211_setup_supplicant_noctl() {
 
 mac80211_prepare_iw_htmode() {
 	case "$htmode" in
-		VHT20|HT20) iw_htmode=HT20;;
-		HT40*|VHT40|VHT160)
+		VHT20|HT20|HE20) iw_htmode=HT20;;
+		HT40*|VHT40|VHT160|\
+		HE40|HE40|HE160)
 			case "$hwmode" in
 				a)
 					case "$(( ($channel / 4) % 2 ))" in
@@ -712,7 +884,7 @@  mac80211_prepare_iw_htmode() {
 			esac
 			[ "$auto_channel" -gt 0 ] && iw_htmode="HT40+"
 		;;
-		VHT80)
+		VHT80|HE80)
 			iw_htmode="80MHZ"
 		;;
 		NONE|NOHT)
@@ -896,7 +1068,8 @@  drv_mac80211_setup() {
 		country chanbw distance \
 		txpower antenna_gain \
 		rxantenna txantenna \
-		frag rts beacon_int:100 htmode
+		frag rts beacon_int:100 htmode \
+		multiple_bssid:0
 	json_get_values basic_rate_list basic_rate
 	json_get_values scan_list scan_list
 	json_select ..
@@ -991,7 +1164,7 @@  drv_mac80211_setup() {
 	mac80211_prepare_iw_htmode
 	for_each_interface "sta adhoc mesh monitor" mac80211_prepare_vif
 	NEWAPLIST=
-	for_each_interface "ap" mac80211_prepare_vif
+	for_each_interface "ap" mac80211_prepare_vif ${multiple_bssid}
 	NEW_MD5=$(test -e "${hostapd_conf_file}" && md5sum ${hostapd_conf_file})
 	OLD_MD5=$(uci -q -P /var/state get wireless._${phy}.md5)
 	if [ "${NEWAPLIST}" != "${OLDAPLIST}" ]; then
diff --git a/package/kernel/mac80211/files/lib/wifi/mac80211.sh b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
index 3e99f06693..e7b3aa82c3 100644
--- a/package/kernel/mac80211/files/lib/wifi/mac80211.sh
+++ b/package/kernel/mac80211/files/lib/wifi/mac80211.sh
@@ -80,18 +80,23 @@  detect_mac80211() {
 		htmode=""
 		ht_capab=""
 
-		iw phy "$dev" info | grep -q 'Capabilities:' && htmode=HT20
+		iw phy "$dev" info | grep -q 'Capabilities:' && {
+			htmode=HT20
+			iw phy "$dev" info | grep -q 'HE.*Capabilities' && htmode="HE20"
+		}
 
-		iw phy "$dev" info | grep -q '\* 5... MHz \[' && {
+		iw phy "$dev" info | grep -q '5180 MHz' && {
 			mode_band="a"
-			channel=$(iw phy "$dev" info | grep '\* 5... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\].*//g')
+			channel="36"
+			iw phy "$dev" info | grep -q '5180 MHz.*disabled' && channel=149
 			iw phy "$dev" info | grep -q 'VHT Capabilities' && htmode="VHT80"
+			iw phy "$dev" info | grep -q 'HE.*Capabilities' && htmode="HE80"
 		}
 
-		iw phy "$dev" info | grep -q '\* 5.... MHz \[' && {
-			mode_band="ad"
-			channel=$(iw phy "$dev" info | grep '\* 5.... MHz \[' | grep '(disabled)' -v -m 1 | sed 's/[^[]*\[\|\|\].*//g')
-			iw phy "$dev" info | grep -q 'Capabilities:' && htmode="HT20"
+		iw phy "$dev" info | grep -q '5955 MHz' && {
+			mode_band="a"
+			channel="1"
+			htmode="HE80"
 		}
 
 		[ -n "$htmode" ] && ht_capab="set wireless.radio${devidx}.htmode=$htmode"