/** * struct iwl_mld_wowlan_status - contains wowlan status data from * all wowlan notifications * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason * @replay_ctr: GTK rekey replay counter * @pattern_number: number of the matched patterns on packets * @last_qos_seq: QoS sequence counter of offloaded tid * @num_of_gtk_rekeys: number of GTK rekeys during D3 * @tid_offloaded_tx: tid used by the firmware to transmit data packets * while in wowlan * @wake_packet: wakeup packet received * @wake_packet_length: wake packet length * @wake_packet_bufsize: wake packet bufsize * @gtk: data of the last two used gtk's by the FW upon resume * @igtk: data of the last used igtk by the FW upon resume * @bigtk: data of the last two used gtk's by the FW upon resume * @ptk: last seq numbers per tid passed by the FW, * holds both in tkip and aes formats
*/ struct iwl_mld_wowlan_status {
u32 wakeup_reasons;
u64 replay_ctr;
u16 pattern_number;
u16 last_qos_seq;
u32 num_of_gtk_rekeys;
u8 tid_offloaded_tx;
u8 *wake_packet;
u32 wake_packet_length;
u32 wake_packet_bufsize; struct iwl_mld_mcast_key_data gtk[WOWLAN_GTK_KEYS_NUM]; struct iwl_mld_mcast_key_data igtk; struct iwl_mld_mcast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM]; struct { struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT];
/** * struct iwl_mld_netdetect_res - contains netdetect results from * match_info_notif * @matched_profiles: bitmap of matched profiles, referencing the * matches passed in the scan offload request * @matches: array of match information, one for each match
*/ struct iwl_mld_netdetect_res {
u32 matched_profiles;
u8 matches[NETDETECT_QUERY_BUF_LEN];
};
/** * struct iwl_mld_resume_data - d3 resume flow data * @notifs_expected: bitmap of expected notifications from fw, * see &enum iwl_mld_d3_notif * @notifs_received: bitmap of received notifications from fw, * see &enum iwl_mld_d3_notif * @d3_end_flags: bitmap of flags from d3_end_notif * @notif_handling_err: error handling one of the resume notifications * @wowlan_status: wowlan status data from all wowlan notifications * @netdetect_res: contains netdetect results from match_info_notif
*/ struct iwl_mld_resume_data {
u32 notifs_expected;
u32 notifs_received;
u32 d3_end_flags; bool notif_handling_err; struct iwl_mld_wowlan_status *wowlan_status; struct iwl_mld_netdetect_res *netdetect_res;
};
/* We store both the TKIP and AES representations coming from the * FW because we decode the data from there before we iterate * the keys and know which type is used.
*/ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
iwl_mld_le64_to_tkip_seq(sc->mcast_rsc[rsc_idx][tid],
&tkip_seq[tid]);
iwl_mld_le64_to_aes_seq(sc->mcast_rsc[rsc_idx][tid],
&aes_seq[tid]);
}
}
/* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc * The gtk ids can be any two numbers between 0 and 3, * the id_map maps between the key id and the index in sc->mcast
*/
rsc_idx =
sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
sc, rsc_idx);
/* if it's as long as the TKIP encryption key, copy MIC key */ if (wowlan_status->gtk[status_idx].len ==
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
memcpy(wowlan_status->gtk[status_idx].key +
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
gtk_data[notif_idx].tkip_mic_key, sizeof(gtk_data[notif_idx].tkip_mic_key));
status_idx++;
}
}
/* mac80211 expects big endian for memcmp() to work, convert. * We don't have the key cipher yet so copy to both to cmac and gmac
*/ for (int i = 0; i < ipn_len; i++) {
seq->aes_gmac.pn[i] = key->ipn[ipn_len - i - 1];
seq->aes_cmac.pn[i] = key->ipn[ipn_len - i - 1];
}
}
if (IWL_FW_CHECK(mld, len < expected_len, "Invalid wowlan_info_notif (expected=%ud got=%ud)\n",
expected_len, len)) returntrue;
if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID, "Invalid tid_offloaded_tx %d\n",
wowlan_status->tid_offloaded_tx)) returntrue;
iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk,
¬if->gtk[0].sc);
iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc); /* only one igtk is passed by FW */
iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]);
iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk);
if (IWL_FW_CHECK(mld, len < sizeof(*notif), "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n", sizeof(*notif), len)) returntrue;
if (IWL_FW_CHECK(mld, !(wowlan_status->wakeup_reasons &
IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT), "Got wake packet but wakeup reason is %x\n",
wowlan_status->wakeup_reasons)) returntrue;
actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif,
wake_packet);
/* actual_size got the padding from the notification, remove it. */ if (expected_size < actual_size)
actual_size = expected_size;
wowlan_status->wake_packet = kmemdup(notif->wake_packet, actual_size,
GFP_ATOMIC); if (!wowlan_status->wake_packet) returntrue;
iwl_mld_set_key_rx_seq_tids(key, is_tkip ?
wowlan_status->ptk.tkip_seq :
wowlan_status->ptk.aes_seq); if (is_tkip) return;
if (WARN_ON(!mld_ptk_pn)) return;
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { for (int i = 1; i < mld->trans->info.num_rxqs; i++)
memcpy(mld_ptk_pn->q[i].pn[tid],
wowlan_status->ptk.aes_seq[tid].ccmp.pn,
IEEE80211_CCMP_PN_LEN);
}
}
/* TODO: check key link id (task=MLO) */ 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_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: case WLAN_CIPHER_SUITE_TKIP: if (sta) {
iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
sta, key,
key->cipher ==
WLAN_CIPHER_SUITE_TKIP); return;
}
if (WARN_ON(data->gtk_cipher &&
data->gtk_cipher != key->cipher)) return;
data->gtk_cipher = key->cipher;
status_idx = key->keyidx == wowlan_status->gtk[1].id;
iwl_mld_set_key_rx_seq(key, &wowlan_status->gtk[status_idx]); break; case WLAN_CIPHER_SUITE_BIP_GMAC_128: case WLAN_CIPHER_SUITE_BIP_GMAC_256: case WLAN_CIPHER_SUITE_BIP_CMAC_256: case WLAN_CIPHER_SUITE_AES_CMAC: if (key->keyidx == 4 || key->keyidx == 5) { if (WARN_ON(data->igtk_cipher &&
data->igtk_cipher != key->cipher)) return;
data->igtk_cipher = key->cipher; if (key->keyidx == wowlan_status->igtk.id)
iwl_mld_set_key_rx_seq(key, &wowlan_status->igtk);
} if (key->keyidx == 6 || key->keyidx == 7) { if (WARN_ON(data->bigtk_cipher &&
data->bigtk_cipher != key->cipher)) return;
key_config = ieee80211_gtk_rekey_add(vif, key_data->id, key, sizeof(key), link_id); if (IS_ERR(key_config)) return;
iwl_mld_set_key_rx_seq(key_config, key_data);
/* The FW holds only one igtk so we keep track of the valid one */ if (key_config->keyidx == 4 || key_config->keyidx == 5) { struct iwl_mld_link *mld_link =
iwl_mld_link_from_mac80211(link_conf);
/* If we had more than one rekey, mac80211 will tell us to * remove the old and add the new so we will update the IGTK in * drv_set_key
*/ if (mld_link->igtk && mld_link->igtk != key_config) { /* mark the old IGTK as not in FW */
mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;
mld_link->igtk = key_config;
}
}
/* Also keep track of the new BIGTK */ if ((key_config->keyidx == 6 || key_config->keyidx == 7) &&
vif->type == NL80211_IFTYPE_STATION) { struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++)
iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
&wowlan_status->gtk[i],
link_conf,
key_iter_data->gtk_cipher);
for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++)
iwl_mld_add_mcast_rekey(vif, key_iter_data->mld,
&wowlan_status->bigtk[i],
link_conf,
key_iter_data->bigtk_cipher);
}
/* Update the pointers of the Tx queue that may have moved during * suspend if the firmware sent frames. * The firmware stores last-used value, we store next value.
*/
WARN_ON(!mld_txq->status.allocated);
iwl_trans_set_q_ptrs(mld->trans, mld_txq->fw_id,
(wowlan_status->last_qos_seq +
0x10) >> 4);
if (!iwl_mld_update_sec_keys(mld, vif, wowlan_status)) returnfalse;
if (wowlan_status->wakeup_reasons &
(IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON |
IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH |
IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) returnfalse;
for (int k = 0; k < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; k++)
n_channels +=
hweight8(matches[i].matching_channels[k]);
match = kzalloc(struct_size(match, channels, n_channels),
GFP_KERNEL); if (!match) return;
/* We inverted the order of the SSIDs in the scan * request, so invert the index here.
*/
idx = netdetect_cfg->n_match_sets - i - 1;
match->ssid.ssid_len =
netdetect_cfg->match_sets[idx].ssid.ssid_len;
memcpy(match->ssid.ssid,
netdetect_cfg->match_sets[idx].ssid.ssid,
match->ssid.ssid_len);
if (netdetect_cfg->n_channels < n_channels) continue;
ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false); if (ret || d3_status != IWL_D3_STATUS_ALIVE) { if (d3_status != IWL_D3_STATUS_ALIVE) {
IWL_INFO(mld, "Device was reset during suspend\n");
ret = -ENOENT;
} else {
IWL_ERR(mld, "Transport resume failed\n");
}
iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); return ret;
}
ret = iwl_wait_notification(&mld->notif_wait, &wait_d3_notif,
IWL_MLD_D3_NOTIF_TIMEOUT); if (ret)
IWL_ERR(mld, "Couldn't get the d3 notif %d\n", ret);
if (resume_data->notif_handling_err)
ret = -EIO;
return ret;
}
int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld)
{ struct iwl_d3_manager_config d3_cfg_cmd_data = {}; int ret;
if (mld->debug_max_sleep) {
d3_cfg_cmd_data.wakeup_host_timer =
cpu_to_le32(mld->debug_max_sleep);
d3_cfg_cmd_data.wakeup_flags =
cpu_to_le32(IWL_WAKEUP_D3_HOST_TIMER);
}
lockdep_assert_wiphy(mld->wiphy);
IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n");
iwl_mld_low_latency_stop(mld);
/* This will happen if iwl_mld_supsend failed with FW error */ if (mld->trans->state == IWL_TRANS_NO_FW &&
test_bit(STATUS_FW_ERROR, &mld->trans->status)) return -ENODEV;
ret = iwl_mld_update_device_power(mld, true); if (ret) {
IWL_ERR(mld, "d3 suspend: couldn't send power_device %d\n", ret); goto out;
}
ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD,
&d3_cfg_cmd_data); if (ret) {
IWL_ERR(mld, "d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret); goto out;
}
ret = iwl_trans_d3_suspend(mld->trans, false, false); if (ret) {
IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret);
} else { /* Async notification might send hcmds, which is not allowed in suspend */
iwl_mld_cancel_async_notifications(mld);
mld->fw_status.in_d3 = true;
}
out: if (ret) {
mld->trans->state = IWL_TRANS_NO_FW;
set_bit(STATUS_FW_ERROR, &mld->trans->status);
}
return ret;
}
int iwl_mld_no_wowlan_resume(struct iwl_mld *mld)
{ struct iwl_mld_resume_data resume_data = {
.notifs_expected =
IWL_D3_NOTIF_D3_END_NOTIF,
}; int ret;
lockdep_assert_wiphy(mld->wiphy);
IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n");
switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP:
cipher = cpu_to_le32(STA_KEY_FLG_CCM);
fallthrough; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: if (!cipher)
cipher = cpu_to_le32(STA_KEY_FLG_GCMP);
fallthrough; case WLAN_CIPHER_SUITE_TKIP: if (!cipher)
cipher = cpu_to_le32(STA_KEY_FLG_TKIP); if (sta) {
key_rsc = data->rsc->ucast_rsc; if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); else
iwl_mld_suspend_set_ucast_pn(mld, sta, key,
key_rsc);
data->have_rsc = true; return;
} /* We're iterating from old to new, there're 4 possible * gtk ids, and only the last two keys matter
*/ if (WARN_ON(data->gtks >=
ARRAY_SIZE(data->found_gtk_idx))) return;
/* For each address we have (and that will fit) fill a target * address struct and combine for NS offload structs with the * solicited node addresses.
*/ for (i = 0, c = 0;
i < wowlan_data->num_target_ipv6_addrs &&
i < n_addrs && c < n_nsc; i++) { int j; struct in6_addr solicited_addr;
/* Because ns is offloaded skip tentative address to avoid * violating RFC4862.
*/ if (test_bit(i, wowlan_data->tentative_addrs)) {
num_skipped++; continue;
}
addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i],
&solicited_addr); for (j = 0; j < n_nsc && j < c; j++) if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
&solicited_addr) == 0) break; if (j == c)
c++;
addrs[i].addr = wowlan_data->target_ipv6_addrs[i];
addrs[i].config_num = cpu_to_le32(j);
nsc[j].dest_ipv6_addr = solicited_addr;
memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
}
if (wowlan_data->num_target_ipv6_addrs - num_skipped)
enabled |= IWL_D3_PROTO_IPV6_VALID;
/* Returns 0 on success, 1 if an error occurred in firmware during d3, * A negative value is expected only in unrecovreable cases.
*/ int iwl_mld_wowlan_resume(struct iwl_mld *mld)
{ struct ieee80211_vif *bss_vif; struct ieee80211_bss_conf *link_conf; struct iwl_mld_netdetect_res netdetect_res; struct iwl_mld_resume_data resume_data = {
.notifs_expected =
IWL_D3_NOTIF_WOWLAN_INFO |
IWL_D3_NOTIF_D3_END_NOTIF,
.netdetect_res = &netdetect_res,
}; int link_id; int ret; bool fw_err = false;
lockdep_assert_wiphy(mld->wiphy);
IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n");
if (!mld->fw_status.in_d3) {
IWL_DEBUG_WOWLAN(mld, "Device_powered_off() was called during wowlan\n"); goto err;
}
bss_vif = iwl_mld_get_bss_vif(mld); if (WARN_ON(!bss_vif)) goto err;
/* We can't have several links upon wowlan entry, * this is enforced in the suspend flow.
*/
WARN_ON(hweight16(bss_vif->active_links) > 1);
link_id = bss_vif->active_links ? __ffs(bss_vif->active_links) : 0;
link_conf = link_conf_dereference_protected(bss_vif, link_id);
if (WARN_ON(!link_conf)) goto err;
iwl_fw_dbg_read_d3_debug_data(&mld->fwrt);
resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status),
GFP_KERNEL); if (!resume_data.wowlan_status) return -ENOMEM;
if (mld->netdetect)
resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO;
ret = iwl_mld_wait_d3_notif(mld, &resume_data, true); if (ret) {
IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret);
fw_err = true; goto err;
}
/* EMLSR state will be cleared if the connection is not kept */ if (keep_connection)
iwl_mld_unblock_emlsr(mld, bss_vif,
IWL_MLD_EMLSR_BLOCKED_WOWLAN); else
ieee80211_resume_disconnect(bss_vif);
}
goto out;
err: if (fw_err) {
mld->trans->state = IWL_TRANS_NO_FW;
set_bit(STATUS_FW_ERROR, &mld->trans->status);
}
mld->fw_status.in_hw_restart = true;
ret = 1;
out:
mld->fw_status.resuming = false;
if (resume_data.wowlan_status) {
kfree(resume_data.wowlan_status->wake_packet);
kfree(resume_data.wowlan_status);
}
return ret;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.