@@ -611,7 +611,7 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
return ERR_PTR(err);
}
-static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
+bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
{
enum nl80211_iftype iftype;
@@ -4812,7 +4812,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
err = -EINVAL;
goto exit;
}
-
/* Interface specific setup */
if (dev_role == NL80211_IFTYPE_AP) {
if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
@@ -4892,7 +4891,6 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
err);
goto exit;
}
-
brcmf_dbg(TRACE, "AP mode configuration complete\n");
} else if (dev_role == NL80211_IFTYPE_P2P_GO) {
err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
@@ -6086,6 +6084,14 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
}
if (brcmf_is_apmode(ifp->vif)) {
+ if (e->event_code == BRCMF_E_ASSOC_IND ||
+ e->event_code == BRCMF_E_REASSOC_IND) {
+ brcmf_findadd_sta(ifp, e->addr);
+ } else if ((e->event_code == BRCMF_E_DISASSOC_IND) ||
+ (e->event_code == BRCMF_E_DEAUTH_IND) ||
+ (e->event_code == BRCMF_E_DEAUTH)) {
+ brcmf_del_sta(ifp, e->addr);
+ }
err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
} else if (brcmf_is_linkup(ifp->vif, e)) {
brcmf_dbg(CONN, "Linkup\n");
@@ -903,7 +903,9 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
init_waitqueue_head(&ifp->pend_8021x_wait);
spin_lock_init(&ifp->netif_stop_lock);
-
+ spin_lock_init(&(ifp)->sta_list_lock);
+ /* Initialize STA info list */
+ INIT_LIST_HEAD(&ifp->sta_list);
if (mac_addr != NULL)
memcpy(ifp->mac_addr, mac_addr, ETH_ALEN);
@@ -1560,3 +1562,111 @@ void __exit brcmf_core_exit(void)
#endif
}
+/**
+ * brcmf_find_sta() - Find STA with MAC address ea in an interface's STA list
+ *
+ * @ifp: interface control information
+ * @ea: mac address
+ */
+struct brcmf_sta *
+brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea)
+{
+ struct brcmf_sta *sta;
+ unsigned long flags;
+
+ spin_lock_irqsave(&(ifp)->sta_list_lock, (flags));
+ list_for_each_entry(sta, &ifp->sta_list, list) {
+ if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) {
+ brcmf_dbg(INFO, "Found STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n",
+ sta->ea.octet[0], sta->ea.octet[1],
+ sta->ea.octet[2], sta->ea.octet[3],
+ sta->ea.octet[4], sta->ea.octet[5]);
+ spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags));
+ return sta;
+ }
+ }
+ spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags));
+
+ return (struct brcmf_sta *)NULL;
+}
+
+/**
+ * brcmf_add_sta() - Add STA into the interface's STA list.
+ *
+ * @ifp: interface control information
+ * @ea: mac address
+ */
+static struct brcmf_sta *
+brcmf_add_sta(struct brcmf_if *ifp, const u8 *ea)
+{
+ struct brcmf_sta *sta;
+ unsigned long flags;
+
+ sta = kzalloc(sizeof(*sta), GFP_KERNEL);
+ if (sta == (struct brcmf_sta *)NULL) {
+ brcmf_err("Alloc failed\n");
+ return (struct brcmf_sta *)NULL;
+ }
+ memcpy(sta->ea.octet, ea, ETH_ALEN);
+ brcmf_dbg(INFO, "Add STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n",
+ sta->ea.octet[0], sta->ea.octet[1],
+ sta->ea.octet[2], sta->ea.octet[3],
+ sta->ea.octet[4], sta->ea.octet[5]);
+
+ /* link the sta and the dhd interface */
+ sta->ifp = ifp;
+ INIT_LIST_HEAD(&sta->list);
+
+ spin_lock_irqsave(&(ifp)->sta_list_lock, (flags));
+
+ list_add_tail(&sta->list, &ifp->sta_list);
+
+ spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags));
+ return sta;
+}
+
+/**
+ * brcmf_del_sta() - Delete STA from the interface's STA list.
+ *
+ * @ifp: interface control information
+ * @ea: mac address
+ */
+void
+brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea)
+{
+ struct brcmf_sta *sta, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&(ifp)->sta_list_lock, (flags));
+ list_for_each_entry_safe(sta, next, &ifp->sta_list, list) {
+ if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) {
+ brcmf_dbg(INFO, "del STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x from sta list\n",
+ ea[0], ea[1], ea[2], ea[3],
+ ea[4], ea[5]);
+ list_del(&sta->list);
+ kfree(sta);
+ }
+ }
+
+ spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags));
+}
+
+/**
+ * brcmf_findadd_sta() - Add STA if it doesn't exist. Not reentrant.
+ *
+ * @ifp: interface control information
+ * @ea: mac address
+ */
+struct brcmf_sta*
+brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea)
+{
+ struct brcmf_sta *sta = NULL;
+
+ sta = brcmf_find_sta(ifp, ea);
+
+ if (!sta) {
+ /* Add entry */
+ sta = brcmf_add_sta(ifp, ea);
+ }
+ return sta;
+}
@@ -193,6 +193,18 @@ struct brcmf_if {
struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES];
u8 ipv6addr_idx;
bool fwil_fwerr;
+ struct list_head sta_list; /* sll of associated stations */
+ spinlock_t sta_list_lock;
+};
+
+struct ether_addr {
+ u8 octet[ETH_ALEN];
+};
+
+struct brcmf_sta {
+ void *ifp; /* associated brcm_if */
+ struct ether_addr ea; /* stations ethernet mac address */
+ struct list_head list; /* link into brcmf_if::sta_list */
};
int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
@@ -215,5 +227,8 @@ int brcmf_net_mon_attach(struct brcmf_if *ifp);
void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
int __init brcmf_core_init(void);
void __exit brcmf_core_exit(void);
-
+void brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea);
+struct brcmf_sta *brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea);
+struct brcmf_sta *brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea);
+bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif);
#endif /* BRCMFMAC_CORE_H */
@@ -1141,7 +1141,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
{
struct brcmf_pub *drvr = msgbuf->drvr;
struct msgbuf_rx_complete *rx_complete;
- struct sk_buff *skb;
+ struct sk_buff *skb, *cpskb = NULL;
+ struct ethhdr *eh;
u16 data_offset;
u16 buflen;
u16 flags;
@@ -1190,6 +1191,34 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf)
return;
}
+ eh = (struct ethhdr *)(skb->data);
+ if (brcmf_is_apmode(ifp->vif)) {
+ skb_set_network_header(skb, sizeof(struct ethhdr));
+ skb->protocol = eh->h_proto;
+ skb->priority = cfg80211_classify8021d(skb, NULL);
+ if (is_unicast_ether_addr(eh->h_dest)) {
+ if (brcmf_find_sta(ifp, eh->h_dest)) {
+ /* determine the priority */
+ if (skb->priority == 0 || skb->priority > 7) {
+ skb->priority =
+ cfg80211_classify8021d(skb,
+ NULL);
+ }
+ brcmf_proto_tx_queue_data(ifp->drvr,
+ ifp->ifidx, skb);
+ return;
+ }
+ } else {
+ cpskb = pskb_copy(skb, GFP_ATOMIC);
+ if (cpskb) {
+ brcmf_proto_tx_queue_data(ifp->drvr,
+ ifp->ifidx,
+ cpskb);
+ } else {
+ brcmf_err("Unable to do skb copy\n");
+ }
+ }
+ }
skb->protocol = eth_type_trans(skb, ifp->ndev);
brcmf_netif_rx(ifp, skb, false);
}