Ejemplo n.º 1
0
static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
{
	u32 dummy;
	int ret;

	ath6kl_warn("firmware crashed\n");

	/*
	 * read counter to clear the interrupt, the debug error interrupt is
	 * counter 0.
	 */
	ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
				     (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
	if (ret)
		ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);

	ath6kl_hif_dump_fw_crash(dev->ar);
	if (debug_mask & ATH6KL_DBG_STACK_DUMP)
		ath6kl_hif_dump_fw_more(dev->ar, DUMP_MASK_FULL_STACK |
					DUMP_MASK_DBGLOG);
	ath6kl_read_fwlogs(dev->ar);
	ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT);

	return ret;
}
Ejemplo n.º 2
0
static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
{
	__le32 regdump_val[REGISTER_DUMP_LEN_MAX];
	u32 i, address, regdump_addr = 0;
	int ret;

	if (ar->target_type != TARGET_TYPE_AR6003)
		return;

	/* the reg dump pointer is copied to the host interest area */
	address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
	address = TARG_VTOP(ar->target_type, address);

	/* read RAM location through diagnostic window */
	ret = ath6kl_diag_read32(ar, address, &regdump_addr);

	if (ret || !regdump_addr) {
		ath6kl_warn("failed to get ptr to register dump area: %d\n",
			    ret);
		return;
	}

	ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n",
		regdump_addr);
	regdump_addr = TARG_VTOP(ar->target_type, regdump_addr);

	/* fetch register dump data */
	ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)&regdump_val[0],
				  REG_DUMP_COUNT_AR6003 * (sizeof(u32)));
	if (ret) {
		ath6kl_warn("failed to get register dump: %d\n", ret);
		return;
	}

	ath6kl_info("crash dump:\n");
	ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version,
		    ar->wiphy->fw_version);

	BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4);

	for (i = 0; i < REG_DUMP_COUNT_AR6003 / 4; i++) {
		ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
			    4 * i,
			    le32_to_cpu(regdump_val[i]),
			    le32_to_cpu(regdump_val[i + 1]),
			    le32_to_cpu(regdump_val[i + 2]),
			    le32_to_cpu(regdump_val[i + 3]));
	}

}
Ejemplo n.º 3
0
static void ath6kl_recovery_hb_timer(unsigned long data)
{
	struct ath6kl *ar = (struct ath6kl *) data;
	int err;

	if (test_bit(RECOVERY_CLEANUP, &ar->flag) ||
	    (ar->state == ATH6KL_STATE_RECOVERY) ||
	    !test_bit(WMI_READY, &ar->flag))
		return;

	if (ar->fw_recovery.hb_pending)
		ar->fw_recovery.hb_misscnt++;
	else
		ar->fw_recovery.hb_misscnt = 0;

	if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) {
		ar->fw_recovery.hb_misscnt = 0;
		ar->fw_recovery.seq_num = 0;
		ar->fw_recovery.hb_pending = false;
		ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE);
		return;
	}

	ar->fw_recovery.seq_num++;
	ar->fw_recovery.hb_pending = true;

	err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi,
						ar->fw_recovery.seq_num, 0);
	if (err)
		ath6kl_warn("Failed to send hb challenge request, err:%d\n",
			    err);

	mod_timer(&ar->fw_recovery.hb_timer, jiffies +
		  msecs_to_jiffies(ar->fw_recovery.hb_poll));
}
Ejemplo n.º 4
0
static int ath6kl_fetch_board_file(struct ath6kl *ar)
{
	const char *filename;
	int ret;

	switch (ar->version.target_ver) {
	case AR6003_REV2_VERSION:
		filename = AR6003_REV2_BOARD_DATA_FILE;
		break;
	default:
		filename = AR6003_REV3_BOARD_DATA_FILE;
		break;
	}

	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
			    &ar->fw_board_len);
	if (ret == 0) {
		/* managed to get proper board file */
		return 0;
	}

	/* there was no proper board file, try to use default instead */
	ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",
		    filename, ret);

	switch (ar->version.target_ver) {
	case AR6003_REV2_VERSION:
		filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE;
		break;
	default:
		filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE;
		break;
	}

	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,
			    &ar->fw_board_len);
	if (ret) {
		ath6kl_err("Failed to get default board file %s: %d\n",
			   filename, ret);
		return ret;
	}

	ath6kl_warn("WARNING! No proper board file was not found, instead using a default board file.\n");
	ath6kl_warn("Most likely your hardware won't work as specified. Install correct board file!\n");

	return 0;
}
Ejemplo n.º 5
0
static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
{
	u32 dummy;
	int ret;

	ath6kl_warn("firmware crashed\n");

	
	ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
				     (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
	if (ret)
		ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);

	ath6kl_hif_dump_fw_crash(dev->ar);
	ath6kl_read_fwlogs(dev->ar);
	ath6kl_tm_crash_event(dev->ar, ATH6KL_TM_FW_TGT_ASSERT);

	return ret;
}
Ejemplo n.º 6
0
static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
{
	u32 dummy;
	int ret;

	ath6kl_warn("firmware crashed\n");

	/*
	 * read counter to clear the interrupt, the debug error interrupt is
	 * counter 0.
	 */
	ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
				     (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
	if (ret)
		ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);

	ath6kl_hif_dump_fw_crash(dev->ar);

	return ret;
}
Ejemplo n.º 7
0
/*
 * Read from the hardware through its diagnostic window. No cooperation
 * from the firmware is required for this.
 */
int ath6kl_diag_read32(struct ath6kl *ar, u32 address, u32 *value)
{
	int ret;

	ret = ath6kl_hif_diag_read32(ar, address, value);
	if (ret) {
		ath6kl_warn("failed to read32 through diagnose window: %d\n",
			    ret);
		return ret;
	}

	return 0;
}
Ejemplo n.º 8
0
void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len)
{
	struct sk_buff *skb;

	if (!buf || buf_len == 0)
		return;

	skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL);
	if (!skb) {
		ath6kl_warn("failed to allocate testmode rx skb!\n");
		return;
	}
	if (nla_put_u32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD) ||
	    nla_put(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf))
		goto nla_put_failure;
	cfg80211_testmode_event(skb, GFP_KERNEL);
	return;

nla_put_failure:
	kfree_skb(skb);
	ath6kl_warn("nla_put failed on testmode rx skb!\n");
}
Ejemplo n.º 9
0
static void ath6kl_hif_dump_fw_more(struct ath6kl *ar, u32 mask)
{
	u32 fw_dump_addr, fw_dump_len;
	u32 address;
	int ret;

	if (ar->target_type != TARGET_TYPE_AR6003 ) {
		ath6kl_warn("not support dump stack for type: %x\n", ar->target_type);
		return;
	}

	if(mask & DUMP_MASK_FULL_STACK) {
		if(ar->wiphy->hw_version == AR6003_HW_2_1_1_VERSION) {
			fw_dump_addr = AR6003_HW211_KERNELSTACK_BASE - DUMP_STACK_OFFSET;
			fw_dump_len = AR6003_HW211_KERNELSTACK_SIZE + DUMP_STACK_OFFSET;
			ath6kl_warn("firmware stack:0x%x, len:0x%x\n",
				    AR6003_HW211_KERNELSTACK_BASE,
				    AR6003_HW211_KERNELSTACK_SIZE);
			ath6kl_hif_dump(ar, fw_dump_addr, fw_dump_len);
		}
	}

	if(mask & DUMP_MASK_DBGLOG) {
		if(ar->wiphy->hw_version == AR6003_HW_2_1_1_VERSION) {
			address = TARG_VTOP(ar->target_type,
					    AR6003_HW211_DBGLOG_ADDR);
			ret = ath6kl_diag_read32(ar, address, &fw_dump_addr);
			if(!ret && fw_dump_addr) {
				fw_dump_len = AR6003_HW211_DBGLOG_SIZE;
				ath6kl_warn("fw dblog:0x%x, len:0x%x\n",
					    fw_dump_addr,
					    AR6003_HW211_DBGLOG_SIZE);
				ath6kl_hif_dump(ar, fw_dump_addr, fw_dump_len);
			}
		}
	}
}
Ejemplo n.º 10
0
static void ath6kl_hif_dump(struct ath6kl *ar, u32 fw_dump_addr, u32 len)
{
	__le32 regdump_val[MAX_DUMP_BYTE_NUM_ONE_ITERATION / 4];
	u32 read_len = 0;
	u32 i = 0,count;
	int ret;
	u32 phy_addr = TARG_VTOP(ar->target_type, fw_dump_addr);

	len = (len + 3) & (~0x3);
	fw_dump_addr = (fw_dump_addr + 3) & (~0x3);

	while(len) {
		read_len = len;
		if(read_len > MAX_DUMP_BYTE_NUM_ONE_ITERATION)
			read_len = MAX_DUMP_BYTE_NUM_ONE_ITERATION;

		phy_addr = TARG_VTOP(ar->target_type, fw_dump_addr);
		ret = ath6kl_diag_read(ar, phy_addr, (u8 *) &regdump_val[0], read_len);
		if (ret) {
			ath6kl_warn("failed to get register dump: %d\n", ret);
			return;
		}

		count = read_len / 4;
		for (i = 0; i < count; i += 4) {
			ath6kl_info("0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
				    le32_to_cpu(fw_dump_addr + 4 * i),
				    le32_to_cpu(regdump_val[i]),
				    le32_to_cpu(regdump_val[i + 1]),
				    le32_to_cpu(regdump_val[i + 2]),
				    le32_to_cpu(regdump_val[i + 3]));
		}

		len -= read_len;
		fw_dump_addr += read_len;
	}
}
Ejemplo n.º 11
0
int ath6kl_read_fwlogs(struct ath6kl *ar)
{
	struct ath6kl_dbglog_hdr debug_hdr;
	struct ath6kl_dbglog_buf debug_buf;
	u32 address, length, dropped, firstbuf, debug_hdr_addr;
	int ret = 0, loop;
	u8 *buf;

	buf = kmalloc(ATH6KL_FWLOG_PAYLOAD_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	address = TARG_VTOP(ar->target_type,
			    ath6kl_get_hi_item_addr(ar,
						    HI_ITEM(hi_dbglog_hdr)));

	ret = ath6kl_diag_read32(ar, address, &debug_hdr_addr);
	if (ret)
		goto out;

	/* Get the contents of the ring buffer */
	if (debug_hdr_addr == 0) {
		ath6kl_warn("Invalid address for debug_hdr_addr\n");
		ret = -EINVAL;
		goto out;
	}

	address = TARG_VTOP(ar->target_type, debug_hdr_addr);
	ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr));

	address = TARG_VTOP(ar->target_type,
			    le32_to_cpu(debug_hdr.dbuf_addr));
	firstbuf = address;
	dropped = le32_to_cpu(debug_hdr.dropped);
	ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));

	loop = 100;

	do {
		address = TARG_VTOP(ar->target_type,
				    le32_to_cpu(debug_buf.buffer_addr));
		length = le32_to_cpu(debug_buf.length);

		if (length != 0 && (le32_to_cpu(debug_buf.length) <=
				    le32_to_cpu(debug_buf.bufsize))) {
			length = ALIGN(length, 4);

			ret = ath6kl_diag_read(ar, address,
					       buf, length);
			if (ret)
				goto out;

			ath6kl_debug_fwlog_event(ar, buf, length);
		}

		address = TARG_VTOP(ar->target_type,
				    le32_to_cpu(debug_buf.next));
		ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf));
		if (ret)
			goto out;

		loop--;

		if (WARN_ON(loop == 0)) {
			ret = -ETIMEDOUT;
			goto out;
		}
	} while (address != firstbuf);

out:
	kfree(buf);

	return ret;
}
Ejemplo n.º 12
0
static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
				struct cfg80211_scan_request *request)
{
	struct ath6kl *ar = (struct ath6kl *)ath6kl_priv(ndev);
	s8 n_channels = 0;
	u16 *channels = NULL;
	int ret = 0;

	if (!ath6kl_cfg80211_ready(ar))
		return -EIO;

	if (!ar->usr_bss_filter) {
		clear_bit(CLEAR_BSSFILTER_ON_BEACON, &ar->flag);
		ret = ath6kl_wmi_bssfilter_cmd(
			ar->wmi,
			(test_bit(CONNECTED, &ar->flag) ?
			 ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0);
		if (ret) {
			ath6kl_err("couldn't set bss filtering\n");
			return ret;
		}
	}

	if (request->n_ssids && request->ssids[0].ssid_len) {
		u8 i;

		if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1))
			request->n_ssids = MAX_PROBED_SSID_INDEX - 1;

		for (i = 0; i < request->n_ssids; i++)
			ath6kl_wmi_probedssid_cmd(ar->wmi, i + 1,
						  SPECIFIC_SSID_FLAG,
						  request->ssids[i].ssid_len,
						  request->ssids[i].ssid);
	}

	if (request->ie) {
		ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_REQ,
					       request->ie, request->ie_len);
		if (ret) {
			ath6kl_err("failed to set Probe Request appie for "
				   "scan");
			return ret;
		}
	}

	/*
	 * Scan only the requested channels if the request specifies a set of
	 * channels. If the list is longer than the target supports, do not
	 * configure the list and instead, scan all available channels.
	 */
	if (request->n_channels > 0 &&
	    request->n_channels <= WMI_MAX_CHANNELS) {
		u8 i;

		n_channels = request->n_channels;

		channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
		if (channels == NULL) {
			ath6kl_warn("failed to set scan channels, "
				    "scan all channels");
			n_channels = 0;
		}

		for (i = 0; i < n_channels; i++)
			channels[i] = request->channels[i]->center_freq;
	}

	ret = ath6kl_wmi_startscan_cmd(ar->wmi, WMI_LONG_SCAN, 0,
				       false, 0, 0, n_channels, channels);
	if (ret)
		ath6kl_err("wmi_startscan_cmd failed\n");
	else
		ar->scan_req = request;

	kfree(channels);

	return ret;
}