aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-22 14:41:58 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-03-06 10:47:51 -0500
commitf0c2646af4f7432f7414e1162377cada06c3c747 (patch)
treee3fa41b5a8e61d795072e89b040b099117ce38ea
parentba5295f8b2c789275cc6ffb0a45e50a8aa3a5c84 (diff)
iwlwifi: mvm: implement remote wake
With remote wake, the firmware creates a TCP connection and sends some configurable data on it, until a special TCP data packet from the server is received that triggers a wakeup. The configuration is a bit tricky because it is based on packet pattern matching but this is hidden in the driver and the exposed API in cfg80211 is just based on the required TCP connection parameters. Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c258
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h51
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c26
3 files changed, 334 insertions, 1 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c
index 376a5776db90..d4578cefe445 100644
--- a/drivers/net/wireless/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/iwlwifi/mvm/d3.c
@@ -62,8 +62,10 @@
62 *****************************************************************************/ 62 *****************************************************************************/
63 63
64#include <linux/etherdevice.h> 64#include <linux/etherdevice.h>
65#include <linux/ip.h>
65#include <net/cfg80211.h> 66#include <net/cfg80211.h>
66#include <net/ipv6.h> 67#include <net/ipv6.h>
68#include <net/tcp.h>
67#include "iwl-modparams.h" 69#include "iwl-modparams.h"
68#include "fw-api.h" 70#include "fw-api.h"
69#include "mvm.h" 71#include "mvm.h"
@@ -402,6 +404,233 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
402 sizeof(cmd), &cmd); 404 sizeof(cmd), &cmd);
403} 405}
404 406
407enum 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
416static __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
422static 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
541static 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
405struct iwl_d3_iter_data { 634struct iwl_d3_iter_data {
406 struct iwl_mvm *mvm; 635 struct iwl_mvm *mvm;
407 struct ieee80211_vif *vif; 636 struct ieee80211_vif *vif;
@@ -640,6 +869,22 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
640 d3_cfg_cmd.wakeup_flags |= 869 d3_cfg_cmd.wakeup_flags |=
641 cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); 870 cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);
642 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
643 iwl_mvm_cancel_scan(mvm); 888 iwl_mvm_cancel_scan(mvm);
644 889
645 iwl_trans_stop_device(mvm->trans); 890 iwl_trans_stop_device(mvm->trans);
@@ -755,6 +1000,10 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
755 if (ret) 1000 if (ret)
756 goto out; 1001 goto out;
757 1002
1003 ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
1004 if (ret)
1005 goto out;
1006
758 /* must be last -- this switches firmware state */ 1007 /* must be last -- this switches firmware state */
759 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,
760 sizeof(d3_cfg_cmd), &d3_cfg_cmd); 1009 sizeof(d3_cfg_cmd), &d3_cfg_cmd);
@@ -874,6 +1123,15 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
874 if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) 1123 if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE)
875 wakeup.four_way_handshake = true; 1124 wakeup.four_way_handshake = true;
876 1125
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;
1134
877 if (status->wake_packet_bufsize) { 1135 if (status->wake_packet_bufsize) {
878 int pktsize = le32_to_cpu(status->wake_packet_bufsize); 1136 int pktsize = le32_to_cpu(status->wake_packet_bufsize);
879 int pktlen = le32_to_cpu(status->wake_packet_length); 1137 int pktlen = le32_to_cpu(status->wake_packet_length);
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
index a442ee11a062..51e015d1dfb2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
@@ -258,7 +258,7 @@ enum iwl_wowlan_wakeup_reason {
258 IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8), 258 IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE = BIT(8),
259 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9), 259 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS = BIT(9),
260 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10), 260 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE = BIT(10),
261 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_TCP_EXTERNAL = BIT(11), 261 /* BIT(11) reserved */
262 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), 262 IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12),
263}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ 263}; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */
264 264
@@ -277,6 +277,55 @@ struct iwl_wowlan_status {
277 u8 wake_packet[]; /* can be truncated from _length to _bufsize */ 277 u8 wake_packet[]; /* can be truncated from _length to _bufsize */
278} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */ 278} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */
279 279
280#define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64
281#define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128
282#define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048
283
284struct iwl_tcp_packet_info {
285 __le16 tcp_pseudo_header_checksum;
286 __le16 tcp_payload_length;
287} __packed; /* TCP_PACKET_INFO_API_S_VER_2 */
288
289struct iwl_tcp_packet {
290 struct iwl_tcp_packet_info info;
291 u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
292 u8 data[IWL_WOWLAN_TCP_MAX_PACKET_LEN];
293} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
294
295struct iwl_remote_wake_packet {
296 struct iwl_tcp_packet_info info;
297 u8 rx_mask[IWL_WOWLAN_MAX_PATTERN_LEN / 8];
298 u8 data[IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN];
299} __packed; /* TCP_PROTOCOL_PACKET_API_S_VER_1 */
300
301struct iwl_wowlan_remote_wake_config {
302 __le32 connection_max_time; /* unused */
303 /* TCP_PROTOCOL_CONFIG_API_S_VER_1 */
304 u8 max_syn_retries;
305 u8 max_data_retries;
306 u8 tcp_syn_ack_timeout;
307 u8 tcp_ack_timeout;
308
309 struct iwl_tcp_packet syn_tx;
310 struct iwl_tcp_packet synack_rx;
311 struct iwl_tcp_packet keepalive_ack_rx;
312 struct iwl_tcp_packet fin_tx;
313
314 struct iwl_remote_wake_packet keepalive_tx;
315 struct iwl_remote_wake_packet wake_rx;
316
317 /* REMOTE_WAKE_OFFSET_INFO_API_S_VER_1 */
318 u8 sequence_number_offset;
319 u8 sequence_number_length;
320 u8 token_offset;
321 u8 token_length;
322 /* REMOTE_WAKE_PROTOCOL_PARAMS_API_S_VER_1 */
323 __le32 initial_sequence_number;
324 __le16 keepalive_interval;
325 __le16 num_tokens;
326 u8 tokens[IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS];
327} __packed; /* REMOTE_WAKE_CONFIG_API_S_VER_2 */
328
280/* TODO: NetDetect API */ 329/* TODO: NetDetect API */
281 330
282#endif /* __fw_api_d3_h__ */ 331#endif /* __fw_api_d3_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 924cabf88b26..ed2d8754c82b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -65,7 +65,9 @@
65#include <linux/skbuff.h> 65#include <linux/skbuff.h>
66#include <linux/netdevice.h> 66#include <linux/netdevice.h>
67#include <linux/etherdevice.h> 67#include <linux/etherdevice.h>
68#include <linux/ip.h>
68#include <net/mac80211.h> 69#include <net/mac80211.h>
70#include <net/tcp.h>
69 71
70#include "iwl-op-mode.h" 72#include "iwl-op-mode.h"
71#include "iwl-io.h" 73#include "iwl-io.h"
@@ -102,6 +104,29 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = {
102 }, 104 },
103}; 105};
104 106
107#ifdef CONFIG_PM_SLEEP
108static const struct nl80211_wowlan_tcp_data_token_feature
109iwl_mvm_wowlan_tcp_token_feature = {
110 .min_len = 0,
111 .max_len = 255,
112 .bufsize = IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS,
113};
114
115static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = {
116 .tok = &iwl_mvm_wowlan_tcp_token_feature,
117 .data_payload_max = IWL_WOWLAN_TCP_MAX_PACKET_LEN -
118 sizeof(struct ethhdr) -
119 sizeof(struct iphdr) -
120 sizeof(struct tcphdr),
121 .data_interval_max = 65535, /* __le16 in API */
122 .wake_payload_max = IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN -
123 sizeof(struct ethhdr) -
124 sizeof(struct iphdr) -
125 sizeof(struct tcphdr),
126 .seq = true,
127};
128#endif
129
105int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) 130int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
106{ 131{
107 struct ieee80211_hw *hw = mvm->hw; 132 struct ieee80211_hw *hw = mvm->hw;
@@ -210,6 +235,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
210 hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; 235 hw->wiphy->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS;
211 hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; 236 hw->wiphy->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN;
212 hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; 237 hw->wiphy->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN;
238 hw->wiphy->wowlan.tcp = &iwl_mvm_wowlan_tcp_support;
213 } 239 }
214#endif 240#endif
215 241