summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorArend van Spriel <arend@broadcom.com>2013-04-03 12:40:31 +0200
committerJohn W. Linville <linville@tuxdriver.com>2013-04-03 15:07:04 -0400
commitce814c1bb484f0efd221a05c936eb942657cebb5 (patch)
tree3999747029843fd68a48e1f94e44a10fd0676de0 /drivers
parentbb8c8063f82ce3eb7b44772202ca944f92ac39f5 (diff)
downloadlwn-ce814c1bb484f0efd221a05c936eb942657cebb5.tar.gz
lwn-ce814c1bb484f0efd221a05c936eb942657cebb5.zip
brcmfmac: handle firmware signal for updating mac descriptor info
Firmware can signal the driver to allocate descriptor info for a given mac address, which will be used for flow control and host queueing. Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Reviewed-by: Hante Meuleman <meuleman@broadcom.com> Reviewed-by: Piotr Haber <phaber@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c6
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h1
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c120
3 files changed, 112 insertions, 15 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
index ac792499b46a..f0949b1e3073 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -141,11 +141,13 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data,
"header_pulls: %u\n"
"header_only_pkt: %u\n"
"tlv_parse_failed: %u\n"
- "tlv_invalid_type: %u\n",
+ "tlv_invalid_type: %u\n"
+ "mac_update_fails: %u\n",
fwstats->header_pulls,
fwstats->header_only_pkt,
fwstats->tlv_parse_failed,
- fwstats->tlv_invalid_type);
+ fwstats->tlv_invalid_type,
+ fwstats->mac_update_failed);
return simple_read_from_buffer(data, count, ppos, buf, res);
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index 4bc646bde16f..371ae5b546af 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -137,6 +137,7 @@ struct brcmf_fws_stats {
u32 tlv_invalid_type;
u32 header_only_pkt;
u32 header_pulls;
+ u32 mac_update_failed;
};
struct brcmf_pub;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
index 7ceaceba1845..85fd0ecd720e 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
@@ -141,6 +141,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
* struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface
*
* @occupied: slot is in use.
+ * @mac_handle: handle for mac entry determined by firmware.
* @interface_id: interface index.
* @state: current state.
* @ac_bitmap: ac queue bitmap.
@@ -150,6 +151,7 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id)
*/
struct brcmf_fws_mac_descriptor {
u8 occupied;
+ u8 mac_handle;
u8 interface_id;
u8 state;
u8 ac_bitmap;
@@ -194,6 +196,7 @@ struct brcmf_fws_mac_descriptor {
struct brcmf_fws_info {
struct brcmf_pub *drvr;
struct brcmf_fws_stats stats;
+ struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE];
};
/**
@@ -217,12 +220,107 @@ static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws,
}
#undef BRCMF_FWS_TLV_DEF
+static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc,
+ u8 *addr, u8 ifidx)
+{
+ brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u\n", addr, ifidx);
+ desc->occupied = 1;
+ desc->state = BRCMF_FWS_STATE_OPEN;
+ desc->requested_credit = 0;
+ /* depending on use may need ifp->bssidx instead */
+ desc->interface_id = ifidx;
+ desc->ac_bitmap = 0xff; /* update this when handling APSD */
+ memcpy(&desc->ea[0], addr, ETH_ALEN);
+}
+
+static
+void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc)
+{
+ brcmf_dbg(TRACE,
+ "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id);
+ desc->occupied = 0;
+ desc->state = BRCMF_FWS_STATE_CLOSE;
+ desc->requested_credit = 0;
+}
+
+static struct brcmf_fws_mac_descriptor *
+brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea)
+{
+ struct brcmf_fws_mac_descriptor *entry;
+ int i;
+
+ brcmf_dbg(TRACE, "enter: ea=%pM\n", ea);
+ if (ea == NULL)
+ return ERR_PTR(-EINVAL);
+
+ entry = &fws->nodes[0];
+ for (i = 0; i < ARRAY_SIZE(fws->nodes); i++) {
+ if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN))
+ return entry;
+ entry++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi)
{
brcmf_dbg(CTL, "rssi %d\n", rssi);
return 0;
}
+static
+int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
+{
+ struct brcmf_fws_mac_descriptor *entry, *existing;
+ u8 mac_handle;
+ u8 ifidx;
+ u8 *addr;
+
+ mac_handle = *data++;
+ ifidx = *data++;
+ addr = data;
+
+ entry = &fws->nodes[mac_handle & 0x1F];
+ if (type == BRCMF_FWS_TYPE_MACDESC_DEL) {
+ brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx);
+ if (entry->occupied) {
+ entry->occupied = 0;
+ entry->state = BRCMF_FWS_STATE_CLOSE;
+ entry->requested_credit = 0;
+ } else {
+ fws->stats.mac_update_failed++;
+ }
+ return 0;
+ }
+
+ brcmf_dbg(TRACE, "add mac %pM idx %d\n", addr, ifidx);
+ existing = brcmf_fws_mac_descriptor_lookup(fws, addr);
+ if (IS_ERR(existing)) {
+ if (!entry->occupied) {
+ entry->mac_handle = mac_handle;
+ brcmf_fws_init_mac_descriptor(entry, addr, ifidx);
+ brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ BRCMF_FWS_PSQ_LEN);
+ } else {
+ fws->stats.mac_update_failed++;
+ }
+ } else {
+ if (entry != existing) {
+ brcmf_dbg(TRACE, "relocate mac\n");
+ memcpy(entry, existing,
+ offsetof(struct brcmf_fws_mac_descriptor, psq));
+ entry->mac_handle = mac_handle;
+ brcmf_fws_clear_mac_descriptor(existing);
+ } else {
+ brcmf_dbg(TRACE, "use existing\n");
+ WARN_ON(entry->mac_handle != mac_handle);
+ /* TODO: what should we do here: continue, reinit, .. */
+ }
+ }
+ return 0;
+}
+
static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data)
{
__le32 timestamp;
@@ -250,11 +348,13 @@ do { \
int brcmf_fws_init(struct brcmf_pub *drvr)
{
- u32 tlv;
+ u32 tlv = 0;
int rc;
/* enable rssi signals */
- tlv = drvr->fw_signals ? BRCMF_FWS_FLAGS_RSSI_SIGNALS : 0;
+ if (drvr->fw_signals)
+ tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS |
+ BRCMF_FWS_FLAGS_XONXOFF_SIGNALS;
spin_lock_init(&drvr->fws_spinlock);
@@ -361,8 +461,10 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET:
case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
case BRCMF_FWS_TYPE_COMP_TXSTATUS:
+ break;
case BRCMF_FWS_TYPE_MACDESC_ADD:
case BRCMF_FWS_TYPE_MACDESC_DEL:
+ brcmf_fws_macdesc_indicate(fws, type, data);
break;
case BRCMF_FWS_TYPE_RSSI:
brcmf_fws_rssi_indicate(fws, *data);
@@ -404,13 +506,7 @@ void brcmf_fws_reset_interface(struct brcmf_if *ifp)
if (!entry)
return;
- entry->occupied = 1;
- entry->state = BRCMF_FWS_STATE_OPEN;
- entry->requested_credit = 0;
- /* depending on use may need ifp->bssidx instead */
- entry->interface_id = ifp->ifidx;
- entry->ac_bitmap = 0xff; /* update this when handling APSD */
- memcpy(&entry->ea[0], ifp->mac_addr, ETH_ALEN);
+ brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
}
void brcmf_fws_add_interface(struct brcmf_if *ifp)
@@ -425,7 +521,7 @@ void brcmf_fws_add_interface(struct brcmf_if *ifp)
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (entry) {
ifp->fws_desc = entry;
- brcmf_fws_reset_interface(ifp);
+ brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx);
brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);
} else {
@@ -442,8 +538,6 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
return;
ifp->fws_desc = NULL;
- entry->occupied = 0;
- entry->state = BRCMF_FWS_STATE_CLOSE;
- entry->requested_credit = 0;
+ brcmf_fws_clear_mac_descriptor(entry);
kfree(entry);
}