|
@@ -236,6 +236,7 @@ void iwl_testmode_cleanup(struct iwl_priv *priv)
|
|
|
iwl_mem_cleanup(priv);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/*
|
|
|
* This function handles the user application commands to the ucode.
|
|
|
*
|
|
@@ -244,8 +245,10 @@ void iwl_testmode_cleanup(struct iwl_priv *priv)
|
|
|
* host command to the ucode.
|
|
|
*
|
|
|
* If any mandatory field is missing, -ENOMSG is replied to the user space
|
|
|
- * application; otherwise, the actual execution result of the host command to
|
|
|
- * ucode is replied.
|
|
|
+ * 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
|
|
@@ -254,6 +257,12 @@ static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
|
|
|
{
|
|
|
struct iwl_priv *priv = hw->priv;
|
|
|
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));
|
|
|
|
|
@@ -263,15 +272,53 @@ static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
|
|
|
return -ENOMSG;
|
|
|
}
|
|
|
|
|
|
- cmd.flags = CMD_ON_DEMAND;
|
|
|
+ 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_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x,"
|
|
|
+ IWL_DEBUG_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x,"
|
|
|
" len %d\n", cmd.id, cmd.flags, cmd.len[0]);
|
|
|
- /* ok, let's submit the command to ucode */
|
|
|
- return iwl_trans_send_cmd(trans(priv), &cmd);
|
|
|
+
|
|
|
+ ret = iwl_trans_send_cmd(trans(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 = (struct iwl_rx_packet *)cmd.reply_page;
|
|
|
+ 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->u), reply_len);
|
|
|
+ iwl_free_pages(priv->shrd, cmd.reply_page);
|
|
|
+
|
|
|
+ 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);
|
|
|
+ return cfg80211_testmode_reply(skb);
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ IWL_DEBUG_INFO(priv, "Failed creating NL attributes\n");
|
|
|
+ return -ENOMSG;
|
|
|
}
|
|
|
|
|
|
|