Here's another set of iwlwifi updates. We continue some refactoring
and have a bunch of random updates all over.
I'll submit a pull request in a few days, but only after my you pull
my previous request, and maybe I'll combine more patches in too.
johannes
From: Ilan Peer <[email protected]>
The iwl-test flows were based on the cfg80211 testmode APIs.
To remove this coupling, the op mode (during the initialization
of the iwl_test object) is responsible to set the callbacks that
should be used by iwl-test to allocate skbs for events and replies
and to send events and replies.
The current op modes implement these callbacks based on the cfg80211
testmode APIs.
Reviewed-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/dvm/rx.c | 2 +-
drivers/net/wireless/iwlwifi/dvm/testmode.c | 31 +++++++-
drivers/net/wireless/iwlwifi/iwl-test.c | 109 +++++++++++++++++----------
drivers/net/wireless/iwlwifi/iwl-test.h | 44 ++++++++++-
4 files changed, 141 insertions(+), 45 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
index afdacb2..c1f7a18 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rx.c
@@ -1143,7 +1143,7 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
* Note that if the ownership flag != IWL_OWNERSHIP_TM the flow
* continues.
*/
- iwl_test_rx(&priv->tst, priv->hw, rxb);
+ iwl_test_rx(&priv->tst, rxb);
#endif
if (priv->ucode_owner != IWL_OWNERSHIP_TM) {
diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c
index aa9518f..57b918c 100644
--- a/drivers/net/wireless/iwlwifi/dvm/testmode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c
@@ -103,10 +103,39 @@ static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode)
return priv->fw->ucode_ver;
}
+static struct sk_buff*
+iwl_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ return cfg80211_testmode_alloc_reply_skb(priv->hw->wiphy, len);
+}
+
+static int iwl_testmode_reply(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+ return cfg80211_testmode_reply(skb);
+}
+
+static struct sk_buff *iwl_testmode_alloc_event(struct iwl_op_mode *op_mode,
+ int len)
+{
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ return cfg80211_testmode_alloc_event_skb(priv->hw->wiphy, len,
+ GFP_ATOMIC);
+}
+
+static void iwl_testmode_event(struct iwl_op_mode *op_mode, struct sk_buff *skb)
+{
+ return cfg80211_testmode_event(skb, GFP_ATOMIC);
+}
+
static struct iwl_test_ops tst_ops = {
.send_cmd = iwl_testmode_send_cmd,
.valid_hw_addr = iwl_testmode_valid_hw_addr,
.get_fw_ver = iwl_testmode_get_fw_ver,
+ .alloc_reply = iwl_testmode_alloc_reply,
+ .reply = iwl_testmode_reply,
+ .alloc_event = iwl_testmode_alloc_event,
+ .event = iwl_testmode_event,
};
void iwl_testmode_init(struct iwl_priv *priv)
@@ -380,7 +409,7 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
- result = iwl_test_handle_cmd(&priv->tst, hw, tb);
+ result = iwl_test_handle_cmd(&priv->tst, tb);
break;
case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c
index 76e1863..7a264ae 100644
--- a/drivers/net/wireless/iwlwifi/iwl-test.c
+++ b/drivers/net/wireless/iwlwifi/iwl-test.c
@@ -61,7 +61,9 @@
*
*****************************************************************************/
+#include <linux/export.h>
#include <net/netlink.h>
+
#include "iwl-io.h"
#include "iwl-fh.h"
#include "iwl-prph.h"
@@ -178,13 +180,51 @@ void iwl_test_free(struct iwl_test *tst)
}
EXPORT_SYMBOL_GPL(iwl_test_free);
+static inline int iwl_test_send_cmd(struct iwl_test *tst,
+ struct iwl_host_cmd *cmd)
+{
+ return tst->ops->send_cmd(tst->trans->op_mode, cmd);
+}
+
+static inline bool iwl_test_valid_hw_addr(struct iwl_test *tst, u32 addr)
+{
+ return tst->ops->valid_hw_addr(addr);
+}
+
+static inline u32 iwl_test_fw_ver(struct iwl_test *tst)
+{
+ return tst->ops->get_fw_ver(tst->trans->op_mode);
+}
+
+static inline struct sk_buff*
+iwl_test_alloc_reply(struct iwl_test *tst, int len)
+{
+ return tst->ops->alloc_reply(tst->trans->op_mode, len);
+}
+
+static inline int iwl_test_reply(struct iwl_test *tst, struct sk_buff *skb)
+{
+ return tst->ops->reply(tst->trans->op_mode, skb);
+}
+
+static inline struct sk_buff*
+iwl_test_alloc_event(struct iwl_test *tst, int len)
+{
+ return tst->ops->alloc_event(tst->trans->op_mode, len);
+}
+
+static inline void
+iwl_test_event(struct iwl_test *tst, struct sk_buff *skb)
+{
+ return tst->ops->event(tst->trans->op_mode, skb);
+}
+
/*
* This function handles the user application commands to the fw. The fw
* commands are sent in a synchronuous manner. In case that the user requested
* to get commands response, it is send to the user.
*/
-static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+static int iwl_test_fw_cmd(struct iwl_test *tst, struct nlattr **tb)
{
struct iwl_host_cmd cmd;
struct iwl_rx_packet *pkt;
@@ -214,7 +254,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n",
cmd.id, cmd.flags, cmd.len[0]);
- ret = tst->ops->send_cmd(tst->trans->op_mode, &cmd);
+ ret = iwl_test_send_cmd(tst, &cmd);
if (ret) {
IWL_ERR(tst->trans, "Failed to send hcmd\n");
return ret;
@@ -230,7 +270,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
}
reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20);
+ skb = iwl_test_alloc_reply(tst, reply_len + 20);
reply_buf = kmalloc(reply_len, GFP_KERNEL);
if (!skb || !reply_buf) {
kfree_skb(skb);
@@ -246,7 +286,7 @@ static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf))
goto nla_put_failure;
- return cfg80211_testmode_reply(skb);
+ return iwl_test_reply(tst, skb);
nla_put_failure:
IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n");
@@ -258,8 +298,7 @@ nla_put_failure:
/*
* Handles the user application commands for register access.
*/
-static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+static int iwl_test_reg(struct iwl_test *tst, struct nlattr **tb)
{
u32 ofs, val32, cmd;
u8 val8;
@@ -293,14 +332,14 @@ static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw,
val32 = iwl_read_direct32(tst->trans, ofs);
IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32);
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ skb = iwl_test_alloc_reply(tst, 20);
if (!skb) {
IWL_ERR(trans, "Memory allocation fail\n");
return -ENOMEM;
}
if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32))
goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
+ status = iwl_test_reply(tst, skb);
if (status < 0)
IWL_ERR(trans, "Error sending msg : %d\n", status);
break;
@@ -343,8 +382,7 @@ nla_put_failure:
* Handles the request to start FW tracing. Allocates of the trace buffer
* and sends a reply to user space with the address of the allocated buffer.
*/
-static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+static int iwl_test_trace_begin(struct iwl_test *tst, struct nlattr **tb)
{
struct sk_buff *skb;
int status = 0;
@@ -378,9 +416,7 @@ static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw,
memset(tst->trace.trace_addr, 0x03B, tst->trace.size);
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
- sizeof(tst->trace.dma_addr) + 20);
-
+ skb = iwl_test_alloc_reply(tst, sizeof(tst->trace.dma_addr) + 20);
if (!skb) {
IWL_ERR(tst->trans, "Memory allocation fail\n");
iwl_test_trace_stop(tst);
@@ -392,7 +428,7 @@ static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw,
(u64 *)&tst->trace.dma_addr))
goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
+ status = iwl_test_reply(tst, skb);
if (status < 0)
IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
@@ -485,7 +521,7 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
iwl_write_prph(trans, addr+i,
*(u32 *)(buf+i));
}
- } else if (tst->ops->valid_hw_addr(addr)) {
+ } else if (iwl_test_valid_hw_addr(tst, addr)) {
_iwl_write_targ_mem_words(trans, addr, buf, size/4);
} else {
return -EINVAL;
@@ -541,8 +577,7 @@ static int iwl_test_notifications(struct iwl_test *tst,
/*
* Handles the request to get the device id
*/
-static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+static int iwl_test_get_dev_id(struct iwl_test *tst, struct nlattr **tb)
{
u32 devid = tst->trans->hw_id;
struct sk_buff *skb;
@@ -550,7 +585,7 @@ static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw,
IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid);
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ skb = iwl_test_alloc_reply(tst, 20);
if (!skb) {
IWL_ERR(tst->trans, "Memory allocation fail\n");
return -ENOMEM;
@@ -558,7 +593,7 @@ static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw,
if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid))
goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
+ status = iwl_test_reply(tst, skb);
if (status < 0)
IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
@@ -572,16 +607,15 @@ nla_put_failure:
/*
* Handles the request to get the FW version
*/
-static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+static int iwl_test_get_fw_ver(struct iwl_test *tst, struct nlattr **tb)
{
struct sk_buff *skb;
int status;
- u32 ver = tst->ops->get_fw_ver(tst->trans->op_mode);
+ u32 ver = iwl_test_fw_ver(tst);
IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver);
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ skb = iwl_test_alloc_reply(tst, 20);
if (!skb) {
IWL_ERR(tst->trans, "Memory allocation fail\n");
return -ENOMEM;
@@ -590,7 +624,7 @@ static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw,
if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver))
goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
+ status = iwl_test_reply(tst, skb);
if (status < 0)
IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
@@ -630,27 +664,26 @@ EXPORT_SYMBOL_GPL(iwl_test_parse);
* Returns 1 for unknown commands (not handled by the test object); negative
* value in case of error.
*/
-int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb)
+int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb)
{
int result;
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_UCODE:
IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n");
- result = iwl_test_fw_cmd(tst, hw, tb);
+ result = iwl_test_fw_cmd(tst, tb);
break;
case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
IWL_DEBUG_INFO(tst->trans, "test cmd to register\n");
- result = iwl_test_reg(tst, hw, tb);
+ result = iwl_test_reg(tst, tb);
break;
case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n");
- result = iwl_test_trace_begin(tst, hw, tb);
+ result = iwl_test_trace_begin(tst, tb);
break;
case IWL_TM_CMD_APP2DEV_END_TRACE:
@@ -671,12 +704,12 @@ int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n");
- result = iwl_test_get_fw_ver(tst, hw, tb);
+ result = iwl_test_get_fw_ver(tst, tb);
break;
case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n");
- result = iwl_test_get_dev_id(tst, hw, tb);
+ result = iwl_test_get_dev_id(tst, tb);
break;
default:
@@ -779,7 +812,7 @@ EXPORT_SYMBOL_GPL(iwl_test_dump);
/*
* Multicast a spontaneous messages from the device to the user space.
*/
-static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
+static void iwl_test_send_rx(struct iwl_test *tst,
struct iwl_rx_cmd_buffer *rxb)
{
struct sk_buff *skb;
@@ -792,8 +825,7 @@ static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
/* the length doesn't include len_n_flags field, so add it manually */
length += sizeof(__le32);
- skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length,
- GFP_ATOMIC);
+ skb = iwl_test_alloc_event(tst, length + 20);
if (skb == NULL) {
IWL_ERR(tst->trans, "Out of memory for message to user\n");
return;
@@ -804,7 +836,7 @@ static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data))
goto nla_put_failure;
- cfg80211_testmode_event(skb, GFP_ATOMIC);
+ iwl_test_event(tst, skb);
return;
nla_put_failure:
@@ -816,10 +848,9 @@ nla_put_failure:
* Called whenever a Rx frames is recevied from the device. If notifications to
* the user space are requested, sends the frames to the user.
*/
-void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct iwl_rx_cmd_buffer *rxb)
+void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb)
{
if (tst->notify)
- iwl_test_send_rx(tst, hw, rxb);
+ iwl_test_send_rx(tst, rxb);
}
EXPORT_SYMBOL_GPL(iwl_test_rx);
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h
index 9946153..e13ffa8 100644
--- a/drivers/net/wireless/iwlwifi/iwl-test.h
+++ b/drivers/net/wireless/iwlwifi/iwl-test.h
@@ -84,11 +84,49 @@ struct iwl_test_mem {
bool in_read;
};
+/*
+ * struct iwl_test_ops: callback to the op mode
+ *
+ * The structure defines the callbacks that the op_mode should handle,
+ * inorder to handle logic that is out of the scope of iwl_test. The
+ * op_mode must set all the callbacks.
+
+ * @send_cmd: handler that is used by the test object to request the
+ * op_mode to send a command to the fw.
+ *
+ * @valid_hw_addr: handler that is used by the test object to request the
+ * op_mode to check if the given address is a valid address.
+ *
+ * @get_fw_ver: handler used to get the FW version.
+ *
+ * @alloc_reply: handler used by the test object to request the op_mode
+ * to allocate an skb for sending a reply to the user, and initialize
+ * the skb. It is assumed that the test object only fills the required
+ * attributes.
+ *
+ * @reply: handler used by the test object to request the op_mode to reply
+ * to a request. The skb is an skb previously allocated by the the
+ * alloc_reply callback.
+ I
+ * @alloc_event: handler used by the test object to request the op_mode
+ * to allocate an skb for sending an event, and initialize
+ * the skb. It is assumed that the test object only fills the required
+ * attributes.
+ *
+ * @reply: handler used by the test object to request the op_mode to send
+ * an event. The skb is an skb previously allocated by the the
+ * alloc_event callback.
+ */
struct iwl_test_ops {
int (*send_cmd)(struct iwl_op_mode *op_modes,
struct iwl_host_cmd *cmd);
bool (*valid_hw_addr)(u32 addr);
u32 (*get_fw_ver)(struct iwl_op_mode *op_mode);
+
+ struct sk_buff *(*alloc_reply)(struct iwl_op_mode *op_mode, int len);
+ int (*reply)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
+ struct sk_buff* (*alloc_event)(struct iwl_op_mode *op_mode, int len);
+ void (*event)(struct iwl_op_mode *op_mode, struct sk_buff *skb);
};
struct iwl_test {
@@ -107,14 +145,12 @@ void iwl_test_free(struct iwl_test *tst);
int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
void *data, int len);
-int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct nlattr **tb);
+int iwl_test_handle_cmd(struct iwl_test *tst, struct nlattr **tb);
int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
struct netlink_callback *cb);
-void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
- struct iwl_rx_cmd_buffer *rxb);
+void iwl_test_rx(struct iwl_test *tst, struct iwl_rx_cmd_buffer *rxb);
static inline void iwl_test_enable_notifications(struct iwl_test *tst,
bool enable)
--
1.7.10
John,
please pull the iwlwifi-next tree.
I've included the patches here, in slightly fixed up versions, a small
additional warning that we need to flag/debug some issues, and also of
course the fix for the EEPROM refactoring vs. addition of the separate
configuration structure for 6035 devices.
The patches I posted previously included a few fixes for code that went
into the tree in my last pull and more debugging utilities.
johannes
The following changes since commit a59f975a78f0606a3a364939dfa9b949d5f87521:
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless (2012-06-12 14:11:13 -0400)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next.git for-john
for you to fetch changes up to 7cd9174d02e2d6df3b6de25fea61ae1b7602d012:
iwlwifi: fix 6035 device parameters (2012-06-12 21:43:28 +0200)
----------------------------------------------------------------
Brandon Misemer (1):
iwlwifi: Fix Makefile build order for built-in driver
Dan Carpenter (1):
iwlwifi: turn on a lockdep assertion
Emmanuel Grumbach (9):
iwlwifi: s/iwl_read_targ_mem_words/iwl_read_targ_mem_bytes
iwlwifi: iwl_{read,write}_targ_mem_words takes dwords
iwlwifi: print more info when a queue is stuck
iwlwifi: don't configure a txq that is being disabled
iwlwifi: remove lock around txq_enable
iwlwifi: comment that setting driver_data overrides info->control
iwlwifi: print even more info when a queue is stuck
iwlwifi: don't modify the timer if we don't Tx
iwlwifi: warn if TFD index and WiFi Seq don't match
Ilan Peer (2):
iwlwifi: refactor testmode
iwlwifi: decouple testmode and iwl-test
Johannes Berg (3):
iwlwifi: fix dynamic loading
Merge remote-tracking branch 'wireless-next/master' into iwlwifi-next
iwlwifi: fix 6035 device parameters
drivers/net/wireless/iwlwifi/Makefile | 10 +-
drivers/net/wireless/iwlwifi/dvm/agn.h | 17 +-
drivers/net/wireless/iwlwifi/dvm/dev.h | 26 +-
drivers/net/wireless/iwlwifi/dvm/lib.c | 2 +-
drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +-
drivers/net/wireless/iwlwifi/dvm/main.c | 6 +-
drivers/net/wireless/iwlwifi/dvm/rx.c | 24 +-
drivers/net/wireless/iwlwifi/dvm/testmode.c | 778 ++----------------
drivers/net/wireless/iwlwifi/dvm/tx.c | 1 +
drivers/net/wireless/iwlwifi/iwl-drv.c | 15 +
drivers/net/wireless/iwlwifi/iwl-fh.h | 2 +
drivers/net/wireless/iwlwifi/iwl-io.c | 20 +-
drivers/net/wireless/iwlwifi/iwl-io.h | 14 +-
drivers/net/wireless/iwlwifi/iwl-test.c | 856 ++++++++++++++++++++
drivers/net/wireless/iwlwifi/iwl-test.h | 161 ++++
.../iwlwifi/{dvm/testmode.h => iwl-testmode.h} | 0
drivers/net/wireless/iwlwifi/pcie/6000.c | 1 +
drivers/net/wireless/iwlwifi/pcie/internal.h | 9 +-
drivers/net/wireless/iwlwifi/pcie/trans.c | 60 +-
drivers/net/wireless/iwlwifi/pcie/tx.c | 95 +--
20 files changed, 1243 insertions(+), 856 deletions(-)
create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.c
create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.h
rename drivers/net/wireless/iwlwifi/{dvm/testmode.h => iwl-testmode.h} (100%)
From: Brandon Misemer <[email protected]>
When the driver is built into the kernel instead of a module
when the system boots it results in a panic. The order things are built in
results in their initialization order when built into the kernel. Wifi
has to be initialized before mvm or dvm.
Reviewed-by: Donald H Fry <[email protected]>
Tested-by: Donald H Fry <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Brandon Misemer <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/Makefile | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index afa9758..170ec33 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -1,7 +1,3 @@
-obj-$(CONFIG_IWLDVM) += dvm/
-
-CFLAGS_iwl-devtrace.o := -I$(src)
-
# common
obj-$(CONFIG_IWLWIFI) += iwlwifi.o
iwlwifi-objs += iwl-io.o
@@ -16,3 +12,8 @@ iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
+
+
+obj-$(CONFIG_IWLDVM) += dvm/
+
+CFLAGS_iwl-devtrace.o := -I$(src)
--
1.7.10
From: Johannes Berg <[email protected]>
Add locking to the dynamic loading code to prevent
corrupting the list if multiple device ever init at
the same time (which cannot happen for multiple PCI
devices, but could happen when different busses init
concurrently.)
Also remove a device from the list when it stops so
the list isn't left corrupted.
Reviewed-by: Donald H Fry <[email protected]>
Tested-by: Donald H Fry <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-drv.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index cdfdfae..cc7fed0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -131,6 +131,8 @@ struct iwl_drv {
#define DVM_OP_MODE 0
#define MVM_OP_MODE 1
+/* Protects the table contents, i.e. the ops pointer & drv list */
+static struct mutex iwlwifi_opmode_table_mtx;
static struct iwlwifi_opmode_table {
const char *name; /* name: iwldvm, iwlmvm, etc */
const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */
@@ -899,6 +901,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
release_firmware(ucode_raw);
complete(&drv->request_firmware_complete);
+ mutex_lock(&iwlwifi_opmode_table_mtx);
op = &iwlwifi_opmode_table[DVM_OP_MODE];
/* add this device to the list of devices using this op_mode */
@@ -910,6 +913,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
} else {
request_module_nowait("%s", op->name);
}
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
return;
@@ -966,6 +970,9 @@ void iwl_drv_stop(struct iwl_drv *drv)
iwl_dealloc_ucode(drv);
+ mutex_lock(&iwlwifi_opmode_table_mtx);
+ list_del(&drv->list);
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
kfree(drv);
}
@@ -988,6 +995,7 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
int i;
struct iwl_drv *drv;
+ mutex_lock(&iwlwifi_opmode_table_mtx);
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
if (strcmp(iwlwifi_opmode_table[i].name, name))
continue;
@@ -995,8 +1003,10 @@ int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops)
list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list)
drv->op_mode = ops->start(drv->trans, drv->cfg,
&drv->fw);
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
return 0;
}
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
return -EIO;
}
EXPORT_SYMBOL_GPL(iwl_opmode_register);
@@ -1006,6 +1016,7 @@ void iwl_opmode_deregister(const char *name)
int i;
struct iwl_drv *drv;
+ mutex_lock(&iwlwifi_opmode_table_mtx);
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
if (strcmp(iwlwifi_opmode_table[i].name, name))
continue;
@@ -1018,8 +1029,10 @@ void iwl_opmode_deregister(const char *name)
drv->op_mode = NULL;
}
}
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
return;
}
+ mutex_unlock(&iwlwifi_opmode_table_mtx);
}
EXPORT_SYMBOL_GPL(iwl_opmode_deregister);
@@ -1027,6 +1040,8 @@ static int __init iwl_drv_init(void)
{
int i;
+ mutex_init(&iwlwifi_opmode_table_mtx);
+
for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++)
INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv);
--
1.7.10
From: Ilan Peer <[email protected]>
Create an object that will enacpsulate the testmode functionality
that is common to all op modes.
* Copy definitions from dvm/dev.h
* Copy the testmode logic from dvm/testmode.c
* Link iwl-test object into the iwlwifi module
* Modify DVM to use iwl-test object
Reviewed-by: Amit Beka <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Ilan Peer <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/Makefile | 1 +
drivers/net/wireless/iwlwifi/dvm/agn.h | 17 +-
drivers/net/wireless/iwlwifi/dvm/dev.h | 26 +-
drivers/net/wireless/iwlwifi/dvm/main.c | 2 +-
drivers/net/wireless/iwlwifi/dvm/rx.c | 24 +-
drivers/net/wireless/iwlwifi/dvm/testmode.c | 769 ++----------------
drivers/net/wireless/iwlwifi/iwl-test.c | 825 ++++++++++++++++++++
drivers/net/wireless/iwlwifi/iwl-test.h | 125 +++
.../iwlwifi/{dvm/testmode.h => iwl-testmode.h} | 0
9 files changed, 1027 insertions(+), 762 deletions(-)
create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.c
create mode 100644 drivers/net/wireless/iwlwifi/iwl-test.h
rename drivers/net/wireless/iwlwifi/{dvm/testmode.h => iwl-testmode.h} (100%)
diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile
index 98c8f64..afa9758 100644
--- a/drivers/net/wireless/iwlwifi/Makefile
+++ b/drivers/net/wireless/iwlwifi/Makefile
@@ -13,5 +13,6 @@ iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o
iwlwifi-objs += pcie/1000.o pcie/2000.o pcie/5000.o pcie/6000.o
iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o
+iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-test.o
ccflags-y += -D__CHECK_ENDIAN__ -I$(src)
diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h
index 2ae3608..6d10241 100644
--- a/drivers/net/wireless/iwlwifi/dvm/agn.h
+++ b/drivers/net/wireless/iwlwifi/dvm/agn.h
@@ -395,8 +395,10 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags)
}
extern int iwl_alive_start(struct iwl_priv *priv);
-/* svtool */
+
+/* testmode support */
#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
+
extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data,
int len);
extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw,
@@ -404,13 +406,16 @@ extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw,
struct netlink_callback *cb,
void *data, int len);
extern void iwl_testmode_init(struct iwl_priv *priv);
-extern void iwl_testmode_cleanup(struct iwl_priv *priv);
+extern void iwl_testmode_free(struct iwl_priv *priv);
+
#else
+
static inline
int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
{
return -ENOSYS;
}
+
static inline
int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
struct netlink_callback *cb,
@@ -418,12 +423,12 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
{
return -ENOSYS;
}
-static inline
-void iwl_testmode_init(struct iwl_priv *priv)
+
+static inline void iwl_testmode_init(struct iwl_priv *priv)
{
}
-static inline
-void iwl_testmode_cleanup(struct iwl_priv *priv)
+
+static inline void iwl_testmode_free(struct iwl_priv *priv)
{
}
#endif
diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h
index 89f2e10..4620b65 100644
--- a/drivers/net/wireless/iwlwifi/dvm/dev.h
+++ b/drivers/net/wireless/iwlwifi/dvm/dev.h
@@ -52,6 +52,8 @@
#include "rs.h"
#include "tt.h"
+#include "iwl-test.h"
+
/* CT-KILL constants */
#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */
#define CT_KILL_THRESHOLD 114 /* in Celsius */
@@ -596,24 +598,6 @@ struct iwl_lib_ops {
void (*temperature)(struct iwl_priv *priv);
};
-#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
-struct iwl_testmode_trace {
- u32 buff_size;
- u32 total_size;
- u32 num_chunks;
- u8 *cpu_addr;
- u8 *trace_addr;
- dma_addr_t dma_addr;
- bool trace_enabled;
-};
-struct iwl_testmode_mem {
- u32 buff_size;
- u32 num_chunks;
- u8 *buff_addr;
- bool read_in_progress;
-};
-#endif
-
struct iwl_wipan_noa_data {
struct rcu_head rcu_head;
u32 length;
@@ -670,8 +654,6 @@ struct iwl_priv {
enum ieee80211_band band;
u8 valid_contexts;
- void (*pre_rx_handler)(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb);
int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
@@ -895,9 +877,9 @@ struct iwl_priv {
struct led_classdev led;
unsigned long blink_on, blink_off;
bool led_registered;
+
#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
- struct iwl_testmode_trace testmode_trace;
- struct iwl_testmode_mem testmode_mem;
+ struct iwl_test tst;
u32 tm_fixed_rate;
#endif
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index 1c2d023..656ed31 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -1548,7 +1548,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
iwl_dbgfs_unregister(priv);
- iwl_testmode_cleanup(priv);
+ iwl_testmode_free(priv);
iwlagn_mac_unregister(priv);
iwl_tt_exit(priv);
diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c
index 0ed90bb8..afdacb2 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rx.c
@@ -1124,8 +1124,6 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
- void (*pre_rx_handler)(struct iwl_priv *,
- struct iwl_rx_cmd_buffer *);
int err = 0;
/*
@@ -1135,19 +1133,19 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
*/
iwl_notification_wait_notify(&priv->notif_wait, pkt);
- /* RX data may be forwarded to userspace (using pre_rx_handler) in one
- * of two cases: the first, that the user owns the uCode through
- * testmode - in such case the pre_rx_handler is set and no further
- * processing takes place. The other case is when the user want to
- * monitor the rx w/o affecting the regular flow - the pre_rx_handler
- * will be set but the ownership flag != IWL_OWNERSHIP_TM and the flow
+#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE
+ /*
+ * RX data may be forwarded to userspace in one
+ * of two cases: the user owns the fw through testmode or when
+ * the user requested to monitor the rx w/o affecting the regular flow.
+ * In these cases the iwl_test object will handle forwarding the rx
+ * data to user space.
+ * Note that if the ownership flag != IWL_OWNERSHIP_TM the flow
* continues.
- * We need to use ACCESS_ONCE to prevent a case where the handler
- * changes between the check and the call.
*/
- pre_rx_handler = ACCESS_ONCE(priv->pre_rx_handler);
- if (pre_rx_handler)
- pre_rx_handler(priv, rxb);
+ iwl_test_rx(&priv->tst, priv->hw, rxb);
+#endif
+
if (priv->ucode_owner != IWL_OWNERSHIP_TM) {
/* Based on type of command response or notification,
* handle those that need handling via function in
diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.c b/drivers/net/wireless/iwlwifi/dvm/testmode.c
index a7b5959..aa9518f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/testmode.c
+++ b/drivers/net/wireless/iwlwifi/dvm/testmode.c
@@ -60,6 +60,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*****************************************************************************/
+
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -69,355 +70,55 @@
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <net/netlink.h>
+
#include "iwl-debug.h"
-#include "iwl-io.h"
#include "iwl-trans.h"
-#include "iwl-fh.h"
-#include "iwl-prph.h"
#include "dev.h"
#include "agn.h"
-#include "testmode.h"
-
-
-/* Periphery registers absolute lower bound. This is used in order to
- * differentiate registery access through HBUS_TARG_PRPH_* and
- * HBUS_TARG_MEM_* accesses.
- */
-#define IWL_TM_ABS_PRPH_START (0xA00000)
-
-/* The TLVs used in the gnl message policy between the kernel module and
- * user space application. iwl_testmode_gnl_msg_policy is to be carried
- * through the NL80211_CMD_TESTMODE channel regulated by nl80211.
- * See testmode.h
- */
-static
-struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
- [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, },
-
- [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, },
- [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, },
-
- [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, },
- [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, },
- [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, },
-
- [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, },
+#include "iwl-test.h"
+#include "iwl-testmode.h"
- [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
-
- [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
- [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
-
- [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
-
- [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
-
- [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, },
- [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, },
-
- [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, },
- [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, },
- [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, },
-
- [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, },
-};
-
-/*
- * See the struct iwl_rx_packet in commands.h for the format of the
- * received events from the device
- */
-static inline int get_event_length(struct iwl_rx_cmd_buffer *rxb)
+static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode,
+ struct iwl_host_cmd *cmd)
{
- struct iwl_rx_packet *pkt = rxb_addr(rxb);
- if (pkt)
- return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- else
- return 0;
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ return iwl_dvm_send_cmd(priv, cmd);
}
-
-/*
- * This function multicasts the spontaneous messages from the device to the
- * user space. It is invoked whenever there is a received messages
- * from the device. This function is called within the ISR of the rx handlers
- * in iwlagn driver.
- *
- * The parsing of the message content is left to the user space application,
- * The message content is treated as unattacked raw data and is encapsulated
- * with IWL_TM_ATTR_UCODE_RX_PKT multicasting to the user space.
- *
- * @priv: the instance of iwlwifi device
- * @rxb: pointer to rx data content received by the ISR
- *
- * See the message policies and TLVs in iwl_testmode_gnl_msg_policy[].
- * For the messages multicasting to the user application, the mandatory
- * TLV fields are :
- * IWL_TM_ATTR_COMMAND must be IWL_TM_CMD_DEV2APP_UCODE_RX_PKT
- * IWL_TM_ATTR_UCODE_RX_PKT for carrying the message content
- */
-
-static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv,
- struct iwl_rx_cmd_buffer *rxb)
+static bool iwl_testmode_valid_hw_addr(u32 addr)
{
- struct ieee80211_hw *hw = priv->hw;
- struct sk_buff *skb;
- void *data;
- int length;
-
- data = (void *)rxb_addr(rxb);
- length = get_event_length(rxb);
+ if (iwlagn_hw_valid_rtc_data_addr(addr))
+ return true;
- if (!data || length == 0)
- return;
+ if (IWLAGN_RTC_INST_LOWER_BOUND <= addr &&
+ addr < IWLAGN_RTC_INST_UPPER_BOUND)
+ return true;
- skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length,
- GFP_ATOMIC);
- if (skb == NULL) {
- IWL_ERR(priv,
- "Run out of memory for messages to user space ?\n");
- return;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
- /* the length doesn't include len_n_flags field, so add it manually */
- nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length + sizeof(__le32), data))
- goto nla_put_failure;
- cfg80211_testmode_event(skb, GFP_ATOMIC);
- return;
-
-nla_put_failure:
- kfree_skb(skb);
- IWL_ERR(priv, "Ouch, overran buffer, check allocation!\n");
+ return false;
}
-void iwl_testmode_init(struct iwl_priv *priv)
+static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode)
{
- priv->pre_rx_handler = NULL;
- priv->testmode_trace.trace_enabled = false;
- priv->testmode_mem.read_in_progress = false;
-}
-
-static void iwl_mem_cleanup(struct iwl_priv *priv)
-{
- if (priv->testmode_mem.read_in_progress) {
- kfree(priv->testmode_mem.buff_addr);
- priv->testmode_mem.buff_addr = NULL;
- priv->testmode_mem.buff_size = 0;
- priv->testmode_mem.num_chunks = 0;
- priv->testmode_mem.read_in_progress = false;
- }
-}
-
-static void iwl_trace_cleanup(struct iwl_priv *priv)
-{
- if (priv->testmode_trace.trace_enabled) {
- if (priv->testmode_trace.cpu_addr &&
- priv->testmode_trace.dma_addr)
- dma_free_coherent(priv->trans->dev,
- priv->testmode_trace.total_size,
- priv->testmode_trace.cpu_addr,
- priv->testmode_trace.dma_addr);
- priv->testmode_trace.trace_enabled = false;
- priv->testmode_trace.cpu_addr = NULL;
- priv->testmode_trace.trace_addr = NULL;
- priv->testmode_trace.dma_addr = 0;
- priv->testmode_trace.buff_size = 0;
- priv->testmode_trace.total_size = 0;
- }
-}
-
-
-void iwl_testmode_cleanup(struct iwl_priv *priv)
-{
- iwl_trace_cleanup(priv);
- iwl_mem_cleanup(priv);
+ struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
+ return priv->fw->ucode_ver;
}
+static struct iwl_test_ops tst_ops = {
+ .send_cmd = iwl_testmode_send_cmd,
+ .valid_hw_addr = iwl_testmode_valid_hw_addr,
+ .get_fw_ver = iwl_testmode_get_fw_ver,
+};
-/*
- * This function handles the user application commands to the ucode.
- *
- * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_CMD_ID and
- * IWL_TM_ATTR_UCODE_CMD_DATA and calls to the handler to send the
- * host command to the ucode.
- *
- * If any mandatory field is missing, -ENOMSG is replied to the user space
- * application; otherwise, waits for the host command to be sent and checks
- * the return code. In case or error, it is returned, otherwise a reply is
- * allocated and the reply RX packet
- * is returned.
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
+void iwl_testmode_init(struct iwl_priv *priv)
{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_host_cmd cmd;
- struct iwl_rx_packet *pkt;
- struct sk_buff *skb;
- void *reply_buf;
- u32 reply_len;
- int ret;
- bool cmd_want_skb;
-
- memset(&cmd, 0, sizeof(struct iwl_host_cmd));
-
- if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] ||
- !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) {
- IWL_ERR(priv, "Missing ucode command mandatory fields\n");
- return -ENOMSG;
- }
-
- cmd.flags = CMD_ON_DEMAND | CMD_SYNC;
- cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]);
- if (cmd_want_skb)
- cmd.flags |= CMD_WANT_SKB;
-
- cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
- cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
- cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
- cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
- IWL_DEBUG_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x,"
- " len %d\n", cmd.id, cmd.flags, cmd.len[0]);
-
- ret = iwl_dvm_send_cmd(priv, &cmd);
- if (ret) {
- IWL_ERR(priv, "Failed to send hcmd\n");
- return ret;
- }
- if (!cmd_want_skb)
- return ret;
-
- /* Handling return of SKB to the user */
- pkt = cmd.resp_pkt;
- if (!pkt) {
- IWL_ERR(priv, "HCMD received a null response packet\n");
- return ret;
- }
-
- reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20);
- reply_buf = kmalloc(reply_len, GFP_KERNEL);
- if (!skb || !reply_buf) {
- kfree_skb(skb);
- kfree(reply_buf);
- return -ENOMEM;
- }
-
- /* The reply is in a page, that we cannot send to user space. */
- memcpy(reply_buf, &(pkt->hdr), reply_len);
- iwl_free_resp(&cmd);
-
- if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
- nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf))
- goto nla_put_failure;
- return cfg80211_testmode_reply(skb);
-
-nla_put_failure:
- IWL_DEBUG_INFO(priv, "Failed creating NL attributes\n");
- return -ENOMSG;
+ iwl_test_init(&priv->tst, priv->trans, &tst_ops);
}
-
-/*
- * This function handles the user application commands for register access.
- *
- * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
- * handlers respectively.
- *
- * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
- * mandatory fields(IWL_TM_ATTR_REG_OFFSET,IWL_TM_ATTR_REG_VALUE32,
- * IWL_TM_ATTR_REG_VALUE8) are missing; Otherwise 0 is replied indicating
- * the success of the command execution.
- *
- * If IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_READ32, the register read
- * value is returned with IWL_TM_ATTR_REG_VALUE32.
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
+void iwl_testmode_free(struct iwl_priv *priv)
{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- u32 ofs, val32, cmd;
- u8 val8;
- struct sk_buff *skb;
- int status = 0;
-
- if (!tb[IWL_TM_ATTR_REG_OFFSET]) {
- IWL_ERR(priv, "Missing register offset\n");
- return -ENOMSG;
- }
- ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]);
- IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs);
-
- /* Allow access only to FH/CSR/HBUS in direct mode.
- Since we don't have the upper bounds for the CSR and HBUS segments,
- we will use only the upper bound of FH for sanity check. */
- cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
- if ((cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 ||
- cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 ||
- cmd == IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8) &&
- (ofs >= FH_MEM_UPPER_BOUND)) {
- IWL_ERR(priv, "offset out of segment (0x0 - 0x%x)\n",
- FH_MEM_UPPER_BOUND);
- return -EINVAL;
- }
-
- switch (cmd) {
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
- val32 = iwl_read_direct32(priv->trans, ofs);
- IWL_INFO(priv, "32bit value to read 0x%x\n", val32);
-
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
- if (!skb) {
- IWL_ERR(priv, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32))
- goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
- if (status < 0)
- IWL_ERR(priv, "Error sending msg : %d\n", status);
- break;
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
- if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
- IWL_ERR(priv, "Missing value to write\n");
- return -ENOMSG;
- } else {
- val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
- IWL_INFO(priv, "32bit value to write 0x%x\n", val32);
- iwl_write_direct32(priv->trans, ofs, val32);
- }
- break;
- case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
- if (!tb[IWL_TM_ATTR_REG_VALUE8]) {
- IWL_ERR(priv, "Missing value to write\n");
- return -ENOMSG;
- } else {
- val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
- IWL_INFO(priv, "8bit value to write 0x%x\n", val8);
- iwl_write8(priv->trans, ofs, val8);
- }
- break;
- default:
- IWL_ERR(priv, "Unknown testmode register command ID\n");
- return -ENOSYS;
- }
-
- return status;
-
-nla_put_failure:
- kfree_skb(skb);
- return -EMSGSIZE;
+ iwl_test_free(&priv->tst);
}
-
static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
{
struct iwl_notification_wait calib_wait;
@@ -469,7 +170,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
struct sk_buff *skb;
unsigned char *rsp_data_ptr = NULL;
int status = 0, rsp_data_len = 0;
- u32 devid, inst_size = 0, data_size = 0;
+ u32 inst_size = 0, data_size = 0;
const struct fw_img *img;
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
@@ -563,39 +264,6 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
break;
- case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
- IWL_INFO(priv, "uCode version raw: 0x%x\n",
- priv->fw->ucode_ver);
-
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
- if (!skb) {
- IWL_ERR(priv, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION,
- priv->fw->ucode_ver))
- goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
- if (status < 0)
- IWL_ERR(priv, "Error sending msg : %d\n", status);
- break;
-
- case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
- devid = priv->trans->hw_id;
- IWL_INFO(priv, "hw version: 0x%x\n", devid);
-
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
- if (!skb) {
- IWL_ERR(priv, "Memory allocation fail\n");
- return -ENOMEM;
- }
- if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid))
- goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
- if (status < 0)
- IWL_ERR(priv, "Error sending msg : %d\n", status);
- break;
-
case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8);
if (!skb) {
@@ -630,125 +298,6 @@ nla_put_failure:
return -EMSGSIZE;
}
-
-/*
- * This function handles the user application commands for uCode trace
- *
- * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
- * handlers respectively.
- *
- * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned
- * value of the actual command execution is replied to the user application.
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct sk_buff *skb;
- int status = 0;
- struct device *dev = priv->trans->dev;
-
- switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
- case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
- if (priv->testmode_trace.trace_enabled)
- return -EBUSY;
-
- if (!tb[IWL_TM_ATTR_TRACE_SIZE])
- priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF;
- else
- priv->testmode_trace.buff_size =
- nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
- if (!priv->testmode_trace.buff_size)
- return -EINVAL;
- if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN ||
- priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX)
- return -EINVAL;
-
- priv->testmode_trace.total_size =
- priv->testmode_trace.buff_size + TRACE_BUFF_PADD;
- priv->testmode_trace.cpu_addr =
- dma_alloc_coherent(dev,
- priv->testmode_trace.total_size,
- &priv->testmode_trace.dma_addr,
- GFP_KERNEL);
- if (!priv->testmode_trace.cpu_addr)
- return -ENOMEM;
- priv->testmode_trace.trace_enabled = true;
- priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN(
- priv->testmode_trace.cpu_addr, 0x100);
- memset(priv->testmode_trace.trace_addr, 0x03B,
- priv->testmode_trace.buff_size);
- skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
- sizeof(priv->testmode_trace.dma_addr) + 20);
- if (!skb) {
- IWL_ERR(priv, "Memory allocation fail\n");
- iwl_trace_cleanup(priv);
- return -ENOMEM;
- }
- if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR,
- sizeof(priv->testmode_trace.dma_addr),
- (u64 *)&priv->testmode_trace.dma_addr))
- goto nla_put_failure;
- status = cfg80211_testmode_reply(skb);
- if (status < 0) {
- IWL_ERR(priv, "Error sending msg : %d\n", status);
- }
- priv->testmode_trace.num_chunks =
- DIV_ROUND_UP(priv->testmode_trace.buff_size,
- DUMP_CHUNK_SIZE);
- break;
-
- case IWL_TM_CMD_APP2DEV_END_TRACE:
- iwl_trace_cleanup(priv);
- break;
- default:
- IWL_ERR(priv, "Unknown testmode mem command ID\n");
- return -ENOSYS;
- }
- return status;
-
-nla_put_failure:
- kfree_skb(skb);
- if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) ==
- IWL_TM_CMD_APP2DEV_BEGIN_TRACE)
- iwl_trace_cleanup(priv);
- return -EMSGSIZE;
-}
-
-static int iwl_testmode_trace_dump(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct netlink_callback *cb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- int idx, length;
-
- if (priv->testmode_trace.trace_enabled &&
- priv->testmode_trace.trace_addr) {
- idx = cb->args[4];
- if (idx >= priv->testmode_trace.num_chunks)
- return -ENOENT;
- length = DUMP_CHUNK_SIZE;
- if (((idx + 1) == priv->testmode_trace.num_chunks) &&
- (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE))
- length = priv->testmode_trace.buff_size %
- DUMP_CHUNK_SIZE;
-
- if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length,
- priv->testmode_trace.trace_addr +
- (DUMP_CHUNK_SIZE * idx)))
- goto nla_put_failure;
- idx++;
- cb->args[4] = idx;
- return 0;
- } else
- return -EFAULT;
-
- nla_put_failure:
- return -ENOBUFS;
-}
-
/*
* This function handles the user application switch ucode ownership.
*
@@ -777,10 +326,10 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]);
if (owner == IWL_OWNERSHIP_DRIVER) {
priv->ucode_owner = owner;
- priv->pre_rx_handler = NULL;
+ iwl_test_enable_notifications(&priv->tst, false);
} else if (owner == IWL_OWNERSHIP_TM) {
- priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
priv->ucode_owner = owner;
+ iwl_test_enable_notifications(&priv->tst, true);
} else {
IWL_ERR(priv, "Invalid owner\n");
return -EINVAL;
@@ -788,180 +337,6 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
return 0;
}
-static int iwl_testmode_indirect_read(struct iwl_priv *priv, u32 addr, u32 size)
-{
- struct iwl_trans *trans = priv->trans;
- unsigned long flags;
- int i;
-
- if (size & 0x3)
- return -EINVAL;
- priv->testmode_mem.buff_size = size;
- priv->testmode_mem.buff_addr =
- kmalloc(priv->testmode_mem.buff_size, GFP_KERNEL);
- if (priv->testmode_mem.buff_addr == NULL)
- return -ENOMEM;
-
- /* Hard-coded periphery absolute address */
- if (IWL_TM_ABS_PRPH_START <= addr &&
- addr < IWL_TM_ABS_PRPH_START + PRPH_END) {
- spin_lock_irqsave(&trans->reg_lock, flags);
- iwl_grab_nic_access(trans);
- iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
- addr | (3 << 24));
- for (i = 0; i < size; i += 4)
- *(u32 *)(priv->testmode_mem.buff_addr + i) =
- iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
- iwl_release_nic_access(trans);
- spin_unlock_irqrestore(&trans->reg_lock, flags);
- } else { /* target memory (SRAM) */
- _iwl_read_targ_mem_words(trans, addr,
- priv->testmode_mem.buff_addr,
- priv->testmode_mem.buff_size / 4);
- }
-
- priv->testmode_mem.num_chunks =
- DIV_ROUND_UP(priv->testmode_mem.buff_size, DUMP_CHUNK_SIZE);
- priv->testmode_mem.read_in_progress = true;
- return 0;
-
-}
-
-static int iwl_testmode_indirect_write(struct iwl_priv *priv, u32 addr,
- u32 size, unsigned char *buf)
-{
- struct iwl_trans *trans = priv->trans;
- u32 val, i;
- unsigned long flags;
-
- if (IWL_TM_ABS_PRPH_START <= addr &&
- addr < IWL_TM_ABS_PRPH_START + PRPH_END) {
- /* Periphery writes can be 1-3 bytes long, or DWORDs */
- if (size < 4) {
- memcpy(&val, buf, size);
- spin_lock_irqsave(&trans->reg_lock, flags);
- iwl_grab_nic_access(trans);
- iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
- (addr & 0x0000FFFF) |
- ((size - 1) << 24));
- iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
- iwl_release_nic_access(trans);
- /* needed after consecutive writes w/o read */
- mmiowb();
- spin_unlock_irqrestore(&trans->reg_lock, flags);
- } else {
- if (size % 4)
- return -EINVAL;
- for (i = 0; i < size; i += 4)
- iwl_write_prph(trans, addr+i,
- *(u32 *)(buf+i));
- }
- } else if (iwlagn_hw_valid_rtc_data_addr(addr) ||
- (IWLAGN_RTC_INST_LOWER_BOUND <= addr &&
- addr < IWLAGN_RTC_INST_UPPER_BOUND)) {
- _iwl_write_targ_mem_words(trans, addr, buf, size/4);
- } else
- return -EINVAL;
- return 0;
-}
-
-/*
- * This function handles the user application commands for SRAM data dump
- *
- * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and
- * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading
- *
- * Several error will be retured, -EBUSY if the SRAM data retrieved by
- * previous command has not been delivered to userspace, or -ENOMSG if
- * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE)
- * are missing, or -ENOMEM if the buffer allocation fails.
- *
- * Otherwise 0 is replied indicating the success of the SRAM reading.
- *
- * @hw: ieee80211_hw object that represents the device
- * @tb: gnl message fields from the user space
- */
-static int iwl_testmode_indirect_mem(struct ieee80211_hw *hw,
- struct nlattr **tb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- u32 addr, size, cmd;
- unsigned char *buf;
-
- /* Both read and write should be blocked, for atomicity */
- if (priv->testmode_mem.read_in_progress)
- return -EBUSY;
-
- cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
- if (!tb[IWL_TM_ATTR_MEM_ADDR]) {
- IWL_ERR(priv, "Error finding memory offset address\n");
- return -ENOMSG;
- }
- addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]);
- if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) {
- IWL_ERR(priv, "Error finding size for memory reading\n");
- return -ENOMSG;
- }
- size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]);
-
- if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ)
- return iwl_testmode_indirect_read(priv, addr, size);
- else {
- if (!tb[IWL_TM_ATTR_BUFFER_DUMP])
- return -EINVAL;
- buf = (unsigned char *) nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]);
- return iwl_testmode_indirect_write(priv, addr, size, buf);
- }
-}
-
-static int iwl_testmode_buffer_dump(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct netlink_callback *cb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- int idx, length;
-
- if (priv->testmode_mem.read_in_progress) {
- idx = cb->args[4];
- if (idx >= priv->testmode_mem.num_chunks) {
- iwl_mem_cleanup(priv);
- return -ENOENT;
- }
- length = DUMP_CHUNK_SIZE;
- if (((idx + 1) == priv->testmode_mem.num_chunks) &&
- (priv->testmode_mem.buff_size % DUMP_CHUNK_SIZE))
- length = priv->testmode_mem.buff_size %
- DUMP_CHUNK_SIZE;
-
- if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length,
- priv->testmode_mem.buff_addr +
- (DUMP_CHUNK_SIZE * idx)))
- goto nla_put_failure;
- idx++;
- cb->args[4] = idx;
- return 0;
- } else
- return -EFAULT;
-
- nla_put_failure:
- return -ENOBUFS;
-}
-
-static int iwl_testmode_notifications(struct ieee80211_hw *hw,
- struct nlattr **tb)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- bool enable;
-
- enable = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]);
- if (enable)
- priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
- else
- priv->pre_rx_handler = NULL;
- return 0;
-}
-
-
/* The testmode gnl message handler that takes the gnl message from the
* user space and parses it per the policy iwl_testmode_gnl_msg_policy, then
* invoke the corresponding handlers.
@@ -987,32 +362,27 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
int result;
- result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
- iwl_testmode_gnl_msg_policy);
- if (result != 0) {
- IWL_ERR(priv, "Error parsing the gnl message : %d\n", result);
+ result = iwl_test_parse(&priv->tst, tb, data, len);
+ if (result)
return result;
- }
- /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
- if (!tb[IWL_TM_ATTR_COMMAND]) {
- IWL_ERR(priv, "Missing testmode command type\n");
- return -ENOMSG;
- }
/* in case multiple accesses to the device happens */
mutex_lock(&priv->mutex);
-
switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
case IWL_TM_CMD_APP2DEV_UCODE:
- IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n");
- result = iwl_testmode_ucode(hw, tb);
- break;
case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
- IWL_DEBUG_INFO(priv, "testmode cmd to register\n");
- result = iwl_testmode_reg(hw, tb);
+ case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
+ case IWL_TM_CMD_APP2DEV_END_TRACE:
+ case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
+ case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
+ case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
+ case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
+ case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
+ result = iwl_test_handle_cmd(&priv->tst, hw, tb);
break;
+
case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
@@ -1020,45 +390,25 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
case IWL_TM_CMD_APP2DEV_GET_EEPROM:
case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
- case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
- case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
IWL_DEBUG_INFO(priv, "testmode cmd to driver\n");
result = iwl_testmode_driver(hw, tb);
break;
- case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
- case IWL_TM_CMD_APP2DEV_END_TRACE:
- case IWL_TM_CMD_APP2DEV_READ_TRACE:
- IWL_DEBUG_INFO(priv, "testmode uCode trace cmd to driver\n");
- result = iwl_testmode_trace(hw, tb);
- break;
-
case IWL_TM_CMD_APP2DEV_OWNERSHIP:
IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n");
result = iwl_testmode_ownership(hw, tb);
break;
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
- IWL_DEBUG_INFO(priv, "testmode indirect memory cmd "
- "to driver\n");
- result = iwl_testmode_indirect_mem(hw, tb);
- break;
-
- case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
- IWL_DEBUG_INFO(priv, "testmode notifications cmd "
- "to driver\n");
- result = iwl_testmode_notifications(hw, tb);
- break;
-
default:
IWL_ERR(priv, "Unknown testmode command\n");
result = -ENOSYS;
break;
}
-
mutex_unlock(&priv->mutex);
+
+ if (result)
+ IWL_ERR(priv, "Test cmd failed result=%d\n", result);
return result;
}
@@ -1066,7 +416,6 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
struct netlink_callback *cb,
void *data, int len)
{
- struct nlattr *tb[IWL_TM_ATTR_MAX];
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
int result;
u32 cmd;
@@ -1075,39 +424,19 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
/* offset by 1 since commands start at 0 */
cmd = cb->args[3] - 1;
} else {
- result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
- iwl_testmode_gnl_msg_policy);
- if (result) {
- IWL_ERR(priv,
- "Error parsing the gnl message : %d\n", result);
+ struct nlattr *tb[IWL_TM_ATTR_MAX];
+
+ result = iwl_test_parse(&priv->tst, tb, data, len);
+ if (result)
return result;
- }
- /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
- if (!tb[IWL_TM_ATTR_COMMAND]) {
- IWL_ERR(priv, "Missing testmode command type\n");
- return -ENOMSG;
- }
cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
cb->args[3] = cmd + 1;
}
/* in case multiple accesses to the device happens */
mutex_lock(&priv->mutex);
- switch (cmd) {
- case IWL_TM_CMD_APP2DEV_READ_TRACE:
- IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n");
- result = iwl_testmode_trace_dump(hw, skb, cb);
- break;
- case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP:
- IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n");
- result = iwl_testmode_buffer_dump(hw, skb, cb);
- break;
- default:
- result = -EINVAL;
- break;
- }
-
+ result = iwl_test_dump(&priv->tst, cmd, skb, cb);
mutex_unlock(&priv->mutex);
return result;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c
new file mode 100644
index 0000000..76e1863
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-test.c
@@ -0,0 +1,825 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <[email protected]>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <net/netlink.h>
+#include "iwl-io.h"
+#include "iwl-fh.h"
+#include "iwl-prph.h"
+#include "iwl-trans.h"
+#include "iwl-test.h"
+#include "iwl-csr.h"
+#include "iwl-testmode.h"
+
+/*
+ * Periphery registers absolute lower bound. This is used in order to
+ * differentiate registery access through HBUS_TARG_PRPH_* and
+ * HBUS_TARG_MEM_* accesses.
+ */
+#define IWL_ABS_PRPH_START (0xA00000)
+
+/*
+ * The TLVs used in the gnl message policy between the kernel module and
+ * user space application. iwl_testmode_gnl_msg_policy is to be carried
+ * through the NL80211_CMD_TESTMODE channel regulated by nl80211.
+ * See iwl-testmode.h
+ */
+static
+struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
+ [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, },
+ [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, },
+
+ [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, },
+ [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, },
+ [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, },
+
+ [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
+
+ [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
+ [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
+ [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
+
+ [IWL_TM_ATTR_MEM_ADDR] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_BUFFER_SIZE] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_BUFFER_DUMP] = { .type = NLA_UNSPEC, },
+
+ [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_FW_TYPE] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_FW_INST_SIZE] = { .type = NLA_U32, },
+ [IWL_TM_ATTR_FW_DATA_SIZE] = { .type = NLA_U32, },
+
+ [IWL_TM_ATTR_ENABLE_NOTIFICATION] = {.type = NLA_FLAG, },
+};
+
+static inline void iwl_test_trace_clear(struct iwl_test *tst)
+{
+ memset(&tst->trace, 0, sizeof(struct iwl_test_trace));
+}
+
+static void iwl_test_trace_stop(struct iwl_test *tst)
+{
+ if (!tst->trace.enabled)
+ return;
+
+ if (tst->trace.cpu_addr && tst->trace.dma_addr)
+ dma_free_coherent(tst->trans->dev,
+ tst->trace.tsize,
+ tst->trace.cpu_addr,
+ tst->trace.dma_addr);
+
+ iwl_test_trace_clear(tst);
+}
+
+static inline void iwl_test_mem_clear(struct iwl_test *tst)
+{
+ memset(&tst->mem, 0, sizeof(struct iwl_test_mem));
+}
+
+static inline void iwl_test_mem_stop(struct iwl_test *tst)
+{
+ if (!tst->mem.in_read)
+ return;
+
+ iwl_test_mem_clear(tst);
+}
+
+/*
+ * Initializes the test object
+ * During the lifetime of the test object it is assumed that the transport is
+ * started. The test object should be stopped before the transport is stopped.
+ */
+void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans,
+ struct iwl_test_ops *ops)
+{
+ tst->trans = trans;
+ tst->ops = ops;
+
+ iwl_test_trace_clear(tst);
+ iwl_test_mem_clear(tst);
+}
+EXPORT_SYMBOL_GPL(iwl_test_init);
+
+/*
+ * Stop the test object
+ */
+void iwl_test_free(struct iwl_test *tst)
+{
+ iwl_test_mem_stop(tst);
+ iwl_test_trace_stop(tst);
+}
+EXPORT_SYMBOL_GPL(iwl_test_free);
+
+/*
+ * This function handles the user application commands to the fw. The fw
+ * commands are sent in a synchronuous manner. In case that the user requested
+ * to get commands response, it is send to the user.
+ */
+static int iwl_test_fw_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ struct iwl_host_cmd cmd;
+ struct iwl_rx_packet *pkt;
+ struct sk_buff *skb;
+ void *reply_buf;
+ u32 reply_len;
+ int ret;
+ bool cmd_want_skb;
+
+ memset(&cmd, 0, sizeof(struct iwl_host_cmd));
+
+ if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] ||
+ !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) {
+ IWL_ERR(tst->trans, "Missing fw command mandatory fields\n");
+ return -ENOMSG;
+ }
+
+ cmd.flags = CMD_ON_DEMAND | CMD_SYNC;
+ cmd_want_skb = nla_get_flag(tb[IWL_TM_ATTR_UCODE_CMD_SKB]);
+ if (cmd_want_skb)
+ cmd.flags |= CMD_WANT_SKB;
+
+ cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
+ cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
+ cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
+ cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
+ IWL_DEBUG_INFO(tst->trans, "test fw cmd=0x%x, flags 0x%x, len %d\n",
+ cmd.id, cmd.flags, cmd.len[0]);
+
+ ret = tst->ops->send_cmd(tst->trans->op_mode, &cmd);
+ if (ret) {
+ IWL_ERR(tst->trans, "Failed to send hcmd\n");
+ return ret;
+ }
+ if (!cmd_want_skb)
+ return ret;
+
+ /* Handling return of SKB to the user */
+ pkt = cmd.resp_pkt;
+ if (!pkt) {
+ IWL_ERR(tst->trans, "HCMD received a null response packet\n");
+ return ret;
+ }
+
+ reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, reply_len + 20);
+ reply_buf = kmalloc(reply_len, GFP_KERNEL);
+ if (!skb || !reply_buf) {
+ kfree_skb(skb);
+ kfree(reply_buf);
+ return -ENOMEM;
+ }
+
+ /* The reply is in a page, that we cannot send to user space. */
+ memcpy(reply_buf, &(pkt->hdr), reply_len);
+ iwl_free_resp(&cmd);
+
+ if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
+ IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
+ nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, reply_len, reply_buf))
+ goto nla_put_failure;
+ return cfg80211_testmode_reply(skb);
+
+nla_put_failure:
+ IWL_DEBUG_INFO(tst->trans, "Failed creating NL attributes\n");
+ kfree(reply_buf);
+ kfree_skb(skb);
+ return -ENOMSG;
+}
+
+/*
+ * Handles the user application commands for register access.
+ */
+static int iwl_test_reg(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ u32 ofs, val32, cmd;
+ u8 val8;
+ struct sk_buff *skb;
+ int status = 0;
+ struct iwl_trans *trans = tst->trans;
+
+ if (!tb[IWL_TM_ATTR_REG_OFFSET]) {
+ IWL_ERR(trans, "Missing reg offset\n");
+ return -ENOMSG;
+ }
+
+ ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]);
+ IWL_DEBUG_INFO(trans, "test reg access cmd offset=0x%x\n", ofs);
+
+ cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
+
+ /*
+ * Allow access only to FH/CSR/HBUS in direct mode.
+ * Since we don't have the upper bounds for the CSR and HBUS segments,
+ * we will use only the upper bound of FH for sanity check.
+ */
+ if (ofs >= FH_MEM_UPPER_BOUND) {
+ IWL_ERR(trans, "offset out of segment (0x0 - 0x%x)\n",
+ FH_MEM_UPPER_BOUND);
+ return -EINVAL;
+ }
+
+ switch (cmd) {
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
+ val32 = iwl_read_direct32(tst->trans, ofs);
+ IWL_DEBUG_INFO(trans, "32 value to read 0x%x\n", val32);
+
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ IWL_ERR(trans, "Memory allocation fail\n");
+ return -ENOMEM;
+ }
+ if (nla_put_u32(skb, IWL_TM_ATTR_REG_VALUE32, val32))
+ goto nla_put_failure;
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ IWL_ERR(trans, "Error sending msg : %d\n", status);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
+ if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
+ IWL_ERR(trans, "Missing value to write\n");
+ return -ENOMSG;
+ } else {
+ val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
+ IWL_DEBUG_INFO(trans, "32b write val=0x%x\n", val32);
+ iwl_write_direct32(tst->trans, ofs, val32);
+ }
+ break;
+
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
+ if (!tb[IWL_TM_ATTR_REG_VALUE8]) {
+ IWL_ERR(trans, "Missing value to write\n");
+ return -ENOMSG;
+ } else {
+ val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
+ IWL_DEBUG_INFO(trans, "8b write val=0x%x\n", val8);
+ iwl_write8(tst->trans, ofs, val8);
+ }
+ break;
+
+ default:
+ IWL_ERR(trans, "Unknown test register cmd ID\n");
+ return -ENOMSG;
+ }
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * Handles the request to start FW tracing. Allocates of the trace buffer
+ * and sends a reply to user space with the address of the allocated buffer.
+ */
+static int iwl_test_trace_begin(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ struct sk_buff *skb;
+ int status = 0;
+
+ if (tst->trace.enabled)
+ return -EBUSY;
+
+ if (!tb[IWL_TM_ATTR_TRACE_SIZE])
+ tst->trace.size = TRACE_BUFF_SIZE_DEF;
+ else
+ tst->trace.size =
+ nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
+
+ if (!tst->trace.size)
+ return -EINVAL;
+
+ if (tst->trace.size < TRACE_BUFF_SIZE_MIN ||
+ tst->trace.size > TRACE_BUFF_SIZE_MAX)
+ return -EINVAL;
+
+ tst->trace.tsize = tst->trace.size + TRACE_BUFF_PADD;
+ tst->trace.cpu_addr = dma_alloc_coherent(tst->trans->dev,
+ tst->trace.tsize,
+ &tst->trace.dma_addr,
+ GFP_KERNEL);
+ if (!tst->trace.cpu_addr)
+ return -ENOMEM;
+
+ tst->trace.enabled = true;
+ tst->trace.trace_addr = (u8 *)PTR_ALIGN(tst->trace.cpu_addr, 0x100);
+
+ memset(tst->trace.trace_addr, 0x03B, tst->trace.size);
+
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
+ sizeof(tst->trace.dma_addr) + 20);
+
+ if (!skb) {
+ IWL_ERR(tst->trans, "Memory allocation fail\n");
+ iwl_test_trace_stop(tst);
+ return -ENOMEM;
+ }
+
+ if (nla_put(skb, IWL_TM_ATTR_TRACE_ADDR,
+ sizeof(tst->trace.dma_addr),
+ (u64 *)&tst->trace.dma_addr))
+ goto nla_put_failure;
+
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
+
+ tst->trace.nchunks = DIV_ROUND_UP(tst->trace.size,
+ DUMP_CHUNK_SIZE);
+
+ return status;
+
+nla_put_failure:
+ kfree_skb(skb);
+ if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) ==
+ IWL_TM_CMD_APP2DEV_BEGIN_TRACE)
+ iwl_test_trace_stop(tst);
+ return -EMSGSIZE;
+}
+
+/*
+ * Handles indirect read from the periphery or the SRAM. The read is performed
+ * to a temporary buffer. The user space application should later issue a dump
+ */
+static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
+{
+ struct iwl_trans *trans = tst->trans;
+ unsigned long flags;
+ int i;
+
+ if (size & 0x3)
+ return -EINVAL;
+
+ tst->mem.size = size;
+ tst->mem.addr = kmalloc(tst->mem.size, GFP_KERNEL);
+ if (tst->mem.addr == NULL)
+ return -ENOMEM;
+
+ /* Hard-coded periphery absolute address */
+ if (IWL_ABS_PRPH_START <= addr &&
+ addr < IWL_ABS_PRPH_START + PRPH_END) {
+ spin_lock_irqsave(&trans->reg_lock, flags);
+ iwl_grab_nic_access(trans);
+ iwl_write32(trans, HBUS_TARG_PRPH_RADDR,
+ addr | (3 << 24));
+ for (i = 0; i < size; i += 4)
+ *(u32 *)(tst->mem.addr + i) =
+ iwl_read32(trans, HBUS_TARG_PRPH_RDAT);
+ iwl_release_nic_access(trans);
+ spin_unlock_irqrestore(&trans->reg_lock, flags);
+ } else { /* target memory (SRAM) */
+ _iwl_read_targ_mem_words(trans, addr,
+ tst->mem.addr,
+ tst->mem.size / 4);
+ }
+
+ tst->mem.nchunks =
+ DIV_ROUND_UP(tst->mem.size, DUMP_CHUNK_SIZE);
+ tst->mem.in_read = true;
+ return 0;
+
+}
+
+/*
+ * Handles indirect write to the periphery or SRAM. The is performed to a
+ * temporary buffer.
+ */
+static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
+ u32 size, unsigned char *buf)
+{
+ struct iwl_trans *trans = tst->trans;
+ u32 val, i;
+ unsigned long flags;
+
+ if (IWL_ABS_PRPH_START <= addr &&
+ addr < IWL_ABS_PRPH_START + PRPH_END) {
+ /* Periphery writes can be 1-3 bytes long, or DWORDs */
+ if (size < 4) {
+ memcpy(&val, buf, size);
+ spin_lock_irqsave(&trans->reg_lock, flags);
+ iwl_grab_nic_access(trans);
+ iwl_write32(trans, HBUS_TARG_PRPH_WADDR,
+ (addr & 0x0000FFFF) |
+ ((size - 1) << 24));
+ iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val);
+ iwl_release_nic_access(trans);
+ /* needed after consecutive writes w/o read */
+ mmiowb();
+ spin_unlock_irqrestore(&trans->reg_lock, flags);
+ } else {
+ if (size % 4)
+ return -EINVAL;
+ for (i = 0; i < size; i += 4)
+ iwl_write_prph(trans, addr+i,
+ *(u32 *)(buf+i));
+ }
+ } else if (tst->ops->valid_hw_addr(addr)) {
+ _iwl_write_targ_mem_words(trans, addr, buf, size/4);
+ } else {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Handles the user application commands for indirect read/write
+ * to/from the periphery or the SRAM.
+ */
+static int iwl_test_indirect_mem(struct iwl_test *tst, struct nlattr **tb)
+{
+ u32 addr, size, cmd;
+ unsigned char *buf;
+
+ /* Both read and write should be blocked, for atomicity */
+ if (tst->mem.in_read)
+ return -EBUSY;
+
+ cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
+ if (!tb[IWL_TM_ATTR_MEM_ADDR]) {
+ IWL_ERR(tst->trans, "Error finding memory offset address\n");
+ return -ENOMSG;
+ }
+ addr = nla_get_u32(tb[IWL_TM_ATTR_MEM_ADDR]);
+ if (!tb[IWL_TM_ATTR_BUFFER_SIZE]) {
+ IWL_ERR(tst->trans, "Error finding size for memory reading\n");
+ return -ENOMSG;
+ }
+ size = nla_get_u32(tb[IWL_TM_ATTR_BUFFER_SIZE]);
+
+ if (cmd == IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ) {
+ return iwl_test_indirect_read(tst, addr, size);
+ } else {
+ if (!tb[IWL_TM_ATTR_BUFFER_DUMP])
+ return -EINVAL;
+ buf = (unsigned char *)nla_data(tb[IWL_TM_ATTR_BUFFER_DUMP]);
+ return iwl_test_indirect_write(tst, addr, size, buf);
+ }
+}
+
+/*
+ * Enable notifications to user space
+ */
+static int iwl_test_notifications(struct iwl_test *tst,
+ struct nlattr **tb)
+{
+ tst->notify = nla_get_flag(tb[IWL_TM_ATTR_ENABLE_NOTIFICATION]);
+ return 0;
+}
+
+/*
+ * Handles the request to get the device id
+ */
+static int iwl_test_get_dev_id(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ u32 devid = tst->trans->hw_id;
+ struct sk_buff *skb;
+ int status;
+
+ IWL_DEBUG_INFO(tst->trans, "hw version: 0x%x\n", devid);
+
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ IWL_ERR(tst->trans, "Memory allocation fail\n");
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(skb, IWL_TM_ATTR_DEVICE_ID, devid))
+ goto nla_put_failure;
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
+
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * Handles the request to get the FW version
+ */
+static int iwl_test_get_fw_ver(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ struct sk_buff *skb;
+ int status;
+ u32 ver = tst->ops->get_fw_ver(tst->trans->op_mode);
+
+ IWL_DEBUG_INFO(tst->trans, "uCode version raw: 0x%x\n", ver);
+
+ skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+ if (!skb) {
+ IWL_ERR(tst->trans, "Memory allocation fail\n");
+ return -ENOMEM;
+ }
+
+ if (nla_put_u32(skb, IWL_TM_ATTR_FW_VERSION, ver))
+ goto nla_put_failure;
+
+ status = cfg80211_testmode_reply(skb);
+ if (status < 0)
+ IWL_ERR(tst->trans, "Error sending msg : %d\n", status);
+
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb);
+ return -EMSGSIZE;
+}
+
+/*
+ * Parse the netlink message and validate that the IWL_TM_ATTR_CMD exists
+ */
+int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
+ void *data, int len)
+{
+ int result;
+
+ result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
+ iwl_testmode_gnl_msg_policy);
+ if (result) {
+ IWL_ERR(tst->trans, "Fail parse gnl msg: %d\n", result);
+ return result;
+ }
+
+ /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
+ if (!tb[IWL_TM_ATTR_COMMAND]) {
+ IWL_ERR(tst->trans, "Missing testmode command type\n");
+ return -ENOMSG;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iwl_test_parse);
+
+/*
+ * Handle test commands.
+ * Returns 1 for unknown commands (not handled by the test object); negative
+ * value in case of error.
+ */
+int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb)
+{
+ int result;
+
+ switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
+ case IWL_TM_CMD_APP2DEV_UCODE:
+ IWL_DEBUG_INFO(tst->trans, "test cmd to uCode\n");
+ result = iwl_test_fw_cmd(tst, hw, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
+ case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
+ IWL_DEBUG_INFO(tst->trans, "test cmd to register\n");
+ result = iwl_test_reg(tst, hw, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
+ IWL_DEBUG_INFO(tst->trans, "test uCode trace cmd to driver\n");
+ result = iwl_test_trace_begin(tst, hw, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_END_TRACE:
+ iwl_test_trace_stop(tst);
+ result = 0;
+ break;
+
+ case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ:
+ case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE:
+ IWL_DEBUG_INFO(tst->trans, "test indirect memory cmd\n");
+ result = iwl_test_indirect_mem(tst, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
+ IWL_DEBUG_INFO(tst->trans, "test notifications cmd\n");
+ result = iwl_test_notifications(tst, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
+ IWL_DEBUG_INFO(tst->trans, "test get FW ver cmd\n");
+ result = iwl_test_get_fw_ver(tst, hw, tb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID:
+ IWL_DEBUG_INFO(tst->trans, "test Get device ID cmd\n");
+ result = iwl_test_get_dev_id(tst, hw, tb);
+ break;
+
+ default:
+ IWL_DEBUG_INFO(tst->trans, "Unknown test command\n");
+ result = 1;
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(iwl_test_handle_cmd);
+
+static int iwl_test_trace_dump(struct iwl_test *tst, struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx, length;
+
+ if (!tst->trace.enabled || !tst->trace.trace_addr)
+ return -EFAULT;
+
+ idx = cb->args[4];
+ if (idx >= tst->trace.nchunks)
+ return -ENOENT;
+
+ length = DUMP_CHUNK_SIZE;
+ if (((idx + 1) == tst->trace.nchunks) &&
+ (tst->trace.size % DUMP_CHUNK_SIZE))
+ length = tst->trace.size %
+ DUMP_CHUNK_SIZE;
+
+ if (nla_put(skb, IWL_TM_ATTR_TRACE_DUMP, length,
+ tst->trace.trace_addr + (DUMP_CHUNK_SIZE * idx)))
+ goto nla_put_failure;
+
+ cb->args[4] = ++idx;
+ return 0;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+static int iwl_test_buffer_dump(struct iwl_test *tst, struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx, length;
+
+ if (!tst->mem.in_read)
+ return -EFAULT;
+
+ idx = cb->args[4];
+ if (idx >= tst->mem.nchunks) {
+ iwl_test_mem_stop(tst);
+ return -ENOENT;
+ }
+
+ length = DUMP_CHUNK_SIZE;
+ if (((idx + 1) == tst->mem.nchunks) &&
+ (tst->mem.size % DUMP_CHUNK_SIZE))
+ length = tst->mem.size % DUMP_CHUNK_SIZE;
+
+ if (nla_put(skb, IWL_TM_ATTR_BUFFER_DUMP, length,
+ tst->mem.addr + (DUMP_CHUNK_SIZE * idx)))
+ goto nla_put_failure;
+
+ cb->args[4] = ++idx;
+ return 0;
+
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+/*
+ * Handle dump commands.
+ * Returns 1 for unknown commands (not handled by the test object); negative
+ * value in case of error.
+ */
+int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int result;
+
+ switch (cmd) {
+ case IWL_TM_CMD_APP2DEV_READ_TRACE:
+ IWL_DEBUG_INFO(tst->trans, "uCode trace cmd\n");
+ result = iwl_test_trace_dump(tst, skb, cb);
+ break;
+
+ case IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP:
+ IWL_DEBUG_INFO(tst->trans, "testmode sram dump cmd\n");
+ result = iwl_test_buffer_dump(tst, skb, cb);
+ break;
+
+ default:
+ result = 1;
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(iwl_test_dump);
+
+/*
+ * Multicast a spontaneous messages from the device to the user space.
+ */
+static void iwl_test_send_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ struct sk_buff *skb;
+ struct iwl_rx_packet *data;
+ int length;
+
+ data = rxb_addr(rxb);
+ length = le32_to_cpu(data->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
+
+ /* the length doesn't include len_n_flags field, so add it manually */
+ length += sizeof(__le32);
+
+ skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length,
+ GFP_ATOMIC);
+ if (skb == NULL) {
+ IWL_ERR(tst->trans, "Out of memory for message to user\n");
+ return;
+ }
+
+ if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
+ IWL_TM_CMD_DEV2APP_UCODE_RX_PKT) ||
+ nla_put(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data))
+ goto nla_put_failure;
+
+ cfg80211_testmode_event(skb, GFP_ATOMIC);
+ return;
+
+nla_put_failure:
+ kfree_skb(skb);
+ IWL_ERR(tst->trans, "Ouch, overran buffer, check allocation!\n");
+}
+
+/*
+ * Called whenever a Rx frames is recevied from the device. If notifications to
+ * the user space are requested, sends the frames to the user.
+ */
+void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct iwl_rx_cmd_buffer *rxb)
+{
+ if (tst->notify)
+ iwl_test_send_rx(tst, hw, rxb);
+}
+EXPORT_SYMBOL_GPL(iwl_test_rx);
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.h b/drivers/net/wireless/iwlwifi/iwl-test.h
new file mode 100644
index 0000000..9946153
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/iwl-test.h
@@ -0,0 +1,125 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ * Intel Linux Wireless <[email protected]>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010 - 2012 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#ifndef __IWL_TEST_H__
+#define __IWL_TEST_H__
+
+#include <linux/types.h>
+#include "iwl-trans.h"
+
+struct iwl_test_trace {
+ u32 size;
+ u32 tsize;
+ u32 nchunks;
+ u8 *cpu_addr;
+ u8 *trace_addr;
+ dma_addr_t dma_addr;
+ bool enabled;
+};
+
+struct iwl_test_mem {
+ u32 size;
+ u32 nchunks;
+ u8 *addr;
+ bool in_read;
+};
+
+struct iwl_test_ops {
+ int (*send_cmd)(struct iwl_op_mode *op_modes,
+ struct iwl_host_cmd *cmd);
+ bool (*valid_hw_addr)(u32 addr);
+ u32 (*get_fw_ver)(struct iwl_op_mode *op_mode);
+};
+
+struct iwl_test {
+ struct iwl_trans *trans;
+ struct iwl_test_ops *ops;
+ struct iwl_test_trace trace;
+ struct iwl_test_mem mem;
+ bool notify;
+};
+
+void iwl_test_init(struct iwl_test *tst, struct iwl_trans *trans,
+ struct iwl_test_ops *ops);
+
+void iwl_test_free(struct iwl_test *tst);
+
+int iwl_test_parse(struct iwl_test *tst, struct nlattr **tb,
+ void *data, int len);
+
+int iwl_test_handle_cmd(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct nlattr **tb);
+
+int iwl_test_dump(struct iwl_test *tst, u32 cmd, struct sk_buff *skb,
+ struct netlink_callback *cb);
+
+void iwl_test_rx(struct iwl_test *tst, struct ieee80211_hw *hw,
+ struct iwl_rx_cmd_buffer *rxb);
+
+static inline void iwl_test_enable_notifications(struct iwl_test *tst,
+ bool enable)
+{
+ tst->notify = enable;
+}
+
+#endif
diff --git a/drivers/net/wireless/iwlwifi/dvm/testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h
similarity index 100%
rename from drivers/net/wireless/iwlwifi/dvm/testmode.h
rename to drivers/net/wireless/iwlwifi/iwl-testmode.h
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
This macro gets the bufsize in bytes.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/dvm/main.c | 4 ++--
drivers/net/wireless/iwlwifi/iwl-io.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c
index 656ed31..e620af3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/main.c
+++ b/drivers/net/wireless/iwlwifi/dvm/main.c
@@ -406,7 +406,7 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv)
base = priv->device_pointers.log_event_table;
if (iwlagn_hw_valid_rtc_data_addr(base)) {
- iwl_read_targ_mem_words(priv->trans, base, &read, sizeof(read));
+ iwl_read_targ_mem_bytes(priv->trans, base, &read, sizeof(read));
capacity = read.capacity;
mode = read.mode;
num_wraps = read.wrap_counter;
@@ -1671,7 +1671,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv)
}
/*TODO: Update dbgfs with ISR error stats obtained below */
- iwl_read_targ_mem_words(trans, base, &table, sizeof(table));
+ iwl_read_targ_mem_bytes(trans, base, &table, sizeof(table));
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 4a9a45f..45a59c9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -79,7 +79,7 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask);
void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr,
void *buf, int words);
-#define iwl_read_targ_mem_words(trans, addr, buf, bufsize) \
+#define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize) \
do { \
BUILD_BUG_ON((bufsize) % sizeof(u32)); \
_iwl_read_targ_mem_words(trans, addr, buf, \
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
Since the queue gets stuck from time to time, we are trying
to get as much information as we can when this occurs.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/pcie/trans.c | 25 +++++++++++++++++++++----
1 file changed, 21 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 4d4cbae..d426e3d 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -315,10 +315,6 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
jiffies_to_msecs(trans_pcie->wd_timeout));
IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
txq->q.read_ptr, txq->q.write_ptr);
- IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n",
- iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq->q.id))
- & (TFD_QUEUE_SIZE_MAX - 1),
- iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id)));
iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
@@ -328,6 +324,27 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i,
iwl_read_direct32(trans, FH_TX_TRB_REG(i)));
+ for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) {
+ u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i));
+ u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7;
+ bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE));
+ u32 tbl_dw =
+ iwl_read_targ_mem(trans,
+ trans_pcie->scd_base_addr +
+ SCD_TRANS_TBL_OFFSET_QUEUE(i));
+
+ if (i & 0x1)
+ tbl_dw = (tbl_dw & 0xFFFF0000) >> 16;
+ else
+ tbl_dw = tbl_dw & 0x0000FFFF;
+
+ IWL_ERR(trans,
+ "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n",
+ i, active ? "" : "in", fifo, tbl_dw,
+ iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)),
+ iwl_read_prph(trans, SCD_QUEUE_WRPTR(i)));
+ }
+
iwl_op_mode_nic_error(trans->op_mode);
}
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
In fragmentation we don't update the write pointer of the
HW immediately. So we shouldn't modify the timer in that
case.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/pcie/trans.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index d426e3d..6a30cce 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -1353,7 +1353,8 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb,
skb->data + hdr_len, secondlen);
/* start timer if queue currently empty */
- if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout)
+ if (txq->need_update && q->read_ptr == q->write_ptr &&
+ trans_pcie->wd_timeout)
mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
/* Tell device the write index *just past* this latest filled TFD */
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
Using the driver_data area in ieee80211_tx_info which
resides in the CB overrides the info->control field.
Add a comment to prevent mistakes.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/dvm/tx.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c
index 0dfaf64..1f9457f 100644
--- a/drivers/net/wireless/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/dvm/tx.c
@@ -403,6 +403,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb)
info->driver_data[0] = ctx;
info->driver_data[1] = dev_cmd;
+ /* From now on, we cannot access info->control */
spin_lock(&priv->sta_lock);
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
This locking isn't needed. The only locking we need is when
we access prph registers but there is already a separate
lock for that.
Since we haven't returned from the mac80211's
IEEE80211_AMPDU_TX_OPERATIONAL ampdu_action, we cannot
receive any Tx frame for that sta / tid while enabling the
queue.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/pcie/internal.h | 5 +----
drivers/net/wireless/iwlwifi/pcie/trans.c | 5 ++---
drivers/net/wireless/iwlwifi/pcie/tx.c | 21 ++-------------------
3 files changed, 5 insertions(+), 26 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 6c7b355..5024fb6 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -339,12 +339,9 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans,
void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
struct iwl_tx_queue *txq,
u16 byte_cnt);
-void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue);
-void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
- int fifo, int sta_id, int tid,
- int frame_limit, u16 ssn);
void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
int sta_id, int tid, int frame_limit, u16 ssn);
+void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue);
void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq,
enum dma_data_direction dma_dir);
int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index,
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 1904fda..4d4cbae 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -1068,9 +1068,8 @@ static void iwl_tx_start(struct iwl_trans *trans)
for (i = 0; i < trans_pcie->n_q_to_fifo; i++) {
int fifo = trans_pcie->setup_q_to_fifo[i];
- __iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION,
- IWL_TID_NON_QOS,
- SCD_FRAME_LIMIT, 0);
+ iwl_trans_pcie_txq_enable(trans, i, fifo, IWL_INVALID_STATION,
+ IWL_TID_NON_QOS, SCD_FRAME_LIMIT, 0);
}
/* Activate all Tx DMA/FIFO channels */
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 18e6c81..a092fb3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -415,14 +415,11 @@ static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id)
(1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
}
-void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
- int fifo, int sta_id, int tid,
- int frame_limit, u16 ssn)
+void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
+ int sta_id, int tid, int frame_limit, u16 ssn)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- lockdep_assert_held(&trans_pcie->irq_lock);
-
if (test_and_set_bit(txq_id, trans_pcie->queue_used))
WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
@@ -480,20 +477,6 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
txq_id, fifo, ssn & 0xff);
}
-void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
- int sta_id, int tid, int frame_limit, u16 ssn)
-{
- struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- unsigned long flags;
-
- spin_lock_irqsave(&trans_pcie->irq_lock, flags);
-
- __iwl_trans_pcie_txq_enable(trans, txq_id, fifo, sta_id,
- tid, frame_limit, ssn);
-
- spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-}
-
void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
--
1.7.10
From: Dan Carpenter <[email protected]>
CMD_SYNC is zero so the if (cmd->flags & CMD_SYNC) is never true and we
never check the assertion.
Signed-off-by: Dan Carpenter <[email protected]>
Reviewed-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/dvm/lib.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c
index cb1ca7a..76f2592 100644
--- a/drivers/net/wireless/iwlwifi/dvm/lib.c
+++ b/drivers/net/wireless/iwlwifi/dvm/lib.c
@@ -1265,7 +1265,7 @@ int iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
* the mutex, this ensures we don't try to send two
* (or more) synchronous commands at a time.
*/
- if (cmd->flags & CMD_SYNC)
+ if (!(cmd->flags & CMD_ASYNC))
lockdep_assert_held(&priv->mutex);
if (priv->ucode_owner == IWL_OWNERSHIP_TM &&
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
This is not needed, we just need to tell the SCD not to use
that queue. We will reconfigure that queue when we will use
it again.
Clean up a bit the code on the way.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/pcie/internal.h | 4 --
drivers/net/wireless/iwlwifi/pcie/tx.c | 73 +++++++++++---------------
2 files changed, 30 insertions(+), 47 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 94201c4..6c7b355 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -340,10 +340,6 @@ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans,
struct iwl_tx_queue *txq,
u16 byte_cnt);
void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue);
-void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index);
-void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, bool active);
void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
int fifo, int sta_id, int tid,
int frame_limit, u16 ssn);
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 35e8216..18e6c81 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -380,8 +380,8 @@ static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans,
tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent;
}
-static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid,
- u16 txq_id)
+static int iwl_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid,
+ u16 txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
u32 tbl_dw_addr;
@@ -405,7 +405,7 @@ static int iwlagn_tx_queue_set_q2ratid(struct iwl_trans *trans, u16 ra_tid,
return 0;
}
-static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id)
+static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id)
{
/* Simply stop the queue, but don't change any configuration;
* the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */
@@ -415,33 +415,6 @@ static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id)
(1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN));
}
-void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index)
-{
- IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d\n", txq_id, index & 0xff);
- iwl_write_direct32(trans, HBUS_TARG_WRPTR,
- (index & 0xff) | (txq_id << 8));
- iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), index);
-}
-
-void iwl_trans_tx_queue_set_status(struct iwl_trans *trans,
- struct iwl_tx_queue *txq,
- int tx_fifo_id, bool active)
-{
- int txq_id = txq->q.id;
-
- iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
- (active << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
- (tx_fifo_id << SCD_QUEUE_STTS_REG_POS_TXF) |
- (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
- SCD_QUEUE_STTS_REG_MSK);
-
- if (active)
- IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d\n",
- txq_id, tx_fifo_id);
- else
- IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
-}
-
void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
int fifo, int sta_id, int tid,
int frame_limit, u16 ssn)
@@ -454,7 +427,7 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
WARN_ONCE(1, "queue %d already used - expect issues", txq_id);
/* Stop this Tx queue before configuring it */
- iwlagn_tx_queue_stop_scheduler(trans, txq_id);
+ iwl_txq_set_inactive(trans, txq_id);
/* Set this queue as a chain-building queue unless it is CMD queue */
if (txq_id != trans_pcie->cmd_queue)
@@ -465,17 +438,27 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
u16 ra_tid = BUILD_RAxTID(sta_id, tid);
/* Map receiver-address / traffic-ID to this queue */
- iwlagn_tx_queue_set_q2ratid(trans, ra_tid, txq_id);
+ iwl_txq_set_ratid_map(trans, ra_tid, txq_id);
/* enable aggregations for the queue */
iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+ } else {
+ /*
+ * disable aggregations for the queue, this will also make the
+ * ra_tid mapping configuration irrelevant since it is now a
+ * non-AGG queue.
+ */
+ iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
}
/* Place first TFD at index corresponding to start sequence number.
* Assumes that ssn_idx is valid (!= 0xFFF) */
trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff);
trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff);
- iwl_trans_set_wr_ptrs(trans, txq_id, ssn);
+
+ iwl_write_direct32(trans, HBUS_TARG_WRPTR,
+ (ssn & 0xff) | (txq_id << 8));
+ iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn);
/* Set up Tx window size and frame limit for this queue */
iwl_write_targ_mem(trans, trans_pcie->scd_base_addr +
@@ -488,8 +471,13 @@ void __iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id,
SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK));
/* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */
- iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id],
- fifo, true);
+ iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id),
+ (1 << SCD_QUEUE_STTS_REG_POS_ACTIVE) |
+ (fifo << SCD_QUEUE_STTS_REG_POS_TXF) |
+ (1 << SCD_QUEUE_STTS_REG_POS_WSL) |
+ SCD_QUEUE_STTS_REG_MSK);
+ IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n",
+ txq_id, fifo, ssn & 0xff);
}
void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
@@ -509,22 +497,21 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo,
void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ u16 rd_ptr, wr_ptr;
if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) {
WARN_ONCE(1, "queue %d not used", txq_id);
return;
}
- iwlagn_tx_queue_stop_scheduler(trans, txq_id);
-
- iwl_clear_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id));
+ rd_ptr = iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id));
+ wr_ptr = iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id));
- trans_pcie->txq[txq_id].q.read_ptr = 0;
- trans_pcie->txq[txq_id].q.write_ptr = 0;
- iwl_trans_set_wr_ptrs(trans, txq_id, 0);
+ WARN_ONCE(rd_ptr != wr_ptr, "queue %d isn't empty: [%d,%d]",
+ txq_id, rd_ptr, wr_ptr);
- iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id],
- 0, false);
+ iwl_txq_set_inactive(trans, txq_id);
+ IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id);
}
/*************** HOST COMMAND QUEUE FUNCTIONS *****/
--
1.7.10
From: Emmanuel Grumbach <[email protected]>
Change its name to better reflect this.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/dvm/mac80211.c | 2 +-
drivers/net/wireless/iwlwifi/iwl-io.c | 20 ++++++++++----------
drivers/net/wireless/iwlwifi/iwl-io.h | 12 ++++++------
drivers/net/wireless/iwlwifi/iwl-test.c | 8 ++++----
4 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index f60be39..7f49526 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -473,7 +473,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
}
if (priv->wowlan_sram)
- _iwl_read_targ_mem_words(
+ _iwl_read_targ_mem_dwords(
priv->trans, 0x800000,
priv->wowlan_sram,
img->sec[IWL_UCODE_SECTION_DATA].len / 4);
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c
index 5f2df70..66c8733 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/iwlwifi/iwl-io.c
@@ -298,8 +298,8 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask)
}
EXPORT_SYMBOL_GPL(iwl_clear_bits_prph);
-void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr,
- void *buf, int words)
+void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords)
{
unsigned long flags;
int offs;
@@ -308,26 +308,26 @@ void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr,
spin_lock_irqsave(&trans->reg_lock, flags);
if (likely(iwl_grab_nic_access(trans))) {
iwl_write32(trans, HBUS_TARG_MEM_RADDR, addr);
- for (offs = 0; offs < words; offs++)
+ for (offs = 0; offs < dwords; offs++)
vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT);
iwl_release_nic_access(trans);
}
spin_unlock_irqrestore(&trans->reg_lock, flags);
}
-EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_words);
+EXPORT_SYMBOL_GPL(_iwl_read_targ_mem_dwords);
u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr)
{
u32 value;
- _iwl_read_targ_mem_words(trans, addr, &value, 1);
+ _iwl_read_targ_mem_dwords(trans, addr, &value, 1);
return value;
}
EXPORT_SYMBOL_GPL(iwl_read_targ_mem);
-int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr,
- void *buf, int words)
+int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords)
{
unsigned long flags;
int offs, result = 0;
@@ -336,7 +336,7 @@ int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr,
spin_lock_irqsave(&trans->reg_lock, flags);
if (likely(iwl_grab_nic_access(trans))) {
iwl_write32(trans, HBUS_TARG_MEM_WADDR, addr);
- for (offs = 0; offs < words; offs++)
+ for (offs = 0; offs < dwords; offs++)
iwl_write32(trans, HBUS_TARG_MEM_WDAT, vals[offs]);
iwl_release_nic_access(trans);
} else
@@ -345,10 +345,10 @@ int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr,
return result;
}
-EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_words);
+EXPORT_SYMBOL_GPL(_iwl_write_targ_mem_dwords);
int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val)
{
- return _iwl_write_targ_mem_words(trans, addr, &val, 1);
+ return _iwl_write_targ_mem_dwords(trans, addr, &val, 1);
}
EXPORT_SYMBOL_GPL(iwl_write_targ_mem);
diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h
index 45a59c9..50d3819 100644
--- a/drivers/net/wireless/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/iwlwifi/iwl-io.h
@@ -76,18 +76,18 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 reg,
u32 bits, u32 mask);
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 reg, u32 mask);
-void _iwl_read_targ_mem_words(struct iwl_trans *trans, u32 addr,
- void *buf, int words);
+void _iwl_read_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords);
#define iwl_read_targ_mem_bytes(trans, addr, buf, bufsize) \
do { \
BUILD_BUG_ON((bufsize) % sizeof(u32)); \
- _iwl_read_targ_mem_words(trans, addr, buf, \
- (bufsize) / sizeof(u32));\
+ _iwl_read_targ_mem_dwords(trans, addr, buf, \
+ (bufsize) / sizeof(u32));\
} while (0)
-int _iwl_write_targ_mem_words(struct iwl_trans *trans, u32 addr,
- void *buf, int words);
+int _iwl_write_targ_mem_dwords(struct iwl_trans *trans, u32 addr,
+ void *buf, int dwords);
u32 iwl_read_targ_mem(struct iwl_trans *trans, u32 addr);
int iwl_write_targ_mem(struct iwl_trans *trans, u32 addr, u32 val);
diff --git a/drivers/net/wireless/iwlwifi/iwl-test.c b/drivers/net/wireless/iwlwifi/iwl-test.c
index 7a264ae..81e8c71 100644
--- a/drivers/net/wireless/iwlwifi/iwl-test.c
+++ b/drivers/net/wireless/iwlwifi/iwl-test.c
@@ -476,9 +476,9 @@ static int iwl_test_indirect_read(struct iwl_test *tst, u32 addr, u32 size)
iwl_release_nic_access(trans);
spin_unlock_irqrestore(&trans->reg_lock, flags);
} else { /* target memory (SRAM) */
- _iwl_read_targ_mem_words(trans, addr,
- tst->mem.addr,
- tst->mem.size / 4);
+ _iwl_read_targ_mem_dwords(trans, addr,
+ tst->mem.addr,
+ tst->mem.size / 4);
}
tst->mem.nchunks =
@@ -522,7 +522,7 @@ static int iwl_test_indirect_write(struct iwl_test *tst, u32 addr,
*(u32 *)(buf+i));
}
} else if (iwl_test_valid_hw_addr(tst, addr)) {
- _iwl_write_targ_mem_words(trans, addr, buf, size/4);
+ _iwl_write_targ_mem_dwords(trans, addr, buf, size / 4);
} else {
return -EINVAL;
}
--
1.7.10
On Tue, 2012-06-12 at 21:57 +0200, Johannes Berg wrote:
> John,
>
> please pull the iwlwifi-next tree.
>
> I've included the patches here, in slightly fixed up versions, a small
> additional warning that we need to flag/debug some issues, and also of
> course the fix for the EEPROM refactoring vs. addition of the separate
> configuration structure for 6035 devices.
>
> The patches I posted previously included a few fixes for code that went
> into the tree in my last pull and more debugging utilities.
Please hold off. We found a crash when firmware is missing due to one of
the patches here (that fix something else unfortunately) -- I'll roll in
the fix and rebuild the pull request today.
johannes
From: Emmanuel Grumbach <[email protected]>
Print some more info from the SCD's SRAM and dump the TRB
from the FH.
Signed-off-by: Emmanuel Grumbach <[email protected]>
Signed-off-by: Johannes Berg <[email protected]>
---
drivers/net/wireless/iwlwifi/iwl-fh.h | 2 ++
drivers/net/wireless/iwlwifi/pcie/trans.c | 13 ++++++++++++-
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index 74bce97..8060466 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -421,6 +421,8 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
(FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4)
#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98)
+#define FH_TX_TRB_REG(_chan) (FH_MEM_LOWER_BOUND + 0x958 + (_chan) * 4)
+
/* Instruct FH to increment the retry count of a packet when
* it is brought from the memory to TX-FIFO
*/
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 1eabb83..1904fda 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -298,6 +298,10 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
struct iwl_tx_queue *txq = (void *)data;
struct iwl_trans_pcie *trans_pcie = txq->trans_pcie;
struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie);
+ u32 scd_sram_addr = trans_pcie->scd_base_addr +
+ SCD_TX_STTS_MEM_LOWER_BOUND + (16 * txq->q.id);
+ u8 buf[16];
+ int i;
spin_lock(&txq->lock);
/* check if triggered erroneously */
@@ -307,7 +311,6 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
}
spin_unlock(&txq->lock);
-
IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id,
jiffies_to_msecs(trans_pcie->wd_timeout));
IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n",
@@ -317,6 +320,14 @@ static void iwl_trans_pcie_queue_stuck_timer(unsigned long data)
& (TFD_QUEUE_SIZE_MAX - 1),
iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq->q.id)));
+ iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf));
+
+ iwl_print_hex_error(trans, buf, sizeof(buf));
+
+ for (i = 0; i < FH_TCSR_CHNL_NUM; i++)
+ IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i,
+ iwl_read_direct32(trans, FH_TX_TRB_REG(i)));
+
iwl_op_mode_nic_error(trans->op_mode);
}
--
1.7.10
I write here since I am currently performing experiments in which I need
specifically to disable RTS frames. Intuitively, I tried to set
iw phyXX set rts off
but btained counter-intuitive results, since RTS effectively dissappear
only for low traffic demands from upper layers (around 1Mbps with 3
nodes). Testing with 3 Mbps make the RTS appeared despite the RTS
setting.
Is there a way to set minstrel_ht not to use RTS (no matter the clever
or not it is) ?
I have searched a bit on the archive but this seems the only
conversation that deals with that.
Thank you very much for any information!
Gui
On 2012-01-28 11:14 PM, Simon Wunderlich wrote:
> I agree to Marek here - if I want to disable RTS (for whatever
reason), I type
>
> iw phy0 set rts off
>
> I would expect to see no RTS frames coming from my WiFi card at all.
If the
> rate control algorithm (whether ath9k or minstrel_ht) wants to be
clever, it should
> still respect this decision. The oven in my kitchen also has a
temperature regulator,
> but if I turn it off I also don't want my oven to be too clever and
burn down the house. ;)
>
> I know, apples and oranges, but I hope this makes this point
understandable. I also
> had quite a debugging night to find the reason for these "wild rts"
frames.
>
> Can't we add something two states, like:
> * "RTS really off" and
> * "RTS usually off, but rc's may still use it" ?
If you send a patch for that, I'm ok with it. The default should be RTS
off, but rc may still use it.
- Feli