/* * Copyright (c) 2011 Espressif System. * * MAC80211 support module */ #include #include #include #include #include #include #include #include #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) #include #endif /* for support scan in p2p concurrent */ #include <../net/mac80211/ieee80211_i.h> #include "esp_pub.h" #include "esp_sip.h" #include "esp_ctrl.h" #include "esp_sif.h" #include "esp_debug.h" #include "esp_wl.h" #include "esp_utils.h" #define ESP_IEEE80211_DBG esp_dbg #define GET_NEXT_SEQ(seq) (((seq) +1) & 0x0fff) #ifdef P2P_CONCURRENT static u8 esp_mac_addr[ETH_ALEN * 2]; #endif static u8 getaddr_index(u8 * addr, struct esp_pub *epub); //add by toron extern int wmt_getsyspara(char *varname, unsigned char *varval, int *varlen); static #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) void #else int #endif /* NEW_KERNEL */ esp_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_LOG, "%s enter\n", __func__); if (!mod_support_no_txampdu() && #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) hw->conf.channel_type != NL80211_CHAN_NO_HT #else !(hw->conf.flags&IEEE80211_CONF_SUPPORT_HT_MODE) #endif ) { struct ieee80211_tx_info * tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr * wh = (struct ieee80211_hdr *)skb->data; if(ieee80211_is_data_qos(wh->frame_control)) { if(!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { u8 tidno = ieee80211_get_qos_ctl(wh)[0] & IEEE80211_QOS_CTL_TID_MASK; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) struct ieee80211_sta *sta = tx_info->control.sta; struct esp_node * node = (struct esp_node *)sta->drv_priv; #else struct esp_node * node = esp_get_node_by_addr(epub, wh->addr1); #endif struct esp_tx_tid *tid = &node->tid[tidno]; //record ssn spin_lock_bh(&epub->tx_ampdu_lock); tid->ssn = GET_NEXT_SEQ(le16_to_cpu(wh->seq_ctrl)>>4); ESP_IEEE80211_DBG(ESP_DBG_TRACE, "tidno:%u,ssn:%u\n", tidno, tid->ssn); spin_unlock_bh(&epub->tx_ampdu_lock); } else { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "tx ampdu pkt, sn:%u, %u\n", le16_to_cpu(wh->seq_ctrl)>>4, skb->len); } } } #ifdef GEN_ERR_CHECKSUM esp_gen_err_checksum(skb); #endif sip_tx_data_pkt_enqueue(epub, skb); if (epub) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) ieee80211_queue_work(hw, &epub->tx_work); #else queue_work(hw->workqueue,&epub->tx_work); #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) return NETDEV_TX_OK; #endif /*2.6.39*/ } static int esp_op_start(struct ieee80211_hw *hw) { struct esp_pub *epub; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__); if (!hw) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no hw!\n", __func__); return -EINVAL; } epub = (struct esp_pub *)hw->priv; if (!epub) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no epub!\n", __func__); return EINVAL; } /*add rfkill poll function*/ atomic_set(&epub->wl.off, 0); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) wiphy_rfkill_start_polling(hw->wiphy); #endif return 0; } static void esp_op_stop(struct ieee80211_hw *hw) { struct esp_pub *epub; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__); if (!hw) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no hw!\n", __func__); return; } epub = (struct esp_pub *)hw->priv; if (!epub) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s no epub!\n", __func__); return; } atomic_set(&epub->wl.off, 1); #ifdef HOST_RESET_BUG mdelay(200); #endif if (epub->wl.scan_req) { hw_scan_done(epub, true); epub->wl.scan_req=NULL; //msleep(2); } } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) #ifdef CONFIG_PM static int esp_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { esp_dbg(ESP_DBG_OP, "%s\n", __func__); return 0; } static int esp_op_resume(struct ieee80211_hw *hw) { esp_dbg(ESP_DBG_OP, "%s\n", __func__); return 0; } #endif //CONFIG_PM #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) static int esp_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) #else static int esp_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) struct ieee80211_vif *vif = conf->vif; #endif struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; struct sip_cmd_setvif svif; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter: type %d, addr %pM\n", __func__, vif->type, conf->mac_addr); #else ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter: type %d, addr %pM\n", __func__, vif->type, vif->addr); #endif memset(&svif, 0, sizeof(struct sip_cmd_setvif)); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) memcpy(svif.mac, conf->mac_addr, ETH_ALEN); evif->index = svif.index = getaddr_index(conf->mac_addr, epub); #else memcpy(svif.mac, vif->addr, ETH_ALEN); evif->index = svif.index = getaddr_index(vif->addr, epub); #endif evif->epub = epub; epub->vif = vif; svif.set = 1; if((1 << svif.index) & epub->vif_slot){ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s interface %d already used\n", __func__, svif.index); return -EOPNOTSUPP; } epub->vif_slot |= 1 << svif.index; if (svif.index == ESP_PUB_MAX_VIF) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s only support MAX %d interface\n", __func__, ESP_PUB_MAX_VIF); return -EOPNOTSUPP; } switch (vif->type) { case NL80211_IFTYPE_STATION: //if (svif.index == 1) // vif->type = NL80211_IFTYPE_UNSPECIFIED; ESP_IEEE80211_DBG(ESP_SHOW, "%s STA \n", __func__); svif.op_mode = 0; svif.is_p2p = 0; break; case NL80211_IFTYPE_AP: ESP_IEEE80211_DBG(ESP_SHOW, "%s AP \n", __func__); svif.op_mode = 1; svif.is_p2p = 0; break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) case NL80211_IFTYPE_P2P_CLIENT: ESP_IEEE80211_DBG(ESP_SHOW, "%s P2P_CLIENT \n", __func__); svif.op_mode = 0; svif.is_p2p = 1; break; case NL80211_IFTYPE_P2P_GO: ESP_IEEE80211_DBG(ESP_SHOW, "%s P2P_GO \n", __func__); svif.op_mode = 1; svif.is_p2p = 1; break; #endif case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: default: ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s does NOT support type %d\n", __func__, vif->type); return -EOPNOTSUPP; } sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(struct sip_cmd_setvif)); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) static int esp_op_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p) { struct esp_pub *epub = (struct esp_pub *)hw->priv; struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; struct sip_cmd_setvif svif; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter,change to if:%d \n", __func__, new_type); if (new_type == NL80211_IFTYPE_AP) { ESP_IEEE80211_DBG(ESP_SHOW, "%s enter,change to AP \n", __func__); } if (vif->type != new_type) { ESP_IEEE80211_DBG(ESP_SHOW, "%s type from %d to %d\n", __func__, vif->type, new_type); } memset(&svif, 0, sizeof(struct sip_cmd_setvif)); memcpy(svif.mac, vif->addr, ETH_ALEN); svif.index = evif->index; svif.set = 2; switch (new_type) { case NL80211_IFTYPE_STATION: svif.op_mode = 0; svif.is_p2p = p2p; break; case NL80211_IFTYPE_AP: svif.op_mode = 1; svif.is_p2p = p2p; break; case NL80211_IFTYPE_P2P_CLIENT: svif.op_mode = 0; svif.is_p2p = 1; break; case NL80211_IFTYPE_P2P_GO: svif.op_mode = 1; svif.is_p2p = 1; break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: default: ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s does NOT support type %d\n", __func__, vif->type); return -EOPNOTSUPP; } sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(struct sip_cmd_setvif)); return 0; } #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) static void esp_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) #else static void esp_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) struct ieee80211_vif *vif = conf->vif; #endif struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; struct sip_cmd_setvif svif; #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, vif addr %pM\n", __func__, conf->mac_addr); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, vif addr %pM, beacon enable %x\n", __func__, conf->mac_addr, vif->bss_conf.enable_beacon); #else ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, vif addr %pM, beacon enable %x\n", __func__, vif->addr, vif->bss_conf.enable_beacon); #endif memset(&svif, 0, sizeof(struct sip_cmd_setvif)); svif.index = evif->index; epub->vif_slot &= ~(1 << svif.index); if(evif->ap_up){ evif->beacon_interval = 0; del_timer_sync(&evif->beacon_timer); evif->ap_up = false; } epub->vif = NULL; evif->epub = NULL; sip_cmd(epub, SIP_CMD_SETVIF, (u8 *)&svif, sizeof(struct sip_cmd_setvif)); /* clean up tx/rx queue */ } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) static void drv_handle_beacon(unsigned long data) { struct ieee80211_vif *vif = (struct ieee80211_vif *) data; struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; struct sk_buff *beacon; struct sk_buff *skb; static int dbgcnt = 0; if(evif->epub == NULL) return; beacon = ieee80211_beacon_get(evif->epub->hw, vif); if (beacon && !(dbgcnt++ % 600)) { ESP_IEEE80211_DBG(ESP_SHOW, " beacon length:%d,fc:0x%x\n", beacon->len, ((struct ieee80211_mgmt *)(beacon->data))->frame_control); } sip_tx_data_pkt_enqueue(evif->epub, beacon); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) mod_timer(&evif->beacon_timer, jiffies+msecs_to_jiffies(vif->bss_conf.beacon_int)); #else mod_timer(&evif->beacon_timer, jiffies+msecs_to_jiffies(evif->beacon_interval)); #endif //FIXME:the packets must be sent at home channel //send buffer mcast frames skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); while (skb) { sip_tx_data_pkt_enqueue(evif->epub, skb); skb = ieee80211_get_buffered_bc(evif->epub->hw, vif); } } static void init_beacon_timer(struct ieee80211_vif *vif) { struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: beacon interval %x\n", __func__, evif->beacon_interval); init_timer(&evif->beacon_timer); //TBD, not init here... evif->beacon_timer.expires=jiffies+msecs_to_jiffies(evif->beacon_interval*1024/1000); evif->beacon_timer.data = (unsigned long) vif; evif->beacon_timer.function = drv_handle_beacon; add_timer(&evif->beacon_timer); } #endif /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static void init_beacon_timer(struct ieee80211_vif *vif) #else static void init_beacon_timer(struct ieee80211_conf *conf) #endif { struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: beacon interval %x\n", __func__, vif->bss_conf.beacon_int); #else ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: beacon interval %x\n", __func__, conf->beacon_int); #endif init_timer(&evif->beacon_timer); //TBD, not init here... #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) evif->beacon_timer.expires=jiffies+msecs_to_jiffies(vif->bss_conf.beacon_int*102/100); evif->beacon_timer.data = (unsigned long) vif; #else evif->beacon_timer.expires=jiffies+msecs_to_jiffies(conf->beacon_int*102/100); evif->beacon_timer.data = (unsigned long) conf; #endif //evif->beacon_timer.data = (unsigned long) vif; evif->beacon_timer.function = drv_handle_beacon; add_timer(&evif->beacon_timer); } */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) static int esp_op_config(struct ieee80211_hw *hw, u32 changed) #else static int esp_op_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) #endif { //struct ieee80211_conf *conf = &hw->conf; struct esp_pub *epub = (struct esp_pub *)hw->priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)) //struct esp_vif *evif = (struct esp_vif *)epub->vif->drv_priv; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter 0x%08x\n", __func__, changed); if (changed&IEEE80211_CONF_CHANGE_CHANNEL) { sip_send_config(epub, &hw->conf); } #else ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter 0x%08x\n", __func__, conf->flags); sip_send_config(epub, &hw->conf); #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)) //evif->beacon_interval = conf->beacon_int; //init_beacon_timer(epub->vif); #endif #if 0 if (changed & IEEE80211_CONF_CHANGE_PS) { struct esp_ps *ps = &epub->ps; ps->dtim_period = conf->ps_dtim_period; ps->max_sleep_period = conf->max_sleep_period; esp_ps_config(epub, ps, (conf->flags & IEEE80211_CONF_PS)); } #endif return 0; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) static int esp_op_config_interface (struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_if_conf *conf) { // assoc = 2 means AP struct esp_pub *epub = (struct esp_pub *)hw->priv; struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; //struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: changed %x, bssid %pM,vif->type = %d\n", __func__, conf->changed, conf->bssid,vif->type); if(conf->bssid) memcpy(epub->wl.bssid, conf->bssid, ETH_ALEN); else memset(epub->wl.bssid, 0, ETH_ALEN); if(vif->type == NL80211_IFTYPE_AP){ if((conf->changed & IEEE80211_IFCC_BEACON)){ sip_send_bss_info_update(epub, evif, (u8*)conf->bssid, 2); //evif->beacon_interval = conf->beacon_int; } else{ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s op----1-- mode unspecified\n", __func__); } } else{ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s op----2-- mode unspecified\n", __func__); } return 0; } #endif static void esp_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, u32 changed) { struct esp_pub *epub = (struct esp_pub *)hw->priv; struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) struct sta_info *sta; struct esp_node *node; struct ieee80211_ht_info *ht_info; u8 addr_0[ETH_ALEN]; memset(addr_0,0,ETH_ALEN); ESP_IEEE80211_DBG(ESP_DBG_OP,"%s enter, changed %x\n",__func__,changed); if((changed & BSS_CHANGED_ASSOC) && (memcmp(epub->wl.bssid,addr_0, ETH_ALEN))) { rcu_read_lock(); node = esp_get_node_by_addr(epub, epub->wl.bssid ); sta = sta_info_get(container_of(hw,struct ieee80211_local,hw), epub->wl.bssid); ht_info = &sta->ht_info; memcpy(node->supp_rates, sta->supp_rates, sizeof(node->supp_rates)); memcpy(&node->ht_info.cap, &ht_info->cap, sizeof(node->ht_info.cap)); memcpy(&node->ht_info.ht_supported, &ht_info->ht_supported, sizeof(node->ht_info.ht_supported)); memcpy(&node->ht_info.ampdu_density, &ht_info->ampdu_density, sizeof(node->ht_info.ampdu_density)); memcpy(&node->ht_info.ampdu_factor, &ht_info->ampdu_factor, sizeof(node->ht_info.ampdu_factor)); if(sta->aid == 0) memcpy(&node->aid, &info->aid, sizeof(node->aid)); else memcpy(&node->aid, &sta->aid, sizeof(node->aid)); rcu_read_unlock(); sip_send_set_sta(epub, evif->index, 1, node, vif, (u8)node->index); } #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); #endif // ieee80211_bss_conf(include/net/mac80211.h) is included in ieee80211_sub_if_data(net/mac80211/ieee80211_i.h) , does bssid=ieee80211_if_ap's ssid ? // in 2.6.27, ieee80211_sub_if_data has ieee80211_bss_conf while in 2.6.32 ieee80211_sub_if_data don't have ieee80211_bss_conf // in 2.6.27, ieee80211_bss_conf->enable_beacon don't exist, does it mean it support beacon always? // ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: vif addr %pM, changed %x, assoc %x, bssid %pM\n", __func__, vif->addr, changed, info->assoc, info->bssid); // sdata->u.sta.bssid ESP_IEEE80211_DBG(ESP_DBG_OP, " %s enter: changed %x, assoc %x, bssid %pM\n", __func__, changed, info->assoc, info->bssid); if (vif->type == NL80211_IFTYPE_STATION) { if ((changed & BSS_CHANGED_BSSID) || ((changed & BSS_CHANGED_ASSOC) && (info->assoc))) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, " %s STA change bssid or assoc\n", __func__); memcpy(epub->wl.bssid, (u8*)info->bssid, ETH_ALEN); sip_send_bss_info_update(epub, evif, (u8*)info->bssid, info->assoc); } else if ((changed & BSS_CHANGED_ASSOC) && (!info->assoc)) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, " %s STA change disassoc\n", __func__); memset(epub->wl.bssid, 0, ETH_ALEN); sip_send_bss_info_update(epub, evif, (u8*)info->bssid, info->assoc); } else { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s wrong mode of STA mode\n", __func__); } } else if (vif->type == NL80211_IFTYPE_AP) { if ((changed & BSS_CHANGED_BEACON_ENABLED) || (changed & BSS_CHANGED_BEACON_INT)) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, " %s AP change enable %d, interval is %d, bssid %pM\n", __func__, info->enable_beacon, info->beacon_int, info->bssid); if (info->enable_beacon && evif->ap_up != true) { evif->beacon_interval = info->beacon_int; init_beacon_timer(vif); sip_send_bss_info_update(epub, evif, (u8*)info->bssid, 2); evif->ap_up = true; } else if (!info->enable_beacon && evif->ap_up && #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) !test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state) #else true #endif ) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, " %s AP disable beacon, interval is %d\n", __func__, info->beacon_int); evif->beacon_interval = 0; del_timer_sync(&evif->beacon_timer); sip_send_bss_info_update(epub, evif, (u8*)info->bssid, 2); evif->ap_up = false; } } } else { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s op mode unspecified\n", __func__); } #endif } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) static u64 esp_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mc_list) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #else static u64 esp_op_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #endif /* NEW_KERNEL && KERNEL_35 */ #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) static void esp_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, u64 multicast) #else static void esp_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, int mc_count, struct dev_addr_list *mc_list) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); epub->rx_filter = 0; if (*total_flags & FIF_PROMISC_IN_BSS) epub->rx_filter |= FIF_PROMISC_IN_BSS; if (*total_flags & FIF_ALLMULTI) epub->rx_filter |= FIF_ALLMULTI; *total_flags = epub->rx_filter; } #if 0 static int esp_op_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) #else static int esp_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, const u8 *local_address,const u8 *address, struct ieee80211_key_conf *key) #endif { u8 i; int ret; struct esp_pub *epub = (struct esp_pub *)hw->priv; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; u8 ifidx = evif->index; #else u8 ifidx = getaddr_index((u8 *)(local_address), epub); #endif u8 *peer_addr,isvalid; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, flags = %x keyindx = %x cmd = %x mac = %pM cipher = %x\n", __func__, key->flags, key->keyidx, cmd, vif->addr, key->cipher); #else ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, flags = %x keyindx = %x cmd = %x cipher = %x\n", __func__, key->flags, key->keyidx, cmd, key->alg); #endif key->flags= key->flags|IEEE80211_KEY_FLAG_GENERATE_IV; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) if (sta) { if (memcmp(sta->addr, epub->wl.bssid, ETH_ALEN)) peer_addr = sta->addr; else peer_addr = epub->wl.bssid; } else { peer_addr=epub->wl.bssid; } #else peer_addr = (u8 *)address; #endif isvalid = (cmd==SET_KEY) ? 1 : 0; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) if ((key->flags&IEEE80211_KEY_FLAG_PAIRWISE) || (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104)) #else if ((key->flags&IEEE80211_KEY_FLAG_PAIRWISE) || (key->alg == ALG_WEP)) #endif { if (isvalid) { for (i = 0; i < 19; i++) { if (epub->hi_map[i].flag == 0) { epub->hi_map[i].flag = 1; key->hw_key_idx = i + 6; memcpy(epub->hi_map[i].mac, peer_addr, ETH_ALEN); break; } } } else { u8 index = key->hw_key_idx - 6; epub->hi_map[index].flag = 0; memset(epub->hi_map[index].mac, 0, ETH_ALEN); } } else { if(isvalid){ for(i = 0; i < 2; i++) if (epub->low_map[ifidx][i].flag == 0) { epub->low_map[ifidx][i].flag = 1; key->hw_key_idx = i + ifidx * 2 + 2; memcpy(epub->low_map[ifidx][i].mac, peer_addr, ETH_ALEN); break; } } else { u8 index = key->hw_key_idx - 2 - ifidx * 2; epub->low_map[ifidx][index].flag = 0; memset(epub->low_map[ifidx][index].mac, 0, ETH_ALEN); } //key->hw_key_idx = key->keyidx + ifidx * 2 + 1; } if (key->hw_key_idx >= 6) { /*send sub_scan task to target*/ //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; if(isvalid) atomic_inc(&epub->wl.ptk_cnt); else atomic_dec(&epub->wl.ptk_cnt); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104) #else if (key->alg == ALG_WEP) #endif { if(isvalid) atomic_inc(&epub->wl.gtk_cnt); else atomic_dec(&epub->wl.gtk_cnt); } } else { /*send sub_scan task to target*/ if(isvalid) atomic_inc(&epub->wl.gtk_cnt); else atomic_dec(&epub->wl.gtk_cnt); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) if((key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104)) #else if((key->alg == ALG_WEP)) #endif { if(isvalid) atomic_inc(&epub->wl.ptk_cnt); else atomic_dec(&epub->wl.ptk_cnt); //epub->wl.ptk = (cmd==SET_KEY) ? key : NULL; } } ret = sip_send_setkey(epub, ifidx, peer_addr, key, isvalid); #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)) if((key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_TKIP)) #else if((key->alg == ALG_TKIP)) #endif { if(ret == 0) atomic_set(&epub->wl.tkip_key_set, 1); } ESP_IEEE80211_DBG(ESP_DBG_OP, "%s exit\n", __func__); return ret; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) static void esp_op_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_key_conf *conf, const u8 *address, u32 iv32, u16 *phase1key) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #else static void esp_op_update_tkip_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_key_conf *conf, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #endif /* KERNEL_35 NEW_KERNEL*/ void hw_scan_done(struct esp_pub *epub, bool aborted) { cancel_delayed_work_sync(&epub->scan_timeout_work); ASSERT(epub->wl.scan_req != NULL); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) ieee80211_scan_completed(epub->hw, aborted); #else ieee80211_scan_completed(epub->hw); #endif if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { sip_trigger_txq_process(epub->sip); } } static void hw_scan_timeout_report(struct work_struct *work) { struct esp_pub *epub = container_of(work, struct esp_pub, scan_timeout_work.work); bool aborted; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw scan done\n"); if (test_and_clear_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags)) { sip_trigger_txq_process(epub->sip); } /*check if normally complete or aborted like timeout/hw error */ aborted = (epub->wl.scan_req) ? true : false; if (aborted==true) { epub->wl.scan_req = NULL; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) ieee80211_scan_completed(epub->hw, aborted); #else ieee80211_scan_completed(epub->hw); #endif } #if 0 static void esp_op_sw_scan_start(struct ieee80211_hw *hw) {} static void esp_op_sw_scan_complete(struct ieee80211_hw *hw) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #endif #if 0 static int esp_op_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } static void esp_op_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, u16 *iv16) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #endif static int esp_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, struct ieee80211_sta *sta) #else static int esp_node_attach(struct ieee80211_hw *hw, u8 ifidx, const u8 *addr) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; struct esp_node *node; u8 tidno; struct esp_tx_tid *tid; int i; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) struct sta_info *info = sta_info_get(container_of(hw,struct ieee80211_local,hw),(u8 *)addr); struct ieee80211_ht_info *ht_info = &info->ht_info; #endif spin_lock_bh(&epub->tx_ampdu_lock); if(hweight32(epub->enodes_maps[ifidx]) < ESP_PUB_MAX_STA && (i = ffz(epub->enodes_map)) < ESP_PUB_MAX_STA + 1){ epub->enodes_map |= (1 << i); epub->enodes_maps[ifidx] |= (1 << i); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) node = (struct esp_node *)sta->drv_priv; epub->enodes[i] = node; node->sta = sta; #else node = &epub->nodes[i]; epub->enodes[i] = node; memcpy(node->addr, addr, ETH_ALEN); memcpy(&node->aid, &info->aid, sizeof(node->aid)); memcpy(node->supp_rates, info->supp_rates, sizeof(node->supp_rates)); memcpy(&node->ht_info.cap, &ht_info->cap, sizeof(node->ht_info.cap)); memcpy(&node->ht_info.ht_supported, &ht_info->ht_supported, sizeof(node->ht_info.ht_supported)); memcpy(&node->ht_info.ampdu_factor, &ht_info->ampdu_factor, sizeof(node->ht_info.ampdu_factor)); memcpy(&node->ht_info.ampdu_density, &ht_info->ampdu_density, sizeof(node->ht_info.ampdu_density)); #endif node->ifidx = ifidx; node->index = i; for(tidno = 0, tid = &node->tid[tidno]; tidno < WME_NUM_TID; tidno++) { tid->ssn = 0; tid->cnt = 0; tid->state = ESP_TID_STATE_INIT; } } else { i = -1; } spin_unlock_bh(&epub->tx_ampdu_lock); return i; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, struct ieee80211_sta *sta) #else static int esp_node_detach(struct ieee80211_hw *hw, u8 ifidx, const u8 *addr) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; u8 map; int i; struct esp_node *node = NULL; spin_lock_bh(&epub->tx_ampdu_lock); map = epub->enodes_maps[ifidx]; while(map != 0){ i = ffs(map) - 1; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) if(epub->enodes[i]->sta == sta){ epub->enodes[i]->sta = NULL; #else if(memcmp(epub->enodes[i]->addr, addr, ETH_ALEN) == 0){ #endif node = epub->enodes[i]; epub->enodes[i] = NULL; epub->enodes_map &= ~(1 << i); epub->enodes_maps[ifidx] &= ~(1 << i); spin_unlock_bh(&epub->tx_ampdu_lock); return i; } map &= ~(1 << i); } spin_unlock_bh(&epub->tx_ampdu_lock); return -1; } struct esp_node * esp_get_node_by_addr(struct esp_pub * epub, const u8 *addr) { int i; u8 map; struct esp_node *node = NULL; if(addr == NULL) return NULL; spin_lock_bh(&epub->tx_ampdu_lock); map = epub->enodes_map; while(map != 0){ i = ffs(map) - 1; if(i < 0){ spin_unlock_bh(&epub->tx_ampdu_lock); return NULL; } map &= ~(1 << i); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) if(memcmp(epub->enodes[i]->sta->addr, addr, ETH_ALEN) == 0) #else if(memcmp(epub->enodes[i]->addr, addr, ETH_ALEN) == 0) #endif { node = epub->enodes[i]; break; } } spin_unlock_bh(&epub->tx_ampdu_lock); return node; } int esp_get_empty_rxampdu(struct esp_pub * epub, const u8 *addr, u8 tid) { int index = -1; if(addr == NULL) return index; spin_lock_bh(&epub->rx_ampdu_lock); if((index = ffz(epub->rxampdu_map)) < ESP_PUB_MAX_RXAMPDU){ epub->rxampdu_map |= BIT(index); epub->rxampdu_node[index] = esp_get_node_by_addr(epub, addr); epub->rxampdu_tid[index] = tid; } else { index = -1; } spin_unlock_bh(&epub->rx_ampdu_lock); return index; } int esp_get_exist_rxampdu(struct esp_pub * epub, const u8 *addr, u8 tid) { u8 map; int index = -1; int i; if(addr == NULL) return index; spin_lock_bh(&epub->rx_ampdu_lock); map = epub->rxampdu_map; while(map != 0){ i = ffs(map) - 1; if(i < 0){ spin_unlock_bh(&epub->rx_ampdu_lock); return index; } map &= ~ BIT(i); if(epub->rxampdu_tid[i] == tid && #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) memcmp(epub->rxampdu_node[i]->sta->addr, addr, ETH_ALEN) == 0 #else memcmp(epub->rxampdu_node[i]->addr, addr, ETH_ALEN) == 0 #endif ){ index = i; break; } } epub->rxampdu_map &= ~ BIT(index); spin_unlock_bh(&epub->rx_ampdu_lock); return index; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) #else static int esp_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *addr) #endif { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) struct esp_pub *epub = (struct esp_pub *)hw->priv; #endif struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; int index; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, addr %pM\n", __func__, addr); index = esp_node_attach(hw, evif->index, addr); #else #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, sta addr %pM\n", __func__, sta->addr); #else ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, vif addr %pM, sta addr %pM\n", __func__, vif->addr, sta->addr); #endif index = esp_node_attach(hw, evif->index, sta); #endif if(index < 0) return -1; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) sip_send_set_sta(epub, evif->index, 1, sta, vif, (u8)index); #else //node = esp_get_node_by_addr(epub, addr); //sip_send_set_sta(epub, evif->index, 1, node, vif, (u8)index); #endif return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) #else static int esp_op_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const u8 *addr) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; struct esp_vif *evif = (struct esp_vif *)vif->drv_priv; int index; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) struct esp_node *node; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, addr %pM\n", __func__, addr); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)) ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, sta addr %pM\n", __func__, sta->addr); #else ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, vif addr %pM, sta addr %pM\n", __func__, vif->addr, sta->addr); #endif //remove a connect in target #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) index = esp_node_detach(hw, evif->index, sta); sip_send_set_sta(epub, evif->index, 0, sta, vif, (u8)index); #else node = esp_get_node_by_addr(epub, addr); index = esp_node_detach(hw, evif->index, addr); sip_send_set_sta(epub, evif->index, 0, node, vif, node->index); #endif return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static void esp_op_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) #else static void esp_op_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, const u8 *addr) #endif { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) struct esp_pub *epub = (struct esp_pub *)hw->priv; #endif ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); switch (cmd) { #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) case STA_NOTIFY_ADD: ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s cmd add\n", __func__); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) esp_op_sta_add(hw, vif, sta); #else memcpy(epub->wl.bssid, addr, ETH_ALEN); esp_op_sta_add(hw, vif, addr); #endif break; case STA_NOTIFY_REMOVE: #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) esp_op_sta_remove(hw, vif, sta); #else esp_op_sta_remove(hw, vif, addr); memset(epub->wl.bssid, 0, ETH_ALEN); #endif break; #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) case STA_NOTIFY_SLEEP: break; case STA_NOTIFY_AWAKE: break; #endif /* NEW_KERNEL */ default: break; } } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) static int esp_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params) #else static int esp_op_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) #endif { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return sip_send_wmm_params(epub, queue, params); } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) static int esp_op_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #endif /* !NEW_KERNEL && !KERNEL_35*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) static u64 esp_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) #else static u64 esp_op_get_tsf(struct ieee80211_hw *hw) #endif { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) static void esp_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) #else static void esp_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) #endif { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) static void esp_op_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) #else static void esp_op_reset_tsf(struct ieee80211_hw *hw) #endif { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) static void esp_op_rfkill_poll(struct ieee80211_hw *hw) { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); wiphy_rfkill_set_hw_state(hw->wiphy, test_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags) ? true : false); } #endif #ifdef HW_SCAN #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) static int esp_op_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req) #else static int esp_op_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) #endif /* NEW_KERNEL && KERNEL_35 */ { struct esp_pub *epub = (struct esp_pub *)hw->priv; int i, ret; bool scan_often = true; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s\n", __func__); ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, %d\n", req->n_ssids); ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, len 1:%d,ssid 1:%s\n", req->ssids->ssid_len, req->ssids->ssid_len == 0? "":(char *)req->ssids->ssid); if(req->n_ssids > 1) ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan, len 2:%d,ssid 2:%s\n", (req->ssids+1)->ssid_len, (req->ssids+1)->ssid_len == 0? "":(char *)(req->ssids + 1)->ssid); /*scan_request is keep allocate untill scan_done,record it to split request into multi sdio_cmd*/ if (atomic_read(&epub->wl.off)) { esp_dbg(ESP_DBG_ERROR, "%s scan but wl off \n", __func__); return -EPERM; } if(req->n_ssids > 1){ struct cfg80211_ssid *ssid2 = req->ssids + 1; if((req->ssids->ssid_len > 0 && ssid2->ssid_len > 0) || req->n_ssids > 2){ ESP_IEEE80211_DBG(ESP_DBG_ERROR, "scan ssid num: %d, ssid1:%s, ssid2:%s,not support\n", req->n_ssids, req->ssids->ssid_len == 0 ? "":(char *)req->ssids->ssid, ssid2->ssid_len == 0? "":(char *)ssid2->ssid); return -EINVAL; } } epub->wl.scan_req = req; for (i = 0; i < req->n_channels; i++) ESP_IEEE80211_DBG(ESP_DBG_TRACE, "eagle hw_scan freq %d\n", req->channels[i]->center_freq); #if 0 for (i = 0; i < req->n_ssids; i++) { if (req->ssids->ssid_len> 0) { req->ssids->ssid[req->ssids->ssid_len]='\0'; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "scan_ssid %d:%s\n", i, req->ssids->ssid); } } #endif /*in connect state, suspend tx data*/ if(epub->sip->support_bgscan && test_bit(ESP_WL_FLAG_CONNECT, &epub->wl.flags) && req->n_channels > 0) { scan_often = epub->scan_permit_valid && time_before(jiffies, epub->scan_permit); epub->scan_permit_valid = true; if (!scan_often) { /* epub->scan_permit = jiffies + msecs_to_jiffies(900); set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); if (atomic_read(&epub->txq_stopped) == false) { atomic_set(&epub->txq_stopped, true); ieee80211_stop_queues(hw); } */ } else { ESP_IEEE80211_DBG(ESP_DBG_LOG, "scan too often\n"); return -EACCES; } } else { scan_often = false; } /*send sub_scan task to target*/ ret = sip_send_scan(epub); if (ret) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "fail to send scan_cmd\n"); return ret; } else { if(!scan_often) { epub->scan_permit = jiffies + msecs_to_jiffies(900); set_bit(ESP_WL_FLAG_STOP_TXQ, &epub->wl.flags); if (atomic_read(&epub->txq_stopped) == false) { atomic_set(&epub->txq_stopped, true); ieee80211_stop_queues(hw); } /*force scan complete in case target fail to report in time*/ ieee80211_queue_delayed_work(hw, &epub->scan_timeout_work, req->n_channels * HZ / 4); } } return 0; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) static int esp_op_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type, int duration) { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, center_freq = %d duration = %d\n", __func__, chan->center_freq, duration); sip_send_roc(epub, chan->center_freq, duration); return 0; } static int esp_op_cancel_remain_on_channel(struct ieee80211_hw *hw) { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); epub->roc_flags= 0; // to disable roc state sip_send_roc(epub, 0, 0); return 0; } #endif /* > 2.6.38 */ #endif void esp_rocdone_process(struct ieee80211_hw *hw, struct sip_evt_roc *report) { struct esp_pub *epub = (struct esp_pub *)hw->priv; ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter, state = %d is_ok = %d\n", __func__, report->state, report->is_ok); //roc process begin if((report->state==1)&&(report->is_ok==1)) { epub->roc_flags=1; //flags in roc state, to fix channel, not change #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) ieee80211_ready_on_channel(hw); #endif } else if ((report->state==0)&&(report->is_ok==1)) //roc process timeout { epub->roc_flags= 0; // to disable roc state #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) ieee80211_remain_on_channel_expired(hw); #endif } } #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) static int esp_op_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const struct cfg80211_bitrate_mask *mask) { ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); ESP_IEEE80211_DBG(ESP_DBG_OP, "%s vif->macaddr[%pM], mask[%d]\n", __func__, vif->addr, mask->control[0].legacy); return 0; } #endif void esp_op_flush(struct ieee80211_hw *hw, bool drop) { ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); do{ struct esp_pub *epub = (struct esp_pub *)hw->priv; unsigned long time = jiffies + msecs_to_jiffies(15); while(atomic_read(&epub->sip->tx_data_pkt_queued)){ if(!time_before(jiffies, time)){ break; } #if !defined(FPGA_LOOPBACK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) ieee80211_queue_work(epub->hw, &epub->tx_work); #else queue_work(epub->esp_wkq, &epub->tx_work); #endif //sip_txq_process(epub); } mdelay(10); }while(0); } #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) static int esp_op_ampdu_action(struct ieee80211_hw *hw, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) #else static int esp_op_ampdu_action(struct ieee80211_hw *hw, enum ieee80211_ampdu_mlme_action action, const u8 *addr, u16 tid, u16 *ssn) #endif #else #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) static int esp_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) #else static int esp_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size) #endif #endif /* NEW_KERNEL && KERNEL_35 */ { int ret = -EOPNOTSUPP; struct esp_pub *epub = (struct esp_pub *)hw->priv; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) struct esp_node * node = (struct esp_node *)sta->drv_priv; #else struct esp_node * node = esp_get_node_by_addr(epub, addr); #endif struct esp_tx_tid * tid_info = &node->tid[tid]; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) u8 buf_size = 64; #endif ESP_IEEE80211_DBG(ESP_DBG_OP, "%s enter \n", __func__); switch(action) { case IEEE80211_AMPDU_TX_START: if (mod_support_no_txampdu() || #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) hw->conf.channel_type == NL80211_CHAN_NO_HT #else !(hw->conf.flags&IEEE80211_CONF_SUPPORT_HT_MODE) #endif ) return ret; //if (vif->p2p || vif->type != NL80211_IFTYPE_STATION) // return ret; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s TX START, addr:%pM,tid:%u\n", __func__, addr, tid); #else ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s TX START, addr:%pM,tid:%u,state:%d\n", __func__, sta->addr, tid, tid_info->state); #endif spin_lock_bh(&epub->tx_ampdu_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ASSERT(tid_info->state == ESP_TID_STATE_TRIGGER); *ssn = tid_info->ssn; tid_info->state = ESP_TID_STATE_PROGRESS; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ieee80211_start_tx_ba_cb_irqsafe(hw, addr, tid); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); #else ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); #endif spin_unlock_bh(&epub->tx_ampdu_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) ret = 0; #else spin_lock_bh(&epub->tx_ampdu_lock); if (tid_info->state != ESP_TID_STATE_PROGRESS) { if (tid_info->state == ESP_TID_STATE_INIT) { printk(KERN_ERR "%s WIFI RESET, IGNORE\n", __func__); spin_unlock_bh(&epub->tx_ampdu_lock); return -ENETRESET; } else { ASSERT(0); } } tid_info->state = ESP_TID_STATE_OPERATIONAL; spin_unlock_bh(&epub->tx_ampdu_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) ret = sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, sta->addr, tid, node->ifidx, buf_size); #else ret = sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, addr, tid, node->ifidx, buf_size); #endif #endif break; case IEEE80211_AMPDU_TX_STOP: #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s TX STOP, addr:%pM,tid:%u\n", __func__, addr, tid); #else ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s TX STOP, addr:%pM,tid:%u,state:%d\n", __func__, sta->addr, tid, tid_info->state); #endif spin_lock_bh(&epub->tx_ampdu_lock); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) if(tid_info->state == ESP_TID_STATE_WAIT_STOP) tid_info->state = ESP_TID_STATE_STOP; else tid_info->state = ESP_TID_STATE_INIT; #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ieee80211_stop_tx_ba_cb_irqsafe(hw, addr, tid); #elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); #else ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); #endif spin_unlock_bh(&epub->tx_ampdu_lock); #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ret = sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, addr, tid, node->ifidx, 0); #else ret = sip_send_ampdu_action(epub, SIP_AMPDU_TX_STOP, sta->addr, tid, node->ifidx, 0); #endif break; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) case IEEE80211_AMPDU_TX_OPERATIONAL: #else case IEEE80211_AMPDU_TX_RESUME: #endif ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s TX OPERATION, addr:%pM,tid:%u,state:%d\n", __func__, sta->addr, tid, tid_info->state); spin_lock_bh(&epub->tx_ampdu_lock); if (tid_info->state != ESP_TID_STATE_PROGRESS) { if (tid_info->state == ESP_TID_STATE_INIT) { printk(KERN_ERR "%s WIFI RESET, IGNORE\n", __func__); spin_unlock_bh(&epub->tx_ampdu_lock); return -ENETRESET; } else { ASSERT(0); } } tid_info->state = ESP_TID_STATE_OPERATIONAL; spin_unlock_bh(&epub->tx_ampdu_lock); ret = sip_send_ampdu_action(epub, SIP_AMPDU_TX_OPERATIONAL, sta->addr, tid, node->ifidx, buf_size); break; #endif case IEEE80211_AMPDU_RX_START: if(mod_support_no_rxampdu() || #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) hw->conf.channel_type == NL80211_CHAN_NO_HT #else !(hw->conf.flags&IEEE80211_CONF_SUPPORT_HT_MODE) #endif ) return ret; if ( #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) (vif->p2p && false) #else false #endif #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)) || false #else || (vif->type != NL80211_IFTYPE_STATION && false) #endif ) return ret; #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s RX START %pM tid %u %u\n", __func__, addr, tid, *ssn); ret = sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, addr, tid, *ssn, 64); #else ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s RX START %pM tid %u %u\n", __func__, sta->addr, tid, *ssn); ret = sip_send_ampdu_action(epub, SIP_AMPDU_RX_START, sta->addr, tid, *ssn, 64); #endif break; case IEEE80211_AMPDU_RX_STOP: #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s RX STOP %pM tid %u\n", __func__, addr, tid); ret = sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, addr, tid, 0, 0); #else ESP_IEEE80211_DBG(ESP_DBG_ERROR, "%s RX STOP %pM tid %u\n", __func__, sta->addr, tid); ret = sip_send_ampdu_action(epub, SIP_AMPDU_RX_STOP, sta->addr, tid, 0, 0); #endif break; default: break; } return ret; } #if 0 static int esp_op_tx_last_beacon(struct ieee80211_hw *hw) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #ifdef CONFIG_NL80211_TESTMODE static int esp_op_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) { ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter \n", __func__); return 0; } #endif /* CONFIG_NL80211_TESTMODE */ #endif static void esp_tx_work(struct work_struct *work) { struct esp_pub *epub = container_of(work, struct esp_pub, tx_work); mutex_lock(&epub->tx_mtx); sip_txq_process(epub); mutex_unlock(&epub->tx_mtx); } #ifndef RX_SENDUP_SYNC //for debug static int data_pkt_dequeue_cnt = 0; static void _esp_flush_rxq(struct esp_pub *epub) { struct sk_buff *skb = NULL; while ((skb = skb_dequeue(&epub->rxq))) { esp_dbg(ESP_DBG_TRACE, "%s call ieee80211_rx \n", __func__); //local_bh_disable(); ieee80211_rx(epub->hw, skb); //local_bh_enable(); } } static void esp_sendup_work(struct work_struct *work) { struct esp_pub *epub = container_of(work, struct esp_pub, sendup_work); spin_lock_bh(&epub->rx_lock); _esp_flush_rxq(epub); spin_unlock_bh(&epub->rx_lock); } #endif /* !RX_SENDUP_SYNC */ static const struct ieee80211_ops esp_mac80211_ops = { .tx = esp_op_tx, .start = esp_op_start, .stop = esp_op_stop, #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) #ifdef CONFIG_PM .suspend = esp_op_suspend, .resume = esp_op_resume, #endif #endif .add_interface = esp_op_add_interface, .remove_interface = esp_op_remove_interface, .config = esp_op_config, .bss_info_changed = esp_op_bss_info_changed, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)) .config_interface = esp_op_config_interface, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) .prepare_multicast = esp_op_prepare_multicast, #endif .configure_filter = esp_op_configure_filter, .set_key = esp_op_set_key, .update_tkip_key = esp_op_update_tkip_key, //.sched_scan_start = esp_op_sched_scan_start, //.sched_scan_stop = esp_op_sched_scan_stop, .set_rts_threshold = esp_op_set_rts_threshold, .sta_notify = esp_op_sta_notify, .conf_tx = esp_op_conf_tx, #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)) .get_tx_stats = esp_op_get_tx_stats, #endif /* KERNEL_VERSION < 2.6.35*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) .change_interface = esp_op_change_interface, #endif .get_tsf = esp_op_get_tsf, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) .set_tsf = esp_op_set_tsf, #endif .reset_tsf = esp_op_reset_tsf, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) .rfkill_poll= esp_op_rfkill_poll, #endif #ifdef HW_SCAN .hw_scan = esp_op_hw_scan, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) .remain_on_channel= esp_op_remain_on_channel, .cancel_remain_on_channel=esp_op_cancel_remain_on_channel, #endif /* >=2.6.38 */ #endif .ampdu_action = esp_op_ampdu_action, //.get_survey = esp_op_get_survey, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) .sta_add = esp_op_sta_add, .sta_remove = esp_op_sta_remove, #endif /* >= 2.6.34 */ #ifdef CONFIG_NL80211_TESTMODE //CFG80211_TESTMODE_CMD(esp_op_tm_cmd) #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) .set_bitrate_mask = esp_op_set_bitrate_mask, #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) .flush = esp_op_flush, #endif }; struct esp_pub * esp_pub_alloc_mac80211(struct device *dev) { struct ieee80211_hw *hw; struct esp_pub *epub; int ret = 0; hw = ieee80211_alloc_hw(sizeof(struct esp_pub), &esp_mac80211_ops); if (hw == NULL) { esp_dbg(ESP_DBG_ERROR, "ieee80211 can't alloc hw!\n"); ret = -ENOMEM; return ERR_PTR(ret); } epub = hw->priv; memset(epub, 0, sizeof(*epub)); epub->hw = hw; SET_IEEE80211_DEV(hw, dev); epub->dev = dev; skb_queue_head_init(&epub->txq); skb_queue_head_init(&epub->txdoneq); skb_queue_head_init(&epub->rxq); spin_lock_init(&epub->tx_ampdu_lock); spin_lock_init(&epub->rx_ampdu_lock); spin_lock_init(&epub->tx_lock); mutex_init(&epub->tx_mtx); spin_lock_init(&epub->rx_lock); INIT_WORK(&epub->tx_work, esp_tx_work); #ifndef RX_SENDUP_SYNC INIT_WORK(&epub->sendup_work, esp_sendup_work); #endif //!RX_SENDUP_SYNC #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)) //epub->esp_wkq = create_freezeable_workqueue("esp_wkq"); epub->esp_wkq = create_singlethread_workqueue("esp_wkq"); #else //epub->esp_wkq = create_freezable_workqueue("esp_wkq"); epub->esp_wkq = create_singlethread_workqueue("esp_wkq"); #endif /* NEW_KERNEL */ if (epub->esp_wkq == NULL) { ret = -ENOMEM; return ERR_PTR(ret); } epub->scan_permit_valid = false; INIT_DELAYED_WORK(&epub->scan_timeout_work, hw_scan_timeout_report); return epub; } int esp_pub_dealloc_mac80211(struct esp_pub *epub) { set_bit(ESP_WL_FLAG_RFKILL, &epub->wl.flags); destroy_workqueue(epub->esp_wkq); mutex_destroy(&epub->tx_mtx); #ifdef ESP_NO_MAC80211 free_netdev(epub->net_dev); wiphy_free(epub->wdev->wiphy); kfree(epub->wdev); #else if (epub->hw) { ieee80211_free_hw(epub->hw); } #endif return 0; } #if 0 static int esp_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_supported_band *sband; struct ieee80211_channel *ch; int i; ESP_IEEE80211_DBG(ESP_DBG_TRACE, "%s enter %d\n", __func__, request->initiator ); //TBD } #endif /* 2G band channels */ static struct ieee80211_channel esp_channels_2ghz[] = { { .hw_value = 1, .center_freq = 2412, .max_power = 25 }, { .hw_value = 2, .center_freq = 2417, .max_power = 25 }, { .hw_value = 3, .center_freq = 2422, .max_power = 25 }, { .hw_value = 4, .center_freq = 2427, .max_power = 25 }, { .hw_value = 5, .center_freq = 2432, .max_power = 25 }, { .hw_value = 6, .center_freq = 2437, .max_power = 25 }, { .hw_value = 7, .center_freq = 2442, .max_power = 25 }, { .hw_value = 8, .center_freq = 2447, .max_power = 25 }, { .hw_value = 9, .center_freq = 2452, .max_power = 25 }, { .hw_value = 10, .center_freq = 2457, .max_power = 25 }, { .hw_value = 11, .center_freq = 2462, .max_power = 25 }, { .hw_value = 12, .center_freq = 2467, .max_power = 25 }, { .hw_value = 13, .center_freq = 2472, .max_power = 25 }, //{ .hw_value = 14, .center_freq = 2484, .max_power = 25 }, }; /* 11G rate */ static struct ieee80211_rate esp_rates_2ghz[] = { { .bitrate = 10, .hw_value = CONF_HW_BIT_RATE_1MBPS, .hw_value_short = CONF_HW_BIT_RATE_1MBPS, }, { .bitrate = 20, .hw_value = CONF_HW_BIT_RATE_2MBPS, .hw_value_short = CONF_HW_BIT_RATE_2MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 55, .hw_value = CONF_HW_BIT_RATE_5_5MBPS, .hw_value_short = CONF_HW_BIT_RATE_5_5MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 110, .hw_value = CONF_HW_BIT_RATE_11MBPS, .hw_value_short = CONF_HW_BIT_RATE_11MBPS, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, { .bitrate = 60, .hw_value = CONF_HW_BIT_RATE_6MBPS, .hw_value_short = CONF_HW_BIT_RATE_6MBPS, }, { .bitrate = 90, .hw_value = CONF_HW_BIT_RATE_9MBPS, .hw_value_short = CONF_HW_BIT_RATE_9MBPS, }, { .bitrate = 120, .hw_value = CONF_HW_BIT_RATE_12MBPS, .hw_value_short = CONF_HW_BIT_RATE_12MBPS, }, { .bitrate = 180, .hw_value = CONF_HW_BIT_RATE_18MBPS, .hw_value_short = CONF_HW_BIT_RATE_18MBPS, }, { .bitrate = 240, .hw_value = CONF_HW_BIT_RATE_24MBPS, .hw_value_short = CONF_HW_BIT_RATE_24MBPS, }, { .bitrate = 360, .hw_value = CONF_HW_BIT_RATE_36MBPS, .hw_value_short = CONF_HW_BIT_RATE_36MBPS, }, { .bitrate = 480, .hw_value = CONF_HW_BIT_RATE_48MBPS, .hw_value_short = CONF_HW_BIT_RATE_48MBPS, }, { .bitrate = 540, .hw_value = CONF_HW_BIT_RATE_54MBPS, .hw_value_short = CONF_HW_BIT_RATE_54MBPS, }, }; static void esp_pub_init_mac80211(struct esp_pub *epub) { struct ieee80211_hw *hw = epub->hw; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) static const u32 cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, }; #endif hw->channel_change_time = 420000; /* in us */ hw->max_listen_interval = 10; hw->flags = IEEE80211_HW_SIGNAL_DBM | #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) IEEE80211_HW_HAS_RATE_CONTROL | #endif /* >= 2.6.33 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) IEEE80211_HW_SUPPORTS_PS | #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) IEEE80211_HW_AMPDU_AGGREGATION | #endif IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; //IEEE80211_HW_PS_NULLFUNC_STACK | //IEEE80211_HW_CONNECTION_MONITOR | //IEEE80211_HW_BEACON_FILTER | //IEEE80211_HW_AMPDU_AGGREGATION | //IEEE80211_HW_REPORTS_TX_ACK_STATUS; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)) hw->max_rx_aggregation_subframes = 0x40; hw->max_tx_aggregation_subframes = 0x40; #endif /* >= 2.6.39 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) hw->wiphy->cipher_suites = cipher_suites; hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); hw->wiphy->max_scan_ie_len = epub->sip->tx_blksz - sizeof(struct sip_hdr) - sizeof(struct sip_cmd_scan); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) /* ONLY station for now, support P2P soon... */ hw->wiphy->interface_modes = #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | #endif BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) hw->wiphy->max_scan_ssids = 2; //hw->wiphy->max_sched_scan_ssids = 16; //hw->wiphy->max_match_sets = 16; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) hw->wiphy->max_remain_on_channel_duration = 5000; #endif atomic_set(&epub->wl.off, 1); epub->wl.sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; epub->wl.sbands[IEEE80211_BAND_2GHZ].channels = esp_channels_2ghz; epub->wl.sbands[IEEE80211_BAND_2GHZ].bitrates = esp_rates_2ghz; epub->wl.sbands[IEEE80211_BAND_2GHZ].n_channels = ARRAY_SIZE(esp_channels_2ghz); epub->wl.sbands[IEEE80211_BAND_2GHZ].n_bitrates = ARRAY_SIZE(esp_rates_2ghz); /*add to support 11n*/ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.ht_supported = true; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.cap = 0x116C;//IEEE80211_HT_CAP_RX_STBC; //IEEE80211_HT_CAP_SGI_20; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)) epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; #else epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.ampdu_factor = 1;//IEEE80211_HT_MAX_AMPDU_16K; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.ampdu_density = 0;//IEEE80211_HT_MPDU_DENSITY_NONE; #endif memset(&epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.mcs, 0, sizeof(epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.mcs)); epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.mcs.rx_mask[0] = 0xff; //epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.mcs.rx_highest = 7; //epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; #else epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.ht_supported = true; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.cap = 0x116C;//IEEE80211_HT_CAP_RX_STBC; //IEEE80211_HT_CAP_SGI_20; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.ampdu_factor = 1;//IEEE80211_HT_MAX_AMPDU_16K; epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.ampdu_density = 0;//IEEE80211_HT_MPDU_DENSITY_NONE; memset(&epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.supp_mcs_set, 0, sizeof(epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.supp_mcs_set)); epub->wl.sbands[IEEE80211_BAND_2GHZ].ht_info.supp_mcs_set[0] = 0xff; #endif /* BAND_5GHZ TBD */ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &epub->wl.sbands[IEEE80211_BAND_2GHZ]; /* BAND_5GHZ TBD */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) /*no fragment*/ hw->wiphy->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; #endif /* handle AC queue in f/w */ hw->queues = 4; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) hw->max_rates = 4; #else hw->max_altrates = 4; #endif #endif //hw->wiphy->reg_notifier = esp_reg_notify; hw->vif_data_size = sizeof(struct esp_vif); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) hw->sta_data_size = sizeof(struct esp_node); #endif //hw->max_rx_aggregation_subframes = 8; } int esp_register_mac80211(struct esp_pub *epub) { int ret = 0; int varlen = 100; unsigned char buf[100] = {0}; #ifdef P2P_CONCURRENT u8 *wlan_addr; u8 *p2p_addr; int idx; #endif ret = wmt_getsyspara("wmt.wifi.addr", buf, &varlen); if (ret == 0){ sscanf(buf, "%x:%x:%x:%x:%x:%x", &(epub->mac_addr[0]),&(epub->mac_addr[1]),&(epub->mac_addr[2]),&(epub->mac_addr[3]),&(epub->mac_addr[4]),&(epub->mac_addr[5])); printk("get mac addr(%s) from env wmt.wifi.addr!!\n", buf); } esp_pub_init_mac80211(epub); #ifdef P2P_CONCURRENT epub->hw->wiphy->addresses = (struct mac_address *)esp_mac_addr; memcpy(&epub->hw->wiphy->addresses[0], epub->mac_addr, ETH_ALEN); memcpy(&epub->hw->wiphy->addresses[1], epub->mac_addr, ETH_ALEN); wlan_addr = (u8 *)&epub->hw->wiphy->addresses[0]; p2p_addr = (u8 *)&epub->hw->wiphy->addresses[1]; for (idx = 0; idx < 64; idx++) { p2p_addr[0] = wlan_addr[0] | 0x02; p2p_addr[0] ^= idx << 2; if (strncmp(p2p_addr, wlan_addr, 6) != 0) break; } epub->hw->wiphy->n_addresses = 2; #else SET_IEEE80211_PERM_ADDR(epub->hw, epub->mac_addr); #endif ret = ieee80211_register_hw(epub->hw); if (ret < 0) { ESP_IEEE80211_DBG(ESP_DBG_ERROR, "unable to register mac80211 hw: %d\n", ret); return ret; } else { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) #ifdef MAC80211_NO_CHANGE rtnl_lock(); if (epub->hw->wiphy->interface_modes & (BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT))) { ret = ieee80211_if_add(hw_to_local(epub->hw), "p2p%d", NULL, NL80211_IFTYPE_STATION, NULL); if (ret) wiphy_warn(epub->hw->wiphy, "Failed to add default virtual iface\n"); } rtnl_unlock(); #endif #endif } set_bit(ESP_WL_FLAG_HW_REGISTERED, &epub->wl.flags); return ret; } static u8 getaddr_index(u8 * addr, struct esp_pub *epub) { #ifdef P2P_CONCURRENT int i; for(i = 0; i < ESP_PUB_MAX_VIF; i++) if(memcmp(addr, (u8 *)&epub->hw->wiphy->addresses[i], ETH_ALEN) == 0) return i; return ESP_PUB_MAX_VIF; #else return 0; #endif }