]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge remote-tracking branch 'net-next/master' into mac80211-next
authorJohannes Berg <johannes.berg@intel.com>
Tue, 4 Oct 2016 07:22:19 +0000 (09:22 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 4 Oct 2016 07:46:44 +0000 (09:46 +0200)
Resolve the merge conflict between Felix's/my and Toke's patches
coming into the tree through net and mac80211-next respectively.
Most of Felix's changes go away due to Toke's new infrastructure
work, my patch changes to "goto begin" (the label wasn't there
before) instead of returning NULL so flow control towards drivers
is preserved better.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
33 files changed:
include/net/cfg80211.h
include/net/fq.h
include/net/fq_impl.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/debugfs_netdev.c
net/mac80211/driver-ops.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mesh_sync.c
net/mac80211/offchannel.c
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-sme.c

index bd26cc6e2d79e6e9a306fe3522c8e02d75e8aabf..fe78f02a242e723e29dfdfb4baf39bbadc68962d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014 Intel Mobile Communications GmbH
- * Copyright 2015      Intel Deutschland GmbH
+ * Copyright 2015-2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -593,6 +593,8 @@ struct survey_info {
        s8 noise;
 };
 
+#define CFG80211_MAX_WEP_KEYS  4
+
 /**
  * struct cfg80211_crypto_settings - Crypto settings
  * @wpa_versions: indicates which, if any, WPA versions are enabled
@@ -610,6 +612,9 @@ struct survey_info {
  *     allowed through even on unauthorized ports
  * @control_port_no_encrypt: TRUE to prevent encryption of control port
  *     protocol frames.
+ * @wep_keys: static WEP keys, if not NULL points to an array of
+ *     CFG80211_MAX_WEP_KEYS WEP keys
+ * @wep_tx_key: key index (0..3) of the default TX static WEP key
  */
 struct cfg80211_crypto_settings {
        u32 wpa_versions;
@@ -621,6 +626,8 @@ struct cfg80211_crypto_settings {
        bool control_port;
        __be16 control_port_ethertype;
        bool control_port_no_encrypt;
+       struct key_params *wep_keys;
+       int wep_tx_key;
 };
 
 /**
@@ -676,6 +683,18 @@ struct cfg80211_acl_data {
        struct mac_address mac_addrs[];
 };
 
+/*
+ * cfg80211_bitrate_mask - masks for bitrate control
+ */
+struct cfg80211_bitrate_mask {
+       struct {
+               u32 legacy;
+               u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
+               u16 vht_mcs[NL80211_VHT_NSS_MAX];
+               enum nl80211_txrate_gi gi;
+       } control[NUM_NL80211_BANDS];
+};
+
 /**
  * struct cfg80211_ap_settings - AP configuration
  *
@@ -700,6 +719,7 @@ struct cfg80211_acl_data {
  *     MAC address based access control
  * @pbss: If set, start as a PCP instead of AP. Relevant for DMG
  *     networks.
+ * @beacon_rate: bitrate to be used for beacons
  */
 struct cfg80211_ap_settings {
        struct cfg80211_chan_def chandef;
@@ -719,6 +739,7 @@ struct cfg80211_ap_settings {
        bool p2p_opp_ps;
        const struct cfg80211_acl_data *acl;
        bool pbss;
+       struct cfg80211_bitrate_mask beacon_rate;
 };
 
 /**
@@ -1351,6 +1372,7 @@ struct mesh_config {
  * @beacon_interval: beacon interval to use
  * @mcast_rate: multicat rate for Mesh Node [6Mbps is the default for 802.11a]
  * @basic_rates: basic rates to use when creating the mesh
+ * @beacon_rate: bitrate to be used for beacons
  *
  * These parameters are fixed when the mesh is created.
  */
@@ -1371,6 +1393,7 @@ struct mesh_setup {
        u16 beacon_interval;
        int mcast_rate[NUM_NL80211_BANDS];
        u32 basic_rates;
+       struct cfg80211_bitrate_mask beacon_rate;
 };
 
 /**
@@ -2010,17 +2033,6 @@ enum wiphy_params_flags {
        WIPHY_PARAM_DYN_ACK             = 1 << 5,
 };
 
-/*
- * cfg80211_bitrate_mask - masks for bitrate control
- */
-struct cfg80211_bitrate_mask {
-       struct {
-               u32 legacy;
-               u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN];
-               u16 vht_mcs[NL80211_VHT_NSS_MAX];
-               enum nl80211_txrate_gi gi;
-       } control[NUM_NL80211_BANDS];
-};
 /**
  * struct cfg80211_pmksa - PMK Security Association
  *
@@ -2301,6 +2313,98 @@ struct cfg80211_qos_map {
        struct cfg80211_dscp_range up[8];
 };
 
+/**
+ * struct cfg80211_nan_conf - NAN configuration
+ *
+ * This struct defines NAN configuration parameters
+ *
+ * @master_pref: master preference (1 - 255)
+ * @dual: dual band operation mode, see &enum nl80211_nan_dual_band_conf
+ */
+struct cfg80211_nan_conf {
+       u8 master_pref;
+       u8 dual;
+};
+
+/**
+ * enum cfg80211_nan_conf_changes - indicates changed fields in NAN
+ * configuration
+ *
+ * @CFG80211_NAN_CONF_CHANGED_PREF: master preference
+ * @CFG80211_NAN_CONF_CHANGED_DUAL: dual band operation
+ */
+enum cfg80211_nan_conf_changes {
+       CFG80211_NAN_CONF_CHANGED_PREF = BIT(0),
+       CFG80211_NAN_CONF_CHANGED_DUAL = BIT(1),
+};
+
+/**
+ * struct cfg80211_nan_func_filter - a NAN function Rx / Tx filter
+ *
+ * @filter: the content of the filter
+ * @len: the length of the filter
+ */
+struct cfg80211_nan_func_filter {
+       const u8 *filter;
+       u8 len;
+};
+
+/**
+ * struct cfg80211_nan_func - a NAN function
+ *
+ * @type: &enum nl80211_nan_function_type
+ * @service_id: the service ID of the function
+ * @publish_type: &nl80211_nan_publish_type
+ * @close_range: if true, the range should be limited. Threshold is
+ *     implementation specific.
+ * @publish_bcast: if true, the solicited publish should be broadcasted
+ * @subscribe_active: if true, the subscribe is active
+ * @followup_id: the instance ID for follow up
+ * @followup_reqid: the requestor instance ID for follow up
+ * @followup_dest: MAC address of the recipient of the follow up
+ * @ttl: time to live counter in DW.
+ * @serv_spec_info: Service Specific Info
+ * @serv_spec_info_len: Service Specific Info length
+ * @srf_include: if true, SRF is inclusive
+ * @srf_bf: Bloom Filter
+ * @srf_bf_len: Bloom Filter length
+ * @srf_bf_idx: Bloom Filter index
+ * @srf_macs: SRF MAC addresses
+ * @srf_num_macs: number of MAC addresses in SRF
+ * @rx_filters: rx filters that are matched with corresponding peer's tx_filter
+ * @tx_filters: filters that should be transmitted in the SDF.
+ * @num_rx_filters: length of &rx_filters.
+ * @num_tx_filters: length of &tx_filters.
+ * @instance_id: driver allocated id of the function.
+ * @cookie: unique NAN function identifier.
+ */
+struct cfg80211_nan_func {
+       enum nl80211_nan_function_type type;
+       u8 service_id[NL80211_NAN_FUNC_SERVICE_ID_LEN];
+       u8 publish_type;
+       bool close_range;
+       bool publish_bcast;
+       bool subscribe_active;
+       u8 followup_id;
+       u8 followup_reqid;
+       struct mac_address followup_dest;
+       u32 ttl;
+       const u8 *serv_spec_info;
+       u8 serv_spec_info_len;
+       bool srf_include;
+       const u8 *srf_bf;
+       u8 srf_bf_len;
+       u8 srf_bf_idx;
+       struct mac_address *srf_macs;
+       int srf_num_macs;
+       struct cfg80211_nan_func_filter *rx_filters;
+       struct cfg80211_nan_func_filter *tx_filters;
+       u8 num_tx_filters;
+       u8 num_rx_filters;
+       u8 instance_id;
+       u64 cookie;
+};
+
 /**
  * struct cfg80211_ops - backend description for wireless configuration
  *
@@ -2589,6 +2693,19 @@ struct cfg80211_qos_map {
  *     and returning to the base channel for communication with the AP.
  * @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
  *     peers must be on the base channel when the call completes.
+ * @start_nan: Start the NAN interface.
+ * @stop_nan: Stop the NAN interface.
+ * @add_nan_func: Add a NAN function. Returns negative value on failure.
+ *     On success @nan_func ownership is transferred to the driver and
+ *     it may access it outside of the scope of this function. The driver
+ *     should free the @nan_func when no longer needed by calling
+ *     cfg80211_free_nan_func().
+ *     On success the driver should assign an instance_id in the
+ *     provided @nan_func.
+ * @del_nan_func: Delete a NAN function.
+ * @nan_change_conf: changes NAN configuration. The changed parameters must
+ *     be specified in @changes (using &enum cfg80211_nan_conf_changes);
+ *     All other parameters must be ignored.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2854,6 +2971,17 @@ struct cfg80211_ops {
        void    (*tdls_cancel_channel_switch)(struct wiphy *wiphy,
                                              struct net_device *dev,
                                              const u8 *addr);
+       int     (*start_nan)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                            struct cfg80211_nan_conf *conf);
+       void    (*stop_nan)(struct wiphy *wiphy, struct wireless_dev *wdev);
+       int     (*add_nan_func)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                               struct cfg80211_nan_func *nan_func);
+       void    (*del_nan_func)(struct wiphy *wiphy, struct wireless_dev *wdev,
+                              u64 cookie);
+       int     (*nan_change_conf)(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  struct cfg80211_nan_conf *conf,
+                                  u32 changes);
 };
 
 /*
@@ -2900,6 +3028,8 @@ struct cfg80211_ops {
  * @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
  * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
  *     beaconing mode (AP, IBSS, Mesh, ...).
+ * @WIPHY_FLAG_HAS_STATIC_WEP: The device supports static WEP key installation
+ *     before connection.
  */
 enum wiphy_flags {
        /* use hole at 0 */
@@ -2925,6 +3055,7 @@ enum wiphy_flags {
        WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL        = BIT(21),
        WIPHY_FLAG_SUPPORTS_5_10_MHZ            = BIT(22),
        WIPHY_FLAG_HAS_CHANNEL_SWITCH           = BIT(23),
+       WIPHY_FLAG_HAS_STATIC_WEP               = BIT(24),
 };
 
 /**
@@ -3302,6 +3433,8 @@ struct wiphy_iftype_ext_capab {
  * @bss_select_support: bitmask indicating the BSS selection criteria supported
  *     by the driver in the .connect() callback. The bit position maps to the
  *     attribute indices defined in &enum nl80211_bss_select_attr.
+ *
+ * @cookie_counter: unique generic cookie counter, used to identify objects.
  */
 struct wiphy {
        /* assign these fields before you register the wiphy */
@@ -3431,6 +3564,8 @@ struct wiphy {
 
        u32 bss_select_support;
 
+       u64 cookie_counter;
+
        char priv[0] __aligned(NETDEV_ALIGN);
 };
 
@@ -3611,6 +3746,7 @@ struct cfg80211_cached_keys;
  *     beacons, 0 when not valid
  * @address: The address for this device, valid only if @netdev is %NULL
  * @p2p_started: true if this is a P2P Device that has been started
+ * @nan_started: true if this is a NAN interface that has been started
  * @cac_started: true if DFS channel availability check has been started
  * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
  * @cac_time_ms: CAC time in ms
@@ -3642,7 +3778,7 @@ struct wireless_dev {
 
        struct mutex mtx;
 
-       bool use_4addr, p2p_started;
+       bool use_4addr, p2p_started, nan_started;
 
        u8 address[ETH_ALEN] __aligned(sizeof(u16));
 
@@ -5550,6 +5686,67 @@ wiphy_ext_feature_isset(struct wiphy *wiphy,
        return (ft_byte & BIT(ftidx % 8)) != 0;
 }
 
+/**
+ * cfg80211_free_nan_func - free NAN function
+ * @f: NAN function that should be freed
+ *
+ * Frees all the NAN function and all it's allocated members.
+ */
+void cfg80211_free_nan_func(struct cfg80211_nan_func *f);
+
+/**
+ * struct cfg80211_nan_match_params - NAN match parameters
+ * @type: the type of the function that triggered a match. If it is
+ *      %NL80211_NAN_FUNC_SUBSCRIBE it means that we replied to a subscriber.
+ *      If it is %NL80211_NAN_FUNC_PUBLISH, it means that we got a discovery
+ *      result.
+ *      If it is %NL80211_NAN_FUNC_FOLLOW_UP, we received a follow up.
+ * @inst_id: the local instance id
+ * @peer_inst_id: the instance id of the peer's function
+ * @addr: the MAC address of the peer
+ * @info_len: the length of the &info
+ * @info: the Service Specific Info from the peer (if any)
+ * @cookie: unique identifier of the corresponding function
+ */
+struct cfg80211_nan_match_params {
+       enum nl80211_nan_function_type type;
+       u8 inst_id;
+       u8 peer_inst_id;
+       const u8 *addr;
+       u8 info_len;
+       const u8 *info;
+       u64 cookie;
+};
+
+/**
+ * cfg80211_nan_match - report a match for a NAN function.
+ * @wdev: the wireless device reporting the match
+ * @match: match notification parameters
+ * @gfp: allocation flags
+ *
+ * This function reports that the a NAN function had a match. This
+ * can be a subscribe that had a match or a solicited publish that
+ * was sent. It can also be a follow up that was received.
+ */
+void cfg80211_nan_match(struct wireless_dev *wdev,
+                       struct cfg80211_nan_match_params *match, gfp_t gfp);
+
+/**
+ * cfg80211_nan_func_terminated - notify about NAN function termination.
+ *
+ * @wdev: the wireless device reporting the match
+ * @inst_id: the local instance id
+ * @reason: termination reason (one of the NL80211_NAN_FUNC_TERM_REASON_*)
+ * @cookie: unique NAN function identifier
+ * @gfp: allocation flags
+ *
+ * This function reports that the a NAN function is terminated.
+ */
+void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
+                                 u8 inst_id,
+                                 enum nl80211_nan_func_term_reason reason,
+                                 u64 cookie, gfp_t gfp);
+
 /* ethtool helper */
 void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info);
 
index 268b49049c373a2ad38e336d1f38131a9fe02886..6d8521a30c5cf3951a868aea9b7817ffda749de4 100644 (file)
@@ -72,9 +72,12 @@ struct fq {
        u32 flows_cnt;
        u32 perturbation;
        u32 limit;
+       u32 memory_limit;
+       u32 memory_usage;
        u32 quantum;
        u32 backlog;
        u32 overlimit;
+       u32 overmemory;
        u32 collisions;
 };
 
index 163f3ed0f05a741ed0f18933895f48a7c91bf754..4e6131cd3f43dfb711049a1925abbea6074ec25b 100644 (file)
@@ -29,6 +29,7 @@ static struct sk_buff *fq_flow_dequeue(struct fq *fq,
        tin->backlog_packets--;
        flow->backlog -= skb->len;
        fq->backlog--;
+       fq->memory_usage -= skb->truesize;
 
        if (flow->backlog == 0) {
                list_del_init(&flow->backlogchain);
@@ -154,6 +155,7 @@ static void fq_tin_enqueue(struct fq *fq,
        flow->backlog += skb->len;
        tin->backlog_bytes += skb->len;
        tin->backlog_packets++;
+       fq->memory_usage += skb->truesize;
        fq->backlog++;
 
        fq_recalc_backlog(fq, tin, flow);
@@ -166,7 +168,7 @@ static void fq_tin_enqueue(struct fq *fq,
 
        __skb_queue_tail(&flow->queue, skb);
 
-       if (fq->backlog > fq->limit) {
+       if (fq->backlog > fq->limit || fq->memory_usage > fq->memory_limit) {
                flow = list_first_entry_or_null(&fq->backlogs,
                                                struct fq_flow,
                                                backlogchain);
@@ -181,6 +183,8 @@ static void fq_tin_enqueue(struct fq *fq,
 
                flow->tin->overlimit++;
                fq->overlimit++;
+               if (fq->memory_usage > fq->memory_limit)
+                       fq->overmemory++;
        }
 }
 
@@ -251,6 +255,7 @@ static int fq_init(struct fq *fq, int flows_cnt)
        fq->perturbation = prandom_u32();
        fq->quantum = 300;
        fq->limit = 8192;
+       fq->memory_limit = 16 << 20; /* 16 MBytes */
 
        fq->flows = kcalloc(fq->flows_cnt, sizeof(fq->flows[0]), GFP_KERNEL);
        if (!fq->flows)
index 5296100f3889920ad92b333a0b6652eac9dc96e2..a810dfcb83c2382cb7fe1954eba7bae1645e9046 100644 (file)
@@ -715,6 +715,7 @@ enum mac80211_tx_info_flags {
  *     frame (PS-Poll or uAPSD).
  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate information
  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xmit path
  *
  * These flags are used in tx_info->control.flags.
  */
@@ -723,6 +724,7 @@ enum mac80211_tx_control_flags {
        IEEE80211_TX_CTRL_PS_RESPONSE           = BIT(1),
        IEEE80211_TX_CTRL_RATE_INJECT           = BIT(2),
        IEEE80211_TX_CTRL_AMSDU                 = BIT(3),
+       IEEE80211_TX_CTRL_FAST_XMIT             = BIT(4),
 };
 
 /*
@@ -2177,6 +2179,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 +2215,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,
@@ -3166,6 +3171,12 @@ enum ieee80211_reconfig_type {
  *     required function.
  *     The callback can sleep.
  *
+ * @offset_tsf: Offset the TSF timer by the specified value in the
+ *     firmware/hardware.  Preferred to set_tsf as it avoids delay between
+ *     calling set_tsf() and hardware getting programmed, which will show up
+ *     as TSF delay. Is not a required function.
+ *     The callback can sleep.
+ *
  * @reset_tsf: Reset the TSF timer and allow firmware/hardware to synchronize
  *     with other STAs in the IBSS. This is only used in IBSS mode. This
  *     function is optional if the firmware/hardware takes full care of
@@ -3420,6 +3431,21 @@ enum ieee80211_reconfig_type {
  *     synchronization which is needed in case driver has in its RSS queues
  *     pending frames that were received prior to the control path action
  *     currently taken (e.g. disassociation) but are not processed yet.
+ *
+ * @start_nan: join an existing NAN cluster, or create a new one.
+ * @stop_nan: leave the NAN cluster.
+ * @nan_change_conf: change NAN configuration. The data in cfg80211_nan_conf
+ *     contains full new configuration and changes specify which parameters
+ *     are changed with respect to the last NAN config.
+ *     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,
@@ -3531,6 +3557,8 @@ struct ieee80211_ops {
        u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
        void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        u64 tsf);
+       void (*offset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                          s64 offset);
        void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
        int (*tx_last_beacon)(struct ieee80211_hw *hw);
        int (*ampdu_action)(struct ieee80211_hw *hw,
@@ -3655,6 +3683,21 @@ struct ieee80211_ops {
        void (*wake_tx_queue)(struct ieee80211_hw *hw,
                              struct ieee80211_txq *txq);
        void (*sync_rx_queues)(struct ieee80211_hw *hw);
+
+       int (*start_nan)(struct ieee80211_hw *hw,
+                        struct ieee80211_vif *vif,
+                        struct cfg80211_nan_conf *conf);
+       int (*stop_nan)(struct ieee80211_hw *hw,
+                       struct ieee80211_vif *vif);
+       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);
 };
 
 /**
@@ -5728,4 +5771,36 @@ 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);
+
+/**
+ * ieee80211_nan_func_match - notify about NAN function match event.
+ *
+ * This function is used to notify mac80211 about NAN function match. The
+ * cookie inside the match struct will be assigned by mac80211.
+ * Note that this function can't be called from hard irq.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @match: match event information
+ * @gfp: allocation flags
+ */
+void ieee80211_nan_func_match(struct ieee80211_vif *vif,
+                             struct cfg80211_nan_match_params *match,
+                             gfp_t gfp);
+
 #endif /* MAC80211_H */
index 2206941514345345128a1a46819a337f4ce812ed..56368e9b462245b6a50a2756abac6233b95650d9 100644 (file)
@@ -48,6 +48,7 @@
 #define NL80211_MULTICAST_GROUP_REG            "regulatory"
 #define NL80211_MULTICAST_GROUP_MLME           "mlme"
 #define NL80211_MULTICAST_GROUP_VENDOR         "vendor"
+#define NL80211_MULTICAST_GROUP_NAN            "nan"
 #define NL80211_MULTICAST_GROUP_TESTMODE       "testmode"
 
 /**
  *     not running. The driver indicates the status of the scan through
  *     cfg80211_scan_done().
  *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ *     %NL80211_ATTR_WDEV interface. This interface must have been previously
+ *     created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
+ *     NAN interface will create or join a cluster. This command must have a
+ *     valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *     %NL80211_ATTR_NAN_DUAL attributes.
+ *     After this command NAN functions can be added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ *     its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ *     with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ *     operation returns the strictly positive and unique instance id
+ *     (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ *     of the function upon success.
+ *     Since instance ID's can be re-used, this cookie is the right
+ *     way to identify the function. This will avoid races when a termination
+ *     event is handled by the user space after it has already added a new
+ *     function that got the same instance id from the kernel as the one
+ *     which just terminated.
+ *     This cookie may be used in NAN events even before the command
+ *     returns, so userspace shouldn't process NAN events until it processes
+ *     the response to this command.
+ *     Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ *     This command is also used as a notification sent when a NAN function is
+ *     terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ *     and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
+ *     must be operational (%NL80211_CMD_START_NAN was executed).
+ *     It must contain at least one of the following attributes:
+ *     %NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ *     This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ *     %NL80211_ATTR_COOKIE.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1026,6 +1062,13 @@ enum nl80211_commands {
 
        NL80211_CMD_ABORT_SCAN,
 
+       NL80211_CMD_START_NAN,
+       NL80211_CMD_STOP_NAN,
+       NL80211_CMD_ADD_NAN_FUNCTION,
+       NL80211_CMD_DEL_NAN_FUNCTION,
+       NL80211_CMD_CHANGE_NAN_CONFIG,
+       NL80211_CMD_NAN_MATCH,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1343,7 +1386,13 @@ enum nl80211_commands {
  *     enum nl80211_band value is used as the index (nla_type() of the nested
  *     data. If a band is not included, it will be configured to allow all
  *     rates based on negotiated supported rates information. This attribute
- *     is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ *     is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ *     and joining mesh networks (not IBSS yet). In the later case, it must
+ *     specify just a single bitrate, which is to be used for the beacon.
+ *     The driver must also specify support for this with the extended
+ *     features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ *     NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ *     NL80211_EXT_FEATURE_BEACON_RATE_VHT.
  *
  * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
  *     at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1733,6 +1782,12 @@ enum nl80211_commands {
  *     regulatory indoor configuration would be owned by the netlink socket
  *     that configured the indoor setting, and the indoor operation would be
  *     cleared when the socket is closed.
+ *     If set during NAN interface creation, the interface will be destroyed
+ *     if the socket is closed just like any other interface. Moreover, only
+ *     the netlink socket that created the interface will be allowed to add
+ *     and remove functions. NAN notifications will be sent in unicast to that
+ *     socket. Without this attribute, any socket can add functions and the
+ *     notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *     the TDLS link initiator.
@@ -1867,6 +1922,21 @@ enum nl80211_commands {
  * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
  *     used to pull the stored data for mesh peer in power save state.
  *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ *     %NL80211_CMD_START_NAN and optionally with
+ *     %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ *     Also, values 1 and 255 are reserved for certification purposes and
+ *     should not be used during a normal device operation.
+ * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
+ *     &enum nl80211_nan_dual_band_conf). This attribute is used with
+ *     %NL80211_CMD_START_NAN and optionally with
+ *     %NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ *     &enum nl80211_nan_func_attributes for description of this nested
+ *     attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ *     See &enum nl80211_nan_match_attributes.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2261,6 +2331,11 @@ enum nl80211_attrs {
 
        NL80211_ATTR_MESH_PEER_AID,
 
+       NL80211_ATTR_NAN_MASTER_PREF,
+       NL80211_ATTR_NAN_DUAL,
+       NL80211_ATTR_NAN_FUNC,
+       NL80211_ATTR_NAN_MATCH,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2339,6 +2414,7 @@ enum nl80211_attrs {
  *     commands to create and destroy one
  * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
  *     This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -2359,6 +2435,7 @@ enum nl80211_iftype {
        NL80211_IFTYPE_P2P_GO,
        NL80211_IFTYPE_P2P_DEVICE,
        NL80211_IFTYPE_OCB,
+       NL80211_IFTYPE_NAN,
 
        /* keep last */
        NUM_NL80211_IFTYPES,
@@ -4551,6 +4628,12 @@ enum nl80211_feature_flags {
  *     (if available).
  * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
  *     channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ *     configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ *     configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ *     configuration (AP/mesh) with VHT rates.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4562,6 +4645,9 @@ enum nl80211_ext_feature_index {
        NL80211_EXT_FEATURE_SCAN_START_TIME,
        NL80211_EXT_FEATURE_BSS_PARENT_TSF,
        NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+       NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+       NL80211_EXT_FEATURE_BEACON_RATE_HT,
+       NL80211_EXT_FEATURE_BEACON_RATE_VHT,
 
        /* add new features before the definition below */
        NUM_NL80211_EXT_FEATURES,
@@ -4855,4 +4941,186 @@ enum nl80211_bss_select_attr {
        NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_nan_dual_band_conf - NAN dual band configuration
+ *
+ * Defines the NAN dual band mode of operation
+ *
+ * @NL80211_NAN_BAND_DEFAULT: device default mode
+ * @NL80211_NAN_BAND_2GHZ: 2.4GHz mode
+ * @NL80211_NAN_BAND_5GHZ: 5GHz mode
+  */
+enum nl80211_nan_dual_band_conf {
+       NL80211_NAN_BAND_DEFAULT        = 1 << 0,
+       NL80211_NAN_BAND_2GHZ           = 1 << 1,
+       NL80211_NAN_BAND_5GHZ           = 1 << 2,
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+       NL80211_NAN_FUNC_PUBLISH,
+       NL80211_NAN_FUNC_SUBSCRIBE,
+       NL80211_NAN_FUNC_FOLLOW_UP,
+
+       /* keep last */
+       __NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+       NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+       NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+       NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+       NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+       NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+       NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ *     specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ *     publish. Defines the transmission type for the publish Service Discovery
+ *     Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ *     publish. Should the solicited publish Service Discovery Frame be sent to
+ *     the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ *     subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ *     The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ *     is follow up. This is a u8.
+ *     The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ *     follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ *     close range. The range itself (RSSI) is defined by the device.
+ *     This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ *     stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ *     specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ *     See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ *     attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ *     nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ *     Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ *     See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+       __NL80211_NAN_FUNC_INVALID,
+       NL80211_NAN_FUNC_TYPE,
+       NL80211_NAN_FUNC_SERVICE_ID,
+       NL80211_NAN_FUNC_PUBLISH_TYPE,
+       NL80211_NAN_FUNC_PUBLISH_BCAST,
+       NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+       NL80211_NAN_FUNC_FOLLOW_UP_ID,
+       NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+       NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+       NL80211_NAN_FUNC_CLOSE_RANGE,
+       NL80211_NAN_FUNC_TTL,
+       NL80211_NAN_FUNC_SERVICE_INFO,
+       NL80211_NAN_FUNC_SRF,
+       NL80211_NAN_FUNC_RX_MATCH_FILTER,
+       NL80211_NAN_FUNC_TX_MATCH_FILTER,
+       NL80211_NAN_FUNC_INSTANCE_ID,
+       NL80211_NAN_FUNC_TERM_REASON,
+
+       /* keep last */
+       NUM_NL80211_NAN_FUNC_ATTR,
+       NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ *     This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ *     &NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ *     &NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ *     and only if &NL80211_NAN_SRF_BF isn't present. This is a nested
+ *     attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+       __NL80211_NAN_SRF_INVALID,
+       NL80211_NAN_SRF_INCLUDE,
+       NL80211_NAN_SRF_BF,
+       NL80211_NAN_SRF_BF_IDX,
+       NL80211_NAN_SRF_MAC_ADDRS,
+
+       /* keep last */
+       NUM_NL80211_NAN_SRF_ATTR,
+       NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ *     match. This is a nested attribute.
+ *     See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ *     that caused the match. This is a nested attribute.
+ *     See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+       __NL80211_NAN_MATCH_INVALID,
+       NL80211_NAN_MATCH_FUNC_LOCAL,
+       NL80211_NAN_MATCH_FUNC_PEER,
+
+       /* keep last */
+       NUM_NL80211_NAN_MATCH_ATTR,
+       NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
 #endif /* __LINUX_NL80211_H */
index e29ff5749944fdde2f1880b94d57231fc91d359b..fd6541f3ade3e1342ac0fdce3ae3403de5752770 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
+ * Copyright (C) 2015-2016 Intel Deutschland GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -152,6 +153,149 @@ static void ieee80211_stop_p2p_device(struct wiphy *wiphy,
        ieee80211_sdata_stop(IEEE80211_WDEV_TO_SUB_IF(wdev));
 }
 
+static int ieee80211_start_nan(struct wiphy *wiphy,
+                              struct wireless_dev *wdev,
+                              struct cfg80211_nan_conf *conf)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       int ret;
+
+       mutex_lock(&sdata->local->chanctx_mtx);
+       ret = ieee80211_check_combinations(sdata, NULL, 0, 0);
+       mutex_unlock(&sdata->local->chanctx_mtx);
+       if (ret < 0)
+               return ret;
+
+       ret = ieee80211_do_open(wdev, true);
+       if (ret)
+               return ret;
+
+       ret = drv_start_nan(sdata->local, sdata, conf);
+       if (ret)
+               ieee80211_sdata_stop(sdata);
+
+       sdata->u.nan.conf = *conf;
+
+       return ret;
+}
+
+static void ieee80211_stop_nan(struct wiphy *wiphy,
+                              struct wireless_dev *wdev)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+       drv_stop_nan(sdata->local, sdata);
+       ieee80211_sdata_stop(sdata);
+}
+
+static int ieee80211_nan_change_conf(struct wiphy *wiphy,
+                                    struct wireless_dev *wdev,
+                                    struct cfg80211_nan_conf *conf,
+                                    u32 changes)
+{
+       struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+       struct cfg80211_nan_conf new_conf;
+       int ret = 0;
+
+       if (sdata->vif.type != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!ieee80211_sdata_running(sdata))
+               return -ENETDOWN;
+
+       new_conf = sdata->u.nan.conf;
+
+       if (changes & CFG80211_NAN_CONF_CHANGED_PREF)
+               new_conf.master_pref = conf->master_pref;
+
+       if (changes & CFG80211_NAN_CONF_CHANGED_DUAL)
+               new_conf.dual = conf->dual;
+
+       ret = drv_nan_change_conf(sdata->local, sdata, &new_conf, changes);
+       if (!ret)
+               sdata->u.nan.conf = new_conf;
+
+       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)
@@ -257,6 +401,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
        case NL80211_IFTYPE_P2P_CLIENT:
@@ -2036,6 +2181,7 @@ static int ieee80211_scan(struct wiphy *wiphy,
                     !(req->flags & NL80211_SCAN_FLAG_AP)))
                        return -EOPNOTSUPP;
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -3377,6 +3523,63 @@ 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);
+
+void ieee80211_nan_func_match(struct ieee80211_vif *vif,
+                             struct cfg80211_nan_match_params *match,
+                             gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct cfg80211_nan_func *func;
+
+       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,  match->inst_id);
+       if (WARN_ON(!func)) {
+               spin_unlock_bh(&sdata->u.nan.func_lock);
+               return;
+       }
+       match->cookie = func->cookie;
+
+       spin_unlock_bh(&sdata->u.nan.func_lock);
+
+       cfg80211_nan_match(ieee80211_vif_to_wdev(vif), match, gfp);
+}
+EXPORT_SYMBOL(ieee80211_nan_func_match);
+
 const struct cfg80211_ops mac80211_config_ops = {
        .add_virtual_intf = ieee80211_add_iface,
        .del_virtual_intf = ieee80211_del_iface,
@@ -3462,4 +3665,9 @@ const struct cfg80211_ops mac80211_config_ops = {
        .set_ap_chanwidth = ieee80211_set_ap_chanwidth,
        .add_tx_ts = ieee80211_add_tx_ts,
        .del_tx_ts = ieee80211_del_tx_ts,
+       .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,
 };
index 74142d07ad31072f8308681a3ff3cfd55da74738..e75cbf6ecc26e4ec7bde777c385e001c0e49e371 100644 (file)
@@ -274,6 +274,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
                                    ieee80211_get_max_required_bw(sdata));
                        break;
                case NL80211_IFTYPE_P2P_DEVICE:
+               case NL80211_IFTYPE_NAN:
                        continue;
                case NL80211_IFTYPE_ADHOC:
                case NL80211_IFTYPE_WDS:
@@ -646,6 +647,9 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_chanctx *curr_ctx = NULL;
        int ret = 0;
 
+       if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_NAN))
+               return -ENOTSUPP;
+
        conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
                                         lockdep_is_held(&local->chanctx_mtx));
 
@@ -718,6 +722,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
 
                switch (sdata->vif.type) {
                case NL80211_IFTYPE_P2P_DEVICE:
+               case NL80211_IFTYPE_NAN:
                        continue;
                case NL80211_IFTYPE_STATION:
                        if (!sdata->u.mgd.associated)
@@ -980,6 +985,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
        case NUM_NL80211_IFTYPES:
                WARN_ON(1);
                break;
index 8ca62b6bb02a570cbfc602b2bfaf612343f53a2e..f56e2f487d096aef3f7e1c083688548f92ed7110 100644 (file)
@@ -89,13 +89,19 @@ static ssize_t aqm_read(struct file *file,
                        "R fq_flows_cnt %u\n"
                        "R fq_backlog %u\n"
                        "R fq_overlimit %u\n"
+                       "R fq_overmemory %u\n"
                        "R fq_collisions %u\n"
+                       "R fq_memory_usage %u\n"
+                       "RW fq_memory_limit %u\n"
                        "RW fq_limit %u\n"
                        "RW fq_quantum %u\n",
                        fq->flows_cnt,
                        fq->backlog,
+                       fq->overmemory,
                        fq->overlimit,
                        fq->collisions,
+                       fq->memory_usage,
+                       fq->memory_limit,
                        fq->limit,
                        fq->quantum);
 
@@ -128,6 +134,8 @@ static ssize_t aqm_write(struct file *file,
 
        if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
                return count;
+       else if (sscanf(buf, "fq_memory_limit %u", &local->fq.memory_limit) == 1)
+               return count;
        else if (sscanf(buf, "fq_quantum %u", &local->fq.quantum) == 1)
                return count;
 
index 5d35c0f37bb7a86b9d964fd3095ded9db60fa980..bcec1240f41d90bec8b97a3bf52f8a8fcf918384 100644 (file)
@@ -556,9 +556,15 @@ static ssize_t ieee80211_if_parse_tsf(
                ret = kstrtoull(buf, 10, &tsf);
                if (ret < 0)
                        return ret;
-               if (tsf_is_delta)
-                       tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
-               if (local->ops->set_tsf) {
+               if (tsf_is_delta && local->ops->offset_tsf) {
+                       drv_offset_tsf(local, sdata, tsf_is_delta * tsf);
+                       wiphy_info(local->hw.wiphy,
+                                  "debugfs offset TSF by %018lld\n",
+                                  tsf_is_delta * tsf);
+               } else if (local->ops->set_tsf) {
+                       if (tsf_is_delta)
+                               tsf = drv_get_tsf(local, sdata) +
+                                     tsf_is_delta * tsf;
                        drv_set_tsf(local, sdata, tsf);
                        wiphy_info(local->hw.wiphy,
                                   "debugfs set TSF to %#018llx\n", tsf);
index c701b6438bd9e262b30e4ac941b1339f0e210835..bb886e7db47f1dcf409a4746d7ee47be8c679462 100644 (file)
@@ -215,6 +215,21 @@ void drv_set_tsf(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+void drv_offset_tsf(struct ieee80211_local *local,
+                   struct ieee80211_sub_if_data *sdata,
+                   s64 offset)
+{
+       might_sleep();
+
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_offset_tsf(local, sdata, offset);
+       if (local->ops->offset_tsf)
+               local->ops->offset_tsf(&local->hw, &sdata->vif, offset);
+       trace_drv_return_void(local);
+}
+
 void drv_reset_tsf(struct ieee80211_local *local,
                   struct ieee80211_sub_if_data *sdata)
 {
index fe35a1c0dc862027ee289fe079146743f9145d4e..09f77e4a8a79d78f5722a85470bfa0862671e43e 100644 (file)
@@ -162,6 +162,7 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
                return;
 
        if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
+                        sdata->vif.type == NL80211_IFTYPE_NAN ||
                         (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
                          !sdata->vif.mu_mimo_owner)))
                return;
@@ -568,6 +569,9 @@ u64 drv_get_tsf(struct ieee80211_local *local,
 void drv_set_tsf(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
                 u64 tsf);
+void drv_offset_tsf(struct ieee80211_local *local,
+                   struct ieee80211_sub_if_data *sdata,
+                   s64 offset);
 void drv_reset_tsf(struct ieee80211_local *local,
                   struct ieee80211_sub_if_data *sdata);
 
@@ -1165,4 +1169,83 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
        local->ops->wake_tx_queue(&local->hw, &txq->txq);
 }
 
+static inline int drv_start_nan(struct ieee80211_local *local,
+                               struct ieee80211_sub_if_data *sdata,
+                               struct cfg80211_nan_conf *conf)
+{
+       int ret;
+
+       might_sleep();
+       check_sdata_in_driver(sdata);
+
+       trace_drv_start_nan(local, sdata, conf);
+       ret = local->ops->start_nan(&local->hw, &sdata->vif, conf);
+       trace_drv_return_int(local, ret);
+       return ret;
+}
+
+static inline void drv_stop_nan(struct ieee80211_local *local,
+                               struct ieee80211_sub_if_data *sdata)
+{
+       might_sleep();
+       check_sdata_in_driver(sdata);
+
+       trace_drv_stop_nan(local, sdata);
+       local->ops->stop_nan(&local->hw, &sdata->vif);
+       trace_drv_return_void(local);
+}
+
+static inline int drv_nan_change_conf(struct ieee80211_local *local,
+                                      struct ieee80211_sub_if_data *sdata,
+                                      struct cfg80211_nan_conf *conf,
+                                      u32 changes)
+{
+       int ret;
+
+       might_sleep();
+       check_sdata_in_driver(sdata);
+
+       if (!local->ops->nan_change_conf)
+               return -EOPNOTSUPP;
+
+       trace_drv_nan_change_conf(local, sdata, conf, changes);
+       ret = local->ops->nan_change_conf(&local->hw, &sdata->vif, conf,
+                                         changes);
+       trace_drv_return_int(local, ret);
+
+       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 */
index e496dee5af089322f42eb273091c754b77b1d192..34c2add2c455978936c5379218620d41269a43a6 100644 (file)
@@ -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;
@@ -813,12 +815,14 @@ enum txq_info_flags {
  * @def_flow: used as a fallback flow when a packet destined to @tin hashes to
  *     a fq_flow which is already owned by a different tin
  * @def_cvars: codel vars for @def_flow
+ * @frags: used to keep fragments created after dequeue
  */
 struct txq_info {
        struct fq_tin tin;
        struct fq_flow def_flow;
        struct codel_vars def_cvars;
        struct codel_stats cstats;
+       struct sk_buff_head frags;
        unsigned long flags;
 
        /* keep last! */
@@ -830,6 +834,20 @@ struct ieee80211_if_mntr {
        u8 mu_follow_addr[ETH_ALEN] __aligned(2);
 };
 
+/**
+ * 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 {
        struct list_head list;
 
@@ -929,6 +947,7 @@ struct ieee80211_sub_if_data {
                struct ieee80211_if_mesh mesh;
                struct ieee80211_if_ocb ocb;
                struct ieee80211_if_mntr mntr;
+               struct ieee80211_if_nan nan;
        } u;
 
 #ifdef CONFIG_MAC80211_DEBUGFS
@@ -1481,6 +1500,13 @@ static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq)
        return container_of(txq, struct txq_info, txq);
 }
 
+static inline bool txq_has_queue(struct ieee80211_txq *txq)
+{
+       struct txq_info *txqi = to_txq_info(txq);
+
+       return !(skb_queue_empty(&txqi->frags) && !txqi->tin.backlog_packets);
+}
+
 static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
 {
        return ether_addr_equal(raddr, addr) ||
index b0abddc714ef14d1e086acdce356639eaf101eeb..638ec0759078c18ec6816bb9de5abe6f6cb40487 100644 (file)
@@ -327,6 +327,9 @@ static int ieee80211_check_queues(struct ieee80211_sub_if_data *sdata,
        int n_queues = sdata->local->hw.queues;
        int i;
 
+       if (iftype == NL80211_IFTYPE_NAN)
+               return 0;
+
        if (iftype != NL80211_IFTYPE_P2P_DEVICE) {
                for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                        if (WARN_ON_ONCE(sdata->vif.hw_queue[i] ==
@@ -545,6 +548,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
        case NL80211_IFTYPE_ADHOC:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_OCB:
+       case NL80211_IFTYPE_NAN:
                /* no special treatment */
                break;
        case NL80211_IFTYPE_UNSPECIFIED:
@@ -646,7 +650,8 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                        local->fif_probe_req++;
                }
 
-               if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE)
+               if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+                   sdata->vif.type != NL80211_IFTYPE_NAN)
                        changed |= ieee80211_reset_erp_info(sdata);
                ieee80211_bss_info_change_notify(sdata, changed);
 
@@ -660,6 +665,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
                        break;
                case NL80211_IFTYPE_WDS:
                case NL80211_IFTYPE_P2P_DEVICE:
+               case NL80211_IFTYPE_NAN:
                        break;
                default:
                        /* not reached */
@@ -792,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);
 
@@ -944,6 +951,18 @@ 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);
@@ -1455,6 +1474,11 @@ 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:
                sdata->vif.bss_conf.bssid = sdata->vif.addr;
@@ -1722,7 +1746,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
 
        ASSERT_RTNL();
 
-       if (type == NL80211_IFTYPE_P2P_DEVICE) {
+       if (type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN) {
                struct wireless_dev *wdev;
 
                sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size,
index ac053a9df36d8246a4de0f0f36c773a28ba9350e..1075ac24c8c5f433690749970d9e27393351caa9 100644 (file)
@@ -821,6 +821,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
             !local->ops->tdls_recv_channel_switch))
                return -EOPNOTSUPP;
 
+       if (WARN_ON(local->hw.wiphy->interface_modes &
+                       BIT(NL80211_IFTYPE_NAN) &&
+                   (!local->ops->start_nan || !local->ops->stop_nan)))
+               return -EINVAL;
+
 #ifdef CONFIG_PM
        if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
                return -EINVAL;
@@ -1058,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",
index 64bc22ad94965c4615eaaa94aae02b45891204e7..faca22cd02b59cf9c79e20307fe47f36a562ad60 100644 (file)
@@ -28,7 +28,7 @@
  * could be, for instance, in case a neighbor is restarted and its TSF counter
  * reset.
  */
-#define TOFFSET_MAXIMUM_ADJUSTMENT 30000               /* 30 ms */
+#define TOFFSET_MAXIMUM_ADJUSTMENT 800         /* 0.8 ms */
 
 struct sync_method {
        u8 method;
@@ -70,9 +70,13 @@ void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
        }
        spin_unlock_bh(&ifmsh->sync_offset_lock);
 
-       tsf = drv_get_tsf(local, sdata);
-       if (tsf != -1ULL)
-               drv_set_tsf(local, sdata, tsf + tsfdelta);
+       if (local->ops->offset_tsf) {
+               drv_offset_tsf(local, sdata, tsfdelta);
+       } else {
+               tsf = drv_get_tsf(local, sdata);
+               if (tsf != -1ULL)
+                       drv_set_tsf(local, sdata, tsf + tsfdelta);
+       }
 }
 
 static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
index 55a9c5b94ce128d06ffb3154173fa2ea62a53f9b..c3f610bba3fe9879cb1594e5b1caf00b8a506d6a 100644 (file)
@@ -128,7 +128,8 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
                if (!ieee80211_sdata_running(sdata))
                        continue;
 
-               if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
+               if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
+                   sdata->vif.type == NL80211_IFTYPE_NAN)
                        continue;
 
                if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
@@ -838,6 +839,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        case NL80211_IFTYPE_P2P_DEVICE:
                need_offchan = true;
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
index f7cf342bab523c52d4a39d63890e4c5997a6db5f..6175db385ba7d085f4d2f614697f8ef7e9d914cd 100644 (file)
@@ -1323,9 +1323,7 @@ static void sta_ps_start(struct sta_info *sta)
                return;
 
        for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
-               struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
-
-               if (txqi->tin.backlog_packets)
+               if (txq_has_queue(sta->sta.txq[tid]))
                        set_bit(tid, &sta->txq_buffered_tids);
                else
                        clear_bit(tid, &sta->txq_buffered_tids);
@@ -3586,6 +3584,9 @@ static bool ieee80211_accept_frame(struct ieee80211_rx_data *rx)
                       ieee80211_is_probe_req(hdr->frame_control) ||
                       ieee80211_is_probe_resp(hdr->frame_control) ||
                       ieee80211_is_beacon(hdr->frame_control);
+       case NL80211_IFTYPE_NAN:
+               /* Currently no frames on NAN interface are allowed */
+               return false;
        default:
                break;
        }
index 011880d633b44c8397d3effa622135dcc3f4ffd2..78e9ecbc96e616d0f90228abb5bcda59147ea73f 100644 (file)
@@ -1202,12 +1202,10 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        if (sta->sta.txq[0]) {
                for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
-                       struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
-
-                       if (!txqi->tin.backlog_packets)
+                       if (!txq_has_queue(sta->sta.txq[i]))
                                continue;
 
-                       drv_wake_tx_queue(local, txqi);
+                       drv_wake_tx_queue(local, to_txq_info(sta->sta.txq[i]));
                }
        }
 
@@ -1638,10 +1636,8 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta,
                        return;
 
                for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
-                       struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
-
                        if (!(driver_release_tids & BIT(tid)) ||
-                           txqi->tin.backlog_packets)
+                           txq_has_queue(sta->sta.txq[tid]))
                                continue;
 
                        sta_info_recalc_tim(sta);
index 77e4c53baefb198ddf5129706d3e95a7ba8f612a..92a47afaa989e6b63b494eacc23c56409f767642 100644 (file)
@@ -984,6 +984,32 @@ TRACE_EVENT(drv_set_tsf,
        )
 );
 
+TRACE_EVENT(drv_offset_tsf,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                s64 offset),
+
+       TP_ARGS(local, sdata, offset),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(s64, tsf_offset)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->tsf_offset = offset;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT  " tsf offset:%lld",
+               LOCAL_PR_ARG, VIF_PR_ARG,
+               (unsigned long long)__entry->tsf_offset
+       )
+);
+
 DEFINE_EVENT(local_sdata_evt, drv_reset_tsf,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata),
@@ -1700,6 +1726,139 @@ TRACE_EVENT(drv_get_expected_throughput,
        )
 );
 
+TRACE_EVENT(drv_start_nan,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct cfg80211_nan_conf *conf),
+
+       TP_ARGS(local, sdata, conf),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(u8, master_pref)
+               __field(u8, dual)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->master_pref = conf->master_pref;
+               __entry->dual = conf->dual;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT
+               ", master preference: %u, dual: %d",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->master_pref,
+               __entry->dual
+       )
+);
+
+TRACE_EVENT(drv_stop_nan,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata),
+
+       TP_ARGS(local, sdata),
+
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT,
+               LOCAL_PR_ARG, VIF_PR_ARG
+       )
+);
+
+TRACE_EVENT(drv_nan_change_conf,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct cfg80211_nan_conf *conf,
+                u32 changes),
+
+       TP_ARGS(local, sdata, conf, changes),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               VIF_ENTRY
+               __field(u8, master_pref)
+               __field(u8, dual)
+               __field(u32, changes)
+       ),
+
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               VIF_ASSIGN;
+               __entry->master_pref = conf->master_pref;
+               __entry->dual = conf->dual;
+               __entry->changes = changes;
+       ),
+
+       TP_printk(
+               LOCAL_PR_FMT  VIF_PR_FMT
+               ", master preference: %u, dual: %d, changes: 0x%x",
+               LOCAL_PR_ARG, VIF_PR_ARG, __entry->master_pref,
+               __entry->dual, __entry->changes
+       )
+);
+
+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.
  */
index 1ff08be90a983fb47cfbc6109270eec28ac4081c..1c56abc496272bb58d71639975e4706c266f2066 100644 (file)
@@ -796,36 +796,6 @@ static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
        return ret;
 }
 
-static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
-                                         struct ieee80211_vif *vif,
-                                         struct ieee80211_sta *pubsta,
-                                         struct sk_buff *skb)
-{
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct ieee80211_txq *txq = NULL;
-
-       if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
-           (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
-               return NULL;
-
-       if (!ieee80211_is_data(hdr->frame_control))
-               return NULL;
-
-       if (pubsta) {
-               u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
-
-               txq = pubsta->txq[tid];
-       } else if (vif) {
-               txq = vif->txq;
-       }
-
-       if (!txq)
-               return NULL;
-
-       return to_txq_info(txq);
-}
-
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
@@ -883,9 +853,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
        tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
        tx->sta->tx_stats.msdu[tid]++;
 
-       if (!ieee80211_get_txq(tx->local, info->control.vif, &tx->sta->sta,
-                              tx->skb))
-               hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
+       hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
 
        return TX_CONTINUE;
 }
@@ -1274,6 +1242,36 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        return TX_CONTINUE;
 }
 
+static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
+                                         struct ieee80211_vif *vif,
+                                         struct ieee80211_sta *pubsta,
+                                         struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_txq *txq = NULL;
+
+       if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
+           (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
+               return NULL;
+
+       if (!ieee80211_is_data(hdr->frame_control))
+               return NULL;
+
+       if (pubsta) {
+               u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+               txq = pubsta->txq[tid];
+       } else if (vif) {
+               txq = vif->txq;
+       }
+
+       if (!txq)
+               return NULL;
+
+       return to_txq_info(txq);
+}
+
 static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
 {
        IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
@@ -1405,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
        fq_flow_init(&txqi->def_flow);
        codel_vars_init(&txqi->def_cvars);
        codel_stats_init(&txqi->cstats);
+       __skb_queue_head_init(&txqi->frags);
 
        txqi->txq.vif = &sdata->vif;
 
@@ -1427,6 +1426,7 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
        struct fq_tin *tin = &txqi->tin;
 
        fq_tin_reset(fq, tin, fq_skb_free_func);
+       ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
 }
 
 int ieee80211_txq_setup_flows(struct ieee80211_local *local)
@@ -1434,6 +1434,8 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
        struct fq *fq = &local->fq;
        int ret;
        int i;
+       bool supp_vht = false;
+       enum nl80211_band band;
 
        if (!local->ops->wake_tx_queue)
                return 0;
@@ -1442,6 +1444,23 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
        if (ret)
                return ret;
 
+       /*
+        * If the hardware doesn't support VHT, it is safe to limit the maximum
+        * queue size. 4 Mbytes is 64 max-size aggregates in 802.11n.
+        */
+       for (band = 0; band < NUM_NL80211_BANDS; band++) {
+               struct ieee80211_supported_band *sband;
+
+               sband = local->hw.wiphy->bands[band];
+               if (!sband)
+                       continue;
+
+               supp_vht = supp_vht || sband->vht_cap.vht_supported;
+       }
+
+       if (!supp_vht)
+               fq->memory_limit = 4 << 20; /* 4 Mbytes */
+
        codel_params_init(&local->cparams);
        local->cparams.interval = MS2TIME(100);
        local->cparams.target = MS2TIME(20);
@@ -1477,54 +1496,46 @@ void ieee80211_txq_teardown_flows(struct ieee80211_local *local)
        spin_unlock_bh(&fq->lock);
 }
 
-struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
-                                    struct ieee80211_txq *txq)
+static bool ieee80211_queue_skb(struct ieee80211_local *local,
+                               struct ieee80211_sub_if_data *sdata,
+                               struct sta_info *sta,
+                               struct sk_buff *skb)
 {
-       struct ieee80211_local *local = hw_to_local(hw);
-       struct txq_info *txqi = container_of(txq, struct txq_info, txq);
-       struct ieee80211_hdr *hdr;
-       struct sk_buff *skb = NULL;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        struct fq *fq = &local->fq;
-       struct fq_tin *tin = &txqi->tin;
+       struct ieee80211_vif *vif;
+       struct txq_info *txqi;
+       struct ieee80211_sta *pubsta;
 
-       spin_lock_bh(&fq->lock);
+       if (!local->ops->wake_tx_queue ||
+           sdata->vif.type == NL80211_IFTYPE_MONITOR)
+               return false;
 
-       if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
-               goto out;
+       if (sta && sta->uploaded)
+               pubsta = &sta->sta;
+       else
+               pubsta = NULL;
 
-       skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
-       if (!skb)
-               goto out;
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               sdata = container_of(sdata->bss,
+                                    struct ieee80211_sub_if_data, u.ap);
 
-       ieee80211_set_skb_vif(skb, txqi);
+       vif = &sdata->vif;
+       txqi = ieee80211_get_txq(local, vif, pubsta, skb);
 
-       hdr = (struct ieee80211_hdr *)skb->data;
-       if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
-               struct sta_info *sta = container_of(txq->sta, struct sta_info,
-                                                   sta);
-               struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       if (!txqi)
+               return false;
 
-               hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
-               if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
-                       info->flags |= IEEE80211_TX_CTL_AMPDU;
-               else
-                       info->flags &= ~IEEE80211_TX_CTL_AMPDU;
-       }
+       info->control.vif = vif;
 
-out:
+       spin_lock_bh(&fq->lock);
+       ieee80211_txq_enqueue(local, txqi, skb);
        spin_unlock_bh(&fq->lock);
 
-       if (skb && skb_has_frag_list(skb) &&
-           !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
-               if (skb_linearize(skb)) {
-                       ieee80211_free_txskb(&local->hw, skb);
-                       return NULL;
-               }
-       }
+       drv_wake_tx_queue(local, txqi);
 
-       return skb;
+       return true;
 }
-EXPORT_SYMBOL(ieee80211_tx_dequeue);
 
 static bool ieee80211_tx_frags(struct ieee80211_local *local,
                               struct ieee80211_vif *vif,
@@ -1533,9 +1544,7 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
                               bool txpending)
 {
        struct ieee80211_tx_control control = {};
-       struct fq *fq = &local->fq;
        struct sk_buff *skb, *tmp;
-       struct txq_info *txqi;
        unsigned long flags;
 
        skb_queue_walk_safe(skbs, skb, tmp) {
@@ -1550,21 +1559,6 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
                }
 #endif
 
-               txqi = ieee80211_get_txq(local, vif, sta, skb);
-               if (txqi) {
-                       info->control.vif = vif;
-
-                       __skb_unlink(skb, skbs);
-
-                       spin_lock_bh(&fq->lock);
-                       ieee80211_txq_enqueue(local, txqi, skb);
-                       spin_unlock_bh(&fq->lock);
-
-                       drv_wake_tx_queue(local, txqi);
-
-                       continue;
-               }
-
                spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
                if (local->queue_stop_reasons[q] ||
                    (!txpending && !skb_queue_empty(&local->pending[q]))) {
@@ -1685,10 +1679,13 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
 /*
  * Invoke TX handlers, return 0 on success and non-zero if the
  * frame was dropped or queued.
+ *
+ * The handlers are split into an early and late part. The latter is everything
+ * that can be sensitive to reordering, and will be deferred to after packets
+ * are dequeued from the intermediate queues (when they are enabled).
  */
-static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
+static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
        ieee80211_tx_result res = TX_DROP;
 
 #define CALL_TXH(txh) \
@@ -1706,6 +1703,31 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
                CALL_TXH(ieee80211_tx_h_rate_ctrl);
 
+ txh_done:
+       if (unlikely(res == TX_DROP)) {
+               I802_DEBUG_INC(tx->local->tx_handlers_drop);
+               if (tx->skb)
+                       ieee80211_free_txskb(&tx->local->hw, tx->skb);
+               else
+                       ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
+               return -1;
+       } else if (unlikely(res == TX_QUEUED)) {
+               I802_DEBUG_INC(tx->local->tx_handlers_queued);
+               return -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Late handlers can be called while the sta lock is held. Handlers that can
+ * cause packets to be generated will cause deadlock!
+ */
+static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+       ieee80211_tx_result res = TX_CONTINUE;
+
        if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
                __skb_queue_tail(&tx->skbs, tx->skb);
                tx->skb = NULL;
@@ -1738,6 +1760,15 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        return 0;
 }
 
+static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
+{
+       int r = invoke_tx_handlers_early(tx);
+
+       if (r)
+               return r;
+       return invoke_tx_handlers_late(tx);
+}
+
 bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif, struct sk_buff *skb,
                              int band, struct ieee80211_sta **sta)
@@ -1812,7 +1843,13 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
                info->hw_queue =
                        sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
 
-       if (!invoke_tx_handlers(&tx))
+       if (invoke_tx_handlers_early(&tx))
+               return false;
+
+       if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
+               return true;
+
+       if (!invoke_tx_handlers_late(&tx))
                result = __ieee80211_tx(local, &tx.skbs, led_len,
                                        tx.sta, txpending);
 
@@ -3156,8 +3193,71 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
        return ret;
 }
 
+/*
+ * Can be called while the sta lock is held. Anything that can cause packets to
+ * be generated will cause deadlock!
+ */
+static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
+                                      struct sta_info *sta, u8 pn_offs,
+                                      struct ieee80211_key *key,
+                                      struct sk_buff *skb)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       struct ieee80211_hdr *hdr = (void *)skb->data;
+       u8 tid = IEEE80211_NUM_TIDS;
+
+       if (key)
+               info->control.hw_key = &key->conf;
+
+       ieee80211_tx_stats(skb->dev, skb->len);
+
+       if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+               tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+               *ieee80211_get_qos_ctl(hdr) = tid;
+               hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
+       } else {
+               info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
+               hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
+               sdata->sequence_number += 0x10;
+       }
+
+       if (skb_shinfo(skb)->gso_size)
+               sta->tx_stats.msdu[tid] +=
+                       DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
+       else
+               sta->tx_stats.msdu[tid]++;
+
+       info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+
+       /* statistics normally done by ieee80211_tx_h_stats (but that
+        * has to consider fragmentation, so is more complex)
+        */
+       sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
+       sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
+
+       if (pn_offs) {
+               u64 pn;
+               u8 *crypto_hdr = skb->data + pn_offs;
+
+               switch (key->conf.cipher) {
+               case WLAN_CIPHER_SUITE_CCMP:
+               case WLAN_CIPHER_SUITE_CCMP_256:
+               case WLAN_CIPHER_SUITE_GCMP:
+               case WLAN_CIPHER_SUITE_GCMP_256:
+                       pn = atomic64_inc_return(&key->conf.tx_pn);
+                       crypto_hdr[0] = pn;
+                       crypto_hdr[1] = pn >> 8;
+                       crypto_hdr[4] = pn >> 16;
+                       crypto_hdr[5] = pn >> 24;
+                       crypto_hdr[6] = pn >> 32;
+                       crypto_hdr[7] = pn >> 40;
+                       break;
+               }
+       }
+}
+
 static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-                               struct net_device *dev, struct sta_info *sta,
+                               struct sta_info *sta,
                                struct ieee80211_fast_tx *fast_tx,
                                struct sk_buff *skb)
 {
@@ -3208,8 +3308,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
                        return true;
        }
 
-       ieee80211_tx_stats(dev, skb->len + extra_head);
-
        if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
            ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
                return true;
@@ -3238,24 +3336,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
        info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
                      IEEE80211_TX_CTL_DONTFRAG |
                      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
-
-       if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
-               *ieee80211_get_qos_ctl(hdr) = tid;
-               if (!ieee80211_get_txq(local, &sdata->vif, &sta->sta, skb))
-                       hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
-       } else {
-               info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
-               hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
-               sdata->sequence_number += 0x10;
-       }
-
-       if (skb_shinfo(skb)->gso_size)
-               sta->tx_stats.msdu[tid] +=
-                       DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
-       else
-               sta->tx_stats.msdu[tid]++;
-
-       info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
+       info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
 
        __skb_queue_head_init(&tx.skbs);
 
@@ -3265,9 +3346,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
        tx.sta = sta;
        tx.key = fast_tx->key;
 
-       if (fast_tx->key)
-               info->control.hw_key = &fast_tx->key->conf;
-
        if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
                tx.skb = skb;
                r = ieee80211_tx_h_rate_ctrl(&tx);
@@ -3281,31 +3359,11 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
                }
        }
 
-       /* statistics normally done by ieee80211_tx_h_stats (but that
-        * has to consider fragmentation, so is more complex)
-        */
-       sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
-       sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
-
-       if (fast_tx->pn_offs) {
-               u64 pn;
-               u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
+       if (ieee80211_queue_skb(local, sdata, sta, skb))
+               return true;
 
-               switch (fast_tx->key->conf.cipher) {
-               case WLAN_CIPHER_SUITE_CCMP:
-               case WLAN_CIPHER_SUITE_CCMP_256:
-               case WLAN_CIPHER_SUITE_GCMP:
-               case WLAN_CIPHER_SUITE_GCMP_256:
-                       pn = atomic64_inc_return(&fast_tx->key->conf.tx_pn);
-                       crypto_hdr[0] = pn;
-                       crypto_hdr[1] = pn >> 8;
-                       crypto_hdr[4] = pn >> 16;
-                       crypto_hdr[5] = pn >> 24;
-                       crypto_hdr[6] = pn >> 32;
-                       crypto_hdr[7] = pn >> 40;
-                       break;
-               }
-       }
+       ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
+                                  fast_tx->key, skb);
 
        if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
                sdata = container_of(sdata->bss,
@@ -3316,6 +3374,94 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
        return true;
 }
 
+struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
+                                    struct ieee80211_txq *txq)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+       struct ieee80211_hdr *hdr;
+       struct sk_buff *skb = NULL;
+       struct fq *fq = &local->fq;
+       struct fq_tin *tin = &txqi->tin;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_tx_data tx;
+       ieee80211_tx_result r;
+
+       spin_lock_bh(&fq->lock);
+
+       if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
+               goto out;
+
+       /* Make sure fragments stay together. */
+       skb = __skb_dequeue(&txqi->frags);
+       if (skb)
+               goto out;
+
+begin:
+       skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
+       if (!skb)
+               goto out;
+
+       ieee80211_set_skb_vif(skb, txqi);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       info = IEEE80211_SKB_CB(skb);
+
+       memset(&tx, 0, sizeof(tx));
+       __skb_queue_head_init(&tx.skbs);
+       tx.local = local;
+       tx.skb = skb;
+       tx.sdata = vif_to_sdata(info->control.vif);
+
+       if (txq->sta)
+               tx.sta = container_of(txq->sta, struct sta_info, sta);
+
+       /*
+        * The key can be removed while the packet was queued, so need to call
+        * this here to get the current key.
+        */
+       r = ieee80211_tx_h_select_key(&tx);
+       if (r != TX_CONTINUE) {
+               ieee80211_free_txskb(&local->hw, skb);
+               goto begin;
+       }
+
+       if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
+               struct sta_info *sta = container_of(txq->sta, struct sta_info,
+                                                   sta);
+               u8 pn_offs = 0;
+
+               if (tx.key &&
+                   (tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
+                       pn_offs = ieee80211_hdrlen(hdr->frame_control);
+
+               ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
+                                          tx.key, skb);
+       } else {
+               if (invoke_tx_handlers_late(&tx))
+                       goto begin;
+
+               skb = __skb_dequeue(&tx.skbs);
+
+               if (!skb_queue_empty(&tx.skbs))
+                       skb_queue_splice_tail(&tx.skbs, &txqi->frags);
+       }
+
+       if (skb && skb_has_frag_list(skb) &&
+           !ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
+               if (skb_linearize(skb)) {
+                       ieee80211_free_txskb(&local->hw, skb);
+                       goto begin;
+               }
+       }
+
+out:
+       spin_unlock_bh(&fq->lock);
+
+       return skb;
+}
+EXPORT_SYMBOL(ieee80211_tx_dequeue);
+
 void __ieee80211_subif_start_xmit(struct sk_buff *skb,
                                  struct net_device *dev,
                                  u32 info_flags)
@@ -3340,7 +3486,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
                fast_tx = rcu_dereference(sta->fast_tx);
 
                if (fast_tx &&
-                   ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
+                   ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
                        goto out;
        }
 
index b6865d88448761ff9a679b36e25d764da9669768..545c79a42a779b949fe4cd62afee7e36685d358c 100644 (file)
@@ -1209,7 +1209,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
        }
 
        if (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
-           sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE) {
+           sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
+           sdata->vif.type != NL80211_IFTYPE_NAN) {
                sdata->vif.bss_conf.qos = enable_qos;
                if (bss_notify)
                        ieee80211_bss_info_change_notify(sdata,
@@ -1748,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;
@@ -1971,6 +2012,13 @@ 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:
@@ -3393,11 +3441,18 @@ void ieee80211_txq_get_depth(struct ieee80211_txq *txq,
                             unsigned long *byte_cnt)
 {
        struct txq_info *txqi = to_txq_info(txq);
+       u32 frag_cnt = 0, frag_bytes = 0;
+       struct sk_buff *skb;
+
+       skb_queue_walk(&txqi->frags, skb) {
+               frag_cnt++;
+               frag_bytes += skb->len;
+       }
 
        if (frame_cnt)
-               *frame_cnt = txqi->tin.backlog_packets;
+               *frame_cnt = txqi->tin.backlog_packets + frag_cnt;
 
        if (byte_cnt)
-               *byte_cnt = txqi->tin.backlog_bytes;
+               *byte_cnt = txqi->tin.backlog_bytes + frag_bytes;
 }
 EXPORT_SYMBOL(ieee80211_txq_get_depth);
index 0f506220a3bde0e4b73c043fa9ea6a7f6b404ed0..5497d022fadabf17512c0f4c0b77ce50cce345dc 100644 (file)
@@ -372,6 +372,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
                break;
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
@@ -946,6 +947,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_WDS:
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
                /* these interface types don't really have a channel */
                return;
        case NL80211_IFTYPE_UNSPECIFIED:
index 4911cd997b9a2f997ee2e0ccedb36a6af3cba786..8201e6d7449e969182152fcc2641a7e0247b7770 100644 (file)
@@ -225,6 +225,23 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
        }
 }
 
+void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
+                      struct wireless_dev *wdev)
+{
+       ASSERT_RTNL();
+
+       if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
+               return;
+
+       if (!wdev->nan_started)
+               return;
+
+       rdev_stop_nan(rdev, wdev);
+       wdev->nan_started = false;
+
+       rdev->opencount--;
+}
+
 void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@@ -242,6 +259,9 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
                case NL80211_IFTYPE_P2P_DEVICE:
                        cfg80211_stop_p2p_device(rdev, wdev);
                        break;
+               case NL80211_IFTYPE_NAN:
+                       cfg80211_stop_nan(rdev, wdev);
+                       break;
                default:
                        break;
                }
@@ -537,6 +557,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
                                    c->limits[j].max > 1))
                                return -EINVAL;
 
+                       /* Only a single NAN can be allowed */
+                       if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
+                                   c->limits[j].max > 1))
+                               return -EINVAL;
+
                        cnt += c->limits[j].max;
                        /*
                         * Don't advertise an unsupported type
@@ -579,6 +604,11 @@ int wiphy_register(struct wiphy *wiphy)
                     !rdev->ops->tdls_cancel_channel_switch)))
                return -EINVAL;
 
+       if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) &&
+                   (!rdev->ops->start_nan || !rdev->ops->stop_nan ||
+                    !rdev->ops->add_nan_func || !rdev->ops->del_nan_func)))
+               return -EINVAL;
+
        /*
         * if a wiphy has unsupported modes for regulatory channel enforcement,
         * opt-out of enforcement checking
@@ -589,6 +619,7 @@ int wiphy_register(struct wiphy *wiphy)
                                       BIT(NL80211_IFTYPE_P2P_GO) |
                                       BIT(NL80211_IFTYPE_ADHOC) |
                                       BIT(NL80211_IFTYPE_P2P_DEVICE) |
+                                      BIT(NL80211_IFTYPE_NAN) |
                                       BIT(NL80211_IFTYPE_AP_VLAN) |
                                       BIT(NL80211_IFTYPE_MONITOR)))
                wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
@@ -916,6 +947,9 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
                cfg80211_mlme_purge_registrations(wdev);
                cfg80211_stop_p2p_device(rdev, wdev);
                break;
+       case NL80211_IFTYPE_NAN:
+               cfg80211_stop_nan(rdev, wdev);
+               break;
        default:
                WARN_ON_ONCE(1);
                break;
@@ -979,6 +1013,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
                /* must be handled by mac80211/driver, has no APIs */
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_NAN:
                /* cannot happen, has no netdev */
                break;
        case NL80211_IFTYPE_AP_VLAN:
index 5555e3c13ae9db52b93993eef1810741e91f24bc..08d2e948c9ad306a0ed40531efc4103877b9f4ae 100644 (file)
@@ -249,8 +249,8 @@ struct cfg80211_event {
 };
 
 struct cfg80211_cached_keys {
-       struct key_params params[4];
-       u8 data[4][WLAN_KEY_LEN_WEP104];
+       struct key_params params[CFG80211_MAX_WEP_KEYS];
+       u8 data[CFG80211_MAX_WEP_KEYS][WLAN_KEY_LEN_WEP104];
        int def;
 };
 
@@ -488,6 +488,9 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev,
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev);
 
+void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
+                      struct wireless_dev *wdev);
+
 #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
 
 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
index eafdfa5798ae4a734368e4a0d3d2ac41e0f357a6..364f900a3dc4d2810c90fa3f613d1b9a08cb5e71 100644 (file)
@@ -43,7 +43,8 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
        cfg80211_hold_bss(bss_from_pub(bss));
        wdev->current_bss = bss_from_pub(bss);
 
-       cfg80211_upload_connect_keys(wdev);
+       if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP))
+               cfg80211_upload_connect_keys(wdev);
 
        nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
                                GFP_KERNEL);
@@ -296,7 +297,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
                ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
                if (!ck)
                        return -ENOMEM;
-               for (i = 0; i < 4; i++)
+               for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
                        ck->params[i].key = ck->data[i];
        }
        err = __cfg80211_join_ibss(rdev, wdev->netdev,
index d6abb0704db5cbcde3afb5d26c4917cd6bd96b5b..cbb48e26a8715b8964333bf9b69c52010289bafb 100644 (file)
@@ -634,6 +634,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
                         * fall through, P2P device only supports
                         * public action frames
                         */
+               case NL80211_IFTYPE_NAN:
                default:
                        err = -EOPNOTSUPP;
                        break;
index fd111e2b559d2fa6340e51e9d66be0edfbbc74cd..c510810f0b7c14909fc3b83ec31ce6c4ac45dfc2 100644 (file)
@@ -56,6 +56,7 @@ enum nl80211_multicast_groups {
        NL80211_MCGRP_REGULATORY,
        NL80211_MCGRP_MLME,
        NL80211_MCGRP_VENDOR,
+       NL80211_MCGRP_NAN,
        NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
 };
 
@@ -65,6 +66,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
        [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG },
        [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME },
        [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR },
+       [NL80211_MCGRP_NAN] = { .name = NL80211_MULTICAST_GROUP_NAN },
 #ifdef CONFIG_NL80211_TESTMODE
        [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE }
 #endif
@@ -409,6 +411,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
                .len = VHT_MUMIMO_GROUPS_DATA_LEN
        },
        [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = { .len = ETH_ALEN },
+       [NL80211_ATTR_NAN_MASTER_PREF] = { .type = NLA_U8 },
+       [NL80211_ATTR_NAN_DUAL] = { .type = NLA_U8 },
+       [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -502,6 +507,39 @@ nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = {
        },
 };
 
+/* policy for NAN function attributes */
+static const struct nla_policy
+nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = {
+       [NL80211_NAN_FUNC_TYPE] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_SERVICE_ID] = { .type = NLA_BINARY,
+                                   .len = NL80211_NAN_FUNC_SERVICE_ID_LEN },
+       [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = { .len = ETH_ALEN },
+       [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG },
+       [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 },
+       [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY,
+                       .len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN },
+       [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED },
+       [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8 },
+       [NL80211_NAN_FUNC_TERM_REASON] = { .type = NLA_U8 },
+};
+
+/* policy for Service Response Filter attributes */
+static const struct nla_policy
+nl80211_nan_srf_policy[NL80211_NAN_SRF_ATTR_MAX + 1] = {
+       [NL80211_NAN_SRF_INCLUDE] = { .type = NLA_FLAG },
+       [NL80211_NAN_SRF_BF] = { .type = NLA_BINARY,
+                                .len =  NL80211_NAN_FUNC_SRF_MAX_LEN },
+       [NL80211_NAN_SRF_BF_IDX] = { .type = NLA_U8 },
+       [NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED },
+};
+
 static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
                                     struct netlink_callback *cb,
                                     struct cfg80211_registered_device **rdev,
@@ -934,6 +972,7 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
        case NL80211_IFTYPE_UNSPECIFIED:
        case NL80211_IFTYPE_OCB:
        case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_P2P_DEVICE:
        case NL80211_IFTYPE_WDS:
        case NUM_NL80211_IFTYPES:
@@ -2819,7 +2858,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
            !(rdev->wiphy.interface_modes & (1 << type)))
                return -EOPNOTSUPP;
 
-       if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+       if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
             rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
            info->attrs[NL80211_ATTR_MAC]) {
                nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
@@ -2875,9 +2914,10 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                       wdev->mesh_id_up_len);
                wdev_unlock(wdev);
                break;
+       case NL80211_IFTYPE_NAN:
        case NL80211_IFTYPE_P2P_DEVICE:
                /*
-                * P2P Device doesn't have a netdev, so doesn't go
+                * P2P Device and NAN do not have a netdev, so don't go
                 * through the netdev notifier and must be added here
                 */
                mutex_init(&wdev->mtx);
@@ -3340,6 +3380,291 @@ static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
+static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
+                          u8 *rates, u8 rates_len)
+{
+       u8 i;
+       u32 mask = 0;
+
+       for (i = 0; i < rates_len; i++) {
+               int rate = (rates[i] & 0x7f) * 5;
+               int ridx;
+
+               for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
+                       struct ieee80211_rate *srate =
+                               &sband->bitrates[ridx];
+                       if (rate == srate->bitrate) {
+                               mask |= 1 << ridx;
+                               break;
+                       }
+               }
+               if (ridx == sband->n_bitrates)
+                       return 0; /* rate not found */
+       }
+
+       return mask;
+}
+
+static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
+                              u8 *rates, u8 rates_len,
+                              u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
+{
+       u8 i;
+
+       memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
+
+       for (i = 0; i < rates_len; i++) {
+               int ridx, rbit;
+
+               ridx = rates[i] / 8;
+               rbit = BIT(rates[i] % 8);
+
+               /* check validity */
+               if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
+                       return false;
+
+               /* check availability */
+               if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
+                       mcs[ridx] |= rbit;
+               else
+                       return false;
+       }
+
+       return true;
+}
+
+static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
+{
+       u16 mcs_mask = 0;
+
+       switch (vht_mcs_map) {
+       case IEEE80211_VHT_MCS_NOT_SUPPORTED:
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_7:
+               mcs_mask = 0x00FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_8:
+               mcs_mask = 0x01FF;
+               break;
+       case IEEE80211_VHT_MCS_SUPPORT_0_9:
+               mcs_mask = 0x03FF;
+               break;
+       default:
+               break;
+       }
+
+       return mcs_mask;
+}
+
+static void vht_build_mcs_mask(u16 vht_mcs_map,
+                              u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
+{
+       u8 nss;
+
+       for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
+               vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
+               vht_mcs_map >>= 2;
+       }
+}
+
+static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
+                            struct nl80211_txrate_vht *txrate,
+                            u16 mcs[NL80211_VHT_NSS_MAX])
+{
+       u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+       u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
+       u8 i;
+
+       if (!sband->vht_cap.vht_supported)
+               return false;
+
+       memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
+
+       /* Build vht_mcs_mask from VHT capabilities */
+       vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
+
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
+                       mcs[i] = txrate->mcs[i];
+               else
+                       return false;
+       }
+
+       return true;
+}
+
+static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
+       [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
+                                   .len = NL80211_MAX_SUPP_RATES },
+       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
+                               .len = NL80211_MAX_SUPP_HT_RATES },
+       [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
+       [NL80211_TXRATE_GI] = { .type = NLA_U8 },
+};
+
+static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
+                                        struct cfg80211_bitrate_mask *mask)
+{
+       struct nlattr *tb[NL80211_TXRATE_MAX + 1];
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int rem, i;
+       struct nlattr *tx_rates;
+       struct ieee80211_supported_band *sband;
+       u16 vht_tx_mcs_map;
+
+       memset(mask, 0, sizeof(*mask));
+       /* Default to all rates enabled */
+       for (i = 0; i < NUM_NL80211_BANDS; i++) {
+               sband = rdev->wiphy.bands[i];
+
+               if (!sband)
+                       continue;
+
+               mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
+               memcpy(mask->control[i].ht_mcs,
+                      sband->ht_cap.mcs.rx_mask,
+                      sizeof(mask->control[i].ht_mcs));
+
+               if (!sband->vht_cap.vht_supported)
+                       continue;
+
+               vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+               vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
+       }
+
+       /* if no rates are given set it back to the defaults */
+       if (!info->attrs[NL80211_ATTR_TX_RATES])
+               goto out;
+
+       /* The nested attribute uses enum nl80211_band as the index. This maps
+        * directly to the enum nl80211_band values used in cfg80211.
+        */
+       BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
+       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
+               enum nl80211_band band = nla_type(tx_rates);
+               int err;
+
+               if (band < 0 || band >= NUM_NL80211_BANDS)
+                       return -EINVAL;
+               sband = rdev->wiphy.bands[band];
+               if (sband == NULL)
+                       return -EINVAL;
+               err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
+                               nla_len(tx_rates), nl80211_txattr_policy);
+               if (err)
+                       return err;
+               if (tb[NL80211_TXRATE_LEGACY]) {
+                       mask->control[band].legacy = rateset_to_mask(
+                               sband,
+                               nla_data(tb[NL80211_TXRATE_LEGACY]),
+                               nla_len(tb[NL80211_TXRATE_LEGACY]));
+                       if ((mask->control[band].legacy == 0) &&
+                           nla_len(tb[NL80211_TXRATE_LEGACY]))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_HT]) {
+                       if (!ht_rateset_to_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_HT]),
+                                       nla_len(tb[NL80211_TXRATE_HT]),
+                                       mask->control[band].ht_mcs))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_VHT]) {
+                       if (!vht_set_mcs_mask(
+                                       sband,
+                                       nla_data(tb[NL80211_TXRATE_VHT]),
+                                       mask->control[band].vht_mcs))
+                               return -EINVAL;
+               }
+               if (tb[NL80211_TXRATE_GI]) {
+                       mask->control[band].gi =
+                               nla_get_u8(tb[NL80211_TXRATE_GI]);
+                       if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
+                               return -EINVAL;
+               }
+
+               if (mask->control[band].legacy == 0) {
+                       /* don't allow empty legacy rates if HT or VHT
+                        * are not even supported.
+                        */
+                       if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
+                             rdev->wiphy.bands[band]->vht_cap.vht_supported))
+                               return -EINVAL;
+
+                       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+                               if (mask->control[band].ht_mcs[i])
+                                       goto out;
+
+                       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                               if (mask->control[band].vht_mcs[i])
+                                       goto out;
+
+                       /* legacy and mcs rates may not be both empty */
+                       return -EINVAL;
+               }
+       }
+
+out:
+       return 0;
+}
+
+static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
+                                  enum nl80211_band band,
+                                  struct cfg80211_bitrate_mask *beacon_rate)
+{
+       u32 count_ht, count_vht, i;
+       u32 rate = beacon_rate->control[band].legacy;
+
+       /* Allow only one rate */
+       if (hweight32(rate) > 1)
+               return -EINVAL;
+
+       count_ht = 0;
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) {
+                       return -EINVAL;
+               } else if (beacon_rate->control[band].ht_mcs[i]) {
+                       count_ht++;
+                       if (count_ht > 1)
+                               return -EINVAL;
+               }
+               if (count_ht && rate)
+                       return -EINVAL;
+       }
+
+       count_vht = 0;
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) {
+                       return -EINVAL;
+               } else if (beacon_rate->control[band].vht_mcs[i]) {
+                       count_vht++;
+                       if (count_vht > 1)
+                               return -EINVAL;
+               }
+               if (count_vht && rate)
+                       return -EINVAL;
+       }
+
+       if ((count_ht && count_vht) || (!rate && !count_ht && !count_vht))
+               return -EINVAL;
+
+       if (rate &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+               return -EINVAL;
+       if (count_ht &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_HT))
+               return -EINVAL;
+       if (count_vht &&
+           !wiphy_ext_feature_isset(&rdev->wiphy,
+                                    NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+               return -EINVAL;
+
+       return 0;
+}
+
 static int nl80211_parse_beacon(struct nlattr *attrs[],
                                struct cfg80211_beacon_data *bcn)
 {
@@ -3569,6 +3894,17 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                                           wdev->iftype))
                return -EINVAL;
 
+       if (info->attrs[NL80211_ATTR_TX_RATES]) {
+               err = nl80211_parse_tx_bitrate_mask(info, &params.beacon_rate);
+               if (err)
+                       return err;
+
+               err = validate_beacon_tx_rate(rdev, params.chandef.chan->band,
+                                             &params.beacon_rate);
+               if (err)
+                       return err;
+       }
+
        if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
                params.smps_mode =
                        nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
@@ -6138,6 +6474,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        wiphy = &rdev->wiphy;
 
+       if (wdev->iftype == NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
 
@@ -8641,238 +8980,21 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
        return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
 }
 
-static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
-                          u8 *rates, u8 rates_len)
-{
-       u8 i;
-       u32 mask = 0;
-
-       for (i = 0; i < rates_len; i++) {
-               int rate = (rates[i] & 0x7f) * 5;
-               int ridx;
-
-               for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
-                       struct ieee80211_rate *srate =
-                               &sband->bitrates[ridx];
-                       if (rate == srate->bitrate) {
-                               mask |= 1 << ridx;
-                               break;
-                       }
-               }
-               if (ridx == sband->n_bitrates)
-                       return 0; /* rate not found */
-       }
-
-       return mask;
-}
-
-static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
-                              u8 *rates, u8 rates_len,
-                              u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
-{
-       u8 i;
-
-       memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
-
-       for (i = 0; i < rates_len; i++) {
-               int ridx, rbit;
-
-               ridx = rates[i] / 8;
-               rbit = BIT(rates[i] % 8);
-
-               /* check validity */
-               if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
-                       return false;
-
-               /* check availability */
-               if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
-                       mcs[ridx] |= rbit;
-               else
-                       return false;
-       }
-
-       return true;
-}
-
-static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
-{
-       u16 mcs_mask = 0;
-
-       switch (vht_mcs_map) {
-       case IEEE80211_VHT_MCS_NOT_SUPPORTED:
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_7:
-               mcs_mask = 0x00FF;
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_8:
-               mcs_mask = 0x01FF;
-               break;
-       case IEEE80211_VHT_MCS_SUPPORT_0_9:
-               mcs_mask = 0x03FF;
-               break;
-       default:
-               break;
-       }
-
-       return mcs_mask;
-}
-
-static void vht_build_mcs_mask(u16 vht_mcs_map,
-                              u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
-{
-       u8 nss;
-
-       for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
-               vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
-               vht_mcs_map >>= 2;
-       }
-}
-
-static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
-                            struct nl80211_txrate_vht *txrate,
-                            u16 mcs[NL80211_VHT_NSS_MAX])
-{
-       u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
-       u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
-       u8 i;
-
-       if (!sband->vht_cap.vht_supported)
-               return false;
-
-       memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
-
-       /* Build vht_mcs_mask from VHT capabilities */
-       vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
-
-       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
-               if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
-                       mcs[i] = txrate->mcs[i];
-               else
-                       return false;
-       }
-
-       return true;
-}
-
-static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
-       [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
-                                   .len = NL80211_MAX_SUPP_RATES },
-       [NL80211_TXRATE_HT] = { .type = NLA_BINARY,
-                               .len = NL80211_MAX_SUPP_HT_RATES },
-       [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
-       [NL80211_TXRATE_GI] = { .type = NLA_U8 },
-};
-
 static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
                                       struct genl_info *info)
 {
-       struct nlattr *tb[NL80211_TXRATE_MAX + 1];
-       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct cfg80211_bitrate_mask mask;
-       int rem, i;
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
-       struct nlattr *tx_rates;
-       struct ieee80211_supported_band *sband;
-       u16 vht_tx_mcs_map;
+       int err;
 
        if (!rdev->ops->set_bitrate_mask)
                return -EOPNOTSUPP;
 
-       memset(&mask, 0, sizeof(mask));
-       /* Default to all rates enabled */
-       for (i = 0; i < NUM_NL80211_BANDS; i++) {
-               sband = rdev->wiphy.bands[i];
-
-               if (!sband)
-                       continue;
-
-               mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
-               memcpy(mask.control[i].ht_mcs,
-                      sband->ht_cap.mcs.rx_mask,
-                      sizeof(mask.control[i].ht_mcs));
-
-               if (!sband->vht_cap.vht_supported)
-                       continue;
-
-               vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
-               vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
-       }
-
-       /* if no rates are given set it back to the defaults */
-       if (!info->attrs[NL80211_ATTR_TX_RATES])
-               goto out;
-
-       /*
-        * The nested attribute uses enum nl80211_band as the index. This maps
-        * directly to the enum nl80211_band values used in cfg80211.
-        */
-       BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
-       nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
-               enum nl80211_band band = nla_type(tx_rates);
-               int err;
-
-               if (band < 0 || band >= NUM_NL80211_BANDS)
-                       return -EINVAL;
-               sband = rdev->wiphy.bands[band];
-               if (sband == NULL)
-                       return -EINVAL;
-               err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
-                               nla_len(tx_rates), nl80211_txattr_policy);
-               if (err)
-                       return err;
-               if (tb[NL80211_TXRATE_LEGACY]) {
-                       mask.control[band].legacy = rateset_to_mask(
-                               sband,
-                               nla_data(tb[NL80211_TXRATE_LEGACY]),
-                               nla_len(tb[NL80211_TXRATE_LEGACY]));
-                       if ((mask.control[band].legacy == 0) &&
-                           nla_len(tb[NL80211_TXRATE_LEGACY]))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_HT]) {
-                       if (!ht_rateset_to_mask(
-                                       sband,
-                                       nla_data(tb[NL80211_TXRATE_HT]),
-                                       nla_len(tb[NL80211_TXRATE_HT]),
-                                       mask.control[band].ht_mcs))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_VHT]) {
-                       if (!vht_set_mcs_mask(
-                                       sband,
-                                       nla_data(tb[NL80211_TXRATE_VHT]),
-                                       mask.control[band].vht_mcs))
-                               return -EINVAL;
-               }
-               if (tb[NL80211_TXRATE_GI]) {
-                       mask.control[band].gi =
-                               nla_get_u8(tb[NL80211_TXRATE_GI]);
-                       if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
-                               return -EINVAL;
-               }
-
-               if (mask.control[band].legacy == 0) {
-                       /* don't allow empty legacy rates if HT or VHT
-                        * are not even supported.
-                        */
-                       if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
-                             rdev->wiphy.bands[band]->vht_cap.vht_supported))
-                               return -EINVAL;
-
-                       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
-                               if (mask.control[band].ht_mcs[i])
-                                       goto out;
-
-                       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
-                               if (mask.control[band].vht_mcs[i])
-                                       goto out;
-
-                       /* legacy and mcs rates may not be both empty */
-                       return -EINVAL;
-               }
-       }
+       err = nl80211_parse_tx_bitrate_mask(info, &mask);
+       if (err)
+               return err;
 
-out:
        return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
 }
 
@@ -8898,6 +9020,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -8943,6 +9066,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_P2P_GO:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -9059,6 +9183,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in
        case NL80211_IFTYPE_P2P_GO:
        case NL80211_IFTYPE_P2P_DEVICE:
                break;
+       case NL80211_IFTYPE_NAN:
        default:
                return -EOPNOTSUPP;
        }
@@ -9340,6 +9465,17 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
                        return err;
        }
 
+       if (info->attrs[NL80211_ATTR_TX_RATES]) {
+               err = nl80211_parse_tx_bitrate_mask(info, &setup.beacon_rate);
+               if (err)
+                       return err;
+
+               err = validate_beacon_tx_rate(rdev, setup.chandef.chan->band,
+                                             &setup.beacon_rate);
+               if (err)
+                       return err;
+       }
+
        return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
 }
 
@@ -10414,38 +10550,581 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
        return 0;
 }
 
-static int nl80211_get_protocol_features(struct sk_buff *skb,
-                                        struct genl_info *info)
+static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info)
 {
-       void *hdr;
-       struct sk_buff *msg;
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct cfg80211_nan_conf conf = {};
+       int err;
 
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
-               return -ENOMEM;
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
 
-       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
-                            NL80211_CMD_GET_PROTOCOL_FEATURES);
-       if (!hdr)
-               goto nla_put_failure;
+       if (wdev->nan_started)
+               return -EEXIST;
 
-       if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
-                       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
-               goto nla_put_failure;
+       if (rfkill_blocked(rdev->rfkill))
+               return -ERFKILL;
 
-       genlmsg_end(msg, hdr);
-       return genlmsg_reply(msg, info);
+       if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF])
+               return -EINVAL;
 
- nla_put_failure:
-       kfree_skb(msg);
-       return -ENOBUFS;
+       if (!info->attrs[NL80211_ATTR_NAN_DUAL])
+               return -EINVAL;
+
+       conf.master_pref =
+               nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+       if (!conf.master_pref)
+               return -EINVAL;
+
+       conf.dual = nla_get_u8(info->attrs[NL80211_ATTR_NAN_DUAL]);
+
+       err = rdev_start_nan(rdev, wdev, &conf);
+       if (err)
+               return err;
+
+       wdev->nan_started = true;
+       rdev->opencount++;
+
+       return 0;
 }
 
-static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct cfg80211_update_ft_ies_params ft_params;
-       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = info->user_ptr[1];
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       cfg80211_stop_nan(rdev, wdev);
+
+       return 0;
+}
+
+static int validate_nan_filter(struct nlattr *filter_attr)
+{
+       struct nlattr *attr;
+       int len = 0, n_entries = 0, rem;
+
+       nla_for_each_nested(attr, filter_attr, rem) {
+               len += nla_len(attr);
+               n_entries++;
+       }
+
+       if (len >= U8_MAX)
+               return -EINVAL;
+
+       return n_entries;
+}
+
+static int handle_nan_filter(struct nlattr *attr_filter,
+                            struct cfg80211_nan_func *func,
+                            bool tx)
+{
+       struct nlattr *attr;
+       int n_entries, rem, i;
+       struct cfg80211_nan_func_filter *filter;
+
+       n_entries = validate_nan_filter(attr_filter);
+       if (n_entries < 0)
+               return n_entries;
+
+       BUILD_BUG_ON(sizeof(*func->rx_filters) != sizeof(*func->tx_filters));
+
+       filter = kcalloc(n_entries, sizeof(*func->rx_filters), GFP_KERNEL);
+       if (!filter)
+               return -ENOMEM;
+
+       i = 0;
+       nla_for_each_nested(attr, attr_filter, rem) {
+               filter[i].filter = kmemdup(nla_data(attr), nla_len(attr),
+                                          GFP_KERNEL);
+               filter[i].len = nla_len(attr);
+               i++;
+       }
+       if (tx) {
+               func->num_tx_filters = n_entries;
+               func->tx_filters = filter;
+       } else {
+               func->num_rx_filters = n_entries;
+               func->rx_filters = filter;
+       }
+
+       return 0;
+}
+
+static int nl80211_nan_add_func(struct sk_buff *skb,
+                               struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct nlattr *tb[NUM_NL80211_NAN_FUNC_ATTR], *func_attr;
+       struct cfg80211_nan_func *func;
+       struct sk_buff *msg = NULL;
+       void *hdr = NULL;
+       int err = 0;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (!info->attrs[NL80211_ATTR_NAN_FUNC])
+               return -EINVAL;
+
+       if (wdev->owner_nlportid &&
+           wdev->owner_nlportid != info->snd_portid)
+               return -ENOTCONN;
+
+       err = nla_parse(tb, NL80211_NAN_FUNC_ATTR_MAX,
+                       nla_data(info->attrs[NL80211_ATTR_NAN_FUNC]),
+                       nla_len(info->attrs[NL80211_ATTR_NAN_FUNC]),
+                       nl80211_nan_func_policy);
+       if (err)
+               return err;
+
+       func = kzalloc(sizeof(*func), GFP_KERNEL);
+       if (!func)
+               return -ENOMEM;
+
+       func->cookie = wdev->wiphy->cookie_counter++;
+
+       if (!tb[NL80211_NAN_FUNC_TYPE] ||
+           nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]) > NL80211_NAN_FUNC_MAX_TYPE) {
+               err = -EINVAL;
+               goto out;
+       }
+
+
+       func->type = nla_get_u8(tb[NL80211_NAN_FUNC_TYPE]);
+
+       if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       memcpy(func->service_id, nla_data(tb[NL80211_NAN_FUNC_SERVICE_ID]),
+              sizeof(func->service_id));
+
+       func->close_range =
+               nla_get_flag(tb[NL80211_NAN_FUNC_CLOSE_RANGE]);
+
+       if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) {
+               func->serv_spec_info_len =
+                       nla_len(tb[NL80211_NAN_FUNC_SERVICE_INFO]);
+               func->serv_spec_info =
+                       kmemdup(nla_data(tb[NL80211_NAN_FUNC_SERVICE_INFO]),
+                               func->serv_spec_info_len,
+                               GFP_KERNEL);
+               if (!func->serv_spec_info) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       if (tb[NL80211_NAN_FUNC_TTL])
+               func->ttl = nla_get_u32(tb[NL80211_NAN_FUNC_TTL]);
+
+       switch (func->type) {
+       case NL80211_NAN_FUNC_PUBLISH:
+               if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               func->publish_type =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_PUBLISH_TYPE]);
+               func->publish_bcast =
+                       nla_get_flag(tb[NL80211_NAN_FUNC_PUBLISH_BCAST]);
+
+               if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) &&
+                       func->publish_bcast) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               break;
+       case NL80211_NAN_FUNC_SUBSCRIBE:
+               func->subscribe_active =
+                       nla_get_flag(tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]);
+               break;
+       case NL80211_NAN_FUNC_FOLLOW_UP:
+               if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] ||
+                   !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               func->followup_id =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]);
+               func->followup_reqid =
+                       nla_get_u8(tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]);
+               memcpy(func->followup_dest.addr,
+                      nla_data(tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]),
+                      sizeof(func->followup_dest.addr));
+               if (func->ttl) {
+                       err = -EINVAL;
+                       goto out;
+               }
+               break;
+       default:
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (tb[NL80211_NAN_FUNC_SRF]) {
+               struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR];
+
+               err = nla_parse(srf_tb, NL80211_NAN_SRF_ATTR_MAX,
+                               nla_data(tb[NL80211_NAN_FUNC_SRF]),
+                               nla_len(tb[NL80211_NAN_FUNC_SRF]), NULL);
+               if (err)
+                       goto out;
+
+               func->srf_include =
+                       nla_get_flag(srf_tb[NL80211_NAN_SRF_INCLUDE]);
+
+               if (srf_tb[NL80211_NAN_SRF_BF]) {
+                       if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] ||
+                           !srf_tb[NL80211_NAN_SRF_BF_IDX]) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       func->srf_bf_len =
+                               nla_len(srf_tb[NL80211_NAN_SRF_BF]);
+                       func->srf_bf =
+                               kmemdup(nla_data(srf_tb[NL80211_NAN_SRF_BF]),
+                                       func->srf_bf_len, GFP_KERNEL);
+                       if (!func->srf_bf) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+
+                       func->srf_bf_idx =
+                               nla_get_u8(srf_tb[NL80211_NAN_SRF_BF_IDX]);
+               } else {
+                       struct nlattr *attr, *mac_attr =
+                               srf_tb[NL80211_NAN_SRF_MAC_ADDRS];
+                       int n_entries, rem, i = 0;
+
+                       if (!mac_attr) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       n_entries = validate_acl_mac_addrs(mac_attr);
+                       if (n_entries <= 0) {
+                               err = -EINVAL;
+                               goto out;
+                       }
+
+                       func->srf_num_macs = n_entries;
+                       func->srf_macs =
+                               kzalloc(sizeof(*func->srf_macs) * n_entries,
+                                       GFP_KERNEL);
+                       if (!func->srf_macs) {
+                               err = -ENOMEM;
+                               goto out;
+                       }
+
+                       nla_for_each_nested(attr, mac_attr, rem)
+                               memcpy(func->srf_macs[i++].addr, nla_data(attr),
+                                      sizeof(*func->srf_macs));
+               }
+       }
+
+       if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) {
+               err = handle_nan_filter(tb[NL80211_NAN_FUNC_TX_MATCH_FILTER],
+                                       func, true);
+               if (err)
+                       goto out;
+       }
+
+       if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) {
+               err = handle_nan_filter(tb[NL80211_NAN_FUNC_RX_MATCH_FILTER],
+                                       func, false);
+               if (err)
+                       goto out;
+       }
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_ADD_NAN_FUNCTION);
+       /* This can't really happen - we just allocated 4KB */
+       if (WARN_ON(!hdr)) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = rdev_add_nan_func(rdev, wdev, func);
+out:
+       if (err < 0) {
+               cfg80211_free_nan_func(func);
+               nlmsg_free(msg);
+               return err;
+       }
+
+       /* propagate the instance id and cookie to userspace  */
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, func->cookie,
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+       if (!func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID,
+                      func->instance_id))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, func_attr);
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
+
+static int nl80211_nan_del_func(struct sk_buff *skb,
+                              struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       u64 cookie;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (!info->attrs[NL80211_ATTR_COOKIE])
+               return -EINVAL;
+
+       if (wdev->owner_nlportid &&
+           wdev->owner_nlportid != info->snd_portid)
+               return -ENOTCONN;
+
+       cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
+
+       rdev_del_nan_func(rdev, wdev, cookie);
+
+       return 0;
+}
+
+static int nl80211_nan_change_config(struct sk_buff *skb,
+                                    struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct wireless_dev *wdev = info->user_ptr[1];
+       struct cfg80211_nan_conf conf = {};
+       u32 changed = 0;
+
+       if (wdev->iftype != NL80211_IFTYPE_NAN)
+               return -EOPNOTSUPP;
+
+       if (!wdev->nan_started)
+               return -ENOTCONN;
+
+       if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) {
+               conf.master_pref =
+                       nla_get_u8(info->attrs[NL80211_ATTR_NAN_MASTER_PREF]);
+               if (conf.master_pref <= 1 || conf.master_pref == 255)
+                       return -EINVAL;
+
+               changed |= CFG80211_NAN_CONF_CHANGED_PREF;
+       }
+
+       if (info->attrs[NL80211_ATTR_NAN_DUAL]) {
+               conf.dual = nla_get_u8(info->attrs[NL80211_ATTR_NAN_DUAL]);
+               changed |= CFG80211_NAN_CONF_CHANGED_DUAL;
+       }
+
+       if (!changed)
+               return -EINVAL;
+
+       return rdev_nan_change_conf(rdev, wdev, &conf, changed);
+}
+
+void cfg80211_nan_match(struct wireless_dev *wdev,
+                       struct cfg80211_nan_match_params *match, gfp_t gfp)
+{
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct nlattr *match_attr, *local_func_attr, *peer_func_attr;
+       struct sk_buff *msg;
+       void *hdr;
+
+       if (WARN_ON(!match->inst_id || !match->peer_inst_id || !match->addr))
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NAN_MATCH);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+                                        wdev->netdev->ifindex)) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, match->cookie,
+                             NL80211_ATTR_PAD) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, match->addr))
+               goto nla_put_failure;
+
+       match_attr = nla_nest_start(msg, NL80211_ATTR_NAN_MATCH);
+       if (!match_attr)
+               goto nla_put_failure;
+
+       local_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_LOCAL);
+       if (!local_func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->inst_id))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, local_func_attr);
+
+       peer_func_attr = nla_nest_start(msg, NL80211_NAN_MATCH_FUNC_PEER);
+       if (!peer_func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_TYPE, match->type) ||
+           nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, match->peer_inst_id))
+               goto nla_put_failure;
+
+       if (match->info && match->info_len &&
+           nla_put(msg, NL80211_NAN_FUNC_SERVICE_INFO, match->info_len,
+                   match->info))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, peer_func_attr);
+       nla_nest_end(msg, match_attr);
+       genlmsg_end(msg, hdr);
+
+       if (!wdev->owner_nlportid)
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       msg, 0, NL80211_MCGRP_NAN, gfp);
+       else
+               genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+                               wdev->owner_nlportid);
+
+       return;
+
+nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_match);
+
+void cfg80211_nan_func_terminated(struct wireless_dev *wdev,
+                                 u8 inst_id,
+                                 enum nl80211_nan_func_term_reason reason,
+                                 u64 cookie, gfp_t gfp)
+{
+       struct wiphy *wiphy = wdev->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct sk_buff *msg;
+       struct nlattr *func_attr;
+       void *hdr;
+
+       if (WARN_ON(!inst_id))
+               return;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_NAN_FUNCTION);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
+                                        wdev->netdev->ifindex)) ||
+           nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
+                             NL80211_ATTR_PAD))
+               goto nla_put_failure;
+
+       func_attr = nla_nest_start(msg, NL80211_ATTR_NAN_FUNC);
+       if (!func_attr)
+               goto nla_put_failure;
+
+       if (nla_put_u8(msg, NL80211_NAN_FUNC_INSTANCE_ID, inst_id) ||
+           nla_put_u8(msg, NL80211_NAN_FUNC_TERM_REASON, reason))
+               goto nla_put_failure;
+
+       nla_nest_end(msg, func_attr);
+       genlmsg_end(msg, hdr);
+
+       if (!wdev->owner_nlportid)
+               genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+                                       msg, 0, NL80211_MCGRP_NAN, gfp);
+       else
+               genlmsg_unicast(wiphy_net(&rdev->wiphy), msg,
+                               wdev->owner_nlportid);
+
+       return;
+
+nla_put_failure:
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_nan_func_terminated);
+
+static int nl80211_get_protocol_features(struct sk_buff *skb,
+                                        struct genl_info *info)
+{
+       void *hdr;
+       struct sk_buff *msg;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+                            NL80211_CMD_GET_PROTOCOL_FEATURES);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
+                       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return genlmsg_reply(msg, info);
+
+ nla_put_failure:
+       kfree_skb(msg);
+       return -ENOBUFS;
+}
+
+static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_update_ft_ies_params ft_params;
+       struct net_device *dev = info->user_ptr[1];
 
        if (!rdev->ops->update_ft_ies)
                return -EOPNOTSUPP;
@@ -11115,7 +11794,14 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
                        dev_hold(dev);
                } else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
-                       if (!wdev->p2p_started) {
+                       if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE &&
+                           !wdev->p2p_started) {
+                               if (rtnl)
+                                       rtnl_unlock();
+                               return -ENETDOWN;
+                       }
+                       if (wdev->iftype == NL80211_IFTYPE_NAN &&
+                           !wdev->nan_started) {
                                if (rtnl)
                                        rtnl_unlock();
                                return -ENETDOWN;
@@ -11748,6 +12434,46 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_START_NAN,
+               .doit = nl80211_start_nan,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_STOP_NAN,
+               .doit = nl80211_stop_nan,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_ADD_NAN_FUNCTION,
+               .doit = nl80211_nan_add_func,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_DEL_NAN_FUNCTION,
+               .doit = nl80211_nan_del_func,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
+               .doit = nl80211_nan_change_config,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
        {
                .cmd = NL80211_CMD_SET_MCAST_RATE,
                .doit = nl80211_set_mcast_rate,
index 85ff30bee2b921c3a132f212d3d4898b2da26660..11cf83c8ad4f5008abb3f1cf8f59058027f2bb2d 100644 (file)
@@ -887,6 +887,64 @@ static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
        trace_rdev_return_void(&rdev->wiphy);
 }
 
+static inline int rdev_start_nan(struct cfg80211_registered_device *rdev,
+                                struct wireless_dev *wdev,
+                                struct cfg80211_nan_conf *conf)
+{
+       int ret;
+
+       trace_rdev_start_nan(&rdev->wiphy, wdev, conf);
+       ret = rdev->ops->start_nan(&rdev->wiphy, wdev, conf);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
+static inline void rdev_stop_nan(struct cfg80211_registered_device *rdev,
+                                struct wireless_dev *wdev)
+{
+       trace_rdev_stop_nan(&rdev->wiphy, wdev);
+       rdev->ops->stop_nan(&rdev->wiphy, wdev);
+       trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_add_nan_func(struct cfg80211_registered_device *rdev,
+                 struct wireless_dev *wdev,
+                 struct cfg80211_nan_func *nan_func)
+{
+       int ret;
+
+       trace_rdev_add_nan_func(&rdev->wiphy, wdev, nan_func);
+       ret = rdev->ops->add_nan_func(&rdev->wiphy, wdev, nan_func);
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
+static inline void rdev_del_nan_func(struct cfg80211_registered_device *rdev,
+                                   struct wireless_dev *wdev, u64 cookie)
+{
+       trace_rdev_del_nan_func(&rdev->wiphy, wdev, cookie);
+       rdev->ops->del_nan_func(&rdev->wiphy, wdev, cookie);
+       trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_nan_change_conf(struct cfg80211_registered_device *rdev,
+                    struct wireless_dev *wdev,
+                    struct cfg80211_nan_conf *conf, u32 changes)
+{
+       int ret;
+
+       trace_rdev_nan_change_conf(&rdev->wiphy, wdev, conf, changes);
+       if (rdev->ops->nan_change_conf)
+               ret = rdev->ops->nan_change_conf(&rdev->wiphy, wdev, conf,
+                                                changes);
+       else
+               ret = -ENOTSUPP;
+       trace_rdev_return_int(&rdev->wiphy, ret);
+       return ret;
+}
+
 static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
                                   struct net_device *dev,
                                   struct cfg80211_acl_data *params)
index c08a3b57dca1268b0f57d5c20cd1e47a16676043..a77db333927ec1bc8ab6efa4ee26b9fc7e711f5e 100644 (file)
@@ -726,7 +726,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
 
        wdev->current_bss = bss_from_pub(bss);
 
-       cfg80211_upload_connect_keys(wdev);
+       if (!(wdev->wiphy->flags & WIPHY_FLAG_HAS_STATIC_WEP))
+               cfg80211_upload_connect_keys(wdev);
 
        rcu_read_lock();
        country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
@@ -1043,6 +1044,9 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
                                connect->crypto.ciphers_pairwise[0] = cipher;
                        }
                }
+
+               connect->crypto.wep_keys = connkeys->params;
+               connect->crypto.wep_tx_key = connkeys->def;
        } else {
                if (WARN_ON(connkeys))
                        return -EINVAL;
index 72b5255cefe209c7506694926121d400f0596a2d..a3d0a91b1e0957766bd77d8cc9a712ce335598ec 100644 (file)
@@ -1889,6 +1889,96 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
        TP_ARGS(wiphy, wdev)
 );
 
+TRACE_EVENT(rdev_start_nan,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+                struct cfg80211_nan_conf *conf),
+       TP_ARGS(wiphy, wdev, conf),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u8, master_pref)
+               __field(u8, dual);
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->master_pref = conf->master_pref;
+               __entry->dual = conf->dual;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+                 ", master preference: %u, dual: %d",
+                 WIPHY_PR_ARG, WDEV_PR_ARG, __entry->master_pref,
+                 __entry->dual)
+);
+
+TRACE_EVENT(rdev_nan_change_conf,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+                struct cfg80211_nan_conf *conf, u32 changes),
+       TP_ARGS(wiphy, wdev, conf, changes),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u8, master_pref)
+               __field(u8, dual);
+               __field(u32, changes);
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->master_pref = conf->master_pref;
+               __entry->dual = conf->dual;
+               __entry->changes = changes;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT
+                 ", master preference: %u, dual: %d, changes: %x",
+                 WIPHY_PR_ARG, WDEV_PR_ARG, __entry->master_pref,
+                 __entry->dual, __entry->changes)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_nan,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+       TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_add_nan_func,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+                const struct cfg80211_nan_func *func),
+       TP_ARGS(wiphy, wdev, func),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u8, func_type)
+               __field(u64, cookie)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->func_type = func->type;
+               __entry->cookie = func->cookie
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type=%u, cookie=%llu",
+                 WIPHY_PR_ARG, WDEV_PR_ARG, __entry->func_type,
+                 __entry->cookie)
+);
+
+TRACE_EVENT(rdev_del_nan_func,
+       TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+                u64 cookie),
+       TP_ARGS(wiphy, wdev, cookie),
+       TP_STRUCT__entry(
+               WIPHY_ENTRY
+               WDEV_ENTRY
+               __field(u64, cookie)
+       ),
+       TP_fast_assign(
+               WIPHY_ASSIGN;
+               WDEV_ASSIGN;
+               __entry->cookie = cookie;
+       ),
+       TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie=%llu",
+                 WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
 TRACE_EVENT(rdev_set_mac_acl,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
                 struct cfg80211_acl_data *params),
index 9e6e2aaa7766147f9fe88b414193c7bedab2b525..8edce22d1b9316bf79e99411ffe3ce66e3ae0a6f 100644 (file)
@@ -912,7 +912,7 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
        if (!wdev->connect_keys)
                return;
 
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++) {
                if (!wdev->connect_keys->params[i].cipher)
                        continue;
                if (rdev_add_key(rdev, dev, i, false, NULL,
@@ -1008,8 +1008,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
        if (otype == NL80211_IFTYPE_AP_VLAN)
                return -EOPNOTSUPP;
 
-       /* cannot change into P2P device type */
-       if (ntype == NL80211_IFTYPE_P2P_DEVICE)
+       /* cannot change into P2P device or NAN */
+       if (ntype == NL80211_IFTYPE_P2P_DEVICE ||
+           ntype == NL80211_IFTYPE_NAN)
                return -EOPNOTSUPP;
 
        if (!rdev->ops->change_virtual_intf ||
@@ -1088,6 +1089,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                        /* not happening */
                        break;
                case NL80211_IFTYPE_P2P_DEVICE:
+               case NL80211_IFTYPE_NAN:
                        WARN_ON(1);
                        break;
                }
@@ -1760,6 +1762,28 @@ int cfg80211_get_station(struct net_device *dev, const u8 *mac_addr,
 }
 EXPORT_SYMBOL(cfg80211_get_station);
 
+void cfg80211_free_nan_func(struct cfg80211_nan_func *f)
+{
+       int i;
+
+       if (!f)
+               return;
+
+       kfree(f->serv_spec_info);
+       kfree(f->srf_bf);
+       kfree(f->srf_macs);
+       for (i = 0; i < f->num_rx_filters; i++)
+               kfree(f->rx_filters[i].filter);
+
+       for (i = 0; i < f->num_tx_filters; i++)
+               kfree(f->tx_filters[i].filter);
+
+       kfree(f->rx_filters);
+       kfree(f->tx_filters);
+       kfree(f);
+}
+EXPORT_SYMBOL(cfg80211_free_nan_func);
+
 /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
 /* Ethernet-II snap header (RFC1042 for most EtherTypes) */
 const unsigned char rfc1042_header[] __aligned(2) =
index 7b97d43b27e124d961a3eb5892fa2d334fead1ce..a220156cf2175a459727fcf67e1291036dca21ae 100644 (file)
@@ -406,12 +406,16 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
        if (pairwise && !addr)
                return -EINVAL;
 
+       /*
+        * In many cases we won't actually need this, but it's better
+        * to do it first in case the allocation fails. Don't use wext.
+        */
        if (!wdev->wext.keys) {
                wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
                                          GFP_KERNEL);
                if (!wdev->wext.keys)
                        return -ENOMEM;
-               for (i = 0; i < 4; i++)
+               for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
                        wdev->wext.keys->params[i].key =
                                wdev->wext.keys->data[i];
        }
@@ -493,7 +497,13 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
        if (err)
                return err;
 
-       if (!addr) {
+       /*
+        * We only need to store WEP keys, since they're the only keys that
+        * can be be set before a connection is established and persist after
+        * disconnecting.
+        */
+       if (!addr && (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+                     params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
                wdev->wext.keys->params[idx] = *params;
                memcpy(wdev->wext.keys->data[idx],
                        params->key, params->key_len);
index 88f1f6931ab8e030bce4ce224ee5d6484798274e..995163830a61f582c11b9234b370bb2c3766f7f0 100644 (file)
@@ -46,7 +46,7 @@ int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
                ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
                if (!ck)
                        return -ENOMEM;
-               for (i = 0; i < 4; i++)
+               for (i = 0; i < CFG80211_MAX_WEP_KEYS; i++)
                        ck->params[i].key = ck->data[i];
        }