diff options
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 280 |
1 files changed, 277 insertions, 3 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 2b76bbc5e61..809f2bf2795 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c | |||
@@ -259,6 +259,7 @@ struct mwl8k_vif { | |||
259 | bool is_hw_crypto_enabled; | 259 | bool is_hw_crypto_enabled; |
260 | }; | 260 | }; |
261 | #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) | 261 | #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) |
262 | #define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) | ||
262 | 263 | ||
263 | struct mwl8k_sta { | 264 | struct mwl8k_sta { |
264 | /* Index into station database. Returned by UPDATE_STADB. */ | 265 | /* Index into station database. Returned by UPDATE_STADB. */ |
@@ -352,6 +353,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = { | |||
352 | #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 | 353 | #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 |
353 | #define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ | 354 | #define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ |
354 | #define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ | 355 | #define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ |
356 | #define MWL8K_CMD_UPDATE_ENCRYPTION 0x1122 /* per-vif */ | ||
355 | #define MWL8K_CMD_UPDATE_STADB 0x1123 | 357 | #define MWL8K_CMD_UPDATE_STADB 0x1123 |
356 | 358 | ||
357 | static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) | 359 | static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) |
@@ -390,6 +392,7 @@ static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize) | |||
390 | MWL8K_CMDNAME(SET_RATEADAPT_MODE); | 392 | MWL8K_CMDNAME(SET_RATEADAPT_MODE); |
391 | MWL8K_CMDNAME(BSS_START); | 393 | MWL8K_CMDNAME(BSS_START); |
392 | MWL8K_CMDNAME(SET_NEW_STN); | 394 | MWL8K_CMDNAME(SET_NEW_STN); |
395 | MWL8K_CMDNAME(UPDATE_ENCRYPTION); | ||
393 | MWL8K_CMDNAME(UPDATE_STADB); | 396 | MWL8K_CMDNAME(UPDATE_STADB); |
394 | default: | 397 | default: |
395 | snprintf(buf, bufsize, "0x%x", cmd); | 398 | snprintf(buf, bufsize, "0x%x", cmd); |
@@ -3241,6 +3244,274 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, | |||
3241 | } | 3244 | } |
3242 | 3245 | ||
3243 | /* | 3246 | /* |
3247 | * CMD_UPDATE_ENCRYPTION. | ||
3248 | */ | ||
3249 | |||
3250 | #define MAX_ENCR_KEY_LENGTH 16 | ||
3251 | #define MIC_KEY_LENGTH 8 | ||
3252 | |||
3253 | struct mwl8k_cmd_update_encryption { | ||
3254 | struct mwl8k_cmd_pkt header; | ||
3255 | |||
3256 | __le32 action; | ||
3257 | __le32 reserved; | ||
3258 | __u8 mac_addr[6]; | ||
3259 | __u8 encr_type; | ||
3260 | |||
3261 | } __attribute__((packed)); | ||
3262 | |||
3263 | struct mwl8k_cmd_set_key { | ||
3264 | struct mwl8k_cmd_pkt header; | ||
3265 | |||
3266 | __le32 action; | ||
3267 | __le32 reserved; | ||
3268 | __le16 length; | ||
3269 | __le16 key_type_id; | ||
3270 | __le32 key_info; | ||
3271 | __le32 key_id; | ||
3272 | __le16 key_len; | ||
3273 | __u8 key_material[MAX_ENCR_KEY_LENGTH]; | ||
3274 | __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; | ||
3275 | __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; | ||
3276 | __le16 tkip_rsc_low; | ||
3277 | __le32 tkip_rsc_high; | ||
3278 | __le16 tkip_tsc_low; | ||
3279 | __le32 tkip_tsc_high; | ||
3280 | __u8 mac_addr[6]; | ||
3281 | } __attribute__((packed)); | ||
3282 | |||
3283 | enum { | ||
3284 | MWL8K_ENCR_ENABLE, | ||
3285 | MWL8K_ENCR_SET_KEY, | ||
3286 | MWL8K_ENCR_REMOVE_KEY, | ||
3287 | MWL8K_ENCR_SET_GROUP_KEY, | ||
3288 | }; | ||
3289 | |||
3290 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP 0 | ||
3291 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE 1 | ||
3292 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP 4 | ||
3293 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED 7 | ||
3294 | #define MWL8K_UPDATE_ENCRYPTION_TYPE_AES 8 | ||
3295 | |||
3296 | enum { | ||
3297 | MWL8K_ALG_WEP, | ||
3298 | MWL8K_ALG_TKIP, | ||
3299 | MWL8K_ALG_CCMP, | ||
3300 | }; | ||
3301 | |||
3302 | #define MWL8K_KEY_FLAG_TXGROUPKEY 0x00000004 | ||
3303 | #define MWL8K_KEY_FLAG_PAIRWISE 0x00000008 | ||
3304 | #define MWL8K_KEY_FLAG_TSC_VALID 0x00000040 | ||
3305 | #define MWL8K_KEY_FLAG_WEP_TXKEY 0x01000000 | ||
3306 | #define MWL8K_KEY_FLAG_MICKEY_VALID 0x02000000 | ||
3307 | |||
3308 | static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, | ||
3309 | struct ieee80211_vif *vif, | ||
3310 | u8 *addr, | ||
3311 | u8 encr_type) | ||
3312 | { | ||
3313 | struct mwl8k_cmd_update_encryption *cmd; | ||
3314 | int rc; | ||
3315 | |||
3316 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
3317 | if (cmd == NULL) | ||
3318 | return -ENOMEM; | ||
3319 | |||
3320 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); | ||
3321 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); | ||
3322 | cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); | ||
3323 | memcpy(cmd->mac_addr, addr, ETH_ALEN); | ||
3324 | cmd->encr_type = encr_type; | ||
3325 | |||
3326 | rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); | ||
3327 | kfree(cmd); | ||
3328 | |||
3329 | return rc; | ||
3330 | } | ||
3331 | |||
3332 | static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, | ||
3333 | u8 *addr, | ||
3334 | struct ieee80211_key_conf *key) | ||
3335 | { | ||
3336 | cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); | ||
3337 | cmd->header.length = cpu_to_le16(sizeof(*cmd)); | ||
3338 | cmd->length = cpu_to_le16(sizeof(*cmd) - | ||
3339 | offsetof(struct mwl8k_cmd_set_key, length)); | ||
3340 | cmd->key_id = cpu_to_le32(key->keyidx); | ||
3341 | cmd->key_len = cpu_to_le16(key->keylen); | ||
3342 | memcpy(cmd->mac_addr, addr, ETH_ALEN); | ||
3343 | |||
3344 | switch (key->cipher) { | ||
3345 | case WLAN_CIPHER_SUITE_WEP40: | ||
3346 | case WLAN_CIPHER_SUITE_WEP104: | ||
3347 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); | ||
3348 | if (key->keyidx == 0) | ||
3349 | cmd->key_info = cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); | ||
3350 | |||
3351 | break; | ||
3352 | case WLAN_CIPHER_SUITE_TKIP: | ||
3353 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); | ||
3354 | cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) | ||
3355 | ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) | ||
3356 | : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); | ||
3357 | cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID | ||
3358 | | MWL8K_KEY_FLAG_TSC_VALID); | ||
3359 | break; | ||
3360 | case WLAN_CIPHER_SUITE_CCMP: | ||
3361 | cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); | ||
3362 | cmd->key_info = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) | ||
3363 | ? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) | ||
3364 | : cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); | ||
3365 | break; | ||
3366 | default: | ||
3367 | return -ENOTSUPP; | ||
3368 | } | ||
3369 | |||
3370 | return 0; | ||
3371 | } | ||
3372 | |||
3373 | static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, | ||
3374 | struct ieee80211_vif *vif, | ||
3375 | u8 *addr, | ||
3376 | struct ieee80211_key_conf *key) | ||
3377 | { | ||
3378 | struct mwl8k_cmd_set_key *cmd; | ||
3379 | int rc; | ||
3380 | int keymlen; | ||
3381 | u32 action; | ||
3382 | u8 idx; | ||
3383 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); | ||
3384 | |||
3385 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
3386 | if (cmd == NULL) | ||
3387 | return -ENOMEM; | ||
3388 | |||
3389 | rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); | ||
3390 | if (rc < 0) | ||
3391 | goto done; | ||
3392 | |||
3393 | idx = key->keyidx; | ||
3394 | |||
3395 | if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) | ||
3396 | action = MWL8K_ENCR_SET_KEY; | ||
3397 | else | ||
3398 | action = MWL8K_ENCR_SET_GROUP_KEY; | ||
3399 | |||
3400 | switch (key->cipher) { | ||
3401 | case WLAN_CIPHER_SUITE_WEP40: | ||
3402 | case WLAN_CIPHER_SUITE_WEP104: | ||
3403 | if (!mwl8k_vif->wep_key_conf[idx].enabled) { | ||
3404 | memcpy(mwl8k_vif->wep_key_conf[idx].key, key, | ||
3405 | sizeof(*key) + key->keylen); | ||
3406 | mwl8k_vif->wep_key_conf[idx].enabled = 1; | ||
3407 | } | ||
3408 | |||
3409 | keymlen = 0; | ||
3410 | action = MWL8K_ENCR_SET_KEY; | ||
3411 | break; | ||
3412 | case WLAN_CIPHER_SUITE_TKIP: | ||
3413 | keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; | ||
3414 | break; | ||
3415 | case WLAN_CIPHER_SUITE_CCMP: | ||
3416 | keymlen = key->keylen; | ||
3417 | break; | ||
3418 | default: | ||
3419 | rc = -ENOTSUPP; | ||
3420 | goto done; | ||
3421 | } | ||
3422 | |||
3423 | memcpy(cmd->key_material, key->key, keymlen); | ||
3424 | cmd->action = cpu_to_le32(action); | ||
3425 | |||
3426 | rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); | ||
3427 | done: | ||
3428 | kfree(cmd); | ||
3429 | |||
3430 | return rc; | ||
3431 | } | ||
3432 | |||
3433 | static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, | ||
3434 | struct ieee80211_vif *vif, | ||
3435 | u8 *addr, | ||
3436 | struct ieee80211_key_conf *key) | ||
3437 | { | ||
3438 | struct mwl8k_cmd_set_key *cmd; | ||
3439 | int rc; | ||
3440 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); | ||
3441 | |||
3442 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
3443 | if (cmd == NULL) | ||
3444 | return -ENOMEM; | ||
3445 | |||
3446 | rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); | ||
3447 | if (rc < 0) | ||
3448 | goto done; | ||
3449 | |||
3450 | if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
3451 | WLAN_CIPHER_SUITE_WEP104) | ||
3452 | mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; | ||
3453 | |||
3454 | cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); | ||
3455 | |||
3456 | rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); | ||
3457 | done: | ||
3458 | kfree(cmd); | ||
3459 | |||
3460 | return rc; | ||
3461 | } | ||
3462 | |||
3463 | static int mwl8k_set_key(struct ieee80211_hw *hw, | ||
3464 | enum set_key_cmd cmd_param, | ||
3465 | struct ieee80211_vif *vif, | ||
3466 | struct ieee80211_sta *sta, | ||
3467 | struct ieee80211_key_conf *key) | ||
3468 | { | ||
3469 | int rc = 0; | ||
3470 | u8 encr_type; | ||
3471 | u8 *addr; | ||
3472 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); | ||
3473 | |||
3474 | if (vif->type == NL80211_IFTYPE_STATION) | ||
3475 | return -EOPNOTSUPP; | ||
3476 | |||
3477 | if (sta == NULL) | ||
3478 | addr = hw->wiphy->perm_addr; | ||
3479 | else | ||
3480 | addr = sta->addr; | ||
3481 | |||
3482 | if (cmd_param == SET_KEY) { | ||
3483 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | ||
3484 | rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); | ||
3485 | if (rc) | ||
3486 | goto out; | ||
3487 | |||
3488 | if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) | ||
3489 | || (key->cipher == WLAN_CIPHER_SUITE_WEP104)) | ||
3490 | encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; | ||
3491 | else | ||
3492 | encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; | ||
3493 | |||
3494 | rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, | ||
3495 | encr_type); | ||
3496 | if (rc) | ||
3497 | goto out; | ||
3498 | |||
3499 | mwl8k_vif->is_hw_crypto_enabled = true; | ||
3500 | |||
3501 | } else { | ||
3502 | rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); | ||
3503 | |||
3504 | if (rc) | ||
3505 | goto out; | ||
3506 | |||
3507 | mwl8k_vif->is_hw_crypto_enabled = false; | ||
3508 | |||
3509 | } | ||
3510 | out: | ||
3511 | return rc; | ||
3512 | } | ||
3513 | |||
3514 | /* | ||
3244 | * CMD_UPDATE_STADB. | 3515 | * CMD_UPDATE_STADB. |
3245 | */ | 3516 | */ |
3246 | struct ewc_ht_info { | 3517 | struct ewc_ht_info { |
@@ -4010,17 +4281,19 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw, | |||
4010 | { | 4281 | { |
4011 | struct mwl8k_priv *priv = hw->priv; | 4282 | struct mwl8k_priv *priv = hw->priv; |
4012 | int ret; | 4283 | int ret; |
4284 | int i; | ||
4285 | struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); | ||
4286 | struct ieee80211_key_conf *key; | ||
4013 | 4287 | ||
4014 | if (!priv->ap_fw) { | 4288 | if (!priv->ap_fw) { |
4015 | ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); | 4289 | ret = mwl8k_cmd_update_stadb_add(hw, vif, sta); |
4016 | if (ret >= 0) { | 4290 | if (ret >= 0) { |
4017 | MWL8K_STA(sta)->peer_id = ret; | 4291 | MWL8K_STA(sta)->peer_id = ret; |
4018 | return 0; | 4292 | ret = 0; |
4019 | } | 4293 | } |
4020 | 4294 | ||
4021 | } else { | 4295 | } else { |
4022 | ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); | 4296 | ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta); |
4023 | return ret; | ||
4024 | } | 4297 | } |
4025 | 4298 | ||
4026 | for (i = 0; i < NUM_WEP_KEYS; i++) { | 4299 | for (i = 0; i < NUM_WEP_KEYS; i++) { |
@@ -4028,7 +4301,7 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw, | |||
4028 | if (mwl8k_vif->wep_key_conf[i].enabled) | 4301 | if (mwl8k_vif->wep_key_conf[i].enabled) |
4029 | mwl8k_set_key(hw, SET_KEY, vif, sta, key); | 4302 | mwl8k_set_key(hw, SET_KEY, vif, sta, key); |
4030 | } | 4303 | } |
4031 | return mwl8k_cmd_set_new_stn_add(hw, vif, sta); | 4304 | return ret; |
4032 | } | 4305 | } |
4033 | 4306 | ||
4034 | static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, | 4307 | static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, |
@@ -4106,6 +4379,7 @@ static const struct ieee80211_ops mwl8k_ops = { | |||
4106 | .bss_info_changed = mwl8k_bss_info_changed, | 4379 | .bss_info_changed = mwl8k_bss_info_changed, |
4107 | .prepare_multicast = mwl8k_prepare_multicast, | 4380 | .prepare_multicast = mwl8k_prepare_multicast, |
4108 | .configure_filter = mwl8k_configure_filter, | 4381 | .configure_filter = mwl8k_configure_filter, |
4382 | .set_key = mwl8k_set_key, | ||
4109 | .set_rts_threshold = mwl8k_set_rts_threshold, | 4383 | .set_rts_threshold = mwl8k_set_rts_threshold, |
4110 | .sta_add = mwl8k_sta_add, | 4384 | .sta_add = mwl8k_sta_add, |
4111 | .sta_remove = mwl8k_sta_remove, | 4385 | .sta_remove = mwl8k_sta_remove, |