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; }
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, ®dump_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 *)®dump_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])); } }
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)); }
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; }
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; }
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; }
/* * 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; }
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"); }
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); } } } }
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 *) ®dump_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; } }
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; }
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; }