git: 103a4f05c77f - main - iwm: update phy context routines; add HT channel width parameter
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 15 Oct 2026 02:19:06 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=103a4f05c77feacacd4e8191b4bfc92a71d1673d
commit 103a4f05c77feacacd4e8191b4bfc92a71d1673d
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2026-10-15 01:49:33 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2026-10-15 01:49:35 +0000
iwm: update phy context routines; add HT channel width parameter
Update the phy context routines to match what the openbsd snapshot
is doing. This involves some HT channel width stuff which although
I am not doing HT right now, it was juts easier to sync these
entirely versus just a partial sync.
* Update the phy context routines to match what openbsd is doing
* Although the openbsd driver has an iwm_phy_ctxt_update() routine
instead of add and modify, I'm leaving it as-is for now.
A bunch of refactoring is going to need to be done to properly
use an iwm_phy_ctxt_update() call and I didn't want to tackle
that in here.
* Add a UHB firmware capability phy context call; I can't test it
but it's mostly copy/paste.
* Add in a util to turn ieee80211_channel into the 'sco' variable
(the HT low/hi parameter) since it's done in openbsd in a
slightly different way.
Locally tested:
* 3160, STA mode
* 7260, STA mode
* 8260, STA mode
* 9260, STA mode
Obtained from: OpenBSD a6e25c727a2214219715b51ba6af2221d7e0d674
Differential Revision: https://reviews.freebsd.org/D60082
---
sys/dev/iwm/if_iwm.c | 8 ++-
sys/dev/iwm/if_iwm_phy_ctxt.c | 120 +++++++++++++++++++++++++++++++++++-------
sys/dev/iwm/if_iwm_phy_ctxt.h | 5 ++
sys/dev/iwm/if_iwm_util.h | 18 +++++++
sys/dev/iwm/if_iwmvar.h | 1 +
5 files changed, 131 insertions(+), 21 deletions(-)
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c
index dd154e44aa8f..e67aad2af7e4 100644
--- a/sys/dev/iwm/if_iwm.c
+++ b/sys/dev/iwm/if_iwm.c
@@ -5042,8 +5042,11 @@ iwm_init_hw(struct iwm_softc *sc)
* going to be overwritten in the other flows.
* For now use the first channel we have.
*/
- if ((error = iwm_phy_ctxt_add(sc,
- &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0)
+ sc->sc_phyctxt[i].id = i;
+ sc->sc_phyctxt[i].channel = &ic->ic_channels[1];
+ error = iwm_phy_ctxt_cmd(sc, &sc->sc_phyctxt[i], 1, 1,
+ IWM_FW_CTXT_ACTION_ADD, 0, IEEE80211_HTINFO_2NDCHAN_NONE);
+ if (error)
goto error;
}
@@ -6482,6 +6485,7 @@ iwm_attach(device_t dev)
ic->ic_flags_ext |= IEEE80211_FEXT_SEQNO_OFFLOAD;
for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
sc->sc_phyctxt[i].id = i;
+ sc->sc_phyctxt[i].sco = IEEE80211_HTINFO_2NDCHAN_NONE;
sc->sc_phyctxt[i].color = 0;
sc->sc_phyctxt[i].ref = 0;
sc->sc_phyctxt[i].channel = NULL;
diff --git a/sys/dev/iwm/if_iwm_phy_ctxt.c b/sys/dev/iwm/if_iwm_phy_ctxt.c
index 7ca86b31267f..7d0f115e3d07 100644
--- a/sys/dev/iwm/if_iwm_phy_ctxt.c
+++ b/sys/dev/iwm/if_iwm_phy_ctxt.c
@@ -188,7 +188,7 @@ iwm_phy_ctxt_cmd_hdr(struct iwm_softc *sc, struct iwm_phy_ctxt *ctxt,
static void
iwm_phy_ctxt_cmd_data(struct iwm_softc *sc,
struct iwm_phy_context_cmd *cmd, struct ieee80211_channel *chan,
- uint8_t chains_static, uint8_t chains_dynamic)
+ uint8_t chains_static, uint8_t chains_dynamic, uint8_t sco)
{
struct ieee80211com *ic = &sc->sc_ic;
uint8_t active_cnt, idle_cnt;
@@ -209,11 +209,23 @@ iwm_phy_ctxt_cmd_data(struct iwm_softc *sc,
IWM_PHY_BAND_24 : IWM_PHY_BAND_5;
cmd->ci.channel = ieee80211_chan2ieee(ic, chan);
-
- /* TODO: HT40 */
- /* TODO: VHT40/VHT80 */
- cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20;
- cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ if (IEEE80211_IS_CHAN_HT40(chan)) {
+ if (sco == IEEE80211_HTINFO_2NDCHAN_ABOVE) {
+ /* secondary chan above -> control chan below */
+ cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE40;
+ } else if (sco == IEEE80211_HTINFO_2NDCHAN_BELOW) {
+ /* secondary chan below -> control chan above */
+ cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_ABOVE;
+ cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE40;
+ } else {
+ cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20;
+ cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ }
+ } else {
+ cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20;
+ cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ }
/* Set rx the chains */
idle_cnt = chains_static;
@@ -240,17 +252,60 @@ iwm_phy_ctxt_cmd_data(struct iwm_softc *sc,
cmd->txchain_info = htole32(iwm_get_valid_tx_ant(sc));
}
-/*
- * Send a command
- * only if something in the configuration changed: in case that this is the
- * first time that the phy configuration is applied or in case that the phy
- * configuration changed from the previous apply.
- */
static int
-iwm_phy_ctxt_apply(struct iwm_softc *sc,
+iwm_phy_ctxt_cmd_uhb(struct iwm_softc *sc, struct iwm_phy_ctxt *ctxt,
+ uint8_t chains_static, uint8_t chains_dynamic, uint32_t action,
+ uint32_t apply_time, uint8_t sco)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct iwm_phy_context_cmd_uhb cmd;
+ uint8_t active_cnt, idle_cnt;
+ struct ieee80211_channel *chan = ctxt->channel;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ctxt->id,
+ ctxt->color));
+ cmd.action = htole32(action);
+ cmd.apply_time = htole32(apply_time);
+
+ cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
+ IWM_PHY_BAND_24 : IWM_PHY_BAND_5;
+ cmd.ci.channel = htole32(ieee80211_chan2ieee(ic, chan));
+ if (IEEE80211_IS_CHAN_HT40(chan)) {
+ if (sco == IEEE80211_HTINFO_2NDCHAN_ABOVE) {
+ /* secondary chan above -> control chan below */
+ cmd.ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ cmd.ci.width = IWM_PHY_VHT_CHANNEL_MODE40;
+ } else if (sco == IEEE80211_HTINFO_2NDCHAN_BELOW) {
+ /* secondary chan below -> control chan above */
+ cmd.ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_ABOVE;
+ cmd.ci.width = IWM_PHY_VHT_CHANNEL_MODE40;
+ } else {
+ cmd.ci.width = IWM_PHY_VHT_CHANNEL_MODE20;
+ cmd.ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ }
+ } else {
+ cmd.ci.width = IWM_PHY_VHT_CHANNEL_MODE20;
+ cmd.ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW;
+ }
+
+ idle_cnt = chains_static;
+ active_cnt = chains_dynamic;
+ cmd.rxchain_info = htole32(iwm_get_valid_rx_ant(sc) <<
+ IWM_PHY_RX_CHAIN_VALID_POS);
+ cmd.rxchain_info |= htole32(idle_cnt << IWM_PHY_RX_CHAIN_CNT_POS);
+ cmd.rxchain_info |= htole32(active_cnt <<
+ IWM_PHY_RX_CHAIN_MIMO_CNT_POS);
+ cmd.txchain_info = htole32(iwm_get_valid_tx_ant(sc));
+
+ return iwm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, 0, sizeof(cmd), &cmd);
+}
+
+int
+iwm_phy_ctxt_cmd(struct iwm_softc *sc,
struct iwm_phy_ctxt *ctxt,
uint8_t chains_static, uint8_t chains_dynamic,
- uint32_t action, uint32_t apply_time)
+ uint32_t action, uint32_t apply_time, uint8_t sco)
{
struct iwm_phy_context_cmd cmd;
int ret;
@@ -260,12 +315,23 @@ iwm_phy_ctxt_apply(struct iwm_softc *sc,
__func__,
ctxt->channel);
+ /*
+ * Intel increased the size of the fw_channel_info struct and neglected
+ * to bump the phy_context_cmd struct, which contains an fw_channel_info
+ * member in the middle.
+ * To keep things simple we use a separate function to handle the larger
+ * variant of the phy context command.
+ */
+ if (iwm_fw_has_capa(sc, IWM_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS))
+ return iwm_phy_ctxt_cmd_uhb(sc, ctxt, chains_static,
+ chains_dynamic, action, apply_time, sco);
+
/* Set the command header fields */
iwm_phy_ctxt_cmd_hdr(sc, ctxt, &cmd, action, apply_time);
/* Set the command data */
iwm_phy_ctxt_cmd_data(sc, &cmd, ctxt->channel,
- chains_static, chains_dynamic);
+ chains_static, chains_dynamic, sco);
ret = iwm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, IWM_CMD_SYNC,
sizeof(struct iwm_phy_context_cmd), &cmd);
@@ -285,14 +351,22 @@ iwm_phy_ctxt_add(struct iwm_softc *sc, struct iwm_phy_ctxt *ctxt,
uint8_t chains_static, uint8_t chains_dynamic)
{
ctxt->channel = chan;
+ int sco, ret;
+
+ sco = iwm_get_phy_sco(sc, chan);
IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD,
"%s: called; channel=%d\n",
__func__,
ieee80211_chan2ieee(&sc->sc_ic, chan));
- return iwm_phy_ctxt_apply(sc, ctxt,
- chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_ADD, 0);
+ ret = iwm_phy_ctxt_cmd(sc, ctxt,
+ chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_ADD, 0, sco);
+ if (ret != 0)
+ return (ret);
+
+ ctxt->sco = sco;
+ return (0);
}
/*
@@ -306,14 +380,22 @@ iwm_phy_ctxt_changed(struct iwm_softc *sc,
uint8_t chains_static, uint8_t chains_dynamic)
{
ctxt->channel = chan;
+ int sco, ret;
+
+ sco = iwm_get_phy_sco(sc, chan);
IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD,
"%s: called; channel=%d\n",
__func__,
ieee80211_chan2ieee(&sc->sc_ic, chan));
- return iwm_phy_ctxt_apply(sc, ctxt,
- chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_MODIFY, 0);
+ ret = iwm_phy_ctxt_cmd(sc, ctxt,
+ chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_MODIFY, 0, sco);
+ if (ret != 0)
+ return (ret);
+
+ ctxt->sco = sco;
+ return (0);
}
/*
diff --git a/sys/dev/iwm/if_iwm_phy_ctxt.h b/sys/dev/iwm/if_iwm_phy_ctxt.h
index dcf07d1620a8..d9547c44a4e1 100644
--- a/sys/dev/iwm/if_iwm_phy_ctxt.h
+++ b/sys/dev/iwm/if_iwm_phy_ctxt.h
@@ -113,4 +113,9 @@ extern int iwm_phy_ctxt_changed(struct iwm_softc *sc,
struct iwm_phy_ctxt *ctxt, struct ieee80211_channel *chan,
uint8_t chains_static, uint8_t chains_dynamic);
+extern int iwm_phy_ctxt_cmd(struct iwm_softc *sc,
+ struct iwm_phy_ctxt *ctxt,
+ uint8_t chains_static, uint8_t chains_dynamic,
+ uint32_t action, uint32_t apply_time, uint8_t sco);
+
#endif /* __IF_IWM_PHY_CTXT_H__ */
diff --git a/sys/dev/iwm/if_iwm_util.h b/sys/dev/iwm/if_iwm_util.h
index 4154efad7992..3512b3bcf8d3 100644
--- a/sys/dev/iwm/if_iwm_util.h
+++ b/sys/dev/iwm/if_iwm_util.h
@@ -159,4 +159,22 @@ iwm_get_phy_config(struct iwm_softc *sc)
return sc->sc_fw.phy_config & phy_config;
}
+/**
+ * @brief Return the HT above/below field for the given channel width
+ *
+ * @param sc driver softc
+ * @param c channel
+ * @return IEEE80211_HTINFO_2NDCHAN_* value
+ */
+static inline uint8_t
+iwm_get_phy_sco(struct iwm_softc *sc, const struct ieee80211_channel *c)
+{
+ if (IEEE80211_IS_CHAN_HT40U(c))
+ return (IEEE80211_HTINFO_2NDCHAN_ABOVE);
+ else if (IEEE80211_IS_CHAN_HT40D(c))
+ return (IEEE80211_HTINFO_2NDCHAN_BELOW);
+ else
+ return (IEEE80211_HTINFO_2NDCHAN_NONE);
+}
+
#endif /* __IF_IWM_UTIL_H__ */
diff --git a/sys/dev/iwm/if_iwmvar.h b/sys/dev/iwm/if_iwmvar.h
index 8f2760f114f3..55a1ec427482 100644
--- a/sys/dev/iwm/if_iwmvar.h
+++ b/sys/dev/iwm/if_iwmvar.h
@@ -349,6 +349,7 @@ struct iwm_phy_ctxt {
uint16_t color;
uint32_t ref;
struct ieee80211_channel *channel;
+ uint8_t sco; /* 40MHz secondary channel offset */
};
struct iwm_bf_data {