iwlwifi: add testmode command for rx forwarding
authorAmit Beka <amit.beka@intel.com>
Wed, 7 Mar 2012 17:52:29 +0000 (09:52 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 8 Mar 2012 18:59:52 +0000 (13:59 -0500)
Added a testmode command which tells iwl_rx_dispatch
to send the RX both as a notification to nl80211 and
with the registered RX handlers.

This is used for monitoring RX from userspace while preserving
the regular flows in the driver.

Signed-off-by: Amit Beka <amit.beka@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/iwlwifi/iwl-agn-rx.c
drivers/net/wireless/iwlwifi/iwl-testmode.c
drivers/net/wireless/iwlwifi/iwl-testmode.h

index e504675f26372f66087d578fc03a5c22df4a71f0..8e7cdfaf10cc32a350c5985864a843ebbca051cc 100644 (file)
@@ -1152,6 +1152,8 @@ 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;
 
        /*
@@ -1161,10 +1163,20 @@ int iwl_rx_dispatch(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb,
         */
        iwl_notification_wait_notify(&priv->notif_wait, pkt);
 
-       if (priv->pre_rx_handler &&
-           priv->ucode_owner == IWL_OWNERSHIP_TM)
-               priv->pre_rx_handler(priv, rxb);
-       else {
+       /* 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
+        * 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);
+       if (priv->ucode_owner != IWL_OWNERSHIP_TM) {
                /* Based on type of command response or notification,
                 *   handle those that need handling via function in
                 *   rx_handlers table.  See iwl_setup_rx_handlers() */
index 1d732092ebe789430b91d6d9bcb47be88128d01a..b8044bcd5044c7ee2a4933d701d0d8e21b4d98f1 100644 (file)
@@ -125,6 +125,8 @@ struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
        [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, },
 };
 
 /*
@@ -194,7 +196,7 @@ nla_put_failure:
 
 void iwl_testmode_init(struct iwl_priv *priv)
 {
-       priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
+       priv->pre_rx_handler = NULL;
        priv->testmode_trace.trace_enabled = false;
        priv->testmode_mem.read_in_progress = false;
 }
@@ -770,9 +772,13 @@ 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) || (owner == IWL_OWNERSHIP_TM))
+       if (owner == IWL_OWNERSHIP_DRIVER) {
                priv->ucode_owner = owner;
-       else {
+               priv->pre_rx_handler = NULL;
+       } else if (owner == IWL_OWNERSHIP_TM) {
+               priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
+               priv->ucode_owner = owner;
+       } else {
                IWL_ERR(priv, "Invalid owner\n");
                return -EINVAL;
        }
@@ -937,6 +943,20 @@ static int iwl_testmode_buffer_dump(struct ieee80211_hw *hw,
        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
@@ -1022,6 +1042,12 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
                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;
index 69b2e80f4071b871469ca5b36c37f93a5a21408c..6ba211b09426044df2a6564ae4cb08a749aa2d06 100644 (file)
  *     Fore reading, a READ command is sent from the userspace and the data
  *     is returned when the user calls a DUMP command.
  *     For writing, only a WRITE command is used.
+ * @IWL_TM_CMD_APP2DEV_NOTIFICATIONS:
+ *     Command to enable/disable notifications (currently RX packets) from the
+ *     driver to userspace.
  */
 enum iwl_tm_cmd_t {
        IWL_TM_CMD_APP2DEV_UCODE                = 1,
@@ -152,7 +155,8 @@ enum iwl_tm_cmd_t {
        IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_READ = 26,
        IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_DUMP = 27,
        IWL_TM_CMD_APP2DEV_INDIRECT_BUFFER_WRITE = 28,
-       IWL_TM_CMD_MAX                          = 29,
+       IWL_TM_CMD_APP2DEV_NOTIFICATIONS        = 29,
+       IWL_TM_CMD_MAX                          = 30,
 };
 
 /*
@@ -256,6 +260,10 @@ enum iwl_tm_cmd_t {
  *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_UCODE this flag
  *     indicates that the user wants to receive the response of the command
  *     in a reply SKB. If it's not present, the response is not returned.
+ * @IWL_TM_ATTR_ENABLE_NOTIFICATIONS:
+ *     When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_NOTIFICATIONS, this
+ *     flag enables (if present) or disables (if not) the forwarding
+ *     to userspace.
  */
 enum iwl_tm_attr_t {
        IWL_TM_ATTR_NOT_APPLICABLE              = 0,
@@ -282,7 +290,8 @@ enum iwl_tm_attr_t {
        IWL_TM_ATTR_FW_INST_SIZE                = 21,
        IWL_TM_ATTR_FW_DATA_SIZE                = 22,
        IWL_TM_ATTR_UCODE_CMD_SKB               = 23,
-       IWL_TM_ATTR_MAX                         = 24,
+       IWL_TM_ATTR_ENABLE_NOTIFICATION         = 24,
+       IWL_TM_ATTR_MAX                         = 25,
 };
 
 /* uCode trace buffer */