|
@@ -1,5 +1,6 @@
|
|
|
/*
|
|
|
* Copyright (c) 2015-2017 Qualcomm Atheros, Inc.
|
|
|
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
|
|
|
*
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
@@ -76,6 +77,109 @@ static int ath10k_wow_cleanup(struct ath10k *ar)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Convert a 802.3 format to a 802.11 format.
|
|
|
+ * +------------+-----------+--------+----------------+
|
|
|
+ * 802.3: |dest mac(6B)|src mac(6B)|type(2B)| body... |
|
|
|
+ * +------------+-----------+--------+----------------+
|
|
|
+ * |__ |_______ |____________ |________
|
|
|
+ * | | | |
|
|
|
+ * +--+------------+----+-----------+---------------+-----------+
|
|
|
+ * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)| 8B |type(2B)| body... |
|
|
|
+ * +--+------------+----+-----------+---------------+-----------+
|
|
|
+ */
|
|
|
+static void ath10k_wow_convert_8023_to_80211
|
|
|
+ (struct cfg80211_pkt_pattern *new,
|
|
|
+ const struct cfg80211_pkt_pattern *old)
|
|
|
+{
|
|
|
+ u8 hdr_8023_pattern[ETH_HLEN] = {};
|
|
|
+ u8 hdr_8023_bit_mask[ETH_HLEN] = {};
|
|
|
+ u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
|
|
|
+ u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
|
|
|
+
|
|
|
+ int total_len = old->pkt_offset + old->pattern_len;
|
|
|
+ int hdr_80211_end_offset;
|
|
|
+
|
|
|
+ struct ieee80211_hdr_3addr *new_hdr_pattern =
|
|
|
+ (struct ieee80211_hdr_3addr *)hdr_80211_pattern;
|
|
|
+ struct ieee80211_hdr_3addr *new_hdr_mask =
|
|
|
+ (struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
|
|
|
+ struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
|
|
|
+ struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
|
|
|
+ int hdr_len = sizeof(*new_hdr_pattern);
|
|
|
+
|
|
|
+ struct rfc1042_hdr *new_rfc_pattern =
|
|
|
+ (struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
|
|
|
+ struct rfc1042_hdr *new_rfc_mask =
|
|
|
+ (struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
|
|
|
+ int rfc_len = sizeof(*new_rfc_pattern);
|
|
|
+
|
|
|
+ memcpy(hdr_8023_pattern + old->pkt_offset,
|
|
|
+ old->pattern, ETH_HLEN - old->pkt_offset);
|
|
|
+ memcpy(hdr_8023_bit_mask + old->pkt_offset,
|
|
|
+ old->mask, ETH_HLEN - old->pkt_offset);
|
|
|
+
|
|
|
+ /* Copy destination address */
|
|
|
+ memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
|
|
|
+ memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
|
|
|
+
|
|
|
+ /* Copy source address */
|
|
|
+ memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
|
|
|
+ memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
|
|
|
+
|
|
|
+ /* Copy logic link type */
|
|
|
+ memcpy(&new_rfc_pattern->snap_type,
|
|
|
+ &old_hdr_pattern->h_proto,
|
|
|
+ sizeof(old_hdr_pattern->h_proto));
|
|
|
+ memcpy(&new_rfc_mask->snap_type,
|
|
|
+ &old_hdr_mask->h_proto,
|
|
|
+ sizeof(old_hdr_mask->h_proto));
|
|
|
+
|
|
|
+ /* Caculate new pkt_offset */
|
|
|
+ if (old->pkt_offset < ETH_ALEN)
|
|
|
+ new->pkt_offset = old->pkt_offset +
|
|
|
+ offsetof(struct ieee80211_hdr_3addr, addr1);
|
|
|
+ else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
|
|
|
+ new->pkt_offset = old->pkt_offset +
|
|
|
+ offsetof(struct ieee80211_hdr_3addr, addr3) -
|
|
|
+ offsetof(struct ethhdr, h_source);
|
|
|
+ else
|
|
|
+ new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
|
|
|
+
|
|
|
+ /* Caculate new hdr end offset */
|
|
|
+ if (total_len > ETH_HLEN)
|
|
|
+ hdr_80211_end_offset = hdr_len + rfc_len;
|
|
|
+ else if (total_len > offsetof(struct ethhdr, h_proto))
|
|
|
+ hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
|
|
|
+ else if (total_len > ETH_ALEN)
|
|
|
+ hdr_80211_end_offset = total_len - ETH_ALEN +
|
|
|
+ offsetof(struct ieee80211_hdr_3addr, addr3);
|
|
|
+ else
|
|
|
+ hdr_80211_end_offset = total_len +
|
|
|
+ offsetof(struct ieee80211_hdr_3addr, addr1);
|
|
|
+
|
|
|
+ new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
|
|
|
+
|
|
|
+ memcpy((u8 *)new->pattern,
|
|
|
+ hdr_80211_pattern + new->pkt_offset,
|
|
|
+ new->pattern_len);
|
|
|
+ memcpy((u8 *)new->mask,
|
|
|
+ hdr_80211_bit_mask + new->pkt_offset,
|
|
|
+ new->pattern_len);
|
|
|
+
|
|
|
+ if (total_len > ETH_HLEN) {
|
|
|
+ /* Copy frame body */
|
|
|
+ memcpy((u8 *)new->pattern + new->pattern_len,
|
|
|
+ (void *)old->pattern + ETH_HLEN - old->pkt_offset,
|
|
|
+ total_len - ETH_HLEN);
|
|
|
+ memcpy((u8 *)new->mask + new->pattern_len,
|
|
|
+ (void *)old->mask + ETH_HLEN - old->pkt_offset,
|
|
|
+ total_len - ETH_HLEN);
|
|
|
+
|
|
|
+ new->pattern_len += total_len - ETH_HLEN;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
|
|
|
struct cfg80211_wowlan *wowlan)
|
|
|
{
|
|
@@ -116,22 +220,40 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif,
|
|
|
|
|
|
for (i = 0; i < wowlan->n_patterns; i++) {
|
|
|
u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
|
|
|
+ u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
|
|
|
+ u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
|
|
|
+ struct cfg80211_pkt_pattern new_pattern = {};
|
|
|
+ struct cfg80211_pkt_pattern old_pattern = patterns[i];
|
|
|
int j;
|
|
|
|
|
|
+ new_pattern.pattern = ath_pattern;
|
|
|
+ new_pattern.mask = ath_bitmask;
|
|
|
if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
|
|
|
continue;
|
|
|
-
|
|
|
/* convert bytemask to bitmask */
|
|
|
for (j = 0; j < patterns[i].pattern_len; j++)
|
|
|
if (patterns[i].mask[j / 8] & BIT(j % 8))
|
|
|
bitmask[j] = 0xff;
|
|
|
+ old_pattern.mask = bitmask;
|
|
|
+ new_pattern = old_pattern;
|
|
|
+
|
|
|
+ if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
|
|
|
+ if (patterns[i].pkt_offset < ETH_HLEN)
|
|
|
+ ath10k_wow_convert_8023_to_80211(&new_pattern,
|
|
|
+ &old_pattern);
|
|
|
+ else
|
|
|
+ new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
ret = ath10k_wmi_wow_add_pattern(ar, arvif->vdev_id,
|
|
|
pattern_id,
|
|
|
- patterns[i].pattern,
|
|
|
- bitmask,
|
|
|
- patterns[i].pattern_len,
|
|
|
- patterns[i].pkt_offset);
|
|
|
+ new_pattern.pattern,
|
|
|
+ new_pattern.mask,
|
|
|
+ new_pattern.pattern_len,
|
|
|
+ new_pattern.pkt_offset);
|
|
|
if (ret) {
|
|
|
ath10k_warn(ar, "failed to add pattern %i to vdev %i: %d\n",
|
|
|
pattern_id,
|
|
@@ -345,6 +467,12 @@ int ath10k_wow_init(struct ath10k *ar)
|
|
|
return -EINVAL;
|
|
|
|
|
|
ar->wow.wowlan_support = ath10k_wowlan_support;
|
|
|
+
|
|
|
+ if (ar->wmi.rx_decap_mode == ATH10K_HW_TXRX_NATIVE_WIFI) {
|
|
|
+ ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
|
|
|
+ ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
|
|
|
+ }
|
|
|
+
|
|
|
ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
|
|
|
ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
|
|
|
|