diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
24 files changed, 449 insertions, 263 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 9bb16bdf6d26..75e12f29d9eb 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -201,7 +201,9 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); /* tx */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb); int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, @@ -485,16 +487,13 @@ static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) } #ifdef CONFIG_IWLWIFI_DEBUGFS -int iwl_dbgfs_register(struct iwl_priv *priv, const char *name); -void iwl_dbgfs_unregister(struct iwl_priv *priv); +int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir); #else -static inline int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) +static inline int iwl_dbgfs_register(struct iwl_priv *priv, + struct dentry *dbgfs_dir) { return 0; } -static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) -{ -} #endif /* CONFIG_IWLWIFI_DEBUGFS */ #ifdef CONFIG_IWLWIFI_DEBUG diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 4a361c55c543..01128c96b5d8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -1055,8 +1055,9 @@ struct iwl_wep_cmd { #define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) -#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0x70 #define RX_RES_PHY_FLAGS_ANTENNA_POS 4 +#define RX_RES_PHY_FLAGS_AGG_MSK cpu_to_le16(1 << 7) #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) #define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 46782f1102ac..1a98fa3ab06d 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -124,6 +124,9 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, const struct fw_img *img; size_t bufsz; + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + /* default is to dump the entire data segment */ if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { priv->dbgfs_sram_offset = 0x800000; @@ -2349,24 +2352,19 @@ DEBUGFS_READ_WRITE_FILE_OPS(calib_disabled); * Create the debugfs files and directories * */ -int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) +int iwl_dbgfs_register(struct iwl_priv *priv, struct dentry *dbgfs_dir) { - struct dentry *phyd = priv->hw->wiphy->debugfsdir; - struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; - - dir_drv = debugfs_create_dir(name, phyd); - if (!dir_drv) - return -ENOMEM; + struct dentry *dir_data, *dir_rf, *dir_debug; - priv->debugfs_dir = dir_drv; + priv->debugfs_dir = dbgfs_dir; - dir_data = debugfs_create_dir("data", dir_drv); + dir_data = debugfs_create_dir("data", dbgfs_dir); if (!dir_data) goto err; - dir_rf = debugfs_create_dir("rf", dir_drv); + dir_rf = debugfs_create_dir("rf", dbgfs_dir); if (!dir_rf) goto err; - dir_debug = debugfs_create_dir("debug", dir_drv); + dir_debug = debugfs_create_dir("debug", dbgfs_dir); if (!dir_debug) goto err; @@ -2412,25 +2410,30 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) /* Calibrations disabled/enabled status*/ DEBUGFS_ADD_FILE(calib_disabled, dir_rf, S_IWUSR | S_IRUSR); - if (iwl_trans_dbgfs_register(priv->trans, dir_debug)) - goto err; + /* + * Create a symlink with mac80211. This is not very robust, as it does + * not remove the symlink created. The implicit assumption is that + * when the opmode exits, mac80211 will also exit, and will remove + * this symlink as part of its cleanup. + */ + if (priv->mac80211_registered) { + char buf[100]; + struct dentry *mac80211_dir, *dev_dir, *root_dir; + + dev_dir = dbgfs_dir->d_parent; + root_dir = dev_dir->d_parent; + mac80211_dir = priv->hw->wiphy->debugfsdir; + + snprintf(buf, 100, "../../%s/%s", root_dir->d_name.name, + dev_dir->d_name.name); + + if (!debugfs_create_symlink("iwlwifi", mac80211_dir, buf)) + goto err; + } + return 0; err: - IWL_ERR(priv, "Can't create the debugfs directory\n"); - iwl_dbgfs_unregister(priv); + IWL_ERR(priv, "failed to create the dvm debugfs entries\n"); return -ENOMEM; } - -/** - * Remove the debugfs files and directories - * - */ -void iwl_dbgfs_unregister(struct iwl_priv *priv) -{ - if (!priv->debugfs_dir) - return; - - debugfs_remove_recursive(priv->debugfs_dir); - priv->debugfs_dir = NULL; -} diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 054f728f6266..8141f91c3725 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -771,6 +771,7 @@ struct iwl_priv { u8 agg_tids_count; struct iwl_rx_phy_res last_phy_res; + u32 ampdu_ref; bool last_phy_res_valid; /* diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index a5f7bce96325..ff8162d4c454 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -195,7 +195,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, ARRAY_SIZE(iwlagn_iface_combinations_dualmode); } - hw->wiphy->max_remain_on_channel_duration = 1000; + hw->wiphy->max_remain_on_channel_duration = 500; hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | @@ -511,14 +511,16 @@ static void iwlagn_mac_set_wakeup(struct ieee80211_hw *hw, bool enabled) } #endif -static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void iwlagn_mac_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - if (iwlagn_tx_skb(priv, skb)) + if (iwlagn_tx_skb(priv, control->sta, skb)) dev_kfree_skb_any(skb); } diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 84d3db5aa506..7ff3f1430678 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -862,7 +862,8 @@ void iwl_down(struct iwl_priv *priv) * No race since we hold the mutex here and a new one * can't come in at this time. */ - ieee80211_remain_on_channel_expired(priv->hw); + if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT) + ieee80211_remain_on_channel_expired(priv->hw); exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); @@ -994,7 +995,11 @@ static void iwl_bg_restart(struct work_struct *data) iwlagn_prepare_restart(priv); mutex_unlock(&priv->mutex); iwl_cancel_deferred_work(priv); - ieee80211_restart_hw(priv->hw); + if (priv->mac80211_registered) + ieee80211_restart_hw(priv->hw); + else + IWL_ERR(priv, + "Cannot request restart before registrating with mac80211"); } else { WARN_ON(1); } @@ -1222,7 +1227,8 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const struct iwl_fw *fw) + const struct iwl_fw *fw, + struct dentry *dbgfs_dir) { struct iwl_priv *priv; struct ieee80211_hw *hw; @@ -1466,13 +1472,17 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, if (iwlagn_mac_setup_register(priv, &fw->ucode_capa)) goto out_destroy_workqueue; - if (iwl_dbgfs_register(priv, DRV_NAME)) - IWL_ERR(priv, - "failed to create debugfs files. Ignoring error\n"); + if (iwl_dbgfs_register(priv, dbgfs_dir)) + goto out_mac80211_unregister; return op_mode; +out_mac80211_unregister: + iwlagn_mac_unregister(priv); out_destroy_workqueue: + iwl_tt_exit(priv); + iwl_testmode_free(priv); + iwl_cancel_deferred_work(priv); destroy_workqueue(priv->workqueue); priv->workqueue = NULL; iwl_uninit_drv(priv); @@ -1493,8 +1503,6 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); - iwl_dbgfs_unregister(priv); - iwl_testmode_free(priv); iwlagn_mac_unregister(priv); diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index 6fddd2785e6e..a82f46c10f5e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -707,11 +707,14 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, */ static bool rs_use_green(struct ieee80211_sta *sta) { - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->ctx; - - return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && - !(ctx->ht.non_gf_sta_present); + /* + * There's a bug somewhere in this code that causes the + * scaling to get stuck because GF+SGI can't be combined + * in SISO rates. Until we find that bug, disable GF, it + * has only limited benefit and we still interoperate with + * GF APs since we can always receive GF transmissions. + */ + return false; } /** diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index fee5cffa1669..5a9c325804f6 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -667,6 +667,7 @@ static int iwlagn_rx_reply_rx_phy(struct iwl_priv *priv, struct iwl_rx_packet *pkt = rxb_addr(rxb); priv->last_phy_res_valid = true; + priv->ampdu_ref++; memcpy(&priv->last_phy_res, pkt->data, sizeof(struct iwl_rx_phy_res)); return 0; @@ -981,6 +982,16 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_AGG_MSK) { + /* + * We know which subframes of an A-MPDU belong + * together since we get a single PHY response + * from the firmware for all of them + */ + rx_status.flag |= RX_FLAG_AMPDU_DETAILS; + rx_status.ampdu_reference = priv->ampdu_ref; + } + /* Set up the HT phy flags */ if (rate_n_flags & RATE_MCS_HT_MSK) rx_status.flag |= RX_FLAG_HT; diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index e3467fa86899..bb9f6252d28f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -612,9 +612,9 @@ static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, return 0; frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - memcpy(frame->da, iwl_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->da); memcpy(frame->sa, ta, ETH_ALEN); - memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->bssid); frame->seq_ctrl = 0; len += 24; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index b29b798f7550..cd9b6de4273e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -128,10 +128,11 @@ int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_addsta_cmd *addsta = - (struct iwl_addsta_cmd *) cmd->payload; - return iwl_process_add_sta_resp(priv, addsta, pkt); + if (!cmd) + return 0; + + return iwl_process_add_sta_resp(priv, (void *)cmd->payload, pkt); } int iwl_send_add_sta(struct iwl_priv *priv, @@ -150,7 +151,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (!(flags & CMD_ASYNC)) { - cmd.flags |= CMD_WANT_SKB; + cmd.flags |= CMD_WANT_SKB | CMD_WANT_HCMD; might_sleep(); } diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 5971a23aa47d..f5ca73a89870 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -127,6 +127,7 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, struct iwl_tx_cmd *tx_cmd, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) { u32 rate_flags; @@ -187,8 +188,7 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) rate_idx = rate_lowest_index( - &priv->eeprom_data->bands[info->band], - info->control.sta); + &priv->eeprom_data->bands[info->band], sta); /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ if (info->band == IEEE80211_BAND_5GHZ) rate_idx += IWL_FIRST_OFDM_RATE; @@ -291,7 +291,9 @@ static int iwl_sta_id_or_broadcast(struct iwl_rxon_context *context, /* * start REPLY_TX command process */ -int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) +int iwlagn_tx_skb(struct iwl_priv *priv, + struct ieee80211_sta *sta, + struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -345,7 +347,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) sta_id = ctx->bcast_sta_id; else { /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast(ctx, info->control.sta); + sta_id = iwl_sta_id_or_broadcast(ctx, sta); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -355,8 +357,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); - if (info->control.sta) - sta_priv = (void *)info->control.sta->drv_priv; + if (sta) + sta_priv = (void *)sta->drv_priv; if (sta_priv && sta_priv->asleep && (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)) { @@ -397,7 +399,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* TODO need this for burst mode later on */ iwlagn_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); - iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, fc); + iwlagn_tx_cmd_build_rate(priv, tx_cmd, info, sta, fc); memset(&info->status, 0, sizeof(info->status)); @@ -431,7 +433,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * only. Check this here. */ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && - tid_data->agg.state != IWL_AGG_OFF, + tid_data->agg.state != IWL_AGG_OFF, "Tx while agg.state = %d", tid_data->agg.state)) goto drop_unlock_sta; diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 6d8d6dd7943f..2cb1efbc5ed1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -295,7 +295,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) static int iwl_verify_sec_sparse(struct iwl_priv *priv, const struct fw_desc *fw_desc) { - __le32 *image = (__le32 *)fw_desc->v_addr; + __le32 *image = (__le32 *)fw_desc->data; u32 len = fw_desc->len; u32 val; u32 i; @@ -319,7 +319,7 @@ static int iwl_verify_sec_sparse(struct iwl_priv *priv, static void iwl_print_mismatch_sec(struct iwl_priv *priv, const struct fw_desc *fw_desc) { - __le32 *image = (__le32 *)fw_desc->v_addr; + __le32 *image = (__le32 *)fw_desc->data; u32 len = fw_desc->len; u32 val; u32 offs; diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 06ca505bb2cc..59a5f78402fc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -29,6 +29,7 @@ #include <linux/tracepoint.h> #include <linux/device.h> +#include "iwl-trans.h" #if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) @@ -237,27 +238,34 @@ TRACE_EVENT(iwlwifi_dbg, #define TRACE_SYSTEM iwlwifi TRACE_EVENT(iwlwifi_dev_hcmd, - TP_PROTO(const struct device *dev, u32 flags, - const void *hcmd0, size_t len0, - const void *hcmd1, size_t len1, - const void *hcmd2, size_t len2), - TP_ARGS(dev, flags, hcmd0, len0, hcmd1, len1, hcmd2, len2), + TP_PROTO(const struct device *dev, + struct iwl_host_cmd *cmd, u16 total_size, + const void *hdr, size_t hdr_len), + TP_ARGS(dev, cmd, total_size, hdr, hdr_len), TP_STRUCT__entry( DEV_ENTRY - __dynamic_array(u8, hcmd0, len0) - __dynamic_array(u8, hcmd1, len1) - __dynamic_array(u8, hcmd2, len2) + __dynamic_array(u8, hcmd, total_size) __field(u32, flags) ), TP_fast_assign( + int i, offset = hdr_len; + DEV_ASSIGN; - memcpy(__get_dynamic_array(hcmd0), hcmd0, len0); - memcpy(__get_dynamic_array(hcmd1), hcmd1, len1); - memcpy(__get_dynamic_array(hcmd2), hcmd2, len2); - __entry->flags = flags; + __entry->flags = cmd->flags; + memcpy(__get_dynamic_array(hcmd), hdr, hdr_len); + + for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + if (!cmd->len[i]) + continue; + if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) + continue; + memcpy((u8 *)__get_dynamic_array(hcmd) + offset, + cmd->data[i], cmd->len[i]); + offset += cmd->len[i]; + } ), TP_printk("[%s] hcmd %#.2x (%ssync)", - __get_str(dev), ((u8 *)__get_dynamic_array(hcmd0))[0], + __get_str(dev), ((u8 *)__get_dynamic_array(hcmd))[0], __entry->flags & CMD_ASYNC ? "a" : "") ); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index cc41cfaedfbd..198634b75ed0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -64,6 +64,7 @@ #include <linux/dma-mapping.h> #include <linux/firmware.h> #include <linux/module.h> +#include <linux/vmalloc.h> #include "iwl-drv.h" #include "iwl-debug.h" @@ -101,6 +102,10 @@ MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); +#ifdef CONFIG_IWLWIFI_DEBUGFS +static struct dentry *iwl_dbgfs_root; +#endif + /** * struct iwl_drv - drv common data * @list: list of drv structures using this opmode @@ -126,6 +131,12 @@ struct iwl_drv { char firmware_name[25]; /* name of firmware file to load */ struct completion request_firmware_complete; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct dentry *dbgfs_drv; + struct dentry *dbgfs_trans; + struct dentry *dbgfs_op_mode; +#endif }; #define DVM_OP_MODE 0 @@ -154,10 +165,8 @@ struct fw_sec { static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) { - if (desc->v_addr) - dma_free_coherent(drv->trans->dev, desc->len, - desc->v_addr, desc->p_addr); - desc->v_addr = NULL; + vfree(desc->data); + desc->data = NULL; desc->len = 0; } @@ -176,25 +185,29 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) } static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, - struct fw_sec *sec) + struct fw_sec *sec) { - if (!sec || !sec->size) { - desc->v_addr = NULL; + void *data; + + desc->data = NULL; + + if (!sec || !sec->size) return -EINVAL; - } - desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size, - &desc->p_addr, GFP_KERNEL); - if (!desc->v_addr) + data = vmalloc(sec->size); + if (!data) return -ENOMEM; desc->len = sec->size; desc->offset = sec->offset; - memcpy(desc->v_addr, sec->data, sec->size); + memcpy(data, sec->data, desc->len); + desc->data = data; + return 0; } -static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context); +static void iwl_req_fw_callback(const struct firmware *ucode_raw, + void *context); #define UCODE_EXPERIMENTAL_INDEX 100 #define UCODE_EXPERIMENTAL_TAG "exp" @@ -231,7 +244,7 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name, drv->trans->dev, - GFP_KERNEL, drv, iwl_ucode_callback); + GFP_KERNEL, drv, iwl_req_fw_callback); } struct fw_img_parsing { @@ -759,13 +772,57 @@ static int validate_sec_sizes(struct iwl_drv *drv, return 0; } +static struct iwl_op_mode * +_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) +{ + const struct iwl_op_mode_ops *ops = op->ops; + struct dentry *dbgfs_dir = NULL; + struct iwl_op_mode *op_mode = NULL; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + drv->dbgfs_op_mode = debugfs_create_dir(op->name, + drv->dbgfs_drv); + if (!drv->dbgfs_op_mode) { + IWL_ERR(drv, + "failed to create opmode debugfs directory\n"); + return op_mode; + } + dbgfs_dir = drv->dbgfs_op_mode; +#endif + + op_mode = ops->start(drv->trans, drv->cfg, &drv->fw, dbgfs_dir); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (!op_mode) { + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; + } +#endif + + return op_mode; +} + +static void _iwl_op_mode_stop(struct iwl_drv *drv) +{ + /* op_mode can be NULL if its start failed */ + if (drv->op_mode) { + iwl_op_mode_stop(drv->op_mode); + drv->op_mode = NULL; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(drv->dbgfs_op_mode); + drv->dbgfs_op_mode = NULL; +#endif + } +} + /** - * iwl_ucode_callback - callback when firmware was loaded + * iwl_req_fw_callback - callback when firmware was loaded * * If loaded successfully, copies the firmware into buffers * for the card to fetch (via DMA). */ -static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) +static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) { struct iwl_drv *drv = context; struct iwl_fw *fw = &drv->fw; @@ -908,8 +965,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) list_add_tail(&drv->list, &op->drv); if (op->ops) { - const struct iwl_op_mode_ops *ops = op->ops; - drv->op_mode = ops->start(drv->trans, drv->cfg, &drv->fw); + drv->op_mode = _iwl_op_mode_start(drv, op); if (!drv->op_mode) { mutex_unlock(&iwlwifi_opmode_table_mtx); @@ -969,24 +1025,51 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans, init_completion(&drv->request_firmware_complete); INIT_LIST_HEAD(&drv->list); +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* Create the device debugfs entries. */ + drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), + iwl_dbgfs_root); + + if (!drv->dbgfs_drv) { + IWL_ERR(drv, "failed to create debugfs directory\n"); + goto err_free_drv; + } + + /* Create transport layer debugfs dir */ + drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); + + if (!drv->trans->dbgfs_dir) { + IWL_ERR(drv, "failed to create transport debugfs directory\n"); + goto err_free_dbgfs; + } +#endif + ret = iwl_request_firmware(drv, true); if (ret) { IWL_ERR(trans, "Couldn't request the fw\n"); - kfree(drv); - drv = NULL; + goto err_fw; } return drv; + +err_fw: +#ifdef CONFIG_IWLWIFI_DEBUGFS +err_free_dbgfs: + debugfs_remove_recursive(drv->dbgfs_drv); +err_free_drv: +#endif + kfree(drv); + drv = NULL; + + return drv; } void iwl_drv_stop(struct iwl_drv *drv) { wait_for_completion(&drv->request_firmware_complete); - /* op_mode can be NULL if its start failed */ - if (drv->op_mode) - iwl_op_mode_stop(drv->op_mode); + _iwl_op_mode_stop(drv); iwl_dealloc_ucode(drv); @@ -1000,6 +1083,10 @@ void iwl_drv_stop(struct iwl_drv *drv) list_del(&drv->list); mutex_unlock(&iwlwifi_opmode_table_mtx); +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(drv->dbgfs_drv); +#endif + kfree(drv); } @@ -1022,15 +1109,18 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) { int i; struct iwl_drv *drv; + struct iwlwifi_opmode_table *op; mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { - if (strcmp(iwlwifi_opmode_table[i].name, name)) + op = &iwlwifi_opmode_table[i]; + if (strcmp(op->name, name)) continue; - iwlwifi_opmode_table[i].ops = ops; - list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) - drv->op_mode = ops->start(drv->trans, drv->cfg, - &drv->fw); + op->ops = ops; + /* TODO: need to handle exceptional case */ + list_for_each_entry(drv, &op->drv, list) + drv->op_mode = _iwl_op_mode_start(drv, op); + mutex_unlock(&iwlwifi_opmode_table_mtx); return 0; } @@ -1051,12 +1141,9 @@ void iwl_opmode_deregister(const char *name) iwlwifi_opmode_table[i].ops = NULL; /* call the stop routine for all devices */ - list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) { - if (drv->op_mode) { - iwl_op_mode_stop(drv->op_mode); - drv->op_mode = NULL; - } - } + list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) + _iwl_op_mode_stop(drv); + mutex_unlock(&iwlwifi_opmode_table_mtx); return; } @@ -1076,6 +1163,14 @@ static int __init iwl_drv_init(void) pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); pr_info(DRV_COPYRIGHT "\n"); +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* Create the root of iwlwifi debugfs subsystem. */ + iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); + + if (!iwl_dbgfs_root) + return -EFAULT; +#endif + return iwl_pci_register_driver(); } module_init(iwl_drv_init); @@ -1083,6 +1178,10 @@ module_init(iwl_drv_init); static void __exit iwl_drv_exit(void) { iwl_pci_unregister_driver(); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove_recursive(iwl_dbgfs_root); +#endif } module_exit(iwl_drv_exit); diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 2cbf137b25bf..285de5f68c05 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -90,9 +90,9 @@ * 4) The bus specific component configures the bus * 5) The bus specific component calls to the drv bus agnostic part * (iwl_drv_start) - * 6) iwl_drv_start fetches the fw ASYNC, iwl_ucode_callback - * 7) iwl_ucode_callback parses the fw file - * 8) iwl_ucode_callback starts the wifi implementation to matches the fw + * 6) iwl_drv_start fetches the fw ASYNC, iwl_req_fw_callback + * 7) iwl_req_fw_callback parses the fw file + * 8) iwl_req_fw_callback starts the wifi implementation to matches the fw */ struct iwl_drv; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index 9c07c670a1ce..a5e425718f56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -85,8 +85,6 @@ struct iwl_eeprom_data { int n_hw_addrs; u8 hw_addr[ETH_ALEN]; - u16 radio_config; - u8 calib_version; __le16 calib_voltage; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 2153e4cc5572..d1a86b66bc51 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -124,8 +124,7 @@ struct iwl_ucode_capabilities { /* one for each uCode image (inst/data, init/runtime/wowlan) */ struct fw_desc { - dma_addr_t p_addr; /* hardware address */ - void *v_addr; /* software address */ + const void *data; /* vmalloc'ed data */ u32 len; /* size in bytes */ u32 offset; /* offset in the device */ }; diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 64886f95664f..c8d9b9517468 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -134,7 +134,8 @@ struct iwl_cfg; struct iwl_op_mode_ops { struct iwl_op_mode *(*start)(struct iwl_trans *trans, const struct iwl_cfg *cfg, - const struct iwl_fw *fw); + const struct iwl_fw *fw, + struct dentry *dbgfs_dir); void (*stop)(struct iwl_op_mode *op_mode); int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 92576a3e84ef..ff1154232885 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -184,14 +184,20 @@ struct iwl_rx_packet { * @CMD_SYNC: The caller will be stalled until the fw responds to the command * @CMD_ASYNC: Return right away and don't want for the response * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the - * response. + * response. The caller needs to call iwl_free_resp when done. + * @CMD_WANT_HCMD: The caller needs to get the HCMD that was sent in the + * response handler. Chunks flagged by %IWL_HCMD_DFL_NOCOPY won't be + * copied. The pointer passed to the response handler is in the transport + * ownership and don't need to be freed by the op_mode. This also means + * that the pointer is invalidated after the op_mode's handler returns. * @CMD_ON_DEMAND: This command is sent by the test mode pipe. */ enum CMD_MODE { CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), - CMD_ON_DEMAND = BIT(2), + CMD_WANT_HCMD = BIT(2), + CMD_ON_DEMAND = BIT(3), }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -460,6 +466,8 @@ struct iwl_trans { size_t dev_cmd_headroom; char dev_cmd_pool_name[50]; + struct dentry *dbgfs_dir; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index f4c3500b68c6..2a4675396707 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -263,8 +263,6 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 -#ifndef CONFIG_IWLWIFI_IDI - static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); @@ -282,8 +280,14 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!trans_pcie->drv) goto out_free_trans; + /* register transport layer debugfs here */ + if (iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir)) + goto out_free_drv; + return 0; +out_free_drv: + iwl_drv_stop(trans_pcie->drv); out_free_trans: iwl_trans_pcie_free(iwl_trans); pci_set_drvdata(pdev, NULL); @@ -301,8 +305,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } -#endif /* CONFIG_IWLWIFI_IDI */ - #ifdef CONFIG_PM_SLEEP static int iwl_pci_suspend(struct device *device) @@ -347,15 +349,6 @@ static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); #endif -#ifdef CONFIG_IWLWIFI_IDI -/* - * Defined externally in iwl-idi.c - */ -int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); -void __devexit iwl_pci_remove(struct pci_dev *pdev); - -#endif /* CONFIG_IWLWIFI_IDI */ - static struct pci_driver iwl_pci_driver = { .name = DRV_NAME, .id_table = iwl_hw_card_ids, diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index d9694c58208c..401178f44a3b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -184,6 +184,7 @@ struct iwl_queue { struct iwl_pcie_tx_queue_entry { struct iwl_device_cmd *cmd; + struct iwl_device_cmd *copy_cmd; struct sk_buff *skb; struct iwl_cmd_meta meta; }; @@ -310,7 +311,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); ******************************************************/ void iwl_bg_rx_replenish(struct work_struct *data); void iwl_irq_tasklet(struct iwl_trans *trans); -void iwlagn_rx_replenish(struct iwl_trans *trans); +void iwl_rx_replenish(struct iwl_trans *trans); void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, struct iwl_rx_queue *q); @@ -350,7 +351,7 @@ int iwl_queue_space(const struct iwl_queue *q); /***************************************************** * Error handling ******************************************************/ -int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display); +int iwl_dump_fh(struct iwl_trans *trans, char **buf); void iwl_dump_csr(struct iwl_trans *trans); /***************************************************** diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 39a6ca1f009c..17c8e5d82681 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -35,10 +35,6 @@ #include "internal.h" #include "iwl-op-mode.h" -#ifdef CONFIG_IWLWIFI_IDI -#include "iwl-amfh.h" -#endif - /****************************************************************************** * * RX path functions @@ -181,15 +177,15 @@ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, } /** - * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr */ -static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) +static inline __le32 iwl_dma_addr2rbd_ptr(dma_addr_t dma_addr) { return cpu_to_le32((u32)(dma_addr >> 8)); } /** - * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool + * iwl_rx_queue_restock - refill RX queue from pre-allocated pool * * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much @@ -199,7 +195,7 @@ static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) * also updates the memory address in the firmware to reference the new * target buffer. */ -static void iwlagn_rx_queue_restock(struct iwl_trans *trans) +static void iwl_rx_queue_restock(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; @@ -207,6 +203,17 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) struct iwl_rx_mem_buffer *rxb; unsigned long flags; + /* + * If the device isn't enabled - not need to try to add buffers... + * This can happen when we stop the device and still have an interrupt + * pending. We stop the APM before we sync the interrupts / tasklets + * because we have to (see comment there). On the other hand, since + * the APM is stopped, we cannot access the HW (in particular not prph). + * So don't try to restock if the APM has been already stopped. + */ + if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) + return; + spin_lock_irqsave(&rxq->lock, flags); while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { /* The overwritten rxb must be a used one */ @@ -219,7 +226,7 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma); + rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -230,7 +237,6 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) if (rxq->free_count <= RX_LOW_WATERMARK) schedule_work(&trans_pcie->rx_replenish); - /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { @@ -241,15 +247,16 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) } } -/** - * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free - * - * When moving to rx_free an SKB is allocated for the slot. +/* + * iwl_rx_allocate - allocate a page for each used RBD * - * Also restock the Rx queue via iwl_rx_queue_restock. - * This is called as a scheduled work item (except for during initialization) + * A used RBD is an Rx buffer that has been given to the stack. To use it again + * a page must be allocated and the RBD must point to the page. This function + * doesn't change the HW pointer but handles the list of pages that is used by + * iwl_rx_queue_restock. The latter function will update the HW to use the newly + * allocated buffers. */ -static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) +static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; @@ -328,23 +335,31 @@ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) } } -void iwlagn_rx_replenish(struct iwl_trans *trans) +/* + * iwl_rx_replenish - Move all used buffers from rx_used to rx_free + * + * When moving to rx_free an page is allocated for the slot. + * + * Also restock the Rx queue via iwl_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +void iwl_rx_replenish(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; - iwlagn_rx_allocate(trans, GFP_KERNEL); + iwl_rx_allocate(trans, GFP_KERNEL); spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } -static void iwlagn_rx_replenish_now(struct iwl_trans *trans) +static void iwl_rx_replenish_now(struct iwl_trans *trans) { - iwlagn_rx_allocate(trans, GFP_ATOMIC); + iwl_rx_allocate(trans, GFP_ATOMIC); - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); } void iwl_bg_rx_replenish(struct work_struct *data) @@ -352,7 +367,7 @@ void iwl_bg_rx_replenish(struct work_struct *data) struct iwl_trans_pcie *trans_pcie = container_of(data, struct iwl_trans_pcie, rx_replenish); - iwlagn_rx_replenish(trans_pcie->trans); + iwl_rx_replenish(trans_pcie->trans); } static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, @@ -421,13 +436,23 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, index = SEQ_TO_INDEX(sequence); cmd_index = get_cmd_index(&txq->q, index); - if (reclaim) - cmd = txq->entries[cmd_index].cmd; - else + if (reclaim) { + struct iwl_pcie_tx_queue_entry *ent; + ent = &txq->entries[cmd_index]; + cmd = ent->copy_cmd; + WARN_ON_ONCE(!cmd && ent->meta.flags & CMD_WANT_HCMD); + } else { cmd = NULL; + } err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); + if (reclaim) { + /* The original command isn't needed any more */ + kfree(txq->entries[cmd_index].copy_cmd); + txq->entries[cmd_index].copy_cmd = NULL; + } + /* * After here, we should always check rxcb._page_stolen, * if it is true then one of the handlers took the page. @@ -520,7 +545,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) count++; if (count >= 8) { rxq->read = i; - iwlagn_rx_replenish_now(trans); + iwl_rx_replenish_now(trans); count = 0; } } @@ -529,9 +554,9 @@ static void iwl_rx_handle(struct iwl_trans *trans) /* Backtrack one entry */ rxq->read = i; if (fill_rx) - iwlagn_rx_replenish_now(trans); + iwl_rx_replenish_now(trans); else - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); } /** @@ -555,7 +580,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) } iwl_dump_csr(trans); - iwl_dump_fh(trans, NULL, false); + iwl_dump_fh(trans, NULL); iwl_op_mode_nic_error(trans->op_mode); } @@ -713,11 +738,9 @@ void iwl_irq_tasklet(struct iwl_trans *trans) /* Disable periodic interrupt; we use it as just a one-shot. */ iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); -#ifdef CONFIG_IWLWIFI_IDI - iwl_amfh_rx_handler(); -#else + iwl_rx_handle(trans); -#endif + /* * Enable periodic interrupt in 8 msec only if we received * real RX interrupt (instead of just periodic int), to catch diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 939c2f78df58..fe0fffd04304 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -216,7 +216,7 @@ static int iwl_rx_init(struct iwl_trans *trans) rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); - iwlagn_rx_replenish(trans); + iwl_rx_replenish(trans); iwl_trans_rx_hw_init(trans, rxq); @@ -492,10 +492,11 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) iwl_tx_queue_unmap(trans, txq_id); /* De-alloc array of command/tx buffers */ - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < txq->q.n_window; i++) + for (i = 0; i < txq->q.n_window; i++) { kfree(txq->entries[i].cmd); + kfree(txq->entries[i].copy_cmd); + } /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) { @@ -675,13 +676,10 @@ static void iwl_set_pwr_vmain(struct iwl_trans *trans) static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int pos; u16 pci_lnk_ctl; - struct pci_dev *pci_dev = trans_pcie->pci_dev; - - pos = pci_pcie_cap(pci_dev); - pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); + pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, + &pci_lnk_ctl); return pci_lnk_ctl; } @@ -854,10 +852,8 @@ static int iwl_nic_init(struct iwl_trans *trans) iwl_op_mode_nic_config(trans->op_mode); -#ifndef CONFIG_IWLWIFI_IDI /* Allocate the RX queue, or reset if it is already allocated */ iwl_rx_init(trans); -#endif /* Allocate or reset and init all Tx and Command queues */ if (iwl_tx_init(trans)) @@ -896,6 +892,7 @@ static int iwl_set_hw_ready(struct iwl_trans *trans) static int iwl_prepare_card_hw(struct iwl_trans *trans) { int ret; + int t = 0; IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); @@ -908,30 +905,25 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans) iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); - ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG, - ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, - CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + do { + ret = iwl_set_hw_ready(trans); + if (ret >= 0) + return 0; - if (ret < 0) - return ret; + usleep_range(200, 1000); + t += 200; + } while (t < 150000); - /* HW should be ready by now, check again. */ - ret = iwl_set_hw_ready(trans); - if (ret >= 0) - return 0; return ret; } /* * ucode */ -static int iwl_load_section(struct iwl_trans *trans, u8 section_num, - const struct fw_desc *section) +static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, + dma_addr_t phy_addr, u32 byte_cnt) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - dma_addr_t phy_addr = section->p_addr; - u32 byte_cnt = section->len; - u32 dst_addr = section->offset; int ret; trans_pcie->ucode_write_complete = false; @@ -945,8 +937,8 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, dst_addr); iwl_write_direct32(trans, - FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), - phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), + phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); iwl_write_direct32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), @@ -965,33 +957,64 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", - section_num); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, trans_pcie->ucode_write_complete, 5 * HZ); if (!ret) { - IWL_ERR(trans, "Could not load the [%d] uCode section\n", - section_num); + IWL_ERR(trans, "Failed to load firmware chunk!\n"); return -ETIMEDOUT; } return 0; } -static int iwl_load_given_ucode(struct iwl_trans *trans, - const struct fw_img *image) +static int iwl_load_section(struct iwl_trans *trans, u8 section_num, + const struct fw_desc *section) { + u8 *v_addr; + dma_addr_t p_addr; + u32 offset; int ret = 0; - int i; - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { - if (!image->sec[i].p_addr) - break; + IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", + section_num); + + v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + + for (offset = 0; offset < section->len; offset += PAGE_SIZE) { + u32 copy_size; - ret = iwl_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; + copy_size = min_t(u32, PAGE_SIZE, section->len - offset); + + memcpy(v_addr, (u8 *)section->data + offset, copy_size); + ret = iwl_load_firmware_chunk(trans, section->offset + offset, + p_addr, copy_size); + if (ret) { + IWL_ERR(trans, + "Could not load the [%d] uCode section\n", + section_num); + break; } + } + + dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr); + return ret; +} + +static int iwl_load_given_ucode(struct iwl_trans *trans, + const struct fw_img *image) +{ + int i, ret = 0; + + for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { + if (!image->sec[i].data) + break; + + ret = iwl_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } /* Remove all resets to allow NIC to operate */ iwl_write32(trans, CSR_RESET, 0); @@ -1184,9 +1207,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) */ if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { iwl_trans_tx_stop(trans); -#ifndef CONFIG_IWLWIFI_IDI iwl_trans_rx_stop(trans); -#endif + /* Power-down device's busmaster DMA clocks */ iwl_write_prph(trans, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); @@ -1442,6 +1464,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) return err; err_free_irq: + trans_pcie->irq_requested = false; free_irq(trans_pcie->irq, trans); error: iwl_free_isr_ict(trans); @@ -1456,14 +1479,16 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, bool hw_rfkill; unsigned long flags; + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + iwl_apm_stop(trans); spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - if (!op_mode_leaving) { /* * Even if we stop the HW, we still want the RF kill @@ -1551,9 +1576,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_trans_pcie_tx_free(trans); -#ifndef CONFIG_IWLWIFI_IDI iwl_trans_pcie_rx_free(trans); -#endif + if (trans_pcie->irq_requested == true) { free_irq(trans_pcie->irq, trans); iwl_free_isr_ict(trans); @@ -1649,13 +1673,9 @@ static const char *get_fh_string(int cmd) #undef IWL_CMD } -int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) +int iwl_dump_fh(struct iwl_trans *trans, char **buf) { int i; -#ifdef CONFIG_IWLWIFI_DEBUG - int pos = 0; - size_t bufsz = 0; -#endif static const u32 fh_tbl[] = { FH_RSCSR_CHNL0_STTS_WPTR_REG, FH_RSCSR_CHNL0_RBDCB_BASE_REG, @@ -1667,29 +1687,35 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) FH_TSSR_TX_STATUS_REG, FH_TSSR_TX_ERROR_REG }; -#ifdef CONFIG_IWLWIFI_DEBUG - if (display) { - bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (buf) { + int pos = 0; + size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); if (!*buf) return -ENOMEM; + pos += scnprintf(*buf + pos, bufsz - pos, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) pos += scnprintf(*buf + pos, bufsz - pos, " %34s: 0X%08x\n", get_fh_string(fh_tbl[i]), iwl_read_direct32(trans, fh_tbl[i])); - } + return pos; } #endif + IWL_ERR(trans, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) IWL_ERR(trans, " %34s: 0X%08x\n", get_fh_string(fh_tbl[i]), iwl_read_direct32(trans, fh_tbl[i])); - } + return 0; } @@ -1769,7 +1795,7 @@ void iwl_dump_csr(struct iwl_trans *trans) #define DEBUGFS_ADD_FILE(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, trans, \ &iwl_dbgfs_##name##_ops)) \ - return -ENOMEM; \ + goto err; \ } while (0) /* file operation */ @@ -1982,11 +2008,11 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_trans *trans = file->private_data; - char *buf; + char *buf = NULL; int pos = 0; ssize_t ret = -EFAULT; - ret = pos = iwl_dump_fh(trans, &buf, true); + ret = pos = iwl_dump_fh(trans, &buf); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); @@ -2033,6 +2059,10 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); DEBUGFS_ADD_FILE(fw_restart, dir, S_IWUSR); return 0; + +err: + IWL_ERR(trans, "failed to create the trans debugfs entry\n"); + return -ENOMEM; } #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 6baf8deef519..105e3af3c621 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -521,12 +521,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) u16 copy_size, cmd_size; bool had_nocopy = false; int i; - u8 *cmd_dest; -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - const void *trace_bufs[IWL_MAX_CMD_TFDS + 1] = {}; - int trace_lens[IWL_MAX_CMD_TFDS + 1] = {}; - int trace_idx; -#endif + u32 cmd_pos; copy_size = sizeof(out_cmd->hdr); cmd_size = sizeof(out_cmd->hdr); @@ -584,15 +579,31 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) INDEX_TO_SEQ(q->write_ptr)); /* and copy the data that needs to be copied */ - - cmd_dest = out_cmd->payload; + cmd_pos = offsetof(struct iwl_device_cmd, payload); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) break; - memcpy(cmd_dest, cmd->data[i], cmd->len[i]); - cmd_dest += cmd->len[i]; + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); + cmd_pos += cmd->len[i]; + } + + WARN_ON_ONCE(txq->entries[idx].copy_cmd); + + /* + * since out_cmd will be the source address of the FH, it will write + * the retry count there. So when the user needs to receivce the HCMD + * that corresponds to the response in the response handler, it needs + * to set CMD_WANT_HCMD. + */ + if (cmd->flags & CMD_WANT_HCMD) { + txq->entries[idx].copy_cmd = + kmemdup(out_cmd, cmd_pos, GFP_ATOMIC); + if (unlikely(!txq->entries[idx].copy_cmd)) { + idx = -ENOMEM; + goto out; + } } IWL_DEBUG_HC(trans, @@ -612,11 +623,6 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) dma_unmap_len_set(out_meta, len, copy_size); iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_bufs[0] = &out_cmd->hdr; - trace_lens[0] = copy_size; - trace_idx = 1; -#endif for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) @@ -635,25 +641,14 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, cmd->len[i], 0); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_bufs[trace_idx] = cmd->data[i]; - trace_lens[trace_idx] = cmd->len[i]; - trace_idx++; -#endif } out_meta->flags = cmd->flags; txq->need_update = 1; - /* check that tracing gets all possible blocks */ - BUILD_BUG_ON(IWL_MAX_CMD_TFDS + 1 != 3); -#ifdef CONFIG_IWLWIFI_DEVICE_TRACING - trace_iwlwifi_dev_hcmd(trans->dev, cmd->flags, - trace_bufs[0], trace_lens[0], - trace_bufs[1], trace_lens[1], - trace_bufs[2], trace_lens[2]); -#endif + trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, + &out_cmd->hdr, copy_size); /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) |