Commit 167e33f4 authored by Ayala Beker's avatar Ayala Beker Committed by Johannes Berg

mac80211: Implement add_nan_func and rm_nan_func

Implement add/rm_nan_func functions and handle NAN function
termination notifications. Handle instance_id allocation for
NAN functions and implement the reconfig flow.
Signed-off-by: default avatarAndrei Otcheretianski <andrei.otcheretianski@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5953ff6d
......@@ -2177,6 +2177,8 @@ enum ieee80211_hw_flags {
* @n_cipher_schemes: a size of an array of cipher schemes definitions.
* @cipher_schemes: a pointer to an array of cipher scheme definitions
* supported by HW.
* @max_nan_de_entries: maximum number of NAN DE functions supported by the
* device.
*/
struct ieee80211_hw {
struct ieee80211_conf conf;
......@@ -2211,6 +2213,7 @@ struct ieee80211_hw {
u8 uapsd_max_sp_len;
u8 n_cipher_schemes;
const struct ieee80211_cipher_scheme *cipher_schemes;
u8 max_nan_de_entries;
};
static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
......@@ -3429,6 +3432,12 @@ enum ieee80211_reconfig_type {
* The driver gets both full configuration and the changed parameters since
* some devices may need the full configuration while others need only the
* changed parameters.
* @add_nan_func: Add a NAN function. Returns 0 on success. The data in
* cfg80211_nan_func must not be referenced outside the scope of
* this call.
* @del_nan_func: Remove a NAN function. The driver must call
* ieee80211_nan_func_terminated() with
* NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST reason code upon removal.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
......@@ -3673,6 +3682,12 @@ struct ieee80211_ops {
int (*nan_change_conf)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf, u32 changes);
int (*add_nan_func)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const struct cfg80211_nan_func *nan_func);
void (*del_nan_func)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u8 instance_id);
};
/**
......@@ -5746,4 +5761,20 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
unsigned long *frame_cnt,
unsigned long *byte_cnt);
/**
* ieee80211_nan_func_terminated - notify about NAN function termination.
*
* This function is used to notify mac80211 about NAN function termination.
* Note that this function can't be called from hard irq.
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
* @inst_id: the local instance id
* @reason: termination reason (one of the NL80211_NAN_FUNC_TERM_REASON_*)
* @gfp: allocation flags
*/
void ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
u8 inst_id,
enum nl80211_nan_func_term_reason reason,
gfp_t gfp);
#endif /* MAC80211_H */
......@@ -174,6 +174,8 @@ static int ieee80211_start_nan(struct wiphy *wiphy,
if (ret)
ieee80211_sdata_stop(sdata);
sdata->u.nan.conf = *conf;
return ret;
}
......@@ -216,6 +218,84 @@ static int ieee80211_nan_change_conf(struct wiphy *wiphy,
return ret;
}
static int ieee80211_add_nan_func(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_nan_func *nan_func)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
int ret;
if (sdata->vif.type != NL80211_IFTYPE_NAN)
return -EOPNOTSUPP;
if (!ieee80211_sdata_running(sdata))
return -ENETDOWN;
spin_lock_bh(&sdata->u.nan.func_lock);
ret = idr_alloc(&sdata->u.nan.function_inst_ids,
nan_func, 1, sdata->local->hw.max_nan_de_entries + 1,
GFP_ATOMIC);
spin_unlock_bh(&sdata->u.nan.func_lock);
if (ret < 0)
return ret;
nan_func->instance_id = ret;
WARN_ON(nan_func->instance_id == 0);
ret = drv_add_nan_func(sdata->local, sdata, nan_func);
if (ret) {
spin_lock_bh(&sdata->u.nan.func_lock);
idr_remove(&sdata->u.nan.function_inst_ids,
nan_func->instance_id);
spin_unlock_bh(&sdata->u.nan.func_lock);
}
return ret;
}
static struct cfg80211_nan_func *
ieee80211_find_nan_func_by_cookie(struct ieee80211_sub_if_data *sdata,
u64 cookie)
{
struct cfg80211_nan_func *func;
int id;
lockdep_assert_held(&sdata->u.nan.func_lock);
idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id) {
if (func->cookie == cookie)
return func;
}
return NULL;
}
static void ieee80211_del_nan_func(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct cfg80211_nan_func *func;
u8 instance_id = 0;
if (sdata->vif.type != NL80211_IFTYPE_NAN ||
!ieee80211_sdata_running(sdata))
return;
spin_lock_bh(&sdata->u.nan.func_lock);
func = ieee80211_find_nan_func_by_cookie(sdata, cookie);
if (func)
instance_id = func->instance_id;
spin_unlock_bh(&sdata->u.nan.func_lock);
if (instance_id)
drv_del_nan_func(sdata->local, sdata, instance_id);
}
static int ieee80211_set_noack_map(struct wiphy *wiphy,
struct net_device *dev,
u16 noack_map)
......@@ -3443,6 +3523,38 @@ static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
return -ENOENT;
}
void ieee80211_nan_func_terminated(struct ieee80211_vif *vif,
u8 inst_id,
enum nl80211_nan_func_term_reason reason,
gfp_t gfp)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct cfg80211_nan_func *func;
u64 cookie;
if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
return;
spin_lock_bh(&sdata->u.nan.func_lock);
func = idr_find(&sdata->u.nan.function_inst_ids, inst_id);
if (WARN_ON(!func)) {
spin_unlock_bh(&sdata->u.nan.func_lock);
return;
}
cookie = func->cookie;
idr_remove(&sdata->u.nan.function_inst_ids, inst_id);
spin_unlock_bh(&sdata->u.nan.func_lock);
cfg80211_free_nan_func(func);
cfg80211_nan_func_terminated(ieee80211_vif_to_wdev(vif), inst_id,
reason, cookie, gfp);
}
EXPORT_SYMBOL(ieee80211_nan_func_terminated);
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
......@@ -3531,4 +3643,6 @@ const struct cfg80211_ops mac80211_config_ops = {
.start_nan = ieee80211_start_nan,
.stop_nan = ieee80211_stop_nan,
.nan_change_conf = ieee80211_nan_change_conf,
.add_nan_func = ieee80211_add_nan_func,
.del_nan_func = ieee80211_del_nan_func,
};
......@@ -1213,4 +1213,36 @@ static inline int drv_nan_change_conf(struct ieee80211_local *local,
return ret;
}
static inline int drv_add_nan_func(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const struct cfg80211_nan_func *nan_func)
{
int ret;
might_sleep();
check_sdata_in_driver(sdata);
if (!local->ops->add_nan_func)
return -EOPNOTSUPP;
trace_drv_add_nan_func(local, sdata, nan_func);
ret = local->ops->add_nan_func(&local->hw, &sdata->vif, nan_func);
trace_drv_return_int(local, ret);
return ret;
}
static inline void drv_del_nan_func(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u8 instance_id)
{
might_sleep();
check_sdata_in_driver(sdata);
trace_drv_del_nan_func(local, sdata, instance_id);
if (local->ops->del_nan_func)
local->ops->del_nan_func(&local->hw, &sdata->vif, instance_id);
trace_drv_return_void(local);
}
#endif /* __MAC80211_DRIVER_OPS */
......@@ -86,6 +86,8 @@ struct ieee80211_local;
#define IEEE80211_DEAUTH_FRAME_LEN (24 /* hdr */ + 2 /* reason */)
#define IEEE80211_MAX_NAN_INSTANCE_ID 255
struct ieee80211_fragment_entry {
struct sk_buff_head skb_list;
unsigned long first_frag_time;
......@@ -834,9 +836,14 @@ struct ieee80211_if_mntr {
* struct ieee80211_if_nan - NAN state
*
* @conf: current NAN configuration
* @func_ids: a bitmap of available instance_id's
*/
struct ieee80211_if_nan {
struct cfg80211_nan_conf conf;
/* protects function_inst_ids */
spinlock_t func_lock;
struct idr function_inst_ids;
};
struct ieee80211_sub_if_data {
......
......@@ -798,6 +798,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct ps_data *ps;
struct cfg80211_chan_def chandef;
bool cancel_scan;
struct cfg80211_nan_func *func;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
......@@ -950,11 +951,22 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_adjust_monitor_flags(sdata, -1);
break;
case NL80211_IFTYPE_NAN:
/* clean all the functions */
spin_lock_bh(&sdata->u.nan.func_lock);
idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) {
idr_remove(&sdata->u.nan.function_inst_ids, i);
cfg80211_free_nan_func(func);
}
idr_destroy(&sdata->u.nan.function_inst_ids);
spin_unlock_bh(&sdata->u.nan.func_lock);
break;
case NL80211_IFTYPE_P2P_DEVICE:
/* relies on synchronize_rcu() below */
RCU_INIT_POINTER(local->p2p_sdata, NULL);
/* fall through */
case NL80211_IFTYPE_NAN:
default:
cancel_work_sync(&sdata->work);
/*
......@@ -1462,9 +1474,13 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_WDS:
sdata->vif.bss_conf.bssid = NULL;
break;
case NL80211_IFTYPE_NAN:
idr_init(&sdata->u.nan.function_inst_ids);
spin_lock_init(&sdata->u.nan.func_lock);
sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
sdata->vif.bss_conf.bssid = sdata->vif.addr;
break;
case NL80211_IFTYPE_UNSPECIFIED:
......
......@@ -1063,6 +1063,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->dynamic_ps_forced_timeout = -1;
if (!local->hw.max_nan_de_entries)
local->hw.max_nan_de_entries = IEEE80211_MAX_NAN_INSTANCE_ID;
result = ieee80211_wep_init(local);
if (result < 0)
wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
......
......@@ -1781,6 +1781,58 @@ TRACE_EVENT(drv_nan_change_conf,
)
);
TRACE_EVENT(drv_add_nan_func,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const struct cfg80211_nan_func *func),
TP_ARGS(local, sdata, func),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u8, type)
__field(u8, inst_id)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->type = func->type;
__entry->inst_id = func->instance_id;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT
", type: %u, inst_id: %u",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->type, __entry->inst_id
)
);
TRACE_EVENT(drv_del_nan_func,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
u8 instance_id),
TP_ARGS(local, sdata, instance_id),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u8, instance_id)
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->instance_id = instance_id;
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT
", instance_id: %u",
LOCAL_PR_ARG, VIF_PR_ARG, __entry->instance_id
)
);
/*
* Tracing for API calls that drivers call.
*/
......
......@@ -1749,6 +1749,46 @@ static void ieee80211_reconfig_stations(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->sta_mtx);
}
static int ieee80211_reconfig_nan(struct ieee80211_sub_if_data *sdata)
{
struct cfg80211_nan_func *func, **funcs;
int res, id, i = 0;
res = drv_start_nan(sdata->local, sdata,
&sdata->u.nan.conf);
if (WARN_ON(res))
return res;
funcs = kzalloc((sdata->local->hw.max_nan_de_entries + 1) *
sizeof(*funcs), GFP_KERNEL);
if (!funcs)
return -ENOMEM;
/* Add all the functions:
* This is a little bit ugly. We need to call a potentially sleeping
* callback for each NAN function, so we can't hold the spinlock.
*/
spin_lock_bh(&sdata->u.nan.func_lock);
idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, id)
funcs[i++] = func;
spin_unlock_bh(&sdata->u.nan.func_lock);
for (i = 0; funcs[i]; i++) {
res = drv_add_nan_func(sdata->local, sdata, funcs[i]);
if (WARN_ON(res))
ieee80211_nan_func_terminated(&sdata->vif,
funcs[i]->instance_id,
NL80211_NAN_FUNC_TERM_REASON_ERROR,
GFP_KERNEL);
}
kfree(funcs);
return 0;
}
int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
......@@ -1972,11 +2012,17 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_bss_info_change_notify(sdata, changed);
}
break;
case NL80211_IFTYPE_NAN:
res = ieee80211_reconfig_nan(sdata);
if (res < 0) {
ieee80211_handle_reconfig_failure(local);
return res;
}
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
/* nothing to do */
break;
case NL80211_IFTYPE_UNSPECIFIED:
......
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