18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2015 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include "core.h" 78c2ecf20Sopenharmony_ci#include "wmi.h" 88c2ecf20Sopenharmony_ci#include "mac.h" 98c2ecf20Sopenharmony_ci#include "p2p.h" 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_cistatic void ath10k_p2p_noa_ie_fill(u8 *data, size_t len, 128c2ecf20Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct ieee80211_p2p_noa_attr *noa_attr; 158c2ecf20Sopenharmony_ci u8 ctwindow_oppps = noa->ctwindow_oppps; 168c2ecf20Sopenharmony_ci u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET; 178c2ecf20Sopenharmony_ci bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT); 188c2ecf20Sopenharmony_ci __le16 *noa_attr_len; 198c2ecf20Sopenharmony_ci u16 attr_len; 208c2ecf20Sopenharmony_ci u8 noa_descriptors = noa->num_descriptors; 218c2ecf20Sopenharmony_ci int i; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* P2P IE */ 248c2ecf20Sopenharmony_ci data[0] = WLAN_EID_VENDOR_SPECIFIC; 258c2ecf20Sopenharmony_ci data[1] = len - 2; 268c2ecf20Sopenharmony_ci data[2] = (WLAN_OUI_WFA >> 16) & 0xff; 278c2ecf20Sopenharmony_ci data[3] = (WLAN_OUI_WFA >> 8) & 0xff; 288c2ecf20Sopenharmony_ci data[4] = (WLAN_OUI_WFA >> 0) & 0xff; 298c2ecf20Sopenharmony_ci data[5] = WLAN_OUI_TYPE_WFA_P2P; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci /* NOA ATTR */ 328c2ecf20Sopenharmony_ci data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; 338c2ecf20Sopenharmony_ci noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */ 348c2ecf20Sopenharmony_ci noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9]; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci noa_attr->index = noa->index; 378c2ecf20Sopenharmony_ci noa_attr->oppps_ctwindow = ctwindow; 388c2ecf20Sopenharmony_ci if (oppps) 398c2ecf20Sopenharmony_ci noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci for (i = 0; i < noa_descriptors; i++) { 428c2ecf20Sopenharmony_ci noa_attr->desc[i].count = 438c2ecf20Sopenharmony_ci __le32_to_cpu(noa->descriptors[i].type_count); 448c2ecf20Sopenharmony_ci noa_attr->desc[i].duration = noa->descriptors[i].duration; 458c2ecf20Sopenharmony_ci noa_attr->desc[i].interval = noa->descriptors[i].interval; 468c2ecf20Sopenharmony_ci noa_attr->desc[i].start_time = noa->descriptors[i].start_time; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci attr_len = 2; /* index + oppps_ctwindow */ 508c2ecf20Sopenharmony_ci attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); 518c2ecf20Sopenharmony_ci *noa_attr_len = __cpu_to_le16(attr_len); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic size_t ath10k_p2p_noa_ie_len_compute(const struct wmi_p2p_noa_info *noa) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci size_t len = 0; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (!noa->num_descriptors && 598c2ecf20Sopenharmony_ci !(noa->ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT)) 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci len += 1 + 1 + 4; /* EID + len + OUI */ 638c2ecf20Sopenharmony_ci len += 1 + 2; /* noa attr + attr len */ 648c2ecf20Sopenharmony_ci len += 1 + 1; /* index + oppps_ctwindow */ 658c2ecf20Sopenharmony_ci len += noa->num_descriptors * sizeof(struct ieee80211_p2p_noa_desc); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return len; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void ath10k_p2p_noa_ie_assign(struct ath10k_vif *arvif, void *ie, 718c2ecf20Sopenharmony_ci size_t len) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->data_lock); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci kfree(arvif->u.ap.noa_data); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci arvif->u.ap.noa_data = ie; 808c2ecf20Sopenharmony_ci arvif->u.ap.noa_len = len; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void __ath10k_p2p_noa_update(struct ath10k_vif *arvif, 848c2ecf20Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 878c2ecf20Sopenharmony_ci void *ie; 888c2ecf20Sopenharmony_ci size_t len; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci lockdep_assert_held(&ar->data_lock); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ath10k_p2p_noa_ie_assign(arvif, NULL, 0); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci len = ath10k_p2p_noa_ie_len_compute(noa); 958c2ecf20Sopenharmony_ci if (!len) 968c2ecf20Sopenharmony_ci return; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci ie = kmalloc(len, GFP_ATOMIC); 998c2ecf20Sopenharmony_ci if (!ie) 1008c2ecf20Sopenharmony_ci return; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ath10k_p2p_noa_ie_fill(ie, len, noa); 1038c2ecf20Sopenharmony_ci ath10k_p2p_noa_ie_assign(arvif, ie, len); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_civoid ath10k_p2p_noa_update(struct ath10k_vif *arvif, 1078c2ecf20Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct ath10k *ar = arvif->ar; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci spin_lock_bh(&ar->data_lock); 1128c2ecf20Sopenharmony_ci __ath10k_p2p_noa_update(arvif, noa); 1138c2ecf20Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct ath10k_p2p_noa_arg { 1178c2ecf20Sopenharmony_ci u32 vdev_id; 1188c2ecf20Sopenharmony_ci const struct wmi_p2p_noa_info *noa; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void ath10k_p2p_noa_update_vdev_iter(void *data, u8 *mac, 1228c2ecf20Sopenharmony_ci struct ieee80211_vif *vif) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct ath10k_vif *arvif = (void *)vif->drv_priv; 1258c2ecf20Sopenharmony_ci struct ath10k_p2p_noa_arg *arg = data; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (arvif->vdev_id != arg->vdev_id) 1288c2ecf20Sopenharmony_ci return; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci ath10k_p2p_noa_update(arvif, arg->noa); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_civoid ath10k_p2p_noa_update_by_vdev_id(struct ath10k *ar, u32 vdev_id, 1348c2ecf20Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct ath10k_p2p_noa_arg arg = { 1378c2ecf20Sopenharmony_ci .vdev_id = vdev_id, 1388c2ecf20Sopenharmony_ci .noa = noa, 1398c2ecf20Sopenharmony_ci }; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic(ar->hw, 1428c2ecf20Sopenharmony_ci IEEE80211_IFACE_ITER_NORMAL, 1438c2ecf20Sopenharmony_ci ath10k_p2p_noa_update_vdev_iter, 1448c2ecf20Sopenharmony_ci &arg); 1458c2ecf20Sopenharmony_ci} 146