From: Mark Kettenis Subject: Re: iwx: scan command version 17 To: Stefan Sperling Cc: tech@openbsd.org Date: Tue, 24 Feb 2026 23:34:52 +0100 > Date: Tue, 24 Feb 2026 16:19:08 +0100 > From: Stefan Sperling > > Add support for scan command version 17 to iwx(4). > This will be needed for Bz devices. > > Tested on AX200, and on AX211 (Bz) with other local changes. > > ok? Also tested on AX210 (MA). ok kettenis@ > M sys/dev/pci/if_iwx.c | 202+ 2- > M sys/dev/pci/if_iwxreg.h | 107+ 0- > > 2 files changed, 309 insertions(+), 2 deletions(-) > > commit - ad326fcaf0c83015c73f258e04d6180e2cc40891 > commit + 18247b7c795ec847bd60dd111d274b8a2e518b06 > blob - 6181f1267153c75984dedfa0f3d82a8898e69b0a > blob + 03d36027340740248ccbb4c47f118b183ae1414e > --- sys/dev/pci/if_iwx.c > +++ sys/dev/pci/if_iwx.c > @@ -420,8 +420,13 @@ void iwx_scan_umac_dwell_v10(struct iwx_softc *, > struct iwx_scan_general_params_v10 *, int); > void iwx_scan_umac_fill_general_p_v10(struct iwx_softc *, > struct iwx_scan_general_params_v10 *, uint16_t, int); > +void iwx_scan_umac_fill_general_p_v11(struct iwx_softc *, > + struct iwx_scan_general_params_v11 *, uint16_t, int); > void iwx_scan_umac_fill_ch_p_v6(struct iwx_softc *, > struct iwx_scan_channel_params_v6 *, uint32_t, int); > +void iwx_scan_umac_fill_ch_p_v7(struct iwx_softc *, > + struct iwx_scan_channel_params_v7 *, uint32_t, int); > +int iwx_umac_scan_v17(struct iwx_softc *, int); > int iwx_umac_scan_v14(struct iwx_softc *, int); > void iwx_mcc_update(struct iwx_softc *, struct iwx_mcc_chub_notif *); > uint8_t iwx_ridx2rate(struct ieee80211_rateset *, int); > @@ -437,6 +442,7 @@ int iwx_mld_mac_ctxt_cmd(struct iwx_softc *, struct iw > int iwx_clear_statistics(struct iwx_softc *); > void iwx_add_task(struct iwx_softc *, struct taskq *, struct task *); > void iwx_del_task(struct iwx_softc *, struct taskq *, struct task *); > +int iwx_initiate_scan(struct iwx_softc *, int); > int iwx_scan(struct iwx_softc *); > int iwx_bgscan(struct ieee80211com *); > void iwx_bgscan_done(struct ieee80211com *, > @@ -7179,6 +7185,45 @@ iwx_umac_scan_fill_channels(struct iwx_softc *sc, > return nchan; > } > > +uint8_t > +iwx_umac_scan_fill_channels_v5(struct iwx_softc *sc, > + struct iwx_scan_channel_cfg_umac_v5 *chan, size_t chan_nitems, > + int n_ssids, uint32_t channel_cfg_flags) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_channel *c; > + uint8_t nchan; > + > + for (nchan = 0, c = &ic->ic_channels[1]; > + c <= &ic->ic_channels[IEEE80211_CHAN_MAX] && > + nchan < chan_nitems && > + nchan < sc->sc_capa_n_scan_channels; > + c++) { > + uint8_t channel_num, band; > + > + if (c->ic_flags == 0) > + continue; > + > + channel_num = ieee80211_mhz2ieee(c->ic_freq, 0); > + if (IEEE80211_IS_CHAN_2GHZ(c)) > + band = IWX_PHY_BAND_24; > + else > + band = IWX_PHY_BAND_5; > + > + chan->channel_num = channel_num; > + chan->psd_20 = -128; > + chan->iter_count = 1; > + chan->iter_interval = 0; > + > + chan->flags = htole32(channel_cfg_flags | > + (band << IWX_CHAN_CFG_FLAGS_BAND_POS)); > + chan++; > + nchan++; > + } > + > + return nchan; > +} > + > int > iwx_fill_probe_req(struct iwx_softc *sc, struct iwx_scan_probe_req *preq) > { > @@ -7395,6 +7440,51 @@ iwx_scan_umac_dwell_v10(struct iwx_softc *sc, > } > > void > +iwx_scan_umac_dwell_v11(struct iwx_softc *sc, > + struct iwx_scan_general_params_v11 *general_params, int bgscan) > +{ > + uint32_t suspend_time, max_out_time; > + uint8_t active_dwell, passive_dwell; > + > + active_dwell = IWX_SCAN_DWELL_ACTIVE; > + passive_dwell = IWX_SCAN_DWELL_PASSIVE; > + > + general_params->adwell_default_social_chn = > + IWX_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; > + general_params->adwell_default_2g = IWX_SCAN_ADWELL_DEFAULT_LB_N_APS; > + general_params->adwell_default_5g = IWX_SCAN_ADWELL_DEFAULT_HB_N_APS; > + > + if (bgscan) > + general_params->adwell_max_budget = > + htole16(IWX_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); > + else > + general_params->adwell_max_budget = > + htole16(IWX_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); > + > + general_params->scan_priority = htole32(IWX_SCAN_PRIORITY_EXT_6); > + if (bgscan) { > + max_out_time = htole32(120); > + suspend_time = htole32(120); > + } else { > + max_out_time = htole32(0); > + suspend_time = htole32(0); > + } > + general_params->max_out_of_time[IWX_SCAN_LB_LMAC_IDX] = > + htole32(max_out_time); > + general_params->suspend_time[IWX_SCAN_LB_LMAC_IDX] = > + htole32(suspend_time); > + general_params->max_out_of_time[IWX_SCAN_HB_LMAC_IDX] = > + htole32(max_out_time); > + general_params->suspend_time[IWX_SCAN_HB_LMAC_IDX] = > + htole32(suspend_time); > + > + general_params->active_dwell[IWX_SCAN_LB_LMAC_IDX] = active_dwell; > + general_params->passive_dwell[IWX_SCAN_LB_LMAC_IDX] = passive_dwell; > + general_params->active_dwell[IWX_SCAN_HB_LMAC_IDX] = active_dwell; > + general_params->passive_dwell[IWX_SCAN_HB_LMAC_IDX] = passive_dwell; > +} > + > +void > iwx_scan_umac_fill_general_p_v10(struct iwx_softc *sc, > struct iwx_scan_general_params_v10 *gp, uint16_t gen_flags, int bgscan) > { > @@ -7411,6 +7501,21 @@ iwx_scan_umac_fill_general_p_v10(struct iwx_softc *sc, > } > > void > +iwx_scan_umac_fill_general_p_v11(struct iwx_softc *sc, > + struct iwx_scan_general_params_v11 *gp, uint16_t gen_flags, int bgscan) > +{ > + iwx_scan_umac_dwell_v11(sc, gp, bgscan); > + > + gp->flags = htole16(gen_flags); > + gp->scan_start_mac_or_link_id = 0; > + > + if (gen_flags & IWX_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) > + gp->num_of_fragments[IWX_SCAN_LB_LMAC_IDX] = 3; > + if (gen_flags & IWX_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2) > + gp->num_of_fragments[IWX_SCAN_HB_LMAC_IDX] = 3; > +} > + > +void > iwx_scan_umac_fill_ch_p_v6(struct iwx_softc *sc, > struct iwx_scan_channel_params_v6 *cp, uint32_t channel_cfg_flags, > int n_ssid) > @@ -7424,6 +7529,20 @@ iwx_scan_umac_fill_ch_p_v6(struct iwx_softc *sc, > cp->n_aps_override[1] = IWX_SCAN_ADWELL_N_APS_SOCIAL_CHS; > } > > +void > +iwx_scan_umac_fill_ch_p_v7(struct iwx_softc *sc, > + struct iwx_scan_channel_params_v7 *cp, uint32_t channel_cfg_flags, > + int n_ssid) > +{ > + cp->flags = IWX_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; > + > + cp->count = iwx_umac_scan_fill_channels_v5(sc, cp->channel_config, > + nitems(cp->channel_config), n_ssid, channel_cfg_flags); > + > + cp->n_aps_override[0] = IWX_SCAN_ADWELL_N_APS_GO_FRIENDLY; > + cp->n_aps_override[1] = IWX_SCAN_ADWELL_N_APS_SOCIAL_CHS; > +} > + > int > iwx_umac_scan_v14(struct iwx_softc *sc, int bgscan) > { > @@ -7484,6 +7603,66 @@ iwx_umac_scan_v14(struct iwx_softc *sc, int bgscan) > return err; > } > > +int > +iwx_umac_scan_v17(struct iwx_softc *sc, int bgscan) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct iwx_host_cmd hcmd = { > + .id = iwx_cmd_id(IWX_SCAN_REQ_UMAC, IWX_LONG_GROUP, 0), > + .len = { 0, }, > + .data = { NULL, }, > + .flags = 0, > + }; > + struct iwx_scan_req_umac_v17 *cmd; > + struct iwx_scan_req_params_v17 *scan_p; > + int err, async = bgscan, n_ssid = 0; > + uint16_t gen_flags; > + uint32_t bitmap_ssid = 0; > + > + cmd = malloc(sizeof(*cmd), M_DEVBUF, > + (async ? M_NOWAIT : M_WAIT) | M_CANFAIL | M_ZERO); > + if (cmd == NULL) > + return ENOMEM; > + > + scan_p = &cmd->scan_params; > + > + cmd->ooc_priority = htole32(IWX_SCAN_PRIORITY_EXT_6); > + cmd->uid = htole32(0); > + > + gen_flags = iwx_scan_umac_flags_v2(sc, bgscan); > + iwx_scan_umac_fill_general_p_v11(sc, &scan_p->general_params, > + gen_flags, bgscan); > + > + scan_p->periodic_params.schedule[0].interval = htole16(0); > + scan_p->periodic_params.schedule[0].iter_count = 1; > + > + err = iwx_fill_probe_req(sc, &scan_p->probe_params.preq); > + if (err) { > + free(cmd, M_DEVBUF, sizeof(*cmd)); > + return err; > + } > + > + if (ic->ic_des_esslen != 0) { > + scan_p->probe_params.direct_scan[0].id = IEEE80211_ELEMID_SSID; > + scan_p->probe_params.direct_scan[0].len = ic->ic_des_esslen; > + memcpy(scan_p->probe_params.direct_scan[0].ssid, > + ic->ic_des_essid, ic->ic_des_esslen); > + bitmap_ssid |= (1 << 0); > + n_ssid = 1; > + } > + > + iwx_scan_umac_fill_ch_p_v7(sc, &scan_p->channel_params, bitmap_ssid, > + n_ssid); > + > + hcmd.len[0] = sizeof(*cmd); > + hcmd.data[0] = (void *)cmd; > + hcmd.flags |= async ? IWX_CMD_ASYNC : 0; > + > + err = iwx_send_cmd(sc, &hcmd); > + free(cmd, M_DEVBUF, sizeof(*cmd)); > + return err; > +} > + > void > iwx_mcc_update(struct iwx_softc *sc, struct iwx_mcc_chub_notif *notif) > { > @@ -7872,6 +8051,27 @@ iwx_del_task(struct iwx_softc *sc, struct taskq *taskq > } > > int > +iwx_initiate_scan(struct iwx_softc *sc, int bgscan) > +{ > + int err; > + uint8_t version; > + > + version = iwx_lookup_cmd_ver(sc, IWX_LONG_GROUP, IWX_SCAN_REQ_UMAC); > + DPRINTF(("%s: scan command version %u\n", DEVNAME(sc), version)); > + > + switch (version) { > + case 17: > + err = iwx_umac_scan_v17(sc, bgscan); > + break; > + default: > + err = iwx_umac_scan_v14(sc, bgscan); > + break; > + } > + > + return err; > +} > + > +int > iwx_scan(struct iwx_softc *sc) > { > struct ieee80211com *ic = &sc->sc_ic; > @@ -7887,7 +8087,7 @@ iwx_scan(struct iwx_softc *sc) > } > } > > - err = iwx_umac_scan_v14(sc, 0); > + err = iwx_initiate_scan(sc, 0); > if (err) { > printf("%s: could not initiate scan\n", DEVNAME(sc)); > return err; > @@ -7924,7 +8124,7 @@ iwx_bgscan(struct ieee80211com *ic) > if (sc->sc_flags & IWX_FLAG_SCANNING) > return 0; > > - err = iwx_umac_scan_v14(sc, 1); > + err = iwx_initiate_scan(sc, 1); > if (err) { > printf("%s: could not initiate scan\n", DEVNAME(sc)); > return err; > blob - a6556c35edaf25e52203713e2701ee6a45431a30 > blob + 379c265ad8b23e0f535fb9da089800655229e90a > --- sys/dev/pci/if_iwxreg.h > +++ sys/dev/pci/if_iwxreg.h > @@ -3300,6 +3300,7 @@ struct iwx_time_quota_cmd { > /* Supported bands */ > #define IWX_PHY_BAND_5 (0) > #define IWX_PHY_BAND_24 (1) > +#define IWX_PHY_BAND_6 (2) > > /* Supported channel width, vary if there is VHT support */ > #define IWX_PHY_VHT_CHANNEL_MODE20 (0x0) > @@ -7281,6 +7282,26 @@ struct iwx_scan_channel_cfg_umac { > } __packed; > > /** > + * struct iwx_scan_channel_cfg_umac_v5 > + * @flags: bitmap - 0-19: directed scan to i'th ssid. > + * bits 30/31: band number (IWX_PHY_BAND_*) > + * @channel_num: channel number 1-13 etc. > + * @psd_20: highest PSD value for all APs known so far > + * on this channel. > + * @iter_count: repetition count for the channel. > + * @iter_interval: interval between two scan iterations on one channel. > + */ > +struct iwx_scan_channel_cfg_umac_v5 { > + uint32_t flags; > + uint8_t channel_num; > + uint8_t psd_20; > + uint8_t iter_count; > + uint8_t iter_interval; > +} __packed; /* SCAN_CHANNEL_CONFIG_API_S_VER_5 */ > + > +#define IWX_CHAN_CFG_FLAGS_BAND_POS 30 > + > +/** > * struct iwx_scan_umac_schedule > * @interval: interval in seconds between scan iterations > * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop > @@ -7578,6 +7599,92 @@ struct iwx_scan_req_umac_v14 { > } __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_14 */ > > /** > + * struct iwx_scan_general_params_v11 - channel params > + * @flags: &enum iwl_umac_scan_general_flags_v2 > + * @reserved: reserved for future > + * @scan_start_mac_or_link_id: report the scan start TSF time according to this > + * mac (up to verion 11) or link (starting with version 12) TSF > + * @active_dwell: dwell time for active scan per LMAC > + * @adwell_default_2g: adaptive dwell default number of APs > + * for 2.4GHz channel > + * @adwell_default_5g: adaptive dwell default number of APs > + * for 5GHz channels > + * @adwell_default_social_chn: adaptive dwell default number of > + * APs per social channel > + * @flags2: for version 11 see &enum iwl_umac_scan_general_params_flags2. > + * Otherwise reserved. > + * @adwell_max_budget: the maximal number of TUs that adaptive dwell > + * can add to the total scan time > + * @max_out_of_time: max out of serving channel time, per LMAC > + * @suspend_time: max suspend time, per LMAC > + * @scan_priority: priority of the request > + * @passive_dwell: continues dwell time for passive channel > + * (without adaptive dwell) > + * @num_of_fragments: number of fragments needed for full fragmented > + * scan coverage. > + */ > +struct iwx_scan_general_params_v11 { > + uint16_t flags; > + uint8_t reserved; > + uint8_t scan_start_mac_or_link_id; > + uint8_t active_dwell[IWX_SCAN_TWO_LMACS]; > + uint8_t adwell_default_2g; > + uint8_t adwell_default_5g; > + uint8_t adwell_default_social_chn; > + uint8_t flags2; > + uint16_t adwell_max_budget; > + uint32_t max_out_of_time[IWX_SCAN_TWO_LMACS]; > + uint32_t suspend_time[IWX_SCAN_TWO_LMACS]; > + uint32_t scan_priority; > + uint8_t passive_dwell[IWX_SCAN_TWO_LMACS]; > + uint8_t num_of_fragments[IWX_SCAN_TWO_LMACS]; > +} __packed; /* SCAN_GENERAL_PARAMS_API_S_VER_12, *_VER_11 and *_VER_10 */ > + > +/** > + * struct iwx_scan_channel_params_v7 - channel params > + * @flags: channel flags &enum iwl_scan_channel_flags > + * @count: num of channels in scan request > + * @n_aps_override: override the number of APs the FW uses to calculate dwell > + * time when adaptive dwell is used. > + * Channel k will use n_aps_override[i] when BIT(20 + i) is set in > + * channel_config[k].flags > + * @channel_config: array of explicit channel configurations > + * for 2.4Ghz and 5.2Ghz bands > + */ > +struct iwx_scan_channel_params_v7 { > + uint8_t flags; > + uint8_t count; > + uint8_t n_aps_override[2]; > + struct iwx_scan_channel_cfg_umac_v5 channel_config[67]; > +} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_7 */ > + > +/** > + * struct iwx_scan_req_params_v17 - scan request parameters (v17) > + * @general_params: &struct iwx_scan_general_params_v11 > + * @channel_params: &struct iwx_scan_channel_params_v7 > + * @periodic_params: &struct iwx_scan_periodic_parms_v1 > + * @probe_params: &struct iwx_scan_probe_params_v4 > + */ > +struct iwx_scan_req_params_v17 { > + struct iwx_scan_general_params_v11 general_params; > + struct iwx_scan_channel_params_v7 channel_params; > + struct iwx_scan_periodic_parms_v1 periodic_params; > + struct iwx_scan_probe_params_v4 probe_params; > +} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_17 - 14 */ > + > +/* > + * struct iwx_scan_req_umac_v17 - scan request command (v17) > + * @uid: scan id, &enum iwl_umac_scan_uid_offsets > + * @ooc_priority: out of channel priority - &enum iwl_scan_priority > + * @scan_params: scan parameters > + */ > +struct iwx_scan_req_umac_v17 { > + uint32_t uid; > + uint32_t ooc_priority; > + struct iwx_scan_req_params_v17 scan_params; > +} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_17 - 14 */ > + > +/** > * struct iwx_umac_scan_abort > * @uid: scan id, &enum iwx_umac_scan_uid_offsets > * @flags: reserved > >