Commit 9411eecb authored by Kang Yang's avatar Kang Yang Committed by Kalle Valo

wifi: ath12k: implement handling of P2P NoA event

The Notice of Absence (NoA) attribute is used by the P2P Group Owner to
signal its absence due to power save timing, concurrent operation, or
off-channel scanning. It is also used in the P2P Presence Request-Response
mechanism.

The NoA attribute shall be present in the P2P IE in the beacon frames
transmitted by a P2P Group Owner when a NoA schedule is being advertised,
or when the CTWindow is non-zero.

So add support to update P2P information after P2P GO is up through
event WMI_P2P_NOA_EVENTID, and always put it in probe resp.

Create p2p.c and p2p.h for P2P related functions and definitions.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.0.1-00029-QCAHKSWPL_SILICONZ-1
Signed-off-by: default avatarKang Yang <quic_kangyang@quicinc.com>
Acked-by: default avatarJeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://msgid.link/20240130040303.370590-6-quic_kangyang@quicinc.com
parent 575ec73c
......@@ -20,7 +20,8 @@ ath12k-y += core.o \
mhi.o \
pci.o \
dp_mon.o \
fw.o
fw.o \
p2p.o
ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
......
......@@ -5173,6 +5173,27 @@ static int ath12k_mac_mgmt_tx(struct ath12k *ar, struct sk_buff *skb,
return 0;
}
static void ath12k_mac_add_p2p_noa_ie(struct ath12k *ar,
struct ieee80211_vif *vif,
struct sk_buff *skb,
bool is_prb_rsp)
{
struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
if (likely(!is_prb_rsp))
return;
spin_lock_bh(&ar->data_lock);
if (arvif->u.ap.noa_data &&
!pskb_expand_head(skb, 0, arvif->u.ap.noa_len,
GFP_ATOMIC))
skb_put_data(skb, arvif->u.ap.noa_data,
arvif->u.ap.noa_len);
spin_unlock_bh(&ar->data_lock);
}
static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
......@@ -5196,10 +5217,11 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
skb_cb->flags |= ATH12K_SKB_CIPHER_SET;
}
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
skb_cb->flags |= ATH12K_SKB_HW_80211_ENCAP;
} else if (ieee80211_is_mgmt(hdr->frame_control)) {
is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control);
ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp);
if (ret) {
ath12k_warn(ar->ab, "failed to queue management frame %d\n",
......@@ -5209,6 +5231,10 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw,
return;
}
/* This is case only for P2P_GO */
if (vif->type == NL80211_IFTYPE_AP && vif->p2p)
ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp);
ret = ath12k_dp_tx(ar, arvif, skb);
if (ret) {
ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret);
......
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <net/mac80211.h>
#include "core.h"
#include "mac.h"
#include "p2p.h"
static void ath12k_p2p_noa_ie_fill(u8 *data, size_t len,
const struct ath12k_wmi_p2p_noa_info *noa)
{
struct ieee80211_p2p_noa_attr *noa_attr;
u8 ctwindow = le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_CTWIN_TU);
bool oppps = le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS);
__le16 *noa_attr_len;
u16 attr_len;
u8 noa_descriptors = le32_get_bits(noa->noa_attr,
WMI_P2P_NOA_INFO_DESC_NUM);
int i;
/* P2P IE */
data[0] = WLAN_EID_VENDOR_SPECIFIC;
data[1] = len - 2;
data[2] = (WLAN_OUI_WFA >> 16) & 0xff;
data[3] = (WLAN_OUI_WFA >> 8) & 0xff;
data[4] = (WLAN_OUI_WFA >> 0) & 0xff;
data[5] = WLAN_OUI_TYPE_WFA_P2P;
/* NOA ATTR */
data[6] = IEEE80211_P2P_ATTR_ABSENCE_NOTICE;
noa_attr_len = (__le16 *)&data[7]; /* 2 bytes */
noa_attr = (struct ieee80211_p2p_noa_attr *)&data[9];
noa_attr->index = le32_get_bits(noa->noa_attr,
WMI_P2P_NOA_INFO_INDEX);
noa_attr->oppps_ctwindow = ctwindow;
if (oppps)
noa_attr->oppps_ctwindow |= IEEE80211_P2P_OPPPS_ENABLE_BIT;
for (i = 0; i < noa_descriptors; i++) {
noa_attr->desc[i].count =
__le32_to_cpu(noa->descriptors[i].type_count);
noa_attr->desc[i].duration = noa->descriptors[i].duration;
noa_attr->desc[i].interval = noa->descriptors[i].interval;
noa_attr->desc[i].start_time = noa->descriptors[i].start_time;
}
attr_len = 2; /* index + oppps_ctwindow */
attr_len += noa_descriptors * sizeof(struct ieee80211_p2p_noa_desc);
*noa_attr_len = __cpu_to_le16(attr_len);
}
static size_t ath12k_p2p_noa_ie_len_compute(const struct ath12k_wmi_p2p_noa_info *noa)
{
size_t len = 0;
if (!(le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_DESC_NUM)) &&
!(le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_OPP_PS)))
return 0;
len += 1 + 1 + 4; /* EID + len + OUI */
len += 1 + 2; /* noa attr + attr len */
len += 1 + 1; /* index + oppps_ctwindow */
len += le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_DESC_NUM) *
sizeof(struct ieee80211_p2p_noa_desc);
return len;
}
static void ath12k_p2p_noa_ie_assign(struct ath12k_vif *arvif, void *ie,
size_t len)
{
struct ath12k *ar = arvif->ar;
lockdep_assert_held(&ar->data_lock);
kfree(arvif->u.ap.noa_data);
arvif->u.ap.noa_data = ie;
arvif->u.ap.noa_len = len;
}
static void __ath12k_p2p_noa_update(struct ath12k_vif *arvif,
const struct ath12k_wmi_p2p_noa_info *noa)
{
struct ath12k *ar = arvif->ar;
void *ie;
size_t len;
lockdep_assert_held(&ar->data_lock);
ath12k_p2p_noa_ie_assign(arvif, NULL, 0);
len = ath12k_p2p_noa_ie_len_compute(noa);
if (!len)
return;
ie = kmalloc(len, GFP_ATOMIC);
if (!ie)
return;
ath12k_p2p_noa_ie_fill(ie, len, noa);
ath12k_p2p_noa_ie_assign(arvif, ie, len);
}
void ath12k_p2p_noa_update(struct ath12k_vif *arvif,
const struct ath12k_wmi_p2p_noa_info *noa)
{
struct ath12k *ar = arvif->ar;
spin_lock_bh(&ar->data_lock);
__ath12k_p2p_noa_update(arvif, noa);
spin_unlock_bh(&ar->data_lock);
}
static void ath12k_p2p_noa_update_vdev_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct ath12k_vif *arvif = ath12k_vif_to_arvif(vif);
struct ath12k_p2p_noa_arg *arg = data;
if (arvif->vdev_id != arg->vdev_id)
return;
ath12k_p2p_noa_update(arvif, arg->noa);
}
void ath12k_p2p_noa_update_by_vdev_id(struct ath12k *ar, u32 vdev_id,
const struct ath12k_wmi_p2p_noa_info *noa)
{
struct ath12k_p2p_noa_arg arg = {
.vdev_id = vdev_id,
.noa = noa,
};
ieee80211_iterate_active_interfaces_atomic(ath12k_ar_to_hw(ar),
IEEE80211_IFACE_ITER_NORMAL,
ath12k_p2p_noa_update_vdev_iter,
&arg);
}
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved..
*/
#ifndef ATH12K_P2P_H
#define ATH12K_P2P_H
#include "wmi.h"
struct ath12k_wmi_p2p_noa_info;
struct ath12k_p2p_noa_arg {
u32 vdev_id;
const struct ath12k_wmi_p2p_noa_info *noa;
};
void ath12k_p2p_noa_update(struct ath12k_vif *arvif,
const struct ath12k_wmi_p2p_noa_info *noa);
void ath12k_p2p_noa_update_by_vdev_id(struct ath12k *ar, u32 vdev_id,
const struct ath12k_wmi_p2p_noa_info *noa);
#endif
......@@ -19,6 +19,7 @@
#include "mac.h"
#include "hw.h"
#include "peer.h"
#include "p2p.h"
struct ath12k_wmi_svc_ready_parse {
bool wmi_svc_bitmap_done;
......@@ -166,6 +167,10 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_twt_enable_event) },
[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT] = {
.min_len = sizeof(struct wmi_twt_disable_event) },
[WMI_TAG_P2P_NOA_INFO] = {
.min_len = sizeof(struct ath12k_wmi_p2p_noa_info) },
[WMI_TAG_P2P_NOA_EVENT] = {
.min_len = sizeof(struct wmi_p2p_noa_event) },
};
static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
......@@ -1053,7 +1058,7 @@ int ath12k_wmi_vdev_start(struct ath12k *ar, struct wmi_vdev_start_req_arg *arg,
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, 0);
/* Note: This is a nested TLV containing:
* [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv]..
* [wmi_tlv][ath12k_wmi_p2p_noa_descriptor][wmi_tlv]..
*/
ptr += sizeof(*tlv);
......@@ -6678,6 +6683,53 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
kfree(tb);
}
static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
const void **tb;
const struct wmi_p2p_noa_event *ev;
const struct ath12k_wmi_p2p_noa_info *noa;
struct ath12k *ar;
int ret, vdev_id;
tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret);
return ret;
}
ev = tb[WMI_TAG_P2P_NOA_EVENT];
noa = tb[WMI_TAG_P2P_NOA_INFO];
if (!ev || !noa) {
ret = -EPROTO;
goto out;
}
vdev_id = __le32_to_cpu(ev->vdev_id);
ath12k_dbg(ab, ATH12K_DBG_WMI,
"wmi tlv p2p noa vdev_id %i descriptors %u\n",
vdev_id, le32_get_bits(noa->noa_attr, WMI_P2P_NOA_INFO_DESC_NUM));
ar = ath12k_mac_get_ar_by_vdev_id(ab, vdev_id);
if (!ar) {
ath12k_warn(ab, "invalid vdev id %d in P2P NoA event\n",
vdev_id);
ret = -EINVAL;
goto out;
}
ath12k_p2p_noa_update_by_vdev_id(ar, vdev_id, noa);
ret = 0;
out:
kfree(tb);
return ret;
}
static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
......@@ -6877,6 +6929,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_TWT_DISABLE_EVENTID:
ath12k_wmi_twt_disable_event(ab, skb);
break;
case WMI_P2P_NOA_EVENTID:
ath12k_wmi_p2p_noa_event(ab, skb);
break;
/* add Unsupported events here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
......
......@@ -3504,6 +3504,37 @@ struct wmi_get_pdev_temperature_cmd {
__le32 pdev_id;
} __packed;
#define WMI_P2P_MAX_NOA_DESCRIPTORS 4
struct wmi_p2p_noa_event {
__le32 vdev_id;
} __packed;
struct ath12k_wmi_p2p_noa_descriptor {
__le32 type_count; /* 255: continuous schedule, 0: reserved */
__le32 duration; /* Absent period duration in micro seconds */
__le32 interval; /* Absent period interval in micro seconds */
__le32 start_time; /* 32 bit tsf time when in starts */
} __packed;
#define WMI_P2P_NOA_INFO_CHANGED_FLAG BIT(0)
#define WMI_P2P_NOA_INFO_INDEX GENMASK(15, 8)
#define WMI_P2P_NOA_INFO_OPP_PS BIT(16)
#define WMI_P2P_NOA_INFO_CTWIN_TU GENMASK(23, 17)
#define WMI_P2P_NOA_INFO_DESC_NUM GENMASK(31, 24)
struct ath12k_wmi_p2p_noa_info {
/* Bit 0 - Flag to indicate an update in NOA schedule
* Bits 7-1 - Reserved
* Bits 15-8 - Index (identifies the instance of NOA sub element)
* Bit 16 - Opp PS state of the AP
* Bits 23-17 - Ctwindow in TUs
* Bits 31-24 - Number of NOA descriptors
*/
__le32 noa_attr;
struct ath12k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS];
} __packed;
#define WMI_BEACON_TX_BUFFER_SIZE 512
struct wmi_bcn_tmpl_cmd {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment