testmode.c 14.3 KB
Newer Older
1 2 3 4 5 6 7
/******************************************************************************
 *
 * 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
 *
J
Johannes Berg 已提交
8
 * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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
25
 * in the file called COPYING.
26 27 28 29 30 31 32
 *
 * Contact Information:
 *  Intel Linux Wireless <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *
 * BSD LICENSE
 *
J
Johannes Berg 已提交
33
 * Copyright(c) 2010 - 2013 Intel Corporation. All rights reserved.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
 * 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.
 *
 *****************************************************************************/
I
Ilan Peer 已提交
63

64 65 66
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
67
#include <linux/dma-mapping.h>
68 69 70 71 72
#include <net/net_namespace.h>
#include <linux/netdevice.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <net/netlink.h>
I
Ilan Peer 已提交
73

74
#include "iwl-debug.h"
75
#include "iwl-trans.h"
76 77
#include "dev.h"
#include "agn.h"
I
Ilan Peer 已提交
78 79
#include "iwl-test.h"
#include "iwl-testmode.h"
W
Wey-Yi Guy 已提交
80

I
Ilan Peer 已提交
81 82
static int iwl_testmode_send_cmd(struct iwl_op_mode *op_mode,
				 struct iwl_host_cmd *cmd)
83
{
I
Ilan Peer 已提交
84 85
	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
	return iwl_dvm_send_cmd(priv, cmd);
86 87
}

I
Ilan Peer 已提交
88
static bool iwl_testmode_valid_hw_addr(u32 addr)
89
{
I
Ilan Peer 已提交
90 91
	if (iwlagn_hw_valid_rtc_data_addr(addr))
		return true;
92

I
Ilan Peer 已提交
93 94 95
	if (IWLAGN_RTC_INST_LOWER_BOUND <= addr &&
	    addr < IWLAGN_RTC_INST_UPPER_BOUND)
		return true;
96

I
Ilan Peer 已提交
97
	return false;
98 99
}

I
Ilan Peer 已提交
100
static u32 iwl_testmode_get_fw_ver(struct iwl_op_mode *op_mode)
101
{
I
Ilan Peer 已提交
102 103
	struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode);
	return priv->fw->ucode_ver;
104 105
}

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
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);
}

I
Ilan Peer 已提交
131 132 133 134
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,
135 136 137 138
	.alloc_reply = iwl_testmode_alloc_reply,
	.reply = iwl_testmode_reply,
	.alloc_event = iwl_testmode_alloc_event,
	.event = iwl_testmode_event,
I
Ilan Peer 已提交
139
};
140

I
Ilan Peer 已提交
141
void iwl_testmode_init(struct iwl_priv *priv)
142
{
I
Ilan Peer 已提交
143
	iwl_test_init(&priv->tst, priv->trans, &tst_ops);
144 145
}

I
Ilan Peer 已提交
146
void iwl_testmode_free(struct iwl_priv *priv)
147
{
I
Ilan Peer 已提交
148
	iwl_test_free(&priv->tst);
149 150 151 152 153
}

static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv)
{
	struct iwl_notification_wait calib_wait;
154 155 156
	static const u8 calib_complete[] = {
		CALIBRATION_COMPLETE_NOTIFICATION
	};
157 158
	int ret;

159
	iwl_init_notification_wait(&priv->notif_wait, &calib_wait,
160
				   calib_complete, ARRAY_SIZE(calib_complete),
161
				   NULL, NULL);
162
	ret = iwl_init_alive_start(priv);
163
	if (ret) {
164
		IWL_ERR(priv, "Fail init calibration: %d\n", ret);
165 166 167
		goto cfg_init_calib_error;
	}

168
	ret = iwl_wait_notification(&priv->notif_wait, &calib_wait, 2 * HZ);
169
	if (ret)
170
		IWL_ERR(priv, "Error detecting"
171 172 173 174
			" CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret);
	return ret;

cfg_init_calib_error:
175
	iwl_remove_notification(&priv->notif_wait, &calib_wait);
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
	return ret;
}

/*
 * This function handles the user application commands for driver.
 *
 * 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.
 *
 * If there's any message responding to the user space, IWL_TM_ATTR_SYNC_RSP
 * is used for carry the message while IWL_TM_ATTR_COMMAND must set to
 * IWL_TM_CMD_DEV2APP_SYNC_RSP.
 *
 * @hw: ieee80211_hw object that represents the device
 * @tb: gnl message fields from the user space
 */
static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb)
{
197
	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
198
	struct iwl_trans *trans = priv->trans;
199 200 201
	struct sk_buff *skb;
	unsigned char *rsp_data_ptr = NULL;
	int status = 0, rsp_data_len = 0;
I
Ilan Peer 已提交
202
	u32 inst_size = 0, data_size = 0;
D
David Spinadel 已提交
203
	const struct fw_img *img;
204 205 206

	switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
	case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
207 208
		rsp_data_ptr = (unsigned char *)priv->cfg->name;
		rsp_data_len = strlen(priv->cfg->name);
209 210 211
		skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
							rsp_data_len + 20);
		if (!skb) {
212
			IWL_ERR(priv, "Memory allocation fail\n");
213 214
			return -ENOMEM;
		}
215 216 217 218 219
		if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
				IWL_TM_CMD_DEV2APP_SYNC_RSP) ||
		    nla_put(skb, IWL_TM_ATTR_SYNC_RSP,
			    rsp_data_len, rsp_data_ptr))
			goto nla_put_failure;
220 221
		status = cfg80211_testmode_reply(skb);
		if (status < 0)
222
			IWL_ERR(priv, "Error sending msg : %d\n", status);
223 224 225
		break;

	case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
226
		status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_INIT);
227
		if (status)
228
			IWL_ERR(priv, "Error loading init ucode: %d\n", status);
229 230 231 232
		break;

	case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
		iwl_testmode_cfg_init_calib(priv);
233
		priv->ucode_loaded = false;
234
		iwl_trans_stop_device(trans);
235 236 237
		break;

	case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
238
		status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_REGULAR);
239
		if (status) {
240
			IWL_ERR(priv,
241 242 243 244 245
				"Error loading runtime ucode: %d\n", status);
			break;
		}
		status = iwl_alive_start(priv);
		if (status)
246
			IWL_ERR(priv,
247 248 249
				"Error starting the device: %d\n", status);
		break;

250 251
	case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
		iwl_scan_cancel_timeout(priv, 200);
252
		priv->ucode_loaded = false;
253
		iwl_trans_stop_device(trans);
254
		status = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN);
255
		if (status) {
256
			IWL_ERR(priv,
257 258 259 260 261
				"Error loading WOWLAN ucode: %d\n", status);
			break;
		}
		status = iwl_alive_start(priv);
		if (status)
262
			IWL_ERR(priv,
263 264 265
				"Error starting the device: %d\n", status);
		break;

266
	case IWL_TM_CMD_APP2DEV_GET_EEPROM:
267
		if (priv->eeprom_blob) {
268
			skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy,
269
				priv->eeprom_blob_size + 20);
270
			if (!skb) {
271
				IWL_ERR(priv, "Memory allocation fail\n");
272 273
				return -ENOMEM;
			}
274 275 276
			if (nla_put_u32(skb, IWL_TM_ATTR_COMMAND,
					IWL_TM_CMD_DEV2APP_EEPROM_RSP) ||
			    nla_put(skb, IWL_TM_ATTR_EEPROM,
277 278
				    priv->eeprom_blob_size,
				    priv->eeprom_blob))
279
				goto nla_put_failure;
280 281
			status = cfg80211_testmode_reply(skb);
			if (status < 0)
282 283
				IWL_ERR(priv, "Error sending msg : %d\n",
					status);
284
		} else
285
			return -ENODATA;
286 287
		break;

288 289
	case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
		if (!tb[IWL_TM_ATTR_FIXRATE]) {
290
			IWL_ERR(priv, "Missing fixrate setting\n");
291 292
			return -ENOMSG;
		}
293
		priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]);
294 295
		break;

296 297 298
	case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
		skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20 + 8);
		if (!skb) {
299
			IWL_ERR(priv, "Memory allocation fail\n");
300 301
			return -ENOMEM;
		}
302
		if (!priv->ucode_loaded) {
303
			IWL_ERR(priv, "No uCode has not been loaded\n");
304 305
			return -EINVAL;
		} else {
306
			img = &priv->fw->img[priv->cur_ucode];
D
David Spinadel 已提交
307 308
			inst_size = img->sec[IWL_UCODE_SECTION_INST].len;
			data_size = img->sec[IWL_UCODE_SECTION_DATA].len;
309
		}
310
		if (nla_put_u32(skb, IWL_TM_ATTR_FW_TYPE, priv->cur_ucode) ||
311 312 313
		    nla_put_u32(skb, IWL_TM_ATTR_FW_INST_SIZE, inst_size) ||
		    nla_put_u32(skb, IWL_TM_ATTR_FW_DATA_SIZE, data_size))
			goto nla_put_failure;
314 315
		status = cfg80211_testmode_reply(skb);
		if (status < 0)
316
			IWL_ERR(priv, "Error sending msg : %d\n", status);
317 318
		break;

319
	default:
320
		IWL_ERR(priv, "Unknown testmode driver command ID\n");
321 322 323 324 325 326 327 328 329
		return -ENOSYS;
	}
	return status;

nla_put_failure:
	kfree_skb(skb);
	return -EMSGSIZE;
}

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
/*
 * This function handles the user application switch ucode ownership.
 *
 * It retrieves the mandatory fields IWL_TM_ATTR_UCODE_OWNER and
 * decide who the current owner of the uCode
 *
 * If the current owner is OWNERSHIP_TM, then the only host command
 * can deliver to uCode is from testmode, all the other host commands
 * will dropped.
 *
 * default driver is the owner of uCode in normal operational mode
 *
 * @hw: ieee80211_hw object that represents the device
 * @tb: gnl message fields from the user space
 */
static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb)
{
347
	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
348 349 350
	u8 owner;

	if (!tb[IWL_TM_ATTR_UCODE_OWNER]) {
351
		IWL_ERR(priv, "Missing ucode owner\n");
352 353 354 355
		return -ENOMSG;
	}

	owner = nla_get_u8(tb[IWL_TM_ATTR_UCODE_OWNER]);
356
	if (owner == IWL_OWNERSHIP_DRIVER) {
357
		priv->ucode_owner = owner;
I
Ilan Peer 已提交
358
		iwl_test_enable_notifications(&priv->tst, false);
359 360
	} else if (owner == IWL_OWNERSHIP_TM) {
		priv->ucode_owner = owner;
I
Ilan Peer 已提交
361
		iwl_test_enable_notifications(&priv->tst, true);
362
	} else {
363
		IWL_ERR(priv, "Invalid owner\n");
364 365 366 367 368
		return -EINVAL;
	}
	return 0;
}

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
/* 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.
 *
 * This function is invoked when there is user space application sending
 * gnl message through the testmode tunnel NL80211_CMD_TESTMODE regulated
 * by nl80211.
 *
 * It retrieves the mandatory field, IWL_TM_ATTR_COMMAND, before
 * dispatching it to the corresponding handler.
 *
 * If IWL_TM_ATTR_COMMAND is missing, -ENOMSG is replied to user application;
 * -ENOSYS is replied to the user application if the command is unknown;
 * Otherwise, the command is dispatched to the respective handler.
 *
 * @hw: ieee80211_hw object that represents the device
 * @data: pointer to user space message
 * @len: length in byte of @data
 */
388
int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
389
{
390
	struct nlattr *tb[IWL_TM_ATTR_MAX];
391
	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
392 393
	int result;

I
Ilan Peer 已提交
394 395
	result = iwl_test_parse(&priv->tst, tb, data, len);
	if (result)
396 397 398
		return result;

	/* in case multiple accesses to the device happens */
399
	mutex_lock(&priv->mutex);
400 401
	switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) {
	case IWL_TM_CMD_APP2DEV_UCODE:
402 403 404
	case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32:
	case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32:
	case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8:
I
Ilan Peer 已提交
405 406 407 408 409 410 411
	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:
412
		result = iwl_test_handle_cmd(&priv->tst, tb);
413
		break;
I
Ilan Peer 已提交
414

415 416 417 418
	case IWL_TM_CMD_APP2DEV_GET_DEVICENAME:
	case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW:
	case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB:
	case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW:
419
	case IWL_TM_CMD_APP2DEV_GET_EEPROM:
420
	case IWL_TM_CMD_APP2DEV_FIXRATE_REQ:
421
	case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW:
422
	case IWL_TM_CMD_APP2DEV_GET_FW_INFO:
423 424 425
		IWL_DEBUG_INFO(priv, "testmode cmd to driver\n");
		result = iwl_testmode_driver(hw, tb);
		break;
W
Wey-Yi Guy 已提交
426

427 428 429 430 431
	case IWL_TM_CMD_APP2DEV_OWNERSHIP:
		IWL_DEBUG_INFO(priv, "testmode change uCode ownership\n");
		result = iwl_testmode_ownership(hw, tb);
		break;

432
	default:
433
		IWL_ERR(priv, "Unknown testmode command\n");
434 435 436
		result = -ENOSYS;
		break;
	}
437
	mutex_unlock(&priv->mutex);
I
Ilan Peer 已提交
438 439 440

	if (result)
		IWL_ERR(priv, "Test cmd failed result=%d\n", result);
441 442
	return result;
}
443

444
int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
445 446 447
		      struct netlink_callback *cb,
		      void *data, int len)
{
448
	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
449 450 451 452 453 454 455
	int result;
	u32 cmd;

	if (cb->args[3]) {
		/* offset by 1 since commands start at 0 */
		cmd = cb->args[3] - 1;
	} else {
I
Ilan Peer 已提交
456 457 458 459
		struct nlattr *tb[IWL_TM_ATTR_MAX];

		result = iwl_test_parse(&priv->tst, tb, data, len);
		if (result)
460 461 462 463 464 465 466
			return result;

		cmd = nla_get_u32(tb[IWL_TM_ATTR_COMMAND]);
		cb->args[3] = cmd + 1;
	}

	/* in case multiple accesses to the device happens */
467
	mutex_lock(&priv->mutex);
I
Ilan Peer 已提交
468
	result = iwl_test_dump(&priv->tst, cmd, skb, cb);
469
	mutex_unlock(&priv->mutex);
470 471
	return result;
}