diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 27b3a429b8e4..cf7d7efb6137 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -72,6 +72,7 @@ DHDCFLAGS += -DPROP_TXSTATUS_VSDB DHDCFLAGS += -DCUSTOM_MAX_TXGLOM_SIZE=32 DHDCFLAGS += -DREPEAT_READFRAME DHDCFLAGS += -DROAM_AP_ENV_DETECTION +DHDCFLAGS += -DWL11U else DHDCFLAGS += -DCUSTOM_SDIO_F2_BLKSIZE=128 endif diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index f44db3df0d95..2a8e478986ef 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -67,6 +67,12 @@ #include #endif +#ifdef WL11U +#if !defined(WL_ENABLE_P2P_IF) && !defined(WL_CFG80211_P2P_DEV_IF) +#error You should enable 'WL_ENABLE_P2P_IF' or 'WL_CFG80211_P2P_DEV_IF' \ + according to Kernel version and is supported only in Android-JB +#endif /* !WL_ENABLE_P2P_IF && !WL_CFG80211_P2P_DEV_IF */ +#endif /* WL11U */ #define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) @@ -434,6 +440,13 @@ static s32 wl_mrg_ie(struct wl_priv *wl, u8 *ie_stream, u16 ie_size); static s32 wl_cp_ie(struct wl_priv *wl, u8 *dst, u16 dst_size); static u32 wl_get_ielen(struct wl_priv *wl); +#ifdef WL11U +bcm_tlv_t * +wl_cfg80211_find_interworking_ie(u8 *parse, u32 len); +static s32 +wl_cfg80211_add_iw_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, + uint8 ie_id, uint8 *data, uint8 data_len); +#endif /* WL11U */ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev, void *data); static void wl_free_wdev(struct wl_priv *wl); @@ -2124,6 +2137,9 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, bool iscan_req; bool escan_req = false; bool p2p_ssid; +#ifdef WL11U + bcm_tlv_t *interworking_ie; +#endif s32 err = 0; s32 bssidx = -1; s32 i; @@ -2232,6 +2248,29 @@ __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, err = BCME_ERROR; goto scan_out; } +#ifdef WL11U + if ((interworking_ie = wl_cfg80211_find_interworking_ie( + (u8 *)request->ie, request->ie_len)) != NULL) { + err = wl_cfg80211_add_iw_ie(wl, ndev, bssidx, + VNDR_IE_CUSTOM_FLAG, interworking_ie->id, + interworking_ie->data, interworking_ie->len); + + if (unlikely(err)) { + goto scan_out; + } + } else if (wl->iw_ie_len != 0) { + /* we have to clear IW IE and disable gratuitous APR */ + wl_cfg80211_add_iw_ie(wl, ndev, bssidx, + VNDR_IE_CUSTOM_FLAG, + DOT11_MNG_INTERWORKING_ID, + 0, 0); + + wldev_iovar_setint_bsscfg(ndev, "grat_arp", 0, + bssidx); + wl->wl11u = FALSE; + /* we don't care about error */ + } +#endif /* WL11U */ err = wl_cfgp2p_set_management_ie(wl, ndev, bssidx, VNDR_IE_PRBREQ_FLAG, (u8 *)request->ie, request->ie_len); @@ -4671,12 +4710,50 @@ wl_cfg80211_config_p2p_pub_af_tx(struct wiphy *wiphy, return err; } +#ifdef WL11U +static bool +wl_cfg80211_check_DFS_channel(struct wl_priv *wl, wl_af_params_t *af_params, + void *frame, u16 frame_len) +{ + struct wl_scan_results *bss_list; + struct wl_bss_info *bi = NULL; + bool result = false; + s32 i; + + /* If DFS channel is 52~148, check to block it or not */ + if (af_params && + (af_params->channel >= 52 && af_params->channel <= 148)) { + if (!wl_cfgp2p_is_p2p_action(frame, frame_len)) { + bss_list = wl->bss_list; + bi = next_bss(bss_list, bi); + for_each_bss(bss_list, bi, i) { + if (CHSPEC_IS5G(bi->chanspec) && + ((bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(bi->chanspec)) + == af_params->channel)) { + result = true; /* do not block the action frame */ + break; + } + } + } + } + else { + result = true; + } + + WL_DBG(("result=%s", result?"true":"false")); + return result; +} +#endif /* WL11U */ + static bool wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, bcm_struct_cfgdev *cfgdev, wl_af_params_t *af_params, wl_action_frame_t *action_frame, u16 action_frame_len, s32 bssidx) { +#ifdef WL11U + struct net_device *ndev = NULL; +#endif /* WL11U */ struct wl_priv *wl = wiphy_priv(wiphy); bool ack = false; u8 category, action; @@ -4687,6 +4764,13 @@ wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, #endif dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); +#ifdef WL11U +#if defined(WL_CFG80211_P2P_DEV_IF) + ndev = dev; +#else + ndev = ndev_to_cfgdev(cfgdev); +#endif /* WL_CFG80211_P2P_DEV_IF */ +#endif /* WL11U */ category = action_frame->data[DOT11_ACTION_CAT_OFF]; action = action_frame->data[DOT11_ACTION_ACT_OFF]; @@ -4758,6 +4842,10 @@ wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, } else { config_af_params.search_channel = false; } +#ifdef WL11U + if (ndev == wl_to_prmry_ndev(wl)) + config_af_params.search_channel = false; +#endif /* WL11U */ #ifdef VSDB /* if connecting on primary iface, sleep for a while before sending af tx for VSDB */ @@ -4770,6 +4858,12 @@ wl_cfg80211_send_action_frame(struct wiphy *wiphy, struct net_device *dev, if (wl_get_drv_status_all(wl, SCANNING)) { wl_notify_escan_complete(wl, wl->escan_info.ndev, true, true); } +#ifdef WL11U + /* handling DFS channel exceptions */ + if (!wl_cfg80211_check_DFS_channel(wl, af_params, action_frame->data, action_frame->len)) { + return false; /* the action frame was blocked */ + } +#endif /* WL11U */ /* set status and destination address before sending af */ if (wl->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { @@ -10926,6 +11020,100 @@ wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, return 0; } +#ifdef WL11U +bcm_tlv_t * +wl_cfg80211_find_interworking_ie(u8 *parse, u32 len) +{ + bcm_tlv_t *ie; + + while ((ie = bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_INTERWORKING_ID))) { + return (bcm_tlv_t *)ie; + } + return NULL; +} + +static s32 +wl_cfg80211_add_iw_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, + uint8 ie_id, uint8 *data, uint8 data_len) +{ + s32 err = BCME_OK; + s32 buf_len; + s32 iecount; + ie_setbuf_t *ie_setbuf; + + if (ie_id != DOT11_MNG_INTERWORKING_ID) + return BCME_UNSUPPORTED; + + /* Validate the pktflag parameter */ + if ((pktflag & ~(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | + VNDR_IE_ASSOCRSP_FLAG | VNDR_IE_AUTHRSP_FLAG | + VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG| + VNDR_IE_CUSTOM_FLAG))) { + WL_ERR(("cfg80211 Add IE: Invalid packet flag 0x%x\n", pktflag)); + return -1; + } + + /* use VNDR_IE_CUSTOM_FLAG flags for none vendor IE . currently fixed value */ + pktflag = htod32(pktflag); + + buf_len = sizeof(ie_setbuf_t) + data_len - 1; + ie_setbuf = (ie_setbuf_t *) kzalloc(buf_len, GFP_KERNEL); + + if (!ie_setbuf) { + WL_ERR(("Error allocating buffer for IE\n")); + return -ENOMEM; + } + + if (wl->iw_ie_len == data_len && !memcmp(wl->iw_ie, data, data_len)) { + WL_ERR(("Previous IW IE is equals to current IE\n")); + err = BCME_OK; + goto exit; + } + + strncpy(ie_setbuf->cmd, "add", VNDR_IE_CMD_LEN - 1); + ie_setbuf->cmd[VNDR_IE_CMD_LEN - 1] = '\0'; + + /* Buffer contains only 1 IE */ + iecount = htod32(1); + memcpy((void *)&ie_setbuf->ie_buffer.iecount, &iecount, sizeof(int)); + memcpy((void *)&ie_setbuf->ie_buffer.ie_list[0].pktflag, &pktflag, sizeof(uint32)); + + /* Now, add the IE to the buffer */ + ie_setbuf->ie_buffer.ie_list[0].ie_data.id = ie_id; + + /* if already set with previous values, delete it first */ + if (wl->iw_ie_len != 0) { + WL_DBG(("Different IW_IE was already set. clear first\n")); + + ie_setbuf->ie_buffer.ie_list[0].ie_data.len = 0; + + err = wldev_iovar_setbuf_bsscfg(ndev, "ie", ie_setbuf, buf_len, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + + if (err != BCME_OK) + goto exit; + } + + ie_setbuf->ie_buffer.ie_list[0].ie_data.len = data_len; + memcpy((uchar *)&ie_setbuf->ie_buffer.ie_list[0].ie_data.data[0], data, data_len); + + err = wldev_iovar_setbuf_bsscfg(ndev, "ie", ie_setbuf, buf_len, + wl->ioctl_buf, WLC_IOCTL_MAXLEN, bssidx, &wl->ioctl_buf_sync); + + if (err == BCME_OK) { + memcpy(wl->iw_ie, data, data_len); + wl->iw_ie_len = data_len; + wl->wl11u = TRUE; + + err = wldev_iovar_setint_bsscfg(ndev, "grat_arp", 1, bssidx); + } + +exit: + if (ie_setbuf) + kfree(ie_setbuf); + return err; +} +#endif /* WL11U */ static void wl_cfg80211_work_handler(struct work_struct * work) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index f31919a841bc..7567c4e2d00c 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -473,6 +473,13 @@ struct parsed_ies { +#ifdef WL11U +/* Max length of Interworking element */ +#define IW_IES_MAX_BUF_LEN 9 +#endif +#ifdef WLFBT +#define FBT_KEYLEN 32 +#endif #define MAX_EVENT_BUF_NUM 16 typedef struct wl_eventmsg_buf { u16 num; @@ -573,6 +580,11 @@ struct wl_priv { struct net_info *_net_info, enum wl_status state, bool set); unsigned long interrested_state; wlc_ssid_t hostapd_ssid; +#ifdef WL11U + bool wl11u; + u8 iw_ie[IW_IES_MAX_BUF_LEN]; + u32 iw_ie_len; +#endif /* WL11U */ bool sched_scan_running; /* scheduled scan req status */ #ifdef WL_SCHED_SCAN struct cfg80211_sched_scan_request *sched_scan_req; /* scheduled scan req */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index 3b47c24440f2..f3e8c364dd41 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -157,6 +157,26 @@ bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) if (sd_act_frm->category != P2PSD_ACTION_CATEGORY) return false; +#ifdef WL11U + /* XXX Hotspot2.0 STA mode can receive only response + * SoftAP mode cannot run Hotspot2.0 compliant Ap because + * Hotspot2.0 support only Enterprise mode + */ + if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP) + return wl_cfgp2p_find_gas_subtype(P2PSD_GAS_OUI_SUBTYPE, + (u8 *)sd_act_frm->query_data + GAS_RESP_OFFSET, + frame_len); + + else if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_CRESP) + return wl_cfgp2p_find_gas_subtype(P2PSD_GAS_OUI_SUBTYPE, + (u8 *)sd_act_frm->query_data + GAS_CRESP_OFFSET, + frame_len); + else if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || + sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ) + return true; + else + return false; +#else if (sd_act_frm->action == P2PSD_ACTION_ID_GAS_IREQ || sd_act_frm->action == P2PSD_ACTION_ID_GAS_IRESP || sd_act_frm->action == P2PSD_ACTION_ID_GAS_CREQ || @@ -164,6 +184,7 @@ bool wl_cfgp2p_is_gas_action(void *frame, u32 frame_len) return true; else return false; +#endif /* WL11U */ } void wl_cfgp2p_print_actframe(bool tx, void *frame, u32 frame_len, u32 channel) {