linux/drivers/net/wireless/realtek/rtlwifi/cam.c
<<
>>
Prefs
   1/******************************************************************************
   2 *
   3 * Copyright(c) 2009-2012  Realtek Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of version 2 of the GNU General Public License as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 *
  14 * The full GNU General Public License is included in this distribution in the
  15 * file called LICENSE.
  16 *
  17 * Contact Information:
  18 * wlanfae <wlanfae@realtek.com>
  19 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
  20 * Hsinchu 300, Taiwan.
  21 *
  22 * Larry Finger <Larry.Finger@lwfinger.net>
  23 *
  24 *****************************************************************************/
  25#include "wifi.h"
  26#include "cam.h"
  27#include <linux/export.h>
  28
  29void rtl_cam_reset_sec_info(struct ieee80211_hw *hw)
  30{
  31        struct rtl_priv *rtlpriv = rtl_priv(hw);
  32
  33        rtlpriv->sec.use_defaultkey = false;
  34        rtlpriv->sec.pairwise_enc_algorithm = NO_ENCRYPTION;
  35        rtlpriv->sec.group_enc_algorithm = NO_ENCRYPTION;
  36        memset(rtlpriv->sec.key_buf, 0, KEY_BUF_SIZE * MAX_KEY_LEN);
  37        memset(rtlpriv->sec.key_len, 0, KEY_BUF_SIZE);
  38        rtlpriv->sec.pairwise_key = NULL;
  39}
  40
  41static void rtl_cam_program_entry(struct ieee80211_hw *hw, u32 entry_no,
  42                           u8 *mac_addr, u8 *key_cont_128, u16 us_config)
  43{
  44        struct rtl_priv *rtlpriv = rtl_priv(hw);
  45
  46        u32 target_command;
  47        u32 target_content = 0;
  48        int entry_i;
  49
  50        RT_PRINT_DATA(rtlpriv, COMP_SEC, DBG_DMESG, "Key content :",
  51                      key_cont_128, 16);
  52
  53        /* 0-1 config + mac, 2-5 fill 128key,6-7 are reserved */
  54        for (entry_i = CAM_CONTENT_COUNT - 1; entry_i >= 0; entry_i--) {
  55                target_command = entry_i + CAM_CONTENT_COUNT * entry_no;
  56                target_command = target_command | BIT(31) | BIT(16);
  57
  58                if (entry_i == 0) {
  59                        target_content = (u32) (*(mac_addr + 0)) << 16 |
  60                            (u32) (*(mac_addr + 1)) << 24 | (u32) us_config;
  61
  62                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI],
  63                                        target_content);
  64                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM],
  65                                        target_command);
  66
  67                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
  68                                 "WRITE %x: %x\n",
  69                                 rtlpriv->cfg->maps[WCAMI], target_content);
  70                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
  71                                 "The Key ID is %d\n", entry_no);
  72                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
  73                                 "WRITE %x: %x\n",
  74                                 rtlpriv->cfg->maps[RWCAM], target_command);
  75
  76                } else if (entry_i == 1) {
  77
  78                        target_content = (u32) (*(mac_addr + 5)) << 24 |
  79                            (u32) (*(mac_addr + 4)) << 16 |
  80                            (u32) (*(mac_addr + 3)) << 8 |
  81                            (u32) (*(mac_addr + 2));
  82
  83                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI],
  84                                        target_content);
  85                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM],
  86                                        target_command);
  87
  88                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
  89                                 "WRITE A4: %x\n", target_content);
  90                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
  91                                 "WRITE A0: %x\n", target_command);
  92
  93                } else {
  94
  95                        target_content =
  96                            (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 3)) <<
  97                            24 | (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 2))
  98                            << 16 |
  99                            (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 1)) << 8
 100                            | (u32) (*(key_cont_128 + (entry_i * 4 - 8) + 0));
 101
 102                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI],
 103                                        target_content);
 104                        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM],
 105                                        target_command);
 106
 107                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
 108                                 "WRITE A4: %x\n", target_content);
 109                        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
 110                                 "WRITE A0: %x\n", target_command);
 111                }
 112        }
 113
 114        RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
 115                 "after set key, usconfig:%x\n", us_config);
 116}
 117
 118u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr,
 119                         u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg,
 120                         u32 ul_default_key, u8 *key_content)
 121{
 122        u32 us_config;
 123        struct rtl_priv *rtlpriv = rtl_priv(hw);
 124
 125        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 126                 "EntryNo:%x, ulKeyId=%x, ulEncAlg=%x, ulUseDK=%x MacAddr %pM\n",
 127                 ul_entry_idx, ul_key_id, ul_enc_alg,
 128                 ul_default_key, mac_addr);
 129
 130        if (ul_key_id == TOTAL_CAM_ENTRY) {
 131                RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
 132                         "ulKeyId exceed!\n");
 133                return 0;
 134        }
 135
 136        if (ul_default_key == 1)
 137                us_config = CFG_VALID | ((u16) (ul_enc_alg) << 2);
 138        else
 139                us_config = CFG_VALID | ((ul_enc_alg) << 2) | ul_key_id;
 140
 141        rtl_cam_program_entry(hw, ul_entry_idx, mac_addr,
 142                              (u8 *)key_content, us_config);
 143
 144        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "end\n");
 145
 146        return 1;
 147
 148}
 149EXPORT_SYMBOL(rtl_cam_add_one_entry);
 150
 151int rtl_cam_delete_one_entry(struct ieee80211_hw *hw,
 152                             u8 *mac_addr, u32 ul_key_id)
 153{
 154        u32 ul_command;
 155        struct rtl_priv *rtlpriv = rtl_priv(hw);
 156
 157        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "key_idx:%d\n", ul_key_id);
 158
 159        ul_command = ul_key_id * CAM_CONTENT_COUNT;
 160        ul_command = ul_command | BIT(31) | BIT(16);
 161
 162        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], 0);
 163        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command);
 164
 165        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 166                 "rtl_cam_delete_one_entry(): WRITE A4: %x\n", 0);
 167        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 168                 "rtl_cam_delete_one_entry(): WRITE A0: %x\n", ul_command);
 169
 170        return 0;
 171
 172}
 173EXPORT_SYMBOL(rtl_cam_delete_one_entry);
 174
 175void rtl_cam_reset_all_entry(struct ieee80211_hw *hw)
 176{
 177        u32 ul_command;
 178        struct rtl_priv *rtlpriv = rtl_priv(hw);
 179
 180        ul_command = BIT(31) | BIT(30);
 181        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command);
 182}
 183EXPORT_SYMBOL(rtl_cam_reset_all_entry);
 184
 185void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index)
 186{
 187        struct rtl_priv *rtlpriv = rtl_priv(hw);
 188
 189        u32 ul_command;
 190        u32 ul_content;
 191        u32 ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES];
 192
 193        switch (rtlpriv->sec.pairwise_enc_algorithm) {
 194        case WEP40_ENCRYPTION:
 195                ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_WEP40];
 196                break;
 197        case WEP104_ENCRYPTION:
 198                ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_WEP104];
 199                break;
 200        case TKIP_ENCRYPTION:
 201                ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_TKIP];
 202                break;
 203        case AESCCMP_ENCRYPTION:
 204                ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES];
 205                break;
 206        default:
 207                ul_enc_algo = rtlpriv->cfg->maps[SEC_CAM_AES];
 208        }
 209
 210        ul_content = (uc_index & 3) | ((u16) (ul_enc_algo) << 2);
 211
 212        ul_content |= BIT(15);
 213        ul_command = CAM_CONTENT_COUNT * uc_index;
 214        ul_command = ul_command | BIT(31) | BIT(16);
 215
 216        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], ul_content);
 217        rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command);
 218
 219        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 220                 "rtl_cam_mark_invalid(): WRITE A4: %x\n", ul_content);
 221        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 222                 "rtl_cam_mark_invalid(): WRITE A0: %x\n", ul_command);
 223}
 224EXPORT_SYMBOL(rtl_cam_mark_invalid);
 225
 226void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index)
 227{
 228        struct rtl_priv *rtlpriv = rtl_priv(hw);
 229
 230        u32 ul_command;
 231        u32 ul_content;
 232        u32 ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES];
 233        u8 entry_i;
 234
 235        switch (rtlpriv->sec.pairwise_enc_algorithm) {
 236        case WEP40_ENCRYPTION:
 237                ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_WEP40];
 238                break;
 239        case WEP104_ENCRYPTION:
 240                ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_WEP104];
 241                break;
 242        case TKIP_ENCRYPTION:
 243                ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_TKIP];
 244                break;
 245        case AESCCMP_ENCRYPTION:
 246                ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES];
 247                break;
 248        default:
 249                ul_encalgo = rtlpriv->cfg->maps[SEC_CAM_AES];
 250        }
 251
 252        for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) {
 253
 254                if (entry_i == 0) {
 255                        ul_content =
 256                            (uc_index & 0x03) | ((u16) (ul_encalgo) << 2);
 257                        ul_content |= BIT(15);
 258
 259                } else {
 260                        ul_content = 0;
 261                }
 262
 263                ul_command = CAM_CONTENT_COUNT * uc_index + entry_i;
 264                ul_command = ul_command | BIT(31) | BIT(16);
 265
 266                rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[WCAMI], ul_content);
 267                rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], ul_command);
 268
 269                RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
 270                         "rtl_cam_empty_entry(): WRITE A4: %x\n",
 271                         ul_content);
 272                RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
 273                         "rtl_cam_empty_entry(): WRITE A0: %x\n",
 274                         ul_command);
 275        }
 276
 277}
 278EXPORT_SYMBOL(rtl_cam_empty_entry);
 279
 280u8 rtl_cam_get_free_entry(struct ieee80211_hw *hw, u8 *sta_addr)
 281{
 282        struct rtl_priv *rtlpriv = rtl_priv(hw);
 283        u32 bitmap = (rtlpriv->sec.hwsec_cam_bitmap) >> 4;
 284        u8 entry_idx = 0;
 285        u8 i, *addr;
 286
 287        if (NULL == sta_addr) {
 288                pr_err("sta_addr is NULL.\n");
 289                return TOTAL_CAM_ENTRY;
 290        }
 291        /* Does STA already exist? */
 292        for (i = 4; i < TOTAL_CAM_ENTRY; i++) {
 293                addr = rtlpriv->sec.hwsec_cam_sta_addr[i];
 294                if (ether_addr_equal_unaligned(addr, sta_addr))
 295                        return i;
 296        }
 297        /* Get a free CAM entry. */
 298        for (entry_idx = 4; entry_idx < TOTAL_CAM_ENTRY; entry_idx++) {
 299                if ((bitmap & BIT(0)) == 0) {
 300                        pr_err("-----hwsec_cam_bitmap: 0x%x entry_idx=%d\n",
 301                               rtlpriv->sec.hwsec_cam_bitmap, entry_idx);
 302                        rtlpriv->sec.hwsec_cam_bitmap |= BIT(0) << entry_idx;
 303                        memcpy(rtlpriv->sec.hwsec_cam_sta_addr[entry_idx],
 304                               sta_addr, ETH_ALEN);
 305                        return entry_idx;
 306                }
 307                bitmap = bitmap >> 1;
 308        }
 309        return TOTAL_CAM_ENTRY;
 310}
 311EXPORT_SYMBOL(rtl_cam_get_free_entry);
 312
 313void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr)
 314{
 315        struct rtl_priv *rtlpriv = rtl_priv(hw);
 316        u32 bitmap;
 317        u8 i, *addr;
 318
 319        if (NULL == sta_addr) {
 320                pr_err("sta_addr is NULL.\n");
 321                return;
 322        }
 323
 324        if (is_zero_ether_addr(sta_addr)) {
 325                pr_err("sta_addr is %pM\n", sta_addr);
 326                return;
 327        }
 328        /* Does STA already exist? */
 329        for (i = 4; i < TOTAL_CAM_ENTRY; i++) {
 330                addr = rtlpriv->sec.hwsec_cam_sta_addr[i];
 331                bitmap = (rtlpriv->sec.hwsec_cam_bitmap) >> i;
 332                if (((bitmap & BIT(0)) == BIT(0)) &&
 333                    (ether_addr_equal_unaligned(addr, sta_addr))) {
 334                        /* Remove from HW Security CAM */
 335                        eth_zero_addr(rtlpriv->sec.hwsec_cam_sta_addr[i]);
 336                        rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i);
 337                        RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG,
 338                                 "&&&&&&&&&del entry %d\n", i);
 339                }
 340        }
 341        return;
 342}
 343EXPORT_SYMBOL(rtl_cam_del_entry);
 344