Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  1/******************************************************************************
  2 *
  3 * GPL LICENSE SUMMARY
  4 *
  5 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of version 2 of the GNU General Public License as
  9 * published by the Free Software Foundation.
 10 *
 11 * This program is distributed in the hope that it will be useful, but
 12 * WITHOUT ANY WARRANTY; without even the implied warranty of
 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 14 * General Public License for more details.
 15 *
 16 * You should have received a copy of the GNU General Public License
 17 * along with this program; if not, write to the Free Software
 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
 19 * USA
 20 *
 21 * The full GNU General Public License is included in this distribution
 22 * in the file called LICENSE.GPL.
 23 *
 24 * Contact Information:
 25 *  Intel Linux Wireless <ilw@linux.intel.com>
 26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
 27 *****************************************************************************/
 28
 29#include <linux/kernel.h>
 30#include <linux/module.h>
 31#include <linux/sched.h>
 32#include <net/mac80211.h>
 33
 34#include "iwl-dev.h"
 35#include "iwl-debug.h"
 36#include "iwl-eeprom.h"
 37#include "iwl-core.h"
 38
 39
 40const char *iwl_legacy_get_cmd_string(u8 cmd)
 41{
 42	switch (cmd) {
 43		IWL_CMD(REPLY_ALIVE);
 44		IWL_CMD(REPLY_ERROR);
 45		IWL_CMD(REPLY_RXON);
 46		IWL_CMD(REPLY_RXON_ASSOC);
 47		IWL_CMD(REPLY_QOS_PARAM);
 48		IWL_CMD(REPLY_RXON_TIMING);
 49		IWL_CMD(REPLY_ADD_STA);
 50		IWL_CMD(REPLY_REMOVE_STA);
 51		IWL_CMD(REPLY_WEPKEY);
 52		IWL_CMD(REPLY_3945_RX);
 53		IWL_CMD(REPLY_TX);
 54		IWL_CMD(REPLY_RATE_SCALE);
 55		IWL_CMD(REPLY_LEDS_CMD);
 56		IWL_CMD(REPLY_TX_LINK_QUALITY_CMD);
 57		IWL_CMD(REPLY_CHANNEL_SWITCH);
 58		IWL_CMD(CHANNEL_SWITCH_NOTIFICATION);
 59		IWL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD);
 60		IWL_CMD(SPECTRUM_MEASURE_NOTIFICATION);
 61		IWL_CMD(POWER_TABLE_CMD);
 62		IWL_CMD(PM_SLEEP_NOTIFICATION);
 63		IWL_CMD(PM_DEBUG_STATISTIC_NOTIFIC);
 64		IWL_CMD(REPLY_SCAN_CMD);
 65		IWL_CMD(REPLY_SCAN_ABORT_CMD);
 66		IWL_CMD(SCAN_START_NOTIFICATION);
 67		IWL_CMD(SCAN_RESULTS_NOTIFICATION);
 68		IWL_CMD(SCAN_COMPLETE_NOTIFICATION);
 69		IWL_CMD(BEACON_NOTIFICATION);
 70		IWL_CMD(REPLY_TX_BEACON);
 71		IWL_CMD(REPLY_TX_PWR_TABLE_CMD);
 72		IWL_CMD(REPLY_BT_CONFIG);
 73		IWL_CMD(REPLY_STATISTICS_CMD);
 74		IWL_CMD(STATISTICS_NOTIFICATION);
 75		IWL_CMD(CARD_STATE_NOTIFICATION);
 76		IWL_CMD(MISSED_BEACONS_NOTIFICATION);
 77		IWL_CMD(REPLY_CT_KILL_CONFIG_CMD);
 78		IWL_CMD(SENSITIVITY_CMD);
 79		IWL_CMD(REPLY_PHY_CALIBRATION_CMD);
 80		IWL_CMD(REPLY_RX_PHY_CMD);
 81		IWL_CMD(REPLY_RX_MPDU_CMD);
 82		IWL_CMD(REPLY_RX);
 83		IWL_CMD(REPLY_COMPRESSED_BA);
 84	default:
 85		return "UNKNOWN";
 86
 87	}
 88}
 89EXPORT_SYMBOL(iwl_legacy_get_cmd_string);
 90
 91#define HOST_COMPLETE_TIMEOUT (HZ / 2)
 92
 93static void iwl_legacy_generic_cmd_callback(struct iwl_priv *priv,
 94				     struct iwl_device_cmd *cmd,
 95				     struct iwl_rx_packet *pkt)
 96{
 97	if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
 98		IWL_ERR(priv, "Bad return from %s (0x%08X)\n",
 99		iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
100		return;
101	}
102
103#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG
104	switch (cmd->hdr.cmd) {
105	case REPLY_TX_LINK_QUALITY_CMD:
106	case SENSITIVITY_CMD:
107		IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n",
108		iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
109		break;
110	default:
111		IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n",
112		iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags);
113	}
114#endif
115}
116
117static int
118iwl_legacy_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
119{
120	int ret;
121
122	BUG_ON(!(cmd->flags & CMD_ASYNC));
123
124	/* An asynchronous command can not expect an SKB to be set. */
125	BUG_ON(cmd->flags & CMD_WANT_SKB);
126
127	/* Assign a generic callback if one is not provided */
128	if (!cmd->callback)
129		cmd->callback = iwl_legacy_generic_cmd_callback;
130
131	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
132		return -EBUSY;
133
134	ret = iwl_legacy_enqueue_hcmd(priv, cmd);
135	if (ret < 0) {
136		IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n",
137			  iwl_legacy_get_cmd_string(cmd->id), ret);
138		return ret;
139	}
140	return 0;
141}
142
143int iwl_legacy_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
144{
145	int cmd_idx;
146	int ret;
147
148	lockdep_assert_held(&priv->mutex);
149
150	BUG_ON(cmd->flags & CMD_ASYNC);
151
152	 /* A synchronous command can not have a callback set. */
153	BUG_ON(cmd->callback);
154
155	IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n",
156			iwl_legacy_get_cmd_string(cmd->id));
157
158	set_bit(STATUS_HCMD_ACTIVE, &priv->status);
159	IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s\n",
160			iwl_legacy_get_cmd_string(cmd->id));
161
162	cmd_idx = iwl_legacy_enqueue_hcmd(priv, cmd);
163	if (cmd_idx < 0) {
164		ret = cmd_idx;
165		IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n",
166			  iwl_legacy_get_cmd_string(cmd->id), ret);
167		goto out;
168	}
169
170	ret = wait_event_timeout(priv->wait_command_queue,
171			!test_bit(STATUS_HCMD_ACTIVE, &priv->status),
172			HOST_COMPLETE_TIMEOUT);
173	if (!ret) {
174		if (test_bit(STATUS_HCMD_ACTIVE, &priv->status)) {
175			IWL_ERR(priv,
176				"Error sending %s: time out after %dms.\n",
177				iwl_legacy_get_cmd_string(cmd->id),
178				jiffies_to_msecs(HOST_COMPLETE_TIMEOUT));
179
180			clear_bit(STATUS_HCMD_ACTIVE, &priv->status);
181			IWL_DEBUG_INFO(priv,
182				"Clearing HCMD_ACTIVE for command %s\n",
183				       iwl_legacy_get_cmd_string(cmd->id));
184			ret = -ETIMEDOUT;
185			goto cancel;
186		}
187	}
188
189	if (test_bit(STATUS_RF_KILL_HW, &priv->status)) {
190		IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n",
191			       iwl_legacy_get_cmd_string(cmd->id));
192		ret = -ECANCELED;
193		goto fail;
194	}
195	if (test_bit(STATUS_FW_ERROR, &priv->status)) {
196		IWL_ERR(priv, "Command %s failed: FW Error\n",
197			       iwl_legacy_get_cmd_string(cmd->id));
198		ret = -EIO;
199		goto fail;
200	}
201	if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) {
202		IWL_ERR(priv, "Error: Response NULL in '%s'\n",
203			  iwl_legacy_get_cmd_string(cmd->id));
204		ret = -EIO;
205		goto cancel;
206	}
207
208	ret = 0;
209	goto out;
210
211cancel:
212	if (cmd->flags & CMD_WANT_SKB) {
213		/*
214		 * Cancel the CMD_WANT_SKB flag for the cmd in the
215		 * TX cmd queue. Otherwise in case the cmd comes
216		 * in later, it will possibly set an invalid
217		 * address (cmd->meta.source).
218		 */
219		priv->txq[priv->cmd_queue].meta[cmd_idx].flags &=
220							~CMD_WANT_SKB;
221	}
222fail:
223	if (cmd->reply_page) {
224		iwl_legacy_free_pages(priv, cmd->reply_page);
225		cmd->reply_page = 0;
226	}
227out:
228	return ret;
229}
230EXPORT_SYMBOL(iwl_legacy_send_cmd_sync);
231
232int iwl_legacy_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd)
233{
234	if (cmd->flags & CMD_ASYNC)
235		return iwl_legacy_send_cmd_async(priv, cmd);
236
237	return iwl_legacy_send_cmd_sync(priv, cmd);
238}
239EXPORT_SYMBOL(iwl_legacy_send_cmd);
240
241int
242iwl_legacy_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data)
243{
244	struct iwl_host_cmd cmd = {
245		.id = id,
246		.len = len,
247		.data = data,
248	};
249
250	return iwl_legacy_send_cmd_sync(priv, &cmd);
251}
252EXPORT_SYMBOL(iwl_legacy_send_cmd_pdu);
253
254int iwl_legacy_send_cmd_pdu_async(struct iwl_priv *priv,
255			   u8 id, u16 len, const void *data,
256			   void (*callback)(struct iwl_priv *priv,
257					    struct iwl_device_cmd *cmd,
258					    struct iwl_rx_packet *pkt))
259{
260	struct iwl_host_cmd cmd = {
261		.id = id,
262		.len = len,
263		.data = data,
264	};
265
266	cmd.flags |= CMD_ASYNC;
267	cmd.callback = callback;
268
269	return iwl_legacy_send_cmd_async(priv, &cmd);
270}
271EXPORT_SYMBOL(iwl_legacy_send_cmd_pdu_async);