/* * 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 #include #include #include #include #include "dhd.h" #include "dhd_bus.h" #include "dhd_dbg.h" #include "tracepoint.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; } 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); return PTR_RET(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" "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" "fifo_credits_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->send_pkts[0], fwstats->send_pkts[1], fwstats->send_pkts[2], fwstats->send_pkts[3], fwstats->send_pkts[4], fwstats->fifo_credits_sent[0], fwstats->fifo_credits_sent[1], fwstats->fifo_credits_sent[2], fwstats->fifo_credits_sent[3], fwstats->fifo_credits_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); }