Enable multiple (up to 8 - HW/FW limitation) simultaneous connections.
Each connection has its own CID (connection ID) that describes chip's
beam-forming entity. Tx Vring should refer to correct CID for frame to reach
its destination.
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
{
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
+
+ int cid = wil_find_cid(wil, mac);
struct wmi_notify_req_cmd cmd = {
- .cid = 0,
+ .cid = cid,
.interval_usec = 0,
};
- if (memcmp(mac, wil->dst_addr[0], ETH_ALEN))
+ wil_info(wil, "%s(%pM) CID %d\n", __func__, mac, cid);
+ if (cid < 0)
return -ENOENT;
/* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */
+ /* TODO: keep stats per CID */
rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20);
if (rc)
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
if (vring->va) {
+ int cid = wil->vring2cid_tid[i][0];
+ int tid = wil->vring2cid_tid[i][1];
char name[10];
snprintf(name, sizeof(name), "tx_%2d", i);
+
+ seq_printf(s, "\n%pM CID %d TID %d\n",
+ wil->sta[cid].addr, cid, tid);
wil_print_vring(s, wil, name, vring, '_', 'H');
}
}
.llseek = seq_lseek,
};
+/*---------Station matrix------------*/
+
+static int wil_sta_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ int i;
+
+ 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);
+ }
+
+ return 0;
+}
+
+static int wil_sta_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_sta_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_sta = {
+ .open = wil_sta_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
/*----------------*/
int wil6210_debugfs_init(struct wil6210_priv *wil)
{
debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox);
debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring);
+ debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta);
debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc);
debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg,
&dbg_txdesc_index);
rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0);
wil->pending_connect_cid = -1;
- if (rc == 0)
+ if (rc == 0) {
+ wil->sta[cid].status = wil_sta_connected;
wil_link_on(wil);
+ } else {
+ wil->sta[cid].status = wil_sta_unused;
+ }
}
int wil_priv_init(struct wil6210_priv *wil)
{
wil_dbg_misc(wil, "%s()\n", __func__);
+ memset(wil->sta, 0, sizeof(wil->sta));
+
mutex_init(&wil->mutex);
mutex_init(&wil->wmi_mutex);
return rc;
}
+
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac)
+{
+ int i;
+ int rc = -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ if ((wil->sta[i].status != wil_sta_unused) &&
+ (0 == memcmp(wil->sta[i].addr, mac, ETH_ALEN))) {
+ rc = i;
+ break;
+ }
+ }
+
+ return rc;
+}
}
vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr);
+ wil->vring2cid_tid[id][0] = cid;
+ wil->vring2cid_tid[id][1] = tid;
+
return 0;
out_free:
wil_vring_free(wil, vring, 1);
static struct vring *wil_find_tx_vring(struct wil6210_priv *wil,
struct sk_buff *skb)
{
- struct vring *v = &wil->vring_tx[0];
+ int i;
+ struct ethhdr *eth = (void *)skb->data;
+ int cid = wil_find_cid(wil, eth->h_dest);
+
+ if (cid < 0)
+ return NULL;
- if (v->va)
- return v;
+ /* TODO: fix for multiple TID */
+ for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) {
+ if (wil->vring2cid_tid[i][0] == cid) {
+ struct vring *v = &wil->vring_tx[i];
+ wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n",
+ __func__, eth->h_dest, i);
+ if (v->va) {
+ return v;
+ } else {
+ wil_dbg_txrx(wil, "vring[%d] not valid\n", i);
+ return NULL;
+ }
+ }
+ }
return NULL;
}
}
_d = &(vring->va[i].tx);
- /* FIXME FW can accept only unicast frames for the peer */
- memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN);
-
pa = dma_map_single(dev, skb->data,
skb_headlen(skb), DMA_TO_DEVICE);
netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct wil6210_priv *wil = ndev_to_wil(ndev);
+ struct ethhdr *eth = (void *)skb->data;
struct vring *vring;
int rc;
}
/* find vring */
- vring = wil_find_tx_vring(wil, skb);
+ if (is_unicast_ether_addr(eth->h_dest)) {
+ vring = wil_find_tx_vring(wil, skb);
+ } else {
+ int i = 0;
+ /* TODO: duplicate for all CID's */
+ vring = &wil->vring_tx[i];
+ if (vring->va) {
+ int cid = wil->vring2cid_tid[i][0];
+ /* FIXME FW can accept only unicast frames */
+ memcpy(skb->data, wil->sta[cid].addr, ETH_ALEN);
+ } else {
+ vring = NULL;
+ }
+ }
if (!vring) {
- wil_err(wil, "No Tx VRING available\n");
+ wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
/* set up vring entry */
u16 peer_tx_sector;
};
+enum wil_sta_status {
+ wil_sta_unused = 0,
+ wil_sta_conn_pending = 1,
+ wil_sta_connected = 2,
+};
+/**
+ * struct wil_sta_info - data for peer
+ *
+ * Peer identified by its CID (connection ID)
+ * NIC performs beam forming for each peer;
+ * if no beam forming done, frame exchange is not
+ * possible.
+ */
+struct wil_sta_info {
+ u8 addr[ETH_ALEN];
+ enum wil_sta_status status;
+};
+
struct wil6210_priv {
struct pci_dev *pdev;
int n_msi;
/* DMA related */
struct vring vring_rx;
struct vring vring_tx[WIL6210_MAX_TX_RINGS];
- u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN];
+ u8 vring2cid_tid[WIL6210_MAX_TX_RINGS][2]; /* [0] - CID, [1] - TID */
+ struct wil_sta_info sta[WIL6210_MAX_CID];
/* scan */
struct cfg80211_scan_request *scan_request;
int wil_up(struct wil6210_priv *wil);
int wil_down(struct wil6210_priv *wil);
void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
+int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
evt->assoc_req_len, evt->assoc_resp_len);
return;
}
+ if (evt->cid >= WIL6210_MAX_CID) {
+ wil_err(wil, "Connect CID invalid : %d\n", evt->cid);
+ return;
+ }
+
ch = evt->channel + 1;
wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
evt->bssid, ch, evt->cid);
/* FIXME FW can transmit only ucast frames to peer */
/* FIXME real ring_id instead of hard coded 0 */
- memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN);
+ memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN);
+ wil->sta[evt->cid].status = wil_sta_conn_pending;
wil->pending_connect_cid = evt->cid;
queue_work(wil->wmi_wq_conn, &wil->connect_worker);
void *d, int len)
{
struct wmi_disconnect_event *evt = d;
+ int cid = wil_find_cid(wil, evt->bssid);
- wil_dbg_wmi(wil, "Disconnect %pM reason %d proto %d wmi\n",
- evt->bssid,
+ wil_dbg_wmi(wil, "Disconnect %pM CID %d reason %d proto %d wmi\n",
+ evt->bssid, cid,
evt->protocol_reason_status, evt->disconnect_reason);
wil->sinfo_gen++;
+ /* TODO: fix for multiple connections */
+
wil6210_disconnect(wil, evt->bssid);
+ if (cid >= 0)
+ wil->sta[cid].status = wil_sta_unused;
}
static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len)