|
@@ -966,6 +966,111 @@ static const struct file_operations fops_diag_reg_write = {
|
|
|
.llseek = default_llseek,
|
|
|
};
|
|
|
|
|
|
+int ath6kl_debug_roam_tbl_event(struct ath6kl *ar, const void *buf,
|
|
|
+ size_t len)
|
|
|
+{
|
|
|
+ const struct wmi_target_roam_tbl *tbl;
|
|
|
+ u16 num_entries;
|
|
|
+
|
|
|
+ if (len < sizeof(*tbl))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ tbl = (const struct wmi_target_roam_tbl *) buf;
|
|
|
+ num_entries = le16_to_cpu(tbl->num_entries);
|
|
|
+ if (sizeof(*tbl) + num_entries * sizeof(struct wmi_bss_roam_info) >
|
|
|
+ len)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (ar->debug.roam_tbl == NULL ||
|
|
|
+ ar->debug.roam_tbl_len < (unsigned int) len) {
|
|
|
+ kfree(ar->debug.roam_tbl);
|
|
|
+ ar->debug.roam_tbl = kmalloc(len, GFP_ATOMIC);
|
|
|
+ if (ar->debug.roam_tbl == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(ar->debug.roam_tbl, buf, len);
|
|
|
+ ar->debug.roam_tbl_len = len;
|
|
|
+
|
|
|
+ if (test_bit(ROAM_TBL_PEND, &ar->flag)) {
|
|
|
+ clear_bit(ROAM_TBL_PEND, &ar->flag);
|
|
|
+ wake_up(&ar->event_wq);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t ath6kl_roam_table_read(struct file *file, char __user *user_buf,
|
|
|
+ size_t count, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct ath6kl *ar = file->private_data;
|
|
|
+ int ret;
|
|
|
+ long left;
|
|
|
+ struct wmi_target_roam_tbl *tbl;
|
|
|
+ u16 num_entries, i;
|
|
|
+ char *buf;
|
|
|
+ unsigned int len, buf_len;
|
|
|
+ ssize_t ret_cnt;
|
|
|
+
|
|
|
+ if (down_interruptible(&ar->sem))
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ set_bit(ROAM_TBL_PEND, &ar->flag);
|
|
|
+
|
|
|
+ ret = ath6kl_wmi_get_roam_tbl_cmd(ar->wmi);
|
|
|
+ if (ret) {
|
|
|
+ up(&ar->sem);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ left = wait_event_interruptible_timeout(
|
|
|
+ ar->event_wq, !test_bit(ROAM_TBL_PEND, &ar->flag), WMI_TIMEOUT);
|
|
|
+ up(&ar->sem);
|
|
|
+
|
|
|
+ if (left <= 0)
|
|
|
+ return -ETIMEDOUT;
|
|
|
+
|
|
|
+ if (ar->debug.roam_tbl == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ tbl = (struct wmi_target_roam_tbl *) ar->debug.roam_tbl;
|
|
|
+ num_entries = le16_to_cpu(tbl->num_entries);
|
|
|
+
|
|
|
+ buf_len = 100 + num_entries * 100;
|
|
|
+ buf = kzalloc(buf_len, GFP_KERNEL);
|
|
|
+ if (buf == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+ len = 0;
|
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
|
+ "roam_mode=%u\n\n"
|
|
|
+ "# roam_util bssid rssi rssidt last_rssi util bias\n",
|
|
|
+ le16_to_cpu(tbl->roam_mode));
|
|
|
+
|
|
|
+ for (i = 0; i < num_entries; i++) {
|
|
|
+ struct wmi_bss_roam_info *info = &tbl->info[i];
|
|
|
+ len += scnprintf(buf + len, buf_len - len,
|
|
|
+ "%d %pM %d %d %d %d %d\n",
|
|
|
+ a_sle32_to_cpu(info->roam_util), info->bssid,
|
|
|
+ info->rssi, info->rssidt, info->last_rssi,
|
|
|
+ info->util, info->bias);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (len > buf_len)
|
|
|
+ len = buf_len;
|
|
|
+
|
|
|
+ ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
|
|
|
+
|
|
|
+ kfree(buf);
|
|
|
+ return ret_cnt;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations fops_roam_table = {
|
|
|
+ .read = ath6kl_roam_table_read,
|
|
|
+ .open = ath6kl_debugfs_open,
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .llseek = default_llseek,
|
|
|
+};
|
|
|
+
|
|
|
int ath6kl_debug_init(struct ath6kl *ar)
|
|
|
{
|
|
|
ar->debug.fwlog_buf.buf = vmalloc(ATH6KL_FWLOG_SIZE);
|
|
@@ -1024,6 +1129,9 @@ int ath6kl_debug_init(struct ath6kl *ar)
|
|
|
debugfs_create_file("war_stats", S_IRUSR, ar->debugfs_phy, ar,
|
|
|
&fops_war_stats);
|
|
|
|
|
|
+ debugfs_create_file("roam_table", S_IRUSR, ar->debugfs_phy, ar,
|
|
|
+ &fops_roam_table);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1031,6 +1139,7 @@ void ath6kl_debug_cleanup(struct ath6kl *ar)
|
|
|
{
|
|
|
vfree(ar->debug.fwlog_buf.buf);
|
|
|
kfree(ar->debug.fwlog_tmp);
|
|
|
+ kfree(ar->debug.roam_tbl);
|
|
|
}
|
|
|
|
|
|
#endif
|