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