Add a custom function for allocating entries in the sec cam. This allows
us to store multiple keys with the same keyidx.
The maximum number of sec cam entries (for 8188f) is 16 according to the
vendor driver.
Set the bssid as mac address for group keys instead of just using the
ethernet broadcast address and use BIT(6) in the sec cam ctrl entry
for differentiating them from pairwise keys like in the vendor driver.
Add the TXDESC_EN_DESC_ID bit and the hw_key_idx to tx
broadcast/multicast packets in AP mode.
Finally, allow the usage of rtl8xxxu_set_key() for AP mode.
Signed-off-by: Martin Kaistra <[email protected]>
---
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 4 ++
.../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 48 +++++++++++++++----
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 889ef7217f142..a0222d2666000 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -498,6 +498,7 @@ struct rtl8xxxu_txdesc40 {
#define DESC_RATE_ID_SHIFT 16
#define DESC_RATE_ID_MASK 0xf
#define TXDESC_NAVUSEHDR BIT(20)
+#define TXDESC_EN_DESC_ID BIT(21)
#define TXDESC_SEC_RC4 0x00400000
#define TXDESC_SEC_AES 0x00c00000
#define TXDESC_PKT_OFFSET_SHIFT 26
@@ -1774,6 +1775,7 @@ struct rtl8xxxu_cfo_tracking {
#define RTL8XXXU_HW_LED_CONTROL 2
#define RTL8XXXU_MAX_MAC_ID_NUM 128
#define RTL8XXXU_BC_MC_MACID 0
+#define RTL8XXXU_MAX_SEC_CAM_NUM 16
struct rtl8xxxu_priv {
struct ieee80211_hw *hw;
@@ -1907,6 +1909,7 @@ struct rtl8xxxu_priv {
char led_name[32];
struct led_classdev led_cdev;
DECLARE_BITMAP(mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
+ DECLARE_BITMAP(cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
};
struct rtl8xxxu_sta_info {
@@ -1918,6 +1921,7 @@ struct rtl8xxxu_sta_info {
struct rtl8xxxu_vif {
int port_num;
+ u8 hw_key_idx;
};
struct rtl8xxxu_rx_urb {
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index c2a577ebd061e..88730791091a7 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -4558,8 +4558,10 @@ static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
* This is a bit of a hack - the lower bits of the cipher
* suite selector happens to match the cipher index in the CAM
*/
- addr = key->keyidx << CAM_CMD_KEY_SHIFT;
+ addr = key->hw_key_idx << CAM_CMD_KEY_SHIFT;
ctrl = (key->cipher & 0x0f) << 2 | key->keyidx | CAM_WRITE_VALID;
+ if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ ctrl |= BIT(6);
for (j = 5; j >= 0; j--) {
switch (j) {
@@ -5545,13 +5547,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
struct rtl8xxxu_tx_urb *tx_urb;
struct ieee80211_sta *sta = NULL;
struct ieee80211_vif *vif = tx_info->control.vif;
+ struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
struct device *dev = &priv->udev->dev;
u32 queue, rts_rate;
u16 pktlen = skb->len;
int tx_desc_size = priv->fops->tx_desc_size;
u8 macid;
int ret;
- bool ampdu_enable, sgi = false, short_preamble = false;
+ bool ampdu_enable, sgi = false, short_preamble = false, bmc = false;
if (skb_headroom(skb) < tx_desc_size) {
dev_warn(dev,
@@ -5593,10 +5596,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
tx_desc->txdw0 =
TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
- is_broadcast_ether_addr(ieee80211_get_DA(hdr)))
+ is_broadcast_ether_addr(ieee80211_get_DA(hdr))) {
tx_desc->txdw0 |= TXDESC_BROADMULTICAST;
+ bmc = true;
+ }
+
tx_desc->txdw1 = cpu_to_le32(queue << TXDESC_QUEUE_SHIFT);
+ macid = rtl8xxxu_get_macid(priv, sta);
if (tx_info->control.hw_key) {
switch (tx_info->control.hw_key->cipher) {
@@ -5611,6 +5618,10 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
default:
break;
}
+ if (bmc && rtlvif->hw_key_idx != 0xff) {
+ tx_desc->txdw1 |= TXDESC_EN_DESC_ID;
+ macid = rtlvif->hw_key_idx;
+ }
}
/* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
@@ -5654,7 +5665,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
else
rts_rate = 0;
- macid = rtl8xxxu_get_macid(priv, sta);
priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble,
ampdu_enable, rts_rate, macid);
@@ -6656,6 +6666,7 @@ static int rtl8xxxu_add_interface(struct ieee80211_hw *hw,
priv->vifs[port_num] = vif;
rtlvif->port_num = port_num;
+ rtlvif->hw_key_idx = 0xff;
rtl8xxxu_set_linktype(priv, vif->type, port_num);
ether_addr_copy(priv->mac_addr, vif->addr);
@@ -6832,11 +6843,19 @@ static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
return 0;
}
+static int rtl8xxxu_get_free_sec_cam(struct ieee80211_hw *hw)
+{
+ struct rtl8xxxu_priv *priv = hw->priv;
+
+ return find_first_zero_bit(priv->cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
+}
+
static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
+ struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
struct rtl8xxxu_priv *priv = hw->priv;
struct device *dev = &priv->udev->dev;
u8 mac_addr[ETH_ALEN];
@@ -6848,9 +6867,6 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
dev_dbg(dev, "%s: cmd %02x, cipher %08x, index %i\n",
__func__, cmd, key->cipher, key->keyidx);
- if (vif->type != NL80211_IFTYPE_STATION)
- return -EOPNOTSUPP;
-
if (key->keyidx > 3)
return -EOPNOTSUPP;
@@ -6874,7 +6890,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ether_addr_copy(mac_addr, sta->addr);
} else {
dev_dbg(dev, "%s: group key\n", __func__);
- eth_broadcast_addr(mac_addr);
+ ether_addr_copy(mac_addr, vif->bss_conf.bssid);
}
val16 = rtl8xxxu_read16(priv, REG_CR);
@@ -6888,16 +6904,28 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
switch (cmd) {
case SET_KEY:
- key->hw_key_idx = key->keyidx;
+
+ retval = rtl8xxxu_get_free_sec_cam(hw);
+ if (retval < 0)
+ return -EOPNOTSUPP;
+
+ key->hw_key_idx = retval;
+
+ if (vif->type == NL80211_IFTYPE_AP && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
+ rtlvif->hw_key_idx = key->hw_key_idx;
+
key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
rtl8xxxu_cam_write(priv, key, mac_addr);
+ set_bit(key->hw_key_idx, priv->cam_map);
retval = 0;
break;
case DISABLE_KEY:
rtl8xxxu_write32(priv, REG_CAM_WRITE, 0x00000000);
val32 = CAM_CMD_POLLING | CAM_CMD_WRITE |
- key->keyidx << CAM_CMD_KEY_SHIFT;
+ key->hw_key_idx << CAM_CMD_KEY_SHIFT;
rtl8xxxu_write32(priv, REG_CAM_CMD, val32);
+ rtlvif->hw_key_idx = 0xff;
+ clear_bit(key->hw_key_idx, priv->cam_map);
retval = 0;
break;
default:
--
2.39.2
On 18/12/2023 16:36, Martin Kaistra wrote:
> Add a custom function for allocating entries in the sec cam. This allows
> us to store multiple keys with the same keyidx.
>
> The maximum number of sec cam entries (for 8188f) is 16 according to the
> vendor driver.
The other chips seem to support more:
RTL8723BU, RTL8192EU, RTL8192FU: 64
RTL8188EU: 32
RTL8710BU: 32 (even though it supports only 16 macid)
I'm not sure about the RTL8723AU and RTL8192CU. Those drivers are
older and different.
Perhaps this should be a member of struct rtl8xxxu_fileops, like
max_macid_num?
>
> Set the bssid as mac address for group keys instead of just using the
> ethernet broadcast address and use BIT(6) in the sec cam ctrl entry
> for differentiating them from pairwise keys like in the vendor driver.
>
> Add the TXDESC_EN_DESC_ID bit and the hw_key_idx to tx
> broadcast/multicast packets in AP mode.
>
> Finally, allow the usage of rtl8xxxu_set_key() for AP mode.
>
> Signed-off-by: Martin Kaistra <[email protected]>
> ---
> .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 4 ++
> .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 48 +++++++++++++++----
> 2 files changed, 42 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> index 889ef7217f142..a0222d2666000 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
> @@ -498,6 +498,7 @@ struct rtl8xxxu_txdesc40 {
> #define DESC_RATE_ID_SHIFT 16
> #define DESC_RATE_ID_MASK 0xf
> #define TXDESC_NAVUSEHDR BIT(20)
> +#define TXDESC_EN_DESC_ID BIT(21)
> #define TXDESC_SEC_RC4 0x00400000
> #define TXDESC_SEC_AES 0x00c00000
> #define TXDESC_PKT_OFFSET_SHIFT 26
> @@ -1774,6 +1775,7 @@ struct rtl8xxxu_cfo_tracking {
> #define RTL8XXXU_HW_LED_CONTROL 2
> #define RTL8XXXU_MAX_MAC_ID_NUM 128
> #define RTL8XXXU_BC_MC_MACID 0
> +#define RTL8XXXU_MAX_SEC_CAM_NUM 16
>
> struct rtl8xxxu_priv {
> struct ieee80211_hw *hw;
> @@ -1907,6 +1909,7 @@ struct rtl8xxxu_priv {
> char led_name[32];
> struct led_classdev led_cdev;
> DECLARE_BITMAP(mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
> + DECLARE_BITMAP(cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
> };
>
> struct rtl8xxxu_sta_info {
> @@ -1918,6 +1921,7 @@ struct rtl8xxxu_sta_info {
>
> struct rtl8xxxu_vif {
> int port_num;
> + u8 hw_key_idx;
> };
>
> struct rtl8xxxu_rx_urb {
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> index c2a577ebd061e..88730791091a7 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
> @@ -4558,8 +4558,10 @@ static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
> * This is a bit of a hack - the lower bits of the cipher
> * suite selector happens to match the cipher index in the CAM
> */
> - addr = key->keyidx << CAM_CMD_KEY_SHIFT;
> + addr = key->hw_key_idx << CAM_CMD_KEY_SHIFT;
> ctrl = (key->cipher & 0x0f) << 2 | key->keyidx | CAM_WRITE_VALID;
> + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
> + ctrl |= BIT(6);
>
> for (j = 5; j >= 0; j--) {
> switch (j) {
> @@ -5545,13 +5547,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
> struct rtl8xxxu_tx_urb *tx_urb;
> struct ieee80211_sta *sta = NULL;
> struct ieee80211_vif *vif = tx_info->control.vif;
> + struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
> struct device *dev = &priv->udev->dev;
> u32 queue, rts_rate;
> u16 pktlen = skb->len;
> int tx_desc_size = priv->fops->tx_desc_size;
> u8 macid;
> int ret;
> - bool ampdu_enable, sgi = false, short_preamble = false;
> + bool ampdu_enable, sgi = false, short_preamble = false, bmc = false;
>
> if (skb_headroom(skb) < tx_desc_size) {
> dev_warn(dev,
> @@ -5593,10 +5596,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
> tx_desc->txdw0 =
> TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
> if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
> - is_broadcast_ether_addr(ieee80211_get_DA(hdr)))
> + is_broadcast_ether_addr(ieee80211_get_DA(hdr))) {
> tx_desc->txdw0 |= TXDESC_BROADMULTICAST;
> + bmc = true;
> + }
> +
>
> tx_desc->txdw1 = cpu_to_le32(queue << TXDESC_QUEUE_SHIFT);
> + macid = rtl8xxxu_get_macid(priv, sta);
>
> if (tx_info->control.hw_key) {
> switch (tx_info->control.hw_key->cipher) {
> @@ -5611,6 +5618,10 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
> default:
> break;
> }
> + if (bmc && rtlvif->hw_key_idx != 0xff) {
> + tx_desc->txdw1 |= TXDESC_EN_DESC_ID;
> + macid = rtlvif->hw_key_idx;
> + }
cpu_to_le32(TXDESC_EN_DESC_ID)
The vendor drivers only do this for data frames. Is it the same here?
> }
>
> /* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
> @@ -5654,7 +5665,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
> else
> rts_rate = 0;
>
> - macid = rtl8xxxu_get_macid(priv, sta);
> priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble,
> ampdu_enable, rts_rate, macid);
>
> @@ -6656,6 +6666,7 @@ static int rtl8xxxu_add_interface(struct ieee80211_hw *hw,
>
> priv->vifs[port_num] = vif;
> rtlvif->port_num = port_num;
> + rtlvif->hw_key_idx = 0xff;
>
> rtl8xxxu_set_linktype(priv, vif->type, port_num);
> ether_addr_copy(priv->mac_addr, vif->addr);
> @@ -6832,11 +6843,19 @@ static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
> return 0;
> }
>
> +static int rtl8xxxu_get_free_sec_cam(struct ieee80211_hw *hw)
> +{
> + struct rtl8xxxu_priv *priv = hw->priv;
> +
> + return find_first_zero_bit(priv->cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
> +}
> +
> static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
> struct ieee80211_vif *vif,
> struct ieee80211_sta *sta,
> struct ieee80211_key_conf *key)
> {
> + struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
> struct rtl8xxxu_priv *priv = hw->priv;
> struct device *dev = &priv->udev->dev;
> u8 mac_addr[ETH_ALEN];
> @@ -6848,9 +6867,6 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
> dev_dbg(dev, "%s: cmd %02x, cipher %08x, index %i\n",
> __func__, cmd, key->cipher, key->keyidx);
>
> - if (vif->type != NL80211_IFTYPE_STATION)
> - return -EOPNOTSUPP;
> -
> if (key->keyidx > 3)
> return -EOPNOTSUPP;
>
> @@ -6874,7 +6890,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
> ether_addr_copy(mac_addr, sta->addr);
> } else {
> dev_dbg(dev, "%s: group key\n", __func__);
> - eth_broadcast_addr(mac_addr);
> + ether_addr_copy(mac_addr, vif->bss_conf.bssid);
> }
>
> val16 = rtl8xxxu_read16(priv, REG_CR);
> @@ -6888,16 +6904,28 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
>
> switch (cmd) {
> case SET_KEY:
> - key->hw_key_idx = key->keyidx;
> +
> + retval = rtl8xxxu_get_free_sec_cam(hw);
> + if (retval < 0)
> + return -EOPNOTSUPP;
> +
> + key->hw_key_idx = retval;
> +
> + if (vif->type == NL80211_IFTYPE_AP && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
> + rtlvif->hw_key_idx = key->hw_key_idx;
> +
> key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
> rtl8xxxu_cam_write(priv, key, mac_addr);
> + set_bit(key->hw_key_idx, priv->cam_map);
> retval = 0;
> break;
> case DISABLE_KEY:
> rtl8xxxu_write32(priv, REG_CAM_WRITE, 0x00000000);
> val32 = CAM_CMD_POLLING | CAM_CMD_WRITE |
> - key->keyidx << CAM_CMD_KEY_SHIFT;
> + key->hw_key_idx << CAM_CMD_KEY_SHIFT;
> rtl8xxxu_write32(priv, REG_CAM_CMD, val32);
> + rtlvif->hw_key_idx = 0xff;
> + clear_bit(key->hw_key_idx, priv->cam_map);
> retval = 0;
> break;
> default:
Am 19.12.23 um 23:29 schrieb Bitterblue Smith:
> On 18/12/2023 16:36, Martin Kaistra wrote:
>> Add a custom function for allocating entries in the sec cam. This allows
>> us to store multiple keys with the same keyidx.
>>
>> The maximum number of sec cam entries (for 8188f) is 16 according to the
>> vendor driver.
>
> The other chips seem to support more:
>
> RTL8723BU, RTL8192EU, RTL8192FU: 64
> RTL8188EU: 32
> RTL8710BU: 32 (even though it supports only 16 macid)
>
> I'm not sure about the RTL8723AU and RTL8192CU. Those drivers are
> older and different.
>
> Perhaps this should be a member of struct rtl8xxxu_fileops, like
> max_macid_num?
Sounds good to me, I will implement this for v2.
>
>>
>> Set the bssid as mac address for group keys instead of just using the
>> ethernet broadcast address and use BIT(6) in the sec cam ctrl entry
>> for differentiating them from pairwise keys like in the vendor driver.
>>
>> Add the TXDESC_EN_DESC_ID bit and the hw_key_idx to tx
>> broadcast/multicast packets in AP mode.
>>
>> Finally, allow the usage of rtl8xxxu_set_key() for AP mode.
>>
>> Signed-off-by: Martin Kaistra <[email protected]>
>> ---
>> .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 4 ++
>> .../wireless/realtek/rtl8xxxu/rtl8xxxu_core.c | 48 +++++++++++++++----
>> 2 files changed, 42 insertions(+), 10 deletions(-)
>>
>> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> index 889ef7217f142..a0222d2666000 100644
>> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
>> @@ -498,6 +498,7 @@ struct rtl8xxxu_txdesc40 {
>> #define DESC_RATE_ID_SHIFT 16
>> #define DESC_RATE_ID_MASK 0xf
>> #define TXDESC_NAVUSEHDR BIT(20)
>> +#define TXDESC_EN_DESC_ID BIT(21)
>> #define TXDESC_SEC_RC4 0x00400000
>> #define TXDESC_SEC_AES 0x00c00000
>> #define TXDESC_PKT_OFFSET_SHIFT 26
>> @@ -1774,6 +1775,7 @@ struct rtl8xxxu_cfo_tracking {
>> #define RTL8XXXU_HW_LED_CONTROL 2
>> #define RTL8XXXU_MAX_MAC_ID_NUM 128
>> #define RTL8XXXU_BC_MC_MACID 0
>> +#define RTL8XXXU_MAX_SEC_CAM_NUM 16
>>
>> struct rtl8xxxu_priv {
>> struct ieee80211_hw *hw;
>> @@ -1907,6 +1909,7 @@ struct rtl8xxxu_priv {
>> char led_name[32];
>> struct led_classdev led_cdev;
>> DECLARE_BITMAP(mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
>> + DECLARE_BITMAP(cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
>> };
>>
>> struct rtl8xxxu_sta_info {
>> @@ -1918,6 +1921,7 @@ struct rtl8xxxu_sta_info {
>>
>> struct rtl8xxxu_vif {
>> int port_num;
>> + u8 hw_key_idx;
>> };
>>
>> struct rtl8xxxu_rx_urb {
>> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
>> index c2a577ebd061e..88730791091a7 100644
>> --- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
>> +++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
>> @@ -4558,8 +4558,10 @@ static void rtl8xxxu_cam_write(struct rtl8xxxu_priv *priv,
>> * This is a bit of a hack - the lower bits of the cipher
>> * suite selector happens to match the cipher index in the CAM
>> */
>> - addr = key->keyidx << CAM_CMD_KEY_SHIFT;
>> + addr = key->hw_key_idx << CAM_CMD_KEY_SHIFT;
>> ctrl = (key->cipher & 0x0f) << 2 | key->keyidx | CAM_WRITE_VALID;
>> + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
>> + ctrl |= BIT(6);
>>
>> for (j = 5; j >= 0; j--) {
>> switch (j) {
>> @@ -5545,13 +5547,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>> struct rtl8xxxu_tx_urb *tx_urb;
>> struct ieee80211_sta *sta = NULL;
>> struct ieee80211_vif *vif = tx_info->control.vif;
>> + struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
>> struct device *dev = &priv->udev->dev;
>> u32 queue, rts_rate;
>> u16 pktlen = skb->len;
>> int tx_desc_size = priv->fops->tx_desc_size;
>> u8 macid;
>> int ret;
>> - bool ampdu_enable, sgi = false, short_preamble = false;
>> + bool ampdu_enable, sgi = false, short_preamble = false, bmc = false;
>>
>> if (skb_headroom(skb) < tx_desc_size) {
>> dev_warn(dev,
>> @@ -5593,10 +5596,14 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>> tx_desc->txdw0 =
>> TXDESC_OWN | TXDESC_FIRST_SEGMENT | TXDESC_LAST_SEGMENT;
>> if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) ||
>> - is_broadcast_ether_addr(ieee80211_get_DA(hdr)))
>> + is_broadcast_ether_addr(ieee80211_get_DA(hdr))) {
>> tx_desc->txdw0 |= TXDESC_BROADMULTICAST;
>> + bmc = true;
>> + }
>> +
>>
>> tx_desc->txdw1 = cpu_to_le32(queue << TXDESC_QUEUE_SHIFT);
>> + macid = rtl8xxxu_get_macid(priv, sta);
>>
>> if (tx_info->control.hw_key) {
>> switch (tx_info->control.hw_key->cipher) {
>> @@ -5611,6 +5618,10 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>> default:
>> break;
>> }
>> + if (bmc && rtlvif->hw_key_idx != 0xff) {
>> + tx_desc->txdw1 |= TXDESC_EN_DESC_ID;
>> + macid = rtlvif->hw_key_idx;
>> + }
>
> cpu_to_le32(TXDESC_EN_DESC_ID)
>
> The vendor drivers only do this for data frames. Is it the same here?
>
>> }
>>
>> /* (tx_info->flags & IEEE80211_TX_CTL_AMPDU) && */
>> @@ -5654,7 +5665,6 @@ static void rtl8xxxu_tx(struct ieee80211_hw *hw,
>> else
>> rts_rate = 0;
>>
>> - macid = rtl8xxxu_get_macid(priv, sta);
>> priv->fops->fill_txdesc(hw, hdr, tx_info, tx_desc, sgi, short_preamble,
>> ampdu_enable, rts_rate, macid);
>>
>> @@ -6656,6 +6666,7 @@ static int rtl8xxxu_add_interface(struct ieee80211_hw *hw,
>>
>> priv->vifs[port_num] = vif;
>> rtlvif->port_num = port_num;
>> + rtlvif->hw_key_idx = 0xff;
>>
>> rtl8xxxu_set_linktype(priv, vif->type, port_num);
>> ether_addr_copy(priv->mac_addr, vif->addr);
>> @@ -6832,11 +6843,19 @@ static int rtl8xxxu_set_rts_threshold(struct ieee80211_hw *hw, u32 rts)
>> return 0;
>> }
>>
>> +static int rtl8xxxu_get_free_sec_cam(struct ieee80211_hw *hw)
>> +{
>> + struct rtl8xxxu_priv *priv = hw->priv;
>> +
>> + return find_first_zero_bit(priv->cam_map, RTL8XXXU_MAX_SEC_CAM_NUM);
>> +}
>> +
>> static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
>> struct ieee80211_vif *vif,
>> struct ieee80211_sta *sta,
>> struct ieee80211_key_conf *key)
>> {
>> + struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
>> struct rtl8xxxu_priv *priv = hw->priv;
>> struct device *dev = &priv->udev->dev;
>> u8 mac_addr[ETH_ALEN];
>> @@ -6848,9 +6867,6 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
>> dev_dbg(dev, "%s: cmd %02x, cipher %08x, index %i\n",
>> __func__, cmd, key->cipher, key->keyidx);
>>
>> - if (vif->type != NL80211_IFTYPE_STATION)
>> - return -EOPNOTSUPP;
>> -
>> if (key->keyidx > 3)
>> return -EOPNOTSUPP;
>>
>> @@ -6874,7 +6890,7 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
>> ether_addr_copy(mac_addr, sta->addr);
>> } else {
>> dev_dbg(dev, "%s: group key\n", __func__);
>> - eth_broadcast_addr(mac_addr);
>> + ether_addr_copy(mac_addr, vif->bss_conf.bssid);
>> }
>>
>> val16 = rtl8xxxu_read16(priv, REG_CR);
>> @@ -6888,16 +6904,28 @@ static int rtl8xxxu_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
>>
>> switch (cmd) {
>> case SET_KEY:
>> - key->hw_key_idx = key->keyidx;
>> +
>> + retval = rtl8xxxu_get_free_sec_cam(hw);
>> + if (retval < 0)
>> + return -EOPNOTSUPP;
>> +
>> + key->hw_key_idx = retval;
>> +
>> + if (vif->type == NL80211_IFTYPE_AP && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
>> + rtlvif->hw_key_idx = key->hw_key_idx;
>> +
>> key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
>> rtl8xxxu_cam_write(priv, key, mac_addr);
>> + set_bit(key->hw_key_idx, priv->cam_map);
>> retval = 0;
>> break;
>> case DISABLE_KEY:
>> rtl8xxxu_write32(priv, REG_CAM_WRITE, 0x00000000);
>> val32 = CAM_CMD_POLLING | CAM_CMD_WRITE |
>> - key->keyidx << CAM_CMD_KEY_SHIFT;
>> + key->hw_key_idx << CAM_CMD_KEY_SHIFT;
>> rtl8xxxu_write32(priv, REG_CAM_CMD, val32);
>> + rtlvif->hw_key_idx = 0xff;
>> + clear_bit(key->hw_key_idx, priv->cam_map);
>> retval = 0;
>> break;
>> default:
>