linux_dsm_epyc7002/drivers/net/wireless/ath/carl9170/fw.c
Christian Lamparter 17f658ac8c carl9170: Only specify interface combinations if more than one interface is possible
Otherwise carl9170 triggers a warning in cfg80211, from net/wireless/core.c

	/* Combinations with just one interface aren't real */
	if (WARN_ON(c->max_interfaces < 2))

Note: The number of supported interfaces is set by
the carl9170 firmware. The default number of
supported interfaces for all current firmwares is 2.
Therefore this warning can only be observed with
custom firmwares.

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2013-01-07 15:16:52 -05:00

444 lines
11 KiB
C

/*
* Atheros CARL9170 driver
*
* firmware parser
*
* Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*/
#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"
static const u8 otus_magic[4] = { OTUS_MAGIC };
static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4],
const unsigned int len, const u8 compatible_revision)
{
const struct carl9170fw_desc_head *iter;
carl9170fw_for_each_hdr(iter, ar->fw.desc) {
if (carl9170fw_desc_cmp(iter, descid, len,
compatible_revision))
return (void *)iter;
}
/* needed to find the LAST desc */
if (carl9170fw_desc_cmp(iter, descid, len,
compatible_revision))
return (void *)iter;
return NULL;
}
static int carl9170_fw_verify_descs(struct ar9170 *ar,
const struct carl9170fw_desc_head *head, unsigned int max_len)
{
const struct carl9170fw_desc_head *pos;
unsigned long pos_addr, end_addr;
unsigned int pos_length;
if (max_len < sizeof(*pos))
return -ENODATA;
max_len = min_t(unsigned int, CARL9170FW_DESC_MAX_LENGTH, max_len);
pos = head;
pos_addr = (unsigned long) pos;
end_addr = pos_addr + max_len;
while (pos_addr < end_addr) {
if (pos_addr + sizeof(*head) > end_addr)
return -E2BIG;
pos_length = le16_to_cpu(pos->length);
if (pos_length < sizeof(*head))
return -EBADMSG;
if (pos_length > max_len)
return -EOVERFLOW;
if (pos_addr + pos_length > end_addr)
return -EMSGSIZE;
if (carl9170fw_desc_cmp(pos, LAST_MAGIC,
CARL9170FW_LAST_DESC_SIZE,
CARL9170FW_LAST_DESC_CUR_VER))
return 0;
pos_addr += pos_length;
pos = (void *)pos_addr;
max_len -= pos_length;
}
return -EINVAL;
}
static void carl9170_fw_info(struct ar9170 *ar)
{
const struct carl9170fw_motd_desc *motd_desc;
unsigned int str_ver_len;
u32 fw_date;
dev_info(&ar->udev->dev, "driver API: %s 2%03d-%02d-%02d [%d-%d]\n",
CARL9170FW_VERSION_GIT, CARL9170FW_VERSION_YEAR,
CARL9170FW_VERSION_MONTH, CARL9170FW_VERSION_DAY,
CARL9170FW_API_MIN_VER, CARL9170FW_API_MAX_VER);
motd_desc = carl9170_fw_find_desc(ar, MOTD_MAGIC,
sizeof(*motd_desc), CARL9170FW_MOTD_DESC_CUR_VER);
if (motd_desc) {
str_ver_len = strnlen(motd_desc->release,
CARL9170FW_MOTD_RELEASE_LEN);
fw_date = le32_to_cpu(motd_desc->fw_year_month_day);
dev_info(&ar->udev->dev, "firmware API: %.*s 2%03d-%02d-%02d\n",
str_ver_len, motd_desc->release,
CARL9170FW_GET_YEAR(fw_date),
CARL9170FW_GET_MONTH(fw_date),
CARL9170FW_GET_DAY(fw_date));
strlcpy(ar->hw->wiphy->fw_version, motd_desc->release,
sizeof(ar->hw->wiphy->fw_version));
}
}
static bool valid_dma_addr(const u32 address)
{
if (address >= AR9170_SRAM_OFFSET &&
address < (AR9170_SRAM_OFFSET + AR9170_SRAM_SIZE))
return true;
return false;
}
static bool valid_cpu_addr(const u32 address)
{
if (valid_dma_addr(address) || (address >= AR9170_PRAM_OFFSET &&
address < (AR9170_PRAM_OFFSET + AR9170_PRAM_SIZE)))
return true;
return false;
}
static int carl9170_fw_checksum(struct ar9170 *ar, const __u8 *data,
size_t len)
{
const struct carl9170fw_otus_desc *otus_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);
if (!last_desc)
return -EINVAL;
otus_desc = carl9170_fw_find_desc(ar, OTUS_MAGIC,
sizeof(*otus_desc), CARL9170FW_OTUS_DESC_CUR_VER);
if (!otus_desc) {
dev_err(&ar->udev->dev, "failed to find compatible firmware "
"descriptor.\n");
return -ENODATA;
}
chk_desc = carl9170_fw_find_desc(ar, CHK_MAGIC,
sizeof(*chk_desc), CARL9170FW_CHK_DESC_CUR_VER);
if (!chk_desc) {
dev_warn(&ar->udev->dev, "Unprotected firmware image.\n");
return 0;
}
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;
if (diff < len)
len -= diff;
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(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;
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 {
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) \
(carl9170fw_supports(otus_desc->feature_set, feat))
if (!SUPP(CARL9170FW_DUMMY_FEATURE)) {
dev_err(&ar->udev->dev, "invalid firmware descriptor "
"format detected.\n");
return -EINVAL;
}
ar->fw.api_version = otus_desc->api_ver;
if (ar->fw.api_version < CARL9170FW_API_MIN_VER ||
ar->fw.api_version > CARL9170FW_API_MAX_VER) {
dev_err(&ar->udev->dev, "unsupported firmware api version.\n");
return -EINVAL;
}
if (!SUPP(CARL9170FW_COMMAND_PHY) || SUPP(CARL9170FW_UNUSABLE) ||
!SUPP(CARL9170FW_HANDLE_BACK_REQ)) {
dev_err(&ar->udev->dev, "firmware does support "
"mandatory features.\n");
return -ECANCELED;
}
if (ilog2(le32_to_cpu(otus_desc->feature_set)) >=
__CARL9170FW_FEATURE_NUM) {
dev_warn(&ar->udev->dev, "driver does not support all "
"firmware features.\n");
}
if (!SUPP(CARL9170FW_COMMAND_CAM)) {
dev_info(&ar->udev->dev, "crypto offloading is disabled "
"by firmware.\n");
ar->disable_offload = true;
}
if (SUPP(CARL9170FW_PSM) && SUPP(CARL9170FW_FIXED_5GHZ_PSM))
ar->hw->flags |= IEEE80211_HW_SUPPORTS_PS;
if (!SUPP(CARL9170FW_USB_INIT_FIRMWARE)) {
dev_err(&ar->udev->dev, "firmware does not provide "
"mandatory interfaces.\n");
return -EINVAL;
}
if (SUPP(CARL9170FW_MINIBOOT))
ar->fw.offset = le16_to_cpu(otus_desc->miniboot_size);
else
ar->fw.offset = 0;
if (SUPP(CARL9170FW_USB_DOWN_STREAM)) {
ar->hw->extra_tx_headroom += sizeof(struct ar9170_stream);
ar->fw.tx_stream = true;
}
if (SUPP(CARL9170FW_USB_UP_STREAM))
ar->fw.rx_stream = true;
if (SUPP(CARL9170FW_RX_FILTER)) {
ar->fw.rx_filter = true;
ar->rx_filter_caps = FIF_FCSFAIL | FIF_PLCPFAIL |
FIF_CONTROL | FIF_PSPOLL | FIF_OTHER_BSS |
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);
ar->fw.rx_size = le16_to_cpu(otus_desc->rx_max_frame_len);
ar->fw.mem_blocks = min_t(unsigned int, otus_desc->tx_descs, 0xfe);
atomic_set(&ar->mem_free_blocks, ar->fw.mem_blocks);
ar->fw.mem_block_size = le16_to_cpu(otus_desc->tx_frag_len);
if (ar->fw.vif_num >= AR9170_MAX_VIRTUAL_MAC || !ar->fw.vif_num ||
ar->fw.mem_blocks < 16 || !ar->fw.cmd_bufs ||
ar->fw.mem_block_size < 64 || ar->fw.mem_block_size > 512 ||
ar->fw.rx_size > 32768 || ar->fw.rx_size < 4096 ||
!valid_cpu_addr(ar->fw.address)) {
dev_err(&ar->udev->dev, "firmware shows obvious signs of "
"malicious tampering.\n");
return -EINVAL;
}
ar->fw.beacon_addr = le32_to_cpu(otus_desc->bcn_addr);
ar->fw.beacon_max_len = le16_to_cpu(otus_desc->bcn_len);
if (valid_dma_addr(ar->fw.beacon_addr) && ar->fw.beacon_max_len >=
AR9170_MAC_BCN_LENGTH_MAX) {
ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
if (SUPP(CARL9170FW_WLANTX_CAB)) {
if_comb_types |=
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_MESH_POINT) |
BIT(NL80211_IFTYPE_P2P_GO);
}
}
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 carl9170_fw_tx_sequence(ar);
}
static struct carl9170fw_desc_head *
carl9170_find_fw_desc(struct ar9170 *ar, const __u8 *fw_data, const size_t len)
{
int scan = 0, found = 0;
if (!carl9170fw_size_check(len)) {
dev_err(&ar->udev->dev, "firmware size is out of bound.\n");
return NULL;
}
while (scan < len - sizeof(struct carl9170fw_desc_head)) {
if (fw_data[scan++] == otus_magic[found])
found++;
else
found = 0;
if (scan >= len)
break;
if (found == sizeof(otus_magic))
break;
}
if (found != sizeof(otus_magic))
return NULL;
return (void *)&fw_data[scan - found];
}
int carl9170_parse_firmware(struct ar9170 *ar)
{
const struct carl9170fw_desc_head *fw_desc = NULL;
const struct firmware *fw = ar->fw.fw;
unsigned long header_offset = 0;
int err;
if (WARN_ON(!fw))
return -EINVAL;
fw_desc = carl9170_find_fw_desc(ar, fw->data, fw->size);
if (!fw_desc) {
dev_err(&ar->udev->dev, "unsupported firmware.\n");
return -ENODATA;
}
header_offset = (unsigned long)fw_desc - (unsigned long)fw->data;
err = carl9170_fw_verify_descs(ar, fw_desc, fw->size - header_offset);
if (err) {
dev_err(&ar->udev->dev, "damaged firmware (%d).\n", err);
return err;
}
ar->fw.desc = fw_desc;
carl9170_fw_info(ar);
err = carl9170_fw(ar, fw->data, fw->size);
if (err) {
dev_err(&ar->udev->dev, "failed to parse firmware (%d).\n",
err);
return err;
}
return 0;
}