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