Support MCC update response used by newer iwm(4) firmware, and verify
authorstsp <stsp@openbsd.org>
Thu, 8 Jul 2021 17:14:08 +0000 (17:14 +0000)
committerstsp <stsp@openbsd.org>
Thu, 8 Jul 2021 17:14:08 +0000 (17:14 +0000)
the size of the response we receive for MCC_UPDATE commands (even
though we aren't doing anything with this response yet).

With the correct expected response length this time. The driver needs
to expect the maximum response size, not sizeof() the response struct.
The actual response size depends on the number of channels stored
in the "world" regulatory profile of the device.

ok sthen@

sys/dev/pci/if_iwm.c
sys/dev/pci/if_iwmreg.h
sys/dev/pci/if_iwmvar.h

index ead7996..e760d1a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwm.c,v 1.342 2021/07/08 00:12:49 stsp Exp $       */
+/*     $OpenBSD: if_iwm.c,v 1.343 2021/07/08 17:14:08 stsp Exp $       */
 
 /*
  * Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -456,6 +456,7 @@ int iwm_fill_probe_req(struct iwm_softc *, struct iwm_scan_probe_req *);
 int    iwm_lmac_scan(struct iwm_softc *, int);
 int    iwm_config_umac_scan(struct iwm_softc *);
 int    iwm_umac_scan(struct iwm_softc *, int);
+void   iwm_mcc_update(struct iwm_softc *, struct iwm_mcc_chub_notif *);
 uint8_t        iwm_ridx2rate(struct ieee80211_rateset *, int);
 int    iwm_rval2ridx(int);
 void   iwm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *);
@@ -698,7 +699,6 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type)
        sc->sc_capa_n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS;
        memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa));
        sc->n_cmd_versions = 0;
-       memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc));
 
        uhdr = (void *)fw->fw_rawdata;
        if (*(uint32_t *)fw->fw_rawdata != 0
@@ -7601,6 +7601,24 @@ iwm_umac_scan(struct iwm_softc *sc, int bgscan)
        return err;
 }
 
+void
+iwm_mcc_update(struct iwm_softc *sc, struct iwm_mcc_chub_notif *notif)
+{
+       struct ieee80211com *ic = &sc->sc_ic;
+       struct ifnet *ifp = IC2IFP(ic);
+       char alpha2[3];
+
+       snprintf(alpha2, sizeof(alpha2), "%c%c",
+           (le16toh(notif->mcc) & 0xff00) >> 8, le16toh(notif->mcc) & 0xff);
+
+       if (ifp->if_flags & IFF_DEBUG) {
+               printf("%s: firmware has detected regulatory domain '%s' "
+                   "(0x%x)\n", DEVNAME(sc), alpha2, le16toh(notif->mcc));
+       }
+
+       /* TODO: Schedule a task to send MCC_UPDATE_CMD? */
+}
+
 uint8_t
 iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx)
 {
@@ -9091,11 +9109,14 @@ iwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2)
        struct iwm_host_cmd hcmd = {
                .id = IWM_MCC_UPDATE_CMD,
                .flags = IWM_CMD_WANT_RESP,
+               .resp_pkt_len = IWM_CMD_RESP_MAX,
                .data = { &mcc_cmd },
        };
+       struct iwm_rx_packet *pkt;
+       size_t resp_len;
        int err;
-       int resp_v2 = isset(sc->sc_enabled_capa,
-           IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2);
+       int resp_v3 = isset(sc->sc_enabled_capa,
+           IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V3);
 
        if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000 &&
            !sc->sc_nvm.lar_enabled) {
@@ -9110,23 +9131,54 @@ iwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2)
        else
                mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW;
 
-       if (resp_v2) {
+       if (resp_v3) { /* same size as resp_v2 */
                hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd);
-               hcmd.resp_pkt_len = sizeof(struct iwm_rx_packet) +
-                   sizeof(struct iwm_mcc_update_resp);
        } else {
                hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1);
-               hcmd.resp_pkt_len = sizeof(struct iwm_rx_packet) +
-                   sizeof(struct iwm_mcc_update_resp_v1);
        }
 
        err = iwm_send_cmd(sc, &hcmd);
        if (err)
                return err;
 
-       iwm_free_resp(sc, &hcmd);
+       pkt = hcmd.resp_pkt;
+       if (!pkt || (pkt->hdr.flags & IWM_CMD_FAILED_MSK)) {
+               err = EIO;
+               goto out;
+       }
 
-       return 0;
+       if (resp_v3) {
+               struct iwm_mcc_update_resp_v3 *resp;
+               resp_len = iwm_rx_packet_payload_len(pkt);
+               if (resp_len < sizeof(*resp)) {
+                       err = EIO;
+                       goto out;
+               }
+
+               resp = (void *)pkt->data;
+               if (resp_len != sizeof(*resp) +
+                   resp->n_channels * sizeof(resp->channels[0])) {
+                       err = EIO;
+                       goto out;
+               }
+       } else {
+               struct iwm_mcc_update_resp_v1 *resp_v1;
+               resp_len = iwm_rx_packet_payload_len(pkt);
+               if (resp_len < sizeof(*resp_v1)) {
+                       err = EIO;
+                       goto out;
+               }
+
+               resp_v1 = (void *)pkt->data;
+               if (resp_len != sizeof(*resp_v1) +
+                   resp_v1->n_channels * sizeof(resp_v1->channels[0])) {
+                       err = EIO;
+                       goto out;
+               }
+       }
+out:
+       iwm_free_resp(sc, &hcmd);
+       return err;
 }
 
 int
@@ -10334,10 +10386,8 @@ iwm_rx_pkt(struct iwm_softc *sc, struct iwm_rx_data *data, struct mbuf_list *ml)
                case IWM_MCC_CHUB_UPDATE_CMD: {
                        struct iwm_mcc_chub_notif *notif;
                        SYNC_RESP_STRUCT(notif, pkt);
-
-                       sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8;
-                       sc->sc_fw_mcc[1] = notif->mcc & 0xff;
-                       sc->sc_fw_mcc[2] = '\0';
+                       iwm_mcc_update(sc, notif);
+                       break;
                }
 
                case IWM_DTS_MEASUREMENT_NOTIFICATION:
index 2a5eb3d..e3e9319 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwmreg.h,v 1.62 2021/07/08 00:12:49 stsp Exp $     */
+/*     $OpenBSD: if_iwmreg.h,v 1.63 2021/07/08 17:14:08 stsp Exp $     */
 
 /******************************************************************************
  *
@@ -927,7 +927,7 @@ enum msix_ivar_for_cause {
 #define IWM_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT      68
 #define IWM_UCODE_TLV_CAPA_BEACON_ANT_SELECTION                71
 #define IWM_UCODE_TLV_CAPA_BEACON_STORING              72
-#define IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2              73
+#define IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V3              73
 #define IWM_UCODE_TLV_CAPA_CT_KILL_BY_FW               74
 #define IWM_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT     75
 #define IWM_UCODE_TLV_CAPA_CTDP_SUPPORT                        76
@@ -6566,7 +6566,7 @@ struct iwm_mcc_update_resp_v1  {
 } __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */
 
 /**
- * iwm_mcc_update_resp - response to MCC_UPDATE_CMD.
+ * iwm_mcc_update_resp_v2 - response to MCC_UPDATE_CMD.
  * Contains the new channel control profile map, if changed, and the new MCC
  * (mobile country code).
  * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
@@ -6581,7 +6581,7 @@ struct iwm_mcc_update_resp_v1  {
  * @channels: channel control data map, DWORD for each channel. Only the first
  *     16bits are used.
  */
-struct iwm_mcc_update_resp {
+struct iwm_mcc_update_resp_v2 {
        uint32_t status;
        uint16_t mcc;
        uint8_t cap;
@@ -6592,6 +6592,36 @@ struct iwm_mcc_update_resp {
        uint32_t channels[0];
 } __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */
 
+#define IWM_GEO_NO_INFO                        0
+#define IWM_GEO_WMM_ETSI_5GHZ_INFO     (1 << 0)
+
+/**
+ * iwm_mcc_update_resp_v3 - response to MCC_UPDATE_CMD.
+ * Contains the new channel control profile map, if changed, and the new MCC
+ * (mobile country code).
+ * The new MCC may be different than what was requested in MCC_UPDATE_CMD.
+ * @status: see &enum iwm_mcc_update_status
+ * @mcc: the new applied MCC
+ * @cap: capabilities for all channels which matches the MCC
+ * @source_id: the MCC source, see IWM_MCC_SOURCE_*
+ * @time: time elapsed from the MCC test start (in 30 seconds TU)
+ * @geo_info: geographic specific profile information
+ * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51
+ *             channels, depending on platform)
+ * @channels: channel control data map, DWORD for each channel. Only the first
+ *     16bits are used.
+ */
+struct iwm_mcc_update_resp_v3 {
+       uint32_t status;
+       uint16_t mcc;
+       uint8_t cap;
+       uint8_t source_id;
+       uint16_t time;
+       uint16_t geo_info;
+       uint32_t n_channels;
+       uint32_t channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_3 */
+
 /**
  * struct iwm_mcc_chub_notif - chub notifies of mcc change
  * (MCC_CHUB_UPDATE_CMD = 0xc9)
index 7d92177..bcda8e9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwmvar.h,v 1.68 2021/07/08 00:12:49 stsp Exp $     */
+/*     $OpenBSD: if_iwmvar.h,v 1.69 2021/07/08 17:14:08 stsp Exp $     */
 
 /*
  * Copyright (c) 2014 genua mbh <info@genua.de>
@@ -548,7 +548,6 @@ struct iwm_softc {
 #define IWM_MAX_FW_CMD_VERSIONS        64
        struct iwm_fw_cmd_version cmd_versions[IWM_MAX_FW_CMD_VERSIONS];
        int n_cmd_versions;
-       char sc_fw_mcc[3];
 
        int sc_intmask;
        int sc_flags;