From: Kirill A. Korinsky Subject: Re: sys/iwx: support powersave To: tech@openbsd.org, stsp@openbsd.org Date: Fri, 06 Feb 2026 18:40:06 +0100 On Sat, 31 Jan 2026 01:42:53 +0100, "Kirill A. Korinsky" 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. > A week using powersave constantly on this card, no issue. Ok? > Index: sys/dev/pci/if_iwx.c > =================================================================== > RCS file: /home/cvs/src/sys/dev/pci/if_iwx.c,v > diff -u -p -r1.194 if_iwx.c > --- sys/dev/pci/if_iwx.c 1 Dec 2025 16:44:13 -0000 1.194 > +++ sys/dev/pci/if_iwx.c 31 Jan 2026 00:39:54 -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); > @@ -6645,8 +6646,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 > @@ -6676,7 +6696,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, > @@ -6684,6 +6705,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 = { > @@ -8669,11 +8766,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 > /* > @@ -8691,13 +8790,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; > @@ -9578,7 +9670,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); > @@ -9932,6 +10024,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; > > /* > @@ -9964,6 +10057,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); > } > @@ -11848,6 +11956,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 > -- wbr, Kirill