static ssize_t ath6kl_bmisstime_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; u16 bmiss_time; char buf[32]; ssize_t len; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; buf[len] = '\0'; if (kstrtou16(buf, 0, &bmiss_time)) return -EINVAL; vif->bmiss_time_t = bmiss_time; /* Enable error report event for bmiss */ ath6kl_wmi_set_err_report_bitmask(ar->wmi, vif->fw_vif_idx, ATH6KL_ERR_REPORT_BMISS_MASK); ath6kl_wmi_bmisstime_cmd(ar->wmi, vif->fw_vif_idx, vif->bmiss_time_t, 0); return count; }
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 #if 1 /* 20120927 Matt */ unsigned long wl_timeout = HZ; #else unsigned long wl_timeout = 5; #endif /* 20120927 Matt */ #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 = #if 1 // by bbelief ath6kl_parse_event_pkt_for_wake_lock(skb); #else ath6kl_parse_event_pkt_for_wake_lock(ar, skb); #endif 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 } }
static ssize_t ath6kl_bmisstime_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[32]; int len; vif = ath6kl_vif_first(ar); if (!vif) return -EIO; len = scnprintf(buf, sizeof(buf), "%u\n", vif->bmiss_time_t); return simple_read_from_buffer(user_buf, count, ppos, buf, len); }
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 0 // by bbelief if (test_and_clear_bit(WOW_RESUME_PRINT, &ar->flag)) { ath6kl_dbg(ATH6KL_DBG_SUSPEND, "(wow) dest mac:%pM, src mac:%pM, type/len :%04x\n", hdr->h_dest, hdr->h_source, be16_to_cpu(hdr->h_proto)); } #endif 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; }
int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len) { struct ath6kl *ar = wiphy_priv(wiphy); struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1]; int err, buf_len; void *buf; err = nla_parse(tb, ATH6KL_TM_ATTR_MAX, data, len, ath6kl_tm_policy); if (err) return err; if (!tb[ATH6KL_TM_ATTR_CMD]) return -EINVAL; switch (nla_get_u32(tb[ATH6KL_TM_ATTR_CMD])) { case ATH6KL_TM_CMD_TCMD: if (!tb[ATH6KL_TM_ATTR_DATA]) return -EINVAL; buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); ath6kl_wmi_test_cmd(ar->wmi, buf, buf_len); return 0; break; #ifdef ATH6KL_SUPPORT_WLAN_HB case ATH6KL_TM_CMD_WLAN_HB: { struct wlan_hb_params *hb_params; struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); if (!vif) return -EINVAL; if (!tb[ATH6KL_TM_ATTR_DATA]) { printk(KERN_ERR "%s: NO DATA\n", __func__); return -EINVAL; } buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); hb_params = (struct wlan_hb_params *)buf; if (hb_params->cmd == NL80211_WLAN_HB_ENABLE) { if (hb_params->enable != 0) { if (ath6kl_enable_wow_hb(ar)) { printk(KERN_ERR "%s: enable hb wow fail\n", __func__); return -EINVAL; } if (hb_params->enable & WLAN_HB_TCP_ENABLE) { ar->wlan_hb_enable |= WLAN_HB_TCP_ENABLE; if (ath6kl_wmi_set_heart_beat_params( ar->wmi, vif->fw_vif_idx, WLAN_HB_TCP_ENABLE)) { printk(KERN_ERR "%s: set heart beat enable fail\n", __func__); return -EINVAL; } } else if (hb_params->enable & WLAN_HB_UDP_ENABLE) { ar->wlan_hb_enable |= WLAN_HB_UDP_ENABLE; } } else { ar->wlan_hb_enable = 0; #ifdef CONFIG_ANDROID if (ath6kl_android_enable_wow_default(ar)) { printk(KERN_ERR "%s: enable android defualt wow fail\n", __func__); } #endif if (ath6kl_wmi_set_heart_beat_params(ar->wmi, vif->fw_vif_idx, 0)) { printk(KERN_ERR "%s: set heart beat enable fail\n", __func__); return -EINVAL; } } } else if (hb_params->cmd == NL80211_WLAN_TCP_PARAMS) { if (ath6kl_wmi_heart_beat_set_tcp_params(ar->wmi, vif->fw_vif_idx, hb_params->params.tcp_params.src_port, hb_params->params.tcp_params.dst_port, hb_params->params.tcp_params.timeout)) { printk(KERN_ERR "%s: set heart beat tcp params fail\n", __func__); return -EINVAL; } } else if (hb_params->cmd == NL80211_WLAN_TCP_FILTER) { if (hb_params->params.tcp_filter.length > WMI_MAX_TCP_FILTER_SIZE) { printk(KERN_ERR "%s: size of tcp filter is too large: %d\n", __func__, hb_params->params.tcp_filter.length); return -E2BIG; } if (ath6kl_wmi_heart_beat_set_tcp_filter(ar->wmi, vif->fw_vif_idx, hb_params->params.tcp_filter.filter, hb_params->params.tcp_filter.length)) { printk(KERN_ERR "%s: set heart beat tcp filter fail\n", __func__); return -EINVAL; } } else if (hb_params->cmd == NL80211_WLAN_UDP_PARAMS) { if (ath6kl_wmi_heart_beat_set_udp_params(ar->wmi, vif->fw_vif_idx, hb_params->params.udp_params.src_port, hb_params->params.udp_params.dst_port, hb_params->params.udp_params.interval, hb_params->params.udp_params.timeout)) { printk(KERN_ERR "%s: set heart beat udp params fail\n", __func__); return -EINVAL; } } else if (hb_params->cmd == NL80211_WLAN_UDP_FILTER) { if (hb_params->params.udp_filter.length > WMI_MAX_UDP_FILTER_SIZE) { printk(KERN_ERR "%s: size of udp filter is too large: %d\n", __func__, hb_params->params.udp_filter.length); return -E2BIG; } if (ath6kl_wmi_heart_beat_set_udp_filter(ar->wmi, vif->fw_vif_idx, hb_params->params.udp_filter.filter, hb_params->params.udp_filter.length)) { printk(KERN_ERR "%s: set heart beat udp filter fail\n", __func__); return -EINVAL; } } else if (hb_params->cmd == NL80211_WLAN_NET_INFO) { if (ath6kl_wmi_heart_beat_set_network_info(ar->wmi, vif->fw_vif_idx, hb_params->params.net_info.device_ip, hb_params->params.net_info.server_ip, hb_params->params.net_info.gateway_ip, hb_params->params.net_info.gateway_mac)) { printk(KERN_ERR "%s: set heart beat network information fail\n", __func__); return -EINVAL; } } } return 0; break; #endif #ifdef ATH6KL_SUPPORT_WIFI_DISC case ATH6KL_TM_CMD_WIFI_DISC: { struct wifi_disc_params *disc_params; struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); if (!vif) return -EINVAL; if (!tb[ATH6KL_TM_ATTR_DATA]) { printk(KERN_ERR "%s: NO DATA\n", __func__); return -EINVAL; } buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); disc_params = (struct wifi_disc_params *)buf; if (disc_params->cmd == NL80211_WIFI_DISC_IE) { u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00}; u8 *ie = NULL; u16 ie_length = disc_params->params.ie_params.length; ie = kmalloc(ie_length+6, GFP_KERNEL); if (ie == NULL) return -ENOMEM; memcpy(ie, ie_hdr, 6); ie[1] = ie_length+4; memcpy(ie+6, disc_params->params.ie_params.ie, ie_length); if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, ie, ie_length+6)) { kfree(ie); printk(KERN_ERR "%s: wifi discovery set probe request ie fail\n", __func__); return -EINVAL; } if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_RESP, ie, ie_length+6)) { kfree(ie); printk(KERN_ERR "%s: wifi discovery set probe response ie fail\n", __func__); return -EINVAL; } kfree(ie); } else if (disc_params->cmd == NL80211_WIFI_DISC_IE_FILTER) { if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, disc_params->params.ie_filter_params.enable, disc_params->params.ie_filter_params.startPos, disc_params->params.ie_filter_params.filter, disc_params->params.ie_filter_params.length)) { printk(KERN_ERR "%s: wifi discovery set ie filter fail\n", __func__); return -EINVAL; } } else if (disc_params->cmd == NL80211_WIFI_DISC_START) { int band, freq, numPeers, random; if (disc_params->params.start_params.channel <= 14) band = IEEE80211_BAND_2GHZ; else band = IEEE80211_BAND_5GHZ; freq = ieee80211_channel_to_frequency( disc_params->params.start_params.channel, band); if (!freq) { printk(KERN_ERR "%s: wifi discovery start channel %d error\n", __func__, disc_params->params.start_params.channel); return -EINVAL; } if (disc_params->params.start_params.numPeers == 0) numPeers = 1; else if (disc_params->params.start_params.numPeers > 4) numPeers = 4; else numPeers = disc_params->params.start_params.numPeers; random = (disc_params->params.start_params.random == 0) ? 100 : disc_params->params.start_params.random; if (disc_params->params.start_params.txPower) ath6kl_wmi_set_tx_pwr_cmd(ar->wmi, vif->fw_vif_idx, disc_params->params.start_params.txPower); /* disable scanning */ ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (ath6kl_wmi_disc_mode_cmd(ar->wmi, vif->fw_vif_idx, 1, freq, disc_params->params.start_params.dwellTime, disc_params->params.start_params.sleepTime, random, numPeers, disc_params->params.start_params.peerTimeout )) { printk(KERN_ERR "%s: wifi discovery start fail\n", __func__); return -EINVAL; } /* change disc state to active */ ar->disc_active = true; } else if (disc_params->cmd == NL80211_WIFI_DISC_STOP) { /* change disc state to inactive */ ar->disc_active = false; if (ath6kl_wmi_disc_mode_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, 0, 0, 0, 0, 0)) { printk(KERN_ERR "%s: wifi discovery stop fail\n", __func__); return -EINVAL; } } } return 0; break; #endif #ifdef ATH6KL_SUPPORT_WIFI_KTK case ATH6KL_TM_CMD_WIFI_KTK: { struct wifi_ktk_params *ktk_params; struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); if (!vif) return -EINVAL; if (!tb[ATH6KL_TM_ATTR_DATA]) { printk(KERN_ERR "%s: NO DATA\n", __func__); return -EINVAL; } buf = nla_data(tb[ATH6KL_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH6KL_TM_ATTR_DATA]); ktk_params = (struct wifi_ktk_params *)buf; if (ktk_params->cmd == NL80211_WIFI_KTK_IE) { u8 ie_hdr[6] = {0xDD, 0x00, 0x00, 0x03, 0x7f, 0x00}; u8 *ie = NULL; u16 ie_length = ktk_params->params.ie_params.length; ie = kmalloc(ie_length+6, GFP_KERNEL); if (ie == NULL) return -ENOMEM; memcpy(ie, ie_hdr, 6); ie[1] = ie_length+4; memcpy(ie+6, ktk_params->params.ie_params.ie, ie_length); if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_RESP, ie, ie_length+6)) { kfree(ie); printk(KERN_ERR "%s: wifi ktk set probe response ie fail\n", __func__); return -EINVAL; } if (ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_BEACON, ie, ie_length+6)) { kfree(ie); printk(KERN_ERR "%s: wifi ktk set beacon ie fail\n", __func__); return -EINVAL; } kfree(ie); } else if (ktk_params->cmd == NL80211_WIFI_KTK_IE_FILTER) { if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, ktk_params->params.ie_filter_params.enable, ktk_params->params.ie_filter_params.startPos, ktk_params->params.ie_filter_params.filter, ktk_params->params.ie_filter_params.length)) { printk(KERN_ERR "%s: wifi ktk set ie filter fail\n", __func__); return -EINVAL; } } else if (ktk_params->cmd == NL80211_WIFI_KTK_START) { ar->ktk_active = true; /* Clear the legacy ie pattern and filter */ if (ath6kl_wmi_disc_ie_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, NULL, 0)) { printk(KERN_ERR "%s: wifi ktk clear ie filter fail\n", __func__); return -EINVAL; } memcpy(ar->ktk_passphrase, ktk_params->params.start_params.passphrase, 16); if (ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, 1, SPECIFIC_SSID_FLAG, ktk_params->params.start_params.ssid_len, ktk_params->params.start_params.ssid)) { printk(KERN_ERR "%s: wifi ktk set probedssid fail\n", __func__); return -EINVAL; } if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi, vif->fw_vif_idx, ADHOC_PS_KTK, 5, 10, 10)) { printk(KERN_ERR "%s: wifi ktk set power save mode on fail\n", __func__); return -EINVAL; } } else if (ktk_params->cmd == NL80211_WIFI_KTK_STOP) { ar->ktk_active = false; if (ath6kl_wmi_ibss_pm_caps_cmd(ar->wmi, vif->fw_vif_idx, ADHOC_PS_DISABLE, 0, 0, 0)) { printk(KERN_ERR "%s: wifi ktk set power save mode off fail\n", __func__); return -EINVAL; } } } return 0; break; #endif default: return -EOPNOTSUPP; } }
void ath6kl_btcoex_adjust_params(struct ath6kl *ar, int wmi_cmd, u8 *buf) { switch (wmi_cmd) { case WMI_SET_BTCOEX_FE_ANT_CMDID: { struct wmi_set_btcoex_fe_antenna_cmd *cmd = (struct wmi_set_btcoex_fe_antenna_cmd *)buf; cmd->fe_antenna_type = fe_antenna_type(ar); /* disable green tx if it's enabled & BT is on */ ar->green_tx_params.enable = false; ath6kl_wmi_set_green_tx_params(ar->wmi, &ar->green_tx_params); } break; case WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID: { struct wmi_set_btcoex_colocated_bt_dev_cmd *cmd = (struct wmi_set_btcoex_colocated_bt_dev_cmd *)buf; /* Regardless of setting, force to use * qcom-colocated BT. It's the current working mode. */ ar->btcoex_info.bt_vendor = BT_DEVICE_TYPE_QCOM; cmd->colocated_bt_dev = BT_DEVICE_TYPE_QCOM; } break; case WMI_SET_BTCOEX_A2DP_CONFIG_CMDID: { struct wmi_set_btcoex_a2dp_config_cmd *cmd = (struct wmi_set_btcoex_a2dp_config_cmd *)buf; struct btcoex_a2dp_config *a2dp_config = &cmd->a2dp_config; struct btcoex_pspoll_a2dp_config *pspoll_config = &cmd->pspoll_config; struct ath6kl_vif *vif; if (ar->btcoex_info.bt_vendor == BT_DEVICE_TYPE_QCOM) { a2dp_config->a2dp_flags |= cpu_to_le32( WMI_A2DP_CONFIG_FLAG_ALLOW_OPTIMIZATION); } else { a2dp_config->a2dp_flags |= cpu_to_le32( WMI_A2DP_CONFIG_FLAG_IS_A2DP_HIGH_PRI); } if (a2dp_config->a2dp_flags & WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) { /* A2DP EDR config overwrites */ if (a2dp_config->a2dp_flags & WMI_A2DP_CONFIG_FLAG_IS_BT_ROLE_MASTER) { a2dp_config->a2dp_flags |= cpu_to_le32( BTCOEX_A2DP_EDR_MAX_BLUETOOTH_TIME); /* disable stomping BT during WLAN scan * or connection */ a2dp_config->a2dp_flags |= cpu_to_le32( WMI_A2DP_CONFIG_FLAG_DIS_SCANCONN_STOMP ); } else { /* use BDR parameter for A2DP EDR slave case */ a2dp_config->a2dp_flags |= cpu_to_le32( BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME); } pspoll_config->a2dp_wlan_max_dur = BTCOEX_A2DP_WLAN_MAX_DUR; } else { /* A2DP BDR config overwrites */ a2dp_config->a2dp_flags |= cpu_to_le32( BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME); pspoll_config->a2dp_wlan_max_dur = BTCOEX_A2DP_BDR_WLAN_MAX_DUR; } /* change A2DP parameters for AP mode*/ vif = ath6kl_vif_first(ar); if (!vif) return; if (vif->nw_type == AP_NETWORK) { if (a2dp_config->a2dp_flags & WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) { pspoll_config->a2dp_wlan_max_dur = BTCOEX_APMODE_A2DP_WLAN_MAX_DUR; a2dp_config->a2dp_flags |= cpu_to_le32( BTCOEX_APMODE_A2DP_MAX_BLUETOOTH_TIME); } else { pspoll_config->a2dp_wlan_max_dur = BTCOEX_APMODE_A2DP_BDR_WLAN_MAX_DUR; a2dp_config->a2dp_flags |= cpu_to_le32( BTCOEX_APMODE_A2DP_BDR_MAX_BLUETOOTH_TIME); } if (ar->version.target_ver == AR6004_HW_1_3_VERSION) { pspoll_config->a2dp_min_bus_cnt = cpu_to_le32( BTCOEX_APMODE_A2DP_MIN_BURST_CNT); } } } break; case WMI_SET_BTCOEX_SCO_CONFIG_CMDID: { struct wmi_set_btcoex_sco_config_cmd *cmd = (struct wmi_set_btcoex_sco_config_cmd *)buf; struct btcoex_sco_config *sco_config = &cmd->sco_config; if (sco_config->sco_flags & WMI_SCO_CONFIG_FLAG_IS_EDR_CAPABLE) { /* disable stomping BT during WLAN scan/connection */ sco_config->sco_flags |= cpu_to_le32( WMI_SCO_CONFIG_FLAG_DIS_SCANCONN_STOMP); } } break; } }