static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) { uint i; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; struct wil_sta_info *sta = &wil->sta[cid]; wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, sta->status); sta->data_port_open = false; if (sta->status != wil_sta_unused) { wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING); switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: /* AP-like interface */ cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL); break; default: break; } sta->status = wil_sta_unused; } for (i = 0; i < WIL_STA_TID_NUM; i++) { struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; sta->tid_rx[i] = NULL; wil_tid_ampdu_rx_free(wil, r); } for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { if (wil->vring2cid_tid[i][0] == cid) wil_vring_fini_tx(wil, i); } memset(&sta->stats, 0, sizeof(sta->stats)); }
static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; struct wireless_dev *wdev = wil_to_wdev(wil); struct net_device *ndev = wil_to_ndev(wil); if (*ppos != 0) { wil_err(wil, "Unable to set SSID substring from [%d]\n", (int)*ppos); return -EINVAL; } if (count > sizeof(wdev->ssid)) { wil_err(wil, "SSID too long, len = %d\n", (int)count); return -EINVAL; } if (netif_running(ndev)) { wil_err(wil, "Unable to change SSID on running interface\n"); return -EINVAL; } wdev->ssid_len = count; return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, buf, count); }
void wil_if_free(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "%s()\n", __func__); if (!ndev) return; wil_priv_deinit(wil); wil_to_ndev(wil) = NULL; free_netdev(ndev); wil_wdev_free(wil); }
static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) { uint i; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; wil_dbg_misc(wil, "%s()\n", __func__); wil_link_off(wil); clear_bit(wil_status_fwconnected, &wil->status); switch (wdev->sme_state) { case CFG80211_SME_CONNECTED: cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, GFP_KERNEL); break; case CFG80211_SME_CONNECTING: cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); break; default: break; } for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) wil_vring_fini_tx(wil, i); clear_bit(wil_status_dontscan, &wil->status); }
void wil_if_remove(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "%s()\n", __func__); unregister_netdev(ndev); }
void wil_link_off(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "%s()\n", __func__); netif_tx_stop_all_queues(ndev); netif_carrier_off(ndev); }
void wil_link_on(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); wil_dbg_misc(wil, "%s()\n", __func__); netif_carrier_on(ndev); wil_dbg_misc(wil, "netif_tx_wake : link on\n"); netif_tx_wake_all_queues(ndev); }
void wil_if_free(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); if (!ndev) return; free_netdev(ndev); wil_priv_deinit(wil); wil_wdev_free(wil); }
static int __wil_up(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; int rc; WARN_ON(!mutex_is_locked(&wil->mutex)); rc = wil_reset(wil); if (rc) return rc; /* Rx VRING. After MAC and beacon */ rc = wil_rx_init(wil); if (rc) return rc; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: wil_dbg_misc(wil, "type: STATION\n"); ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: wil_dbg_misc(wil, "type: AP\n"); ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: wil_dbg_misc(wil, "type: P2P_CLIENT\n"); ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: wil_dbg_misc(wil, "type: P2P_GO\n"); ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: wil_dbg_misc(wil, "type: Monitor\n"); ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ break; default: return -EOPNOTSUPP; } /* MAC address - pre-requisite for other commands */ wmi_set_mac_address(wil, ndev->dev_addr); napi_enable(&wil->napi_rx); napi_enable(&wil->napi_tx); set_bit(wil_status_napi_en, &wil->status); return 0; }
void __wil_info(struct wil6210_priv *wil, const char *fmt, ...) { struct net_device *ndev = wil_to_ndev(wil); struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); vaf.va = &args; netdev_info(ndev, "%pV", &vaf); trace_wil6210_log_info(&vaf); va_end(args); }
int wil_if_add(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); int rc; wil_dbg_misc(wil, "%s()\n", __func__); rc = register_netdev(ndev); if (rc < 0) { dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); return rc; } return 0; }
int wil_if_add(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); int rc; rc = register_netdev(ndev); if (rc < 0) { dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); return rc; } wil_link_off(wil); return 0; }
void wil_dbg_ratelimited(const struct wil6210_priv *wil, const char *fmt, ...) { struct va_format vaf; va_list args; if (!net_ratelimit()) return; va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; netdev_dbg(wil_to_ndev(wil), "%pV", &vaf); trace_wil6210_log_dbg(&vaf); va_end(args); }
void __wil_err_ratelimited(struct wil6210_priv *wil, const char *fmt, ...) { if (net_ratelimit()) { struct net_device *ndev = wil_to_ndev(wil); struct va_format vaf = { .fmt = fmt, }; va_list args; va_start(args, fmt); vaf.va = &args; netdev_err(ndev, "%pV", &vaf); trace_wil6210_log_err(&vaf); va_end(args); } }
static int wil_suspend_radio_off(struct wil6210_priv *wil) { int rc = 0; struct net_device *ndev = wil_to_ndev(wil); wil_dbg_pm(wil, "suspend radio off\n"); set_bit(wil_status_suspending, wil->status); if (test_bit(wil_status_collecting_dumps, wil->status)) { /* Device collects crash dump, cancel the suspend */ wil_dbg_pm(wil, "reject suspend while collecting crash dump\n"); clear_bit(wil_status_suspending, wil->status); wil->suspend_stats.rejected_by_host++; return -EBUSY; } /* if netif up, hardware is alive, shut it down */ if (ndev->flags & IFF_UP) { rc = wil_down(wil); if (rc) { wil_err(wil, "wil_down : %d\n", rc); wil->suspend_stats.r_off.failed_suspends++; goto out; } } /* Disable PCIe IRQ to prevent sporadic IRQs when PCIe is suspending */ wil_dbg_pm(wil, "Disabling PCIe IRQ before suspending\n"); wil_disable_irq(wil); if (wil->platform_ops.suspend) { rc = wil->platform_ops.suspend(wil->platform_handle, false); if (rc) { wil_enable_irq(wil); wil->suspend_stats.r_off.failed_suspends++; goto out; } } set_bit(wil_status_suspended, wil->status); out: clear_bit(wil_status_suspending, wil->status); wil_dbg_pm(wil, "suspend radio off: %d\n", rc); return rc; }
static int wil_resume_radio_off(struct wil6210_priv *wil) { int rc = 0; struct net_device *ndev = wil_to_ndev(wil); wil_dbg_pm(wil, "Enabling PCIe IRQ\n"); wil_enable_irq(wil); /* if netif up, bring hardware up * During open(), IFF_UP set after actual device method * invocation. This prevent recursive call to wil_up() * wil_status_suspended will be cleared in wil_reset */ if (ndev->flags & IFF_UP) rc = wil_up(wil); else clear_bit(wil_status_suspended, wil->status); return rc; }
/*---reset---*/ static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; struct net_device *ndev = wil_to_ndev(wil); /** * BUG: * this code does NOT sync device state with the rest of system * use with care, debug only!!! */ rtnl_lock(); dev_close(ndev); ndev->flags &= ~IFF_UP; rtnl_unlock(); wil_reset(wil, true); return len; }
static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { int cid = -ENOENT; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; might_sleep(); if (bssid) { cid = wil_find_cid(wil, bssid); wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); } else { wil_dbg_misc(wil, "%s(all)\n", __func__); } if (cid >= 0) /* disconnect 1 peer */ wil_disconnect_cid(wil, cid); else /* disconnect all */ for (cid = 0; cid < WIL6210_MAX_CID; cid++) wil_disconnect_cid(wil, cid); /* link state */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: wil_link_off(wil); if (test_bit(wil_status_fwconnected, &wil->status)) { clear_bit(wil_status_fwconnected, &wil->status); cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, GFP_KERNEL); } else if (test_bit(wil_status_fwconnecting, &wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); } clear_bit(wil_status_fwconnecting, &wil->status); break; default: break; } }
/* * Write mgmt frame to this file to send it */ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, size_t len, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; #if 0 struct net_device *ndev = wil_to_ndev(wil); #endif struct wiphy *wiphy = wil_to_wiphy(wil); struct wireless_dev *wdev = wil_to_wdev(wil); int rc; void *frame = kmalloc(len, GFP_KERNEL); if (!frame) return -ENOMEM; if (copy_from_user(frame, buf, len)) return -EIO; #if 0 rc = wmi_set_channel(wil, 1); if (rc) return rc; rc = wmi_call(wil, WMI_RX_ON_CMDID, NULL, 0, WMI_RX_ON_DONE_EVENTID, NULL, 0, 20); if (rc) return rc; #endif rc = wil_cfg80211_mgmt_tx(wiphy, wdev, wdev->preset_chandef.chan, true, 0, frame, len, true, false, NULL); kfree(frame); wil_info(wil, "%s() -> %d\n", __func__, rc); #if 0 msleep(300); rtnl_lock(); dev_close(ndev); ndev->flags &= ~IFF_UP; rtnl_unlock(); wil_reset(wil); #endif return len; }
/*---------info------------*/ static int wil_info_debugfs_show(struct seq_file *s, void *data) { struct wil6210_priv *wil = s->private; struct net_device *ndev = wil_to_ndev(wil); 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 void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) { int cid = -ENOENT; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; might_sleep(); if (bssid) { cid = wil_find_cid(wil, bssid); wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); } else { wil_dbg_misc(wil, "%s(all)\n", __func__); } if (cid >= 0) /* disconnect 1 peer */ wil_disconnect_cid(wil, cid); else {/* disconnect all */ /* * BUG FIX: Disconnect single sta only * We may get to here few times * with BSSID address */ if(ndev->dev_addr && bssid) { if(ether_addr_equal(ndev->dev_addr, bssid) ) { for (cid = 0; cid < WIL6210_MAX_CID; cid++) wil_disconnect_cid(wil, cid); } } } /* link state */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: wil_link_off(wil); clear_bit(wil_status_fwconnected, &wil->status); switch (wdev->sme_state) { case CFG80211_SME_CONNECTED: cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL, 0, GFP_KERNEL); break; case CFG80211_SME_CONNECTING: cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); break; default: break; } wil_dbg_misc(wil, "clear_bit(wil_status_dontscan)\n"); clear_bit(wil_status_dontscan, &wil->status); break; default: /* AP-like interface and monitor: * never scan, always connected */ if (bssid) cfg80211_del_sta(ndev, bssid, GFP_KERNEL); break; } }
void wil_if_remove(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); unregister_netdev(ndev); }
int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime) { int rc = 0; struct wireless_dev *wdev = wil->wdev; struct net_device *ndev = wil_to_ndev(wil); bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY, wil->fw_capabilities); wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system"); if (wmi_only || debug_fw) { wil_dbg_pm(wil, "Deny any suspend - %s mode\n", wmi_only ? "wmi_only" : "debug_fw"); rc = -EBUSY; goto out; } if (is_runtime && !wil->platform_ops.suspend) { rc = -EBUSY; goto out; } if (!(ndev->flags & IFF_UP)) { /* can always sleep when down */ wil_dbg_pm(wil, "Interface is down\n"); goto out; } if (test_bit(wil_status_resetting, wil->status)) { wil_dbg_pm(wil, "Delay suspend when resetting\n"); rc = -EBUSY; goto out; } if (wil->recovery_state != fw_recovery_idle) { wil_dbg_pm(wil, "Delay suspend during recovery\n"); rc = -EBUSY; goto out; } /* interface is running */ switch (wdev->iftype) { case NL80211_IFTYPE_MONITOR: wil_dbg_pm(wil, "Sniffer\n"); rc = -EBUSY; goto out; /* for STA-like interface, don't runtime suspend */ case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (test_bit(wil_status_fwconnecting, wil->status)) { wil_dbg_pm(wil, "Delay suspend when connecting\n"); rc = -EBUSY; goto out; } /* Runtime pm not supported in case the interface is up */ if (is_runtime) { wil_dbg_pm(wil, "STA-like interface\n"); rc = -EBUSY; goto out; } break; /* AP-like interface - can't suspend */ default: wil_dbg_pm(wil, "AP-like interface\n"); rc = -EBUSY; break; } out: wil_dbg_pm(wil, "can_suspend: %s => %s (%d)\n", is_runtime ? "runtime" : "system", rc ? "No" : "Yes", rc); if (rc) wil->suspend_stats.rejected_by_host++; return rc; }
void wil_memcpy_toio_halp_vote(struct wil6210_priv *wil, volatile void __iomem *dst, const void *src, size_t count) { wil_halp_vote(wil); wil_memcpy_toio_32(dst, src, count); wil_halp_unvote(wil); } static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, u16 reason_code, bool from_event) __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { uint i; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; struct wil_sta_info *sta = &wil->sta[cid]; might_sleep(); wil_dbg_misc(wil, "disconnect_cid: CID %d, status %d\n", cid, sta->status); /* inform upper/lower layers */ if (sta->status != wil_sta_unused) { if (!from_event) { bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ? disable_ap_sme : false; wmi_disconnect_sta(wil, sta->addr, reason_code, true, del_sta); }
static int wil_bf_debugfs_show(struct seq_file *s, void *data) { int rc; int i; struct wil6210_priv *wil = s->private; 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, &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" " 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, 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, }; /*---------SSID------------*/ static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; struct wireless_dev *wdev = wil_to_wdev(wil); return simple_read_from_buffer(user_buf, count, ppos, wdev->ssid, wdev->ssid_len); } static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct wil6210_priv *wil = file->private_data; struct wireless_dev *wdev = wil_to_wdev(wil); struct net_device *ndev = wil_to_ndev(wil); if (*ppos != 0) { wil_err(wil, "Unable to set SSID substring from [%d]\n", (int)*ppos); return -EINVAL; } if (count > sizeof(wdev->ssid)) { wil_err(wil, "SSID too long, len = %d\n", (int)count); return -EINVAL; } if (netif_running(ndev)) { wil_err(wil, "Unable to change SSID on running interface\n"); return -EINVAL; } wdev->ssid_len = count; return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, buf, count); } static const struct file_operations fops_ssid = { .read = wil_read_file_ssid, .write = wil_write_file_ssid, .open = simple_open, }; /*---------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_to_wdev(wil); 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"; 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\n", i, p->addr, status); 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_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_to_ndev(wil); 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_up(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; struct ieee80211_channel *channel = wdev->preset_chandef.chan; int rc; int bi; u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); rc = wil_reset(wil); if (rc) return rc; /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); switch (wdev->iftype) { case NL80211_IFTYPE_STATION: wil_dbg_misc(wil, "type: STATION\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_AP: wil_dbg_misc(wil, "type: AP\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_CLIENT: wil_dbg_misc(wil, "type: P2P_CLIENT\n"); bi = 0; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_P2P_GO: wil_dbg_misc(wil, "type: P2P_GO\n"); bi = 100; ndev->type = ARPHRD_ETHER; break; case NL80211_IFTYPE_MONITOR: wil_dbg_misc(wil, "type: Monitor\n"); bi = 0; ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ break; default: return -EOPNOTSUPP; } /* Apply profile in the following order: */ /* SSID and channel for the AP */ switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (wdev->ssid_len == 0) { wil_err(wil, "SSID not set\n"); return -EINVAL; } rc = wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); if (rc) return rc; break; default: break; } /* MAC address - pre-requisite for other commands */ wmi_set_mac_address(wil, ndev->dev_addr); /* Set up beaconing if required. */ if (bi > 0) { rc = wmi_pcp_start(wil, bi, wmi_nettype, (channel ? channel->hw_value : 0)); if (rc) return rc; } /* Rx VRING. After MAC and beacon */ wil_rx_init(wil); return 0; }