diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_linux.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_linux.c | 376 |
1 files changed, 317 insertions, 59 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 03ba34a2c82..d869be4ef07 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -86,6 +86,9 @@ typedef struct histo_ { uint32 bin[NUMBIN]; } histo_t; +#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 */ @@ -170,6 +173,7 @@ typedef struct dhd_if { bool txflowcontrol; /* Per interface flow control indicator */ char name[IFNAMSIZ+1]; /* linux interface name */ uint8 bssidx; /* bsscfg index for the interface */ + bool set_multicast; } dhd_if_t; #ifdef WLMEDIA_HTSF @@ -257,7 +261,6 @@ typedef struct dhd_info { int hang_was_sent; /* Thread to issue ioctl for multicast */ - bool set_multicast; bool set_macaddress; struct ether_addr macvalue; wait_queue_head_t ctrl_wait; @@ -1018,6 +1021,7 @@ dhd_op_if(dhd_if_t *ifp) } if (ret < 0) { + ifp->set_multicast = FALSE; if (ifp->net) { free_netdev(ifp->net); } @@ -1074,13 +1078,13 @@ _dhd_sysioc_thread(void *data) #endif /* SOFTAP */ if (dhd->iflist[i]->state) dhd_op_if(dhd->iflist[i]); -#ifdef SOFTAP + if (dhd->iflist[i] == NULL) { DHD_TRACE(("\n\n %s: interface %d just been removed," "!\n\n", __FUNCTION__, i)); continue; } - +#ifdef SOFTAP if (in_ap && dhd->set_macaddress) { DHD_TRACE(("attempt to set MAC for %s in AP Mode," "blocked. \n", dhd->iflist[i]->net->name)); @@ -1088,15 +1092,15 @@ _dhd_sysioc_thread(void *data) continue; } - if (in_ap && dhd->set_multicast) { + if (in_ap && dhd->iflist[i]->set_multicast) { DHD_TRACE(("attempt to set MULTICAST list for %s" "in AP Mode, blocked. \n", dhd->iflist[i]->net->name)); - dhd->set_multicast = FALSE; + dhd->iflist[i]->set_multicast = FALSE; continue; } #endif /* SOFTAP */ - if (dhd->set_multicast) { - dhd->set_multicast = FALSE; + if (dhd->iflist[i]->set_multicast) { + dhd->iflist[i]->set_multicast = FALSE; _dhd_set_multicast_list(dhd, i); } if (dhd->set_macaddress) { @@ -1145,7 +1149,7 @@ dhd_set_multicast_list(struct net_device *dev) return; ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); - dhd->set_multicast = TRUE; + dhd->iflist[ifidx]->set_multicast = TRUE; up(&dhd->thr_sysioc_ctl.sema); } @@ -1605,14 +1609,12 @@ dhd_watchdog_thread(void *data) /* This thread doesn't need any user-level access, * so get rid of all our resources */ -#ifdef DHD_SCHED if (dhd_watchdog_prio > 0) { struct sched_param param; param.sched_priority = (dhd_watchdog_prio < MAX_RT_PRIO)? dhd_watchdog_prio:(MAX_RT_PRIO-1); setScheduler(current, SCHED_FIFO, ¶m); } -#endif /* DHD_SCHED */ DAEMONIZE("dhd_watchdog"); @@ -1698,14 +1700,12 @@ dhd_dpc_thread(void *data) /* This thread doesn't need any user-level access, * so get rid of all our resources */ -#ifdef DHD_SCHED if (dhd_dpc_prio > 0) { struct sched_param param; param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); setScheduler(current, SCHED_FIFO, ¶m); } -#endif /* DHD_SCHED */ DAEMONIZE("dhd_dpc"); /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ @@ -2605,9 +2605,6 @@ dhd_bus_start(dhd_pub_t *dhdp) int ret = -1; dhd_info_t *dhd = (dhd_info_t*)dhdp->info; unsigned long flags; -#ifdef EMBEDDED_PLATFORM - char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ -#endif /* EMBEDDED_PLATFORM */ ASSERT(dhd); @@ -2674,50 +2671,6 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_os_sdunlock(dhdp); -#ifdef EMBEDDED_PLATFORM - bcm_mkiovar("event_msgs", dhdp->eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhdp, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); - bcopy(iovbuf, dhdp->eventmask, WL_EVENTING_MASK_LEN); - - setbit(dhdp->eventmask, WLC_E_SET_SSID); - setbit(dhdp->eventmask, WLC_E_PRUNE); - setbit(dhdp->eventmask, WLC_E_AUTH); - setbit(dhdp->eventmask, WLC_E_REASSOC); - setbit(dhdp->eventmask, WLC_E_REASSOC_IND); - setbit(dhdp->eventmask, WLC_E_DEAUTH_IND); - setbit(dhdp->eventmask, WLC_E_DISASSOC_IND); - setbit(dhdp->eventmask, WLC_E_DISASSOC); - setbit(dhdp->eventmask, WLC_E_JOIN); - setbit(dhdp->eventmask, WLC_E_ASSOC_IND); - setbit(dhdp->eventmask, WLC_E_PSK_SUP); - setbit(dhdp->eventmask, WLC_E_LINK); - setbit(dhdp->eventmask, WLC_E_NDIS_LINK); - setbit(dhdp->eventmask, WLC_E_MIC_ERROR); - setbit(dhdp->eventmask, WLC_E_PMKID_CACHE); - setbit(dhdp->eventmask, WLC_E_TXFAIL); - setbit(dhdp->eventmask, WLC_E_JOIN_START); - setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE); - setbit(dhdp->eventmask, WLC_E_ACTION_FRAME_RX); - setbit(dhdp->eventmask, WLC_E_ACTION_FRAME_COMPLETE); -#if defined(WLP2P) - setbit(dhdp->eventmask, WLC_E_P2P_PROBREQ_MSG); -#endif /* WLP2P */ -#ifdef PNO_SUPPORT - setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND); -#endif /* PNO_SUPPORT */ - -/* enable dongle roaming event */ - setbit(dhdp->eventmask, WLC_E_ROAM); - - - dhdp->pktfilter_count = 4; - /* Setup filter to allow only unicast */ - dhdp->pktfilter[0] = "100 0 0 0 0x01 0x00"; - dhdp->pktfilter[1] = NULL; - dhdp->pktfilter[2] = NULL; - dhdp->pktfilter[3] = NULL; -#endif /* EMBEDDED_PLATFORM */ - #ifdef READ_MACADDR dhd_read_macaddr(dhd); #endif @@ -2734,6 +2687,311 @@ dhd_bus_start(dhd_pub_t *dhdp) } int +dhd_preinit_ioctls(dhd_pub_t *dhd) +{ + int ret = 0; + char eventmask[WL_EVENTING_MASK_LEN]; + char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */ + + uint up = 0; + uint power_mode = PM_FAST; + uint32 dongle_align = DHD_SDALIGN; + uint32 glom = 0; + uint bcn_timeout = 4; + uint retry_max = 3; +#if defined(ARP_OFFLOAD_SUPPORT) + int arpoe = 1; +#endif + int scan_assoc_time = 40; + int scan_unassoc_time = 40; + char buf[WLC_IOCTL_SMLEN]; + char *ptr; + uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ +#if defined(SOFTAP) + uint dtim = 1; +#endif +#if (defined(AP) && !defined(WLP2P)) || (!defined(AP) && defined(WL_CFG80211)) + uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ +#endif + +#if defined(AP) || defined(WLP2P) + uint32 apsta = 1; /* Enable APSTA mode */ +#endif /* defined(AP) || defined(WLP2P) */ +#ifdef GET_CUSTOM_MAC_ENABLE + struct ether_addr ea_addr; +#endif /* GET_CUSTOM_MAC_ENABLE */ + + DHD_TRACE(("Enter %s\n", __FUNCTION__)); + dhd->op_mode = 0; +#ifdef GET_CUSTOM_MAC_ENABLE + ret = dhd_custom_get_mac_address(ea_addr.octet); + if (!ret) { + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + } else { +#endif /* GET_CUSTOM_MAC_ENABLE */ + /* Get the default device MAC address directly from firmware */ + memset(buf, 0, sizeof(buf)); + bcm_mkiovar("cur_etheraddr", 0, 0, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), + FALSE, 0)) < 0) { + DHD_ERROR(("%s: can't get MAC address , error=%d\n", __FUNCTION__, ret)); + return BCME_NOTUP; + } + /* Update public MAC address after reading from Firmware */ + memcpy(dhd->mac.octet, buf, ETHER_ADDR_LEN); +#ifdef GET_CUSTOM_MAC_ENABLE + } +#endif /* GET_CUSTOM_MAC_ENABLE */ + +#ifdef SET_RANDOM_MAC_SOFTAP + if (strstr(fw_path, "_apsta") != NULL) { + uint rand_mac; + + srandom32((uint)jiffies); + rand_mac = random32(); + iovbuf[0] = 0x02; /* locally administered bit */ + iovbuf[1] = 0x1A; + iovbuf[2] = 0x11; + iovbuf[3] = (unsigned char)(rand_mac & 0x0F) | 0xF0; + iovbuf[4] = (unsigned char)(rand_mac >> 8); + iovbuf[5] = (unsigned char)(rand_mac >> 16); + + bcm_mkiovar("cur_etheraddr", (void *)iovbuf, ETHER_ADDR_LEN, buf, sizeof(buf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); + if (ret < 0) { + DHD_ERROR(("%s: can't set MAC address , error=%d\n", __FUNCTION__, ret)); + } else + memcpy(dhd->mac.octet, iovbuf, ETHER_ADDR_LEN); + } +#endif /* SET_RANDOM_MAC_SOFTAP */ + + DHD_TRACE(("Firmware = %s\n", fw_path)); +#if !defined(AP) && defined(WLP2P) + /* Check if firmware with WFD support used */ + if (strstr(fw_path, "_p2p") != NULL) { + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, + iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s APSTA for WFD failed ret= %d\n", __FUNCTION__, ret)); + } else { + dhd->op_mode |= WFD_MASK; +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif /* (ARP_OFFLOAD_SUPPORT) */ + dhd_pkt_filter_enable = FALSE; + } + } +#endif + +#if !defined(AP) && defined(WL_CFG80211) + /* Check if firmware with HostAPD support used */ + if (strstr(fw_path, "_apsta") != NULL) { + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); + } else { + dhd->op_mode |= HOSTAPD_MASK; +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif /* (ARP_OFFLOAD_SUPPORT) */ + dhd_pkt_filter_enable = FALSE; + } + } +#endif + + if ((dhd->op_mode != WFD_MASK) && (dhd->op_mode != HOSTAPD_MASK)) { + /* STA only operation mode */ + dhd->op_mode |= STA_MASK; + dhd_pkt_filter_enable = TRUE; + } + + DHD_ERROR(("Firmware up: op_mode=%d, " + "Broadcom Dongle Host Driver mac=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", + dhd->op_mode, + dhd->mac.octet[0], dhd->mac.octet[1], dhd->mac.octet[2], + dhd->mac.octet[3], dhd->mac.octet[4], dhd->mac.octet[5])); + + /* Set Country code */ + if (dhd->dhd_cspec.ccode[0] != 0) { + bcm_mkiovar("country", (char *)&dhd->dhd_cspec, + sizeof(wl_country_t), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); + } + + /* Set Listen Interval */ + bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + ptr = buf; + bcm_mkiovar("ver", (char *)&buf, 4, buf, sizeof(buf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), FALSE, 0)) < 0) + DHD_ERROR(("%s failed %d\n", __FUNCTION__, ret)); + else { + bcmstrtok(&ptr, "\n", 0); + /* Print fw version info */ + DHD_ERROR(("Firmware version = %s\n", buf)); + } + /* Set PowerSave mode */ + dhd_wl_ioctl_cmd(dhd, WLC_SET_PM, (char *)&power_mode, sizeof(power_mode), TRUE, 0); + + /* Match Host and Dongle rx alignment */ + bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* disable glom option per default */ + bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + /* Setup timeout if Beacons are lost and roam is off to report link down */ + bcm_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + /* Setup assoc_retry_max count to reconnect target AP in dongle */ + bcm_mkiovar("assoc_retry_max", (char *)&retry_max, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#if defined(AP) && !defined(WLP2P) + /* Turn off MPC in AP mode */ + bcm_mkiovar("mpc", (char *)&mpc, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + bcm_mkiovar("apsta", (char *)&apsta, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* defined(AP) && !defined(WLP2P) */ + +#if defined(SOFTAP) + if (ap_fw_loaded == TRUE) { + dhd_wl_ioctl_cmd(dhd, WLC_SET_DTIMPRD, (char *)&dtim, sizeof(dtim), TRUE, 0); + } +#endif + +#if defined(KEEP_ALIVE) + { + /* Set Keep Alive : be sure to use FW with -keepalive */ + int res; + +#if defined(SOFTAP) + if (ap_fw_loaded == FALSE) +#endif + if ((res = dhd_keep_alive_onoff(dhd)) < 0) + DHD_ERROR(("%s set keeplive failed %d\n", + __FUNCTION__, res)); + } +#endif /* defined(KEEP_ALIVE) */ + + + /* Read event_msgs mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0)) < 0) { + DHD_ERROR(("%s read Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN); + + /* Setup event_msgs */ + setbit(eventmask, WLC_E_SET_SSID); + setbit(eventmask, WLC_E_PRUNE); + setbit(eventmask, WLC_E_AUTH); + setbit(eventmask, WLC_E_REASSOC); + setbit(eventmask, WLC_E_REASSOC_IND); + setbit(eventmask, WLC_E_DEAUTH_IND); + setbit(eventmask, WLC_E_DISASSOC_IND); + setbit(eventmask, WLC_E_DISASSOC); + setbit(eventmask, WLC_E_JOIN); + setbit(eventmask, WLC_E_ASSOC_IND); + setbit(eventmask, WLC_E_PSK_SUP); + setbit(eventmask, WLC_E_LINK); + setbit(eventmask, WLC_E_NDIS_LINK); + setbit(eventmask, WLC_E_MIC_ERROR); + setbit(eventmask, WLC_E_PMKID_CACHE); + setbit(eventmask, WLC_E_TXFAIL); + setbit(eventmask, WLC_E_JOIN_START); + setbit(eventmask, WLC_E_SCAN_COMPLETE); +#ifdef WLMEDIA_HTSF + setbit(eventmask, WLC_E_HTSFSYNC); +#endif /* WLMEDIA_HTSF */ +#ifdef PNO_SUPPORT + setbit(eventmask, WLC_E_PFN_NET_FOUND); +#endif /* PNO_SUPPORT */ + /* enable dongle roaming event */ + setbit(eventmask, WLC_E_ROAM); +#ifdef WL_CFG80211 + setbit(eventmask, WLC_E_ACTION_FRAME_RX); + setbit(eventmask, WLC_E_ACTION_FRAME_COMPLETE); + setbit(eventmask, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE); + setbit(eventmask, WLC_E_P2P_PROBREQ_MSG); + setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); + setbit(eventmask, WLC_E_ESCAN_RESULT); +#endif /* WL_CFG80211 */ + + /* Write updated Event mask */ + bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s Set Event mask failed %d\n", __FUNCTION__, ret)); + goto done; + } + + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_CHANNEL_TIME, (char *)&scan_assoc_time, + sizeof(scan_assoc_time), TRUE, 0); + dhd_wl_ioctl_cmd(dhd, WLC_SET_SCAN_UNASSOC_TIME, (char *)&scan_unassoc_time, + sizeof(scan_unassoc_time), TRUE, 0); + +#ifdef ARP_OFFLOAD_SUPPORT + /* Set and enable ARP offload feature for STA only */ + if (arpoe +#if defined(SOFTAP) + && (!ap_fw_loaded) +#endif /* (OEM_ANDROID) && defined(SOFTAP) */ + ) { + dhd_arp_offload_set(dhd, dhd_arp_mode); + dhd_arp_offload_enable(dhd, arpoe); + } else { + dhd_arp_offload_set(dhd, 0); + dhd_arp_offload_enable(dhd, FALSE); + } +#endif /* ARP_OFFLOAD_SUPPORT */ + +#ifdef PKT_FILTER_SUPPORT + /* Setup defintions for pktfilter , enable in suspend */ + dhd->pktfilter_count = 4; + /* Setup filter to allow only unicast */ + dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; + dhd->pktfilter[1] = NULL; + dhd->pktfilter[2] = NULL; + dhd->pktfilter[3] = NULL; + +#if defined(SOFTAP) + if (ap_fw_loaded) { + int i; + for (i = 0; i < dhd->pktfilter_count; i++) { + dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], + 0, dhd_master_mode); + } + } +#endif /* (SOFTAP) */ +#endif /* PKT_FILTER_SUPPORT */ + + /* Force STA UP */ + ret = dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&up, sizeof(up), TRUE, 0); + if (ret < 0) + goto done; + + +done: + return ret; +} + + +int dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, int set) { char buf[strlen(name) + 1 + cmd_len]; |