diff options
Diffstat (limited to 'drivers/net/wireless/eagle/esp_mac80211.c')
-rwxr-xr-x | drivers/net/wireless/eagle/esp_mac80211.c | 2117 |
1 files changed, 2117 insertions, 0 deletions
diff --git a/drivers/net/wireless/eagle/esp_mac80211.c b/drivers/net/wireless/eagle/esp_mac80211.c new file mode 100755 index 00000000..1a24ce64 --- /dev/null +++ b/drivers/net/wireless/eagle/esp_mac80211.c @@ -0,0 +1,2117 @@ +/* + * Copyright (c) 2011 Espressif System. + * + * MAC80211 support module + */ +#include <linux/etherdevice.h> +#include <linux/workqueue.h> +#include <linux/nl80211.h> +#include <linux/ieee80211.h> +#include <linux/slab.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> +#include <linux/version.h> +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) +#include <net/regulatory.h> +#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 +} + |