diff options
Diffstat (limited to 'drivers/net/wireless/ath/carl9170/fw.c')
| -rw-r--r-- | drivers/net/wireless/ath/carl9170/fw.c | 177 | 
1 files changed, 111 insertions, 66 deletions
diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index ae6c006bbc5..47d5c2e910a 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -23,15 +23,11 @@  #include <linux/kernel.h>  #include <linux/firmware.h>  #include <linux/crc32.h> +#include <linux/module.h>  #include "carl9170.h"  #include "fwcmd.h"  #include "version.h" -#define MAKE_STR(symbol) #symbol -#define TO_STR(symbol) MAKE_STR(symbol) -#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER) -MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT); -  static const u8 otus_magic[4] = { OTUS_MAGIC };  static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], @@ -145,11 +141,15 @@ static bool valid_cpu_addr(const u32 address)  	return false;  } -static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) +static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data, +				size_t len)  {  	const struct carl9170fw_otus_desc *otus_desc; -	const struct carl9170fw_chk_desc *chk_desc;  	const struct carl9170fw_last_desc *last_desc; +	const struct carl9170fw_chk_desc *chk_desc; +	unsigned long fin, diff; +	unsigned int dsc_len; +	u32 crc32;  	last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,  		sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER); @@ -167,36 +167,86 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)  	chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,  		sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER); -	if (chk_desc) { -		unsigned long fin, diff; -		unsigned int dsc_len; -		u32 crc32; +	if (!chk_desc) { +		dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); +		return 0; +	} -		dsc_len = min_t(unsigned int, len, +	dsc_len = min_t(unsigned int, len,  			(unsigned long)chk_desc - (unsigned long)otus_desc); -		fin = (unsigned long) last_desc + sizeof(*last_desc); -		diff = fin - (unsigned long) otus_desc; +	fin = (unsigned long) last_desc + sizeof(*last_desc); +	diff = fin - (unsigned long) otus_desc; -		if (diff < len) -			len -= diff; +	if (diff < len) +		len -= diff; -		if (len < 256) -			return -EIO; +	if (len < 256) +		return -EIO; -		crc32 = crc32_le(~0, data, len); -		if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { -			dev_err(&ar->udev->dev, "fw checksum test failed.\n"); -			return -ENOEXEC; -		} +	crc32 = crc32_le(~0, data, len); +	if (cpu_to_le32(crc32) != chk_desc->fw_crc32) { +		dev_err(&ar->udev->dev, "fw checksum test failed.\n"); +		return -ENOEXEC; +	} + +	crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); +	if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { +		dev_err(&ar->udev->dev, "descriptor check failed.\n"); +		return -EINVAL; +	} +	return 0; +} + +static int carl9170_fw_tx_sequence(struct ar9170 *ar) +{ +	const struct carl9170fw_txsq_desc *txsq_desc; -		crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len); -		if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) { -			dev_err(&ar->udev->dev, "descriptor check failed.\n"); +	txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc), +					  CARL9170FW_TXSQ_DESC_CUR_VER); +	if (txsq_desc) { +		ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr); +		if (!valid_cpu_addr(ar->fw.tx_seq_table))  			return -EINVAL; -		}  	} else { -		dev_warn(&ar->udev->dev, "Unprotected firmware image.\n"); +		ar->fw.tx_seq_table = 0; +	} + +	return 0; +} + +static void carl9170_fw_set_if_combinations(struct ar9170 *ar, +					    u16 if_comb_types) +{ +	if (ar->fw.vif_num < 2) +		return; + +	ar->if_comb_limits[0].max = ar->fw.vif_num; +	ar->if_comb_limits[0].types = if_comb_types; + +	ar->if_combs[0].num_different_channels = 1; +	ar->if_combs[0].max_interfaces = ar->fw.vif_num; +	ar->if_combs[0].limits = ar->if_comb_limits; +	ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits); + +	ar->hw->wiphy->iface_combinations = ar->if_combs; +	ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs); +} + +static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) +{ +	const struct carl9170fw_otus_desc *otus_desc; +	int err; +	u16 if_comb_types; + +	err = carl9170_fw_checksum(ar, data, len); +	if (err) +		return err; + +	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC, +		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER); +	if (!otus_desc) { +		return -ENODATA;  	}  #define SUPP(feat)						\ @@ -232,10 +282,10 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)  	if (!SUPP(CARL9170FW_COMMAND_CAM)) {  		dev_info(&ar->udev->dev, "crypto offloading is disabled "  			 "by firmware.\n"); -		ar->disable_offload = true; +		ar->fw.disable_offload_fw = true;  	} -	if (SUPP(CARL9170FW_PSM)) +	if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))  		ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;  	if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) { @@ -264,6 +314,18 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)  			FIF_PROMISC_IN_BSS;  	} +	if (SUPP(CARL9170FW_HW_COUNTERS)) +		ar->fw.hw_counters = true; + +	if (SUPP(CARL9170FW_WOL)) +		device_set_wakeup_enable(&ar->udev->dev, true); + +	if (SUPP(CARL9170FW_RX_BA_FILTER)) +		ar->fw.ba_filter = true; + +	if_comb_types = BIT(NL80211_IFTYPE_STATION) | +			BIT(NL80211_IFTYPE_P2P_CLIENT); +  	ar->fw.vif_num = otus_desc->vif_num;  	ar->fw.cmd_bufs = otus_desc->cmd_bufs;  	ar->fw.address = le32_to_cpu(otus_desc->fw_address); @@ -290,13 +352,29 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)  		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);  		if (SUPP(CARL9170FW_WLANTX_CAB)) { -			ar->hw->wiphy->interface_modes |= -				BIT(NL80211_IFTYPE_AP); +			if_comb_types |= +				BIT(NL80211_IFTYPE_AP) | +				BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_MAC80211_MESH +			if_comb_types |= +				BIT(NL80211_IFTYPE_MESH_POINT); +#endif /* CONFIG_MAC80211_MESH */  		}  	} +	carl9170_fw_set_if_combinations(ar, if_comb_types); + +	ar->hw->wiphy->interface_modes |= if_comb_types; + +	ar->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + +	/* As IBSS Encryption is software-based, IBSS RSN is supported. */ +	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | +		 WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_SUPPORTS_TDLS; +  #undef SUPPORTED -	return 0; +	return carl9170_fw_tx_sequence(ar);  }  static struct carl9170fw_desc_head * @@ -329,39 +407,6 @@ carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)  	return (void *)&fw_data[scan - found];  } -int carl9170_fw_fix_eeprom(struct ar9170 *ar) -{ -	const struct carl9170fw_fix_desc *fix_desc = NULL; -	unsigned int i, n, off; -	u32 *data = (void *)&ar->eeprom; - -	fix_desc = carl9170_fw_find_desc(ar, FIX_MAGIC, -		sizeof(*fix_desc), CARL9170FW_FIX_DESC_CUR_VER); - -	if (!fix_desc) -		return 0; - -	n = (le16_to_cpu(fix_desc->head.length) - sizeof(*fix_desc)) / -	    sizeof(struct carl9170fw_fix_entry); - -	for (i = 0; i < n; i++) { -		off = le32_to_cpu(fix_desc->data[i].address) - -		      AR9170_EEPROM_START; - -		if (off >= sizeof(struct ar9170_eeprom) || (off & 3)) { -			dev_err(&ar->udev->dev, "Skip invalid entry %d\n", i); -			continue; -		} - -		data[off / sizeof(*data)] &= -			le32_to_cpu(fix_desc->data[i].mask); -		data[off / sizeof(*data)] |= -			le32_to_cpu(fix_desc->data[i].value); -	} - -	return 0; -} -  int carl9170_parse_firmware(struct ar9170 *ar)  {  	const struct carl9170fw_desc_head *fw_desc = NULL;  | 
