brcmfmac: Add TDLS support to msgbuf.
authorHante Meuleman <meuleman@broadcom.com>
Wed, 30 Jul 2014 11:20:07 +0000 (13:20 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 31 Jul 2014 17:45:29 +0000 (13:45 -0400)
TDLS connections require dedicated flowrings. This patches adds
TDLS event handling and flowring creation/deletion based on these
events.

Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Daniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
drivers/net/wireless/brcm80211/brcmfmac/flowring.c
drivers/net/wireless/brcm80211/brcmfmac/flowring.h
drivers/net/wireless/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
drivers/net/wireless/brcm80211/brcmfmac/proto.c
drivers/net/wireless/brcm80211/brcmfmac/proto.h
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c

index 10b48c2c4b36ab8a4b58e53b9a74116e58b059e7..a159ff3427de9fa7c3e42c508bc6a0c8930e5cdb 100644 (file)
@@ -349,6 +349,12 @@ brcmf_proto_bcdc_delete_peer(struct brcmf_pub *drvr, int ifidx,
 {
 }
 
+static void
+brcmf_proto_bcdc_add_tdls_peer(struct brcmf_pub *drvr, int ifidx,
+                              u8 peer[ETH_ALEN])
+{
+}
+
 int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
 {
        struct brcmf_bcdc *bcdc;
@@ -369,6 +375,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
        drvr->proto->txdata = brcmf_proto_bcdc_txdata;
        drvr->proto->configure_addr_mode = brcmf_proto_bcdc_configure_addr_mode;
        drvr->proto->delete_peer = brcmf_proto_bcdc_delete_peer;
+       drvr->proto->add_tdls_peer = brcmf_proto_bcdc_add_tdls_peer;
        drvr->proto->pd = bcdc;
 
        drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
index 07009046fda50cc3d74d97f6d68d9d47fbb3f0f7..a1016b811284cd748af4537fa95fe3f9297fb003 100644 (file)
@@ -49,6 +49,23 @@ static const u8 brcmf_flowring_prio2fifo[] = {
 };
 
 
+static bool
+brcmf_flowring_is_tdls_mac(struct brcmf_flowring *flow, u8 mac[ETH_ALEN])
+{
+       struct brcmf_flowring_tdls_entry *search;
+
+       search = flow->tdls_entry;
+
+       while (search) {
+               if (memcmp(search->mac, mac, ETH_ALEN) == 0)
+                       return true;
+               search = search->next;
+       }
+
+       return false;
+}
+
+
 u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
                          u8 prio, u8 ifidx)
 {
@@ -67,6 +84,10 @@ u32 brcmf_flowring_lookup(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
                mac = (u8 *)ALLFFMAC;
                fifo = 0;
        }
+       if ((sta) && (flow->tdls_active) &&
+           (brcmf_flowring_is_tdls_mac(flow, da))) {
+               sta = false;
+       }
        hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
                          BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
        found = false;
@@ -106,15 +127,17 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN],
                mac = (u8 *)ALLFFMAC;
                fifo = 0;
        }
+       if ((sta) && (flow->tdls_active) &&
+           (brcmf_flowring_is_tdls_mac(flow, da))) {
+               sta = false;
+       }
        hash_idx =  sta ? BRCMF_FLOWRING_HASH_STA(fifo, ifidx) :
                          BRCMF_FLOWRING_HASH_AP(mac, fifo, ifidx);
        found = false;
        hash = flow->hash;
        for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
-               if (((sta) &&
-                    (hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX)) ||
-                   ((!sta) &&
-                    (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0))) {
+               if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) &&
+                   (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) {
                        found = true;
                        break;
                }
@@ -356,12 +379,21 @@ void brcmf_flowring_detach(struct brcmf_flowring *flow)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
        struct brcmf_pub *drvr = bus_if->drvr;
+       struct brcmf_flowring_tdls_entry *search;
+       struct brcmf_flowring_tdls_entry *remove;
        u8 flowid;
 
        for (flowid = 0; flowid < flow->nrofrings; flowid++) {
                if (flow->rings[flowid])
                        brcmf_msgbuf_delete_flowring(drvr, flowid);
        }
+
+       search = flow->tdls_entry;
+       while (search) {
+               remove = search;
+               search = search->next;
+               kfree(remove);
+       }
        kfree(flow->rings);
        kfree(flow);
 }
@@ -396,11 +428,25 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,
        struct brcmf_bus *bus_if = dev_get_drvdata(flow->dev);
        struct brcmf_pub *drvr = bus_if->drvr;
        struct brcmf_flowring_hash *hash;
+       struct brcmf_flowring_tdls_entry *prev;
+       struct brcmf_flowring_tdls_entry *search;
        u32 i;
        u8 flowid;
        bool sta;
 
        sta = (flow->addr_mode[ifidx] == ADDR_INDIRECT);
+
+       search = flow->tdls_entry;
+       prev = NULL;
+       while (search) {
+               if (memcmp(search->mac, peer, ETH_ALEN) == 0) {
+                       sta = false;
+                       break;
+               }
+               prev = search;
+               search = search->next;
+       }
+
        hash = flow->hash;
        for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) {
                if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) &&
@@ -412,4 +458,44 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,
                        }
                }
        }
+
+       if (search) {
+               if (prev)
+                       prev->next = search->next;
+               else
+                       flow->tdls_entry = search->next;
+               kfree(search);
+               if (flow->tdls_entry == NULL)
+                       flow->tdls_active = false;
+       }
+}
+
+
+void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx,
+                                 u8 peer[ETH_ALEN])
+{
+       struct brcmf_flowring_tdls_entry *tdls_entry;
+       struct brcmf_flowring_tdls_entry *search;
+
+       tdls_entry = kzalloc(sizeof(*tdls_entry), GFP_ATOMIC);
+       if (tdls_entry == NULL)
+               return;
+
+       memcpy(tdls_entry->mac, peer, ETH_ALEN);
+       tdls_entry->next = NULL;
+       if (flow->tdls_entry == NULL) {
+               flow->tdls_entry = tdls_entry;
+       } else {
+               search = flow->tdls_entry;
+               if (memcmp(search->mac, peer, ETH_ALEN) == 0)
+                       return;
+               while (search->next) {
+                       search = search->next;
+                       if (memcmp(search->mac, peer, ETH_ALEN) == 0)
+                               return;
+               }
+               search->next = tdls_entry;
+       }
+
+       flow->tdls_active = true;
 }
index cb9644ca6ecec5cd0456abd4e43cc65cd6afd71b..a34cd394c616c0a11f861c06f18030d3c7129133 100644 (file)
@@ -40,6 +40,11 @@ struct brcmf_flowring_ring {
        struct sk_buff_head skblist;
 };
 
+struct brcmf_flowring_tdls_entry {
+       u8 mac[ETH_ALEN];
+       struct brcmf_flowring_tdls_entry *next;
+};
+
 struct brcmf_flowring {
        struct device *dev;
        struct brcmf_flowring_hash hash[BRCMF_FLOWRING_HASHSIZE];
@@ -47,6 +52,8 @@ struct brcmf_flowring {
        spinlock_t block_lock;
        enum proto_addr_mode addr_mode[BRCMF_MAX_IFS];
        u16 nrofrings;
+       bool tdls_active;
+       struct brcmf_flowring_tdls_entry *tdls_entry;
 };
 
 
@@ -70,6 +77,8 @@ void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx,
                                        enum proto_addr_mode addr_mode);
 void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx,
                                u8 peer[ETH_ALEN]);
+void brcmf_flowring_add_tdls_peer(struct brcmf_flowring *flow, int ifidx,
+                                 u8 peer[ETH_ALEN]);
 
 
 #endif /* BRCMFMAC_FLOWRING_H */
index fad77dd2a3a543f511de0371eaf94ed4a1fc34f1..4f1daabc551b0824c4f72bc22faed58fd47a67d5 100644 (file)
@@ -293,7 +293,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
                        goto event_free;
                }
 
-               ifp = drvr->iflist[emsg.bsscfgidx];
+               if ((event->code == BRCMF_E_TDLS_PEER_EVENT) &&
+                   (emsg.bsscfgidx == 1))
+                       ifp = drvr->iflist[0];
+               else
+                       ifp = drvr->iflist[emsg.bsscfgidx];
                err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg,
                                                    event->data);
                if (err) {
index 51b53a73d07464c0bb9b8b5a6f551e20314c37ce..dd20b1862d44b94121076e6287e4f704c9f1daee 100644 (file)
@@ -102,6 +102,7 @@ struct brcmf_event;
        BRCMF_ENUM_DEF(DCS_REQUEST, 73) \
        BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
        BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
+       BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
        BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
        BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128)
 
@@ -155,6 +156,10 @@ enum brcmf_fweh_event_code {
 #define BRCMF_E_REASON_TSPEC_REJECTED          7
 #define BRCMF_E_REASON_BETTER_AP               8
 
+#define BRCMF_E_REASON_TDLS_PEER_DISCOVERED    0
+#define BRCMF_E_REASON_TDLS_PEER_CONNECTED     1
+#define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED  2
+
 /* action field values for brcmf_ifevent */
 #define BRCMF_E_IF_ADD                         1
 #define BRCMF_E_IF_DEL                         2
index c7a1c59ba6c3e3510dbd612701ec4f592bd70d7c..535c7eb01b3a423deb264c8db55a2858306b67ee 100644 (file)
@@ -751,6 +751,15 @@ brcmf_msgbuf_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
 }
 
 
+static void
+brcmf_msgbuf_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+       struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
+
+       brcmf_flowring_add_tdls_peer(msgbuf->flow, ifidx, peer);
+}
+
+
 static void
 brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf)
 {
@@ -1298,6 +1307,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr)
        drvr->proto->txdata = brcmf_msgbuf_txdata;
        drvr->proto->configure_addr_mode = brcmf_msgbuf_configure_addr_mode;
        drvr->proto->delete_peer = brcmf_msgbuf_delete_peer;
+       drvr->proto->add_tdls_peer = brcmf_msgbuf_add_tdls_peer;
        drvr->proto->pd = msgbuf;
 
        init_waitqueue_head(&msgbuf->ioctl_resp_wait);
index 44b1cb466d4e6f3ad5b507f75e2e1710952c22fb..62b940723339f825a2ded722e19ec39c8e644dee 100644 (file)
@@ -54,7 +54,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr)
        if ((proto->txdata == NULL) || (proto->hdrpull == NULL) ||
            (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL) ||
            (proto->configure_addr_mode == NULL) ||
-           (proto->delete_peer == NULL)) {
+           (proto->delete_peer == NULL) || (proto->add_tdls_peer == NULL)) {
                brcmf_err("Not all proto handlers have been installed\n");
                goto fail;
        }
index 55942e3561a3ff12db66327aedf514acbdd73968..971172ff686c80fc16b61ab083372eab783c0ef3 100644 (file)
@@ -36,6 +36,8 @@ struct brcmf_proto {
                                    enum proto_addr_mode addr_mode);
        void (*delete_peer)(struct brcmf_pub *drvr, int ifidx,
                            u8 peer[ETH_ALEN]);
+       void (*add_tdls_peer)(struct brcmf_pub *drvr, int ifidx,
+                             u8 peer[ETH_ALEN]);
        void *pd;
 };
 
@@ -74,6 +76,11 @@ brcmf_proto_delete_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
 {
        drvr->proto->delete_peer(drvr, ifidx, peer);
 }
+static inline void
+brcmf_proto_add_tdls_peer(struct brcmf_pub *drvr, int ifidx, u8 peer[ETH_ALEN])
+{
+       drvr->proto->add_tdls_peer(drvr, ifidx, peer);
+}
 
 
 #endif /* BRCMFMAC_PROTO_H */
index a0e555a9f5f7252db5e455701b3933d61fcba439..02fe706fc9ec8cbeed88baacc377c33e6892dd6d 100644 (file)
@@ -4155,6 +4155,27 @@ static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
        clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
 }
 
+static s32
+brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
+                            const struct brcmf_event_msg *e, void *data)
+{
+       switch (e->reason) {
+       case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
+               brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
+               break;
+       case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
+               brcmf_dbg(TRACE, "TDLS Peer Connected\n");
+               brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+               break;
+       case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
+               brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
+               brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
+               break;
+       }
+
+       return 0;
+}
+
 static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
 {
        int ret;
@@ -5691,6 +5712,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
        if (err) {
                brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
                wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+       } else {
+               brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
+                                   brcmf_notify_tdls_peer_event);
        }
 
        return cfg;