Commit 6d9d32b8 authored by Johannes Berg's avatar Johannes Berg

iwlwifi: mvm: keep connection to AP after WoWLAN

Until now, after WoWLAN, we weren't able to keep the
connection to the AP because the firmware didn't give
us the right information. Since the firmware API has
been changed to include all the information we need,
change the driver to work with the new API (if it is
available) and program all the relevant information
in mac80211 to keep the connection.
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 04a4e1fd
...@@ -84,6 +84,8 @@ ...@@ -84,6 +84,8 @@
* @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
* @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
* from the probe request template. * from the probe request template.
* @IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping
* connection when going back to D0
* @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
* @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
* @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan.
...@@ -105,6 +107,7 @@ enum iwl_ucode_tlv_flag { ...@@ -105,6 +107,7 @@ enum iwl_ucode_tlv_flag {
IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10),
IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11),
IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12),
IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API = BIT(14),
IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15),
IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16),
IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17), IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17),
......
...@@ -863,6 +863,13 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -863,6 +863,13 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_nonqos_seq_query_cmd query_cmd = {
.get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET),
.mac_id_n_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color)),
};
struct iwl_host_cmd cmd = { struct iwl_host_cmd cmd = {
.id = NON_QOS_TX_COUNTER_CMD, .id = NON_QOS_TX_COUNTER_CMD,
.flags = CMD_SYNC | CMD_WANT_SKB, .flags = CMD_SYNC | CMD_WANT_SKB,
...@@ -870,21 +877,57 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, ...@@ -870,21 +877,57 @@ static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm,
int err; int err;
u32 size; u32 size;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
cmd.data[0] = &query_cmd;
cmd.len[0] = sizeof(query_cmd);
}
err = iwl_mvm_send_cmd(mvm, &cmd); err = iwl_mvm_send_cmd(mvm, &cmd);
if (err) if (err)
return err; return err;
size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
size -= sizeof(cmd.resp_pkt->hdr); size -= sizeof(cmd.resp_pkt->hdr);
if (size != sizeof(__le32)) if (size < sizeof(__le16)) {
err = -EINVAL; err = -EINVAL;
else } else {
err = le32_to_cpup((__le32 *)cmd.resp_pkt->data); err = le16_to_cpup((__le16 *)cmd.resp_pkt->data);
/* new API returns next, not last-used seqno */
if (mvm->fw->ucode_capa.flags &
IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
err -= 0x10;
}
iwl_free_resp(&cmd); iwl_free_resp(&cmd);
return err; return err;
} }
void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_nonqos_seq_query_cmd query_cmd = {
.get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET),
.mac_id_n_color =
cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
mvmvif->color)),
.value = cpu_to_le16(mvmvif->seqno),
};
/* return if called during restart, not resume from D3 */
if (!mvmvif->seqno_valid)
return;
mvmvif->seqno_valid = false;
if (!(mvm->fw->ucode_capa.flags &
IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API))
return;
if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC,
sizeof(query_cmd), &query_cmd))
IWL_ERR(mvm, "failed to set non-QoS seqno\n");
}
static int __iwl_mvm_suspend(struct ieee80211_hw *hw, static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan, struct cfg80211_wowlan *wowlan,
bool test) bool test)
...@@ -1203,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ...@@ -1203,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return __iwl_mvm_suspend(hw, wowlan, false); return __iwl_mvm_suspend(hw, wowlan, false);
} }
/* converted data from the different status responses */
struct iwl_wowlan_status_data {
u16 pattern_number;
u16 qos_seq_ctr[8];
u32 wakeup_reasons;
u32 wake_packet_length;
u32 wake_packet_bufsize;
const u8 *wake_packet;
};
static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct iwl_wowlan_status *status) struct iwl_wowlan_status_data *status)
{ {
struct sk_buff *pkt = NULL; struct sk_buff *pkt = NULL;
struct cfg80211_wowlan_wakeup wakeup = { struct cfg80211_wowlan_wakeup wakeup = {
.pattern_idx = -1, .pattern_idx = -1,
}; };
struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
u32 reasons = le32_to_cpu(status->wakeup_reasons); u32 reasons = status->wakeup_reasons;
if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
wakeup_report = NULL; wakeup_report = NULL;
...@@ -1224,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1224,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN)
wakeup.pattern_idx = wakeup.pattern_idx =
le16_to_cpu(status->pattern_number); status->pattern_number;
if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH))
...@@ -1252,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1252,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
wakeup.tcp_match = true; wakeup.tcp_match = true;
if (status->wake_packet_bufsize) { if (status->wake_packet_bufsize) {
int pktsize = le32_to_cpu(status->wake_packet_bufsize); int pktsize = status->wake_packet_bufsize;
int pktlen = le32_to_cpu(status->wake_packet_length); int pktlen = status->wake_packet_length;
const u8 *pktdata = status->wake_packet; const u8 *pktdata = status->wake_packet;
struct ieee80211_hdr *hdr = (void *)pktdata; struct ieee80211_hdr *hdr = (void *)pktdata;
int truncated = pktlen - pktsize; int truncated = pktlen - pktsize;
...@@ -1333,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1333,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
kfree_skb(pkt); kfree_skb(pkt);
} }
static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc,
struct ieee80211_key_seq *seq)
{
u64 pn;
pn = le64_to_cpu(sc->pn);
seq->ccmp.pn[0] = pn >> 40;
seq->ccmp.pn[1] = pn >> 32;
seq->ccmp.pn[2] = pn >> 24;
seq->ccmp.pn[3] = pn >> 16;
seq->ccmp.pn[4] = pn >> 8;
seq->ccmp.pn[5] = pn;
}
static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc,
struct ieee80211_key_seq *seq)
{
seq->tkip.iv32 = le32_to_cpu(sc->iv32);
seq->tkip.iv16 = le16_to_cpu(sc->iv16);
}
static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs,
struct ieee80211_key_conf *key)
{
int tid;
BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
for (tid = 0; tid < IWL_NUM_RSC; tid++) {
struct ieee80211_key_seq seq = {};
iwl_mvm_aes_sc_to_seq(&scs[tid], &seq);
ieee80211_set_key_rx_seq(key, tid, &seq);
}
}
static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs,
struct ieee80211_key_conf *key)
{
int tid;
BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS);
for (tid = 0; tid < IWL_NUM_RSC; tid++) {
struct ieee80211_key_seq seq = {};
iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq);
ieee80211_set_key_rx_seq(key, tid, &seq);
}
}
static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
struct iwl_wowlan_status_v6 *status)
{
union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key);
break;
case WLAN_CIPHER_SUITE_TKIP:
iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key);
break;
default:
WARN_ON(1);
}
}
struct iwl_mvm_d3_gtk_iter_data {
struct iwl_wowlan_status_v6 *status;
void *last_gtk;
u32 cipher;
bool find_phase, unhandled_cipher;
int num_keys;
};
static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key,
void *_data)
{
struct iwl_mvm_d3_gtk_iter_data *data = _data;
if (data->unhandled_cipher)
return;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
/* ignore WEP completely, nothing to do */
return;
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_TKIP:
/* we support these */
break;
default:
/* everything else (even CMAC for MFP) - disconnect from AP */
data->unhandled_cipher = true;
return;
}
data->num_keys++;
/*
* pairwise key - update sequence counters only;
* note that this assumes no TDLS sessions are active
*/
if (sta) {
struct ieee80211_key_seq seq = {};
union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc;
if (data->find_phase)
return;
switch (key->cipher) {
case WLAN_CIPHER_SUITE_CCMP:
iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq);
iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key);
break;
case WLAN_CIPHER_SUITE_TKIP:
iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq);
iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key);
break;
}
ieee80211_set_key_tx_seq(key, &seq);
/* that's it for this key */
return;
}
if (data->find_phase) {
data->last_gtk = key;
data->cipher = key->cipher;
return;
}
if (data->status->num_of_gtk_rekeys)
ieee80211_remove_key(key);
else if (data->last_gtk == key)
iwl_mvm_set_key_rx_seq(key, data->status);
}
static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_wowlan_status_v6 *status)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_d3_gtk_iter_data gtkdata = {
.status = status,
};
if (!status || !vif->bss_conf.bssid)
return false;
/* find last GTK that we used initially, if any */
gtkdata.find_phase = true;
ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_d3_update_gtks, &gtkdata);
/* not trying to keep connections with MFP/unhandled ciphers */
if (gtkdata.unhandled_cipher)
return false;
if (!gtkdata.num_keys)
return true;
if (!gtkdata.last_gtk)
return false;
/*
* invalidate all other GTKs that might still exist and update
* the one that we used
*/
gtkdata.find_phase = false;
ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_d3_update_gtks, &gtkdata);
if (status->num_of_gtk_rekeys) {
struct ieee80211_key_conf *key;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {
.conf.cipher = gtkdata.cipher,
.conf.keyidx = status->gtk.key_index,
};
switch (gtkdata.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
memcpy(conf.conf.key, status->gtk.decrypt_key,
WLAN_KEY_LEN_CCMP);
break;
case WLAN_CIPHER_SUITE_TKIP:
conf.conf.keylen = WLAN_KEY_LEN_TKIP;
memcpy(conf.conf.key, status->gtk.decrypt_key, 16);
/* leave TX MIC key zeroed, we don't use it anyway */
memcpy(conf.conf.key +
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
status->gtk.tkip_mic_key, 8);
break;
}
key = ieee80211_gtk_rekey_add(vif, &conf.conf);
if (IS_ERR(key))
return false;
iwl_mvm_set_key_rx_seq(key, status);
}
if (status->num_of_gtk_rekeys) {
__be64 replay_ctr =
cpu_to_be64(le64_to_cpu(status->replay_ctr));
ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
(void *)&replay_ctr, GFP_KERNEL);
}
mvmvif->seqno_valid = true;
/* +0x10 because the set API expects next-to-use, not last-used */
mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10;
return true;
}
/* releases the MVM mutex */ /* releases the MVM mutex */
static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
u32 base = mvm->error_event_table; u32 base = mvm->error_event_table;
...@@ -1347,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1347,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
.id = WOWLAN_GET_STATUSES, .id = WOWLAN_GET_STATUSES,
.flags = CMD_SYNC | CMD_WANT_SKB, .flags = CMD_SYNC | CMD_WANT_SKB,
}; };
struct iwl_wowlan_status *status; struct iwl_wowlan_status_data status;
int ret, len; struct iwl_wowlan_status_v6 *status_v6;
int ret, len, status_size, i;
bool keep;
struct ieee80211_sta *ap_sta;
struct iwl_mvm_sta *mvm_ap_sta;
iwl_trans_read_mem_bytes(mvm->trans, base, iwl_trans_read_mem_bytes(mvm->trans, base,
&err_info, sizeof(err_info)); &err_info, sizeof(err_info));
...@@ -1381,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, ...@@ -1381,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
if (!cmd.resp_pkt) if (!cmd.resp_pkt)
goto out_unlock; goto out_unlock;
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)
status_size = sizeof(struct iwl_wowlan_status_v6);
else
status_size = sizeof(struct iwl_wowlan_status_v4);
len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { if (len - sizeof(struct iwl_cmd_header) < status_size) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
goto out_free_resp; goto out_free_resp;
} }
status = (void *)cmd.resp_pkt->data; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) {
status_v6 = (void *)cmd.resp_pkt->data;
status.pattern_number = le16_to_cpu(status_v6->pattern_number);
for (i = 0; i < 8; i++)
status.qos_seq_ctr[i] =
le16_to_cpu(status_v6->qos_seq_ctr[i]);
status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons);
status.wake_packet_length =
le32_to_cpu(status_v6->wake_packet_length);
status.wake_packet_bufsize =
le32_to_cpu(status_v6->wake_packet_bufsize);
status.wake_packet = status_v6->wake_packet;
} else {
struct iwl_wowlan_status_v4 *status_v4;
status_v6 = NULL;
status_v4 = (void *)cmd.resp_pkt->data;
status.pattern_number = le16_to_cpu(status_v4->pattern_number);
for (i = 0; i < 8; i++)
status.qos_seq_ctr[i] =
le16_to_cpu(status_v4->qos_seq_ctr[i]);
status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons);
status.wake_packet_length =
le32_to_cpu(status_v4->wake_packet_length);
status.wake_packet_bufsize =
le32_to_cpu(status_v4->wake_packet_bufsize);
status.wake_packet = status_v4->wake_packet;
}
if (len - sizeof(struct iwl_cmd_header) != if (len - sizeof(struct iwl_cmd_header) !=
sizeof(*status) + status_size + ALIGN(status.wake_packet_bufsize, 4)) {
ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
goto out_free_resp; goto out_free_resp;
} }
/* still at hard-coded place 0 for D3 image */
ap_sta = rcu_dereference_protected(
mvm->fw_id_to_mac_id[0],
lockdep_is_held(&mvm->mutex));
if (IS_ERR_OR_NULL(ap_sta))
goto out_free_resp;
mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;
for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
u16 seq = status.qos_seq_ctr[i];
/* firmware stores last-used value, we store next value */
seq += 0x10;
mvm_ap_sta->tid_data[i].seq_number = seq;
}
/* now we have all the data we need, unlock to avoid mac80211 issues */ /* now we have all the data we need, unlock to avoid mac80211 issues */
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
iwl_mvm_report_wakeup_reasons(mvm, vif, status); iwl_mvm_report_wakeup_reasons(mvm, vif, &status);
keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6);
iwl_free_resp(&cmd); iwl_free_resp(&cmd);
return; return keep;
out_free_resp: out_free_resp:
iwl_free_resp(&cmd); iwl_free_resp(&cmd);
out_unlock: out_unlock:
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
return false;
} }
static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
...@@ -1429,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) ...@@ -1429,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
#endif #endif
} }
static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
/* skip the one we keep connection on */
if (data == vif)
return;
if (vif->type == NL80211_IFTYPE_STATION)
ieee80211_resume_disconnect(vif);
}
static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
{ {
struct iwl_d3_iter_data resume_iter_data = { struct iwl_d3_iter_data resume_iter_data = {
...@@ -1437,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) ...@@ -1437,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
struct ieee80211_vif *vif = NULL; struct ieee80211_vif *vif = NULL;
int ret; int ret;
enum iwl_d3_status d3_status; enum iwl_d3_status d3_status;
bool keep = false;
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
...@@ -1462,7 +1803,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) ...@@ -1462,7 +1803,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
/* query SRAM first in case we want event logging */ /* query SRAM first in case we want event logging */
iwl_mvm_read_d3_sram(mvm); iwl_mvm_read_d3_sram(mvm);
iwl_mvm_query_wakeup_reasons(mvm, vif); keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
/* has unlocked the mutex, so skip that */ /* has unlocked the mutex, so skip that */
goto out; goto out;
...@@ -1470,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) ...@@ -1470,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
out: out:
if (!test && vif) if (!test)
ieee80211_resume_disconnect(vif); ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
/* return 1 to reconfigure the device */ /* return 1 to reconfigure the device */
set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
......
...@@ -335,7 +335,7 @@ enum iwl_wowlan_wakeup_reason { ...@@ -335,7 +335,7 @@ enum iwl_wowlan_wakeup_reason {
IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
struct iwl_wowlan_status { struct iwl_wowlan_status_v4 {
__le64 replay_ctr; __le64 replay_ctr;
__le16 pattern_number; __le16 pattern_number;
__le16 non_qos_seq_ctr; __le16 non_qos_seq_ctr;
...@@ -350,6 +350,29 @@ struct iwl_wowlan_status { ...@@ -350,6 +350,29 @@ struct iwl_wowlan_status {
u8 wake_packet[]; /* can be truncated from _length to _bufsize */ u8 wake_packet[]; /* can be truncated from _length to _bufsize */
} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */ } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
struct iwl_wowlan_gtk_status {
u8 key_index;
u8 reserved[3];
u8 decrypt_key[16];
u8 tkip_mic_key[8];
struct iwl_wowlan_rsc_tsc_params_cmd rsc;
} __packed;
struct iwl_wowlan_status_v6 {
struct iwl_wowlan_gtk_status gtk;
__le64 replay_ctr;
__le16 pattern_number;
__le16 non_qos_seq_ctr;
__le16 qos_seq_ctr[8];
__le32 wakeup_reasons;
__le32 num_of_gtk_rekeys;
__le32 transmitted_ndps;
__le32 received_beacons;
__le32 wake_packet_length;
__le32 wake_packet_bufsize;
u8 wake_packet[]; /* can be truncated from _length to _bufsize */
} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */
#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 #define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 #define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
......
...@@ -372,4 +372,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v) ...@@ -372,4 +372,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v)
return 0xFFFFFFFF / v; return 0xFFFFFFFF / v;
} }
#define IWL_NONQOS_SEQ_GET 0x1
#define IWL_NONQOS_SEQ_SET 0x2
struct iwl_nonqos_seq_query_cmd {
__le32 get_set_flag;
__le32 mac_id_n_color;
__le16 value;
__le16 reserved;
} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */
#endif /* __fw_api_mac_h__ */ #endif /* __fw_api_mac_h__ */
...@@ -1042,6 +1042,9 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) ...@@ -1042,6 +1042,9 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
if (ret) if (ret)
return ret; return ret;
/* will only do anything at resume from D3 time */
iwl_mvm_set_last_nonqos_seq(mvm, vif);
mvmvif->uploaded = true; mvmvif->uploaded = true;
return 0; return 0;
} }
......
...@@ -313,6 +313,9 @@ struct iwl_mvm_vif { ...@@ -313,6 +313,9 @@ struct iwl_mvm_vif {
int tx_key_idx; int tx_key_idx;
bool seqno_valid;
u16 seqno;
#if IS_ENABLED(CONFIG_IPV6) #if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */ /* IPv6 addresses for WoWLAN */
struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
...@@ -796,6 +799,15 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, ...@@ -796,6 +799,15 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw,
void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, int idx); struct ieee80211_vif *vif, int idx);
extern const struct file_operations iwl_dbgfs_d3_test_ops; extern const struct file_operations iwl_dbgfs_d3_test_ops;
#ifdef CONFIG_PM_SLEEP
void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
struct ieee80211_vif *vif);
#else
static inline void
iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
#endif
/* BT Coex */ /* BT Coex */
int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
......
...@@ -337,8 +337,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, ...@@ -337,8 +337,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm,
if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE)
mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]);
/* for HW restart - need to reset the seq_number etc... */ /* for HW restart - reset everything but the sequence number */
memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
u16 seq = mvm_sta->tid_data[i].seq_number;
memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i]));
mvm_sta->tid_data[i].seq_number = seq;
}
ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); ret = iwl_mvm_sta_send_to_fw(mvm, sta, false);
if (ret) if (ret)
......
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