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;
	}
}
int ath6kl_wmi_set_customer_testmode_cmd(struct ath6kl_vif *vif, athcfg_wcmd_testmode_t *val)
{
	struct sk_buff *skb;
	struct WMI_CUSTOMER_TESTMODE_STRUCT *cmd;
	int ret = 0;
    bool send_wmi_flag = false;
    
    switch(val->operation) 
    {
        case ATHCFG_WCMD_TESTMODE_BSSID:
            {
                if(memcmp(testmode_private.bssid,val->bssid,sizeof(testmode_private.bssid)) != 0) {
                    memcpy(testmode_private.bssid,val->bssid,sizeof(testmode_private.bssid));
                    send_wmi_flag = true;
                }
                //printk("%s[%d] testmode_private.bssid=\"%pM\"\n",__func__,__LINE__,testmode_private.bssid);
            }
        break;
        case ATHCFG_WCMD_TESTMODE_CHAN:
            {
                if(testmode_private.chan != val->chan) {
                    testmode_private.chan = val->chan;
                    send_wmi_flag = true;
                }
                //printk("%s[%d] testmode_private.chan=%d\n",__func__,__LINE__,testmode_private.chan);        
            }
            break;
        case ATHCFG_WCMD_TESTMODE_RX:
            {
                if(testmode_private.rx != val->rx) {
                    testmode_private.rx = val->rx;   
                    send_wmi_flag = true;
                }
            }        
            break;
        case ATHCFG_WCMD_TESTMODE_ANT:            
        default:
            printk("%s[%d]Not support\n",__func__,__LINE__);
            return -1;
    }
    
    //send WMI to target
    if(send_wmi_flag) {
        testmode_private.rx = val->rx;
        skb = ath6kl_wmi_get_new_buf(sizeof(*cmd));
        if (!skb)
            return -ENOMEM;

        cmd = (struct WMI_CUSTOMER_TESTMODE_STRUCT *) skb->data;
        //transfer to little endian
        memcpy(cmd->bssid, val->bssid,  sizeof(cmd->bssid));
        cmd->chan = cpu_to_le32(val->chan); 
        cmd->operation = cpu_to_le16(val->operation);
        cmd->antenna = val->antenna;
        cmd->rx = val->rx; 

        cmd->rssi_combined = cpu_to_le32(val->rssi_combined); 
        cmd->rssi0 = cpu_to_le32(val->rssi0); 
        cmd->rssi1 = cpu_to_le32(val->rssi1); 
        cmd->rssi2 = cpu_to_le32(val->rssi2); 
        
        ret = ath6kl_wmi_cmd_send(vif, skb, WMI_SET_CUSTOM_TESTMODE,
                      NO_SYNC_WMIFLAG);
    }  
    
    if(val->operation == ATHCFG_WCMD_TESTMODE_RX) {
        if(val->rx == 1) {
            s8 n_channels = 1;
            u16 *channels = NULL;
            int i;
            set_bit(SONY_WMI_TESTMODE_RX, &vif->flag);

            //set scan param
            ath6kl_wmi_scanparams_cmd(
                            vif, 
                            0, 
                            0xffff, 
                            0,     
                            vif->sc_params.minact_chdwell_time,
                            vif->sc_params.maxact_chdwell_time,//0xffff, 
                            1000,//vif->sc_params.pas_chdwell_time, msec
                            vif->sc_params.short_scan_ratio,
                            (vif->sc_params.scan_ctrl_flags & ~ACTIVE_SCAN_CTRL_FLAGS), 
                            //vif->sc_params.scan_ctrl_flags, 
                            vif->sc_params.max_dfsch_act_time, 
                            vif->sc_params.maxact_scan_per_ssid);                  

            //assign request channel
            channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL);
            if (channels == NULL) {
                n_channels = 0;
            }
            
            for (i = 0; i < n_channels; i++) {
                channels[i] = ieee80211_channel_to_frequency_ath6kl((testmode_private.chan == 0 ? 1:testmode_private.chan),IEEE80211_NUM_BANDS);                    
                //printk("%s[%d]channels[%d]=%d,testmode_private.chan=%d\n",__func__,__LINE__,i,channels[i],testmode_private.chan);
            }

            ret = ath6kl_wmi_bssfilter_cmd(
                 vif,
                 ALL_BSS_FILTER, 0);
            if (ret) {
                printk("%s[%d] Fail\n",__func__,__LINE__);
                goto rx_fail;
            }                 
            ret = ath6kl_wmi_startscan_cmd(vif, WMI_LONG_SCAN, 1,
                               false, 0, 0, n_channels, channels);                              
            if (ret) {
                printk("%s[%d] Fail\n",__func__,__LINE__);
                goto rx_fail;
            }
rx_fail:            
            kfree(channels);
        } else {
            //cancel test mode scan
            clear_bit(SONY_WMI_TESTMODE_RX, &vif->flag);                 
			ath6kl_wmi_scanparams_cmd(vif, 
									   vif->sc_params.fg_start_period, 
									   vif->sc_params.fg_end_period, 
									   vif->sc_params.bg_period, 
									   vif->sc_params.minact_chdwell_time, 
									   vif->sc_params.maxact_chdwell_time, 
									   vif->sc_params.pas_chdwell_time,
									   vif->sc_params.short_scan_ratio, 
									   vif->sc_params.scan_ctrl_flags, 
									   vif->sc_params.max_dfsch_act_time,
									   vif->sc_params.maxact_scan_per_ssid);	
        }
    }
	return ret;
}