Commit 187e52cc authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Luciano Coelho

wlcore: ROC on AP channel before auth reply

Start a ROC on the AP channel beforing sending the authentication reply
to a connecting STA. This ROC is held up to 1 second via a timer. If the
station is authorized and added by mac80211, the ROC is extended until
the station is fully authorized.
We make sure not to ROC twice when several stations are connecting in
parallel and to only release the ROC when both the pending-reply timer
and the STA-state callbacks do not require it.
Signed-off-by: default avatarArik Nemtsov <arik@wizery.com>
Signed-off-by: default avatarEliad Peller <eliad@wizery.com>
Signed-off-by: default avatarLuciano Coelho <luciano.coelho@intel.com>
parent 772eb433
......@@ -2008,6 +2008,47 @@ static void wlcore_connection_loss_work(struct work_struct *work)
mutex_unlock(&wl->mutex);
}
static void wlcore_pending_auth_complete_work(struct work_struct *work)
{
struct delayed_work *dwork;
struct wl1271 *wl;
struct wl12xx_vif *wlvif;
unsigned long time_spare;
int ret;
dwork = container_of(work, struct delayed_work, work);
wlvif = container_of(dwork, struct wl12xx_vif,
pending_auth_complete_work);
wl = wlvif->wl;
mutex_lock(&wl->mutex);
if (unlikely(wl->state != WLCORE_STATE_ON))
goto out;
/*
* Make sure a second really passed since the last auth reply. Maybe
* a second auth reply arrived while we were stuck on the mutex.
* Check for a little less than the timeout to protect from scheduler
* irregularities.
*/
time_spare = jiffies +
msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
if (!time_after(time_spare, wlvif->pending_auth_reply_time))
goto out;
ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;
/* cancel the ROC if active */
wlcore_update_inconn_sta(wl, wlvif, NULL, false);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
}
static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
{
u8 policy = find_first_zero_bit(wl->rate_policies_map,
......@@ -2159,6 +2200,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
wlcore_channel_switch_work);
INIT_DELAYED_WORK(&wlvif->connection_loss_work,
wlcore_connection_loss_work);
INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
wlcore_pending_auth_complete_work);
INIT_LIST_HEAD(&wlvif->list);
setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
......@@ -2590,6 +2633,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
cancel_work_sync(&wlvif->rx_streaming_disable_work);
cancel_delayed_work_sync(&wlvif->connection_loss_work);
cancel_delayed_work_sync(&wlvif->channel_switch_work);
cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
mutex_lock(&wl->mutex);
}
......@@ -3969,6 +4013,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
}
} else {
if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
/*
* AP might be in ROC in case we have just
* sent auth reply. handle it.
*/
if (test_bit(wlvif->role_id, wl->roc_map))
wl12xx_croc(wl, wlvif->role_id);
ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
if (ret < 0)
goto out;
......@@ -4656,28 +4707,48 @@ static void wlcore_roc_if_possible(struct wl1271 *wl,
wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
}
static void wlcore_update_inconn_sta(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct wl1271_station *wl_sta,
bool in_connection)
/*
* when wl_sta is NULL, we treat this call as if coming from a
* pending auth reply.
* wl->mutex must be taken and the FW must be awake when the call
* takes place.
*/
void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct wl1271_station *wl_sta, bool in_conn)
{
if (in_connection) {
if (WARN_ON(wl_sta->in_connection))
if (in_conn) {
if (WARN_ON(wl_sta && wl_sta->in_connection))
return;
wl_sta->in_connection = true;
if (!wlvif->inconn_count++)
if (!wlvif->ap_pending_auth_reply &&
!wlvif->inconn_count)
wlcore_roc_if_possible(wl, wlvif);
if (wl_sta) {
wl_sta->in_connection = true;
wlvif->inconn_count++;
} else {
wlvif->ap_pending_auth_reply = true;
}
} else {
if (!wl_sta->in_connection)
if (wl_sta && !wl_sta->in_connection)
return;
if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
return;
if (WARN_ON(wl_sta && !wlvif->inconn_count))
return;
if (wl_sta) {
wl_sta->in_connection = false;
wlvif->inconn_count--;
if (WARN_ON(wlvif->inconn_count < 0))
return;
} else {
wlvif->ap_pending_auth_reply = false;
}
if (!wlvif->inconn_count)
if (test_bit(wlvif->role_id, wl->roc_map))
if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
test_bit(wlvif->role_id, wl->roc_map))
wl12xx_croc(wl, wlvif->role_id);
}
}
......
......@@ -86,19 +86,34 @@ void wl1271_free_tx_id(struct wl1271 *wl, int id)
EXPORT_SYMBOL(wl1271_free_tx_id);
static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)(skb->data +
sizeof(struct wl1271_tx_hw_descr));
if (!ieee80211_is_auth(hdr->frame_control))
return;
/*
* add the station to the known list before transmitting the
* authentication response. this way it won't get de-authed by FW
* when transmitting too soon.
*/
hdr = (struct ieee80211_hdr *)(skb->data +
sizeof(struct wl1271_tx_hw_descr));
if (ieee80211_is_auth(hdr->frame_control))
wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
/*
* ROC for 1 second on the AP channel for completing the connection.
* Note the ROC will be continued by the update_sta_state callbacks
* once the station reaches the associated state.
*/
wlcore_update_inconn_sta(wl, wlvif, NULL, true);
wlvif->pending_auth_reply_time = jiffies;
cancel_delayed_work(&wlvif->pending_auth_complete_work);
ieee80211_queue_delayed_work(wl->hw,
&wlvif->pending_auth_complete_work,
msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT));
}
static void wl1271_tx_regulate_link(struct wl1271 *wl,
......@@ -404,7 +419,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
wl1271_tx_ap_update_inconnection_sta(wl, skb);
wl1271_tx_ap_update_inconnection_sta(wl, wlvif, skb);
wl1271_tx_regulate_link(wl, wlvif, hlid);
}
......
......@@ -56,6 +56,9 @@
/* Used for management frames and dummy packets */
#define WL1271_TID_MGMT 7
/* stop a ROC for pending authentication reply after this time (ms) */
#define WLCORE_PEND_AUTH_ROC_TIMEOUT 1000
struct wl127x_tx_mem {
/*
* Number of extra memory blocks to allocate for this packet
......
......@@ -481,6 +481,8 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf);
void wlcore_regdomain_config(struct wl1271 *wl);
void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct wl1271_station *wl_sta, bool in_conn);
static inline void
wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
......
......@@ -456,6 +456,15 @@ struct wl12xx_vif {
*/
int hw_queue_base;
/* do we have a pending auth reply? (and ROC) */
bool ap_pending_auth_reply;
/* time when we sent the pending auth reply */
unsigned long pending_auth_reply_time;
/* work for canceling ROC after pending auth reply */
struct delayed_work pending_auth_complete_work;
/*
* This struct must be last!
* data that has to be saved acrossed reconfigs (e.g. recovery)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment