iwlwifi: mvm: add a generic cipher scheme support
authorMax Stepanov <Max.Stepanov@intel.com>
Tue, 27 Aug 2013 16:56:13 +0000 (19:56 +0300)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 17 Dec 2013 17:39:38 +0000 (19:39 +0200)
This patch adds a cipher scheme support to extend a set of
the supported ciphers. The driver reads a cipher scheme list TLV
from FW image and passes it to mac80211 on hw registration.
After the cipher schemes are registered the driver handles key
installation and Tx/Rx calls related to the new ciphers.

Signed-off-by: Max Stepanov <Max.Stepanov@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/tx.c

index ff570027e9dd9fa02f3e8360af6966b480c68069..4bebfb58fc7be083f72e0492b3b0aa4df7f34710 100644 (file)
@@ -322,6 +322,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces,
        pieces->img[type].sec[sec].offset = offset;
 }
 
+static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
+{
+       int i, j;
+       struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
+       struct iwl_fw_cipher_scheme *fwcs;
+       struct ieee80211_cipher_scheme *cs;
+       u32 cipher;
+
+       if (len < sizeof(*l) ||
+           len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
+               fwcs = &l->cs[j];
+               cipher = le32_to_cpu(fwcs->cipher);
+
+               /* we skip schemes with zero cipher suite selector */
+               if (!cipher)
+                       continue;
+
+               cs = &fw->cs[j++];
+               cs->cipher = cipher;
+               cs->iftype = BIT(NL80211_IFTYPE_STATION);
+               cs->hdr_len = fwcs->hdr_len;
+               cs->pn_len = fwcs->pn_len;
+               cs->pn_off = fwcs->pn_off;
+               cs->key_idx_off = fwcs->key_idx_off;
+               cs->key_idx_mask = fwcs->key_idx_mask;
+               cs->key_idx_shift = fwcs->key_idx_shift;
+               cs->mic_len = fwcs->mic_len;
+       }
+
+       return 0;
+}
+
 /*
  * Gets uCode section from tlv.
  */
@@ -729,6 +764,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                return -EINVAL;
                        }
                        break;
+               case IWL_UCODE_TLV_CSCHEME:
+                       if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
+                               goto invalid_tlv_len;
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
index 6c6c35c5228cabd71d1520f533af7bd3adf8ad26..4f95734b28118b93e3b144c58e1b41aa682b9890 100644 (file)
@@ -125,6 +125,7 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_SECURE_SEC_INIT   = 25,
        IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
        IWL_UCODE_TLV_NUM_OF_CPU        = 27,
+       IWL_UCODE_TLV_CSCHEME           = 28,
 };
 
 struct iwl_ucode_tlv {
index 75db087120c30fc8f2201f1f4de1be4d3e8e270c..b2a4c1d51c0e531161a959bdbf90d11a85a96ad4 100644 (file)
@@ -209,6 +209,44 @@ enum iwl_fw_phy_cfg {
        FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
 };
 
+#define IWL_UCODE_MAX_CS               1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+       __le32 cipher;
+       u8 flags;
+       u8 hdr_len;
+       u8 pn_len;
+       u8 pn_off;
+       u8 key_idx_off;
+       u8 key_idx_mask;
+       u8 key_idx_shift;
+       u8 mic_len;
+       u8 hw_cipher;
+} __packed;
+
+/**
+ * struct iwl_fw_cscheme_list - a cipher scheme list
+ * @size: a number of entries
+ * @cs: cipher scheme entries
+ */
+struct iwl_fw_cscheme_list {
+       u8 size;
+       struct iwl_fw_cipher_scheme cs[];
+} __packed;
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -224,6 +262,7 @@ enum iwl_fw_phy_cfg {
  * @inst_evtlog_size: event log size for runtime ucode.
  * @inst_errlog_ptr: error log offfset for runtime ucode.
  * @mvm_fw: indicates this is MVM firmware
+ * @cipher_scheme: optional external cipher scheme.
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -243,6 +282,8 @@ struct iwl_fw {
        u32 phy_config;
 
        bool mvm_fw;
+
+       struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
 };
 
 static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
index 4aca5933a65d1619fc3e2e419ced4ddddc3d44c1..8c73ba74b6fd1ec1f53cee998492951ccfa51783 100644 (file)
@@ -138,7 +138,14 @@ enum iwl_sta_flags {
 
 /**
  * enum iwl_sta_key_flag - key flags for the ADD_STA host command
- * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
+ * @STA_KEY_FLG_NO_ENC: no encryption
+ * @STA_KEY_FLG_WEP: WEP encryption algorithm
+ * @STA_KEY_FLG_CCM: CCMP encryption algorithm
+ * @STA_KEY_FLG_TKIP: TKIP encryption algorithm
+ * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
+ * @STA_KEY_FLG_CMAC: CMAC encryption algorithm
+ * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
  * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
  *     station info array (1 - n 1X mode)
  * @STA_KEY_FLG_KEYID_MSK: the index of the key
@@ -152,6 +159,7 @@ enum iwl_sta_key_flag {
        STA_KEY_FLG_WEP                 = (1 << 0),
        STA_KEY_FLG_CCM                 = (2 << 0),
        STA_KEY_FLG_TKIP                = (3 << 0),
+       STA_KEY_FLG_EXT                 = (4 << 0),
        STA_KEY_FLG_CMAC                = (6 << 0),
        STA_KEY_FLG_ENC_UNKNOWN         = (7 << 0),
        STA_KEY_FLG_EN_MSK              = (7 << 0),
index d606197bde8f41aeba9c7580b9c9f4d8dc8dd959..22864671d66c4115c415a1cf38460fc9d6ecffff 100644 (file)
@@ -132,6 +132,7 @@ enum iwl_tx_flags {
 #define TX_CMD_SEC_WEP                 0x01
 #define TX_CMD_SEC_CCM                 0x02
 #define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_EXT                 0x04
 #define TX_CMD_SEC_MSK                 0x07
 #define TX_CMD_SEC_WEP_KEY_IDX_POS     6
 #define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
index 3af413386ad971c659b942b556e528f9debc179d..c9cfaadc40a2812430241f6a037e1532349466ed 100644 (file)
@@ -1053,6 +1053,7 @@ enum iwl_mvm_rx_status {
        RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
        RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_EXT_ENC                  = (4 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
index f0f8b2c7ebef20ac070631b2c5e7cd9d2286f9b0..8a99ecc2c5887e6d38ce72db153e666b2f37283e 100644 (file)
@@ -261,6 +261,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
 
+       /* currently FW API supports only one optional cipher scheme */
+       if (mvm->fw->cs && mvm->fw->cs->cipher) {
+               mvm->hw->n_cipher_schemes = 1;
+               mvm->hw->cipher_schemes = mvm->fw->cs;
+       }
+
 #ifdef CONFIG_PM_SLEEP
        if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
@@ -1342,7 +1348,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                 */
                return 0;
        default:
-               return -EOPNOTSUPP;
+               /* currently FW supports only one optional cipher scheme */
+               if (hw->n_cipher_schemes &&
+                   hw->cipher_schemes->cipher == key->cipher)
+                       key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+               else
+                       return -EOPNOTSUPP;
        }
 
        mutex_lock(&mvm->mutex);
index 3a1f3982109d1dbc02bccba42588545e000b8271..454341cc3b274c0a2c27ab6354d566a9c10d0a3a 100644 (file)
@@ -251,6 +251,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                stats->flag |= RX_FLAG_DECRYPTED;
                return 0;
 
+       case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+                       return -1;
+               stats->flag |= RX_FLAG_DECRYPTED;
+               return 0;
+
        default:
                IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
        }
index 7a5b7473eafa615e946d3d4fe0802129149858bc..4ea00da54b05093466cb43d75e6682b95265f300 100644 (file)
@@ -1123,8 +1123,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        default:
-               WARN_ON(1);
-               return -EINVAL;
+               key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
        }
 
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
@@ -1288,8 +1288,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                                           0, NULL, CMD_SYNC);
                break;
        default:
-               IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
-               ret = -EINVAL;
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
+                                          sta_id, 0, NULL, CMD_SYNC);
        }
 
        if (ret)
index d87649ac88e1419fde870f4f7fdb4abc1d520fab..735f86da79856d6a97180641f21468117c39c5d5 100644 (file)
@@ -253,8 +253,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
                memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
                break;
        default:
-               IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
-               break;
+               tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
        }
 }