iwlwifi: mvm: add MCC update FW API
authorArik Nemtsov <arik@wizery.com>
Tue, 4 Mar 2014 17:54:12 +0000 (19:54 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Thu, 12 Mar 2015 07:57:21 +0000 (09:57 +0200)
The new API sets an MCC (mobile country code) to FW and receives a
channel structure to be used as a basis for an updated regulatory domain.

Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c

index 6842545535582246cf5369327c7748344ce98a97..9bb36d79c2bd3b6f32e7370db651533eb3bdad5c 100644 (file)
@@ -157,6 +157,7 @@ do {                                                                \
 /* 0x0000F000 - 0x00001000 */
 #define IWL_DL_ASSOC           0x00001000
 #define IWL_DL_DROP            0x00002000
+#define IWL_DL_LAR             0x00004000
 #define IWL_DL_COEX            0x00008000
 /* 0x000F0000 - 0x00010000 */
 #define IWL_DL_FW              0x00010000
@@ -219,5 +220,6 @@ do {                                                                \
 #define IWL_DEBUG_POWER(p, f, a...)    IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
 #define IWL_DEBUG_11H(p, f, a...)      IWL_DEBUG(p, IWL_DL_11H, f, ## a)
 #define IWL_DEBUG_RPM(p, f, a...)      IWL_DEBUG(p, IWL_DL_RPM, f, ## a)
+#define IWL_DEBUG_LAR(p, f, a...)      IWL_DEBUG(p, IWL_DL_LAR, f, ## a)
 
 #endif
index d95b472137318863e0cac5109d383b83182d5cbe..85afede5c7c463a62a2b5d88a2e8931f1f12ad0a 100644 (file)
@@ -212,6 +212,9 @@ enum {
        REPLY_RX_MPDU_CMD = 0xc1,
        BA_NOTIF = 0xc5,
 
+       /* Location Aware Regulatory */
+       MCC_UPDATE_CMD = 0xc8,
+
        MARKER_CMD = 0xcb,
 
        /* BT Coex */
@@ -1674,4 +1677,49 @@ struct iwl_shared_mem_cfg {
        __le32 page_buff_size;
 } __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
 
+/***********************************
+ * Location Aware Regulatory (LAR) API - MCC updates
+ ***********************************/
+
+/**
+ * struct iwl_mcc_update_cmd - Request the device to update geographic
+ * regulatory profile according to the given MCC (Mobile Country Code).
+ * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain.
+ * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the
+ * MCC in the cmd response will be the relevant MCC in the NVM.
+ * @mcc: given mobile country code
+ * @reserved: reserved for alignment
+ */
+struct iwl_mcc_update_cmd {
+       __le16 mcc;
+       __le16 reserved;
+} __packed; /* LAR_UPDATE_MCC_CMD_API_S */
+
+/**
+ * iwl_mcc_update_resp - 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: 0 for success, 1 no change in channel profile, 2 invalid input.
+ * @mcc: the new applied MCC
+ * @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 iwl_mcc_update_resp {
+       __le32 status;
+       __le16 mcc;
+       __le16 reserved;
+       __le32 n_channels;
+       __le32 channels[0];
+} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */
+
+enum iwl_mcc_update_status {
+       MCC_RESP_NEW_CHAN_PROFILE,
+       MCC_RESP_SAME_CHAN_PROFILE,
+       MCC_RESP_INVALID,
+       MCC_RESP_NVM_DISABLED,
+};
+
 #endif /* __fw_api_h__ */
index e10172d69eaa16be03e4a5b4bf7eeda15821dfbb..a0aa3b1dc7a574f2a0e8fcde0a07a1507c4f9770 100644 (file)
@@ -910,6 +910,11 @@ static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm)
               (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT);
 }
 
+static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
+{
+       return mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_LAR_SUPPORT;
+}
+
 static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
 {
        return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG;
@@ -1389,6 +1394,10 @@ void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 int iwl_mvm_get_temp(struct iwl_mvm *mvm);
 
+/* Location Aware Regulatory */
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2);
+
 /* smart fifo */
 int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                      bool added_vif);
index 5383429d96c1c49f91539c8440b6016cee9dd31c..96107b80e130c14d46fbb9017f54e88a627143fe 100644 (file)
@@ -570,3 +570,87 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 
        return 0;
 }
+
+struct iwl_mcc_update_resp *
+iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2)
+{
+       struct iwl_mcc_update_cmd mcc_update_cmd = {
+               .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
+       };
+       struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL;
+       struct iwl_rx_packet *pkt;
+       struct iwl_host_cmd cmd = {
+               .id = MCC_UPDATE_CMD,
+               .flags = CMD_WANT_SKB,
+               .data = { &mcc_update_cmd },
+       };
+
+       int ret;
+       u32 status;
+       int resp_len, n_channels;
+       u16 mcc;
+
+       if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
+               return ERR_PTR(-EOPNOTSUPP);
+
+       cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
+
+       IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c'\n",
+                     alpha2[0], alpha2[1]);
+
+       ret = iwl_mvm_send_cmd(mvm, &cmd);
+       if (ret)
+               return ERR_PTR(ret);
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n",
+                       pkt->hdr.flags);
+               ret = -EIO;
+               goto exit;
+       }
+
+       /* Extract MCC response */
+       mcc_resp = (void *)pkt->data;
+       status = le32_to_cpu(mcc_resp->status);
+
+       if (status == MCC_RESP_INVALID) {
+               IWL_ERR(mvm,
+                       "FW ERROR: MCC update with invalid parameter '%c%c'\n",
+                       alpha2[0], alpha2[1]);
+               ret = -EINVAL;
+               goto exit;
+       } else if (status == MCC_RESP_NVM_DISABLED) {
+               ret = 0;
+               /* resp_cp will be NULL */
+               goto exit;
+       }
+
+       mcc = le16_to_cpu(mcc_resp->mcc);
+
+       /* W/A for a FW/NVM issue - returns 0x00 for the world domain */
+       if (mcc == 0) {
+               mcc = 0x3030;  /* "00" - world */
+               mcc_resp->mcc = cpu_to_le16(mcc);
+       }
+
+       n_channels =  __le32_to_cpu(mcc_resp->n_channels);
+       IWL_DEBUG_LAR(mvm,
+                     "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n",
+                     status, mcc, mcc >> 8, mcc & 0xff,
+                     !!(status == MCC_RESP_SAME_CHAN_PROFILE), n_channels);
+
+       resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32);
+       resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
+       if (!resp_cp) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       ret = 0;
+exit:
+       iwl_free_resp(&cmd);
+       if (ret)
+               return ERR_PTR(ret);
+       return resp_cp;
+}
index fe40922a6b0d467c86da151e2c786a265e46a1e2..72b8e19004c40ddce4d43d46a18086bfefffb1fa 100644 (file)
@@ -358,6 +358,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(TDLS_CHANNEL_SWITCH_CMD),
        CMD(TDLS_CHANNEL_SWITCH_NOTIFICATION),
        CMD(TDLS_CONFIG_CMD),
+       CMD(MCC_UPDATE_CMD),
 };
 #undef CMD