diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/wow.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath10k/wow.c | 138 | 
1 files changed, 133 insertions, 5 deletions
diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index c4cbccb29b31..a6b179f88d36 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -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;  | 
