summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/wma/inc/wma.h14
-rw-r--r--core/wma/src/wma_features.c308
2 files changed, 314 insertions, 8 deletions
diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h
index 3e1d6115d7ed..74f029fa93a7 100644
--- a/core/wma/inc/wma.h
+++ b/core/wma/inc/wma.h
@@ -166,6 +166,20 @@
#define WMA_MCAST_IPV4_MAC_ADDR (0x01)
#define WMA_MCAST_IPV6_MAC_ADDR (0x33)
+#define WMA_IS_EAPOL_GET_MIN_LEN 14
+#define WMA_EAPOL_SUBTYPE_GET_MIN_LEN 21
+#define WMA_EAPOL_INFO_GET_MIN_LEN 23
+#define WMA_IS_DHCP_GET_MIN_LEN 38
+#define WMA_DHCP_SUBTYPE_GET_MIN_LEN 0x11D
+#define WMA_DHCP_INFO_GET_MIN_LEN 50
+#define WMA_IS_ARP_GET_MIN_LEN 14
+#define WMA_ARP_SUBTYPE_GET_MIN_LEN 22
+#define WMA_IPV4_PROTO_GET_MIN_LEN 24
+#define WMA_IPV4_PKT_INFO_GET_MIN_LEN 42
+#define WMA_ICMP_SUBTYPE_GET_MIN_LEN 35
+#define WMA_IPV6_PROTO_GET_MIN_LEN 21
+#define WMA_IPV6_PKT_INFO_GET_MIN_LEN 62
+#define WMA_ICMPV6_SUBTYPE_GET_MIN_LEN 55
/* Roaming default values
* All time and period values are in milliseconds.
diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c
index 0803b8c4ce03..2b87fd7fee89 100644
--- a/core/wma/src/wma_features.c
+++ b/core/wma/src/wma_features.c
@@ -2617,6 +2617,287 @@ static bool tlv_check_required(int32_t reason)
}
/**
+ * wma_pkt_proto_subtype_to_string() - to convert proto subtype
+ * of data packet to string.
+ * @proto_subtype: proto subtype for data packet
+ *
+ * This function returns the string for the proto subtype of
+ * data packet.
+ *
+ * Return: string for proto subtype for data packet
+ */
+const char *
+wma_pkt_proto_subtype_to_string(enum qdf_proto_subtype proto_subtype)
+{
+ switch (proto_subtype) {
+ case QDF_PROTO_EAPOL_M1:
+ return "EAPOL M1";
+ case QDF_PROTO_EAPOL_M2:
+ return "EAPOL M2";
+ case QDF_PROTO_EAPOL_M3:
+ return "EAPOL M3";
+ case QDF_PROTO_EAPOL_M4:
+ return "EAPOL M4";
+ case QDF_PROTO_DHCP_DISCOVER:
+ return "DHCP DISCOVER";
+ case QDF_PROTO_DHCP_REQUEST:
+ return "DHCP REQUEST";
+ case QDF_PROTO_DHCP_OFFER:
+ return "DHCP OFFER";
+ case QDF_PROTO_DHCP_ACK:
+ return "DHCP ACK";
+ case QDF_PROTO_DHCP_NACK:
+ return "DHCP NACK";
+ case QDF_PROTO_DHCP_RELEASE:
+ return "DHCP RELEASE";
+ case QDF_PROTO_DHCP_INFORM:
+ return "DHCP INFORM";
+ case QDF_PROTO_DHCP_DECLINE:
+ return "DHCP DECLINE";
+ case QDF_PROTO_ARP_REQ:
+ return "ARP REQUEST";
+ case QDF_PROTO_ARP_RES:
+ return "ARP RESPONSE";
+ case QDF_PROTO_ICMP_REQ:
+ return "ICMP REQUEST";
+ case QDF_PROTO_ICMP_RES:
+ return "ICMP RESPONSE";
+ case QDF_PROTO_ICMPV6_REQ:
+ return "ICMPV6 REQUEST";
+ case QDF_PROTO_ICMPV6_RES:
+ return "ICMPV6 RESPONSE";
+ case QDF_PROTO_IPV4_UDP:
+ return "IPV4 UDP Packet";
+ case QDF_PROTO_IPV4_TCP:
+ return "IPV4 TCP Packet";
+ case QDF_PROTO_IPV6_UDP:
+ return "IPV6 UDP Packet";
+ case QDF_PROTO_IPV6_TCP:
+ return "IPV6 TCP Packet";
+ default:
+ return "Invalid Packet";
+ }
+}
+
+/**
+ * wma_wow_get_pkt_proto_subtype() - get the proto subtype
+ * of the packet.
+ * @data: Pointer to data buffer
+ * @len: length of the data buffer
+ *
+ * This function gives the proto subtype of the packet.
+ *
+ * Return: proto subtype of the packet.
+ */
+static enum qdf_proto_subtype
+wma_wow_get_pkt_proto_subtype(uint8_t *data,
+ uint32_t len)
+{
+ if (len >= WMA_IS_DHCP_GET_MIN_LEN) {
+ if (qdf_nbuf_data_is_ipv4_dhcp_pkt(data)) {
+ if (len >= WMA_DHCP_SUBTYPE_GET_MIN_LEN)
+ return qdf_nbuf_data_get_dhcp_subtype(data);
+ QDF_TRACE(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_ERROR, "DHCP Packet");
+ return QDF_PROTO_INVALID;
+ }
+ }
+ if (len >= WMA_IS_EAPOL_GET_MIN_LEN) {
+ if (qdf_nbuf_data_is_ipv4_eapol_pkt(data)) {
+ if (len >= WMA_EAPOL_SUBTYPE_GET_MIN_LEN)
+ return qdf_nbuf_data_get_eapol_subtype(data);
+ QDF_TRACE(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_ERROR, "EAPOL Packet");
+ return QDF_PROTO_INVALID;
+ }
+ }
+ if (len >= WMA_IS_ARP_GET_MIN_LEN) {
+ if (qdf_nbuf_data_is_ipv4_arp_pkt(data)) {
+ if (len >= WMA_ARP_SUBTYPE_GET_MIN_LEN)
+ return qdf_nbuf_data_get_arp_subtype(data);
+ QDF_TRACE(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_ERROR, "ARP Packet");
+ return QDF_PROTO_INVALID;
+ }
+ }
+ if (len >= WMA_IPV4_PROTO_GET_MIN_LEN) {
+ if (qdf_nbuf_data_is_icmp_pkt(data)) {
+ if (len >= WMA_ICMP_SUBTYPE_GET_MIN_LEN)
+ return qdf_nbuf_data_get_icmp_subtype(data);
+ QDF_TRACE(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_ERROR, "ICMP Packet");
+ return QDF_PROTO_INVALID;
+ } else if (qdf_nbuf_data_is_ipv4_udp_pkt(data)) {
+ return QDF_PROTO_IPV4_UDP;
+ } else if (qdf_nbuf_data_is_ipv4_tcp_pkt(data)) {
+ return QDF_PROTO_IPV4_TCP;
+ }
+ }
+ if (len >= WMA_IPV6_PROTO_GET_MIN_LEN) {
+ if (qdf_nbuf_data_is_icmpv6_pkt(data)) {
+ if (len >= WMA_ICMPV6_SUBTYPE_GET_MIN_LEN)
+ return qdf_nbuf_data_get_icmpv6_subtype(data);
+ QDF_TRACE(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_ERROR, "ICMPV6 Packet");
+ return QDF_PROTO_INVALID;
+ } else if (qdf_nbuf_data_is_ipv6_udp_pkt(data)) {
+ return QDF_PROTO_IPV6_UDP;
+ } else if (qdf_nbuf_data_is_ipv6_tcp_pkt(data)) {
+ return QDF_PROTO_IPV6_TCP;
+ }
+ }
+
+ return QDF_PROTO_INVALID;
+}
+
+/**
+ * wma_wow_parse_data_pkt_buffer() - API to parse data buffer for data
+ * packet that resulted in WOW wakeup.
+ * @data: Pointer to data buffer
+ * @buf_len: data buffer length
+ *
+ * This function parses the data buffer received (first few bytes of
+ * skb->data) to get informaton like src mac addr, dst mac addr, packet
+ * len, seq_num, etc.
+ *
+ * Return: void
+ */
+static void wma_wow_parse_data_pkt_buffer(uint8_t *data,
+ uint32_t buf_len)
+{
+ enum qdf_proto_subtype proto_subtype;
+ uint16_t pkt_len, key_len, seq_num;
+ uint32_t transaction_id, tcp_seq_num;
+
+ WMA_LOGD("wow_buf_pkt_len: %d", buf_len);
+ if (buf_len >= QDF_NBUF_TRAC_ETH_TYPE_OFFSET)
+ WMA_LOGE("Src_mac: " MAC_ADDRESS_STR " Dst_mac: " MAC_ADDRESS_STR,
+ MAC_ADDR_ARRAY(data),
+ MAC_ADDR_ARRAY(data + QDF_NBUF_SRC_MAC_OFFSET));
+ else
+ goto end;
+
+ proto_subtype = wma_wow_get_pkt_proto_subtype(data, buf_len);
+ switch (proto_subtype) {
+ case QDF_PROTO_EAPOL_M1:
+ case QDF_PROTO_EAPOL_M2:
+ case QDF_PROTO_EAPOL_M3:
+ case QDF_PROTO_EAPOL_M4:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_EAPOL_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ EAPOL_PKT_LEN_OFFSET));
+ key_len = (uint16_t)(*(uint16_t *)(data +
+ EAPOL_KEY_LEN_OFFSET));
+ WMA_LOGE("Pkt_len: %d, Key_len: %d",
+ ani_cpu_to_be16(pkt_len),
+ ani_cpu_to_be16(key_len));
+ }
+ break;
+
+ case QDF_PROTO_DHCP_DISCOVER:
+ case QDF_PROTO_DHCP_REQUEST:
+ case QDF_PROTO_DHCP_OFFER:
+ case QDF_PROTO_DHCP_ACK:
+ case QDF_PROTO_DHCP_NACK:
+ case QDF_PROTO_DHCP_RELEASE:
+ case QDF_PROTO_DHCP_INFORM:
+ case QDF_PROTO_DHCP_DECLINE:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_DHCP_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ DHCP_PKT_LEN_OFFSET));
+ transaction_id = (uint32_t)(*(uint32_t *)(data +
+ DHCP_TRANSACTION_ID_OFFSET));
+ WMA_LOGE("Pkt_len: %d, Transaction_id: %d",
+ ani_cpu_to_be16(pkt_len),
+ ani_cpu_to_be16(transaction_id));
+ }
+ break;
+
+ case QDF_PROTO_ARP_REQ:
+ case QDF_PROTO_ARP_RES:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ break;
+
+ case QDF_PROTO_ICMP_REQ:
+ case QDF_PROTO_ICMP_RES:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_IPV4_PKT_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ IPV4_PKT_LEN_OFFSET));
+ seq_num = (uint16_t)(*(uint16_t *)(data +
+ ICMP_SEQ_NUM_OFFSET));
+ WMA_LOGE("Pkt_len: %d, Seq_num: %d",
+ ani_cpu_to_be16(pkt_len),
+ ani_cpu_to_be16(seq_num));
+ }
+ break;
+
+ case QDF_PROTO_ICMPV6_REQ:
+ case QDF_PROTO_ICMPV6_RES:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_IPV6_PKT_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ IPV6_PKT_LEN_OFFSET));
+ seq_num = (uint16_t)(*(uint16_t *)(data +
+ ICMPV6_SEQ_NUM_OFFSET));
+ WMA_LOGE("Pkt_len: %d, Seq_num: %d",
+ ani_cpu_to_be16(pkt_len),
+ ani_cpu_to_be16(seq_num));
+ }
+ break;
+
+ case QDF_PROTO_IPV4_UDP:
+ case QDF_PROTO_IPV4_TCP:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_IPV4_PKT_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ IPV4_PKT_LEN_OFFSET));
+ WMA_LOGE("Pkt_len: %d",
+ ani_cpu_to_be16(pkt_len));
+ if (proto_subtype == QDF_PROTO_IPV4_TCP) {
+ tcp_seq_num = (uint32_t)(*(uint32_t *)(data +
+ IPV4_TCP_SEQ_NUM_OFFSET));
+ WMA_LOGE("TCP_seq_num: %d",
+ ani_cpu_to_be16(tcp_seq_num));
+ }
+ }
+ break;
+
+ case QDF_PROTO_IPV6_UDP:
+ case QDF_PROTO_IPV6_TCP:
+ WMA_LOGE("WOW Wakeup: %s rcvd",
+ wma_pkt_proto_subtype_to_string(proto_subtype));
+ if (buf_len >= WMA_IPV6_PKT_INFO_GET_MIN_LEN) {
+ pkt_len = (uint16_t)(*(uint16_t *)(data +
+ IPV6_PKT_LEN_OFFSET));
+ WMA_LOGE("Pkt_len: %d",
+ ani_cpu_to_be16(pkt_len));
+ if (proto_subtype == QDF_PROTO_IPV6_TCP) {
+ tcp_seq_num = (uint32_t)(*(uint32_t *)(data +
+ IPV6_TCP_SEQ_NUM_OFFSET));
+ WMA_LOGE("TCP_seq_num: %d",
+ ani_cpu_to_be16(tcp_seq_num));
+ }
+ }
+ break;
+
+ default:
+end:
+ WMA_LOGE("wow_buf_pkt_len: %d", buf_len);
+ WMA_LOGE("Invalid Packet Type or Smaller WOW packet buffer than expected");
+ break;
+ }
+}
+
+/**
* wma_wow_wakeup_host_event() - wakeup host event handler
* @handle: wma handle
* @event: event data
@@ -2764,14 +3045,25 @@ int wma_wow_wakeup_host_event(void *handle, uint8_t *event,
/* First 4-bytes of wow_packet_buffer is the length */
qdf_mem_copy((uint8_t *) &wow_buf_pkt_len,
param_buf->wow_packet_buffer, 4);
- wma_wow_wake_up_stats(wma,
- param_buf->wow_packet_buffer + 4,
- wow_buf_pkt_len,
- WOW_REASON_PATTERN_MATCH_FOUND);
- qdf_trace_hex_dump(QDF_MODULE_ID_WMA,
- QDF_TRACE_LEVEL_DEBUG,
- param_buf->wow_packet_buffer + 4,
- wow_buf_pkt_len);
+ if (wow_buf_pkt_len) {
+ uint8_t *data;
+
+ wma_wow_wake_up_stats(wma,
+ param_buf->wow_packet_buffer + 4,
+ wow_buf_pkt_len,
+ WOW_REASON_PATTERN_MATCH_FOUND);
+ qdf_trace_hex_dump(QDF_MODULE_ID_WMA,
+ QDF_TRACE_LEVEL_DEBUG,
+ param_buf->wow_packet_buffer + 4,
+ wow_buf_pkt_len);
+
+ data = (uint8_t *)
+ (param_buf->wow_packet_buffer + 4);
+ wma_wow_parse_data_pkt_buffer(data,
+ wow_buf_pkt_len);
+ } else {
+ WMA_LOGE("wow packet buffer is empty");
+ }
} else {
WMA_LOGE("No wow packet buffer present");
}