/* * Copyright (c) 2015 South Silicon Valley Microelectronics Inc. * Copyright (c) 2015 iComm Corporation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) #include #else #include #endif #include #include #include #include #include "dev_tbl.h" #include "dev.h" #include "lib.h" #include "ssv_rc.h" #include "ap.h" #include "efuse.h" #include "sar.h" #include "ssv_cfgvendor.h" #ifdef CONFIG_SSV_SUPPORT_ANDROID #include "ssv_pm.h" #endif #include "linux_80211.h" #if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) #include "linux_2_6_35.h" #endif #ifdef CONFIG_SSV6XXX_DEBUGFS #include "ssv6xxx_debugfs.h" #endif #ifdef MULTI_THREAD_ENCRYPT #include #include #endif MODULE_AUTHOR("iComm Semiconductor Co., Ltd"); MODULE_DESCRIPTION("Support for SSV6xxx wireless LAN cards."); MODULE_SUPPORTED_DEVICE("SSV6xxx 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); #define WIFI_FIRMWARE_NAME "ssv6051-sw.bin" static const struct ieee80211_iface_limit ssv6xxx_p2p_limits[] = { { .max = 2, .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_AP), }, }; static const struct ieee80211_iface_combination ssv6xxx_iface_combinations_p2p[] = { { .num_different_channels = 1, .max_interfaces = SSV6200_MAX_VIF, .beacon_int_infra_match = true, .limits = ssv6xxx_p2p_limits, .n_limits = ARRAY_SIZE(ssv6xxx_p2p_limits), }, }; #define LBYTESWAP(a) ((((a) & 0x00ff00ff) << 8) | \ (((a) & 0xff00ff00) >> 8)) #define LONGSWAP(a) ((LBYTESWAP(a) << 16) | (LBYTESWAP(a) >> 16)) #define CHAN2G(_freq,_idx) { \ .band = INDEX_80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_idx), \ .max_power = 20, \ } #ifndef WLAN_CIPHER_SUITE_SMS4 #define WLAN_CIPHER_SUITE_SMS4 0x00147201 #endif #define SHPCHECK(__hw_rate,__flags) \ ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate +3 ) : 0) #define RATE(_bitrate,_hw_rate,_flags) { \ .bitrate = (_bitrate), \ .flags = (_flags), \ .hw_value = (_hw_rate), \ .hw_value_short = SHPCHECK(_hw_rate,_flags) \ } extern struct ssv6xxx_cfg ssv_cfg; static const struct ieee80211_channel ssv6200_2ghz_chantable[] = { CHAN2G(2412, 1 ), CHAN2G(2417, 2 ), CHAN2G(2422, 3 ), CHAN2G(2427, 4 ), CHAN2G(2432, 5 ), CHAN2G(2437, 6 ), CHAN2G(2442, 7 ), CHAN2G(2447, 8 ), CHAN2G(2452, 9 ), CHAN2G(2457, 10), CHAN2G(2462, 11), CHAN2G(2467, 12), CHAN2G(2472, 13), CHAN2G(2484, 14), }; static struct ieee80211_rate ssv6200_legacy_rates[] = { RATE(10, 0x00, 0), RATE(20, 0x01, IEEE80211_RATE_SHORT_PREAMBLE), RATE(55, 0x02, IEEE80211_RATE_SHORT_PREAMBLE), RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE), RATE(60, 0x07, 0), RATE(90, 0x08, 0), RATE(120, 0x09, 0), RATE(180, 0x0a, 0), RATE(240, 0x0b, 0), RATE(360, 0x0c, 0), RATE(480, 0x0d, 0), RATE(540, 0x0e, 0), }; #ifdef CONFIG_SSV_SUPPORT_ANDROID extern struct ssv_softc * ssv_notify_sc; #endif #ifdef CONFIG_SSV_CABRIO_E struct ssv6xxx_ch_cfg ch_cfg_z[] = { {ADR_ABB_REGISTER_1, 0, 0x151559fc}, {ADR_LDO_REGISTER, 0, 0x00eb7c1c}, {ADR_RX_ADC_REGISTER, 0, 0x20d000d2} }; struct ssv6xxx_ch_cfg ch_cfg_p[] = { {ADR_ABB_REGISTER_1, 0, 0x151559fc}, {ADR_RX_ADC_REGISTER, 0, 0x20d000d2} }; int ssv6xxx_do_iq_calib(struct ssv_hw *sh, struct ssv6xxx_iqk_cfg *p_cfg) { struct sk_buff *skb; struct cfg_host_cmd *host_cmd; int ret = 0; printk("# Do init_cali (iq)\n"); skb = ssv_skb_alloc(HOST_CMD_HDR_LEN + IQK_CFG_LEN + PHY_SETTING_SIZE + RF_SETTING_SIZE); if(skb == NULL) { printk("init ssv6xxx_do_iq_calib fail!!!\n"); return (-1); } if((PHY_SETTING_SIZE > MAX_PHY_SETTING_TABLE_SIZE) || (RF_SETTING_SIZE > MAX_RF_SETTING_TABLE_SIZE)) { printk("Please recheck RF or PHY table size!!!\n"); WARN_ON(1); return (-1); } skb->data_len = HOST_CMD_HDR_LEN + IQK_CFG_LEN + PHY_SETTING_SIZE + RF_SETTING_SIZE; skb->len = skb->data_len; host_cmd = (struct cfg_host_cmd *)skb->data; host_cmd->c_type = HOST_CMD; host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_INIT_CALI; host_cmd->len = skb->data_len; p_cfg->phy_tbl_size = PHY_SETTING_SIZE; p_cfg->rf_tbl_size = RF_SETTING_SIZE; memcpy(host_cmd->dat32, p_cfg, IQK_CFG_LEN); memcpy(host_cmd->dat8+IQK_CFG_LEN, phy_setting, PHY_SETTING_SIZE); memcpy(host_cmd->dat8+IQK_CFG_LEN+PHY_SETTING_SIZE, ssv6200_rf_tbl, RF_SETTING_SIZE); sh->hci.hci_ops->hci_send_cmd(skb); ssv_skb_free(skb); { u32 timeout; sh->sc->iq_cali_done = IQ_CALI_RUNNING; set_current_state(TASK_INTERRUPTIBLE); timeout = wait_event_interruptible_timeout(sh->sc->fw_wait_q, sh->sc->iq_cali_done, msecs_to_jiffies(500)); set_current_state(TASK_RUNNING); if (timeout == 0) return -ETIME; if (sh->sc->iq_cali_done != IQ_CALI_OK) return (-1); } return ret; } #endif #define HT_CAP_RX_STBC_ONE_STREAM 0x1 #ifdef CONFIG_SSV_WAPI static const u32 ssv6xxx_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_SMS4, WLAN_CIPHER_SUITE_AES_CMAC }; #endif #if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) static const struct wiphy_wowlan_support wowlan_support = { #ifdef SSV_WAKEUP_HOST .flags = WIPHY_WOWLAN_ANY, #else .flags = WIPHY_WOWLAN_DISCONNECT, #endif .n_patterns = 0, .pattern_max_len = 0, .pattern_min_len = 0, #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) .max_pkt_offset = 0, #endif }; #endif static void ssv6xxx_set_80211_hw_capab(struct ssv_softc *sc) { struct ieee80211_hw *hw=sc->hw; struct ssv_hw *sh=sc->sh; struct ieee80211_sta_ht_cap *ht_info; #ifdef CONFIG_SSV_WAPI hw->wiphy->cipher_suites = ssv6xxx_cipher_suites; hw->wiphy->n_cipher_suites = ARRAY_SIZE(ssv6xxx_cipher_suites); #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) hw->flags = IEEE80211_HW_SIGNAL_DBM; #else ieee80211_hw_set(hw, SIGNAL_DBM); #endif #ifdef CONFIG_SSV_SUPPORT_ANDROID #endif hw->rate_control_algorithm = "ssv6xxx_rate_control"; ht_info = &sc->sbands[INDEX_80211_BAND_2GHZ].ht_cap; ampdu_db_log("sh->cfg.hw_caps = 0x%x\n", sh->cfg.hw_caps); if (sh->cfg.hw_caps & SSV6200_HW_CAP_HT) { if (sh->cfg.hw_caps & SSV6200_HW_CAP_AMPDU_RX) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; ampdu_db_log("set IEEE80211_HW_AMPDU_AGGREGATION(0x%x)\n", ((hw->flags)&IEEE80211_HW_AMPDU_AGGREGATION)); #else ieee80211_hw_set(hw, AMPDU_AGGREGATION); ampdu_db_log("set IEEE80211_HW_AMPDU_AGGREGATION(%d)\n", ieee80211_hw_check(hw, AMPDU_AGGREGATION)); #endif } ht_info->cap = IEEE80211_HT_CAP_SM_PS; if (sh->cfg.hw_caps & SSV6200_HW_CAP_GF) { ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= HT_CAP_RX_STBC_ONE_STREAM<cfg.hw_caps & SSV6200_HT_CAP_SGI_20) ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); ht_info->mcs.rx_mask[0] = 0xff; ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; ht_info->mcs.rx_highest = cpu_to_le16(SSV6200_RX_HIGHEST_RATE); ht_info->ht_supported = true; } hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) if (sh->cfg.hw_caps & SSV6200_HW_CAP_P2P) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT); hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_GO); hw->wiphy->iface_combinations = ssv6xxx_iface_combinations_p2p; hw->wiphy->n_iface_combinations = ARRAY_SIZE(ssv6xxx_iface_combinations_p2p); #if LINUX_VERSION_CODE < KERNEL_VERSION(3,5,0) hw->wiphy->flags |= WIPHY_FLAG_ENFORCE_COMBINATIONS; #endif } #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(3,5,0) hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; #endif if (sh->cfg.hw_caps & SSV6200_HW_CAP_AP){ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); #if LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; #endif } #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0) if (sh->cfg.hw_caps & SSV6200_HW_CAP_TDLS){ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; printk("TDLS function enabled in sta.cfg\n"); } #endif hw->queues = 4; hw->max_rates = 4; #if LINUX_VERSION_CODE < KERNEL_VERSION(3,14,0) hw->channel_change_time = 5000; #endif hw->max_listen_interval = 1; hw->max_rate_tries = HW_MAX_RATE_TRIES; hw->extra_tx_headroom = TXPB_OFFSET + AMPDU_DELIMITER_LEN; if (sizeof(struct ampdu_hdr_st) > SSV_SKB_info_size) hw->extra_tx_headroom += sizeof(struct ampdu_hdr_st); else hw->extra_tx_headroom += SSV_SKB_info_size; if (sh->cfg.hw_caps & SSV6200_HW_CAP_2GHZ) { hw->wiphy->bands[INDEX_80211_BAND_2GHZ] = &sc->sbands[INDEX_80211_BAND_2GHZ]; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) if (sh->cfg.hw_caps & SSV6200_HW_CAP_AMPDU_TX) #ifdef PREFER_RX hw->max_rx_aggregation_subframes = 64; #else hw->max_rx_aggregation_subframes = 16; #endif else hw->max_rx_aggregation_subframes = 12; hw->max_tx_aggregation_subframes = 64; #endif hw->sta_data_size = sizeof(struct ssv_sta_priv_data); hw->vif_data_size = sizeof(struct ssv_vif_priv_data); memcpy(sh->maddr[0].addr, &sh->cfg.maddr[0][0], ETH_ALEN); hw->wiphy->addresses = sh->maddr; hw->wiphy->n_addresses = 1; if (sh->cfg.hw_caps & SSV6200_HW_CAP_P2P) { int i; for (i = 1; i < SSV6200_MAX_HW_MAC_ADDR; i++) { memcpy(sh->maddr[i].addr, sh->maddr[i-1].addr, ETH_ALEN); sh->maddr[i].addr[5]++; hw->wiphy->n_addresses++; } } if (!is_zero_ether_addr(sh->cfg.maddr[1])) { memcpy(sh->maddr[1].addr, sh->cfg.maddr[1], ETH_ALEN); if (hw->wiphy->n_addresses < 2) hw->wiphy->n_addresses = 2; } #if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) #if (LINUX_VERSION_CODE < KERNEL_VERSION(3,11,0)) hw->wiphy->wowlan = wowlan_support; #else hw->wiphy->wowlan = &wowlan_support; #endif #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(CONFIG_SSV_VENDOR_EXT_SUPPORT) { int err = 0; struct ssv_softc *softc = (struct ssv_softc *)hw->priv; if (softc) { set_wiphy_dev(hw->wiphy, softc->dev); *((struct ssv_softc **)wiphy_priv(hw->wiphy)) = softc; } printk("Registering Vendor80211\n"); err = ssv_cfgvendor_attach(hw->wiphy); if (unlikely(err < 0)) { printk("Couldn not attach vendor commands (%d)\n", err); } } #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 14, 0)) || defined(WL_VENDOR_EXT_SUPPORT) */ } #ifdef MULTI_THREAD_ENCRYPT int ssv6xxx_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { struct ssv_softc *sc = container_of(nfb, struct ssv_softc, cpu_nfb); int hotcpu = (unsigned long)hcpu; struct ssv_encrypt_task_list *ta = NULL; switch (action) { case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: case CPU_DOWN_FAILED: { int cpu = 0; list_for_each_entry(ta, &sc->encrypt_task_head, list) { if(cpu == hotcpu) break; cpu++; } if(ta->encrypt_task->state & TASK_UNINTERRUPTIBLE) { kthread_bind(ta->encrypt_task, hotcpu); } printk("encrypt_task %p state is %ld\n", ta->encrypt_task, ta->encrypt_task->state); break; } case CPU_ONLINE: case CPU_ONLINE_FROZEN: { int cpu = 0; list_for_each_entry(ta, &sc->encrypt_task_head, list) { if(cpu == hotcpu) { ta->cpu_offline = 0; if ( (ta->started == 0) && (cpu_online(cpu)) ) { wake_up_process(ta->encrypt_task); ta->started = 1; printk("wake up encrypt_task %p state is %ld, cpu = %d\n", ta->encrypt_task, ta->encrypt_task->state, cpu); } break; } cpu++; } printk("encrypt_task %p state is %ld\n", ta->encrypt_task, ta->encrypt_task->state); break; } #ifdef CONFIG_HOTPLUG_CPU case CPU_UP_CANCELED: case CPU_UP_CANCELED_FROZEN: case CPU_DOWN_PREPARE: { int cpu = 0; list_for_each_entry(ta, &sc->encrypt_task_head, list) { if(cpu == hotcpu) { ta->cpu_offline = 1; break; } cpu++; } printk("p = %p\n",ta->encrypt_task); break; } case CPU_DEAD: case CPU_DEAD_FROZEN: { break; } #endif } return NOTIFY_OK; } #endif void ssv6xxx_watchdog_restart_hw(struct ssv_softc *sc) { printk("%s(): \n", __FUNCTION__); sc->restart_counter++; sc->force_triger_reset = true; sc->beacon_info[0].pubf_addr = 0x00; sc->beacon_info[1].pubf_addr = 0x00; ieee80211_restart_hw(sc->hw); } #ifdef CONFIG_SSV_RSSI extern struct rssi_res_st rssi_res; #endif void ssv6200_watchdog_timeout(unsigned long arg) { #ifdef CONFIG_SSV_RSSI static u32 count=0; struct rssi_res_st *rssi_tmp0 = NULL, *rssi_tmp1 = NULL; #endif struct ssv_softc *sc = (struct ssv_softc *)arg; if(sc->watchdog_flag == WD_BARKING) { ssv6xxx_watchdog_restart_hw(sc); mod_timer(&sc->watchdog_timeout, jiffies + WATCHDOG_TIMEOUT); return; } if (sc->watchdog_flag != WD_SLEEP) sc->watchdog_flag = WD_BARKING; #ifdef CONFIG_SSV_RSSI count++; if(count == 6) { count = 0; if (list_empty(&rssi_res.rssi_list)) { return; } list_for_each_entry_safe(rssi_tmp0, rssi_tmp1, &rssi_res.rssi_list, rssi_list) { if (rssi_tmp0->timeout) { list_del_rcu(&rssi_tmp0->rssi_list); kfree(rssi_tmp0); } } } #endif mod_timer(&sc->watchdog_timeout, jiffies + WATCHDOG_TIMEOUT); return; } static void ssv6xxx_preload_sw_cipher(void) { #ifdef USE_LOCAL_CRYPTO struct crypto_blkcipher *tmpblkcipher; struct crypto_cipher *tmpcipher; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) struct crypto_ahash *tmphash; #else struct crypto_hash *tmphash; #endif printk("Pre-load cipher\n"); tmpblkcipher = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tmpblkcipher)) { printk(" ARC4 cipher allocate fail \n"); } else { crypto_free_blkcipher(tmpblkcipher); } tmpcipher = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tmpcipher)) { printk(" aes cipher allocate fail \n"); } else { crypto_free_cipher(tmpcipher); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) tmphash =crypto_alloc_ahash("michael_mic", 0, CRYPTO_ALG_ASYNC); #else tmphash =crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); #endif if (IS_ERR(tmphash)) { printk(" mic hash allocate fail \n"); } else { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,6,0) crypto_free_ahash(tmphash); #else crypto_free_hash(tmphash); #endif } #endif } static int ssv6xxx_init_softc(struct ssv_softc *sc) { void *channels; int ret=0; #ifdef MULTI_THREAD_ENCRYPT unsigned int cpu; #endif sc->sc_flags = SC_OP_INVALID; mutex_init(&sc->mutex); mutex_init(&sc->mem_mutex); sc->config_wq= create_singlethread_workqueue("ssv6xxx_cong_wq"); sc->thermal_wq= create_singlethread_workqueue("ssv6xxx_thermal_wq"); INIT_DELAYED_WORK(&sc->thermal_monitor_work, thermal_monitor); INIT_WORK(&sc->set_tim_work, ssv6200_set_tim_work); INIT_WORK(&sc->bcast_start_work, ssv6200_bcast_start_work); INIT_DELAYED_WORK(&sc->bcast_stop_work, ssv6200_bcast_stop_work); INIT_DELAYED_WORK(&sc->bcast_tx_work, ssv6200_bcast_tx_work); INIT_WORK(&sc->set_ampdu_rx_add_work, ssv6xxx_set_ampdu_rx_add_work); INIT_WORK(&sc->set_ampdu_rx_del_work, ssv6xxx_set_ampdu_rx_del_work); #ifdef CONFIG_SSV_SUPPORT_ANDROID #ifdef CONFIG_HAS_EARLYSUSPEND sc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; sc->early_suspend.suspend = ssv6xxx_early_suspend; sc->early_suspend.resume = ssv6xxx_late_resume; register_early_suspend(&sc->early_suspend); #endif ssv_wakelock_init(sc); #endif sc->mac_deci_tbl = sta_deci_tbl; memset((void *)&sc->tx, 0, sizeof(struct ssv_tx)); sc->tx.hw_txqid[WMM_AC_VO] = 3; sc->tx.ac_txqid[3] = WMM_AC_VO; sc->tx.hw_txqid[WMM_AC_VI] = 2; sc->tx.ac_txqid[2] = WMM_AC_VI; sc->tx.hw_txqid[WMM_AC_BE] = 1; sc->tx.ac_txqid[1] = WMM_AC_BE; sc->tx.hw_txqid[WMM_AC_BK] = 0; sc->tx.ac_txqid[0] = WMM_AC_BK; INIT_LIST_HEAD(&sc->tx.ampdu_tx_que); spin_lock_init(&sc->tx.ampdu_tx_que_lock); memset((void *)&sc->rx, 0, sizeof(struct ssv_rx)); spin_lock_init(&sc->rx.rxq_lock); skb_queue_head_init(&sc->rx.rxq_head); sc->rx.rx_buf = ssv_skb_alloc(MAX_FRAME_SIZE); if (sc->rx.rx_buf == NULL) return -ENOMEM; memset(&sc->bcast_txq, 0, sizeof(struct ssv6xxx_bcast_txq)); spin_lock_init(&sc->bcast_txq.txq_lock); skb_queue_head_init(&sc->bcast_txq.qhead); spin_lock_init(&sc->ps_state_lock); #ifdef CONFIG_P2P_NOA spin_lock_init(&sc->p2p_noa.p2p_config_lock); #endif if (sc->sh->cfg.hw_caps & SSV6200_HW_CAP_2GHZ) { channels = kmemdup(ssv6200_2ghz_chantable, sizeof(ssv6200_2ghz_chantable), GFP_KERNEL); if (!channels) { kfree(sc->rx.rx_buf); return -ENOMEM; } sc->sbands[INDEX_80211_BAND_2GHZ].channels = channels; sc->sbands[INDEX_80211_BAND_2GHZ].band = INDEX_80211_BAND_2GHZ; sc->sbands[INDEX_80211_BAND_2GHZ].n_channels = ARRAY_SIZE(ssv6200_2ghz_chantable); sc->sbands[INDEX_80211_BAND_2GHZ].bitrates = ssv6200_legacy_rates; sc->sbands[INDEX_80211_BAND_2GHZ].n_bitrates = ARRAY_SIZE(ssv6200_legacy_rates); } sc->cur_channel = NULL; sc->hw_chan = (-1); ssv6xxx_set_80211_hw_capab(sc); ret = ssv6xxx_rate_control_register(); if (ret != 0) { printk("%s(): Failed to register rc algorithm.\n", __FUNCTION__); } #ifdef MULTI_THREAD_ENCRYPT skb_queue_head_init(&sc->preprocess_q); skb_queue_head_init(&sc->crypted_q); #ifdef CONFIG_SSV6XXX_DEBUGFS sc->max_preprocess_q_len = 0; sc->max_crypted_q_len = 0; #endif spin_lock_init(&sc->crypt_st_lock); INIT_LIST_HEAD(&sc->encrypt_task_head); for_each_cpu(cpu, cpu_present_mask) { struct ssv_encrypt_task_list *ta = kzalloc(sizeof(*ta), GFP_KERNEL); memset(ta, 0, sizeof(*ta)); ta->encrypt_task = kthread_create_on_node(ssv6xxx_encrypt_task, sc, cpu_to_node(cpu), "%d/ssv6xxx_encrypt_task", cpu); init_waitqueue_head(&ta->encrypt_wait_q); if (!IS_ERR(ta->encrypt_task)) { printk("[MT-ENCRYPT]: create kthread %p for CPU %d, ret = %d\n", ta->encrypt_task, cpu, ret); #ifdef KTHREAD_BIND kthread_bind(ta->encrypt_task, cpu); #endif list_add_tail(&ta->list, &sc->encrypt_task_head); if (cpu_online(cpu)) { wake_up_process(ta->encrypt_task); ta->started = 1; } ta->cpu_no = cpu; } else { printk("[MT-ENCRYPT]: Fail to create kthread\n"); } } sc->cpu_nfb.notifier_call = ssv6xxx_cpu_callback; #ifdef KTHREAD_BIND register_cpu_notifier(&sc->cpu_nfb); #endif #endif init_waitqueue_head(&sc->tx_wait_q); sc->tx_wait_q_woken = 0; skb_queue_head_init(&sc->tx_skb_q); #ifdef CONFIG_SSV6XXX_DEBUGFS sc->max_tx_skb_q_len = 0; #endif sc->tx_task = kthread_run(ssv6xxx_tx_task, sc, "ssv6xxx_tx_task"); sc->tx_q_empty = false; skb_queue_head_init(&sc->tx_done_q); init_waitqueue_head(&sc->rx_wait_q); sc->rx_wait_q_woken = 0; skb_queue_head_init(&sc->rx_skb_q); sc->rx_task = kthread_run(ssv6xxx_rx_task, sc, "ssv6xxx_rx_task"); ssv6xxx_preload_sw_cipher(); init_timer(&sc->watchdog_timeout); sc->watchdog_timeout.expires = jiffies + 20*HZ; sc->watchdog_timeout.data = (unsigned long)sc; sc->watchdog_timeout.function = ssv6200_watchdog_timeout; init_waitqueue_head(&sc->fw_wait_q); #ifdef CONFIG_SSV_RSSI INIT_LIST_HEAD(&rssi_res.rssi_list); rssi_res.rssi = 0; #endif add_timer(&sc->watchdog_timeout); //if(get_flash_info(sc) == 1) sc->is_sar_enabled = get_flash_info(sc); if (sc->is_sar_enabled) queue_delayed_work(sc->thermal_wq, &sc->thermal_monitor_work, THERMAL_MONITOR_TIME); //schedule_delayed_work(&sc->thermal_monitor_work, THERMAL_MONITOR_TIME); return ret; } void ssv6xxx_deinit_prepare(void) { printk("%s\n", __FUNCTION__); #ifdef CONFIG_SSV_SUPPORT_ANDROID printk("%s :ps_status = %d\n", __FUNCTION__,PWRSV_PREPARE); ssv6xxx_watchdog_controller(ssv_notify_sc->sh ,(u8)SSV6XXX_HOST_CMD_WATCHDOG_STOP); ssv_notify_sc->ps_status = PWRSV_PREPARE; while(ssv_notify_sc->bScanning == true){ printk("+"); msleep(100); } printk("\n"); #endif } static int ssv6xxx_deinit_softc(struct ssv_softc *sc) { void *channels; struct sk_buff* skb; u8 remain_size; #ifdef MULTI_THREAD_ENCRYPT struct ssv_encrypt_task_list *qtask = NULL; int counter = 0; #endif printk("%s():\n", __FUNCTION__); if (sc->sh->cfg.hw_caps & SSV6200_HW_CAP_2GHZ) { channels = sc->sbands[INDEX_80211_BAND_2GHZ].channels; kfree(channels); } #ifdef CONFIG_SSV_SUPPORT_ANDROID #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&sc->early_suspend); #endif ssv_wakelock_destroy(sc); #endif ssv_skb_free(sc->rx.rx_buf); sc->rx.rx_buf = NULL; ssv6xxx_rate_control_unregister(); cancel_delayed_work_sync(&sc->bcast_tx_work); //ssv6xxx_watchdog_controller(sc->sh ,(u8)SSV6XXX_HOST_CMD_WATCHDOG_STOP); del_timer_sync(&sc->watchdog_timeout); cancel_delayed_work(&sc->thermal_monitor_work); sc->ps_status = PWRSV_PREPARE; flush_workqueue(sc->thermal_wq); destroy_workqueue(sc->thermal_wq); do{ skb = ssv6200_bcast_dequeue(&sc->bcast_txq, &remain_size); if(skb) ssv6xxx_txbuf_free_skb(skb, (void*)sc); else break; }while(remain_size); #ifdef MULTI_THREAD_ENCRYPT unregister_cpu_notifier(&sc->cpu_nfb); if (!list_empty(&sc->encrypt_task_head)) { for (qtask = list_entry((&sc->encrypt_task_head)->next, typeof(*qtask), list); !list_empty(&sc->encrypt_task_head); qtask = list_entry((&sc->encrypt_task_head)->next, typeof(*qtask), list)) { counter++; printk("Stopping encrypt task %d: ...\n", counter); kthread_stop(qtask->encrypt_task); printk("Stopped encrypt task %d: ...\n", counter); list_del(&qtask->list); kfree(qtask); } } #endif if (sc->tx_task != NULL) { printk("Stopping TX task...\n"); kthread_stop(sc->tx_task); sc->tx_task = NULL; printk("Stopped TX task.\n"); } if (sc->rx_task != NULL) { printk("Stopping RX task...\n"); kthread_stop(sc->rx_task); sc->rx_task = NULL; printk("Stopped RX task.\n"); } #ifdef MULTI_THREAD_ENCRYPT while((skb = skb_dequeue(&sc->preprocess_q)) != NULL) { SKB_info *skb_info = (SKB_info *)skb->head; if(skb_info->crypt_st == PKT_CRYPT_ST_ENC_PRE) ssv6xxx_txbuf_free_skb(skb, (void*)sc); else dev_kfree_skb_any(skb); } while((skb = skb_dequeue(&sc->crypted_q)) != NULL) { SKB_info *skb_info = (SKB_info *)skb->head; if(skb_info->crypt_st == PKT_CRYPT_ST_ENC_DONE) ssv6xxx_txbuf_free_skb(skb, (void*)sc); else dev_kfree_skb_any(skb); } printk("[MT-ENCRYPT]: end of de-init\n"); #endif destroy_workqueue(sc->config_wq); return 0; } static void ssv6xxx_hw_set_replay_ignore(struct ssv_hw *sh,u8 ignore) { u32 temp; SMAC_REG_READ(sh,ADR_SCRT_SET,&temp); temp = temp & SCRT_RPLY_IGNORE_I_MSK; temp |= (ignore << SCRT_RPLY_IGNORE_SFT); SMAC_REG_WRITE(sh,ADR_SCRT_SET, temp); } extern char *cfgfirmwarepath ; int ssv6xxx_init_mac(struct ssv_hw *sh) { struct ssv_softc *sc=sh->sc; int i = 0 , ret=0; u8 temp_path[256] = ""; #ifdef SSV6200_ECO u32 *ptr, id_len, regval, temp[0x8]; #else u32 *ptr, id_len, regval; #endif char *chip_id = sh->chip_id; printk(KERN_INFO "SVN version %d\n", ssv_root_version); printk(KERN_INFO "SVN ROOT URL %s \n", SSV_ROOT_URl); printk(KERN_INFO "COMPILER HOST %s \n", COMPILERHOST); printk(KERN_INFO "COMPILER DATE %s \n", COMPILERDATE); printk(KERN_INFO "COMPILER OS %s \n", COMPILEROS); printk(KERN_INFO "COMPILER OS ARCH %s \n", COMPILEROSARCH); SMAC_REG_READ(sh, ADR_IC_TIME_TAG_1, ®val); sh->chip_tag = ((u64)regval<<32); SMAC_REG_READ(sh, ADR_IC_TIME_TAG_0, ®val); sh->chip_tag |= (regval); printk(KERN_INFO "CHIP TAG: %llx \n", sh->chip_tag); SMAC_REG_READ(sh, ADR_CHIP_ID_3, ®val); *((u32 *)&chip_id[0]) = (u32)LONGSWAP(regval); SMAC_REG_READ(sh, ADR_CHIP_ID_2, ®val); *((u32 *)&chip_id[4]) = (u32)LONGSWAP(regval); SMAC_REG_READ(sh, ADR_CHIP_ID_1, ®val); *((u32 *)&chip_id[8]) = (u32)LONGSWAP(regval); SMAC_REG_READ(sh, ADR_CHIP_ID_0, ®val); *((u32 *)&chip_id[12]) = (u32)LONGSWAP(regval); chip_id[12+sizeof(u32)] = 0; printk(KERN_INFO "CHIP ID: %s \n",chip_id); if(sc->ps_status == PWRSV_ENABLE){ #ifdef CONFIG_SSV_SUPPORT_ANDROID printk(KERN_INFO "%s: wifi Alive lock timeout after 3 secs!\n",__FUNCTION__); { ssv_wake_timeout(sc, 3); printk(KERN_INFO "wifi Alive lock!\n"); } #endif #ifdef CONFIG_SSV_HW_ENCRYPT_SW_DECRYPT SMAC_REG_WRITE(sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #else SMAC_REG_WRITE(sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_ENCRYPT_SEC<<4)|(M_ENG_HWHCI<<8)); #endif SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #if Enable_AMPDU_FW_Retry SMAC_REG_WRITE(sh, ADR_RX_FLOW_CTRL, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8)); #else SMAC_REG_WRITE(sh, ADR_RX_FLOW_CTRL, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #endif SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_TB0+6*4, (sc->mac_deci_tbl[6])); return ret; } SMAC_REG_SET_BITS(sh, ADR_PHY_EN_1, (0 << RG_PHY_MD_EN_SFT), RG_PHY_MD_EN_MSK); SMAC_REG_WRITE(sh, ADR_BRG_SW_RST, 1 << MAC_SW_RST_SFT); do { SMAC_REG_READ(sh, ADR_BRG_SW_RST, & regval); i ++; if (i >10000){ printk("MAC reset fail !!!!\n"); WARN_ON(1); ret = 1; goto exit; } } while (regval != 0); SMAC_REG_WRITE(sc->sh, ADR_TXQ4_MTX_Q_AIFSN, 0xffff2101); SMAC_REG_SET_BITS(sc->sh, ADR_MTX_BCN_EN_MISC, 0, MTX_HALT_MNG_UNTIL_DTIM_MSK); SMAC_REG_WRITE(sh, ADR_CONTROL, 0x12000006); SMAC_REG_WRITE(sh, ADR_RX_TIME_STAMP_CFG, ((28<tx_desc_len) << TX_INFO_SIZE_SFT) | ((u32)(sh->rx_desc_len) << RX_INFO_SIZE_SFT) | ((u32)(sh->rx_pinfo_pad) << RX_LAST_PHY_SIZE_SFT ) ); SMAC_REG_READ(sh,ADR_MMU_CTRL, ®val); regval |= (0xff<sh, ADR_MTX_BCN_EN_MISC, ®val); regval|=(1<sh, ADR_MTX_BCN_EN_MISC, regval); #ifdef SSV6200_ECO SMAC_REG_WRITE(sh, 0xcd010004, 0x1213); for(i=0;ihw_buf_ptr[i] = ssv6xxx_pbuf_alloc(sc, sizeof(phy_info_tbl)+ sizeof(struct ssv6xxx_hw_sec), NOTYPE_BUF); if((sh->hw_buf_ptr[i]>>28) != 8) { printk("opps allocate pbuf error\n"); WARN_ON(1); ret = 1; goto exit; } } else { sh->hw_buf_ptr[i] = ssv6xxx_pbuf_alloc(sc, sizeof(struct ssv6xxx_hw_sec), NOTYPE_BUF); if((sh->hw_buf_ptr[i]>>28) != 8) { printk("opps allocate pbuf error\n"); WARN_ON(1); ret = 1; goto exit; } } } for (i = 0; i < 0x8; i++) { temp[i] = 0; temp[i] = ssv6xxx_pbuf_alloc(sc, 256,NOTYPE_BUF); } for (i = 0; i < 0x8; i++) { if(temp[i] == 0x800e0000) printk("0x800e0000\n"); else ssv6xxx_pbuf_free(sc,temp[i]); } #else sh->hw_buf_ptr = ssv6xxx_pbuf_alloc(sc, sizeof(phy_info_tbl)+ sizeof(struct ssv6xxx_hw_sec), NOTYPE_BUF); if((sh->hw_buf_ptr>>28) != 8) { printk("opps allocate pbuf error\n"); WARN_ON(1); ret = 1; goto exit; } #endif #ifdef SSV6200_ECO for(i=0;ihw_sec_key[i] = sh->hw_buf_ptr[i]; for(i=0;ihw_sec_key[i]+x, 0); } } SMAC_REG_READ(sh, ADR_SCRT_SET, ®val); regval &= SCRT_PKT_ID_I_MSK; regval |= ((sh->hw_sec_key[0] >> 16) << SCRT_PKT_ID_SFT); SMAC_REG_WRITE(sh, ADR_SCRT_SET, regval); sh->hw_pinfo = sh->hw_sec_key[0] + sizeof(struct ssv6xxx_hw_sec); for(i=0, ptr=phy_info_tbl; ihw_sec_key = sh->hw_buf_ptr; for(i=0; ihw_sec_key+i, 0); } SMAC_REG_READ(sh, ADR_SCRT_SET, ®val); regval &= SCRT_PKT_ID_I_MSK; regval |= ((sh->hw_sec_key >> 16) << SCRT_PKT_ID_SFT); SMAC_REG_WRITE(sh, ADR_SCRT_SET, regval); sh->hw_pinfo = sh->hw_sec_key + sizeof(struct ssv6xxx_hw_sec); for(i=0, ptr=phy_info_tbl; ihw_pinfo+i*4, *ptr); SMAC_REG_CONFIRM(sh, sh->hw_pinfo+i*4, *ptr); } for(i=0; ihw_pinfo+ (PHY_INFO_TBL2_SIZE<<2)+i*4, *ptr); SMAC_REG_CONFIRM(sh, sh->hw_pinfo+ (PHY_INFO_TBL2_SIZE<<2)+i*4, *ptr); } SMAC_REG_WRITE(sh, ADR_INFO_RATE_OFFSET, 0x00040000); SMAC_REG_WRITE(sh, ADR_INFO_IDX_ADDR, sh->hw_pinfo); SMAC_REG_WRITE(sh, ADR_INFO_LEN_ADDR, sh->hw_pinfo+(PHY_INFO_TBL2_SIZE)*4); printk("ADR_INFO_IDX_ADDR[%08x] ADR_INFO_LEN_ADDR[%08x]\n", sh->hw_pinfo, sh->hw_pinfo+(PHY_INFO_TBL2_SIZE)*4); SMAC_REG_WRITE(sh, ADR_GLBLE_SET, (0 << OP_MODE_SFT) | (0 << SNIFFER_MODE_SFT) | (1 << DUP_FLT_SFT) | (SSV6200_TX_PKT_RSVD_SETTING << TX_PKT_RSVD_SFT) | ((u32)(RXPB_OFFSET) << PB_OFFSET_SFT) ); SMAC_REG_WRITE(sh, ADR_STA_MAC_0, *((u32 *)&sh->cfg.maddr[0][0])); SMAC_REG_WRITE(sh, ADR_STA_MAC_1, *((u32 *)&sh->cfg.maddr[0][4])); SMAC_REG_WRITE(sh, ADR_BSSID_0, *((u32 *)&sc->bssid[0])); SMAC_REG_WRITE(sh, ADR_BSSID_1, *((u32 *)&sc->bssid[4])); SMAC_REG_WRITE(sh, ADR_TX_ETHER_TYPE_0, 0x00000000); SMAC_REG_WRITE(sh, ADR_TX_ETHER_TYPE_1, 0x00000000); SMAC_REG_WRITE(sh, ADR_RX_ETHER_TYPE_0, 0x00000000); SMAC_REG_WRITE(sh, ADR_RX_ETHER_TYPE_1, 0x00000000); SMAC_REG_WRITE(sh, ADR_REASON_TRAP0, 0x7FBC7F87); SMAC_REG_WRITE(sh, ADR_REASON_TRAP1, 0x0000003F); #ifndef FW_WSID_WATCH_LIST SMAC_REG_WRITE(sh, ADR_TRAP_HW_ID, M_ENG_HWHCI); #else SMAC_REG_WRITE(sh, ADR_TRAP_HW_ID, M_ENG_CPU); #endif SMAC_REG_WRITE(sh, ADR_WSID0, 0x00000000); SMAC_REG_WRITE(sh, ADR_WSID1, 0x00000000); #ifdef CONFIG_SSV_HW_ENCRYPT_SW_DECRYPT SMAC_REG_WRITE(sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #else SMAC_REG_WRITE(sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_ENCRYPT_SEC<<4)|(M_ENG_HWHCI<<8)); #endif #if defined(CONFIG_P2P_NOA) || defined(CONFIG_RX_MGMT_CHECK) SMAC_REG_WRITE(sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8)); #else SMAC_REG_WRITE(sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #endif #if Enable_AMPDU_FW_Retry SMAC_REG_WRITE(sh, ADR_RX_FLOW_CTRL, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8)); #else SMAC_REG_WRITE(sh, ADR_RX_FLOW_CTRL, M_ENG_MACRX|(M_ENG_HWHCI<<4)); #endif ssv6xxx_hw_set_replay_ignore(sh, 1); ssv6xxx_update_decision_table(sc); SMAC_REG_SET_BITS(sc->sh, ADR_GLBLE_SET, SSV6200_OPMODE_STA, OP_MODE_MSK); SMAC_REG_WRITE(sh, ADR_SDIO_MASK, 0xfffe1fff); #ifdef CONFIG_SSV_TX_LOWTHRESHOLD SMAC_REG_WRITE(sh, ADR_TX_LIMIT_INTR, 0x80000000 | SSV6200_TX_LOWTHRESHOLD_ID_TRIGGER << 16 |SSV6200_TX_LOWTHRESHOLD_PAGE_TRIGGER); #else SMAC_REG_WRITE(sh, ADR_MB_THRESHOLD6, 0x80000000); SMAC_REG_WRITE(sh, ADR_MB_THRESHOLD8, 0x04020000); SMAC_REG_WRITE(sh, ADR_MB_THRESHOLD9, 0x00000404); #endif #ifdef CONFIG_SSV_SUPPORT_BTCX SMAC_REG_WRITE(sh, ADR_BTCX0,COEXIST_EN_MSK|(WIRE_MODE_SZ<cfg.firmware_path[0] != 0x00) { sprintf(temp_path,"%s%s",sh->cfg.firmware_path,WIFI_FIRMWARE_NAME); ret = SMAC_LOAD_FW(sh,temp_path,1); printk(KERN_INFO "Firmware name sh->cfg.firmware_path=%s\n", temp_path); } else { printk(KERN_INFO "Firmware name CABRIO_E_FIRMWARE_NAME=%s\n", WIFI_FIRMWARE_NAME); ret = SMAC_LOAD_FW(sh,WIFI_FIRMWARE_NAME,0); } SMAC_REG_READ(sh, FW_VERSION_REG, ®val); if (regval == ssv_firmware_version) { SMAC_REG_SET_BITS(sh, ADR_PHY_EN_1, (1 << RG_PHY_MD_EN_SFT), RG_PHY_MD_EN_MSK); printk(KERN_INFO "Firmware version %d\n", regval); } else { printk(KERN_INFO "!! SOS !! SOS !!\n"); printk(KERN_INFO "Firmware version not mapping %d\n", regval); ret = -1; } ssv6xxx_watchdog_controller(sh ,(u8)SSV6XXX_HOST_CMD_WATCHDOG_START); exit: return ret; } void ssv6xxx_deinit_mac(struct ssv_softc *sc) { #ifdef SSV6200_ECO int i; for(i = 0; i < SSV_RC_MAX_STA; i++) { if(sc->sh->hw_buf_ptr[i]) ssv6xxx_pbuf_free(sc, sc->sh->hw_buf_ptr[i]); } #else if(sc->sh->hw_buf_ptr) ssv6xxx_pbuf_free(sc, sc->sh->hw_buf_ptr); #endif } void inline ssv6xxx_deinit_hw(struct ssv_softc *sc) { printk("%s(): \n", __FUNCTION__); ssv6xxx_deinit_mac(sc); } void ssv6xxx_restart_hw(struct ssv_softc *sc) { printk("%s(): \n", __FUNCTION__); printk("**************************\n"); printk("*** Software MAC reset ***\n"); printk("**************************\n"); sc->restart_counter++; sc->force_triger_reset = true; HCI_STOP(sc->sh); SMAC_REG_WRITE(sc->sh, 0xce000004, 0x0); sc->beacon_info[0].pubf_addr = 0x00; sc->beacon_info[1].pubf_addr = 0x00; ieee80211_restart_hw(sc->hw); } #ifdef CONFIG_SSV_CABRIO_E extern struct ssv6xxx_iqk_cfg init_iqk_cfg; #endif static int ssv6xxx_init_hw(struct ssv_hw *sh) { int ret=0,i=0,x=0; #ifdef CONFIG_SSV_CABRIO_E u32 regval; #endif sh->tx_desc_len = SSV6XXX_TX_DESC_LEN; sh->rx_desc_len = SSV6XXX_RX_DESC_LEN; sh->rx_pinfo_pad = 0x04; sh->tx_page_available = SSV6200_PAGE_TX_THRESHOLD; sh->ampdu_divider = SSV6XXX_AMPDU_DIVIDER; memset(sh->page_count, 0, sizeof(sh->page_count)); #ifdef CONFIG_SSV_CABRIO_E if (sh->cfg.force_chip_identity) { printk("Force use external RF setting [%08x]\n",sh->cfg.force_chip_identity); sh->cfg.chip_identity = sh->cfg.force_chip_identity; } if(sh->cfg.chip_identity == SSV6051Z) { sh->p_ch_cfg = &ch_cfg_z[0]; sh->ch_cfg_size = sizeof(ch_cfg_z) / sizeof(struct ssv6xxx_ch_cfg); memcpy(phy_info_tbl,phy_info_6051z,sizeof(phy_info_6051z)); } else if(sh->cfg.chip_identity == SSV6051P) { sh->p_ch_cfg = &ch_cfg_p[0]; sh->ch_cfg_size = sizeof(ch_cfg_p) / sizeof(struct ssv6xxx_ch_cfg); } switch (sh->cfg.chip_identity) { case SSV6051Q_P1: case SSV6051Q_P2: case SSV6051Q: printk("SSV6051Q setting\n"); for (i=0; icfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_26M) { init_iqk_cfg.cfg_xtal = SSV6XXX_IQK_CFG_XTAL_26M; printk("SSV6XXX_IQK_CFG_XTAL_26M\n"); } else if(sh->cfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_40M) { init_iqk_cfg.cfg_xtal = SSV6XXX_IQK_CFG_XTAL_40M; printk("SSV6XXX_IQK_CFG_XTAL_40M\n"); } else if(sh->cfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_24M) { printk("SSV6XXX_IQK_CFG_XTAL_24M\n"); init_iqk_cfg.cfg_xtal = SSV6XXX_IQK_CFG_XTAL_24M; for(i=0; icfg.crystal_frequency_offset) { ssv6200_rf_tbl[i].data &= RG_XOSC_CBANK_XO_I_MSK; ssv6200_rf_tbl[i].data |= (sh->cfg.crystal_frequency_offset << RG_XOSC_CBANK_XO_SFT); } } } for (i=0; icfg.chip_identity) { case SSV6051Q_P1: case SSV6051Q_P2: case SSV6051Q: printk("SSV6051Q setting [0x5B606C72]\n"); phy_setting[i].data = 0x5B606C72; break; case SSV6051Z: printk("SSV6051Z setting [0x60606060]\n"); phy_setting[i].data = 0x60606060; break; case SSV6051P: printk("SSV6051P setting [0x6C726C72]\n"); phy_setting[i].data = 0x6C726C72; break; default: printk("Use default power setting\n"); break; } if (sh->cfg.wifi_tx_gain_level_b){ phy_setting[i].data &= 0xffff0000; phy_setting[i].data |= wifi_tx_gain[sh->cfg.wifi_tx_gain_level_b] & 0x0000ffff; } if (sh->cfg.wifi_tx_gain_level_gn){ phy_setting[i].data &= 0x0000ffff; phy_setting[i].data |= wifi_tx_gain[sh->cfg.wifi_tx_gain_level_gn] & 0xffff0000; } printk("TX power setting 0x%x\n",phy_setting[i].data); init_iqk_cfg.cfg_def_tx_scale_11b = (phy_setting[i].data>>0) & 0xff; init_iqk_cfg.cfg_def_tx_scale_11b_p0d5 = (phy_setting[i].data>>8) & 0xff; init_iqk_cfg.cfg_def_tx_scale_11g = (phy_setting[i].data>>16) & 0xff; init_iqk_cfg.cfg_def_tx_scale_11g_p0d5 = (phy_setting[i].data>>24) & 0xff; break; } } if(sh->cfg.volt_regulator == SSV6XXX_VOLT_LDO_CONVERT) { printk("Volt regulator LDO\n"); for(i=0; icfg.volt_regulator == SSV6XXX_VOLT_DCDC_CONVERT) { printk("Volt regulator DCDC\n"); for(i=0; isc, sh->cfg.def_chan))) return ret; if (ret == 0) ret = SMAC_REG_WRITE(sh, ADR_PHY_EN_1, (RG_PHYRX_MD_EN_MSK | RG_PHYTX_MD_EN_MSK | RG_PHY11GN_MD_EN_MSK | RG_PHY11B_MD_EN_MSK | RG_PHYRXFIFO_MD_EN_MSK | RG_PHYTXFIFO_MD_EN_MSK | RG_PHY11BGN_MD_EN_MSK)); return ret; } static void ssv6xxx_check_mac2(struct ssv_hw *sh) { const u8 addr_mask[6]={0xfd, 0xff, 0xff, 0xff, 0xff, 0xfc}; u8 i; bool invalid = false; for ( i=0; i<6; i++) { if ((ssv_cfg.maddr[0][i] & addr_mask[i]) != (ssv_cfg.maddr[1][i] & addr_mask[i])){ invalid = true; printk (" i %d , mac1[i] %x, mac2[i] %x, mask %x \n",i, ssv_cfg.maddr[0][i] ,ssv_cfg.maddr[1][i],addr_mask[i]); break; } } if (invalid){ memcpy(&ssv_cfg.maddr[1][0], &ssv_cfg.maddr[0][0], 6); ssv_cfg.maddr[1][5] ^= 0x01; if (ssv_cfg.maddr[1][5] < ssv_cfg.maddr[0][5]){ u8 temp; temp = ssv_cfg.maddr[0][5]; ssv_cfg.maddr[0][5] = ssv_cfg.maddr[1][5]; ssv_cfg.maddr[1][5] = temp; sh->cfg.maddr[0][5] = ssv_cfg.maddr[0][5]; } printk("MAC 2 address invalid!!\n" ); printk("After modification, MAC1 %pM, MAC2 %pM\n",ssv_cfg.maddr[0], ssv_cfg.maddr[1]); } } static int ssv6xxx_read_configuration(struct ssv_hw *sh) { extern u32 sdio_sr_bhvr; if(is_valid_ether_addr(&ssv_cfg.maddr[0][0])) memcpy(&sh->cfg.maddr[0][0], &ssv_cfg.maddr[0][0], ETH_ALEN); if(is_valid_ether_addr(&ssv_cfg.maddr[1][0])){ ssv6xxx_check_mac2(sh); memcpy(&sh->cfg.maddr[1][0], &ssv_cfg.maddr[1][0], ETH_ALEN); } if(ssv_cfg.hw_caps) sh->cfg.hw_caps = ssv_cfg.hw_caps; else sh->cfg.hw_caps = SSV6200_HW_CAP_HT | SSV6200_HW_CAP_2GHZ | SSV6200_HW_CAP_SECURITY | SSV6200_HW_CAP_P2P| SSV6200_HT_CAP_SGI_20| SSV6200_HW_CAP_AMPDU_RX| SSV6200_HW_CAP_AMPDU_TX| SSV6200_HW_CAP_AP; if(ssv_cfg.def_chan) sh->cfg.def_chan = ssv_cfg.def_chan; else sh->cfg.def_chan = 6; sh->cfg.use_wpa2_only = ssv_cfg.use_wpa2_only; if(ssv_cfg.crystal_type == 26) sh->cfg.crystal_type = SSV6XXX_IQK_CFG_XTAL_26M; else if(ssv_cfg.crystal_type == 40) sh->cfg.crystal_type = SSV6XXX_IQK_CFG_XTAL_40M; else if(ssv_cfg.crystal_type == 24) sh->cfg.crystal_type = SSV6XXX_IQK_CFG_XTAL_24M; else { printk("Please redefine xtal_clock(wifi.cfg)!!\n"); WARN_ON(1); return 1; } if(ssv_cfg.volt_regulator < 2) sh->cfg.volt_regulator = ssv_cfg.volt_regulator; else { printk("Please redefine volt_regulator(wifi.cfg)!!\n"); WARN_ON(1); return 1; } sh->cfg.wifi_tx_gain_level_gn = ssv_cfg.wifi_tx_gain_level_gn; sh->cfg.wifi_tx_gain_level_b = ssv_cfg.wifi_tx_gain_level_b; sh->cfg.rssi_ctl = ssv_cfg.rssi_ctl; sh->cfg.sr_bhvr = ssv_cfg.sr_bhvr; sdio_sr_bhvr = ssv_cfg.sr_bhvr; sh->cfg.force_chip_identity = ssv_cfg.force_chip_identity; strncpy(sh->cfg.firmware_path,ssv_cfg.firmware_path,sizeof(sh->cfg.firmware_path)-1); strncpy(sh->cfg.flash_bin_path, ssv_cfg.flash_bin_path,sizeof(sh->cfg.flash_bin_path) - 1); strncpy(sh->cfg.mac_address_path,ssv_cfg.mac_address_path,sizeof(sh->cfg.mac_address_path)-1); strncpy(sh->cfg.mac_output_path,ssv_cfg.mac_output_path,sizeof(sh->cfg.mac_output_path)-1); sh->cfg.ignore_efuse_mac = ssv_cfg.ignore_efuse_mac; sh->cfg.mac_address_mode = ssv_cfg.mac_address_mode; return 0; } static int ssv6xxx_read_hw_info(struct ssv_softc *sc) { struct ssv_hw *sh; sh = kzalloc(sizeof(struct ssv_hw), GFP_KERNEL); if (sh == NULL) return -ENOMEM; memset((void *)sh, 0, sizeof(struct ssv_hw)); sc->sh = sh; sh->sc = sc; sh->priv = sc->dev->platform_data; if(ssv6xxx_read_configuration(sh)) return -ENOMEM; sh->hci.dev = sc->dev; sh->hci.hci_ops = NULL; sh->hci.hci_rx_cb = ssv6200_rx; sh->hci.rx_cb_args = (void *)sc; sh->hci.hci_tx_cb= ssv6xxx_tx_cb; sh->hci.tx_cb_args = (void *)sc; #ifdef RATE_CONTROL_REALTIME_UPDATA sh->hci.hci_skb_update_cb = ssv6xxx_tx_rate_update; sh->hci.skb_update_args = (void *)sc; #else sh->hci.hci_skb_update_cb = NULL; sh->hci.skb_update_args = NULL; #endif sh->hci.hci_tx_flow_ctrl_cb = ssv6200_tx_flow_control; sh->hci.tx_fctrl_cb_args = (void *)sc; sh->hci.hci_tx_q_empty_cb = ssv6xxx_tx_q_empty_cb; sh->hci.tx_q_empty_args = (void *)sc; sh->hci.if_ops = sh->priv->ops; sh->hci.hci_tx_buf_free_cb = ssv6xxx_txbuf_free_skb; sh->hci.tx_buf_free_args = (void *)sc; return 0; } static int ssv6xxx_init_device(struct ssv_softc *sc, const char *name) { struct ieee80211_hw *hw = sc->hw; struct ssv_hw *sh; int error = 0; BUG_ON(!sc->dev->platform_data); if ((error=ssv6xxx_read_hw_info(sc)) != 0) { return error; } sh = sc->sh; if (sh->cfg.hw_caps == 0) return -1; ssv6xxx_hci_register(&sh->hci); efuse_read_all_map(sh); if ((error=ssv6xxx_init_softc(sc)) != 0) { ssv6xxx_deinit_softc(sc); ssv6xxx_hci_deregister(); kfree(sh); return error; } if ((error=ssv6xxx_init_hw(sc->sh)) != 0) { ssv6xxx_deinit_hw(sc); ssv6xxx_deinit_softc(sc); ssv6xxx_hci_deregister(); kfree(sh); return error; } if ((error=ieee80211_register_hw(hw)) != 0) { printk(KERN_ERR "Failed to register w. %d.\n", error); ssv6xxx_deinit_hw(sc); ssv6xxx_deinit_softc(sc); ssv6xxx_hci_deregister(); kfree(sh); return error; } #ifdef CONFIG_SSV6XXX_DEBUGFS ssv6xxx_init_debugfs(sc, name); #endif return 0; } static void ssv6xxx_deinit_device(struct ssv_softc *sc) { printk("%s(): \n", __FUNCTION__); #ifdef CONFIG_SSV6XXX_DEBUGFS ssv6xxx_deinit_debugfs(sc); #endif ssv6xxx_rf_disable(sc->sh); ieee80211_unregister_hw(sc->hw); ssv6xxx_deinit_hw(sc); ssv6xxx_deinit_softc(sc); ssv6xxx_hci_deregister(); kfree(sc->sh); } extern struct ieee80211_ops ssv6200_ops; int ssv6xxx_dev_probe(struct platform_device *pdev) { #ifdef CONFIG_SSV6200_CLI_ENABLE extern struct ssv_softc *ssv_dbg_sc; #endif #ifdef CONFIG_SSV_SMARTLINK extern struct ssv_softc *ssv_smartlink_sc; #endif struct ssv_softc *softc; struct ieee80211_hw *hw; int ret; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified!\n"); return -EINVAL; } printk("%s(): ssv6200 device found !\n", __FUNCTION__); hw = ieee80211_alloc_hw(sizeof(struct ssv_softc), &ssv6200_ops); if (hw == NULL) { dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); return -ENOMEM; } SET_IEEE80211_DEV(hw, &pdev->dev); dev_set_drvdata(&pdev->dev, hw); memset((void *)hw->priv, 0, sizeof(struct ssv_softc)); softc = hw->priv; softc->hw = hw; softc->dev = &pdev->dev; ret = ssv6xxx_init_device(softc, pdev->name); if (ret) { dev_err(&pdev->dev, "Failed to initialize device\n"); ieee80211_free_hw(hw); return ret; } #ifdef CONFIG_SSV6200_CLI_ENABLE ssv_dbg_sc = softc; #endif #ifdef CONFIG_SSV_SMARTLINK ssv_smartlink_sc = softc; #endif #ifdef CONFIG_SSV_SUPPORT_ANDROID ssv_notify_sc = softc; #endif wiphy_info(hw->wiphy, "%s\n", "SSV6200 of South Silicon Valley"); return 0; } EXPORT_SYMBOL(ssv6xxx_dev_probe); int ssv6xxx_dev_remove(struct platform_device *pdev) { struct ieee80211_hw *hw=dev_get_drvdata(&pdev->dev); struct ssv_softc *softc=hw->priv; printk("ssv6xxx_dev_remove(): pdev=%p, hw=%p\n", pdev, hw); ssv6xxx_deinit_device(softc); printk("ieee80211_free_hw(): \n"); ieee80211_free_hw(hw); pr_info("ssv6200: Driver unloaded\n"); return 0; } EXPORT_SYMBOL(ssv6xxx_dev_remove); static const struct platform_device_id ssv6xxx_id_table[] = { { .name = "ssv6200", .driver_data = 0x00, }, {}, }; MODULE_DEVICE_TABLE(platform, ssv6xxx_id_table); static struct platform_driver ssv6xxx_driver = { .probe = ssv6xxx_dev_probe, #if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0) .remove = __devexit_p(ssv6xxx_dev_remove), #else .remove = ssv6xxx_dev_remove, #endif .id_table = ssv6xxx_id_table, .driver = { .name = "SSV WLAN driver", .owner = THIS_MODULE, } }; #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) int ssv6xxx_init(void) #else static int __init ssv6xxx_init(void) #endif { extern void *ssv_dbg_phy_table; extern u32 ssv_dbg_phy_len; extern void *ssv_dbg_rf_table; extern u32 ssv_dbg_rf_len; ssv_dbg_phy_table = (void *)ssv6200_phy_tbl; ssv_dbg_phy_len = sizeof(ssv6200_phy_tbl)/sizeof(struct ssv6xxx_dev_table); ssv_dbg_rf_table = (void *)ssv6200_rf_tbl; ssv_dbg_rf_len = sizeof(ssv6200_rf_tbl)/sizeof(struct ssv6xxx_dev_table); return platform_driver_register(&ssv6xxx_driver); } #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) void ssv6xxx_exit(void) #else static void __exit ssv6xxx_exit(void) #endif { platform_driver_unregister(&ssv6xxx_driver); } #if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO)) EXPORT_SYMBOL(ssv6xxx_init); EXPORT_SYMBOL(ssv6xxx_exit); #else module_init(ssv6xxx_init); module_exit(ssv6xxx_exit); #endif