高通QSDK iwinfo工具分析

概述

iwinfo是基于IOCTRL的获取无线接口信息的命令

基本命令

fprintf(stderr,
			"Usage:\n"
			"	iwinfo <device> info\n"
			"	iwinfo <device> scan\n"
			"	iwinfo <device> txpowerlist\n"
			"	iwinfo <device> freqlist\n"
			"	iwinfo <device> assoclist\n"
			"	iwinfo <device> countrylist\n"
			"	iwinfo <device> htmodelist\n"
			"	iwinfo <backend> phyname <section>\n"
		);

代码分析

  1. main函数在iwinfo_cli.c中

    int main(int argc, char **argv)
    {
        const struct iwinfo_ops *iw;
        // 根据 athx 得到相应的iw ops 下面分析 
        iw = iwinfo_backend(argv[1]);
    
        for (i = 2; i < argc; i++){
            switch(argv[i][0]) //argv[2][0]  比如 argv[2] = info  那么 argv[2][0]=i 执行case i
            {
            case 'i':
                print_info(iw, argv[1]);
                break;
    
            case 's':
                print_scanlist(iw, argv[1]);
                break;
    
            case 't':
                print_txpwrlist(iw, argv[1]);
                break;
    
            case 'f':
                print_freqlist(iw, argv[1]);
                break;
    
            case 'a':
                print_assoclist(iw, argv[1]);
                break;
    
            case 'c':
                print_countrylist(iw, argv[1]);
                break;
    
            case 'h':
                print_htmodelist(iw, argv[1]);
                break;
    
            default:
                fprintf(stderr, "Unknown command: %s\n", argv[i]);
                rv = 1;
            }
        }
    }
    
  2. 下面分析iwinfo_backend函数

    //iwinfo_lib.c
    const struct iwinfo_ops * iwinfo_backend(const char *ifname)
    {
    	int i;
    
    	for (i = 0; i < ARRAY_SIZE(backends); i++)
    		if (backends[i]->probe(ifname)) // probe 返回1的时候说明找到对应的ops
    			return backends[i];
    
        // 这里的backends 是个全局变量,定义如下:
        /*
            static const struct iwinfo_ops *backends[] = {
                #ifdef USE_NL80211
                    &nl80211_ops,
                #endif
                #ifdef USE_MADWIFI
                    &madwifi_ops,
                #endif
                #ifdef USE_QCAWIFI
                        &qcawifi_ops,
                #endif
                #ifdef USE_WL
                    &wl_ops,
                #endif
                    &wext_ops,
            };
        */
        /*
            假如ifname = ath2 下面会轮询调用backends[i]->probe(ath2),会调用ops->probe(ath2)
            这里分析高通qcawifi_ops
            
        */
    	return NULL;
    }
    
    const struct iwinfo_ops qcawifi_ops = {
    	.name             = "qcawifi",
    	.probe            = qcawifi_probe,
    	.channel          = qcawifi_get_channel,
    	.frequency        = qcawifi_get_frequency,
    	.frequency_offset = qcawifi_get_frequency_offset,
    	.txpower          = qcawifi_get_txpower,
    	.txpower_offset   = qcawifi_get_txpower_offset,
    	.bitrate          = qcawifi_get_bitrate,
    	.signal           = qcawifi_get_signal,
    	.noise            = qcawifi_get_noise,
    	.quality          = qcawifi_get_quality,
    	.quality_max      = qcawifi_get_quality_max,
    	.mbssid_support   = qcawifi_get_mbssid_support,
    	.hwmodelist       = qcawifi_get_hwmodelist,
    	.mode             = qcawifi_get_mode,
    	.ssid             = qcawifi_get_ssid,
    	.bssid            = qcawifi_get_bssid,
    	.country          = qcawifi_get_country,
    	.hardware_id      = qcawifi_get_hardware_id,
    	.hardware_name    = qcawifi_get_hardware_name,
    	.encryption       = qcawifi_get_encryption,
    	.phyname          = qcawifi_get_phyname,
    	.assoclist        = qcawifi_get_assoclist,
    	.txpwrlist        = qcawifi_get_txpwrlist,
    	.scanlist         = qcawifi_get_scanlist,
    	.freqlist         = qcawifi_get_freqlist,
    	.countrylist      = qcawifi_get_countrylist,
    	.close            = qcawifi_close
    };
    
    int qcawifi_probe(const char *ifname)
    {
    	return ( !!qcawifi_isvap(ifname, NULL) || qcawifi_iswifi(ifname) );
        {
            // 这两个函数解释参见下面,如果此时输入的是ath2 这里qcawifi_isvap(ifname, NULL) 为wifi   !!wifi 即为 1 
            // qcawifi_iswifi(ifname) 返回0
        }
    }
    // 判断ifname是 vap 还是wifi  ath2是vap  wifi2 是wifi
    
    /*
     *   cat /sys/class/net/ath2/parent 
     *   wifi2
     *
     *  ifname=ath2 返回wifi
     *  ifname=wifi2 返回NULL
     */
    static char * qcawifi_isvap(const char *ifname, const char *wifiname)
    {
    	int fd, ln;
    	char path[64];
    	char *ret = NULL;
    	static char name[IFNAMSIZ];
    
    	if( strlen(ifname) <= 9 )
    	{
    		sprintf(path, "/sys/class/net/%s/parent", ifname);
            //如果输入是wifi2 这里打开会失败 ret=NULL
    		if( (fd = open(path, O_RDONLY)) > -1 )
    		{
    			if( wifiname != NULL )
    			{   // 这里一般不会进,忽略
    				if( read(fd, name, strlen(wifiname)) == strlen(wifiname) )
    					ret = strncmp(name, wifiname, strlen(wifiname))
    						? NULL : name;
    			}
    			else if( (ln = read(fd, name, IFNAMSIZ)) >= 4 )
    			{
                    // 如果输入的是ath2  这里的ret是wifi
    				name[ln-1] = 0;
    				ret = name;
    			}
    
    			(void) close(fd);
    		}
    	}
    
    	return ret;
    }
    /*
    *  cat /sys/class/net/wifi2/hwcaps 
    *  80211an/ac/ax
    *
    *  ifname=ath2 返回0
    *  ifname=wifi2 返回1
    */
    static int qcawifi_iswifi(const char *ifname)
    {
    	int fd, ln;
    	int ret = 0;
    	char prot[16];
    	char path[64];
    
    	/* qcawifi has a "hwcaps" file in wifiN sysfs to define the
    	 * protocol actually supported by the hardware */
    	sprintf(path, "/sys/class/net/%s/hwcaps", ifname);
    
    	if( (fd = open(path, O_RDONLY)) > -1 )
    	{
    		if( ln = read(fd, prot, sizeof(prot)))
    			ret = !strncmp(prot, "802.11", 6); //如果输入是wifi2 这里ret=1
    
    		close(fd);
    	}
    
    	return ret;
    }
    
    
    
  3. 下面以 iwinfo ath2 assoclist 为例说明

    //很明显 这里会调用到
    //       case 'a':
     //           print_assoclist(iw, argv[1]);
     //           break;
    
    
    static void print_assoclist(const struct iwinfo_ops *iw, const char *ifname)
    {
    	int i, len;
    	char buf[IWINFO_BUFSIZE];
    	struct iwinfo_assoclist_entry *e;
        // qcawifi_get_assoclist 函数会被调用
    	if (iw->assoclist(ifname, buf, &len))
    	{
    		printf("No information available\n");
    		return;
    	}
    	else if (len <= 0)
    	{
    		printf("No station connected\n");
    		return;
    	}
    
    	for (i = 0; i < len; i += sizeof(struct iwinfo_assoclist_entry))
    	{
    		e = (struct iwinfo_assoclist_entry *) &buf[i];
    
    		printf("%s  %s / %s (SNR %d)  %d ms ago\n",
    			format_bssid(e->mac),
    			format_signal(e->signal),
    			format_noise(e->noise),
    			(e->signal - e->noise),
    			e->inactive);
    
    		printf("	RX: %-38s  %8d Pkts.\n",
    			format_assocrate(&e->rx_rate),
    			e->rx_packets
    		);
    
    		printf("	TX: %-38s  %8d Pkts.\n",
    			format_assocrate(&e->tx_rate),
    			e->tx_packets
    		);
    
    		printf("	expected throughput: %s\n\n",
    			format_rate(e->thr));
    	}
    }
    
    
  4. 下面分析qcawifi_get_assoclist函数

    int qcawifi_get_assoclist(const char *ifname, char *buf, int *len)
    {
    	int bl, tl, noise;
    	uint8_t *cp;
    	uint8_t tmp[24*1024];
    	struct ieee80211req_sta_info *si;
    	struct iwinfo_assoclist_entry entry;
    
    	if( qcawifi_iswifi(ifname) )
    		return -1;
    
    	if( (tl = get80211priv(ifname, IEEE80211_IOCTL_STA_INFO, tmp, 24*1024)) > 0 )
    	{
    		cp = tmp;
    		bl = 0;
    
    		if( qcawifi_get_noise(ifname, &noise) )
    			noise = 0;
    
    		do {
    			si = (struct ieee80211req_sta_info *) cp;
    
    			memset(&entry, 0, sizeof(entry));
    
    			entry.signal = (si->isi_rssi - 95);
    			entry.noise  = noise;
    			memcpy(entry.mac, &si->isi_macaddr, 6);
    
    			entry.inactive = si->isi_inact * 1000;
    
    			entry.tx_packets = (si->isi_txseqs[0] & IEEE80211_SEQ_SEQ_MASK)
    				>> IEEE80211_SEQ_SEQ_SHIFT;
    
    			entry.rx_packets = (si->isi_rxseqs[0] & IEEE80211_SEQ_SEQ_MASK)
    				>> IEEE80211_SEQ_SEQ_SHIFT;
    
    			if(si->isi_txratekbps == 0)
    				entry.tx_rate.rate = (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2 * 1000;
    			else
    				entry.tx_rate.rate = si->isi_txratekbps;
    
    			entry.rx_rate.rate = si->isi_rxratekbps;
    
    			entry.rx_rate.mcs = -1;
    			entry.tx_rate.mcs = -1;
    
    			memcpy(&buf[bl], &entry, sizeof(struct iwinfo_assoclist_entry));
    
    			bl += sizeof(struct iwinfo_assoclist_entry);
    			cp += si->isi_len;
    			tl -= si->isi_len;
    		} while (tl >= sizeof(struct ieee80211req_sta_info));
    
    		*len = bl;
    		return 0;
    	}
    
    	return -1;
    }
    
    
  5. 下面分析get80211priv

    static int get80211priv(const char *ifname, int op, void *data, size_t len)
    {
    	struct iwreq iwr;
    
    	if( qcawifi_wrq(&iwr, ifname, op, data, len) < 0 )
    		return -1;
    
    	return iwr.u.data.length;
    }
    
    static int qcawifi_wrq(struct iwreq *wrq, const char *ifname, int cmd, void *data, size_t len)
    {
    	strncpy(wrq->ifr_name, ifname, IFNAMSIZ);
    
    	if( data != NULL )
    	{
    		if( len < IFNAMSIZ )
    		{
    			memcpy(wrq->u.name, data, len);
    		}
    		else
    		{
    			wrq->u.data.pointer = data;
    			wrq->u.data.length = len;
    		}
    	}
    
    	return iwinfo_ioctl(cmd, wrq);
    }
    
    
    //iwinfo_utils.c
    static int ioctl_socket = -1;
    struct uci_context *uci_ctx = NULL;
    
    static int iwinfo_ioctl_socket(void)
    {
    	/* Prepare socket */
    	if (ioctl_socket == -1)
    	{
    		ioctl_socket = socket(AF_INET, SOCK_DGRAM, 0);
    		fcntl(ioctl_socket, F_SETFD, fcntl(ioctl_socket, F_GETFD) | FD_CLOEXEC);
    	}
    
    	return ioctl_socket;
    }
    
    int iwinfo_ioctl(int cmd, void *ifr)
    {
    	int s = iwinfo_ioctl_socket();
    	return ioctl(s, cmd, ifr); // ioctl 调用
    }
    
  6. 到驱动层

    int
    ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
    {
        switch (cmd)
        {
            case IEEE80211_IOCTL_STA_INFO:
            return ieee80211_ioctl_getstainfo(dev, (struct iwreq *) ifr);
    
        }
    }
    
    
    
    static int
    ieee80211_ioctl_getstainfo(struct net_device *dev, struct iwreq *iwr)
    {
        osif_dev  *osifp = ath_netdev_priv(dev);
        wlan_if_t vap = osifp->os_if;
        int error = 0, status = 0;
        uint32_t data_length = 0;
        void *p;
        uint32_t memory_allocated;
    
        memory_allocated = iwr->u.data.length;
        data_length = ieee80211_ucfg_getstaspace(vap);
        if (data_length == 0)
            return -EPERM;
    
        if ((data_length > iwr->u.data.length) && (iwr->u.data.flags == 0)) {
            return data_length;
        }
    
        if((iwr->u.data.flags == 1) && (data_length > memory_allocated*LIST_STA_SPLIT_UNIT)) {
            return -EAGAIN;
        }
    
        p = (void *)OS_MALLOC(osifp->os_handle, data_length, GFP_KERNEL);
        if (p == NULL)
            return -ENOMEM;
    
        status = ieee80211_ucfg_getstainfo(vap, p, &data_length);
    
        if (data_length > 0) {
            iwr->u.data.length = data_length;
            error = _copy_to_user(iwr->u.data.pointer, p, data_length);
            status = error ? -EFAULT : 0;
        } else {
            iwr->u.data.length = 0;
        }
    
    
        OS_FREE(p);
    
        return status;
    }
    
    
    //qca/src/qca-wifi/umac/base/ieee80211_ucfg.c
    int ieee80211_ucfg_getstainfo(wlan_if_t vap, struct ieee80211req_sta_info *si, uint32_t *len)
    {
        struct stainforeq req;
    
    
        if (*len < sizeof(struct ieee80211req_sta_info))
            return -EFAULT;
    
        /* estimate space required for station info */
        req.space = sizeof(struct stainforeq);
        req.vap = vap;
    
        if (*len > 0)
        {
            size_t space = *len;
    
            if (si == NULL)
                return -ENOMEM;
    
            req.si = si;
            req.space = *len;
    
            wlan_iterate_station_list(vap, get_sta_info, &req);
            *len = space - req.space;
        }
        else
            *len = 0;
    
        return 0;
    }
    //qca/src/qca-wifi/umac/base/ieee80211_node.c
    int32_t wlan_iterate_station_list(wlan_if_t vap,ieee80211_sta_iter_func iter_func,void *arg)
    {
        return ieee80211_iterate_node_list(vap, iter_func, arg,
                                           IEEE80211_NODE_ITER_F_ASSOC_STA);
    }
    
    //qca/src/qca-wifi/umac/base/ieee80211_node.c
    static int32_t
    ieee80211_iterate_node_list(wlan_if_t vap,ieee80211_sta_iter_func iter_func,void *arg, u_int32_t flag)
    {
      struct ieee80211com *ic = vap->iv_ic;
      struct ieee80211_iter_arg *itr_arg = NULL;
      int i, count;
    
      itr_arg = (struct ieee80211_iter_arg *)qdf_mem_malloc(sizeof(struct ieee80211_iter_arg));
      if (itr_arg == NULL) {
              return -1;
      }
    
      itr_arg->count=0;
      itr_arg->vap=vap;
      itr_arg->flag=flag;
    
      /*
       * we can not call the call back function iter_func from the ieee80211_sta_iter.
       * because the ieee80211_iter is called with nt lock held and will result in
       * dead lock if the implementation of iter_func calls bcak into umac to query more
       * info about the node (which is more likely).
       * instaed the ieee80211_sta_iter collects all the nodes in to the nodes array
       * part of the itr_arg and also increments the ref count on these nodes so that
       * they wont get freed.
       */
    
      wlan_objmgr_iterate_peerobj_list(vap->vdev_obj, ieee80211_node_iter,
                                       (void *)itr_arg, WLAN_MLME_SB_ID);
      for (i = 0;i < itr_arg->count; ++i)
      {
          if (i == ic->ic_num_clients) break;
          if (iter_func) {
              /*
               * node has been refed in ieee80211_sta_iter
               * so safe to acces the contentes of the node.
               */
              (* iter_func) (arg, itr_arg->nodes[i]);  // iter_func=get_sta_info 下面分析该函数
          }
          /* decrement the ref count which is incremented above in ieee80211_sta_iter */
          ieee80211_free_node(itr_arg->nodes[i], WLAN_MLME_SB_ID);
      }
      count = itr_arg->count;
      qdf_mem_free(itr_arg);
      return (count);
    }
    
    void
    get_sta_info(void *arg, wlan_node_t node)
    {
        struct stainforeq *req = arg;
        wlan_if_t vap = req->vap;
        struct ieee80211req_sta_info *si;
        size_t ielen, len;
        u_int8_t *cp;
        u_int8_t    ni_ie[IEEE80211_MAX_OPT_IE];
        u_int16_t ni_ie_len = IEEE80211_MAX_OPT_IE;
        u_int8_t *macaddr = wlan_node_getmacaddr(node);
        wlan_snr_info snr_info;
        wlan_chan_t chan = wlan_node_get_chan(node);
        ieee80211_rate_info rinfo;
        u_int32_t jiffies_now=0, jiffies_delta=0, jiffies_assoc=0;
        struct wlan_objmgr_psoc *psoc;
        u_int32_t op_class=0, op_rates=0;
        ol_txrx_soc_handle soc_dp_handle;
        struct wlan_objmgr_pdev *pdev;
        QDF_STATUS status;
        cdp_peer_stats_param_t buf = {0};
    
        /* already ignore invalid nodes in UMAC */
        if (chan == IEEE80211_CHAN_ANYC) { /* XXX bogus entry */
            return;
        }
        if (!vap || !vap->iv_ic)
            return;
    
        pdev = vap->iv_ic->ic_pdev_obj;
        psoc = wlan_pdev_get_psoc(pdev);
    
        len = sta_space(node, &ielen, vap);
        if (len > req->space) {
            return;
        }
    
        si = req->si;
        si->awake_time = node->awake_time;
        si->ps_time = node->ps_time;
        /* if node state is currently in power save when the wlanconfig command is given,
           add time from previous_ps_time until current time to power save time */
        if(node->ps_state == 1)
        {
        si->ps_time += qdf_get_system_timestamp() - node->previous_ps_time;
        }
        /* if node state is currently in active state when the wlanconfig command is given,
           add time from previous_ps_time until current time to awake time */
        else if(node->ps_state == 0)
        {
        si->awake_time += qdf_get_system_timestamp() - node->previous_ps_time;
        }
        si->isi_assoc_time = wlan_node_get_assocuptime(node);
        jiffies_assoc = wlan_node_get_assocuptime(node);		/* Jiffies to timespec conversion for si->isi_tr069_assoc_time */
        jiffies_now = OS_GET_TICKS();
        jiffies_delta = jiffies_now - jiffies_assoc;
        jiffies_to_timespec(jiffies_delta, &si->isi_tr069_assoc_time);
        si->isi_len = len;
        si->isi_ie_len = ielen;
        si->isi_freq = wlan_channel_frequency(chan);
        si->isi_band = reg_wifi_band_to_wlan_band_id(wlan_reg_freq_to_band(si->isi_freq));
        if(!vap->iv_ic->ic_is_target_lithium){
            qdf_err("ic_is_target_lithium if null");
            return;
        }
        if(vap->iv_ic->ic_is_target_lithium(psoc)){
            if(!vap->iv_ic->ic_get_cur_hw_nf){
                qdf_err("ic_get_cur_hw_nf is null");
                return;
            }
            si->isi_nf = vap->iv_ic->ic_get_cur_hw_nf(vap->iv_ic);
        } else {
            if(!vap->iv_ic->ic_get_cur_chan_nf){
                qdf_err("ic_get_cur_hw_nf is null");
                return;
            }
            si->isi_nf = vap->iv_ic->ic_get_cur_chan_nf(vap->iv_ic);
        }
        si->isi_ieee = wlan_channel_ieee(chan);
        si->isi_flags = wlan_channel_flags(chan);
        si->isi_state = wlan_node_get_state_flag(node);
        si->isi_ps = node->ps_state;
        si->isi_authmode =  wlan_node_get_authmode(node);
        if (wlan_node_getsnr(node, &snr_info, WLAN_SNR_RX) == 0) {
            si->isi_rssi = snr_info.avg_snr;
            si->isi_min_rssi = node->ni_snr_min;
            si->isi_max_rssi = node->ni_snr_max;
        }
        si->isi_capinfo = wlan_node_getcapinfo(node);
    #if ATH_BAND_STEERING
        si->isi_pwrcapinfo = wlan_node_getpwrcapinfo(node);
    #endif
    #if UMAC_SUPPORT_RRM
        OS_MEMCPY(si->isi_rrm_caps, node->ni_rrm_caps, sizeof(si->isi_rrm_caps));
    #endif
        si->isi_athflags = wlan_node_get_ath_flags(node);
        si->isi_erp = wlan_node_get_erp(node);
        si->isi_operating_bands = wlan_node_get_operating_bands(node);
        si->isi_beacon_measurement_support = wlan_node_has_extflag(node, IEEE80211_NODE_BCN_MEASURE_SUPPORT);
        IEEE80211_ADDR_COPY(si->isi_macaddr, macaddr);
    
        if (wlan_node_txrate_info(node, &rinfo) == 0) {
            si->isi_txratekbps = rinfo.rate;
            si->isi_maxrate_per_client = rinfo.maxrate_per_client;
    #if ATH_EXTRA_RATE_INFO_STA
            si->isi_tx_rate_mcs = rinfo.mcs;
            si->isi_tx_rate_flags = rinfo.flags;
    #endif
    
        }
    
        /* supported operating classes */
        if (node->ni_supp_op_class_ie != NULL) {
            si->isi_curr_op_class = node->ni_supp_op_cl.curr_op_class;
            si->isi_num_of_supp_class = node->ni_supp_op_cl.num_of_supp_class;
            for(op_class = 0; op_class < node->ni_supp_op_cl.num_of_supp_class &&
                op_class < MAX_NUM_OPCLASS_SUPPORTED; op_class++) {
                si->isi_supp_class[op_class] = node->ni_supp_op_cl.supp_class[op_class];
            }
        }
        else {
              si->isi_num_of_supp_class = 0;
        }
    
        /* supported channels */
        if (node->ni_supp_chan_ie != NULL) {
            si->isi_first_channel = node->ni_first_channel;
            si->isi_nr_channels = node->ni_nr_channels;
        }
        else {
             si->isi_nr_channels = 0;
        }
    
        /* supported rates */
        for (op_rates = 0;op_rates < node->ni_rates.rs_nrates;op_rates++) {
             si->isi_rates[op_rates] = node->ni_rates.rs_rates[op_rates];
        }
    
        memset(&rinfo, 0, sizeof(rinfo));
        if (wlan_node_rxrate_info(node, &rinfo) == 0) {
            si->isi_rxratekbps = rinfo.rate;
    #if ATH_EXTRA_RATE_INFO_STA
            si->isi_rx_rate_mcs = rinfo.mcs;
            si->isi_rx_rate_flags = rinfo.flags;
    #endif
    
        }
        si->isi_associd = wlan_node_get_associd(node);
        si->isi_txpower = wlan_node_get_txpower(node);
        si->isi_vlan = wlan_node_get_vlan(node);
        si->isi_cipher = IEEE80211_CIPHER_NONE;
        if (wlan_get_param(vap, IEEE80211_FEATURE_PRIVACY)) {
            do {
                ieee80211_cipher_type uciphers[1];
                int count = 0;
                count = wlan_node_get_ucast_ciphers(node, uciphers, 1);
                if (count == 1) {
                    si->isi_cipher |= 1<<uciphers[0];
                }
            } while (0);
        }
        wlan_node_get_txseqs(node, si->isi_txseqs, sizeof(si->isi_txseqs));
        wlan_node_get_rxseqs(node, si->isi_rxseqs, sizeof(si->isi_rxseqs));
        si->isi_uapsd = wlan_node_get_uapsd(node);
        si->isi_opmode = IEEE80211_STA_OPMODE_NORMAL;
    
        psoc = wlan_pdev_get_psoc(pdev);
        soc_dp_handle = wlan_psoc_get_dp_handle(psoc);
        if (!soc_dp_handle)
            return;
    
        status = cdp_txrx_get_peer_stats_param(soc_dp_handle,
                                         wlan_vdev_get_id(node->peer_obj->peer_objmgr.vdev),
                                         node->peer_obj->macaddr, cdp_peer_tx_inactive_time,
                                         &buf);
        if (QDF_IS_STATUS_ERROR(status))
            return;
    
        si->isi_inact = buf.tx_inactive_time;
        /* 11n */
        si->isi_htcap = wlan_node_get_htcap(node);
        si->isi_stamode= wlan_node_get_mode(node);
        si->isi_curr_mode = get_phymode_from_chwidth(vap->iv_ic, node);
    
    #if ATH_SUPPORT_EXT_STAT
        si->isi_vhtcap = wlan_node_get_vhtcap(node);
        si->isi_chwidth = (u_int8_t) wlan_node_get_chwidth(node);
    #endif
    
        /* Extended capabilities */
        si->isi_ext_cap = wlan_node_get_extended_capabilities(node);
        si->isi_ext_cap2 = wlan_node_get_extended_capabilities2(node);
        si->isi_ext_cap3 = wlan_node_get_extended_capabilities3(node);
        si->isi_ext_cap4 = wlan_node_get_extended_capabilities4(node);
        si->isi_nss = wlan_node_get_nss(node);
        si->isi_supp_nss = wlan_node_get_nss_capability(node);
        si->isi_is_256qam = wlan_node_get_256qam_support(node);
        si->isi_is_he = !!(IEEE80211_NODE_USE_HE(node));
        if (si->isi_is_he) {
            qdf_mem_copy(&si->isi_hecap_rxmcsnssmap,
                         &node->ni_he.hecap_rxmcsnssmap,
                         sizeof(u_int16_t) * HEHANDLE_CAP_TXRX_MCS_NSS_SIZE);
            qdf_mem_copy(&si->isi_hecap_txmcsnssmap,
                         &node->ni_he.hecap_txmcsnssmap,
                         sizeof(u_int16_t) * HEHANDLE_CAP_TXRX_MCS_NSS_SIZE);
            qdf_mem_copy(&si->isi_hecap_phyinfo,
                         &node->ni_he.hecap_phyinfo,
                         sizeof(u_int32_t) * HEHANDLE_CAP_PHYINFO_SIZE);
        }
    
        cp = (u_int8_t *)(si+1);
    
        if(!wlan_node_getwpaie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
        if(!wlan_node_getwmeie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
        if(!wlan_node_getathie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
        if(!wlan_node_getwpsie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
        if (!wlan_node_get_suppchanie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
        if (!wlan_node_get_opclassie(vap, macaddr, ni_ie, &ni_ie_len)) {
            OS_MEMCPY(cp, ni_ie, ni_ie_len);
            cp += ni_ie_len;
            ni_ie_len = IEEE80211_MAX_OPT_IE;
        }
    
        req->si = (
        struct ieee80211req_sta_info *)(((u_int8_t *)si) + len);
        req->space -= len;
    }