Skip to content
10 changes: 9 additions & 1 deletion drivers/net/wireless/ath/ath12k/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@ struct ath12k_link_vif {
u16 num_stations;
bool is_csa_in_progress;
struct wiphy_work bcn_tx_work;

bool set_wds_vdev_param;
bool nawds_enabled;
};

struct ath12k_vif {
Expand Down Expand Up @@ -491,6 +494,10 @@ struct ath12k_link_sta {
/* link address similar to ieee80211_link_sta */
u8 addr[ETH_ALEN];

u16 tcl_metadata;
u16 ast_hash;
u16 ast_idx;

/* the following are protected by ar->data_lock */
u32 changed; /* IEEE80211_RC_* */
u32 bw;
Expand Down Expand Up @@ -526,7 +533,8 @@ struct ath12k_sta {
u16 free_logical_link_idx_map;

enum ieee80211_sta_state state;
u16 tcl_metadata;

bool enable_4addr;
};

#define ATH12K_HALF_20MHZ_BW 10
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath12k/dp_peer.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ struct ath12k_dp_peer {
struct ath12k_dp_link_peer __rcu *link_peers[ATH12K_NUM_MAX_LINKS];
struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1];
struct ath12k_dp_rx_tid rx_tid[IEEE80211_NUM_TIDS + 1];

bool use_4addr;
};

struct ath12k_dp_link_peer *
Expand Down
67 changes: 46 additions & 21 deletions drivers/net/wireless/ath/ath12k/dp_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1095,20 +1095,33 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev,
static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
struct sk_buff *msdu,
enum hal_encrypt_type enctype,
struct hal_rx_desc_data *rx_info)
struct hal_rx_desc_data *rx_info,
enum ath12k_dp_rx_decap_type decap_type)
{
struct ieee80211_hdr *hdr;
struct ethhdr *eth;
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}};
struct ath12k_dp_rx_rfc1042_hdr *llc;

eth = (struct ethhdr *)msdu->data;
ether_addr_copy(da, eth->h_dest);
ether_addr_copy(sa, eth->h_source);
rfc.snap_type = eth->h_proto;
skb_pull(msdu, sizeof(*eth));
if (decap_type == DP_RX_DECAP_TYPE_8023) {
/*
* For 802.3 frames, eth->h_proto carries a length field, not
* an EtherType. The actual EtherType is in the LLC/SNAP header
* that follows the Ethernet header.
*/
llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + sizeof(*eth));
rfc.snap_type = llc->snap_type;
skb_pull(msdu, sizeof(*eth) + sizeof(*llc));
} else {
rfc.snap_type = eth->h_proto;
skb_pull(msdu, sizeof(*eth));
}
memcpy(skb_push(msdu, sizeof(rfc)), &rfc,
sizeof(rfc));
ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info);
Expand All @@ -1124,11 +1137,13 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
enum hal_encrypt_type enctype,
bool decrypted,
struct hal_rx_desc_data *rx_info)
struct hal_rx_desc_data *rx_info,
struct ath12k_dp_peer *peer)
{
enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type;
struct ethhdr *ehdr;

switch (rx_info->decap_type) {
switch (decap_type) {
case DP_RX_DECAP_TYPE_NATIVE_WIFI:
ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info);
break;
Expand All @@ -1142,19 +1157,40 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
/* mac80211 allows fast path only for authorized STA */
if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) {
ATH12K_SKB_RXCB(msdu)->is_eapol = true;
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}

if (peer && !peer->use_4addr &&
rx_info->is_from_ds && rx_info->is_to_ds) {
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}

/* PN for mcast packets will be validated in mac80211;
* remove eth header and add 802.11 header.
*/
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted)
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}

rx_info->rx_status->flag |= RX_FLAG_8023;
break;
case DP_RX_DECAP_TYPE_8023:
/* TODO: Handle undecap for these formats */
break;
/*
* Note that ethernet decap format indicates that the decapped
* packet is either Ethernet 2 (DIX) or 802.3 (uses SNAP/LLC).
*/
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
decap_type);
break;
}
rx_info->rx_status->flag |= RX_FLAG_8023;
}
}
EXPORT_SYMBOL(ath12k_dp_rx_h_undecap);
Expand Down Expand Up @@ -1336,9 +1372,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
struct ath12k_dp_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ieee80211_rx_status *status = rx_info->rx_status;
u8 decap = rx_info->decap_type;
bool is_mcbc = rxcb->is_mcbc;
bool is_eapol = rxcb->is_eapol;

peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);

Expand Down Expand Up @@ -1383,15 +1417,6 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc

/* TODO: trace rx packet */

/* PN for multicast packets are not validate in HW,
* so skip 802.3 rx path
* Also, fast_rx expects the STA to be authorized, hence
* eapol packets are sent in slow path.
*/
if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol &&
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
rx_status->flag |= RX_FLAG_8023;

ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
}
EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/wireless/ath/ath12k/dp_rx.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ void ath12k_dp_extract_rx_desc_data(struct ath12k_hal *hal,
void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu,
enum hal_encrypt_type enctype,
bool decrypted,
struct hal_rx_desc_data *rx_info);
struct hal_rx_desc_data *rx_info,
struct ath12k_dp_peer *peer);
void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struct *napi,
struct sk_buff *msdu,
struct hal_rx_desc_data *rx_info);
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/wireless/ath/ath12k/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,9 @@ struct hal_rx_desc_data {
addr2_present:1,
is_mcbc:1,
seq_ctl_valid:1,
fc_valid:1;
fc_valid:1,
is_to_ds:1,
is_from_ds:1;
u16 msdu_len;
u16 peer_id;
u16 seq_no;
Expand Down
131 changes: 123 additions & 8 deletions drivers/net/wireless/ath/ath12k/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -6582,6 +6582,79 @@ static int ath12k_mac_station_disassoc(struct ath12k *ar,
return 0;
}

static int ath12k_mac_sta_set_4addr(struct wiphy *wiphy, struct ath12k_sta *ahsta)
{
struct ath12k_dp_link_peer *peer;
struct ath12k_link_vif *arvif;
struct ath12k_link_sta *arsta;
struct ath12k_vif *ahvif;
struct ath12k_dp *dp;
unsigned long links;
struct ath12k *ar;
u8 link_id;
int ret;

links = ahsta->links_map;
for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
arsta = wiphy_dereference(wiphy, ahsta->link[link_id]);
if (!arsta)
continue;

arvif = arsta->arvif;
ahvif = arvif->ahvif;
ar = arvif->ar;

if (arvif->set_wds_vdev_param)
goto skip_nawds;

ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"setting USE_4ADDR for peer %pM\n", arsta->addr);

ret = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id,
WMI_PEER_USE_4ADDR,
WMI_PEER_4ADDR_ALLOW_EAPOL_DATA_FRAME);
if (ret) {
ath12k_warn(ar->ab, "failed to set peer %pM 4addr capability: %d\n",
arsta->addr, ret);
return ret;
}

if (ahvif->dp_vif.tx_encap_type != ATH12K_HW_TXRX_ETHERNET)
goto skip_nawds;

ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_AP_ENABLE_NAWDS,
WDS_EXT_ENABLE);
if (ret) {
ath12k_warn(ar->ab, "failed to set vdev %d nawds parameter: %d\n",
arvif->vdev_id, ret);
return ret;
}

arvif->nawds_enabled = true;

skip_nawds:
dp = ath12k_ab_to_dp(ar->ab);
spin_lock_bh(&dp->dp_lock);
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, arvif->vdev_id,
arsta->addr);
if (peer && peer->dp_peer) {
peer->dp_peer->ucast_ra_only = true;
peer->dp_peer->use_4addr = true;
} else {
spin_unlock_bh(&dp->dp_lock);
ath12k_warn(ar->ab, "failed to find DP peer for %pM\n",
arsta->addr);
return -ENOENT;
}

spin_unlock_bh(&dp->dp_lock);
}

return 0;
}

static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
{
struct ieee80211_link_sta *link_sta;
Expand Down Expand Up @@ -7073,10 +7146,7 @@ static int ath12k_mac_station_add(struct ath12k *ar,
ath12k_dbg(ab, ATH12K_DBG_MAC, "Added peer: %pM for VDEV: %d\n",
arsta->addr, arvif->vdev_id);

/* ------------------------------
FORCE WDS / 4-ADDRESS MODE
----------------------------- */
// if (ieee80211_vif_is_mesh(vif)) {
if (ieee80211_vif_is_mesh(vif)) {
ret = ath12k_wmi_set_peer_param(ar, arsta->addr,
arvif->vdev_id,
WMI_PEER_USE_4ADDR, 1);
Expand All @@ -7085,7 +7155,7 @@ static int ath12k_mac_station_add(struct ath12k *ar,
arsta->addr, ret);
goto free_peer;
}
//}
}

ret = ath12k_dp_peer_setup(ar, arvif->vdev_id, arsta->addr);
if (ret) {
Expand Down Expand Up @@ -7894,6 +7964,28 @@ int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ath12k_mac_op_sta_set_txpwr);

void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool enabled)
{
struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta);

lockdep_assert_wiphy(hw->wiphy);

/*
* 4-address mode disabled option is available only for station
* interface from mac80211, and we have wds_vdev_param for station
* interface and target will not allow to disable the wds_vdev_param
* during run time. So, add support only for enable case, for
* disable case station interface needs to be reconnect.
*/
if (enabled && !ahsta->enable_4addr) {
if (!ath12k_mac_sta_set_4addr(hw->wiphy, ahsta))
ahsta->enable_4addr = true;
}
}
EXPORT_SYMBOL(ath12k_mac_op_sta_set_4addr);

void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
Expand Down Expand Up @@ -10033,12 +10125,14 @@ static void ath12k_mac_update_vif_offload(struct ath12k_link_vif *arvif)
vif->offload_flags &= ~(IEEE80211_OFFLOAD_ENCAP_ENABLED |
IEEE80211_OFFLOAD_DECAP_ENABLED);

if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED)
if (vif->offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED) {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_ETHERNET;
else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags))
vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR;
} else if (test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_RAW;
else
} else {
ahvif->dp_vif.tx_encap_type = ATH12K_HW_TXRX_NATIVE_WIFI;
}

ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
param_id, ahvif->dp_vif.tx_encap_type);
Expand Down Expand Up @@ -10248,6 +10342,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
struct ieee80211_hw *hw = ah->hw;
struct ath12k_vif *ahvif = arvif->ahvif;
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
struct ath12k_wmi_vdev_create_arg vdev_arg = {};
struct ath12k_wmi_peer_create_arg peer_param = {};
struct ieee80211_bss_conf *link_conf = NULL;
Expand Down Expand Up @@ -10416,6 +10511,26 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
goto err_peer_del;
}

/*
* There could be race condition in firmware for the station
* interface between enabling 4-address peer WMI param and
* sending 4-address frame (NULL or EAPOL via TCL).
* Make the station as WDS while bringup itself
* to avoid the race condition
*/
if (vif->type == NL80211_IFTYPE_STATION &&
(wdev && wdev->use_4addr)) {
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_WDS,
1);
if (ret) {
ath12k_warn(ar->ab, "failed to set WDS vdev param: %d\n",
ret);
goto err_peer_del;
}
arvif->set_wds_vdev_param = true;
}

if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) &&
ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) {
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/ath/ath12k/mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
int ath12k_mac_op_sta_set_txpwr(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
void ath12k_mac_op_sta_set_4addr(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool enabled);
void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
Expand Down
Loading
Loading