wcn36xx: Implement beacon filtering
authorBryan O'Donoghue <bryan.odonoghue@linaro.org>
Tue, 14 Dec 2021 13:46:30 +0000 (13:46 +0000)
committerKalle Valo <quic_kvalo@quicinc.com>
Thu, 16 Dec 2021 15:37:36 +0000 (17:37 +0200)
The prima driver facilitates the direct programming of beacon filter tables via
SMD commands.

The purpose of beacon filters is quote:

/* When beacon filtering is enabled, firmware will
 * analyze the selected beacons received during BMPS,
 * and monitor any changes in the IEs as listed below.
 * The format of the table is:
 *    - EID
 *    - Check for IE presence
 *    - Byte offset
 *    - Byte value
 *    - Bit Mask
 *    - Byte reference
 */

The default filter table looks something like this:

tBeaconFilterIe gaBcnFilterTable[12] =
{
  { WLAN_EID_DS_PARAMS, 0u, { 0u, 0u, 0u, 0u } },
  { WLAN_EID_ERP_INFO, 0u, { 0u, 0u, 248u, 0u } },
  { WLAN_EID_EDCA_PARAM_SET, 0u, { 0u, 0u, 240u, 0u } },
  { WLAN_EID_QOS_CAPA, 0u, { 0u, 0u, 240u, 0u } },
  { WLAN_EID_CHANNEL_SWITCH, 1u, { 0u, 0u, 0u, 0u } },
  { WLAN_EID_QUIET, 1u, { 0u, 0u, 0u, 0u } },
  { WLAN_EID_HT_OPERATION, 0u, { 0u, 0u, 0u, 0u } },
  { WLAN_EID_HT_OPERATION, 0u, { 1u, 0u, 248u, 0u } },
  { WLAN_EID_HT_OPERATION, 0u, { 2u, 0u, 235u, 0u } },
  { WLAN_EID_HT_OPERATION, 0u, { 5u, 0u, 253u, 0u } },
  { WLAN_EID_PWR_CONSTRAINT, 0u, { 0u, 0u, 0u, 0u } },
  { WLAN_EID_OPMODE_NOTIF, 0u, { 0u, 0u, 0u, 0u } }
};

Add in an equivalent filter set as present in the prima Linux driver.
For now omit the beacon filter "rem" command as the driver does not have an
explicit call to that SMD command. The filter mask should only count when
we are inside BMPS anyway.

Replicating the ability to program the filter table gives us scope to add and
remove elements in future. For now though this patch makes the rote-copy of the
downstream Linux beacon filter table, which we can tweak as desired from now
on.

Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20211214134630.2214840-4-bryan.odonoghue@linaro.org
drivers/net/wireless/ath/wcn36xx/hal.h
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wcn36xx/smd.h

index e9fec11..2a1db97 100644 (file)
@@ -3468,6 +3468,22 @@ struct beacon_filter_ie {
        u8 ref;
 } __packed;
 
+#define WCN36XX_FILTER_CAPABILITY_MASK         0x73cf
+#define WCN36XX_FILTER_IE_DS_CHANNEL_MASK      0x00
+#define WCN36XX_FILTER_IE_ERP_FILTER_MASK      0xF8
+#define WCN36XX_FILTER_IE_EDCA_FILTER_MASK     0xF0
+#define WCN36XX_FILTER_IE_QOS_FILTER_MASK      0xF0
+#define WCN36XX_FILTER_IE_CHANNEL_SWITCH_MASK  0x00
+#define WCN36XX_FILTER_IE_HT_BYTE0_FILTER_MASK 0x00
+#define WCN36XX_FILTER_IE_HT_BYTE1_FILTER_MASK 0xF8
+#define WCN36XX_FILTER_IE_HT_BYTE2_FILTER_MASK 0xEB
+#define WCN36XX_FILTER_IE_HT_BYTE5_FILTER_MASK 0xFD
+#define WCN36XX_FILTER_IE_PWR_CONSTRAINT_MASK  0x00
+#define WCN36XX_FILTER_IE_OPMODE_NOTIF_MASK    0x00
+#define WCN36XX_FILTER_IE_VHTOP_CHWIDTH_MASK   0xFC
+#define WCN36XX_FILTER_IE_RSN_MASK             0x00
+#define WCN36XX_FILTER_IE_VENDOR_MASK          0x00
+
 /* The above structure would be followed by multiple of below mentioned
  * structure
  */
index 4074398..9575d73 100644 (file)
@@ -934,6 +934,8 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                         * place where AID is available.
                         */
                        wcn36xx_smd_config_sta(wcn, vif, sta);
+                       if (vif->type == NL80211_IFTYPE_STATION)
+                               wcn36xx_smd_add_beacon_filter(wcn, vif);
                        wcn36xx_enable_keep_alive_null_packet(wcn, vif);
                } else {
                        wcn36xx_dbg(WCN36XX_DBG_MAC,
index bcc3530..caeb689 100644 (file)
@@ -3193,6 +3193,91 @@ out:
        return ret;
 }
 
+#define BEACON_FILTER(eid, presence, offs, val, mask, ref_val) \
+       {                                       \
+               .element_id = eid,              \
+               .check_ie_presence = presence,  \
+               .offset = offs,                 \
+               .value = val,                   \
+               .bitmask = mask,                \
+               .ref = ref_val,                 \
+       }
+
+static const struct beacon_filter_ie bcn_filter_ies[] = {
+       BEACON_FILTER(WLAN_EID_DS_PARAMS, 0, 0, 0,
+                     WCN36XX_FILTER_IE_DS_CHANNEL_MASK, 0),
+       BEACON_FILTER(WLAN_EID_ERP_INFO, 0, 0, 0,
+                     WCN36XX_FILTER_IE_ERP_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_EDCA_PARAM_SET, 0, 0, 0,
+                     WCN36XX_FILTER_IE_EDCA_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_QOS_CAPA, 0, 0, 0,
+                     WCN36XX_FILTER_IE_QOS_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_CHANNEL_SWITCH, 1, 0, 0,
+                     WCN36XX_FILTER_IE_CHANNEL_SWITCH_MASK, 0),
+       BEACON_FILTER(WLAN_EID_HT_OPERATION, 0, 0, 0,
+                     WCN36XX_FILTER_IE_HT_BYTE0_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_HT_OPERATION, 0, 2, 0,
+                     WCN36XX_FILTER_IE_HT_BYTE2_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_HT_OPERATION, 0, 5, 0,
+                     WCN36XX_FILTER_IE_HT_BYTE5_FILTER_MASK, 0),
+       BEACON_FILTER(WLAN_EID_PWR_CONSTRAINT, 0, 0, 0,
+                     WCN36XX_FILTER_IE_PWR_CONSTRAINT_MASK, 0),
+       BEACON_FILTER(WLAN_EID_OPMODE_NOTIF, 0, 0, 0,
+                     WCN36XX_FILTER_IE_OPMODE_NOTIF_MASK, 0),
+       BEACON_FILTER(WLAN_EID_VHT_OPERATION, 0, 0, 0,
+                     WCN36XX_FILTER_IE_VHTOP_CHWIDTH_MASK, 0),
+       BEACON_FILTER(WLAN_EID_RSN, 1, 0, 0,
+                     WCN36XX_FILTER_IE_RSN_MASK, 0),
+       BEACON_FILTER(WLAN_EID_VENDOR_SPECIFIC, 1, 0, 0,
+                     WCN36XX_FILTER_IE_VENDOR_MASK, 0),
+};
+
+int wcn36xx_smd_add_beacon_filter(struct wcn36xx *wcn,
+                                 struct ieee80211_vif *vif)
+{
+       struct wcn36xx_hal_add_bcn_filter_req_msg msg_body, *body;
+       struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
+       u8 *payload;
+       size_t payload_size;
+       int ret;
+
+       if (!get_feat_caps(wcn->fw_feat_caps, BCN_FILTER))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&wcn->hal_mutex);
+       INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BCN_FILTER_REQ);
+
+       PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
+
+       body = (struct wcn36xx_hal_add_bcn_filter_req_msg *)wcn->hal_buf;
+       body->capability_info = vif->bss_conf.assoc_capability;
+       body->capability_mask = WCN36XX_FILTER_CAPABILITY_MASK;
+       body->beacon_interval = vif->bss_conf.beacon_int;
+       body->ie_num = ARRAY_SIZE(bcn_filter_ies);
+       body->bss_index = vif_priv->bss_index;
+
+       payload = ((u8 *)body) + body->header.len;
+       payload_size = sizeof(bcn_filter_ies);
+       memcpy(payload, &bcn_filter_ies, payload_size);
+
+       body->header.len += payload_size;
+
+       ret = wcn36xx_smd_send_and_wait(wcn, body->header.len);
+       if (ret) {
+               wcn36xx_err("Sending add bcn_filter failed\n");
+               goto out;
+       }
+
+       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
+       if (ret) {
+               wcn36xx_err("add bcn filter response failed err=%d\n", ret);
+               goto out;
+       }
+out:
+       mutex_unlock(&wcn->hal_mutex);
+       return ret;
+}
+
 int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
                            void *buf, int len, void *priv, u32 addr)
 {
@@ -3248,6 +3333,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
        case WCN36XX_HAL_ENTER_IMPS_RSP:
        case WCN36XX_HAL_EXIT_IMPS_RSP:
        case WCN36XX_HAL_UPDATE_CHANNEL_LIST_RSP:
+       case WCN36XX_HAL_ADD_BCN_FILTER_RSP:
                memcpy(wcn->hal_buf, buf, len);
                wcn->hal_rsp_len = len;
                complete(&wcn->hal_rsp_compl);
index cfde153..957cfa8 100644 (file)
@@ -167,4 +167,7 @@ int wcn36xx_smd_host_resume(struct wcn36xx *wcn);
 int wcn36xx_smd_enter_imps(struct wcn36xx *wcn);
 int wcn36xx_smd_exit_imps(struct wcn36xx *wcn);
 
+int wcn36xx_smd_add_beacon_filter(struct wcn36xx *wcn,
+                                 struct ieee80211_vif *vif);
+
 #endif /* _SMD_H_ */