提交 1ebf8b42 编写于 作者: D David S. Miller

Merge tag 'mac80211-next-for-davem-2016-10-04' of...

Merge tag 'mac80211-next-for-davem-2016-10-04' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next

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: NDavid S. Miller <davem@davemloft.net>
......@@ -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);
......
......@@ -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;
};
......
......@@ -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)
......
......@@ -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 */
......@@ -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"
/**
......@@ -838,6 +839,41 @@
* 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 */
......@@ -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,
};
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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)
{
......
......@@ -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 */
......@@ -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) ||
......
......@@ -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,
......
......@@ -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",
......
......@@ -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,
......
......@@ -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;
}
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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.
*/
......
......@@ -796,36 +796,6 @@ static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid)
return ret;
}
static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
struct ieee80211_vif *vif,
struct ieee80211_sta *pubsta,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_txq *txq = NULL;
if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
return NULL;
if (!ieee80211_is_data(hdr->frame_control))
return NULL;
if (pubsta) {
u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
txq = pubsta->txq[tid];
} else if (vif) {
txq = vif->txq;
}
if (!txq)
return NULL;
return to_txq_info(txq);
}
static ieee80211_tx_result debug_noinline
ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
{
......@@ -883,9 +853,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
tx->sta->tx_stats.msdu[tid]++;
if (!ieee80211_get_txq(tx->local, info->control.vif, &tx->sta->sta,
tx->skb))
hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid);
return TX_CONTINUE;
}
......@@ -1274,6 +1242,36 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
return TX_CONTINUE;
}
static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local,
struct ieee80211_vif *vif,
struct ieee80211_sta *pubsta,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_txq *txq = NULL;
if ((info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) ||
(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE))
return NULL;
if (!ieee80211_is_data(hdr->frame_control))
return NULL;
if (pubsta) {
u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
txq = pubsta->txq[tid];
} else if (vif) {
txq = vif->txq;
}
if (!txq)
return NULL;
return to_txq_info(txq);
}
static void ieee80211_set_skb_enqueue_time(struct sk_buff *skb)
{
IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
......@@ -1405,6 +1403,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
fq_flow_init(&txqi->def_flow);
codel_vars_init(&txqi->def_cvars);
codel_stats_init(&txqi->cstats);
__skb_queue_head_init(&txqi->frags);
txqi->txq.vif = &sdata->vif;
......@@ -1427,6 +1426,7 @@ void ieee80211_txq_purge(struct ieee80211_local *local,
struct fq_tin *tin = &txqi->tin;
fq_tin_reset(fq, tin, fq_skb_free_func);
ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
}
int ieee80211_txq_setup_flows(struct ieee80211_local *local)
......@@ -1434,6 +1434,8 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
struct fq *fq = &local->fq;
int ret;
int i;
bool supp_vht = false;
enum nl80211_band band;
if (!local->ops->wake_tx_queue)
return 0;
......@@ -1442,6 +1444,23 @@ int ieee80211_txq_setup_flows(struct ieee80211_local *local)
if (ret)
return ret;
/*
* If the hardware doesn't support VHT, it is safe to limit the maximum
* queue size. 4 Mbytes is 64 max-size aggregates in 802.11n.
*/
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
}
if (!supp_vht)
fq->memory_limit = 4 << 20; /* 4 Mbytes */
codel_params_init(&local->cparams);
local->cparams.interval = MS2TIME(100);
local->cparams.target = MS2TIME(20);
......@@ -1477,54 +1496,46 @@ void ieee80211_txq_teardown_flows(struct ieee80211_local *local)
spin_unlock_bh(&fq->lock);
}
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
static bool ieee80211_queue_skb(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = container_of(txq, struct txq_info, txq);
struct ieee80211_hdr *hdr;
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;
struct ieee80211_vif *vif;
struct txq_info *txqi;
struct ieee80211_sta *pubsta;
spin_lock_bh(&fq->lock);
if (!local->ops->wake_tx_queue ||
sdata->vif.type == NL80211_IFTYPE_MONITOR)
return false;
if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
goto out;
if (sta && sta->uploaded)
pubsta = &sta->sta;
else
pubsta = NULL;
skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
if (!skb)
goto out;
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
ieee80211_set_skb_vif(skb, txqi);
vif = &sdata->vif;
txqi = ieee80211_get_txq(local, vif, pubsta, skb);
hdr = (struct ieee80211_hdr *)skb->data;
if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
struct sta_info *sta = container_of(txq->sta, struct sta_info,
sta);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!txqi)
return false;
hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid);
if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
info->flags |= IEEE80211_TX_CTL_AMPDU;
else
info->flags &= ~IEEE80211_TX_CTL_AMPDU;
}
info->control.vif = vif;
out:
spin_lock_bh(&fq->lock);
ieee80211_txq_enqueue(local, txqi, skb);
spin_unlock_bh(&fq->lock);
if (skb && skb_has_frag_list(skb) &&
!ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
if (skb_linearize(skb)) {
ieee80211_free_txskb(&local->hw, skb);
return NULL;
}
}
drv_wake_tx_queue(local, txqi);
return skb;
return true;
}
EXPORT_SYMBOL(ieee80211_tx_dequeue);
static bool ieee80211_tx_frags(struct ieee80211_local *local,
struct ieee80211_vif *vif,
......@@ -1533,9 +1544,7 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
bool txpending)
{
struct ieee80211_tx_control control = {};
struct fq *fq = &local->fq;
struct sk_buff *skb, *tmp;
struct txq_info *txqi;
unsigned long flags;
skb_queue_walk_safe(skbs, skb, tmp) {
......@@ -1550,21 +1559,6 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local,
}
#endif
txqi = ieee80211_get_txq(local, vif, sta, skb);
if (txqi) {
info->control.vif = vif;
__skb_unlink(skb, skbs);
spin_lock_bh(&fq->lock);
ieee80211_txq_enqueue(local, txqi, skb);
spin_unlock_bh(&fq->lock);
drv_wake_tx_queue(local, txqi);
continue;
}
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
if (local->queue_stop_reasons[q] ||
(!txpending && !skb_queue_empty(&local->pending[q]))) {
......@@ -1685,10 +1679,13 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
/*
* Invoke TX handlers, return 0 on success and non-zero if the
* frame was dropped or queued.
*
* The handlers are split into an early and late part. The latter is everything
* that can be sensitive to reordering, and will be deferred to after packets
* are dequeued from the intermediate queues (when they are enabled).
*/
static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
ieee80211_tx_result res = TX_DROP;
#define CALL_TXH(txh) \
......@@ -1706,6 +1703,31 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
CALL_TXH(ieee80211_tx_h_rate_ctrl);
txh_done:
if (unlikely(res == TX_DROP)) {
I802_DEBUG_INC(tx->local->tx_handlers_drop);
if (tx->skb)
ieee80211_free_txskb(&tx->local->hw, tx->skb);
else
ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
return -1;
} else if (unlikely(res == TX_QUEUED)) {
I802_DEBUG_INC(tx->local->tx_handlers_queued);
return -1;
}
return 0;
}
/*
* Late handlers can be called while the sta lock is held. Handlers that can
* cause packets to be generated will cause deadlock!
*/
static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
ieee80211_tx_result res = TX_CONTINUE;
if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION)) {
__skb_queue_tail(&tx->skbs, tx->skb);
tx->skb = NULL;
......@@ -1738,6 +1760,15 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
return 0;
}
static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
{
int r = invoke_tx_handlers_early(tx);
if (r)
return r;
return invoke_tx_handlers_late(tx);
}
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct sk_buff *skb,
int band, struct ieee80211_sta **sta)
......@@ -1812,7 +1843,13 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
info->hw_queue =
sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
if (!invoke_tx_handlers(&tx))
if (invoke_tx_handlers_early(&tx))
return false;
if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
return true;
if (!invoke_tx_handlers_late(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
......@@ -3156,8 +3193,71 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
return ret;
}
/*
* Can be called while the sta lock is held. Anything that can cause packets to
* be generated will cause deadlock!
*/
static void ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, u8 pn_offs,
struct ieee80211_key *key,
struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
u8 tid = IEEE80211_NUM_TIDS;
if (key)
info->control.hw_key = &key->conf;
ieee80211_tx_stats(skb->dev, skb->len);
if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
*ieee80211_get_qos_ctl(hdr) = tid;
hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
} else {
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
sdata->sequence_number += 0x10;
}
if (skb_shinfo(skb)->gso_size)
sta->tx_stats.msdu[tid] +=
DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
else
sta->tx_stats.msdu[tid]++;
info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
/* statistics normally done by ieee80211_tx_h_stats (but that
* has to consider fragmentation, so is more complex)
*/
sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
if (pn_offs) {
u64 pn;
u8 *crypto_hdr = skb->data + pn_offs;
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
pn = atomic64_inc_return(&key->conf.tx_pn);
crypto_hdr[0] = pn;
crypto_hdr[1] = pn >> 8;
crypto_hdr[4] = pn >> 16;
crypto_hdr[5] = pn >> 24;
crypto_hdr[6] = pn >> 32;
crypto_hdr[7] = pn >> 40;
break;
}
}
}
static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
struct net_device *dev, struct sta_info *sta,
struct sta_info *sta,
struct ieee80211_fast_tx *fast_tx,
struct sk_buff *skb)
{
......@@ -3208,8 +3308,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
return true;
}
ieee80211_tx_stats(dev, skb->len + extra_head);
if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
return true;
......@@ -3238,24 +3336,7 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
IEEE80211_TX_CTL_DONTFRAG |
(tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
*ieee80211_get_qos_ctl(hdr) = tid;
if (!ieee80211_get_txq(local, &sdata->vif, &sta->sta, skb))
hdr->seq_ctrl = ieee80211_tx_next_seq(sta, tid);
} else {
info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ;
hdr->seq_ctrl = cpu_to_le16(sdata->sequence_number);
sdata->sequence_number += 0x10;
}
if (skb_shinfo(skb)->gso_size)
sta->tx_stats.msdu[tid] +=
DIV_ROUND_UP(skb->len, skb_shinfo(skb)->gso_size);
else
sta->tx_stats.msdu[tid]++;
info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT;
__skb_queue_head_init(&tx.skbs);
......@@ -3265,9 +3346,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
tx.sta = sta;
tx.key = fast_tx->key;
if (fast_tx->key)
info->control.hw_key = &fast_tx->key->conf;
if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
tx.skb = skb;
r = ieee80211_tx_h_rate_ctrl(&tx);
......@@ -3281,31 +3359,11 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
}
}
/* statistics normally done by ieee80211_tx_h_stats (but that
* has to consider fragmentation, so is more complex)
*/
sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len;
sta->tx_stats.packets[skb_get_queue_mapping(skb)]++;
if (fast_tx->pn_offs) {
u64 pn;
u8 *crypto_hdr = skb->data + fast_tx->pn_offs;
if (ieee80211_queue_skb(local, sdata, sta, skb))
return true;
switch (fast_tx->key->conf.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_CCMP_256:
case WLAN_CIPHER_SUITE_GCMP:
case WLAN_CIPHER_SUITE_GCMP_256:
pn = atomic64_inc_return(&fast_tx->key->conf.tx_pn);
crypto_hdr[0] = pn;
crypto_hdr[1] = pn >> 8;
crypto_hdr[4] = pn >> 16;
crypto_hdr[5] = pn >> 24;
crypto_hdr[6] = pn >> 32;
crypto_hdr[7] = pn >> 40;
break;
}
}
ieee80211_xmit_fast_finish(sdata, sta, fast_tx->pn_offs,
fast_tx->key, skb);
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
......@@ -3316,6 +3374,94 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
return true;
}
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct ieee80211_local *local = hw_to_local(hw);
struct txq_info *txqi = container_of(txq, struct txq_info, txq);
struct ieee80211_hdr *hdr;
struct sk_buff *skb = NULL;
struct fq *fq = &local->fq;
struct fq_tin *tin = &txqi->tin;
struct ieee80211_tx_info *info;
struct ieee80211_tx_data tx;
ieee80211_tx_result r;
spin_lock_bh(&fq->lock);
if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
goto out;
/* Make sure fragments stay together. */
skb = __skb_dequeue(&txqi->frags);
if (skb)
goto out;
begin:
skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
if (!skb)
goto out;
ieee80211_set_skb_vif(skb, txqi);
hdr = (struct ieee80211_hdr *)skb->data;
info = IEEE80211_SKB_CB(skb);
memset(&tx, 0, sizeof(tx));
__skb_queue_head_init(&tx.skbs);
tx.local = local;
tx.skb = skb;
tx.sdata = vif_to_sdata(info->control.vif);
if (txq->sta)
tx.sta = container_of(txq->sta, struct sta_info, sta);
/*
* The key can be removed while the packet was queued, so need to call
* this here to get the current key.
*/
r = ieee80211_tx_h_select_key(&tx);
if (r != TX_CONTINUE) {
ieee80211_free_txskb(&local->hw, skb);
goto begin;
}
if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
struct sta_info *sta = container_of(txq->sta, struct sta_info,
sta);
u8 pn_offs = 0;
if (tx.key &&
(tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
pn_offs = ieee80211_hdrlen(hdr->frame_control);
ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
tx.key, skb);
} else {
if (invoke_tx_handlers_late(&tx))
goto begin;
skb = __skb_dequeue(&tx.skbs);
if (!skb_queue_empty(&tx.skbs))
skb_queue_splice_tail(&tx.skbs, &txqi->frags);
}
if (skb && skb_has_frag_list(skb) &&
!ieee80211_hw_check(&local->hw, TX_FRAG_LIST)) {
if (skb_linearize(skb)) {
ieee80211_free_txskb(&local->hw, skb);
goto begin;
}
}
out:
spin_unlock_bh(&fq->lock);
return skb;
}
EXPORT_SYMBOL(ieee80211_tx_dequeue);
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev,
u32 info_flags)
......@@ -3340,7 +3486,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
fast_tx = rcu_dereference(sta->fast_tx);
if (fast_tx &&
ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
goto out;
}
......
......@@ -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);
......@@ -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:
......
......@@ -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:
......
......@@ -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
......
......@@ -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,
......
......@@ -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;
......
此差异已折叠。
......@@ -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)
......
......@@ -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;
......
......@@ -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),
......
......@@ -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) =
......
......@@ -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);
......
......@@ -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];
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册