Download raw body.
sys/iwx: support powersave
On Fri, 06 Feb 2026 18:57:55 +0100,
Klemens Nanni <kn@openbsd.org> wrote:
>
> 06.02.2026 20:40, Kirill A. Korinsky пишет:
> > On Sat, 31 Jan 2026 01:42:53 +0100,
> > "Kirill A. Korinsky" <kirill@korins.ky> wrote:
> >>
> >> tech@,
> >>
> >> here a patch which introduces support of powersave in a way like it
> >> implemented in iwn and wpi.
> >>
> >> I run it for quite some time on:
> >>
> >> iwx0 at pci0 dev 20 function 3 "Intel Wi-Fi 6 AX201" rev 0x00, msix
> >>
> >> works, no regression or issue noticed.
>
> How much power does it save in which situations?
>
> This would be always on regardless of ifconfig(8)'s knob, right?
>
> powersave
> Enable 802.11 power saving mode. This option is generally only
> relevant to older devices where power saving is disabled by
> default. On modern hardware, drivers will ask the firmware to
> automatically enable any applicable power-saving features.
>
> -powersave
> Disable 802.11 power saving mode.
>
No, it follow the same pattern like iwn and wpi: to enable it, someone needs
to explicity adds powersave into ifconfig args, and -powersave to disable.
But I think I should document in man in this diff that it supports
powersave, similar with iwn and wpi.
Thus, here was a cleanup to remove "driver does not support powersave mode"
https://github.com/openbsd/src/commit/36238301d0ac55314d56a48bfc01c37e59963ad2
Here an updated diff:
Index: share/man/man4/iwx.4
===================================================================
RCS file: /home/cvs/src/share/man/man4/iwx.4,v
diff -u -p -r1.22 iwx.4
--- share/man/man4/iwx.4 1 Dec 2025 16:44:13 -0000 1.22
+++ share/man/man4/iwx.4 6 Feb 2026 18:33:51 -0000
@@ -76,7 +76,7 @@ driver supports protected management fra
of management frames such as deauthentication, disassociation, and block-ack
negotiation, on networks using WPA.
.Pp
-In BSS mode the driver supports background scanning;
+In BSS mode the driver supports powersave mode and background scanning;
see
.Xr ifconfig 8 .
.Pp
Index: sys/dev/pci/if_iwx.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_iwx.c,v
diff -u -p -r1.195 if_iwx.c
--- sys/dev/pci/if_iwx.c 6 Feb 2026 16:27:45 -0000 1.195
+++ sys/dev/pci/if_iwx.c 6 Feb 2026 18:37:51 -0000
@@ -406,6 +406,7 @@ void iwx_power_build_cmd(struct iwx_soft
struct iwx_mac_power_cmd *);
int iwx_power_mac_update_mode(struct iwx_softc *, struct iwx_node *);
int iwx_power_update_device(struct iwx_softc *);
+int iwx_set_pslevel(struct iwx_softc *, int, int, int);
int iwx_enable_beacon_filter(struct iwx_softc *, struct iwx_node *);
int iwx_disable_beacon_filter(struct iwx_softc *);
int iwx_add_sta_cmd(struct iwx_softc *, struct iwx_node *, int);
@@ -6646,8 +6647,27 @@ iwx_power_build_cmd(struct iwx_softc *sc
keep_alive = roundup(keep_alive, 1000) / 1000;
cmd->keep_alive_seconds = htole16(keep_alive);
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
- cmd->flags = htole16(IWX_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+ if (ic->ic_opmode != IEEE80211_M_MONITOR &&
+ (ic->ic_flags & IEEE80211_F_PMGTON)) {
+ const struct iwx_pmgt *pmgt;
+ int range;
+
+ if (dtim_period <= 2)
+ range = 0;
+ else if (dtim_period <= 10)
+ range = 1;
+ else
+ range = 2;
+ pmgt = &iwx_pmgt[range][3];
+ cmd->flags = htole16(IWX_POWER_FLAGS_POWER_SAVE_ENA_MSK |
+ IWX_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+ cmd->rx_data_timeout = htole32(pmgt->rxtimeout * 1024);
+ cmd->tx_data_timeout = htole32(pmgt->txtimeout * 1024);
+ if (pmgt->skip_dtim != 0) {
+ cmd->flags |= htole16(IWX_POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ cmd->skip_dtim_periods = pmgt->skip_dtim + 1;
+ }
+ }
}
int
@@ -6677,7 +6697,8 @@ iwx_power_update_device(struct iwx_softc
struct iwx_device_power_cmd cmd = { };
struct ieee80211com *ic = &sc->sc_ic;
- if (ic->ic_opmode != IEEE80211_M_MONITOR)
+ if (ic->ic_opmode != IEEE80211_M_MONITOR &&
+ (ic->ic_flags & IEEE80211_F_PMGTON))
cmd.flags = htole16(IWX_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
return iwx_send_cmd_pdu(sc,
@@ -6685,6 +6706,82 @@ iwx_power_update_device(struct iwx_softc
}
int
+iwx_set_pslevel(struct iwx_softc *sc, int dtim, int level, int async)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct iwx_device_power_cmd dcmd = { };
+ struct iwx_mac_power_cmd mcmd;
+ struct iwx_node *in;
+ struct ieee80211_node *ni;
+ const struct iwx_pmgt *pmgt;
+ int range, skip_dtim, cmd_flags, err;
+ int dtim_period, dtim_msec, keep_alive;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ return 0;
+
+ if (dtim == 0) {
+ dtim = 1;
+ skip_dtim = 0;
+ } else
+ skip_dtim = -1;
+
+ if (dtim <= 2)
+ range = 0;
+ else if (dtim <= 10)
+ range = 1;
+ else
+ range = 2;
+
+ pmgt = &iwx_pmgt[range][level];
+ if (skip_dtim == -1)
+ skip_dtim = pmgt->skip_dtim;
+
+ if (level != 0)
+ dcmd.flags = htole16(IWX_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ cmd_flags = async ? IWX_CMD_ASYNC : 0;
+ err = iwx_send_cmd_pdu(sc, IWX_POWER_TABLE_CMD, cmd_flags,
+ sizeof(dcmd), &dcmd);
+ if (err)
+ return err;
+
+ if ((sc->sc_flags & IWX_FLAG_MAC_ACTIVE) == 0)
+ return 0;
+
+ in = (void *)ic->ic_bss;
+ ni = &in->in_ni;
+
+ memset(&mcmd, 0, sizeof(mcmd));
+ mcmd.id_and_color = htole32(IWX_FW_CMD_ID_AND_COLOR(in->in_id,
+ in->in_color));
+ dtim_period = ni->ni_dtimperiod ? ni->ni_dtimperiod : 1;
+ dtim_msec = dtim_period * ni->ni_intval;
+ keep_alive = MAX(3 * dtim_msec, 1000 * IWX_POWER_KEEP_ALIVE_PERIOD_SEC);
+ keep_alive = roundup(keep_alive, 1000) / 1000;
+ mcmd.keep_alive_seconds = htole16(keep_alive);
+
+ if (level != 0) {
+ mcmd.flags = htole16(IWX_POWER_FLAGS_POWER_SAVE_ENA_MSK |
+ IWX_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+ mcmd.rx_data_timeout = htole32(pmgt->rxtimeout * 1024);
+ mcmd.tx_data_timeout = htole32(pmgt->txtimeout * 1024);
+ if (skip_dtim != 0) {
+ mcmd.flags |= htole16(IWX_POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ mcmd.skip_dtim_periods = skip_dtim + 1;
+ }
+ }
+
+ err = iwx_send_cmd_pdu(sc, IWX_MAC_PM_POWER_TABLE, cmd_flags,
+ sizeof(mcmd), &mcmd);
+ if (err != 0)
+ return err;
+
+ return iwx_update_beacon_abort(sc, in, !!(mcmd.flags &
+ htole16(IWX_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)));
+}
+
+int
iwx_enable_beacon_filter(struct iwx_softc *sc, struct iwx_node *in)
{
struct iwx_beacon_filter_cmd cmd = {
@@ -8711,11 +8808,13 @@ iwx_run(struct iwx_softc *sc)
return err;
}
- err = iwx_power_update_device(sc);
- if (err) {
- printf("%s: could not send power command (error %d)\n",
- DEVNAME(sc), err);
- return err;
+ if (ic->ic_flags & IEEE80211_F_PMGTON) {
+ err = iwx_set_pslevel(sc, 0, 3, 1);
+ if (err) {
+ printf("%s: could not send power command (error %d)\n",
+ DEVNAME(sc), err);
+ return err;
+ }
}
#ifdef notyet
/*
@@ -8733,13 +8832,6 @@ iwx_run(struct iwx_softc *sc)
if (ic->ic_opmode == IEEE80211_M_MONITOR)
return 0;
- err = iwx_power_mac_update_mode(sc, in);
- if (err) {
- printf("%s: could not update MAC power (error %d)\n",
- DEVNAME(sc), err);
- return err;
- }
-
/* Start at lowest available bit-rate. Firmware will raise. */
in->in_ni.ni_txrate = 0;
in->in_ni.ni_txmcs = 0;
@@ -9631,7 +9723,7 @@ iwx_init_hw(struct iwx_softc *sc)
goto err;
}
- err = iwx_power_update_device(sc);
+ err = iwx_set_pslevel(sc, 0, 0, 0);
if (err) {
printf("%s: could not send power command (error %d)\n",
DEVNAME(sc), err);
@@ -9985,6 +10077,7 @@ int
iwx_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct iwx_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
int s, err = 0, generation = sc->sc_generation;
/*
@@ -10017,6 +10110,21 @@ iwx_ioctl(struct ifnet *ifp, u_long cmd,
}
break;
+ case SIOCS80211POWER:
+ err = ieee80211_ioctl(ifp, cmd, data);
+ if (err != ENETRESET)
+ break;
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ if (ic->ic_flags & IEEE80211_F_PMGTON)
+ err = iwx_set_pslevel(sc, 0, 3, 0);
+ else /* back to CAM */
+ err = iwx_set_pslevel(sc, 0, 0, 0);
+ } else {
+ /* Defer until transition to IEEE80211_S_RUN. */
+ err = 0;
+ }
+ break;
+
default:
err = ieee80211_ioctl(ifp, cmd, data);
}
@@ -11901,6 +12009,7 @@ iwx_attach(struct device *parent, struct
ic->ic_caps =
IEEE80211_C_QOS | IEEE80211_C_TX_AMPDU | /* A-MPDU */
IEEE80211_C_ADDBA_OFFLOAD | /* device sends ADDBA/DELBA frames */
+ IEEE80211_C_PMGT | /* power management */
IEEE80211_C_WEP | /* WEP */
IEEE80211_C_RSN | /* WPA/RSN */
IEEE80211_C_SCANALL | /* device scans all channels at once */
Index: sys/dev/pci/if_iwxreg.h
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_iwxreg.h,v
diff -u -p -r1.58 if_iwxreg.h
--- sys/dev/pci/if_iwxreg.h 1 Dec 2025 16:44:13 -0000 1.58
+++ sys/dev/pci/if_iwxreg.h 30 Jan 2026 23:52:35 -0000
@@ -5096,6 +5096,42 @@ struct iwx_mac_power_cmd {
#define IWX_DEFAULT_PS_TX_DATA_TIMEOUT (100 * 1000)
#define IWX_DEFAULT_PS_RX_DATA_TIMEOUT (100 * 1000)
+#define IWX_NDTIMRANGES 3
+#define IWX_NPOWERLEVELS 6
+static const struct iwx_pmgt {
+ uint32_t rxtimeout;
+ uint32_t txtimeout;
+ int skip_dtim;
+} iwx_pmgt[IWX_NDTIMRANGES][IWX_NPOWERLEVELS] = {
+ /* DTIM <= 2 */
+ {
+ { 0, 0, 0 }, /* CAM */
+ { 200, 500, 0 }, /* PS level 1 */
+ { 200, 300, 0 }, /* PS level 2 */
+ { 50, 100, 0 }, /* PS level 3 */
+ { 50, 25, 1 }, /* PS level 4 */
+ { 25, 25, 2 } /* PS level 5 */
+ },
+ /* 3 <= DTIM <= 10 */
+ {
+ { 0, 0, 0 }, /* CAM */
+ { 200, 500, 0 }, /* PS level 1 */
+ { 200, 300, 0 }, /* PS level 2 */
+ { 50, 100, 0 }, /* PS level 3 */
+ { 50, 25, 1 }, /* PS level 4 */
+ { 25, 25, 2 } /* PS level 5 */
+ },
+ /* DTIM >= 11 */
+ {
+ { 0, 0, 0 }, /* CAM */
+ { 200, 500, 0 }, /* PS level 1 */
+ { 200, 300, 0 }, /* PS level 2 */
+ { 50, 100, 0 }, /* PS level 3 */
+ { 50, 25, 0 }, /* PS level 4 */
+ { 25, 25, 0 } /* PS level 5 */
+ }
+};
+
/*
* struct iwx_uapsd_misbehaving_ap_notif - FW sends this notification when
* associated AP is identified as improperly implementing uAPSD protocol.
--
wbr, Kirill
sys/iwx: support powersave