Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bd60982 authored by Lior David's avatar Lior David Committed by Kalle Valo
Browse files

wil6210: multiple VIFs support for connections and data path



Track the connection status per-VIF.
The data path code is also updated to support multiple VIFs.
This includes RX and TX VRING management, NAPI poll loops,
RX reordering and related code.
Power management code used to check if the main interface
is up or based on connection state of the main interface,
adapt this code to take all VIFs into account.

Signed-off-by: default avatarLior David <liord@codeaurora.org>
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 404bbb3c
Loading
Loading
Loading
Loading
+15 −17
Original line number Diff line number Diff line
@@ -319,7 +319,7 @@ int wil_cid_fill_sinfo(struct wil6210_vif *vif, int cid,
	sinfo->tx_packets = stats->tx_packets;
	sinfo->tx_failed = stats->tx_errors;

	if (test_bit(wil_status_fwconnected, wil->status)) {
	if (test_bit(wil_vif_fwconnected, vif->status)) {
		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
		if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING,
			     wil->fw_capabilities))
@@ -490,11 +490,10 @@ wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
			return ERR_PTR(-EINVAL);
		}

		vif = kzalloc(sizeof(*vif), GFP_KERNEL);
		if (!vif)
		p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
		if (!p2p_wdev)
			return ERR_PTR(-ENOMEM);

		p2p_wdev = vif_to_wdev(vif);
		p2p_wdev->iftype = type;
		p2p_wdev->wiphy = wiphy;
		/* use our primary ethernet address */
@@ -904,8 +903,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
	wil_dbg_misc(wil, "connect, mid=%d\n", vif->mid);
	wil_print_connect_params(wil, sme);

	if (test_bit(wil_status_fwconnecting, wil->status) ||
	    test_bit(wil_status_fwconnected, wil->status))
	if (test_bit(wil_vif_fwconnecting, vif->status) ||
	    test_bit(wil_vif_fwconnected, vif->status))
		return -EALREADY;

	if (sme->ie_len > WMI_MAX_IE_LEN) {
@@ -1009,18 +1008,19 @@ static int wil_cfg80211_connect(struct wiphy *wiphy,
	ether_addr_copy(conn.bssid, bss->bssid);
	ether_addr_copy(conn.dst_mac, bss->bssid);

	set_bit(wil_status_fwconnecting, wil->status);
	set_bit(wil_vif_fwconnecting, vif->status);

	rc = wmi_send(wil, WMI_CONNECT_CMDID, vif->mid, &conn, sizeof(conn));
	if (rc == 0) {
		netif_carrier_on(ndev);
		if (!wil_has_other_active_ifaces(wil, ndev, false, true))
			wil6210_bus_request(wil, WIL_MAX_BUS_REQUEST_KBPS);
		vif->bss = bss;
		/* Connect can take lots of time */
		mod_timer(&vif->connect_timer,
			  jiffies + msecs_to_jiffies(5000));
	} else {
		clear_bit(wil_status_fwconnecting, wil->status);
		clear_bit(wil_vif_fwconnecting, vif->status);
	}

 out:
@@ -1040,8 +1040,8 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
	wil_dbg_misc(wil, "disconnect: reason=%d, mid=%d\n",
		     reason_code, vif->mid);

	if (!(test_bit(wil_status_fwconnecting, wil->status) ||
	      test_bit(wil_status_fwconnected, wil->status))) {
	if (!(test_bit(wil_vif_fwconnecting, vif->status) ||
	      test_bit(wil_vif_fwconnected, vif->status))) {
		wil_err(wil, "Disconnect was called while disconnected\n");
		return 0;
	}
@@ -1946,7 +1946,7 @@ static int wil_cfg80211_suspend(struct wiphy *wiphy,
	mutex_lock(&wil->mutex);
	mutex_lock(&wil->vif_mutex);
	wil_p2p_stop_radio_operations(wil);
	wil_abort_scan(ndev_to_vif(wil->main_ndev), true);
	wil_abort_scan_all_vifs(wil, true);
	mutex_unlock(&wil->vif_mutex);
	mutex_unlock(&wil->mutex);

@@ -2234,7 +2234,6 @@ void wil_cfg80211_deinit(struct wil6210_priv *wil)
void wil_p2p_wdev_free(struct wil6210_priv *wil)
{
	struct wireless_dev *p2p_wdev;
	struct wil6210_vif *vif;

	mutex_lock(&wil->vif_mutex);
	p2p_wdev = wil->p2p_wdev;
@@ -2243,8 +2242,7 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil)
	mutex_unlock(&wil->vif_mutex);
	if (p2p_wdev) {
		cfg80211_unregister_wdev(p2p_wdev);
		vif = wdev_to_vif(wil, p2p_wdev);
		kfree(vif);
		kfree(p2p_wdev);
	}
}

@@ -2538,7 +2536,7 @@ static int wil_rf_sector_get_selected(struct wiphy *wiphy,
			return -ENOENT;
		}
	} else {
		if (test_bit(wil_status_fwconnected, wil->status)) {
		if (test_bit(wil_vif_fwconnected, vif->status)) {
			wil_err(wil, "must specify MAC address when connected\n");
			return -EINVAL;
		}
@@ -2665,7 +2663,7 @@ static int wil_rf_sector_set_selected(struct wiphy *wiphy,
			cid = -1;
		}
	} else {
		if (test_bit(wil_status_fwconnected, wil->status)) {
		if (test_bit(wil_vif_fwconnected, vif->status)) {
			wil_err(wil, "must specify MAC address when connected\n");
			return -EINVAL;
		}
+16 −4
Original line number Diff line number Diff line
@@ -1201,12 +1201,13 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
{
	struct wil6210_priv *wil = s->private;
	struct station_info sinfo;
	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
	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:
@@ -1219,9 +1220,15 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
			status = "connected";
			break;
		}
		seq_printf(s, "[%d] %pM %s\n", i, p->addr, status);
		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) {
		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;
@@ -1229,6 +1236,8 @@ static int wil_link_debugfs_show(struct seq_file *s, void *data)
			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");
		}
	}

@@ -1420,6 +1429,7 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
		struct wil_sta_info *p = &wil->sta[i];
		char *status = "unknown";
		u8 aid = 0;
		u8 mid;

		switch (p->status) {
		case wil_sta_unused:
@@ -1433,7 +1443,9 @@ __acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock)
			aid = p->aid;
			break;
		}
		seq_printf(s, "[%d] %pM %s AID %d\n", i, p->addr, status, aid);
		mid = (p->status != wil_sta_unused) ? p->mid : U8_MAX;
		seq_printf(s, "[%d] %pM %s MID %d AID %d\n", i, p->addr, status,
			   mid, aid);

		if (p->status == wil_sta_connected) {
			spin_lock_bh(&p->tid_rx_lock);
+1 −1
Original line number Diff line number Diff line
@@ -127,7 +127,7 @@ void wil6210_unmask_irq_tx(struct wil6210_priv *wil)

void wil6210_unmask_irq_rx(struct wil6210_priv *wil)
{
	bool unmask_rx_htrsh = test_bit(wil_status_fwconnected, wil->status);
	bool unmask_rx_htrsh = atomic_read(&wil->connected_vifs) > 0;

	wil_w(wil, RGF_DMA_EP_RX_ICR + offsetof(struct RGF_ICR, IMC),
	      unmask_rx_htrsh ? WIL6210_IMC_RX : WIL6210_IMC_RX_NO_RX_HTRSH);
+55 −23
Original line number Diff line number Diff line
@@ -175,6 +175,15 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
		     cid, sta->mid, sta->status);
	/* inform upper/lower layers */
	if (sta->status != wil_sta_unused) {
		if (vif->mid != sta->mid) {
			wil_err(wil, "STA MID mismatch with VIF MID(%d)\n",
				vif->mid);
			/* let FW override sta->mid but be more strict with
			 * user space requests
			 */
			if (!from_event)
				return;
		}
		if (!from_event) {
			bool del_sta = (wdev->iftype == NL80211_IFTYPE_AP) ?
						disable_ap_sme : false;
@@ -277,32 +286,35 @@ static void _wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid,
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
		wil_bcast_fini(vif);
		wil_update_net_queues_bh(wil, NULL, true);
		wil_update_net_queues_bh(wil, vif, NULL, true);
		netif_carrier_off(ndev);
		if (!wil_has_other_active_ifaces(wil, ndev, false, true))
			wil6210_bus_request(wil, WIL_DEFAULT_BUS_REQUEST_KBPS);

		if (test_bit(wil_status_fwconnected, wil->status)) {
			clear_bit(wil_status_fwconnected, wil->status);
		if (test_and_clear_bit(wil_vif_fwconnected, vif->status)) {
			atomic_dec(&wil->connected_vifs);
			cfg80211_disconnected(ndev, reason_code,
					      NULL, 0,
					      vif->locally_generated_disc,
					      GFP_KERNEL);
			vif->locally_generated_disc = false;
		} else if (test_bit(wil_status_fwconnecting, wil->status)) {
		} else if (test_bit(wil_vif_fwconnecting, vif->status)) {
			cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0,
						WLAN_STATUS_UNSPECIFIED_FAILURE,
						GFP_KERNEL);
			vif->bss = NULL;
		}
		clear_bit(wil_status_fwconnecting, wil->status);
		clear_bit(wil_vif_fwconnecting, vif->status);
		break;
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_P2P_GO:
		if (!wil_vif_is_connected(wil, vif->mid)) {
			wil_update_net_queues_bh(wil, NULL, true);
			clear_bit(wil_status_fwconnected, wil->status);
			wil_update_net_queues_bh(wil, vif, NULL, true);
			if (test_and_clear_bit(wil_vif_fwconnected,
					       vif->status))
				atomic_dec(&wil->connected_vifs);
		} else {
			wil_update_net_queues_bh(wil, NULL, false);
			wil_update_net_queues_bh(wil, vif, NULL, false);
		}
		break;
	default:
@@ -322,11 +334,11 @@ void wil_disconnect_worker(struct work_struct *work)
		struct wmi_disconnect_event evt;
	} __packed reply;

	if (test_bit(wil_status_fwconnected, wil->status))
	if (test_bit(wil_vif_fwconnected, vif->status))
		/* connect succeeded after all */
		return;

	if (!test_bit(wil_status_fwconnecting, wil->status))
	if (!test_bit(wil_vif_fwconnecting, vif->status))
		/* already disconnected */
		return;

@@ -338,11 +350,11 @@ void wil_disconnect_worker(struct work_struct *work)
		return;
	}

	wil_update_net_queues_bh(wil, NULL, true);
	wil_update_net_queues_bh(wil, vif, NULL, true);
	netif_carrier_off(ndev);
	cfg80211_connect_result(ndev, NULL, NULL, 0, NULL, 0,
				WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
	clear_bit(wil_status_fwconnecting, wil->status);
	clear_bit(wil_vif_fwconnecting, vif->status);
}

static int wil_wait_for_recovery(struct wil6210_priv *wil)
@@ -1087,6 +1099,20 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync)
	}
}

void wil_abort_scan_all_vifs(struct wil6210_priv *wil, bool sync)
{
	int i;

	lockdep_assert_held(&wil->vif_mutex);

	for (i = 0; i < wil->max_vifs; i++) {
		struct wil6210_vif *vif = wil->vifs[i];

		if (vif)
			wil_abort_scan(vif, sync);
	}
}

int wil_ps_update(struct wil6210_priv *wil, enum wmi_ps_profile_type ps_profile)
{
	int rc;
@@ -1139,6 +1165,7 @@ static int wil_restore_vifs(struct wil6210_priv *wil)
		vif = wil->vifs[i];
		if (!vif)
			continue;
		vif->ap_isolate = 0;
		if (vif->mid) {
			ndev = vif_to_ndev(vif);
			wdev = vif_to_wdev(vif);
@@ -1162,10 +1189,10 @@ static int wil_restore_vifs(struct wil6210_priv *wil)
 */
int wil_reset(struct wil6210_priv *wil, bool load_fw)
{
	int rc;
	int rc, i;
	unsigned long status_flags = BIT(wil_status_resetting);
	int no_flash;
	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
	struct wil6210_vif *vif;

	wil_dbg_misc(wil, "reset\n");

@@ -1214,17 +1241,23 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
		goto out;
	}

	mutex_lock(&wil->vif_mutex);
	wil_abort_scan_all_vifs(wil, false);
	mutex_unlock(&wil->vif_mutex);

	for (i = 0; i < wil->max_vifs; i++) {
		vif = wil->vifs[i];
		if (vif) {
			cancel_work_sync(&vif->disconnect_worker);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
			wil6210_disconnect(vif, NULL,
					   WLAN_REASON_DEAUTH_LEAVING, false);
		}
	}
	wil_bcast_fini_all(wil);

	/* Disable device led before reset*/
	wmi_led_cfg(wil, false);

	mutex_lock(&wil->vif_mutex);
	wil_abort_scan(vif, false);
	mutex_unlock(&wil->vif_mutex);

	/* prevent NAPI from being scheduled and prevent wmi commands */
	mutex_lock(&wil->wmi_mutex);
	if (test_bit(wil_status_suspending, wil->status))
@@ -1294,7 +1327,6 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
	}

	/* init after reset */
	vif->ap_isolate = 0;
	reinit_completion(&wil->wmi_ready);
	reinit_completion(&wil->wmi_call);
	reinit_completion(&wil->halp.comp);
@@ -1446,7 +1478,7 @@ int __wil_down(struct wil6210_priv *wil)

	mutex_lock(&wil->vif_mutex);
	wil_p2p_stop_radio_operations(wil);
	wil_abort_scan(ndev_to_vif(wil->main_ndev), false);
	wil_abort_scan_all_vifs(wil, false);
	mutex_unlock(&wil->vif_mutex);

	return wil_reset(wil, false);
+41 −5
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ bool wil_has_other_active_ifaces(struct wil6210_priv *wil,

bool wil_has_active_ifaces(struct wil6210_priv *wil, bool up, bool ok)
{
	/* use NULL ndev argument to check all interfaces */
	return wil_has_other_active_ifaces(wil, NULL, up, ok);
}

@@ -130,11 +131,19 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget)
	for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) {
		struct vring *vring = &wil->vring_tx[i];
		struct vring_tx_data *txdata = &wil->vring_tx_data[i];
		struct wil6210_vif *vif;

		if (!vring->va || !txdata->enabled ||
		    txdata->mid >= wil->max_vifs)
			continue;

		if (!vring->va || !txdata->enabled)
		vif = wil->vifs[txdata->mid];
		if (unlikely(!vif)) {
			wil_dbg_txrx(wil, "Invalid MID %d\n", txdata->mid);
			continue;
		}

		tx_done += wil_tx_complete(wil, i);
		tx_done += wil_tx_complete(vif, i);
	}

	if (tx_done < budget) {
@@ -232,6 +241,8 @@ static void wil_vif_init(struct wil6210_vif *vif)
	INIT_WORK(&vif->p2p.delayed_listen_work, wil_p2p_delayed_listen_work);

	INIT_LIST_HEAD(&vif->probe_client_pending);

	vif->net_queue_stopped = 1;
}

static u8 wil_vif_find_free_mid(struct wil6210_priv *wil)
@@ -406,12 +417,14 @@ int wil_if_add(struct wil6210_priv *wil)
		return rc;
	}

	netif_napi_add(ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
	init_dummy_netdev(&wil->napi_ndev);
	netif_napi_add(&wil->napi_ndev, &wil->napi_rx, wil6210_netdev_poll_rx,
		       WIL6210_NAPI_BUDGET);
	netif_tx_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx,
	netif_tx_napi_add(&wil->napi_ndev,
			  &wil->napi_tx, wil6210_netdev_poll_tx,
			  WIL6210_NAPI_BUDGET);

	wil_update_net_queues_bh(wil, NULL, true);
	wil_update_net_queues_bh(wil, vif, NULL, true);

	rtnl_lock();
	rc = wil_vif_add(wil, vif);
@@ -450,10 +463,29 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
	 */
	unregister_netdevice(ndev);

	mutex_lock(&wil->mutex);
	wil6210_disconnect(vif, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
	mutex_unlock(&wil->mutex);

	if (any_active && vif->mid != 0)
		wmi_port_delete(wil, vif->mid);

	/* make sure no one is accessing the VIF before removing */
	mutex_lock(&wil->vif_mutex);
	wil->vifs[mid] = NULL;
	/* ensure NAPI code will see the NULL VIF */
	wmb();
	if (test_bit(wil_status_napi_en, wil->status)) {
		napi_synchronize(&wil->napi_rx);
		napi_synchronize(&wil->napi_tx);
	}
	mutex_unlock(&wil->vif_mutex);

	flush_work(&wil->wmi_event_worker);
	del_timer_sync(&vif->connect_timer);
	cancel_work_sync(&vif->disconnect_worker);
	wil_probe_client_flush(vif);
	cancel_work_sync(&vif->probe_client_worker);
	/* for VIFs, ndev will be freed by destructor after RTNL is unlocked.
	 * the main interface will be freed in wil_if_free, we need to keep it
	 * a bit longer so logging macros will work.
@@ -470,5 +502,9 @@ void wil_if_remove(struct wil6210_priv *wil)
	rtnl_lock();
	wil_vif_remove(wil, 0);
	rtnl_unlock();

	netif_napi_del(&wil->napi_tx);
	netif_napi_del(&wil->napi_rx);

	wiphy_unregister(wdev->wiphy);
}
Loading