M7350/external/compat-wireless/drivers/net/wireless/ath/ath6kl/pm.c

239 lines
5.8 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/*
* Copyright (c) 2004-2011 Atheros Communications 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
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "core.h"
static bool ath6kl_parse_event_pkt_for_wake_lock(struct sk_buff *skb)
{
u16 cmd_id;
bool need_wake = false;
if (skb->len < sizeof(u16))
return need_wake;
cmd_id = *(const u16 *) skb->data;
cmd_id = le16_to_cpu(cmd_id);
switch (cmd_id) {
case WMI_CONNECT_EVENTID:
need_wake = true;
break;
default:
/* Don't wake lock the system for other events */
break;
}
return need_wake;
}
static bool ath6kl_parse_ip_pkt_for_wake_lock(struct sk_buff *skb)
{
const u8 ipsec_keepalve[] = { 0x11, 0x94, 0x11, 0x94, 0x00,
0x09, 0x00, 0x00, 0xff };
bool need_wake = true;
u16 size;
u8 *udp;
u8 ihl;
if (skb->len >= 24 && (*((u8 *)skb->data + 23) == 0x11)) {
udp = (u8 *)skb->data + 14;
ihl = (*udp & 0x0f) * sizeof(u32);
udp += ihl;
size = 14 + ihl + sizeof(ipsec_keepalve);
if ((skb->len >= size) &&
!memcmp(udp, ipsec_keepalve, sizeof(ipsec_keepalve) - 3) &&
udp[8] == 0xff) {
/*
* RFC 3948 UDP Encapsulation of IPsec ESP Packets
* src and dest port must be 4500
* Receivers MUST NOT depend upon the UDP checksum
* being zero
* Sender must use 1 byte payload with 0xff
* Receiver SHOULD ignore a received NAT-keepalive pkt
*
* IPSec over UDP NAT keepalive packet. Just ignore
*/
need_wake = false;
}
}
return need_wake;
}
static bool ath6kl_parse_data_pkt_for_wake_lock(struct ath6kl *ar,
struct sk_buff *skb)
{
struct net_device *ndev;
struct ath6kl_vif *vif;
struct ethhdr *hdr;
bool need_wake = false;
u16 dst_port;
vif = ath6kl_vif_first(ar);
if (!vif)
return need_wake;
if (skb->len < sizeof(struct ethhdr))
return need_wake;
hdr = (struct ethhdr *) skb->data;
if (!is_multicast_ether_addr(hdr->h_dest)) {
switch (ntohs(hdr->h_proto)) {
case 0x0800: /* IP */
need_wake = ath6kl_parse_ip_pkt_for_wake_lock(skb);
break;
case 0x888e: /* EAPOL */
case 0x88c7: /* RSN_PREAUTH */
case 0x88b4: /* WAPI */
need_wake = true;
break;
default:
break;
}
} else if (!is_broadcast_ether_addr(hdr->h_dest)) {
if (skb->len >= 14 + 20) { /* mDNS packets */
u8 *dst_ipaddr = (u8 *)(skb->data + 14 + 20 - 4);
ndev = vif->ndev;
if (((dst_ipaddr[3] & 0xf8) == 0xf8) &&
(vif->nw_type == AP_NETWORK ||
(ndev->flags & IFF_ALLMULTI ||
ndev->flags & IFF_MULTICAST)))
need_wake = true;
}
} else if (vif->nw_type == AP_NETWORK) {
switch (ntohs(hdr->h_proto)) {
case 0x0800: /* IP */
if (skb->len >= 14 + 20 + 2) {
dst_port = *(u16 *)(skb->data + 14 + 20);
/* dhcp req */
need_wake = (ntohs(dst_port) == 0x43);
}
break;
case 0x0806:
need_wake = true;
default:
break;
}
}
return need_wake;
}
void ath6kl_config_suspend_wake_lock(struct ath6kl *ar, struct sk_buff *skb,
bool is_event_pkt)
{
struct ath6kl_vif *vif;
#ifdef CONFIG_HAS_WAKELOCK
unsigned long wl_timeout = 5;
#endif
bool need_wake = false;
vif = ath6kl_vif_first(ar);
if (!vif)
return;
if (
#ifdef CONFIG_HAS_EARLYSUSPEND
ar->screen_off &&
#endif
skb && test_bit(CONNECTED, &vif->flags)) {
if (is_event_pkt) { /* Ctrl pkt received */
need_wake =
ath6kl_parse_event_pkt_for_wake_lock(skb);
if (need_wake) {
#ifdef CONFIG_HAS_WAKELOCK
wl_timeout = 3 * HZ;
#endif
}
} else /* Data pkt received */
need_wake = ath6kl_parse_data_pkt_for_wake_lock(ar,
skb);
}
if (need_wake) {
#ifdef CONFIG_HAS_WAKELOCK
/*
* Keep the host wake up if there is any event
* and pkt comming in.
*/
wake_lock_timeout(&ar->wake_lock, wl_timeout);
#endif
}
}
#ifdef CONFIG_HAS_WAKELOCK
void ath6kl_p2p_acquire_wakelock(struct ath6kl *ar, int wl_timeout)
{
if (!wake_lock_active(&ar->p2p_wake_lock))
wake_lock_timeout(&ar->p2p_wake_lock, wl_timeout);
return;
}
void ath6kl_p2p_release_wakelock(struct ath6kl *ar)
{
if (wake_lock_active(&ar->p2p_wake_lock))
wake_unlock(&ar->p2p_wake_lock);
return;
}
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static void ath6kl_early_suspend(struct early_suspend *handler)
{
struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend);
if (ar)
ar->screen_off = true;
}
static void ath6kl_late_resume(struct early_suspend *handler)
{
struct ath6kl *ar = container_of(handler, struct ath6kl, early_suspend);
if (ar)
ar->screen_off = false;
}
#endif
void ath6kl_setup_android_resource(struct ath6kl *ar)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
ar->screen_off = false;
ar->early_suspend.suspend = ath6kl_early_suspend;
ar->early_suspend.resume = ath6kl_late_resume;
ar->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
register_early_suspend(&ar->early_suspend);
#endif
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&ar->wake_lock, WAKE_LOCK_SUSPEND, "ath6kl_suspend_wl");
wake_lock_init(&ar->p2p_wake_lock,
WAKE_LOCK_SUSPEND,
"ath6kl_p2p_suspend_wl");
#endif
}
void ath6kl_cleanup_android_resource(struct ath6kl *ar)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&ar->early_suspend);
#endif
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_destroy(&ar->wake_lock);
wake_lock_destroy(&ar->p2p_wake_lock);
#endif
}