This series contains couple of fixes for brcmsmac that were reported
on the wireless list. Also exposed some brcmfmac internals through
debugfs.
Arend van Spriel (5):
brcmsmac: remove brcms_set_hint() function
brcmsmac: fix smatch warning found in ampdu.c
brcmfmac: add debugfs helper functions
brcmfmac: expose sdio internal counters in debugfs
brcmfmac: introduce checkdied debugfs functionality
drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 3 +
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 126 +++++
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 59 +++
.../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 7 +
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 499 ++++++++++++++++----
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 5 +-
.../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 +-
8 files changed, 608 insertions(+), 107 deletions(-)
create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
--
1.7.9.5
This patch adds debugfs support to brcmfmac. It provide helper functions
to setup the debugfs folder structure for the driver, which has following
hierarchy:
<debugfs_mount>/brcmfmac/<dev_name>/
ie.: /sys/kernel/debug/brcmfmac/mmc0:0001:2/
The new source file provides functions to create and remove the two
folders and a function to retrieve the device-specific folder so files
can be created in it.
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +
drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 3 +
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 63 ++++++++++++++++++++
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 23 +++++++
.../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 7 +++
5 files changed, 98 insertions(+)
create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index abb4803..9d5170b 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -34,3 +34,5 @@ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
sdio_chip.o
brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
usb.o
+brcmfmac-$(CONFIG_BRCMDBG) += \
+ dhd_dbg.o
\ No newline at end of file
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 9f63701..a11fe54 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -613,6 +613,9 @@ struct brcmf_pub {
struct work_struct multicast_work;
u8 macvalue[ETH_ALEN];
atomic_t pend_8021x_cnt;
+#ifdef DEBUG
+ struct dentry *dbgfs_dir;
+#endif
};
struct brcmf_if_event {
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
new file mode 100644
index 0000000..0a7a3d5
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2012 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/debugfs.h>
+#include <linux/if_ether.h>
+#include <linux/if.h>
+#include <linux/ieee80211.h>
+
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+
+static struct dentry *root_folder;
+
+void brcmf_debugfs_init(void)
+{
+ root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (IS_ERR(root_folder))
+ root_folder = NULL;
+}
+
+void brcmf_debugfs_exit(void)
+{
+ if (!root_folder)
+ return;
+
+ debugfs_remove_recursive(root_folder);
+ root_folder = NULL;
+}
+
+int brcmf_debugfs_attach(struct brcmf_pub *drvr)
+{
+ if (!root_folder)
+ return -ENODEV;
+
+ drvr->dbgfs_dir = debugfs_create_dir(dev_name(drvr->dev), root_folder);
+ return PTR_RET(drvr->dbgfs_dir);
+}
+
+void brcmf_debugfs_detach(struct brcmf_pub *drvr)
+{
+ if (!IS_ERR_OR_NULL(drvr->dbgfs_dir))
+ debugfs_remove_recursive(drvr->dbgfs_dir);
+}
+
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
+{
+ return drvr->dbgfs_dir;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index a2c4576..0efb226 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -76,4 +76,27 @@ do { \
extern int brcmf_msg_level;
+struct brcmf_pub;
+#ifdef DEBUG
+void brcmf_debugfs_init(void);
+void brcmf_debugfs_exit(void);
+int brcmf_debugfs_attach(struct brcmf_pub *drvr);
+void brcmf_debugfs_detach(struct brcmf_pub *drvr);
+struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
+#else
+static inline void brcmf_debugfs_init(void)
+{
+}
+static inline void brcmf_debugfs_exit(void)
+{
+}
+static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr)
+{
+ return 0;
+}
+static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr)
+{
+}
+#endif
+
#endif /* _BRCMF_DBG_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
index 8933f9b..01cf6c0 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -1007,6 +1007,9 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev)
drvr->bus_if->drvr = drvr;
drvr->dev = dev;
+ /* create device debugfs folder */
+ brcmf_debugfs_attach(drvr);
+
/* Attach and link in the protocol */
ret = brcmf_proto_attach(drvr);
if (ret != 0) {
@@ -1123,6 +1126,7 @@ void brcmf_detach(struct device *dev)
brcmf_proto_detach(drvr);
}
+ brcmf_debugfs_detach(drvr);
bus_if->drvr = NULL;
kfree(drvr);
}
@@ -1192,6 +1196,8 @@ exit:
static void brcmf_driver_init(struct work_struct *work)
{
+ brcmf_debugfs_init();
+
#ifdef CONFIG_BRCMFMAC_SDIO
brcmf_sdio_init();
#endif
@@ -1219,6 +1225,7 @@ static void __exit brcmfmac_module_exit(void)
#ifdef CONFIG_BRCMFMAC_USB
brcmf_usb_exit();
#endif
+ brcmf_debugfs_exit();
}
module_init(brcmfmac_module_init);
--
1.7.9.5
On Sun, Jun 10, 2012 at 06:57:29AM +0200, Arend van Spriel wrote:
> On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
> > On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
> >> The checkdied functionality provides useful information for analyzing
> >> firmware crashes. By exposing this information to a debugfs file users
> >> can easily provide its content in bug reports. The functionality is
> >> available only when CONFIG_BRCMDBG is selected.
> >>
> >> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
> >> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
> >> Signed-off-by: Arend van Spriel <[email protected]>
> >> ---
> >> drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 332 +++++++++++++++++++-
> >> 1 file changed, 331 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> >> index a07fb01..5a33b42 100644
> >> --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> >> +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> >> @@ -31,6 +31,7 @@
> >> #include <linux/firmware.h>
> >> #include <linux/module.h>
> >> #include <linux/bcma/bcma.h>
> >> +#include <linux/debugfs.h>
> >> #include <asm/unaligned.h>
> >> #include <defs.h>
> >> #include <brcmu_wifi.h>
> >> @@ -48,6 +49,9 @@
> >>
> >> #define CBUF_LEN (128)
> >>
> >> +/* Device console log buffer state */
> >> +#define CONSOLE_BUFFER_MAX 2024
> >
> > Just out of curiosity; what is the significance of this number?
>
> It is used in the code fragment below. I have to admit the comment is
> not explaining its significance.
>
> >> + /* allocate buffer for console data */
> >> + if (console_size <= CONSOLE_BUFFER_MAX)
> >> + conbuf = kzalloc(console_size+1, GFP_ATOMIC);
> >> +
> >> + if (!conbuf)
> >> + return -ENOMEM;
>
> Probably would be better to use vmalloc() here or at least use
> GFP_KERNEL here.
>
> John, any recommendations?
vmalloc seems reasonable...
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.
On 06/10/2012 07:14 AM, G?bor Stefanik wrote:
> On Sun, Jun 10, 2012 at 6:57 AM, Arend van Spriel <[email protected]> wrote:
>> On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
>>> On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
>>>>
>>>> +/* Device console log buffer state */
>>>> +#define CONSOLE_BUFFER_MAX 2024
>>>
>>> Just out of curiosity; what is the significance of this number?
>>
>> It is used in the code fragment below. I have to admit the comment is
>> not explaining its significance.
>
> But why this particular number? Did you mean 2048?
>
Not sure. Maybe the console info has 24 bytes overhead leaving 2024
bytes of raw buffer data, but I can only guess.
Gr. AvS
The function brcms_set_hint() does not add any functionality
so regulatory_hint() can be called directly. The error value
has been removed from the message when regulatory_hint() fails.
Reported-by: Seth Forshee <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
.../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 50f92a0..341e06a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -721,14 +721,6 @@ static const struct ieee80211_ops brcms_ops = {
.flush = brcms_ops_flush,
};
-/*
- * is called in brcms_bcma_probe() context, therefore no locking required.
- */
-static int brcms_set_hint(struct brcms_info *wl, char *abbrev)
-{
- return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
-}
-
void brcms_dpc(unsigned long data)
{
struct brcms_info *wl;
@@ -1068,9 +1060,9 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev)
wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
"%d\n", __func__, err);
- if (wl->pub->srom_ccode[0] && brcms_set_hint(wl, wl->pub->srom_ccode))
- wiphy_err(wl->wiphy, "%s: regulatory_hint failed, status %d\n",
- __func__, err);
+ if (wl->pub->srom_ccode[0] &&
+ regulatory_hint(wl->wiphy, wl->pub->srom_ccode))
+ wiphy_err(wl->wiphy, "%s: regulatory hint failed\n", __func__);
n_adapters_found++;
return wl;
--
1.7.9.5
The checkdied functionality provides useful information for analyzing
firmware crashes. By exposing this information to a debugfs file users
can easily provide its content in bug reports. The functionality is
available only when CONFIG_BRCMDBG is selected.
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 332 +++++++++++++++++++-
1 file changed, 331 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index a07fb01..5a33b42 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -31,6 +31,7 @@
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/bcma/bcma.h>
+#include <linux/debugfs.h>
#include <asm/unaligned.h>
#include <defs.h>
#include <brcmu_wifi.h>
@@ -48,6 +49,9 @@
#define CBUF_LEN (128)
+/* Device console log buffer state */
+#define CONSOLE_BUFFER_MAX 2024
+
struct rte_log_le {
__le32 buf; /* Can't be pointer on (64-bit) hosts */
__le32 buf_size;
@@ -281,7 +285,7 @@ struct rte_console {
* Shared structure between dongle and the host.
* The structure contains pointers to trap or assert information.
*/
-#define SDPCM_SHARED_VERSION 0x0002
+#define SDPCM_SHARED_VERSION 0x0003
#define SDPCM_SHARED_VERSION_MASK 0x00FF
#define SDPCM_SHARED_ASSERT_BUILT 0x0100
#define SDPCM_SHARED_ASSERT 0x0200
@@ -428,6 +432,29 @@ struct brcmf_console {
u8 *buf; /* Log buffer (host copy) */
uint last; /* Last buffer read index */
};
+
+struct brcmf_trap_info {
+ __le32 type;
+ __le32 epc;
+ __le32 cpsr;
+ __le32 spsr;
+ __le32 r0; /* a1 */
+ __le32 r1; /* a2 */
+ __le32 r2; /* a3 */
+ __le32 r3; /* a4 */
+ __le32 r4; /* v1 */
+ __le32 r5; /* v2 */
+ __le32 r6; /* v3 */
+ __le32 r7; /* v4 */
+ __le32 r8; /* v5 */
+ __le32 r9; /* sb/v6 */
+ __le32 r10; /* sl/v7 */
+ __le32 r11; /* fp/v8 */
+ __le32 r12; /* ip */
+ __le32 r13; /* sp */
+ __le32 r14; /* lr */
+ __le32 pc; /* r15 */
+};
#endif /* DEBUG */
struct sdpcm_shared {
@@ -439,6 +466,7 @@ struct sdpcm_shared {
u32 console_addr; /* Address of struct rte_console */
u32 msgtrace_addr;
u8 tag[32];
+ u32 brpt_addr;
};
struct sdpcm_shared_le {
@@ -450,6 +478,7 @@ struct sdpcm_shared_le {
__le32 console_addr; /* Address of struct rte_console */
__le32 msgtrace_addr;
u8 tag[32];
+ __le32 brpt_addr;
};
@@ -2953,13 +2982,311 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
}
#ifdef DEBUG
+static inline bool brcmf_sdio_valid_shared_address(u32 addr)
+{
+ return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
+}
+
+static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh)
+{
+ u32 addr;
+ int rv;
+ u32 shaddr = 0;
+ struct sdpcm_shared_le sh_le;
+ __le32 addr_le;
+
+ shaddr = bus->ramsize - 4;
+
+ /*
+ * Read last word in socram to determine
+ * address of sdpcm_shared structure
+ */
+ rv = brcmf_sdbrcm_membytes(bus, false, shaddr,
+ (u8 *)&addr_le, 4);
+ if (rv < 0)
+ return rv;
+
+ addr = le32_to_cpu(addr_le);
+
+ brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
+
+ /*
+ * Check if addr is valid.
+ * NVRAM length at the end of memory should have been overwritten.
+ */
+ if (!brcmf_sdio_valid_shared_address(addr)) {
+ brcmf_dbg(ERROR, "invalid sdpcm_shared address 0x%08X\n",
+ addr);
+ return -EINVAL;
+ }
+
+ /* Read hndrte_shared structure */
+ rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le,
+ sizeof(struct sdpcm_shared_le));
+ if (rv < 0)
+ return rv;
+
+ /* Endianness */
+ sh->flags = le32_to_cpu(sh_le.flags);
+ sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
+ sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
+ sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
+ sh->assert_line = le32_to_cpu(sh_le.assert_line);
+ sh->console_addr = le32_to_cpu(sh_le.console_addr);
+ sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
+
+ if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
+ brcmf_dbg(ERROR,
+ "sdpcm_shared version mismatch: dhd %d dongle %d\n",
+ SDPCM_SHARED_VERSION,
+ sh->flags & SDPCM_SHARED_VERSION_MASK);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh, char __user *data,
+ size_t count)
+{
+ u32 addr, console_ptr, console_size, console_index;
+ char *conbuf = NULL;
+ __le32 sh_val;
+ int rv;
+ loff_t pos = 0;
+ int nbytes = 0;
+
+ /* obtain console information from device memory */
+ addr = sh->console_addr + offsetof(struct rte_console, log_le);
+ rv = brcmf_sdbrcm_membytes(bus, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_ptr = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
+ rv = brcmf_sdbrcm_membytes(bus, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_size = le32_to_cpu(sh_val);
+
+ addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
+ rv = brcmf_sdbrcm_membytes(bus, false, addr,
+ (u8 *)&sh_val, sizeof(u32));
+ if (rv < 0)
+ return rv;
+ console_index = le32_to_cpu(sh_val);
+
+ /* allocate buffer for console data */
+ if (console_size <= CONSOLE_BUFFER_MAX)
+ conbuf = kzalloc(console_size+1, GFP_ATOMIC);
+
+ if (!conbuf)
+ return -ENOMEM;
+
+ /* obtain the console data from device */
+ conbuf[console_size] = '\0';
+ rv = brcmf_sdbrcm_membytes(bus, false, console_ptr, (u8 *)conbuf,
+ console_size);
+ if (rv < 0)
+ goto done;
+
+ rv = simple_read_from_buffer(data, count, &pos,
+ conbuf + console_index,
+ console_size - console_index);
+ if (rv < 0)
+ goto done;
+
+ nbytes = rv;
+ if (console_index > 0) {
+ pos = 0;
+ rv = simple_read_from_buffer(data+nbytes, count, &pos,
+ conbuf, console_index - 1);
+ if (rv < 0)
+ goto done;
+ rv += nbytes;
+ }
+done:
+ kfree(conbuf);
+ return rv;
+}
+
+static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
+ char __user *data, size_t count)
+{
+ int error, res;
+ char buf[350];
+ struct brcmf_trap_info tr;
+ int nbytes;
+ loff_t pos = 0;
+
+ if ((sh->flags & SDPCM_SHARED_TRAP) == 0)
+ return 0;
+
+ error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr,
+ sizeof(struct brcmf_trap_info));
+ if (error < 0)
+ return error;
+
+ nbytes = brcmf_sdio_dump_console(bus, sh, data, count);
+ if (nbytes < 0)
+ return nbytes;
+
+ res = scnprintf(buf, sizeof(buf),
+ "dongle trap info: type 0x%x @ epc 0x%08x\n"
+ " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
+ " lr 0x%08x pc 0x%08x offset 0x%x\n"
+ " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n"
+ " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n",
+ le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
+ le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
+ le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
+ le32_to_cpu(tr.pc), le32_to_cpu(sh->trap_addr),
+ le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
+ le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
+ le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
+ le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
+
+ error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res);
+ if (error < 0)
+ return error;
+
+ nbytes += error;
+ return nbytes;
+}
+
+static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
+ struct sdpcm_shared *sh, char __user *data,
+ size_t count)
+{
+ int error = 0;
+ char buf[200];
+ char file[80] = "?";
+ char expr[80] = "<???>";
+ int res;
+ loff_t pos = 0;
+
+ if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ return 0;
+ } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
+ brcmf_dbg(INFO, "no assert in dongle\n");
+ return 0;
+ }
+
+ if (sh->assert_file_addr != 0) {
+ error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr,
+ (u8 *)file, 80);
+ if (error < 0)
+ return error;
+ }
+ if (sh->assert_exp_addr != 0) {
+ error = brcmf_sdbrcm_membytes(bus, false, sh->assert_exp_addr,
+ (u8 *)expr, 80);
+ if (error < 0)
+ return error;
+ }
+
+ res = scnprintf(buf, sizeof(buf),
+ "dongle assert: %s:%d: assert(%s)\n",
+ file, sh->assert_line, expr);
+ return simple_read_from_buffer(data, count, &pos, buf, res);
+}
+
+static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
+{
+ int error;
+ struct sdpcm_shared sh;
+
+ down(&bus->sdsem);
+ error = brcmf_sdio_readshared(bus, &sh);
+ up(&bus->sdsem);
+
+ if (error < 0)
+ return error;
+
+ if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
+ brcmf_dbg(INFO, "firmware not built with -assert\n");
+ else if (sh.flags & SDPCM_SHARED_ASSERT)
+ brcmf_dbg(ERROR, "assertion in dongle\n");
+
+ if (sh.flags & SDPCM_SHARED_TRAP)
+ brcmf_dbg(ERROR, "firmware trap in dongle\n");
+
+ return 0;
+}
+
+static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ int error = 0;
+ struct sdpcm_shared sh;
+ int nbytes = 0;
+ loff_t pos = *ppos;
+
+ if (pos != 0)
+ return 0;
+
+ down(&bus->sdsem);
+ error = brcmf_sdio_readshared(bus, &sh);
+ if (error < 0)
+ goto done;
+
+ error = brcmf_sdio_assert_info(bus, &sh, data, count);
+ if (error < 0)
+ goto done;
+
+ nbytes = error;
+ error = brcmf_sdio_trap_info(bus, &sh, data, count);
+ if (error < 0)
+ goto done;
+
+ error += nbytes;
+ *ppos += error;
+done:
+ up(&bus->sdsem);
+ return error;
+}
+
+static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct brcmf_sdio *bus = f->private_data;
+ int res;
+
+ res = brcmf_sdbrcm_died_dump(bus, data, count, ppos);
+ if (res > 0)
+ *ppos += res;
+ return (ssize_t)res;
+}
+
+static const struct file_operations brcmf_sdio_forensic_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = brcmf_sdio_forensic_read
+};
+
static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
{
struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
+ struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
+ if (IS_ERR_OR_NULL(dentry))
+ return;
+
+ debugfs_create_file("forensics", S_IRUGO, dentry, bus,
+ &brcmf_sdio_forensic_ops);
brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
}
#else
+static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
+{
+ return 0;
+}
+
static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
{
}
@@ -2991,11 +3318,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
rxlen, msglen);
} else if (timeleft == 0) {
brcmf_dbg(ERROR, "resumed on timeout\n");
+ brcmf_sdbrcm_checkdied(bus);
} else if (pending) {
brcmf_dbg(CTL, "cancelled\n");
return -ERESTARTSYS;
} else {
brcmf_dbg(CTL, "resumed for unknown reason?\n");
+ brcmf_sdbrcm_checkdied(bus);
}
if (rxlen)
@@ -3817,6 +4146,7 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus)
static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
{
brcmf_dbg(TRACE, "Enter\n");
+
if (bus) {
/* De-register interrupt handler */
brcmf_sdio_intr_unregister(bus->sdiodev);
--
1.7.9.5
On Sun, Jun 10, 2012 at 6:57 AM, Arend van Spriel <[email protected]> wrote:
> On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
>> On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
>>> The checkdied functionality provides useful information for analyzing
>>> firmware crashes. By exposing this information to a debugfs file users
>>> can easily provide its content in bug reports. The functionality is
>>> available only when CONFIG_BRCMDBG is selected.
>>>
>>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
>>> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
>>> Signed-off-by: Arend van Spriel <[email protected]>
>>> ---
>>> ?drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | ?332 +++++++++++++++++++-
>>> ?1 file changed, 331 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>>> index a07fb01..5a33b42 100644
>>> --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>>> @@ -31,6 +31,7 @@
>>> ?#include <linux/firmware.h>
>>> ?#include <linux/module.h>
>>> ?#include <linux/bcma/bcma.h>
>>> +#include <linux/debugfs.h>
>>> ?#include <asm/unaligned.h>
>>> ?#include <defs.h>
>>> ?#include <brcmu_wifi.h>
>>> @@ -48,6 +49,9 @@
>>>
>>> ?#define CBUF_LEN ? ? ? (128)
>>>
>>> +/* Device console log buffer state */
>>> +#define CONSOLE_BUFFER_MAX ? ? 2024
>>
>> Just out of curiosity; what is the significance of this number?
>
> It is used in the code fragment below. I have to admit the comment is
> not explaining its significance.
But why this particular number? Did you mean 2048?
>
>>> + ? ? ? /* allocate buffer for console data */
>>> + ? ? ? if (console_size <= CONSOLE_BUFFER_MAX)
>>> + ? ? ? ? ? ? ? conbuf = kzalloc(console_size+1, GFP_ATOMIC);
>>> +
>>> + ? ? ? if (!conbuf)
>>> + ? ? ? ? ? ? ? return -ENOMEM;
>
> Probably would be better to use vmalloc() here or at least use
> GFP_KERNEL here.
>
> John, any recommendations?
>
> Gr. AvS
>
--
Vista: [V]iruses, [I]ntruders, [S]pyware, [T]rojans and [A]dware. :-)
On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
> The checkdied functionality provides useful information for analyzing
> firmware crashes. By exposing this information to a debugfs file users
> can easily provide its content in bug reports. The functionality is
> available only when CONFIG_BRCMDBG is selected.
>
> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
> Signed-off-by: Arend van Spriel <[email protected]>
> ---
> ?drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | ?332 +++++++++++++++++++-
> ?1 file changed, 331 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> index a07fb01..5a33b42 100644
> --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
> @@ -31,6 +31,7 @@
> ?#include <linux/firmware.h>
> ?#include <linux/module.h>
> ?#include <linux/bcma/bcma.h>
> +#include <linux/debugfs.h>
> ?#include <asm/unaligned.h>
> ?#include <defs.h>
> ?#include <brcmu_wifi.h>
> @@ -48,6 +49,9 @@
>
> ?#define CBUF_LEN ? ? ? (128)
>
> +/* Device console log buffer state */
> +#define CONSOLE_BUFFER_MAX ? ? 2024
Just out of curiosity; what is the significance of this number?
> +
> ?struct rte_log_le {
> ? ? ? ?__le32 buf; ? ? ? ? ? ? /* Can't be pointer on (64-bit) hosts */
> ? ? ? ?__le32 buf_size;
> @@ -281,7 +285,7 @@ struct rte_console {
> ?* Shared structure between dongle and the host.
> ?* The structure contains pointers to trap or assert information.
> ?*/
> -#define SDPCM_SHARED_VERSION ? ? ? 0x0002
> +#define SDPCM_SHARED_VERSION ? ? ? 0x0003
> ?#define SDPCM_SHARED_VERSION_MASK ?0x00FF
> ?#define SDPCM_SHARED_ASSERT_BUILT ?0x0100
> ?#define SDPCM_SHARED_ASSERT ? ? ? ?0x0200
> @@ -428,6 +432,29 @@ struct brcmf_console {
> ? ? ? ?u8 *buf; ? ? ? ? ? ? ? ?/* Log buffer (host copy) */
> ? ? ? ?uint last; ? ? ? ? ? ? ?/* Last buffer read index */
> ?};
> +
> +struct brcmf_trap_info {
> + ? ? ? __le32 ? ? ? ? ?type;
> + ? ? ? __le32 ? ? ? ? ?epc;
> + ? ? ? __le32 ? ? ? ? ?cpsr;
> + ? ? ? __le32 ? ? ? ? ?spsr;
> + ? ? ? __le32 ? ? ? ? ?r0; ? ? /* a1 */
> + ? ? ? __le32 ? ? ? ? ?r1; ? ? /* a2 */
> + ? ? ? __le32 ? ? ? ? ?r2; ? ? /* a3 */
> + ? ? ? __le32 ? ? ? ? ?r3; ? ? /* a4 */
> + ? ? ? __le32 ? ? ? ? ?r4; ? ? /* v1 */
> + ? ? ? __le32 ? ? ? ? ?r5; ? ? /* v2 */
> + ? ? ? __le32 ? ? ? ? ?r6; ? ? /* v3 */
> + ? ? ? __le32 ? ? ? ? ?r7; ? ? /* v4 */
> + ? ? ? __le32 ? ? ? ? ?r8; ? ? /* v5 */
> + ? ? ? __le32 ? ? ? ? ?r9; ? ? /* sb/v6 */
> + ? ? ? __le32 ? ? ? ? ?r10; ? ?/* sl/v7 */
> + ? ? ? __le32 ? ? ? ? ?r11; ? ?/* fp/v8 */
> + ? ? ? __le32 ? ? ? ? ?r12; ? ?/* ip */
> + ? ? ? __le32 ? ? ? ? ?r13; ? ?/* sp */
> + ? ? ? __le32 ? ? ? ? ?r14; ? ?/* lr */
> + ? ? ? __le32 ? ? ? ? ?pc; ? ? /* r15 */
> +};
> ?#endif ? ? ? ? ? ? ? ? ? ? ? ? /* DEBUG */
>
> ?struct sdpcm_shared {
> @@ -439,6 +466,7 @@ struct sdpcm_shared {
> ? ? ? ?u32 console_addr; ? ? ? /* Address of struct rte_console */
> ? ? ? ?u32 msgtrace_addr;
> ? ? ? ?u8 tag[32];
> + ? ? ? u32 brpt_addr;
> ?};
>
> ?struct sdpcm_shared_le {
> @@ -450,6 +478,7 @@ struct sdpcm_shared_le {
> ? ? ? ?__le32 console_addr; ? ?/* Address of struct rte_console */
> ? ? ? ?__le32 msgtrace_addr;
> ? ? ? ?u8 tag[32];
> + ? ? ? __le32 brpt_addr;
> ?};
>
>
> @@ -2953,13 +2982,311 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
> ?}
>
> ?#ifdef DEBUG
> +static inline bool brcmf_sdio_valid_shared_address(u32 addr)
> +{
> + ? ? ? return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff));
> +}
> +
> +static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sdpcm_shared *sh)
> +{
> + ? ? ? u32 addr;
> + ? ? ? int rv;
> + ? ? ? u32 shaddr = 0;
> + ? ? ? struct sdpcm_shared_le sh_le;
> + ? ? ? __le32 addr_le;
> +
> + ? ? ? shaddr = bus->ramsize - 4;
> +
> + ? ? ? /*
> + ? ? ? ?* Read last word in socram to determine
> + ? ? ? ?* address of sdpcm_shared structure
> + ? ? ? ?*/
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, shaddr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(u8 *)&addr_le, 4);
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? return rv;
> +
> + ? ? ? addr = le32_to_cpu(addr_le);
> +
> + ? ? ? brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr);
> +
> + ? ? ? /*
> + ? ? ? ?* Check if addr is valid.
> + ? ? ? ?* NVRAM length at the end of memory should have been overwritten.
> + ? ? ? ?*/
> + ? ? ? if (!brcmf_sdio_valid_shared_address(addr)) {
> + ? ? ? ? ? ? ? ? ? ? ? brcmf_dbg(ERROR, "invalid sdpcm_shared address 0x%08X\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? addr);
> + ? ? ? ? ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /* Read hndrte_shared structure */
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?sizeof(struct sdpcm_shared_le));
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? return rv;
> +
> + ? ? ? /* Endianness */
> + ? ? ? sh->flags = le32_to_cpu(sh_le.flags);
> + ? ? ? sh->trap_addr = le32_to_cpu(sh_le.trap_addr);
> + ? ? ? sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr);
> + ? ? ? sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr);
> + ? ? ? sh->assert_line = le32_to_cpu(sh_le.assert_line);
> + ? ? ? sh->console_addr = le32_to_cpu(sh_le.console_addr);
> + ? ? ? sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr);
> +
> + ? ? ? if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
> + ? ? ? ? ? ? ? brcmf_dbg(ERROR,
> + ? ? ? ? ? ? ? ? ? ? ? ? "sdpcm_shared version mismatch: dhd %d dongle %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? SDPCM_SHARED_VERSION,
> + ? ? ? ? ? ? ? ? ? ? ? ? sh->flags & SDPCM_SHARED_VERSION_MASK);
> + ? ? ? ? ? ? ? return -EPROTO;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sdpcm_shared *sh, char __user *data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?size_t count)
> +{
> + ? ? ? u32 addr, console_ptr, console_size, console_index;
> + ? ? ? char *conbuf = NULL;
> + ? ? ? __le32 sh_val;
> + ? ? ? int rv;
> + ? ? ? loff_t pos = 0;
> + ? ? ? int nbytes = 0;
> +
> + ? ? ? /* obtain console information from device memory */
> + ? ? ? addr = sh->console_addr + offsetof(struct rte_console, log_le);
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, addr,
> + ? ? ? ? ? ? ? ? ? ? ? (u8 *)&sh_val, sizeof(u32));
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? return rv;
> + ? ? ? console_ptr = le32_to_cpu(sh_val);
> +
> + ? ? ? addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, addr,
> + ? ? ? ? ? ? ? ? ? ? ? (u8 *)&sh_val, sizeof(u32));
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? return rv;
> + ? ? ? console_size = le32_to_cpu(sh_val);
> +
> + ? ? ? addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, addr,
> + ? ? ? ? ? ? ? ? ? ? ? (u8 *)&sh_val, sizeof(u32));
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? return rv;
> + ? ? ? console_index = le32_to_cpu(sh_val);
> +
> + ? ? ? /* allocate buffer for console data */
> + ? ? ? if (console_size <= CONSOLE_BUFFER_MAX)
> + ? ? ? ? ? ? ? conbuf = kzalloc(console_size+1, GFP_ATOMIC);
> +
> + ? ? ? if (!conbuf)
> + ? ? ? ? ? ? ? return -ENOMEM;
> +
> + ? ? ? /* obtain the console data from device */
> + ? ? ? conbuf[console_size] = '\0';
> + ? ? ? rv = brcmf_sdbrcm_membytes(bus, false, console_ptr, (u8 *)conbuf,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?console_size);
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? goto done;
> +
> + ? ? ? rv = simple_read_from_buffer(data, count, &pos,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?conbuf + console_index,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?console_size - console_index);
> + ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? goto done;
> +
> + ? ? ? nbytes = rv;
> + ? ? ? if (console_index > 0) {
> + ? ? ? ? ? ? ? pos = 0;
> + ? ? ? ? ? ? ? rv = simple_read_from_buffer(data+nbytes, count, &pos,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?conbuf, console_index - 1);
> + ? ? ? ? ? ? ? if (rv < 0)
> + ? ? ? ? ? ? ? ? ? ? ? goto done;
> + ? ? ? ? ? ? ? rv += nbytes;
> + ? ? ? }
> +done:
> + ? ? ? kfree(conbuf);
> + ? ? ? return rv;
> +}
> +
> +static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? char __user *data, size_t count)
> +{
> + ? ? ? int error, res;
> + ? ? ? char buf[350];
> + ? ? ? struct brcmf_trap_info tr;
> + ? ? ? int nbytes;
> + ? ? ? loff_t pos = 0;
> +
> + ? ? ? if ((sh->flags & SDPCM_SHARED_TRAP) == 0)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct brcmf_trap_info));
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? return error;
> +
> + ? ? ? nbytes = brcmf_sdio_dump_console(bus, sh, data, count);
> + ? ? ? if (nbytes < 0)
> + ? ? ? ? ? ? ? return nbytes;
> +
> + ? ? ? res = scnprintf(buf, sizeof(buf),
> + ? ? ? ? ? ? ? ? ? ? ? "dongle trap info: type 0x%x @ epc 0x%08x\n"
> + ? ? ? ? ? ? ? ? ? ? ? " ?cpsr 0x%08x spsr 0x%08x sp 0x%08x\n"
> + ? ? ? ? ? ? ? ? ? ? ? " ?lr ? 0x%08x pc ? 0x%08x offset 0x%x\n"
> + ? ? ? ? ? ? ? ? ? ? ? " ?r0 ? 0x%08x r1 ? 0x%08x r2 0x%08x r3 0x%08x\n"
> + ? ? ? ? ? ? ? ? ? ? ? " ?r4 ? 0x%08x r5 ? 0x%08x r6 0x%08x r7 0x%08x\n",
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.type), le32_to_cpu(tr.epc),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.r13), le32_to_cpu(tr.r14),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.pc), le32_to_cpu(sh->trap_addr),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.r0), le32_to_cpu(tr.r1),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.r2), le32_to_cpu(tr.r3),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.r4), le32_to_cpu(tr.r5),
> + ? ? ? ? ? ? ? ? ? ? ? le32_to_cpu(tr.r6), le32_to_cpu(tr.r7));
> +
> + ? ? ? error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res);
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? return error;
> +
> + ? ? ? nbytes += error;
> + ? ? ? return nbytes;
> +}
> +
> +static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sdpcm_shared *sh, char __user *data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size_t count)
> +{
> + ? ? ? int error = 0;
> + ? ? ? char buf[200];
> + ? ? ? char file[80] = "?";
> + ? ? ? char expr[80] = "<???>";
> + ? ? ? int res;
> + ? ? ? loff_t pos = 0;
> +
> + ? ? ? if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
> + ? ? ? ? ? ? ? brcmf_dbg(INFO, "firmware not built with -assert\n");
> + ? ? ? ? ? ? ? return 0;
> + ? ? ? } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) {
> + ? ? ? ? ? ? ? brcmf_dbg(INFO, "no assert in dongle\n");
> + ? ? ? ? ? ? ? return 0;
> + ? ? ? }
> +
> + ? ? ? if (sh->assert_file_addr != 0) {
> + ? ? ? ? ? ? ? error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (u8 *)file, 80);
> + ? ? ? ? ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? ? ? ? ? return error;
> + ? ? ? }
> + ? ? ? if (sh->assert_exp_addr != 0) {
> + ? ? ? ? ? ? ? error = brcmf_sdbrcm_membytes(bus, false, sh->assert_exp_addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (u8 *)expr, 80);
> + ? ? ? ? ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? ? ? ? ? return error;
> + ? ? ? }
> +
> + ? ? ? res = scnprintf(buf, sizeof(buf),
> + ? ? ? ? ? ? ? ? ? ? ? "dongle assert: %s:%d: assert(%s)\n",
> + ? ? ? ? ? ? ? ? ? ? ? file, sh->assert_line, expr);
> + ? ? ? return simple_read_from_buffer(data, count, &pos, buf, res);
> +}
> +
> +static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
> +{
> + ? ? ? int error;
> + ? ? ? struct sdpcm_shared sh;
> +
> + ? ? ? down(&bus->sdsem);
> + ? ? ? error = brcmf_sdio_readshared(bus, &sh);
> + ? ? ? up(&bus->sdsem);
> +
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? return error;
> +
> + ? ? ? if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0)
> + ? ? ? ? ? ? ? brcmf_dbg(INFO, "firmware not built with -assert\n");
> + ? ? ? else if (sh.flags & SDPCM_SHARED_ASSERT)
> + ? ? ? ? ? ? ? brcmf_dbg(ERROR, "assertion in dongle\n");
> +
> + ? ? ? if (sh.flags & SDPCM_SHARED_TRAP)
> + ? ? ? ? ? ? ? brcmf_dbg(ERROR, "firmware trap in dongle\n");
> +
> + ? ? ? return 0;
> +}
> +
> +static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size_t count, loff_t *ppos)
> +{
> + ? ? ? int error = 0;
> + ? ? ? struct sdpcm_shared sh;
> + ? ? ? int nbytes = 0;
> + ? ? ? loff_t pos = *ppos;
> +
> + ? ? ? if (pos != 0)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? down(&bus->sdsem);
> + ? ? ? error = brcmf_sdio_readshared(bus, &sh);
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? goto done;
> +
> + ? ? ? error = brcmf_sdio_assert_info(bus, &sh, data, count);
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? goto done;
> +
> + ? ? ? nbytes = error;
> + ? ? ? error = brcmf_sdio_trap_info(bus, &sh, data, count);
> + ? ? ? if (error < 0)
> + ? ? ? ? ? ? ? goto done;
> +
> + ? ? ? error += nbytes;
> + ? ? ? *ppos += error;
> +done:
> + ? ? ? up(&bus->sdsem);
> + ? ? ? return error;
> +}
> +
> +static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size_t count, loff_t *ppos)
> +{
> + ? ? ? struct brcmf_sdio *bus = f->private_data;
> + ? ? ? int res;
> +
> + ? ? ? res = brcmf_sdbrcm_died_dump(bus, data, count, ppos);
> + ? ? ? if (res > 0)
> + ? ? ? ? ? ? ? *ppos += res;
> + ? ? ? return (ssize_t)res;
> +}
> +
> +static const struct file_operations brcmf_sdio_forensic_ops = {
> + ? ? ? .owner = THIS_MODULE,
> + ? ? ? .open = simple_open,
> + ? ? ? .read = brcmf_sdio_forensic_read
> +};
> +
> ?static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
> ?{
> ? ? ? ?struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
> + ? ? ? struct dentry *dentry = brcmf_debugfs_get_devdir(drvr);
>
> + ? ? ? if (IS_ERR_OR_NULL(dentry))
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? debugfs_create_file("forensics", S_IRUGO, dentry, bus,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? &brcmf_sdio_forensic_ops);
> ? ? ? ?brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
> ?}
> ?#else
> +static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
> +{
> + ? ? ? return 0;
> +}
> +
> ?static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
> ?{
> ?}
> @@ -2991,11 +3318,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
> ? ? ? ? ? ? ? ? ? ? ? ? ?rxlen, msglen);
> ? ? ? ?} else if (timeleft == 0) {
> ? ? ? ? ? ? ? ?brcmf_dbg(ERROR, "resumed on timeout\n");
> + ? ? ? ? ? ? ? brcmf_sdbrcm_checkdied(bus);
> ? ? ? ?} else if (pending) {
> ? ? ? ? ? ? ? ?brcmf_dbg(CTL, "cancelled\n");
> ? ? ? ? ? ? ? ?return -ERESTARTSYS;
> ? ? ? ?} else {
> ? ? ? ? ? ? ? ?brcmf_dbg(CTL, "resumed for unknown reason?\n");
> + ? ? ? ? ? ? ? brcmf_sdbrcm_checkdied(bus);
> ? ? ? ?}
>
> ? ? ? ?if (rxlen)
> @@ -3817,6 +4146,7 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus)
> ?static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
> ?{
> ? ? ? ?brcmf_dbg(TRACE, "Enter\n");
> +
> ? ? ? ?if (bus) {
> ? ? ? ? ? ? ? ?/* De-register interrupt handler */
> ? ? ? ? ? ? ? ?brcmf_sdio_intr_unregister(bus->sdiodev);
> --
> 1.7.9.5
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
--
Vista: [V]iruses, [I]ntruders, [S]pyware, [T]rojans and [A]dware. :-)
On 06/11/2012 08:43 PM, John W. Linville wrote:
> On Sun, Jun 10, 2012 at 06:57:29AM +0200, Arend van Spriel wrote:
>> On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
>>> On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
>>>> The checkdied functionality provides useful information for analyzing
>>>> firmware crashes. By exposing this information to a debugfs file users
>>>> can easily provide its content in bug reports. The functionality is
>>>> available only when CONFIG_BRCMDBG is selected.
>>>>
>>>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
>>>> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
>>>> Signed-off-by: Arend van Spriel <[email protected]>
>>>> ---
>>>> + /* allocate buffer for console data */
>>>> + if (console_size <= CONSOLE_BUFFER_MAX)
>>>> + conbuf = kzalloc(console_size+1, GFP_ATOMIC);
>>>> +
>>>> + if (!conbuf)
>>>> + return -ENOMEM;
>>
>> Probably would be better to use vmalloc() here or at least use
>> GFP_KERNEL here.
>>
>> John, any recommendations?
>
> vmalloc seems reasonable...
>
Thanks, John
Can you take the first four patches? I will make a v2 for this one. Let
me know if that is ok with you.
Gr. AvS
The structure brcmf_sdio contains a number of counters that are useful
for debugging. These were not available in user-space. This patch
exposes them in debugfs under the filename 'counters'.
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 63 ++++++++
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 36 +++++
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 169 +++++++++-----------
3 files changed, 175 insertions(+), 93 deletions(-)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
index 0a7a3d5..7f89540 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c
@@ -17,12 +17,14 @@
#include <linux/if_ether.h>
#include <linux/if.h>
#include <linux/ieee80211.h>
+#include <linux/module.h>
#include <defs.h>
#include <brcmu_wifi.h>
#include <brcmu_utils.h>
#include "dhd.h"
#include "dhd_bus.h"
+#include "dhd_dbg.h"
static struct dentry *root_folder;
@@ -61,3 +63,64 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
{
return drvr->dbgfs_dir;
}
+
+static
+ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct brcmf_sdio_count *sdcnt = f->private_data;
+ char buf[750];
+ int res;
+
+ /* only allow read from start */
+ if (*ppos > 0)
+ return 0;
+
+ res = scnprintf(buf, sizeof(buf),
+ "intrcount: %u\nlastintrs: %u\n"
+ "pollcnt: %u\nregfails: %u\n"
+ "tx_sderrs: %u\nfcqueued: %u\n"
+ "rxrtx: %u\nrx_toolong: %u\n"
+ "rxc_errors: %u\nrx_hdrfail: %u\n"
+ "rx_badhdr: %u\nrx_badseq: %u\n"
+ "fc_rcvd: %u\nfc_xoff: %u\n"
+ "fc_xon: %u\nrxglomfail: %u\n"
+ "rxglomframes: %u\nrxglompkts: %u\n"
+ "f2rxhdrs: %u\nf2rxdata: %u\n"
+ "f2txdata: %u\nf1regdata: %u\n"
+ "tickcnt: %u\ntx_ctlerrs: %lu\n"
+ "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n"
+ "rx_ctlpkts: %lu\nrx_readahead: %lu\n",
+ sdcnt->intrcount, sdcnt->lastintrs,
+ sdcnt->pollcnt, sdcnt->regfails,
+ sdcnt->tx_sderrs, sdcnt->fcqueued,
+ sdcnt->rxrtx, sdcnt->rx_toolong,
+ sdcnt->rxc_errors, sdcnt->rx_hdrfail,
+ sdcnt->rx_badhdr, sdcnt->rx_badseq,
+ sdcnt->fc_rcvd, sdcnt->fc_xoff,
+ sdcnt->fc_xon, sdcnt->rxglomfail,
+ sdcnt->rxglomframes, sdcnt->rxglompkts,
+ sdcnt->f2rxhdrs, sdcnt->f2rxdata,
+ sdcnt->f2txdata, sdcnt->f1regdata,
+ sdcnt->tickcnt, sdcnt->tx_ctlerrs,
+ sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs,
+ sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt);
+
+ return simple_read_from_buffer(data, count, ppos, buf, res);
+}
+
+static const struct file_operations brcmf_debugfs_sdio_counter_ops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = brcmf_debugfs_sdio_counter_read
+};
+
+void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
+ struct brcmf_sdio_count *sdcnt)
+{
+ struct dentry *dentry = drvr->dbgfs_dir;
+
+ if (!IS_ERR_OR_NULL(dentry))
+ debugfs_create_file("counters", S_IRUGO, dentry,
+ sdcnt, &brcmf_debugfs_sdio_counter_ops);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
index 0efb226..b784920 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -76,6 +76,40 @@ do { \
extern int brcmf_msg_level;
+/*
+ * hold counter variables used in brcmfmac sdio driver.
+ */
+struct brcmf_sdio_count {
+ uint intrcount; /* Count of device interrupt callbacks */
+ uint lastintrs; /* Count as of last watchdog timer */
+ uint pollcnt; /* Count of active polls */
+ uint regfails; /* Count of R_REG failures */
+ uint tx_sderrs; /* Count of tx attempts with sd errors */
+ uint fcqueued; /* Tx packets that got queued */
+ uint rxrtx; /* Count of rtx requests (NAK to dongle) */
+ uint rx_toolong; /* Receive frames too long to receive */
+ uint rxc_errors; /* SDIO errors when reading control frames */
+ uint rx_hdrfail; /* SDIO errors on header reads */
+ uint rx_badhdr; /* Bad received headers (roosync?) */
+ uint rx_badseq; /* Mismatched rx sequence number */
+ uint fc_rcvd; /* Number of flow-control events received */
+ uint fc_xoff; /* Number which turned on flow-control */
+ uint fc_xon; /* Number which turned off flow-control */
+ uint rxglomfail; /* Failed deglom attempts */
+ uint rxglomframes; /* Number of glom frames (superframes) */
+ uint rxglompkts; /* Number of packets from glom frames */
+ uint f2rxhdrs; /* Number of header reads */
+ uint f2rxdata; /* Number of frame data reads */
+ uint f2txdata; /* Number of f2 frame writes */
+ uint f1regdata; /* Number of f1 register accesses */
+ uint tickcnt; /* Number of watchdog been schedule */
+ ulong tx_ctlerrs; /* Err of sending ctrl frames */
+ ulong tx_ctlpkts; /* Ctrl frames sent to dongle */
+ ulong rx_ctlerrs; /* Err of processing rx ctrl frames */
+ ulong rx_ctlpkts; /* Ctrl frames processed from dongle */
+ ulong rx_readahead_cnt; /* packets where header read-ahead was used */
+};
+
struct brcmf_pub;
#ifdef DEBUG
void brcmf_debugfs_init(void);
@@ -83,6 +117,8 @@ void brcmf_debugfs_exit(void);
int brcmf_debugfs_attach(struct brcmf_pub *drvr);
void brcmf_debugfs_detach(struct brcmf_pub *drvr);
struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr);
+void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr,
+ struct brcmf_sdio_count *sdcnt);
#else
static inline void brcmf_debugfs_init(void)
{
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
index 1dbf2be..a07fb01 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -502,12 +502,9 @@ struct brcmf_sdio {
bool intr; /* Use interrupts */
bool poll; /* Use polling */
bool ipend; /* Device interrupt is pending */
- uint intrcount; /* Count of device interrupt callbacks */
- uint lastintrs; /* Count as of last watchdog timer */
uint spurious; /* Count of spurious interrupts */
uint pollrate; /* Ticks between device polls */
uint polltick; /* Tick counter */
- uint pollcnt; /* Count of active polls */
#ifdef DEBUG
uint console_interval;
@@ -515,8 +512,6 @@ struct brcmf_sdio {
uint console_addr; /* Console address from shared struct */
#endif /* DEBUG */
- uint regfails; /* Count of R_REG failures */
-
uint clkstate; /* State of sd and backplane clock(s) */
bool activity; /* Activity flag for clock down */
s32 idletime; /* Control for activity timeout */
@@ -531,33 +526,6 @@ struct brcmf_sdio {
/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
bool usebufpool;
- /* Some additional counters */
- uint tx_sderrs; /* Count of tx attempts with sd errors */
- uint fcqueued; /* Tx packets that got queued */
- uint rxrtx; /* Count of rtx requests (NAK to dongle) */
- uint rx_toolong; /* Receive frames too long to receive */
- uint rxc_errors; /* SDIO errors when reading control frames */
- uint rx_hdrfail; /* SDIO errors on header reads */
- uint rx_badhdr; /* Bad received headers (roosync?) */
- uint rx_badseq; /* Mismatched rx sequence number */
- uint fc_rcvd; /* Number of flow-control events received */
- uint fc_xoff; /* Number which turned on flow-control */
- uint fc_xon; /* Number which turned off flow-control */
- uint rxglomfail; /* Failed deglom attempts */
- uint rxglomframes; /* Number of glom frames (superframes) */
- uint rxglompkts; /* Number of packets from glom frames */
- uint f2rxhdrs; /* Number of header reads */
- uint f2rxdata; /* Number of frame data reads */
- uint f2txdata; /* Number of f2 frame writes */
- uint f1regdata; /* Number of f1 register accesses */
- uint tickcnt; /* Number of watchdog been schedule */
- unsigned long tx_ctlerrs; /* Err of sending ctrl frames */
- unsigned long tx_ctlpkts; /* Ctrl frames sent to dongle */
- unsigned long rx_ctlerrs; /* Err of processing rx ctrl frames */
- unsigned long rx_ctlpkts; /* Ctrl frames processed from dongle */
- unsigned long rx_readahead_cnt; /* Number of packets where header
- * read-ahead was used. */
-
u8 *ctrl_frame_buf;
u32 ctrl_frame_len;
bool ctrl_frame_stat;
@@ -583,6 +551,7 @@ struct brcmf_sdio {
u32 fw_ptr;
bool txoff; /* Transmit flow-controlled */
+ struct brcmf_sdio_count sdcnt;
};
/* clkstate */
@@ -945,7 +914,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
if (ret == 0)
w_sdreg32(bus, SMB_INT_ACK,
offsetof(struct sdpcmd_regs, tosbmailbox));
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
/* Dongle recomposed rx frames, accept them again */
if (hmb_data & HMB_DATA_NAKHANDLED) {
@@ -984,12 +953,12 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
HMB_DATA_FCDATA_SHIFT;
if (fcbits & ~bus->flowcontrol)
- bus->fc_xoff++;
+ bus->sdcnt.fc_xoff++;
if (bus->flowcontrol & ~fcbits)
- bus->fc_xon++;
+ bus->sdcnt.fc_xon++;
- bus->fc_rcvd++;
+ bus->sdcnt.fc_rcvd++;
bus->flowcontrol = fcbits;
}
@@ -1021,7 +990,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
SFC_RF_TERM, &err);
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
/* Wait until the packet has been flushed (device/FIFO stable) */
for (lastrbc = retries = 0xffff; retries > 0; retries--) {
@@ -1029,7 +998,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
SBSDIO_FUNC1_RFRAMEBCHI, &err);
lo = brcmf_sdio_regrb(bus->sdiodev,
SBSDIO_FUNC1_RFRAMEBCLO, &err);
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0))
break;
@@ -1047,11 +1016,11 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries);
if (rtx) {
- bus->rxrtx++;
+ bus->sdcnt.rxrtx++;
err = w_sdreg32(bus, SMB_NAK,
offsetof(struct sdpcmd_regs, tosbmailbox));
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
if (err == 0)
bus->rxskip = true;
}
@@ -1243,7 +1212,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
dlen);
errcode = -1;
}
- bus->f2rxdata++;
+ bus->sdcnt.f2rxdata++;
/* On failure, kill the superframe, allow a couple retries */
if (errcode < 0) {
@@ -1256,7 +1225,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
} else {
bus->glomerr = 0;
brcmf_sdbrcm_rxfail(bus, true, false);
- bus->rxglomfail++;
+ bus->sdcnt.rxglomfail++;
brcmf_sdbrcm_free_glom(bus);
}
return 0;
@@ -1312,7 +1281,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
if (rxseq != seq) {
brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n",
seq, rxseq);
- bus->rx_badseq++;
+ bus->sdcnt.rx_badseq++;
rxseq = seq;
}
@@ -1376,7 +1345,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
} else {
bus->glomerr = 0;
brcmf_sdbrcm_rxfail(bus, true, false);
- bus->rxglomfail++;
+ bus->sdcnt.rxglomfail++;
brcmf_sdbrcm_free_glom(bus);
}
bus->nextlen = 0;
@@ -1402,7 +1371,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
if (rxseq != seq) {
brcmf_dbg(GLOM, "rx_seq %d, expected %d\n",
seq, rxseq);
- bus->rx_badseq++;
+ bus->sdcnt.rx_badseq++;
rxseq = seq;
}
rxseq++;
@@ -1441,8 +1410,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
down(&bus->sdsem);
}
- bus->rxglomframes++;
- bus->rxglompkts += bus->glom.qlen;
+ bus->sdcnt.rxglomframes++;
+ bus->sdcnt.rxglompkts += bus->glom.qlen;
}
return num;
}
@@ -1526,7 +1495,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
brcmf_dbg(ERROR, "%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
len, len - doff, bus->sdiodev->bus_if->maxctl);
bus->sdiodev->bus_if->dstats.rx_errors++;
- bus->rx_toolong++;
+ bus->sdcnt.rx_toolong++;
brcmf_sdbrcm_rxfail(bus, false, false);
goto done;
}
@@ -1536,13 +1505,13 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
bus->sdiodev->sbwad,
SDIO_FUNC_2,
F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen);
- bus->f2rxdata++;
+ bus->sdcnt.f2rxdata++;
/* Control frame failures need retransmission */
if (sdret < 0) {
brcmf_dbg(ERROR, "read %d control bytes failed: %d\n",
rdlen, sdret);
- bus->rxc_errors++;
+ bus->sdcnt.rxc_errors++;
brcmf_sdbrcm_rxfail(bus, true, true);
goto done;
}
@@ -1589,7 +1558,7 @@ brcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen,
/* Read the entire frame */
sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC, *pkt);
- bus->f2rxdata++;
+ bus->sdcnt.f2rxdata++;
if (sdret < 0) {
brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n",
@@ -1630,7 +1599,7 @@ brcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf,
if ((u16)~(*len ^ check)) {
brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n",
nextlen, *len, check);
- bus->rx_badhdr++;
+ bus->sdcnt.rx_badhdr++;
brcmf_sdbrcm_rxfail(bus, false, false);
goto fail;
}
@@ -1746,7 +1715,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
bus->nextlen = 0;
}
- bus->rx_readahead_cnt++;
+ bus->sdcnt.rx_readahead_cnt++;
/* Handle Flow Control */
fcbits = SDPCM_FCMASK_VALUE(
@@ -1754,12 +1723,12 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
if (bus->flowcontrol != fcbits) {
if (~bus->flowcontrol & fcbits)
- bus->fc_xoff++;
+ bus->sdcnt.fc_xoff++;
if (bus->flowcontrol & ~fcbits)
- bus->fc_xon++;
+ bus->sdcnt.fc_xon++;
- bus->fc_rcvd++;
+ bus->sdcnt.fc_rcvd++;
bus->flowcontrol = fcbits;
}
@@ -1767,7 +1736,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
if (rxseq != seq) {
brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n",
seq, rxseq);
- bus->rx_badseq++;
+ bus->sdcnt.rx_badseq++;
rxseq = seq;
}
@@ -1814,11 +1783,11 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC, bus->rxhdr,
BRCMF_FIRSTREAD);
- bus->f2rxhdrs++;
+ bus->sdcnt.f2rxhdrs++;
if (sdret < 0) {
brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret);
- bus->rx_hdrfail++;
+ bus->sdcnt.rx_hdrfail++;
brcmf_sdbrcm_rxfail(bus, true, true);
continue;
}
@@ -1840,7 +1809,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
if ((u16) ~(len ^ check)) {
brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n",
len, check);
- bus->rx_badhdr++;
+ bus->sdcnt.rx_badhdr++;
brcmf_sdbrcm_rxfail(bus, false, false);
continue;
}
@@ -1861,7 +1830,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
if ((doff < SDPCM_HDRLEN) || (doff > len)) {
brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n",
doff, len, SDPCM_HDRLEN, seq);
- bus->rx_badhdr++;
+ bus->sdcnt.rx_badhdr++;
brcmf_sdbrcm_rxfail(bus, false, false);
continue;
}
@@ -1880,19 +1849,19 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
if (bus->flowcontrol != fcbits) {
if (~bus->flowcontrol & fcbits)
- bus->fc_xoff++;
+ bus->sdcnt.fc_xoff++;
if (bus->flowcontrol & ~fcbits)
- bus->fc_xon++;
+ bus->sdcnt.fc_xon++;
- bus->fc_rcvd++;
+ bus->sdcnt.fc_rcvd++;
bus->flowcontrol = fcbits;
}
/* Check and update sequence number */
if (rxseq != seq) {
brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq);
- bus->rx_badseq++;
+ bus->sdcnt.rx_badseq++;
rxseq = seq;
}
@@ -1937,7 +1906,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
brcmf_dbg(ERROR, "too long: len %d rdlen %d\n",
len, rdlen);
bus->sdiodev->bus_if->dstats.rx_errors++;
- bus->rx_toolong++;
+ bus->sdcnt.rx_toolong++;
brcmf_sdbrcm_rxfail(bus, false, false);
continue;
}
@@ -1960,7 +1929,7 @@ brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
/* Read the remaining frame data */
sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC, pkt);
- bus->f2rxdata++;
+ bus->sdcnt.f2rxdata++;
if (sdret < 0) {
brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen,
@@ -2147,18 +2116,18 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad,
SDIO_FUNC_2, F2SYNC, pkt);
- bus->f2txdata++;
+ bus->sdcnt.f2txdata++;
if (ret < 0) {
/* On failure, abort the command and terminate the frame */
brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
ret);
- bus->tx_sderrs++;
+ bus->sdcnt.tx_sderrs++;
brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
SFC_WF_TERM, NULL);
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
for (i = 0; i < 3; i++) {
u8 hi, lo;
@@ -2166,7 +2135,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
SBSDIO_FUNC1_WFRAMEBCHI, NULL);
lo = brcmf_sdio_regrb(bus->sdiodev,
SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0))
break;
}
@@ -2224,7 +2193,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
ret = r_sdreg32(bus, &intstatus,
offsetof(struct sdpcmd_regs,
intstatus));
- bus->f2txdata++;
+ bus->sdcnt.f2txdata++;
if (ret != 0)
break;
if (intstatus & bus->hostintmask)
@@ -2417,7 +2386,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
bus->ipend = false;
err = r_sdreg32(bus, &newstatus,
offsetof(struct sdpcmd_regs, intstatus));
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
if (err != 0)
newstatus = 0;
newstatus &= bus->hostintmask;
@@ -2426,7 +2395,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
err = w_sdreg32(bus, newstatus,
offsetof(struct sdpcmd_regs,
intstatus));
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
}
}
@@ -2445,7 +2414,7 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
err = r_sdreg32(bus, &newstatus,
offsetof(struct sdpcmd_regs, intstatus));
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
bus->fcstate =
!!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
intstatus |= (newstatus & bus->hostintmask);
@@ -2510,13 +2479,13 @@ clkwait:
terminate the frame */
brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
ret);
- bus->tx_sderrs++;
+ bus->sdcnt.tx_sderrs++;
brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
SFC_WF_TERM, &err);
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
for (i = 0; i < 3; i++) {
u8 hi, lo;
@@ -2526,7 +2495,7 @@ clkwait:
lo = brcmf_sdio_regrb(bus->sdiodev,
SBSDIO_FUNC1_WFRAMEBCLO,
&err);
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
if ((hi == 0) && (lo == 0))
break;
}
@@ -2657,7 +2626,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
/* Check for existing queue, current flow-control,
pending event, or pending clock */
brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
- bus->fcqueued++;
+ bus->sdcnt.fcqueued++;
/* Priority based enq */
spin_lock_bh(&bus->txqlock);
@@ -2845,13 +2814,13 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
/* On failure, abort the command and terminate the frame */
brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
ret);
- bus->tx_sderrs++;
+ bus->sdcnt.tx_sderrs++;
brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
SFC_WF_TERM, NULL);
- bus->f1regdata++;
+ bus->sdcnt.f1regdata++;
for (i = 0; i < 3; i++) {
u8 hi, lo;
@@ -2859,7 +2828,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
SBSDIO_FUNC1_WFRAMEBCHI, NULL);
lo = brcmf_sdio_regrb(bus->sdiodev,
SBSDIO_FUNC1_WFRAMEBCLO, NULL);
- bus->f1regdata += 2;
+ bus->sdcnt.f1regdata += 2;
if (hi == 0 && lo == 0)
break;
}
@@ -2976,13 +2945,26 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
up(&bus->sdsem);
if (ret)
- bus->tx_ctlerrs++;
+ bus->sdcnt.tx_ctlerrs++;
else
- bus->tx_ctlpkts++;
+ bus->sdcnt.tx_ctlpkts++;
return ret ? -EIO : 0;
}
+#ifdef DEBUG
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+ struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr;
+
+ brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
+}
+#else
+static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
+{
+}
+#endif /* DEBUG */
+
static int
brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
{
@@ -3017,9 +2999,9 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
}
if (rxlen)
- bus->rx_ctlpkts++;
+ bus->sdcnt.rx_ctlpkts++;
else
- bus->rx_ctlerrs++;
+ bus->sdcnt.rx_ctlerrs++;
return rxlen ? (int)rxlen : -ETIMEDOUT;
}
@@ -3419,7 +3401,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
return 0;
/* Start the watchdog timer */
- bus->tickcnt = 0;
+ bus->sdcnt.tickcnt = 0;
brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
down(&bus->sdsem);
@@ -3512,7 +3494,7 @@ void brcmf_sdbrcm_isr(void *arg)
return;
}
/* Count the interrupt call */
- bus->intrcount++;
+ bus->sdcnt.intrcount++;
bus->ipend = true;
/* Shouldn't get this interrupt if we're sleeping? */
@@ -3554,7 +3536,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
bus->polltick = 0;
/* Check device if no interrupts */
- if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
+ if (!bus->intr ||
+ (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) {
if (!bus->dpc_sched) {
u8 devpend;
@@ -3569,7 +3552,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
/* If there is something, make like the ISR and
schedule the DPC */
if (intstatus) {
- bus->pollcnt++;
+ bus->sdcnt.pollcnt++;
bus->ipend = true;
bus->dpc_sched = true;
@@ -3581,7 +3564,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
}
/* Update interrupt tracking */
- bus->lastintrs = bus->intrcount;
+ bus->sdcnt.lastintrs = bus->sdcnt.intrcount;
}
#ifdef DEBUG
/* Poll for console output periodically */
@@ -3793,7 +3776,7 @@ brcmf_sdbrcm_watchdog_thread(void *data)
if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
brcmf_sdbrcm_bus_watchdog(bus);
/* Count the tick for reference */
- bus->tickcnt++;
+ bus->sdcnt.tickcnt++;
} else
break;
}
@@ -3834,7 +3817,6 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus)
static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
{
brcmf_dbg(TRACE, "Enter\n");
-
if (bus) {
/* De-register interrupt handler */
brcmf_sdio_intr_unregister(bus->sdiodev);
@@ -3938,6 +3920,7 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
goto fail;
}
+ brcmf_sdio_debugfs_create(bus);
brcmf_dbg(INFO, "completed!!\n");
/* if firmware path present try to download and bring up bus */
--
1.7.9.5
On Wed, Jun 13, 2012 at 03:35:28PM +0200, Arend van Spriel wrote:
> On 06/11/2012 08:43 PM, John W. Linville wrote:
> > On Sun, Jun 10, 2012 at 06:57:29AM +0200, Arend van Spriel wrote:
> >> On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
> >>> On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
> >>>> The checkdied functionality provides useful information for analyzing
> >>>> firmware crashes. By exposing this information to a debugfs file users
> >>>> can easily provide its content in bug reports. The functionality is
> >>>> available only when CONFIG_BRCMDBG is selected.
> >>>>
> >>>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
> >>>> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
> >>>> Signed-off-by: Arend van Spriel <[email protected]>
> >>>> ---
>
> >>>> + /* allocate buffer for console data */
> >>>> + if (console_size <= CONSOLE_BUFFER_MAX)
> >>>> + conbuf = kzalloc(console_size+1, GFP_ATOMIC);
> >>>> +
> >>>> + if (!conbuf)
> >>>> + return -ENOMEM;
> >>
> >> Probably would be better to use vmalloc() here or at least use
> >> GFP_KERNEL here.
> >>
> >> John, any recommendations?
> >
> > vmalloc seems reasonable...
> >
>
> Thanks, John
>
> Can you take the first four patches? I will make a v2 for this one. Let
> me know if that is ok with you.
Sure, fine.
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.
On 06/10/2012 04:13 AM, G?bor Stefanik wrote:
> On Sat, Jun 9, 2012 at 10:51 PM, Arend van Spriel <[email protected]> wrote:
>> The checkdied functionality provides useful information for analyzing
>> firmware crashes. By exposing this information to a debugfs file users
>> can easily provide its content in bug reports. The functionality is
>> available only when CONFIG_BRCMDBG is selected.
>>
>> Reviewed-by: Pieter-Paul Giesberts <[email protected]>
>> Reviewed-by: Franky (Zhenhui) Lin <[email protected]>
>> Signed-off-by: Arend van Spriel <[email protected]>
>> ---
>> drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 332 +++++++++++++++++++-
>> 1 file changed, 331 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>> index a07fb01..5a33b42 100644
>> --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>> +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
>> @@ -31,6 +31,7 @@
>> #include <linux/firmware.h>
>> #include <linux/module.h>
>> #include <linux/bcma/bcma.h>
>> +#include <linux/debugfs.h>
>> #include <asm/unaligned.h>
>> #include <defs.h>
>> #include <brcmu_wifi.h>
>> @@ -48,6 +49,9 @@
>>
>> #define CBUF_LEN (128)
>>
>> +/* Device console log buffer state */
>> +#define CONSOLE_BUFFER_MAX 2024
>
> Just out of curiosity; what is the significance of this number?
It is used in the code fragment below. I have to admit the comment is
not explaining its significance.
>> + /* allocate buffer for console data */
>> + if (console_size <= CONSOLE_BUFFER_MAX)
>> + conbuf = kzalloc(console_size+1, GFP_ATOMIC);
>> +
>> + if (!conbuf)
>> + return -ENOMEM;
Probably would be better to use vmalloc() here or at least use
GFP_KERNEL here.
John, any recommendations?
Gr. AvS
This patch fixes potential NULL pointer dereference in ampdu. This
was found running smatch static code checker. Smatch warning says:
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c:741 brcms_c_sendampdu()
warn: variable dereferenced before check 'p'
Reported-by: Dan Carpenter <[email protected]>
Reviewed-by: Pieter-Paul Giesberts <[email protected]>
Signed-off-by: Arend van Spriel <[email protected]>
---
drivers/net/wireless/brcm80211/brcmsmac/ampdu.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
index 95b5902..01b190a 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
@@ -735,10 +735,8 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
* a candidate for aggregation
*/
p = pktq_ppeek(&qi->q, prec);
- /* tx_info must be checked with current p */
- tx_info = IEEE80211_SKB_CB(p);
-
if (p) {
+ tx_info = IEEE80211_SKB_CB(p);
if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
((u8) (p->priority) == tid)) {
plen = p->len + AMPDU_MAX_MPDU_OVERHEAD;
@@ -759,6 +757,7 @@ brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
p = NULL;
continue;
}
+ /* next packet fit for aggregation so dequeue */
p = brcmu_pktq_pdeq(&qi->q, prec);
} else {
p = NULL;
--
1.7.9.5