|
@@ -232,6 +232,9 @@ struct mwl8k_priv {
|
|
struct completion firmware_loading_complete;
|
|
struct completion firmware_loading_complete;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+#define MAX_WEP_KEY_LEN 13
|
|
|
|
+#define NUM_WEP_KEYS 4
|
|
|
|
+
|
|
/* Per interface specific private data */
|
|
/* Per interface specific private data */
|
|
struct mwl8k_vif {
|
|
struct mwl8k_vif {
|
|
struct list_head list;
|
|
struct list_head list;
|
|
@@ -242,8 +245,21 @@ struct mwl8k_vif {
|
|
|
|
|
|
/* Non AMPDU sequence number assigned by driver. */
|
|
/* Non AMPDU sequence number assigned by driver. */
|
|
u16 seqno;
|
|
u16 seqno;
|
|
|
|
+
|
|
|
|
+ /* Saved WEP keys */
|
|
|
|
+ struct {
|
|
|
|
+ u8 enabled;
|
|
|
|
+ u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN];
|
|
|
|
+ } wep_key_conf[NUM_WEP_KEYS];
|
|
|
|
+
|
|
|
|
+ /* BSSID */
|
|
|
|
+ u8 bssid[ETH_ALEN];
|
|
|
|
+
|
|
|
|
+ /* A flag to indicate is HW crypto is enabled for this bssid */
|
|
|
|
+ bool is_hw_crypto_enabled;
|
|
};
|
|
};
|
|
#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
|
|
#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv))
|
|
|
|
+#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8))
|
|
|
|
|
|
struct mwl8k_sta {
|
|
struct mwl8k_sta {
|
|
/* Index into station database. Returned by UPDATE_STADB. */
|
|
/* Index into station database. Returned by UPDATE_STADB. */
|
|
@@ -337,6 +353,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {
|
|
#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
|
|
#define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203
|
|
#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */
|
|
#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */
|
|
#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
|
|
#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */
|
|
|
|
+#define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */
|
|
#define MWL8K_CMD_UPDATE_STADB 0x1123
|
|
#define MWL8K_CMD_UPDATE_STADB 0x1123
|
|
|
|
|
|
static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
|
|
static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
|
|
@@ -375,6 +392,7 @@ static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)
|
|
MWL8K_CMDNAME(SET_RATEADAPT_MODE);
|
|
MWL8K_CMDNAME(SET_RATEADAPT_MODE);
|
|
MWL8K_CMDNAME(BSS_START);
|
|
MWL8K_CMDNAME(BSS_START);
|
|
MWL8K_CMDNAME(SET_NEW_STN);
|
|
MWL8K_CMDNAME(SET_NEW_STN);
|
|
|
|
+ MWL8K_CMDNAME(UPDATE_ENCRYPTION);
|
|
MWL8K_CMDNAME(UPDATE_STADB);
|
|
MWL8K_CMDNAME(UPDATE_STADB);
|
|
default:
|
|
default:
|
|
snprintf(buf, bufsize, "0x%x", cmd);
|
|
snprintf(buf, bufsize, "0x%x", cmd);
|
|
@@ -715,10 +733,12 @@ static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)
|
|
skb_pull(skb, sizeof(*tr) - hdrlen);
|
|
skb_pull(skb, sizeof(*tr) - hdrlen);
|
|
}
|
|
}
|
|
|
|
|
|
-static inline void mwl8k_add_dma_header(struct sk_buff *skb)
|
|
|
|
|
|
+static void
|
|
|
|
+mwl8k_add_dma_header(struct sk_buff *skb, int tail_pad)
|
|
{
|
|
{
|
|
struct ieee80211_hdr *wh;
|
|
struct ieee80211_hdr *wh;
|
|
int hdrlen;
|
|
int hdrlen;
|
|
|
|
+ int reqd_hdrlen;
|
|
struct mwl8k_dma_data *tr;
|
|
struct mwl8k_dma_data *tr;
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -730,11 +750,13 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)
|
|
wh = (struct ieee80211_hdr *)skb->data;
|
|
wh = (struct ieee80211_hdr *)skb->data;
|
|
|
|
|
|
hdrlen = ieee80211_hdrlen(wh->frame_control);
|
|
hdrlen = ieee80211_hdrlen(wh->frame_control);
|
|
- if (hdrlen != sizeof(*tr))
|
|
|
|
- skb_push(skb, sizeof(*tr) - hdrlen);
|
|
|
|
|
|
+ reqd_hdrlen = sizeof(*tr);
|
|
|
|
+
|
|
|
|
+ if (hdrlen != reqd_hdrlen)
|
|
|
|
+ skb_push(skb, reqd_hdrlen - hdrlen);
|
|
|
|
|
|
if (ieee80211_is_data_qos(wh->frame_control))
|
|
if (ieee80211_is_data_qos(wh->frame_control))
|
|
- hdrlen -= 2;
|
|
|
|
|
|
+ hdrlen -= IEEE80211_QOS_CTL_LEN;
|
|
|
|
|
|
tr = (struct mwl8k_dma_data *)skb->data;
|
|
tr = (struct mwl8k_dma_data *)skb->data;
|
|
if (wh != &tr->wh)
|
|
if (wh != &tr->wh)
|
|
@@ -747,9 +769,52 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)
|
|
* payload". That is, everything except for the 802.11 header.
|
|
* payload". That is, everything except for the 802.11 header.
|
|
* This includes all crypto material including the MIC.
|
|
* This includes all crypto material including the MIC.
|
|
*/
|
|
*/
|
|
- tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr));
|
|
|
|
|
|
+ tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void mwl8k_encapsulate_tx_frame(struct sk_buff *skb)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_hdr *wh;
|
|
|
|
+ struct ieee80211_tx_info *tx_info;
|
|
|
|
+ struct ieee80211_key_conf *key_conf;
|
|
|
|
+ int data_pad;
|
|
|
|
+
|
|
|
|
+ wh = (struct ieee80211_hdr *)skb->data;
|
|
|
|
+
|
|
|
|
+ tx_info = IEEE80211_SKB_CB(skb);
|
|
|
|
+
|
|
|
|
+ key_conf = NULL;
|
|
|
|
+ if (ieee80211_is_data(wh->frame_control))
|
|
|
|
+ key_conf = tx_info->control.hw_key;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Make sure the packet header is in the DMA header format (4-address
|
|
|
|
+ * without QoS), the necessary crypto padding between the header and the
|
|
|
|
+ * payload has already been provided by mac80211, but it doesn't add tail
|
|
|
|
+ * padding when HW crypto is enabled.
|
|
|
|
+ *
|
|
|
|
+ * We have the following trailer padding requirements:
|
|
|
|
+ * - WEP: 4 trailer bytes (ICV)
|
|
|
|
+ * - TKIP: 12 trailer bytes (8 MIC + 4 ICV)
|
|
|
|
+ * - CCMP: 8 trailer bytes (MIC)
|
|
|
|
+ */
|
|
|
|
+ data_pad = 0;
|
|
|
|
+ if (key_conf != NULL) {
|
|
|
|
+ switch (key_conf->cipher) {
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP40:
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP104:
|
|
|
|
+ data_pad = 4;
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
|
|
+ data_pad = 12;
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
|
|
+ data_pad = 8;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mwl8k_add_dma_header(skb, data_pad);
|
|
|
|
+}
|
|
|
|
|
|
/*
|
|
/*
|
|
* Packet reception for 88w8366 AP firmware.
|
|
* Packet reception for 88w8366 AP firmware.
|
|
@@ -778,6 +843,13 @@ struct mwl8k_rxd_8366_ap {
|
|
|
|
|
|
#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80
|
|
#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST 0x80
|
|
|
|
|
|
|
|
+/* 8366 AP rx_status bits */
|
|
|
|
+#define MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK 0x80
|
|
|
|
+#define MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR 0xFF
|
|
|
|
+#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR 0x02
|
|
|
|
+#define MWL8K_8366_AP_RXSTAT_WEP_DECRYPT_ICV_ERR 0x04
|
|
|
|
+#define MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR 0x08
|
|
|
|
+
|
|
static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
|
|
static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr)
|
|
{
|
|
{
|
|
struct mwl8k_rxd_8366_ap *rxd = _rxd;
|
|
struct mwl8k_rxd_8366_ap *rxd = _rxd;
|
|
@@ -834,10 +906,16 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,
|
|
} else {
|
|
} else {
|
|
status->band = IEEE80211_BAND_2GHZ;
|
|
status->band = IEEE80211_BAND_2GHZ;
|
|
}
|
|
}
|
|
- status->freq = ieee80211_channel_to_frequency(rxd->channel);
|
|
|
|
|
|
+ status->freq = ieee80211_channel_to_frequency(rxd->channel,
|
|
|
|
+ status->band);
|
|
|
|
|
|
*qos = rxd->qos_control;
|
|
*qos = rxd->qos_control;
|
|
|
|
|
|
|
|
+ if ((rxd->rx_status != MWL8K_8366_AP_RXSTAT_GENERAL_DECRYPT_ERR) &&
|
|
|
|
+ (rxd->rx_status & MWL8K_8366_AP_RXSTAT_DECRYPT_ERR_MASK) &&
|
|
|
|
+ (rxd->rx_status & MWL8K_8366_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR))
|
|
|
|
+ status->flag |= RX_FLAG_MMIC_ERROR;
|
|
|
|
+
|
|
return le16_to_cpu(rxd->pkt_len);
|
|
return le16_to_cpu(rxd->pkt_len);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -876,6 +954,11 @@ struct mwl8k_rxd_sta {
|
|
#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001
|
|
#define MWL8K_STA_RATE_INFO_MCS_FORMAT 0x0001
|
|
|
|
|
|
#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02
|
|
#define MWL8K_STA_RX_CTRL_OWNED_BY_HOST 0x02
|
|
|
|
+#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR 0x04
|
|
|
|
+/* ICV=0 or MIC=1 */
|
|
|
|
+#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE 0x08
|
|
|
|
+/* Key is uploaded only in failure case */
|
|
|
|
+#define MWL8K_STA_RX_CTRL_KEY_INDEX 0x30
|
|
|
|
|
|
static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
|
|
static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)
|
|
{
|
|
{
|
|
@@ -931,9 +1014,13 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,
|
|
} else {
|
|
} else {
|
|
status->band = IEEE80211_BAND_2GHZ;
|
|
status->band = IEEE80211_BAND_2GHZ;
|
|
}
|
|
}
|
|
- status->freq = ieee80211_channel_to_frequency(rxd->channel);
|
|
|
|
|
|
+ status->freq = ieee80211_channel_to_frequency(rxd->channel,
|
|
|
|
+ status->band);
|
|
|
|
|
|
*qos = rxd->qos_control;
|
|
*qos = rxd->qos_control;
|
|
|
|
+ if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) &&
|
|
|
|
+ (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE))
|
|
|
|
+ status->flag |= RX_FLAG_MMIC_ERROR;
|
|
|
|
|
|
return le16_to_cpu(rxd->pkt_len);
|
|
return le16_to_cpu(rxd->pkt_len);
|
|
}
|
|
}
|
|
@@ -1092,9 +1179,25 @@ static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
|
|
ieee80211_queue_work(hw, &priv->finalize_join_worker);
|
|
ieee80211_queue_work(hw, &priv->finalize_join_worker);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list,
|
|
|
|
+ u8 *bssid)
|
|
|
|
+{
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif;
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(mwl8k_vif,
|
|
|
|
+ vif_list, list) {
|
|
|
|
+ if (memcmp(bssid, mwl8k_vif->bssid,
|
|
|
|
+ ETH_ALEN) == 0)
|
|
|
|
+ return mwl8k_vif;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
|
|
static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
|
|
{
|
|
{
|
|
struct mwl8k_priv *priv = hw->priv;
|
|
struct mwl8k_priv *priv = hw->priv;
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif = NULL;
|
|
struct mwl8k_rx_queue *rxq = priv->rxq + index;
|
|
struct mwl8k_rx_queue *rxq = priv->rxq + index;
|
|
int processed;
|
|
int processed;
|
|
|
|
|
|
@@ -1104,6 +1207,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
|
|
void *rxd;
|
|
void *rxd;
|
|
int pkt_len;
|
|
int pkt_len;
|
|
struct ieee80211_rx_status status;
|
|
struct ieee80211_rx_status status;
|
|
|
|
+ struct ieee80211_hdr *wh;
|
|
__le16 qos;
|
|
__le16 qos;
|
|
|
|
|
|
skb = rxq->buf[rxq->head].skb;
|
|
skb = rxq->buf[rxq->head].skb;
|
|
@@ -1130,8 +1234,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
|
|
|
|
|
|
rxq->rxd_count--;
|
|
rxq->rxd_count--;
|
|
|
|
|
|
- skb_put(skb, pkt_len);
|
|
|
|
- mwl8k_remove_dma_header(skb, qos);
|
|
|
|
|
|
+ wh = &((struct mwl8k_dma_data *)skb->data)->wh;
|
|
|
|
|
|
/*
|
|
/*
|
|
* Check for a pending join operation. Save a
|
|
* Check for a pending join operation. Save a
|
|
@@ -1141,6 +1244,46 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)
|
|
if (mwl8k_capture_bssid(priv, (void *)skb->data))
|
|
if (mwl8k_capture_bssid(priv, (void *)skb->data))
|
|
mwl8k_save_beacon(hw, skb);
|
|
mwl8k_save_beacon(hw, skb);
|
|
|
|
|
|
|
|
+ if (ieee80211_has_protected(wh->frame_control)) {
|
|
|
|
+
|
|
|
|
+ /* Check if hw crypto has been enabled for
|
|
|
|
+ * this bss. If yes, set the status flags
|
|
|
|
+ * accordingly
|
|
|
|
+ */
|
|
|
|
+ mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list,
|
|
|
|
+ wh->addr1);
|
|
|
|
+
|
|
|
|
+ if (mwl8k_vif != NULL &&
|
|
|
|
+ mwl8k_vif->is_hw_crypto_enabled == true) {
|
|
|
|
+ /*
|
|
|
|
+ * When MMIC ERROR is encountered
|
|
|
|
+ * by the firmware, payload is
|
|
|
|
+ * dropped and only 32 bytes of
|
|
|
|
+ * mwl8k Firmware header is sent
|
|
|
|
+ * to the host.
|
|
|
|
+ *
|
|
|
|
+ * We need to add four bytes of
|
|
|
|
+ * key information. In it
|
|
|
|
+ * MAC80211 expects keyidx set to
|
|
|
|
+ * 0 for triggering Counter
|
|
|
|
+ * Measure of MMIC failure.
|
|
|
|
+ */
|
|
|
|
+ if (status.flag & RX_FLAG_MMIC_ERROR) {
|
|
|
|
+ struct mwl8k_dma_data *tr;
|
|
|
|
+ tr = (struct mwl8k_dma_data *)skb->data;
|
|
|
|
+ memset((void *)&(tr->data), 0, 4);
|
|
|
|
+ pkt_len += 4;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!ieee80211_is_auth(wh->frame_control))
|
|
|
|
+ status.flag |= RX_FLAG_IV_STRIPPED |
|
|
|
|
+ RX_FLAG_DECRYPTED |
|
|
|
|
+ RX_FLAG_MMIC_STRIPPED;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ skb_put(skb, pkt_len);
|
|
|
|
+ mwl8k_remove_dma_header(skb, qos);
|
|
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
|
|
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
|
|
ieee80211_rx_irqsafe(hw, skb);
|
|
ieee80211_rx_irqsafe(hw, skb);
|
|
|
|
|
|
@@ -1443,7 +1586,11 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)
|
|
else
|
|
else
|
|
qos = 0;
|
|
qos = 0;
|
|
|
|
|
|
- mwl8k_add_dma_header(skb);
|
|
|
|
|
|
+ if (priv->ap_fw)
|
|
|
|
+ mwl8k_encapsulate_tx_frame(skb);
|
|
|
|
+ else
|
|
|
|
+ mwl8k_add_dma_header(skb, 0);
|
|
|
|
+
|
|
wh = &((struct mwl8k_dma_data *)skb->data)->wh;
|
|
wh = &((struct mwl8k_dma_data *)skb->data)->wh;
|
|
|
|
|
|
tx_info = IEEE80211_SKB_CB(skb);
|
|
tx_info = IEEE80211_SKB_CB(skb);
|
|
@@ -3098,6 +3245,274 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,
|
|
return rc;
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * CMD_UPDATE_ENCRYPTION.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#define MAX_ENCR_KEY_LENGTH 16
|
|
|
|
+#define MIC_KEY_LENGTH 8
|
|
|
|
+
|
|
|
|
+struct mwl8k_cmd_update_encryption {
|
|
|
|
+ struct mwl8k_cmd_pkt header;
|
|
|
|
+
|
|
|
|
+ __le32 action;
|
|
|
|
+ __le32 reserved;
|
|
|
|
+ __u8 mac_addr[6];
|
|
|
|
+ __u8 encr_type;
|
|
|
|
+
|
|
|
|
+} __attribute__((packed));
|
|
|
|
+
|
|
|
|
+struct mwl8k_cmd_set_key {
|
|
|
|
+ struct mwl8k_cmd_pkt header;
|
|
|
|
+
|
|
|
|
+ __le32 action;
|
|
|
|
+ __le32 reserved;
|
|
|
|
+ __le16 length;
|
|
|
|
+ __le16 key_type_id;
|
|
|
|
+ __le32 key_info;
|
|
|
|
+ __le32 key_id;
|
|
|
|
+ __le16 key_len;
|
|
|
|
+ __u8 key_material[MAX_ENCR_KEY_LENGTH];
|
|
|
|
+ __u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
|
|
|
|
+ __u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
|
|
|
|
+ __le16 tkip_rsc_low;
|
|
|
|
+ __le32 tkip_rsc_high;
|
|
|
|
+ __le16 tkip_tsc_low;
|
|
|
|
+ __le32 tkip_tsc_high;
|
|
|
|
+ __u8 mac_addr[6];
|
|
|
|
+} __attribute__((packed));
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ MWL8K_ENCR_ENABLE,
|
|
|
|
+ MWL8K_ENCR_SET_KEY,
|
|
|
|
+ MWL8K_ENCR_REMOVE_KEY,
|
|
|
|
+ MWL8K_ENCR_SET_GROUP_KEY,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0
|
|
|
|
+#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1
|
|
|
|
+#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4
|
|
|
|
+#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7
|
|
|
|
+#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8
|
|
|
|
+
|
|
|
|
+enum {
|
|
|
|
+ MWL8K_ALG_WEP,
|
|
|
|
+ MWL8K_ALG_TKIP,
|
|
|
|
+ MWL8K_ALG_CCMP,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004
|
|
|
|
+#define MWL8K_KEY_FLAG_PAIRWISE 0x00000008
|
|
|
|
+#define MWL8K_KEY_FLAG_TSC_VALID 0x00000040
|
|
|
|
+#define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000
|
|
|
|
+#define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000
|
|
|
|
+
|
|
|
|
+static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ u8 *addr,
|
|
|
|
+ u8 encr_type)
|
|
|
|
+{
|
|
|
|
+ struct mwl8k_cmd_update_encryption *cmd;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
|
+ if (cmd == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
|
|
|
|
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
|
|
|
|
+ cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE);
|
|
|
|
+ memcpy(cmd->mac_addr, addr, ETH_ALEN);
|
|
|
|
+ cmd->encr_type = encr_type;
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
|
|
|
|
+ kfree(cmd);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd,
|
|
|
|
+ u8 *addr,
|
|
|
|
+ struct ieee80211_key_conf *key)
|
|
|
|
+{
|
|
|
|
+ cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION);
|
|
|
|
+ cmd->header.length = cpu_to_le16(sizeof(*cmd));
|
|
|
|
+ cmd->length = cpu_to_le16(sizeof(*cmd) -
|
|
|
|
+ offsetof(struct mwl8k_cmd_set_key, length));
|
|
|
|
+ cmd->key_id = cpu_to_le32(key->keyidx);
|
|
|
|
+ cmd->key_len = cpu_to_le16(key->keylen);
|
|
|
|
+ memcpy(cmd->mac_addr, addr, ETH_ALEN);
|
|
|
|
+
|
|
|
|
+ switch (key->cipher) {
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP40:
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP104:
|
|
|
|
+ cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP);
|
|
|
|
+ if (key->keyidx == 0)
|
|
|
|
+ cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
|
|
+ cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP);
|
|
|
|
+ cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
|
|
|
+ ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
|
|
|
|
+ : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
|
|
|
|
+ cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID
|
|
|
|
+ | MWL8K_KEY_FLAG_TSC_VALID);
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
|
|
+ cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP);
|
|
|
|
+ cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
|
|
|
+ ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE)
|
|
|
|
+ : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ return -ENOTSUPP;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ u8 *addr,
|
|
|
|
+ struct ieee80211_key_conf *key)
|
|
|
|
+{
|
|
|
|
+ struct mwl8k_cmd_set_key *cmd;
|
|
|
|
+ int rc;
|
|
|
|
+ int keymlen;
|
|
|
|
+ u32 action;
|
|
|
|
+ u8 idx;
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
|
|
|
|
+
|
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
|
+ if (cmd == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
|
|
|
|
+ if (rc < 0)
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ idx = key->keyidx;
|
|
|
|
+
|
|
|
|
+ if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
|
|
|
+ action = MWL8K_ENCR_SET_KEY;
|
|
|
|
+ else
|
|
|
|
+ action = MWL8K_ENCR_SET_GROUP_KEY;
|
|
|
|
+
|
|
|
|
+ switch (key->cipher) {
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP40:
|
|
|
|
+ case WLAN_CIPHER_SUITE_WEP104:
|
|
|
|
+ if (!mwl8k_vif->wep_key_conf[idx].enabled) {
|
|
|
|
+ memcpy(mwl8k_vif->wep_key_conf[idx].key, key,
|
|
|
|
+ sizeof(*key) + key->keylen);
|
|
|
|
+ mwl8k_vif->wep_key_conf[idx].enabled = 1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ keymlen = 0;
|
|
|
|
+ action = MWL8K_ENCR_SET_KEY;
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
|
|
+ keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH;
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
|
|
+ keymlen = key->keylen;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ rc = -ENOTSUPP;
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(cmd->key_material, key->key, keymlen);
|
|
|
|
+ cmd->action = cpu_to_le32(action);
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
|
|
|
|
+done:
|
|
|
|
+ kfree(cmd);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ u8 *addr,
|
|
|
|
+ struct ieee80211_key_conf *key)
|
|
|
|
+{
|
|
|
|
+ struct mwl8k_cmd_set_key *cmd;
|
|
|
|
+ int rc;
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
|
|
|
|
+
|
|
|
|
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
|
|
|
|
+ if (cmd == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_encryption_set_cmd_info(cmd, addr, key);
|
|
|
|
+ if (rc < 0)
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
+ WLAN_CIPHER_SUITE_WEP104)
|
|
|
|
+ mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0;
|
|
|
|
+
|
|
|
|
+ cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY);
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
|
|
|
|
+done:
|
|
|
|
+ kfree(cmd);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int mwl8k_set_key(struct ieee80211_hw *hw,
|
|
|
|
+ enum set_key_cmd cmd_param,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ struct ieee80211_sta *sta,
|
|
|
|
+ struct ieee80211_key_conf *key)
|
|
|
|
+{
|
|
|
|
+ int rc = 0;
|
|
|
|
+ u8 encr_type;
|
|
|
|
+ u8 *addr;
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
|
|
|
|
+
|
|
|
|
+ if (vif->type == NL80211_IFTYPE_STATION)
|
|
|
|
+ return -EOPNOTSUPP;
|
|
|
|
+
|
|
|
|
+ if (sta == NULL)
|
|
|
|
+ addr = hw->wiphy->perm_addr;
|
|
|
|
+ else
|
|
|
|
+ addr = sta->addr;
|
|
|
|
+
|
|
|
|
+ if (cmd_param == SET_KEY) {
|
|
|
|
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
|
|
|
|
+ rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40)
|
|
|
|
+ || (key->cipher == WLAN_CIPHER_SUITE_WEP104))
|
|
|
|
+ encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP;
|
|
|
|
+ else
|
|
|
|
+ encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED;
|
|
|
|
+
|
|
|
|
+ rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr,
|
|
|
|
+ encr_type);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ mwl8k_vif->is_hw_crypto_enabled = true;
|
|
|
|
+
|
|
|
|
+ } else {
|
|
|
|
+ rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key);
|
|
|
|
+
|
|
|
|
+ if (rc)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ mwl8k_vif->is_hw_crypto_enabled = false;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+out:
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* CMD_UPDATE_STADB.
|
|
* CMD_UPDATE_STADB.
|
|
*/
|
|
*/
|
|
@@ -3469,6 +3884,8 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,
|
|
mwl8k_vif->vif = vif;
|
|
mwl8k_vif->vif = vif;
|
|
mwl8k_vif->macid = macid;
|
|
mwl8k_vif->macid = macid;
|
|
mwl8k_vif->seqno = 0;
|
|
mwl8k_vif->seqno = 0;
|
|
|
|
+ memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN);
|
|
|
|
+ mwl8k_vif->is_hw_crypto_enabled = false;
|
|
|
|
|
|
/* Set the mac address. */
|
|
/* Set the mac address. */
|
|
mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
|
|
mwl8k_cmd_set_mac_addr(hw, vif, vif->addr);
|
|
@@ -3866,18 +4283,27 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw,
|
|
{
|
|
{
|
|
struct mwl8k_priv *priv = hw->priv;
|
|
struct mwl8k_priv *priv = hw->priv;
|
|
int ret;
|
|
int ret;
|
|
|
|
+ int i;
|
|
|
|
+ struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif);
|
|
|
|
+ struct ieee80211_key_conf *key;
|
|
|
|
|
|
if (!priv->ap_fw) {
|
|
if (!priv->ap_fw) {
|
|
ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
|
|
ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);
|
|
if (ret >= 0) {
|
|
if (ret >= 0) {
|
|
MWL8K_STA(sta)->peer_id = ret;
|
|
MWL8K_STA(sta)->peer_id = ret;
|
|
- return 0;
|
|
|
|
|
|
+ ret = 0;
|
|
}
|
|
}
|
|
|
|
|
|
- return ret;
|
|
|
|
|
|
+ } else {
|
|
|
|
+ ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);
|
|
}
|
|
}
|
|
|
|
|
|
- return mwl8k_cmd_set_new_stn_add(hw, vif, sta);
|
|
|
|
|
|
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
|
|
|
|
+ key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key);
|
|
|
|
+ if (mwl8k_vif->wep_key_conf[i].enabled)
|
|
|
|
+ mwl8k_set_key(hw, SET_KEY, vif, sta, key);
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
|
|
static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
|
static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
|
@@ -3932,7 +4358,8 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
|
|
static int
|
|
static int
|
|
mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
enum ieee80211_ampdu_mlme_action action,
|
|
enum ieee80211_ampdu_mlme_action action,
|
|
- struct ieee80211_sta *sta, u16 tid, u16 *ssn)
|
|
|
|
|
|
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
|
|
|
|
+ u8 buf_size)
|
|
{
|
|
{
|
|
switch (action) {
|
|
switch (action) {
|
|
case IEEE80211_AMPDU_RX_START:
|
|
case IEEE80211_AMPDU_RX_START:
|
|
@@ -3955,6 +4382,7 @@ static const struct ieee80211_ops mwl8k_ops = {
|
|
.bss_info_changed = mwl8k_bss_info_changed,
|
|
.bss_info_changed = mwl8k_bss_info_changed,
|
|
.prepare_multicast = mwl8k_prepare_multicast,
|
|
.prepare_multicast = mwl8k_prepare_multicast,
|
|
.configure_filter = mwl8k_configure_filter,
|
|
.configure_filter = mwl8k_configure_filter,
|
|
|
|
+ .set_key = mwl8k_set_key,
|
|
.set_rts_threshold = mwl8k_set_rts_threshold,
|
|
.set_rts_threshold = mwl8k_set_rts_threshold,
|
|
.sta_add = mwl8k_sta_add,
|
|
.sta_add = mwl8k_sta_add,
|
|
.sta_remove = mwl8k_sta_remove,
|
|
.sta_remove = mwl8k_sta_remove,
|