diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/d3.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/d3.c | 364 |
1 files changed, 336 insertions, 28 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index c64d864799cd..d4578cefe445 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c | |||
@@ -22,7 +22,7 @@ | |||
22 | * USA | 22 | * USA |
23 | * | 23 | * |
24 | * The full GNU General Public License is included in this distribution | 24 | * The full GNU General Public License is included in this distribution |
25 | * in the file called LICENSE.GPL. | 25 | * in the file called COPYING. |
26 | * | 26 | * |
27 | * Contact Information: | 27 | * Contact Information: |
28 | * Intel Linux Wireless <ilw@linux.intel.com> | 28 | * Intel Linux Wireless <ilw@linux.intel.com> |
@@ -61,8 +61,11 @@ | |||
61 | * | 61 | * |
62 | *****************************************************************************/ | 62 | *****************************************************************************/ |
63 | 63 | ||
64 | #include <linux/etherdevice.h> | ||
65 | #include <linux/ip.h> | ||
64 | #include <net/cfg80211.h> | 66 | #include <net/cfg80211.h> |
65 | #include <net/ipv6.h> | 67 | #include <net/ipv6.h> |
68 | #include <net/tcp.h> | ||
66 | #include "iwl-modparams.h" | 69 | #include "iwl-modparams.h" |
67 | #include "fw-api.h" | 70 | #include "fw-api.h" |
68 | #include "mvm.h" | 71 | #include "mvm.h" |
@@ -192,6 +195,11 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, | |||
192 | sizeof(wkc), &wkc); | 195 | sizeof(wkc), &wkc); |
193 | data->error = ret != 0; | 196 | data->error = ret != 0; |
194 | 197 | ||
198 | mvm->ptk_ivlen = key->iv_len; | ||
199 | mvm->ptk_icvlen = key->icv_len; | ||
200 | mvm->gtk_ivlen = key->iv_len; | ||
201 | mvm->gtk_icvlen = key->icv_len; | ||
202 | |||
195 | /* don't upload key again */ | 203 | /* don't upload key again */ |
196 | goto out_unlock; | 204 | goto out_unlock; |
197 | } | 205 | } |
@@ -304,9 +312,13 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, | |||
304 | */ | 312 | */ |
305 | if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { | 313 | if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { |
306 | key->hw_key_idx = 0; | 314 | key->hw_key_idx = 0; |
315 | mvm->ptk_ivlen = key->iv_len; | ||
316 | mvm->ptk_icvlen = key->icv_len; | ||
307 | } else { | 317 | } else { |
308 | data->gtk_key_idx++; | 318 | data->gtk_key_idx++; |
309 | key->hw_key_idx = data->gtk_key_idx; | 319 | key->hw_key_idx = data->gtk_key_idx; |
320 | mvm->gtk_ivlen = key->iv_len; | ||
321 | mvm->gtk_icvlen = key->icv_len; | ||
310 | } | 322 | } |
311 | 323 | ||
312 | ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); | 324 | ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, true); |
@@ -392,6 +404,233 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, | |||
392 | sizeof(cmd), &cmd); | 404 | sizeof(cmd), &cmd); |
393 | } | 405 | } |
394 | 406 | ||
407 | enum iwl_mvm_tcp_packet_type { | ||
408 | MVM_TCP_TX_SYN, | ||
409 | MVM_TCP_RX_SYNACK, | ||
410 | MVM_TCP_TX_DATA, | ||
411 | MVM_TCP_RX_ACK, | ||
412 | MVM_TCP_RX_WAKE, | ||
413 | MVM_TCP_TX_FIN, | ||
414 | }; | ||
415 | |||
416 | static __le16 pseudo_hdr_check(int len, __be32 saddr, __be32 daddr) | ||
417 | { | ||
418 | __sum16 check = tcp_v4_check(len, saddr, daddr, 0); | ||
419 | return cpu_to_le16(be16_to_cpu((__force __be16)check)); | ||
420 | } | ||
421 | |||
422 | static void iwl_mvm_build_tcp_packet(struct iwl_mvm *mvm, | ||
423 | struct ieee80211_vif *vif, | ||
424 | struct cfg80211_wowlan_tcp *tcp, | ||
425 | void *_pkt, u8 *mask, | ||
426 | __le16 *pseudo_hdr_csum, | ||
427 | enum iwl_mvm_tcp_packet_type ptype) | ||
428 | { | ||
429 | struct { | ||
430 | struct ethhdr eth; | ||
431 | struct iphdr ip; | ||
432 | struct tcphdr tcp; | ||
433 | u8 data[]; | ||
434 | } __packed *pkt = _pkt; | ||
435 | u16 ip_tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr); | ||
436 | int i; | ||
437 | |||
438 | pkt->eth.h_proto = cpu_to_be16(ETH_P_IP), | ||
439 | pkt->ip.version = 4; | ||
440 | pkt->ip.ihl = 5; | ||
441 | pkt->ip.protocol = IPPROTO_TCP; | ||
442 | |||
443 | switch (ptype) { | ||
444 | case MVM_TCP_TX_SYN: | ||
445 | case MVM_TCP_TX_DATA: | ||
446 | case MVM_TCP_TX_FIN: | ||
447 | memcpy(pkt->eth.h_dest, tcp->dst_mac, ETH_ALEN); | ||
448 | memcpy(pkt->eth.h_source, vif->addr, ETH_ALEN); | ||
449 | pkt->ip.ttl = 128; | ||
450 | pkt->ip.saddr = tcp->src; | ||
451 | pkt->ip.daddr = tcp->dst; | ||
452 | pkt->tcp.source = cpu_to_be16(tcp->src_port); | ||
453 | pkt->tcp.dest = cpu_to_be16(tcp->dst_port); | ||
454 | /* overwritten for TX SYN later */ | ||
455 | pkt->tcp.doff = sizeof(struct tcphdr) / 4; | ||
456 | pkt->tcp.window = cpu_to_be16(65000); | ||
457 | break; | ||
458 | case MVM_TCP_RX_SYNACK: | ||
459 | case MVM_TCP_RX_ACK: | ||
460 | case MVM_TCP_RX_WAKE: | ||
461 | memcpy(pkt->eth.h_dest, vif->addr, ETH_ALEN); | ||
462 | memcpy(pkt->eth.h_source, tcp->dst_mac, ETH_ALEN); | ||
463 | pkt->ip.saddr = tcp->dst; | ||
464 | pkt->ip.daddr = tcp->src; | ||
465 | pkt->tcp.source = cpu_to_be16(tcp->dst_port); | ||
466 | pkt->tcp.dest = cpu_to_be16(tcp->src_port); | ||
467 | break; | ||
468 | default: | ||
469 | WARN_ON(1); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | switch (ptype) { | ||
474 | case MVM_TCP_TX_SYN: | ||
475 | /* firmware assumes 8 option bytes - 8 NOPs for now */ | ||
476 | memset(pkt->data, 0x01, 8); | ||
477 | ip_tot_len += 8; | ||
478 | pkt->tcp.doff = (sizeof(struct tcphdr) + 8) / 4; | ||
479 | pkt->tcp.syn = 1; | ||
480 | break; | ||
481 | case MVM_TCP_TX_DATA: | ||
482 | ip_tot_len += tcp->payload_len; | ||
483 | memcpy(pkt->data, tcp->payload, tcp->payload_len); | ||
484 | pkt->tcp.psh = 1; | ||
485 | pkt->tcp.ack = 1; | ||
486 | break; | ||
487 | case MVM_TCP_TX_FIN: | ||
488 | pkt->tcp.fin = 1; | ||
489 | pkt->tcp.ack = 1; | ||
490 | break; | ||
491 | case MVM_TCP_RX_SYNACK: | ||
492 | pkt->tcp.syn = 1; | ||
493 | pkt->tcp.ack = 1; | ||
494 | break; | ||
495 | case MVM_TCP_RX_ACK: | ||
496 | pkt->tcp.ack = 1; | ||
497 | break; | ||
498 | case MVM_TCP_RX_WAKE: | ||
499 | ip_tot_len += tcp->wake_len; | ||
500 | pkt->tcp.psh = 1; | ||
501 | pkt->tcp.ack = 1; | ||
502 | memcpy(pkt->data, tcp->wake_data, tcp->wake_len); | ||
503 | break; | ||
504 | } | ||
505 | |||
506 | switch (ptype) { | ||
507 | case MVM_TCP_TX_SYN: | ||
508 | case MVM_TCP_TX_DATA: | ||
509 | case MVM_TCP_TX_FIN: | ||
510 | pkt->ip.tot_len = cpu_to_be16(ip_tot_len); | ||
511 | pkt->ip.check = ip_fast_csum(&pkt->ip, pkt->ip.ihl); | ||
512 | break; | ||
513 | case MVM_TCP_RX_WAKE: | ||
514 | for (i = 0; i < DIV_ROUND_UP(tcp->wake_len, 8); i++) { | ||
515 | u8 tmp = tcp->wake_mask[i]; | ||
516 | mask[i + 6] |= tmp << 6; | ||
517 | if (i + 1 < DIV_ROUND_UP(tcp->wake_len, 8)) | ||
518 | mask[i + 7] = tmp >> 2; | ||
519 | } | ||
520 | /* fall through for ethernet/IP/TCP headers mask */ | ||
521 | case MVM_TCP_RX_SYNACK: | ||
522 | case MVM_TCP_RX_ACK: | ||
523 | mask[0] = 0xff; /* match ethernet */ | ||
524 | /* | ||
525 | * match ethernet, ip.version, ip.ihl | ||
526 | * the ip.ihl half byte is really masked out by firmware | ||
527 | */ | ||
528 | mask[1] = 0x7f; | ||
529 | mask[2] = 0x80; /* match ip.protocol */ | ||
530 | mask[3] = 0xfc; /* match ip.saddr, ip.daddr */ | ||
531 | mask[4] = 0x3f; /* match ip.daddr, tcp.source, tcp.dest */ | ||
532 | mask[5] = 0x80; /* match tcp flags */ | ||
533 | /* leave rest (0 or set for MVM_TCP_RX_WAKE) */ | ||
534 | break; | ||
535 | }; | ||
536 | |||
537 | *pseudo_hdr_csum = pseudo_hdr_check(ip_tot_len - sizeof(struct iphdr), | ||
538 | pkt->ip.saddr, pkt->ip.daddr); | ||
539 | } | ||
540 | |||
541 | static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, | ||
542 | struct ieee80211_vif *vif, | ||
543 | struct cfg80211_wowlan_tcp *tcp) | ||
544 | { | ||
545 | struct iwl_wowlan_remote_wake_config *cfg; | ||
546 | struct iwl_host_cmd cmd = { | ||
547 | .id = REMOTE_WAKE_CONFIG_CMD, | ||
548 | .len = { sizeof(*cfg), }, | ||
549 | .dataflags = { IWL_HCMD_DFL_NOCOPY, }, | ||
550 | .flags = CMD_SYNC, | ||
551 | }; | ||
552 | int ret; | ||
553 | |||
554 | if (!tcp) | ||
555 | return 0; | ||
556 | |||
557 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | ||
558 | if (!cfg) | ||
559 | return -ENOMEM; | ||
560 | cmd.data[0] = cfg; | ||
561 | |||
562 | cfg->max_syn_retries = 10; | ||
563 | cfg->max_data_retries = 10; | ||
564 | cfg->tcp_syn_ack_timeout = 1; /* seconds */ | ||
565 | cfg->tcp_ack_timeout = 1; /* seconds */ | ||
566 | |||
567 | /* SYN (TX) */ | ||
568 | iwl_mvm_build_tcp_packet( | ||
569 | mvm, vif, tcp, cfg->syn_tx.data, NULL, | ||
570 | &cfg->syn_tx.info.tcp_pseudo_header_checksum, | ||
571 | MVM_TCP_TX_SYN); | ||
572 | cfg->syn_tx.info.tcp_payload_length = 0; | ||
573 | |||
574 | /* SYN/ACK (RX) */ | ||
575 | iwl_mvm_build_tcp_packet( | ||
576 | mvm, vif, tcp, cfg->synack_rx.data, cfg->synack_rx.rx_mask, | ||
577 | &cfg->synack_rx.info.tcp_pseudo_header_checksum, | ||
578 | MVM_TCP_RX_SYNACK); | ||
579 | cfg->synack_rx.info.tcp_payload_length = 0; | ||
580 | |||
581 | /* KEEPALIVE/ACK (TX) */ | ||
582 | iwl_mvm_build_tcp_packet( | ||
583 | mvm, vif, tcp, cfg->keepalive_tx.data, NULL, | ||
584 | &cfg->keepalive_tx.info.tcp_pseudo_header_checksum, | ||
585 | MVM_TCP_TX_DATA); | ||
586 | cfg->keepalive_tx.info.tcp_payload_length = | ||
587 | cpu_to_le16(tcp->payload_len); | ||
588 | cfg->sequence_number_offset = tcp->payload_seq.offset; | ||
589 | /* length must be 0..4, the field is little endian */ | ||
590 | cfg->sequence_number_length = tcp->payload_seq.len; | ||
591 | cfg->initial_sequence_number = cpu_to_le32(tcp->payload_seq.start); | ||
592 | cfg->keepalive_interval = cpu_to_le16(tcp->data_interval); | ||
593 | if (tcp->payload_tok.len) { | ||
594 | cfg->token_offset = tcp->payload_tok.offset; | ||
595 | cfg->token_length = tcp->payload_tok.len; | ||
596 | cfg->num_tokens = | ||
597 | cpu_to_le16(tcp->tokens_size % tcp->payload_tok.len); | ||
598 | memcpy(cfg->tokens, tcp->payload_tok.token_stream, | ||
599 | tcp->tokens_size); | ||
600 | } else { | ||
601 | /* set tokens to max value to almost never run out */ | ||
602 | cfg->num_tokens = cpu_to_le16(65535); | ||
603 | } | ||
604 | |||
605 | /* ACK (RX) */ | ||
606 | iwl_mvm_build_tcp_packet( | ||
607 | mvm, vif, tcp, cfg->keepalive_ack_rx.data, | ||
608 | cfg->keepalive_ack_rx.rx_mask, | ||
609 | &cfg->keepalive_ack_rx.info.tcp_pseudo_header_checksum, | ||
610 | MVM_TCP_RX_ACK); | ||
611 | cfg->keepalive_ack_rx.info.tcp_payload_length = 0; | ||
612 | |||
613 | /* WAKEUP (RX) */ | ||
614 | iwl_mvm_build_tcp_packet( | ||
615 | mvm, vif, tcp, cfg->wake_rx.data, cfg->wake_rx.rx_mask, | ||
616 | &cfg->wake_rx.info.tcp_pseudo_header_checksum, | ||
617 | MVM_TCP_RX_WAKE); | ||
618 | cfg->wake_rx.info.tcp_payload_length = | ||
619 | cpu_to_le16(tcp->wake_len); | ||
620 | |||
621 | /* FIN */ | ||
622 | iwl_mvm_build_tcp_packet( | ||
623 | mvm, vif, tcp, cfg->fin_tx.data, NULL, | ||
624 | &cfg->fin_tx.info.tcp_pseudo_header_checksum, | ||
625 | MVM_TCP_TX_FIN); | ||
626 | cfg->fin_tx.info.tcp_payload_length = 0; | ||
627 | |||
628 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
629 | kfree(cfg); | ||
630 | |||
631 | return ret; | ||
632 | } | ||
633 | |||
395 | struct iwl_d3_iter_data { | 634 | struct iwl_d3_iter_data { |
396 | struct iwl_mvm *mvm; | 635 | struct iwl_mvm *mvm; |
397 | struct ieee80211_vif *vif; | 636 | struct ieee80211_vif *vif; |
@@ -630,6 +869,22 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
630 | d3_cfg_cmd.wakeup_flags |= | 869 | d3_cfg_cmd.wakeup_flags |= |
631 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); | 870 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); |
632 | 871 | ||
872 | if (wowlan->tcp) { | ||
873 | /* | ||
874 | * The firmware currently doesn't really look at these, only | ||
875 | * the IWL_WOWLAN_WAKEUP_LINK_CHANGE bit. We have to set that | ||
876 | * reason bit since losing the connection to the AP implies | ||
877 | * losing the TCP connection. | ||
878 | * Set the flags anyway as long as they exist, in case this | ||
879 | * will be changed in the firmware. | ||
880 | */ | ||
881 | wowlan_config_cmd.wakeup_filter |= | ||
882 | cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | | ||
883 | IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | | ||
884 | IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | | ||
885 | IWL_WOWLAN_WAKEUP_LINK_CHANGE); | ||
886 | } | ||
887 | |||
633 | iwl_mvm_cancel_scan(mvm); | 888 | iwl_mvm_cancel_scan(mvm); |
634 | 889 | ||
635 | iwl_trans_stop_device(mvm->trans); | 890 | iwl_trans_stop_device(mvm->trans); |
@@ -649,6 +904,11 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
649 | /* We reprogram keys and shouldn't allocate new key indices */ | 904 | /* We reprogram keys and shouldn't allocate new key indices */ |
650 | memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); | 905 | memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); |
651 | 906 | ||
907 | mvm->ptk_ivlen = 0; | ||
908 | mvm->ptk_icvlen = 0; | ||
909 | mvm->ptk_ivlen = 0; | ||
910 | mvm->ptk_icvlen = 0; | ||
911 | |||
652 | /* | 912 | /* |
653 | * The D3 firmware still hardcodes the AP station ID for the | 913 | * The D3 firmware still hardcodes the AP station ID for the |
654 | * BSS we're associated with as 0. As a result, we have to move | 914 | * BSS we're associated with as 0. As a result, we have to move |
@@ -740,6 +1000,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
740 | if (ret) | 1000 | if (ret) |
741 | goto out; | 1001 | goto out; |
742 | 1002 | ||
1003 | ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp); | ||
1004 | if (ret) | ||
1005 | goto out; | ||
1006 | |||
743 | /* must be last -- this switches firmware state */ | 1007 | /* must be last -- this switches firmware state */ |
744 | ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, | 1008 | ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SYNC, |
745 | sizeof(d3_cfg_cmd), &d3_cfg_cmd); | 1009 | sizeof(d3_cfg_cmd), &d3_cfg_cmd); |
@@ -783,7 +1047,6 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | |||
783 | struct iwl_wowlan_status *status; | 1047 | struct iwl_wowlan_status *status; |
784 | u32 reasons; | 1048 | u32 reasons; |
785 | int ret, len; | 1049 | int ret, len; |
786 | bool pkt8023 = false; | ||
787 | struct sk_buff *pkt = NULL; | 1050 | struct sk_buff *pkt = NULL; |
788 | 1051 | ||
789 | iwl_trans_read_mem_bytes(mvm->trans, base, | 1052 | iwl_trans_read_mem_bytes(mvm->trans, base, |
@@ -824,7 +1087,8 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | |||
824 | status = (void *)cmd.resp_pkt->data; | 1087 | status = (void *)cmd.resp_pkt->data; |
825 | 1088 | ||
826 | if (len - sizeof(struct iwl_cmd_header) != | 1089 | if (len - sizeof(struct iwl_cmd_header) != |
827 | sizeof(*status) + le32_to_cpu(status->wake_packet_bufsize)) { | 1090 | sizeof(*status) + |
1091 | ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { | ||
828 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); | 1092 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); |
829 | goto out; | 1093 | goto out; |
830 | } | 1094 | } |
@@ -836,61 +1100,105 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | |||
836 | goto report; | 1100 | goto report; |
837 | } | 1101 | } |
838 | 1102 | ||
839 | if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) { | 1103 | if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) |
840 | wakeup.magic_pkt = true; | 1104 | wakeup.magic_pkt = true; |
841 | pkt8023 = true; | ||
842 | } | ||
843 | 1105 | ||
844 | if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) { | 1106 | if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) |
845 | wakeup.pattern_idx = | 1107 | wakeup.pattern_idx = |
846 | le16_to_cpu(status->pattern_number); | 1108 | le16_to_cpu(status->pattern_number); |
847 | pkt8023 = true; | ||
848 | } | ||
849 | 1109 | ||
850 | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | | 1110 | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | |
851 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) | 1111 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) |
852 | wakeup.disconnect = true; | 1112 | wakeup.disconnect = true; |
853 | 1113 | ||
854 | if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) { | 1114 | if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) |
855 | wakeup.gtk_rekey_failure = true; | 1115 | wakeup.gtk_rekey_failure = true; |
856 | pkt8023 = true; | ||
857 | } | ||
858 | 1116 | ||
859 | if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) { | 1117 | if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) |
860 | wakeup.rfkill_release = true; | 1118 | wakeup.rfkill_release = true; |
861 | pkt8023 = true; | ||
862 | } | ||
863 | 1119 | ||
864 | if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) { | 1120 | if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) |
865 | wakeup.eap_identity_req = true; | 1121 | wakeup.eap_identity_req = true; |
866 | pkt8023 = true; | ||
867 | } | ||
868 | 1122 | ||
869 | if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) { | 1123 | if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) |
870 | wakeup.four_way_handshake = true; | 1124 | wakeup.four_way_handshake = true; |
871 | pkt8023 = true; | 1125 | |
872 | } | 1126 | if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) |
1127 | wakeup.tcp_connlost = true; | ||
1128 | |||
1129 | if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) | ||
1130 | wakeup.tcp_nomoretokens = true; | ||
1131 | |||
1132 | if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) | ||
1133 | wakeup.tcp_match = true; | ||
873 | 1134 | ||
874 | if (status->wake_packet_bufsize) { | 1135 | if (status->wake_packet_bufsize) { |
875 | u32 pktsize = le32_to_cpu(status->wake_packet_bufsize); | 1136 | int pktsize = le32_to_cpu(status->wake_packet_bufsize); |
876 | u32 pktlen = le32_to_cpu(status->wake_packet_length); | 1137 | int pktlen = le32_to_cpu(status->wake_packet_length); |
1138 | const u8 *pktdata = status->wake_packet; | ||
1139 | struct ieee80211_hdr *hdr = (void *)pktdata; | ||
1140 | int truncated = pktlen - pktsize; | ||
1141 | |||
1142 | /* this would be a firmware bug */ | ||
1143 | if (WARN_ON_ONCE(truncated < 0)) | ||
1144 | truncated = 0; | ||
1145 | |||
1146 | if (ieee80211_is_data(hdr->frame_control)) { | ||
1147 | int hdrlen = ieee80211_hdrlen(hdr->frame_control); | ||
1148 | int ivlen = 0, icvlen = 4; /* also FCS */ | ||
877 | 1149 | ||
878 | if (pkt8023) { | ||
879 | pkt = alloc_skb(pktsize, GFP_KERNEL); | 1150 | pkt = alloc_skb(pktsize, GFP_KERNEL); |
880 | if (!pkt) | 1151 | if (!pkt) |
881 | goto report; | 1152 | goto report; |
882 | memcpy(skb_put(pkt, pktsize), status->wake_packet, | 1153 | |
883 | pktsize); | 1154 | memcpy(skb_put(pkt, hdrlen), pktdata, hdrlen); |
1155 | pktdata += hdrlen; | ||
1156 | pktsize -= hdrlen; | ||
1157 | |||
1158 | if (ieee80211_has_protected(hdr->frame_control)) { | ||
1159 | if (is_multicast_ether_addr(hdr->addr1)) { | ||
1160 | ivlen = mvm->gtk_ivlen; | ||
1161 | icvlen += mvm->gtk_icvlen; | ||
1162 | } else { | ||
1163 | ivlen = mvm->ptk_ivlen; | ||
1164 | icvlen += mvm->ptk_icvlen; | ||
1165 | } | ||
1166 | } | ||
1167 | |||
1168 | /* if truncated, FCS/ICV is (partially) gone */ | ||
1169 | if (truncated >= icvlen) { | ||
1170 | icvlen = 0; | ||
1171 | truncated -= icvlen; | ||
1172 | } else { | ||
1173 | icvlen -= truncated; | ||
1174 | truncated = 0; | ||
1175 | } | ||
1176 | |||
1177 | pktsize -= ivlen + icvlen; | ||
1178 | pktdata += ivlen; | ||
1179 | |||
1180 | memcpy(skb_put(pkt, pktsize), pktdata, pktsize); | ||
1181 | |||
884 | if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) | 1182 | if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) |
885 | goto report; | 1183 | goto report; |
886 | wakeup.packet = pkt->data; | 1184 | wakeup.packet = pkt->data; |
887 | wakeup.packet_present_len = pkt->len; | 1185 | wakeup.packet_present_len = pkt->len; |
888 | wakeup.packet_len = pkt->len - (pktlen - pktsize); | 1186 | wakeup.packet_len = pkt->len - truncated; |
889 | wakeup.packet_80211 = false; | 1187 | wakeup.packet_80211 = false; |
890 | } else { | 1188 | } else { |
1189 | int fcslen = 4; | ||
1190 | |||
1191 | if (truncated >= 4) { | ||
1192 | truncated -= 4; | ||
1193 | fcslen = 0; | ||
1194 | } else { | ||
1195 | fcslen -= truncated; | ||
1196 | truncated = 0; | ||
1197 | } | ||
1198 | pktsize -= fcslen; | ||
891 | wakeup.packet = status->wake_packet; | 1199 | wakeup.packet = status->wake_packet; |
892 | wakeup.packet_present_len = pktsize; | 1200 | wakeup.packet_present_len = pktsize; |
893 | wakeup.packet_len = pktlen; | 1201 | wakeup.packet_len = pktlen - truncated; |
894 | wakeup.packet_80211 = true; | 1202 | wakeup.packet_80211 = true; |
895 | } | 1203 | } |
896 | } | 1204 | } |