| /* |
| * Copyright (c) 2012 Broadcom Corporation |
| * |
| * 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/debugfs.h> |
| #include <linux/netdevice.h> |
| #include <linux/module.h> |
| |
| #include <brcmu_wifi.h> |
| #include <brcmu_utils.h> |
| #include "dhd.h" |
| #include "dhd_bus.h" |
| #include "dhd_dbg.h" |
| |
| static struct dentry *root_folder; |
| |
| void brcmf_debugfs_init(void) |
| { |
| root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); |
| if (IS_ERR(root_folder)) |
| root_folder = NULL; |
| } |
| |
| void brcmf_debugfs_exit(void) |
| { |
| if (!root_folder) |
| return; |
| |
| debugfs_remove_recursive(root_folder); |
| root_folder = NULL; |
| } |
| |
| static |
| ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data, |
| size_t count, loff_t *ppos) |
| { |
| struct brcmf_pub *drvr = f->private_data; |
| struct brcmf_bus *bus = drvr->bus_if; |
| char buf[40]; |
| int res; |
| |
| /* only allow read from start */ |
| if (*ppos > 0) |
| return 0; |
| |
| res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n", |
| bus->chip, bus->chip, bus->chiprev); |
| return simple_read_from_buffer(data, count, ppos, buf, res); |
| } |
| |
| static const struct file_operations brcmf_debugfs_chipinfo_ops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = brcmf_debugfs_chipinfo_read |
| }; |
| |
| static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) |
| { |
| struct dentry *dentry = drvr->dbgfs_dir; |
| |
| if (!IS_ERR_OR_NULL(dentry)) |
| debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr, |
| &brcmf_debugfs_chipinfo_ops); |
| return 0; |
| } |
| |
| int brcmf_debugfs_attach(struct brcmf_pub *drvr) |
| { |
| struct device *dev = drvr->bus_if->dev; |
| |
| if (!root_folder) |
| return -ENODEV; |
| |
| drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); |
| brcmf_debugfs_create_chipinfo(drvr); |
| return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); |
| } |
| |
| void brcmf_debugfs_detach(struct brcmf_pub *drvr) |
| { |
| if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) |
| debugfs_remove_recursive(drvr->dbgfs_dir); |
| } |
| |
| struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) |
| { |
| return drvr->dbgfs_dir; |
| } |
| |
| static |
| ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data, |
| size_t count, loff_t *ppos) |
| { |
| struct brcmf_sdio_count *sdcnt = f->private_data; |
| char buf[750]; |
| int res; |
| |
| /* only allow read from start */ |
| if (*ppos > 0) |
| return 0; |
| |
| res = scnprintf(buf, sizeof(buf), |
| "intrcount: %u\nlastintrs: %u\n" |
| "pollcnt: %u\nregfails: %u\n" |
| "tx_sderrs: %u\nfcqueued: %u\n" |
| "rxrtx: %u\nrx_toolong: %u\n" |
| "rxc_errors: %u\nrx_hdrfail: %u\n" |
| "rx_badhdr: %u\nrx_badseq: %u\n" |
| "fc_rcvd: %u\nfc_xoff: %u\n" |
| "fc_xon: %u\nrxglomfail: %u\n" |
| "rxglomframes: %u\nrxglompkts: %u\n" |
| "f2rxhdrs: %u\nf2rxdata: %u\n" |
| "f2txdata: %u\nf1regdata: %u\n" |
| "tickcnt: %u\ntx_ctlerrs: %lu\n" |
| "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" |
| "rx_ctlpkts: %lu\nrx_readahead: %lu\n", |
| sdcnt->intrcount, sdcnt->lastintrs, |
| sdcnt->pollcnt, sdcnt->regfails, |
| sdcnt->tx_sderrs, sdcnt->fcqueued, |
| sdcnt->rxrtx, sdcnt->rx_toolong, |
| sdcnt->rxc_errors, sdcnt->rx_hdrfail, |
| sdcnt->rx_badhdr, sdcnt->rx_badseq, |
| sdcnt->fc_rcvd, sdcnt->fc_xoff, |
| sdcnt->fc_xon, sdcnt->rxglomfail, |
| sdcnt->rxglomframes, sdcnt->rxglompkts, |
| sdcnt->f2rxhdrs, sdcnt->f2rxdata, |
| sdcnt->f2txdata, sdcnt->f1regdata, |
| sdcnt->tickcnt, sdcnt->tx_ctlerrs, |
| sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, |
| sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); |
| |
| return simple_read_from_buffer(data, count, ppos, buf, res); |
| } |
| |
| static const struct file_operations brcmf_debugfs_sdio_counter_ops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = brcmf_debugfs_sdio_counter_read |
| }; |
| |
| void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, |
| struct brcmf_sdio_count *sdcnt) |
| { |
| struct dentry *dentry = drvr->dbgfs_dir; |
| |
| if (!IS_ERR_OR_NULL(dentry)) |
| debugfs_create_file("counters", S_IRUGO, dentry, |
| sdcnt, &brcmf_debugfs_sdio_counter_ops); |
| } |
| |
| static |
| ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, |
| size_t count, loff_t *ppos) |
| { |
| struct brcmf_fws_stats *fwstats = f->private_data; |
| char buf[650]; |
| int res; |
| |
| /* only allow read from start */ |
| if (*ppos > 0) |
| return 0; |
| |
| res = scnprintf(buf, sizeof(buf), |
| "header_pulls: %u\n" |
| "header_only_pkt: %u\n" |
| "tlv_parse_failed: %u\n" |
| "tlv_invalid_type: %u\n" |
| "mac_update_fails: %u\n" |
| "ps_update_fails: %u\n" |
| "if_update_fails: %u\n" |
| "pkt2bus: %u\n" |
| "generic_error: %u\n" |
| "rollback_success: %u\n" |
| "rollback_failed: %u\n" |
| "delayq_full: %u\n" |
| "supprq_full: %u\n" |
| "txs_indicate: %u\n" |
| "txs_discard: %u\n" |
| "txs_suppr_core: %u\n" |
| "txs_suppr_ps: %u\n" |
| "txs_tossed: %u\n" |
| "txs_host_tossed: %u\n" |
| "bus_flow_block: %u\n" |
| "fws_flow_block: %u\n" |
| "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" |
| "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", |
| fwstats->header_pulls, |
| fwstats->header_only_pkt, |
| fwstats->tlv_parse_failed, |
| fwstats->tlv_invalid_type, |
| fwstats->mac_update_failed, |
| fwstats->mac_ps_update_failed, |
| fwstats->if_update_failed, |
| fwstats->pkt2bus, |
| fwstats->generic_error, |
| fwstats->rollback_success, |
| fwstats->rollback_failed, |
| fwstats->delayq_full_error, |
| fwstats->supprq_full_error, |
| fwstats->txs_indicate, |
| fwstats->txs_discard, |
| fwstats->txs_supp_core, |
| fwstats->txs_supp_ps, |
| fwstats->txs_tossed, |
| fwstats->txs_host_tossed, |
| fwstats->bus_flow_block, |
| fwstats->fws_flow_block, |
| fwstats->send_pkts[0], fwstats->send_pkts[1], |
| fwstats->send_pkts[2], fwstats->send_pkts[3], |
| fwstats->send_pkts[4], |
| fwstats->requested_sent[0], |
| fwstats->requested_sent[1], |
| fwstats->requested_sent[2], |
| fwstats->requested_sent[3], |
| fwstats->requested_sent[4]); |
| |
| return simple_read_from_buffer(data, count, ppos, buf, res); |
| } |
| |
| static const struct file_operations brcmf_debugfs_fws_stats_ops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = brcmf_debugfs_fws_stats_read |
| }; |
| |
| void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, |
| struct brcmf_fws_stats *stats) |
| { |
| struct dentry *dentry = drvr->dbgfs_dir; |
| |
| if (!IS_ERR_OR_NULL(dentry)) |
| debugfs_create_file("fws_stats", S_IRUGO, dentry, |
| stats, &brcmf_debugfs_fws_stats_ops); |
| } |