/****************************************************************************** * * Copyright(c) 2009-2012 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * 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. * * The full GNU General Public License is included in this distribution in the * file called LICENSE. * * Contact Information: * wlanfae * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, * Hsinchu 300, Taiwan. * * Larry Finger *****************************************************************************/ #include "wifi.h" #include "cam.h" #include #include #ifdef CONFIG_RTLWIFI_DEBUG void _rtl_dbg_trace(struct rtl_priv *rtlpriv, u64 comp, int level, const char *fmt, ...) { if (unlikely((comp & rtlpriv->cfg->mod_params->debug_mask) && level <= rtlpriv->cfg->mod_params->debug_level)) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; pr_info(":<%lx> %pV", in_interrupt(), &vaf); va_end(args); } } EXPORT_SYMBOL_GPL(_rtl_dbg_trace); void _rtl_dbg_print(struct rtl_priv *rtlpriv, u64 comp, int level, const char *fmt, ...) { if (unlikely((comp & rtlpriv->cfg->mod_params->debug_mask) && level <= rtlpriv->cfg->mod_params->debug_level)) { struct va_format vaf; va_list args; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; pr_info("%pV", &vaf); va_end(args); } } EXPORT_SYMBOL_GPL(_rtl_dbg_print); void _rtl_dbg_print_data(struct rtl_priv *rtlpriv, u64 comp, int level, const char *titlestring, const void *hexdata, int hexdatalen) { if (unlikely(((comp) & rtlpriv->cfg->mod_params->debug_mask) && ((level) <= rtlpriv->cfg->mod_params->debug_level))) { pr_info("In process \"%s\" (pid %i): %s\n", current->comm, current->pid, titlestring); print_hex_dump_bytes("", DUMP_PREFIX_NONE, hexdata, hexdatalen); } } EXPORT_SYMBOL_GPL(_rtl_dbg_print_data); struct rtl_debugfs_priv { struct rtl_priv *rtlpriv; int (*cb_read)(struct seq_file *m, void *v); ssize_t (*cb_write)(struct file *filp, const char __user *buffer, size_t count, loff_t *loff); u32 cb_data; }; static struct dentry *debugfs_topdir; static int rtl_debug_get_common(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; return debugfs_priv->cb_read(m, v); } static int dl_debug_open_common(struct inode *inode, struct file *file) { return single_open(file, rtl_debug_get_common, inode->i_private); } static const struct file_operations file_ops_common = { .open = dl_debug_open_common, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int rtl_debug_get_mac_page(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; u32 page = debugfs_priv->cb_data; int i, n; int max = 0xff; for (n = 0; n <= max; ) { seq_printf(m, "\n%8.8x ", n + page); for (i = 0; i < 4 && n <= max; i++, n += 4) seq_printf(m, "%8.8x ", rtl_read_dword(rtlpriv, (page | n))); } seq_puts(m, "\n"); return 0; } #define RTL_DEBUG_IMPL_MAC_SERIES(page, addr) \ static struct rtl_debugfs_priv rtl_debug_priv_mac_ ##page = { \ .cb_read = rtl_debug_get_mac_page, \ .cb_data = addr, \ } RTL_DEBUG_IMPL_MAC_SERIES(0, 0x0000); RTL_DEBUG_IMPL_MAC_SERIES(1, 0x0100); RTL_DEBUG_IMPL_MAC_SERIES(2, 0x0200); RTL_DEBUG_IMPL_MAC_SERIES(3, 0x0300); RTL_DEBUG_IMPL_MAC_SERIES(4, 0x0400); RTL_DEBUG_IMPL_MAC_SERIES(5, 0x0500); RTL_DEBUG_IMPL_MAC_SERIES(6, 0x0600); RTL_DEBUG_IMPL_MAC_SERIES(7, 0x0700); RTL_DEBUG_IMPL_MAC_SERIES(10, 0x1000); RTL_DEBUG_IMPL_MAC_SERIES(11, 0x1100); RTL_DEBUG_IMPL_MAC_SERIES(12, 0x1200); RTL_DEBUG_IMPL_MAC_SERIES(13, 0x1300); RTL_DEBUG_IMPL_MAC_SERIES(14, 0x1400); RTL_DEBUG_IMPL_MAC_SERIES(15, 0x1500); RTL_DEBUG_IMPL_MAC_SERIES(16, 0x1600); RTL_DEBUG_IMPL_MAC_SERIES(17, 0x1700); static int rtl_debug_get_bb_page(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; struct ieee80211_hw *hw = rtlpriv->hw; u32 page = debugfs_priv->cb_data; int i, n; int max = 0xff; for (n = 0; n <= max; ) { seq_printf(m, "\n%8.8x ", n + page); for (i = 0; i < 4 && n <= max; i++, n += 4) seq_printf(m, "%8.8x ", rtl_get_bbreg(hw, (page | n), 0xffffffff)); } seq_puts(m, "\n"); return 0; } #define RTL_DEBUG_IMPL_BB_SERIES(page, addr) \ static struct rtl_debugfs_priv rtl_debug_priv_bb_ ##page = { \ .cb_read = rtl_debug_get_bb_page, \ .cb_data = addr, \ } RTL_DEBUG_IMPL_BB_SERIES(8, 0x0800); RTL_DEBUG_IMPL_BB_SERIES(9, 0x0900); RTL_DEBUG_IMPL_BB_SERIES(a, 0x0a00); RTL_DEBUG_IMPL_BB_SERIES(b, 0x0b00); RTL_DEBUG_IMPL_BB_SERIES(c, 0x0c00); RTL_DEBUG_IMPL_BB_SERIES(d, 0x0d00); RTL_DEBUG_IMPL_BB_SERIES(e, 0x0e00); RTL_DEBUG_IMPL_BB_SERIES(f, 0x0f00); RTL_DEBUG_IMPL_BB_SERIES(18, 0x1800); RTL_DEBUG_IMPL_BB_SERIES(19, 0x1900); RTL_DEBUG_IMPL_BB_SERIES(1a, 0x1a00); RTL_DEBUG_IMPL_BB_SERIES(1b, 0x1b00); RTL_DEBUG_IMPL_BB_SERIES(1c, 0x1c00); RTL_DEBUG_IMPL_BB_SERIES(1d, 0x1d00); RTL_DEBUG_IMPL_BB_SERIES(1e, 0x1e00); RTL_DEBUG_IMPL_BB_SERIES(1f, 0x1f00); static int rtl_debug_get_reg_rf(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; struct ieee80211_hw *hw = rtlpriv->hw; enum radio_path rfpath = debugfs_priv->cb_data; int i, n; int max = 0x40; if (IS_HARDWARE_TYPE_8822B(rtlpriv)) max = 0xff; seq_printf(m, "\nPATH(%d)", rfpath); for (n = 0; n <= max; ) { seq_printf(m, "\n%8.8x ", n); for (i = 0; i < 4 && n <= max; n += 1, i++) seq_printf(m, "%8.8x ", rtl_get_rfreg(hw, rfpath, n, 0xffffffff)); } seq_puts(m, "\n"); return 0; } #define RTL_DEBUG_IMPL_RF_SERIES(page, addr) \ static struct rtl_debugfs_priv rtl_debug_priv_rf_ ##page = { \ .cb_read = rtl_debug_get_reg_rf, \ .cb_data = addr, \ } RTL_DEBUG_IMPL_RF_SERIES(a, RF90_PATH_A); RTL_DEBUG_IMPL_RF_SERIES(b, RF90_PATH_B); static int rtl_debug_get_cam_register(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; int start = debugfs_priv->cb_data; u32 target_cmd = 0; u32 target_val = 0; u8 entry_i = 0; u32 ulstatus; int i = 100, j = 0; int end = (start + 11 > TOTAL_CAM_ENTRY ? TOTAL_CAM_ENTRY : start + 11); /* This dump the current register page */ seq_printf(m, "\n#################### SECURITY CAM (%d-%d) ##################\n", start, end - 1); for (j = start; j < end; j++) { seq_printf(m, "\nD: %2x > ", j); for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) { /* polling bit, and No Write enable, and address */ target_cmd = entry_i + CAM_CONTENT_COUNT * j; target_cmd = target_cmd | BIT(31); /* Check polling bit is clear */ while ((i--) >= 0) { ulstatus = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM]); if (ulstatus & BIT(31)) continue; else break; } rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[RWCAM], target_cmd); target_val = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[RCAMO]); seq_printf(m, "%8.8x ", target_val); } } seq_puts(m, "\n"); return 0; } #define RTL_DEBUG_IMPL_CAM_SERIES(page, addr) \ static struct rtl_debugfs_priv rtl_debug_priv_cam_ ##page = { \ .cb_read = rtl_debug_get_cam_register, \ .cb_data = addr, \ } RTL_DEBUG_IMPL_CAM_SERIES(1, 0); RTL_DEBUG_IMPL_CAM_SERIES(2, 11); RTL_DEBUG_IMPL_CAM_SERIES(3, 22); static int rtl_debug_get_btcoex(struct seq_file *m, void *v) { struct rtl_debugfs_priv *debugfs_priv = m->private; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; if (rtlpriv->cfg->ops->get_btc_status()) rtlpriv->btcoexist.btc_ops->btc_display_bt_coex_info(rtlpriv, m); seq_puts(m, "\n"); return 0; } static struct rtl_debugfs_priv rtl_debug_priv_btcoex = { .cb_read = rtl_debug_get_btcoex, .cb_data = 0, }; static ssize_t rtl_debugfs_set_write_reg(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtl_debugfs_priv *debugfs_priv = filp->private_data; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; char tmp[32 + 1]; int tmp_len; u32 addr, val, len; int num; if (count < 3) return -EFAULT; tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); if (!buffer || copy_from_user(tmp, buffer, tmp_len)) return count; tmp[tmp_len] = '\0'; /* write BB/MAC register */ num = sscanf(tmp, "%x %x %x", &addr, &val, &len); if (num != 3) return count; switch (len) { case 1: rtl_write_byte(rtlpriv, addr, (u8)val); break; case 2: rtl_write_word(rtlpriv, addr, (u16)val); break; case 4: rtl_write_dword(rtlpriv, addr, val); break; default: /*printk("error write length=%d", len);*/ break; } return count; } static struct rtl_debugfs_priv rtl_debug_priv_write_reg = { .cb_write = rtl_debugfs_set_write_reg, }; static ssize_t rtl_debugfs_set_write_h2c(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtl_debugfs_priv *debugfs_priv = filp->private_data; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; struct ieee80211_hw *hw = rtlpriv->hw; char tmp[32 + 1]; int tmp_len; u8 h2c_len, h2c_data_packed[8]; int h2c_data[8]; /* idx 0: cmd */ int i; if (count < 3) return -EFAULT; tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); if (copy_from_user(tmp, buffer, tmp_len)) return -EFAULT; tmp[tmp_len] = '\0'; h2c_len = sscanf(tmp, "%X %X %X %X %X %X %X %X", &h2c_data[0], &h2c_data[1], &h2c_data[2], &h2c_data[3], &h2c_data[4], &h2c_data[5], &h2c_data[6], &h2c_data[7]); if (h2c_len == 0) return -EINVAL; for (i = 0; i < h2c_len; i++) h2c_data_packed[i] = (u8)h2c_data[i]; rtlpriv->cfg->ops->fill_h2c_cmd(hw, h2c_data_packed[0], h2c_len - 1, &h2c_data_packed[1]); return count; } static struct rtl_debugfs_priv rtl_debug_priv_write_h2c = { .cb_write = rtl_debugfs_set_write_h2c, }; static ssize_t rtl_debugfs_set_write_rfreg(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtl_debugfs_priv *debugfs_priv = filp->private_data; struct rtl_priv *rtlpriv = debugfs_priv->rtlpriv; struct ieee80211_hw *hw = rtlpriv->hw; char tmp[32 + 1]; int tmp_len; int num; int path; u32 addr, bitmask, data; if (count < 3) return -EFAULT; tmp_len = (count > sizeof(tmp) - 1 ? sizeof(tmp) - 1 : count); if (!buffer || copy_from_user(tmp, buffer, tmp_len)) return count; tmp[tmp_len] = '\0'; num = sscanf(tmp, "%X %X %X %X", &path, &addr, &bitmask, &data); if (num != 4) { RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG, "Format is \n"); return count; } rtl_set_rfreg(hw, path, addr, bitmask, data); return count; } static struct rtl_debugfs_priv rtl_debug_priv_write_rfreg = { .cb_write = rtl_debugfs_set_write_rfreg, }; static int rtl_debugfs_close(struct inode *inode, struct file *filp) { return 0; } static ssize_t rtl_debugfs_common_write(struct file *filp, const char __user *buffer, size_t count, loff_t *loff) { struct rtl_debugfs_priv *debugfs_priv = filp->private_data; return debugfs_priv->cb_write(filp, buffer, count, loff); } static const struct file_operations file_ops_common_write = { .owner = THIS_MODULE, .write = rtl_debugfs_common_write, .open = simple_open, .release = rtl_debugfs_close, }; #define RTL_DEBUGFS_ADD_CORE(name, mode, fopname) \ do { \ rtl_debug_priv_ ##name.rtlpriv = rtlpriv; \ if (!debugfs_create_file(#name, mode, \ parent, &rtl_debug_priv_ ##name, \ &file_ops_ ##fopname)) \ pr_err("Unable to initialize debugfs:%s/%s\n", \ rtlpriv->dbg.debugfs_name, \ #name); \ } while (0) #define RTL_DEBUGFS_ADD(name) \ RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0444, common) #define RTL_DEBUGFS_ADD_W(name) \ RTL_DEBUGFS_ADD_CORE(name, S_IFREG | 0222, common_write) void rtl_debug_add_one(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); struct dentry *parent; snprintf(rtlpriv->dbg.debugfs_name, 18, "%pMF", rtlefuse->dev_addr); rtlpriv->dbg.debugfs_dir = debugfs_create_dir(rtlpriv->dbg.debugfs_name, debugfs_topdir); if (!rtlpriv->dbg.debugfs_dir) { pr_err("Unable to init debugfs:/%s/%s\n", rtlpriv->cfg->name, rtlpriv->dbg.debugfs_name); return; } parent = rtlpriv->dbg.debugfs_dir; RTL_DEBUGFS_ADD(mac_0); RTL_DEBUGFS_ADD(mac_1); RTL_DEBUGFS_ADD(mac_2); RTL_DEBUGFS_ADD(mac_3); RTL_DEBUGFS_ADD(mac_4); RTL_DEBUGFS_ADD(mac_5); RTL_DEBUGFS_ADD(mac_6); RTL_DEBUGFS_ADD(mac_7); RTL_DEBUGFS_ADD(bb_8); RTL_DEBUGFS_ADD(bb_9); RTL_DEBUGFS_ADD(bb_a); RTL_DEBUGFS_ADD(bb_b); RTL_DEBUGFS_ADD(bb_c); RTL_DEBUGFS_ADD(bb_d); RTL_DEBUGFS_ADD(bb_e); RTL_DEBUGFS_ADD(bb_f); RTL_DEBUGFS_ADD(mac_10); RTL_DEBUGFS_ADD(mac_11); RTL_DEBUGFS_ADD(mac_12); RTL_DEBUGFS_ADD(mac_13); RTL_DEBUGFS_ADD(mac_14); RTL_DEBUGFS_ADD(mac_15); RTL_DEBUGFS_ADD(mac_16); RTL_DEBUGFS_ADD(mac_17); RTL_DEBUGFS_ADD(bb_18); RTL_DEBUGFS_ADD(bb_19); RTL_DEBUGFS_ADD(bb_1a); RTL_DEBUGFS_ADD(bb_1b); RTL_DEBUGFS_ADD(bb_1c); RTL_DEBUGFS_ADD(bb_1d); RTL_DEBUGFS_ADD(bb_1e); RTL_DEBUGFS_ADD(bb_1f); RTL_DEBUGFS_ADD(rf_a); RTL_DEBUGFS_ADD(rf_b); RTL_DEBUGFS_ADD(cam_1); RTL_DEBUGFS_ADD(cam_2); RTL_DEBUGFS_ADD(cam_3); RTL_DEBUGFS_ADD(btcoex); RTL_DEBUGFS_ADD_W(write_reg); RTL_DEBUGFS_ADD_W(write_h2c); RTL_DEBUGFS_ADD_W(write_rfreg); } EXPORT_SYMBOL_GPL(rtl_debug_add_one); void rtl_debug_remove_one(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); debugfs_remove_recursive(rtlpriv->dbg.debugfs_dir); rtlpriv->dbg.debugfs_dir = NULL; } EXPORT_SYMBOL_GPL(rtl_debug_remove_one); void rtl_debugfs_add_topdir(void) { debugfs_topdir = debugfs_create_dir("rtlwifi", NULL); } void rtl_debugfs_remove_topdir(void) { debugfs_remove_recursive(debugfs_topdir); } #endif