-/* $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 <info@genua.de>
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);
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)
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;
}
/* 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;
}
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)
pkt = hcmd.resp_pkt;
if (!pkt || (pkt->hdr.flags & IWX_CMD_FAILED_MSK)) {
- DPRINTF(("SCD_QUEUE_CFG command failed\n"));
err = EIO;
goto out;
}
{
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) {
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",
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)
{
err = iwx_clear_statistics(sc);
if (err)
- goto rm_sta;
+ goto rm_mgmt_queue;
/*
* Prevent the FW from wandering off channel during association
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);
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,
-/* $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,
#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
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