network: wireless: bcm4329: Add "HANG" event and console monitoring
authorHoward Harte <hharte@broadcom.com>
Thu, 20 May 2010 22:07:13 +0000 (15:07 -0700)
committerColin Cross <ccross@android.com>
Thu, 30 Sep 2010 00:49:36 +0000 (17:49 -0700)
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
drivers/net/wireless/bcm4329/Makefile
drivers/net/wireless/bcm4329/dhd.h
drivers/net/wireless/bcm4329/dhd_linux.c
drivers/net/wireless/bcm4329/dhd_sdio.c
drivers/net/wireless/bcm4329/include/epivers.h
drivers/net/wireless/bcm4329/include/hndrte_cons.h [new file with mode: 0644]
drivers/net/wireless/bcm4329/wl_iw.c

index 46202e9865f54583cdfdd1d0ffc0f19c06e8acab..6d71341d3c3551df46537c92a93aae46228c25b9 100644 (file)
@@ -4,7 +4,7 @@ DHDCFLAGS = -DLINUX -DBCMDRIVER -DBCMDONGLEHOST -DDHDTHREAD -DBCMWPA2         \
        -DDHD_FIRSTREAD=64 -DDHD_GPL -DDHD_SCHED -DBDC -DTOE -DDHD_BCMEVENTS  \
        -DSHOW_EVENTS -DBCMSDIO -DDHD_GPL -DBCMLXSDMMC -DBCMPLATFORM_BUS      \
        -Wall -Wstrict-prototypes -Werror -DOOB_INTR_ONLY -DCUSTOMER_HW2      \
-       -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DSOFTAP                        \
+       -DDHD_USE_STATIC_BUF -DMMC_SDIO_ABORT -DDHD_DEBUG_TRAP -DSOFTAP       \
        -Idrivers/net/wireless/bcm4329 -Idrivers/net/wireless/bcm4329/include
 
 DHDOFILES = dhd_linux.o linux_osl.o bcmutils.o dhd_common.o dhd_custom_gpio.o \
index 3b22eb73d69e4036431f89d0947288a822c11b8b..845a316240812990d7ce41d03284dbbde9d05dd4 100644 (file)
 
 #include <wlioctl.h>
 
+#ifdef DHD_DEBUG
+#ifndef DHD_DEBUG_TRAP
+#define DHD_DEBUG_TRAP
+#endif
+#endif
 
 /* Forward decls */
 struct dhd_bus;
@@ -320,6 +325,7 @@ typedef enum cust_gpio_modes {
        WLAN_POWER_OFF
 } cust_gpio_modes_t;
 extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag);
+extern int wl_iw_send_priv_event(struct net_device *dev, char *flag);
 /*
  * Insmod parameters for debug/test
  */
@@ -327,6 +333,10 @@ extern int wl_iw_iscan_set_scan_broadcast_prep(struct net_device *dev, uint flag
 /* Watchdog timer interval */
 extern uint dhd_watchdog_ms;
 
+#if defined(DHD_DEBUG)
+/* Console output poll interval */
+extern uint dhd_console_ms;
+#endif
 
 /* Use interrupts */
 extern uint dhd_intr;
index 2661d8f2bb2aa5e35e28358530a209abf9192d25..287a46457644db18aa66d9407107e8d6f2742c8b 100644 (file)
@@ -302,6 +302,11 @@ module_param(dhd_sysioc, uint, 0);
 uint dhd_watchdog_ms = 10;
 module_param(dhd_watchdog_ms, uint, 0);
 
+#ifdef DHD_DEBUG
+/* Console poll interval */
+uint dhd_console_ms = 0;
+module_param(dhd_console_ms, uint, 0);
+#endif
 
 /* Watchdog thread priority, -1 to use kernel timer */
 int dhd_watchdog_prio = 97;
@@ -949,6 +954,11 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
                DHD_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
                         __FUNCTION__, dhd->pub.up, dhd->pub.busstate));
                netif_stop_queue(net);
+               /* Send Event when bus down detected during data session */
+               if (dhd->pub.busstate == DHD_BUS_DOWN)  {
+                       DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+                       wl_iw_send_priv_event(net, "HANG");
+               }
                return -ENODEV;
        }
 
@@ -1627,6 +1637,10 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd)
 
        bcmerror = dhd_prot_ioctl(&dhd->pub, ifidx, (wl_ioctl_t *)&ioc, buf, buflen);
 
+       if (bcmerror == -ETIMEDOUT) {
+               DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
+               wl_iw_send_priv_event(net, "HANG");
+       }
 done:
        if (!bcmerror && buf && ioc.buf) {
                if (copy_to_user(ioc.buf, buf, buflen))
@@ -1646,7 +1660,6 @@ dhd_stop(struct net_device *net)
        dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net);
 
        DHD_TRACE(("%s: Enter\n", __FUNCTION__));
-
        if (dhd->pub.up == 0) {
                return 0;
        }
@@ -2111,6 +2124,8 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
        printf("%s: Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", net->name,
               dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
               dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
+
+#ifdef CONFIG_WIRELESS_EXT
 #ifdef SOFTAP
        if (ifidx == 0)
                /* Don't call for SOFTAP Interface in SOFTAP MODE */
@@ -2118,6 +2133,7 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
 #else
                wl_iw_iscan_set_scan_broadcast_prep(net, 1);
 #endif /* SOFTAP */
+#endif /* CONFIG_WIRELESS_EXT */
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
        up(&dhd_registration_sem);
@@ -2415,12 +2431,12 @@ void
 dhd_os_wd_timer(void *bus, uint wdtick)
 {
        dhd_pub_t *pub = bus;
-       dhd_info_t *dhd = (dhd_info_t *)pub->info;
        static uint save_dhd_watchdog_ms = 0;
+       dhd_info_t *dhd = (dhd_info_t *)pub->info;
 
-       if (pub->busstate == DHD_BUS_DOWN) {
+       /* don't start the wd until fw is loaded */
+       if (pub->busstate == DHD_BUS_DOWN)
                return;
-       }
 
        /* Totally stop the timer */
        if (!wdtick && dhd->wd_timer_valid == TRUE) {
index 5859c4a71c4d96aa78d5609ca4fdd09e4ee32210..d94c70a7b47b19d55e69fc5c06ed8965a4b9f5de 100644 (file)
@@ -21,7 +21,7 @@
  * software in any way with any other Broadcom software provided under a license
  * other than the GPL, without Broadcom's express prior written consent.
  *
- * $Id: dhd_sdio.c,v 1.157.2.27.2.33.2.109 2010/04/22 05:52:46 Exp $
+ * $Id: dhd_sdio.c,v 1.157.2.27.2.33.2.109.2.2 2010/05/18 01:13:11 Exp $
  */
 
 #include <typedefs.h>
 #include <bcmutils.h>
 #include <bcmendian.h>
 #include <bcmdevs.h>
-
 #include <siutils.h>
 #include <hndpmu.h>
 #include <hndsoc.h>
-#include <hndrte_armtrap.h>
 #include <sbchipc.h>
 #include <sbhnddma.h>
-
 #include <sdio.h>
 #include <sbsdio.h>
 #include <sbsdpcmdev.h>
 #include <dhdioctl.h>
 #include <sdiovar.h>
 
+#ifdef DHD_DEBUG
+#include <hndrte_cons.h>
+#endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+#include <hndrte_armtrap.h>
+#endif /* DHD_DEBUG_TRAP */
+
 #define QLEN           256     /* bulk rx and tx queue lengths */
 #define FCHI           (QLEN - 10)
 #define FCLOW          (FCHI / 2)
 DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
 extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
 
+#ifdef DHD_DEBUG
+/* Device console log buffer state */
+typedef struct dhd_console {
+       uint            count;                  /* Poll interval msec counter */
+       uint            log_addr;               /* Log struct address (fixed) */
+       hndrte_log_t    log;                    /* Log struct (host copy) */
+       uint            bufsize;                /* Size of log buffer */
+       uint8           *buf;                   /* Log buffer (host copy) */
+       uint            last;                   /* Last buffer read index */
+} dhd_console_t;
+#endif /* DHD_DEBUG */
 
 /* Private data for SDIO bus interaction */
 typedef struct dhd_bus {
@@ -209,6 +224,10 @@ typedef struct dhd_bus {
        uint            polltick;               /* Tick counter */
        uint            pollcnt;                /* Count of active polls */
 
+#ifdef DHD_DEBUG
+       dhd_console_t   console;                /* Console output polling support */
+       uint            console_addr;           /* Console address from shared struct */
+#endif /* DHD_DEBUG */
 
        uint            regfails;               /* Count of R_REG/W_REG failures */
 
@@ -405,7 +424,10 @@ static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
 static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start);
 #endif
 
+#ifdef DHD_DEBUG_TRAP
 static int dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size);
+static int dhdsdio_mem_dump(dhd_bus_t *bus);
+#endif /* DHD_DEBUG_TRAP */
 static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
 
 static void dhdsdio_release(dhd_bus_t *bus, osl_t *osh);
@@ -1335,17 +1357,21 @@ dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen)
                         __FUNCTION__, rxlen, msglen));
        } else if (timeleft == 0) {
                DHD_ERROR(("%s: resumed on timeout\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
                dhd_os_sdlock(bus->dhd);
                dhdsdio_checkdied(bus, NULL, 0);
                dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
        } else if (pending == TRUE) {
                DHD_CTL(("%s: cancelled\n", __FUNCTION__));
                return -ERESTARTSYS;
        } else {
                DHD_CTL(("%s: resumed for unknown reason?\n", __FUNCTION__));
+#ifdef DHD_DEBUG_TRAP
                dhd_os_sdlock(bus->dhd);
                dhdsdio_checkdied(bus, NULL, 0);
                dhd_os_sdunlock(bus->dhd);
+#endif /* DHD_DEBUG_TRAP */
        }
 
        if (rxlen)
@@ -1365,7 +1391,9 @@ enum {
        IOV_SDCIS,
        IOV_MEMBYTES,
        IOV_MEMSIZE,
+#ifdef DHD_DEBUG_TRAP
        IOV_CHECKDIED,
+#endif
        IOV_DOWNLOAD,
        IOV_FORCEEVEN,
        IOV_SDIOD_DRIVE,
@@ -1416,8 +1444,10 @@ const bcm_iovar_t dhdsdio_iovars[] = {
        {"rxbound",     IOV_RXBOUND,    0,      IOVT_UINT32,    0 },
        {"txminmax", IOV_TXMINMAX,      0,      IOVT_UINT32,    0 },
        {"cpu",         IOV_CPU,        0,      IOVT_BOOL,      0 },
-       {"checkdied",   IOV_CHECKDIED,  0,      IOVT_BUFFER,    0 },
 #endif /* DHD_DEBUG */
+#ifdef DHD_DEBUG_TRAP
+       {"checkdied",   IOV_CHECKDIED,  0,      IOVT_BUFFER,    0 },
+#endif /* DHD_DEBUG_TRAP  */
 #ifdef SDTEST
        {"extloop",     IOV_EXTLOOP,    0,      IOVT_BOOL,      0 },
        {"pktgen",      IOV_PKTGEN,     0,      IOVT_BUFFER,    sizeof(dhd_pktgen_t) },
@@ -1646,6 +1676,7 @@ xfer_done:
        return bcmerror;
 }
 
+#ifdef DHD_DEBUG_TRAP
 static int
 dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
 {
@@ -1692,7 +1723,83 @@ dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
 
        return BCME_OK;
 }
+#endif
 
+#define CONSOLE_LINE_MAX       192
+
+#ifdef DHD_DEBUG
+static int
+dhdsdio_readconsole(dhd_bus_t *bus)
+{
+       dhd_console_t *c = &bus->console;
+       uint8 line[CONSOLE_LINE_MAX], ch;
+       uint32 n, idx, addr;
+       int rv;
+
+       /* Don't do anything until FWREADY updates console address */
+       if (bus->console_addr == 0)
+               return 0;
+
+       /* Read console log struct */
+       addr = bus->console_addr + OFFSETOF(hndrte_cons_t, log);
+       if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&c->log, sizeof(c->log))) < 0)
+               return rv;
+
+       /* Allocate console buffer (one time only) */
+       if (c->buf == NULL) {
+               c->bufsize = ltoh32(c->log.buf_size);
+               if ((c->buf = MALLOC(bus->dhd->osh, c->bufsize)) == NULL)
+                       return BCME_NOMEM;
+       }
+
+       idx = ltoh32(c->log.idx);
+
+       /* Protect against corrupt value */
+       if (idx > c->bufsize)
+               return BCME_ERROR;
+
+       /* Skip reading the console buffer if the index pointer has not moved */
+       if (idx == c->last)
+               return BCME_OK;
+
+       /* Read the console buffer */
+       addr = ltoh32(c->log.buf);
+       if ((rv = dhdsdio_membytes(bus, FALSE, addr, c->buf, c->bufsize)) < 0)
+               return rv;
+
+       while (c->last != idx) {
+               for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+                       if (c->last == idx) {
+                               /* This would output a partial line.  Instead, back up
+                                * the buffer pointer and output this line next time around.
+                                */
+                               if (c->last >= n)
+                                       c->last -= n;
+                               else
+                                       c->last = c->bufsize - n;
+                               goto break2;
+                       }
+                       ch = c->buf[c->last];
+                       c->last = (c->last + 1) % c->bufsize;
+                       if (ch == '\n')
+                               break;
+                       line[n] = ch;
+               }
+
+               if (n > 0) {
+                       if (line[n - 1] == '\r')
+                               n--;
+                       line[n] = 0;
+                       printf("CONSOLE: %s\n", line);
+               }
+       }
+break2:
+
+       return BCME_OK;
+}
+#endif
+
+#ifdef DHD_DEBUG_TRAP
 static int
 dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size)
 {
@@ -1785,7 +1892,7 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size)
                                goto done;
 
                        bcm_bprintf(&strbuf,
-                       "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
+                       "Dongle trap type 0x%x @ pc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
                        "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
                        "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n",
                        tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13, tr.r14, tr.pc,
@@ -1798,6 +1905,11 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size)
                DHD_ERROR(("%s: %s\n", __FUNCTION__, strbuf.origbuf));
        }
 
+       if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+                       /* Mem dump to a file on device */
+                       dhdsdio_mem_dump(bus);
+       }
+
 done:
        if (mbuffer)
                MFREE(bus->dhd->osh, mbuffer, msize);
@@ -1807,6 +1919,14 @@ done:
        return bcmerror;
 }
 
+static int
+dhdsdio_mem_dump(dhd_bus_t *bus)
+{
+       int ret = 0;
+       return ret;
+}
+#endif /* DHD_DEBUG_TRAP */
+
 int
 dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len)
 {
@@ -4489,6 +4609,19 @@ dhd_bus_watchdog(dhd_pub_t *dhdp)
                bus->lastintrs = bus->intrcount;
        }
 
+#ifdef DHD_DEBUG
+       /* Poll for console output periodically */
+       if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) {
+               bus->console.count += dhd_watchdog_ms;
+               if (bus->console.count >= dhd_console_ms) {
+                       bus->console.count -= dhd_console_ms;
+                       /* Make sure backplane clock is on */
+                       dhdsdio_clkctl(bus, CLK_AVAIL, FALSE);
+                       if (dhdsdio_readconsole(bus) < 0)
+                               dhd_console_ms = 0;     /* On error, stop trying */
+               }
+       }
+#endif /* DHD_DEBUG */
 
 #ifdef SDTEST
        /* Generate packets if configured */
index 3ab7ad74a9c0d43a2911641a6394c8d5bc8d1025..a76ea3f85d6e1a73fa4e0df7f38df8570345094b 100644 (file)
 
 #define        EPI_RC_NUMBER           223
 
-#define        EPI_INCREMENTAL_NUMBER  0
+#define        EPI_INCREMENTAL_NUMBER  1
 
 #define        EPI_BUILD_NUMBER        0
 
-#define        EPI_VERSION             4, 218, 223, 0
+#define        EPI_VERSION             4, 218, 223, 1
 
-#define        EPI_VERSION_NUM         0x04dadf00
+#define        EPI_VERSION_NUM         0x04dadf01
 
 
-#define        EPI_VERSION_STR         "4.218.223.0"
-#define        EPI_ROUTER_VERSION_STR  "4.219.223.0"
+#define        EPI_VERSION_STR         "4.218.223.1"
+#define        EPI_ROUTER_VERSION_STR  "4.219.223.1"
 
 #endif 
diff --git a/drivers/net/wireless/bcm4329/include/hndrte_cons.h b/drivers/net/wireless/bcm4329/include/hndrte_cons.h
new file mode 100644 (file)
index 0000000..f2334a0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ *
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ *
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ *
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: hndrte_cons.h,v 13.1.2.3 2010/02/18 01:09:39 Exp $
+ */
+
+#include <typedefs.h>
+
+#define CBUF_LEN       (128)
+#define LOG_BUF_LEN    1024
+
+typedef struct {
+       uint32          buf;
+       uint            buf_size;
+       uint            idx;
+       char            *_buf_compat;
+} hndrte_log_t;
+
+typedef struct {
+       volatile uint   vcons_in;
+       volatile uint   vcons_out;
+       hndrte_log_t    log;
+       uint            cbuf_idx;
+       char            cbuf[CBUF_LEN];
+} hndrte_cons_t;
index 381397b6c737007b92377dd7f76a7bcf948c39ef..8fb3963854754a8942b602f21bb0f7ab12e956c8 100644 (file)
@@ -884,7 +884,7 @@ wl_iw_get_rssi(
        return error;
 }
 
-static int
+int
 wl_iw_send_priv_event(
        struct net_device *dev,
        char *flag