// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2019-2020 Realtek Corporation
*/
#include "cam.h"
#include "debug.h"
#include "fw.h"
#include "mac.h"
static struct sk_buff *
rtw89_cam_get_sec_key_cmd(struct rtw89_dev *rtwdev,
struct rtw89_sec_cam_entry *sec_cam,
bool ext_key)
{
struct sk_buff *skb;
u32 cmd_len = H2C_SEC_CAM_LEN;
u32 key32[4];
u8 *cmd;
int i, j;
skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, cmd_len);
if (!skb)
return NULL;
skb_put_zero(skb, cmd_len);
for (i = 0; i < 4; i++) {
j = i * 4;
j += ext_key ? 16 : 0;
key32[i] = FIELD_PREP(GENMASK(7, 0), sec_cam->key[j + 0]) |
FIELD_PREP(GENMASK(15, 8), sec_cam->key[j + 1]) |
FIELD_PREP(GENMASK(23, 16), sec_cam->key[j + 2]) |
FIELD_PREP(GENMASK(31, 24), sec_cam->key[j + 3]);
}
cmd = skb->data;
RTW89_SET_FWCMD_SEC_IDX(cmd, sec_cam->sec_cam_idx + (ext_key ? 1 : 0));
RTW89_SET_FWCMD_SEC_OFFSET(cmd, sec_cam->offset);
RTW89_SET_FWCMD_SEC_LEN(cmd, sec_cam->len);
RTW89_SET_FWCMD_SEC_TYPE(cmd, sec_cam->type);
RTW89_SET_FWCMD_SEC_EXT_KEY(cmd, ext_key);
RTW89_SET_FWCMD_SEC_SPP_MODE(cmd, sec_cam->spp_mode);
RTW89_SET_FWCMD_SEC_KEY0(cmd, key32[0]);
RTW89_SET_FWCMD_SEC_KEY1(cmd, key32[1]);
RTW89_SET_FWCMD_SEC_KEY2(cmd, key32[2]);
RTW89_SET_FWCMD_SEC_KEY3(cmd, key32[3]);
return skb;
}
static int rtw89_cam_send_sec_key_cmd(struct rtw89_dev *rtwdev,
struct rtw89_sec_cam_entry *sec_cam)
{
struct sk_buff *skb, *ext_skb;
int ret;
skb = rtw89_cam_get_sec_key_cmd(rtwdev, sec_cam, false);
if (!skb) {
rtw89_err(rtwdev, "failed to get sec key command\n");
return -ENOMEM;
}
rtw89_h2c_pkt_set_hdr(rtwdev, skb,
FWCMD_TYPE_H2C,
H2C_CAT_MAC,
H2C_CL_MAC_SEC_CAM,
H2C_FUNC_MAC_SEC_UPD, 1, 0,
H2C_SEC_CAM_LEN);
ret = rtw89_h2c_tx(rtwdev, skb, false);
if (ret) {
rtw89_err(rtwdev, "failed to send sec key h2c: %d\n", ret);
dev_kfree_skb(skb);
return ret;
}
if (!sec_cam->ext_key)
return 0;
ext_skb = rtw89_cam_get_sec_key_cmd(rtwdev, sec_cam, true);
if (!ext_skb) {
rtw89_err(rtwdev, "failed to get ext sec key command\n");
return -ENOMEM;
}
rtw89_h2c_pkt_set_hdr(rtwdev, ext_skb,
FWCMD_TYPE_H2C,
H2C_CAT_MAC,
H2C_CL_MAC_SEC_CAM,
H2C_FUNC_MAC_SEC_UPD,
1, 0, H2C_SEC_CAM_LEN);
ret = rtw89_h2c_tx(rtwdev, ext_skb, false);
if (ret) {
rtw89_err(rtwdev, "failed to send ext sec key h2c: %d\n", ret);
dev_kfree_skb(ext_skb);
return ret;
}
return 0;
}
static int rtw89_cam_get_avail_sec_cam(struct rtw89_dev *rtwdev,
u8 *sec_cam_idx, bool ext_key)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_cam_info *cam_info = &rtwdev->cam_info;
u8 sec_cam_num = chip->scam_num;
u8 idx = 0;
if (!ext_key) {
idx = find_first_zero_bit(cam_info->sec_cam_map, sec_cam_num);
if (idx >= sec_cam_num)
return -EBUSY;
set_bit(idx, cam_info->sec_cam_map);
*sec_cam_idx = idx;
return 0;
}
again:
idx = find_next_zero_bit(cam_info->sec_cam_map, sec_cam_num, idx);
if (idx >= sec_cam_num - 1)
return -EBUSY;
/* ext keys need two cam entries for 256-bit key */
if (test_bit(idx + 1, cam_info->sec_cam_map)) {
idx++;
goto again;
}
set_bit(idx, cam_info->sec_cam_map);
set_bit(idx + 1, cam_info->sec_cam_map);
*sec_cam_idx = idx;
return 0;
}
static int rtw89_cam_get_addr_cam_key_idx(struct rtw89_addr_cam_entry *addr_cam,
struct rtw89_sec_cam_entry *sec_cam,
struct ieee80211_key_conf *key,
u8 *key_idx)
{
u8 idx;
/* RTW89_ADDR_CAM_SEC_NONE : not enabled
* RTW89_ADDR_CAM_SEC_ALL_UNI : 0 - 6 unicast
* RTW89_ADDR_CAM_SEC_NORMAL : 0 - 1 unicast, 2 - 4 group, 5 - 6 BIP
* RTW89_ADDR_CAM_SEC_4GROUP : 0 - 1 unicast, 2 - 5 group, 6 BIP
*/
switch (addr_cam->sec_ent_mode) {
case RTW89_ADDR_CAM_SEC_NONE:
return -EINVAL;
case RTW89_ADDR_CAM_SEC_ALL_UNI:
idx = find_first_zero_bit(addr_cam->sec_cam_map,
RTW89_SEC_CAM_IN_ADDR_CAM);
if (idx >= RTW89_SEC_CAM_IN_ADDR_CAM)
return -EBUSY;
*key_idx = idx;
break;
case RTW89_ADDR_CAM_SEC_NORMAL:
if (sec_cam->type == RTW89_SEC_KEY_TYPE_BIP_CCMP128) {
idx = find_next_zero_bit(addr_cam->sec_cam_map,
RTW89_SEC_CAM_IN_ADDR_CAM, 5);
if (idx > 6)
return -EBUSY;
*key_idx = idx;
break;
}
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
idx = find_next_zero_bit(addr_cam->sec_cam_map,
RTW89_SEC_CAM_IN_ADDR_CAM, 0);
if (idx > 1)
return -EBUSY;
*key_idx = idx;
break;
}
/* Group keys */
idx = find_next_zero_bit(addr_cam->sec_cam_map,
RTW89_SEC_CAM_IN_ADDR_CAM, 2);
if (idx > 4)
return -EBUSY;
*key_idx = idx;
break;
case RTW89_ADDR_CAM_SEC_4GROUP:
if (sec_cam->type == RTW89_SEC_KEY_TYPE_BIP_CCMP128) {
if (test_bit(6, addr_cam