1fbf80cd3SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
200c4da27SChristian Lamparter /*
300c4da27SChristian Lamparter  * Atheros CARL9170 driver
400c4da27SChristian Lamparter  *
500c4da27SChristian Lamparter  * firmware parser
600c4da27SChristian Lamparter  *
700c4da27SChristian Lamparter  * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
800c4da27SChristian Lamparter  */
900c4da27SChristian Lamparter 
1000c4da27SChristian Lamparter #include <linux/kernel.h>
1100c4da27SChristian Lamparter #include <linux/firmware.h>
1200c4da27SChristian Lamparter #include <linux/crc32.h>
139d9779e7SPaul Gortmaker #include <linux/module.h>
1400c4da27SChristian Lamparter #include "carl9170.h"
1500c4da27SChristian Lamparter #include "fwcmd.h"
1600c4da27SChristian Lamparter #include "version.h"
1700c4da27SChristian Lamparter 
1800c4da27SChristian Lamparter static const u8 otus_magic[4] = { OTUS_MAGIC };
1900c4da27SChristian Lamparter 
carl9170_fw_find_desc(struct ar9170 * ar,const u8 descid[4],const unsigned int len,const u8 compatible_revision)2000c4da27SChristian Lamparter static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
2100c4da27SChristian Lamparter 	const unsigned int len, const u8 compatible_revision)
2200c4da27SChristian Lamparter {
2300c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *iter;
2400c4da27SChristian Lamparter 
2500c4da27SChristian Lamparter 	carl9170fw_for_each_hdr(iter, ar->fw.desc) {
2600c4da27SChristian Lamparter 		if (carl9170fw_desc_cmp(iter, descid, len,
2700c4da27SChristian Lamparter 					compatible_revision))
2800c4da27SChristian Lamparter 			return (void *)iter;
2900c4da27SChristian Lamparter 	}
3000c4da27SChristian Lamparter 
3100c4da27SChristian Lamparter 	/* needed to find the LAST desc */
3200c4da27SChristian Lamparter 	if (carl9170fw_desc_cmp(iter, descid, len,
3300c4da27SChristian Lamparter 				compatible_revision))
3400c4da27SChristian Lamparter 		return (void *)iter;
3500c4da27SChristian Lamparter 
3600c4da27SChristian Lamparter 	return NULL;
3700c4da27SChristian Lamparter }
3800c4da27SChristian Lamparter 
carl9170_fw_verify_descs(struct ar9170 * ar,const struct carl9170fw_desc_head * head,unsigned int max_len)3900c4da27SChristian Lamparter static int carl9170_fw_verify_descs(struct ar9170 *ar,
4000c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *head, unsigned int max_len)
4100c4da27SChristian Lamparter {
4200c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *pos;
4300c4da27SChristian Lamparter 	unsigned long pos_addr, end_addr;
4400c4da27SChristian Lamparter 	unsigned int pos_length;
4500c4da27SChristian Lamparter 
4600c4da27SChristian Lamparter 	if (max_len < sizeof(*pos))
4700c4da27SChristian Lamparter 		return -ENODATA;
4800c4da27SChristian Lamparter 
4900c4da27SChristian Lamparter 	max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);
5000c4da27SChristian Lamparter 
5100c4da27SChristian Lamparter 	pos = head;
5200c4da27SChristian Lamparter 	pos_addr = (unsigned long) pos;
5300c4da27SChristian Lamparter 	end_addr = pos_addr + max_len;
5400c4da27SChristian Lamparter 
5500c4da27SChristian Lamparter 	while (pos_addr < end_addr) {
5600c4da27SChristian Lamparter 		if (pos_addr + sizeof(*head) > end_addr)
5700c4da27SChristian Lamparter 			return -E2BIG;
5800c4da27SChristian Lamparter 
5900c4da27SChristian Lamparter 		pos_length = le16_to_cpu(pos->length);
6000c4da27SChristian Lamparter 
6100c4da27SChristian Lamparter 		if (pos_length < sizeof(*head))
6200c4da27SChristian Lamparter 			return -EBADMSG;
6300c4da27SChristian Lamparter 
6400c4da27SChristian Lamparter 		if (pos_length > max_len)
6500c4da27SChristian Lamparter 			return -EOVERFLOW;
6600c4da27SChristian Lamparter 
6700c4da27SChristian Lamparter 		if (pos_addr + pos_length > end_addr)
6800c4da27SChristian Lamparter 			return -EMSGSIZE;
6900c4da27SChristian Lamparter 
7000c4da27SChristian Lamparter 		if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
7100c4da27SChristian Lamparter 					CARL9170FW_LAST_DESC_SIZE,
7200c4da27SChristian Lamparter 					CARL9170FW_LAST_DESC_CUR_VER))
7300c4da27SChristian Lamparter 			return 0;
7400c4da27SChristian Lamparter 
7500c4da27SChristian Lamparter 		pos_addr += pos_length;
7600c4da27SChristian Lamparter 		pos = (void *)pos_addr;
7700c4da27SChristian Lamparter 		max_len -= pos_length;
7800c4da27SChristian Lamparter 	}
7900c4da27SChristian Lamparter 	return -EINVAL;
8000c4da27SChristian Lamparter }
8100c4da27SChristian Lamparter 
carl9170_fw_info(struct ar9170 * ar)8200c4da27SChristian Lamparter static void carl9170_fw_info(struct ar9170 *ar)
8300c4da27SChristian Lamparter {
8400c4da27SChristian Lamparter 	const struct carl9170fw_motd_desc *motd_desc;
8500c4da27SChristian Lamparter 	unsigned int str_ver_len;
8600c4da27SChristian Lamparter 	u32 fw_date;
8700c4da27SChristian Lamparter 
8800c4da27SChristian Lamparter 	dev_info(&ar->udev->dev, "driver   API: %s 2%03d-%02d-%02d [%d-%d]\n",
8900c4da27SChristian Lamparter 		CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
9000c4da27SChristian Lamparter 		CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
9100c4da27SChristian Lamparter 		CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);
9200c4da27SChristian Lamparter 
9300c4da27SChristian Lamparter 	motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
9400c4da27SChristian Lamparter 		sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);
9500c4da27SChristian Lamparter 
9600c4da27SChristian Lamparter 	if (motd_desc) {
9700c4da27SChristian Lamparter 		str_ver_len = strnlen(motd_desc->release,
9800c4da27SChristian Lamparter 			CARL9170FW_MOTD_RELEASE_LEN);
9900c4da27SChristian Lamparter 
10000c4da27SChristian Lamparter 		fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
10100c4da27SChristian Lamparter 
10200c4da27SChristian Lamparter 		dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
10300c4da27SChristian Lamparter 			 str_ver_len, motd_desc->release,
10400c4da27SChristian Lamparter 			 CARL9170FW_GET_YEAR(fw_date),
10500c4da27SChristian Lamparter 			 CARL9170FW_GET_MONTH(fw_date),
10600c4da27SChristian Lamparter 			 CARL9170FW_GET_DAY(fw_date));
10700c4da27SChristian Lamparter 
108*bf99f11dSWolfram Sang 		strscpy(ar->hw->wiphy->fw_version, motd_desc->release,
10900c4da27SChristian Lamparter 			sizeof(ar->hw->wiphy->fw_version));
11000c4da27SChristian Lamparter 	}
11100c4da27SChristian Lamparter }
11200c4da27SChristian Lamparter 
valid_dma_addr(const u32 address)11300c4da27SChristian Lamparter static bool valid_dma_addr(const u32 address)
11400c4da27SChristian Lamparter {
11500c4da27SChristian Lamparter 	if (address >= AR9170_SRAM_OFFSET &&
11600c4da27SChristian Lamparter 	    address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
11700c4da27SChristian Lamparter 		return true;
11800c4da27SChristian Lamparter 
11900c4da27SChristian Lamparter 	return false;
12000c4da27SChristian Lamparter }
12100c4da27SChristian Lamparter 
valid_cpu_addr(const u32 address)12200c4da27SChristian Lamparter static bool valid_cpu_addr(const u32 address)
12300c4da27SChristian Lamparter {
12400c4da27SChristian Lamparter 	if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
12500c4da27SChristian Lamparter 	    address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
12600c4da27SChristian Lamparter 		return true;
12700c4da27SChristian Lamparter 
12800c4da27SChristian Lamparter 	return false;
12900c4da27SChristian Lamparter }
13000c4da27SChristian Lamparter 
carl9170_fw_checksum(struct ar9170 * ar,const __u8 * data,size_t len)13165a69228SChristian Lamparter static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data,
13265a69228SChristian Lamparter 				size_t len)
13300c4da27SChristian Lamparter {
13400c4da27SChristian Lamparter 	const struct carl9170fw_otus_desc *otus_desc;
13500c4da27SChristian Lamparter 	const struct carl9170fw_last_desc *last_desc;
13665a69228SChristian Lamparter 	const struct carl9170fw_chk_desc *chk_desc;
13765a69228SChristian Lamparter 	unsigned long fin, diff;
13865a69228SChristian Lamparter 	unsigned int dsc_len;
13965a69228SChristian Lamparter 	u32 crc32;
14000c4da27SChristian Lamparter 
14100c4da27SChristian Lamparter 	last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,
14200c4da27SChristian Lamparter 		sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER);
14300c4da27SChristian Lamparter 	if (!last_desc)
14400c4da27SChristian Lamparter 		return -EINVAL;
14500c4da27SChristian Lamparter 
14600c4da27SChristian Lamparter 	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
14700c4da27SChristian Lamparter 		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
14800c4da27SChristian Lamparter 	if (!otus_desc) {
14900c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "failed to find compatible firmware "
15000c4da27SChristian Lamparter 			"descriptor.\n");
15100c4da27SChristian Lamparter 		return -ENODATA;
15200c4da27SChristian Lamparter 	}
15300c4da27SChristian Lamparter 
15400c4da27SChristian Lamparter 	chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
15500c4da27SChristian Lamparter 		sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);
15600c4da27SChristian Lamparter 
15765a69228SChristian Lamparter 	if (!chk_desc) {
15865a69228SChristian Lamparter 		dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
15965a69228SChristian Lamparter 		return 0;
16065a69228SChristian Lamparter 	}
16100c4da27SChristian Lamparter 
16200c4da27SChristian Lamparter 	dsc_len = min_t(unsigned int, len,
16300c4da27SChristian Lamparter 			(unsigned long)chk_desc - (unsigned long)otus_desc);
16400c4da27SChristian Lamparter 
16500c4da27SChristian Lamparter 	fin = (unsigned long) last_desc + sizeof(*last_desc);
16600c4da27SChristian Lamparter 	diff = fin - (unsigned long) otus_desc;
16700c4da27SChristian Lamparter 
16800c4da27SChristian Lamparter 	if (diff < len)
16900c4da27SChristian Lamparter 		len -= diff;
17000c4da27SChristian Lamparter 
17100c4da27SChristian Lamparter 	if (len < 256)
17200c4da27SChristian Lamparter 		return -EIO;
17300c4da27SChristian Lamparter 
17400c4da27SChristian Lamparter 	crc32 = crc32_le(~0, data, len);
17500c4da27SChristian Lamparter 	if (cpu_to_le32(crc32) != chk_desc->fw_crc32) {
17600c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "fw checksum test failed.\n");
17700c4da27SChristian Lamparter 		return -ENOEXEC;
17800c4da27SChristian Lamparter 	}
17900c4da27SChristian Lamparter 
18000c4da27SChristian Lamparter 	crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len);
18100c4da27SChristian Lamparter 	if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) {
18200c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "descriptor check failed.\n");
18300c4da27SChristian Lamparter 		return -EINVAL;
18400c4da27SChristian Lamparter 	}
18565a69228SChristian Lamparter 	return 0;
18665a69228SChristian Lamparter }
18765a69228SChristian Lamparter 
carl9170_fw_tx_sequence(struct ar9170 * ar)18865a69228SChristian Lamparter static int carl9170_fw_tx_sequence(struct ar9170 *ar)
18965a69228SChristian Lamparter {
19065a69228SChristian Lamparter 	const struct carl9170fw_txsq_desc *txsq_desc;
19165a69228SChristian Lamparter 
19265a69228SChristian Lamparter 	txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc),
19365a69228SChristian Lamparter 					  CARL9170FW_TXSQ_DESC_CUR_VER);
19465a69228SChristian Lamparter 	if (txsq_desc) {
19565a69228SChristian Lamparter 		ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr);
19665a69228SChristian Lamparter 		if (!valid_cpu_addr(ar->fw.tx_seq_table))
19765a69228SChristian Lamparter 			return -EINVAL;
19800c4da27SChristian Lamparter 	} else {
19965a69228SChristian Lamparter 		ar->fw.tx_seq_table = 0;
20065a69228SChristian Lamparter 	}
20165a69228SChristian Lamparter 
20265a69228SChristian Lamparter 	return 0;
20365a69228SChristian Lamparter }
20465a69228SChristian Lamparter 
carl9170_fw_set_if_combinations(struct ar9170 * ar,u16 if_comb_types)20517f658acSChristian Lamparter static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
20617f658acSChristian Lamparter 					    u16 if_comb_types)
20717f658acSChristian Lamparter {
20817f658acSChristian Lamparter 	if (ar->fw.vif_num < 2)
20917f658acSChristian Lamparter 		return;
21017f658acSChristian Lamparter 
21117f658acSChristian Lamparter 	ar->if_comb_limits[0].max = ar->fw.vif_num;
21217f658acSChristian Lamparter 	ar->if_comb_limits[0].types = if_comb_types;
21317f658acSChristian Lamparter 
21417f658acSChristian Lamparter 	ar->if_combs[0].num_different_channels = 1;
21517f658acSChristian Lamparter 	ar->if_combs[0].max_interfaces = ar->fw.vif_num;
21617f658acSChristian Lamparter 	ar->if_combs[0].limits = ar->if_comb_limits;
21717f658acSChristian Lamparter 	ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
21817f658acSChristian Lamparter 
21917f658acSChristian Lamparter 	ar->hw->wiphy->iface_combinations = ar->if_combs;
22017f658acSChristian Lamparter 	ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
22117f658acSChristian Lamparter }
22217f658acSChristian Lamparter 
carl9170_fw(struct ar9170 * ar,const __u8 * data,size_t len)22365a69228SChristian Lamparter static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
22465a69228SChristian Lamparter {
22565a69228SChristian Lamparter 	const struct carl9170fw_otus_desc *otus_desc;
22665a69228SChristian Lamparter 	int err;
22765a69228SChristian Lamparter 	u16 if_comb_types;
22865a69228SChristian Lamparter 
22965a69228SChristian Lamparter 	err = carl9170_fw_checksum(ar, data, len);
23065a69228SChristian Lamparter 	if (err)
23165a69228SChristian Lamparter 		return err;
23265a69228SChristian Lamparter 
23365a69228SChristian Lamparter 	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
23465a69228SChristian Lamparter 		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
23565a69228SChristian Lamparter 	if (!otus_desc) {
23665a69228SChristian Lamparter 		return -ENODATA;
23700c4da27SChristian Lamparter 	}
23800c4da27SChristian Lamparter 
23900c4da27SChristian Lamparter #define SUPP(feat)						\
24000c4da27SChristian Lamparter 	(carl9170fw_supports(otus_desc->feature_set, feat))
24100c4da27SChristian Lamparter 
24200c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
24300c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "invalid firmware descriptor "
24400c4da27SChristian Lamparter 			"format detected.\n");
24500c4da27SChristian Lamparter 		return -EINVAL;
24600c4da27SChristian Lamparter 	}
24700c4da27SChristian Lamparter 
24800c4da27SChristian Lamparter 	ar->fw.api_version = otus_desc->api_ver;
24900c4da27SChristian Lamparter 
25000c4da27SChristian Lamparter 	if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
25100c4da27SChristian Lamparter 	    ar->fw.api_version > CARL9170FW_API_MAX_VER) {
25200c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
25300c4da27SChristian Lamparter 		return -EINVAL;
25400c4da27SChristian Lamparter 	}
25500c4da27SChristian Lamparter 
25600c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
25700c4da27SChristian Lamparter 	    !SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
25800c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware does support "
25900c4da27SChristian Lamparter 			"mandatory features.\n");
26000c4da27SChristian Lamparter 		return -ECANCELED;
26100c4da27SChristian Lamparter 	}
26200c4da27SChristian Lamparter 
26300c4da27SChristian Lamparter 	if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
26400c4da27SChristian Lamparter 		__CARL9170FW_FEATURE_NUM) {
26500c4da27SChristian Lamparter 		dev_warn(&ar->udev->dev, "driver does not support all "
26600c4da27SChristian Lamparter 			 "firmware features.\n");
26700c4da27SChristian Lamparter 	}
26800c4da27SChristian Lamparter 
26900c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_COMMAND_CAM)) {
27000c4da27SChristian Lamparter 		dev_info(&ar->udev->dev, "crypto offloading is disabled "
27100c4da27SChristian Lamparter 			 "by firmware.\n");
2727a5c7307SChristian Lamparter 		ar->fw.disable_offload_fw = true;
27300c4da27SChristian Lamparter 	}
27400c4da27SChristian Lamparter 
2751205f543SChristian Lamparter 	if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
27630686bf7SJohannes Berg 		ieee80211_hw_set(ar->hw, SUPPORTS_PS);
27700c4da27SChristian Lamparter 
27800c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
27900c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware does not provide "
28000c4da27SChristian Lamparter 			"mandatory interfaces.\n");
28100c4da27SChristian Lamparter 		return -EINVAL;
28200c4da27SChristian Lamparter 	}
28300c4da27SChristian Lamparter 
28400c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_MINIBOOT))
28500c4da27SChristian Lamparter 		ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
28600c4da27SChristian Lamparter 	else
28700c4da27SChristian Lamparter 		ar->fw.offset = 0;
28800c4da27SChristian Lamparter 
28900c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
29000c4da27SChristian Lamparter 		ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
29100c4da27SChristian Lamparter 		ar->fw.tx_stream = true;
29200c4da27SChristian Lamparter 	}
29300c4da27SChristian Lamparter 
29400c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_USB_UP_STREAM))
29500c4da27SChristian Lamparter 		ar->fw.rx_stream = true;
29600c4da27SChristian Lamparter 
2975c895691SChristian Lamparter 	if (SUPP(CARL9170FW_RX_FILTER)) {
2985c895691SChristian Lamparter 		ar->fw.rx_filter = true;
2995c895691SChristian Lamparter 		ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL |
300df140465SJohannes Berg 			FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS;
3015c895691SChristian Lamparter 	}
3025c895691SChristian Lamparter 
3037ccc83b0SChristian Lamparter 	if (SUPP(CARL9170FW_HW_COUNTERS))
3047ccc83b0SChristian Lamparter 		ar->fw.hw_counters = true;
3057ccc83b0SChristian Lamparter 
306c42d6cf2SChristian Lamparter 	if (SUPP(CARL9170FW_WOL))
307c42d6cf2SChristian Lamparter 		device_set_wakeup_enable(&ar->udev->dev, true);
308c42d6cf2SChristian Lamparter 
309c9122c0dSChristian Lamparter 	if (SUPP(CARL9170FW_RX_BA_FILTER))
310c9122c0dSChristian Lamparter 		ar->fw.ba_filter = true;
311c9122c0dSChristian Lamparter 
312df64962fSChristian Lamparter 	if_comb_types = BIT(NL80211_IFTYPE_STATION) |
313df64962fSChristian Lamparter 			BIT(NL80211_IFTYPE_P2P_CLIENT);
314df64962fSChristian Lamparter 
31500c4da27SChristian Lamparter 	ar->fw.vif_num = otus_desc->vif_num;
31600c4da27SChristian Lamparter 	ar->fw.cmd_bufs = otus_desc->cmd_bufs;
31700c4da27SChristian Lamparter 	ar->fw.address = le32_to_cpu(otus_desc->fw_address);
31800c4da27SChristian Lamparter 	ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
31900c4da27SChristian Lamparter 	ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
32000c4da27SChristian Lamparter 	atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
32100c4da27SChristian Lamparter 	ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);
32200c4da27SChristian Lamparter 
32300c4da27SChristian Lamparter 	if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
32400c4da27SChristian Lamparter 	    ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
32500c4da27SChristian Lamparter 	    ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
32600c4da27SChristian Lamparter 	    ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
32700c4da27SChristian Lamparter 	    !valid_cpu_addr(ar->fw.address)) {
32800c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware shows obvious signs of "
32900c4da27SChristian Lamparter 			"malicious tampering.\n");
33000c4da27SChristian Lamparter 		return -EINVAL;
33100c4da27SChristian Lamparter 	}
33200c4da27SChristian Lamparter 
33300c4da27SChristian Lamparter 	ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
33400c4da27SChristian Lamparter 	ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);
33500c4da27SChristian Lamparter 
33600c4da27SChristian Lamparter 	if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
33700c4da27SChristian Lamparter 	    AR9170_MAC_BCN_LENGTH_MAX) {
33800c4da27SChristian Lamparter 		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
33900c4da27SChristian Lamparter 
34000c4da27SChristian Lamparter 		if (SUPP(CARL9170FW_WLANTX_CAB)) {
341b14fba7eSChristian Lamparter 			if_comb_types |= BIT(NL80211_IFTYPE_AP);
3426c653f66SChristian Lamparter 
3436c653f66SChristian Lamparter #ifdef CONFIG_MAC80211_MESH
3446c653f66SChristian Lamparter 			if_comb_types |=
3456c653f66SChristian Lamparter 				BIT(NL80211_IFTYPE_MESH_POINT);
3466c653f66SChristian Lamparter #endif /* CONFIG_MAC80211_MESH */
34700c4da27SChristian Lamparter 		}
34800c4da27SChristian Lamparter 	}
34900c4da27SChristian Lamparter 
35017f658acSChristian Lamparter 	carl9170_fw_set_if_combinations(ar, if_comb_types);
351df64962fSChristian Lamparter 
352df64962fSChristian Lamparter 	ar->hw->wiphy->interface_modes |= if_comb_types;
353df64962fSChristian Lamparter 
354d1f3de71SChristian Lamparter 	ar->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
355d1f3de71SChristian Lamparter 
356d1f3de71SChristian Lamparter 	/* As IBSS Encryption is software-based, IBSS RSN is supported. */
357d1f3de71SChristian Lamparter 	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
358d1f3de71SChristian Lamparter 		 WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_SUPPORTS_TDLS;
35981ddbb5cSJohannes Berg 
36000c4da27SChristian Lamparter #undef SUPPORTED
36165a69228SChristian Lamparter 	return carl9170_fw_tx_sequence(ar);
36200c4da27SChristian Lamparter }
36300c4da27SChristian Lamparter 
36400c4da27SChristian Lamparter static struct carl9170fw_desc_head *
carl9170_find_fw_desc(struct ar9170 * ar,const __u8 * fw_data,const size_t len)36500c4da27SChristian Lamparter carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)
36600c4da27SChristian Lamparter 
36700c4da27SChristian Lamparter {
36800c4da27SChristian Lamparter 	int scan = 0, found = 0;
36900c4da27SChristian Lamparter 
37000c4da27SChristian Lamparter 	if (!carl9170fw_size_check(len)) {
37100c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
37200c4da27SChristian Lamparter 		return NULL;
37300c4da27SChristian Lamparter 	}
37400c4da27SChristian Lamparter 
37500c4da27SChristian Lamparter 	while (scan < len - sizeof(struct carl9170fw_desc_head)) {
37600c4da27SChristian Lamparter 		if (fw_data[scan++] == otus_magic[found])
37700c4da27SChristian Lamparter 			found++;
37800c4da27SChristian Lamparter 		else
37900c4da27SChristian Lamparter 			found = 0;
38000c4da27SChristian Lamparter 
38100c4da27SChristian Lamparter 		if (scan >= len)
38200c4da27SChristian Lamparter 			break;
38300c4da27SChristian Lamparter 
38400c4da27SChristian Lamparter 		if (found == sizeof(otus_magic))
38500c4da27SChristian Lamparter 			break;
38600c4da27SChristian Lamparter 	}
38700c4da27SChristian Lamparter 
38800c4da27SChristian Lamparter 	if (found != sizeof(otus_magic))
38900c4da27SChristian Lamparter 		return NULL;
39000c4da27SChristian Lamparter 
39100c4da27SChristian Lamparter 	return (void *)&fw_data[scan - found];
39200c4da27SChristian Lamparter }
39300c4da27SChristian Lamparter 
carl9170_parse_firmware(struct ar9170 * ar)39400c4da27SChristian Lamparter int carl9170_parse_firmware(struct ar9170 *ar)
39500c4da27SChristian Lamparter {
39600c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *fw_desc = NULL;
39700c4da27SChristian Lamparter 	const struct firmware *fw = ar->fw.fw;
39800c4da27SChristian Lamparter 	unsigned long header_offset = 0;
39900c4da27SChristian Lamparter 	int err;
40000c4da27SChristian Lamparter 
40100c4da27SChristian Lamparter 	if (WARN_ON(!fw))
40200c4da27SChristian Lamparter 		return -EINVAL;
40300c4da27SChristian Lamparter 
40400c4da27SChristian Lamparter 	fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);
40500c4da27SChristian Lamparter 
40600c4da27SChristian Lamparter 	if (!fw_desc) {
40700c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "unsupported firmware.\n");
40800c4da27SChristian Lamparter 		return -ENODATA;
40900c4da27SChristian Lamparter 	}
41000c4da27SChristian Lamparter 
41100c4da27SChristian Lamparter 	header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;
41200c4da27SChristian Lamparter 
41300c4da27SChristian Lamparter 	err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
41400c4da27SChristian Lamparter 	if (err) {
41500c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
41600c4da27SChristian Lamparter 		return err;
41700c4da27SChristian Lamparter 	}
41800c4da27SChristian Lamparter 
41900c4da27SChristian Lamparter 	ar->fw.desc = fw_desc;
42000c4da27SChristian Lamparter 
42100c4da27SChristian Lamparter 	carl9170_fw_info(ar);
42200c4da27SChristian Lamparter 
42300c4da27SChristian Lamparter 	err = carl9170_fw(ar, fw->data, fw->size);
42400c4da27SChristian Lamparter 	if (err) {
42500c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
42600c4da27SChristian Lamparter 			err);
42700c4da27SChristian Lamparter 		return err;
42800c4da27SChristian Lamparter 	}
42900c4da27SChristian Lamparter 
43000c4da27SChristian Lamparter 	return 0;
43100c4da27SChristian Lamparter }
432