@@ -3269,6 +3269,11 @@ enum ieee80211_reconfig_type {
* the function call.
*
* @wake_tx_queue: Called when new packets have been added to the queue.
+ *
+ * @perform_ftm: Perform a Fine Timing Measurement with the given request
+ * parameters. The given request can only be used within the function call.
+ * @abort_ftm: Abort a Fine Timing Measurement request. The given cookie must
+ * match that of the active FTM request.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -3508,6 +3513,11 @@ struct ieee80211_ops {
void (*wake_tx_queue)(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
+
+ int (*perform_ftm)(struct ieee80211_hw *hw, u64 cookie,
+ struct ieee80211_vif *vif,
+ struct cfg80211_ftm_request *ftm_req);
+ int (*abort_ftm)(struct ieee80211_hw *hw, u64 cookie);
};
/**
@@ -3322,6 +3322,60 @@ static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
return -ENOENT;
}
+static u64 ieee80211_msrment_cookie(struct ieee80211_local *local,
+ enum nl80211_msrment_type type)
+{
+ ASSERT_RTNL();
+
+ local->msrment_cookie_counter++;
+ if (local->msrment_cookie_counter == (1ULL << 48))
+ local->msrment_cookie_counter = 1;
+
+ return ((u64)type << 48) | local->msrment_cookie_counter;
+}
+
+static int ieee80211_perform_msrment(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_msrment_request *request,
+ u64 *cookie)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_vif *vif = wdev_to_ieee80211_vif(wdev);
+
+ *cookie = ieee80211_msrment_cookie(local, request->type);
+
+ switch (request->type) {
+ case NL80211_MSRMENT_TYPE_FTM:
+ if (!local->ops->perform_ftm)
+ return -EOPNOTSUPP;
+ return local->ops->perform_ftm(&local->hw, *cookie, vif,
+ &request->u.ftm);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int ieee80211_abort_msrment(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ enum nl80211_msrment_type type = cookie >> 48;
+
+ switch (type) {
+ case NL80211_MSRMENT_TYPE_FTM:
+ if (!local->ops->abort_ftm)
+ return -EOPNOTSUPP;
+ return local->ops->abort_ftm(&local->hw, cookie);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -3342,6 +3396,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.get_station = ieee80211_get_station,
.dump_station = ieee80211_dump_station,
.dump_survey = ieee80211_dump_survey,
+ .perform_msrment = ieee80211_perform_msrment,
+ .abort_msrment = ieee80211_abort_msrment,
#ifdef CONFIG_MAC80211_MESH
.add_mpath = ieee80211_add_mpath,
.del_mpath = ieee80211_del_mpath,
@@ -1347,11 +1347,13 @@ struct ieee80211_local {
struct cfg80211_chan_def monitor_chandef;
/* extended capabilities provided by mac80211 */
- u8 ext_capa[8];
+ u8 ext_capa[9];
/* TDLS channel switch */
struct work_struct tdls_chsw_work;
struct sk_buff_head skb_queue_tdls_chsw;
+
+ u64 msrment_cookie_counter;
};
static inline struct ieee80211_sub_if_data *
@@ -1006,6 +1006,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA))
local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+ if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_FTM_INITIATOR)
+ local->ext_capa[8] |= WLAN_EXT_CAPA9_FTM_INITIATOR;
+
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
result = wiphy_register(local->hw.wiphy);