diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c index 0ffb277f9909..e87dfb0f1b3a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c @@ -108,69 +108,99 @@ mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version) return ret; } +/* A table's layout is decided by the version byte in MTCL, but some firmware + * labels a version 2 table as version 1. Accept whichever layout the table + * actually has: for every accepted entry count the two do not overlap, since + * one is 6 + 5n bytes and the other 7 + 7n. + */ +static bool +mt792x_asar_fits(u8 *table, int len, int prelen, int sarlen, int nr_off, + int min, int max) +{ + int tblcnt; + + if (len < prelen || (len - prelen) % sarlen) + return false; + + tblcnt = (len - prelen) / sarlen; + if (tblcnt < min || tblcnt > max) + return false; + + return tblcnt == table[nr_off]; +} + /* MTDS : Dynamic SAR Power Table */ static int -mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version) +mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version, + u8 *used) { - int len, ret, sarlen, prelen, tblcnt; - bool enable; + int len, ret; + u8 *t; ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len); if (ret) return ret; - /* Table content validation */ - switch (version) { - case 1: - enable = ((struct mt792x_asar_dyn *)*table)->enable; - sarlen = sizeof(struct mt792x_asar_dyn_limit); - prelen = sizeof(struct mt792x_asar_dyn); - break; - case 2: - enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable; - sarlen = sizeof(struct mt792x_asar_dyn_limit_v2); - prelen = sizeof(struct mt792x_asar_dyn_v2); - break; - default: - return -EINVAL; - } + t = *table; - tblcnt = (len - prelen) / sarlen; - if (!enable || - tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN) + /* Table content validation */ + if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_dyn), + sizeof(struct mt792x_asar_dyn_limit), + offsetof(struct mt792x_asar_dyn, nr_tbl), + MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN)) + *used = 1; + else if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_dyn_v2), + sizeof(struct mt792x_asar_dyn_limit_v2), + offsetof(struct mt792x_asar_dyn_v2, nr_tbl), + MT792x_ASAR_MIN_DYN, MT792x_ASAR_MAX_DYN)) + *used = 2; + else return -EINVAL; + if (!t[offsetof(struct mt792x_asar_dyn, enable)]) + return -EINVAL; + + if (version && *used != version) + dev_info(dev->mt76.dev, + "MTDS is v%u, MTCL says v%u; using v%u\n", + *used, version, *used); + return 0; } /* MTGS : Geo SAR Power Table */ static int -mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version) +mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version, + u8 *used) { - int len, ret, sarlen, prelen, tblcnt; + int len, ret; + u8 *t; ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len); if (ret) return ret; - /* Table content validation */ - switch (version) { - case 1: - sarlen = sizeof(struct mt792x_asar_geo_limit); - prelen = sizeof(struct mt792x_asar_geo); - break; - case 2: - sarlen = sizeof(struct mt792x_asar_geo_limit_v2); - prelen = sizeof(struct mt792x_asar_geo_v2); - break; - default: - return -EINVAL; - } + t = *table; - tblcnt = (len - prelen) / sarlen; - if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO) + /* Table content validation */ + if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_geo), + sizeof(struct mt792x_asar_geo_limit), + offsetof(struct mt792x_asar_geo, nr_tbl), + MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO)) + *used = 1; + else if (mt792x_asar_fits(t, len, sizeof(struct mt792x_asar_geo_v2), + sizeof(struct mt792x_asar_geo_limit_v2), + offsetof(struct mt792x_asar_geo_v2, nr_tbl), + MT792x_ASAR_MIN_GEO, MT792x_ASAR_MAX_GEO)) + *used = 2; + else return -EINVAL; + if (version && *used != version) + dev_info(dev->mt76.dev, + "MTGS is v%u, MTCL says v%u; using v%u\n", + *used, version, *used); + return 0; } @@ -205,14 +235,16 @@ int mt792x_init_acpi_sar(struct mt792x_dev *dev) asar->countrylist = NULL; } - ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver); + ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver, + &asar->dyn_ver); if (ret) { devm_kfree(dev->mt76.dev, asar->dyn); asar->dyn = NULL; } /* MTGS is optional */ - ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver); + ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver, + &asar->geo_ver); if (ret) { devm_kfree(dev->mt76.dev, asar->geo); asar->geo = NULL; @@ -254,7 +286,7 @@ mt792x_asar_get_geo_pwr(struct mt792x_phy *phy, break; } - if (asar->ver == 1) { + if (asar->geo_ver == 1) { band_pwr = &asar->geo->tbl[idx].band[0]; max = ARRAY_SIZE(asar->geo->tbl[idx].band); } else { @@ -305,7 +337,7 @@ mt792x_asar_range_pwr(struct mt792x_phy *phy, if (!capa) return 127; - if (asar->ver == 1) { + if (asar->dyn_ver == 1) { limit = &asar->dyn->tbl[0].frp[0]; max = ARRAY_SIZE(asar->dyn->tbl[0].frp); } else { diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h index 474033073831..1d42f24b05f6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h @@ -105,6 +105,8 @@ struct mt792x_asar_fg { struct mt792x_acpi_sar { u8 ver; + u8 dyn_ver; + u8 geo_ver; union { struct mt792x_asar_dyn *dyn; struct mt792x_asar_dyn_v2 *dyn_v2;