summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2017-08-13 15:34:55 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2017-08-13 15:34:55 +0000
commit9a193c422ecd5f28cd1f89c503b528fd11de1890 (patch)
tree81d414cd8e31c55137618c4fd23c609005118670 /sys/dev
parent0af59077f28e5e3e52e3bb246392ef1b85666306 (diff)
Add proper support for iwm(4) firmware's time event. Cancel the event if
it is still scheduled before tearing down firmware state in iwm_unauth(). This change does not address any particular known issue, but matches what Linux does. If it causes any problems, let me know. ok tb@
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/pci/if_iwm.c101
-rw-r--r--sys/dev/pci/if_iwmvar.h5
2 files changed, 95 insertions, 11 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 20f4a2526e9..6daf6b0e98c 100644
--- a/sys/dev/pci/if_iwm.c
+++ b/sys/dev/pci/if_iwm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwm.c,v 1.209 2017/08/13 15:02:45 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.210 2017/08/13 15:34:54 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -312,6 +312,7 @@ int iwm_send_time_event_cmd(struct iwm_softc *,
const struct iwm_time_event_cmd_v2 *);
void iwm_protect_session(struct iwm_softc *, struct iwm_node *, uint32_t,
uint32_t);
+void iwm_unprotect_session(struct iwm_softc *, struct iwm_node *);
int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, uint16_t,
uint8_t *, uint16_t *);
int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
@@ -2200,14 +2201,47 @@ iwm_send_time_event_cmd(struct iwm_softc *sc,
const struct iwm_time_event_cmd_v2 *cmd)
{
struct iwm_time_event_cmd_v1 cmd_v1;
+ struct iwm_host_cmd hcmd = {
+ .id = IWM_TIME_EVENT_CMD,
+ .flags = IWM_CMD_WANT_SKB,
+ };
+ struct iwm_rx_packet *pkt;
+ struct iwm_time_event_resp *resp;
+ uint32_t resp_len;
+ int err;
+
+ if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_TIME_EVENT_API_V2) {
+ hcmd.data[0] = cmd;
+ hcmd.len[0] = sizeof(*cmd);
+ } else {
+ iwm_te_v2_to_v1(cmd, &cmd_v1);
+ hcmd.data[0] = &cmd_v1;
+ hcmd.len[0] = sizeof(cmd_v1);
+ }
+ err = iwm_send_cmd(sc, &hcmd);
+ if (err)
+ return err;
+
+ pkt = hcmd.resp_pkt;
+ if (!pkt || (pkt->hdr.flags & IWM_CMD_FAILED_MSK)) {
+ err = EIO;
+ goto out;
+ }
- if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_TIME_EVENT_API_V2)
- return iwm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD,
- 0, sizeof(*cmd), cmd);
+ resp_len = iwm_rx_packet_payload_len(pkt);
+ if (resp_len != sizeof(*resp)) {
+ err = EIO;
+ goto out;
+ }
- iwm_te_v2_to_v1(cmd, &cmd_v1);
- return iwm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, 0,
- sizeof(cmd_v1), &cmd_v1);
+ resp = (void *)pkt->data;
+ if (le32toh(resp->status) == 0)
+ sc->sc_time_event_uid = le32toh(resp->unique_id);
+ else
+ err = EIO;
+out:
+ iwm_free_resp(sc, &hcmd);
+ return err;
}
void
@@ -2216,6 +2250,10 @@ iwm_protect_session(struct iwm_softc *sc, struct iwm_node *in,
{
struct iwm_time_event_cmd_v2 time_cmd;
+ /* Do nothing if a time event is already scheduled. */
+ if (sc->sc_flags & IWM_FLAG_TE_ACTIVE)
+ return;
+
memset(&time_cmd, 0, sizeof(time_cmd));
time_cmd.action = htole32(IWM_FW_CTXT_ACTION_ADD);
@@ -2236,7 +2274,32 @@ iwm_protect_session(struct iwm_softc *sc, struct iwm_node *in,
IWM_TE_V2_NOTIF_HOST_EVENT_END |
IWM_T2_V2_START_IMMEDIATELY);
- iwm_send_time_event_cmd(sc, &time_cmd);
+ if (iwm_send_time_event_cmd(sc, &time_cmd) == 0)
+ sc->sc_flags |= IWM_FLAG_TE_ACTIVE;
+
+ DELAY(100);
+}
+
+void
+iwm_unprotect_session(struct iwm_softc *sc, struct iwm_node *in)
+{
+ struct iwm_time_event_cmd_v2 time_cmd;
+
+ /* Do nothing if the time event has already ended. */
+ if ((sc->sc_flags & IWM_FLAG_TE_ACTIVE) == 0)
+ return;
+
+ memset(&time_cmd, 0, sizeof(time_cmd));
+
+ time_cmd.action = htole32(IWM_FW_CTXT_ACTION_REMOVE);
+ time_cmd.id_and_color =
+ htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color));
+ time_cmd.id = htole32(sc->sc_time_event_uid);
+
+ if (iwm_send_time_event_cmd(sc, &time_cmd) == 0)
+ sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE;
+
+ DELAY(100);
}
/*
@@ -5424,7 +5487,6 @@ iwm_auth(struct iwm_softc *sc)
else
duration = IEEE80211_DUR_TU;
iwm_protect_session(sc, in, duration, in->in_ni.ni_intval / 2);
- DELAY(100);
return 0;
@@ -5450,6 +5512,8 @@ iwm_deauth(struct iwm_softc *sc)
splassert(IPL_NET);
+ iwm_unprotect_session(sc, in);
+
if (sc->sc_flags & IWM_FLAG_STA_ACTIVE) {
err = iwm_rm_sta_cmd(sc, in);
if (err) {
@@ -6452,6 +6516,7 @@ iwm_stop(struct ifnet *ifp, int disable)
sc->sc_flags &= ~IWM_FLAG_MAC_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_BINDING_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE;
+ sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE;
sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
@@ -6967,7 +7032,6 @@ iwm_notif_intr(struct iwm_softc *sc)
case IWM_POWER_TABLE_CMD:
case IWM_PHY_CONTEXT_CMD:
case IWM_BINDING_CONTEXT_CMD:
- case IWM_TIME_EVENT_CMD:
case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD):
case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC):
case IWM_SCAN_OFFLOAD_REQUEST_CMD:
@@ -6986,6 +7050,16 @@ iwm_notif_intr(struct iwm_softc *sc)
}
break;
+ case IWM_TIME_EVENT_CMD: {
+ struct iwm_time_event_resp *resp;
+ if (sc->sc_wantresp == ((qid << 16) | idx)) {
+ SYNC_RESP_STRUCT(resp, pkt);
+ memcpy(sc->sc_cmd_resp,
+ pkt, sizeof(*pkt) + sizeof(*resp));
+ }
+ break;
+ }
+
/* ignore */
case 0x6c: /* IWM_PHY_DB_CMD */
break;
@@ -7033,7 +7107,14 @@ iwm_notif_intr(struct iwm_softc *sc)
case IWM_TIME_EVENT_NOTIFICATION: {
struct iwm_time_event_notif *notif;
+ uint32_t action;
SYNC_RESP_STRUCT(notif, pkt);
+
+ if (sc->sc_time_event_uid != le32toh(notif->unique_id))
+ break;
+ action = le32toh(notif->action);
+ if (action & IWM_TE_V2_NOTIF_HOST_EVENT_END)
+ sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE;
break;
}
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index ca728a746bb..39afae06c6a 100644
--- a/sys/dev/pci/if_iwmvar.h
+++ b/sys/dev/pci/if_iwmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmvar.h,v 1.32 2017/08/12 19:23:42 stsp Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.33 2017/08/13 15:34:54 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -285,6 +285,7 @@ struct iwm_rx_ring {
#define IWM_FLAG_MAC_ACTIVE 0x08 /* MAC context added to firmware */
#define IWM_FLAG_BINDING_ACTIVE 0x10 /* MAC->PHY binding added to firmware */
#define IWM_FLAG_STA_ACTIVE 0x20 /* AP added to firmware station table */
+#define IWM_FLAG_TE_ACTIVE 0x40 /* time event is scheduled */
struct iwm_ucode_status {
uint32_t uc_error_event_table;
@@ -471,6 +472,8 @@ struct iwm_softc {
struct iwm_rx_phy_info sc_last_phy_info;
int sc_ampdu_ref;
+ uint32_t sc_time_event_uid;
+
/* phy contexts. we only use the first one */
struct iwm_phy_ctxt sc_phyctxt[IWM_NUM_PHY_CTX];