/*
* Atheros CARL9170 driver
*
* 802.11 & command trap routines
*
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
* Copyright 2009, 2010, Christian Lamparter <chunkeey@googlemail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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; see the file COPYING. If not, see
* http://www.gnu.org/licenses/.
*
* This file incorporates work covered by the following copyright and
* permission notice:
* Copyright (c) 2007-2008 Atheros Communications, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/crc32.h>
#include <net/mac80211.h>
#include "carl9170.h"
#include "hw.h"
#include "cmd.h"
static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
{
bool restart = false;
enum carl9170_restart_reasons reason = CARL9170_RR_NO_REASON;
if (len > 3) {
if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
ar->fw.err_counter++;
if (ar->fw.err_counter > 3) {
restart = true;
reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
}
}
if (memcmp(buf, CARL9170_BUG_MAGIC, 3) == 0) {
ar->fw.bug_counter++;
restart = true;
reason = CARL9170_RR_FATAL_FIRMWARE_ERROR;
}
}
wiphy_info(ar->hw->wiphy, "FW: %.*s\n", len, buf);
if (restart)
carl9170_restart(ar, reason);
}
static void carl9170_handle_ps(struct ar9170 *ar, struct carl9170_rsp *rsp)
{
u32 ps;
bool new_ps;
ps = le32_to_cpu(rsp->psm.state);
new_ps = (ps & CARL9170_PSM_COUNTER) != CARL9170_PSM_WAKE;
if (ar->ps.state != new_ps) {
if (!new_ps) {
ar->ps.sleep_ms = jiffies_to_msecs(jiffies -
ar->ps.last_action);
}
ar->ps.last_action = jiffies;
ar->ps.state = new_ps;
}
}
static int carl9170_check_sequence(struct ar9170 *ar, unsigned int seq)
{
if (ar->cmd_seq < -1)
return 0;
/*
* Initialize Counter
*/
if (ar->cmd_seq < 0)
ar->cmd_seq = seq;
/*
* The sequence is strictly monotonic increasing and it never skips!
*
* Therefore we can safely assume that whenever we received an
* unexpected sequence we have lost some valuable data.
*/
if (seq != ar->cmd_seq) {
int count;
count = (seq - ar->cmd_seq) % ar->fw.cmd_bufs;
wiphy_err(ar->hw->wiphy, "lost %d command responses/traps! "
"w:%d g:%d\n", count, ar->cmd_seq, seq);
carl9170_restart(ar, CARL9170_RR_LOST_RSP);
return -EIO;
}
ar->cmd_seq = (ar->cmd_seq + 1) % ar->fw.cmd_bufs;
return 0;
}
static void carl9170_cmd_callback(struct ar9170 *ar, u32 len, void *buffer)
{
/*
* Some commands may have a variable response length
* and we cannot predict the correct length in advance.
* So we only check if we provided enough space for the data.
*/
if (unlikely(ar->readlen != (len - 4))) {
dev_warn(&ar->udev->dev, "received invalid command response:"
"got %d, instead of %d\n", len - 4, ar->readlen);
print_hex_dump_bytes("carl9170 cmd:", DUMP_PREFIX_OFFSET,
ar->cmd_buf, (ar->cmd.hdr.len + 4) & 0x3f);
print_hex_dump_bytes("carl9170 rsp:", DUMP_PREFIX_OFFSET,
buffer, len);
/*
* Do not complete. The command times out,
* and we get a stack trace from there.
*/
carl9170_restart(ar, CARL9170_RR_INVALID_RSP);
}
spin_lock(&ar->cmd_lock);
if (ar->readbuf)