diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c
index f55bb68486893fab7269ce284e15dd60dd7aa906..fd69e4e24fdccf8ac0e96e2b36ecbcdf694615bc 100644
--- a/drivers/net/wireless/bcmdhd/bcmevent.c
+++ b/drivers/net/wireless/bcmdhd/bcmevent.c
@@ -25,9 +25,12 @@
#include <typedefs.h>
#include <bcmutils.h>
+#include <bcmendian.h>
#include <proto/ethernet.h>
#include <proto/bcmeth.h>
#include <proto/bcmevent.h>
+#include <proto/dnglevent.h>
+#include <proto/802.11.h>
/* Table of event name strings for UIs and debugging dumps */
@@ -187,3 +190,121 @@ const char *bcmevent_get_name(uint event_type)
*/
return ((event_name) ? event_name : "Unknown Event");
}
+
+/*
+ * Validate if the event is proper and if valid copy event header to event.
+ * If proper event pointer is passed, to just validate, pass NULL to event.
+ *
+ * Return values are
+ * BCME_OK - It is a BRCM event or BRCM dongle event
+ * BCME_NOTFOUND - Not BRCM, not an event, may be okay
+ * BCME_BADLEN - Bad length, should not process, just drop
+ */
+int
+is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype,
+ bcm_event_msg_u_t *out_event)
+{
+ uint16 len;
+ uint16 subtype;
+ uint16 usr_subtype;
+ bcm_event_t *bcm_event;
+ uint8 *pktend;
+ int err = BCME_OK;
+
+ pktend = (uint8 *)pktdata + pktlen;
+ bcm_event = (bcm_event_t *)pktdata;
+
+ /* only care about 16-bit subtype / length versions */
+ if ((uint8 *)&bcm_event->bcm_hdr < pktend) {
+ uint8 short_subtype = *(uint8 *)&bcm_event->bcm_hdr;
+ if (!(short_subtype & 0x80)) {
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+ }
+
+ /* must have both ether_header and bcmeth_hdr */
+ if (pktlen < OFFSETOF(bcm_event_t, event)) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ /* check length in bcmeth_hdr */
+ len = ntoh16_ua((void *)&bcm_event->bcm_hdr.length);
+ if (((uint8 *)&bcm_event->bcm_hdr.version + len) > pktend) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ /* match on subtype, oui and usr subtype for BRCM events */
+ subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.subtype);
+ if (subtype != BCMILCP_SUBTYPE_VENDOR_LONG) {
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+
+ if (bcmp(BRCM_OUI, &bcm_event->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+
+ /* if it is a bcm_event or bcm_dngl_event_t, validate it */
+ usr_subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.usr_subtype);
+ switch (usr_subtype) {
+ case BCMILCP_BCM_SUBTYPE_EVENT:
+ if (pktlen < sizeof(bcm_event_t)) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ len = sizeof(bcm_event_t) + ntoh32_ua((void *)&bcm_event->event.datalen);
+ if ((uint8 *)pktdata + len > pktend) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ if (exp_usr_subtype && (exp_usr_subtype != usr_subtype)) {
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+
+ if (out_event) {
+ /* ensure BRCM event pkt aligned */
+ memcpy(&out_event->event, &bcm_event->event, sizeof(wl_event_msg_t));
+ }
+
+ break;
+ case BCMILCP_BCM_SUBTYPE_DNGLEVENT:
+ if (pktlen < sizeof(bcm_dngl_event_t)) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ len = sizeof(bcm_dngl_event_t) +
+ ntoh16_ua((void *)&((bcm_dngl_event_t *)pktdata)->dngl_event.datalen);
+ if ((uint8 *)pktdata + len > pktend) {
+ err = BCME_BADLEN;
+ goto done;
+ }
+
+ if (exp_usr_subtype && (exp_usr_subtype != usr_subtype)) {
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+
+ if (out_event) {
+ /* ensure BRCM dngl event pkt aligned */
+ memcpy(&out_event->dngl_event, &((bcm_dngl_event_t *)pktdata)->dngl_event,
+ sizeof(bcm_dngl_event_msg_t));
+ }
+
+ break;
+ default:
+ err = BCME_NOTFOUND;
+ goto done;
+ }
+
+done:
+ return err;
+}
+
diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h
index 02a9f43065c7e06e6035b9aa6715c3ddbb07004d..a7b566b627cb0dc43dc6770fbb1c3987e3ba8337 100644
--- a/drivers/net/wireless/bcmdhd/dhd.h
+++ b/drivers/net/wireless/bcmdhd/dhd.h
@@ -728,6 +728,7 @@ extern int net_os_send_hang_message(struct net_device *dev);
extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata,
size_t pktlen, wl_event_msg_t *, void **data_ptr, void *);
extern void wl_event_to_host_order(wl_event_msg_t * evt);
+extern int wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu);
extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len);
extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set,
diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c
index 01cc8be004f7d034da1bc9fa3fda800c5de3a11b..abe0982ccdd0857057aa9aff393bbb7cb1c4efdf 100644
--- a/drivers/net/wireless/bcmdhd/dhd_common.c
+++ b/drivers/net/wireless/bcmdhd/dhd_common.c
@@ -1591,6 +1591,20 @@ wl_show_host_event(dhd_pub_t *dhd_pub, wl_event_msg_t *event, void *event_data,
}
#endif /* SHOW_EVENTS */
+/* Check whether packet is a BRCM event pkt. If it is, record event data. */
+int wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu)
+{
+ int ret;
+
+ ret = is_wlc_event_frame(pktdata, pktlen, 0, evu);
+ if (ret != BCME_OK) {
+ DHD_ERROR(("%s: Invalid event frame, err = %d\n",
+ __FUNCTION__, ret));
+ }
+
+ return ret;
+}
+
int
wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen,
wl_event_msg_t *event, void **data_ptr, void *raw_event)
@@ -1602,22 +1616,30 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen,
uint16 flags;
int evlen;
int hostidx;
+ int ret;
+ uint16 usr_subtype;
+ bcm_event_msg_u_t evu;
- if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
- DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
- return (BCME_ERROR);
+ ret = wl_host_event_get_data(pktdata, pktlen, &evu);
+ if (ret != BCME_OK) {
+ return ret;
}
- /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
- if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
- DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
- return (BCME_ERROR);
- }
+ usr_subtype = ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype);
+ switch (usr_subtype) {
+ case BCMILCP_BCM_SUBTYPE_EVENT:
+ memcpy(event, &evu.event, sizeof(wl_event_msg_t));
+ *data_ptr = &pvt_data[1];
+ break;
+
+ case BCMILCP_BCM_SUBTYPE_DNGLEVENT:
- if (pktlen < sizeof(bcm_event_t))
- return (BCME_ERROR);
+ return BCME_NOTFOUND;
+ default:
+ return BCME_NOTFOUND;
+ }
- *data_ptr = &pvt_data[1];
+ /* start wl_event_msg process */
event_data = *data_ptr;
@@ -1628,13 +1650,7 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen,
flags = ntoh16_ua((void *)&event->flags);
status = ntoh32_ua((void *)&event->status);
datalen = ntoh32_ua((void *)&event->datalen);
- if (datalen > pktlen)
- return (BCME_ERROR);
-
evlen = datalen + sizeof(bcm_event_t);
- if (evlen > pktlen) {
- return (BCME_ERROR);
- }
/* find equivalent host index for event ifidx */
hostidx = dhd_ifidx2hostidx(dhd_pub->info, event->ifidx);
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c
index 74e3ed9941a67a7222f8e2fc3db54f4ad5e9bbc0..1d511136b82dca1109d079a5f3b148404aad06e3 100644
--- a/drivers/net/wireless/bcmdhd/dhd_linux.c
+++ b/drivers/net/wireless/bcmdhd/dhd_linux.c
@@ -2765,16 +2765,22 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan)
memset(&event, 0, sizeof(event));
if ((ntoh16(skb->protocol) == ETHER_TYPE_BRCM) &&
(len >= sizeof(bcm_event_t))) {
- dhd_wl_host_event(dhd, &ifidx,
+ int ret_event;
+ ret_event = dhd_wl_host_event(dhd, &ifidx,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
skb_mac_header(skb),
#else
skb->mac.raw,
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */
- len - 2,
+ len,
&event,
&data);
+ if (ret_event != BCME_OK) {
+ PKTFREE(dhdp->osh, pktbuf, FALSE);
+ continue;
+ }
+
wl_event_to_host_order(&event);
if (!tout_ctrl)
tout_ctrl = DHD_PACKET_TIMEOUT_MS;
@@ -6967,11 +6973,11 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
* Wireless ext is on primary interface only
*/
- ASSERT(dhd->iflist[*ifidx] != NULL);
- ASSERT(dhd->iflist[*ifidx]->net != NULL);
+ ASSERT(dhd->iflist[*ifidx] != NULL);
+ ASSERT(dhd->iflist[*ifidx]->net != NULL);
if (dhd->iflist[*ifidx]->net) {
- wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
+ wl_iw_event(dhd->iflist[*ifidx]->net, event, *data);
}
}
#endif /* defined(WL_WIRELESS_EXT) */
@@ -6979,6 +6985,7 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
#ifdef WL_CFG80211
ASSERT(dhd->iflist[*ifidx] != NULL);
ASSERT(dhd->iflist[*ifidx]->net != NULL);
+
if (dhd->iflist[*ifidx]->net)
wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data);
#endif /* defined(WL_CFG80211) */
diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h
index ac3397808035cb5f7b4f19654f110690a99e5b97..127ee26f4be73478a018b33e5470be3fb0941c25 100644
--- a/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h
+++ b/drivers/net/wireless/bcmdhd/include/proto/bcmeth.h
@@ -90,6 +90,8 @@
*/
/* #define BCMILCP_BCM_SUBTYPE_EAPOL 3 */
#define BCMILCP_BCM_SUBTYPE_DPT 4
+#define BCMILCP_BCM_SUBTYPE_DNGLEVENT 5
+
#define BCMILCP_BCM_SUBTYPEHDR_MINLENGTH 8
#define BCMILCP_BCM_SUBTYPEHDR_VERSION 0
diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h
index 51006b542e8226160242757e634f310ec5e1aaea..802210018ae340e6d58a035170a235be259695dc 100644
--- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h
+++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h
@@ -40,6 +40,7 @@
#endif
/* #include <ethernet.h> -- TODO: req., excluded to overwhelming coupling (break up ethernet.h) */
#include <proto/bcmeth.h>
+#include <proto/dnglevent.h>
/* This marks the start of a packed structure section. */
#include <packed_section_start.h>
@@ -94,6 +95,18 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
/* data portion follows */
} BWL_POST_PACKED_STRUCT bcm_event_t;
+/*
+ * used by host event
+ * Note: If additional event types are added, it should come on
+is_wlc_event_frame() as well.
+ */
+typedef union bcm_event_msg_u {
+ wl_event_msg_t event;
+ bcm_dngl_event_msg_t dngl_event;
+
+ /* add new event here */
+} bcm_event_msg_u_t;
+
#define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header))
/* Event messages */
@@ -232,6 +245,8 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event {
/* define an API for getting the string name of an event */
extern const char *bcmevent_get_name(uint event_type);
+/* validate if the event is proper and if valid copy event header to event */
+extern int is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype, bcm_event_msg_u_t *out_event);
/* Event status codes */
diff --git a/drivers/net/wireless/bcmdhd/include/proto/dnglevent.h b/drivers/net/wireless/bcmdhd/include/proto/dnglevent.h
new file mode 100644
index 0000000000000000000000000000000000000000..584e9d2901396eb9e29a984376ac15e611397f35
--- /dev/null
+++ b/drivers/net/wireless/bcmdhd/include/proto/dnglevent.h
@@ -0,0 +1,80 @@
+/*
+ * Broadcom Event protocol definitions
+ *
+ * $Copyright Open Broadcom Corporation$
+ *
+ * Dependencies: proto/bcmeth.h
+ *
+ * $Id: dnglevent.h $
+ *
+ */
+
+/*
+ * Broadcom dngl Ethernet Events protocol defines
+ *
+ */
+
+#ifndef _DNGLEVENT_H_
+#define _DNGLEVENT_H_
+
+#ifndef _TYPEDEFS_H_
+#include <typedefs.h>
+#endif
+#include <proto/bcmeth.h>
+
+/* This marks the start of a packed structure section. */
+#include <packed_section_start.h>
+#define BCM_DNGL_EVENT_MSG_VERSION 1
+#define DNGL_E_SOCRAM_IND 0x2
+typedef BWL_PRE_PACKED_STRUCT struct
+{
+ uint16 version; /* Current version is 1 */
+ uint16 reserved; /* reserved for any future extension */
+ uint16 event_type; /* DNGL_E_SOCRAM_IND */
+ uint16 datalen; /* Length of the event payload */
+} BWL_POST_PACKED_STRUCT bcm_dngl_event_msg_t;
+
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_event {
+ struct ether_header eth;
+ bcmeth_hdr_t bcm_hdr;
+ bcm_dngl_event_msg_t dngl_event;
+ /* data portion follows */
+} BWL_POST_PACKED_STRUCT bcm_dngl_event_t;
+
+
+/* SOCRAM_IND type tags */
+#define SOCRAM_IND_ASSRT_TAG 0x1
+#define SOCRAM_IND_TAG_HEALTH_CHECK 0x2
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_socramind {
+ uint16 tag; /* data tag */
+ uint16 length; /* data length */
+ uint8 value[1]; /* data value with variable length specified by length */
+} BWL_POST_PACKED_STRUCT bcm_dngl_socramind_t;
+
+/* Health check top level module tags */
+#define HEALTH_CHECK_TOP_LEVEL_MODULE_PCIEDEV_RTE 1
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_healthcheck {
+ uint16 top_module_tag; /* top level module tag */
+ uint16 top_module_len; /* Type of PCIE issue indication */
+ uint8 value[1]; /* data value with variable length specified by length */
+} BWL_POST_PACKED_STRUCT bcm_dngl_healthcheck_t;
+
+#define HEALTH_CHECK_PCIEDEV_VERSION 1
+#define HEALTH_CHECK_PCIEDEV_FLAG_IN_D3_SHIFT 0
+#define HEALTH_CHECK_PCIEDEV_FLAG_IN_D3_FLAG 1 << HEALTH_CHECK_PCIEDEV_FLAG_IN_D3_SHIFT
+/* PCIE Module TAGs */
+#define HEALTH_CHECK_PCIEDEV_INDUCED_IND 0x1
+#define HEALTH_CHECK_PCIEDEV_H2D_DMA_IND 0x2
+#define HEALTH_CHECK_PCIEDEV_D2H_DMA_IND 0x3
+typedef BWL_PRE_PACKED_STRUCT struct bcm_dngl_pcie_hc {
+ uint16 version; /* HEALTH_CHECK_PCIEDEV_VERSION */
+ uint16 reserved;
+ uint16 pcie_err_ind_type; /* PCIE Module TAGs */
+ uint16 pcie_flag;
+ uint32 pcie_control_reg;
+} BWL_POST_PACKED_STRUCT bcm_dngl_pcie_hc_t;
+
+/* This marks the end of a packed structure section. */
+#include <packed_section_end.h>
+
+#endif /* _DNGLEVENT_H_ */