|
@@ -296,6 +296,7 @@ static struct conf_drv_settings default_conf = {
|
|
|
};
|
|
|
|
|
|
static void __wl1271_op_remove_interface(struct wl1271 *wl);
|
|
|
+static void wl1271_free_ap_keys(struct wl1271 *wl);
|
|
|
|
|
|
|
|
|
static void wl1271_device_release(struct device *dev)
|
|
@@ -1193,6 +1194,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
|
|
|
wl->flags = 0;
|
|
|
wl->vif = NULL;
|
|
|
wl->filters = 0;
|
|
|
+ wl1271_free_ap_keys(wl);
|
|
|
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
|
|
|
|
|
|
for (i = 0; i < NUM_TX_QUEUES; i++)
|
|
@@ -1627,38 +1629,192 @@ out:
|
|
|
kfree(fp);
|
|
|
}
|
|
|
|
|
|
+static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
|
|
|
+ u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
|
|
|
+ u16 tx_seq_16)
|
|
|
+{
|
|
|
+ struct wl1271_ap_key *ap_key;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
|
|
|
+
|
|
|
+ if (key_size > MAX_KEY_SIZE)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Find next free entry in ap_keys. Also check we are not replacing
|
|
|
+ * an existing key.
|
|
|
+ */
|
|
|
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
|
|
|
+ if (wl->recorded_ap_keys[i] == NULL)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (wl->recorded_ap_keys[i]->id == id) {
|
|
|
+ wl1271_warning("trying to record key replacement");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == MAX_NUM_KEYS)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
|
|
|
+ if (!ap_key)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ ap_key->id = id;
|
|
|
+ ap_key->key_type = key_type;
|
|
|
+ ap_key->key_size = key_size;
|
|
|
+ memcpy(ap_key->key, key, key_size);
|
|
|
+ ap_key->hlid = hlid;
|
|
|
+ ap_key->tx_seq_32 = tx_seq_32;
|
|
|
+ ap_key->tx_seq_16 = tx_seq_16;
|
|
|
+
|
|
|
+ wl->recorded_ap_keys[i] = ap_key;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void wl1271_free_ap_keys(struct wl1271 *wl)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
|
|
|
+ kfree(wl->recorded_ap_keys[i]);
|
|
|
+ wl->recorded_ap_keys[i] = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int wl1271_ap_init_hwenc(struct wl1271 *wl)
|
|
|
+{
|
|
|
+ int i, ret = 0;
|
|
|
+ struct wl1271_ap_key *key;
|
|
|
+ bool wep_key_added = false;
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
|
|
|
+ if (wl->recorded_ap_keys[i] == NULL)
|
|
|
+ break;
|
|
|
+
|
|
|
+ key = wl->recorded_ap_keys[i];
|
|
|
+ ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
|
|
|
+ key->id, key->key_type,
|
|
|
+ key->key_size, key->key,
|
|
|
+ key->hlid, key->tx_seq_32,
|
|
|
+ key->tx_seq_16);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (key->key_type == KEY_WEP)
|
|
|
+ wep_key_added = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (wep_key_added) {
|
|
|
+ ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ wl1271_free_ap_keys(wl);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
|
|
|
+ u8 key_size, const u8 *key, u32 tx_seq_32,
|
|
|
+ u16 tx_seq_16, struct ieee80211_sta *sta)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
|
|
|
+
|
|
|
+ if (is_ap) {
|
|
|
+ struct wl1271_station *wl_sta;
|
|
|
+ u8 hlid;
|
|
|
+
|
|
|
+ if (sta) {
|
|
|
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
|
|
|
+ hlid = wl_sta->hlid;
|
|
|
+ } else {
|
|
|
+ hlid = WL1271_AP_BROADCAST_HLID;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
|
|
|
+ /*
|
|
|
+ * We do not support removing keys after AP shutdown.
|
|
|
+ * Pretend we do to make mac80211 happy.
|
|
|
+ */
|
|
|
+ if (action != KEY_ADD_OR_REPLACE)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = wl1271_record_ap_key(wl, id,
|
|
|
+ key_type, key_size,
|
|
|
+ key, hlid, tx_seq_32,
|
|
|
+ tx_seq_16);
|
|
|
+ } else {
|
|
|
+ ret = wl1271_cmd_set_ap_key(wl, action,
|
|
|
+ id, key_type, key_size,
|
|
|
+ key, hlid, tx_seq_32,
|
|
|
+ tx_seq_16);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ const u8 *addr;
|
|
|
+ static const u8 bcast_addr[ETH_ALEN] = {
|
|
|
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
|
|
+ };
|
|
|
+
|
|
|
+ addr = sta ? sta->addr : bcast_addr;
|
|
|
+
|
|
|
+ if (is_zero_ether_addr(addr)) {
|
|
|
+ /* We dont support TX only encryption */
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* The wl1271 does not allow to remove unicast keys - they
|
|
|
+ will be cleared automatically on next CMD_JOIN. Ignore the
|
|
|
+ request silently, as we dont want the mac80211 to emit
|
|
|
+ an error message. */
|
|
|
+ if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ ret = wl1271_cmd_set_sta_key(wl, action,
|
|
|
+ id, key_type, key_size,
|
|
|
+ key, addr, tx_seq_32,
|
|
|
+ tx_seq_16);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* the default WEP key needs to be configured at least once */
|
|
|
+ if (key_type == KEY_WEP) {
|
|
|
+ ret = wl1271_cmd_set_sta_default_wep_key(wl,
|
|
|
+ wl->default_key);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|
|
struct ieee80211_vif *vif,
|
|
|
struct ieee80211_sta *sta,
|
|
|
struct ieee80211_key_conf *key_conf)
|
|
|
{
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
- const u8 *addr;
|
|
|
int ret;
|
|
|
u32 tx_seq_32 = 0;
|
|
|
u16 tx_seq_16 = 0;
|
|
|
u8 key_type;
|
|
|
|
|
|
- static const u8 bcast_addr[ETH_ALEN] =
|
|
|
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
-
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");
|
|
|
|
|
|
- addr = sta ? sta->addr : bcast_addr;
|
|
|
-
|
|
|
- wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
|
|
|
- wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
|
|
|
+ wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
|
|
|
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
|
|
|
key_conf->cipher, key_conf->keyidx,
|
|
|
key_conf->keylen, key_conf->flags);
|
|
|
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
|
|
|
|
|
|
- if (is_zero_ether_addr(addr)) {
|
|
|
- /* We dont support TX only encryption */
|
|
|
- ret = -EOPNOTSUPP;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
@@ -1705,36 +1861,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|
|
|
|
|
switch (cmd) {
|
|
|
case SET_KEY:
|
|
|
- ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE,
|
|
|
- key_conf->keyidx, key_type,
|
|
|
- key_conf->keylen, key_conf->key,
|
|
|
- addr, tx_seq_32, tx_seq_16);
|
|
|
+ ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
|
|
|
+ key_conf->keyidx, key_type,
|
|
|
+ key_conf->keylen, key_conf->key,
|
|
|
+ tx_seq_32, tx_seq_16, sta);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("Could not add or replace key");
|
|
|
goto out_sleep;
|
|
|
}
|
|
|
-
|
|
|
- /* the default WEP key needs to be configured at least once */
|
|
|
- if (key_type == KEY_WEP) {
|
|
|
- ret = wl1271_cmd_set_sta_default_wep_key(wl,
|
|
|
- wl->default_key);
|
|
|
- if (ret < 0)
|
|
|
- goto out_sleep;
|
|
|
- }
|
|
|
break;
|
|
|
|
|
|
case DISABLE_KEY:
|
|
|
- /* The wl1271 does not allow to remove unicast keys - they
|
|
|
- will be cleared automatically on next CMD_JOIN. Ignore the
|
|
|
- request silently, as we dont want the mac80211 to emit
|
|
|
- an error message. */
|
|
|
- if (!is_broadcast_ether_addr(addr))
|
|
|
- break;
|
|
|
-
|
|
|
- ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE,
|
|
|
- key_conf->keyidx, key_type,
|
|
|
- key_conf->keylen, key_conf->key,
|
|
|
- addr, 0, 0);
|
|
|
+ ret = wl1271_set_key(wl, KEY_REMOVE,
|
|
|
+ key_conf->keyidx, key_type,
|
|
|
+ key_conf->keylen, key_conf->key,
|
|
|
+ 0, 0, sta);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("Could not remove key");
|
|
|
goto out_sleep;
|
|
@@ -1753,7 +1894,6 @@ out_sleep:
|
|
|
out_unlock:
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
-out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -2019,6 +2159,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
|
|
|
|
|
|
set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
|
|
|
wl1271_debug(DEBUG_AP, "started AP");
|
|
|
+
|
|
|
+ ret = wl1271_ap_init_hwenc(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out;
|
|
|
}
|
|
|
} else {
|
|
|
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
|