]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blobdiff - drivers/net/wireless/ath/ath11k/wmi.c
ath11k: put hw to DBS using WMI_PDEV_SET_HW_MODE_CMDID
[mirror_ubuntu-hirsute-kernel.git] / drivers / net / wireless / ath / ath11k / wmi.c
index 6e173737c0eb2a967d448ebee1a2ee6f87a01208..79b7f991c61217b80a048dc4a952f598f7b56fdb 100644 (file)
@@ -122,6 +122,12 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
                = { .min_len = sizeof(struct wmi_stats_event) },
        [WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]
                = { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) },
+       [WMI_TAG_HOST_SWFDA_EVENT] = {
+               .min_len = sizeof(struct wmi_fils_discovery_event) },
+       [WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT] = {
+               .min_len = sizeof(struct wmi_probe_resp_tx_status_event) },
+       [WMI_TAG_VDEV_DELETE_RESP_EVENT] = {
+               .min_len = sizeof(struct wmi_vdev_delete_resp_event) },
 };
 
 #define PRIMAP(_hw_mode_) \
@@ -1682,7 +1688,8 @@ int ath11k_wmi_vdev_install_key(struct ath11k *ar,
 
 static inline void
 ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
-                          struct peer_assoc_params *param)
+                          struct peer_assoc_params *param,
+                          bool hw_crypto_disabled)
 {
        cmd->peer_flags = 0;
 
@@ -1736,7 +1743,8 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
                cmd->peer_flags |= WMI_PEER_AUTH;
        if (param->need_ptk_4_way) {
                cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY;
-               cmd->peer_flags &= ~WMI_PEER_AUTH;
+               if (!hw_crypto_disabled)
+                       cmd->peer_flags &= ~WMI_PEER_AUTH;
        }
        if (param->need_gtk_2_way)
                cmd->peer_flags |= WMI_PEER_NEED_GTK_2_WAY;
@@ -1803,7 +1811,9 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
        cmd->peer_new_assoc = param->peer_new_assoc;
        cmd->peer_associd = param->peer_associd;
 
-       ath11k_wmi_copy_peer_flags(cmd, param);
+       ath11k_wmi_copy_peer_flags(cmd, param,
+                                  test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED,
+                                           &ar->ab->dev_flags));
 
        ether_addr_copy(cmd->peer_macaddr.addr, param->peer_mac);
 
@@ -1946,6 +1956,11 @@ void ath11k_wmi_start_scan_init(struct ath11k *ar,
                                  WMI_SCAN_EVENT_DEQUEUED;
        arg->scan_flags |= WMI_SCAN_CHAN_STAT_EVENT;
        arg->num_bssid = 1;
+
+       /* fill bssid_list[0] with 0xff, otherwise bssid and RA will be
+        * ZEROs in probe request
+        */
+       eth_broadcast_addr(arg->bssid_list[0].addr);
 }
 
 static inline void
@@ -3064,6 +3079,137 @@ int ath11k_wmi_send_bss_color_change_enable_cmd(struct ath11k *ar, u32 vdev_id,
        return ret;
 }
 
+int ath11k_wmi_fils_discovery_tmpl(struct ath11k *ar, u32 vdev_id,
+                                  struct sk_buff *tmpl)
+{
+       struct wmi_tlv *tlv;
+       struct sk_buff *skb;
+       void *ptr;
+       int ret, len;
+       size_t aligned_len;
+       struct wmi_fils_discovery_tmpl_cmd *cmd;
+
+       aligned_len = roundup(tmpl->len, 4);
+       len = sizeof(*cmd) + TLV_HDR_SIZE + aligned_len;
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI vdev %i set FILS discovery template\n", vdev_id);
+
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_fils_discovery_tmpl_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                    WMI_TAG_FILS_DISCOVERY_TMPL_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+       cmd->vdev_id = vdev_id;
+       cmd->buf_len = tmpl->len;
+       ptr = skb->data + sizeof(*cmd);
+
+       tlv = ptr;
+       tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
+                     FIELD_PREP(WMI_TLV_LEN, aligned_len);
+       memcpy(tlv->value, tmpl->data, tmpl->len);
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_FILS_DISCOVERY_TMPL_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "WMI vdev %i failed to send FILS discovery template command\n",
+                           vdev_id);
+               dev_kfree_skb(skb);
+       }
+       return ret;
+}
+
+int ath11k_wmi_probe_resp_tmpl(struct ath11k *ar, u32 vdev_id,
+                              struct sk_buff *tmpl)
+{
+       struct wmi_probe_tmpl_cmd *cmd;
+       struct wmi_bcn_prb_info *probe_info;
+       struct wmi_tlv *tlv;
+       struct sk_buff *skb;
+       void *ptr;
+       int ret, len;
+       size_t aligned_len = roundup(tmpl->len, 4);
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI vdev %i set probe response template\n", vdev_id);
+
+       len = sizeof(*cmd) + sizeof(*probe_info) + TLV_HDR_SIZE + aligned_len;
+
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_probe_tmpl_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PRB_TMPL_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+       cmd->vdev_id = vdev_id;
+       cmd->buf_len = tmpl->len;
+
+       ptr = skb->data + sizeof(*cmd);
+
+       probe_info = ptr;
+       len = sizeof(*probe_info);
+       probe_info->tlv_header = FIELD_PREP(WMI_TLV_TAG,
+                                           WMI_TAG_BCN_PRB_INFO) |
+                                FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+       probe_info->caps = 0;
+       probe_info->erp = 0;
+
+       ptr += sizeof(*probe_info);
+
+       tlv = ptr;
+       tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
+                     FIELD_PREP(WMI_TLV_LEN, aligned_len);
+       memcpy(tlv->value, tmpl->data, tmpl->len);
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_PRB_TMPL_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "WMI vdev %i failed to send probe response template command\n",
+                           vdev_id);
+               dev_kfree_skb(skb);
+       }
+       return ret;
+}
+
+int ath11k_wmi_fils_discovery(struct ath11k *ar, u32 vdev_id, u32 interval,
+                             bool unsol_bcast_probe_resp_enabled)
+{
+       struct sk_buff *skb;
+       int ret, len;
+       struct wmi_fils_discovery_cmd *cmd;
+
+       ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+                  "WMI vdev %i set %s interval to %u TU\n",
+                  vdev_id, unsol_bcast_probe_resp_enabled ?
+                  "unsolicited broadcast probe response" : "FILS discovery",
+                  interval);
+
+       len = sizeof(*cmd);
+       skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_fils_discovery_cmd *)skb->data;
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ENABLE_FILS_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+       cmd->vdev_id = vdev_id;
+       cmd->interval = interval;
+       cmd->config = unsol_bcast_probe_resp_enabled;
+
+       ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_ENABLE_FILS_CMDID);
+       if (ret) {
+               ath11k_warn(ar->ab,
+                           "WMI vdev %i failed to send FILS discovery enable/disable command\n",
+                           vdev_id);
+               dev_kfree_skb(skb);
+       }
+       return ret;
+}
+
 static void
 ath11k_fill_band_to_mac_param(struct ath11k_base  *soc,
                              struct wmi_host_pdev_band_to_mac *band_to_mac)
@@ -3333,6 +3479,35 @@ int ath11k_wmi_wait_for_unified_ready(struct ath11k_base *ab)
        return 0;
 }
 
+int ath11k_wmi_set_hw_mode(struct ath11k_base *ab,
+                          enum wmi_host_hw_mode_config_type mode)
+{
+       struct wmi_pdev_set_hw_mode_cmd_param *cmd;
+       struct sk_buff *skb;
+       struct ath11k_wmi_base *wmi_ab = &ab->wmi_ab;
+       int len;
+       int ret;
+
+       len = sizeof(*cmd);
+
+       skb = ath11k_wmi_alloc_skb(wmi_ab, len);
+       cmd = (struct wmi_pdev_set_hw_mode_cmd_param *)skb->data;
+
+       cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_HW_MODE_CMD) |
+                         FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+       cmd->pdev_id = WMI_PDEV_ID_SOC;
+       cmd->hw_mode_index = mode;
+
+       ret = ath11k_wmi_cmd_send(&wmi_ab->wmi[0], skb, WMI_PDEV_SET_HW_MODE_CMDID);
+       if (ret) {
+               ath11k_warn(ab, "failed to send WMI_PDEV_SET_HW_MODE_CMDID\n");
+               dev_kfree_skb(skb);
+       }
+
+       return ret;
+}
+
 int ath11k_wmi_cmd_init(struct ath11k_base *ab)
 {
        struct ath11k_wmi_base *wmi_sc = &ab->wmi_ab;
@@ -3351,13 +3526,11 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab)
        init_param.hw_mode_id = wmi_sc->preferred_hw_mode;
        init_param.mem_chunks = wmi_sc->mem_chunks;
 
-       if (wmi_sc->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE)
+       if (ab->hw_params.single_pdev_only)
                init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX;
 
-       if (ab->hw_params.needs_band_to_mac) {
-               init_param.num_band_to_mac = ab->num_radios;
-               ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac);
-       }
+       init_param.num_band_to_mac = ab->num_radios;
+       ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac);
 
        return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param);
 }
@@ -4242,6 +4415,34 @@ static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *
        return 0;
 }
 
+static int ath11k_pull_vdev_del_resp_ev(struct ath11k_base *ab,
+                                       struct sk_buff *skb,
+                                       u32 *vdev_id)
+{
+       const void **tb;
+       const struct wmi_vdev_delete_resp_event *ev;
+       int ret;
+
+       tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath11k_warn(ab, "failed to parse tlv: %d\n", ret);
+               return ret;
+       }
+
+       ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT];
+       if (!ev) {
+               ath11k_warn(ab, "failed to fetch vdev delete resp ev");
+               kfree(tb);
+               return -EPROTO;
+       }
+
+       *vdev_id = ev->vdev_id;
+
+       kfree(tb);
+       return 0;
+}
+
 static int ath11k_pull_bcn_tx_status_ev(struct ath11k_base *ab, void *evt_buf,
                                        u32 len, u32 *vdev_id,
                                        u32 *tx_status)
@@ -5563,15 +5764,54 @@ static int ath11k_ready_event(struct ath11k_base *ab, struct sk_buff *skb)
 static void ath11k_peer_delete_resp_event(struct ath11k_base *ab, struct sk_buff *skb)
 {
        struct wmi_peer_delete_resp_event peer_del_resp;
+       struct ath11k *ar;
 
        if (ath11k_pull_peer_del_resp_ev(ab, skb, &peer_del_resp) != 0) {
                ath11k_warn(ab, "failed to extract peer delete resp");
                return;
        }
 
-       /* TODO: Do we need to validate whether ath11k_peer_find() return NULL
-        *       Why this is needed when there is HTT event for peer delete
-        */
+       rcu_read_lock();
+       ar = ath11k_mac_get_ar_by_vdev_id(ab, peer_del_resp.vdev_id);
+       if (!ar) {
+               ath11k_warn(ab, "invalid vdev id in peer delete resp ev %d",
+                           peer_del_resp.vdev_id);
+               rcu_read_unlock();
+               return;
+       }
+
+       complete(&ar->peer_delete_done);
+       rcu_read_unlock();
+       ath11k_dbg(ab, ATH11K_DBG_WMI, "peer delete resp for vdev id %d addr %pM\n",
+                  peer_del_resp.vdev_id, peer_del_resp.peer_macaddr.addr);
+}
+
+static void ath11k_vdev_delete_resp_event(struct ath11k_base *ab,
+                                         struct sk_buff *skb)
+{
+       struct ath11k *ar;
+       u32 vdev_id = 0;
+
+       if (ath11k_pull_vdev_del_resp_ev(ab, skb, &vdev_id) != 0) {
+               ath11k_warn(ab, "failed to extract vdev delete resp");
+               return;
+       }
+
+       rcu_read_lock();
+       ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id);
+       if (!ar) {
+               ath11k_warn(ab, "invalid vdev id in vdev delete resp ev %d",
+                           vdev_id);
+               rcu_read_unlock();
+               return;
+       }
+
+       complete(&ar->vdev_delete_done);
+
+       rcu_read_unlock();
+
+       ath11k_dbg(ab, ATH11K_DBG_WMI, "vdev delete resp for vdev id %d\n",
+                  vdev_id);
 }
 
 static inline const char *ath11k_wmi_vdev_resp_print(u32 vdev_resp_status)
@@ -5650,7 +5890,7 @@ static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *sk
        }
 
        rcu_read_lock();
-       ar = ath11k_mac_get_ar_vdev_stop_status(ab, vdev_id);
+       ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id);
        if (!ar) {
                ath11k_warn(ab, "invalid vdev id in vdev stopped ev %d",
                            vdev_id);
@@ -6429,6 +6669,68 @@ ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,
        ath11k_thermal_event_temperature(ar, ev.temp);
 }
 
+static void ath11k_fils_discovery_event(struct ath11k_base *ab,
+                                       struct sk_buff *skb)
+{
+       const void **tb;
+       const struct wmi_fils_discovery_event *ev;
+       int ret;
+
+       tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath11k_warn(ab,
+                           "failed to parse FILS discovery event tlv %d\n",
+                           ret);
+               return;
+       }
+
+       ev = tb[WMI_TAG_HOST_SWFDA_EVENT];
+       if (!ev) {
+               ath11k_warn(ab, "failed to fetch FILS discovery event\n");
+               kfree(tb);
+               return;
+       }
+
+       ath11k_warn(ab,
+                   "FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n",
+                   ev->vdev_id, ev->fils_tt, ev->tbtt);
+
+       kfree(tb);
+}
+
+static void ath11k_probe_resp_tx_status_event(struct ath11k_base *ab,
+                                             struct sk_buff *skb)
+{
+       const void **tb;
+       const struct wmi_probe_resp_tx_status_event *ev;
+       int ret;
+
+       tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath11k_warn(ab,
+                           "failed to parse probe response transmission status event tlv: %d\n",
+                           ret);
+               return;
+       }
+
+       ev = tb[WMI_TAG_OFFLOAD_PRB_RSP_TX_STATUS_EVENT];
+       if (!ev) {
+               ath11k_warn(ab,
+                           "failed to fetch probe response transmission status event");
+               kfree(tb);
+               return;
+       }
+
+       if (ev->tx_status)
+               ath11k_warn(ab,
+                           "Probe response transmission failed for vdev_id %u, status %u\n",
+                           ev->vdev_id, ev->tx_status);
+
+       kfree(tb);
+}
+
 static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
 {
        struct wmi_cmd_hdr *cmd_hdr;
@@ -6515,9 +6817,14 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
                ath11k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
                break;
+       case WMI_HOST_FILS_DISCOVERY_EVENTID:
+               ath11k_fils_discovery_event(ab, skb);
+               break;
+       case WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID:
+               ath11k_probe_resp_tx_status_event(ab, skb);
+               break;
        /* add Unsupported events here */
        case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
-       case WMI_VDEV_DELETE_RESP_EVENTID:
        case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
        case WMI_TWT_ENABLE_EVENTID:
        case WMI_TWT_DISABLE_EVENTID:
@@ -6528,6 +6835,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
        case WMI_PDEV_DFS_RADAR_DETECTION_EVENTID:
                ath11k_wmi_pdev_dfs_radar_detected_event(ab, skb);
                break;
+       case WMI_VDEV_DELETE_RESP_EVENTID:
+               ath11k_vdev_delete_resp_event(ab, skb);
+               break;
        /* TODO: Add remaining events */
        default:
                ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id);