162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015 Qualcomm Atheros, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "core.h" 762306a36Sopenharmony_ci#include "wmi.h" 862306a36Sopenharmony_ci#include "mac.h" 962306a36Sopenharmony_ci#include "p2p.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic void ath10k_p2p_noa_ie_fill(u8 *data, size_t len, 1262306a36Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 1362306a36Sopenharmony_ci{ 1462306a36Sopenharmony_ci struct ieee80211_p2p_noa_attr *noa_attr; 1562306a36Sopenharmony_ci u8 ctwindow_oppps = noa->ctwindow_oppps; 1662306a36Sopenharmony_ci u8 ctwindow = ctwindow_oppps >> WMI_P2P_OPPPS_CTWINDOW_OFFSET; 1762306a36Sopenharmony_ci bool oppps = !!(ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT); 1862306a36Sopenharmony_ci __le16 *noa_attr_len; 1962306a36Sopenharmony_ci u16 attr_len; 2062306a36Sopenharmony_ci u8 noa_descriptors = noa->num_descriptors; 2162306a36Sopenharmony_ci int i; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci /* P2P IE */ 2462306a36Sopenharmony_ci data[0] = WLAN_EID_VENDOR_SPECIFIC; 2562306a36Sopenharmony_ci data[1] = len - 2; 2662306a36Sopenharmony_ci data[2] = (WLAN_OUI_WFA >> 16) & 0xff; 2762306a36Sopenharmony_ci data[3] = (WLAN_OUI_WFA >> 8) & 0xff; 2862306a36Sopenharmony_ci data[4] = (WLAN_OUI_WFA >> 0) & 0xff; 2962306a36Sopenharmony_ci data[5] = WLAN_OUI_TYPE_WFA_P2P; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* NOA ATTR */ 3262306a36Sopenharmony_ci data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; 3362306a36Sopenharmony_ci noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */ 3462306a36Sopenharmony_ci noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9]; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci noa_attr->index = noa->index; 3762306a36Sopenharmony_ci noa_attr->oppps_ctwindow = ctwindow; 3862306a36Sopenharmony_ci if (oppps) 3962306a36Sopenharmony_ci noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci for (i = 0; i < noa_descriptors; i++) { 4262306a36Sopenharmony_ci noa_attr->desc[i].count = 4362306a36Sopenharmony_ci __le32_to_cpu(noa->descriptors[i].type_count); 4462306a36Sopenharmony_ci noa_attr->desc[i].duration = noa->descriptors[i].duration; 4562306a36Sopenharmony_ci noa_attr->desc[i].interval = noa->descriptors[i].interval; 4662306a36Sopenharmony_ci noa_attr->desc[i].start_time = noa->descriptors[i].start_time; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci attr_len = 2; /* index + oppps_ctwindow */ 5062306a36Sopenharmony_ci attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc); 5162306a36Sopenharmony_ci *noa_attr_len = __cpu_to_le16(attr_len); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic size_t ath10k_p2p_noa_ie_len_compute(const struct wmi_p2p_noa_info *noa) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci size_t len = 0; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!noa->num_descriptors && 5962306a36Sopenharmony_ci !(noa->ctwindow_oppps & WMI_P2P_OPPPS_ENABLE_BIT)) 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci len += 1 + 1 + 4; /* EID + len + OUI */ 6362306a36Sopenharmony_ci len += 1 + 2; /* noa attr + attr len */ 6462306a36Sopenharmony_ci len += 1 + 1; /* index + oppps_ctwindow */ 6562306a36Sopenharmony_ci len += noa->num_descriptors * sizeof(struct ieee80211_p2p_noa_desc); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return len; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic void ath10k_p2p_noa_ie_assign(struct ath10k_vif *arvif, void *ie, 7162306a36Sopenharmony_ci size_t len) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci lockdep_assert_held(&ar->data_lock); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci kfree(arvif->u.ap.noa_data); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci arvif->u.ap.noa_data = ie; 8062306a36Sopenharmony_ci arvif->u.ap.noa_len = len; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void __ath10k_p2p_noa_update(struct ath10k_vif *arvif, 8462306a36Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 8762306a36Sopenharmony_ci void *ie; 8862306a36Sopenharmony_ci size_t len; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci lockdep_assert_held(&ar->data_lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ath10k_p2p_noa_ie_assign(arvif, NULL, 0); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci len = ath10k_p2p_noa_ie_len_compute(noa); 9562306a36Sopenharmony_ci if (!len) 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci ie = kmalloc(len, GFP_ATOMIC); 9962306a36Sopenharmony_ci if (!ie) 10062306a36Sopenharmony_ci return; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci ath10k_p2p_noa_ie_fill(ie, len, noa); 10362306a36Sopenharmony_ci ath10k_p2p_noa_ie_assign(arvif, ie, len); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid ath10k_p2p_noa_update(struct ath10k_vif *arvif, 10762306a36Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct ath10k *ar = arvif->ar; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci spin_lock_bh(&ar->data_lock); 11262306a36Sopenharmony_ci __ath10k_p2p_noa_update(arvif, noa); 11362306a36Sopenharmony_ci spin_unlock_bh(&ar->data_lock); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct ath10k_p2p_noa_arg { 11762306a36Sopenharmony_ci u32 vdev_id; 11862306a36Sopenharmony_ci const struct wmi_p2p_noa_info *noa; 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void ath10k_p2p_noa_update_vdev_iter(void *data, u8 *mac, 12262306a36Sopenharmony_ci struct ieee80211_vif *vif) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct ath10k_vif *arvif = (void *)vif->drv_priv; 12562306a36Sopenharmony_ci struct ath10k_p2p_noa_arg *arg = data; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (arvif->vdev_id != arg->vdev_id) 12862306a36Sopenharmony_ci return; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci ath10k_p2p_noa_update(arvif, arg->noa); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_civoid ath10k_p2p_noa_update_by_vdev_id(struct ath10k *ar, u32 vdev_id, 13462306a36Sopenharmony_ci const struct wmi_p2p_noa_info *noa) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct ath10k_p2p_noa_arg arg = { 13762306a36Sopenharmony_ci .vdev_id = vdev_id, 13862306a36Sopenharmony_ci .noa = noa, 13962306a36Sopenharmony_ci }; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ieee80211_iterate_active_interfaces_atomic(ar->hw, 14262306a36Sopenharmony_ci ATH10K_ITER_NORMAL_FLAGS, 14362306a36Sopenharmony_ci ath10k_p2p_noa_update_vdev_iter, 14462306a36Sopenharmony_ci &arg); 14562306a36Sopenharmony_ci} 146