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