Merge tag 'mac80211-next-for-davem-2016-10-04' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Wed, 5 Oct 2016 00:14:07 +0000 (20:14 -0400)
committerDavid S. Miller <davem@davemloft.net>
Wed, 5 Oct 2016 00:14:07 +0000 (20:14 -0400)
Johannes Berg says:

====================
This time around, we have
 * Neighbor Awareness Networking (NAN) APIs
 * a fix for a previous patch that caused memory corruption
   in wireless extensions key settings
 * beacon rate configuration for AP and mesh
 * memory limits for mac80211's internal TXQs
 * a (fairly involved) fix for the TXQ vs. crypto problems
 * direct cfg80211 driver API for WEP keys

This also pulls in net-next to fix the merge conflicts, see
the merge commit for more details.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
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 bd26cc6..fe78f02 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 268b490..6d8521a 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 163f3ed..4e6131c 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 5296100..a810dfc 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 2206941..56368e9 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 e29ff57..fd6541f 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 74142d0..e75cbf6 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 8ca62b6..f56e2f4 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 5d35c0f..bcec124 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 c701b64..bb886e7 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 fe35a1c..09f77e4 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 e496dee..34c2add 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 b0abddc..638ec07 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 ac053a9..1075ac2 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 64bc22a..faca22c 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 55a9c5b..c3f610b 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 f7cf342..6175db3 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 011880d..78e9ecb 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 77e4c53..92a47af 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 1ff08be..1c56abc 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 @@ out:
        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 b6865d8..545c79a 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 0f50622..5497d02 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 4911cd9..8201e6d 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 5555e3c..08d2e94 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 eafdfa5..364f900 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 d6abb07..cbb48e2 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 fd111e2..c510810 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 85ff30b..11cf83c 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 c08a3b5..a77db33 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 72b5255..a3d0a91 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 9e6e2aa..8edce22 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 7b97d43..a220156 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 88f1f69..9951638 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];
        }