Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: sys/iwx: support powersave
To:
tech@openbsd.org, stsp@openbsd.org
Date:
Fri, 06 Feb 2026 18:40:06 +0100

Download raw body.

Thread
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.
>

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