Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

wifi: mwifiex: add rgpower table loading support

Marvell/NXP Wi-Fi adapters allow fine-grained adjustment of the transmit
power levels and various other internal parameters. This is done by
sending command streams to the adapter. One storage format of these
command streams are the rgpower tables, which consist of multiple
command blocks in the following format:

command_block_1 = {
XX XX LL LL XX XX ..
}
command_block_n = {
XX XX LL LL XX XX XX ..
}

XX = raw byte as hex chars
LL = total length of the "raw" command block

These command blocks are parsed into their binary representation and
then send to the adapter. The parsing logic was adapted from NXP's
mwifiex driver[1].

The rgpower tables matching the currently set regulatory domain are
automatically requested and applied. If not found the existing device
tree provided power tables are tried as well.

[1]:
https://github.com/nxp-imx/mwifiex/blob/7a8beaa1605cb0870dc7ba3312c76df91cb0d6cf/mlan/mlan_cmdevt.c#L812

Signed-off-by: Stefan Kerkmann <s.kerkmann@pengutronix.de>
Link: https://patch.msgid.link/20250804-feature-mwifiex-rgpower-table-loading-v1-1-358e70a4d45e@pengutronix.de
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Stefan Kerkmann and committed by
Johannes Berg
7b6f16a2 f90caeba

+180 -1
+5
drivers/net/wireless/marvell/mwifiex/main.c
··· 494 494 return; 495 495 } 496 496 497 + if (adapter->rgpower_data) { 498 + release_firmware(adapter->rgpower_data); 499 + adapter->rgpower_data = NULL; 500 + } 501 + 497 502 mwifiex_unregister(adapter); 498 503 pr_debug("info: %s: free adapter\n", __func__); 499 504 }
+3
drivers/net/wireless/marvell/mwifiex/main.h
··· 982 982 u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; 983 983 u16 max_mgmt_ie_index; 984 984 const struct firmware *cal_data; 985 + const struct firmware *rgpower_data; 985 986 struct device_node *dt_node; 986 987 987 988 /* 11AC */ ··· 1580 1579 int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, 1581 1580 struct device_node *node, const char *prefix); 1582 1581 void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); 1582 + int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data, 1583 + const size_t size); 1583 1584 1584 1585 extern const struct ethtool_ops mwifiex_ethtool_ops; 1585 1586
+115
drivers/net/wireless/marvell/mwifiex/sta_cmd.c
··· 1483 1483 return 0; 1484 1484 } 1485 1485 1486 + static int mwifiex_rgpower_table_advance_to_content(u8 **pos, const u8 *data, 1487 + const size_t size) 1488 + { 1489 + while (*pos - data < size) { 1490 + /* skip spaces, tabs and empty lines */ 1491 + if (**pos == '\r' || **pos == '\n' || **pos == '\0' || 1492 + isspace(**pos)) { 1493 + (*pos)++; 1494 + continue; 1495 + } 1496 + /* skip line comments */ 1497 + if (**pos == '#') { 1498 + *pos = strchr(*pos, '\n'); 1499 + if (!*pos) 1500 + return -EINVAL; 1501 + (*pos)++; 1502 + continue; 1503 + } 1504 + return 0; 1505 + } 1506 + return 0; 1507 + } 1508 + 1509 + int mwifiex_send_rgpower_table(struct mwifiex_private *priv, const u8 *data, 1510 + const size_t size) 1511 + { 1512 + int ret = 0; 1513 + bool start_raw = false; 1514 + u8 *ptr, *token, *pos = NULL; 1515 + u8 *_data __free(kfree) = NULL; 1516 + struct mwifiex_adapter *adapter = priv->adapter; 1517 + struct mwifiex_ds_misc_cmd *hostcmd __free(kfree) = NULL; 1518 + 1519 + hostcmd = kzalloc(sizeof(*hostcmd), GFP_KERNEL); 1520 + if (!hostcmd) 1521 + return -ENOMEM; 1522 + 1523 + _data = kmemdup(data, size, GFP_KERNEL); 1524 + if (!_data) { 1525 + kfree(hostcmd); 1526 + return -ENOMEM; 1527 + } 1528 + 1529 + pos = _data; 1530 + ptr = hostcmd->cmd; 1531 + while ((pos - _data) < size) { 1532 + ret = mwifiex_rgpower_table_advance_to_content(&pos, _data, size); 1533 + if (ret) { 1534 + mwifiex_dbg( 1535 + adapter, ERROR, 1536 + "%s: failed to advance to content in rgpower table\n", 1537 + __func__); 1538 + return ret; 1539 + } 1540 + 1541 + if (*pos == '}' && start_raw) { 1542 + memcpy(&hostcmd->len, &hostcmd->cmd[2], sizeof(u16)); 1543 + ret = mwifiex_send_cmd(priv, 0, 0, 0, hostcmd, false); 1544 + if (ret) { 1545 + mwifiex_dbg(adapter, ERROR, 1546 + "%s: failed to send hostcmd %d\n", 1547 + __func__, ret); 1548 + return ret; 1549 + } 1550 + 1551 + memset(hostcmd->cmd, 0, MWIFIEX_SIZE_OF_CMD_BUFFER); 1552 + ptr = hostcmd->cmd; 1553 + start_raw = false; 1554 + pos++; 1555 + continue; 1556 + } 1557 + 1558 + if (!start_raw) { 1559 + pos = strchr(pos, '='); 1560 + if (pos) { 1561 + pos = strchr(pos, '{'); 1562 + if (pos) { 1563 + start_raw = true; 1564 + pos++; 1565 + continue; 1566 + } 1567 + } 1568 + mwifiex_dbg(adapter, ERROR, 1569 + "%s: syntax error in hostcmd\n", __func__); 1570 + return -EINVAL; 1571 + } 1572 + 1573 + if (start_raw) { 1574 + while ((*pos != '\r' && *pos != '\n') && 1575 + (token = strsep((char **)&pos, " "))) { 1576 + if (ptr - hostcmd->cmd >= 1577 + MWIFIEX_SIZE_OF_CMD_BUFFER) { 1578 + mwifiex_dbg( 1579 + adapter, ERROR, 1580 + "%s: hostcmd is larger than %d, aborting\n", 1581 + __func__, MWIFIEX_SIZE_OF_CMD_BUFFER); 1582 + return -ENOMEM; 1583 + } 1584 + 1585 + ret = kstrtou8(token, 16, ptr); 1586 + if (ret < 0) { 1587 + mwifiex_dbg( 1588 + adapter, ERROR, 1589 + "%s: failed to parse hostcmd %d token: %s\n", 1590 + __func__, ret, token); 1591 + return ret; 1592 + } 1593 + ptr++; 1594 + } 1595 + } 1596 + } 1597 + 1598 + return ret; 1599 + } 1600 + 1486 1601 /* This function prepares command of set_cfg_data. */ 1487 1602 static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, 1488 1603 struct host_cmd_ds_command *cmd, void *data_buf)
+57 -1
drivers/net/wireless/marvell/mwifiex/sta_ioctl.c
··· 180 180 return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); 181 181 } 182 182 183 - void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) 183 + static void mwifiex_dnld_dt_txpwr_table(struct mwifiex_private *priv) 184 184 { 185 185 if (priv->adapter->dt_node) { 186 186 char txpwr[] = {"marvell,00_txpwrlimit"}; ··· 188 188 memcpy(&txpwr[8], priv->adapter->country_code, 2); 189 189 mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); 190 190 } 191 + } 192 + 193 + static int mwifiex_request_rgpower_table(struct mwifiex_private *priv) 194 + { 195 + struct mwifiex_802_11d_domain_reg *domain_info = &priv->adapter->domain_reg; 196 + struct mwifiex_adapter *adapter = priv->adapter; 197 + char rgpower_table_name[30]; 198 + char country_code[3]; 199 + 200 + strscpy(country_code, domain_info->country_code, sizeof(country_code)); 201 + 202 + /* World regulatory domain "00" has WW as country code */ 203 + if (strncmp(country_code, "00", 2) == 0) 204 + strscpy(country_code, "WW", sizeof(country_code)); 205 + 206 + snprintf(rgpower_table_name, sizeof(rgpower_table_name), 207 + "nxp/rgpower_%s.bin", country_code); 208 + 209 + mwifiex_dbg(adapter, INFO, "info: %s: requesting regulatory power table %s\n", 210 + __func__, rgpower_table_name); 211 + 212 + if (adapter->rgpower_data) { 213 + release_firmware(adapter->rgpower_data); 214 + adapter->rgpower_data = NULL; 215 + } 216 + 217 + if ((request_firmware(&adapter->rgpower_data, rgpower_table_name, 218 + adapter->dev))) { 219 + mwifiex_dbg( 220 + adapter, INFO, 221 + "info: %s: failed to request regulatory power table\n", 222 + __func__); 223 + return -EIO; 224 + } 225 + 226 + return 0; 227 + } 228 + 229 + static int mwifiex_dnld_rgpower_table(struct mwifiex_private *priv) 230 + { 231 + int ret; 232 + 233 + ret = mwifiex_request_rgpower_table(priv); 234 + if (ret) 235 + return ret; 236 + 237 + return mwifiex_send_rgpower_table(priv, priv->adapter->rgpower_data->data, 238 + priv->adapter->rgpower_data->size); 239 + } 240 + 241 + void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) 242 + { 243 + if (mwifiex_dnld_rgpower_table(priv) == 0) 244 + return; 245 + 246 + mwifiex_dnld_dt_txpwr_table(priv); 191 247 } 192 248 193 249 static int mwifiex_process_country_ie(struct mwifiex_private *priv,