static int wil_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; int cid = wil_find_cid_by_idx(wil, idx); if (cid < 0) return -ENOENT; memcpy(mac, wil->sta[cid].addr, ETH_ALEN); wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); rc = wil_cid_fill_sinfo(wil, cid, sinfo); return rc; }
/*---------link------------*/ static int wil_link_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct station_info sinfo; int i, rc; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; switch (p->status) { case wil_sta_unused: status = "unused "; break; case wil_sta_conn_pending: status = "pending "; break; case wil_sta_connected: status = "connected"; break; } seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status, (p->data_port_open ? " data_port_open" : "")); if (p->status == wil_sta_connected) { rc = wil_cid_fill_sinfo(wil, i, &sinfo); if (rc) return rc; seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs); seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs); seq_printf(s, " SQ = %d\n", sinfo.signal); } } return 0; }
static int wil_bf_debugfs_show(struct seq_file *s, void *data) { int rc; int i; struct wil6210_priv *wil = s->private; struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev); struct wmi_notify_req_cmd cmd = { .interval_usec = 0, }; struct { struct wmi_cmd_hdr wmi; struct wmi_notify_req_done_event evt; } __packed reply; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { u32 status; cmd.cid = i; rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, vif->mid, &cmd, sizeof(cmd), WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); /* if reply is all-0, ignore this CID */ if (rc || is_all_zeros(&reply.evt, sizeof(reply.evt))) continue; status = le32_to_cpu(reply.evt.status); seq_printf(s, "CID %d {\n" " TSF = 0x%016llx\n" " TxMCS = %2d TxTpt = %4d\n" " SQI = %4d\n" " RSSI = %4d\n" " Status = 0x%08x %s\n" " Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n" " Goodput(rx:tx) %4d:%4d\n" "}\n", i, le64_to_cpu(reply.evt.tsf), le16_to_cpu(reply.evt.bf_mcs), le32_to_cpu(reply.evt.tx_tpt), reply.evt.sqi, reply.evt.rssi, status, wil_bfstatus_str(status), le16_to_cpu(reply.evt.my_rx_sector), le16_to_cpu(reply.evt.my_tx_sector), le16_to_cpu(reply.evt.other_rx_sector), le16_to_cpu(reply.evt.other_tx_sector), le32_to_cpu(reply.evt.rx_goodput), le32_to_cpu(reply.evt.tx_goodput)); } return 0; } static int wil_bf_seq_open(struct inode *inode, struct file *file) { return single_open(file, wil_bf_debugfs_show, inode->i_private); } static const struct file_operations fops_bf = { .open = wil_bf_seq_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; /*---------temp------------*/ static void print_temp(struct seq_file *s, const char *prefix, u32 t) { switch (t) { case 0: case ~(u32)0: seq_printf(s, "%s N/A\n", prefix); break; default: seq_printf(s, "%s %d.%03d\n", prefix, t / 1000, t % 1000); break; } } static int wil_temp_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; u32 t_m, t_r; int rc = wmi_get_temperature(wil, &t_m, &t_r); if (rc) { seq_puts(s, "Failed\n"); return 0; } print_temp(s, "T_mac =", t_m); print_temp(s, "T_radio =", t_r); return 0; } static int wil_temp_seq_open(struct inode *inode, struct file *file) { return single_open(file, wil_temp_debugfs_show, inode->i_private); } static const struct file_operations fops_temp = { .open = wil_temp_seq_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; /*---------freq------------*/ static int wil_freq_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct wireless_dev *wdev = wil->main_ndev->ieee80211_ptr; u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0; seq_printf(s, "Freq = %d\n", freq); return 0; } static int wil_freq_seq_open(struct inode *inode, struct file *file) { return single_open(file, wil_freq_debugfs_show, inode->i_private); } static const struct file_operations fops_freq = { .open = wil_freq_seq_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; /*---------link------------*/ static int wil_link_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct station_info sinfo; int i, rc; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { struct wil_sta_info *p = &wil->sta[i]; char *status = "unknown"; struct wil6210_vif *vif; u8 mid; switch (p->status) { case wil_sta_unused: status = "unused "; break; case wil_sta_conn_pending: status = "pending "; break; case wil_sta_connected: status = "connected"; break; } mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX; seq_printf(s, "[%d][MID %d] %pM %s\n", i, mid, p->addr, status); if (p->status != wil_sta_connected) continue; vif = (mid < wil->max_vifs) ? wil->vifs[mid] : NULL; if (vif) { rc = wil_cid_fill_sinfo(vif, i, &sinfo); if (rc) return rc; seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs); seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs); seq_printf(s, " SQ = %d\n", sinfo.signal); } else { seq_puts(s, " INVALID MID\n"); } } return 0; } static int wil_link_seq_open(struct inode *inode, struct file *file) { return single_open(file, wil_link_debugfs_show, inode->i_private); } static const struct file_operations fops_link = { .open = wil_link_seq_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; /*---------info------------*/ static int wil_info_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct net_device *ndev = wil->main_ndev; int is_ac = power_supply_is_system_supplied(); int rx = atomic_xchg(&wil->isr_count_rx, 0); int tx = atomic_xchg(&wil->isr_count_tx, 0); static ulong rxf_old, txf_old; ulong rxf = ndev->stats.rx_packets; ulong txf = ndev->stats.tx_packets; unsigned int i; /* >0 : AC; 0 : battery; <0 : error */ seq_printf(s, "AC powered : %d\n", is_ac); seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old); seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old); rxf_old = rxf; txf_old = txf; #define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \ " " __stringify(x) : "" for (i = 0; i < ndev->num_tx_queues; i++) { struct netdev_queue *txq = netdev_get_tx_queue(ndev, i); unsigned long state = txq->state; seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state, CHECK_QSTATE(DRV_XOFF), CHECK_QSTATE(STACK_XOFF), CHECK_QSTATE(FROZEN) ); } #undef CHECK_QSTATE return 0; } static int wil_info_seq_open(struct inode *inode, struct file *file) { return single_open(file, wil_info_debugfs_show, inode->i_private); } static const struct file_operations fops_info = { .open = wil_info_seq_open, .release = single_release, .read = seq_read, .llseek = seq_lseek, }; /*---------recovery------------*/ /* mode = [manual|auto] * state = [idle|pending|running] */ static ssize_t wil_read_file_recovery(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; char buf[80]; int n; static const char * const sstate[] = {"idle", "pending", "running"}; n = snprintf(buf, sizeof(buf), "mode = %s\nstate = %s\n", no_fw_recovery ? "manual" : "auto", sstate[wil->recovery_state]); n = min_t(int, n, sizeof(buf)); return simple_read_from_buffer(user_buf, count, ppos, buf, n); } static ssize_t wil_write_file_recovery(struct file *file, const char __user *buf_, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; static const char run_command[] = "run"; char buf[sizeof(run_command) + 1]; /* to detect "runx" */ ssize_t rc; if (wil->recovery_state != fw_recovery_pending) { wil_err(wil, "No recovery pending\n"); return -EINVAL; } if (*ppos != 0) { wil_err(wil, "Offset [%d]\n", (int)*ppos); return -EINVAL; } if (count > sizeof(buf)) { wil_err(wil, "Input too long, len = %d\n", (int)count); return -EINVAL; } rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, buf_, count); if (rc < 0) return rc; buf[rc] = '\0'; if (0 == strcmp(buf, run_command)) wil_set_recovery_state(wil, fw_recovery_running); else wil_err(wil, "Bad recovery command \"%s\"\n", buf); return rc; } static const struct file_operations fops_recovery = { .read = wil_read_file_recovery, .write = wil_write_file_recovery, .open = simple_open, }; /*---------Station matrix------------*/ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) { int i; u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; unsigned long long drop_dup = r->drop_dup, drop_old = r->drop_old; seq_printf(s, "([%2d] %3d TU) 0x%03x [", r->buf_size, r->timeout, r->head_seq_num); for (i = 0; i < r->buf_size; i++) { if (i == index) seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); else seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); } seq_printf(s, "] total %llu drop %llu (dup %llu + old %llu) last 0x%03x\n", r->total, drop_dup + drop_old, drop_dup, drop_old, r->ssn_last_drop); }
static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, struct station_info *sinfo) { struct wmi_notify_req_cmd cmd = { .cid = cid, .interval_usec = 0, }; struct { struct wil6210_mbox_hdr_wmi wmi; struct wmi_notify_req_done_event evt; } __packed reply; struct wil_net_stats *stats = &wil->sta[cid].stats; int rc; rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); if (rc) return rc; wil_dbg_wmi(wil, "Link status for CID %d: {\n" " MCS %d TSF 0x%016llx\n" " BF status 0x%08x SNR 0x%08x SQI %d%%\n" " Tx Tpt %d goodput %d Rx goodput %d\n" " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", cid, le16_to_cpu(reply.evt.bf_mcs), le64_to_cpu(reply.evt.tsf), reply.evt.status, le32_to_cpu(reply.evt.snr_val), reply.evt.sqi, le32_to_cpu(reply.evt.tx_tpt), le32_to_cpu(reply.evt.tx_goodput), le32_to_cpu(reply.evt.rx_goodput), le16_to_cpu(reply.evt.my_rx_sector), le16_to_cpu(reply.evt.my_tx_sector), le16_to_cpu(reply.evt.other_rx_sector), le16_to_cpu(reply.evt.other_tx_sector)); sinfo->generation = wil->sinfo_gen; sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | STATION_INFO_RX_BITRATE | STATION_INFO_TX_BITRATE | STATION_INFO_RX_DROP_MISC | STATION_INFO_TX_FAILED; sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->rxrate.mcs = stats->last_mcs_rx; sinfo->rx_bytes = stats->rx_bytes; sinfo->rx_packets = stats->rx_packets; sinfo->rx_dropped_misc = stats->rx_dropped; sinfo->tx_bytes = stats->tx_bytes; sinfo->tx_packets = stats->tx_packets; sinfo->tx_failed = stats->tx_errors; if (test_bit(wil_status_fwconnected, &wil->status)) { sinfo->filled |= STATION_INFO_SIGNAL; sinfo->signal = reply.evt.sqi; } return rc; } static int wil_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, u8 *mac, struct station_info *sinfo) { struct wil6210_priv *wil = wiphy_to_wil(wiphy); int rc; int cid = wil_find_cid(wil, mac); wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); if (cid < 0) return cid; rc = wil_cid_fill_sinfo(wil, cid, sinfo); return rc; } /* * Find @idx-th active STA for station dump. */ static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx) { int i; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { if (wil->sta[i].status == wil_sta_unused) continue; if (idx == 0) return i; idx--; } return -ENOENT; }