xref: /linux/drivers/net/wireless/ath/ath11k/p2p.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
1 // SPDX-License-Identifier: BSD-3-Clause-Clear
2 /*
3  * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include "core.h"
7 #include "wmi.h"
8 #include "mac.h"
9 #include "p2p.h"
10 
ath11k_p2p_noa_ie_fill(u8 * data,size_t len,const struct ath11k_wmi_p2p_noa_info * noa)11 static void ath11k_p2p_noa_ie_fill(u8 *data, size_t len,
12 				   const struct ath11k_wmi_p2p_noa_info *noa)
13 {
14 	struct ieee80211_p2p_noa_attr *noa_attr;
15 	u8 noa_descriptors, ctwindow;
16 	bool oppps;
17 	__le16 *noa_attr_len;
18 	u16 attr_len;
19 	int i;
20 
21 	ctwindow = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_CTWIN_TU);
22 	oppps = u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS);
23 	noa_descriptors = u32_get_bits(noa->noa_attr,
24 				       WMI_P2P_NOA_INFO_DESC_NUM);
25 
26 	/* P2P IE */
27 	data[0] = WLAN_EID_VENDOR_SPECIFIC;
28 	data[1] = len - 2;
29 	data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
30 	data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
31 	data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
32 	data[5] = WLAN_OUI_TYPE_WFA_P2P;
33 
34 	/* NOA ATTR */
35 	data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
36 	noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
37 	noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
38 
39 	noa_attr->index = u32_get_bits(noa->noa_attr,
40 				       WMI_P2P_NOA_INFO_INDEX);
41 	noa_attr->oppps_ctwindow = ctwindow;
42 	if (oppps)
43 		noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
44 
45 	for (i = 0; i < noa_descriptors; i++) {
46 		noa_attr->desc[i].count = noa->descriptors[i].type_count;
47 		noa_attr->desc[i].duration =
48 			cpu_to_le32(noa->descriptors[i].duration);
49 		noa_attr->desc[i].interval =
50 			cpu_to_le32(noa->descriptors[i].interval);
51 		noa_attr->desc[i].start_time =
52 			cpu_to_le32(noa->descriptors[i].start_time);
53 	}
54 
55 	attr_len = 2; /* index + oppps_ctwindow */
56 	attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
57 	*noa_attr_len = __cpu_to_le16(attr_len);
58 }
59 
60 static size_t
ath11k_p2p_noa_ie_len_compute(const struct ath11k_wmi_p2p_noa_info * noa)61 ath11k_p2p_noa_ie_len_compute(const struct ath11k_wmi_p2p_noa_info *noa)
62 {
63 	size_t len = 0;
64 	u8 noa_descriptors = u32_get_bits(noa->noa_attr,
65 					  WMI_P2P_NOA_INFO_DESC_NUM);
66 
67 	if (!(noa_descriptors) &&
68 	    !(u32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS)))
69 		return 0;
70 
71 	len += 1 + 1 + 4; /* EID + len + OUI */
72 	len += 1 + 2; /* noa attr + attr len */
73 	len += 1 + 1; /* index + oppps_ctwindow */
74 	len += noa_descriptors *
75 		sizeof(struct ieee80211_p2p_noa_desc);
76 
77 	return len;
78 }
79 
ath11k_p2p_noa_ie_assign(struct ath11k_vif * arvif,void * ie,size_t len)80 static void ath11k_p2p_noa_ie_assign(struct ath11k_vif *arvif, void *ie,
81 				     size_t len)
82 {
83 	struct ath11k *ar = arvif->ar;
84 
85 	lockdep_assert_held(&ar->data_lock);
86 
87 	kfree(arvif->u.ap.noa_data);
88 
89 	arvif->u.ap.noa_data = ie;
90 	arvif->u.ap.noa_len = len;
91 }
92 
__ath11k_p2p_noa_update(struct ath11k_vif * arvif,const struct ath11k_wmi_p2p_noa_info * noa)93 static void __ath11k_p2p_noa_update(struct ath11k_vif *arvif,
94 				    const struct ath11k_wmi_p2p_noa_info *noa)
95 {
96 	struct ath11k *ar = arvif->ar;
97 	void *ie;
98 	size_t len;
99 
100 	lockdep_assert_held(&ar->data_lock);
101 
102 	ath11k_p2p_noa_ie_assign(arvif, NULL, 0);
103 
104 	len = ath11k_p2p_noa_ie_len_compute(noa);
105 	if (!len)
106 		return;
107 
108 	ie = kmalloc(len, GFP_ATOMIC);
109 	if (!ie)
110 		return;
111 
112 	ath11k_p2p_noa_ie_fill(ie, len, noa);
113 	ath11k_p2p_noa_ie_assign(arvif, ie, len); }
114 
ath11k_p2p_noa_update(struct ath11k_vif * arvif,const struct ath11k_wmi_p2p_noa_info * noa)115 void ath11k_p2p_noa_update(struct ath11k_vif *arvif,
116 			   const struct ath11k_wmi_p2p_noa_info *noa)
117 {
118 	struct ath11k *ar = arvif->ar;
119 
120 	spin_lock_bh(&ar->data_lock);
121 	__ath11k_p2p_noa_update(arvif, noa);
122 	spin_unlock_bh(&ar->data_lock);
123 }
124 
ath11k_p2p_noa_update_vdev_iter(void * data,u8 * mac,struct ieee80211_vif * vif)125 static void ath11k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
126 					    struct ieee80211_vif *vif)
127 {
128 	struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
129 	struct ath11k_p2p_noa_arg *arg = data;
130 
131 	if (arvif->vdev_id != arg->vdev_id)
132 		return;
133 
134 	ath11k_p2p_noa_update(arvif, arg->noa);
135 }
136 
ath11k_p2p_noa_update_by_vdev_id(struct ath11k * ar,u32 vdev_id,const struct ath11k_wmi_p2p_noa_info * noa)137 void ath11k_p2p_noa_update_by_vdev_id(struct ath11k *ar, u32 vdev_id,
138 				      const struct ath11k_wmi_p2p_noa_info *noa)
139 {
140 	struct ath11k_p2p_noa_arg arg = {
141 		.vdev_id = vdev_id,
142 		.noa = noa,
143 	};
144 
145 	ieee80211_iterate_active_interfaces_atomic(ar->hw,
146 						   IEEE80211_IFACE_ITER_NORMAL,
147 						   ath11k_p2p_noa_update_vdev_iter,
148 						   &arg);
149 }
150