|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * Copyright (c) 2012-2015 Qualcomm Atheros, Inc.
|
|
|
+ * Copyright (c) 2012-2016 Qualcomm Atheros, Inc.
|
|
|
*
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
@@ -426,6 +426,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
|
|
|
const size_t assoc_req_ie_offset = sizeof(u16) * 2;
|
|
|
/* capinfo(u16) + status_code(u16) + associd(u16) + IEs */
|
|
|
const size_t assoc_resp_ie_offset = sizeof(u16) * 3;
|
|
|
+ int rc;
|
|
|
|
|
|
if (len < sizeof(*evt)) {
|
|
|
wil_err(wil, "Connect event too short : %d bytes\n", len);
|
|
@@ -445,8 +446,8 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
|
|
|
}
|
|
|
|
|
|
ch = evt->channel + 1;
|
|
|
- wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n",
|
|
|
- evt->bssid, ch, evt->cid);
|
|
|
+ wil_info(wil, "Connect %pM channel [%d] cid %d\n",
|
|
|
+ evt->bssid, ch, evt->cid);
|
|
|
wil_hex_dump_wmi("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1,
|
|
|
evt->assoc_info, len - sizeof(*evt), true);
|
|
|
|
|
@@ -468,20 +469,67 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
|
|
|
assoc_resp_ielen = 0;
|
|
|
}
|
|
|
|
|
|
+ mutex_lock(&wil->mutex);
|
|
|
+ if (test_bit(wil_status_resetting, wil->status) ||
|
|
|
+ !test_bit(wil_status_fwready, wil->status)) {
|
|
|
+ wil_err(wil, "status_resetting, cancel connect event, CID %d\n",
|
|
|
+ evt->cid);
|
|
|
+ mutex_unlock(&wil->mutex);
|
|
|
+ /* no need for cleanup, wil_reset will do that */
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
|
|
|
(wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
|
|
|
if (!test_bit(wil_status_fwconnecting, wil->status)) {
|
|
|
wil_err(wil, "Not in connecting state\n");
|
|
|
+ mutex_unlock(&wil->mutex);
|
|
|
return;
|
|
|
}
|
|
|
del_timer_sync(&wil->connect_timer);
|
|
|
- cfg80211_connect_result(ndev, evt->bssid,
|
|
|
- assoc_req_ie, assoc_req_ielen,
|
|
|
- assoc_resp_ie, assoc_resp_ielen,
|
|
|
- WLAN_STATUS_SUCCESS, GFP_KERNEL);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* FIXME FW can transmit only ucast frames to peer */
|
|
|
+ /* FIXME real ring_id instead of hard coded 0 */
|
|
|
+ ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
|
|
|
+ wil->sta[evt->cid].status = wil_sta_conn_pending;
|
|
|
|
|
|
+ rc = wil_tx_init(wil, evt->cid);
|
|
|
+ if (rc) {
|
|
|
+ wil_err(wil, "%s: config tx vring failed for CID %d, rc (%d)\n",
|
|
|
+ __func__, evt->cid, rc);
|
|
|
+ wmi_disconnect_sta(wil, wil->sta[evt->cid].addr,
|
|
|
+ WLAN_REASON_UNSPECIFIED, false);
|
|
|
+ } else {
|
|
|
+ wil_info(wil, "%s: successful connection to CID %d\n",
|
|
|
+ __func__, evt->cid);
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((wdev->iftype == NL80211_IFTYPE_STATION) ||
|
|
|
+ (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
|
|
|
+ if (rc) {
|
|
|
+ netif_tx_stop_all_queues(ndev);
|
|
|
+ netif_carrier_off(ndev);
|
|
|
+ wil_err(wil,
|
|
|
+ "%s: cfg80211_connect_result with failure\n",
|
|
|
+ __func__);
|
|
|
+ cfg80211_connect_result(ndev, evt->bssid, NULL, 0,
|
|
|
+ NULL, 0,
|
|
|
+ WLAN_STATUS_UNSPECIFIED_FAILURE,
|
|
|
+ GFP_KERNEL);
|
|
|
+ goto out;
|
|
|
+ } else {
|
|
|
+ cfg80211_connect_result(ndev, evt->bssid,
|
|
|
+ assoc_req_ie, assoc_req_ielen,
|
|
|
+ assoc_resp_ie, assoc_resp_ielen,
|
|
|
+ WLAN_STATUS_SUCCESS,
|
|
|
+ GFP_KERNEL);
|
|
|
+ }
|
|
|
} else if ((wdev->iftype == NL80211_IFTYPE_AP) ||
|
|
|
(wdev->iftype == NL80211_IFTYPE_P2P_GO)) {
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
memset(&sinfo, 0, sizeof(sinfo));
|
|
|
|
|
|
sinfo.generation = wil->sinfo_gen++;
|
|
@@ -492,17 +540,21 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len)
|
|
|
}
|
|
|
|
|
|
cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL);
|
|
|
+ } else {
|
|
|
+ wil_err(wil, "%s: unhandled iftype %d for CID %d\n",
|
|
|
+ __func__, wdev->iftype, evt->cid);
|
|
|
+ goto out;
|
|
|
}
|
|
|
- clear_bit(wil_status_fwconnecting, wil->status);
|
|
|
- set_bit(wil_status_fwconnected, wil->status);
|
|
|
|
|
|
- /* FIXME FW can transmit only ucast frames to peer */
|
|
|
- /* FIXME real ring_id instead of hard coded 0 */
|
|
|
- ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
|
|
|
- wil->sta[evt->cid].status = wil_sta_conn_pending;
|
|
|
+ wil->sta[evt->cid].status = wil_sta_connected;
|
|
|
+ set_bit(wil_status_fwconnected, wil->status);
|
|
|
+ netif_tx_wake_all_queues(ndev);
|
|
|
|
|
|
- wil->pending_connect_cid = evt->cid;
|
|
|
- queue_work(wil->wq_service, &wil->connect_worker);
|
|
|
+out:
|
|
|
+ if (rc)
|
|
|
+ wil->sta[evt->cid].status = wil_sta_unused;
|
|
|
+ clear_bit(wil_status_fwconnecting, wil->status);
|
|
|
+ mutex_unlock(&wil->mutex);
|
|
|
}
|
|
|
|
|
|
static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
|
|
@@ -511,8 +563,8 @@ static void wmi_evt_disconnect(struct wil6210_priv *wil, int id,
|
|
|
struct wmi_disconnect_event *evt = d;
|
|
|
u16 reason_code = le16_to_cpu(evt->protocol_reason_status);
|
|
|
|
|
|
- wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
|
|
|
- evt->bssid, reason_code, evt->disconnect_reason);
|
|
|
+ wil_info(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
|
|
|
+ evt->bssid, reason_code, evt->disconnect_reason);
|
|
|
|
|
|
wil->sinfo_gen++;
|
|
|
|
|
@@ -727,6 +779,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
|
|
void __iomem *src;
|
|
|
ulong flags;
|
|
|
unsigned n;
|
|
|
+ unsigned int num_immed_reply = 0;
|
|
|
|
|
|
if (!test_bit(wil_status_mbox_ready, wil->status)) {
|
|
|
wil_err(wil, "Reset in progress. Cannot handle WMI event\n");
|
|
@@ -736,6 +789,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
|
|
for (n = 0;; n++) {
|
|
|
u16 len;
|
|
|
bool q;
|
|
|
+ bool immed_reply = false;
|
|
|
|
|
|
r->head = wil_r(wil, RGF_MBOX +
|
|
|
offsetof(struct wil6210_mbox_ctl, rx.head));
|
|
@@ -784,6 +838,13 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
|
|
struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
|
|
|
u16 id = le16_to_cpu(wmi->id);
|
|
|
u32 tstamp = le32_to_cpu(wmi->timestamp);
|
|
|
+ if (wil->reply_id && wil->reply_id == id) {
|
|
|
+ if (wil->reply_buf) {
|
|
|
+ memcpy(wil->reply_buf, wmi,
|
|
|
+ min(len, wil->reply_size));
|
|
|
+ immed_reply = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
|
|
|
id, wmi->mid, tstamp);
|
|
@@ -799,15 +860,24 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
|
|
|
wil_w(wil, RGF_MBOX +
|
|
|
offsetof(struct wil6210_mbox_ctl, rx.tail), r->tail);
|
|
|
|
|
|
- /* add to the pending list */
|
|
|
- spin_lock_irqsave(&wil->wmi_ev_lock, flags);
|
|
|
- list_add_tail(&evt->list, &wil->pending_wmi_ev);
|
|
|
- spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
|
|
- q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
|
|
|
- wil_dbg_wmi(wil, "queue_work -> %d\n", q);
|
|
|
+ if (immed_reply) {
|
|
|
+ wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n",
|
|
|
+ __func__, wil->reply_id);
|
|
|
+ kfree(evt);
|
|
|
+ num_immed_reply++;
|
|
|
+ complete(&wil->wmi_call);
|
|
|
+ } else {
|
|
|
+ /* add to the pending list */
|
|
|
+ spin_lock_irqsave(&wil->wmi_ev_lock, flags);
|
|
|
+ list_add_tail(&evt->list, &wil->pending_wmi_ev);
|
|
|
+ spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
|
|
|
+ q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
|
|
|
+ wil_dbg_wmi(wil, "queue_work -> %d\n", q);
|
|
|
+ }
|
|
|
}
|
|
|
/* normally, 1 event per IRQ should be processed */
|
|
|
- wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
|
|
|
+ wil_dbg_wmi(wil, "%s -> %d events queued, %d completed\n", __func__,
|
|
|
+ n - num_immed_reply, num_immed_reply);
|
|
|
}
|
|
|
|
|
|
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
|
|
@@ -1184,7 +1254,8 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason)
|
|
|
+int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason,
|
|
|
+ bool full_disconnect)
|
|
|
{
|
|
|
int rc;
|
|
|
u16 reason_code;
|
|
@@ -1208,19 +1279,20 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason)
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
- /* call event handler manually after processing wmi_call,
|
|
|
- * to avoid deadlock - disconnect event handler acquires wil->mutex
|
|
|
- * while it is already held here
|
|
|
- */
|
|
|
- reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
|
|
|
-
|
|
|
- wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
|
|
|
- reply.evt.bssid, reason_code,
|
|
|
- reply.evt.disconnect_reason);
|
|
|
+ if (full_disconnect) {
|
|
|
+ /* call event handler manually after processing wmi_call,
|
|
|
+ * to avoid deadlock - disconnect event handler acquires
|
|
|
+ * wil->mutex while it is already held here
|
|
|
+ */
|
|
|
+ reason_code = le16_to_cpu(reply.evt.protocol_reason_status);
|
|
|
|
|
|
- wil->sinfo_gen++;
|
|
|
- wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
|
|
|
+ wil_dbg_wmi(wil, "Disconnect %pM reason [proto %d wmi %d]\n",
|
|
|
+ reply.evt.bssid, reason_code,
|
|
|
+ reply.evt.disconnect_reason);
|
|
|
|
|
|
+ wil->sinfo_gen++;
|
|
|
+ wil6210_disconnect(wil, reply.evt.bssid, reason_code, true);
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1348,14 +1420,11 @@ static void wmi_event_handle(struct wil6210_priv *wil,
|
|
|
id, wil->reply_id);
|
|
|
/* check if someone waits for this event */
|
|
|
if (wil->reply_id && wil->reply_id == id) {
|
|
|
- if (wil->reply_buf) {
|
|
|
- memcpy(wil->reply_buf, wmi,
|
|
|
- min(len, wil->reply_size));
|
|
|
- } else {
|
|
|
- wmi_evt_call_handler(wil, id, evt_data,
|
|
|
- len - sizeof(*wmi));
|
|
|
- }
|
|
|
- wil_dbg_wmi(wil, "Complete WMI 0x%04x\n", id);
|
|
|
+ WARN_ON(wil->reply_buf);
|
|
|
+ wmi_evt_call_handler(wil, id, evt_data,
|
|
|
+ len - sizeof(*wmi));
|
|
|
+ wil_dbg_wmi(wil, "%s: Complete WMI 0x%04x\n",
|
|
|
+ __func__, id);
|
|
|
complete(&wil->wmi_call);
|
|
|
return;
|
|
|
}
|