iwl-hcmd.c 6.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/******************************************************************************
 *
 * GPL LICENSE SUMMARY
 *
 * Copyright(c) 2008 - 2011 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 <ilw@linux.intel.com>
 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 *****************************************************************************/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <net/mac80211.h>

#include "iwl-dev.h"
#include "iwl-debug.h"
#include "iwl-eeprom.h"
#include "iwl-core.h"


S
Stanislaw Gruszka 已提交
40
const char *il_get_cmd_string(u8 cmd)
41 42
{
	switch (cmd) {
S
Stanislaw Gruszka 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
		IL_CMD(REPLY_ALIVE);
		IL_CMD(REPLY_ERROR);
		IL_CMD(REPLY_RXON);
		IL_CMD(REPLY_RXON_ASSOC);
		IL_CMD(REPLY_QOS_PARAM);
		IL_CMD(REPLY_RXON_TIMING);
		IL_CMD(REPLY_ADD_STA);
		IL_CMD(REPLY_REMOVE_STA);
		IL_CMD(REPLY_WEPKEY);
		IL_CMD(REPLY_3945_RX);
		IL_CMD(REPLY_TX);
		IL_CMD(REPLY_RATE_SCALE);
		IL_CMD(REPLY_LEDS_CMD);
		IL_CMD(REPLY_TX_LINK_QUALITY_CMD);
		IL_CMD(REPLY_CHANNEL_SWITCH);
		IL_CMD(CHANNEL_SWITCH_NOTIFICATION);
		IL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD);
		IL_CMD(SPECTRUM_MEASURE_NOTIFICATION);
		IL_CMD(POWER_TABLE_CMD);
		IL_CMD(PM_SLEEP_NOTIFICATION);
		IL_CMD(PM_DEBUG_STATISTIC_NOTIFIC);
		IL_CMD(REPLY_SCAN_CMD);
		IL_CMD(REPLY_SCAN_ABORT_CMD);
		IL_CMD(SCAN_START_NOTIFICATION);
		IL_CMD(SCAN_RESULTS_NOTIFICATION);
		IL_CMD(SCAN_COMPLETE_NOTIFICATION);
		IL_CMD(BEACON_NOTIFICATION);
		IL_CMD(REPLY_TX_BEACON);
		IL_CMD(REPLY_TX_PWR_TABLE_CMD);
		IL_CMD(REPLY_BT_CONFIG);
		IL_CMD(REPLY_STATISTICS_CMD);
		IL_CMD(STATISTICS_NOTIFICATION);
		IL_CMD(CARD_STATE_NOTIFICATION);
		IL_CMD(MISSED_BEACONS_NOTIFICATION);
		IL_CMD(REPLY_CT_KILL_CONFIG_CMD);
		IL_CMD(SENSITIVITY_CMD);
		IL_CMD(REPLY_PHY_CALIBRATION_CMD);
		IL_CMD(REPLY_RX_PHY_CMD);
		IL_CMD(REPLY_RX_MPDU_CMD);
		IL_CMD(REPLY_RX);
		IL_CMD(REPLY_COMPRESSED_BA);
84 85 86 87 88
	default:
		return "UNKNOWN";

	}
}
S
Stanislaw Gruszka 已提交
89
EXPORT_SYMBOL(il_get_cmd_string);
90 91 92

#define HOST_COMPLETE_TIMEOUT (HZ / 2)

S
Stanislaw Gruszka 已提交
93
static void il_generic_cmd_callback(struct il_priv *il,
S
Stanislaw Gruszka 已提交
94
				     struct il_device_cmd *cmd,
95
				     struct il_rx_pkt *pkt)
96
{
S
Stanislaw Gruszka 已提交
97
	if (pkt->hdr.flags & IL_CMD_FAILED_MSK) {
98
		IL_ERR("Bad return from %s (0x%08X)\n",
S
Stanislaw Gruszka 已提交
99
		il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
100 101 102
		return;
	}

103
#ifdef CONFIG_IWLEGACY_DEBUG
104 105 106
	switch (cmd->hdr.cmd) {
	case REPLY_TX_LINK_QUALITY_CMD:
	case SENSITIVITY_CMD:
107
		D_HC_DUMP("back from %s (0x%08X)\n",
S
Stanislaw Gruszka 已提交
108
		il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
109 110
		break;
	default:
111
		D_HC("back from %s (0x%08X)\n",
S
Stanislaw Gruszka 已提交
112
		il_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
113 114 115 116 117
	}
#endif
}

static int
S
Stanislaw Gruszka 已提交
118
il_send_cmd_async(struct il_priv *il, struct il_host_cmd *cmd)
119 120 121 122 123 124 125 126 127 128
{
	int ret;

	BUG_ON(!(cmd->flags & CMD_ASYNC));

	/* An asynchronous command can not expect an SKB to be set. */
	BUG_ON(cmd->flags & CMD_WANT_SKB);

	/* Assign a generic callback if one is not provided */
	if (!cmd->callback)
S
Stanislaw Gruszka 已提交
129
		cmd->callback = il_generic_cmd_callback;
130

S
Stanislaw Gruszka 已提交
131
	if (test_bit(STATUS_EXIT_PENDING, &il->status))
132 133
		return -EBUSY;

S
Stanislaw Gruszka 已提交
134
	ret = il_enqueue_hcmd(il, cmd);
135
	if (ret < 0) {
136
		IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
S
Stanislaw Gruszka 已提交
137
			  il_get_cmd_string(cmd->id), ret);
138 139 140 141 142
		return ret;
	}
	return 0;
}

S
Stanislaw Gruszka 已提交
143
int il_send_cmd_sync(struct il_priv *il, struct il_host_cmd *cmd)
144 145 146 147
{
	int cmd_idx;
	int ret;

S
Stanislaw Gruszka 已提交
148
	lockdep_assert_held(&il->mutex);
149

150 151 152 153 154
	BUG_ON(cmd->flags & CMD_ASYNC);

	 /* A synchronous command can not have a callback set. */
	BUG_ON(cmd->callback);

155
	D_INFO("Attempting to send sync command %s\n",
S
Stanislaw Gruszka 已提交
156
			il_get_cmd_string(cmd->id));
157

S
Stanislaw Gruszka 已提交
158
	set_bit(STATUS_HCMD_ACTIVE, &il->status);
159
	D_INFO("Setting HCMD_ACTIVE for command %s\n",
S
Stanislaw Gruszka 已提交
160
			il_get_cmd_string(cmd->id));
161

S
Stanislaw Gruszka 已提交
162
	cmd_idx = il_enqueue_hcmd(il, cmd);
163 164
	if (cmd_idx < 0) {
		ret = cmd_idx;
165
		IL_ERR("Error sending %s: enqueue_hcmd failed: %d\n",
S
Stanislaw Gruszka 已提交
166
			  il_get_cmd_string(cmd->id), ret);
167 168 169
		goto out;
	}

S
Stanislaw Gruszka 已提交
170 171
	ret = wait_event_timeout(il->wait_command_queue,
			!test_bit(STATUS_HCMD_ACTIVE, &il->status),
172 173
			HOST_COMPLETE_TIMEOUT);
	if (!ret) {
S
Stanislaw Gruszka 已提交
174
		if (test_bit(STATUS_HCMD_ACTIVE, &il->status)) {
175
			IL_ERR(
176
				"Error sending %s: time out after %dms.\n",
S
Stanislaw Gruszka 已提交
177
				il_get_cmd_string(cmd->id),
178 179
				jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));

S
Stanislaw Gruszka 已提交
180
			clear_bit(STATUS_HCMD_ACTIVE, &il->status);
181
			D_INFO(
182
				"Clearing HCMD_ACTIVE for command %s\n",
S
Stanislaw Gruszka 已提交
183
				       il_get_cmd_string(cmd->id));
184 185 186 187 188
			ret = -ETIMEDOUT;
			goto cancel;
		}
	}

S
Stanislaw Gruszka 已提交
189
	if (test_bit(STATUS_RF_KILL_HW, &il->status)) {
190
		IL_ERR("Command %s aborted: RF KILL Switch\n",
S
Stanislaw Gruszka 已提交
191
			       il_get_cmd_string(cmd->id));
192 193 194
		ret = -ECANCELED;
		goto fail;
	}
S
Stanislaw Gruszka 已提交
195
	if (test_bit(STATUS_FW_ERROR, &il->status)) {
196
		IL_ERR("Command %s failed: FW Error\n",
S
Stanislaw Gruszka 已提交
197
			       il_get_cmd_string(cmd->id));
198 199 200 201
		ret = -EIO;
		goto fail;
	}
	if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
202
		IL_ERR("Error: Response NULL in '%s'\n",
S
Stanislaw Gruszka 已提交
203
			  il_get_cmd_string(cmd->id));
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
		ret = -EIO;
		goto cancel;
	}

	ret = 0;
	goto out;

cancel:
	if (cmd->flags & CMD_WANT_SKB) {
		/*
		 * Cancel the CMD_WANT_SKB flag for the cmd in the
		 * TX cmd queue. Otherwise in case the cmd comes
		 * in later, it will possibly set an invalid
		 * address (cmd->meta.source).
		 */
S
Stanislaw Gruszka 已提交
219
		il->txq[il->cmd_queue].meta[cmd_idx].flags &=
220 221 222 223
							~CMD_WANT_SKB;
	}
fail:
	if (cmd->reply_page) {
S
Stanislaw Gruszka 已提交
224
		il_free_pages(il, cmd->reply_page);
225 226 227 228 229
		cmd->reply_page = 0;
	}
out:
	return ret;
}
S
Stanislaw Gruszka 已提交
230
EXPORT_SYMBOL(il_send_cmd_sync);
231

S
Stanislaw Gruszka 已提交
232
int il_send_cmd(struct il_priv *il, struct il_host_cmd *cmd)
233 234
{
	if (cmd->flags & CMD_ASYNC)
S
Stanislaw Gruszka 已提交
235
		return il_send_cmd_async(il, cmd);
236

S
Stanislaw Gruszka 已提交
237
	return il_send_cmd_sync(il, cmd);
238
}
S
Stanislaw Gruszka 已提交
239
EXPORT_SYMBOL(il_send_cmd);
240 241

int
S
Stanislaw Gruszka 已提交
242
il_send_cmd_pdu(struct il_priv *il, u8 id, u16 len, const void *data)
243
{
S
Stanislaw Gruszka 已提交
244
	struct il_host_cmd cmd = {
245 246 247 248 249
		.id = id,
		.len = len,
		.data = data,
	};

S
Stanislaw Gruszka 已提交
250
	return il_send_cmd_sync(il, &cmd);
251
}
S
Stanislaw Gruszka 已提交
252
EXPORT_SYMBOL(il_send_cmd_pdu);
253

S
Stanislaw Gruszka 已提交
254
int il_send_cmd_pdu_async(struct il_priv *il,
255
			   u8 id, u16 len, const void *data,
S
Stanislaw Gruszka 已提交
256
			   void (*callback)(struct il_priv *il,
S
Stanislaw Gruszka 已提交
257
					    struct il_device_cmd *cmd,
258
					    struct il_rx_pkt *pkt))
259
{
S
Stanislaw Gruszka 已提交
260
	struct il_host_cmd cmd = {
261 262 263 264 265 266 267 268
		.id = id,
		.len = len,
		.data = data,
	};

	cmd.flags |= CMD_ASYNC;
	cmd.callback = callback;

S
Stanislaw Gruszka 已提交
269
	return il_send_cmd_async(il, &cmd);
270
}
S
Stanislaw Gruszka 已提交
271
EXPORT_SYMBOL(il_send_cmd_pdu_async);