/* * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
staticvoid wcn36xx_feat_caps_info(struct wcn36xx *wcn)
{ int i;
for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, i)) {
wcn36xx_dbg(WCN36XX_DBG_MAC, "FW Cap %s\n",
wcn36xx_firmware_get_cap_name(i));
}
}
}
/* SMD initialization */
ret = wcn36xx_smd_open(wcn); if (ret) {
wcn36xx_err("Failed to open smd channel: %d\n", ret); goto out_err;
}
/* Allocate memory pools for Mgmt BD headers and Data BD headers */
ret = wcn36xx_dxe_allocate_mem_pools(wcn); if (ret) {
wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); goto out_smd_close;
}
ret = wcn36xx_dxe_alloc_ctl_blks(wcn); if (ret) {
wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); goto out_free_dxe_pool;
}
ret = wcn36xx_smd_load_nv(wcn); if (ret) {
wcn36xx_err("Failed to push NV to chip\n"); goto out_free_dxe_ctl;
}
ret = wcn36xx_smd_start(wcn); if (ret) {
wcn36xx_err("Failed to start chip\n"); goto out_free_dxe_ctl;
}
if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
ret = wcn36xx_smd_feature_caps_exchange(wcn); if (ret)
wcn36xx_warn("Exchange feature caps failed\n"); else
wcn36xx_feat_caps_info(wcn);
}
/* DMA channel initialization */
ret = wcn36xx_dxe_init(wcn); if (ret) {
wcn36xx_err("DXE init failed\n"); goto out_smd_stop;
}
staticvoid wcn36xx_change_opchannel(struct wcn36xx *wcn, int ch)
{ struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *tmp; struct ieee80211_supported_band *band; struct ieee80211_channel *channel = NULL; unsignedlong flags; int i, j;
for (i = 0; i < ARRAY_SIZE(wcn->hw->wiphy->bands); i++) {
band = wcn->hw->wiphy->bands[i]; if (!band) break; for (j = 0; j < band->n_channels; j++) { if (HW_VALUE_CHANNEL(band->channels[j].hw_value) == ch) {
channel = &band->channels[j]; break;
}
} if (channel) break;
}
if (!channel) {
wcn36xx_err("Cannot tune to channel %d\n", ch); return;
}
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { int ch = WCN36XX_HW_CHANNEL(wcn);
wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n",
ch);
if (wcn->sw_scan_opchannel == ch && wcn->sw_scan_channel) { /* If channel is the initial operating channel, we may * want to receive/transmit regular data packets, then * simply stop the scan session and exit PS mode.
*/ if (wcn->sw_scan_channel)
wcn36xx_smd_end_scan(wcn, wcn->sw_scan_channel); if (wcn->sw_scan_init) {
wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN,
wcn->sw_scan_vif);
}
} elseif (wcn->sw_scan) { /* A scan is ongoing, do not change the operating * channel, but start a scan session on the channel.
*/ if (wcn->sw_scan_channel)
wcn36xx_smd_end_scan(wcn, wcn->sw_scan_channel); if (!wcn->sw_scan_init) { /* This can fail if we are unable to notify the * operating channel.
*/
ret = wcn36xx_smd_init_scan(wcn,
HAL_SYS_MODE_SCAN,
wcn->sw_scan_vif); if (ret) {
mutex_unlock(&wcn->conf_mutex); return -EIO;
}
}
wcn36xx_smd_start_scan(wcn, ch);
} else {
wcn36xx_change_opchannel(wcn, ch);
}
}
if (changed & IEEE80211_CONF_CHANGE_PS)
wcn36xx_change_ps(wcn, hw->conf.flags & IEEE80211_CONF_PS);
if (changed & IEEE80211_CONF_CHANGE_IDLE) { if (hw->conf.flags & IEEE80211_CONF_IDLE)
wcn36xx_smd_enter_imps(wcn); else
wcn36xx_smd_exit_imps(wcn);
}
/* FW handles MC filtering only when connected as STA */ if (*total & FIF_ALLMULTI)
wcn36xx_smd_set_mc_list(wcn, vif, NULL); elseif (NL80211_IFTYPE_STATION == vif->type && tmp->sta_assoc)
wcn36xx_smd_set_mc_list(wcn, vif, fp);
}
switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_WEP40:
vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; break; case WLAN_CIPHER_SUITE_WEP104:
vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP104; break; case WLAN_CIPHER_SUITE_CCMP:
vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; break; case WLAN_CIPHER_SUITE_TKIP:
vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; break; default:
wcn36xx_err("Unsupported key type 0x%x\n",
key_conf->cipher);
ret = -EOPNOTSUPP; goto out;
}
switch (cmd) { case SET_KEY: if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { /* * Supplicant is sending key in the wrong order: * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) * but HW expects it to be in the order as described in * IEEE 802.11 spec (see chapter 11.7) like this: * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b)
*/
memcpy(key, key_conf->key, 16);
memcpy(key + 16, key_conf->key + 24, 8);
memcpy(key + 24, key_conf->key + 16, 8);
} else {
memcpy(key, key_conf->key, key_conf->keylen);
}
if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) {
sta_priv->is_data_encrypted = true; /* Reconfigure bss with encrypt_type */ if (NL80211_IFTYPE_STATION == vif->type) {
wcn36xx_smd_config_bss(wcn,
vif,
sta,
sta->addr, true);
wcn36xx_smd_config_sta(wcn, vif, sta);
}
if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { /* ieee80211_scan_completed will be called on FW scan
* indication */
wcn36xx_smd_stop_hw_scan(wcn);
}
}
/* * Holding conf_mutex ensures mutal exclusion with * wcn36xx_sta_remove() and as such ensures that sta * won't be freed while we're operating on it. As such * we do not need to hold the rcu_read_lock().
*/
sta = ieee80211_find_sta(vif, bss_conf->bssid); if (!sta) {
wcn36xx_err("sta %pM is not found\n",
bss_conf->bssid); goto out;
}
sta_priv = wcn36xx_sta_to_priv(sta);
wcn36xx_smd_set_link_st(wcn, bss_conf->bssid,
vif->addr,
WCN36XX_HAL_LINK_POSTASSOC_STATE);
wcn36xx_smd_config_bss(wcn, vif, sta,
bss_conf->bssid, true);
sta_priv->aid = vif->cfg.aid; /* * config_sta must be called from because this is the * place where AID is available.
*/
wcn36xx_smd_config_sta(wcn, vif, sta); if (vif->type == NL80211_IFTYPE_STATION)
wcn36xx_smd_add_beacon_filter(wcn, vif);
wcn36xx_enable_keep_alive_null_packet(wcn, vif);
} else {
wcn36xx_dbg(WCN36XX_DBG_MAC, "disassociated bss %pM vif %pM AID=%d\n",
bss_conf->bssid,
vif->addr,
vif->cfg.aid);
vif_priv->sta_assoc = false;
wcn36xx_smd_set_link_st(wcn,
bss_conf->bssid,
vif->addr,
WCN36XX_HAL_LINK_IDLE_STATE);
}
}
if (changed & BSS_CHANGED_AP_PROBE_RESP) {
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n");
skb = ieee80211_proberesp_get(hw, vif); if (!skb) {
wcn36xx_err("failed to alloc probereq skb\n"); goto out;
}
/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ staticint wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
u32 value)
{ struct wcn36xx *wcn = hw->priv;
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value);
/* * For STA mode HW will be configured on BSS_CHANGED_ASSOC because * at this stage AID is not available yet.
*/ if (NL80211_IFTYPE_STATION != vif->type) {
wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn));
sta_priv->aid = sta->aid;
wcn36xx_smd_config_sta(wcn, vif, sta);
}
vif = wcn36xx_get_first_assoc_vif(wcn); if (vif) {
ret = wcn36xx_smd_arp_offload(wcn, vif, true); if (ret) goto out;
ret = wcn36xx_smd_ipv6_ns_offload(wcn, vif, true); if (ret) goto out;
ret = wcn36xx_smd_gtk_offload(wcn, vif, true); if (ret) goto out;
ret = wcn36xx_smd_set_power_params(wcn, true); if (ret) goto out;
ret = wcn36xx_smd_wlan_host_suspend_ind(wcn);
}
/* Disable IRQ, we don't want to handle any packet before mac80211 is * resumed and ready to receive packets.
*/
disable_irq(wcn->tx_irq);
disable_irq(wcn->rx_irq);
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n",
action, tid);
mutex_lock(&wcn->conf_mutex);
switch (action) { case IEEE80211_AMPDU_RX_START:
sta_priv->tid = tid;
session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0,
get_sta_index(vif, sta_priv)); if (session < 0) {
ret = session; goto out;
}
wcn36xx_smd_add_ba(wcn, session); break; case IEEE80211_AMPDU_RX_STOP:
wcn36xx_smd_del_ba(wcn, tid, 0, get_sta_index(vif, sta_priv)); break; case IEEE80211_AMPDU_TX_START:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
spin_unlock_bh(&sta_priv->ampdu_lock);
/* Replace the mac80211 ssn with the firmware one */
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu ssn = %u\n", *ssn);
wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv), tid, ssn);
wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu fw-ssn = %u\n", *ssn);
/* Start BA session */
session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1,
get_sta_index(vif, sta_priv)); if (session < 0) {
ret = session; goto out;
}
ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; break; case IEEE80211_AMPDU_TX_OPERATIONAL:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL;
spin_unlock_bh(&sta_priv->ampdu_lock);
break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_STOP_CONT:
spin_lock_bh(&sta_priv->ampdu_lock);
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE;
spin_unlock_bh(&sta_priv->ampdu_lock);
/* Map the CCU memory */
index = of_property_match_string(mmio_node, "reg-names", "ccu");
wcn->ccu_base = of_iomap(mmio_node, index); if (!wcn->ccu_base) {
wcn36xx_err("failed to map ccu memory\n");
ret = -ENOMEM; goto put_mmio_node;
}
/* Map the DXE memory */
index = of_property_match_string(mmio_node, "reg-names", "dxe");
wcn->dxe_base = of_iomap(mmio_node, index); if (!wcn->dxe_base) {
wcn36xx_err("failed to map dxe memory\n");
ret = -ENOMEM; goto unmap_ccu;
}
/* External RF module */
iris_node = of_get_child_by_name(mmio_node, "iris"); if (iris_node) { if (of_device_is_compatible(iris_node, "qcom,wcn3620"))
wcn->rf_id = RF_IRIS_WCN3620; if (of_device_is_compatible(iris_node, "qcom,wcn3660") ||
of_device_is_compatible(iris_node, "qcom,wcn3660b"))
wcn->rf_id = RF_IRIS_WCN3660; if (of_device_is_compatible(iris_node, "qcom,wcn3680"))
wcn->rf_id = RF_IRIS_WCN3680;
of_node_put(iris_node);
}
ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32)); if (ret < 0) {
wcn36xx_err("failed to set DMA mask: %d\n", ret); goto out_wq;
}
wcn->nv_file = WLAN_NV_FILE;
ret = of_property_read_string(wcn->dev->parent->of_node, "firmware-name", &wcn->nv_file); if (ret < 0 && ret != -EINVAL) {
wcn36xx_err("failed to read \"firmware-name\" property: %d\n", ret); goto out_wq;
}
wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); if (IS_ERR(wcn->smd_channel)) {
wcn36xx_err("failed to open WLAN_CTRL channel\n");
ret = PTR_ERR(wcn->smd_channel); goto out_wq;
}
addr = of_get_property(pdev->dev.of_node, "local-mac-address", &ret); if (addr && ret != ETH_ALEN) {
wcn36xx_err("invalid local-mac-address\n");
ret = -EINVAL; goto out_destroy_ept;
} elseif (addr) {
wcn36xx_info("mac address: %pM\n", addr);
SET_IEEE80211_PERM_ADDR(wcn->hw, addr);
}
ret = wcn36xx_platform_get_resources(wcn, pdev); if (ret) goto out_destroy_ept;
wcn36xx_init_ieee80211(wcn);
ret = ieee80211_register_hw(wcn->hw); if (ret) goto out_unmap;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.