struct mwifiex_wmm_desc wmm;
atomic_t wmm_tx_pending[IEEE80211_NUM_ACS];
struct list_head sta_list;
- /* spin lock for associated station list */
+ /* spin lock for associated station/TDLS peers list */
spinlock_t sta_list_spinlock;
+ struct list_head auto_tdls_list;
+ /* spin lock for auto TDLS peer list */
+ spinlock_t auto_tdls_lock;
struct list_head tx_ba_stream_tbl_ptr;
/* spin lock for tx_ba_stream_tbl_ptr queue */
spinlock_t tx_ba_stream_tbl_lock;
bool hs2_enabled;
struct station_parameters *sta_params;
struct sk_buff_head tdls_txq;
+ u8 check_tdls_tx;
+ struct timer_list auto_tdls_timer;
+ bool auto_tdls_timer_active;
};
enum mwifiex_ba_status {
struct mwifiex_tdls_capab tdls_cap;
};
+struct mwifiex_auto_tdls_peer {
+ struct list_head list;
+ u8 mac_addr[ETH_ALEN];
+ u8 tdls_status;
+ int rssi;
+ long rssi_jiffies;
+ u8 failure_count;
+ u8 do_discover;
+ u8 do_setup;
+};
+
struct mwifiex_if_ops {
int (*init_if) (struct mwifiex_adapter *);
void (*cleanup_if) (struct mwifiex_adapter *);
struct mwifiex_chan_stats *chan_stats;
u32 num_in_chan_stats;
int survey_idx;
+ bool auto_tdls;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
u32 pri_chan, u8 chan_bw);
int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter);
+int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb);
+void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv);
+void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
+ const u8 *mac, u8 link_status);
+void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
+ u8 *mac, s8 snr, s8 nflr);
+void mwifiex_check_auto_tdls(unsigned long context);
+void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
+void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
+void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+
#ifdef CONFIG_DEBUG_FS
void mwifiex_debugfs_init(void);
void mwifiex_debugfs_remove(void);
}
mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+ mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP);
memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN);
tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK;
return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER,
memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq));
mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE);
+ mwifiex_auto_tdls_update_peer_status(priv, peer,
+ TDLS_SETUP_COMPLETE);
} else {
dev_dbg(priv->adapter->dev,
"tdls: enable link %pM failed\n", peer);
mwifiex_del_sta_entry(priv, peer);
}
mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN);
+ mwifiex_auto_tdls_update_peer_status(priv, peer,
+ TDLS_NOT_SETUP);
return -1;
}
mwifiex_del_all_sta_list(priv);
}
+
+int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+ u8 mac[ETH_ALEN];
+
+ ether_addr_copy(mac, skb->data);
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) {
+ if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH &&
+ peer->tdls_status == TDLS_NOT_SETUP &&
+ (peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT)) {
+ peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ dev_dbg(priv->adapter->dev,
+ "setup TDLS link, peer=%pM rssi=%d\n",
+ peer->mac_addr, peer->rssi);
+
+ cfg80211_tdls_oper_request(priv->netdev,
+ peer->mac_addr,
+ NL80211_TDLS_SETUP,
+ 0, GFP_ATOMIC);
+ peer->do_setup = false;
+ priv->check_tdls_tx = false;
+ } else if (peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT &&
+ peer->do_discover) {
+ mwifiex_send_tdls_data_frame(priv,
+ peer->mac_addr,
+ WLAN_TDLS_DISCOVERY_REQUEST,
+ 1, 0, NULL, 0);
+ peer->do_discover = false;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+
+ return 0;
+}
+
+void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv)
+{
+ struct mwifiex_auto_tdls_peer *peer, *tmp_node;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) {
+ list_del(&peer->list);
+ kfree(peer);
+ }
+
+ INIT_LIST_HEAD(&priv->auto_tdls_list);
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+ priv->check_tdls_tx = false;
+}
+
+void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac)
+{
+ struct mwifiex_auto_tdls_peer *tdls_peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) {
+ tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ tdls_peer->rssi_jiffies = jiffies;
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+ return;
+ }
+ }
+
+ /* create new TDLS peer */
+ tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC);
+ if (tdls_peer) {
+ ether_addr_copy(tdls_peer->mac_addr, mac);
+ tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS;
+ tdls_peer->rssi_jiffies = jiffies;
+ INIT_LIST_HEAD(&tdls_peer->list);
+ list_add_tail(&tdls_peer->list, &priv->auto_tdls_list);
+ dev_dbg(priv->adapter->dev, "Add auto TDLS peer= %pM to list\n",
+ mac);
+ }
+
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv,
+ const u8 *mac, u8 link_status)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) {
+ if ((link_status == TDLS_NOT_SETUP) &&
+ (peer->tdls_status == TDLS_SETUP_INPROGRESS))
+ peer->failure_count++;
+ else if (link_status == TDLS_SETUP_COMPLETE)
+ peer->failure_count = 0;
+
+ peer->tdls_status = link_status;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv,
+ u8 *mac, s8 snr, s8 nflr)
+{
+ struct mwifiex_auto_tdls_peer *peer;
+ unsigned long flags;
+
+ if (!priv->adapter->auto_tdls)
+ return;
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(peer, &priv->auto_tdls_list, list) {
+ if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) {
+ peer->rssi = nflr - snr;
+ peer->rssi_jiffies = jiffies;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+}
+
+void mwifiex_check_auto_tdls(unsigned long context)
+{
+ struct mwifiex_private *priv = (struct mwifiex_private *)context;
+ struct mwifiex_auto_tdls_peer *tdls_peer;
+ unsigned long flags;
+ u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED;
+
+ if (WARN_ON_ONCE(!priv || !priv->adapter)) {
+ pr_err("mwifiex: %s: adapter or private structure is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (unlikely(!priv->adapter->auto_tdls))
+ return;
+
+ if (!priv->auto_tdls_timer_active) {
+ dev_dbg(priv->adapter->dev,
+ "auto TDLS timer inactive; return");
+ return;
+ }
+
+ priv->check_tdls_tx = false;
+
+ if (list_empty(&priv->auto_tdls_list)) {
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies +
+ msecs_to_jiffies(MWIFIEX_TIMER_10S));
+ return;
+ }
+
+ spin_lock_irqsave(&priv->auto_tdls_lock, flags);
+ list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) {
+ if ((jiffies - tdls_peer->rssi_jiffies) >
+ (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) {
+ tdls_peer->rssi = 0;
+ tdls_peer->do_discover = true;
+ priv->check_tdls_tx = true;
+ }
+
+ if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) ||
+ !tdls_peer->rssi) &&
+ tdls_peer->tdls_status == TDLS_SETUP_COMPLETE) {
+ tdls_peer->tdls_status = TDLS_LINK_TEARDOWN;
+ dev_dbg(priv->adapter->dev,
+ "teardown TDLS link,peer=%pM rssi=%d\n",
+ tdls_peer->mac_addr, -tdls_peer->rssi);
+ tdls_peer->do_discover = true;
+ priv->check_tdls_tx = true;
+ cfg80211_tdls_oper_request(priv->netdev,
+ tdls_peer->mac_addr,
+ NL80211_TDLS_TEARDOWN,
+ reason, GFP_ATOMIC);
+ } else if (tdls_peer->rssi &&
+ tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH &&
+ tdls_peer->tdls_status == TDLS_NOT_SETUP &&
+ tdls_peer->failure_count <
+ MWIFIEX_TDLS_MAX_FAIL_COUNT) {
+ priv->check_tdls_tx = true;
+ tdls_peer->do_setup = true;
+ dev_dbg(priv->adapter->dev,
+ "check TDLS with peer=%pM rssi=%d\n",
+ tdls_peer->mac_addr, -tdls_peer->rssi);
+ }
+ }
+ spin_unlock_irqrestore(&priv->auto_tdls_lock, flags);
+
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
+}
+
+void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv)
+{
+ init_timer(&priv->auto_tdls_timer);
+ priv->auto_tdls_timer.function = mwifiex_check_auto_tdls;
+ priv->auto_tdls_timer.data = (unsigned long)priv;
+ priv->auto_tdls_timer_active = true;
+ mod_timer(&priv->auto_tdls_timer,
+ jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S));
+}
+
+void mwifiex_clean_auto_tdls(struct mwifiex_private *priv)
+{
+ if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) &&
+ priv->adapter->auto_tdls &&
+ priv->bss_type == MWIFIEX_BSS_TYPE_STA) {
+ priv->auto_tdls_timer_active = false;
+ del_timer(&priv->auto_tdls_timer);
+ mwifiex_flush_auto_tdls_list(priv);
+ }
+}