blob: 131a73d16f98701ffe62279d1fb6304adc0e740b [file] [log] [blame]
Cindy H. Kao4613e722011-05-06 10:40:15 -07001/******************************************************************************
2 *
3 * This file is provided under a dual BSD/GPLv2 license. When using or
4 * redistributing this file, you may do so under either license.
5 *
6 * GPL LICENSE SUMMARY
7 *
8 * Copyright(c) 2010 - 2011 Intel Corporation. All rights reserved.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
22 * USA
23 *
24 * The full GNU General Public License is included in this distribution
25 * in the file called LICENSE.GPL.
26 *
27 * Contact Information:
28 * Intel Linux Wireless <ilw@linux.intel.com>
29 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
30 *
31 * BSD LICENSE
32 *
33 * Copyright(c) 2010 - 2011 Intel Corporation. All rights reserved.
34 * All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 *
40 * * Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in
44 * the documentation and/or other materials provided with the
45 * distribution.
46 * * Neither the name Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
49 *
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
53 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
54 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
60 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 *
62 *****************************************************************************/
63#include <linux/init.h>
64#include <linux/kernel.h>
65#include <linux/module.h>
Emmanuel Grumbach522376d2011-09-06 09:31:19 -070066#include <linux/dma-mapping.h>
Cindy H. Kao4613e722011-05-06 10:40:15 -070067#include <net/net_namespace.h>
68#include <linux/netdevice.h>
69#include <net/cfg80211.h>
70#include <net/mac80211.h>
71#include <net/netlink.h>
72
Cindy H. Kao4613e722011-05-06 10:40:15 -070073#include "iwl-dev.h"
74#include "iwl-core.h"
75#include "iwl-debug.h"
Cindy H. Kao4613e722011-05-06 10:40:15 -070076#include "iwl-io.h"
77#include "iwl-agn.h"
78#include "iwl-testmode.h"
Emmanuel Grumbachbdfbf092011-07-08 08:46:16 -070079#include "iwl-trans.h"
Cindy H. Kao4613e722011-05-06 10:40:15 -070080
81/* The TLVs used in the gnl message policy between the kernel module and
82 * user space application. iwl_testmode_gnl_msg_policy is to be carried
83 * through the NL80211_CMD_TESTMODE channel regulated by nl80211.
84 * See iwl-testmode.h
85 */
86static
87struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = {
88 [IWL_TM_ATTR_COMMAND] = { .type = NLA_U32, },
89
90 [IWL_TM_ATTR_UCODE_CMD_ID] = { .type = NLA_U8, },
91 [IWL_TM_ATTR_UCODE_CMD_DATA] = { .type = NLA_UNSPEC, },
92
93 [IWL_TM_ATTR_REG_OFFSET] = { .type = NLA_U32, },
94 [IWL_TM_ATTR_REG_VALUE8] = { .type = NLA_U8, },
95 [IWL_TM_ATTR_REG_VALUE32] = { .type = NLA_U32, },
96
97 [IWL_TM_ATTR_SYNC_RSP] = { .type = NLA_UNSPEC, },
98 [IWL_TM_ATTR_UCODE_RX_PKT] = { .type = NLA_UNSPEC, },
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -070099
Wey-Yi Guy4babc352011-05-03 18:05:12 -0700100 [IWL_TM_ATTR_EEPROM] = { .type = NLA_UNSPEC, },
101
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700102 [IWL_TM_ATTR_TRACE_ADDR] = { .type = NLA_UNSPEC, },
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700103 [IWL_TM_ATTR_TRACE_DUMP] = { .type = NLA_UNSPEC, },
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700104 [IWL_TM_ATTR_TRACE_SIZE] = { .type = NLA_U32, },
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700105
Wey-Yi Guy64898542011-05-03 18:05:13 -0700106 [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, },
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700107
Wey-Yi Guye98a1932011-07-08 08:46:26 -0700108 [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, },
Hsu, Kenny306713f2011-11-22 23:03:39 -0800109
110 [IWL_TM_ATTR_SRAM_ADDR] = { .type = NLA_U32, },
111 [IWL_TM_ATTR_SRAM_SIZE] = { .type = NLA_U32, },
112 [IWL_TM_ATTR_SRAM_DUMP] = { .type = NLA_UNSPEC, },
Hsu, Kennye1a38fe2011-11-28 00:55:38 -0800113
114 [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, },
Cindy H. Kao4613e722011-05-06 10:40:15 -0700115};
116
117/*
118 * See the struct iwl_rx_packet in iwl-commands.h for the format of the
119 * received events from the device
120 */
121static inline int get_event_length(struct iwl_rx_mem_buffer *rxb)
122{
123 struct iwl_rx_packet *pkt = rxb_addr(rxb);
124 if (pkt)
125 return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
126 else
127 return 0;
128}
129
130
131/*
132 * This function multicasts the spontaneous messages from the device to the
133 * user space. It is invoked whenever there is a received messages
134 * from the device. This function is called within the ISR of the rx handlers
135 * in iwlagn driver.
136 *
137 * The parsing of the message content is left to the user space application,
138 * The message content is treated as unattacked raw data and is encapsulated
139 * with IWL_TM_ATTR_UCODE_RX_PKT multicasting to the user space.
140 *
141 * @priv: the instance of iwlwifi device
142 * @rxb: pointer to rx data content received by the ISR
143 *
144 * See the message policies and TLVs in iwl_testmode_gnl_msg_policy[].
145 * For the messages multicasting to the user application, the mandatory
146 * TLV fields are :
147 * IWL_TM_ATTR_COMMAND must be IWL_TM_CMD_DEV2APP_UCODE_RX_PKT
148 * IWL_TM_ATTR_UCODE_RX_PKT for carrying the message content
149 */
150
151static void iwl_testmode_ucode_rx_pkt(struct iwl_priv *priv,
152 struct iwl_rx_mem_buffer *rxb)
153{
154 struct ieee80211_hw *hw = priv->hw;
155 struct sk_buff *skb;
156 void *data;
157 int length;
158
159 data = (void *)rxb_addr(rxb);
160 length = get_event_length(rxb);
161
162 if (!data || length == 0)
163 return;
164
165 skb = cfg80211_testmode_alloc_event_skb(hw->wiphy, 20 + length,
166 GFP_ATOMIC);
167 if (skb == NULL) {
168 IWL_DEBUG_INFO(priv,
169 "Run out of memory for messages to user space ?\n");
170 return;
171 }
172 NLA_PUT_U32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_UCODE_RX_PKT);
173 NLA_PUT(skb, IWL_TM_ATTR_UCODE_RX_PKT, length, data);
174 cfg80211_testmode_event(skb, GFP_ATOMIC);
175 return;
176
177nla_put_failure:
178 kfree_skb(skb);
179 IWL_DEBUG_INFO(priv, "Ouch, overran buffer, check allocation!\n");
180}
181
182void iwl_testmode_init(struct iwl_priv *priv)
183{
184 priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700185 priv->testmode_trace.trace_enabled = false;
Hsu, Kenny306713f2011-11-22 23:03:39 -0800186 priv->testmode_sram.sram_readed = false;
187}
188
Wey-Yi Guy6b5a26f52011-11-23 07:08:22 -0800189static void iwl_sram_cleanup(struct iwl_priv *priv)
Hsu, Kenny306713f2011-11-22 23:03:39 -0800190{
191 if (priv->testmode_sram.sram_readed) {
192 kfree(priv->testmode_sram.buff_addr);
193 priv->testmode_sram.buff_addr = NULL;
194 priv->testmode_sram.buff_size = 0;
195 priv->testmode_sram.num_chunks = 0;
196 priv->testmode_sram.sram_readed = false;
197 }
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700198}
199
200static void iwl_trace_cleanup(struct iwl_priv *priv)
201{
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700202 if (priv->testmode_trace.trace_enabled) {
203 if (priv->testmode_trace.cpu_addr &&
204 priv->testmode_trace.dma_addr)
Don Fry26bfc0c2011-10-10 07:26:56 -0700205 dma_free_coherent(bus(priv)->dev,
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700206 priv->testmode_trace.total_size,
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700207 priv->testmode_trace.cpu_addr,
208 priv->testmode_trace.dma_addr);
209 priv->testmode_trace.trace_enabled = false;
210 priv->testmode_trace.cpu_addr = NULL;
211 priv->testmode_trace.trace_addr = NULL;
212 priv->testmode_trace.dma_addr = 0;
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700213 priv->testmode_trace.buff_size = 0;
214 priv->testmode_trace.total_size = 0;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700215 }
216}
217
218
219void iwl_testmode_cleanup(struct iwl_priv *priv)
220{
221 iwl_trace_cleanup(priv);
Hsu, Kenny306713f2011-11-22 23:03:39 -0800222 iwl_sram_cleanup(priv);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700223}
224
225/*
226 * This function handles the user application commands to the ucode.
227 *
228 * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_CMD_ID and
229 * IWL_TM_ATTR_UCODE_CMD_DATA and calls to the handler to send the
230 * host command to the ucode.
231 *
232 * If any mandatory field is missing, -ENOMSG is replied to the user space
233 * application; otherwise, the actual execution result of the host command to
234 * ucode is replied.
235 *
236 * @hw: ieee80211_hw object that represents the device
237 * @tb: gnl message fields from the user space
238 */
239static int iwl_testmode_ucode(struct ieee80211_hw *hw, struct nlattr **tb)
240{
241 struct iwl_priv *priv = hw->priv;
242 struct iwl_host_cmd cmd;
243
244 memset(&cmd, 0, sizeof(struct iwl_host_cmd));
245
246 if (!tb[IWL_TM_ATTR_UCODE_CMD_ID] ||
247 !tb[IWL_TM_ATTR_UCODE_CMD_DATA]) {
248 IWL_DEBUG_INFO(priv,
249 "Error finding ucode command mandatory fields\n");
250 return -ENOMSG;
251 }
252
Wey-Yi Guyc7c11152011-07-08 08:46:25 -0700253 cmd.flags = CMD_ON_DEMAND;
Cindy H. Kao4613e722011-05-06 10:40:15 -0700254 cmd.id = nla_get_u8(tb[IWL_TM_ATTR_UCODE_CMD_ID]);
Johannes Berg3fa50732011-05-04 07:50:38 -0700255 cmd.data[0] = nla_data(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
256 cmd.len[0] = nla_len(tb[IWL_TM_ATTR_UCODE_CMD_DATA]);
Johannes Berg4ce7cc22011-05-13 11:57:40 -0700257 cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY;
Cindy H. Kao4613e722011-05-06 10:40:15 -0700258 IWL_INFO(priv, "testmode ucode command ID 0x%x, flags 0x%x,"
Johannes Berg3fa50732011-05-04 07:50:38 -0700259 " len %d\n", cmd.id, cmd.flags, cmd.len[0]);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700260 /* ok, let's submit the command to ucode */
Emmanuel Grumbache6bb4c92011-08-25 23:10:48 -0700261 return iwl_trans_send_cmd(trans(priv), &cmd);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700262}
263
264
265/*
266 * This function handles the user application commands for register access.
267 *
268 * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
269 * handlers respectively.
270 *
271 * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
272 * mandatory fields(IWL_TM_ATTR_REG_OFFSET,IWL_TM_ATTR_REG_VALUE32,
273 * IWL_TM_ATTR_REG_VALUE8) are missing; Otherwise 0 is replied indicating
274 * the success of the command execution.
275 *
276 * If IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_REG_READ32, the register read
277 * value is returned with IWL_TM_ATTR_REG_VALUE32.
278 *
279 * @hw: ieee80211_hw object that represents the device
280 * @tb: gnl message fields from the user space
281 */
282static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
283{
284 struct iwl_priv *priv = hw->priv;
285 u32 ofs, val32;
286 u8 val8;
287 struct sk_buff *skb;
288 int status = 0;
289
290 if (!tb[IWL_TM_ATTR_REG_OFFSET]) {
291 IWL_DEBUG_INFO(priv, "Error finding register offset\n");
292 return -ENOMSG;
293 }
294 ofs = nla_get_u32(tb[IWL_TM_ATTR_REG_OFFSET]);
295 IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs);
296
297 switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
Hsu, Kenny72bcacc2011-11-15 19:24:32 -0800298 case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
Emmanuel Grumbach83ed9012011-08-25 23:11:14 -0700299 val32 = iwl_read32(bus(priv), ofs);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700300 IWL_INFO(priv, "32bit value to read 0x%x\n", val32);
301
302 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
303 if (!skb) {
304 IWL_DEBUG_INFO(priv, "Error allocating memory\n");
305 return -ENOMEM;
306 }
307 NLA_PUT_U32(skb, IWL_TM_ATTR_REG_VALUE32, val32);
308 status = cfg80211_testmode_reply(skb);
309 if (status < 0)
310 IWL_DEBUG_INFO(priv,
311 "Error sending msg : %d\n", status);
312 break;
Hsu, Kenny72bcacc2011-11-15 19:24:32 -0800313 case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
Cindy H. Kao4613e722011-05-06 10:40:15 -0700314 if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
315 IWL_DEBUG_INFO(priv,
316 "Error finding value to write\n");
317 return -ENOMSG;
318 } else {
319 val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
320 IWL_INFO(priv, "32bit value to write 0x%x\n", val32);
Emmanuel Grumbach83ed9012011-08-25 23:11:14 -0700321 iwl_write32(bus(priv), ofs, val32);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700322 }
323 break;
Hsu, Kenny72bcacc2011-11-15 19:24:32 -0800324 case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
Cindy H. Kao4613e722011-05-06 10:40:15 -0700325 if (!tb[IWL_TM_ATTR_REG_VALUE8]) {
326 IWL_DEBUG_INFO(priv, "Error finding value to write\n");
327 return -ENOMSG;
328 } else {
329 val8 = nla_get_u8(tb[IWL_TM_ATTR_REG_VALUE8]);
330 IWL_INFO(priv, "8bit value to write 0x%x\n", val8);
Emmanuel Grumbach83ed9012011-08-25 23:11:14 -0700331 iwl_write8(bus(priv), ofs, val8);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700332 }
333 break;
Hsu, Kenny72bcacc2011-11-15 19:24:32 -0800334 case IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32:
335 val32 = iwl_read_prph(bus(priv), ofs);
336 IWL_INFO(priv, "32bit value to read 0x%x\n", val32);
337
338 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
339 if (!skb) {
340 IWL_DEBUG_INFO(priv, "Error allocating memory\n");
341 return -ENOMEM;
342 }
343 NLA_PUT_U32(skb, IWL_TM_ATTR_REG_VALUE32, val32);
344 status = cfg80211_testmode_reply(skb);
345 if (status < 0)
346 IWL_DEBUG_INFO(priv,
347 "Error sending msg : %d\n", status);
348 break;
349 case IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32:
350 if (!tb[IWL_TM_ATTR_REG_VALUE32]) {
351 IWL_DEBUG_INFO(priv,
352 "Error finding value to write\n");
353 return -ENOMSG;
354 } else {
355 val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]);
356 IWL_INFO(priv, "32bit value to write 0x%x\n", val32);
357 iwl_write_prph(bus(priv), ofs, val32);
358 }
359 break;
Cindy H. Kao4613e722011-05-06 10:40:15 -0700360 default:
361 IWL_DEBUG_INFO(priv, "Unknown testmode register command ID\n");
362 return -ENOSYS;
363 }
364
365 return status;
366
367nla_put_failure:
368 kfree_skb(skb);
369 return -EMSGSIZE;
370}
371
372
373static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
374{
375 struct iwl_notification_wait calib_wait;
376 int ret;
377
Don Frydd5fe102011-11-28 16:13:19 -0800378 iwl_init_notification_wait(priv->shrd, &calib_wait,
Cindy H. Kao4613e722011-05-06 10:40:15 -0700379 CALIBRATION_COMPLETE_NOTIFICATION,
380 NULL, NULL);
381 ret = iwlagn_init_alive_start(priv);
382 if (ret) {
383 IWL_DEBUG_INFO(priv,
384 "Error configuring init calibration: %d\n", ret);
385 goto cfg_init_calib_error;
386 }
387
Don Frydd5fe102011-11-28 16:13:19 -0800388 ret = iwl_wait_notification(priv->shrd, &calib_wait, 2 * HZ);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700389 if (ret)
390 IWL_DEBUG_INFO(priv, "Error detecting"
391 " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret);
392 return ret;
393
394cfg_init_calib_error:
Don Frydd5fe102011-11-28 16:13:19 -0800395 iwl_remove_notification(priv->shrd, &calib_wait);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700396 return ret;
397}
398
399/*
400 * This function handles the user application commands for driver.
401 *
402 * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
403 * handlers respectively.
404 *
405 * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned
406 * value of the actual command execution is replied to the user application.
407 *
408 * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP
409 * is used for carry the message while IWL_TM_ATTR_COMMAND must set to
410 * IWL_TM_CMD_DEV2APP_SYNC_RSP.
411 *
412 * @hw: ieee80211_hw object that represents the device
413 * @tb: gnl message fields from the user space
414 */
415static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
416{
417 struct iwl_priv *priv = hw->priv;
418 struct sk_buff *skb;
419 unsigned char *rsp_data_ptr = NULL;
420 int status = 0, rsp_data_len = 0;
421
422 switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
423 case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
424 rsp_data_ptr = (unsigned char *)priv->cfg->name;
425 rsp_data_len = strlen(priv->cfg->name);
426 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
427 rsp_data_len + 20);
428 if (!skb) {
429 IWL_DEBUG_INFO(priv,
430 "Error allocating memory\n");
431 return -ENOMEM;
432 }
433 NLA_PUT_U32(skb, IWL_TM_ATTR_COMMAND,
434 IWL_TM_CMD_DEV2APP_SYNC_RSP);
435 NLA_PUT(skb, IWL_TM_ATTR_SYNC_RSP,
436 rsp_data_len, rsp_data_ptr);
437 status = cfg80211_testmode_reply(skb);
438 if (status < 0)
439 IWL_DEBUG_INFO(priv, "Error sending msg : %d\n",
440 status);
441 break;
442
443 case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
Don Fry8929c242011-11-10 06:55:08 -0800444 status = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700445 if (status)
446 IWL_DEBUG_INFO(priv,
447 "Error loading init ucode: %d\n", status);
448 break;
449
450 case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
451 iwl_testmode_cfg_init_calib(priv);
Emmanuel Grumbache6bb4c92011-08-25 23:10:48 -0700452 iwl_trans_stop_device(trans(priv));
Cindy H. Kao4613e722011-05-06 10:40:15 -0700453 break;
454
455 case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
Don Fry8929c242011-11-10 06:55:08 -0800456 status = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700457 if (status) {
458 IWL_DEBUG_INFO(priv,
459 "Error loading runtime ucode: %d\n", status);
460 break;
461 }
462 status = iwl_alive_start(priv);
463 if (status)
464 IWL_DEBUG_INFO(priv,
465 "Error starting the device: %d\n", status);
466 break;
467
Hsu, Kennyd4af19f2011-11-24 22:26:53 -0800468 case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
469 iwl_scan_cancel_timeout(priv, 200);
470 iwl_trans_stop_device(trans(priv));
471 status = iwlagn_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
472 if (status) {
473 IWL_DEBUG_INFO(priv,
474 "Error loading WOWLAN ucode: %d\n", status);
475 break;
476 }
477 status = iwl_alive_start(priv);
478 if (status)
479 IWL_DEBUG_INFO(priv,
480 "Error starting the device: %d\n", status);
481 break;
482
Wey-Yi Guy4babc352011-05-03 18:05:12 -0700483 case IWL_TM_CMD_APP2DEV_GET_EEPROM:
484 if (priv->eeprom) {
485 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
486 priv->cfg->base_params->eeprom_size + 20);
487 if (!skb) {
488 IWL_DEBUG_INFO(priv,
489 "Error allocating memory\n");
490 return -ENOMEM;
491 }
492 NLA_PUT_U32(skb, IWL_TM_ATTR_COMMAND,
493 IWL_TM_CMD_DEV2APP_EEPROM_RSP);
494 NLA_PUT(skb, IWL_TM_ATTR_EEPROM,
495 priv->cfg->base_params->eeprom_size,
496 priv->eeprom);
497 status = cfg80211_testmode_reply(skb);
498 if (status < 0)
499 IWL_DEBUG_INFO(priv,
500 "Error sending msg : %d\n",
501 status);
502 } else
503 return -EFAULT;
504 break;
505
Wey-Yi Guy64898542011-05-03 18:05:13 -0700506 case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
507 if (!tb[IWL_TM_ATTR_FIXRATE]) {
508 IWL_DEBUG_INFO(priv,
509 "Error finding fixrate setting\n");
510 return -ENOMSG;
511 }
Wey-Yi Guy4e308112011-07-08 08:46:28 -0700512 priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
Wey-Yi Guy64898542011-05-03 18:05:13 -0700513 break;
514
Hsu, Kennye1a38fe2011-11-28 00:55:38 -0800515 case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
516 IWL_INFO(priv, "uCode version raw: 0x%x\n", priv->ucode_ver);
517
518 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
519 if (!skb) {
520 IWL_DEBUG_INFO(priv, "Error allocating memory\n");
521 return -ENOMEM;
522 }
523 NLA_PUT_U32(skb, IWL_TM_ATTR_FW_VERSION, priv->ucode_ver);
524 status = cfg80211_testmode_reply(skb);
525 if (status < 0)
526 IWL_DEBUG_INFO(priv,
527 "Error sending msg : %d\n", status);
528 break;
529
Cindy H. Kao4613e722011-05-06 10:40:15 -0700530 default:
531 IWL_DEBUG_INFO(priv, "Unknown testmode driver command ID\n");
532 return -ENOSYS;
533 }
534 return status;
535
536nla_put_failure:
537 kfree_skb(skb);
538 return -EMSGSIZE;
539}
540
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700541
542/*
543 * This function handles the user application commands for uCode trace
544 *
545 * It retrieves command ID carried with IWL_TM_ATTR_COMMAND and calls to the
546 * handlers respectively.
547 *
548 * If it's an unknown commdn ID, -ENOSYS is replied; otherwise, the returned
549 * value of the actual command execution is replied to the user application.
550 *
551 * @hw: ieee80211_hw object that represents the device
552 * @tb: gnl message fields from the user space
553 */
554static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb)
555{
556 struct iwl_priv *priv = hw->priv;
557 struct sk_buff *skb;
558 int status = 0;
Don Fry26bfc0c2011-10-10 07:26:56 -0700559 struct device *dev = bus(priv)->dev;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700560
561 switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
562 case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
563 if (priv->testmode_trace.trace_enabled)
564 return -EBUSY;
565
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700566 if (!tb[IWL_TM_ATTR_TRACE_SIZE])
567 priv->testmode_trace.buff_size = TRACE_BUFF_SIZE_DEF;
568 else
569 priv->testmode_trace.buff_size =
570 nla_get_u32(tb[IWL_TM_ATTR_TRACE_SIZE]);
571 if (!priv->testmode_trace.buff_size)
572 return -EINVAL;
573 if (priv->testmode_trace.buff_size < TRACE_BUFF_SIZE_MIN ||
574 priv->testmode_trace.buff_size > TRACE_BUFF_SIZE_MAX)
575 return -EINVAL;
576
577 priv->testmode_trace.total_size =
578 priv->testmode_trace.buff_size + TRACE_BUFF_PADD;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700579 priv->testmode_trace.cpu_addr =
580 dma_alloc_coherent(dev,
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700581 priv->testmode_trace.total_size,
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700582 &priv->testmode_trace.dma_addr,
583 GFP_KERNEL);
584 if (!priv->testmode_trace.cpu_addr)
585 return -ENOMEM;
586 priv->testmode_trace.trace_enabled = true;
587 priv->testmode_trace.trace_addr = (u8 *)PTR_ALIGN(
588 priv->testmode_trace.cpu_addr, 0x100);
589 memset(priv->testmode_trace.trace_addr, 0x03B,
Wey-Yi Guy49b72102011-05-30 10:29:37 -0700590 priv->testmode_trace.buff_size);
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700591 skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
592 sizeof(priv->testmode_trace.dma_addr) + 20);
593 if (!skb) {
594 IWL_DEBUG_INFO(priv,
595 "Error allocating memory\n");
596 iwl_trace_cleanup(priv);
597 return -ENOMEM;
598 }
599 NLA_PUT(skb, IWL_TM_ATTR_TRACE_ADDR,
600 sizeof(priv->testmode_trace.dma_addr),
601 (u64 *)&priv->testmode_trace.dma_addr);
602 status = cfg80211_testmode_reply(skb);
603 if (status < 0) {
604 IWL_DEBUG_INFO(priv,
605 "Error sending msg : %d\n",
606 status);
607 }
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700608 priv->testmode_trace.num_chunks =
609 DIV_ROUND_UP(priv->testmode_trace.buff_size,
Hsu, Kennye056a652011-11-22 23:05:02 -0800610 DUMP_CHUNK_SIZE);
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700611 break;
612
613 case IWL_TM_CMD_APP2DEV_END_TRACE:
614 iwl_trace_cleanup(priv);
615 break;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700616 default:
617 IWL_DEBUG_INFO(priv, "Unknown testmode mem command ID\n");
618 return -ENOSYS;
619 }
620 return status;
621
622nla_put_failure:
623 kfree_skb(skb);
624 if (nla_get_u32(tb[IWL_TM_ATTR_COMMAND]) ==
625 IWL_TM_CMD_APP2DEV_BEGIN_TRACE)
626 iwl_trace_cleanup(priv);
627 return -EMSGSIZE;
628}
629
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700630static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, struct nlattr **tb,
631 struct sk_buff *skb,
632 struct netlink_callback *cb)
633{
634 struct iwl_priv *priv = hw->priv;
635 int idx, length;
636
637 if (priv->testmode_trace.trace_enabled &&
638 priv->testmode_trace.trace_addr) {
639 idx = cb->args[4];
640 if (idx >= priv->testmode_trace.num_chunks)
641 return -ENOENT;
Hsu, Kennye056a652011-11-22 23:05:02 -0800642 length = DUMP_CHUNK_SIZE;
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700643 if (((idx + 1) == priv->testmode_trace.num_chunks) &&
Hsu, Kennye056a652011-11-22 23:05:02 -0800644 (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE))
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700645 length = priv->testmode_trace.buff_size %
Hsu, Kennye056a652011-11-22 23:05:02 -0800646 DUMP_CHUNK_SIZE;
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700647
648 NLA_PUT(skb, IWL_TM_ATTR_TRACE_DUMP, length,
649 priv->testmode_trace.trace_addr +
Hsu, Kennye056a652011-11-22 23:05:02 -0800650 (DUMP_CHUNK_SIZE * idx));
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700651 idx++;
652 cb->args[4] = idx;
653 return 0;
654 } else
655 return -EFAULT;
656
657 nla_put_failure:
658 return -ENOBUFS;
659}
660
Wey-Yi Guye98a1932011-07-08 08:46:26 -0700661/*
662 * This function handles the user application switch ucode ownership.
663 *
664 * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and
665 * decide who the current owner of the uCode
666 *
667 * If the current owner is OWNERSHIP_TM, then the only host command
668 * can deliver to uCode is from testmode, all the other host commands
669 * will dropped.
670 *
671 * default driver is the owner of uCode in normal operational mode
672 *
673 * @hw: ieee80211_hw object that represents the device
674 * @tb: gnl message fields from the user space
675 */
676static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
677{
678 struct iwl_priv *priv = hw->priv;
679 u8 owner;
680
681 if (!tb[IWL_TM_ATTR_UCODE_OWNER]) {
682 IWL_DEBUG_INFO(priv, "Error finding ucode owner\n");
683 return -ENOMSG;
684 }
685
686 owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]);
687 if ((owner == IWL_OWNERSHIP_DRIVER) || (owner == IWL_OWNERSHIP_TM))
Emmanuel Grumbachfd656932011-08-25 23:11:19 -0700688 priv->shrd->ucode_owner = owner;
Wey-Yi Guye98a1932011-07-08 08:46:26 -0700689 else {
690 IWL_DEBUG_INFO(priv, "Invalid owner\n");
691 return -EINVAL;
692 }
693 return 0;
694}
695
Hsu, Kenny306713f2011-11-22 23:03:39 -0800696/*
697 * This function handles the user application commands for SRAM data dump
698 *
699 * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and
700 * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading
701 *
702 * Several error will be retured, -EBUSY if the SRAM data retrieved by
703 * previous command has not been delivered to userspace, or -ENOMSG if
704 * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE)
705 * are missing, or -ENOMEM if the buffer allocation fails.
706 *
707 * Otherwise 0 is replied indicating the success of the SRAM reading.
708 *
709 * @hw: ieee80211_hw object that represents the device
710 * @tb: gnl message fields from the user space
711 */
712static int iwl_testmode_sram(struct ieee80211_hw *hw, struct nlattr **tb)
713{
714 struct iwl_priv *priv = hw->priv;
Kenny Hsu76de2f22011-11-23 06:57:22 -0800715 u32 base, ofs, size, maxsize;
Hsu, Kenny306713f2011-11-22 23:03:39 -0800716
717 if (priv->testmode_sram.sram_readed)
718 return -EBUSY;
719
720 if (!tb[IWL_TM_ATTR_SRAM_ADDR]) {
721 IWL_DEBUG_INFO(priv, "Error finding SRAM offset address\n");
722 return -ENOMSG;
723 }
724 ofs = nla_get_u32(tb[IWL_TM_ATTR_SRAM_ADDR]);
725 if (!tb[IWL_TM_ATTR_SRAM_SIZE]) {
726 IWL_DEBUG_INFO(priv, "Error finding size for SRAM reading\n");
727 return -ENOMSG;
728 }
729 size = nla_get_u32(tb[IWL_TM_ATTR_SRAM_SIZE]);
Don Fry3d6acef2011-11-28 17:05:01 -0800730 switch (priv->shrd->ucode_type) {
Kenny Hsu76de2f22011-11-23 06:57:22 -0800731 case IWL_UCODE_REGULAR:
732 maxsize = trans(priv)->ucode_rt.data.len;
733 break;
734 case IWL_UCODE_INIT:
735 maxsize = trans(priv)->ucode_init.data.len;
736 break;
737 case IWL_UCODE_WOWLAN:
738 maxsize = trans(priv)->ucode_wowlan.data.len;
739 break;
740 case IWL_UCODE_NONE:
741 IWL_DEBUG_INFO(priv, "Error, uCode does not been loaded\n");
742 return -ENOSYS;
743 default:
744 IWL_DEBUG_INFO(priv, "Error, unsupported uCode type\n");
745 return -ENOSYS;
746 }
747 if ((ofs + size) > maxsize) {
748 IWL_DEBUG_INFO(priv, "Invalid offset/size: out of range\n");
749 return -EINVAL;
750 }
Hsu, Kenny306713f2011-11-22 23:03:39 -0800751 priv->testmode_sram.buff_size = (size / 4) * 4;
752 priv->testmode_sram.buff_addr =
753 kmalloc(priv->testmode_sram.buff_size, GFP_KERNEL);
754 if (priv->testmode_sram.buff_addr == NULL) {
755 IWL_DEBUG_INFO(priv, "Error allocating memory\n");
756 return -ENOMEM;
757 }
758 base = 0x800000;
759 _iwl_read_targ_mem_words(bus(priv), base + ofs,
760 priv->testmode_sram.buff_addr,
761 priv->testmode_sram.buff_size / 4);
762 priv->testmode_sram.num_chunks =
Hsu, Kennye056a652011-11-22 23:05:02 -0800763 DIV_ROUND_UP(priv->testmode_sram.buff_size, DUMP_CHUNK_SIZE);
Hsu, Kenny306713f2011-11-22 23:03:39 -0800764 priv->testmode_sram.sram_readed = true;
765 return 0;
766}
767
768static int iwl_testmode_sram_dump(struct ieee80211_hw *hw, struct nlattr **tb,
769 struct sk_buff *skb,
770 struct netlink_callback *cb)
771{
772 struct iwl_priv *priv = hw->priv;
773 int idx, length;
774
775 if (priv->testmode_sram.sram_readed) {
776 idx = cb->args[4];
777 if (idx >= priv->testmode_sram.num_chunks) {
778 iwl_sram_cleanup(priv);
779 return -ENOENT;
780 }
Hsu, Kennye056a652011-11-22 23:05:02 -0800781 length = DUMP_CHUNK_SIZE;
Hsu, Kenny306713f2011-11-22 23:03:39 -0800782 if (((idx + 1) == priv->testmode_sram.num_chunks) &&
Hsu, Kennye056a652011-11-22 23:05:02 -0800783 (priv->testmode_sram.buff_size % DUMP_CHUNK_SIZE))
Hsu, Kenny306713f2011-11-22 23:03:39 -0800784 length = priv->testmode_sram.buff_size %
Hsu, Kennye056a652011-11-22 23:05:02 -0800785 DUMP_CHUNK_SIZE;
Hsu, Kenny306713f2011-11-22 23:03:39 -0800786
787 NLA_PUT(skb, IWL_TM_ATTR_SRAM_DUMP, length,
788 priv->testmode_sram.buff_addr +
Hsu, Kennye056a652011-11-22 23:05:02 -0800789 (DUMP_CHUNK_SIZE * idx));
Hsu, Kenny306713f2011-11-22 23:03:39 -0800790 idx++;
791 cb->args[4] = idx;
792 return 0;
793 } else
794 return -EFAULT;
795
796 nla_put_failure:
797 return -ENOBUFS;
798}
799
Wey-Yi Guye98a1932011-07-08 08:46:26 -0700800
Cindy H. Kao4613e722011-05-06 10:40:15 -0700801/* The testmode gnl message handler that takes the gnl message from the
802 * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then
803 * invoke the corresponding handlers.
804 *
805 * This function is invoked when there is user space application sending
806 * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated
807 * by nl80211.
808 *
809 * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before
810 * dispatching it to the corresponding handler.
811 *
812 * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application;
813 * -ENOSYS is replied to the user application if the command is unknown;
814 * Otherwise, the command is dispatched to the respective handler.
815 *
816 * @hw: ieee80211_hw object that represents the device
817 * @data: pointer to user space message
818 * @len: length in byte of @data
819 */
Wey-Yi Guyade4c642011-10-10 07:27:11 -0700820int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
Cindy H. Kao4613e722011-05-06 10:40:15 -0700821{
Wey-Yi Guya6779272011-07-11 10:47:39 -0700822 struct nlattr *tb[IWL_TM_ATTR_MAX];
Cindy H. Kao4613e722011-05-06 10:40:15 -0700823 struct iwl_priv *priv = hw->priv;
824 int result;
825
826 result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
827 iwl_testmode_gnl_msg_policy);
828 if (result != 0) {
829 IWL_DEBUG_INFO(priv,
830 "Error parsing the gnl message : %d\n", result);
831 return result;
832 }
833
834 /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
835 if (!tb[IWL_TM_ATTR_COMMAND]) {
836 IWL_DEBUG_INFO(priv, "Error finding testmode command type\n");
837 return -ENOMSG;
838 }
839 /* in case multiple accesses to the device happens */
Emmanuel Grumbach6ac2f832011-08-25 23:10:44 -0700840 mutex_lock(&priv->shrd->mutex);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700841
842 switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
843 case IWL_TM_CMD_APP2DEV_UCODE:
844 IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n");
845 result = iwl_testmode_ucode(hw, tb);
846 break;
Hsu, Kenny72bcacc2011-11-15 19:24:32 -0800847 case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
848 case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
849 case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
850 case IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32:
851 case IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32:
Cindy H. Kao4613e722011-05-06 10:40:15 -0700852 IWL_DEBUG_INFO(priv, "testmode cmd to register\n");
853 result = iwl_testmode_reg(hw, tb);
854 break;
855 case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
856 case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
857 case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
858 case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
Wey-Yi Guy4babc352011-05-03 18:05:12 -0700859 case IWL_TM_CMD_APP2DEV_GET_EEPROM:
Wey-Yi Guy64898542011-05-03 18:05:13 -0700860 case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
Hsu, Kennyd4af19f2011-11-24 22:26:53 -0800861 case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
Hsu, Kennye1a38fe2011-11-28 00:55:38 -0800862 case IWL_TM_CMD_APP2DEV_GET_FW_VERSION:
Cindy H. Kao4613e722011-05-06 10:40:15 -0700863 IWL_DEBUG_INFO(priv, "testmode cmd to driver\n");
864 result = iwl_testmode_driver(hw, tb);
865 break;
Wey-Yi Guy7a4e5282011-05-06 10:21:28 -0700866
867 case IWL_TM_CMD_APP2DEV_BEGIN_TRACE:
868 case IWL_TM_CMD_APP2DEV_END_TRACE:
869 case IWL_TM_CMD_APP2DEV_READ_TRACE:
870 IWL_DEBUG_INFO(priv, "testmode uCode trace cmd to driver\n");
871 result = iwl_testmode_trace(hw, tb);
872 break;
873
Wey-Yi Guye98a1932011-07-08 08:46:26 -0700874 case IWL_TM_CMD_APP2DEV_OWNERSHIP:
875 IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n");
876 result = iwl_testmode_ownership(hw, tb);
877 break;
878
Hsu, Kenny306713f2011-11-22 23:03:39 -0800879 case IWL_TM_CMD_APP2DEV_READ_SRAM:
880 IWL_DEBUG_INFO(priv, "testmode sram read cmd to driver\n");
881 result = iwl_testmode_sram(hw, tb);
882 break;
883
Cindy H. Kao4613e722011-05-06 10:40:15 -0700884 default:
885 IWL_DEBUG_INFO(priv, "Unknown testmode command\n");
886 result = -ENOSYS;
887 break;
888 }
889
Emmanuel Grumbach6ac2f832011-08-25 23:10:44 -0700890 mutex_unlock(&priv->shrd->mutex);
Cindy H. Kao4613e722011-05-06 10:40:15 -0700891 return result;
892}
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700893
Wey-Yi Guyade4c642011-10-10 07:27:11 -0700894int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700895 struct netlink_callback *cb,
896 void *data, int len)
897{
898 struct nlattr *tb[IWL_TM_ATTR_MAX];
899 struct iwl_priv *priv = hw->priv;
900 int result;
901 u32 cmd;
902
903 if (cb->args[3]) {
904 /* offset by 1 since commands start at 0 */
905 cmd = cb->args[3] - 1;
906 } else {
907 result = nla_parse(tb, IWL_TM_ATTR_MAX - 1, data, len,
908 iwl_testmode_gnl_msg_policy);
909 if (result) {
910 IWL_DEBUG_INFO(priv,
911 "Error parsing the gnl message : %d\n", result);
912 return result;
913 }
914
915 /* IWL_TM_ATTR_COMMAND is absolutely mandatory */
916 if (!tb[IWL_TM_ATTR_COMMAND]) {
917 IWL_DEBUG_INFO(priv,
918 "Error finding testmode command type\n");
919 return -ENOMSG;
920 }
921 cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
922 cb->args[3] = cmd + 1;
923 }
924
925 /* in case multiple accesses to the device happens */
Emmanuel Grumbach6ac2f832011-08-25 23:10:44 -0700926 mutex_lock(&priv->shrd->mutex);
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700927 switch (cmd) {
928 case IWL_TM_CMD_APP2DEV_READ_TRACE:
929 IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n");
930 result = iwl_testmode_trace_dump(hw, tb, skb, cb);
931 break;
Hsu, Kenny306713f2011-11-22 23:03:39 -0800932 case IWL_TM_CMD_APP2DEV_DUMP_SRAM:
933 IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n");
934 result = iwl_testmode_sram_dump(hw, tb, skb, cb);
935 break;
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700936 default:
937 result = -EINVAL;
938 break;
939 }
940
Emmanuel Grumbach6ac2f832011-08-25 23:10:44 -0700941 mutex_unlock(&priv->shrd->mutex);
Wey-Yi Guyeb64dca2011-05-31 08:03:02 -0700942 return result;
943}