From: Peter Hessler Subject: Re: support new statistics command for iwx(4) BZ firmware To: tech@openbsd.org Date: Fri, 27 Feb 2026 19:45:07 +0100 On 2026 Feb 25 (Wed) at 10:10:23 +0100 (+0100), Stefan Sperling wrote: :iwx(4) firmware for BZ devices will error out if we do not use a new :command to clear firmware statistics during association. This command :resets beacon counters and such in firmware, as far as I understand. : :Tested on AX200 and AX211 (BZ). : :ok? : Looks good to me, tested on AX211. OK :M sys/dev/pci/if_iwx.c | 82+ 1- :M sys/dev/pci/if_iwxreg.h | 241+ 0- :M sys/dev/pci/if_iwxvar.h | 2+ 0- : :3 files changed, 325 insertions(+), 1 deletion(-) : :commit - 6655131ffaa501eb72e7a2f2d3136cff9374dc7c :commit + 8b875e9369664196a41e34c921249fe052ab569d :blob - 03d36027340740248ccbb4c47f118b183ae1414e :blob + 9f84173e3c058bd9bd9509d4c7d4a90a42abae67 :--- sys/dev/pci/if_iwx.c :+++ sys/dev/pci/if_iwx.c :@@ -8005,7 +8005,7 @@ iwx_mld_mac_ctxt_cmd(struct iwx_softc *sc, struct iwx_ : } : : int :-iwx_clear_statistics(struct iwx_softc *sc) :+iwx_send_statistics_cmd_clear(struct iwx_softc *sc) : { : struct iwx_statistics_cmd scmd = { : .flags = htole32(IWX_STATISTICS_FLG_CLEAR) :@@ -8027,6 +8027,61 @@ iwx_clear_statistics(struct iwx_softc *sc) : return 0; : } : :+int :+iwx_send_system_statistics_cmd_clear(struct iwx_softc *sc) :+{ :+ struct iwx_system_statistics_cmd scmd = { :+ .cfg_mask = htole32(IWX_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK), :+ .type_id_mask = htole32(IWX_STATS_NTFY_TYPE_ID_OPER | :+ IWX_STATS_NTFY_TYPE_ID_OPER_PART1), :+ }; :+ struct iwx_host_cmd cmd = { :+ .id = IWX_WIDE_ID(IWX_SYSTEM_GROUP, IWX_SYSTEM_STATISTICS_CMD), :+ .len[0] = sizeof(scmd), :+ .data[0] = &scmd, :+ .flags = IWX_CMD_ASYNC, :+ }; :+ int err; :+ :+ sc->sc_system_stats_cleared = 0; :+ :+ err = iwx_send_cmd(sc, &cmd); :+ if (err) :+ return err; :+ :+ /* Wait for SYSTEM_STATISTICS_END_NOTIF firmware notification. */ :+ while (!sc->sc_system_stats_cleared) { :+ err = tsleep_nsec(&sc->sc_system_stats_cleared, 0, "iwxstat", :+ SEC_TO_NSEC(1)); :+ if (err) :+ break; :+ } :+ :+ return err; :+} :+ :+int :+iwx_clear_statistics(struct iwx_softc *sc) :+{ :+ uint32_t version; :+ :+ version = iwx_lookup_cmd_ver(sc, IWX_SYSTEM_GROUP, :+ IWX_SYSTEM_STATISTICS_CMD); :+ :+ switch (version) { :+ case IWX_FW_CMD_VER_UNKNOWN: :+ return iwx_send_statistics_cmd_clear(sc); :+ case 1: :+ return iwx_send_system_statistics_cmd_clear(sc); :+ default: :+ printf("%s: unknown statistics command version %d\n", :+ DEVNAME(sc), version); :+ break; :+ } :+ :+ return 0; :+} :+ : void : iwx_add_task(struct iwx_softc *sc, struct taskq *taskq, struct task *task) : { :@@ -10948,6 +11003,32 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *d : wakeup(&sc->sc_init_complete); : break; : :+ case IWX_WIDE_ID(IWX_SYSTEM_GROUP, IWX_SYSTEM_STATISTICS_CMD): :+ break; :+ :+ case IWX_WIDE_ID(IWX_SYSTEM_GROUP, :+ IWX_SYSTEM_STATISTICS_END_NOTIF): { :+ struct iwx_system_statistics_end_notif *notif; :+ SYNC_RESP_STRUCT(notif, pkt); :+ sc->sc_system_stats_cleared = 1; :+ wakeup(&sc->sc_system_stats_cleared); :+ break; :+ } :+ :+ case IWX_WIDE_ID(IWX_STATISTICS_GROUP, :+ IWX_STATISTICS_OPER_NOTIF): { :+ struct iwx_system_statistics_notif_oper *notif; :+ SYNC_RESP_STRUCT(notif, pkt); :+ break; :+ } :+ :+ case IWX_WIDE_ID(IWX_STATISTICS_GROUP, :+ IWX_STATISTICS_OPER_PART1_NOTIF): { :+ struct iwx_system_statistics_part1_notif_oper *notif; :+ SYNC_RESP_STRUCT(notif, pkt); :+ break; :+ } :+ : default: : handled = 0; : printf("%s: unhandled firmware response 0x%x/0x%x " :blob - 379c265ad8b23e0f535fb9da089800655229e90a :blob + 30c01620bfc3718d35645148a6f79e32c313796f :--- sys/dev/pci/if_iwxreg.h :+++ sys/dev/pci/if_iwxreg.h :@@ -1997,6 +1997,9 @@ struct iwx_tx_queue_cfg_rsp { : #define IWX_DATA_PATH_GROUP 0x5 : #define IWX_PROT_OFFLOAD_GROUP 0xb : #define IWX_REGULATORY_AND_NVM_GROUP 0xc :+#define IWX_XVT_GROUP 0xe :+#define IWX_DEBUG_GROUP 0xf :+#define IWX_STATISTICS_GROUP 0x10 : : /* SYSTEM_GROUP group subcommand IDs */ : :@@ -2004,6 +2007,8 @@ struct iwx_tx_queue_cfg_rsp { : #define IWX_SOC_CONFIGURATION_CMD 0x01 : #define IWX_INIT_EXTENDED_CFG_CMD 0x03 : #define IWX_FW_ERROR_RECOVERY_CMD 0x07 :+#define IWX_SYSTEM_STATISTICS_CMD 0x0f :+#define IWX_SYSTEM_STATISTICS_END_NOTIF 0xfd : : /* MAC_CONF group subcommand IDs */ : #define IWX_SESSION_PROTECTION_CMD 0x05 :@@ -2029,6 +2034,13 @@ struct iwx_tx_queue_cfg_rsp { : #define IWX_NVM_GET_INFO 0x02 : #define IWX_PNVM_INIT_COMPLETE 0xfe : :+/* STATISTICS group subcommand IDs */ :+#define IWX_STATISTICS_OPER_NOTIF 0x0 :+#define IWX_STATISTICS_OPER_PART1_NOTIF 0x1 :+#define IWX_STATISTICS_OPER_PART2_NOTIF 0x2 :+#define IWX_STATISTICS_OPER_PART3_NOTIF 0x3 :+#define IWX_STATISTICS_OPER_PART4_NOTIF 0x4 :+ : /* : * struct iwx_dqa_enable_cmd : * @cmd_queue: the TXQ number of the command queue :@@ -4143,7 +4155,236 @@ struct iwx_statistics_cmd { : uint32_t flags; : } __packed; /* STATISTICS_CMD_API_S_VER_1 */ : :+/** :+ * enum iwx_statistics_notify_type_id - type_id used in system statistics :+ * command :+ * @IWX_STATS_NTFY_TYPE_ID_OPER: request legacy statistics :+ * @IWX_STATS_NTFY_TYPE_ID_OPER_PART1: request operational part1 statistics :+ * @IWX_STATS_NTFY_TYPE_ID_OPER_PART2: request operational part2 statistics :+ * @IWX_STATS_NTFY_TYPE_ID_OPER_PART3: request operational part3 statistics :+ * @IWX_STATS_NTFY_TYPE_ID_OPER_PART4: request operational part4 statistics :+ */ :+enum iwx_statistics_notify_type_id { :+ IWX_STATS_NTFY_TYPE_ID_OPER = (1 << 0), :+ IWX_STATS_NTFY_TYPE_ID_OPER_PART1 = (1 << 1), :+ IWX_STATS_NTFY_TYPE_ID_OPER_PART2 = (1 << 2), :+ IWX_STATS_NTFY_TYPE_ID_OPER_PART3 = (1 << 3), :+ IWX_STATS_NTFY_TYPE_ID_OPER_PART4 = (1 << 4), :+}; : :+/** :+ * enum iwx_statistics_cfg_flags - cfg_mask used in system statistics command :+ * @IWX_STATS_CFG_FLG_DISABLE_NTFY_MSK: 0 for enable, 1 for disable :+ * @IWX_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK: 0 for periodic, 1 for on-demand :+ * @IWX_STATS_CFG_FLG_RESET_MSK: 0 for reset statistics after :+ * sending the notification, 1 for do not reset statistics after sending :+ * the notification :+ */ :+enum iwx_statistics_cfg_flags { :+ IWX_STATS_CFG_FLG_DISABLE_NTFY_MSK = (1 << 0), :+ IWX_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK = (1 << 1), :+ IWX_STATS_CFG_FLG_RESET_MSK = (1 << 2), :+}; :+ :+/** :+ * struct iwx_system_statistics_cmd - system statistics command :+ * @cfg_mask: configuration mask, &enum iwx_statistics_cfg_flags :+ * @config_time_sec: time in sec for periodic notification :+ * @type_id_mask: type_id masks, &enum iwx_statistics_notify_type_id :+ */ :+struct iwx_system_statistics_cmd { :+ uint32_t cfg_mask; :+ uint32_t config_time_sec; :+ uint32_t type_id_mask; :+} __packed; /* STATISTICS_FW_CMD_API_S_VER_1 */ :+ :+/** :+ * enum iwx_fw_statistics_type - statistics type :+ * :+ * @FW_STATISTICS_OPERATIONAL: operational statistics :+ * @FW_STATISTICS_PHY: phy statistics :+ * @FW_STATISTICS_MAC: mac statistics :+ * @FW_STATISTICS_RX: rx statistics :+ * @FW_STATISTICS_TX: tx statistics :+ * @FW_STATISTICS_DURATION: duration statistics :+ * @FW_STATISTICS_HE: he statistics :+ */ :+enum iwx_fw_statistics_type { :+ FW_STATISTICS_OPERATIONAL, :+ FW_STATISTICS_PHY, :+ FW_STATISTICS_MAC, :+ FW_STATISTICS_RX, :+ FW_STATISTICS_TX, :+ FW_STATISTICS_DURATION, :+ FW_STATISTICS_HE, :+}; /* FW_STATISTICS_TYPE_API_E_VER_1 */ :+ :+#define IWX_STATISTICS_TYPE_MSK 0x7f :+ :+/** :+ * struct iwx_statistics_ntfy_hdr - statistics notification header :+ * :+ * @type: struct type :+ * @version: version of the struct :+ * @size: size in bytes :+ */ :+struct iwx_statistics_ntfy_hdr { :+ uint8_t type; :+ uint8_t version; :+ uint16_t size; :+}; /* STATISTICS_NTFY_HDR_API_S_VER_1 */ :+ :+/** :+ * struct iwx_stats_ntfy_per_link - per-link statistics :+ * :+ * @beacon_filter_average_energy: Average energy [-dBm] of the 2 :+ * antennas. :+ * @air_time: air time :+ * @beacon_counter: all beacons (both filtered and not filtered) :+ * @beacon_average_energy: Average energy [-dBm] of all beacons :+ * (both filtered and not filtered) :+ * @beacon_rssi_a: beacon RSSI on antenna A :+ * @beacon_rssi_b: beacon RSSI on antenna B :+ * @rx_bytes: RX byte count :+ */ :+struct iwx_stats_ntfy_per_link { :+ uint32_t beacon_filter_average_energy; :+ uint32_t air_time; :+ uint32_t beacon_counter; :+ uint32_t beacon_average_energy; :+ uint32_t beacon_rssi_a; :+ uint32_t beacon_rssi_b; :+ uint32_t rx_bytes; :+} __packed; /* STATISTICS_NTFY_PER_LINK_API_S_VER_1 */ :+ :+/** :+ * struct iwl_stats_ntfy_part1_per_link - part1 per link statistics :+ * :+ * @rx_time: rx time :+ * @tx_time: tx time :+ * @rx_action: action frames handled by FW :+ * @tx_action: action frames generated and transmitted by FW :+ * @cca_defers: cca defer count :+ * @beacon_filtered: filtered out beacons :+ */ :+struct iwx_stats_ntfy_part1_per_link { :+ uint64_t rx_time; :+ uint64_t tx_time; :+ uint32_t rx_action; :+ uint32_t tx_action; :+ uint32_t cca_defers; :+ uint32_t beacon_filtered; :+} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_PER_LINK_API_S_VER_1 */ :+ :+/** :+ * struct iwx_stats_ntfy_per_mac - per MAC statistics :+ * :+ * @beacon_filter_average_energy: Average energy [-dBm] of the 2 :+ * antennas. :+ * @air_time: air time :+ * @beacon_counter: all beacons (both filtered and not filtered) :+ * @beacon_average_energy: all beacons (both filtered and not :+ * filtered) :+ * @beacon_rssi_a: beacon RSSI on antenna A :+ * @beacon_rssi_b: beacon RSSI on antenna B :+ * @rx_bytes: RX byte count :+ */ :+struct iwx_stats_ntfy_per_mac { :+ uint32_t beacon_filter_average_energy; :+ uint32_t air_time; :+ uint32_t beacon_counter; :+ uint32_t beacon_average_energy; :+ uint32_t beacon_rssi_a; :+ uint32_t beacon_rssi_b; :+ uint32_t rx_bytes; :+} __packed; /* STATISTICS_NTFY_PER_MAC_API_S_VER_1 */ :+ :+#define IWX_STATS_MAX_BW_INDEX 5 :+ :+/** :+ * struct iwx_stats_ntfy_per_phy - per PHY statistics :+ * @channel_load: channel load :+ * @channel_load_by_us: device contribution to MCLM :+ * @channel_load_not_by_us: other devices' contribution to MCLM :+ * @clt: CLT HW timer (TIM_CH_LOAD2) :+ * @act: active accumulator SW :+ * @elp: elapsed time accumulator SW :+ * @rx_detected_per_ch_width: number of deferred TX per channel width, :+ * 0 - 20, 1/2/3 - 40/80/160 :+ * @success_per_ch_width: number of frames that got ACK/BACK/CTS :+ * per channel BW. note, BACK counted as 1 :+ * @fail_per_ch_width: number of frames that didn't get ACK/BACK/CTS :+ * per channel BW. note BACK counted as 1 :+ * @last_tx_ch_width_indx: last txed frame channel width index :+ */ :+struct iwx_stats_ntfy_per_phy { :+ uint32_t channel_load; :+ uint32_t channel_load_by_us; :+ uint32_t channel_load_not_by_us; :+ uint32_t clt; :+ uint32_t act; :+ uint32_t elp; :+ uint32_t rx_detected_per_ch_width[IWX_STATS_MAX_BW_INDEX]; :+ uint32_t success_per_ch_width[IWX_STATS_MAX_BW_INDEX]; :+ uint32_t fail_per_ch_width[IWX_STATS_MAX_BW_INDEX]; :+ uint32_t last_tx_ch_width_indx; :+} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */ :+ :+/* unknown channel load (due to not being active on channel) */ :+#define IWX_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff :+ :+/** :+ * struct iwx_stats_ntfy_per_sta - per STA statistics :+ * :+ * @average_energy: in fact it is minus the energy.. :+ */ :+struct iwx_stats_ntfy_per_sta { :+ uint32_t average_energy; :+} __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */ :+ :+ :+#define IWX_FW_MAX_ACTIVE_LINKS_NUM 2 :+#define IWX_FW_MAX_LINK_ID 3 :+#define IWX_STATS_MAX_PHY_OPERATIONAL 3 :+#define IWX_STATS_MAX_FW_LINKS (IWX_FW_MAX_LINK_ID + 1) :+ :+/** :+ * struct iwx_system_statistics_notif_oper - statistics notification :+ * :+ * @time_stamp: time when the notification is sent from firmware :+ * @per_link: per link statistics, &struct iwl_stats_ntfy_per_link :+ * @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy :+ * @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta :+ */ :+struct iwx_system_statistics_notif_oper { :+ uint32_t time_stamp; :+ struct iwx_stats_ntfy_per_link per_link[IWX_STATS_MAX_FW_LINKS]; :+ struct iwx_stats_ntfy_per_phy per_phy[IWX_STATS_MAX_PHY_OPERATIONAL]; :+ struct iwx_stats_ntfy_per_sta per_sta[IWX_STATION_COUNT]; :+} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */ :+ :+/** :+ * struct iwx_system_statistics_part1_notif_oper - part1 stats notification :+ * :+ * @time_stamp: time when the notification is sent from firmware :+ * @per_link: per link statistics &struct iwl_stats_ntfy_part1_per_link :+ * @per_phy_crc_error_stats: per phy crc error statistics :+ */ :+struct iwx_system_statistics_part1_notif_oper { :+ uint32_t time_stamp; :+ struct iwx_stats_ntfy_part1_per_link per_link[IWX_STATS_MAX_FW_LINKS]; :+ uint32_t per_phy_crc_error_stats[IWX_STATS_MAX_PHY_OPERATIONAL]; :+} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */ :+ :+/** :+ * struct iwx_system_statistics_end_notif - statistics end notification :+ * :+ * @time_stamp: time when the notification is sent from firmware :+ */ :+struct iwx_system_statistics_end_notif { :+ uint32_t time_stamp; :+} __packed; /* STATISTICS_FW_NTFY_END_API_S_VER_1 */ :+ : /*********************************** : * Smart Fifo API : ***********************************/ :blob - 081e2b4a9ba620acba3f48f2ed725f20db0a0b02 :blob + bd6c4bcd4f3957e104205a11c25118a4c62b1849 :--- sys/dev/pci/if_iwxvar.h :+++ sys/dev/pci/if_iwxvar.h :@@ -734,6 +734,8 @@ struct iwx_softc { : #define IWX_CALIB_COMPLETE 0x02 : #define IWX_PNVM_COMPLETE 0x04 : :+ int sc_system_stats_cleared; :+ : struct iwx_ucode_status sc_uc; : char sc_fwver[32]; : : -- The faster we go, the rounder we get. -- The Grateful Dead