/* * Main code of XRadio drivers * * Copyright (c) 2013 * Xradio Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /*Linux version 3.4.0 compilation*/ #include #include #include #include #include #include #include #include #include #include #include "platform.h" #include "xradio.h" #include "txrx.h" #include "sbus.h" #include "fwio.h" #include "hwio.h" #include "bh.h" #include "sta.h" #include "ap.h" #include "scan.h" #include "pm.h" #include "xr_version.h" MODULE_AUTHOR("XRadioTech"); MODULE_DESCRIPTION("XRadioTech WLAN driver core"); MODULE_LICENSE("GPL"); MODULE_ALIAS("xradio_core"); #define XRADIO_DEV_VER "_HT40_01.30" char *drv_version = XRADIO_VERSION XRADIO_DEV_VER; char *drv_buildtime = __DATE__" "__TIME__; #define XRADIO_MAC_CHARLEN 18 #ifdef XRADIO_MACPARAM_HEX /* insmod xradio_wlan.ko macaddr=0xDC,0x44,0x6D,0x00,0x00,0x00 */ static u8 xradio_macaddr_param[ETH_ALEN] = { 0x0 }; module_param_array_named(macaddr, xradio_macaddr_param, byte, NULL, S_IRUGO); #else /* insmod xradio_wlan.ko macaddr=xx:xx:xx:xx:xx:xx */ static char *xradio_macaddr_param; module_param_named(macaddr, xradio_macaddr_param, charp, S_IRUGO); #endif MODULE_PARM_DESC(macaddr, "First MAC address"); #ifdef HW_RESTART void xradio_restart_work(struct work_struct *work); #endif /* TODO: use rates and channels from the device */ #define RATETAB_ENT(_rate, _rateid, _flags) \ { \ .bitrate = (_rate), \ .hw_value = (_rateid), \ .flags = (_flags), \ } static struct ieee80211_rate xradio_rates[] = { RATETAB_ENT(10, 0, 0), RATETAB_ENT(20, 1, 0), RATETAB_ENT(55, 2, 0), RATETAB_ENT(110, 3, 0), RATETAB_ENT(60, 6, 0), RATETAB_ENT(90, 7, 0), RATETAB_ENT(120, 8, 0), RATETAB_ENT(180, 9, 0), RATETAB_ENT(240, 10, 0), RATETAB_ENT(360, 11, 0), RATETAB_ENT(480, 12, 0), RATETAB_ENT(540, 13, 0), }; static struct ieee80211_rate xradio_mcs_rates[] = { RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), }; #define xradio_g_rates (xradio_rates + 0) #define xradio_a_rates (xradio_rates + 4) #define xradio_n_rates (xradio_mcs_rates) #define xradio_g_rates_size (ARRAY_SIZE(xradio_rates)) #define xradio_a_rates_size (ARRAY_SIZE(xradio_rates) - 4) #define xradio_n_rates_size (ARRAY_SIZE(xradio_mcs_rates)) #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define CHAN5G(_channel, _flags) { \ .band = NL80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel xradio_2ghz_chantable[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; #ifdef CONFIG_XRADIO_5GHZ_SUPPORT static struct ieee80211_channel xradio_5ghz_chantable[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0), CHAN5G(184, 0), CHAN5G(188, 0), CHAN5G(192, 0), CHAN5G(196, 0), CHAN5G(200, 0), CHAN5G(204, 0), CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; #endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ static struct ieee80211_supported_band xradio_band_2ghz = { .channels = xradio_2ghz_chantable, .n_channels = ARRAY_SIZE(xradio_2ghz_chantable), .bitrates = xradio_g_rates, .n_bitrates = xradio_g_rates_size, .ht_cap = { .cap = IEEE80211_HT_CAP_GRN_FLD #ifdef SUPPORT_HT40 #ifndef SUPPORT_NON_HT40_CHIP | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 #endif #endif | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), .ht_supported = 1, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { .rx_mask[0] = 0xFF, #ifdef SUPPORT_HT40 .rx_highest = __cpu_to_le16(0x0), #else .rx_highest = __cpu_to_le16(0x41), #endif .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, }; #ifdef CONFIG_XRADIO_5GHZ_SUPPORT static struct ieee80211_supported_band xradio_band_5ghz = { .channels = xradio_5ghz_chantable, .n_channels = ARRAY_SIZE(xradio_5ghz_chantable), .bitrates = xradio_a_rates, .n_bitrates = xradio_a_rates_size, .ht_cap = { .cap = IEEE80211_HT_CAP_GRN_FLD #ifdef SUPPORT_HT40 #ifndef SUPPORT_NON_HT40_CHIP | IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 #endif #endif | (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), .ht_supported = 1, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, .mcs = { .rx_mask[0] = 0xFF, #ifdef SUPPORT_HT40 .rx_highest = __cpu_to_le16(0x0), #else .rx_highest = __cpu_to_le16(0x41), #endif .tx_params = IEEE80211_HT_MCS_TX_DEFINED, }, }, }; #endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ static const unsigned long xradio_ttl[] = { 1 * HZ, /* VO */ 2 * HZ, /* VI */ 5 * HZ, /* BE */ 10 * HZ /* BK */ }; static const struct ieee80211_ops xradio_ops = { .start = xradio_start, .stop = xradio_stop, .add_interface = xradio_add_interface, .remove_interface = xradio_remove_interface, .change_interface = xradio_change_interface, .tx = xradio_tx, .hw_scan = xradio_hw_scan, #ifdef ROAM_OFFLOAD .sched_scan_start = xradio_hw_sched_scan_start, .sched_scan_stop = xradio_hw_sched_scan_stop, #endif /*ROAM_OFFLOAD*/ .set_tim = xradio_set_tim, .sta_notify = xradio_sta_notify, .sta_add = xradio_sta_add, .sta_remove = xradio_sta_remove, .set_key = xradio_set_key, .set_rts_threshold = xradio_set_rts_threshold, .config = xradio_config, .bss_info_changed = xradio_bss_info_changed, .prepare_multicast = xradio_prepare_multicast, .configure_filter = xradio_configure_filter, .conf_tx = xradio_conf_tx, .get_stats = xradio_get_stats, .ampdu_action = xradio_ampdu_action, .flush = xradio_flush, #ifdef CONFIG_PM .suspend = xradio_wow_suspend, .resume = xradio_wow_resume, #endif /* CONFIG_PM */ /* Intentionally not offloaded: */ /*.channel_switch = xradio_channel_switch, */ .remain_on_channel = xradio_remain_on_channel, .cancel_remain_on_channel = xradio_cancel_remain_on_channel, #ifdef IPV6_FILTERING .set_data_filter = xradio_set_data_filter, #endif /*IPV6_FILTERING*/ #ifdef CONFIG_XRADIO_TESTMODE .testmode_cmd = xradio_testmode_cmd, #endif /* CONFIG_XRADIO_TESTMODE */ }; struct xradio_common *g_hw_priv; #ifdef CONFIG_PM #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) static const struct wiphy_wowlan_support xradio_wowlan = { .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, }; #endif #endif /**************************** functions **********************************/ void xradio_version_show(void) { /* Show XRADIO version and compile time */ xradio_dbg(XRADIO_DBG_ALWY, "Driver Label:%s %s\n", DRV_VERSION, DRV_BUILDTIME); /************* Linux Kernel config *************/ #ifdef CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES]\n"); #endif #ifdef CONFIG_XRADIO_USE_GPIO_IRQ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_GPIO_IRQ]\n"); #endif #ifdef CONFIG_XRADIO_5GHZ_SUPPORT xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_5GHZ_SUPPORT]\n"); #endif #ifdef CONFIG_XRADIO_WAPI_SUPPORT xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_WAPI_SUPPORT]\n"); #endif #ifdef CONFIG_XRADIO_USE_EXTENSIONS xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_EXTENSIONS]\n"); #endif #ifdef CONFIG_PM xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_PM]\n"); #endif #ifdef CONFIG_XRADIO_SDIO xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_SDIO]\n"); #endif #ifdef CONFIG_XRADIO_DUMP_ON_ERROR xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DUMP_ON_ERROR]\n"); #endif #ifdef CONFIG_XRADIO_DEBUGFS xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DEBUGFS]\n"); #endif #ifdef CONFIG_XRADIO_ITP xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_ITP]\n"); #endif #ifdef CONFIG_XRADIO_TESTMODE xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_TESTMODE]\n"); #endif /************ XRADIO Make File config ************/ #ifdef P2P_MULTIVIF xradio_dbg(XRADIO_DBG_NIY, "[P2P_MULTIVIF]\n"); #endif #ifdef MCAST_FWDING xradio_dbg(XRADIO_DBG_NIY, "[MCAST_FWDING]\n"); #endif #ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_SUSPEND_RESUME_FILTER_ENABLE]\n"); #endif #ifdef AP_AGGREGATE_FW_FIX xradio_dbg(XRADIO_DBG_NIY, "[AP_AGGREGATE_FW_FIX]\n"); #endif #ifdef AP_HT_CAP_UPDATE xradio_dbg(XRADIO_DBG_NIY, "[AP_HT_CAP_UPDATE]\n"); #endif #ifdef PROBE_RESP_EXTRA_IE xradio_dbg(XRADIO_DBG_NIY, "[PROBE_RESP_EXTRA_IE]\n"); #endif #ifdef IPV6_FILTERING xradio_dbg(XRADIO_DBG_NIY, "[IPV6_FILTERING]\n"); #endif #ifdef ROAM_OFFLOAD xradio_dbg(XRADIO_DBG_NIY, "[ROAM_OFFLOAD]\n"); #endif #ifdef TES_P2P_0002_ROC_RESTART xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_0002_ROC_RESTART]\n"); #endif #ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_EXTEND_INACTIVITY_CNT]\n"); #endif #ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_DISABLE_EAPOL_FILTER]\n"); #endif #ifdef HAS_PUT_TASK_STRUCT xradio_dbg(XRADIO_DBG_NIY, "[HAS_PUT_TASK_STRUCT]\n"); #endif /************* XRADIO.h config *************/ #ifdef HIDDEN_SSID xradio_dbg(XRADIO_DBG_NIY, "[HIDDEN_SSID]\n"); #endif #ifdef ROC_DEBUG xradio_dbg(XRADIO_DBG_NIY, "[ROC_DEBUG]\n"); #endif #ifdef XRADIO_RRM xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_RRM]\n"); #endif } /* return 0: failed*/ static inline int xradio_macaddr_val2char(char *c_mac, const u8 *v_mac) { SYS_BUG(!v_mac || !c_mac); return sprintf(c_mac, "%02x:%02x:%02x:%02x:%02x:%02x\n", v_mac[0], v_mac[1], v_mac[2], v_mac[3], v_mac[4], v_mac[5]); } #ifndef XRADIO_MACPARAM_HEX static int xradio_macaddr_char2val(u8 *v_mac, const char *c_mac) { int i = 0; const char *tmp_char = c_mac; SYS_BUG(!v_mac || !c_mac); for (i = 0; i < ETH_ALEN; i++) { if (*tmp_char != 0) { v_mac[i] = simple_strtoul(tmp_char, (char **)&tmp_char, 16); } else { xradio_dbg(XRADIO_DBG_ERROR, "%s, Len Error\n", __func__); return -1; } if (i < ETH_ALEN - 1 && *tmp_char != ':') { xradio_dbg(XRADIO_DBG_ERROR, "%s, Format or Len Error\n", __func__); return -1; } tmp_char++; } return 0; } #endif /*bit0: 1=multicast, 0=unicast*/ /*bit1: 1=locally, 0=universally(vendor use)*/ #define MACADDR_VAILID(a) ( \ (a[0] != 0 || a[1] != 0 || \ a[2] != 0 || a[3] != 0 || \ a[4] != 0 || a[5] != 0) && \ !(a[0] & 0x3)) static void xradio_get_mac_addrs(u8 *macaddr) { int ret = 0; SYS_BUG(!macaddr); /* Check mac addrs param, if exsist, use it first.*/ #ifdef XRADIO_MACPARAM_HEX memcpy(macaddr, xradio_macaddr_param, ETH_ALEN); #else if (xradio_macaddr_param) { ret = xradio_macaddr_char2val(macaddr, xradio_macaddr_param); } #endif /* Use random value to set mac addr for the first time, * and save it in wifi config file. TODO: read from product ID*/ if (ret < 0 || !MACADDR_VAILID(macaddr)) { #ifdef XRADIO_MACPARAM_HEX ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 1); #else char c_mac[XRADIO_MAC_CHARLEN+2] = {0}; ret = access_file(WIFI_CONF_PATH, c_mac, XRADIO_MAC_CHARLEN, 1); if (ret >= 0) { ret = xradio_macaddr_char2val(macaddr, c_mac); } #endif if (ret < 0 || !MACADDR_VAILID(macaddr)) { get_random_bytes(macaddr, 6); macaddr[0] &= 0xFC; #ifdef XRADIO_MACPARAM_HEX ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 0); #else ret = xradio_macaddr_val2char(c_mac, macaddr); ret = access_file(WIFI_CONF_PATH, c_mac, ret, 0); #endif if (ret < 0) xradio_dbg(XRADIO_DBG_ERROR, "Access_file failed, path:%s!\n", WIFI_CONF_PATH); if (!MACADDR_VAILID(macaddr)) { xradio_dbg(XRADIO_DBG_WARN, "Use default Mac addr!\n"); macaddr[0] = 0xDC; macaddr[1] = 0x44; macaddr[2] = 0x6D; } else { xradio_dbg(XRADIO_DBG_NIY, "Use random Mac addr!\n"); } } else { xradio_dbg(XRADIO_DBG_NIY, "Use Mac addr in file!\n"); } } xradio_dbg(XRADIO_DBG_NIY, "MACADDR=%02x:%02x:%02x:%02x:%02x:%02x\n", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); } static void xradio_set_ifce_comb(struct xradio_common *hw_priv, struct ieee80211_hw *hw) { xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); #ifdef P2P_MULTIVIF hw_priv->if_limits1[0].max = 2; #else hw_priv->if_limits1[0].max = 1; #endif hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION); hw_priv->if_limits1[1].max = 1; hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP); #ifdef P2P_MULTIVIF hw_priv->if_limits2[0].max = 3; #else hw_priv->if_limits2[0].max = 2; #endif hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION); #ifdef P2P_MULTIVIF hw_priv->if_limits3[0].max = 2; #else hw_priv->if_limits3[0].max = 1; #endif hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION); hw_priv->if_limits3[1].max = 1; hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); /* TODO:COMBO: mac80211 doesn't yet support more than 1 * different channel */ hw_priv->if_combs[0].num_different_channels = 1; #ifdef P2P_MULTIVIF hw_priv->if_combs[0].max_interfaces = 3; #else hw_priv->if_combs[0].max_interfaces = 2; #endif hw_priv->if_combs[0].limits = hw_priv->if_limits1; hw_priv->if_combs[0].n_limits = 2; hw_priv->if_combs[1].num_different_channels = 1; #ifdef P2P_MULTIVIF hw_priv->if_combs[1].max_interfaces = 3; #else hw_priv->if_combs[1].max_interfaces = 2; #endif hw_priv->if_combs[1].limits = hw_priv->if_limits2; hw_priv->if_combs[1].n_limits = 1; hw_priv->if_combs[2].num_different_channels = 1; #ifdef P2P_MULTIVIF hw_priv->if_combs[2].max_interfaces = 3; #else hw_priv->if_combs[2].max_interfaces = 2; #endif hw_priv->if_combs[2].limits = hw_priv->if_limits3; hw_priv->if_combs[2].n_limits = 2; hw->wiphy->iface_combinations = &hw_priv->if_combs[0]; hw->wiphy->n_iface_combinations = 3; } static int xradio_device_init(struct xradio_common *hw_priv) { int ret = 0; SYS_BUG(!hw_priv); /* Add pm device. */ hw_priv->plat_device = platform_device_alloc(XRADIO_PLAT_DEVICE, 0); if (!hw_priv->plat_device) { xradio_dbg(XRADIO_DBG_ERROR, "%s:platform_device_alloc failed!\n", __func__); return -ENOMEM; } hw_priv->plat_device->dev.platform_data = hw_priv; ret = platform_device_add(hw_priv->plat_device); if (ret) { xradio_dbg(XRADIO_DBG_ERROR, "%s:platform_device_add failed(%d)!\n", __func__, ret); kfree(hw_priv->plat_device); hw_priv->plat_device = NULL; return ret; } return 0; } static void xradio_device_deinit(struct xradio_common *hw_priv) { if (hw_priv->plat_device) { hw_priv->plat_device->dev.platform_data = NULL; /* kfree is already do in platform_device_unregister.*/ platform_device_unregister(hw_priv->plat_device); hw_priv->plat_device = NULL; hw_priv->pdev = NULL; } else { xradio_dbg(XRADIO_DBG_WARN, "%s:hw_priv->plat_device is NULL!\n", __func__); } } struct ieee80211_hw *xradio_init_common(size_t hw_priv_data_len) { int i; struct ieee80211_hw *hw; struct xradio_common *hw_priv; struct ieee80211_supported_band *sband; int band; xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); /* Alloc ieee_802.11 hw and xradio_common struct. */ hw = mac80211_alloc_hw(hw_priv_data_len, &xradio_ops); if (!hw) return NULL; hw_priv = hw->priv; xradio_dbg(XRADIO_DBG_ALWY, "Allocated hw_priv @ %p\n", hw_priv); xradio_dbg(XRADIO_DBG_MSG, "xradio_common_size=%zu, vif_data_size=%zu\n", sizeof(struct xradio_common), sizeof(struct xradio_vif)); memset(hw_priv, 0, sizeof(*hw_priv)); /* Get MAC address. */ xradio_get_mac_addrs((u8 *)&hw_priv->addresses[0]); memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN); hw_priv->addresses[1].addr[5] += 0x01; #ifdef P2P_MULTIVIF memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr, ETH_ALEN); hw_priv->addresses[2].addr[4] ^= 0x80; #endif /* Initialize members of hw_priv. */ hw_priv->hw = hw; hw_priv->if_id_slot = 0; hw_priv->roc_if_id = -1; atomic_set(&hw_priv->num_vifs, 0); /* initial rates and channels TODO: fetch from FW */ hw_priv->rates = xradio_rates; hw_priv->mcs_rates = xradio_n_rates; #ifdef ROAM_OFFLOAD hw_priv->auto_scanning = 0; hw_priv->frame_rcvd = 0; hw_priv->num_scanchannels = 0; hw_priv->num_2g_channels = 0; hw_priv->num_5g_channels = 0; #endif /*ROAM_OFFLOAD*/ #ifdef AP_AGGREGATE_FW_FIX /* Enable block ACK for 4 TID (BE,VI,VI,VO). */ hw_priv->ba_tid_mask = 0xB1; /*due to HW limitations*/ #else /* Enable block ACK for every TID but voice. */ hw_priv->ba_tid_mask = 0x3F; #endif hw_priv->noise = -94; /* hw_priv->beacon_req_id = cpu_to_le32(0); */ /* Initialize members of ieee80211_hw, it works in UMAC. */ hw->sta_data_size = sizeof(struct xradio_sta_priv); hw->vif_data_size = sizeof(struct xradio_vif); hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | /* Aggregation is fully controlled by firmware. * Do not need any support from the mac80211 stack */ /* IEEE80211_HW_AMPDU_AGGREGATION | */ #if defined(CONFIG_XRADIO_USE_EXTENSIONS) IEEE80211_HW_SUPPORTS_P2P_PS | IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | #endif /* CONFIG_XRADIO_USE_EXTENSIONS */ IEEE80211_HW_BEACON_FILTER; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); /* Support only for limited wowlan functionalities */ #ifdef CONFIG_PM #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) hw->wiphy->wowlan = &xradio_wowlan; #else hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT; hw->wiphy->wowlan.n_patterns = 0; #endif #endif #if defined(CONFIG_XRADIO_USE_EXTENSIONS) hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; #endif /* CONFIG_XRADIO_USE_EXTENSIONS */ #if defined(CONFIG_XRADIO_DISABLE_BEACON_HINTS) hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; #endif hw->wiphy->n_addresses = XRWL_MAX_VIFS; hw->wiphy->addresses = hw_priv->addresses; /* * max_remain_on_channel_duration will bigger than 500 in * wpa_supplicant v2.3, change to 2500 and default value=5000 in umac. */ hw->wiphy->max_remain_on_channel_duration = 2500; hw->channel_change_time = 500; /* TODO: find actual value */ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */ + 12 /* TKIP ICV and MIC */; hw->wiphy->bands[NL80211_BAND_2GHZ] = &xradio_band_2ghz; #ifdef CONFIG_XRADIO_5GHZ_SUPPORT hw->wiphy->bands[NL80211_BAND_5GHZ] = &xradio_band_5ghz; #endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ hw->queues = AC_QUEUE_NUM; hw->max_rates = MAX_RATES_STAGE; hw->max_rate_tries = MAX_RATES_RETRY; /* Channel params have to be cleared before registering wiphy again */ for (band = 0; band < NUM_NL80211_BANDS; band++) { sband = hw->wiphy->bands[band]; if (!sband) continue; for (i = 0; i < sband->n_channels; i++) { sband->channels[i].flags = 0; sband->channels[i].max_antenna_gain = 0; sband->channels[i].max_power = 30; } } /* * hw_priv->channel init value is the local->oper_channel init value; * when transplanting,take care. */ for (band = 0; band < NUM_NL80211_BANDS; band++) { sband = hw->wiphy->bands[band]; if (!sband) continue; if (!hw_priv->channel) { hw_priv->channel = &sband->channels[2]; } } hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr); /* Initialize locks. */ spin_lock_init(&hw_priv->vif_list_lock); sema_init(&hw_priv->wsm_cmd_sema, 1); sema_init(&hw_priv->conf_lock, 1); sema_init(&hw_priv->wsm_oper_lock, 1); atomic_set(&hw_priv->tx_lock, 0); sema_init(&hw_priv->tx_lock_sem, 1); sema_init(&hw_priv->dtor_lock, 1); hw_priv->workqueue = create_singlethread_workqueue(XRADIO_WORKQUEUE); hw_priv->spare_workqueue = create_singlethread_workqueue(XRADIO_SPARE_WORKQUEUE); sema_init(&hw_priv->scan.lock, 1); sema_init(&hw_priv->scan.status_lock, 1); INIT_WORK(&hw_priv->scan.work, xradio_scan_work); #ifdef ROAM_OFFLOAD INIT_WORK(&hw_priv->scan.swork, xradio_sched_scan_work); #endif /*ROAM_OFFLOAD*/ INIT_DELAYED_WORK(&hw_priv->scan.probe_work, xradio_probe_work); INIT_DELAYED_WORK(&hw_priv->scan.timeout, xradio_scan_timeout); hw_priv->scan.scan_failed_cnt = 0; INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, xradio_rem_chan_timeout); INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); atomic_set(&hw_priv->upload_count, 0); for (i = 0; i < (XRWL_MAX_VIFS-1); ++i) { hw_priv->scan_delay_status[i] = XRADIO_SCAN_ALLOW; hw_priv->scan_delay_time[i] = 0; } spin_lock_init(&hw_priv->event_queue_lock); INIT_LIST_HEAD(&hw_priv->event_queue); INIT_WORK(&hw_priv->event_handler, xradio_event_handler); INIT_WORK(&hw_priv->ba_work, xradio_ba_work); spin_lock_init(&hw_priv->ba_lock); init_timer(&hw_priv->ba_timer); hw_priv->ba_timer.data = (unsigned long)hw_priv; hw_priv->ba_timer.function = xradio_ba_timer; init_timer(&hw_priv->BT_timer); hw_priv->BT_timer.data = (unsigned long)hw_priv; hw_priv->BT_timer.function = xradio_bt_timer; if (unlikely(xradio_queue_stats_init(&hw_priv->tx_queue_stats, WLAN_LINK_ID_MAX, xradio_skb_dtor, hw_priv))) { mac80211_free_hw(hw); return NULL; } for (i = 0; i < AC_QUEUE_NUM; ++i) { if (unlikely(xradio_queue_init(&hw_priv->tx_queue[i], &hw_priv->tx_queue_stats, i, XRWL_MAX_QUEUE_SZ, xradio_ttl[i]))) { for (; i > 0; i--) xradio_queue_deinit(&hw_priv->tx_queue[i - 1]); xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); mac80211_free_hw(hw); return NULL; } } init_waitqueue_head(&hw_priv->channel_switch_done); init_waitqueue_head(&hw_priv->wsm_cmd_wq); init_waitqueue_head(&hw_priv->wsm_wakeup_done); init_waitqueue_head(&hw_priv->wsm_startup_done); init_waitqueue_head(&hw_priv->offchannel_wq); hw_priv->wsm_caps.firmwareReady = 0; hw_priv->driver_ready = 0; hw_priv->offchannel_done = 0; wsm_buf_init(&hw_priv->wsm_cmd_buf, xr_sdio_blksize_align(1024)); spin_lock_init(&hw_priv->wsm_cmd.lock); tx_policy_init(hw_priv); xradio_init_resv_skb(hw_priv); for (i = 0; i < XRWL_MAX_VIFS; i++) hw_priv->hw_bufs_used_vif[i] = 0; #ifdef MCAST_FWDING wsm_init_release_buffer_request(hw_priv); hw_priv->buf_released = 0; #endif hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; #if defined(CONFIG_XRADIO_DEBUG) hw_priv->wsm_enable_wsm_dumps = 0; hw_priv->wsm_dump_max_size = WSM_DUMP_MAX_SIZE; #endif /* CONFIG_XRADIO_DEBUG */ hw_priv->query_packetID = 0; atomic_set(&hw_priv->query_cnt, 0); INIT_WORK(&hw_priv->query_work, wsm_query_work); atomic_set(&hw_priv->suspend_state, XRADIO_RESUME); #ifdef HW_RESTART hw_priv->exit_sync = false; hw_priv->hw_restart = false; INIT_WORK(&hw_priv->hw_restart_work, xradio_restart_work); #endif #ifdef CONFIG_XRADIO_TESTMODE hw_priv->test_frame.data = NULL; hw_priv->test_frame.len = 0; spin_lock_init(&hw_priv->tsm_lock); INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout, xradio_advance_scan_timeout); #endif /*CONFIG_XRADIO_TESTMODE*/ xradio_set_ifce_comb(hw_priv, hw_priv->hw); #ifdef MONITOR_MODE hw_priv->monitor_if_id = -1; hw_priv->monitor_running = false; #endif #ifdef BOOT_NOT_READY_FIX hw_priv->boot_not_ready_cnt = 0; hw_priv->boot_not_ready = 0; #endif hw_priv->join_chan = 1; if (!g_hw_priv) { g_hw_priv = hw_priv; return hw; } else { /*error:didn't release hw_priv last time. */ mac80211_free_hw(hw); xradio_dbg(XRADIO_DBG_ERROR, "g_hw_priv is not NULL @ %p!\n", g_hw_priv); return NULL; } } void xradio_free_common(struct ieee80211_hw *dev) { int i; struct xradio_common *hw_priv = dev->priv; xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); #ifdef CONFIG_XRADIO_TESTMODE kfree(hw_priv->test_frame.data); #endif /* CONFIG_XRADIO_TESTMODE */ cancel_work_sync(&hw_priv->query_work); del_timer_sync(&hw_priv->ba_timer); del_timer_sync(&hw_priv->BT_timer); wsm_buf_deinit(&hw_priv->wsm_cmd_buf); flush_workqueue(hw_priv->workqueue); destroy_workqueue(hw_priv->workqueue); hw_priv->workqueue = NULL; flush_workqueue(hw_priv->spare_workqueue); destroy_workqueue(hw_priv->spare_workqueue); hw_priv->spare_workqueue = NULL; xradio_deinit_resv_skb(hw_priv); if (hw_priv->skb_cache) { dev_kfree_skb(hw_priv->skb_cache); hw_priv->skb_cache = NULL; } for (i = 0; i < 4; ++i) xradio_queue_deinit(&hw_priv->tx_queue[i]); xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); #ifdef CONFIG_XRADIO_DEBUG for (i = 0; i < XRWL_MAX_VIFS; i++) { if (hw_priv->vif_list[i]) xradio_dbg(XRADIO_DBG_ERROR, "vif_list[%d]is not NULL @ %p!\n", i, hw_priv->vif_list[i]); } #endif #ifdef MCAST_FWDING wsm_deinit_release_buffer(hw_priv); #endif /* unsigned int i; */ mac80211_free_hw(dev); g_hw_priv = NULL; } int xradio_register_common(struct ieee80211_hw *dev) { int err = 0; struct xradio_common *hw_priv = dev->priv; xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); SET_IEEE80211_DEV(dev, hw_priv->pdev); err = mac80211_register_hw(dev); if (err) { xradio_dbg(XRADIO_DBG_ERROR, "Cannot register device (%d).\n", err); return err; } xradio_dbg(XRADIO_DBG_MSG, "is registered as '%s'\n", wiphy_name(dev->wiphy)); xradio_debug_init_common(hw_priv); #if (SUPPORT_EPTA) SYS_WARN(wsm_set_epta_stat_dbg_ctrl(hw_priv, epta_stat_dbg_ctrl)); #endif hw_priv->driver_ready = 1; wake_up(&hw_priv->wsm_startup_done); return 0; } void xradio_unregister_common(struct ieee80211_hw *dev) { struct xradio_common *hw_priv = dev->priv; xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); #ifdef CONFIG_XRADIO_ETF if (etf_is_connect()) { return ; } #endif if (wiphy_dev(dev->wiphy)) { mac80211_unregister_hw(dev); SET_IEEE80211_DEV(dev, NULL); xradio_debug_release_common(hw_priv); } hw_priv->driver_ready = 0; } #ifdef HW_RESTART int xradio_core_reinit(struct xradio_common *hw_priv) { int ret = 0; u16 ctrl_reg; int i = 0; struct xradio_vif *priv = NULL; struct wsm_operational_mode mode = { .power_mode = wsm_power_mode_quiescent, .disableMoreFlagUsage = true, }; if (!hw_priv) { xradio_dbg(XRADIO_DBG_ERROR, "%s hw_priv is NULL!\n", __func__); return -1; } /* Need some time for restart hardware, don't suspend again.*/ #ifdef CONFIG_PM xradio_pm_lock_awake(&hw_priv->pm_state); #endif xradio_dbg(XRADIO_DBG_ALWY, "%s %d!\n", __func__, __LINE__); /* Disconnect with AP or STAs. */ xradio_for_each_vif(hw_priv, priv, i) { if (!priv) continue; if (priv->join_status == XRADIO_JOIN_STATUS_STA) { mac80211_connection_loss(priv->vif); msleep(200); } else if (priv->join_status == XRADIO_JOIN_STATUS_AP) { wsm_send_disassoc_to_self(hw_priv, priv); msleep(200); } xradio_remove_interface(hw_priv->hw, priv->vif); } xradio_stop(hw_priv->hw); /* lock queue of network. */ xradio_tx_queues_lock(hw_priv); #ifdef BH_PROC_THREAD /* restart proc thread. */ bh_proc_reinit(hw_priv); #endif /*deinit dev */ xradio_dev_deinit(hw_priv); /*reinit status refer to hif. */ hw_priv->powersave_enabled = false; hw_priv->wsm_caps.firmwareReady = 0; atomic_set(&hw_priv->bh_rx, 0); atomic_set(&hw_priv->bh_tx, 0); atomic_set(&hw_priv->bh_term, 0); hw_priv->buf_id_tx = 0; hw_priv->buf_id_rx = 0; hw_priv->wsm_rx_seq = 0; hw_priv->wsm_tx_seq = 0; hw_priv->device_can_sleep = 0; hw_priv->hw_bufs_used = 0; #ifdef BOOT_NOT_READY_FIX hw_priv->boot_not_ready_cnt = 0; hw_priv->boot_not_ready = 0; #endif memset(&hw_priv->hw_bufs_used_vif, 0, sizeof(hw_priv->hw_bufs_used_vif)); for (i = 0; i < (XRWL_MAX_VIFS-1); ++i) { hw_priv->scan_delay_status[i] = XRADIO_SCAN_ALLOW; hw_priv->scan_delay_time[i] = 0; } atomic_set(&hw_priv->query_cnt, 0); hw_priv->query_packetID = 0; tx_policy_init(hw_priv); #ifdef BOOT_NOT_READY_FIX restart: #endif /*move parent to plat_device*/ ret = device_move(&hw_priv->hw->wiphy->dev, &hw_priv->plat_device->dev, 0); if (ret < 0) { xradio_dbg(XRADIO_DBG_ERROR, "%s:device move parent to plat_device failed\n", __func__); goto exit; } ret = mac80211_ifdev_move(hw_priv->hw, &hw_priv->plat_device->dev, 0); if (ret < 0) { xradio_dbg(XRADIO_DBG_ERROR, "%s:net_device move parent to plat_device failed\n", __func__); goto exit; } /*reinit sdio sbus. */ sbus_sdio_deinit(); hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops, &hw_priv->sbus_priv); if (!hw_priv->pdev) { xradio_dbg(XRADIO_DBG_ERROR, "%s:sbus_sdio_init failed\n", __func__); ret = -ETIMEDOUT; goto exit; } /*move parent to sdio device*/ ret = device_move(&hw_priv->hw->wiphy->dev, hw_priv->pdev, 1); if (ret < 0) { xradio_dbg(XRADIO_DBG_ERROR, "%s:device move parent to sdio failed\n", __func__); goto exit; } SET_IEEE80211_DEV(hw_priv->hw, hw_priv->pdev); ret = mac80211_ifdev_move(hw_priv->hw, hw_priv->pdev, 1); if (ret < 0) { xradio_dbg(XRADIO_DBG_ERROR, "%s:net_device move parent to sdio failed\n", __func__); goto exit; } /*wake up bh thread. */ if (hw_priv->bh_thread == NULL) { hw_priv->bh_error = 0; atomic_set(&hw_priv->tx_lock, 0); xradio_register_bh(hw_priv); } else { #ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF WARN_ON(xradio_bh_resume(hw_priv)); #endif } /* Load firmware and register Interrupt Handler */ ret = xradio_load_firmware(hw_priv); #ifdef BOOT_NOT_READY_FIX if (ret && hw_priv->boot_not_ready && (hw_priv->boot_not_ready_cnt < 6)) { ret = 0; hw_priv->boot_not_ready = 0; if (hw_priv->bh_thread != NULL) xradio_unregister_bh(hw_priv); goto restart; } #endif if (ret) { xradio_dbg(XRADIO_DBG_ERROR, "%s:xradio_load_firmware failed(%d).\n", __func__, ret); goto exit; } /* Set sdio blocksize. */ hw_priv->sbus_ops->lock(hw_priv->sbus_priv); SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, SDIO_BLOCK_SIZE)); hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { /* TODO: Needs to find how to reset device */ /* in QUEUE mode properly. */ xradio_dbg(XRADIO_DBG_ERROR, "%s:Firmware Startup Timeout!\n", __func__); ret = -ETIMEDOUT; goto exit; } xradio_dbg(XRADIO_DBG_ALWY, "%s:Firmware Startup Done.\n", __func__); /* Keep device wake up. */ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT); if (xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &ctrl_reg)) ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &ctrl_reg); SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT)); /* * We finish device restart here. */ hw_priv->hw_restart = false; atomic_set(&hw_priv->suspend_state, XRADIO_RESUME); wake_up(&hw_priv->wsm_wakeup_done); /* Set device mode parameter. */ for (i = 0; i < xrwl_get_nr_hw_ifaces(hw_priv); i++) { /* Set low-power mode. */ ret = wsm_set_operational_mode(hw_priv, &mode, i); /* Enable multi-TX confirmation */ ret = wsm_use_multi_tx_conf(hw_priv, true, i); } #if (SUPPORT_EPTA) SYS_WARN(wsm_set_epta_stat_dbg_ctrl(hw_priv, epta_stat_dbg_ctrl)); #endif #if 0 /* * IF USE xradio_register_common TO REINIT, U SHOULD ENABLE THIS. * fix super standby hang in scan interface. * Since resume from super standby, mac system will register ieee80211_hw to * subsystem, and the wiphy->max_scan_ie_len will decrease 47 every round whose * initial value is IEEE80211_MAX_DATA_LEN, after about 40~50 round, this value will * under 143. and the 143 is the default scan ie length from supplicant, this will return * -22 in nl80211.c * if (n_ssids > wiphy->max_scan_ssids) { * return -EINVAL; * } * we need to reinit this value in super standby recovery. */ hw_priv->hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; #endif /* * Restart umac, use mac80211_restart_hw. * Use xradio_register_common may cause umac scan sync problems. */ if (!ret) { mac80211_restart_hw(hw_priv->hw); } /* * Unlock queue of network. regardless of xradio_tx_queues_lock, * this must be here to make sure all queue is unlock. */ for (i = 0; i < 4; ++i) { struct xradio_queue *queue = &hw_priv->tx_queue[i]; spin_lock_bh(&queue->lock); if (queue->tx_locked_cnt > 0) { queue->tx_locked_cnt = 0; mac80211_wake_queue(hw_priv->hw, queue->queue_id); } spin_unlock_bh(&queue->lock); } exit: #ifdef CONFIG_PM xradio_pm_unlock_awake(&hw_priv->pm_state); #endif xradio_dbg(XRADIO_DBG_ALWY, "%s end!\n", __func__); return ret; } void xradio_restart_work(struct work_struct *work) { struct xradio_common *hw_priv = container_of(work, struct xradio_common, hw_restart_work); xradio_dbg(XRADIO_DBG_ALWY, "%s\n", __func__); hw_priv->hw_restart_work_running = 1; if (hw_priv->bh_error) { xradio_unregister_bh(hw_priv); } if (unlikely(xradio_core_reinit(hw_priv))) { xradio_dbg(XRADIO_DBG_ALWY, "%s again!\n", __func__); down(&hw_priv->wsm_cmd_sema); hw_priv->hw_restart = true; up(&hw_priv->wsm_cmd_sema); if (hw_priv->bh_thread != NULL) xradio_unregister_bh(hw_priv); #ifdef ERROR_HANG_DRIVER if (error_hang_driver) { /*it will arrive here if error occurs in poweroff resume.*/ hw_priv->bh_error = 0x80000000; /*make it different from real bh error.*/ wsm_printk(XRADIO_DBG_ERROR, "%s error_hang_driver\n", __func__); hw_priv->hw_restart_work_running = 0; return ; } #endif xradio_core_reinit(hw_priv); } hw_priv->hw_restart_work_running = 0; } #endif int xradio_core_init(void) { int err = -ENOMEM; u16 ctrl_reg; int if_id; struct ieee80211_hw *dev; struct xradio_common *hw_priv; struct wsm_operational_mode mode = { .power_mode = wsm_power_mode_quiescent, .disableMoreFlagUsage = true, }; xradio_version_show(); /*init xradio_common */ dev = xradio_init_common(sizeof(struct xradio_common)); if (!dev) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_init_common failed\n"); return err; } hw_priv = dev->priv; if (xradio_device_init(hw_priv)) { xradio_free_common(dev); xradio_dbg(XRADIO_DBG_ERROR, "xradio_device_init failed\n"); return err; } #ifdef BH_PROC_THREAD err = bh_proc_init(hw_priv); if (err) { bh_printk(XRADIO_DBG_ERROR, "%s bh_proc_init err=%d\n", __func__, err); return err; } #endif #ifdef BOOT_NOT_READY_FIX start: #endif /*init sdio sbus */ hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops, &hw_priv->sbus_priv); if (!hw_priv->pdev) { err = -ETIMEDOUT; xradio_dbg(XRADIO_DBG_ERROR, "sbus_sdio_init failed\n"); goto err1; } /* WSM callbacks. */ hw_priv->wsm_cbc.scan_complete = xradio_scan_complete_cb; hw_priv->wsm_cbc.tx_confirm = xradio_tx_confirm_cb; hw_priv->wsm_cbc.rx = xradio_rx_cb; hw_priv->wsm_cbc.suspend_resume = xradio_suspend_resume; /* hw_priv->wsm_cbc.set_pm_complete = xradio_set_pm_complete_cb; */ hw_priv->wsm_cbc.channel_switch = xradio_channel_switch_cb; /*init pm and wakelock. */ #ifdef CONFIG_PM err = xradio_pm_init(&hw_priv->pm_state, hw_priv); if (err) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_pm_init failed(%d).\n", err); goto err2; } #endif /* Register bh thread*/ err = xradio_register_bh(hw_priv); if (err) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_register_bh failed(%d).\n", err); goto err3; } /* Load firmware and register Interrupt Handler */ err = xradio_load_firmware(hw_priv); #ifdef BOOT_NOT_READY_FIX if (err && hw_priv->boot_not_ready && (hw_priv->boot_not_ready_cnt < 6)) {/*workaround that */ hw_priv->boot_not_ready = 0; err = 0; xradio_unregister_bh(hw_priv); #ifdef CONFIG_PM xradio_pm_deinit(&hw_priv->pm_state); #endif sbus_sdio_deinit(); goto start; } #endif if (err) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_load_firmware failed(%d).\n", err); goto err4; } /* Set sdio blocksize. */ hw_priv->sbus_ops->lock(hw_priv->sbus_priv); SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, SDIO_BLOCK_SIZE)); hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { /* TODO: Needs to find how to reset device */ /* in QUEUE mode properly. */ xradio_dbg(XRADIO_DBG_ERROR, "Firmware Startup Timeout!\n"); err = -ETIMEDOUT; goto err5; } xradio_dbg(XRADIO_DBG_ALWY, "Firmware Startup Done.\n"); /* Keep device wake up. */ err = xradio_reg_bit_operate(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT, 0); if (err) { xradio_dbg(XRADIO_DBG_ERROR, "device wake up failed(%d)!\n", err); goto err5; } SYS_WARN(xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &ctrl_reg)); SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT)); /* ETF mode don't need following steps, just return here*/ #ifdef CONFIG_XRADIO_ETF if (etf_is_connect()) { etf_set_core(hw_priv); xradio_dbg(XRADIO_DBG_ALWY, "%s Enter ETF mode!\n", __func__); return 0; } #endif /* Set device mode parameter. */ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); if_id++) { u32 multi_enabled = __cpu_to_le32(BIT(0)); /* Set low-power mode. */ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, if_id)); if (hw_priv->wsm_caps.firmwareCap & 0x8000) { multi_enabled |= __cpu_to_le32(BIT(2)); xradio_dbg(XRADIO_DBG_WARN, "enable Multi-Rx!\n"); } /* Enable multi-TX confirmation */ SYS_WARN(wsm_use_multi_tx_conf(hw_priv, multi_enabled, if_id)); } /* Register wireless net device. */ err = xradio_register_common(dev); if (err) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_register_common failed(%d)!\n", err); goto err5; } return err; err5: xradio_dev_deinit(hw_priv); err4: xradio_unregister_bh(hw_priv); err3: #ifdef CONFIG_PM xradio_pm_deinit(&hw_priv->pm_state); err2: #endif #ifdef ERROR_HANG_DRIVER /*we want to use sdio in hang driver.*/ if (!error_hang_driver) { sbus_sdio_deinit(); } #else sbus_sdio_deinit(); #endif err1: #ifdef ERROR_HANG_DRIVER xradio_hang_driver_for_debug(hw_priv, err); #endif xradio_device_deinit(hw_priv); xradio_free_common(dev); return err; } EXPORT_SYMBOL_GPL(xradio_core_init); void xradio_core_deinit(void) { xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); if (g_hw_priv) { #ifdef HW_RESTART down(&g_hw_priv->wsm_cmd_sema); g_hw_priv->exit_sync = true; up(&g_hw_priv->wsm_cmd_sema); cancel_work_sync(&g_hw_priv->hw_restart_work); #endif #ifdef ERROR_HANG_DRIVER /*we hang driver here before unregister.*/ xradio_hang_driver_for_debug(g_hw_priv, g_hw_priv->bh_error); #endif #ifdef CONFIG_XRADIO_ETF if (etf_is_connect()) { etf_set_core(NULL); xradio_dbg(XRADIO_DBG_ALWY, "%s Exit ETF mode!\n", __func__); } else xradio_unregister_common(g_hw_priv->hw); #else xradio_unregister_common(g_hw_priv->hw); #endif #ifdef BH_PROC_THREAD bh_proc_deinit(g_hw_priv); #endif xradio_dev_deinit(g_hw_priv); xradio_unregister_bh(g_hw_priv); #ifdef CONFIG_PM xradio_pm_deinit(&g_hw_priv->pm_state); #endif xradio_device_deinit(g_hw_priv); xradio_free_common(g_hw_priv->hw); sbus_sdio_deinit(); } return; } EXPORT_SYMBOL_GPL(xradio_core_deinit); /* Init Module function -> Called by insmod */ static int __init xradio_core_entry(void) { int ret = 0; xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); ret = xradio_plat_init(); if (ret) { xradio_dbg(XRADIO_DBG_ERROR, "xradio_plat_init failed(%d)!\n", ret); return ret; } ret = xradio_host_dbg_init(); if (ret) { xradio_dbg(XRADIO_DBG_ERROR, "%s dbg init failed(%d)!\n", __func__, ret); xradio_plat_deinit(); return ret; } #ifdef CONFIG_XRADIO_ETF ret = xradio_etf_init(); if (ret) { xradio_dbg(XRADIO_DBG_ERROR, "%s ETF init failed(%d)!\n", __func__, ret); xradio_host_dbg_deinit(); xradio_plat_deinit(); return ret; } #endif return 0; } /* Called at Driver Unloading */ static void __exit xradio_core_exit(void) { #ifdef CONFIG_XRADIO_ETF xradio_etf_deinit(); #endif xradio_host_dbg_deinit(); xradio_plat_deinit(); xradio_dbg(XRADIO_DBG_TRC, "%s\n", __func__); } module_init(xradio_core_entry); module_exit(xradio_core_exit);