100c4da27SChristian Lamparter /*
200c4da27SChristian Lamparter  * Atheros CARL9170 driver
300c4da27SChristian Lamparter  *
400c4da27SChristian Lamparter  * firmware parser
500c4da27SChristian Lamparter  *
600c4da27SChristian Lamparter  * Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
700c4da27SChristian Lamparter  *
800c4da27SChristian Lamparter  * This program is free software; you can redistribute it and/or modify
900c4da27SChristian Lamparter  * it under the terms of the GNU General Public License as published by
1000c4da27SChristian Lamparter  * the Free Software Foundation; either version 2 of the License, or
1100c4da27SChristian Lamparter  * (at your option) any later version.
1200c4da27SChristian Lamparter  *
1300c4da27SChristian Lamparter  * This program is distributed in the hope that it will be useful,
1400c4da27SChristian Lamparter  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1500c4da27SChristian Lamparter  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1600c4da27SChristian Lamparter  * GNU General Public License for more details.
1700c4da27SChristian Lamparter  *
1800c4da27SChristian Lamparter  * You should have received a copy of the GNU General Public License
1900c4da27SChristian Lamparter  * along with this program; see the file COPYING.  If not, see
2000c4da27SChristian Lamparter  * http://www.gnu.org/licenses/.
2100c4da27SChristian Lamparter  */
2200c4da27SChristian Lamparter 
2300c4da27SChristian Lamparter #include <linux/kernel.h>
2400c4da27SChristian Lamparter #include <linux/firmware.h>
2500c4da27SChristian Lamparter #include <linux/crc32.h>
269d9779e7SPaul Gortmaker #include <linux/module.h>
2700c4da27SChristian Lamparter #include "carl9170.h"
2800c4da27SChristian Lamparter #include "fwcmd.h"
2900c4da27SChristian Lamparter #include "version.h"
3000c4da27SChristian Lamparter 
3100c4da27SChristian Lamparter static const u8 otus_magic[4] = { OTUS_MAGIC };
3200c4da27SChristian Lamparter 
3300c4da27SChristian Lamparter static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
3400c4da27SChristian Lamparter 	const unsigned int len, const u8 compatible_revision)
3500c4da27SChristian Lamparter {
3600c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *iter;
3700c4da27SChristian Lamparter 
3800c4da27SChristian Lamparter 	carl9170fw_for_each_hdr(iter, ar->fw.desc) {
3900c4da27SChristian Lamparter 		if (carl9170fw_desc_cmp(iter, descid, len,
4000c4da27SChristian Lamparter 					compatible_revision))
4100c4da27SChristian Lamparter 			return (void *)iter;
4200c4da27SChristian Lamparter 	}
4300c4da27SChristian Lamparter 
4400c4da27SChristian Lamparter 	/* needed to find the LAST desc */
4500c4da27SChristian Lamparter 	if (carl9170fw_desc_cmp(iter, descid, len,
4600c4da27SChristian Lamparter 				compatible_revision))
4700c4da27SChristian Lamparter 		return (void *)iter;
4800c4da27SChristian Lamparter 
4900c4da27SChristian Lamparter 	return NULL;
5000c4da27SChristian Lamparter }
5100c4da27SChristian Lamparter 
5200c4da27SChristian Lamparter static int carl9170_fw_verify_descs(struct ar9170 *ar,
5300c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *head, unsigned int max_len)
5400c4da27SChristian Lamparter {
5500c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *pos;
5600c4da27SChristian Lamparter 	unsigned long pos_addr, end_addr;
5700c4da27SChristian Lamparter 	unsigned int pos_length;
5800c4da27SChristian Lamparter 
5900c4da27SChristian Lamparter 	if (max_len < sizeof(*pos))
6000c4da27SChristian Lamparter 		return -ENODATA;
6100c4da27SChristian Lamparter 
6200c4da27SChristian Lamparter 	max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);
6300c4da27SChristian Lamparter 
6400c4da27SChristian Lamparter 	pos = head;
6500c4da27SChristian Lamparter 	pos_addr = (unsigned long) pos;
6600c4da27SChristian Lamparter 	end_addr = pos_addr + max_len;
6700c4da27SChristian Lamparter 
6800c4da27SChristian Lamparter 	while (pos_addr < end_addr) {
6900c4da27SChristian Lamparter 		if (pos_addr + sizeof(*head) > end_addr)
7000c4da27SChristian Lamparter 			return -E2BIG;
7100c4da27SChristian Lamparter 
7200c4da27SChristian Lamparter 		pos_length = le16_to_cpu(pos->length);
7300c4da27SChristian Lamparter 
7400c4da27SChristian Lamparter 		if (pos_length < sizeof(*head))
7500c4da27SChristian Lamparter 			return -EBADMSG;
7600c4da27SChristian Lamparter 
7700c4da27SChristian Lamparter 		if (pos_length > max_len)
7800c4da27SChristian Lamparter 			return -EOVERFLOW;
7900c4da27SChristian Lamparter 
8000c4da27SChristian Lamparter 		if (pos_addr + pos_length > end_addr)
8100c4da27SChristian Lamparter 			return -EMSGSIZE;
8200c4da27SChristian Lamparter 
8300c4da27SChristian Lamparter 		if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
8400c4da27SChristian Lamparter 					CARL9170FW_LAST_DESC_SIZE,
8500c4da27SChristian Lamparter 					CARL9170FW_LAST_DESC_CUR_VER))
8600c4da27SChristian Lamparter 			return 0;
8700c4da27SChristian Lamparter 
8800c4da27SChristian Lamparter 		pos_addr += pos_length;
8900c4da27SChristian Lamparter 		pos = (void *)pos_addr;
9000c4da27SChristian Lamparter 		max_len -= pos_length;
9100c4da27SChristian Lamparter 	}
9200c4da27SChristian Lamparter 	return -EINVAL;
9300c4da27SChristian Lamparter }
9400c4da27SChristian Lamparter 
9500c4da27SChristian Lamparter static void carl9170_fw_info(struct ar9170 *ar)
9600c4da27SChristian Lamparter {
9700c4da27SChristian Lamparter 	const struct carl9170fw_motd_desc *motd_desc;
9800c4da27SChristian Lamparter 	unsigned int str_ver_len;
9900c4da27SChristian Lamparter 	u32 fw_date;
10000c4da27SChristian Lamparter 
10100c4da27SChristian Lamparter 	dev_info(&ar->udev->dev, "driver   API: %s 2%03d-%02d-%02d [%d-%d]\n",
10200c4da27SChristian Lamparter 		CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
10300c4da27SChristian Lamparter 		CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
10400c4da27SChristian Lamparter 		CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);
10500c4da27SChristian Lamparter 
10600c4da27SChristian Lamparter 	motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
10700c4da27SChristian Lamparter 		sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);
10800c4da27SChristian Lamparter 
10900c4da27SChristian Lamparter 	if (motd_desc) {
11000c4da27SChristian Lamparter 		str_ver_len = strnlen(motd_desc->release,
11100c4da27SChristian Lamparter 			CARL9170FW_MOTD_RELEASE_LEN);
11200c4da27SChristian Lamparter 
11300c4da27SChristian Lamparter 		fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
11400c4da27SChristian Lamparter 
11500c4da27SChristian Lamparter 		dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
11600c4da27SChristian Lamparter 			 str_ver_len, motd_desc->release,
11700c4da27SChristian Lamparter 			 CARL9170FW_GET_YEAR(fw_date),
11800c4da27SChristian Lamparter 			 CARL9170FW_GET_MONTH(fw_date),
11900c4da27SChristian Lamparter 			 CARL9170FW_GET_DAY(fw_date));
12000c4da27SChristian Lamparter 
12100c4da27SChristian Lamparter 		strlcpy(ar->hw->wiphy->fw_version, motd_desc->release,
12200c4da27SChristian Lamparter 			sizeof(ar->hw->wiphy->fw_version));
12300c4da27SChristian Lamparter 	}
12400c4da27SChristian Lamparter }
12500c4da27SChristian Lamparter 
12600c4da27SChristian Lamparter static bool valid_dma_addr(const u32 address)
12700c4da27SChristian Lamparter {
12800c4da27SChristian Lamparter 	if (address >= AR9170_SRAM_OFFSET &&
12900c4da27SChristian Lamparter 	    address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
13000c4da27SChristian Lamparter 		return true;
13100c4da27SChristian Lamparter 
13200c4da27SChristian Lamparter 	return false;
13300c4da27SChristian Lamparter }
13400c4da27SChristian Lamparter 
13500c4da27SChristian Lamparter static bool valid_cpu_addr(const u32 address)
13600c4da27SChristian Lamparter {
13700c4da27SChristian Lamparter 	if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
13800c4da27SChristian Lamparter 	    address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
13900c4da27SChristian Lamparter 		return true;
14000c4da27SChristian Lamparter 
14100c4da27SChristian Lamparter 	return false;
14200c4da27SChristian Lamparter }
14300c4da27SChristian Lamparter 
14465a69228SChristian Lamparter static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data,
14565a69228SChristian Lamparter 				size_t len)
14600c4da27SChristian Lamparter {
14700c4da27SChristian Lamparter 	const struct carl9170fw_otus_desc *otus_desc;
14800c4da27SChristian Lamparter 	const struct carl9170fw_last_desc *last_desc;
14965a69228SChristian Lamparter 	const struct carl9170fw_chk_desc *chk_desc;
15065a69228SChristian Lamparter 	unsigned long fin, diff;
15165a69228SChristian Lamparter 	unsigned int dsc_len;
15265a69228SChristian Lamparter 	u32 crc32;
15300c4da27SChristian Lamparter 
15400c4da27SChristian Lamparter 	last_desc = carl9170_fw_find_desc(ar, LAST_MAGIC,
15500c4da27SChristian Lamparter 		sizeof(*last_desc), CARL9170FW_LAST_DESC_CUR_VER);
15600c4da27SChristian Lamparter 	if (!last_desc)
15700c4da27SChristian Lamparter 		return -EINVAL;
15800c4da27SChristian Lamparter 
15900c4da27SChristian Lamparter 	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
16000c4da27SChristian Lamparter 		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
16100c4da27SChristian Lamparter 	if (!otus_desc) {
16200c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "failed to find compatible firmware "
16300c4da27SChristian Lamparter 			"descriptor.\n");
16400c4da27SChristian Lamparter 		return -ENODATA;
16500c4da27SChristian Lamparter 	}
16600c4da27SChristian Lamparter 
16700c4da27SChristian Lamparter 	chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
16800c4da27SChristian Lamparter 		sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);
16900c4da27SChristian Lamparter 
17065a69228SChristian Lamparter 	if (!chk_desc) {
17165a69228SChristian Lamparter 		dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
17265a69228SChristian Lamparter 		return 0;
17365a69228SChristian Lamparter 	}
17400c4da27SChristian Lamparter 
17500c4da27SChristian Lamparter 	dsc_len = min_t(unsigned int, len,
17600c4da27SChristian Lamparter 			(unsigned long)chk_desc - (unsigned long)otus_desc);
17700c4da27SChristian Lamparter 
17800c4da27SChristian Lamparter 	fin = (unsigned long) last_desc + sizeof(*last_desc);
17900c4da27SChristian Lamparter 	diff = fin - (unsigned long) otus_desc;
18000c4da27SChristian Lamparter 
18100c4da27SChristian Lamparter 	if (diff < len)
18200c4da27SChristian Lamparter 		len -= diff;
18300c4da27SChristian Lamparter 
18400c4da27SChristian Lamparter 	if (len < 256)
18500c4da27SChristian Lamparter 		return -EIO;
18600c4da27SChristian Lamparter 
18700c4da27SChristian Lamparter 	crc32 = crc32_le(~0, data, len);
18800c4da27SChristian Lamparter 	if (cpu_to_le32(crc32) != chk_desc->fw_crc32) {
18900c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "fw checksum test failed.\n");
19000c4da27SChristian Lamparter 		return -ENOEXEC;
19100c4da27SChristian Lamparter 	}
19200c4da27SChristian Lamparter 
19300c4da27SChristian Lamparter 	crc32 = crc32_le(crc32, (void *)otus_desc, dsc_len);
19400c4da27SChristian Lamparter 	if (cpu_to_le32(crc32) != chk_desc->hdr_crc32) {
19500c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "descriptor check failed.\n");
19600c4da27SChristian Lamparter 		return -EINVAL;
19700c4da27SChristian Lamparter 	}
19865a69228SChristian Lamparter 	return 0;
19965a69228SChristian Lamparter }
20065a69228SChristian Lamparter 
20165a69228SChristian Lamparter static int carl9170_fw_tx_sequence(struct ar9170 *ar)
20265a69228SChristian Lamparter {
20365a69228SChristian Lamparter 	const struct carl9170fw_txsq_desc *txsq_desc;
20465a69228SChristian Lamparter 
20565a69228SChristian Lamparter 	txsq_desc = carl9170_fw_find_desc(ar, TXSQ_MAGIC, sizeof(*txsq_desc),
20665a69228SChristian Lamparter 					  CARL9170FW_TXSQ_DESC_CUR_VER);
20765a69228SChristian Lamparter 	if (txsq_desc) {
20865a69228SChristian Lamparter 		ar->fw.tx_seq_table = le32_to_cpu(txsq_desc->seq_table_addr);
20965a69228SChristian Lamparter 		if (!valid_cpu_addr(ar->fw.tx_seq_table))
21065a69228SChristian Lamparter 			return -EINVAL;
21100c4da27SChristian Lamparter 	} else {
21265a69228SChristian Lamparter 		ar->fw.tx_seq_table = 0;
21365a69228SChristian Lamparter 	}
21465a69228SChristian Lamparter 
21565a69228SChristian Lamparter 	return 0;
21665a69228SChristian Lamparter }
21765a69228SChristian Lamparter 
21865a69228SChristian Lamparter static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)
21965a69228SChristian Lamparter {
22065a69228SChristian Lamparter 	const struct carl9170fw_otus_desc *otus_desc;
22165a69228SChristian Lamparter 	int err;
22265a69228SChristian Lamparter 	u16 if_comb_types;
22365a69228SChristian Lamparter 
22465a69228SChristian Lamparter 	err = carl9170_fw_checksum(ar, data, len);
22565a69228SChristian Lamparter 	if (err)
22665a69228SChristian Lamparter 		return err;
22765a69228SChristian Lamparter 
22865a69228SChristian Lamparter 	otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
22965a69228SChristian Lamparter 		sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
23065a69228SChristian Lamparter 	if (!otus_desc) {
23165a69228SChristian Lamparter 		return -ENODATA;
23200c4da27SChristian Lamparter 	}
23300c4da27SChristian Lamparter 
23400c4da27SChristian Lamparter #define SUPP(feat)						\
23500c4da27SChristian Lamparter 	(carl9170fw_supports(otus_desc->feature_set, feat))
23600c4da27SChristian Lamparter 
23700c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
23800c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "invalid firmware descriptor "
23900c4da27SChristian Lamparter 			"format detected.\n");
24000c4da27SChristian Lamparter 		return -EINVAL;
24100c4da27SChristian Lamparter 	}
24200c4da27SChristian Lamparter 
24300c4da27SChristian Lamparter 	ar->fw.api_version = otus_desc->api_ver;
24400c4da27SChristian Lamparter 
24500c4da27SChristian Lamparter 	if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
24600c4da27SChristian Lamparter 	    ar->fw.api_version > CARL9170FW_API_MAX_VER) {
24700c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
24800c4da27SChristian Lamparter 		return -EINVAL;
24900c4da27SChristian Lamparter 	}
25000c4da27SChristian Lamparter 
25100c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
25200c4da27SChristian Lamparter 	    !SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
25300c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware does support "
25400c4da27SChristian Lamparter 			"mandatory features.\n");
25500c4da27SChristian Lamparter 		return -ECANCELED;
25600c4da27SChristian Lamparter 	}
25700c4da27SChristian Lamparter 
25800c4da27SChristian Lamparter 	if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
25900c4da27SChristian Lamparter 		__CARL9170FW_FEATURE_NUM) {
26000c4da27SChristian Lamparter 		dev_warn(&ar->udev->dev, "driver does not support all "
26100c4da27SChristian Lamparter 			 "firmware features.\n");
26200c4da27SChristian Lamparter 	}
26300c4da27SChristian Lamparter 
26400c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_COMMAND_CAM)) {
26500c4da27SChristian Lamparter 		dev_info(&ar->udev->dev, "crypto offloading is disabled "
26600c4da27SChristian Lamparter 			 "by firmware.\n");
26700c4da27SChristian Lamparter 		ar->disable_offload = true;
26800c4da27SChristian Lamparter 	}
26900c4da27SChristian Lamparter 
2701205f543SChristian Lamparter 	if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
27100c4da27SChristian Lamparter 		ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
27200c4da27SChristian Lamparter 
27300c4da27SChristian Lamparter 	if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
27400c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware does not provide "
27500c4da27SChristian Lamparter 			"mandatory interfaces.\n");
27600c4da27SChristian Lamparter 		return -EINVAL;
27700c4da27SChristian Lamparter 	}
27800c4da27SChristian Lamparter 
27900c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_MINIBOOT))
28000c4da27SChristian Lamparter 		ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
28100c4da27SChristian Lamparter 	else
28200c4da27SChristian Lamparter 		ar->fw.offset = 0;
28300c4da27SChristian Lamparter 
28400c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
28500c4da27SChristian Lamparter 		ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
28600c4da27SChristian Lamparter 		ar->fw.tx_stream = true;
28700c4da27SChristian Lamparter 	}
28800c4da27SChristian Lamparter 
28900c4da27SChristian Lamparter 	if (SUPP(CARL9170FW_USB_UP_STREAM))
29000c4da27SChristian Lamparter 		ar->fw.rx_stream = true;
29100c4da27SChristian Lamparter 
2925c895691SChristian Lamparter 	if (SUPP(CARL9170FW_RX_FILTER)) {
2935c895691SChristian Lamparter 		ar->fw.rx_filter = true;
2945c895691SChristian Lamparter 		ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL |
2955c895691SChristian Lamparter 			FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS |
2965c895691SChristian Lamparter 			FIF_PROMISC_IN_BSS;
2975c895691SChristian Lamparter 	}
2985c895691SChristian Lamparter 
2997ccc83b0SChristian Lamparter 	if (SUPP(CARL9170FW_HW_COUNTERS))
3007ccc83b0SChristian Lamparter 		ar->fw.hw_counters = true;
3017ccc83b0SChristian Lamparter 
302c42d6cf2SChristian Lamparter 	if (SUPP(CARL9170FW_WOL))
303c42d6cf2SChristian Lamparter 		device_set_wakeup_enable(&ar->udev->dev, true);
304c42d6cf2SChristian Lamparter 
305c9122c0dSChristian Lamparter 	if (SUPP(CARL9170FW_RX_BA_FILTER))
306c9122c0dSChristian Lamparter 		ar->fw.ba_filter = true;
307c9122c0dSChristian Lamparter 
308df64962fSChristian Lamparter 	if_comb_types = BIT(NL80211_IFTYPE_STATION) |
309df64962fSChristian Lamparter 			BIT(NL80211_IFTYPE_P2P_CLIENT);
310df64962fSChristian Lamparter 
31100c4da27SChristian Lamparter 	ar->fw.vif_num = otus_desc->vif_num;
31200c4da27SChristian Lamparter 	ar->fw.cmd_bufs = otus_desc->cmd_bufs;
31300c4da27SChristian Lamparter 	ar->fw.address = le32_to_cpu(otus_desc->fw_address);
31400c4da27SChristian Lamparter 	ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
31500c4da27SChristian Lamparter 	ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
31600c4da27SChristian Lamparter 	atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
31700c4da27SChristian Lamparter 	ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);
31800c4da27SChristian Lamparter 
31900c4da27SChristian Lamparter 	if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
32000c4da27SChristian Lamparter 	    ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
32100c4da27SChristian Lamparter 	    ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
32200c4da27SChristian Lamparter 	    ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
32300c4da27SChristian Lamparter 	    !valid_cpu_addr(ar->fw.address)) {
32400c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware shows obvious signs of "
32500c4da27SChristian Lamparter 			"malicious tampering.\n");
32600c4da27SChristian Lamparter 		return -EINVAL;
32700c4da27SChristian Lamparter 	}
32800c4da27SChristian Lamparter 
32900c4da27SChristian Lamparter 	ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
33000c4da27SChristian Lamparter 	ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);
33100c4da27SChristian Lamparter 
33200c4da27SChristian Lamparter 	if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
33300c4da27SChristian Lamparter 	    AR9170_MAC_BCN_LENGTH_MAX) {
33400c4da27SChristian Lamparter 		ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
33500c4da27SChristian Lamparter 
33600c4da27SChristian Lamparter 		if (SUPP(CARL9170FW_WLANTX_CAB)) {
337df64962fSChristian Lamparter 			if_comb_types |=
338c426ee24SJohannes Berg 				BIT(NL80211_IFTYPE_AP) |
339c426ee24SJohannes Berg 				BIT(NL80211_IFTYPE_P2P_GO);
3406c653f66SChristian Lamparter 
3416c653f66SChristian Lamparter #ifdef CONFIG_MAC80211_MESH
3426c653f66SChristian Lamparter 			if_comb_types |=
3436c653f66SChristian Lamparter 				BIT(NL80211_IFTYPE_MESH_POINT);
3446c653f66SChristian Lamparter #endif /* CONFIG_MAC80211_MESH */
34500c4da27SChristian Lamparter 		}
34600c4da27SChristian Lamparter 	}
34700c4da27SChristian Lamparter 
348df64962fSChristian Lamparter 	ar->if_comb_limits[0].max = ar->fw.vif_num;
349df64962fSChristian Lamparter 	ar->if_comb_limits[0].types = if_comb_types;
350df64962fSChristian Lamparter 
351df64962fSChristian Lamparter 	ar->if_combs[0].num_different_channels = 1;
352df64962fSChristian Lamparter 	ar->if_combs[0].max_interfaces = ar->fw.vif_num;
353df64962fSChristian Lamparter 	ar->if_combs[0].limits = ar->if_comb_limits;
354df64962fSChristian Lamparter 	ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
355df64962fSChristian Lamparter 
356df64962fSChristian Lamparter 	ar->hw->wiphy->iface_combinations = ar->if_combs;
357df64962fSChristian Lamparter 	ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
358df64962fSChristian Lamparter 
359df64962fSChristian Lamparter 	ar->hw->wiphy->interface_modes |= if_comb_types;
360df64962fSChristian Lamparter 
36181ddbb5cSJohannes Berg 	ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
36281ddbb5cSJohannes Berg 
36300c4da27SChristian Lamparter #undef SUPPORTED
36465a69228SChristian Lamparter 	return carl9170_fw_tx_sequence(ar);
36500c4da27SChristian Lamparter }
36600c4da27SChristian Lamparter 
36700c4da27SChristian Lamparter static struct carl9170fw_desc_head *
36800c4da27SChristian Lamparter carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)
36900c4da27SChristian Lamparter 
37000c4da27SChristian Lamparter {
37100c4da27SChristian Lamparter 	int scan = 0, found = 0;
37200c4da27SChristian Lamparter 
37300c4da27SChristian Lamparter 	if (!carl9170fw_size_check(len)) {
37400c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
37500c4da27SChristian Lamparter 		return NULL;
37600c4da27SChristian Lamparter 	}
37700c4da27SChristian Lamparter 
37800c4da27SChristian Lamparter 	while (scan < len - sizeof(struct carl9170fw_desc_head)) {
37900c4da27SChristian Lamparter 		if (fw_data[scan++] == otus_magic[found])
38000c4da27SChristian Lamparter 			found++;
38100c4da27SChristian Lamparter 		else
38200c4da27SChristian Lamparter 			found = 0;
38300c4da27SChristian Lamparter 
38400c4da27SChristian Lamparter 		if (scan >= len)
38500c4da27SChristian Lamparter 			break;
38600c4da27SChristian Lamparter 
38700c4da27SChristian Lamparter 		if (found == sizeof(otus_magic))
38800c4da27SChristian Lamparter 			break;
38900c4da27SChristian Lamparter 	}
39000c4da27SChristian Lamparter 
39100c4da27SChristian Lamparter 	if (found != sizeof(otus_magic))
39200c4da27SChristian Lamparter 		return NULL;
39300c4da27SChristian Lamparter 
39400c4da27SChristian Lamparter 	return (void *)&fw_data[scan - found];
39500c4da27SChristian Lamparter }
39600c4da27SChristian Lamparter 
39700c4da27SChristian Lamparter int carl9170_parse_firmware(struct ar9170 *ar)
39800c4da27SChristian Lamparter {
39900c4da27SChristian Lamparter 	const struct carl9170fw_desc_head *fw_desc = NULL;
40000c4da27SChristian Lamparter 	const struct firmware *fw = ar->fw.fw;
40100c4da27SChristian Lamparter 	unsigned long header_offset = 0;
40200c4da27SChristian Lamparter 	int err;
40300c4da27SChristian Lamparter 
40400c4da27SChristian Lamparter 	if (WARN_ON(!fw))
40500c4da27SChristian Lamparter 		return -EINVAL;
40600c4da27SChristian Lamparter 
40700c4da27SChristian Lamparter 	fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);
40800c4da27SChristian Lamparter 
40900c4da27SChristian Lamparter 	if (!fw_desc) {
41000c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "unsupported firmware.\n");
41100c4da27SChristian Lamparter 		return -ENODATA;
41200c4da27SChristian Lamparter 	}
41300c4da27SChristian Lamparter 
41400c4da27SChristian Lamparter 	header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;
41500c4da27SChristian Lamparter 
41600c4da27SChristian Lamparter 	err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
41700c4da27SChristian Lamparter 	if (err) {
41800c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
41900c4da27SChristian Lamparter 		return err;
42000c4da27SChristian Lamparter 	}
42100c4da27SChristian Lamparter 
42200c4da27SChristian Lamparter 	ar->fw.desc = fw_desc;
42300c4da27SChristian Lamparter 
42400c4da27SChristian Lamparter 	carl9170_fw_info(ar);
42500c4da27SChristian Lamparter 
42600c4da27SChristian Lamparter 	err = carl9170_fw(ar, fw->data, fw->size);
42700c4da27SChristian Lamparter 	if (err) {
42800c4da27SChristian Lamparter 		dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
42900c4da27SChristian Lamparter 			err);
43000c4da27SChristian Lamparter 		return err;
43100c4da27SChristian Lamparter 	}
43200c4da27SChristian Lamparter 
43300c4da27SChristian Lamparter 	return 0;
43400c4da27SChristian Lamparter }
435