|
@@ -1028,6 +1028,7 @@ mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer)
|
|
|
}
|
|
|
|
|
|
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,
|
|
@@ -1072,6 +1073,8 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer)
|
|
|
|
|
|
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);
|
|
@@ -1085,6 +1088,8 @@ mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *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;
|
|
|
}
|
|
@@ -1152,3 +1157,231 @@ void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv)
|
|
|
|
|
|
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);
|
|
|
+ }
|
|
|
+}
|