From 58389a2823c8f063d9bb329cd5453689b7dad7d1 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 13 Sep 2011 15:51:00 -0700 Subject: [PATCH] net: wireless: bcmdhd: Update to Version 5.90.125.84 - Fix no scan problem for cfg80211 when Scan Event missed - Improve logic to generate Event via cfg80211 when FW hangs - Fix a crash on Android driver removal - DHD read has chance to crash under long stress : fix (dhd_sdio.c) - Fix power consumption issue as sending disassoc explicitly - Change the logic for listen timer to solve memory leak and guarantee listen timing - Adding wl_cfg80211_get_station for HostAPD Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 11 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 8 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 13 +- drivers/net/wireless/bcmdhd/dhd_common.c | 12 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 39 ++- drivers/net/wireless/bcmdhd/dhd_sdio.c | 40 ++- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/include/linuxver.h | 2 + drivers/net/wireless/bcmdhd/include/wlioctl.h | 43 +++ drivers/net/wireless/bcmdhd/linux_osl.c | 34 +- drivers/net/wireless/bcmdhd/wl_android.c | 54 ++- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 397 ++++++++++++++--------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 15 +- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 53 +-- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 15 +- drivers/net/wireless/bcmdhd/wldev_common.c | 9 +- 16 files changed, 512 insertions(+), 241 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c index 8c328b8..a48e85c 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c @@ -198,9 +198,14 @@ sdioh_detach(osl_t *osh, sdioh_info_t *sd) sdio_release_host(gInstance->func[2]); /* Disable Function 1 */ - sdio_claim_host(gInstance->func[1]); - sdio_disable_func(gInstance->func[1]); - sdio_release_host(gInstance->func[1]); + if (gInstance->func[1]) { + sdio_claim_host(gInstance->func[1]); + sdio_disable_func(gInstance->func[1]); + sdio_release_host(gInstance->func[1]); + } + + gInstance->func[1] = NULL; + gInstance->func[2] = NULL; /* deregister irq */ sdioh_sdmmc_osfree(sd); diff --git a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c index c73e04c..8baa60b 100644 --- a/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c +++ b/drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c @@ -26,7 +26,7 @@ #include #include -#include /* SDIO Specs */ +#include /* SDIO Device and Protocol Specs */ #include /* bcmsdh to/from specific controller APIs */ #include /* to get msglevel bit values */ @@ -133,6 +133,11 @@ static void bcmsdh_sdmmc_remove(struct sdio_func *func) if (func->num == 2) { sd_trace(("F2 found, calling bcmsdh_remove...\n")); bcmsdh_remove(&func->dev); + } else if (func->num == 1) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + gInstance->func[1] = NULL; } } @@ -259,7 +264,6 @@ int sdio_function_init(void) error = sdio_register_driver(&bcmsdh_sdmmc_driver); - return error; } diff --git a/drivers/net/wireless/bcmdhd/dhd_cdc.c b/drivers/net/wireless/bcmdhd/dhd_cdc.c index 67d9481..3a4de96 100644 --- a/drivers/net/wireless/bcmdhd/dhd_cdc.c +++ b/drivers/net/wireless/bcmdhd/dhd_cdc.c @@ -53,7 +53,7 @@ * defined in dhd_sdio.c (amount of header tha might be added) * plus any space that might be needed for alignment padding. */ -#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for +#define ROUND_UP_MARGIN 2048 /* Biggest SDIO block size possible for * round off at the end of buffer */ @@ -68,6 +68,7 @@ typedef struct dhd_wlfc_commit_info { void* p; } dhd_wlfc_commit_info_t; #endif /* PROP_TXSTATUS */ + typedef struct dhd_prot { uint16 reqid; uint8 pending; @@ -218,6 +219,7 @@ dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 DHD_TRACE(("%s: Enter\n", __FUNCTION__)); DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len)); + if (dhd->busstate == DHD_BUS_DOWN) { DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__)); return -EIO; @@ -287,7 +289,6 @@ dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len) goto done; } - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); ASSERT(len <= WLC_IOCTL_MAXLEN); @@ -1333,6 +1334,7 @@ _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map return rc; } + int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) @@ -1444,6 +1446,7 @@ _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac, return rc; } + int dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx) { @@ -1808,6 +1811,7 @@ dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info) /* pick up the implicit credit from this packet */ if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) { if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) { + int lender, credit_returned = 0; /* Note that borrower is fifo_id */ /* Return credits to highest priority lender first */ @@ -2227,7 +2231,7 @@ dhd_wlfc_enable(dhd_pub_t *dhd) wlfc->hostif_flow_state[i] = OFF; } - /* + /* create the SENDQ containing sub-queues for all AC precedences + 1 for bc/mc traffic */ @@ -2497,7 +2501,6 @@ dhd_prot_init(dhd_pub_t *dhd) DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - /* Get the device rev info */ memset(&revinfo, 0, sizeof(revinfo)); ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0); @@ -2509,7 +2512,7 @@ dhd_prot_init(dhd_pub_t *dhd) ret = dhd_wlfc_init(dhd); #endif -#ifndef WL_CFG80211 +#if !defined(WL_CFG80211) ret = dhd_preinit_ioctls(dhd); #endif /* WL_CFG80211 */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 94d3262..9996267 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -1432,7 +1432,8 @@ dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable) __FUNCTION__, arp_enable)); } -void dhd_aoe_arp_clr(dhd_pub_t *dhd) +void +dhd_aoe_arp_clr(dhd_pub_t *dhd) { int ret = 0; int iov_len = 0; @@ -1445,7 +1446,8 @@ void dhd_aoe_arp_clr(dhd_pub_t *dhd) DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); } -void dhd_aoe_hostip_clr(dhd_pub_t *dhd) +void +dhd_aoe_hostip_clr(dhd_pub_t *dhd) { int ret = 0; int iov_len = 0; @@ -1458,7 +1460,8 @@ void dhd_aoe_hostip_clr(dhd_pub_t *dhd) DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret)); } -void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr) +void +dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr) { int iov_len = 0; char iovbuf[32]; @@ -1475,7 +1478,8 @@ void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr) __FUNCTION__)); } -int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) +int +dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) { int retcode, i; int iov_len = 0; diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 6f19cb7..42f3528 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -89,6 +89,7 @@ typedef struct histo_ { #if !ISPOWEROF2(DHD_SDALIGN) #error DHD_SDALIGN is not a power of 2! #endif + static histo_t vi_d1, vi_d2, vi_d3, vi_d4; #endif /* WLMEDIA_HTSF */ @@ -1412,7 +1413,6 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) continue; } - pnext = PKTNEXT(dhdp->osh, pktbuf); PKTSETNEXT(wl->sh.osh, pktbuf, NULL); @@ -2192,6 +2192,7 @@ done: return OSL_ERROR(bcmerror); } +#ifdef WL_CFG80211 static int dhd_cleanup_virt_ifaces(dhd_info_t *dhd) { @@ -2226,6 +2227,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) return 0; } +#endif /* WL_CFG80211 */ static int dhd_stop(struct net_device *net) @@ -2666,7 +2668,9 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("Enter %s:\n", __FUNCTION__)); +#ifdef DHDTHREAD dhd_os_sdlock(dhdp); +#endif /* DHDTHREAD */ /* try to download image and nvram to the dongle */ if ((dhd->pub.busstate == DHD_BUS_DOWN) && @@ -2677,12 +2681,16 @@ dhd_bus_start(dhd_pub_t *dhdp) fw_path, nv_path))) { DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", __FUNCTION__, fw_path, nv_path)); +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ return -1; } } if (dhd->pub.busstate != DHD_BUS_LOAD) { +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ return -ENETDOWN; } @@ -2692,21 +2700,27 @@ dhd_bus_start(dhd_pub_t *dhdp) /* Bring up the bus */ if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { + DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ return ret; } #if defined(OOB_INTR_ONLY) /* Host registration for OOB interrupt */ if (bcmsdh_register_oob_intr(dhdp)) { /* deactivate timer and wait for the handler to finish */ + flags = dhd_os_spin_lock(&dhd->pub); dhd->wd_timer_valid = FALSE; dhd_os_spin_unlock(&dhd->pub, flags); del_timer_sync(&dhd->timer); DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ return -ENODEV; } @@ -2721,11 +2735,15 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_os_spin_unlock(&dhd->pub, flags); del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ return -ENODEV; } +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ #ifdef READ_MACADDR dhd_read_macaddr(dhd); @@ -2960,6 +2978,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_AUTH); setbit(eventmask, WLC_E_REASSOC); setbit(eventmask, WLC_E_REASSOC_IND); + setbit(eventmask, WLC_E_DEAUTH); setbit(eventmask, WLC_E_DEAUTH_IND); setbit(eventmask, WLC_E_DISASSOC_IND); setbit(eventmask, WLC_E_DISASSOC); @@ -3195,6 +3214,12 @@ static int dhd_device_event(struct notifier_block *this, DHD_ARPOE(("%s: [%s] Up IP: 0x%x\n", __FUNCTION__, ifa->ifa_label, ifa->ifa_address)); + /* firmware not downloaded, do nothing */ + if (dhd->pub.busstate == DHD_BUS_DOWN) { + DHD_ERROR(("%s: bus is down, exit\n", __FUNCTION__)); + break; + } + #ifdef AOE_IP_ALIAS_SUPPORT if (ifa->ifa_label[strlen(ifa->ifa_label)-2] == 0x3a) { DHD_ARPOE(("%s:add aliased IP to AOE hostip cache\n", @@ -3415,10 +3440,16 @@ void dhd_detach(dhd_pub_t *dhdp) /* delete all interfaces, start with virtual */ if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { + int i = 1; dhd_if_t *ifp; - /* Cleanup all virtual Interfaces */ - dhd_cleanup_virt_ifaces(dhd); + /* Cleanup virtual interfaces */ + for (i = 1; i < DHD_MAX_IFS; i++) + if (dhd->iflist[i]) { + dhd->iflist[i]->state = WLC_E_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } /* delete primary interface 0 */ ifp = dhd->iflist[0]; @@ -4348,7 +4379,7 @@ int dhd_os_wake_lock_timeout(dhd_pub_t *pub) #ifdef CONFIG_HAS_WAKELOCK if (dhd->wakelock_timeout_enable) wake_lock_timeout(&dhd->wl_rxwake, - dhd->wakelock_timeout_enable * HZ); + dhd->wakelock_timeout_enable * HZ); #endif dhd->wakelock_timeout_enable = 0; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index cdb2448..37ab680 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -380,7 +380,6 @@ static const uint max_roundup = 512; /* Try doing readahead */ static bool dhd_readahead; - /* To check if there's window offered */ #define DATAOK(bus) \ (((uint8)(bus->tx_max - bus->tx_seq) > 2) && \ @@ -2021,7 +2020,8 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) bcm_bprintf(&strbuf, "Dongle trap type 0x%x @ epc 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\n", + "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\n", ltoh32(tr.type), ltoh32(tr.epc), ltoh32(tr.cpsr), ltoh32(tr.spsr), ltoh32(tr.r13), ltoh32(tr.r14), ltoh32(tr.pc), ltoh32(sdpcm_shared.trap_addr), @@ -2029,30 +2029,35 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) ltoh32(tr.r4), ltoh32(tr.r5), ltoh32(tr.r6), ltoh32(tr.r7)); addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log); - if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_ptr, sizeof(console_ptr))) < 0) goto printbuf; addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.buf_size); - if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_size, sizeof(console_size))) < 0) + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_size, sizeof(console_size))) < 0) goto printbuf; addr = sdpcm_shared.console_addr + OFFSETOF(hndrte_cons_t, log.idx); - if ((rv = dhdsdio_membytes(bus, FALSE, addr, (uint8 *)&console_index, sizeof(console_index))) < 0) + if ((rv = dhdsdio_membytes(bus, FALSE, addr, + (uint8 *)&console_index, sizeof(console_index))) < 0) goto printbuf; console_ptr = ltoh32(console_ptr); console_size = ltoh32(console_size); console_index = ltoh32(console_index); - if (console_size > CONSOLE_BUFFER_MAX || !(console_buffer = MALLOC(bus->dhd->osh, console_size))) + if (console_size > CONSOLE_BUFFER_MAX || + !(console_buffer = MALLOC(bus->dhd->osh, console_size))) goto printbuf; - if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, (uint8 *)console_buffer, console_size)) < 0) + if ((rv = dhdsdio_membytes(bus, FALSE, console_ptr, + (uint8 *)console_buffer, console_size)) < 0) goto printbuf; - for ( i = 0, n = 0; i < console_size; i += n + 1 ) { + for (i = 0, n = 0; i < console_size; i += n + 1) { for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { - ch = console_buffer[ (console_index + i + n) % console_size]; + ch = console_buffer[(console_index + i + n) % console_size]; if (ch == '\n') break; line[n] = ch; @@ -2063,8 +2068,10 @@ dhdsdio_checkdied(dhd_bus_t *bus, uint8 *data, uint size) if (line[n - 1] == '\r') n--; line[n] = 0; - /* Don't use DHD_ERROR macro since we print a lot of information quickly */ - /* The macro will truncate a lot of the printfs */ + /* Don't use DHD_ERROR macro since we print + * a lot of information quickly. The macro + * will truncate a lot of the printfs + */ if (dhd_msg_level & DHD_ERROR_VAL) printf("CONSOLE: %s\n", line); @@ -3541,7 +3548,7 @@ dhdsdio_rxglom(dhd_bus_t *bus, uint8 rxseq) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq + 2; + txmax = bus->tx_seq; } bus->tx_max = txmax; @@ -3962,7 +3969,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq + 2; + txmax = bus->tx_seq; } bus->tx_max = txmax; @@ -4119,7 +4126,7 @@ dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished) if ((uint8)(txmax - bus->tx_seq) > 0x40) { DHD_ERROR(("%s: got unlikely tx max %d with tx_seq %d\n", __FUNCTION__, txmax, bus->tx_seq)); - txmax = bus->tx_seq + 2; + txmax = bus->tx_seq; } bus->tx_max = txmax; @@ -6182,8 +6189,9 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) if (bus->dhd->dongle_reset) { /* Turn on WLAN */ +#ifdef DHDTHREAD dhd_os_sdlock(dhdp); - +#endif /* DHDTHREAD */ /* Reset SD client */ bcmsdh_reset(bus->sdh); @@ -6223,7 +6231,9 @@ dhd_bus_devreset(dhd_pub_t *dhdp, uint8 flag) } else bcmerror = BCME_SDIO_ERROR; +#ifdef DHDTHREAD dhd_os_sdunlock(dhdp); +#endif /* DHDTHREAD */ } else { bcmerror = BCME_SDIO_ERROR; DHD_INFO(("%s called when dongle is not in reset\n", diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index ab96c07..60f842c 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 125 -#define EPI_INCREMENTAL_NUMBER 78 +#define EPI_INCREMENTAL_NUMBER 84 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 125, 78 +#define EPI_VERSION 5, 90, 125, 84 -#define EPI_VERSION_NUM 0x055a7d4e +#define EPI_VERSION_NUM 0x055a7d54 #define EPI_VERSION_DEV 5.90.125 -#define EPI_VERSION_STR "5.90.125.78" +#define EPI_VERSION_STR "5.90.125.84" #endif diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h index e1c62b7..96844db 100644 --- a/drivers/net/wireless/bcmdhd/include/linuxver.h +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -70,7 +70,9 @@ #include #include #include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) #include +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) #undef IP_TOS #endif diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index 1059de1..9357552 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -657,6 +657,49 @@ typedef struct wl_assoc_info { #define WLC_ASSOC_REQ_IS_REASSOC 0x01 +typedef struct { + uint16 ver; + uint16 len; + uint16 cap; + uint32 flags; + uint32 idle; + struct ether_addr ea; + wl_rateset_t rateset; + uint32 in; + uint32 listen_interval_inms; + uint32 tx_pkts; + uint32 tx_failures; + uint32 rx_ucast_pkts; + uint32 rx_mcast_pkts; + uint32 tx_rate; + uint32 rx_rate; + uint32 rx_decrypt_succeeds; + uint32 rx_decrypt_failures; +} sta_info_t; + +#define WL_OLD_STAINFO_SIZE OFFSETOF(sta_info_t, tx_pkts) + +#define WL_STA_VER 3 + + +#define WL_STA_BRCM 0x1 +#define WL_STA_WME 0x2 +#define WL_STA_ABCAP 0x4 +#define WL_STA_AUTHE 0x8 +#define WL_STA_ASSOC 0x10 +#define WL_STA_AUTHO 0x20 +#define WL_STA_WDS 0x40 +#define WL_STA_WDS_LINKUP 0x80 +#define WL_STA_PS 0x100 +#define WL_STA_APSD_BE 0x200 +#define WL_STA_APSD_BK 0x400 +#define WL_STA_APSD_VI 0x800 +#define WL_STA_APSD_VO 0x1000 +#define WL_STA_N_CAP 0x2000 +#define WL_STA_SCBSTATS 0x4000 + +#define WL_WDS_LINKUP WL_STA_WDS_LINKUP + #define WLC_TXFILTER_OVERRIDE_DISABLED 0 #define WLC_TXFILTER_OVERRIDE_ENABLED 1 diff --git a/drivers/net/wireless/bcmdhd/linux_osl.c b/drivers/net/wireless/bcmdhd/linux_osl.c index 17b68e5..1a54437 100644 --- a/drivers/net/wireless/bcmdhd/linux_osl.c +++ b/drivers/net/wireless/bcmdhd/linux_osl.c @@ -68,6 +68,7 @@ typedef struct bcm_static_pkt { struct semaphore osl_pkt_sem; unsigned char pkt_use[STATIC_PKT_MAX_NUM * 2]; } bcm_static_pkt_t; + static bcm_static_pkt_t *bcm_static_skb = 0; #endif @@ -169,10 +170,14 @@ osl_t * osl_attach(void *pdev, uint bustype, bool pkttag) { osl_t *osh; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; osh = kmalloc(sizeof(osl_t), flags); +#else + osh = kmalloc(sizeof(osl_t), GFP_ATOMIC); +#endif ASSERT(osh); bzero(osh, sizeof(osl_t)); @@ -218,13 +223,10 @@ osl_attach(void *pdev, uint bustype, bool pkttag) sema_init(&bcm_static_buf->static_sem, 1); - bcm_static_buf->buf_ptr = (unsigned char *)bcm_static_buf + STATIC_BUF_SIZE; - } - if (!bcm_static_skb) - { + if (!bcm_static_skb) { int i; void *skb_buff_ptr = 0; bcm_static_skb = (bcm_static_pkt_t *)((char *)bcm_static_buf + 2048); @@ -251,7 +253,7 @@ osl_detach(osl_t *osh) kfree(osh); } -struct sk_buff *osl_alloc_skb(unsigned int len) +static struct sk_buff *osl_alloc_skb(unsigned int len) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; @@ -325,10 +327,14 @@ osl_ctfpool_replenish(osl_t *osh, uint thresh) int32 osl_ctfpool_init(osl_t *osh, uint numobj, uint size) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; osh->ctfpool = kmalloc(sizeof(ctfpool_t), flags); +#else + osh->ctfpool = kmalloc(sizeof(ctfpool_t), GFP_ATOMIC); +#endif ASSERT(osh->ctfpool); bzero(osh->ctfpool, sizeof(ctfpool_t)); @@ -543,20 +549,17 @@ osl_pktfree(osl_t *osh, void *p, bool send) } #ifdef DHD_USE_STATIC_BUF -void* +void * osl_pktget_static(osl_t *osh, uint len) { - int i = 0; + int i; struct sk_buff *skb; - if (len > (PAGE_SIZE * 2)) { printk("%s: attempt to allocate huge packet (0x%x)\n", __FUNCTION__, len); - printk("Do we really need this big skb??\n"); return osl_pktget(osh, len); } - down(&bcm_static_skb->osl_pkt_sem); if (len <= PAGE_SIZE) { @@ -614,7 +617,6 @@ osl_pktfree_static(osl_t *osh, void *p, bool send) down(&bcm_static_skb->osl_pkt_sem); bcm_static_skb->pkt_use[i + STATIC_PKT_MAX_NUM] = 0; up(&bcm_static_skb->osl_pkt_sem); - return; } } @@ -622,6 +624,7 @@ osl_pktfree_static(osl_t *osh, void *p, bool send) return osl_pktfree(osh, p, send); } #endif + uint32 osl_pci_read_config(osl_t *osh, uint offset, uint size) { @@ -702,13 +705,18 @@ void * osl_malloc(osl_t *osh, uint size) { void *addr; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; + if (osh) ASSERT(osh->magic == OS_HANDLE_MAGIC); flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; if ((addr = kmalloc(size, flags)) == NULL) { +#else + if ((addr = kmalloc(size, GFP_ATOMIC)) == NULL) { +#endif if (osh) osh->failed++; return (NULL); @@ -836,10 +844,14 @@ void * osl_pktdup(osl_t *osh, void *skb) { void * p; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) gfp_t flags; flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; if ((p = skb_clone((struct sk_buff *)skb, flags)) == NULL) +#else + if ((p = skb_clone((struct sk_buff*)skb, GFP_ATOMIC)) == NULL) +#endif return NULL; #ifdef CTFPOOL diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 36110f95..8bc6b36 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -110,7 +110,8 @@ void dhd_dev_init_ioctl(struct net_device *dev); int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr); int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command); #else -int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) { return 0; } +int wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pdev_addr) +{ return 0; } #endif extern bool ap_fw_loaded; @@ -158,6 +159,7 @@ static int wl_android_get_rssi(struct net_device *net, char *command, int total_ error = wldev_get_rssi(net, &rssi); if (error) return -1; + error = wldev_get_ssid(net, &ssid); if (error) return -1; @@ -187,7 +189,7 @@ static int wl_android_set_suspendopt(struct net_device *dev, char *command, int if (ret_now != suspend_flag) { if (!(ret = net_os_set_suspend(dev, ret_now))) DHD_INFO(("%s: Suspend Flag %d -> %d\n", - __FUNCTION__, ret_now, suspend_flag)); + __FUNCTION__, ret_now, suspend_flag)); else DHD_ERROR(("%s: failed %d\n", __FUNCTION__, ret)); } @@ -220,6 +222,27 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t int pno_repeat = 0; int pno_freq_expo_max = 0; +#ifdef PNO_SET_DEBUG + int i; + char pno_in_example[] = { + 'P', 'N', 'O', 'S', 'E', 'T', 'U', 'P', ' ', + 'S', '1', '2', '0', + 'S', + 0x05, + 'd', 'l', 'i', 'n', 'k', + 'S', + 0x04, + 'G', 'O', 'O', 'G', + 'T', + '0', 'B', + 'R', + '2', + 'M', + '2', + 0x00 + }; +#endif /* PNO_SET_DEBUG */ + DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { @@ -227,6 +250,14 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t goto exit_proc; } +#ifdef PNO_SET_DEBUG + memcpy(command, pno_in_example, sizeof(pno_in_example)); + for (i = 0; i < sizeof(pno_in_example); i++) + printf("%02X ", command[i]); + printf("\n"); + total_len = sizeof(pno_in_example); +#endif + str_ptr = command + strlen(CMD_PNOSETUP_SET); tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); @@ -448,7 +479,6 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) /* TBD: BTCOEXSCAN-STOP */ } else if (strnicmp(command, CMD_BTCOEXMODE, strlen(CMD_BTCOEXMODE)) == 0) { - /* TBD: BTCOEXMODE */ uint mode = *(command + strlen(CMD_BTCOEXMODE) + 1) - '0'; if (mode == 1) @@ -527,8 +557,10 @@ int wl_android_init(void) dhd_download_fw_on_driverload = FALSE; #endif /* ENABLE_INSMOD_NO_FW_LOAD */ #ifdef CUSTOMER_HW2 - if (!iface_name[0]) + if (!iface_name[0]) { + memset(iface_name, 0, IFNAMSIZ); bcm_strncpy_s(iface_name, IFNAMSIZ, "wlan", IFNAMSIZ); + } #endif /* CUSTOMER_HW2 */ return ret; } @@ -542,12 +574,24 @@ int wl_android_exit(void) int wl_android_post_init(void) { + struct net_device *ndev; int ret = 0; + char buf[IFNAMSIZ]; if (!dhd_download_fw_on_driverload) { /* Call customer gpio to turn off power with WL_REG_ON signal */ dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); g_wifi_on = 0; - + } else { + memset(buf, 0, IFNAMSIZ); +#ifdef CUSTOMER_HW2 + snprintf(buf, IFNAMSIZ, "%s%d", iface_name, 0); +#else + snprintf(buf, IFNAMSIZ, "%s%d", "eth", 0); +#endif + if ((ndev = dev_get_by_name (&init_net, buf)) != NULL) { + dhd_dev_init_ioctl(ndev); + dev_put(ndev); + } } return ret; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 8a77231..13530d2 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -52,7 +52,6 @@ #include #include #include - #include #include #include @@ -80,7 +79,6 @@ u32 wl_dbg_level = WL_DBG_ERR; #define WL_4329_FW_FILE "brcm/bcm4329-fullmac-4-218-248-5.bin" #define WL_4329_NVRAM_FILE "brcm/bcm4329-fullmac-4-218-248-5.txt" -#define WL_TRACE(a) printk("%s ", __FUNCTION__); printk a #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" #define MAX_WAIT_TIME 1500 @@ -1318,7 +1316,7 @@ wl_run_iscan(struct wl_iscan_ctrl *iscan, struct cfg80211_scan_request *request, iscan->ioctl_buf, WLC_IOCTL_MEDLEN); if (unlikely(err)) { if (err == -EBUSY) { - WL_INFO(("system busy : iscan canceled\n")); + WL_ERR(("system busy : iscan canceled\n")); } else { WL_ERR(("error (%d)\n", err)); } @@ -1498,6 +1496,8 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, s32 i; u32 wpsie_len = 0; u8 wpsie[IE_MAX_LEN]; + + WL_DBG(("Enter wiphy (%p)\n", wiphy)); if (unlikely(wl_get_drv_status(wl, SCANNING))) { WL_ERR(("Scanning already : status (%d)\n", (int)wl->status)); return -EAGAIN; @@ -1512,8 +1512,8 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, return -EOPNOTSUPP; } - WL_DBG(("wiphy (%p)\n", wiphy)); - + /* Arm scan timeout timer */ + mod_timer(&wl->scan_timeout, jiffies + WL_SCAN_TIMER_INTERVAL_MS * HZ / 1000); iscan_req = false; spec_scan = false; if (request) { /* scan bss */ @@ -1640,7 +1640,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, sizeof(sr->ssid), false); if (err) { if (err == -EBUSY) { - WL_INFO(("system busy : scan for \"%s\" " + WL_ERR(("system busy : scan for \"%s\" " "canceled\n", sr->ssid.SSID)); } else { WL_ERR(("WLC_SCAN error (%d)\n", err)); @@ -2240,24 +2240,34 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, wl->channel = 0; WL_DBG(("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len)); err = wl_set_wpa_version(dev, sme); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("Invalid wpa_version\n")); return err; + } err = wl_set_auth_type(dev, sme); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("Invalid auth type\n")); return err; + } err = wl_set_set_cipher(dev, sme); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("Invalid ciper\n")); return err; + } err = wl_set_key_mgmt(dev, sme); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("Invalid key mgmt\n")); return err; + } err = wl_set_set_sharedkey(dev, sme); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("Invalid shared key\n")); return err; + } wl_update_prof(wl, NULL, sme->bssid, WL_PROF_BSSID); /* @@ -2284,7 +2294,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, join_params.ssid.SSID_len)); } wl_set_drv_status(wl, CONNECTING); - err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, false); + err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, true); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); wl_clr_drv_status(wl, CONNECTING); @@ -2317,7 +2327,7 @@ wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, memcpy(&scbval.ea, &wl->bssid, ETHER_ADDR_LEN); scbval.val = htod32(scbval.val); err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, - sizeof(scb_val_t), false); + sizeof(scb_val_t), true); if (unlikely(err)) { wl_clr_drv_status(wl, DISCONNECTING); WL_ERR(("error (%d)\n", err)); @@ -2359,7 +2369,7 @@ wl_cfg80211_set_tx_power(struct wiphy *wiphy, /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; disable = htod32(disable); - err = wldev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable), false); + err = wldev_ioctl(ndev, WLC_SET_RADIO, &disable, sizeof(disable), true); if (unlikely(err)) { WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); return err; @@ -2422,7 +2432,7 @@ wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, index = (u32) key_idx; index = htod32(index); err = wldev_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, - sizeof(index), false); + sizeof(index), true); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); } @@ -2616,16 +2626,6 @@ exit: return err; } -#ifdef NOT_YET - /* TODO: Removed in P2P, check later --lm */ - val = 1; /* assume shared key. otherwise 0 */ - val = htod32(val); - err = wldev_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val), false); - if (unlikely(err)) { - WL_ERR(("WLC_SET_AUTH error (%d)\n", err)); - return err; - } -#endif return err; } @@ -2734,51 +2734,72 @@ wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, { struct wl_priv *wl = wiphy_priv(wiphy); scb_val_t scb_val; - int rssi; + s32 rssi; s32 rate; s32 err = 0; + sta_info_t *sta; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + s8 eabuf[ETHER_ADDR_STR_LEN]; +#endif CHECK_SYS_UP(wl); - if (unlikely - (memcmp(mac, wl_read_prof(wl, WL_PROF_BSSID), ETHER_ADDR_LEN))) { - WL_ERR(("Wrong Mac address\n")); - return -ENOENT; - } - - /* Report the current tx rate */ - err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); - if (err) { - WL_ERR(("Could not get rate (%d)\n", err)); - } else { - rate = dtoh32(rate); - sinfo->filled |= STATION_INFO_TX_BITRATE; - sinfo->txrate.legacy = rate * 5; - WL_DBG(("Rate %d Mbps\n", (rate / 2))); - } - - if (wl_get_drv_status(wl, CONNECTED)) { - memset(&scb_val, 0, sizeof(scb_val)); - scb_val.val = 0; - err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, - sizeof(scb_val_t), false); - if (unlikely(err)) { - WL_ERR(("Could not get rssi (%d)\n", err)); + if (get_mode_by_netdev(wl, dev) == WL_MODE_AP) { + err = wldev_iovar_getbuf(dev, "sta_info", (struct ether_addr *)mac, + ETHER_ADDR_LEN, ioctlbuf, sizeof(ioctlbuf)); + if (err < 0) { + WL_ERR(("GET STA INFO failed, %d\n", err)); return err; } - rssi = dtoh32(scb_val.val); - sinfo->filled |= STATION_INFO_SIGNAL; - sinfo->signal = rssi; - WL_DBG(("RSSI %d dBm\n", rssi)); - } - -#if defined(ANDROID_WIRELESS_PATCH) - err = wldev_ioctl(dev, WLC_GET_RATE, &sinfo->link_speed, sizeof(sinfo->link_speed), false); - sinfo->link_speed = sinfo->link_speed / 2; /* Convert internal 500Kbps to Mpbs */ - if (!err) - sinfo->filled |= STATION_LINK_SPEED; - else - WL_ERR(("WLC_GET_RATE failed\n")); + sinfo->filled = STATION_INFO_INACTIVE_TIME; + sta = (sta_info_t *)ioctlbuf; + sta->len = dtoh16(sta->len); + sta->cap = dtoh16(sta->cap); + sta->flags = dtoh32(sta->flags); + sta->idle = dtoh32(sta->idle); + sta->in = dtoh32(sta->in); + sinfo->inactive_time = sta->idle * 1000; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + if (sta->flags & WL_STA_ASSOC) { + sinfo->filled |= STATION_INFO_CONNECTED_TIME; + sinfo->connected_time = sta->in; + } + WL_INFO(("STA %s : idle time : %d sec, connected time :%d ms\n", + bcm_ether_ntoa((const struct ether_addr *)mac, eabuf), sinfo->inactive_time, + sta->idle * 1000)); #endif + } else if (get_mode_by_netdev(wl, dev) == WL_MODE_BSS) { + if (memcmp(mac, wl_read_prof(wl, WL_PROF_BSSID), + ETHER_ADDR_LEN)) { + WL_ERR(("Wrong Mac address\n")); + return -ENOENT; + } + + /* Report the current tx rate */ + err = wldev_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate), false); + if (err) { + WL_ERR(("Could not get rate (%d)\n", err)); + } else { + rate = dtoh32(rate); + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + WL_DBG(("Rate %d Mbps\n", (rate / 2))); + } + + if (wl_get_drv_status(wl, CONNECTED)) { + memset(&scb_val, 0, sizeof(scb_val)); + scb_val.val = 0; + err = wldev_ioctl(dev, WLC_GET_RSSI, &scb_val, + sizeof(scb_val_t), false); + if (err) { + WL_ERR(("Could not get rssi (%d)\n", err)); + return err; + } + rssi = dtoh32(scb_val.val); + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + WL_DBG(("RSSI %d dBm\n", rssi)); + } + } return err; } @@ -2800,7 +2821,7 @@ wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, } pm = htod32(pm); WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); - err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), false); + err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { if (err == -ENODEV) WL_DBG(("net_device is not ready yet\n")); @@ -2862,6 +2883,8 @@ static s32 wl_cfg80211_suspend(struct wiphy *wiphy) { struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *ndev = wl_to_prmry_ndev(wl); + unsigned long flags; + if (unlikely(!wl_get_drv_status(wl, READY))) { WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); @@ -2875,16 +2898,14 @@ static s32 wl_cfg80211_suspend(struct wiphy *wiphy) wl_delay(500); return -EAGAIN; } + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; - /* Make sure WPA_supplicant receives scan abort event */ - wl_delay(500); - return -EAGAIN; } wl_clr_drv_status(wl, SCANNING); wl_clr_drv_status(wl, SCAN_ABORTING); - + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); return 0; } @@ -3054,12 +3075,14 @@ wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size) *out_params_size = params_size; /* rtn size to the caller */ return params; } + s32 wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev) { wl_scan_params_t *params; s32 params_size; s32 err = BCME_OK; + unsigned long flags; WL_DBG(("Enter\n")); @@ -3070,13 +3093,22 @@ wl_cfg80211_scan_abort(struct wl_priv *wl, struct net_device *ndev) err = -ENOMEM; } /* Do a scan abort to stop the driver's scan engine */ - err = wldev_ioctl(ndev, WLC_SCAN, params, params_size, false); + err = wldev_ioctl(ndev, WLC_SCAN, params, params_size, true); if (err < 0) { WL_ERR(("scan abort failed \n")); } + del_timer_sync(&wl->scan_timeout); + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); + if (wl->scan_request) { + cfg80211_scan_done(wl->scan_request, true); + wl->scan_request = NULL; + } + wl_clr_drv_status(wl, SCANNING); + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); return err; } + static s32 wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel * channel, @@ -3322,7 +3354,7 @@ wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, channel = ieee80211_frequency_to_channel(chan->center_freq); WL_DBG(("netdev_ifidx(%d), chan_type(%d) target channel(%d) \n", dev->ifindex, channel_type, channel)); - wldev_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel), false); + wldev_ioctl(dev, WLC_SET_CHANNEL, &channel, sizeof(channel), true); return err; } @@ -3578,7 +3610,7 @@ exit: } static s32 -wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, +wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev, struct beacon_parameters *info) { s32 err = BCME_OK; @@ -3654,8 +3686,6 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, } wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len + p2pie_len); - wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_ASSOCRSP_FLAG, - beacon_ie, wpsie_len + p2pie_len); /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -3670,7 +3700,7 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, WL_ERR(("WPA2 IE parsing error")); goto exit; } - err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), false); + err = wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); if (err < 0) { WL_ERR(("SET INFRA error %d\n", err)); goto exit; @@ -3698,9 +3728,9 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, WL_DBG(("SSID is (%s) in Head \n", ssid.SSID)); ssid.SSID_len = ssid_ie->len; wldev_iovar_setint(dev, "mpc", 0); - wldev_ioctl(dev, WLC_DOWN, &ap, sizeof(s32), false); - wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), false); - if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), false)) < 0) { + wldev_ioctl(dev, WLC_DOWN, &ap, sizeof(s32), true); + wldev_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(s32), true); + if ((err = wldev_ioctl(dev, WLC_SET_AP, &ap, sizeof(s32), true)) < 0) { WL_ERR(("setting AP mode failed %d \n", err)); return err; } @@ -3756,7 +3786,21 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_DBG(("No WPSIE in beacon \n")); } - err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), false); + if (info->interval) { + if ((err = wldev_ioctl(dev, WLC_SET_BCNPRD, + &info->interval, sizeof(s32), true)) < 0) { + WL_ERR(("Beacon Interval Set Error, %d\n", err)); + return err; + } + } + if (info->dtim_period) { + if ((err = wldev_ioctl(dev, WLC_SET_DTIMPRD, + &info->dtim_period, sizeof(s32), true)) < 0) { + WL_ERR(("DTIM Interval Set Error, %d\n", err)); + return err; + } + } + err = wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), true); if (unlikely(err)) { WL_ERR(("WLC_UP error (%d)\n", err)); return err; @@ -3768,7 +3812,7 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, join_params.ssid.SSID_len = htod32(ssid.SSID_len); /* create softap */ if ((err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, - join_params_size, false)) == 0) { + join_params_size, true)) == 0) { wl_clr_drv_status(wl, AP_CREATING); wl_set_drv_status(wl, AP_CREATED); } @@ -3880,26 +3924,6 @@ exit: return err; } -#if defined(ANDROID_WIRELESS_PATCH) -static s32 -wl_cfg80211_drv_start(struct wiphy *wiphy, struct net_device *dev) -{ - s32 err = 0; - - printk("Android driver start command\n"); - return err; -} - -static s32 -wl_cfg80211_drv_stop(struct wiphy *wiphy, struct net_device *dev) -{ - s32 err = 0; - - printk("Android driver stop command\n"); - return err; -} -#endif /* defined(ANDROID_WIRELESS_PATCH) */ - static struct cfg80211_ops wl_cfg80211_ops = { .add_virtual_intf = wl_cfg80211_add_virtual_iface, .del_virtual_intf = wl_cfg80211_del_virtual_iface, @@ -3930,11 +3954,8 @@ static struct cfg80211_ops wl_cfg80211_ops = { .mgmt_frame_register = wl_cfg80211_mgmt_frame_register, .change_bss = wl_cfg80211_change_bss, .set_channel = wl_cfg80211_set_channel, - .set_beacon = wl_cfg80211_set_beacon, -#if defined(ANDROID_WIRELESS_PATCH) - .drv_start = wl_cfg80211_drv_start, - .drv_stop = wl_cfg80211_drv_stop -#endif + .set_beacon = wl_cfg80211_add_set_beacon, + .add_beacon = wl_cfg80211_add_set_beacon, }; static s32 wl_mode_to_nl80211_iftype(s32 mode) @@ -4132,9 +4153,21 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) mgmt->u.beacon.capab_info, &bi->BSSID)); signal = notif_bss_info->rssi * 100; + + if (wl->p2p_supported && p2p_on(wl)) { + /* &&TODO !memcmp(mgmt->bssid, wl_to_p2p_bss(wl, P2PAPI_BSSCFG_DEVICE), bi->SSID_len) */ + /* && TODO !memcmp(bi->SSID, wl_to_p2p_bss(wl, P2PAPI_BSSCFG_CONNECTION), bi->SSID_len) */ + /* find the P2PIE, if we do not find it, we will discard this frame */ + wifi_p2p_ie_t * p2p_ie; + if ((p2p_ie = wl_cfgp2p_find_p2pie((u8 *)beacon_proberesp->variable, + wl_get_ielen(wl))) == NULL) { + WL_ERR(("Couldn't find P2PIE in probe response/beacon\n")); + kfree(notif_bss_info); + return err; + } + } if (unlikely(!cfg80211_inform_bss_frame(wiphy, channel, mgmt, - le16_to_cpu - (notif_bss_info->frame_len), + le16_to_cpu(notif_bss_info->frame_len), signal, GFP_KERNEL))) { WL_ERR(("cfg80211_inform_bss_frame error\n")); kfree(notif_bss_info); @@ -4226,7 +4259,8 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, if (get_mode_by_netdev(wl, ndev) == WL_MODE_AP) { memcpy(body, data, len); wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - &da, sizeof(struct ether_addr), ioctlbuf, sizeof(ioctlbuf), bsscfgidx); + NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); + memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); err = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); switch (event) { case WLC_E_ASSOC_IND: @@ -4239,10 +4273,10 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, fc = FC_DISASSOC; break; case WLC_E_DEAUTH_IND: - fc = FC_DEAUTH; + fc = FC_DISASSOC; break; case WLC_E_DEAUTH: - fc = FC_DEAUTH; + fc = FC_DISASSOC; break; default: fc = 0; @@ -4274,7 +4308,7 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, } else if (event == WLC_E_DISASSOC_IND) { cfg80211_send_disassoc(ndev, mgmt_frame, len); } else if ((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH)) { - cfg80211_send_deauth(ndev, mgmt_frame, len); + cfg80211_send_disassoc(ndev, mgmt_frame, len); } } else { @@ -4285,13 +4319,14 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, act = true; wl_update_prof(wl, e, &act, WL_PROF_ACT); if (wl_is_ibssmode(wl, ndev)) { - printk("cfg80211_ibss_joined"); + printk("cfg80211_ibss_joined\n"); cfg80211_ibss_joined(ndev, (s8 *)&e->addr, GFP_KERNEL); WL_DBG(("joined in IBSS network\n")); } else { if (!wl_get_drv_status(wl, DISCONNECTING)) { - printk("wl_bss_connect_done succeeded"); + printk("wl_bss_connect_done succeeded status=(0x%x)\n", + (int)wl->status); wl_bss_connect_done(wl, ndev, e, data, true); WL_DBG(("joined in BSS network \"%s\"\n", ((struct wlc_ssid *) @@ -4301,31 +4336,49 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, } else if (wl_is_linkdown(wl, e)) { if (wl->scan_request) { + del_timer_sync(&wl->scan_timeout); if (wl->escan_on) { wl_notify_escan_complete(wl, true); } else wl_iscan_aborted(wl); } if (wl_get_drv_status(wl, CONNECTED)) { - printk("link down, call cfg80211_disconnected "); + scb_val_t scbval; + printk("link down, call cfg80211_disconnected\n"); wl_clr_drv_status(wl, CONNECTED); + /* To make sure disconnect, explictly send dissassoc + * for BSSID 00:00:00:00:00:00 issue + */ + scbval.val = WLAN_REASON_DEAUTH_LEAVING; + memcpy(&scbval.ea, &wl->bssid, ETHER_ADDR_LEN); + scbval.val = htod32(scbval.val); + wldev_ioctl(ndev, WLC_DISASSOC, &scbval, + sizeof(scb_val_t), true); cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); wl_link_down(wl); wl_init_prof(wl->profile); } else if (wl_get_drv_status(wl, CONNECTING)) { - printk("link down, during connecting"); + printk("link down, during connecting\n"); wl_bss_connect_done(wl, ndev, e, data, false); } wl_clr_drv_status(wl, DISCONNECTING); } else if (wl_is_nonetwork(wl, e)) { - printk("connect failed e->status 0x%x", (int)ntoh32(e->status)); + printk("connect failed event=%d e->status 0x%x\n", + event, (int)ntoh32(e->status)); + /* Clean up any pending scan request */ + if (wl->scan_request) { + del_timer_sync(&wl->scan_timeout); + if (wl->escan_on) { + wl_notify_escan_complete(wl, true); + } else + wl_iscan_aborted(wl); + } if (wl_get_drv_status(wl, CONNECTING)) wl_bss_connect_done(wl, ndev, e, data, false); } else { printk("%s nothing\n", __FUNCTION__); } - printk("\n"); } exit: if (isfree) @@ -4362,7 +4415,7 @@ wl_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len) buflen = bcm_mkiovar(name, buf, len, wl->ioctl_buf, WL_IOCTL_LEN_MAX); BUG_ON(unlikely(!buflen)); - return wldev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen, false); + return wldev_ioctl(dev, WLC_SET_VAR, wl->ioctl_buf, buflen, true); } static s32 @@ -4620,8 +4673,10 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, conn_info->resp_ie_len, completed ? WLAN_STATUS_SUCCESS : WLAN_STATUS_AUTH_TIMEOUT, GFP_KERNEL); - WL_DBG(("Report connect result - connection %s\n", - completed ? "succeeded" : "failed")); + if (completed) + WL_INFO(("Report connect result - connection succeeded\n")); + else + WL_ERR(("Report connect result - connection failed\n")); } return err; } @@ -4654,6 +4709,7 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, struct wl_scan_results *bss_list; u32 len = WL_SCAN_BUF_MAX; s32 err = 0; + unsigned long flags; WL_DBG(("Enter \n")); if (wl->iscan_on && wl->iscan_kickstart) @@ -4697,11 +4753,14 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, goto scan_done_out; scan_done_out: + del_timer_sync(&wl->scan_timeout); + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); if (wl->scan_request) { WL_DBG(("cfg80211_scan_done\n")); cfg80211_scan_done(wl->scan_request, false); wl->scan_request = NULL; } + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); rtnl_unlock(); return err; } @@ -4779,8 +4838,9 @@ wl_notify_rx_mgmt_frame(struct wl_priv *wl, struct net_device *ndev, #endif if (event == WLC_E_ACTION_FRAME_RX) { wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr", - &da, sizeof(struct ether_addr), ioctlbuf, sizeof(ioctlbuf), bsscfgidx); + NULL, 0, ioctlbuf, sizeof(ioctlbuf), bsscfgidx); wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + memcpy(da.octet, ioctlbuf, ETHER_ADDR_LEN); err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, &mgmt_frame, &mgmt_frame_len, (u8 *)((wl_event_rx_frame_data_t *)rxframe + 1)); @@ -4999,6 +5059,7 @@ static void wl_term_iscan(struct wl_priv *wl) static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) { struct wl_priv *wl = iscan_to_wl(iscan); + unsigned long flags; WL_DBG(("Enter \n")); if (unlikely(!wl_get_drv_status(wl, SCANNING))) { @@ -5006,11 +5067,13 @@ static void wl_notify_iscan_complete(struct wl_iscan_ctrl *iscan, bool aborted) WL_ERR(("Scan complete while device not scanning\n")); return; } + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); wl_clr_drv_status(wl, SCANNING); if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); wl->iscan_kickstart = false; } @@ -5153,6 +5216,16 @@ static s32 wl_iscan_thread(void *data) return 0; } +static void wl_scan_timeout(unsigned long data) +{ + struct wl_priv *wl = (struct wl_priv *)data; + + if (wl->scan_request) { + WL_ERR(("timer expired\n")); + wl_notify_escan_complete(wl, true); + } +} + static void wl_iscan_timer(unsigned long data) { struct wl_iscan_ctrl *iscan = (struct wl_iscan_ctrl *)data; @@ -5195,6 +5268,8 @@ static void wl_init_iscan_handler(struct wl_iscan_ctrl *iscan) static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted) { + unsigned long flags; + WL_DBG(("Enter \n")); if (unlikely(!wl_get_drv_status(wl, SCANNING))) { wl_clr_drv_status(wl, SCANNING); @@ -5206,10 +5281,12 @@ static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted) if (wl->p2p_supported && p2p_on(wl)) wl_clr_p2p_status(wl, SCANNING); + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; } + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); } static s32 wl_escan_handler(struct wl_priv *wl, @@ -5295,6 +5372,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (likely(wl->scan_request)) { rtnl_lock(); + del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN COMPLETED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); @@ -5306,6 +5384,7 @@ static s32 wl_escan_handler(struct wl_priv *wl, wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (likely(wl->scan_request)) { rtnl_lock(); + del_timer_sync(&wl->scan_timeout); WL_INFO(("ESCAN ABORTED\n")); wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; wl_inform_bss(wl); @@ -5313,6 +5392,18 @@ static s32 wl_escan_handler(struct wl_priv *wl, rtnl_unlock(); } } + else { + WL_ERR(("unexpected Escan Event %d : abort\n", status)); + wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (likely(wl->scan_request)) { + rtnl_lock(); + del_timer_sync(&wl->scan_timeout); + wl->bss_list = (wl_scan_results_t *)wl->escan_info.escan_buf; + wl_inform_bss(wl); + wl_notify_escan_complete(wl, true); + rtnl_unlock(); + } + } exit: return err; } @@ -5342,6 +5433,10 @@ static s32 wl_init_scan(struct wl_priv *wl) wl->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler; wl->escan_info.escan_state = WL_ESCAN_STATE_IDLE; } + /* Init scan_timeout timer */ + init_timer(&wl->scan_timeout); + wl->scan_timeout.data = (unsigned long) wl; + wl->scan_timeout.function = wl_scan_timeout; return err; } @@ -5396,6 +5491,7 @@ static void wl_deinit_priv(struct wl_priv *wl) wl->dongle_up = false; /* dongle down */ wl_flush_eq(wl); wl_link_down(wl); + del_timer_sync(&wl->scan_timeout); wl_term_iscan(wl); wl_deinit_priv_mem(wl); } @@ -5589,6 +5685,7 @@ wl_cfg80211_event(struct net_device *ndev, const wl_event_msg_t * e, void *data) if (event_type == WLC_E_PFN_NET_FOUND) WL_ERR((" PNO Event\n")); + if (likely(!wl_enq_event(wl, ndev, event_type, e, data))) wl_wakeup_event(wl); } @@ -5714,7 +5811,7 @@ static s32 wl_dongle_mode(struct wl_priv *wl, struct net_device *ndev, s32 iftyp return err; } infra = htod32(infra); - err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), false); + err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(infra), true); if (unlikely(err)) { WL_ERR(("WLC_SET_INFRA error (%d)\n", err)); return err; @@ -5747,7 +5844,7 @@ static s32 wl_dongle_add_remove_eventmsg(struct net_device *ndev, u16 event, boo } bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("Set event_msgs error (%d)\n", err)); goto dongle_eventmsg_out; @@ -5772,7 +5869,7 @@ static s32 wl_dongle_up(struct net_device *ndev, u32 up) { s32 err = 0; - err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), false); + err = wldev_ioctl(ndev, WLC_UP, &up, sizeof(up), true); if (unlikely(err)) { WL_ERR(("WLC_UP error (%d)\n", err)); } @@ -5784,7 +5881,7 @@ static s32 wl_dongle_power(struct net_device *ndev, u32 power_mode) s32 err = 0; WL_TRACE(("In\n")); - err = wldev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode), false); + err = wldev_ioctl(ndev, WLC_SET_PM, &power_mode, sizeof(power_mode), true); if (unlikely(err)) { WL_ERR(("WLC_SET_PM error (%d)\n", err)); } @@ -5801,14 +5898,14 @@ wl_dongle_glom(struct net_device *ndev, u32 glom, u32 dongle_align) /* Match Host and Dongle rx alignment */ bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("txglomalign error (%d)\n", err)); goto dongle_glom_out; } /* disable glom option per default */ bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("txglom error (%d)\n", err)); goto dongle_glom_out; @@ -5828,7 +5925,7 @@ wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) if (roamvar) { bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("bcn_timeout error (%d)\n", err)); goto dongle_rom_out; @@ -5836,7 +5933,7 @@ wl_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) } /* Enable/Disable built-in roaming to allow supplicant to take care of roaming */ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (unlikely(err)) { WL_ERR(("roam_off error (%d)\n", err)); goto dongle_rom_out; @@ -5852,7 +5949,7 @@ wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, s32 err = 0; err = wldev_ioctl(ndev, WLC_SET_SCAN_CHANNEL_TIME, &scan_assoc_time, - sizeof(scan_assoc_time), false); + sizeof(scan_assoc_time), true); if (err) { if (err == -EOPNOTSUPP) { WL_INFO(("Scan assoc time is not supported\n")); @@ -5862,7 +5959,7 @@ wl_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time, goto dongle_scantime_out; } err = wldev_ioctl(ndev, WLC_SET_SCAN_UNASSOC_TIME, &scan_unassoc_time, - sizeof(scan_unassoc_time), false); + sizeof(scan_unassoc_time), true); if (err) { if (err == -EOPNOTSUPP) { WL_INFO(("Scan unassoc time is not supported\n")); @@ -5886,7 +5983,7 @@ wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol) /* Set ARP offload */ bcm_mkiovar("arpoe", (char *)&arpoe, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (err) { if (err == -EOPNOTSUPP) WL_INFO(("arpoe is not supported\n")); @@ -5896,7 +5993,7 @@ wl_dongle_offload(struct net_device *ndev, s32 arpoe, s32 arp_ol) goto dongle_offload_out; } bcm_mkiovar("arp_ol", (char *)&arp_ol, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (err) { if (err == -EOPNOTSUPP) WL_INFO(("arp_ol is not supported\n")); @@ -5995,7 +6092,7 @@ static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode) memcpy((char *)pkt_filterp, &pkt_filter, WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN); - err = wldev_ioctl(ndev, WLC_SET_VAR, buf, buf_len, false); + err = wldev_ioctl(ndev, WLC_SET_VAR, buf, buf_len, true); if (err) { if (err == -EOPNOTSUPP) { WL_INFO(("filter not supported\n")); @@ -6008,7 +6105,7 @@ static s32 wl_dongle_filter(struct net_device *ndev, u32 filter_mode) /* set mode to allow pattern */ bcm_mkiovar("pkt_filter_mode", (char *)&filter_mode, 4, iovbuf, sizeof(iovbuf)); - err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), false); + err = wldev_ioctl(ndev, WLC_SET_VAR, iovbuf, sizeof(iovbuf), true); if (err) { if (err == -EOPNOTSUPP) { WL_INFO(("filter_mode not supported\n")); @@ -6139,6 +6236,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) static s32 __wl_cfg80211_down(struct wl_priv *wl) { s32 err = 0; + unsigned long flags; WL_TRACE(("In\n")); /* Check if cfg80211 interface is already down */ @@ -6148,6 +6246,7 @@ static s32 __wl_cfg80211_down(struct wl_priv *wl) wl_set_drv_status(wl, SCAN_ABORTING); wl_term_iscan(wl); + flags = dhd_os_spin_lock((dhd_pub_t *)(wl->pub)); if (wl->scan_request) { cfg80211_scan_done(wl->scan_request, true); wl->scan_request = NULL; @@ -6158,6 +6257,7 @@ static s32 __wl_cfg80211_down(struct wl_priv *wl) wl_clr_drv_status(wl, CONNECTING); wl_clr_drv_status(wl, CONNECTED); wl_clr_drv_status(wl, DISCONNECTING); + dhd_os_spin_unlock((dhd_pub_t *)(wl->pub), flags); if (wl_get_drv_status(wl, AP_CREATED)) { wl_clr_drv_status(wl, AP_CREATED); wl_clr_drv_status(wl, AP_CREATING); @@ -6220,6 +6320,7 @@ s32 wl_cfg80211_down(void) return err; } + static s32 wl_dongle_probecap(struct wl_priv *wl) { s32 err = 0; @@ -6669,7 +6770,7 @@ dev_wlc_bufvar_set(struct net_device *dev, char *name, char *buf, int len) bcm_mkiovar(name, buf, len, ioctlbuf, sizeof(ioctlbuf)); - return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf, sizeof(ioctlbuf), false)); + return (wldev_ioctl(dev, WLC_SET_VAR, ioctlbuf, sizeof(ioctlbuf), true)); } /* get named driver variable to uint register value and return error indication @@ -6699,7 +6800,7 @@ static bool btcoex_is_sco_active(struct net_device *dev) ioc_res = dev_wlc_intvar_get_reg(dev, "btc_params", 27, ¶m27); - WL_INFO(("%s, sample[%d], btc params: 27:%x\n", + WL_TRACE(("%s, sample[%d], btc params: 27:%x\n", __FUNCTION__, i, param27)); if (ioc_res < 0) { @@ -6712,7 +6813,7 @@ static bool btcoex_is_sco_active(struct net_device *dev) } if (sco_id_cnt > 2) { - WL_INFO(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", + WL_TRACE(("%s, sco/esco detected, pkt id_cnt:%d samples:%d\n", __FUNCTION__, sco_id_cnt, i)); res = TRUE; break; @@ -6753,7 +6854,7 @@ static int set_btc_esco_params(struct net_device *dev, bool trump_sco) */ /* 1st save current */ - WL_INFO(("Do new SCO/eSCO coex algo {save &" + WL_TRACE(("Do new SCO/eSCO coex algo {save &" "override}\n")); if ((!dev_wlc_intvar_get_reg(dev, "btc_params", 50, &saved_reg50)) && (!dev_wlc_intvar_get_reg(dev, "btc_params", 51, &saved_reg51)) && @@ -6761,7 +6862,7 @@ static int set_btc_esco_params(struct net_device *dev, bool trump_sco) (!dev_wlc_intvar_get_reg(dev, "btc_params", 65, &saved_reg65)) && (!dev_wlc_intvar_get_reg(dev, "btc_params", 71, &saved_reg71))) { saved_status = TRUE; - WL_INFO(("%s saved bt_params[50,51,64,65,71]:" + WL_TRACE(("%s saved bt_params[50,51,64,65,71]:" "0x%x 0x%x 0x%x 0x%x 0x%x\n", __FUNCTION__, saved_reg50, saved_reg51, saved_reg64, saved_reg65, saved_reg71)); @@ -6772,7 +6873,7 @@ static int set_btc_esco_params(struct net_device *dev, bool trump_sco) return -1; } - WL_INFO(("override with [50,51,64,65,71]:" + WL_TRACE(("override with [50,51,64,65,71]:" "0x%x 0x%x 0x%x 0x%x 0x%x\n", *(u32 *)(buf_reg50va_dhcp_on+4), *(u32 *)(buf_reg51va_dhcp_on+4), @@ -6794,7 +6895,7 @@ static int set_btc_esco_params(struct net_device *dev, bool trump_sco) saved_status = TRUE; } else if (saved_status) { /* restore previously saved bt params */ - WL_INFO(("Do new SCO/eSCO coex algo {save &" + WL_TRACE(("Do new SCO/eSCO coex algo {save &" "override}\n")); regaddr = 50; @@ -6813,7 +6914,7 @@ static int set_btc_esco_params(struct net_device *dev, bool trump_sco) dev_wlc_intvar_set_reg(dev, "btc_params", (char *)®addr, (char *)&saved_reg71); - WL_INFO(("restore bt_params[50,51,64,65,71]:" + WL_TRACE(("restore bt_params[50,51,64,65,71]:" "0x%x 0x%x 0x%x 0x%x 0x%x\n", saved_reg50, saved_reg51, saved_reg64, saved_reg65, saved_reg71)); @@ -6989,7 +7090,7 @@ wl_cfg80211_btcoex_deinit(struct wl_priv *wl) kfree(wl->btcoex_info); wl->btcoex_info = NULL; } -#endif /* OEM_ANDROID */ +#endif /* COEX_DHCP */ int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) { @@ -7022,7 +7123,7 @@ int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) (!dev_wlc_intvar_get_reg(dev, "btc_params", 41, &saved_reg41)) && (!dev_wlc_intvar_get_reg(dev, "btc_params", 68, &saved_reg68))) { saved_status = TRUE; - WL_INFO(("Saved 0x%x 0x%x 0x%x\n", + WL_TRACE(("Saved 0x%x 0x%x 0x%x\n", saved_reg66, saved_reg41, saved_reg68)); /* Disable PM mode during dhpc session */ @@ -7048,7 +7149,7 @@ int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) btco_inf->bt_state = BT_DHCP_START; btco_inf->timer_on = 1; mod_timer(&btco_inf->timer, btco_inf->timer.expires); - WL_INFO(("%s enable BT DHCP Timer\n", + WL_TRACE(("%s enable BT DHCP Timer\n", __FUNCTION__)); } #endif /* COEX_DHCP */ @@ -7064,14 +7165,14 @@ int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) #ifdef COEX_DHCP /* Stop any bt timer because DHCP session is done */ - WL_INFO(("%s disable BT DHCP Timer\n", __FUNCTION__)); + WL_TRACE(("%s disable BT DHCP Timer\n", __FUNCTION__)); if (btco_inf->timer_on) { btco_inf->timer_on = 0; del_timer_sync(&btco_inf->timer); if (btco_inf->bt_state != BT_DHCP_IDLE) { /* need to restore original btc flags & extra btc params */ - WL_INFO(("%s bt->bt_state:%d\n", + WL_TRACE(("%s bt->bt_state:%d\n", __FUNCTION__, btco_inf->bt_state)); /* wake up btcoex thread to restore btlags+params */ schedule_work(&btco_inf->work); @@ -7096,7 +7197,7 @@ int wl_cfg80211_set_btcoex_dhcp(struct net_device *dev, char *command) dev_wlc_intvar_set_reg(dev, "btc_params", (char *)®addr, (char *)&saved_reg68); - WL_INFO(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", + WL_TRACE(("restore regs {66,41,68} <- 0x%x 0x%x 0x%x\n", saved_reg66, saved_reg41, saved_reg68)); } saved_status = FALSE; diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index 4159bd7..9fae79e 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -52,11 +52,11 @@ struct wl_ibss; #define dtohchanspec(i) i #define WL_DBG_NONE 0 +#define WL_DBG_TRACE (1 << 4) #define WL_DBG_SCAN (1 << 3) #define WL_DBG_DBG (1 << 2) #define WL_DBG_INFO (1 << 1) #define WL_DBG_ERR (1 << 0) -#define WL_DBG_MASK ((WL_DBG_DBG | WL_DBG_INFO | WL_DBG_ERR) << 1) /* 0 invalidates all debug messages. default is 1 */ #define WL_DBG_LEVEL 0xFF @@ -82,6 +82,13 @@ do { \ printk args; \ } \ } while (0) +#define WL_TRACE(args) \ +do { \ + if (wl_dbg_level & WL_DBG_TRACE) { \ + printk(KERN_ERR "CFG80211-TRACE) %s :", __func__); \ + printk args; \ + } \ +} while (0) #if (WL_DBG_LEVEL > 0) #define WL_DBG(args) \ do { \ @@ -94,6 +101,7 @@ do { \ #define WL_DBG(args) #endif /* (WL_DBG_LEVEL > 0) */ + #define WL_SCAN_RETRY_MAX 3 /* used for ibss scan */ #define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used * for 2.6.33 kernel @@ -122,6 +130,9 @@ do { \ #define WL_FILE_NAME_MAX 256 #define WL_DWELL_TIME 200 #define VWDEV_CNT 3 + +#define WL_SCAN_TIMER_INTERVAL_MS 8000 /* Scan timeout */ + /* dongle status */ enum wl_status { WL_STATUS_READY = 0, @@ -196,7 +207,6 @@ struct wl_conf { typedef s32(*EVENT_HANDLER) (struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); - /* bss inform structure for cfg80211 interface */ struct wl_cfg80211_bss_info { u16 band; @@ -408,6 +418,7 @@ struct wl_priv { struct p2p_info *p2p; bool p2p_supported; struct btcoex_info *btcoex_info; + struct timer_list scan_timeout; /* Timer for catch scan event timeout */ }; #define wl_to_wiphy(w) (w->wdev->wiphy) diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index 2487a67..dd5f5fa 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -59,7 +59,7 @@ wl_cfgp2p_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u static s32 wl_cfgp2p_vndr_ie(struct net_device *ndev, s32 bssidx, s32 pktflag, s8 *oui, s32 ie_id, s8 *data, s32 data_len, s32 delete); -/* +/* * Initialize variables related to P2P * */ @@ -99,7 +99,7 @@ wl_cfgp2p_init_priv(struct wl_priv *wl) wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE) = 0; wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = NULL; wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = 0; - + spin_lock_init(&wl->p2p->timer_lock); return BCME_OK; } @@ -129,9 +129,9 @@ wl_cfgp2p_set_firm_p2p(struct wl_priv *wl) wldev_iovar_getint(ndev, "apsta", &val); if (val == 0) { val = 1; - wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), false); + wldev_ioctl(ndev, WLC_DOWN, &val, sizeof(s32), true); wldev_iovar_setint(ndev, "apsta", val); - wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), false); + wldev_ioctl(ndev, WLC_UP, &val, sizeof(s32), true); } val = 1; /* Disable firmware roaming for P2P */ @@ -958,13 +958,15 @@ wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, CFGP2P_DBG((" Enter\n")); if (wl_get_p2p_status(wl, LISTEN_EXPIRED) == 0) { wl_set_p2p_status(wl, LISTEN_EXPIRED); - - if (wl->p2p->listen_timer) - del_timer_sync(wl->p2p->listen_timer); - + if (timer_pending(&wl->p2p->listen_timer)) { + spin_lock_bh(&wl->p2p->timer_lock); + del_timer_sync(&wl->p2p->listen_timer); + spin_unlock_bh(&wl->p2p->timer_lock); + } cfg80211_remain_on_channel_expired(ndev, wl->cache_cookie, &wl->remain_on_chan, wl->remain_on_chan_type, GFP_KERNEL); - } + } else + wl_clr_p2p_status(wl, LISTEN_EXPIRED); return ret; @@ -972,7 +974,7 @@ wl_cfgp2p_listen_complete(struct wl_priv *wl, struct net_device *ndev, /* * Timer expire callback function for LISTEN - * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, + * We can't report cfg80211_remain_on_channel_expired from Timer ISR context, * so lets do it from thread context. */ static void @@ -986,7 +988,7 @@ wl_cfgp2p_listen_expired(unsigned long data) wl_cfg80211_event(wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_DEVICE), &msg, NULL); } -/* +/* * Do a P2P Listen on the given channel for the given duration. * A listen consists of sitting idle and responding to P2P probe requests * with a P2P probe response. @@ -1010,6 +1012,7 @@ wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) } while (0); s32 ret = BCME_OK; + struct timer_list *_timer; CFGP2P_DBG((" Enter Channel : %d, Duration : %d\n", channel, duration_ms)); if (unlikely(wl_get_p2p_status(wl, DISCOVERY_ON) == 0)) { @@ -1018,26 +1021,24 @@ wl_cfgp2p_discover_listen(struct wl_priv *wl, s32 channel, u32 duration_ms) ret = BCME_NOTREADY; goto exit; } - + if (!wl_get_p2p_status(wl, LISTEN_EXPIRED)) { + wl_set_p2p_status(wl, LISTEN_EXPIRED); + if (timer_pending(&wl->p2p->listen_timer)) { + spin_lock_bh(&wl->p2p->timer_lock); + del_timer_sync(&wl->p2p->listen_timer); + spin_unlock_bh(&wl->p2p->timer_lock); + } + } else wl_clr_p2p_status(wl, LISTEN_EXPIRED); wl_cfgp2p_set_p2p_mode(wl, WL_P2P_DISC_ST_LISTEN, channel, (u16) duration_ms, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE)); + _timer = &wl->p2p->listen_timer; - if (wl->p2p->listen_timer) - del_timer_sync(wl->p2p->listen_timer); - - wl->p2p->listen_timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL); - - if (wl->p2p->listen_timer == NULL) { - CFGP2P_ERR(("listen_timer allocation failed\n")); - return -ENOMEM; - } - - /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , + /* We will wait to receive WLC_E_P2P_DISC_LISTEN_COMPLETE from dongle , * otherwise we will wait up to duration_ms + 200ms */ - INIT_TIMER(wl->p2p->listen_timer, wl_cfgp2p_listen_expired, duration_ms, 200); + INIT_TIMER(_timer, wl_cfgp2p_listen_expired, duration_ms, 200); #undef INIT_TIMER exit: @@ -1308,7 +1309,7 @@ wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) s32 wl_cfgp2p_down(struct wl_priv *wl) { - if (wl->p2p->listen_timer) - del_timer_sync(wl->p2p->listen_timer); + if (timer_pending(&wl->p2p->listen_timer)) + del_timer_sync(&wl->p2p->listen_timer); return 0; } diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index b08504d..5aff468 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -45,11 +45,11 @@ typedef enum { #define IE_MAX_LEN 300 /* Structure to hold all saved P2P and WPS IEs for a BSSCFG */ struct p2p_saved_ie { - u8 p2p_probe_req_ie[IE_MAX_LEN]; - u8 p2p_probe_res_ie[IE_MAX_LEN]; - u8 p2p_assoc_req_ie[IE_MAX_LEN]; - u8 p2p_assoc_res_ie[IE_MAX_LEN]; - u8 p2p_beacon_ie[IE_MAX_LEN]; + u8 p2p_probe_req_ie[IE_MAX_LEN]; + u8 p2p_probe_res_ie[IE_MAX_LEN]; + u8 p2p_assoc_req_ie[IE_MAX_LEN]; + u8 p2p_assoc_res_ie[IE_MAX_LEN]; + u8 p2p_beacon_ie[IE_MAX_LEN]; u32 p2p_probe_req_ie_len; u32 p2p_probe_res_ie_len; u32 p2p_assoc_req_ie_len; @@ -73,8 +73,9 @@ struct p2p_info { struct ether_addr dev_addr; struct ether_addr int_addr; struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX]; - struct timer_list *listen_timer; + struct timer_list listen_timer; wlc_ssid_t ssid; + spinlock_t timer_lock; }; /* dongle status */ @@ -96,7 +97,7 @@ enum wl_cfgp2p_status { #define wl_to_p2p_bss_ndev(w, type) ((wl)->p2p->bss_idx[type].dev) #define wl_to_p2p_bss_bssidx(w, type) ((wl)->p2p->bss_idx[type].bssidx) #define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie) -#define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) +#define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) #define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) #define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index e69561e..92c4198 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -37,7 +37,6 @@ #define dtoh16(i) i #define htodchanspec(i) i #define dtohchanspec(i) i - extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); s32 wldev_ioctl( @@ -78,7 +77,7 @@ s32 wldev_iovar_getbuf( s32 iovar_len = 0; iovar_len = wldev_mkiovar(iovar_name, param, paramlen, buf, buflen); - ret = wldev_ioctl(dev, WLC_GET_VAR, buf, iovar_len, FALSE); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); return ret; } @@ -184,7 +183,7 @@ s32 wldev_iovar_getbuf_bsscfg( s32 iovar_len = 0; iovar_len = wldev_mkiovar_bsscfg(iovar_name, param, paramlen, buf, buflen, bsscfg_idx); - ret = wldev_ioctl(dev, WLC_GET_VAR, buf, iovar_len, FALSE); + ret = wldev_ioctl(dev, WLC_GET_VAR, buf, buflen, FALSE); return ret; } @@ -310,7 +309,7 @@ int wldev_set_country( return error; error = wldev_iovar_getbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf)); if (error < 0) DHD_ERROR(("%s: get country failed = %d\n", __FUNCTION__, error)); @@ -328,7 +327,7 @@ int wldev_set_country( memcpy(cspec.ccode, country_code, WLC_CNTRY_BUF_SZ); get_customized_country_code((char *)&cspec.country_abbrev, &cspec); error = wldev_iovar_setbuf(dev, "country", &cspec, sizeof(cspec), - smbuf, sizeof(smbuf)); + smbuf, sizeof(smbuf)); if (error < 0) { DHD_ERROR(("%s: set country for %s as %s rev %d failed\n", __FUNCTION__, country_code, cspec.ccode, cspec.rev)); -- 2.7.4