From: stsp Date: Mon, 6 Mar 2023 11:18:37 +0000 (+0000) Subject: support the new iwx SCD_QUEUE_CONFIG command X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=ea78257db901aa74f5fea069b0422774d2cb0bb7;p=openbsd support the new iwx SCD_QUEUE_CONFIG command Required for adding/removing Tx queues on new firmware versions. Semantics have changed yet again. Good old iwm(4) required manual removal of related Tx queues when a station is removed from firmware. At some point iwx(4) firmware removed this requirement and demanded that queue removal would be done implicitly by firmware instead. And now the firmware engineers at Intel have again changed their minds about this, and explicit queue removal is required again. It is sad that we are being driven through such a marathon of changes just in order to pick up some firmware security fixes... --- diff --git a/sys/dev/pci/if_iwx.c b/sys/dev/pci/if_iwx.c index 43e0c8c8a5b..d12efc25daf 100644 --- a/sys/dev/pci/if_iwx.c +++ b/sys/dev/pci/if_iwx.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwx.c,v 1.162 2023/03/06 11:08:56 stsp Exp $ */ +/* $OpenBSD: if_iwx.c,v 1.163 2023/03/06 11:18:37 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh @@ -447,6 +447,7 @@ void iwx_bgscan_done_task(void *); int iwx_umac_scan_abort(struct iwx_softc *); int iwx_scan_abort(struct iwx_softc *); int iwx_enable_mgmt_queue(struct iwx_softc *); +int iwx_disable_mgmt_queue(struct iwx_softc *); int iwx_rs_rval2idx(uint8_t); uint16_t iwx_rs_ht_rates(struct iwx_softc *, struct ieee80211_node *, int); uint16_t iwx_rs_vht_rates(struct iwx_softc *, struct ieee80211_node *, int); @@ -2815,31 +2816,52 @@ int iwx_enable_txq(struct iwx_softc *sc, int sta_id, int qid, int tid, int num_slots) { - struct iwx_tx_queue_cfg_cmd cmd; struct iwx_rx_packet *pkt; struct iwx_tx_queue_cfg_rsp *resp; + struct iwx_tx_queue_cfg_cmd cmd_v0; + struct iwx_scd_queue_cfg_cmd cmd_v3; struct iwx_host_cmd hcmd = { - .id = IWX_SCD_QUEUE_CFG, .flags = IWX_CMD_WANT_RESP, .resp_pkt_len = sizeof(*pkt) + sizeof(*resp), }; struct iwx_tx_ring *ring = &sc->txq[qid]; - int err, fwqid; + int err, fwqid, cmd_ver; uint32_t wr_idx; size_t resp_len; iwx_reset_tx_ring(sc, ring); - memset(&cmd, 0, sizeof(cmd)); - cmd.sta_id = sta_id; - cmd.tid = tid; - cmd.flags = htole16(IWX_TX_QUEUE_CFG_ENABLE_QUEUE); - cmd.cb_size = htole32(IWX_TFD_QUEUE_CB_SIZE(num_slots)); - cmd.byte_cnt_addr = htole64(ring->bc_tbl.paddr); - cmd.tfdq_addr = htole64(ring->desc_dma.paddr); - - hcmd.data[0] = &cmd; - hcmd.len[0] = sizeof(cmd); + cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN) { + memset(&cmd_v0, 0, sizeof(cmd_v0)); + cmd_v0.sta_id = sta_id; + cmd_v0.tid = tid; + cmd_v0.flags = htole16(IWX_TX_QUEUE_CFG_ENABLE_QUEUE); + cmd_v0.cb_size = htole32(IWX_TFD_QUEUE_CB_SIZE(num_slots)); + cmd_v0.byte_cnt_addr = htole64(ring->bc_tbl.paddr); + cmd_v0.tfdq_addr = htole64(ring->desc_dma.paddr); + hcmd.id = IWX_SCD_QUEUE_CFG; + hcmd.data[0] = &cmd_v0; + hcmd.len[0] = sizeof(cmd_v0); + } else if (cmd_ver == 3) { + memset(&cmd_v3, 0, sizeof(cmd_v3)); + cmd_v3.operation = htole32(IWX_SCD_QUEUE_ADD); + cmd_v3.u.add.tfdq_dram_addr = htole64(ring->desc_dma.paddr); + cmd_v3.u.add.bc_dram_addr = htole64(ring->bc_tbl.paddr); + cmd_v3.u.add.cb_size = htole32(IWX_TFD_QUEUE_CB_SIZE(num_slots)); + cmd_v3.u.add.flags = htole32(0); + cmd_v3.u.add.sta_mask = htole32(1 << sta_id); + cmd_v3.u.add.tid = tid; + hcmd.id = IWX_WIDE_ID(IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + hcmd.data[0] = &cmd_v3; + hcmd.len[0] = sizeof(cmd_v3); + } else { + printf("%s: unsupported SCD_QUEUE_CFG command version %d\n", + DEVNAME(sc), cmd_ver); + return ENOTSUP; + } err = iwx_send_cmd(sc, &hcmd); if (err) @@ -2847,14 +2869,12 @@ iwx_enable_txq(struct iwx_softc *sc, int sta_id, int qid, int tid, pkt = hcmd.resp_pkt; if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) { - DPRINTF(("SCD_QUEUE_CFG command failed\n")); err = EIO; goto out; } resp_len = iwx_rx_packet_payload_len(pkt); if (resp_len != sizeof(*resp)) { - DPRINTF(("SCD_QUEUE_CFG returned %zu bytes, expected %zu bytes\n", resp_len, sizeof(*resp))); err = EIO; goto out; } @@ -2865,14 +2885,11 @@ iwx_enable_txq(struct iwx_softc *sc, int sta_id, int qid, int tid, /* Unlike iwlwifi, we do not support dynamic queue ID assignment. */ if (fwqid != qid) { - DPRINTF(("requested qid %d but %d was assigned\n", qid, fwqid)); err = EIO; goto out; } if (wr_idx != ring->cur_hw) { - DPRINTF(("fw write index is %d but ring is %d\n", - wr_idx, ring->cur_hw)); err = EIO; goto out; } @@ -2887,27 +2904,44 @@ out: int iwx_disable_txq(struct iwx_softc *sc, int sta_id, int qid, uint8_t tid) { - struct iwx_tx_queue_cfg_cmd cmd; struct iwx_rx_packet *pkt; struct iwx_tx_queue_cfg_rsp *resp; + struct iwx_tx_queue_cfg_cmd cmd_v0; + struct iwx_scd_queue_cfg_cmd cmd_v3; struct iwx_host_cmd hcmd = { - .id = IWX_SCD_QUEUE_CFG, .flags = IWX_CMD_WANT_RESP, .resp_pkt_len = sizeof(*pkt) + sizeof(*resp), }; struct iwx_tx_ring *ring = &sc->txq[qid]; - int err; - - memset(&cmd, 0, sizeof(cmd)); - cmd.sta_id = sta_id; - cmd.tid = tid; - cmd.flags = htole16(0); /* clear "queue enabled" flag */ - cmd.cb_size = htole32(0); - cmd.byte_cnt_addr = htole64(0); - cmd.tfdq_addr = htole64(0); + int err, cmd_ver; - hcmd.data[0] = &cmd; - hcmd.len[0] = sizeof(cmd); + cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN) { + memset(&cmd_v0, 0, sizeof(cmd_v0)); + cmd_v0.sta_id = sta_id; + cmd_v0.tid = tid; + cmd_v0.flags = htole16(0); /* clear "queue enabled" flag */ + cmd_v0.cb_size = htole32(0); + cmd_v0.byte_cnt_addr = htole64(0); + cmd_v0.tfdq_addr = htole64(0); + hcmd.id = IWX_SCD_QUEUE_CFG, + hcmd.data[0] = &cmd_v0; + hcmd.len[0] = sizeof(cmd_v0); + } else if (cmd_ver == 3) { + memset(&cmd_v3, 0, sizeof(cmd_v3)); + cmd_v3.operation = htole32(IWX_SCD_QUEUE_REMOVE); + cmd_v3.u.remove.sta_mask = htole32(1 << sta_id); + cmd_v3.u.remove.tid = tid; + hcmd.id = IWX_WIDE_ID(IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + hcmd.data[0] = &cmd_v3; + hcmd.len[0] = sizeof(cmd_v3); + } else { + printf("%s: unsupported SCD_QUEUE_CFG command version %d\n", + DEVNAME(sc), cmd_ver); + return ENOTSUP; + } err = iwx_send_cmd(sc, &hcmd); if (err) @@ -2915,7 +2949,6 @@ iwx_disable_txq(struct iwx_softc *sc, int sta_id, int qid, uint8_t tid) pkt = hcmd.resp_pkt; if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) { - DPRINTF(("SCD_QUEUE_CFG command failed\n")); err = EIO; goto out; } @@ -6757,7 +6790,7 @@ iwx_rm_sta(struct iwx_softc *sc, struct iwx_node *in) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; - int err, i; + int err, i, cmd_ver; err = iwx_flush_sta(sc, in); if (err) { @@ -6765,6 +6798,33 @@ iwx_rm_sta(struct iwx_softc *sc, struct iwx_node *in) DEVNAME(sc), err); return err; } + + /* + * New SCD_QUEUE_CONFIG API requires explicit queue removal + * before a station gets removed. + */ + cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + if (cmd_ver != 0 && cmd_ver != IWX_FW_CMD_VER_UNKNOWN) { + err = iwx_disable_mgmt_queue(sc); + if (err) + return err; + for (i = IWX_FIRST_AGG_TX_QUEUE; + i < IWX_LAST_AGG_TX_QUEUE; i++) { + struct iwx_tx_ring *ring = &sc->txq[i]; + if ((sc->qenablemsk & (1 << i)) == 0) + continue; + err = iwx_disable_txq(sc, IWX_STATION_ID, + ring->qid, ring->tid); + if (err) { + printf("%s: could not disable Tx queue %d " + "(error %d)\n", DEVNAME(sc), ring->qid, + err); + return err; + } + } + } + err = iwx_rm_sta_cmd(sc, in); if (err) { printf("%s: could not remove STA (error %d)\n", @@ -7652,6 +7712,30 @@ iwx_enable_mgmt_queue(struct iwx_softc *sc) return 0; } +int +iwx_disable_mgmt_queue(struct iwx_softc *sc) +{ + int err, cmd_ver; + + /* Explicit removal is only required with old SCD_QUEUE_CFG command. */ + cmd_ver = iwx_lookup_cmd_ver(sc, IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD); + if (cmd_ver == 0 || cmd_ver == IWX_FW_CMD_VER_UNKNOWN) + return 0; + + sc->first_data_qid = IWX_DQA_CMD_QUEUE + 1; + + err = iwx_disable_txq(sc, IWX_STATION_ID, sc->first_data_qid, + IWX_MGMT_TID); + if (err) { + printf("%s: could not disable Tx queue %d (error %d)\n", + DEVNAME(sc), sc->first_data_qid, err); + return err; + } + + return 0; +} + int iwx_rs_rval2idx(uint8_t rval) { @@ -8092,7 +8176,7 @@ iwx_auth(struct iwx_softc *sc) err = iwx_clear_statistics(sc); if (err) - goto rm_sta; + goto rm_mgmt_queue; /* * Prevent the FW from wandering off channel during association @@ -8103,6 +8187,9 @@ iwx_auth(struct iwx_softc *sc) else duration = IEEE80211_DUR_TU; return iwx_schedule_session_protection(sc, in, duration); +rm_mgmt_queue: + if (generation == sc->sc_generation) + iwx_disable_mgmt_queue(sc); rm_sta: if (generation == sc->sc_generation) { iwx_rm_sta_cmd(sc, in); @@ -9880,6 +9967,8 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *data, struct mbuf_list *ml) break; } + case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, + IWX_SCD_QUEUE_CONFIG_CMD): case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_RX_BAID_ALLOCATION_CONFIG_CMD): case IWX_WIDE_ID(IWX_MAC_CONF_GROUP, diff --git a/sys/dev/pci/if_iwxreg.h b/sys/dev/pci/if_iwxreg.h index 6cb4bb54602..083ffb9e2fb 100644 --- a/sys/dev/pci/if_iwxreg.h +++ b/sys/dev/pci/if_iwxreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwxreg.h,v 1.50 2023/03/06 11:08:56 stsp Exp $ */ +/* $OpenBSD: if_iwxreg.h,v 1.51 2023/03/06 11:18:37 stsp Exp $ */ /*- * Based on BSD-licensed source modules in the Linux iwlwifi driver, @@ -2008,6 +2008,7 @@ struct iwx_tx_queue_cfg_rsp { #define IWX_RLC_CONFIG_CMD 0x08 #define IWX_TLC_MNG_CONFIG_CMD 0x0f #define IWX_RX_BAID_ALLOCATION_CONFIG_CMD 0x16 +#define IWX_SCD_QUEUE_CONFIG_CMD 0x17 #define IWX_RX_NO_DATA_NOTIF 0xf5 #define IWX_TLC_MNG_UPDATE_NOTIF 0xf7 @@ -5428,6 +5429,57 @@ struct iwx_rx_baid_cfg_resp { uint32_t baid; }; /* RX_BAID_ALLOCATION_RESPONSE_API_S_VER_1 */ +/** + * scheduler queue operation + * @IWX_SCD_QUEUE_ADD: allocate a new queue + * @IWX_SCD_QUEUE_REMOVE: remove a queue + * @IWX_SCD_QUEUE_MODIFY: modify a queue + */ +#define IWX_SCD_QUEUE_ADD 0 +#define IWX_SCD_QUEUE_REMOVE 1 +#define IWX_SCD_QUEUE_MODIFY 2 + +/** + * struct iwx_scd_queue_cfg_cmd - scheduler queue allocation command + * @operation: the operation, see &enum iwl_scd_queue_cfg_operation + * @u.add.sta_mask: station mask + * @u.add.tid: TID + * @u.add.reserved: reserved + * @u.add.flags: flags from &enum iwl_tx_queue_cfg_actions, except + * %TX_QUEUE_CFG_ENABLE_QUEUE is not valid + * @u.add.cb_size: size code + * @u.add.bc_dram_addr: byte-count table IOVA + * @u.add.tfdq_dram_addr: TFD queue IOVA + * @u.remove.sta_mask: station mask of queue to remove + * @u.remove.tid: TID of queue to remove + * @u.modify.old_sta_mask: old station mask for modify + * @u.modify.tid: TID of queue to modify + * @u.modify.new_sta_mask: new station mask for modify + */ +struct iwx_scd_queue_cfg_cmd { + uint32_t operation; + union { + struct { + uint32_t sta_mask; + uint8_t tid; + uint8_t reserved[3]; + uint32_t flags; + uint32_t cb_size; + uint64_t bc_dram_addr; + uint64_t tfdq_dram_addr; + } __packed add; /* TX_QUEUE_CFG_CMD_ADD_API_S_VER_1 */ + struct { + uint32_t sta_mask; + uint32_t tid; + } __packed remove; /* TX_QUEUE_CFG_CMD_REMOVE_API_S_VER_1 */ + struct { + uint32_t old_sta_mask; + uint32_t tid; + uint32_t new_sta_mask; + } __packed modify; /* TX_QUEUE_CFG_CMD_MODIFY_API_S_VER_1 */ + } __packed u; /* TX_QUEUE_CFG_CMD_OPERATION_API_U_VER_1 */ +} __packed; /* TX_QUEUE_CFG_CMD_API_S_VER_3 */ + /** * Options for TLC config flags * @IWX_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC. For HE this enables STBC for