From 0946fa3463a0dcf4cca25c8c66ecbe8900a90435 Mon Sep 17 00:00:00 2001 From: Howard Harte Date: Thu, 20 May 2010 15:07:13 -0700 Subject: [PATCH] network: wireless: bcm4329: Add "HANG" event and console monitoring Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/Makefile | 2 +- drivers/net/wireless/bcm4329/dhd.h | 10 ++ drivers/net/wireless/bcm4329/dhd_linux.c | 24 ++- drivers/net/wireless/bcm4329/dhd_sdio.c | 145 +++++++++++++++++- .../net/wireless/bcm4329/include/epivers.h | 10 +- .../wireless/bcm4329/include/hndrte_cons.h | 43 ++++++ drivers/net/wireless/bcm4329/wl_iw.c | 2 +- 7 files changed, 219 insertions(+), 17 deletions(-) create mode 100644 drivers/net/wireless/bcm4329/include/hndrte_cons.h diff --git a/drivers/net/wireless/bcm4329/Makefile b/drivers/net/wireless/bcm4329/Makefile index 46202e9865f5..6d71341d3c35 100644 --- a/drivers/net/wireless/bcm4329/Makefile +++ b/drivers/net/wireless/bcm4329/Makefile @@ -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 \ diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 3b22eb73d69e..845a31624081 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -59,6 +59,11 @@ #include +#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; diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 2661d8f2bb2a..287a46457644 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -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) { diff --git a/drivers/net/wireless/bcm4329/dhd_sdio.c b/drivers/net/wireless/bcm4329/dhd_sdio.c index 5859c4a71c4d..d94c70a7b47b 100644 --- a/drivers/net/wireless/bcm4329/dhd_sdio.c +++ b/drivers/net/wireless/bcm4329/dhd_sdio.c @@ -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 @@ -36,14 +36,11 @@ #include #include #include - #include #include #include -#include #include #include - #include #include #include @@ -61,6 +58,13 @@ #include #include +#ifdef DHD_DEBUG +#include +#endif /* DHD_DEBUG */ +#ifdef DHD_DEBUG_TRAP +#include +#endif /* DHD_DEBUG_TRAP */ + #define QLEN 256 /* bulk rx and tx queue lengths */ #define FCHI (QLEN - 10) #define FCLOW (FCHI / 2) @@ -142,6 +146,17 @@ 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 */ diff --git a/drivers/net/wireless/bcm4329/include/epivers.h b/drivers/net/wireless/bcm4329/include/epivers.h index 3ab7ad74a9c0..a76ea3f85d6e 100644 --- a/drivers/net/wireless/bcm4329/include/epivers.h +++ b/drivers/net/wireless/bcm4329/include/epivers.h @@ -33,16 +33,16 @@ #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 index 000000000000..f2334a0a3394 --- /dev/null +++ b/drivers/net/wireless/bcm4329/include/hndrte_cons.h @@ -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 + +#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; diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index 381397b6c737..8fb396385475 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -884,7 +884,7 @@ wl_iw_get_rssi( return error; } -static int +int wl_iw_send_priv_event( struct net_device *dev, char *flag -- 2.34.1