blob: b2583e4fd731fca77282dc7926ee133245c6e1d8 [file] [log] [blame]
/*
* Common code for wl command-line swiss-army-knife utility
*
* Copyright (C) 2016, Broadcom Corporation
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
* the contents of this file may not be disclosed to third parties, copied
* or duplicated in any form, in whole or in part, without the prior
* written permission of Broadcom Corporation.
*
* $Id: wlu.c 647609 2016-07-06 21:27:18Z $
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <wlioctl.h>
#define CLMDOWNLOAD
/* Because IL_BIGENDIAN was removed there are few warnings that need
* to be fixed. Windows was not compiled earlier with IL_BIGENDIAN.
* Hence these warnings were not seen earlier.
* For now ignore the following warnings
*/
#ifdef WIN32
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4761)
#endif
#include <typedefs.h>
#include <epivers.h>
#include <proto/ethernet.h>
#include <proto/802.11.h>
#include <proto/802.1d.h>
#include <proto/802.11e.h>
#include <proto/wpa.h>
#include <proto/bcmip.h>
#include <proto/wps.h>
#include <bcmwifi_rates.h>
#include "wlu_rates_matrix.h"
#ifdef BCMCCX
#include <proto/802.11_ccx.h>
#endif
#ifdef WLBTAMP
#include <proto/bt_amp_hci.h>
#endif
#include <sdiovar.h>
#include <bcmutils.h>
#include <bcmendian.h>
#include <bcmwifi_channels.h>
#include <bcmsrom_fmt.h>
#include <bcmsrom_tbl.h>
#include "wlu_common.h"
#include "wlu.h"
#include <bcmcdc.h>
#if defined(WLPFN)
#ifndef TARGETENV_android
#include <unistd.h>
#endif
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/if_packet.h>
#endif /* WLPFN */
#ifdef WLEXTLOG
#include <wlc_extlog_idstr.h>
#endif
#include <inttypes.h>
#include <miniopt.h>
#include <errno.h>
#if defined CLMDOWNLOAD
#include <sys/stat.h>
#include <trxhdr.h>
#include <stdio.h>
#include <errno.h>
#ifndef WIN32
#include <fcntl.h>
#endif /* WIN32 */
#endif
#if LCNCONF || SSLPNCONF
#define MAX_CHUNK_LEN 1456 /* 8 * 7 * 26 */
#else
#define MAX_CHUNK_LEN 1408 /* 8 * 8 * 22 */
#endif
#include <bcm_mpool_pub.h>
#include <proto/bcmipv6.h>
#ifdef EVENT_LOG_COMPILE
#define EVENT_LOG_DUMPER
#include <event_log.h>
#endif
/* For backwards compatibility, the absense of the define 'NO_FILESYSTEM_SUPPORT'
* implies that a filesystem is supported.
*/
#if !defined(BWL_NO_FILESYSTEM_SUPPORT)
#define BWL_FILESYSTEM_SUPPORT
#endif
cmd_func_t wl_int;
static cmd_func_t wl_print_deprecate;
static cmd_func_t wl_void, wl_rssi, wl_rssi_event, wl_phy_rssi_ant, wl_gmode;
static cmd_func_t wlu_dump, wlu_mempool, wlu_srdump, wlu_srwrite, wlu_srvar, wl_nvsource;
static cmd_func_t wlu_ciswrite, wlu_cisupdate, wlu_cisdump, wlu_offloads_stats;
static cmd_func_t wl_rate, wl_rate_mrate, wl_phy_rate, wl_bss_max;
static cmd_func_t wl_channel, wl_chanspec, wl_chanim_state, wl_chanim_mode, wl_rclass;
static cmd_func_t wl_radio, wl_version, wl_list, wl_band, wl_bandlist, wl_phylist;
static cmd_func_t wl_join, wl_tssi, wl_txpwr, wl_atten, wl_evm, wl_country;
static cmd_func_t wl_out, wl_txpwr1, wl_country_ie_override;
static cmd_func_t wl_maclist, wl_get_pktcnt, wl_upgrade;
static cmd_func_t wl_maclist_1, wl_default_rateset;
static cmd_func_t wl_rateset, wl_interfere, wl_interfere_override;
static cmd_func_t wl_radar_args, wl_radar_thrs, wl_dfs_status;
static cmd_func_t wl_get_txpwr_limit, wl_get_current_power, wl_get_instant_power;
static cmd_func_t wl_get_current_txppr, wl_get_txpwr_target_max;
static cmd_func_t wl_var_get, wl_var_getint, wl_var_getinthex, wl_var_getandprintstr;
static cmd_func_t wl_var_setint, wl_addwep, wl_rmwep;
static cmd_func_t wl_nvdump, wl_nvget, wl_nvset, wl_sta_info, wl_chan_info;
static cmd_func_t wl_wme_ac_req, wl_add_ie, wl_del_ie, _wl_list_ie;
static cmd_func_t wl_wme_apsd_sta, wl_wme_dp, wl_lifetime;
static cmd_func_t wl_rand, wl_otpw, wl_otpraw, wl_counters, wl_delta_stats;
static cmd_func_t wl_assoc_info, wl_wme_counters, wl_devpath;
static cmd_func_t wl_management_info;
static cmd_func_t wl_eventbitvec, wl_diag, wl_var_void;
static cmd_func_t wl_auto_channel_sel;
static cmd_func_t wl_bsscfg_int, wl_bsscfg_enable;
static cmd_func_t wl_msglevel, wl_plcphdr, wl_reg, wl_macreg, wl_band_elm;
static cmd_func_t wl_phymsglevel;
static cmd_func_t wl_rateparam, wl_wepstatus, wl_status, wl_spect;
static cmd_func_t wl_sup_rateset, wl_scan, wl_send_csa, wl_iscan, wl_escan;
static cmd_func_t wl_roamparms;
/* WLOTA_EN START */
static cmd_func_t wl_ota_loadtest, wl_otatest_status, wl_load_cmd_stream;
static cmd_func_t wl_ota_teststop;
/* WLOTA_EN END */
#ifdef EXTENDED_SCAN
static cmd_func_t wl_extdscan;
#endif
static cmd_func_t wl_dump_chanlist, wl_primary_key, wl_measure_req, wl_send_quiet;
static cmd_func_t wl_dump_chanspecs, wl_dump_chanspecs_defset, wl_cur_mcsset;
static cmd_func_t wl_wsec, wl_keys, wl_wsec_test;
static cmd_func_t wl_channels_in_country;
static cmd_func_t wl_wpa_auth, wl_tsc, wl_deauth_rc, wl_ssid, wl_bssid, wl_smfstats;
static cmd_func_t wl_wds_wpa_role_old, wl_wds_wpa_role, wl_set_pmk;
static cmd_func_t wl_rm_request, wl_rm_report;
static cmd_func_t wl_join_pref, wl_assoc_pref;
static cmd_func_t wl_dump_networks, wl_mac, wl_revinfo, wl_iov_mac, wl_iov_pktqlog_params;
static cmd_func_t wl_cac, wl_tslist, wl_tspec, wl_tslist_ea, wl_tspec_ea, wl_cac_delts_ea;
static cmd_func_t wl_varstr, wl_var_setintandprintstr;
static cmd_func_t wl_rifs;
static cmd_func_t wl_rifs_advert;
static cmd_func_t wl_test_tssi, wl_test_tssi_offs, wl_phy_rssiant, wl_rxiq;
static cmd_func_t wl_obss_scan, wl_obss_coex_action;
static cmd_func_t wl_dump_lq;
static cmd_func_t wl_monitor_lq;
static cmd_func_t wl_test_idletssi;
#ifdef WLPFN
static cmd_func_t wl_pfn_set;
static cmd_func_t wl_pfn_add;
static cmd_func_t wl_pfn_add_bssid;
static cmd_func_t wl_pfn_cfg;
static cmd_func_t wl_pfn;
static cmd_func_t wl_pfnbest;
static cmd_func_t wl_pfn_suspend;
static cmd_func_t wl_pfnlbest;
static cmd_func_t wl_pfn_mem;
static cmd_func_t wl_pfn_event_check;
static cmd_func_t wl_escan_event_check;
static cmd_func_t wl_escanresults;
static cmd_func_t wl_event_filter;
#endif /* WLPFN */
#ifdef WLP2PO
static cmd_func_t wl_p2po_listen;
static cmd_func_t wl_p2po_addsvc;
static cmd_func_t wl_p2po_delsvc;
static cmd_func_t wl_p2po_sd_reqresp;
static cmd_func_t wl_p2po_listen_channel;
static cmd_func_t wl_p2po_stop;
static cmd_func_t wl_p2po_results;
#endif /* WLP2PO */
#ifdef WLANQPO
static cmd_func_t wl_anqpo_set;
static cmd_func_t wl_anqpo_stop_query;
static cmd_func_t wl_anqpo_start_query;
static cmd_func_t wl_anqpo_ignore_ssid_list;
static cmd_func_t wl_anqpo_ignore_bssid_list;
static cmd_func_t wl_anqpo_results;
#endif /* WLANQPO */
static cmd_func_t wl_hs20_ie;
static cmd_func_t wl_wowl_pattern, wl_wowl_wakeind, wl_wowl_pkt, wl_wowl_status;
static cmd_func_t wl_wowl_wake_reason, wl_wowl_extended_magic;
static cmd_func_t wl_reassoc;
static cmd_func_t wl_sd_reg, wl_sd_msglevel, wl_sd_blocksize, wl_sd_mode;
static cmd_func_t wl_overlay;
static cmd_func_t wl_pmkid_info;
#ifdef BCMCCX
static cmd_func_t wl_leap;
#endif
static void wl_rate_histo_print(wl_mac_ratehisto_res_t *rate_histo_res);
static cmd_func_t wl_rate_histo;
static cmd_func_t wl_mac_rate_histo;
static cmd_func_t wl_sample_collect;
static cmd_func_t wlu_reg3args;
static cmd_func_t wl_tpc_lm;
static cmd_func_t wlu_reg2args;
static cmd_func_t wme_tx_params;
static cmd_func_t wme_maxbw_params;
static cmd_func_t wl_ampdu_tid, wl_ampdu_activate_test;
static cmd_func_t wl_ampdu_retry_limit_tid;
static cmd_func_t wl_ampdu_rr_retry_limit_tid;
static cmd_func_t wl_ampdu_send_addba;
static cmd_func_t wl_ampdu_send_delba;
static cmd_func_t wl_dpt_deny;
static cmd_func_t wl_dpt_endpoint;
static cmd_func_t wl_dpt_pmk;
static cmd_func_t wl_dpt_fname;
static cmd_func_t wl_dpt_list;
#ifdef WLTDLS
static cmd_func_t wl_tdls_endpoint;
static cmd_func_t wl_tdls_wfd_ie;
#endif /* WLTDLS */
#ifdef WLBTAMP
static cmd_func_t wl_HCI_cmd;
static cmd_func_t wl_HCI_ACL_data;
static cmd_func_t wl_get_btamp_log;
#endif
static cmd_func_t wl_actframe;
static cmd_func_t wl_gpioout;
static cmd_func_t wl_bw_cap;
static cmd_func_t wl_nrate, wl_antsel, wl_txcore;
static cmd_func_t wl_txcore_pwr_offset;
#ifdef PLC
static cmd_func_t wl_plc;
#endif /* PLC */
static cmd_func_t wl_txfifo_sz;
static cmd_func_t wl_pkteng, wl_pkteng_stats;
static cmd_func_t wl_offload_cmpnt;
static cmd_func_t wl_hostip, wl_arp_stats, wl_toe_stats, wl_nshostip;
static cmd_func_t wl_mcast_ackmac, wl_mcast_ackreq, wl_mcast_status;
static cmd_func_t wl_mcast_actf_time, wl_mcast_rssi_thresh, wl_mcast_stats;
static cmd_func_t wl_mcast_rssi_delta, wl_mcast_vsie, wl_mcast_ar_timeout;
#ifdef WLOFFLD
static cmd_func_t wl_ol_notify_bcn_ie;
#endif
#if defined(WLNDOE) || defined(WLOFFLD)
static cmd_func_t wl_hostipv6;
#endif
#ifdef WLNDOE
static cmd_func_t wl_ndstatus, wl_hostipv6, wl_solicitipv6, wl_remoteipv6;
#endif
static cmd_func_t wl_phy_papdepstbl;
int wl_seq_batch_in_client(bool enable);
cmd_func_t wl_seq_start;
cmd_func_t wl_seq_stop;
static cmd_func_t wl_phy_txiqcc, wl_phy_txlocc;
static cmd_func_t wl_rssi_cal_freq_grp_2g;
static cmd_func_t wl_phy_rssi_gain_delta_2g, wl_phy_rssi_gain_delta_5g;
static cmd_func_t wl_phy_rssi_gain_delta_2g_sub;
static cmd_func_t wl_phy_rxgainerr_2g, wl_phy_rxgainerr_5g;
static cmd_func_t wl_phytable, wl_phy_pavars, wl_phy_povars;
static cmd_func_t wl_phy_fem, wl_phy_maxpower, wl_antgain, wl_phy_txpwrindex;
static cmd_func_t wl_keep_alive;
static cmd_func_t wl_mkeep_alive;
static cmd_func_t wl_srchmem;
static cmd_func_t wl_phy_rpcalvars;
static cmd_func_t wl_pkt_filter_add;
static cmd_func_t wl_pkt_filter_enable;
static cmd_func_t wl_pkt_filter_list;
static cmd_func_t wl_pkt_filter_stats;
#ifdef TRAFFIC_MGMT
static cmd_func_t wl_trf_mgmt_config;
static cmd_func_t wl_trf_mgmt_filters_add;
static cmd_func_t wl_trf_mgmt_filters_addex;
static cmd_func_t wl_trf_mgmt_filters_remove;
static cmd_func_t wl_trf_mgmt_filters_removeex;
static cmd_func_t wl_trf_mgmt_filters_list;
static cmd_func_t wl_trf_mgmt_filters_clear;
static cmd_func_t wl_trf_mgmt_bandwidth;
static cmd_func_t wl_trf_mgmt_flags;
static cmd_func_t wl_trf_mgmt_stats;
static cmd_func_t wl_trf_mgmt_stats_clear;
static cmd_func_t wl_trf_mgmt_shaping_info;
#endif /* TRAFFIC_MGMT */
#ifdef CLMDOWNLOAD
static cmd_func_t wl_clmload;
#endif /* CLMDOWNLOAD */
static cmd_func_t wl_ledbh;
#ifdef RWL_WIFI
/* Function added to support RWL_WIFI Transport */
static cmd_func_t wl_wifiserver;
#endif
static cmd_func_t wl_led_blink_sync;
static cmd_func_t wl_cca_get_stats;
static cmd_func_t wl_itfr_get_stats;
static cmd_func_t wl_rrm;
static cmd_func_t wl_rrm_nbr_req;
static cmd_func_t wl_rrm_bcn_req;
static cmd_func_t wl_rrm_chload_req;
static cmd_func_t wl_rrm_noise_req;
static cmd_func_t wl_rrm_frame_req;
static cmd_func_t wl_rrm_stat_req;
static cmd_func_t wl_rrm_lm_req;
static cmd_func_t wl_rrm_nbr_list;
static cmd_func_t wl_rrm_nbr_del_nbr;
static cmd_func_t wl_rrm_nbr_add_nbr;
static cmd_func_t wl_wnm;
static cmd_func_t wl_wnm_bsstq;
static cmd_func_t wl_tclas_add;
static cmd_func_t wl_tclas_del;
static cmd_func_t wl_tclas_list;
#ifdef WLWNM
static cmd_func_t wl_wnm_tfsreq_add;
static cmd_func_t wl_wnm_dms_set;
static cmd_func_t wl_wnm_dms_status;
static cmd_func_t wl_wnm_dms_term;
static cmd_func_t wl_wnm_service_term;
static cmd_func_t wl_wnm_timbc_offset;
static cmd_func_t wl_wnm_timbc_set;
static cmd_func_t wl_wnm_timbc_status;
static cmd_func_t wl_wnm_maxidle;
static cmd_func_t wl_wnm_bsstrans_req;
static cmd_func_t wl_wnm_keepalives_max_idle;
#endif /* WLWNM */
static cmd_func_t wl_chanim_acs_record;
static cmd_func_t wl_chanim_stats;
static cmd_func_t wl_txdelay_params;
static cmd_func_t wl_intfer_params;
#ifdef WLP2P
static cmd_func_t wl_p2p_state;
static cmd_func_t wl_p2p_scan;
static cmd_func_t wl_p2p_ifadd;
static cmd_func_t wl_p2p_ifdel;
static cmd_func_t wl_p2p_ifupd;
static cmd_func_t wl_p2p_if;
static cmd_func_t wl_p2p_ops;
static cmd_func_t wl_p2p_noa;
#endif
static cmd_func_t wl_rpmt;
static cmd_func_t wl_spatial_policy, wl_ratetbl_ppr;
static cmd_func_t wl_sarlimit;
static cmd_func_t wl_bmon_bssid;
static cmd_func_t wl_ie;
static cmd_func_t wl_wnm_url;
#ifdef SR_DEBUG
static cmd_func_t wl_dump_pmu;
static cmd_func_t wl_pmu_keep_on;
#endif /* SR_DEBUG */
#ifdef TBTT_OFFSET_STAT
static cmd_func_t wl_tbtt_offset_stat;
#endif /* TBTT_OFFSET_STAT */
static cmd_func_t wl_staprio;
static cmd_func_t wl_stamon_sta_config;
static cmd_func_t wl_monitor_promisc_level;
static cmd_func_t wl_bcnlenhist;
static cmd_func_t wl_aibss_bcn_force_config;
#if defined(DWDS)
static cmd_func_t wl_dwds_config;
#endif
static cmd_func_t wl_bss_peer_info;
static cmd_func_t wl_aibss_txfail_config;
static void wl_txppr_print(ppr_t *ppr, int cck, uint flags);
static void wl_txppr_print_bw(ppr_t *ppr, int cck, uint flags, wl_tx_bw_t bw);
static int8 wl_ppr_get_pwr(ppr_t* pprptr, reg_rate_index_t rate_idx, wl_tx_bw_t bw);
static void wl_txpwr_array_row_print(ppr_t* pprptr, int8 channel_bandwidth,
reg_rate_index_t rate_idx);
static void wl_txpwr_array_print(ppr_t* pprptr, int8 channel_bandwidth, bool verbose, bool is5G);
static void wl_txpwr_ppr_print(ppr_t* pprptr, int vb, ppr_rate_type_t type,
clm_rate_group_id_t gid, int8 bw, reg_rate_index_t *rate_index, bool is5G);
static void wl_txpwr_print_row(const char *label, uint8 chains, int8 pwr20,
int8 pwr20in40, int8 pwr40, int8 pwr80, int8 pwr20in80, int8 pwr40in80,
int8 unsupported_rate, int8 channel_bandwidth, bool verbose);
static int wl_array_check_val(int8 *pwr, uint count, int8 val);
static void wl_txpwr_regulatory_array_row_print(int8 *powers, int8 *powers_subchan1,
int8 *powers_subchan2, int8 channel_bandwidth, reg_rate_index_t rate_index);
static void wl_txpwr_regulatory_array_print(int8 *powers, int8 *powers_subchan1,
int8 *powers_subchan2, int8 channel_bandwidth, bool verbose);
static int wl_array_uniform(uint8 *pwr, int start, int count);
static int wl_parse_rateset(void *wl, wl_rateset_args_t* rs, char **argv);
static void wl_print_mcsset(char *mcsset);
static void wl_print_vhtmcsset(uint16 *mcsset);
static void dump_networks(char *buf);
void dump_bss_info(wl_bss_info_t *bi);
static void wl_dump_wpa_rsn_ies(uint8* cp, uint len);
static void wl_rsn_ie_dump(bcm_tlv_t *ie);
static cmd_func_t wl_power_sel_params;
static cmd_func_t wl_phy_force_vsdb_chans;
int wlu_get(void *wl, int cmd, void *buf, int len);
int wlu_set(void *wl, int cmd, void *buf, int len);
static int _wl_dump_lq(void *wl);
/* 802.11i/WPA RSN IE parsing utilities */
typedef struct {
uint16 version;
wpa_suite_mcast_t *mcast;
wpa_suite_ucast_t *ucast;
wpa_suite_auth_key_mgmt_t *akm;
uint8 *capabilities;
} rsn_parse_info_t;
static int wl_rsn_ie_parse_info(uint8* buf, uint len, rsn_parse_info_t *rsn);
static uint wl_rsn_ie_decode_cntrs(uint cntr_field);
static int wl_parse_assoc_params(char **argv, wl_assoc_params_t *params, bool *prescanned);
static int wl_join_prescanned(void *wl, wl_join_params_t *join_params, uint *join_params_size);
#define wl_parse_reassoc_params(argv, params) wl_parse_assoc_params(argv, \
(wl_assoc_params_t *)(params), NULL)
static int wl_parse_channel_list(char* list_str, uint16* channel_list, int channel_num);
static int wl_parse_chanspec_list(char* list_str, chanspec_t *chanspec_list, int chanspec_num);
static chanspec_t wl_chspec_to_driver(chanspec_t chanspec);
static uint32 wl_chspec32_to_driver(chanspec_t chanspec);
static chanspec_t wl_chspec_from_driver(chanspec_t chanspec);
static chanspec_t wl_chspec32_from_driver(uint32 chanspec);
#ifdef EXTENDED_SCAN
static int wl_parse_extdchannel_list(char* list_str,
chan_scandata_t* channel_list, int channel_num);
#endif
static uint16 wl_qdbm_to_mw(uint8 qdbm);
static uint8 wl_mw_to_qdbm(uint16 mw);
static int wl_cfg_option(char **argv, const char *fn_name, int *bsscfg_idx, int *consumed);
static int get_oui_bytes(uchar *oui_str, uchar *oui);
static int get_ie_data(uchar *data_str, uchar *ie_data, int len);
static void wl_printrate(int val);
static int rate_string2int(char *s);
static char *rate_int2string(char *buf, int val);
static int wl_get_iscan(void *wl, char *buf, uint buf_len);
int wlu_var_getbuf(void *wl, const char *iovar, void *param, int param_len, void **bufptr);
int wlu_var_getbuf_sm(void *wl, const char *iovar, void *param, int param_len, void **bufptr);
int wlu_var_getbuf_med(void *wl, const char *iovar, void *param, int param_len, void **bufptr);
int wlu_var_setbuf(void *wl, const char *iovar, void *param, int param_len);
int wlu_iovar_get(void *wl, const char *iovar, void *outbuf, int len);
int wlu_iovar_set(void *wl, const char *iovar, void *param, int paramlen);
int wlu_iovar_getint(void *wl, const char *iovar, int *pval);
int wlu_iovar_setint(void *wl, const char *iovar, int val);
static int wl_bssiovar_mkbuf(const char *iovar, int bssidx, void *param,
int paramlen, void *bufptr, int buflen, int *perr);
int wlu_bssiovar_setbuf(void* wl, const char *iovar, int bssidx,
void *param, int paramlen, void *bufptr, int buflen);
static int wl_bssiovar_getbuf(void* wl, const char *iovar, int bssidx,
void *param, int paramlen, void *bufptr, int buflen);
static int wl_bssiovar_set(void *wl, const char *iovar, int bssidx, void *param, int paramlen);
int wlu_bssiovar_get(void *wl, const char *iovar, int bssidx, void *outbuf, int len);
static int wl_bssiovar_setint(void *wl, const char *iovar, int bssidx, int val);
static int wl_bssiovar_getint(void *wl, const char *iovar, int bssidx, int *pval);
static int wl_vndr_ie(void *wl, const char *command, uint32 pktflag_ok, char **argv);
static void wl_dump_ie_buf(vndr_ie_buf_t *ie_getbuf);
static int hexstrtobitvec(const char *cp, uchar *bitvec, int veclen);
static void wl_join_pref_print_ie(bcm_tlv_t *ie);
static void wl_join_pref_print_akm(uint8* suite);
static void wl_join_pref_print_cipher_suite(uint8* suite);
static void wl_print_tspec(tspec_arg_t *ts);
static void wl_cac_addts_usage(void);
static void wl_cac_delts_usage(void);
static cmd_func_t wl_txmcsset;
static cmd_func_t wl_rxmcsset;
static int wl_mimo_stf(void *wl, cmd_t *cmd, char **argv);
#ifdef WLEXTLOG
static int wl_extlog(void *wl, cmd_t *cmd, char **argv);
static int wl_extlog_cfg(void *wl, cmd_t *cmd, char **argv);
#endif
static int wl_assertlog(void *wl, cmd_t *cmd, char **argv);
static int wl_tsf(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_config(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_sha256(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_sa_query(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_disassoc(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_deauth(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_assoc(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_auth(void *wl, cmd_t *cmd, char **argv);
static int wl_mfp_reassoc(void *wl, cmd_t *cmd, char **argv);
static cmd_func_t wl_scb_bs_data;
static int wl_bssload_static(void *wl, cmd_t *cmd, char **argv);
#ifdef EVENT_LOG_COMPILE
static int wl_event_log_set_init(void *wl, cmd_t *cmd, char **argv);
static int wl_event_log_set_expand(void *wl, cmd_t *cmd, char **argv);
static int wl_event_log_set_shrink(void *wl, cmd_t *cmd, char **argv);
static int wl_event_log_tag_control(void *wl, cmd_t *cmd, char **argv);
#endif
static int wl_sleep_ret_ext(void *wl, cmd_t *cmd, char **argv);
static char *ver2str(unsigned int vms, unsigned int vls);
/* some OSes (FC4) have trouble allocating (kmalloc) 128KB worth of memory,
* hence keeping WL_DUMP_BUF_LEN below that
*/
#if defined(BWL_SMALL_WLU_DUMP_BUF)
#define WL_DUMP_BUF_LEN (4 * 1024)
#else
#define WL_DUMP_BUF_LEN (127 * 1024)
#endif
#define CMDLINESZ 80
#define OUI_STR_SIZE 8 /* OUI string size */
#define MAX_OUI_SIZE 3 /* MAX OUI size */
#define MAX_BYTE_CHARS 2 /* MAX num chars */
#define MAX_DATA_COLS 16 /* MAX data cols */
#define DIV_QUO(num, div) ((num)/div) /* Return the quotient of division to avoid floats */
#define DIV_REM(num, div) (((num%div) * 100)/div) /* Return the remainder of division */
#define RADIO_CORE_SYN (0x0 << 12)
#define RADIO_CORE_TX0 (0x2 << 12)
#define RADIO_CORE_TX1 (0x3 << 12)
#define RADIO_CORE_RX0 (0x6 << 12)
#define RADIO_CORE_RX1 (0x7 << 12)
#define RADIO_CORE_CR0 (0x0 << 10)
#define RADIO_CORE_CR1 (0x1 << 10)
#define RADIO_CORE_CR2 (0x2 << 10)
#define RADIO_CORE_ALL (0x3 << 10)
#define RADIO_2069_CORE_CR0 (0x0 << 9)
#define RADIO_2069_CORE_CR1 (0x1 << 9)
#define RADIO_2069_CORE_CR2 (0x2 << 9)
#define RADIO_2069_CORE_ALL (0x3 << 9)
#define RADIO_2069_CORE_PLL (0x4 << 9)
/* IOCtl version read from targeted driver */
static int ioctl_version;
/* dword align allocation */
static union {
char bufdata[WLC_IOCTL_MAXLEN];
uint32 alignme;
} bufstruct_wlu;
static char *buf = (char*) &bufstruct_wlu.bufdata;
/* integer output format, default to signed integer */
static uint8 int_fmt;
/*
* Country names and abbreviations from ISO 3166
*/
typedef struct {
const char *name; /* Long name */
const char *abbrev; /* Abbreviation */
} cntry_name_t;
cntry_name_t cntry_names[]; /* At end of this file */
typedef struct {
uint value;
const char *string;
} dbg_msg_t;
typedef struct {
uint value;
const char *string;
} phy_msg_t;
typedef struct {
uint value;
const char *string;
} monitor_promisc_level_msg_t;
#define WL_SCAN_PARAMS_SSID_MAX 10
#define SCAN_USAGE "" \
"\tDefault to an active scan across all channels for any SSID.\n" \
"\tOptional arg: SSIDs, list of [up to 10] SSIDs to scan (comma or space separated).\n" \
"\tOptions:\n" \
"\t-s S, --ssid=S\t\tSSIDs to scan\n" \
"\t-t ST, --scan_type=ST\t[active|passive|prohibit|offchan|hotspot] scan type\n" \
"\t--bss_type=BT\t\t[bss/infra|ibss/adhoc] bss type to scan\n" \
"\t-b MAC, --bssid=MAC\tparticular BSSID MAC address to scan, xx:xx:xx:xx:xx:xx\n" \
"\t-n N, --nprobes=N\tnumber of probes per scanned channel\n" \
"\t-a N, --active=N\tdwell time per channel for active scanning\n" \
"\t-p N, --passive=N\tdwell time per channel for passive scanning\n" \
"\t-h N, --home=N\t\tdwell time for the home channel between channel scans\n" \
"\t-c L, --channels=L\tcomma or space separated list of channels to scan" \
#define RATE_2G_USAGE \
"\tEither \"auto\", or a simple CCK/DSSS/OFDM rate value:\n" \
"\t1 2 5.5 11 6 9 12 18 24 36 48 54\n\n" \
"\tOr options to specify legacy, HT, or VHT rate:\n" \
"\t-r R, --rate=R : legacy rate (CCK, DSSS, OFDM)\n" \
"\t-h M, --ht=M : HT MCS index [0-23]\n" \
"\t-v M[xS], --vht=M[xS] : VHT MCS index M [0-9],\n" \
"\t : and optionally Nss S [1-8], eg. 5x2 is MCS=5, Nss=2\n" \
"\t-c cM[sS] : VHT (c notation) MCS index M [0-9],\n" \
"\t : and optionally Nss S [1-8], eg. c5s2 is MCS=5, Nss=2\n" \
"\t-s S, --ss=S : VHT Nss [1-8], number of spatial streams, default 1.\n" \
"\t : Only used with -v/--vht when MxS format is not used\n" \
"\t-x T, --exp=T : Tx Expansion, number of tx chains (NTx) beyond the minimum\n" \
"\t : required for the space-time-streams, exp = NTx - Nsts\n" \
"\t--stbc : Use STBC expansion, otherwise no STBC\n" \
"\t-l, --ldpc : Use LDPC encoding, otherwise no LDPC\n" \
"\t-g, --sgi : SGI, Short Guard Interval, otherwise standard GI\n" \
"\t-b, --bandwidth : transmit bandwidth MHz; 20, 40, 80"
#define RATE_5G_USAGE \
"\tEither \"auto\", or a simple OFDM rate value:\n" \
"\t6 9 12 18 24 36 48 54\n\n" \
"\tOr options to specify legacy OFDM, HT, or VHT rate:\n" \
"\t-r R, --rate=R : legacy OFDM rate\n" \
"\t-h M, --ht=M : HT MCS index [0-23]\n" \
"\t-v M[xS], --vht=M[xS] : VHT MCS index M [0-9],\n" \
"\t : and optionally Nss S [1-8], eg. 5x2 is MCS=5, Nss=2\n" \
"\t-c cM[sS] : VHT (c notation) MCS index M [0-9],\n" \
"\t : and optionally Nss S [1-8], eg. c5s2 is MCS=5, Nss=2\n" \
"\t-s S, --ss=S : VHT Nss [1-8], number of spatial streams, default 1.\n" \
"\t : Only used with -v/--vht when MxS format is not used\n" \
"\t-x T, --exp=T : Tx Expansion, number of tx chains (NTx) beyond the minimum\n" \
"\t : required for the space-time-streams, exp = NTx - Nsts\n" \
"\t--stbc : Use STBC expansion, otherwise no STBC\n" \
"\t-l, --ldpc : Use LDPC encoding, otherwise no LDPC\n" \
"\t-g, --sgi : SGI, Short Guard Interval, otherwise standard GI\n" \
"\t-b, --bandwidth : transmit bandwidth MHz; 20, 40, 80"
#define MONITOR_PROMISC_LEVEL_USAGE \
"\tUsage: wl monitor_promisc_level [<bitmap> | <+|-name>]\n" \
"\tbitmap values and corresponding name are the following:\n" \
"\tArgs:\n" \
"\t\tbit:0:promisc: " \
"When set, address filter accepts all received frames." \
"When cleared, the address filter accepts only those frames " \
"that match the BSSID or local MAC address\n" \
"\t\tbit:1:ctrl: " \
"When set, the RX filter accepts all received control frames " \
"that are accepted by the address filter. " \
"When cleared, the RX filter rejects all control frames other " \
"than PS poll frames." \
"\t\tbit:3:fcs: " \
"When set, the RX filter forwards received frames with FCS " \
"errors to the driver." \
"When cleared, frames with FCS errors are discarded.\n\n" \
"\tExample: wl monitor_promisc_level +promisc\n" \
"\tExample: wl monitor_promisc_level 0x2\n" \
"\tExample: wl monitor_promisc_level 0"
#define WDS_TYPE_USAGE \
"\tUsage: wl wds_type -i <ifname>\n" \
"\tifname is the name of the interface to query the type.\n" \
"\tReturn values:\n" \
"\t\t0:The interface type is neither WDS nor DWDS.\n" \
"\t\t1:The interface is WDS type.\n" \
"\t\t2:The interface is DWDS type.\n"
/* the default behavior is batching in driver,
* to indicate client batching, users should specify --interactive and --clientbatch
*/
static bool batch_in_client;
/* If the new command needs to be part of 'wc.exe' tool used for WMM,
* be sure to modify wc_cmds[] array as well
*
* If you add a command, please update wlu_cmd.c cmd2cat to categorize the command.
*/
cmd_t wl_cmds[] = {
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "ver", wl_version, -1, -1,
"get version information" },
{ "cmds", wl_list, -1, -1,
"generate a short list of available commands"},
#endif
{ "up", wl_void, -1, WLC_UP,
"reinitialize and mark adapter up (operational)" },
{ "down", wl_void, -1, WLC_DOWN,
"reset and mark adapter down (disabled)" },
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "out", wl_out, -1, WLC_OUT,
"mark adapter down but do not reset hardware(disabled)\n"
"\tOn dualband cards, cards must be bandlocked before use."},
{ "clk", wl_int, WLC_GET_CLK, WLC_SET_CLK,
"set board clock state. return error for set_clk attempt if the driver is not down\n"
"\t0: clock off\n"
"\t1: clock on" },
{ "restart", wl_void, -1, WLC_RESTART,
"Restart driver. Driver must already be down."},
{ "reboot", wl_void, -1, WLC_REBOOT,
"Reboot platform"},
{ "radio", wl_radio, WLC_GET_RADIO, WLC_SET_RADIO,
"Set the radio on or off.\n"
"\t\"on\" or \"off\"" },
{ "dump", wlu_dump, WLC_GET_VAR, -1,
"Give suboption \"list\" to list various suboptions" },
{ "ol_stats", wlu_offloads_stats, WLC_GET_VAR, -1,
"Give suboption \"list\" to list various suboptions" },
{ "ol_eventlog", wlu_offloads_stats, WLC_GET_VAR, -1,
"Give suboption \"list\" to list various suboptions" },
{ "ol_cons", wlu_offloads_stats, WLC_GET_VAR, WLC_SET_VAR,
"Display the ARM console or issue a command to the ARM console\n"
" Usage: ol_cons [<cmd>]\n"
"\t\"?\" - Display the list of active console commands"
},
{ "ol_wowl_cons", wlu_offloads_stats, WLC_GET_VAR, -1,
"Give suboption \"list\" to list various suboptions" },
{ "ol_clr", wlu_offloads_stats, WLC_GET_VAR, -1,
"Give suboption \"list\" to list various suboptions" },
{ "srclear", wlu_srwrite, -1, WLC_SET_SROM,
"Clears first 'len' bytes of the srom, len in decimal or hex\n"
"\tUsage: srclear <len>" },
{ "srdump", wlu_srdump, WLC_GET_SROM, -1,
"print contents of SPROM to stdout" },
{ "srwrite", wlu_srwrite, -1, WLC_SET_SROM,
"Write the srom: srwrite byteoffset value" },
{ "srcrc", wlu_srwrite, WLC_GET_SROM, -1,
"Get the CRC for input binary file" },
{ "ciswrite", wlu_ciswrite, -1, WLC_SET_VAR,
"Write specified <file> to the SDIO CIS source (either SROM or OTP)"},
{ "cisupdate", wlu_cisupdate, -1, WLC_SET_VAR,
"Write a hex byte stream to specified byte offset to the CIS source (either SROM or OTP)\n"
"--preview option allows you to review the update without committing it\n"
"\t<byte offset> <hex byte stream> [--preview]" },
{ "cisdump", wlu_cisdump, WLC_GET_VAR, -1,
"Display the content of the SDIO CIS source\n"
"\t-b <file> -- also write raw bytes to <file>\n"
"\t<len> -- optional count of bytes to display (must be even)"},
{ "cis_source", wl_varint, WLC_GET_VAR, -1,
"Display which source is used for the SDIO CIS"},
{ "cisconvert", wlu_srvar, -1, -1,
"Print CIS tuple for given name=value pair" },
{ "rdvar", wlu_srvar, WLC_GET_SROM, -1,
"Read a named variable to the srom" },
{ "wrvar", wlu_srvar, WLC_GET_SROM, WLC_SET_SROM,
"Write a named variable to the srom" },
{ "nvram_source", wl_nvsource, WLC_GET_VAR, -1,
"Display which source is used for nvram"},
{ "nvram_dump", wl_nvdump, WLC_NVRAM_DUMP, -1,
"print nvram variables to stdout" },
{ "nvset", wl_nvset, -1, WLC_NVRAM_SET,
"set an nvram variable\n"
"\tname=value (no spaces around \'=\')" },
{ "nvget", wl_nvget, WLC_NVRAM_GET, -1,
"get the value of an nvram variable" },
{ "nvram_get", wl_nvget, WLC_NVRAM_GET, -1,
"get the value of an nvram variable" },
{ "revinfo", wl_revinfo, WLC_GET_REVINFO, -1,
"get hardware revision information" },
{ "customvar1", wl_var_getinthex, -1, -1,
"print the value of customvar1 in hex format" },
{ "msglevel", wl_msglevel, WLC_GET_VAR, WLC_SET_VAR,
"set driver console debugging message bitvector\n"
"\ttype \'wl msglevel ?\' for values" },
{ "phymsglevel", wl_phymsglevel, WLC_GET_VAR, WLC_SET_VAR,
"set phy debugging message bitvector\n"
"\ttype \'wl phymsglevel ?\' for values" },
{ "PM", wl_int, WLC_GET_PM, WLC_SET_PM,
"set driver power management mode:\n"
"\t0: CAM (constantly awake)\n"
"\t1: PS (power-save)\n"
"\t2: FAST PS mode" },
{ "wake", wl_int, WLC_GET_WAKE, WLC_SET_WAKE,
"set driver power-save mode sleep state:\n"
"\t0: core-managed\n"
"\t1: awake" },
{ "promisc", wl_int, WLC_GET_PROMISC, WLC_SET_PROMISC,
"set promiscuous mode ethernet address reception\n"
"\t0 - disable\n"
"\t1 - enable" },
{ "monitor", wl_int, WLC_GET_MONITOR, WLC_SET_MONITOR,
"set monitor mode\n"
"\t0 - disable\n"
"\t1 - enable active monitor mode (interface still operates)" },
{ "frag", wl_print_deprecate, -1, -1, "Deprecated. Use fragthresh." },
{ "rts", wl_print_deprecate, -1, -1, "Deprecated. Use rtsthresh." },
{ "cwmin", wl_int, WLC_GET_CWMIN, WLC_SET_CWMIN,
"Set the cwmin. (integer [1, 255])" },
{ "cwmax", wl_int, WLC_GET_CWMAX, WLC_SET_CWMAX,
"Set the cwmax. (integer [256, 2047])" },
{ "srl", wl_int, WLC_GET_SRL, WLC_SET_SRL,
"Set the short retry limit. (integer [1, 255])" },
{ "lrl", wl_int, WLC_GET_LRL, WLC_SET_LRL,
"Set the long retry limit. (integer [1, 255])" },
{ "rate", wl_rate_mrate, WLC_GET_RATE, -1,
"force a fixed rate:\n"
"\tvalid values for 802.11a are (6, 9, 12, 18, 24, 36, 48, 54)\n"
"\tvalid values for 802.11b are (1, 2, 5.5, 11)\n"
"\tvalid values for 802.11g are (1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "mrate", wl_rate_mrate, -1, -1, /* Deprecated. Use "bg_mrate" or "a_mrate" */
"force a fixed multicast rate:\n"
"\tvalid values for 802.11a are (6, 9, 12, 18, 24, 36, 48, 54)\n"
"\tvalid values for 802.11b are (1, 2, 5.5, 11)\n"
"\tvalid values for 802.11g are (1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "a_rate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR,
"force a fixed rate for the A PHY:\n"
"\tvalid values for 802.11a are (6, 9, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "a_mrate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR,
"force a fixed multicast rate for the A PHY:\n"
"\tvalid values for 802.11a are (6, 9, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "bg_rate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR,
"force a fixed rate for the B/G PHY:\n"
"\tvalid values for 802.11b are (1, 2, 5.5, 11)\n"
"\tvalid values for 802.11g are (1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "bg_mrate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR,
"force a fixed multicast rate for the B/G PHY:\n"
"\tvalid values for 802.11b are (1, 2, 5.5, 11)\n"
"\tvalid values for 802.11g are (1, 2, 5.5, 6, 9, 11, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate" },
{ "2g_rate", wl_rate, WLC_GET_VAR, WLC_SET_VAR,
"Force a fixed rate for data frames in the 2.4G band:\n\n"
RATE_2G_USAGE
},
{ "2g_mrate", wl_rate, WLC_GET_VAR, WLC_SET_VAR,
"Force a fixed rate for mulitcast/broadcast data frames in the 2.4G band:\n\n"
RATE_2G_USAGE
},
{ "5g_rate", wl_rate, WLC_GET_VAR, WLC_SET_VAR,
"Force a fixed rate for data frames in the 5G band:\n\n"
RATE_5G_USAGE
},
{ "5g_mrate", wl_rate, WLC_GET_VAR, WLC_SET_VAR,
"Force a fixed rate for mulitcast/broadcast data frames in the 5G band:\n\n"
RATE_5G_USAGE
},
{ "infra", wl_int, WLC_GET_INFRA, WLC_SET_INFRA,
"Set Infrastructure mode: 0 (IBSS) or 1 (Infra BSS)" },
{ "ap", wl_int, WLC_GET_AP, WLC_SET_AP,
"Set AP mode: 0 (STA) or 1 (AP)" },
{ "bssid", wl_bssid, WLC_GET_BSSID, -1,
"Get the BSSID value, error if STA and not associated"},
{ "bssmax", wl_bss_max, WLC_GET_VAR, -1,
"get number of BSSes " },
{ "bw_cap", wl_bw_cap, WLC_GET_VAR, WLC_SET_VAR,
"Get/set the per-band bandwidth.\n"
"Usage: wl bw_cap <2g|5g> [<cap>]\n"
"\t2g|5g - Band: 2.4GHz or 5GHz respectively\n"
"cap:\n"
"\t0x1 - 20MHz\n"
"\t0x3 - 20/40MHz\n"
"\t0x7 - 20/40/80MHz\n"
"\t0xff - Unrestricted" },
#endif
{ "channel", wl_channel, WLC_GET_CHANNEL, WLC_SET_CHANNEL,
"Set the channel:\n"
"\tvalid channels for 802.11b/g (2.4GHz band) are 1 through 14\n"
"\tvalid channels for 802.11a (5 GHz band) are:\n"
"\t\t36, 40, 44, 48, 52, 56, 60, 64,\n"
"\t\t100, 104, 108, 112, 116,120, 124, 128, 132, 136, 140,\n"
"\t\t149, 153, 157, 161,\n"
"\t\t184, 188, 192, 196, 200, 204, 208, 212, 216"},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "cur_mcsset", wl_cur_mcsset, WLC_GET_VAR, -1,
"Get the current mcs set"
},
{ "clmver", wl_var_getandprintstr, WLC_GET_VAR, -1,
"Get version information for CLM data and tools"},
{ "roam_channels_in_cache", wl_dump_chanspecs, WLC_GET_VAR, -1,
"Get a list of channels in roam cache" },
{ "roam_channels_in_hotlist", wl_dump_chanspecs, WLC_GET_VAR, -1,
"Get a list of channels in roam hot channel list" },
{ "chanspecs", wl_dump_chanspecs, WLC_GET_VAR, -1,
"Get all the valid chanspecs (default: all within current locale):\n"
"\t-b band (5(a) or 2(b/g))\n"
"\t-w bandwidth, 20, 40 or 80\n"
"\t[-c country_abbrev]"
},
{ "chanspecs_defset", wl_dump_chanspecs_defset, WLC_GET_VAR, -1,
"Get default chanspecs for current driver settings (default: all within current locale)"
},
{ "chanspec", wl_chanspec, WLC_GET_VAR, WLC_SET_VAR,
"Set current or configured channel:\n"
"\t20MHz : [2g|5g]<channel>[/20]\n"
"\t40MHz : [2g|5g]<channel>/40[u,l]\n"
"\t80MHz : [5g]<channel>/80\n"
"\toptional band 2g or 5g, default to 2g if channel <= 14\n"
"\tchannel number (0-200)\n"
"\tbandwidth, 20, 40, or 80, default 20\n"
"\tprimary sideband for 40MHz on 2g, l=lower, u=upper\n"
"OR Set channel with legacy format:\n"
"\t-c channel number (0-224)\n"
"\t-b band (5(a) or 2(b/g))\n"
"\t-w bandwidth 20 or 40\n"
"\t-s ctl sideband, -1=lower, 0=none, 1=upper"},
{ "rclass", wl_rclass, WLC_GET_VAR, -1,
"Get operation class:\n"
"\t chanspec \n"},
{ "dfs_channel_forced", wl_chanspec, WLC_GET_VAR, WLC_SET_VAR,
"Set <channel>[a,b][n][u,l]\n"
"\tchannel number (0-224)\n"
"\tband a=5G, b=2G, default to 2G if channel <= 14\n"
"\tbandwidth, n=10, non for 20 & 40\n"
"\tctl sideband, l=lower, u=upper"},
{ "tssi", wl_tssi, WLC_GET_TSSI, -1,
"Get the tssi value from radio" },
{ "txpwr", wl_txpwr, -1, -1, /* Deprecated. Use "txpwr1" */
"Set tx power in milliwatts. Range [1, 84]." },
{ "txpwr1", wl_txpwr1, WLC_GET_VAR, WLC_SET_VAR,
"Set tx power in in various units. Choose one of (default: dbm): \n"
"\t-d dbm units\n"
"\t-q quarter dbm units\n"
"\t-m milliwatt units\n"
"Can be combined with:\n"
"\t-o turn on override to disable regulatory and other limitations\n"
"Use wl txpwr -1 to restore defaults"},
{ "txpathpwr", wl_int, WLC_GET_TX_PATH_PWR, WLC_SET_TX_PATH_PWR,
"Turn the tx path power on or off on 2050 radios" },
{ "txpwrlimit", wl_get_txpwr_limit, WLC_CURRENT_PWR, -1,
"Return current tx power limit" },
{ "powerindex", wl_int, WLC_GET_PWRIDX, WLC_SET_PWRIDX,
"Set the transmit power for A band(0-63).\n"
"\t-1 - default value" },
{ "atten", wl_atten, WLC_GET_ATTEN, WLC_SET_ATTEN,
"Set the transmit attenuation for B band. Args: bb radio txctl1.\n"
"\tauto to revert to automatic control\n"
"\tmanual to supspend automatic control" },
{ "phyreg", wl_reg, WLC_GET_PHYREG, WLC_SET_PHYREG,
"Get/Set a phy register:\n"
"\toffset [ value ] [ band ]" },
{ "radioreg", wl_reg, WLC_GET_RADIOREG, WLC_SET_RADIOREG,
"Get/Set a radio register:\n"
"\toffset [ value ] [ band/core ]\n"
"HTPHY:\n"
"\tGet a radio register: wl radioreg [ offset ] [ cr0/cr1/cr2 ]\n"
"\tSet a radio register: wl radioreg [ offset ] [ value ] [ cr0/cr1/cr2/all ]\n"
"ACPHY:\n"
"\tGet a radio register: wl radioreg [ offset ] [ cr0/cr1/cr2/pll ]\n"
"\tSet a radio register: wl radioreg [ offset ] [ value ] [ cr0/cr1/cr2/pll/all ]"},
{ "ucflags", wl_reg, WLC_GET_UCFLAGS, WLC_SET_UCFLAGS,
"Get/Set ucode flags 1, 2, 3(16 bits each)\n"
"\toffset [ value ] [ band ]" },
{ "shmem", wl_reg, WLC_GET_SHMEM, WLC_SET_SHMEM,
"Get/Set a shared memory location:\n"
"\toffset [ value ] [band ]" },
{ "macreg", wl_macreg, WLC_R_REG, WLC_W_REG,
"Get/Set any mac registers(include IHR and SB):\n"
"\tmacreg offset size[2,4] [ value ] [ band ]" },
{ "ucantdiv", wl_int, WLC_GET_UCANTDIV, WLC_SET_UCANTDIV,
"Enable/disable ucode antenna diversity (1/0 or on/off)" },
{ "gpioout", wl_gpioout, -1, -1,
"Set any GPIO pins to any value. Use with caution as GPIOs would be "
"assigned to chipcommon\n"
"\tUsage: gpiomask gpioval"},
{ "devpath", wl_devpath, WLC_GET_VAR, -1,
"print device path" },
{ "pcieserdesreg", wlu_reg3args, WLC_GET_VAR, WLC_SET_VAR,
"g/set SERDES registers: dev offset [val]"},
{ "ampdu_activate_test", wl_ampdu_activate_test, -1, WLC_SET_VAR,
"actiate" },
/* nphy parameter setting is internal only for now */
{ "ampdu_tid", wl_ampdu_tid, WLC_GET_VAR, WLC_SET_VAR,
"enable/disable per-tid ampdu; usage: wl ampdu_tid <tid> [0/1]" },
{ "ampdu_retry_limit_tid", wl_ampdu_retry_limit_tid, WLC_GET_VAR, WLC_SET_VAR,
"Set per-tid ampdu retry limit; usage: wl ampdu_retry_limit_tid <tid> [0~31]" },
{ "ampdu_rr_retry_limit_tid", wl_ampdu_rr_retry_limit_tid, WLC_GET_VAR, WLC_SET_VAR,
"Set per-tid ampdu regular rate retry limit; usage: "
"wl ampdu_rr_retry_limit_tid <tid> [0~31]" },
{ "ampdu_send_addba", wl_ampdu_send_addba, WLC_GET_VAR, WLC_SET_VAR,
"send addba to specified ea-tid; usage: wl ampdu_send_addba <tid> <ea>" },
{ "ampdu_send_delba", wl_ampdu_send_delba, WLC_GET_VAR, WLC_SET_VAR,
"send delba to specified ea-tid; usage: wl ampdu_send_delba <tid> <ea>" },
{ "ampdu_clear_dump", wl_var_void, -1, WLC_SET_VAR,
"clear ampdu counters"},
{ "ampdu_txq_prof_start", wl_var_void, -1, WLC_SET_VAR,
"start sample txq profiling data"},
{ "ampdu_txq_prof_dump", wl_var_void, -1, WLC_SET_VAR,
"show txq histogram"},
{ "ampdu_txq_ss", wl_var_void, -1, WLC_SET_VAR,
"take txq snapshot"},
{ "dpt_deny", wl_dpt_deny, WLC_GET_VAR, WLC_SET_VAR,
"adds/removes ea to dpt deny list\n"
"\tusage: wl dpt_deny <add,remove> <ea>" },
{ "dpt_endpoint", wl_dpt_endpoint, WLC_GET_VAR, WLC_SET_VAR,
"creates/updates/deletes dpt endpoint for ea\n"
"\tusage: wl dpt_endpoint <create, update, delete> <ea>" },
{ "dpt_pmk", wl_dpt_pmk, -1, WLC_SET_VAR,
"sets DPT pre-shared key" },
{ "dpt_fname", wl_dpt_fname, WLC_GET_VAR, WLC_SET_VAR,
"sets/gets DPT friendly name" },
{ "dpt_list", wl_dpt_list, WLC_GET_VAR, -1,
"gets status of all dpt peers" },
#ifdef WLBTAMP
{ "HCI_cmd", wl_HCI_cmd, WLC_GET_VAR, WLC_SET_VAR,
"carries HCI commands to the driver\n"
"\tusage: wl HCI_cmd <command> <args>" },
{ "HCI_ACL_data", wl_HCI_ACL_data, WLC_GET_VAR, WLC_SET_VAR,
"carries HCI ACL data packet to the driver\n"
"\tusage: wl HCI_ACL_data <logical link handle> <data>" },
{ "btamp_statelog", wl_get_btamp_log, WLC_GET_VAR, WLC_SET_VAR,
"Return state transistion log of BTAMP" },
#endif /* WLBTAMP */
{ "actframe", wl_actframe, -1, WLC_SET_VAR,
"Send a Vendor specific Action frame to a channel\n"
"\tusage: wl actframe <Dest Mac Addr> <data> channel dwell-time <BSSID>" },
{ "antdiv", wl_int, WLC_GET_ANTDIV, WLC_SET_ANTDIV,
"Set antenna diversity for rx\n"
"\t0 - force use of antenna 0\n"
"\t1 - force use of antenna 1\n"
"\t3 - automatic selection of antenna diversity" },
{ "txant", wl_int, WLC_GET_TXANT, WLC_SET_TXANT,
"Set the transmit antenna\n"
"\t0 - force use of antenna 0\n"
"\t1 - force use of antenna 1\n"
"\t3 - use the RX antenna selection that was in force during\n"
"\t the most recently received good PLCP header" },
{ "plcphdr", wl_plcphdr, WLC_GET_PLCPHDR, WLC_SET_PLCPHDR,
"Set the plcp header.\n"
"\t\"long\" or \"auto\" or \"debug\"" },
{ "phytype", wl_int, WLC_GET_PHYTYPE, -1,
"Get phy type" },
{ "rateparam", wl_rateparam, -1, WLC_SET_RATE_PARAMS,
"set driver rate selection tunables\n"
"\targ 1: tunable id\n"
"\targ 2: tunable value" },
{ "wepstatus", wl_wepstatus, -1, -1, /* Deprecated. Use "wsec" */
"Set or Get WEP status\n"
"\twepstatus [on|off]" },
{ "primary_key", wl_primary_key, WLC_GET_KEY_PRIMARY, WLC_SET_KEY_PRIMARY,
"Set or get index of primary key" },
{ "addwep", wl_addwep, -1, WLC_SET_KEY,
"Set an encryption key. The key must be 5, 13 or 16 bytes long, or\n"
"\t10, 26, 32, or 64 hex digits long. The encryption algorithm is\n"
"\tautomatically selected based on the key size. keytype is accepted\n"
"\tonly when key length is 16 bytes/32 hex digits and specifies\n"
"\twhether AES-OCB or AES-CCM encryption is used. Default is ccm.\n"
"\tWAPI is selected if key len is 32 and arguments contain wapi.\n"
"\taddwep <keyindex> <keydata> [ocb | ccm | wapi] [notx] [xx:xx:xx:xx:xx:xx]" },
{ "rmwep", wl_rmwep, -1, WLC_SET_KEY,
"Remove the encryption key at the specified key index." },
{ "keys", wl_keys, WLC_GET_KEY, -1,
"Prints a list of the current WEP keys" },
{ "tsc", wl_tsc, WLC_GET_KEY_SEQ, -1,
"Print Tx Sequence Couter for key at specified key index." },
{ "wsec_test", wl_wsec_test, -1, WLC_SET_WSEC_TEST,
"Generate wsec errors\n"
"\twsec_test <test_type> <keyindex|xx:xx:xx:xx:xx:xx>\n"
"\ttype \'wl wsec_test ?\' for test_types" },
{ "tkip_countermeasures", wl_int, -1, WLC_TKIP_COUNTERMEASURES,
"Enable or disable TKIP countermeasures (TKIP-enabled AP only)\n"
"\t0 - disable\n"
"\t1 - enable" },
{ "wsec_restrict", wl_bsscfg_int, WLC_GET_WEP_RESTRICT, WLC_SET_WEP_RESTRICT,
"Drop unencrypted packets if WSEC is enabled\n"
"\t0 - disable\n"
"\t1 - enable" },
{ "eap", wl_int, WLC_GET_EAP_RESTRICT, WLC_SET_EAP_RESTRICT,
"restrict traffic to 802.1X packets until 802.1X authorization succeeds\n"
"\t0 - disable\n"
"\t1 - enable" },
{ "cur_etheraddr", wl_iov_mac, -1, -1,
"Get/set the current hw address" },
{ "perm_etheraddr", wl_iov_mac, -1, -1,
"Get the permanent address from NVRAM" },
{ "authorize", wl_mac, -1, WLC_SCB_AUTHORIZE,
"restrict traffic to 802.1X packets until 802.1X authorization succeeds" },
{ "deauthorize", wl_mac, -1, WLC_SCB_DEAUTHORIZE,
"do not restrict traffic to 802.1X packets until 802.1X authorization succeeds" },
#endif
{ "deauthenticate", wl_deauth_rc, -1, WLC_SCB_DEAUTHENTICATE_FOR_REASON,
"deauthenticate a STA from the AP with optional reason code (AP ONLY)" },
{ "wsec", wl_wsec, WLC_GET_WSEC, WLC_SET_WSEC,
"wireless security bit vector\n"
"\t1 - WEP enabled\n"
"\t2 - TKIP enabled\n"
"\t4 - AES enabled\n"
"\t8 - WSEC in software\n"
"\t0x80 - FIPS enabled\n"
"\t0x100 - WAPI enabled\n"
"\t0x200 - MFP capable\n"
"\t0x400 - MFP required\n"
"\t0x800 - MFP use KDF (SHA256)"
},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "auth", wl_bsscfg_int, WLC_GET_AUTH, WLC_SET_AUTH,
"set/get 802.11 authentication type. 0 = OpenSystem, 1= SharedKey, 3=Open/Shared" },
#endif
{ "wpa_auth", wl_wpa_auth, WLC_GET_WPA_AUTH, WLC_SET_WPA_AUTH,
"Bitvector of WPA authorization modes:\n"
"\t1 WPA-NONE\n"
"\t2 WPA-802.1X/WPA-Professional\n"
"\t4 WPA-PSK/WPA-Personal\n"
#ifdef BCMCCX
"\t8 CCKM (WPA)\n"
#endif /* BCMCCX */
#if defined(BCMCCX)
"\t16 CCKM (WPA2)\n"
#endif /* BCMCCX */
"\t64 WPA2-802.1X/WPA2-Professional\n"
"\t128 WPA2-PSK/WPA2-Personal\n"
"\t0 disable WPA"
},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "wpa_cap", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get 802.11i RSN capabilities" },
{ "set_pmk", wl_set_pmk, -1, WLC_SET_WSEC_PMK,
"Set passphrase for PMK in driver-resident supplicant." },
{ "scan", wl_scan, -1, WLC_SCAN,
"Initiate a scan.\n" SCAN_USAGE
},
{ "roamscan_parms", wl_roamparms, WLC_GET_VAR, WLC_SET_VAR,
"set/get roam scan parameters\n"
"Use standard scan params syntax below,"
"but only active/passive/home times, nprobes, and type are used.\n"
"All other values are silently discarded.\n"
SCAN_USAGE
},
{ "iscan_s", wl_iscan, -1, WLC_SET_VAR,
"Initiate an incremental scan.\n" SCAN_USAGE
},
{ "iscan_c", wl_iscan, -1, WLC_SET_VAR,
"Continue an incremental scan.\n" SCAN_USAGE
},
{ "scancache_clear", wl_var_void, -1, WLC_SET_VAR,
"clear the scan cache"},
{ "escan", wl_escan, -1, WLC_SET_VAR,
"Start an escan.\n" SCAN_USAGE
},
{ "escanabort", wl_escan, -1, WLC_SET_VAR,
"Abort an escan.\n" SCAN_USAGE
},
#ifdef EXTENDED_SCAN
{ "extdscan", wl_extdscan, -1, WLC_SET_VAR,
"Initiate an extended scan.\n"
"\tDefault to an active scan across all channels for any SSID.\n"
"\tOptional args: list of SSIDs to scan.\n"
"\tOptions:\n"
"\t-s S1 S2 S3, --ssid=S1 S2 S3\t\tSSIDs to scan, comma or space separated\n"
"\t-x x, --split_scan=ST\t[split_scan] scan type\n"
"\t-t ST, --scan_type=ST\t[background:0/forcedbackground:1/foreground:2] scan type\n"
"\t-n N, --nprobes=N\tnumber of probes per scanned channel, per SSID\n"
"\t-c L, --channels=L\tcomma or space separated list of channels to scan"},
#endif
{ "passive", wl_int, WLC_GET_PASSIVE_SCAN, WLC_SET_PASSIVE_SCAN,
"Puts scan engine into passive mode" },
{ "regulatory", wl_int, WLC_GET_REGULATORY, WLC_SET_REGULATORY,
"Get/Set regulatory domain mode (802.11d). Driver must be down." },
{ "spect", wl_spect, WLC_GET_SPECT_MANAGMENT, WLC_SET_SPECT_MANAGMENT,
"Get/Set 802.11h Spectrum Management mode.\n"
"\t0 - Off\n"
"\t1 - Loose interpretation of 11h spec - may join non-11h APs\n"
"\t2 - Strict interpretation of 11h spec - may not join non-11h APs\n"
"\t3 - Disable 11h and enable 11d\n"
"\t4 - Loose interpretation of 11h+d spec - may join non-11h APs"
},
{ "scanabort", wl_var_void, -1, WLC_SET_VAR,
"Abort a scan." },
{ "scanresults", wl_dump_networks, WLC_SCAN_RESULTS, -1,
"Return results from last scan." },
{ "iscanresults", wl_dump_networks, WLC_GET_VAR, -1,
"Return results from last iscan. Specify a buflen (max 8188)\n"
"\tto artificially limit the size of the results buffer.\n"
"\tiscanresults [buflen]"},
{ "assoc", wl_status, -1, -1,
"Print information about current network association.\n"
"\t(also known as \"status\")" },
#endif
{ "status", wl_status, -1, -1,
"Print information about current network association.\n"
"\t(also known as \"assoc\")" },
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "disassoc", wl_void, -1, WLC_DISASSOC,
"Disassociate from the current BSS/IBSS." },
{ "chanlist", wl_print_deprecate, WLC_GET_VALID_CHANNELS, -1,
"Deprecated. Use channels." },
{ "channels", wl_dump_chanlist, WLC_GET_VALID_CHANNELS, -1,
"Return valid channels for the current settings." },
{ "channels_in_country", wl_channels_in_country, WLC_GET_CHANNELS_IN_COUNTRY, -1,
"Return valid channels for the country specified.\n"
"\tArg 1 is the country abbreviation\n"
"\tArg 2 is the band(a or b)"},
{ "curpower", wl_get_current_power, WLC_CURRENT_PWR, -1,
"Return current tx power settings.\n"
"\t-v, --verbose: display the power settings for every "
"rate even when every rate in a rate group has the same power." },
{ "txpwr_target_max", wl_get_txpwr_target_max, WLC_GET_VAR, -1,
"Return current max tx target power settings.\n" },
{ "curppr", wl_get_current_txppr, WLC_GET_VAR, -1,
"Return current tx power per rate offset."},
{ "txinstpwr", wl_get_instant_power, WLC_GET_VAR, -1,
"Return tx power based on instant TSSI "},
{ "scansuppress", wl_int, WLC_GET_SCANSUPPRESS, WLC_SET_SCANSUPPRESS,
"Suppress all scans for testing.\n"
"\t0 - allow scans\n"
"\t1 - suppress scans" },
{ "evm", wl_evm, -1, WLC_EVM,
"Start an EVM test on the given channel, or stop EVM test.\n"
"\tArg 1 is channel number 1-14, or \"off\" or 0 to stop the test.\n"
"\tArg 2 is optional rate (1, 2, 5.5 or 11)"},
{ "rateset", wl_rateset, WLC_GET_RATESET, WLC_SET_RATESET,
"Returns or sets the supported and basic rateset, (b) indicates basic\n"
"\tWith no args, returns the rateset. Args are\n"
"\trateset \"default\" | \"all\" | <arbitrary rateset> [-m|-v <list of mcs masks>]\n"
"\t\tdefault - driver defaults\n"
"\t\tall - all rates are basic rates\n"
"\t\tarbitrary rateset - list of rates\n"
"\tList of rates are in Mbps and each rate is optionally followed\n"
"\tby \"(b)\" or \"b\" for a Basic rate. Example: 1(b) 2b 5.5 11\n"
"\tAt least one rate must be Basic for a legal rateset.\n\n"
"\t-m sets HT rates (bitmasks, 00-ff). Least significant bit is MCS0.\n"
"\t example: 'rateset -m 0x3f 0x01' limits rates to MCS0-MCS5 and MCS8\n\n"
"\t-v sets VHT MCS values for each supported count of spatial streams.\n"
"\t example: 'rateset -v 3ff 1ff ff' limits vht rates to MCS 0-9 for 1 stream,\n"
"\t MCS 0-8 for 2 streams, and MCS 0-7 for 3 streams."
},
{ "default_rateset", wl_default_rateset, WLC_GET_VAR, -1,
"Returns supported rateset of given phy.\n"
"\tYou have to insert following Args\n"
"\t\tArg 1. Phy Type: have to be one of the following: "
"[a, b, g, n, lp, ssn, ht, lcn, lcn40, ac]\n"
"\t\tArg 2. Band Type: 2 for 2.4G or 5 for 5G\n"
"\t\tArg 3. CCK Only: 1 for CCK Only or 0 for CCK and OFDM rates\n"
"\t\tArg 4. Basic Rates: 1 for all rates WITH basic rates or "
"0 for all rates WITHOUT basic rates\n"
"\t\tArg 5. MCS Rates: 1 for all rates WITH MCS rates or "
"0 for all rates WITHOUT MCS rates\n"
"\t\tArg 6. Bandwidth: have to be one of the following: [10, 20, 40, 80, 160]\n"
"\t\tArg 7. TX/RX Stream: \"tx\" for TX streams or \"rx\" for RX streams\n"
"\t\tExample: PHY: AC, Band 2.4G, CCK rates only, With Basec rates, "
"WithOut MCS rates, BW: 40 and TX streams\n"
"\t\tInput: default_rateset ac 2 0 1 0 40 tx\n"
},
{ "roam_trigger", wl_band_elm, WLC_GET_ROAM_TRIGGER, WLC_SET_ROAM_TRIGGER,
"Get or Set the roam trigger RSSI threshold:\n"
"\tGet: roam_trigger [a|b]\n"
"\tSet: roam_trigger <integer> [a|b|all]\n"
"\tinteger - 0: default\n"
"\t 1: optimize bandwidth\n"
"\t 2: optimize distance\n"
"\t [-1, -99]: dBm trigger value"},
{ "roam_delta", wl_band_elm, WLC_GET_ROAM_DELTA, WLC_SET_ROAM_DELTA,
"Set the roam candidate qualification delta. roam_delta [integer [, a/b]]" },
{ "roam_scan_period", wl_int, WLC_GET_ROAM_SCAN_PERIOD, WLC_SET_ROAM_SCAN_PERIOD,
"Set the roam candidate qualification delta. (integer)" },
{ "suprates", wl_sup_rateset, WLC_GET_SUP_RATESET_OVERRIDE, WLC_SET_SUP_RATESET_OVERRIDE,
"Returns or sets the 11g override for the supported rateset\n"
"\tWith no args, returns the rateset. Args are a list of rates,\n"
"\tor 0 or -1 to specify an empty rateset to clear the override.\n"
"\tList of rates are in Mbps, example: 1 2 5.5 11"},
{ "scan_channel_time", wl_int, WLC_GET_SCAN_CHANNEL_TIME, WLC_SET_SCAN_CHANNEL_TIME,
"Get/Set scan channel time"},
{ "scan_unassoc_time", wl_int, WLC_GET_SCAN_UNASSOC_TIME, WLC_SET_SCAN_UNASSOC_TIME,
"Get/Set unassociated scan channel dwell time"},
{ "scan_home_time", wl_int, WLC_GET_SCAN_HOME_TIME, WLC_SET_SCAN_HOME_TIME,
"Get/Set scan home channel dwell time"},
{ "scan_passive_time", wl_int, WLC_GET_SCAN_PASSIVE_TIME, WLC_SET_SCAN_PASSIVE_TIME,
"Get/Set passive scan channel dwell time"},
{ "scan_nprobes", wl_int, WLC_GET_SCAN_NPROBES, WLC_SET_SCAN_NPROBES,
"Get/Set scan parameter for number of probes to use per channel scanned"},
{ "prb_resp_timeout", wl_int, WLC_GET_PRB_RESP_TIMEOUT, WLC_SET_PRB_RESP_TIMEOUT,
"Get/Set probe response timeout"},
{ "channel_qa", wl_int, WLC_GET_CHANNEL_QA, -1,
"Get last channel quality measurment"},
{ "channel_qa_start", wl_void, -1, WLC_START_CHANNEL_QA,
"Start a channel quality measurment"},
{ "country", wl_country, WLC_GET_COUNTRY, WLC_SET_COUNTRY,
"Select Country Code for driver operational region\n"
"\tFor simple country setting: wl country <country>\n"
"\tWhere <country> is either a long name or country code from ISO 3166; "
"for example \"Germany\" or \"DE\"\n"
"\n\tFor a specific built-in country definition: "
"wl country <built-in> [<advertised-country>]\n"
"\tWhere <built-in> is a country country code followed by '/' and "
"regulatory revision number.\n"
"\tFor example, \"US/3\".\n"
"\tAnd where <advertised-country> is either a long name or country code from ISO 3166.\n"
"\tIf <advertised-country> is omitted, it will be the same as the built-in country code.\n"
"\n\tUse 'wl country list [band(a or b)]' for the list of supported countries"},
{ "country_ie_override", wl_country_ie_override, WLC_GET_VAR, WLC_SET_VAR,
"To set/get country ie"},
{ "autocountry_default", wl_varstr, WLC_GET_VAR, WLC_SET_VAR,
"Select Country Code for use with Auto Contry Discovery"},
{ "join", wl_join, -1, -1,
"Join a specified network SSID.\n"
"\tUsage: join <ssid> [key <0-3>:xxxxx] [imode bss|ibss] "
"[amode open|shared|openshared|wpa|wpapsk|wpa2|wpa2psk|wpanone|ftpsk] [options]\n"
"\tOptions:\n"
"\t-b MAC, --bssid=MAC \tBSSID (xx:xx:xx:xx:xx:xx) to scan and join\n"
"\t-c CL, --chanspecs=CL \tchanspecs (comma or space separated list)\n"
"\tprescanned \tuses channel and bssid list from scanresults\n"
"\t-p, -passive: force passive assoc scan (useful for P2P)"},
#endif
{ "ssid", wl_ssid, WLC_GET_SSID, WLC_SET_SSID,
"Set or get a configuration's SSID.\n"
"\twl ssid [-C num]|[--cfg=num] [<ssid>]\n"
"\tIf the configuration index 'num' is not given, configuraion #0 is assumed and\n"
"\tsetting will initiate an assoication attempt if in infrastructure mode,\n"
"\tor join/creation of an IBSS if in IBSS mode,\n"
"\tor creation of a BSS if in AP mode."},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
#ifdef BCMCCX
{ "leap", wl_leap, WLC_GET_LEAP_LIST, WLC_SET_LEAP_LIST,
"Set parameters for LEAP authentication\n"
"\tleap <ssid> <username> <password> [domain]" },
#endif /* BCMCCX */
{ "mac", wl_maclist, WLC_GET_MACLIST, WLC_SET_MACLIST,
"Set or get the list of source MAC address matches.\n"
"\twl mac xx:xx:xx:xx:xx:xx [xx:xx:xx:xx:xx:xx ...]\n"
"\tTo Clear the list: wl mac none" },
{ "macmode", wl_int, WLC_GET_MACMODE, WLC_SET_MACMODE,
"Set the mode of the MAC list.\n"
"\t0 - Disable MAC address matching.\n"
"\t1 - Deny association to stations on the MAC list.\n"
"\t2 - Allow association to stations on the MAC list."},
{ "wds", wl_maclist, WLC_GET_WDSLIST, WLC_SET_WDSLIST,
"Set or get the list of WDS member MAC addresses.\n"
"\tSet using a space separated list of MAC addresses.\n"
"\twl wds xx:xx:xx:xx:xx:xx [xx:xx:xx:xx:xx:xx ...]" },
{ "lazywds", wl_int, WLC_GET_LAZYWDS, WLC_SET_LAZYWDS,
"Set or get \"lazy\" WDS mode (dynamically grant WDS membership to anyone)."},
{ "noise", wl_int, WLC_GET_PHY_NOISE, -1,
"Get noise (moving average) right after tx in dBm" },
{ "fqacurcy", wl_int, -1, WLC_FREQ_ACCURACY,
"Manufacturing test: set frequency accuracy mode.\n"
"\tfreqacuracy syntax is: fqacurcy <channel>\n"
"\tArg is channel number 1-14, or 0 to stop the test." },
{ "crsuprs", wl_int, -1, WLC_CARRIER_SUPPRESS,
"Manufacturing test: set carrier suppression mode.\n"
"\tcarriersuprs syntax is: crsuprs <channel>\n"
"\tArg is channel number 1-14, or 0 to stop the test." },
{ "longtrain", wl_int, -1, WLC_LONGTRAIN,
"Manufacturing test: set longtraining mode.\n"
"\tlongtrain syntax is: longtrain <channel>\n"
"\tArg is A band channel number or 0 to stop the test." },
{ "band", wl_band, WLC_GET_BAND, WLC_SET_BAND,
"Returns or sets the current band\n"
"\tauto - auto switch between available bands (default)\n"
"\ta - force use of 802.11a band\n"
"\tb - force use of 802.11b band" },
{ "bands", wl_bandlist, WLC_GET_BANDLIST, -1,
"Return the list of available 802.11 bands" },
{ "phylist", wl_phylist, WLC_GET_PHYLIST, -1,
"Return the list of available phytypes" },
{ "shortslot", wl_int, WLC_GET_SHORTSLOT, -1,
"Get current 11g Short Slot Timing mode. (0=long, 1=short)" },
{ "shortslot_override", wl_int, WLC_GET_SHORTSLOT_OVERRIDE, WLC_SET_SHORTSLOT_OVERRIDE,
"Get/Set 11g Short Slot Timing mode override. (-1=auto, 0=long, 1=short)" },
{ "shortslot_restrict", wl_int, WLC_GET_SHORTSLOT_RESTRICT, WLC_SET_SHORTSLOT_RESTRICT,
"Get/Set AP Restriction on associations for 11g Short Slot Timing capable STAs.\n"
"\t0 - Do not restrict association based on ShortSlot capability\n"
"\t1 - Restrict association to STAs with ShortSlot capability" },
{ "ignore_bcns", wl_int, WLC_GET_IGNORE_BCNS, WLC_SET_IGNORE_BCNS,
"AP only (G mode): Check for beacons without NONERP element"
"(0=Examine beacons, 1=Ignore beacons)" },
{ "pktcnt", wl_get_pktcnt, WLC_GET_PKTCNTS, -1,
"Get the summary of good and bad packets." },
{ "upgrade", wl_upgrade, -1, WLC_UPGRADE,
"Upgrade the firmware on an embedded device" },
{ "gmode", wl_gmode, WLC_GET_GMODE, WLC_SET_GMODE,
"Set the 54g Mode (LegacyB|Auto||GOnly|BDeferred|Performance|LRS)" },
{ "gmode_protection", wl_int, WLC_GET_GMODE_PROTECTION, -1,
"Get G protection mode. (0=disabled, 1=enabled)" },
{ "gmode_protection_control", wl_int, WLC_GET_PROTECTION_CONTROL,
WLC_SET_PROTECTION_CONTROL,
"Get/Set 11g protection mode control alg."
"(0=always off, 1=monitor local association, 2=monitor overlapping BSS)" },
{ "gmode_protection_override", wl_int, WLC_GET_GMODE_PROTECTION_OVERRIDE,
WLC_SET_GMODE_PROTECTION_OVERRIDE,
"Get/Set 11g protection mode override. (-1=auto, 0=disable, 1=enable)" },
{ "protection_control", wl_int, WLC_GET_PROTECTION_CONTROL,
WLC_SET_PROTECTION_CONTROL,
"Get/Set protection mode control alg."
"(0=always off, 1=monitor local association, 2=monitor overlapping BSS)" },
{ "legacy_erp", wl_int, WLC_GET_LEGACY_ERP, WLC_SET_LEGACY_ERP,
"Get/Set 11g legacy ERP inclusion (0=disable, 1=enable)" },
{ "scb_timeout", wl_int, WLC_GET_SCB_TIMEOUT, WLC_SET_SCB_TIMEOUT,
"AP only: inactivity timeout value for authenticated stas" },
{ "assoclist", wl_maclist, WLC_GET_ASSOCLIST, -1,
"AP only: Get the list of associated MAC addresses."},
{ "isup", wl_int, WLC_GET_UP, -1,
"Get driver operational state (0=down, 1=up)"},
{ "rssi", wl_rssi, WLC_GET_RSSI, -1,
"Get the current RSSI val, for an AP you must specify the mac addr of the STA" },
{ "rssi_event", wl_rssi_event, WLC_GET_VAR, WLC_SET_VAR,
"Set parameters associated with RSSI event notification\n"
"\tusage: wl rssi_event <rate_limit> <rssi_levels>\n"
"\trate_limit: Number of events posted to application will be limited"
" to 1 per this rate limit. Set to 0 to disable rate limit.\n"
"\trssi_levels: Variable number of RSSI levels (maximum 8) "
" in increasing order (e.g. -85 -70 -60). An event will be posted"
" each time the RSSI of received beacons/packets crosses a level."},
{ "fasttimer", wl_print_deprecate, -1, -1,
"Deprecated. Use fast_timer."},
{ "slowtimer", wl_print_deprecate, -1, -1,
"Deprecated. Use slow_timer."},
{ "glacialtimer", wl_print_deprecate, -1, -1,
"Deprecated. Use glacial_timer."},
{ "radar", wl_int, WLC_GET_RADAR, WLC_SET_RADAR,
"Enable/Disable radar"},
{ "radarargs", wl_radar_args, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set Radar parameters in \n"
"\torder as version, npulses, ncontig, min_pw, max_pw, thresh0,\n"
"\tthresh1, blank, fmdemodcfg, npulses_lp, min_pw_lp, max_pw_lp,\n"
"\tmin_fm_lp, max_span_lp, min_deltat, max_deltat,\n"
"\tautocorr, st_level_time, t2_min, fra_pulse_err, npulses_fra,\n"
"\tnpulses_stg2, npulses_stg3, percal_mask, quant, \n"
"\tmin_burst_intv_lp, max_burst_intv_lp, nskip_rst_lp, max_pw_tol, feature_mask"},
{ "radarargs40", wl_radar_args, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set Radar parameters for 40Mhz channel in \n"
"\torder as version, npulses, ncontig, min_pw, max_pw, thresh0,\n"
"\tthresh1, blank, fmdemodcfg, npulses_lp, min_pw_lp, max_pw_lp,\n"
"\tmin_fm_lp, max_span_lp, min_deltat, max_deltat,\n"
"\tautocorr, st_level_time, t2_min, fra_pulse_err, npulses_fra,\n"
"\tnpulses_stg2, npulses_stg3, percal_mask, quant, \n"
"\tmin_burst_intv_lp, max_burst_intv_lp, nskip_rst_lp, max_pw_tol, feature_mask"},
{ "radarthrs", wl_radar_thrs, -1, WLC_SET_VAR,
"Set Radar threshold for both 20 & 40MHz & 80MHz BW:\n"
"\torder as thresh0_20_lo, thresh1_20_lo, thresh0_40_lo, thresh1_40_lo\n"
"\tthresh0_80_lo, thresh1_80_lo, thresh0_20_hi, thresh1_20_hi\n"
"\tthresh0_40_hi, thresh1_40_hi, thresh0_80_hi, thresh1_80_hi\n"},
{ "dfs_status", wl_dfs_status, WLC_GET_VAR, -1,
"Get dfs status"},
{ "interference", wl_interfere, WLC_GET_INTERFERENCE_MODE, WLC_SET_INTERFERENCE_MODE,
"Get/Set interference mitigation mode. Choices are:\n"
"\t0 = none\n"
"\t1 = non wlan\n"
"\t2 = wlan manual\n"
"\t3 = wlan automatic\n"
"\t4 = wlan automatic with noise reduction"},
{ "interference_override", wl_interfere_override,
WLC_GET_INTERFERENCE_OVERRIDE_MODE,
WLC_SET_INTERFERENCE_OVERRIDE_MODE,
"Get/Set interference mitigation override. Choices are:\n"
"\t0 = no interference mitigation\n"
"\t1 = non wlan\n"
"\t2 = wlan manual\n"
"\t3 = wlan automatic\n"
"\t4 = wlan automatic with noise reduction\n"
"\t-1 = remove override, override disabled"},
{ "frameburst", wl_int, WLC_GET_FAKEFRAG, WLC_SET_FAKEFRAG,
"Disable/Enable frameburst mode" },
{ "pwr_percent", wl_int, WLC_GET_PWROUT_PERCENTAGE, WLC_SET_PWROUT_PERCENTAGE,
"Get/Set power output percentage"},
{ "toe", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable tcpip offload feature"},
{ "toe_ol", wl_offload_cmpnt, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set tcpip offload components"},
{ "toe_stats", wl_toe_stats, WLC_GET_VAR, -1,
"Display checksum offload statistics"},
{ "toe_stats_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear checksum offload statistics"},
#endif
{ "arpoe", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable arp agent offload feature"},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "arp_ol", wl_offload_cmpnt, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set arp offload components"},
{ "arp_peerage", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set age of the arp entry in minutes"},
{ "arp_table_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear arp cache"},
#ifdef WLOFFLD
{ "ol_notify_bcn_ie", wl_ol_notify_bcn_ie, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable IE ID notification"},
{ "ol_arp_hostip", wl_hostip, WLC_GET_VAR, WLC_SET_VAR,
"Add a host-ip address or display them"},
{ "ol_nd_hostip", wl_hostipv6, WLC_GET_VAR, WLC_SET_VAR,
"Add a local host-ipv6 address or display them"},
#endif
{ "arp_hostip", wl_hostip, WLC_GET_VAR, WLC_SET_VAR,
"Add a host-ip address or display them"},
{ "arp_hostip_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear all host-ip addresses"},
{ "ns_hostip", wl_nshostip, WLC_GET_VAR, WLC_SET_VAR,
"Add a ns-ip address or display then"},
{ "ns_hostip_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear all ns-ip addresses"},
{ "arp_stats", wl_arp_stats, WLC_GET_VAR, -1,
"Display ARP offload statistics"},
{ "arp_stats_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear ARP offload statistics"},
{ "wet", wl_int, WLC_GET_WET, WLC_SET_WET,
"Get/Set wireless ethernet bridging mode"},
{ "bi", wl_int, WLC_GET_BCNPRD, WLC_SET_BCNPRD,
"Get/Set the beacon period (bi=beacon interval)"},
{ "dtim", wl_int, WLC_GET_DTIMPRD, WLC_SET_DTIMPRD,
"Get/Set DTIM"},
{ "wds_remote_mac", wl_mac, WLC_WDS_GET_REMOTE_HWADDR, -1,
"Get WDS link remote endpoint's MAC address"},
{ "wds_wpa_role_old", wl_wds_wpa_role_old, WLC_WDS_GET_WPA_SUP, -1,
"Get WDS link local endpoint's WPA role (old)"},
{ "wds_wpa_role", wl_wds_wpa_role, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set WDS link local endpoint's WPA role"},
{ "authe_sta_list", wl_maclist_1, WLC_GET_VAR, -1,
"Get authenticated sta mac address list"},
{ "autho_sta_list", wl_maclist_1, WLC_GET_VAR, -1,
"Get authorized sta mac address list"},
{ "measure_req", wl_measure_req, -1, WLC_MEASURE_REQUEST,
"Send an 802.11h measurement request.\n"
"\tUsage: wl measure_req <type> <target MAC addr>\n"
"\tMeasurement types are: TPC, Basic, CCA, RPI\n"
"\tTarget MAC addr format is xx:xx:xx:xx:xx:xx"},
{ "quiet", wl_send_quiet, -1, WLC_SEND_QUIET,
"Send an 802.11h quiet command.\n"
"\tUsage: wl quiet <TBTTs until start>, <duration (in TUs)>, <offset (in TUs)>"},
#endif
{ "csa", wl_send_csa, -1, WLC_SET_VAR,
"Send an 802.11h channel switch anouncement with chanspec:\n"
"\t<mode> <count> <channel>[a,b][n][u,l][frame type]\n"
"\tmode (0 or 1)\n"
"\tcount (0-254)\n"
"\tchannel format:\n"
"\t20MHz : [2g|5g]<channel>[/20]\n"
"\t40MHz : [2g|5g]<channel>/40[u,l]\n"
"\t80MHz : [5g]<channel>/80\n"
"\toptional band 2g or 5g, default to 2g if channel <= 14\n"
"\tchannel number (0-200)\n"
"\tbandwidth, 20, 40, or 80, default 20\n"
"\tprimary sideband for 40MHz on 2g, l=lower, u=upper\n"
"\tcsa frame type(optional), default is broadcast if not specified, u=unicast"},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "constraint", wl_int, -1, WLC_SEND_PWR_CONSTRAINT,
"Send an 802.11h Power Constraint IE\n"
"\tUsage: wl constraint 1-255 db"},
{ "rm_req", wl_rm_request, -1, WLC_SET_VAR,
"Request a radio measurement of type basic, cca, or rpi\n"
"\tspecify a series of measurement types each followed by options.\n"
"\texample: wl rm_req cca -c 1 -d 50 cca -c 6 cca -c 11\n"
"\tOptions:\n"
"\t-t n numeric token id for measurement set or measurement\n"
"\t-c n channel\n"
"\t-d n duration in TUs (1024 us)\n"
"\t-p parallel flag, measurement starts at the same time as previous\n"
"\n"
"\tEach measurement specified uses the same channel and duration as the\n"
"\tprevious unless a new channel or duration is specified."},
{ "rm_rep", wl_rm_report, WLC_GET_VAR, -1,
"Get current radio measurement report"},
{ "join_pref", wl_join_pref, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get join target preferences."},
{ "assoc_pref", wl_assoc_pref, WLC_GET_ASSOC_PREFER, WLC_SET_ASSOC_PREFER,
"Set/Get association preference.\n"
"Usage: wl assoc_pref [auto|a|b|g]"},
{ "wme", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set WME (Wireless Multimedia Extensions) mode (0=off, 1=on, -1=auto)"},
{ "wme_ac", wl_wme_ac_req, WLC_GET_VAR, WLC_SET_VAR,
"wl wme_ac ap|sta [be|bk|vi|vo [ecwmax|ecwmin|txop|aifsn|acm <value>] ...]"},
{ "wme_apsd", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set APSD (Automatic Power Save Delivery) mode on AP (0=off, 1=on)" },
{ "wme_apsd_sta", wl_wme_apsd_sta, WLC_GET_VAR, WLC_SET_VAR,
"Set APSD parameters on STA. Driver must be down.\n"
"Usage: wl wme_apsd_sta <max_sp_len> <be> <bk> <vi> <vo>\n"
" <max_sp_len>: number of frames per USP: 0 (all), 2, 4, or 6\n"
" <xx>: value 0 to disable, 1 to enable U-APSD per AC" },
{ "wme_dp", wl_wme_dp, WLC_GET_VAR, WLC_SET_VAR,
"Set AC queue discard policy.\n"
"Usage: wl wme_dp <be> <bk> <vi> <vo>\n"
" <xx>: value 0 for newest-first, 1 for oldest-first" },
{ "wme_counters", wl_wme_counters, WLC_GET_VAR, -1,
"print WMM stats" },
{ "wme_clear_counters", wl_var_void, -1, WLC_SET_VAR,
"clear WMM counters"},
{ "wme_tx_params", wme_tx_params, -1, -1,
"wl wme_tx_params [be|bk|vi|vo [short|sfb|long|lfb|max_rate <value>] ...]"},
{ "wme_maxbw_params", wme_maxbw_params, WLC_GET_VAR, WLC_SET_VAR,
"wl wme_maxbw_params [be|bk|vi|vo <value> ....]"},
{ "lifetime", wl_lifetime, WLC_GET_VAR, WLC_SET_VAR,
"Set Lifetime parameter (milliseconds) for each ac.\n"
"wl lifetime be|bk|vi|vo [<value>]"},
{ "reinit", wl_void, -1, WLC_INIT,
"Reinitialize device"},
{ "sta_info", wl_sta_info, WLC_GET_VAR, -1,
"wl sta_info <xx:xx:xx:xx:xx:xx>"},
{ "staprio", wl_staprio, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get sta priority \n"
"Usage: wl staprio <xx:xx:xx:xx:xx:xx> <prio> \n"
"<prio>: 0~3"},
{ "pktq_stats", wl_iov_pktqlog_params, WLC_GET_VAR, -1,
"Dumps packet queue log info for [C] common, [A] AMPDU, [N] NAR or [P] power save queues\n"
"A:, N: or P: are used to prefix a MAC address (a colon : separator is necessary),\n"
"or else C: is used alone. The '+' option after the colon gives more details.\n"
"Up to 4 parameters may be given, the common queue is default when no parameters\n"
"are supplied\n"
"Use '/<PREC>' as suffix to restrict to certain prec indices; multiple /<PREC>/<PREC>/..."
"can be used\n"
"Also, '//' as a suffix to the MAC address or 'C://' will enable automatic logging of\n"
"all prec as they are seen.\n"
"Full automatic operation is also possible with the shorthand\n"
"'A:' (or 'A://'), 'P:' (or 'P://') etc which scans through all known addresses for\n"
"those parameters that take a MAC address.\n"
"wl pktq_stats [C:[+]]|[A:[+]|P:[+]|N:[+]<xx:xx:xx:xx:xx:xx>][/<PREC>[/<PREC>]][//]..." },
{ "bs_data", wl_scb_bs_data, WLC_GET_VAR, -1, "Display per station band steering data\n"
"usage: bs_data [options]\n"
" options are:\n"
" -comma Use commas to separate values rather than blanks.\n"
" -tab Use <TAB> to separate values rather than blanks.\n"
" -raw Display raw values as received from driver.\n"
" -noidle Do not display idle stations\n"
" -noreset Do not reset counters after reading" },
{ "cap", wl_var_getandprintstr, WLC_GET_VAR, -1, "driver capabilities"},
{ "malloc_dump", wl_print_deprecate, -1, -1, "Deprecated. Folded under 'wl dump malloc"},
{ "chan_info", wl_chan_info, WLC_GET_VAR, -1, "channel info"},
{ "add_ie", wl_add_ie, -1, WLC_SET_VAR,
"Add a vendor proprietary IE to 802.11 management packets\n"
"Usage: wl add_ie <pktflag> length OUI hexdata\n"
"<pktflag>: Bit 0 - Beacons\n"
" Bit 1 - Probe Rsp\n"
" Bit 2 - Assoc/Reassoc Rsp\n"
" Bit 3 - Auth Rsp\n"
" Bit 4 - Probe Req\n"
" Bit 5 - Assoc/Reassoc Req\n"
"Example: wl add_ie 3 10 00:90:4C 0101050c121a03\n"
" to add this IE to beacons and probe responses" },
{ "del_ie", wl_del_ie, -1, WLC_SET_VAR,
"Delete a vendor proprietary IE from 802.11 management packets\n"
"Usage: wl del_ie <pktflag> length OUI hexdata\n"
"<pktflag>: Bit 0 - Beacons\n"
" Bit 1 - Probe Rsp\n"
" Bit 2 - Assoc/Reassoc Rsp\n"
" Bit 3 - Auth Rsp\n"
" Bit 4 - Probe Req\n"
" Bit 5 - Assoc/Reassoc Req\n"
"Example: wl del_ie 3 10 00:90:4C 0101050c121a03" },
{ "list_ie", _wl_list_ie, WLC_GET_VAR, -1,
"Dump the list of vendor proprietary IEs" },
{ "rand", wl_rand, WLC_GET_VAR, -1,
"Get a 2-byte Random Number from the MAC's PRNG\n"
"Usage: wl rand"},
{ "otpraw", wl_otpraw, WLC_GET_VAR, WLC_SET_VAR,
"Read/Write raw data to on-chip otp\n"
"Usage: wl otpraw <offset> <bits> [<data>]"},
{ "otpw", wl_otpw, -1, WLC_OTPW,
"Write an srom image to on-chip otp\n"
"Usage: wl otpw file"},
{ "nvotpw", wl_otpw, -1, WLC_NVOTPW,
"Write nvram to on-chip otp\n"
"Usage: wl nvotpw file"},
{ "bcmerrorstr", wl_var_getandprintstr, WLC_GET_VAR, -1, "errorstring"},
{ "freqtrack", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set Frequency Tracking Mode (0=Auto, 1=On, 2=OFF)"},
{ "eventing", wl_eventbitvec, WLC_GET_VAR, WLC_SET_VAR,
"set/get hex filter bitmask for MAC event reporting up to application layer"},
{ "event_msgs", wl_eventbitvec, WLC_GET_VAR, WLC_SET_VAR,
"set/get hex filter bitmask for MAC event reporting via packet indications"},
{ "counters", wl_counters, WLC_GET_VAR, -1,
"Return driver counter values" },
{ "delta_stats_interval", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"set/get the delta statistics interval in seconds (0 to disable)"},
{ "delta_stats", wl_delta_stats, WLC_GET_VAR, -1,
"get the delta statistics for the last interval" },
{ "assoc_info", wl_assoc_info, WLC_GET_VAR, -1,
"Returns the assoc req and resp information [STA only]" },
{ "beacon_info", wl_management_info, WLC_GET_VAR, -1,
"Returns the 802.11 management frame beacon information\n"
"Usage: wl beacon_info [-f file] [-r]\n"
"\t-f Write beacon data to file\n"
"\t-r Raw hex dump of beacon data" },
{ "probe_resp_info", wl_management_info, WLC_GET_VAR, -1,
"Returns the 802.11 management frame probe response information\n"
"Usage: wl probe_resp_info [-f file] [-r]\n"
"\t-f Write probe response data to file\n"
"\t-r Raw hex dump of probe response data" },
{ "autochannel", wl_auto_channel_sel, WLC_GET_CHANNEL_SEL, WLC_START_CHANNEL_SEL,
"auto channel selection: \n"
"\t1 to issue a channel scanning;\n"
"\t2 to set chanspec based on the channel scan result;\n"
"\twithout argument to only show the chanspec selected; \n"
"\tssid must set to null before this process, RF must be up"},
{ "csscantimer", wl_int, WLC_GET_CS_SCAN_TIMER, WLC_SET_CS_SCAN_TIMER,
"auto channel scan timer in minutes (0 to disable)" },
{ "closed", wl_int, WLC_GET_CLOSED, WLC_SET_CLOSED,
"hides the network from active scans, 0 or 1.\n"
"\t0 is open, 1 is hide" },
{ "pmkid_info", wl_pmkid_info, WLC_GET_VAR, WLC_SET_VAR,
"Returns the pmkid table" },
#endif
{ "bss", wl_bsscfg_enable, WLC_GET_VAR, WLC_SET_VAR,
"set/get BSS enabled status: up/down"},
#ifdef BUILD_FEATURE_WIFI_BCM_WL
{ "closednet", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get BSS closed network attribute"},
{ "ap_isolate", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get AP isolation"},
{ "eap_restrict", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get EAP restriction"},
{ "diag", wl_diag, WLC_GET_VAR, -1,
"diag testindex(1-interrupt, 2-loopback, 3-memory, 4-led);"
" precede by 'wl down' and follow by 'wl up'" },
{ "reset_d11cnts", wl_var_void, -1, WLC_SET_VAR,
"reset 802.11 MIB counters"},
{ "staname", wl_varstr, WLC_GET_VAR, WLC_SET_VAR,
"get/set station name: \n"
"\tMaximum name length is 15 bytes"},
{ "apname", wl_varstr, WLC_GET_VAR, -1,
"get AP name"},
{ "otpdump", wl_var_setintandprintstr, WLC_GET_VAR, -1,
"Dump raw otp"},
{ "otpstat", wl_var_setintandprintstr, WLC_GET_VAR, -1,
"Dump OTP status"},
{ "nrate", wl_nrate, WLC_GET_VAR, WLC_SET_VAR,
"\"auto\" to clear a rate override, or:\n"
"-r legacy rate (CCK, OFDM)\n"
"-m HT MCS index\n"
"-s stf mode (0=SISO,1=CDD,2=STBC,3=SDM)\n"
"-w Override MCS only to support STA's with/without STBC capability"},
{ "mimo_txbw", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"get/set mimo txbw (2=20Mhz(lower), 3=20Mhz upper, 4=40Mhz)"},
{ "cac_addts", wl_cac, -1, WLC_SET_VAR,
"add TSPEC, error if STA is not associated or WME is not enabled\n"
"\targ: TSPEC parameter input list"},
{ "cac_delts", wl_cac, -1, WLC_SET_VAR,
"delete TSPEC, error if STA is not associated or WME is not enabled\n"
"\targ: TSINFO for the target tspec"},
{ "cac_delts_ea", wl_cac_delts_ea, -1, WLC_SET_VAR,
"delete TSPEC, error if STA is not associated or WME is not enabled\n"
"\targ1: Desired TSINFO for the target tspec\n"
"\targ2: Desired MAC address"},
{ "cac_tslist", wl_tslist, WLC_GET_VAR, -1,
"Get the list of TSINFO in driver\n"
"\teg. 'wl cac_tslist' get a list of TSINFO"},
{ "cac_tslist_ea", wl_tslist_ea, WLC_GET_VAR, -1,
"Get the list of TSINFO for given STA in driver\n"
"\teg. 'wl cac_tslist_ea ea' get a list of TSINFO"},
{ "cac_tspec", wl_tspec, WLC_GET_VAR, -1,
"Get specific TSPEC with matching TSINFO\n"
"\teg. 'wl cac_tspec 0xaa 0xbb 0xcc' where 0xaa 0xbb & 0xcc are TSINFO octets"},
{ "cac_tspec_ea", wl_tspec_ea, WLC_GET_VAR, -1,
"Get specific TSPEC for given STA with matching TSINFO\n"
"\teg. 'wl cac_tspec 0xaa 0xbb 0xcc xx:xx:xx:xx:xx:xx'\n"
"\t where 0xaa 0xbb & 0xcc are TSINFO octets and xx is mac address"},
{ "sd_cis", wl_var_getandprintstr, WLC_GET_VAR, -1,
"dump sdio CIS"},
{ "sd_devreg", wl_sd_reg, WLC_GET_VAR, WLC_SET_VAR,
"g/set device register across SDIO bus"},
{ "sd_drivestrength", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"g/set SDIO bus drive strenth in mA"},
{ "sd_hostreg", wl_sd_reg, WLC_GET_VAR, WLC_SET_VAR,
"g/set local controller register"},
{ "sd_blockmode", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"g/set blockmode"},
{ "sd_blocksize", wl_sd_blocksize, WLC_GET_VAR, WLC_SET_VAR,
"g/set block size for a function"},
{ "sd_ints", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"g/set client ints"},
{ "sd_dma", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"g/set dma usage"},
{ "sd_numints", wl_varint, WLC_GET_VAR, -1,
"number of device interrupts"},
{ "sd_numlocalints", wl_varint, WLC_GET_VAR, -1,
"number of non-device controller interrupts"},
{ "sd_divisor", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"set the divisor for SDIO clock generation"},
{ "sd_mode", wl_sd_mode, WLC_GET_VAR, WLC_SET_VAR,
"g/set SDIO bus mode (spi, sd1, sd4)"},
{ "sd_highspeed", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"set the high-speed clocking mode"},
{ "sd_msglevel", wl_sd_msglevel, WLC_GET_VAR, WLC_SET_VAR,
"g/set debug message level"},
{ "overlay", wl_overlay, WLC_GET_VAR, WLC_SET_VAR,
"overlay virt_addr phy_addr size"},
{ "phy_txpwrindex", wl_phy_txpwrindex, WLC_GET_VAR, WLC_SET_VAR,
"usage: (set) phy_txpwrindex core0_idx core1_idx core2_idx core3_idx"
" (get) phy_txpwrindex, return format: core0_idx core1_idx core2_idx core3_idx"
"Set/Get txpwrindex"
},
{ "rssi_cal_freq_grp_2g", wl_rssi_cal_freq_grp_2g, WLC_GET_VAR, WLC_SET_VAR,
"usage: wl_rssi_cal_freq_grp_2g [chan_1_2,chan_3_4,...,chan_13_14]\n"
"Each of the variables like - chan_1_2 is a byte"
"Upper nibble of this byte is for chan1 and lower for chan2"
"MSB of the nibble tells if the channel is used for calibration"
"3 LSB's tell which group the channel falls in"
"Set/get rssi calibration frequency grouping"
},
{ "phy_rssi_gain_delta_2gb0", wl_phy_rssi_gain_delta_2g_sub, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2gb0 [val0 val1 ....]\n"
"Number of arguments can be - "
"\t 8 for single core (4345 and 4350)"
"\t 9 by specifying core_num followed by 8 arguments (4345 and 4350)"
"\t 16 for both cores (4350)"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_2gb1", wl_phy_rssi_gain_delta_2g_sub, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2gb1 [val0 val1 ....]\n"
"Number of arguments can be - "
"\t 8 for single core (4345 and 4350)"
"\t 9 by specifying core_num followed by 8 arguments (4345 and 4350)"
"\t 16 for both cores (4350)"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_2gb2", wl_phy_rssi_gain_delta_2g_sub, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2gb2 [val0 val1 ....]\n"
"Number of arguments can be - "
"\t 8 for single core (4345 and 4350)"
"\t 9 by specifying core_num followed by 8 arguments (4345 and 4350)"
"\t 16 for both cores (4350)"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_2gb3", wl_phy_rssi_gain_delta_2g_sub, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2gb3 [val0 val1 ....]\n"
"Number of arguments can be - "
"\t 8 for single core (4345 and 4350)"
"\t 9 by specifying core_num followed by 8 arguments (4345 and 4350)"
"\t 16 for both cores (4350)"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_2gb4", wl_phy_rssi_gain_delta_2g_sub, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2gb4 [val0 val1 ....]\n"
"Number of arguments can be - "
"\t 8 for single core (4345 and 4350)"
"\t 9 by specifying core_num followed by 8 arguments (4345 and 4350)"
"\t 16 for both cores (4350)"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_2g", wl_phy_rssi_gain_delta_2g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_2g [val0 val1 ....]\n"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_5gl", wl_phy_rssi_gain_delta_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_5gl [val0 val1 ....]\n"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_5gml", wl_phy_rssi_gain_delta_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_5gml [val0 val1 ....]\n"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_5gmu", wl_phy_rssi_gain_delta_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_5gmu [val0 val1 ....]\n"
"Set/get rssi gain delta values"
},
{ "phy_rssi_gain_delta_5gh", wl_phy_rssi_gain_delta_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rssi_gain_delta_5gh [val0 val1 ....]\n"
"Set/get rssi gain delta values"
},
{ "phy_rxgainerr_2g", wl_phy_rxgainerr_2g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rxgainerr_2g [val0 val1 ....]\n"
"Set/get rx gain delta values"
},
{ "phy_rxgainerr_5gl", wl_phy_rxgainerr_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rxgainerr_5gl [val0 val1 ....]\n"
"Set/get rx gain delta values"
},
{ "phy_rxgainerr_5gm", wl_phy_rxgainerr_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rxgainerr_5gml [val0 val1 ....]\n"
"Set/get rx gain delta values"
},
{ "phy_rxgainerr_5gh", wl_phy_rxgainerr_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rxgainerr_5gmu [val0 val1 ....]\n"
"Set/get rx gain delta values"
},
{ "phy_rxgainerr_5gu", wl_phy_rxgainerr_5g, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_rxgainerr_5gh [val0 val1 ....]\n"
"Set/get rx gain delta values"
},
{ "phy_test_tssi", wl_test_tssi, WLC_GET_VAR, -1,
"wl phy_test_tssi val"},
{ "phy_test_tssi_offs", wl_test_tssi_offs, WLC_GET_VAR, -1,
"wl phy_test_tssi_offs val"},
{ "phy_rssiant", wl_phy_rssiant, WLC_GET_VAR, -1,
"wl phy_rssiant antindex(0-3)"},
{ "phy_rssi_ant", wl_phy_rssi_ant, WLC_GET_VAR, WLC_SET_VAR,
"Get RSSI per antenna (only gives RSSI of current antenna for SISO PHY)"},
{ "lpphy_papdepstbl", wl_phy_papdepstbl, -1, WLC_GET_VAR,
"print papd eps table; Usage: wl lpphy_papdepstbl"
},
{ "phy_test_idletssi", wl_test_idletssi, WLC_GET_VAR, -1,
"get idletssi for the given core; wl phy_test_idletssi corenum"},
{ "phy_setrptbl", wl_var_void, -1, WLC_SET_VAR,
"populate the reciprocity compensation table based on SROM cal content\n\n"
"\tusage: wl phy_setrptbl"},
{ "phy_forceimpbf", wl_var_void, -1, WLC_SET_VAR,
"force the beamformer into implicit TXBF mode and ready to construct steering matrix\n\n"
"\tusage: wl phy_forceimpbf"},
{ "phy_forcesteer", wl_var_setint, -1, WLC_SET_VAR,
"force the beamformer to apply steering matrix when TXBF is turned on\n\n"
"\tusage: wl phy_forcesteer 1/0"},
{ "lcnphy_papdepstbl", wl_phy_papdepstbl, -1, WLC_GET_VAR,
"print papd eps table; Usage: wl lcnphy_papdepstbl"
},
{ "rifs", wl_rifs, WLC_GET_VAR, WLC_SET_VAR,
"set/get the rifs status; usage: wl rifs <1/0> (On/Off)"
},
{ "rifs_advert", wl_rifs_advert, WLC_GET_VAR, WLC_SET_VAR,
"set/get the rifs mode advertisement status; usage: wl rifs_advert <-1/0> (Auto/Off)"
},
{ "phy_rxiqest", wl_rxiq, WLC_GET_VAR, -1,
"Get phy RX IQ noise in dBm:\n"
"\t-s # of samples (2^n)\n"
"\t-a antenna select, 0,1 or 3\n"
"\t-r resolution select, 0 (coarse) or 1 (fine)\n"
"\t-f lpf hpc override select, 0 (hpc unchanged) or 1 (overridden to ltrn mode)\n"
"\t-w dig lpf override select, 0 (lpf unchanged) or 1 (overridden to ltrn_lpf mode)"
"\t or 2 (bypass)\n"
"\t-g gain-correction select, 0 (disable), 1(enable full correction) \n"
"\t 2 (enable temperature correction) or 3(verify rssi_gain_delta)\n"
"\t-e extra INITgain in dB on top of default. Valid values = {0, 3, 6, .., 21, 24}\n"
"\t-i gain mode select, 0 (default gain), 1 (fixed high gain) or 4 (fixed low gain). \n"
},
{ "phy_txiqcc", wl_phy_txiqcc, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_txiqcc [a b]\n"
"Set/get the iqcc a, b values"
},
{ "phy_txlocc", wl_phy_txlocc, WLC_GET_VAR, WLC_SET_VAR,
"usage: phy_txlocc [di dq ei eq fi fq]\n"
"Set/get locc di dq ei eq fi fq values"
},
{ "phytable", wl_phytable, WLC_GET_VAR, WLC_SET_VAR,
"usage: wl phytable table_id offset width_of_table_element [table_element]\n"
"Set/get table element of a table with the given ID at the given offset\n"
"Note that table width supplied should be 8 or 16 or 32\n"
"table ID, table offset can not be negative"
},
/* WLOTA_EN START */
{ "ota_teststop", wl_ota_teststop, -1, WLC_SET_VAR,
"\tUsage: ota_teststop \n"
},
{ "ota_loadtest", wl_ota_loadtest, -1, WLC_SET_VAR,
"\tUsage: ota_loadtest [filename] \n"
"\t\tpicks up ota_test.txt if file is not given \n"
},
{ "ota_stream", wl_load_cmd_stream, -1, WLC_SET_VAR,
"\tUsage: wl ota_stream start : to start the test\n"
"\twl ota_stream ota_sync \n"
"\twl ota_stream test_setup synchtimeoout(seconds) synchbreak/loop synchmac txmac rxmac \n"
"\twl ota_stream ota_tx chan bandwidth contrlchan rates stf txant rxant tx_ifs tx_len"
"num_pkt pwrctrl start:delta:end \n"
"\twl ota_stream ota_rx chan bandwidth contrlchan -1 stf txant rxant tx_ifs"
"tx_len num_pkt \n"
"\twl ota_stream stop : to stop the test\n"
},
{ "ota_teststatus", wl_otatest_status, WLC_GET_VAR, -1,
"\tUsage: otatest_status"
"\t\tDisplays current running test details"
"\totatest_status n "
"\t\tdisplays test arguments for nth line \n"
},
/* WLOTA_EN END */
{ "force_vsdb_chans", wl_phy_force_vsdb_chans, WLC_GET_VAR, WLC_SET_VAR,
"Set/get channels for forced vsdb mode\n"
"usage: wl force_vsdb_chans chan1 chan2\n"
"Note: Give chan in the same format as chanspec: eg force_vsdb_chans 1l 48u\n"
},
{ "pavars", wl_phy_pavars, WLC_GET_VAR, WLC_SET_VAR,
"Set/get temp PA parameters\n"
"usage: wl down\n"
" wl pavars pa2gw0a0=0x1 pa2gw1a0=0x2 pa2gw2a0=0x3 ... \n"
" wl pavars\n"
" wl up\n"
" override the PA parameters after driver attach(srom read), before diver up\n"
" These override values will be propogated to HW when driver goes up\n"
" PA parameters in one band range (2g, 5gl, 5g, 5gh) must all present if\n"
" one of them is specified in the command, otherwise it will be filled with 0"
},
{ "povars", wl_phy_povars, WLC_GET_VAR, WLC_SET_VAR,
"Set/get temp power offset\n"
"usage: wl down\n"
" wl povars cck2gpo=0x1 ofdm2gpo=0x2 mcs2gpo=0x3 ... \n"
" wl povars\n"
" wl up\n"
" override the power offset after driver attach(srom read), before diver up\n"
" These override values will be propogated to HW when driver goes up\n"
" power offsets in one band range (2g, 5gl, 5g, 5gh) must all present if\n"
" one of them is specified in the command, otherwise it will be filled with 0"
" cck(2g only), ofdm, and mcs(0-7) for NPHY are supported "
},
{ "rpcalvars", wl_phy_rpcalvars, WLC_GET_VAR, WLC_SET_VAR,
"Set/get temp RPCAL parameters\n"
"usage: wl down\n"
" wl rpcalvars rpcal2g=0x1 \n"
" wl rpcalvars\n"
" wl up\n"
" override the RPCAL parameters after driver attach(srom read), before diver up\n"
" These override values will be propogated to HW when driver goes up\n"
" Only the RPCAL parameter specified in the command is updated, the rest is untouched \n"
},
{ "fem", wl_phy_fem, WLC_GET_VAR, WLC_SET_VAR,
"Set temp fem2g/5g value\n"
"usage: wl fem (tssipos2g=0x1 extpagain2g=0x2 pdetrange2g=0x1 triso2g=0x1 antswctl2g=0)\n"
" (tssipos5g=0x1 extpagain5g=0x2 pdetrange5g=0x1 triso5g=0x1 antswctl5g=0)"
},
{ "antgain", wl_antgain, WLC_GET_VAR, WLC_SET_VAR,
"Set temp ag0/1 value\n"
"usage: wl antgain ag0=0x1 ag1=0x2"
},
{ "maxpower", wl_phy_maxpower, WLC_GET_VAR, WLC_SET_VAR,
"Set temp maxp2g(5g)a0(a1) value\n"
"usage: wl maxpower maxp2ga0=0x1 maxp2ga1=0x2 maxp5ga0=0xff maxp5ga1=0xff\n"
" maxp5gla0=0x3 maxp5gla1=0x4 maxp5gha0=0x5 maxp5gha1=0x6"
},
{ "phy_antsel", wl_antsel, WLC_GET_VAR, -1,
"get/set antenna configuration \n"
"\tset: -1(AUTO), 0xAB(fixed antenna selection)\n"
"\t\twhere A and B is the antenna numbers used for RF chain 1 and 0 respectively\n"
"\tquery: <utx>[AUTO] <urx>[AUTO] <dtx>[AUTO] <drx>[AUTO]\n"
"\t\twhere utx = TX unicast antenna configuration\n"
"\t\t\turx = RX unicast antenna configuration\n"
"\t\t\tdtx = TX default (non-unicast) antenna configuration\n"
"\t\t\tdrx = RX default (non-unicast) antenna configuration"
},
{ "txcore", wl_txcore, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl txcore -k <CCK core mask> -o <OFDM core mask> -s <1..4> -c <core bitmap>\n"
"\t-k CCK core mask\n"
"\t-o OFDM core mask\n"
"\t-s # of space-time-streams\n"
"\t-c active core (bitmask) to be used when transmitting frames"
},
{ "txcore_override", wl_txcore, WLC_GET_VAR, -1,
"Usage: wl txcore_override\n"
"\tget the user override of txcore"
},
{ "txchain_pwr_offset", wl_txcore_pwr_offset, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl txchain_pwr_offset [qdBm offsets]\n"
"\tGet/Set the current offsets for each core in qdBm (quarter dBm)"
},
{ "sample_collect", wl_sample_collect, WLC_PHY_SAMPLE_COLLECT, -1,
"Optional parameters ACPHY/HTPHY/(NPHY with NREV >= 7) are:\n"
"\t-f File name to dump the sample buffer (default \"sample_collect.dat\")\n"
"\t-t Trigger condition (default now)\n"
"\t\t now, good_fcs, bad_fcs, bad_plcp, crs, crs_glitch, crs_deassert\n"
"\t-b PreTrigger duration in us (default 10)\n"
"\t-a PostTrigger duration in us (default 10) \n"
"\t-m Sample collect mode (default 1) \n"
"\t\tSC_MODE_0_sd_adc\t\t\t0\n"
"\t\tSC_MODE_1_sd_adc_5bits\t\t\t1\n"
"\t\tSC_MODE_2_cic0\t\t\t\t2\n"
"\t\tSC_MODE_3_cic1\t\t\t\t3\n"
"\t\tSC_MODE_4s_rx_farrow_1core\t\t4\n"
"\t\tSC_MODE_4m_rx_farrow\t\t\t5\n"
"\t\tSC_MODE_5_iq_comp\t\t\t6\n"
"\t\tSC_MODE_6_dc_filt\t\t\t7\n"
"\t\tSC_MODE_7_rx_filt\t\t\t8\n"
"\t\tSC_MODE_8_rssi\t\t\t\t9\n"
"\t\tSC_MODE_9_rssi_all\t\t\t10\n"
"\t\tSC_MODE_10_tx_farrow\t\t\t11\n"
"\t\tSC_MODE_11_gpio\t\t\t\t12\n"
"\t\tSC_MODE_12_gpio_trans\t\t\t13\n"
"\t\tSC_MODE_14_spect_ana\t\t\t14\n"
"\t\tSC_MODE_5s_iq_comp\t\t\t15\n"
"\t\tSC_MODE_6s_dc_filt\t\t\t16\n"
"\t\tSC_MODE_7s_rx_filt\t\t\t17\n"
"\t\t HTPHY: 0=adc, 1..3=adc+rssi, 4=gpio\n"
"\t\t NPHY: 1=Dual-Core adc[9:2], 2=Core0 adc[9:0], 3=Core1 adc[9:0], gpio=gpio\n"
"\t-g GPIO mux select (default 0)\n"
"\t\t use only for gpio mode\n"
"\t-d Downsample enable (default 0)\n"
"\t\t use only for HTPHY\n"
"\t-e BeDeaf enable (default 0)\n"
"\t-i Timeout in units of 10us. (ACPHY is in 10ms unit) (default 1000)\n"
"Optional parameters (NPHY with NREV < 7) are:\n"
"\t-f File name to dump the sample buffer (binary format, default \"sample_collect.dat\")\n"
"\t-u Sample collect duration in us (default 60)\n"
"\t-c Cores to do sample collect, only if BW=40MHz (default both)\n"
"Optional parameters LCN40PHY are:\n"
"\t-f File name to dump the sample buffer (default \"sample_collect.dat\")\n"
"\t-t Trigger condition (default now)\n"
"\t\t now\n"
"\t-s Trigger State (default 0)\n"
"\t-x Module_Sel1 (default 2)\n"
"\t-y Module_Sel2 (default 6)\n"
"\t-n Number of samples (Max 2048, default 2048)\n"
"For (NREV < 7), the NPHY buffer returned has the format:\n"
"\tIn 20MHz [(uint16)num_bytes, <I(core0), Q(core0), I(core1), Q(core1)>]\n"
"\tIn 40MHz [(uint16)num_bytes(core0), <I(core0), Q(core0)>,\n"
"\t\t(uint16)num_bytes(core1), <I(core1), Q(core1)>]"},
#ifdef PLC
{ "plc", wl_plc, WLC_GET_VAR, WLC_SET_VAR,
"set/get the plc config params; usage: wl plc [[0|1] [node_list]"
"[link_affinity|mac_affinity <etheraddr> [<affinity>]]]" },
#endif /* PLC */
{ "txfifo_sz", wl_txfifo_sz, WLC_GET_VAR, WLC_SET_VAR,
"set/get the txfifo size; usage: wl txfifo_sz <fifonum> <size_in_bytes>" },
#ifdef WLPFN
{ "pfnset", wl_pfn_set, -1, -1,
"Configures preferred network offload parameter\n"
"\tpfnset syntax is: pfnset [scanfrq xxxxx(30 sec)] [netimeout xxxx(60 sec)]"
"[slowfrq xxxx(180 sec)] [bestn (2)|[1-BESTN_MAX]] [mscan (0)|[0-MSCAN_MAX]]"
"[bdscan (0)|1] [adapt (off)|[smart, strict, slow]]"
"[rssi_delta xxxx(30 dBm)] [sort (listorder)|rssi] [bkgscan (0)|1] [immediateevent (0)|1]"
"[immediate 0|(1)] [repeat (10)|[1-20]] [exp (2)|[1-5]] [separate 0|(1)]"},
{ "pfnadd", wl_pfn_add, -1, -1,
"Adding SSID based preferred networks to monitor and connect\n"
"\tpfnadd syntax is: pfnadd ssid <SSID> [hidden (0)|1]"
"[imode (bss)|ibss]"
"[amode (open)|shared] [wpa_auth (wpadisabled)|wpapsk|wpa2psk|wpanone|any]"
"[wsec WEP|TKIP|AES|TKIPAES] [suppress (neither)|found|lost] [rssi <rssi>(0 dBm)]\n"
"Up to 16 SSID networks can be added together in one pfnadd\n"
"\tTo specify more than one WPA methods, use a number (same format as wpa_auth iovar) "
"as the parameter of wpa_auth (e.g. 0x84 for wpapsk and wpa2psk.)"},
{ "pfnadd_bssid", wl_pfn_add_bssid, -1, -1,
"Adding BSSID based preferred networks to monitor and connect\n"
"\tpfnadd_bssid syntax is: pfnadd_bssid bssid <BSSID> [suppress (neither)|found|lost]"
"[rssi <rssi>(0 dBm)]\n"
"\tUp to 150 BSSIDs can be added together in one pfnadd_bssid\n"},
{ "pfncfg", wl_pfn_cfg, -1, -1,
"Configures channel list and report type\n"
"Usage: pfncfg [channel <list>] [report <type>] [prohibited 1|0]\n"
"\treport <type> is ssidonly, bssidonly, or both (default: both)\n"
"\tprohibited flag 1: allow and (passively) scan any channel (default 0)"},
{ "pfn", wl_pfn, -1, -1,
"Enable/disable preferred network off load monitoring\n"
"\tpfn syntax is: pfn 0|1"},
{ "pfnclear", wl_var_void, -1, WLC_SET_VAR,
"Clear the preferred network list\n"
"\tpfnclear syntax is: pfnclear"},
{ "pfnbest", wl_pfnbest, -1, -1,
"Get the best n networks in each of up to m scans, with 16bit timestamp\n"
"\tpfnbest syntax is: pfnbest"},
{ "pfnlbest", wl_pfnlbest, -1, -1,
"Get the best n networks in each scan, up to m scans, with 32bit timestmp\n"
"\tpfnbest syntax is: pfnlbest"},
{ "pfnsuspend", wl_pfn_suspend, -1, -1,
"Suspend/resume pno scan\n"
"\tpfnsuspend syntax is: pfnsuspend 0|1"},
{ "pfnmem", wl_pfn_mem, -1, -1,
"Get supported mscan with given bestn\n"
"\tpfnmem syntax is: pfnmscan bestn [1-BESTN_MAX]"},
{ "pfneventchk", wl_pfn_event_check, -1, -1,
"Listen and prints the preferred network off load event from dongle\n"
"\tpfneventchk syntax is: pfneventchk [ifname]"},
{ "escan_event_check", wl_escan_event_check, -1, -1,
"Listen and prints the escan events from the dongle\n"
"\tescan_event_check syntax is: escan_event_check ifname flag\n"
"\tflag 1 = sync_id info, 2 = bss info, 4 = state + bss info [default], "
"8 = TLV check for IEs"},
{ "escanresults", wl_escanresults, -1, WLC_SET_VAR,
"Start escan and display results.\n" SCAN_USAGE
},
{ "event_filter", wl_event_filter, -1, -1,
"Set/get event filter\n"
"\tevent_filter syntax is: event_filter [value]"},
#endif /* WLPFN */
#ifdef WLP2PO
{ "p2po_listen", wl_p2po_listen, -1, WLC_SET_VAR,
"start listen (with period and interval)\n"
"\tusage: p2po_listen [\"period\"] [\"interval\"]\n"
},
{ "p2po_find", wl_var_void, -1, WLC_SET_VAR,
"start discovery\n"
},
{ "p2po_stop", wl_p2po_stop, -1, WLC_SET_VAR,
"stop both P2P listen and P2P device discovery offload(0:stop; 1:pause).\n"
"\tusage: p2po_stop <0|1>\n"
},
{ "p2po_addsvc", wl_p2po_addsvc, -1, WLC_SET_VAR,
"add query-service pair\n"
"\tusage: p2po_addsvc <protocol> <\"query\"> <\"response\">\n"
"\t\t<protocol>: 1 = Bonjour, 2 = UPnP\n"
},
{ "p2po_delsvc", wl_p2po_delsvc, -1, WLC_SET_VAR,
"delete query-service pair\n"
"\tusage: p2po_delsvc <protocol> <\"query\">\n"
"\t\t<protocol>: 1 = Bonjour, 2 = UPnP, 0 = delete all\n"
},
{ "p2po_sd_req_resp", wl_p2po_sd_reqresp, -1, WLC_SET_VAR,
"find a service\n"
"\tusage: p2po_sd_req_resp <protocol> <\"query\">\n"
"\t\t<protocol>: 1 = Bonjour, 2 = UPnP\n"
},
{ "p2po_sd_cancel", wl_var_void, -1, WLC_SET_VAR,
"cancel finding a service\n"
},
{ "p2po_listen_channel", wl_p2po_listen_channel, -1, WLC_SET_VAR,
"set listen channel to channel 1, 6, 11, or default\n"
"\tusage: p2po_listen_channel <1|6|11|0>\n"
},
{ "p2po_results", wl_p2po_results, -1, WLC_SET_VAR,
"Listens and displays P2PO results.\n"
},
#endif /* WLP2PO */
#ifdef WLANQPO
{ "anqpo_set", wl_anqpo_set, -1, -1,
"set ANQP offload parameters\n"
"\tusage: anqpo_set [max_retransmit <number>]\n"
"\t\t[response_timeout <msec>] [max_comeback_delay <msec>]\n"
"\t\t[max_retries <number>] [query \"encoded ANQP query\"]\n"
},
{ "anqpo_stop_query", wl_anqpo_stop_query, -1, WLC_SET_VAR,
"stop ANQP query\n"
"\tusage: anqpo_stop_query\n"
},
{ "anqpo_start_query", wl_anqpo_start_query, -1, WLC_SET_VAR,
"start ANQP query to peer(s)\n"
"\tusage: anqpo_start_query <channel> <xx:xx:xx:xx:xx:xx>\n"
"\t\t[<channel> <xx:xx:xx:xx:xx:xx>]>\n"
},
{ "anqpo_auto_hotspot", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"automatic ANQP query to maximum number of hotspot APs, default 0 (disabled)\n"
"\tusage: anqpo_auto_hotspot [max]\n"
},
{ "anqpo_ignore_mode", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"ignore duplicate SSIDs or BSSIDs, default 0 (SSID)\n"
"\tusage: anqpo_ignore_mode [0 (SSID) | 1 (BSSID)]\n"
},
{ "anqpo_ignore_ssid_list", wl_anqpo_ignore_ssid_list, WLC_GET_VAR, WLC_SET_VAR,
"get, clear, set, or append to ANQP offload ignore SSID list\n"
"\tusage: wl anqpo_ignore_ssid_list [clear |\n"
"\t\tset <ssid1> [ssid2] |\n"
"\t\tappend <ssid3> [ssid4]>\n"
},
{ "anqpo_ignore_bssid_list", wl_anqpo_ignore_bssid_list, WLC_GET_VAR, WLC_SET_VAR,
"get, clear, set, or append to ANQP offload ignore BSSID list\n"
"\tusage: wl anqpo_ignore_bssid_list [clear |\n"
"\t\tset <xx:xx:xx:xx:xx:xx> [xx:xx:xx:xx:xx:xx] |\n"
"\t\tappend <xx:xx:xx:xx:xx:xx> [xx:xx:xx:xx:xx:xx]]>\n"
},
{ "anqpo_results", wl_anqpo_results, -1, WLC_SET_VAR,
"Listens and displays ANQP results.\n"
},
#endif /* WLANQPO */
{ "hs20_ie", wl_hs20_ie, -1, WLC_SET_VAR,
"set hotspot 2.0 indication IE\n"
"\tusage: wl hs20_ie <length> <hexdata>\n"
},
{"rate_histo", wl_rate_histo, -1, WLC_GET_VAR,
"Get rate hostrogram"
},
{ "pkteng_start", wl_pkteng, -1, WLC_SET_VAR,
"start packet engine tx usage: wl pkteng_start <xx:xx:xx:xx:xx:xx>"
" <tx|txwithack> [(async)|sync] [ipg] [len] [nframes] [src]\n"
"\tstart packet engine rx usage: wl pkteng_start <xx:xx:xx:xx:xx:xx>"
" <rx|rxwithack> [(async)|sync] [rxframes] [rxtimeout]\n"
"\tsync: synchronous mode\n"
"\tipg: inter packet gap in us\n"
"\tlen: packet length\n"
"\tnframes: number of frames; 0 indicates continuous tx test\n"
"\tsrc: source mac address\n"
"\trxframes: number of receive frames (sync mode only)\n"
"\trxtimeout: maximum timout in msec (sync mode only)"},
{ "pkteng_stop", wl_pkteng, -1, WLC_SET_VAR,
"stop packet engine; usage: wl pkteng_stop <tx|rx>"},
{ "pkteng_stats", wl_pkteng_stats, -1, WLC_GET_VAR,
"packet engine stats; usage: wl pkteng_stats"},
{ "wowl", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/disable WOWL events\n"
" 0 - Clear all events\n"
"Bit 0 - Wakeup on Magic Packet\n"
"Bit 1 - Wakeup on NetPattern (use 'wl wowl_pattern' to configure pattern)\n"
"Bit 2 - Wakeup on loss-of-link due to Disassociation/Deauth\n"
"Bit 3 - Wakeup on retrograde tsf\n"
"Bit 4 - Wakeup on loss of beacon (use 'wl wowl_bcn_loss' to configure time)"},
{ "wowl_bcn_loss", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set #of seconds of beacon loss for wakeup event"},
{ "wowl_pattern", wl_wowl_pattern, -1, -1,
"usage: wowl_pattern [ [clr | [[ add | del ] offset mask value ]]]\n"
"No options -- lists existing pattern list\n"
"add -- Adds the pattern to the list\n"
"del -- Removes a pattern from the list\n"
"clr -- Clear current list\n"
"offset -- Starting offset for the pattern\n"
"mask -- Mask to be used for pattern. Bit i of mask => byte i of the pattern\n"
"value -- Value of the pattern"
},
/* wowl_keepalive shares structures and functions with "mkeep_alive" */
{"wowl_keepalive", wl_mkeep_alive, WLC_GET_VAR, WLC_SET_VAR,
"Send specified keep alive packet periodically in wowl mode.\n"
"\tUsage: wl wowl_keepalive <index0-1> <period> <packet>\n"
"\t\tindex: 0 - 1.\n"
"\t\tperiod: Re-transmission period in milli-seconds. 0 to disable packet transmits.\n"
"\t\tpacket: Hex packet contents to transmit. The packet contents should include "
"the entire ethernet packet (ethernet header, IP header, UDP header, and UDP "
"payload) specified in network byte order.\n"
"\n\te.g. Send keep alive packet every 30 seconds using id-1:\n"
"\twl wowl_keepalive 1 30000 0x0014a54b164f000f66f45b7e08004500001e000040004011c"
"52a0a8830700a88302513c413c4000a00000a0d\n" },
{ "wowl_wakeind", wl_wowl_wakeind, WLC_GET_VAR, WLC_SET_VAR,
"usage: wowl_wakeind [clear]\n"
"Shows last system wakeup event indications from PCI and D11 cores\n"
"clear - Clear the indications"
},
{ "wowl_status", wl_wowl_status, WLC_GET_VAR, -1,
"usage: wowl_status [clear]\n"
"Shows last system wakeup setting"
},
{"wowl_pkt", wl_wowl_pkt, -1, -1,
"Send a wakeup frame to wakup a sleeping STA in WAKE mode\n"
"Usage: wl wowl_pkt <len> <dst ea | bcast | ucast <STA ea>>"
"[ magic [<STA ea>] | net <offset> <pattern> <reason code> ]\n"
"e.g. To send bcast magic frame -- "
"wl wowl_pkt 102 bcast magic 00:90:4c:AA:BB:CC\n"
" To send ucast magic frame -- "
"wl wowl_pkt 102 ucast 00:90:4c:aa:bb:cc magic\n"
" To send a frame with L2 unicast - "
"wl wowl_pkt 102 00:90:4c:aa:bb:cc net 0 0x00904caabbcc 0x03\n"
" NOTE: offset for netpattern frame starts from \"Dest EA\" of ethernet frame."
"So dest ea will be used only when offset is >= 6\n"
" To send a eapol identity frame with L2 unicast - "
"wl wowl_pkt 102 00:90:4c:aa:bb:cc eapid id-string"},
{"wowl_ext_magic", wl_wowl_extended_magic, WLC_GET_VAR, WLC_SET_VAR,
"Set 6-byte extended magic pattern\n"
"Usage: wl wowl_ext_magic 0x112233445566"},
{ "wme_apsd_trigger", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set Periodic APSD Trigger Frame Timer timeout in ms (0=off)"},
{ "wme_autotrigger", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable sending of APSD Trigger frame when all ac are delivery enabled"},
{ "reassoc", wl_reassoc, -1, WLC_REASSOC,
"Initiate a (re)association request.\n"
"\tUsage: wl reassoc <bssid> [options]\n"
"\tOptions:\n"
"\t-c CL, --chanspecs=CL \tchanspecs (comma or space separated list)"},
{ "send_nulldata", wl_iov_mac, -1, -1,
"Sed a null frame to the specified hw address" },
{ "btc_params", wlu_reg2args, WLC_GET_VAR, WLC_SET_VAR, "g/set BT Coex parameters"},
{ "btc_flags", wlu_reg2args, WLC_GET_VAR, WLC_SET_VAR, "g/set BT Coex flags"},
{ "obss_scan_params", wl_obss_scan, WLC_GET_VAR, WLC_SET_VAR,
"set/get Overlapping BSS scan parameters\n"
"Usage: wl obss_scan a b c d e ...; where\n"
"\ta-Passive Dwell, {5-1000TU}, default = 100\n"
"\tb-Active Dwell, {10-1000TU}, default = 20\n"
"\tc-Width Trigger Scan Interval, {10-900sec}, default = 300\n"
"\td-Passive Total per Channel, {200-10000TU}, default = 200\n"
"\te-Active Total per Channel, {20-1000TU}, default = 20\n"
"\tf-Channel Transition Delay Factor, {5-100}, default = 5\n"
"\tg-Activity Threshold, {0-100%}, default = 25"},
{"mkeep_alive", wl_mkeep_alive, WLC_GET_VAR, WLC_SET_VAR,
"Send specified \"mkeep-alive\" packet periodically.\n"
"\tUsage: wl mkeep_alive <index0-3> <period> <packet>\n"
"\t\tindex: 0 - 3.\n"
"\t\tperiod: Re-transmission period in milli-seconds. 0 to disable packet transmits.\n"
"\t\tpacket: Hex packet contents to transmit. The packet contents should include "
"the entire ethernet packet (ethernet header, IP header, UDP header, and UDP "
"payload) specified in network byte order. If no packet is specified, a nulldata frame "
"will be sent instead.\n"
"\n\te.g. Send keep alive packet every 30 seconds using id-1:\n"
"\twl mkeep_alive 1 30000 0x0014a54b164f000f66f45b7e08004500001e000040004011c"
"52a0a8830700a88302513c413c4000a00000a0d" },
{"keep_alive", wl_keep_alive, -1, -1,
"Send specified \"keep-alive\" packet periodically.\n"
"\tUsage: wl keep_alive <period> <packet>\n"
"\t\tperiod: Re-transmission period in milli-seconds. 0 to disable packet transmits.\n"
"\t\tpacket: Hex packet contents to transmit. The packet contents should include "
"the entire ethernet packet (ethernet header, IP header, UDP header, and UDP "
"payload) specified in network byte order.\n"
"\n\te.g. Send keep alive packet every 30 seconds:\n"
"\twl keep_alive 30000 0x0014a54b164f000f66f45b7e08004500001e000040004011c"
"52a0a8830700a88302513c413c4000a00000a0d" },
{ "srchmem", wl_srchmem, WLC_GET_VAR, WLC_SET_VAR,
"g/set ucode srch engine memory"},
{ "pkt_filter_add", wl_pkt_filter_add, -1, -1,
"Install a packet filter.\n"
"\tUsage: wl pkt_filter_add <id> <polarity> <type> <offset> <bitmask> <pattern>\n"
"\tid: Integer. User specified id.\n"
"\ttype: 0 (Pattern matching filter)\n"
"\t 5 (Pattern matching filter with timeout event)\n"
"\toffset: (type 0): Integer offset in received packet to start matching.\n"
"\t (type 5): Integer offset in received packet to start matching.\n"
"\tpolarity: Set to 1 to negate match result. 0 is default.\n"
"\tbitmask: Hex bitmask that indicates which bits of 'pattern' to match.\n"
"\t Must be same size as 'pattern'. Bit 0 of bitmask corresponds\n"
"\t to bit 0 of pattern, etc. If bit N of bitmask is 0, then do\n"
"\t *not* match bit N of the pattern with the received payload. If\n"
"\t bit N of bitmask is 1, then perform match.\n"
"\tpattern: Hex pattern to match. Must be same size as <bitmask>.\n"
"\ttimeout: (type 5): Number of seconds to wait before sending a timeout event when\n"
"\t a matching pattern packet is not received.\n"},
{ "pkt_filter_clear_stats", wl_varint, -1, WLC_SET_VAR,
"Clear packet filter statistic counter values.\n"
"\tUsage: wl pkt_filter_clear_stats <id>" },
{ "pkt_filter_enable", wl_pkt_filter_enable, -1, -1,
"Enable/disable a packet filter.\n"
"\tUsage: wl pkt_filter_enable <id> <0|1>"},
{ "pkt_filter_list", wl_pkt_filter_list, -1, -1,
"List installed packet filters.\n"
"\tUsage: wl pkt_filter_list [val]\n"
"\tval: 0 (disabled filters) 1 (enabled filters)"},
{ "pkt_filter_mode", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set packet filter match action.\n"
"\tUsage: wl pkt_filter_mode <value>\n"
"\tvalue: 1 - Forward packet on match, discard on non-match (default).\n"
"\t 0 - Discard packet on match, forward on non-match." },
{ "pkt_filter_delete", wl_varint, -1, WLC_SET_VAR,
"Uninstall a packet filter.\n"
"\tUsage: wl pkt_filter_delete <id>"},
{ "pkt_filter_stats", wl_pkt_filter_stats, -1, -1,
"Retrieve packet filter statistic counter values.\n"
"\tUsage: wl pkt_filter_stats <id>"},
#ifdef TRAFFIC_MGMT
{"trf_mgmt_config", wl_trf_mgmt_config, WLC_GET_VAR, WLC_SET_VAR,
"Sets/gets traffic management configuration.\n"
"\tUsage: wl trf_mgmt_config [<enable> \n"
"\t [<host IP address> <host IP subnet mask> \n"
"\t <downlink kbps> <uplink kbps> [<flags>]]] \n"
"\tenable: 0 - Disable traffic management\n"
"\t 1 - Enables traffic management (host IP arguments required)\n"
"\tFlag values are the following:\n"
"\t0x0001 : Add DSCP values to tx packets\n"
"\t0x0002 : Disable traffic shaping...just do priority classification\n"
"\nIf no arguments are entered, the current traffic management configuration \n"
"is displayed.\n"
"\ne.g. Configure traffic management and specify local ip addr. and bandwidth data:\n"
"\nwl trf_mgmt_config 1 12.0.0.1 255.0.0.0 5000 650\n"},
{"trf_mgmt_filters_add", wl_trf_mgmt_filters_add, -1, WLC_SET_VAR,
"Adds a traffic management filter.\n"
"\tUsage: wl trf_mgmt_filter_add [dst_port src_port prot priority]\n"
"\tdst_port : Destination TCP or UDP port \n"
"\tsrc_port : Source TCP or UDP port (0 - wildcard for any source port)\n"
"\tprot : L4 protocol (6 - TCP, 17 - UDP)\n"
"\tpriority : Priority value (see trf_mgmt_priority_values enum) \n"
"\ne.g. Add a tcp wildcard filter:\n"
"\nwl trf_mgmt_filters_add 80 0 6 2\n"},
{"trf_mgmt_filters_addex", wl_trf_mgmt_filters_addex, -1, WLC_SET_VAR,
"Adds a traffic management filter.\n"
"\tUsage: wl trf_mgmt_filter_add flag [dst_port src_port prot priority]\n"
"\tUsage: wl trf_mgmt_filter_add flag [dst_mac priority] \n"
"\tFlag values are the following:\n"
"\t0x0000 : filter on tcp/udp src/dst port\n"
"\t0x0001 : filter on destination MAC address\n"
"\t0x0010 : do not update the packet priority \n"
"\t0x0020 : Tag packets as Favored\n"
"\tdst_mac : Destination MAC address \n"
"\tdst_port : Destination TCP or UDP port \n"
"\tsrc_port : Source TCP or UDP port (0 - wildcard for any source port)\n"
"\tprot : L4 protocol (6 - TCP, 17 - UDP)\n"
"\tpriority : Priority value (see trf_mgmt_priority_values enum) \n"
"\ne.g. Add a tcp wildcard filter for all src/dst ports:\n"
"\nwl trf_mgmt_filters_addex 0 0 0 6 2\n"
"\ne.g. Add a dst mac address filter\n"
"\nwl trf_mgmt_filters_addex 0x31 aa:bb:cc:dd:ee:ff 2\n"},
{"trf_mgmt_filters_remove", wl_trf_mgmt_filters_remove, -1, WLC_SET_VAR,
"Removes a traffic management filter.\n"
"\tUsage: wl trf_mgmt_filter_remove [dst_port src_port prot]\n"
"\tdst_port : Destination TCP or UDP port \n"
"\tsrc_port : Source TCP or UDP port (0 - wildcard for any source port)\n"
"\tprot : L4 protocol (6 - TCP, 17 - UDP)\n"
"\ne.g. Remove a tcp wildcard filter:\n"
"\nwl trf_mgmt_filters_remove 80 0 6\n"},
{"trf_mgmt_filters_removeex", wl_trf_mgmt_filters_removeex, -1, WLC_SET_VAR,
"Removes a traffic management filter.\n"
"\tUsage: wl trf_mgmt_filter_remove flag [dst_port src_port prot]\n"
"\tUsage: wl trf_mgmt_filter_remove flag [dst_mac]\n"
"\tFlag values are the following:\n"
"\t0x0000 : filter on tcp/udp src/dst port\n"
"\t0x0001 : filter on destination MAC address\n"
"\t0x0010 : do not update the packet priority \n"
"\t0x0020 : Tag packets as Favored\n"
"\tdst_mac : Destination MAC address \n"
"\tdst_port : Destination TCP or UDP port \n"
"\tsrc_port : Source TCP or UDP port (0 - wildcard for any source port)\n"
"\tprot : L4 protocol (6 - TCP, 17 - UDP)\n"
"\ne.g. Remove a tcp wildcard filter:\n"
"\nwl trf_mgmt_filters_removeex 0 80 0 6\n"
"\nwl trf_mgmt_filters_removeex 0x31 00:90:4c:52:a8:83\n"},
{"trf_mgmt_filters_list", wl_trf_mgmt_filters_list, WLC_GET_VAR, -1,
"Lists all traffic management filters.\n"
"\tUsage: wl trf_mgmt_filter_list\n"},
{"trf_mgmt_filters_clear", wl_trf_mgmt_filters_clear, -1, WLC_SET_VAR,
"Clears all traffic management filters.\n"
"\tUsage: wl trf_mgmt_filters_clear\n"},
{"trf_mgmt_bandwidth", wl_trf_mgmt_bandwidth, WLC_GET_VAR, WLC_SET_VAR,
"Sets/gets traffic management bandwidth configuration.\n"
"\tUsage: wl trf_mgmt_bandwidth \n"
"\t [downlink uplink min_tx_bk min_tx_be min_tx_vi\n"
"\t [min_rx_b min_rx_be min_rx_vi]]\n"
"\tdownlink : downlink bandwidth (kbps)\n"
"\tuplink : uplink bandwidth (kbps)\n"
"\tmin_tx_bk : min. guaranteed tx bandwidth percentage for BK (kbps)\n"
"\tmin_tx_be : min. guaranteed tx bandwidth percentage for BE (kbps)\n"
"\tmin_tx_vi : min. guaranteed tx bandwidth percentage for VI (kbps)\n"
"\n(min_tx_bo + min_tx_be + min_tx_vi) must equal 100.\n"
"\tmin_rx_bk : min. guaranteed rx bandwidth percentage for BK (kbps)\n"
"\tmin_rx_be : min. guaranteed rx bandwidth percentage for BE (kbps)\n"
"\tmin_rx_vi : min. guaranteed rx bandwidth percentage for VI (kbps)\n"
"\n(min_rx_bk + min_rx_be + min_rx_vi) must equal 100."
"\nIf no rx gandwidth arguments are entered, tx bandwidth is used for rx."
"\nIf no arguments are entered, the current bandwidth configuration is displayed."},
{"trf_mgmt_flags", wl_trf_mgmt_flags, WLC_GET_VAR, WLC_SET_VAR,
"Sets/gets traffic management operational flags.\n"
"\tUsage: wl trf_mgmt_flags [flags]\n\n"
"\tFlag values are the following:\n"
"\t0x0001 : Add DSCP values to tx packets\n"
"\t0x0002 : Disable traffic shaping...just do priority classification\n"
"\nIf no arguments are entered, the current operational flags are displayed."},
{"trf_mgmt_stats", wl_trf_mgmt_stats, WLC_GET_VAR, -1,
"Gets traffic management statistics.\n"
"\tUsage: wl trf_mgmt_stats [index]\n"
"\tindex : Queue index \n"},
{"trf_mgmt_stats_clear", wl_trf_mgmt_stats_clear, -1, WLC_SET_VAR,
"Clears traffic management statistics.\n"
"\tUsage: wl trf_mgmt_stats_clear\n"},
{"trf_mgmt_shaping_info", wl_trf_mgmt_shaping_info, WLC_GET_VAR, -1,
"Gets traffic management shaping parameters.\n"
"\tUsage: wl trf_mgmt_shaping_info [index]\n"
"\tindex : Queue index \n"},
#endif /* TRAFFIC_MGMT */
{ "seq_start", wl_seq_start, -1, WLC_SET_VAR,
"Initiates command batching sequence. Subsequent IOCTLs will be queued until\n"
"seq_stop is received."},
{ "seq_stop", wl_seq_stop, -1, WLC_SET_VAR,
"Defines the end of command batching sequence. Queued IOCTLs will be executed."},
{ "seq_delay", wl_varint, -1, WLC_SET_VAR,
"Driver should spin for the indicated amount of time.\n"
"It is only valid within the context of batched commands."},
{ "seq_error_index", wl_varint, WLC_GET_VAR, -1,
"Used to retrieve the index (starting at 1) of the command that failed within a batch"},
#ifdef CLMDOWNLOAD
{ "clmload", wl_clmload, -1, WLC_SET_VAR,
"Used to download CLM data onto the dongle"},
#endif /* CLMDOWNLOAD */
{ "bmac_reboot", wl_var_void, -1, WLC_SET_VAR,
"Reboot BMAC"},
#ifdef RWL_WIFI
{ "findserver", wl_wifiserver, -1, -1,
"Used to find the remote server with proper mac address given by the user,this "
"cmd is specific to wifi protocol."},
#endif
{ "txmcsset", wl_txmcsset, WLC_GET_VAR, -1, "get Transmit MCS rateset for 11N device"},
{ "rxmcsset", wl_rxmcsset, WLC_GET_VAR, -1, "get Receive MCS rateset for 11N device"},
{ "mimo_ss_stf", wl_mimo_stf, WLC_GET_VAR, WLC_SET_VAR,
"get/set SS STF mode.\n"
"\tUsage: wl mimo_ss_stf <value> <-b a | b>\n"
"\tvalue: 0 - SISO; 1 - CDD\n"
"\t-b(band): a - 5G; b - 2.4G"},
#ifdef WLEXTLOG
{ "extlog", wl_extlog, WLC_GET_VAR, -1,
"get external logs\n"
"\tUsage: wl extlog <from_last> <number>\n"
"\tfrom_last: 1 - from the last log record; 0 - whole log recrods\n"
"\tnumber: number of log records to get, MAX is 32."},
{ "extlog_clr", wl_var_void, -1, WLC_SET_VAR, "clear external log records"},
{ "extlog_cfg", wl_extlog_cfg, WLC_GET_VAR, WLC_SET_VAR,
"get/set external log configuration"},
#endif
{ "assertlog", wl_assertlog, WLC_GET_VAR, -1,
"get external assert logs\n"
"\tUsage: wl assertlog"},
{ "assert_type", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"set/get the asset_bypass flag; usage: wl assert_type <1/0> (On/Off)"
},
{ "ledbh", wl_ledbh, WLC_GET_VAR, WLC_SET_VAR,
"set/get led behavior\n"
"\tUsage: wl ledbh [0-3] [0-15]"},
{ "obss_coex_action", wl_obss_coex_action, -1, WLC_SET_VAR,
"send OBSS 20/40 Coexistence Mangement Action Frame\n"
"\tUsage: wl obss_coex_action -i <1/0> -w <1/0> -c <channel list>\n"
"\t -i: 40MHz intolerate bit; -w: 20MHz width Req bit;\n"
"\t -c: channel list, 1 - 14\n"
"\t At least one option must be provided"
},
{"chanim_state", wl_chanim_state, WLC_GET_VAR, -1,
"get channel interference state\n"
"\tUsage: wl chanim_state channel\n"
"\tValid channels: 1 - 14\n"
"\treturns: 0 - Acceptable; 1 - Severe"
},
{"chanim_mode", wl_chanim_mode, WLC_GET_VAR, WLC_SET_VAR,
"get/set channel interference measure (chanim) mode\n"
"\tUsage: wl chanim_mode <value>\n"
"\tvalue: 0 - disabled; 1 - detection only; 2 - detection and avoidance"
},
{ "ledbh", wl_ledbh, WLC_GET_VAR, WLC_SET_VAR, "set/get led behavior\n"
"\tUsage: wl ledbh [0-3] [0-15]"},
{ "led_blink_sync", wl_led_blink_sync, WLC_GET_VAR, WLC_SET_VAR, "set/get led_blink_sync\n"
"\tUsage: wl led_blink_sync [0-3] [0/1]"},
#ifdef BCMWAPI_WAI
{ "wai_restrict", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get WAI restriction"},
{ "wai_rekey", wl_iov_mac, -1, -1,
"set rekey event"},
#endif /* BCMWAPI_WAI */
{"cca_get_stats", wl_cca_get_stats, WLC_GET_VAR, -1,
"Usage: wl cca_stats [-c channel] [-s num seconds][-n]\n"
"\t -c channel: Optional. specify channel. 0 = All channels. Default = current channel \n"
"\t -n: no analysis of results\n"
"\t -s num_seconds: Optional. Default = 10, Max = 60\n"
"\t -i: list individual measurements in addition to the averages\n"
"\t -curband: Only recommend channels on current band"
},
{ "itfr_get_stats", wl_itfr_get_stats, WLC_GET_VAR, -1,
"get interference source information"
},
{ "itfr_enab", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"get/set STA interference detection mode(STA only)\n"
"\t 0 - disable\n"
"\t 1 - enable maual detection\n"
"\t 2 - enable auto detection"
},
{ "itfr_detect", wl_var_void, -1, WLC_SET_VAR,
"issue an interference detection request"
},
{ "smfstats", wl_smfstats, WLC_GET_VAR, WLC_SET_VAR,
"get/clear selected management frame (smf) stats"
"\twl smfstats [-C num]|[--cfg=num] [auth]|[assoc]|[reassoc]|[clear]\n"
"\tclear - to clear the stats" },
#ifdef RWL_DONGLE
{ "dongleset", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable uart driver"
},
#endif
{ "manfinfo", wl_var_getandprintstr, WLC_GET_VAR, -1,
"show chip package info in OTP"},
{ "rrm", wl_rrm, WLC_GET_VAR, WLC_SET_VAR,
"enable or disable RRM feature\n"
"\tUsage: wl rrm [0/1] to disable/enable RRM feature"},
{ "rrm_bcn_req", wl_rrm_bcn_req, -1, WLC_SET_VAR,
"send 11k beacon measurement request\n"
"\tUsage: wl rrm_bcn_req [bcn mode] [da] [duration] [random int] [channel] [ssid]"
" [repetitions]"},
{ "rrm_chload_req", wl_rrm_chload_req, -1, WLC_SET_VAR,
"send 11k channel load measurement request\n"
"\tUsage: wl rrm_chload_req [regulatory] [da] [duration] [random int] [channel]"
" [repetitions]"},
{ "rrm_noise_req", wl_rrm_noise_req, -1, WLC_SET_VAR,
"send 11k noise measurement request\n"
"\tUsage: wl rrm_noise_req [regulatory] [da] [duration] [random int] [channel]"
" [repetitions] "},
{ "rrm_frame_req", wl_rrm_frame_req, -1, WLC_SET_VAR,
"send 11k frame measurement request\n"
"\tUsage: wl rrm_frame_req [regulatory] [da] [duration] [random int] [channel] [ta]"
" [repetitions]"},
{ "rrm_stat_req", wl_rrm_stat_req, -1, WLC_SET_VAR,
"send 11k stat measurement request\n"
"\tUsage: wl rrm_stat_req [da] [random int] [duration] [peer] [group id] [repetitions]"},
{ "rrm_lm_req", wl_rrm_lm_req, -1, WLC_SET_VAR,
"send 11k link measurement request\n"
"\tUsage: wl rrm_lm_req [da]"},
{ "rrm_nbr_req", wl_rrm_nbr_req, -1, WLC_SET_VAR,
"send 11k neighbor report measurement request\n"
"\tUsage: wl rrm_nbr_req [ssid]"},
{ "rrm_nbr_list", wl_rrm_nbr_list, WLC_GET_VAR, -1,
"get 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_list"},
{ "rrm_nbr_del_nbr", wl_rrm_nbr_del_nbr, -1, WLC_SET_VAR,
"delete node from 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_del_nbr [bssid]"},
{ "rrm_nbr_add_nbr", wl_rrm_nbr_add_nbr, -1, WLC_SET_VAR,
"add node to 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_add_nbr [bssid] [bssid info] [regulatory] [channel] [phytype]"},
{ "wnm", wl_wnm, WLC_GET_VAR, WLC_SET_VAR,
"set driver wnm feature mask\n"
"\ttype \'wl msglevel ?\' for values" },
{ "wnm_bsstq", wl_wnm_bsstq, -1, WLC_SET_VAR,
"send 11v BSS transition management query\n"
"\tUsage: wl wnm_bsstq [ssid]"},
{ "tclas_add", wl_tclas_add, -1, WLC_SET_VAR,
"add tclas frame classifier type entry\n"
"\tUsage: wl tclas_add <user priority> <type> <mask> <...>\n"
"\ttype 0 eth2: <src mac> <dst mac> <ether type>\n"
"\ttype 1/4 ipv4: <ver> <src> <dst> <s_port> <d_port> <dscp> <prot>\n"
"\ttype 2 802.1Q: <vlan tag>\n"
"\ttype 3 filter: <offset> <value> <mask>\n"
"\ttype 4 ipv6: <ver> <src> <dst> <s_port> <d_port> <dscp> <nxt_hdr> <flw_lbl>\n"
"\ttype 5 802.1D/Q: <802.1Q PCP> <802.1Q CFI> <802.1Q VID>"
},
{ "tclas_del", wl_tclas_del, -1, WLC_SET_VAR,
"delete tclas frame classifier type entry\n"
"\tUsage: wl tclas_del [<idx> [<len>]]"
},
{ "tclas_list", wl_tclas_list, WLC_GET_VAR, -1,
"list the added tclas frame classifier type entry\n"
"\tUsage: wl tclas_list"
},
#ifdef WLWNM
{ "wnm_tfsreq_add", wl_wnm_tfsreq_add, -1, WLC_SET_VAR,
"add one tfs request element and send tfs request frame\n"
"\tUsage: wl wnm_tfsreq_add <tfs_id> <tfs_action_code> <tfs_subelem_id> <send>\n"
"\ttfs_id: a non-zero value (1 ~ 255)\n"
"\ttfs_action_code bitfield: 1: delete after match, 2: notify\n"
"\ttfs_subelem_id: TFS subelement (0 for none or 1 for previous tclas_add)\n"
"\tsend: 0: store element, 1: send all stored elements"
},
{ "wnm_dms_set", wl_wnm_dms_set, -1, WLC_SET_VAR,
"Optionally add pending DMS desc (after tclas_add) and optionally register all desc\n"
"on AP side to enable the service (with send=1)"
"\tUsage: wl wnm_dms_set <send> [<user_id> [<tc_pro>]]\n"
"\t\tsend: 0: store descriptor, 1: send all stored descs/enable DMS on AP\n"
"\t\tuser_id: new ID to assign to the created desc (if TCLAS added)\n"
"\t\t or existing ID to enable on AP (if no TCLAS added), 0 for all desc\n"
"\t\ttc_pro: TCLAS processing element (if several TCLAS added)"
},
{ "wnm_dms_status", wl_wnm_dms_status, WLC_GET_VAR, -1,
"list all DMS descriptors and provide their internal and AP status\n"
"\tUsage: wl wl_wnm_dms_status"
},
{ "wnm_dms_term", wl_wnm_dms_term, -1, WLC_SET_VAR,
"Disable registered DMS des on AP side and optionally discard them\n"
"\tUsage: wl wnm_dms_term <del> [<user_id>]\n"
"\t\tdel: Discard desc after disabling the service on AP side\n"
"\t\tuser_id: desc to disable/delete, 0 for all desc"
},
{ "wnm_service_term", wl_wnm_service_term, -1, WLC_SET_VAR,
"Disable service. Check specific wnm_XXX_term for more info\n"
"\tUsage: wl wnm_service_term <srv> <service realted params>\n"
"\t\tsrv: 1 for DMS, 2 for FMS, 3 for TFS"
},
{ "wnm_timbc_offset", wl_wnm_timbc_offset, WLC_GET_VAR, WLC_SET_VAR,
"get/set TIM broadcast offset by -32768 period > offset(us) > 32768\n"
"CAUTION!! Due to resource limitation, one radio can have only one set of TIMBC offset\n"
"setting. MBSS need to share the same setting\n"
"\tUsage: wl wnm_timbc_offset <offset> [<tsf_present> [<fix_interval> [<rate_ovreride>]]]\n"
"\t\toffset: in unit of us. Transmit TIM frame in specific TBTT transmit time time\n"
"\t\ttsf_present: can be omitted. If set to 1, timestamp field will present in TIM frame."
"If omitted, default setup to 1\n"
"\t\tfix_interval: can be omitted. If set with non-zero value, override STA request "
"interval in TIM Broadcast request. If omitted, default setup to 0\n"
"\t\trate_override: can be omitted. In unit of 500k, max setup to 108. If set, override"
"override high rate used to transmit TIM broadcast high rate frame"
},
{ "wnm_timbc_set", wl_wnm_timbc_set, -1, WLC_SET_VAR,
"Enable/disable TIM Broadcast. Station will send appropriate request if AP suport TIMBC\n"
"\tUsage: wl wnm_timbc_set <interval> [<flags> [<min_rate> [<max_rate>]]]\n"
"\t\tinterval: Beacon interval requested for TIM frames, 0 to disable TIM BC\n"
"\t\tflags: Bitfield with minimal requirements to keep the service enabled (check doc)\n"
"\t\tmin_rate: Minimal rate requirement, in Mbps, for TIM high or low rate frames\n"
"\t\tmax_rate: Maximal rate requirement"
},
{ "wnm_timbc_status", wl_wnm_timbc_status, WLC_GET_VAR, -1,
"Retrieve TIM Broadcast configuration set with current AP"
},
{ "wnm_maxidle", wl_wnm_maxidle, WLC_GET_VAR, WLC_SET_VAR,
"setup WNM BSS Max Idle Period interval and option\n"
"\tUsage: wl wnm_maxidle <Idle Period> <Option>\n"
"\tIdle Period: in unit of 1000TU(1.024s)\n"
"\tOption: protected keep alive required(0 ~ 1)"
},
{ "wnm_bsstrans_query", wl_wnm_bsstq, -1, WLC_SET_VAR,
"send 11v BSS transition management query\n"
"\tUsage: wl wnm_bsstrans_query [ssid]"},
{ "wnm_bsstrans_req", wl_wnm_bsstrans_req, -1, WLC_SET_VAR,
"send BSS transition management request frame with BSS termination included bit set\n"
"\tUsage: wl wnm_bsstrans_req <reqmode> <tbtt> <dur> [unicast]\n"
"\treqmode: request mode of BSS transition request\n"
"\ttbtt: time of BSS to end of life, in unit of TBTT, max to 65535\n"
"\tdur: time of BSS to keep off, in unit of minute, max to 65535\n"
"\tunicast: [1|0] unicast or broadcast to notify STA in BSS. Default in unicast.\n"
},
{ "wnm_keepalives_max_idle", wl_wnm_keepalives_max_idle, -1, -1,
"\tUsage: wl wnm_keepalives_max_idle <keepalives_per_bss_max_idle> <mkeepalive_index>"
" [<max_interval>]\n"
"set/get the number of keepalives, mkeep-alive index and max_interval configured"
" per BSS-Idle period.\n"},
#endif /* WLWNM */
#ifdef WLP2P
{ "p2p_ssid", wl_ssid, -1, WLC_SET_VAR,
"set WiFi P2P wildcard ssid.\n"
"\tUsage: wl p2p_ssid <ssid>"
},
{ "p2p_state", wl_p2p_state, -1, WLC_SET_VAR,
"set WiFi P2P discovery state.\n"
"\tUsage: wl p2p_state <state> [<chanspec> <dwell time>]"
},
{ "p2p_scan", wl_p2p_scan, -1, WLC_SET_VAR,
"initiate WiFi P2P scan.\n"
"\tUsage: wl p2p_scan S|E <scan parms>\n"
SCAN_USAGE
},
{ "p2p_ifadd", wl_p2p_ifadd, -1, WLC_SET_VAR,
"add WiFi P2P interface\n"
"\tUsage: wl p2p_ifadd <MAC-address> go|client|dyngo [chanspec]\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_ifdel", wl_p2p_ifdel, -1, WLC_SET_VAR,
"delete WiFi P2P interface\n"
"\tUsage: wl p2p_ifdel <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_ifupd", wl_p2p_ifupd, -1, WLC_SET_VAR,
"update an interface to WiFi P2P interface\n"
"\tUsage: wl p2p_ifupd <MAC-address> go|client\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_if", wl_p2p_if, WLC_GET_VAR, -1,
"query WiFi P2P interface bsscfg index\n"
"\tUsage: wl p2p_if <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_noa", wl_p2p_noa, WLC_GET_VAR, WLC_SET_VAR,
"set/get WiFi P2P NoA schedule\n"
"\tUsage: wl p2p_noa <type> <type-specific-params>\n"
"\t\ttype 0: Scheduled Absence (on GO): <type> <action> <action-specific-params>\n"
"\t\t\taction -1: Cancel the schedule: <type> <action>\n"
"\t\t\taction 0,1,2: <type> <action> <option> <option-specific-params>\n"
"\t\t\t\taction 0: Do nothing during absence periods\n"
"\t\t\t\taction 1: Sleep during absence periods\n"
"\t\t\t\toption 0: <start:tsf> <interval> <duration> <count> ...\n"
"\t\t\t\toption 1 [<start-percentage>] <duration-percentage>\n"
"\t\t\t\toption 2 <start:tsf-offset> <interval> <duration> <count>\n"
"\t\ttype 1: Requested Absence (on GO): \n"
"\t\t\taction -1: Cancel the schedule: <type> <action>\n"
"\t\t\taction 2: <type> <action> <option> <option-specific-params>\n"
"\t\t\t\taction 2: Turn off GO beacons and probe responses during absence period\n"
"\t\t\t\toption 2 <start:tsf-offset> <interval> <duration> <count>"
},
{ "p2p_ops", wl_p2p_ops, WLC_GET_VAR, WLC_SET_VAR,
"set/get WiFi P2P OppPS and CTWindow\n"
"\tUsage: wl p2p_ops <ops> [<ctw>]\n"
"\t\t<ops>:\n"
"\t\t\t0: Disable OppPS\n"
"\t\t\t1: Enable OppPS\n"
"\t\t<ctw>:\n"
"\t\t\t10 and up to beacon interval"
},
{ "p2p_da_override", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set WiFi P2P device interface addr\n"
"\tUsage: wl p2p_da_override <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx\n"
"(When MAC-address is set to 00:00:00:00:00:00, default da restored)"
},
#endif /* WLP2P */
{ "pm_dur", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Retrieve accumulated PM duration information (GET only)\n"
},
{ "mpc_dur", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Retrieve accumulated MPC duration information in ms (GET) or clear accumulator (SET)\n"
"\tUsage: wl mpc_dur <any-number-to-clear>"},
{"chanim_acs_record", wl_chanim_acs_record, WLC_GET_VAR, -1,
"get the auto channel scan record. \n"
"\t Usage: wl acs_record"
},
{"chanim_stats", wl_chanim_stats, WLC_GET_VAR, -1,
"get chanim stats \n"
"\t Usage: wl chanim_stats"
},
{"txdelay_params", wl_txdelay_params, WLC_GET_VAR, -1,
"get chanim stats \n"
"\t Usage: wl txdelay_params ratio cnt period tune"
},
{"intfer_params", wl_intfer_params, WLC_GET_VAR, WLC_SET_VAR,
"set/get intfer params \n"
"\tUsage: wl intfer_params period (in sec) cnt(0~4) txfail_thresh tcptxfail_thresh\n"
"\tperiod=0: disable Driver monitor txfail"
},
{ "dngl_wd", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"enable or disable dongle keep alive watchdog timer\n"
"\tUsage: wl dngl_wd 0\\1 (to turn off\\on)"},
{ "tsf", wl_tsf, WLC_GET_VAR, WLC_SET_VAR,
"set/get tsf register\n"
"\tUsage: wl tsf [<high> <low>]"},
{ "tpc_mode", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/disable AP TPC.\n"
"Usage: wl tpc_mode <mode> \n"
"\t0 - disable, 1 - BSS power control, 2 - AP power control, 3 - Both (1) and (2)"},
{ "tpc_period", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set AP TPC periodicity in secs.\n"
"Usage: wl tpc_period <secs> "},
{ "tpc_lm", wl_tpc_lm, WLC_GET_VAR, -1,
"Get current link margins."},
{ "mfp_config", wl_mfp_config, -1, WLC_SET_VAR,
"Config PMF capability\n"
"\tusage: wl mfp 0/disable, 1/capable, 2/requred" },
{ "mfp_sha256", wl_mfp_sha256, WLC_GET_VAR, WLC_SET_VAR,
"Config SHA256 capability\n"
"\tusage: wl sha256 0/disable, 1/enable" },
{ "mfp_sa_query", wl_mfp_sa_query, -1, WLC_SET_VAR,
"Send a sa query req/resp to a peer\n"
"\tusage: wl mfp_sa_query flag action id" },
{ "mfp_disassoc", wl_mfp_disassoc, WLC_GET_VAR, WLC_SET_VAR,
"send bogus disassoc\n"
"\tUsage: wl mfp_disassoc"},
{ "mfp_deauth", wl_mfp_deauth, WLC_GET_VAR, WLC_SET_VAR,
"send bogus deauth\n"
"\tUsage: wl mfp_dedauth"},
{ "mfp_assoc", wl_mfp_assoc, WLC_GET_VAR, WLC_SET_VAR,
"send assoc\n"
"\tUsage: wl mfp_assoc"},
{ "mfp_auth", wl_mfp_auth, WLC_GET_VAR, WLC_SET_VAR,
"send auth\n"
"\tUsage: wl mfp_auth"},
{ "mfp_reassoc", wl_mfp_reassoc, WLC_GET_VAR, WLC_SET_VAR,
"send reassoc\n"
"\tUsage: wl mfp_reassoc"},
{ "monitor_lq", wl_monitor_lq, WLC_GET_VAR, WLC_SET_VAR,
"Start/Stop monitoring link quality metrics - RSSI and SNR\n"
"\tUsage: wl monitor_lq <0: turn off / 1: turn on"},
{ "monitor_lq_status", wl_dump_lq, WLC_GET_VAR, -1 /* Set not reqd */,
"Returns averaged link quality metrics - RSSI and SNR values"},
{ "mac_rate_histo", wl_mac_rate_histo, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl mac_rate_histo <mac address> <access category> <num_pkts>\n"
"\t(MAC address e.g. 00:11:20:11:33:33)\n"
"\t(Access Category(AC) - 0x10:for entire MAC or 0x4:for video AC for this MAC)\n"
"\t(num_pkts (optional) - Number of packets to average - max 64 for AC 0x10,"
" max 32 for AC 0x4)"
},
{ "rpmt", wl_rpmt, -1, WLC_SET_VAR, "rpmt <pm1-to> <pm0-to>"},
#ifdef WLTDLS
{ "tdls_endpoint", wl_tdls_endpoint, WLC_GET_VAR, WLC_SET_VAR,
"Available TDLS operations to each TDLS peer.\n"
"\tusage: wl tdls_endpoint <disc, create, delete, PM, wake, cw> <ea> [chanspec]\n"
"\t [chanspec] only applies to 'cw' operaton.\n\n"
"\t addendum:\n"
"\t wl tdls_endpoint wfd_disc <ea> sends a WFD tunneled Probe Request"},
{ "tdls_wfd_ie", wl_tdls_wfd_ie, WLC_GET_VAR, -1,
"To set, get and clear additional WFD IE in setup_req and setup_resp\n"
"\tTo set2, get2 and clear2 additional WFD IE in tunneled probe_req and probe_resp\n"
"\tusage: wl tdls_wfd_ie get <own|peer_eth_addr#> [ip] [port]\n"
"\t wl tdls_wfd_ie get2 <own|peer_eth_addr#> [alt_mac] [port] [PC_bit]\n"
"\t\t peer_eth_addr#: HH:HH:HH:HH:HH:HH\n"
"\t\t and peer must be TDLS connected (only in case of setup)\n\n"
"\t wl tdls_wfd_ie <clr|clr2> own\n\n"
"\t wl tdls_wfd_ie set own wfd_ie_hexa_string [ip# [port# [type# [bssid#]]]]\n"
"\t wl tdls_wfd_ie set2 own wfd_ie_hexa_string [alt_mac# [port# [type#]]]\n"
"\t\t wfd_ie_hexa_string: should start with the full WFD IE header\n"
"\t\t e.g. 0xDDXX506F9A0A...\n"
"\t\t ip#: XXX.XXX.XXX.XXX\n"
"\t\t alt_mac#: HH:HH:HH:HH:HH:HH\n"
"\t\t port#: 0-65535\n"
"\t\t type#: 0 for source, 1 for primary sink\n"
"\t\t bssid#: HH:HH:HH:HH:HH:HH"},
#endif /* WLTDLS */
{ "spatial_policy", wl_spatial_policy, WLC_GET_VAR, WLC_SET_VAR,
"set/get spatial_policy\n"
"\tUsage: wl spatial_policy <-1: auto / 0: turn off / 1: turn on>\n"
"\t to control individual band/sub-band use\n"
"\t wl spatial_policy a b c d e\n"
"\t where a is 2.4G band setting\n"
"\t where b is 5G lower band setting\n"
"\t where c is 5G middle band setting\n"
"\t where d is 5G high band setting\n"
"\t where e is 5G upper band setting"},
{ "ie", wl_ie, WLC_GET_VAR, WLC_SET_VAR,
"set/get IE\n"
"\tUsage: For set: wl ie type length hexdata\n"
"\t For get: wl ie type" },
{ "wnm_url", wl_wnm_url, WLC_GET_VAR, WLC_SET_VAR,
"set/get wnm session information url\n"
"Usage for set: wl wnm_url length urlstring\n"
"Usage for get: wl wnm_url" },
{ "ratetbl_ppr", wl_ratetbl_ppr, WLC_GET_VAR, WLC_SET_VAR,
"Usage: For get: wl ratetbl_ppr\n"
"\t For set: wl ratetbl_ppr <rate> <ppr>" },
{ "wowl_wakeup_reason", wl_wowl_wake_reason, WLC_GET_VAR, -1 /* Set not reqd */,
"Returns pattern id and associated wakeup reason"},
{ "mempool", wlu_mempool, WLC_GET_VAR, -1,
"Get memory pool statistics" },
#ifdef SR_DEBUG
{ "sr_dump_pmu", wl_dump_pmu, WLC_GET_VAR, WLC_SET_VAR,
"Dump value of PMU registers"},
{ "sr_pmu_keep_on", wl_pmu_keep_on, WLC_GET_VAR, WLC_SET_VAR,
"Keep resource on"},
{ "sr_skip", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Skip Save Restore Operation"},
{ "sr_power_island", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Keep power islands on/off"},
#endif /* SR_DEBUG */
#ifdef UC_FATAL
{ "uc_fatal", wl_varint, WLC_GET_VAR, -1,
"dump ucode info"},
#endif /* UC_FATAL */
#ifdef WLNDOE
{ "nd_hostip", wl_hostipv6, WLC_GET_VAR, WLC_SET_VAR,
"Add a local host-ipv6 address or display them"},
{ "nd_solicitip", wl_solicitipv6, WLC_GET_VAR, WLC_SET_VAR,
"Add a local host solicit ipv6 address or display them"},
{ "nd_remoteip", wl_remoteipv6, WLC_GET_VAR, WLC_SET_VAR,
"Add a local remote ipv6 address or display them"},
{ "nd_status", wl_ndstatus, WLC_GET_VAR, -1,
"Displays Neighbor Discovery Status"},
{ "nd_hostip_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear all host-ip addresses"},
{ "nd_macaddr", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Get/set the MAC address for offload" },
{ "nd_status_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear neighbor discovery status"},
#endif
{ "antdiv_bcnloss", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"0 - Disable Rx antenna flip feature based on consecutive beacon loss\n"
"\tX - beacon loss count after which Rx ant will be flipped\n"
"\tUsage: wl antdiv_bcnloss <beaconloss_count>\n"
},
{ "lpc_params", wl_power_sel_params, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get Link Power Control params\n"
"\tUsage: wl powersel_params <tp_ratio_thresh> <rate_stab_thresh>\n"
"\t\t<pwr_stab_thresh> <pwr_sel_exp_time>\n"},
{ "mode_reqd", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get operational capabilities required for STA to associate to the BSS "
"supported by the interface.\n"
"\tUsage: wl [-i ifname] mode_reqd [value]\n"
"\t wl mode_reqd [-C bss_idx ] [value]\n"
"\t\t <ifname> is the name of the interface corresponding to the BSS.\n"
"\t\t\t If the <ifname> is not given, the primary BSS is assumed.\n"
"\t\t <bss_idx> is the the BSS configuration index.\n"
"\t\t\t If the <bss_idx> is not given, configuraion #0 is assumed\n"
"\t\t <value> is the numeric values in the range [0..3]\n"
"\t\t 0 - no requirements on joining devices.\n"
"\t\t 1 - devices must advertise ERP (11g) capabilities to be allowed to associate\n"
"\t\t\t to a 2.4 GHz BSS.\n"
"\t\t 2 - devices must advertise HT (11n) capabilities to be allowed to associate\n"
"\t\t\t to a BSS.\n"
"\t\t 3 - devices must advertise VHT (11ac) capabilities to be allowed to associate\n"
"\t\t\t to a BSS.\n"
"\tThe command returns an error if the BSS interface is up.\n"
"\tThis configuration can only be changed while the BSS interface is down.\n"
"\tNote that support for HT implies support for ERP,\n"
"\tand support for VHT implies support for HT."},
{ "nar_clear_dump", wl_var_void, -1, WLC_SET_VAR,
"Clear non-aggregated regulation counters"},
{ "sar_limit", wl_sarlimit, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get sar_limit\n"
"\tusage: (set) sar_limit <2Gcore0 2Gcore1 2Gcore2 2Gcore3 5G[0]core0 5G[0]core1...>\n"
"\t (get) sar_limit, return sar limit table\n"
"\tunit: all input/output values are absolute and in unit of qdbm\n"
},
{ "bmon_bssid", wl_bmon_bssid, WLC_GET_VAR, WLC_SET_VAR,
"Set monitored BSSID\n"
"\tusage: bmon_bssid xx:xx:xx:xx:xx:xx 0|1\n"},
#ifdef EVENT_LOG_COMPILE
{ "event_log_set_init", wl_event_log_set_init, -1, WLC_SET_VAR,
"Initialize an event log set\n"
"\tUsage: wl event_log_set_init <set> <size>\n"},
{ "event_log_set_expand", wl_event_log_set_expand, -1, WLC_SET_VAR,
"Increase the size of an event log set\n"
"\tUsage: wl event_log_set_expand <set> <size>\n"},
{ "event_log_set_shrink", wl_event_log_set_shrink, -1, WLC_SET_VAR,
"Decrease the size of an event log set\n"
"\tUsage: wl event_log_set_expand <set>\n"},
{ "event_log_tag_control", wl_event_log_tag_control, -1, WLC_SET_VAR,
"Modify the state of an event log tag\n"
"\tUsage: wl event_log_tag_control <tag> <set> <flags>\n"},
#endif /* EVENT_LOG_COMPILE */
{ "rmc_ackmac", wl_mcast_ackmac, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get ACK required multicast mac address\n"
"\tusage: wl rmc_ackmac -i [index] -t [multicast mac address]\n"
},
{ "rmc_ackreq", wl_mcast_ackreq, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get ACK rmc_mode 0 disable, 1 enable transmitter, 2 enable initiator \n"
"\tusage: wl rmc_ackreq [mode] \n"
},
{ "rmc_txrate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get a fixed transmit rate for the reliable multicast:\n"
"\tvalid values for 802.11ac are (6, 9, 12, 18, 24, 36, 48, 54)\n"
"\t-1 (default) means automatically determine the best rate\n"
},
{ "rmc_status", wl_mcast_status, WLC_GET_VAR, -1,
"Display reliable multicast client status\n"
},
{ "rmc_actf_time", wl_mcast_actf_time, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get mcast action frame tx time period in ms\n"
"\tusage: wl rmc_actf_time [value] \n"
},
{ "rmc_ar_timeout", wl_mcast_ar_timeout, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get rmc active receiver timeout in ms\n"
"\tusage: wl rmc_ar_timeout [duration in ms] \n"
},
{ "rmc_rssi_thresh", wl_mcast_rssi_thresh, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get minimum rssi needed for a station to be an active receiver\n"
"\tusage: wl rmc_rssi_thresh [value] \n"
},
{ "rmc_stats", wl_mcast_stats, WLC_GET_VAR, WLC_SET_VAR,
"Display/Clear reliable multicast client statistical counters\n"
"\tusage: wl rmc_stats [arg] \n"
},
{ "rmc_rssi_delta", wl_mcast_rssi_delta, WLC_GET_VAR, WLC_SET_VAR,
"Display/Set RSSI delta to switch receive leader\n"
"\tusage: wl rmc_rssi_delta [arg] \n"
},
{ "rmc_vsie", wl_mcast_vsie, WLC_GET_VAR, WLC_SET_VAR,
"Display/Set vendor specific IE contents\n"
"\tusage: wl rmc_vsie [OUI] [Data] \n"
},
{ "pm2_sleep_ret_ext", wl_sleep_ret_ext, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set Dynamic Fast Return To Sleep params"},
{ "sta_monitor", wl_stamon_sta_config, WLC_GET_VAR, WLC_SET_VAR,
"wl sta_monitor [<add/del> <xx:xx:xx:xx:xx:xx>]"},
{ "monitor_promisc_level", wl_monitor_promisc_level, WLC_GET_VAR, WLC_SET_VAR,
"Set a bitmap of different MAC promiscuous level of monitor mode.\n\n"
MONITOR_PROMISC_LEVEL_USAGE},
#if defined(DWDS)
{ "dwds_config", wl_dwds_config, -1, WLC_SET_VAR,
"wl dwds_config <enable/disable> <sta/ap> <xx:xx:xx:xx:xx:xx>"},
#endif
{ "aibss_bcn_force_config", wl_aibss_bcn_force_config, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set AIBSS beacon force configuration \n"
"wl aibss_bcn_force_config <initial_min_bcn_dur,min_bcn_dur,initial_bcn_flood_dur>\n"},
{"bcnlenhist", wl_bcnlenhist, WLC_GET_VAR, -1,
"Usage: wl bcnlenhist [0]"},
{ "wds_type", wl_varint, WLC_GET_VAR, -1,
"Indicate whether the interface to which this IOVAR is sent is of WDS or DWDS type.\n\n"
WDS_TYPE_USAGE},
{ "bss_peer_info", wl_bss_peer_info, WLC_GET_VAR, -1,
"Get BSS peer info of all the peer's in the indivudual interface\n"
"\tIf a non-zero MAC address is specified, gets the peer info of the PEER alone\n"
"\tUsage: wl bss_peer_info [MAC address]"},
{ "bssload_static", wl_bssload_static, WLC_GET_VAR, WLC_SET_VAR,
"get or set static BSS load\n"
"\tusage: wl bssload_static [off | <sta_count> <chan_util> <acc>]\n"},
{ "aibss_txfail_config", wl_aibss_txfail_config, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get txfail configuration for bcn_timeout and max tx retries\n"
"\tUsage: wl aibss_txfail_config [bcn_timeout, max_retry]"},
#ifdef TBTT_OFFSET_STAT
{ "tbtt_offset_stat", wl_tbtt_offset_stat, WLC_GET_VAR, -1,
"Get TBTT offset stat (only for STA mode)\n"
"\tUsage: wl tbtt_offset_stat\n"},
#endif /* TBTT_OFFSET_STAT */
#endif
{ NULL, NULL, 0, 0, NULL }
};
cmd_t wl_varcmd = {"var", wl_varint, -1, -1, "unrecognized name, type -h for help"};
#ifdef WC_TOOL
/* Include any commands for wc tool used for WMM
* These need to be only the command names from port_cmds and wl_cmds array
*/
static const char *wc_cmds[] = {
"ver", "cmds", "up", "down",
"gmode", "listen", "wme", "wme_ac", "wme_apsd",
"wme_apsd_sta", "wme_dp"
};
#else
static const char **wc_cmds = NULL;
#endif /* WC_TOOL */
/* initialize stuff needed before processing the command */
void
wl_cmd_init(void)
{
int_fmt = INT_FMT_DEC;
g_wlc_idx = -1;
}
void
wlu_init(void)
{
/* Init global variables at run-time, not as part of the declaration.
* This is required to support init/de-init of the driver. Initialization
* of globals as part of the declaration results in non-deterministic
* behaviour since the value of the globals may be different on the
* first time that the driver is initialized vs subsequent initializations.
*/
int_fmt = INT_FMT_DEC;
g_wlc_idx = -1;
batch_in_client = FALSE;
init_cmd_batchingmode();
}
int
wl_check(void *wl)
{
int ret;
int val;
if ((ret = wlu_get(wl, WLC_GET_MAGIC, &val, sizeof(int))) < 0)
return ret;
/* Detect if IOCTL swapping is necessary */
if (val == (int)bcmswap32(WLC_IOCTL_MAGIC))
{
val = bcmswap32(val);
g_swap = TRUE;
}
if (val != WLC_IOCTL_MAGIC)
return -1;
if ((ret = wlu_get(wl, WLC_GET_VERSION, &val, sizeof(int))) < 0)
return ret;
ioctl_version = dtoh32(val);
if (ioctl_version != WLC_IOCTL_VERSION &&
ioctl_version != 1) {
fprintf(stderr, "Version mismatch, please upgrade. Got %d, expected %d or 1\n",
ioctl_version, WLC_IOCTL_VERSION);
return -1;
}
return 0;
}
static int
ARGCNT(char **argv)
{
int i;
for (i = 0; argv[i] != NULL; i ++)
;
return i;
}
/* parse/validate the command line arguments */
/*
* pargv is updated upon return if the first argument is an option.
* It remains intact otherwise.
*/
int
wl_option(char ***pargv, char **pifname, int *phelp)
{
char *ifname = NULL;
int help = FALSE;
int status = CMD_OPT;
char **argv = *pargv;
while (*argv) {
/* select different adapter */
if (!strcmp(*argv, "-a") || !strcmp(*argv, "-i")) {
char *opt = *argv++;
ifname = *argv;
if (!ifname) {
fprintf(stderr,
"error: expected interface name after option %s\n", opt);
status = CMD_ERR;
break;
}
}
/* integer output format */
else if (!strcmp(*argv, "-d"))
int_fmt = INT_FMT_DEC;
else if (!strcmp(*argv, "-u"))
int_fmt = INT_FMT_UINT;
else if (!strcmp(*argv, "-x"))
int_fmt = INT_FMT_HEX;
/* command usage */
else if (!strcmp(*argv, "-h") || !strcmp(*argv, "--help"))
help = TRUE;
else if (!strcmp(*argv, "--clientbatch")) {
wl_seq_batch_in_client(TRUE);
}
/* To handle endian mis-matches between wl utility and wl driver */
else if (!strcmp(*argv, "--es")) {
g_swap = TRUE;
}
else if (!stricmp(*argv, "-W") || !strcmp(*argv, "--wlc")) {
char *opt = *argv++;
char *endptr = NULL;
if (*argv) {
g_wlc_idx = strtol(*argv, &endptr, 0);
}
if (endptr == *argv) {
fprintf(stderr,
"error: expected wlc integer index after option %s\n", opt);
status = CMD_ERR;
/* just to ensure that we trigger error */
argv--;
break;
}
}
/* start of non wl options */
else {
status = CMD_WL;
break;
}
/* consume the argument */
argv ++;
break;
}
*phelp = help;
*pifname = ifname;
*pargv = argv;
return status;
}
void
wl_cmd_usage(FILE *fid, cmd_t *cmd)
{
if (strlen(cmd->name) >= 8)
fprintf(fid, "%s\n\t%s\n\n", cmd->name, cmd->help);
else
fprintf(fid, "%s\t%s\n\n", cmd->name, cmd->help);
}
static int
wl_print_deprecate(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(argv);
wl_cmd_usage(stderr, cmd); /* warning string is in command table */
return 0;
}
/* Dump out short list of commands */
static int
wl_list(void *wl, cmd_t *garb, char **argv)
{
cmd_t *cmd;
int nrows, i, len;
char *list_buf;
int letter, col, row, pad;
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(garb);
UNUSED_PARAMETER(argv);
for (cmd = wl_cmds, nrows = 0; cmd->name; cmd++)
/* Check for wc_cmd */
if (wc_cmd_check(cmd->name))
nrows++;
nrows /= 4;
nrows++;
len = nrows * 80 + 2;
list_buf = malloc(len);
if (list_buf == NULL) {
fprintf(stderr, "Failed to allocate buffer of %d bytes\n", len);
return BCME_NOMEM;
}
for (i = 0; i < len; i++)
*(list_buf+i) = 0;
row = col = 0;
for (letter = 'a'; letter < 'z'; letter++) {
for (cmd = wl_cmds; cmd->name; cmd++) {
/* Check for wc_cmd */
if (!wc_cmd_check(cmd->name))
continue;
if (cmd->name[0] == letter || cmd->name[0] == letter - 0x20) {
strcat(list_buf+row*80, cmd->name);
pad = 18 * (col + 1) - strlen(list_buf+row*80);
if (pad < 1)
pad = 1;
for (; pad; pad--)
strcat(list_buf+row*80, " ");
row++;
if (row == nrows) {
col++; row = 0;
}
}
}
}
for (row = 0; row < nrows; row++)
printf("%s\n", list_buf+row*80);
printf("\n");
free(list_buf);
return (0);
}
void
wl_cmds_usage(FILE *fid, cmd_t *port_cmds)
{
cmd_t *port_cmd;
cmd_t *cmd;
/* print usage of port commands */
for (port_cmd = port_cmds; port_cmd && port_cmd->name; port_cmd++)
/* Check for wc_cmd */
if (wc_cmd_check(port_cmd->name))
wl_cmd_usage(fid, port_cmd);
/* print usage of common commands without port counterparts */
for (cmd = wl_cmds; cmd->name; cmd++) {
/* search if port counterpart exists */
for (port_cmd = port_cmds; port_cmd && port_cmd->name; port_cmd++)
if (!strcmp(port_cmd->name, cmd->name))
break;
/* Also, check for this being a wc_cmd */
if ((!port_cmd || !port_cmd->name) && (wc_cmd_check(cmd->name)))
wl_cmd_usage(fid, cmd);
}
}
void
wl_usage(FILE *fid, cmd_t *port_cmds)
{
fprintf(fid, "Usage: %s [-a|i <adapter>]"
" [-h] [-d|u|x] [-w|--wlc <index>] <command> [arguments]\n", wlu_av0);
fprintf(fid, "\n");
fprintf(fid, " -h this message and command descriptions\n");
fprintf(fid, " -h [cmd] command description for cmd\n");
fprintf(fid, " -a, -i adapter name or number\n");
fprintf(fid, " -d output format signed integer\n");
fprintf(fid, " -u output format unsigned integer\n");
fprintf(fid, " -x output format hexdecimal\n");
fprintf(fid, " -w <idx> index of WLC for RSDB only\n");
fprintf(fid, "\n");
wl_cmds_usage(fid, port_cmds);
}
void
wl_printint(int val)
{
switch (int_fmt) {
case INT_FMT_UINT:
printf("%u\n", val);
break;
case INT_FMT_HEX:
printf("0x%x\n", val);
break;
case INT_FMT_DEC:
default:
printf("%d\n", val);
break;
}
}
/* Common routine to check for an option arg specifying the configuration index.
* Takes the syntax -C num, --cfg=num, --config=num, or --configuration=num
* Returns BCME_BADARG if there is a command line parsing error.
* Returns 0 if no error, and sets *consumed to the number of argv strings
* used. Sets *bsscfg_idx to the index to use. Will set *bsscfg_idx to zero if there
* was no config arg.
*/
static int
wl_cfg_option(char **argv, const char *fn_name, int *bsscfg_idx, int *consumed)
{
miniopt_t mo;
int opt_err;
*bsscfg_idx = 0;
*consumed = 0;
miniopt_init(&mo, fn_name, NULL, FALSE);
/* process the first option */
opt_err = miniopt(&mo, argv);
/* check for no args or end of options */
if (opt_err == -1)
return 0;
/* check for no options, just a positional arg encountered */
if (mo.positional)
return 0;
/* check for error parsing options */
if (opt_err == 1)
return BCME_USAGE_ERROR;
/* check for -C, --cfg=X, --config=X, --configuration=X */
if (mo.opt == 'C' ||
!strcmp(mo.key, "cfg") ||
!strcmp(mo.key, "config") ||
!strcmp(mo.key, "configuration")) {
if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an integer for the configuartion index\n",
fn_name, mo.valstr);
return BCME_BADARG;
}
*bsscfg_idx = mo.val;
*consumed = mo.consumed;
}
return 0;
}
static int
wl_void(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(argv);
if (cmd->set < 0)
return -1;
return wlu_set(wl, cmd->set, NULL, 0);
}
int
wl_int(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char *endptr = NULL;
if (!*++argv) {
if (cmd->get == -1)
return -1;
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(int))) < 0)
return ret;
val = dtoh32(val);
wl_printint(val);
} else {
if (cmd->set == -1)
return -1;
if (!stricmp(*argv, "on"))
val = 1;
else if (!stricmp(*argv, "off"))
val = 0;
else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
val = htod32(val);
ret = wlu_set(wl, cmd->set, &val, sizeof(int));
}
return ret;
}
/* Return a new chanspec given a legacy chanspec
* Returns INVCHANSPEC on error
*/
static chanspec_t
wl_chspec_from_legacy(chanspec_t legacy_chspec)
{
chanspec_t chspec;
/* get the channel number */
chspec = LCHSPEC_CHANNEL(legacy_chspec);
/* convert the band */
if (LCHSPEC_IS2G(legacy_chspec)) {
chspec |= WL_CHANSPEC_BAND_2G;
} else {
chspec |= WL_CHANSPEC_BAND_5G;
}
/* convert the bw and sideband */
if (LCHSPEC_IS20(legacy_chspec)) {
chspec |= WL_CHANSPEC_BW_20;
} else {
chspec |= WL_CHANSPEC_BW_40;
if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
chspec |= WL_CHANSPEC_CTL_SB_L;
} else {
chspec |= WL_CHANSPEC_CTL_SB_U;
}
}
if (wf_chspec_malformed(chspec)) {
fprintf(stderr, "wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n",
chspec);
return INVCHANSPEC;
}
return chspec;
}
/* Return a legacy chanspec given a new chanspec
* Returns INVCHANSPEC on error
*/
chanspec_t
wl_chspec_to_legacy(chanspec_t chspec)
{
chanspec_t lchspec;
if (wf_chspec_malformed(chspec)) {
fprintf(stderr, "wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n",
chspec);
return INVCHANSPEC;
}
/* get the channel number */
lchspec = CHSPEC_CHANNEL(chspec);
/* convert the band */
if (CHSPEC_IS2G(chspec)) {
lchspec |= WL_LCHANSPEC_BAND_2G;
} else {
lchspec |= WL_LCHANSPEC_BAND_5G;
}
/* convert the bw and sideband */
if (CHSPEC_IS20(chspec)) {
lchspec |= WL_LCHANSPEC_BW_20;
lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
} else if (CHSPEC_IS40(chspec)) {
lchspec |= WL_LCHANSPEC_BW_40;
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
} else {
lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
}
} else {
/* cannot express the bandwidth */
char chanbuf[CHANSPEC_STR_LEN];
fprintf(stderr,
"wl_chspec_to_legacy: unable to convert chanspec %s (0x%04X) "
"to pre-11ac format\n",
wf_chspec_ntoa(chspec, chanbuf), chspec);
return INVCHANSPEC;
}
return lchspec;
}
/* given a chanspec value, do the endian and chanspec version conversion to
* a chanspec_t value
* Returns INVCHANSPEC on error
*/
static chanspec_t
wl_chspec_to_driver(chanspec_t chanspec)
{
if (ioctl_version == 1) {
chanspec = wl_chspec_to_legacy(chanspec);
if (chanspec == INVCHANSPEC) {
return chanspec;
}
}
chanspec = htodchanspec(chanspec);
return chanspec;
}
/* given a chanspec value, do the endian and chanspec version conversion to
* a chanspec_t value in a 32 bit integer
* Returns INVCHANSPEC on error
*/
static uint32
wl_chspec32_to_driver(chanspec_t chanspec)
{
uint32 val;
if (ioctl_version == 1) {
chanspec = wl_chspec_to_legacy(chanspec);
if (chanspec == INVCHANSPEC) {
return chanspec;
}
}
val = htod32((uint32)chanspec);
return val;
}
/* given a chanspec value from the driver, do the endian and chanspec version conversion to
* a chanspec_t value
* Returns INVCHANSPEC on error
*/
static chanspec_t
wl_chspec_from_driver(chanspec_t chanspec)
{
chanspec = dtohchanspec(chanspec);
if (ioctl_version == 1) {
chanspec = wl_chspec_from_legacy(chanspec);
}
return chanspec;
}
/* given a chanspec value from the driver in a 32 bit integer, do the endian and
* chanspec version conversion to a chanspec_t value
* Returns INVCHANSPEC on error
*/
static chanspec_t
wl_chspec32_from_driver(uint32 chanspec32)
{
chanspec_t chanspec;
chanspec = (chanspec_t)dtoh32(chanspec32);
if (ioctl_version == 1) {
chanspec = wl_chspec_from_legacy(chanspec);
}
return chanspec;
}
#ifdef CLMDOWNLOAD
/*
Generic interface for downloading required data onto the dongle
*/
int
download2dongle(void *wl, uint16 dload_type, unsigned char *dload_buf, int len)
{
struct wl_dload_data *dload_ptr = (struct wl_dload_data *)dload_buf;
int err = 0;
int dload_data_offset;
dload_data_offset = OFFSETOF(wl_dload_data_t, data);
dload_ptr->flag = (DLOAD_HANDLER_VER << DLOAD_FLAG_VER_SHIFT);
dload_ptr->dload_type = dload_type;
dload_ptr->len = htod32(len - dload_data_offset);
dload_ptr->crc = 0;
len = len + 8 - (len%8);
err = wlu_iovar_setbuf(wl, "generic_dload",
dload_buf, len, buf, WLC_IOCTL_MEDLEN);
return err;
}
int
dload_clm(void *wl, uint32 datalen, unsigned char *org_buf, int ds_id)
{
int num_chunks, chunk_len, cumulative_len = 0;
int size2alloc;
unsigned char *new_buf;
wl_clm_dload_info_t *clm_info_ptr;
int err = 0, clm_info_offset, chunk_offset;
clm_info_offset = OFFSETOF(wl_dload_data_t, data);
chunk_offset = OFFSETOF(wl_clm_dload_info_t, data_chunk);
num_chunks = datalen/MAX_CHUNK_LEN;
if (datalen % MAX_CHUNK_LEN != 0)
num_chunks++;
size2alloc = clm_info_offset + chunk_offset + MAX_CHUNK_LEN;
if ((new_buf = (unsigned char *)malloc(size2alloc)) != NULL) {
memset(new_buf, 0, size2alloc);
clm_info_ptr = (wl_clm_dload_info_t*)((uint8 *)new_buf + clm_info_offset);
clm_info_ptr->num_chunks = num_chunks;
clm_info_ptr->clm_total_len = datalen;
clm_info_ptr->ds_id = ds_id;
do {
if (datalen >= MAX_CHUNK_LEN)
chunk_len = MAX_CHUNK_LEN;
else
chunk_len = datalen;
memset(new_buf + clm_info_offset + chunk_offset, 0,
size2alloc - clm_info_offset - chunk_offset);
clm_info_ptr->chunk_len = htod32(chunk_len);
memcpy(&clm_info_ptr->data_chunk[0], org_buf + cumulative_len, chunk_len);
clm_info_ptr->chunk_offset = cumulative_len;
cumulative_len += chunk_len;
err = download2dongle(wl, DL_TYPE_CLM, new_buf,
chunk_len + clm_info_offset + chunk_offset);
datalen = datalen - chunk_len;
} while ((datalen > 0) && (err == 0));
free(new_buf);
} else {
err = BCME_NOMEM;
}
return err;
}
#define CLM_INPUT_FILE_MIN_LEN 32
int
process_clm_data(void *wl, char *clmfn, int ds_id)
{
int ret = 0;
FILE *fp = NULL;
unsigned int clm_filelen;
struct stat filest;
unsigned long status = 0;
unsigned char *new_buf = NULL;
uint32 clm_data_len;
unsigned char *new_ptr;
int ifd;
const char trx_magic_string[] = {'H', 'D', 'R', '0'};
const char clm_magic_string[] = {'C', 'L', 'M', ' ', 'D', 'A', 'T', 'A'};
if (clmfn == NULL) {
printf("Reverting any previous %s CLM download\n",
ds_id ? "incremental":"base");
/* Performing a zero length CLM download reverts the previous download
* of that type.
*/
clm_data_len = 0;
new_ptr = NULL;
goto do_clm_load;
}
/* Open the clm download file */
if (!(fp = fopen(clmfn, "rb"))) {
fprintf(stderr, "unable to open input file %s\n", clmfn);
ret = BCME_BADARG;
goto error;
}
ifd = fileno(fp);
if (fstat(ifd, &filest)) {
fprintf(stderr, "fstat on input file %s return error %s\n", clmfn, strerror(errno));
ret = BCME_ERROR;
goto error;
}
clm_filelen = filest.st_size;
if (clm_filelen == 0) {
fprintf(stderr, "input file %s is empty (i.e. zero length)\n", clmfn);
ret = BCME_ERROR;
goto error;
}
if ((new_buf = malloc(clm_filelen)) == NULL) {
fprintf(stderr, "unable to allocate %u bytes based on input file size!\n",
clm_filelen);
ret = BCME_NOMEM;
goto error;
}
/* We can read the pure CLM binary file or a trx format wrapped CLM binary.
* The CLM download iovar receives the CLM binary file content so any difference
* in processing is limited to our local processing.
*
* The trx wrapped CLM binary was the original approach but it now being phased
* out for the simpler, non-wrapped binary. To maintain backward compatibility
* for some period of time, wlu is accepting both formats transparently to the
* user. The two file types are identified by the magic word(s) at the beginning
* of each. A trx file starts with "HDR0" and a CLM binary starts with "CLM DATA".
*/
status = fread(new_buf, 1, clm_filelen, fp);
/* Basic sanity check on size. Make sure there is enough for any magic string plus
* a little more for good measure.
*/
if (status < CLM_INPUT_FILE_MIN_LEN) {
fprintf(stderr, "size of input file %s is less than %d bytes."
" This can't be a CLM file!\n", clmfn, CLM_INPUT_FILE_MIN_LEN);
ret = BCME_ERROR;
goto error;
} else if (status != clm_filelen) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
clmfn, clm_filelen);
ret = BCME_ERROR;
goto error;
}
/* trx wrapped CLM binary file? Look for the 'HDR0' magic string */
if (memcmp(new_buf, trx_magic_string, sizeof(trx_magic_string)) == 0)
{
struct trx_header *trx;
/* Extract trx header */
trx = (struct trx_header *)new_buf;
/* The clm binary data follows the trx header and the length is the trx
* header's first offset field. All other trx header files are unused.
* Note length in the offset doesn't include the trx header or any
* trailing/padding zeroes add by the trx format.
*/
clm_data_len = trx->offsets[0];
/* A sanity testing on length value in the trx offset field. It can't
* indicate the following CLM data is bigger than the actual file.
*/
if (clm_data_len + sizeof(struct trx_header) > clm_filelen) {
fprintf(stderr,
"input file %s trx offset[0] is inconsistent"
" for being a CLM file\n",
clmfn);
ret = -1;
goto error;
}
new_ptr = new_buf + sizeof(struct trx_header);
} else if (memcmp(new_buf, clm_magic_string, sizeof(clm_magic_string)) == 0) {
/* pure CLM binary file? CLM binary files start with 'CLM DATA' */
clm_data_len = clm_filelen;
new_ptr = new_buf;
} else {
fprintf(stderr, "input file is missing trx or CLM binary magic string\n");
ret = -1;
goto error;
}
printf("Downloading file %s as a %s CLM\n", clmfn, ds_id ? "incremental":"base");
do_clm_load:
ret = dload_clm(wl, clm_data_len, new_ptr, ds_id);
error:
if (new_buf)
free(new_buf);
if (fp)
fclose(fp);
return ret;
}
static int
wl_clmload(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
++argv;
if (cmd->set == -1)
return -1;
{
int ds_id = atoi(*argv);
char* fname = *++argv;
if ((ds_id == 0) || (ds_id == 1))
ret = process_clm_data(wl, fname, ds_id);
}
return ret;
}
#endif /* CLMDOWNLOAD */
static int
wl_bsscfg_int(void *wl, cmd_t *cmd, char **argv)
{
char *endptr = NULL;
char *val_name;
int bsscfg_idx = 0;
int val = 0;
int consumed;
int ret;
val_name = *argv++;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv, val_name, &bsscfg_idx, &consumed)) != 0)
return ret;
/* handle a bsscfg int with a legacy ioctl */
if (consumed == 0 && cmd->set != WLC_SET_VAR) {
/* back up to the orig command and run as an ioctl int */
argv--;
return wl_int(wl, cmd, argv);
}
argv += consumed;
if (!*argv) {
/* This is a GET */
if (cmd->get == -1)
return -1;
if (consumed == 0)
ret = wlu_iovar_getint(wl, val_name, &val);
else
ret = wl_bssiovar_getint(wl, val_name, bsscfg_idx, &val);
if (ret < 0)
return ret;
wl_printint(val);
} else {
/* This is a SET */
if (cmd->set == -1)
return -1;
if (!stricmp(*argv, "on"))
val = 1;
else if (!stricmp(*argv, "off"))
val = 0;
else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
if (consumed == 0)
ret = wlu_iovar_setint(wl, val_name, val);
else
ret = wl_bssiovar_setint(wl, val_name, bsscfg_idx, val);
}
return ret;
}
static int
wl_bsscfg_enable(void *wl, cmd_t *cmd, char **argv)
{
char *endptr;
const char *val_name = "bss";
int bsscfg_idx = 0;
int val;
int consumed;
int ret;
UNUSED_PARAMETER(cmd);
/* skip the command name */
argv++;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv, val_name, &bsscfg_idx, &consumed)) != 0)
return ret;
argv += consumed;
if (consumed == 0) { /* Use the -i parameter if that was present */
bsscfg_idx = -1;
}
if (!*argv) {
bsscfg_idx = htod32(bsscfg_idx);
ret = wlu_iovar_getbuf(wl, val_name, &bsscfg_idx, sizeof(bsscfg_idx),
buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
val = *(int*)buf;
val = dtoh32(val);
if (val)
printf("up\n");
else
printf("down\n");
return 0;
} else {
struct {
int cfg;
int val;
} bss_setbuf;
if (!stricmp(*argv, "ap"))
val = 3;
else if (!stricmp(*argv, "sta"))
val = 2;
else if (!stricmp(*argv, "up"))
val = 1;
else if (!stricmp(*argv, "down"))
val = 0;
else if (!stricmp(*argv, "del"))
val = -1;
else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
bss_setbuf.cfg = htod32(bsscfg_idx);
bss_setbuf.val = htod32(val);
return wlu_iovar_set(wl, val_name, &bss_setbuf, sizeof(bss_setbuf));
}
}
/* Get/Set the gmode config */
static int
wl_gmode(void *wl, cmd_t *cmd, char **argv)
{
char *endptr = NULL;
int ret = 0, val;
if (!*++argv) {
const char *gconfig;
/* Get the current G mode */
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(val))) < 0)
return ret;
val = dtoh32(val);
switch (val) {
case GMODE_LEGACY_B:
gconfig = "54g Legacy B";
break;
case GMODE_AUTO:
gconfig = "54g Auto";
break;
case GMODE_ONLY:
gconfig = "54g Only";
break;
case GMODE_PERFORMANCE:
gconfig = "54g Performance";
break;
case GMODE_LRS:
gconfig = "54g LRS";
break;
default:
gconfig = "unknown";
break;
}
printf("%s (%d)\n", gconfig, val);
} else {
/* Set the new G mode */
if (!strnicmp(*argv, "legacy", 6))
val = GMODE_LEGACY_B;
else if (!strnicmp(*argv, "auto", 4))
val = GMODE_AUTO;
else if (!strnicmp(*argv, "gonly", 5))
val = GMODE_ONLY;
else if (!strnicmp(*argv, "perf", 4))
val = GMODE_PERFORMANCE;
else if (!strnicmp(*argv, "lrs", 3))
val = GMODE_LRS;
else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
/* Set the gmode configration */
val = htod32(val);
if ((ret = wlu_set(wl, cmd->set, &val, sizeof(val))))
goto done;
}
done:
return (ret);
}
static dbg_msg_t wl_sd_msgs[] = {
{SDH_ERROR_VAL, "error"},
{SDH_TRACE_VAL, "trace"},
{SDH_INFO_VAL, "info"},
{SDH_DATA_VAL, "data"},
{SDH_CTRL_VAL, "control"}
};
static int
wl_sd_msglevel(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint val, last_val = 0, msglevel_add = 0, msglevel_del = 0;
char *endptr = NULL;
int msglevel;
dbg_msg_t *dbg_msg = wl_sd_msgs;
if ((ret = wlu_iovar_getint(wl, cmd->name, &msglevel)) < 0)
return (ret);
if (!*++argv) {
printf("0x%x ", msglevel);
for (i = 0; (val = dbg_msg[i].value); i++) {
if ((msglevel & val) && (val != last_val))
printf(" %s", dbg_msg[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char *s = *argv;
if (*s == '+' || *s == '-')
s++;
else
msglevel_del = ~0; /* make the whole list absolute */
val = strtoul(s, &endptr, 0);
/* not a plain integer if not all the string was parsed by strtoul */
if (*endptr != '\0') {
for (i = 0; (val = dbg_msg[i].value); i++)
if (stricmp(dbg_msg[i].string, s) == 0)
break;
if (!val)
goto usage;
}
if (**argv == '-')
msglevel_del |= val;
else
msglevel_add |= val;
++argv;
}
msglevel &= ~msglevel_del;
msglevel |= msglevel_add;
return (wlu_iovar_setint(wl, cmd->name, msglevel));
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (val = dbg_msg[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, dbg_msg[i].string);
else
fprintf(stderr, ", %s", dbg_msg[i].string);
last_val = val;
}
fprintf(stderr, "\n");
return 0;
}
static int
wl_sd_blocksize(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int argc;
char *endptr = NULL;
void *ptr = NULL;
int func, size;
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
if (argc < 1 || argc > 2) {
printf("required args: function [size] (size 0 means max)\n");
return BCME_USAGE_ERROR;
}
func = strtol(argv[1], &endptr, 0);
if (*endptr != '\0') {
printf("Invaild function: %s\n", argv[1]);
return BCME_USAGE_ERROR;
}
if (argc > 1) {
size = strtol(argv[2], &endptr, 0);
if (*endptr != '\0') {
printf("Invalid size: %s\n", argv[1]);
return BCME_USAGE_ERROR;
}
}
if (argc == 1) {
func = htod32(func);
if ((ret = wlu_var_getbuf(wl, cmd->name, &func, sizeof(func), &ptr)) >= 0)
printf("Function %d block size: %d\n", func, dtoh32(*(int*)ptr));
} else {
printf("Setting function %d block size to %d\n", func, size);
size &= 0x0000ffff; size |= (func << 16);
size = htod32(size);
ret = wlu_var_setbuf(wl, cmd->name, &size, sizeof(size));
}
return (ret);
}
static int
wl_sd_mode(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int argc;
int sdmode;
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
if (argv[1]) {
if (!strcmp(argv[1], "spi")) {
strcpy(argv[1], "0");
} else if (!strcmp(argv[1], "sd1")) {
strcpy(argv[1], "1");
} else if (!strcmp(argv[1], "sd4")) {
strcpy(argv[1], "2");
} else {
return BCME_USAGE_ERROR;
}
ret = wl_var_setint(wl, cmd, argv);
} else {
if ((ret = wl_var_get(wl, cmd, argv))) {
return (ret);
} else {
sdmode = dtoh32(*(int32*)buf);
printf("SD Mode is: %s\n",
sdmode == 0 ? "SPI"
: sdmode == 1 ? "SD1"
: sdmode == 2 ? "SD4" : "Unknown");
}
}
return (ret);
}
static int
wl_sd_reg(void *wl, cmd_t *cmd, char **argv)
{
int ret;
sdreg_t sdreg;
char *endptr = NULL;
uint argc;
void *ptr = NULL;
memset(&sdreg, 0, sizeof(sdreg));
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
/* hostreg: offset [value]; devreg: func offset [value] */
if (!strcmp(cmd->name, "sd_hostreg")) {
argv++;
if (argc < 1) {
printf("required args: offset [value]\n");
return BCME_USAGE_ERROR;
}
} else if (!strcmp(cmd->name, "sd_devreg")) {
argv++;
if (argc < 2) {
printf("required args: func offset [value]\n");
return BCME_USAGE_ERROR;
}
sdreg.func = htod32(strtol(*argv++, &endptr, 0));
if (*endptr != '\0') {
printf("Bad func.\n");
return BCME_USAGE_ERROR;
}
} else {
return BCME_USAGE_ERROR;
}
sdreg.offset = htod32(strtol(*argv++, &endptr, 0));
if (*endptr != '\0') {
printf("Bad offset\n");
return BCME_USAGE_ERROR;
}
/* third arg: value */
if (*argv) {
sdreg.value = htod32(strtol(*argv, &endptr, 0));
if (*endptr != '\0') {
printf("Bad Value\n");
return BCME_USAGE_ERROR;
}
}
/* no third arg means get, otherwise set */
if (!*argv) {
if ((ret = wlu_var_getbuf(wl, cmd->name, &sdreg, sizeof(sdreg), &ptr)) >= 0)
printf("0x%x\n", dtoh32(*(int *)ptr));
} else {
ret = wlu_var_setbuf(wl, cmd->name, &sdreg, sizeof(sdreg));
}
return (ret);
}
static int
wl_overlay(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int argc;
char *endptr = NULL;
void *ptr = NULL;
int param[3];
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
if (argc < 1 || argc > 3) {
printf("required args: virt_addr phy_addr size\n");
return BCME_USAGE_ERROR;
}
param[0] = strtol(argv[1], &endptr, 0);
if (*endptr != '\0' || (param[0] & ~0x003FFE00) != 0) {
printf("Invaild virtual address: %s\n", argv[1]);
return BCME_BADARG;
}
if (argc == 1) {
if ((ret = wlu_var_getbuf(wl, cmd->name, param, sizeof(int), &ptr)) >= 0) {
wl_hexdump((uchar *)ptr, 512);
}
return (ret);
}
param[1] = strtol(argv[2], &endptr, 0);
if (*endptr != '\0' || (param[1] & ~0x003FFE00) != 0) {
printf("Invaild physical Address: %s\n", argv[2]);
return BCME_BADARG;
}
if (argc == 3) {
param[2] = strtol(argv[3], &endptr, 0);
if (*endptr != '\0' || param[2] < 0 || param[2] > 7) {
printf("Invaild size: %s\n", argv[3]);
return BCME_BADARG;
}
} else {
param[2] = 0;
}
printf("Setting virtual Address 0x%x to physical Address 0x%x, size is %d\n",
param[0], param[1], param[2]);
ret = wlu_var_setbuf(wl, cmd->name, param, sizeof(param));
return (ret);
}
static int
wl_reg(void *wl, cmd_t *cmd, char **argv)
{
int reg;
int ret;
struct {
int val;
int band;
} x;
char *endptr = NULL;
uint argc;
bool core_cmd;
wlc_rev_info_t revinfo;
uint32 phytype;
/* eat command name */
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* required arg: reg offset */
if (argc < 1)
return BCME_USAGE_ERROR;
reg = strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
x.val = 0;
x.band = WLC_BAND_AUTO;
core_cmd = FALSE;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
/* Second arg: value or band or "radio core" */
if (argc >= 2) {
if (!stricmp(argv[1], "a"))
x.band = WLC_BAND_5G;
else if (!stricmp(argv[1], "b"))
x.band = WLC_BAND_2G;
else {
/* For NPHY Rev >= 3, the 2nd argument can be
the radio core
*/
if (strcmp(cmd->name, "radioreg") == 0) {
if (strcmp(argv[1], "syn") == 0) {
reg |= RADIO_CORE_SYN;
core_cmd = TRUE;
} else if (strcmp(argv[1], "tx0") == 0) {
reg |= RADIO_CORE_TX0;
core_cmd = TRUE;
} else if (strcmp(argv[1], "tx1") == 0) {
reg |= RADIO_CORE_TX1;
core_cmd = TRUE;
} else if (strcmp(argv[1], "rx0") == 0) {
reg |= RADIO_CORE_RX0;
core_cmd = TRUE;
} else if (strcmp(argv[1], "rx1") == 0) {
reg |= RADIO_CORE_RX1;
core_cmd = TRUE;
}
}
/* For HTPHY/ACPHY, the 2nd argument can be
the radio core
*/
if (strcmp(cmd->name, "radioreg") == 0) {
if (phytype == WLC_PHY_TYPE_AC) {
if (strcmp(argv[1], "cr0") == 0) {
reg |= RADIO_2069_CORE_CR0;
core_cmd = TRUE;
} else if (strcmp(argv[1], "cr1") == 0) {
reg |= RADIO_2069_CORE_CR1;
core_cmd = TRUE;
} else if (strcmp(argv[1], "cr2") == 0) {
reg |= RADIO_2069_CORE_CR2;
core_cmd = TRUE;
} else if (strcmp(argv[1], "pll") == 0) {
reg |= RADIO_2069_CORE_PLL;
core_cmd = TRUE;
}
} else {
if (strcmp(argv[1], "cr0") == 0) {
reg |= RADIO_CORE_CR0;
core_cmd = TRUE;
} else if (strcmp(argv[1], "cr1") == 0) {
reg |= RADIO_CORE_CR1;
core_cmd = TRUE;
} else if (strcmp(argv[1], "cr2") == 0) {
reg |= RADIO_CORE_CR2;
core_cmd = TRUE;
}
}
}
/* If the second argument is a value */
if (!core_cmd) {
x.val = strtol(argv[1], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
}
}
/* Third arg: band OR "radio core" */
if (argc >= 3) {
if (!stricmp(argv[2], "a"))
x.band = WLC_BAND_5G;
else if (!stricmp(argv[2], "b"))
x.band = WLC_BAND_2G;
else {
/* For NPHY Rev >= 3, the 3rd argument can be
the radio core
*/
core_cmd = FALSE;
if (strcmp(cmd->name, "radioreg") == 0) {
if (strcmp(argv[2], "syn") == 0) {
reg |= RADIO_CORE_SYN;
core_cmd = TRUE;
} else if (strcmp(argv[2], "tx0") == 0) {
reg |= RADIO_CORE_TX0;
core_cmd = TRUE;
} else if (strcmp(argv[2], "tx1") == 0) {
reg |= RADIO_CORE_TX1;
core_cmd = TRUE;
} else if (strcmp(argv[2], "rx0") == 0) {
reg |= RADIO_CORE_RX0;
core_cmd = TRUE;
} else if (strcmp(argv[2], "rx1") == 0) {
reg |= RADIO_CORE_RX1;
core_cmd = TRUE;
}
}
/* For HTPHY/ACPHY, the 3rd argument can be
the radio core
*/
if (phytype == WLC_PHY_TYPE_AC) {
if (strcmp(argv[2], "cr0") == 0) {
reg |= RADIO_2069_CORE_CR0;
core_cmd = TRUE;
} else if (strcmp(argv[2], "cr1") == 0) {
reg |= RADIO_2069_CORE_CR1;
core_cmd = TRUE;
} else if (strcmp(argv[2], "cr2") == 0) {
reg |= RADIO_2069_CORE_CR2;
core_cmd = TRUE;
} else if (strcmp(argv[2], "pll") == 0) {
reg |= RADIO_2069_CORE_PLL;
core_cmd = TRUE;
} else if (strcmp(argv[2], "all") == 0) {
reg |= RADIO_2069_CORE_ALL;
core_cmd = TRUE;
}
} else {
if (strcmp(cmd->name, "radioreg") == 0) {
if (strcmp(argv[2], "cr0") == 0) {
reg |= RADIO_CORE_CR0;
core_cmd = TRUE;
} else if (strcmp(argv[2], "cr1") == 0) {
reg |= RADIO_CORE_CR1;
core_cmd = TRUE;
} else if (strcmp(argv[2], "cr2") == 0) {
reg |= RADIO_CORE_CR2;
core_cmd = TRUE;
} else if (strcmp(argv[2], "all") == 0) {
reg |= RADIO_CORE_ALL;
core_cmd = TRUE;
}
}
}
if (!core_cmd) {
return BCME_USAGE_ERROR;
}
}
}
x.val = (x.val << 16) | (reg & 0xffff);
/* issue the get or set ioctl */
if ((argc == 1) || ((argc == 2) && ((x.band != WLC_BAND_AUTO) || core_cmd))) {
x.band = htod32(x.band);
x.val = htod32(x.val);
if ((ret = wlu_get(wl, cmd->get, &x, sizeof(x))) < 0)
return (ret);
printf("0x%04x\n", (uint16)(dtoh32(x.val)));
} else {
x.band = htod32(x.band);
x.val = htod32(x.val);
ret = wlu_set(wl, cmd->set, &x, sizeof(x));
}
return (ret);
}
static int
wl_gpioout(void *wl, cmd_t *cmd, char **argv)
{
uint32 mask;
uint32 val;
char *endptr = NULL;
uint argc;
uint32 *int_ptr;
UNUSED_PARAMETER(cmd);
val = 0;
/* eat command name */
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* Get and print the values */
if (argc == 0) {
uint32 gpio_cntrl;
uint32 gpio_out;
uint32 gpio_outen;
int ret;
if ((ret = wlu_iovar_get(wl, "gpioout", buf, sizeof(uint32) *3)) < 0)
return ret;
gpio_cntrl = dtoh32(((uint32 *)buf)[0]);
gpio_out = dtoh32(((uint32 *)buf)[1]);
gpio_outen = dtoh32(((uint32 *)buf)[2]);
printf("gpiocontrol 0x%x gpioout 0x%x gpioouten 0x%x\n", gpio_cntrl,
gpio_out, gpio_outen);
return 0;
}
/* required arg: mask value */
if (argc < 2)
return BCME_USAGE_ERROR;
mask = strtoul(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
val = strtoul(argv[1], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if ((~mask & val) != 0)
return BCME_BADARG;
int_ptr = (uint32 *)buf;
mask = htod32(mask);
memcpy(int_ptr, (const void *)&mask, sizeof(mask));
int_ptr++;
val = htod32(val);
memcpy(int_ptr, (const void *)&val, sizeof(val));
return wlu_iovar_set(wl, "gpioout", buf, sizeof(uint32) *2);
}
static int
wl_macreg(void *wl, cmd_t *cmd, char **argv)
{
int reg;
int size;
uint32 val;
int ret;
char *endptr = NULL;
rw_reg_t rwt;
uint argc;
val = 0;
/* eat command name */
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* required arg: reg offset */
if (argc < 1)
return BCME_USAGE_ERROR;
reg = strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
/* required arg: reg size */
if (argc < 2)
return BCME_USAGE_ERROR;
size = strtol(argv[1], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
rwt.band = WLC_BAND_AUTO;
/* Third arg: new value or band */
if (argc >= 3) {
if (!stricmp(argv[2], "a"))
rwt.band = WLC_BAND_5G;
else if (!stricmp(argv[2], "b"))
rwt.band = WLC_BAND_2G;
else {
val = strtoul(argv[2], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
}
/* Fourth arg: band */
if (argc >= 4) {
if (!stricmp(argv[3], "a"))
rwt.band = WLC_BAND_5G;
else if (!stricmp(argv[3], "b"))
rwt.band = WLC_BAND_2G;
else
return BCME_USAGE_ERROR;
}
if ((argc == 2) || ((argc == 3) && (rwt.band != WLC_BAND_AUTO))) {
rwt.band = htod32(rwt.band);
rwt.byteoff = htod32(reg);
rwt.size = htod32(size);
if ((ret = wlu_get(wl, cmd->get, &rwt, sizeof(rw_reg_t))) < 0)
return (ret);
printf("0x%04x\n", dtoh32(rwt.val));
}
else {
rwt.band = htod32(rwt.band);
rwt.byteoff = htod32(reg);
rwt.size = htod32(size);
rwt.val = htod32(val);
ret = wlu_set(wl, cmd->set, &rwt, sizeof(rw_reg_t));
}
return (ret);
}
/*
* get or get a band specific variable
* the band can be a/b/all or omitted. "all"(set only)
* means all supported bands. blank means current band
*/
static int
wl_band_elm(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct {
int val;
int band;
} x;
char *endptr = NULL;
uint argc;
/* eat command name */
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
x.val = 0;
x.band = WLC_BAND_AUTO;
/* First arg: value or band */
if (argc >= 1) {
if (!stricmp(argv[0], "a"))
x.band = WLC_BAND_5G;
else if (!stricmp(argv[0], "b"))
x.band = WLC_BAND_2G;
else if (!stricmp(argv[0], "all"))
x.band = WLC_BAND_ALL;
else {
x.val = strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
}
/* Second arg: band */
if (argc >= 2) {
if (!stricmp(argv[1], "a"))
x.band = WLC_BAND_5G;
else if (!stricmp(argv[1], "b"))
x.band = WLC_BAND_2G;
else if (!stricmp(argv[1], "all"))
x.band = WLC_BAND_ALL;
else
return BCME_USAGE_ERROR;
}
/* issue the get or set ioctl */
if ((argc == 0) || ((argc == 1) && (x.band != WLC_BAND_AUTO))) {
if (x.band == WLC_BAND_ALL) {
printf("band option \"all\" is for set only, not get\n");
return BCME_USAGE_ERROR;
}
x.band = htod32(x.band);
if ((ret = wlu_get(wl, cmd->get, &x, sizeof(x))) < 0)
return (ret);
printf("%s is 0x%04x(%d)\n", cmd->name, (uint16)(dtoh32(x.val)), dtoh32(x.val));
} else {
x.band = htod32(x.band);
x.val = htod32(x.val);
ret = wlu_set(wl, cmd->set, &x, sizeof(x));
}
return (ret);
}
/* Command may or may not take a MAC address */
static int
wl_rssi(void *wl, cmd_t *cmd, char **argv)
{
int ret;
scb_val_t scb_val;
int32 rssi;
if (!*++argv) {
if ((ret = wlu_get(wl, cmd->get, &rssi, sizeof(rssi))) < 0)
return ret;
printf("%d\n", dtoh32(rssi));
return 0;
} else {
if (!wl_ether_atoe(*argv, &scb_val.ea))
return BCME_USAGE_ERROR;
if ((ret = wlu_get(wl, cmd->get, &scb_val, sizeof(scb_val))) < 0)
return ret;
printf("%d\n", dtoh32(scb_val.val));
return 0;
}
}
static int
wl_rssi_event(void *wl, cmd_t *cmd, char **argv)
{
int ret;
if (!*++argv) {
/* get */
void *ptr = NULL;
wl_rssi_event_t rssi;
uint i;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
memcpy(&rssi, ptr, sizeof(rssi));
rssi.rate_limit_msec = dtoh32(rssi.rate_limit_msec);
printf("%d", rssi.rate_limit_msec);
for (i = 0; i < rssi.num_rssi_levels; i++) {
printf(" %d", rssi.rssi_levels[i]);
}
printf("\n");
} else {
/* set */
wl_rssi_event_t rssi;
memset(&rssi, 0, sizeof(wl_rssi_event_t));
rssi.rate_limit_msec = atoi(*argv);
while (*++argv && rssi.num_rssi_levels < MAX_RSSI_LEVELS) {
rssi.rssi_levels[rssi.num_rssi_levels++] = atoi(*argv);
if (rssi.num_rssi_levels > 1) {
if (rssi.rssi_levels[rssi.num_rssi_levels - 1] <=
rssi.rssi_levels[rssi.num_rssi_levels - 2]) {
/* rssi levels must be in increasing order */
return BCME_USAGE_ERROR;
}
}
}
if (*argv) {
/* too many parameters */
return BCME_USAGE_ERROR;
}
rssi.rate_limit_msec = htod32(rssi.rate_limit_msec);
ret = wlu_var_setbuf(wl, cmd->name, &rssi, sizeof(rssi));
}
return ret;
}
static int
wl_phy_rssi_ant(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
uint i;
wl_rssi_ant_t *rssi_ant_p;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
rssi_ant_p = (wl_rssi_ant_t *)ptr;
rssi_ant_p->version = dtoh32(rssi_ant_p->version);
rssi_ant_p->count = dtoh32(rssi_ant_p->count);
if (rssi_ant_p->count == 0) {
printf("not supported on this chip\n");
} else {
for (i = 0; i < rssi_ant_p->count; i++)
printf("rssi[%d] %d ", i, rssi_ant_p->rssi_ant[i]);
printf("\n");
}
} else {
ret = BCME_USAGE_ERROR;
}
return ret;
}
/* Commands that take a MAC address */
static int
wl_mac(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct ether_addr ea;
if (!*++argv) {
if ((ret = wlu_get(wl, cmd->get, &ea, ETHER_ADDR_LEN)) < 0)
return ret;
printf("%s\n", wl_ether_etoa(&ea));
return 0;
} else {
if (!wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
return wlu_set(wl, cmd->set, &ea, ETHER_ADDR_LEN);
}
}
/* IO variables that take a MAC address */
static int
wl_iov_mac(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct ether_addr ea = {{0, 0, 0, 0, 0, 0}};
if (argv[1]) { /* set */
if (!wl_ether_atoe(argv[1], &ea)) {
printf(" ERROR: no valid ether addr provided\n");
return BCME_USAGE_ERROR;
}
if ((ret = wlu_iovar_set(wl, cmd->name, &ea, ETHER_ADDR_LEN)) < 0) {
printf("Error setting variable %s\n", argv[0]);
return ret;
}
return 0;
} else { /* get */
if ((ret = wlu_iovar_get(wl, cmd->name, &ea, ETHER_ADDR_LEN)) < 0) {
printf("Error getting variable %s\n", argv[0]);
return ret;
}
printf("%s %s\n", argv[0], wl_ether_etoa(&ea));
}
return 0;
}
static void
wl_txq_prec_dump(wl_iov_pktq_log_t* iov_pktq_log, bool hide_unknown, bool is_aqm)
{
#define PREC_DUMPV(v4, v5) ((iov_pktq_log->version == 4) ? (v4) : (v5))
#define v4hstubL "prec: rqstd, stored,selfsave, saved,fulldrop, dropped," \
"sacrficd, retried, rtsfail,rtrydrop, psretry,supprssd, " \
"acked,utlisatn,q length,Data Mbits/s,Phy Mbits/s,Rate Mbits/s"
#define v4hstubS "prec: rqstd, stored, dropped, retried, rtsfail,rtrydrop, " \
"psretry, acked,utlisatn,q length,Data Mbits/s,Phy Mbits/s"
#define v4fstubL " %02u: %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, " \
"%7u, %7u, %7u, %7u, %8.2f, %8.2f, %8.2f"
#define v4fstubS " %02u: %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, %7u, " \
"%8.2f, %8.2f"
#define v4fstubL_aqm " %02u: %7u, %7u, %7u, %7u, %7u, %7u, %7u, -, -, " \
"%7u, %7u, %7u, %7u, %7u, %7u, %8.2f, -, -"
#define v4fstubS_aqm " %02u: %7u, %7u, %7u, -, -, %7u, %7u, %7u, %7u, " \
"%7u, %8.2f, -"
const char* v4headingsL = v4hstubL" (+v%d.)\n";
const char* v4headingsS = v4hstubS" (+v%d.)\n";
const char* v5headingsL = v4hstubL", %%air, %%effcy (v%d)\n";
const char* v5headingsS = v4hstubS", %%air, %%effcy (v%d)\n";
const char* v4formL = v4fstubL"\n";
const char* v4formS = v4fstubS"\n";
const char* v4formL_aqm = v4fstubL_aqm"\n";
const char* v4formS_aqm = v4fstubS_aqm"\n";
const char* v5formL = v4fstubL", %6.1f, %5.1f\n";
const char* v5formS = v4fstubS", %6.1f, %5.1f\n";
const char* v5formL_aqm = v4fstubL_aqm", -, -\n";
const char* v5formS_aqm = v4fstubS_aqm", -, -\n";
char* headings;
uint8 index;
uint8 prec;
uint32 prec_mask = 0;
char marker[4] = "[X]";
pktq_log_format_v05_t* logv05 = NULL;
pktq_log_format_v04_t* logv04 = NULL;
if (iov_pktq_log->version == 0x04) {
logv04 = &iov_pktq_log->pktq_log.v04;
}
else if (iov_pktq_log->version == 0x05) {
logv05 = &iov_pktq_log->pktq_log.v05;
}
else {
fprintf(stderr, "Unknown/unsupported binary format (%x)\n",
iov_pktq_log->version);
return;
}
headings = PREC_DUMPV(&logv04->headings[0], &logv05->headings[0]);
for (index = 0; index < (uint8)iov_pktq_log->params.num_addrs; index++) {
char* heading_start;
char* heading_end;
uint32 num_prec = 0;
prec_mask = PREC_DUMPV(logv04->counter_info[index],
logv05->counter_info[index]);
num_prec = PREC_DUMPV(logv04->num_prec[index],
logv05->num_prec[index]);
/* test for 'unknown' data; unknown means either that
* the queue is invalid or else that the logging
* is not active at all.
*/
if (((prec_mask & 0xFFFF) == 0) && hide_unknown) {
continue;
}
if ((num_prec == 0) && hide_unknown) {
continue;
}
/* search for string marker - the marker is of the form
"[<index>]" where index is a single ascii numeral
*/
marker[1] = '0' + index;
heading_start = strstr(headings, marker);
/* The driver may pass back an optional character
* string for additional info
*/
if (heading_start != NULL) {
heading_start += strlen(marker);
heading_end = strstr(heading_start, marker);
if (heading_end == NULL) {
heading_end = heading_start + strlen(heading_start);
}
while (heading_start < heading_end) {
fputc(*heading_start++, stdout);
}
}
/* Note that this is zero if the data is invalid */
if (!num_prec) {
fprintf(stdout, "Parameter %c:%s not valid\n",
iov_pktq_log->params.addr_type[index] != 0 ?
iov_pktq_log->params.addr_type[index] & 0x7F : ' ',
wl_ether_etoa(&iov_pktq_log->params.ea[index]));
continue;
}
/* check for short form or long form (top bit set) */
fprintf(stdout,
iov_pktq_log->params.addr_type[index] & 0x80 ?
PREC_DUMPV(v4headingsL, v5headingsL) :
PREC_DUMPV(v4headingsS, v5headingsS),
iov_pktq_log->version);
for (prec = 0; prec < num_prec; prec++) {
float tput = 0.0;
float txrate_succ = 0.0;
float txrate_main = 0.0;
pktq_log_counters_v05_t counters;
uint32 try_count = 0;
float airuse = 0.0;
float efficiency = 0.0;
if (!(prec_mask & (1 << prec))) {
continue;
}
if (iov_pktq_log->version == 5) {
counters = logv05->counters[index][prec];
}
else {
/* the following is a trick - it is possible because
* V4 and V5 are both common except that V5 has extra fields
* at the end
*/
memcpy(&counters, &logv04->counters[index][prec],
sizeof(pktq_log_counters_v04_t));
counters.airtime = 0;
}
txrate_succ = (float)counters.txrate_succ * 0.5;
if (counters.time_delta != 0) {
/* convert bytes to bits */
tput = (float)counters.throughput;
tput *= 8.0;
if (counters.airtime) {
efficiency = 100.0 * tput / (float)counters.airtime;
}
/* converts to rate of bits per us,
because time_delta is in micro-seconds
*/
tput /= (float)counters.time_delta;
/* Calculate % airtime */
airuse = counters.airtime * 100.0 / (float)counters.time_delta;
}
if (!(is_aqm && (prec & 1))) {
uint32 acked = counters.acked;
try_count = counters.acked + counters.retry;
if (is_aqm && (prec_mask & (1 << (prec + 1)))) {
pktq_log_counters_v05_t hi;
if (iov_pktq_log->version == 5) {
hi = logv05->counters[index][prec + 1];
}
else {
/* the following is a trick - it is possible
* fields V4 and V5 are both common except
* that V5 has extra fields at the end
*/
memcpy(&hi, &logv04->counters[index][prec + 1],
sizeof(pktq_log_counters_v04_t));
}
acked += hi.acked;
try_count += hi.acked + hi.retry;
if (counters.airtime) {
float t = (float)hi.throughput;
t /= (float)counters.airtime;
efficiency += 100.0 * 8.0 * t;
}
}
if (acked) {
txrate_succ /= (float) acked;
if (counters.txrate_succ) {
efficiency /= txrate_succ;
}
else {
efficiency = 0;
}
}
else {
txrate_succ = 0;
efficiency = 0;
}
}
if (try_count) {
txrate_main = (float)counters.txrate_main * 0.5;
txrate_main /= (float)try_count;
}
if (iov_pktq_log->params.addr_type[index] & 0x80) {
/* long form */
if (is_aqm && (prec & 1)) {
/* aqm format for hi-prec */
fprintf(stdout, PREC_DUMPV(v4formL_aqm, v5formL_aqm),
prec,
counters.requested,
counters.stored,
counters.selfsaved,
counters.saved,
counters.full_dropped,
counters.dropped,
counters.sacrificed,
counters.retry_drop,
counters.ps_retry,
counters.suppress,
counters.acked,
counters.max_used,
counters.queue_capacity,
tput);
}
else {
fprintf(stdout, PREC_DUMPV(v4formL, v5formL),
prec,
counters.requested,
counters.stored,
counters.selfsaved,
counters.saved,
counters.full_dropped,
counters.dropped,
counters.sacrificed,
counters.retry,
counters.rtsfail,
counters.retry_drop,
counters.ps_retry,
counters.suppress,
counters.acked,
counters.max_used,
counters.queue_capacity,
tput, txrate_succ,
txrate_main,
airuse, efficiency);
}
}
else {
/* short form */
if (is_aqm && (prec & 1)) {
/* aqm format for hi-prec */
fprintf(stdout, PREC_DUMPV(v4formS_aqm, v5formS_aqm),
prec,
counters.requested,
counters.stored,
counters.dropped,
counters.retry_drop,
counters.ps_retry,
counters.acked,
counters.max_used,
counters.queue_capacity,
tput);
}
else {
fprintf(stdout, PREC_DUMPV(v4formS, v5formS),
prec,
counters.requested,
counters.stored,
counters.dropped,
counters.retry,
counters.rtsfail,
counters.retry_drop,
counters.ps_retry,
counters.acked,
counters.max_used,
counters.queue_capacity,
tput, txrate_succ,
airuse, efficiency);
}
}
}
fputs("\n", stdout);
if (iov_pktq_log->version == 5 &&
(logv05->pspretend_time_delta[index] != (uint32)-1)) {
fprintf(stdout, "Total time in ps pretend state is %d milliseconds\n\n",
(logv05->pspretend_time_delta[index] + 500)/1000);
}
}
}
static int
wl_scb_bs_data(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 flag_bits = 0;
int argn;
enum { DISPLAY_COOKED, DISPLAY_RAW } display_mode = DISPLAY_COOKED;
iov_bs_data_struct_t *data = (iov_bs_data_struct_t *)buf;
char sep = ' ';
bool skip_idle = FALSE;
float total_throughput = 0.0;
UNUSED_PARAMETER(cmd); /* cmd->name should match argv[0] ? */
if (!argv[0]) {
fprintf(stderr, "%s: argv[0] missing\n", __FUNCTION__);
return BCME_BADARG;
}
for (argn = 1; argv[argn]; ++argn) {
if (!strcmp(argv[argn], "-noreset")) { /* do not reset counters after reading */
flag_bits |= SCB_BS_DATA_FLAG_NO_RESET;
} else
if (!strcmp(argv[argn], "-raw")) { /* Display raw counters */
display_mode = DISPLAY_RAW;
} else
if (!strcmp(argv[argn], "-tab")) { /* Tab separator */
sep = '\t';
} else
if (!strcmp(argv[argn], "-comma")) { /* Comma separator */
sep = ',';
} else
if (!strcmp(argv[argn], "-noidle")) { /* Skip idle stations */
skip_idle = TRUE;
} else
if (!strcmp(argv[argn], "-help") || !strcmp(argv[argn], "-h")) {
/* Display usage, do not complain about unknown option. */
return BCME_USAGE_ERROR;
} else {
fprintf(stderr, "%s: unknown option: %s\n", argv[0], argv[argn]);
return BCME_USAGE_ERROR;
}
}
flag_bits = htod32(flag_bits);
err = wlu_iovar_getbuf(wl, argv[0], &flag_bits, sizeof(flag_bits), buf, WLC_IOCTL_MAXLEN);
if (err) {
return (err);
}
data->structure_version = dtoh16(data->structure_version);
if (data->structure_version != SCB_BS_DATA_STRUCT_VERSION) {
fprintf(stderr, "wlu / wl driver mismatch, expect V%d format, got %d.\n",
SCB_BS_DATA_STRUCT_VERSION, data->structure_version);
return BCME_IOCTL_ERROR;
}
data->structure_count = dtoh16(data->structure_count);
if (data->structure_count == 0) {
printf("No stations are currently associated.\n");
return BCME_OK;
}
/* Display Column headers - mac address always, then, depending on display mode */
printf("%17s%c", "Station Address", sep);
switch (display_mode) {
case DISPLAY_RAW:
printf("%9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s\n",
"retry_drop", "rtsfail", "retry", "txrate_main",
"txrate_succ", "acked", "throughput", "time_delta", "airtime");
break;
case DISPLAY_COOKED:
printf("%10s%c%10s%c%10s%c%10s%c%10s\n", "PHY Mbps", sep, "Data Mbps", sep,
"Air Use", sep, "Data Use", sep, "Retries");
break;
}
/* Convert returned counters to host byte order, and sum up total throughput */
for (argn = 0; argn < data->structure_count; ++argn) {
iov_bs_data_record_t *rec;
iov_bs_data_counters_t *ctr;
float data_rate;
rec = &data->structure_record[argn];
ctr = &rec->station_counters;
#define DEVICE_TO_HOST(xyzzy) ctr->xyzzy = dtoh32(ctr->xyzzy)
DEVICE_TO_HOST(retry_drop);
DEVICE_TO_HOST(rtsfail);
DEVICE_TO_HOST(retry);
DEVICE_TO_HOST(txrate_main);
DEVICE_TO_HOST(txrate_succ);
DEVICE_TO_HOST(acked);
DEVICE_TO_HOST(throughput);
DEVICE_TO_HOST(time_delta);
DEVICE_TO_HOST(airtime);
#undef DEVICE_TO_HOST
/* Calculate data rate in bits per second, rather than bytes per second */
data_rate = (ctr->time_delta) ?
(float)ctr->throughput * 8.0 / (float)ctr->time_delta : 0.0;
total_throughput += data_rate;
}
for (argn = 0; argn < data->structure_count; ++argn) {
iov_bs_data_record_t *rec;
iov_bs_data_counters_t *ctr;
rec = &data->structure_record[argn];
ctr = &rec->station_counters;
if (skip_idle && (ctr->acked == 0)) continue;
printf("%17s%c", wl_ether_etoa(&rec->station_address), sep);
switch (display_mode) {
case DISPLAY_RAW:
printf("%9d %9d %9d %9d %9d %9d %9d %9d %9d\n",
ctr->retry_drop, ctr->rtsfail, ctr->retry,
ctr->txrate_main, ctr->txrate_succ, ctr->acked,
ctr->throughput, ctr->time_delta, ctr->airtime);
break;
case DISPLAY_COOKED:
{
float data_rate;
float phy_rate;
float use, air, rtr;
/* Calculate PHY rate */
phy_rate = (ctr->acked) ?
(float)ctr->txrate_succ * 0.5 / (float)ctr->acked : 0.0;
/* Calculate Data rate */
data_rate = (ctr->time_delta) ?
(float)ctr->throughput * 8.0 / (float)ctr->time_delta : 0.0;
/* Calculate use percentage amongst throughput from all stations */
use = (total_throughput) ? data_rate / total_throughput * 100 : 0.0;
/* Calculate % airtime */
air = (ctr->time_delta) ? ((float)ctr->airtime * 100.0 /
(float) ctr->time_delta) : 0.0;
/* Calculate retry percentage */
rtr = (ctr->acked) ? (float)ctr->retry / (float)ctr->acked * 100 : 0.0;
printf("%10.1f%c%10.1f%c%9.1f%%%c%9.1f%%%c%9.1f%%\n",
phy_rate, sep, data_rate, sep, air, sep, use, sep, rtr);
}
break;
}
}
return BCME_OK;
}
/* IO variables that take MAC addresses (with optional single letter prefix)
* and output a string buffer
*/
static int
wl_iov_pktqlog_params(void *wl, cmd_t *cmd, char **argv)
{
int ret;
char** macaddrs = argv + 1;
wl_iov_mac_full_params_t* full_params = (wl_iov_mac_full_params_t*)buf;
wl_iov_mac_params_t* params = &full_params->params;
wl_iov_mac_extra_params_t* extra_params = &full_params->extra_params;
wl_iov_mac_params_t loop_params;
wl_iov_mac_extra_params_t loop_extra_params;
uint32 index;
bool loop_assoclist = FALSE;
struct maclist* maclist = NULL;
wlc_rev_info_t revinfo;
uint32 corerev;
if (cmd->get < 0)
return -1;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
corerev = dtoh32(revinfo.corerev);
memset(full_params, 0, sizeof(*full_params));
memset(&loop_params, 0, sizeof(loop_params));
memset(&loop_extra_params, 0, sizeof(loop_extra_params));
/* only pass up to WL_IOV_MAC_PARAM_LEN parameters */
while (params->num_addrs < WL_IOV_MAC_PARAM_LEN && *macaddrs) {
bool full_auto = FALSE;
char* ptr = *macaddrs;
uint32 bitmask;
/* is there a prefix character? */
if (ptr[1] == ':') {
params->addr_type[params->num_addrs] = toupper((int)(ptr[0]));
/* move ptr to skip over prefix */
ptr += 2;
/* is there the 'long form' option ? */
if (ptr[0] == '+') {
/* check for + additional info option, set top bit */
params->addr_type[params->num_addrs] |= 0x80;
ptr++;
}
if ((ptr[0] == 0) || (ptr[0] == '/' || ptr[0] == ',')) {
/* this is the fully automatic mode */
full_auto = TRUE;
}
}
/* the prefix C: denotes no given MAC address (to refer to "common") */
if ((params->addr_type[params->num_addrs] & 0x7F) == 'C') {
full_auto = FALSE;
}
else if (full_auto) {
loop_assoclist = TRUE;
loop_params.addr_type[loop_params.num_addrs] =
params->addr_type[params->num_addrs];
}
else if (wl_ether_atoe(ptr, &params->ea[params->num_addrs])) {
/* length of MAC addr string excl end char */
ptr += (ETHER_ADDR_STR_LEN - 1);
}
else {
params->addr_type[params->num_addrs] = 0;
printf("Bad parameter '%s'\n", *macaddrs);
++macaddrs;
continue;
}
bitmask = 0;
while (ptr && (ptr[0] == ',' || ptr[0] == '/') &&
((ptr[1] >= '0' && ptr[1] <= '9') ||
ptr[1] == '/' || ptr[1] == ',')) {
uint8 prec;
char* endptr = 0;
if (ptr[1] == '/' || ptr[1] == ',') {
/* this is the 'auto' setting */
bitmask |= PKTQ_LOG_AUTO;
ptr += 2;
}
else {
ptr++;
prec = (uint8)strtoul(ptr, &endptr, 10);
if (prec <= 15) {
bitmask |= (1 << prec);
}
else {
printf("Bad precedence %d (will be ignored)\n",
prec);
}
ptr = endptr;
}
}
if (bitmask == 0) {
/* PKTQ_LOG_DEF_PREC is ignored in V4, it is used to indicate no prec was
* selected
*/
bitmask = 0xFFFF | PKTQ_LOG_DEF_PREC;
}
if (full_auto) {
loop_extra_params.addr_info[loop_params.num_addrs] = bitmask;
loop_params.num_addrs++;
}
else {
extra_params->addr_info[params->num_addrs] = bitmask;
params->num_addrs ++;
}
++macaddrs;
}
while (*macaddrs) {
printf("Ignoring excess parameter '%s' (maximum number of params is %d)\n",
*macaddrs, WL_IOV_MAC_PARAM_LEN);
++macaddrs;
}
/* if no valid params found, pass default prefix 'C' with no mac address */
if (params->num_addrs == 0 && !loop_assoclist)
{
params->addr_type[0] = 'C';
extra_params->addr_info[0] = 0xFFFF;
params->num_addrs = 1;
}
if (params->num_addrs) {
/* set a "version" indication (ie extra_params present) */
params->num_addrs |= (4 << 8);
if ((ret = wlu_iovar_getbuf(wl, cmd->name, params,
sizeof(*params) + sizeof(*extra_params),
buf, WLC_IOCTL_MAXLEN)) < 0) {
fprintf(stderr, "Error getting variable %s\n", argv[0]);
return ret;
}
wl_txq_prec_dump((wl_iov_pktq_log_t*)buf, FALSE, corerev >= 40);
}
if (!loop_assoclist) {
return 0;
}
maclist = malloc(WLC_IOCTL_MEDLEN);
if (!maclist) {
fprintf(stderr, "unable to allocate memory\n");
return -ENOMEM;
}
maclist->count = htod32((WLC_IOCTL_MEDLEN - sizeof(int)) / ETHER_ADDR_LEN);
if ((ret = wlu_get(wl, WLC_GET_ASSOCLIST, maclist, WLC_IOCTL_MEDLEN)) < 0) {
fprintf(stderr, "Cannot get assoclist\n");
free(maclist);
return ret;
}
maclist->count = dtoh32(maclist->count);
if (maclist->count == 0) {
fprintf(stderr, "No available addresses in assoclist for automatic operation\n");
free(maclist);
return 0;
}
for (index = 0; index < loop_params.num_addrs; index++) {
uint32 ea_index = 0;
while (ea_index < maclist->count) {
memset(full_params, 0, sizeof(*full_params));
while ((params->num_addrs < WL_IOV_MAC_PARAM_LEN) &&
(ea_index < maclist->count)) {
params->addr_type[params->num_addrs] = loop_params.addr_type[index];
params->ea[params->num_addrs] = maclist->ea[ea_index];
extra_params->addr_info[params->num_addrs] =
loop_extra_params.addr_info[index] | PKTQ_LOG_AUTO;
params->num_addrs++;
ea_index++;
}
/* set a "version" indication (ie extra_params present) */
params->num_addrs |= (4 << 8);
if ((ret = wlu_iovar_getbuf(wl, cmd->name, params,
sizeof(*params) + sizeof(*extra_params),
buf, WLC_IOCTL_MAXLEN)) < 0) {
fprintf(stderr, "Error getting %s\n", argv[0]);
free(maclist);
return ret;
}
wl_txq_prec_dump((wl_iov_pktq_log_t*)buf, TRUE, corerev >= 40);
}
}
free(maclist);
return 0;
}
static int
wlu_dump(void *wl, cmd_t *cmd, char **argv)
{
int ret;
char *dump_buf;
int bcmerr;
if (cmd->get < 0)
return -1;
dump_buf = malloc(WL_DUMP_BUF_LEN);
if (dump_buf == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n", WL_DUMP_BUF_LEN);
return BCME_NOMEM;
}
memset(dump_buf, 0, WL_DUMP_BUF_LEN);
/* skip the command name */
argv++;
/* If no args given, get the subset of 'wl dump all'
* Otherwise, if args are given, they are the dump section names.
*/
if (*argv == NULL) {
/* query for the 'dump' without any argument */
ret = wlu_iovar_getbuf(wl, "dump", NULL, 0, dump_buf, WL_DUMP_BUF_LEN);
/* if the query is successful, continue on and print the result. */
/* if the query fails, check for a legacy driver that does not support
* the "dump" iovar, and instead issue a WLC_DUMP ioctl.
*/
if (ret) {
wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (bcmerr == BCME_UNSUPPORTED) {
ret = wlu_get(wl, WLC_DUMP, dump_buf, WL_DUMP_BUF_LEN);
if (ret) {
fprintf(stderr, "dump: error on query of WLC_DUMP\n");
}
} else {
fprintf(stderr, "dump: error on query of dump list\n");
}
}
} else {
/* create the dump section name list */
while (*argv) {
/* add space delimiter if this is not the first section name */
if (dump_buf[0] != '\0')
strcat(dump_buf, " ");
strcat(dump_buf, *argv);
argv++;
}
/* This is a "space" added at end of last argument */
strcat(dump_buf, " ");
ret = wlu_iovar_getbuf(wl, "dump", dump_buf, strlen(dump_buf),
dump_buf, WL_DUMP_BUF_LEN);
}
if (!ret) {
fputs(dump_buf, stdout);
}
free(dump_buf);
return ret;
}
static int
wlu_offloads_stats(void *wl, cmd_t *cmd, char **argv)
{
int ret;
char *dump_buf;
int bufsz = WL_DUMP_BUF_LEN;
bool cons_cmd = FALSE;
if (cmd->get < 0)
return -1;
if (!strcmp(cmd->name, "ol_clr"))
{
ret = wlu_iovar_get(wl, cmd->name, NULL, 0);
return ret;
}
if (!strcmp(cmd->name, "ol_cons")) {
/* Check for command */
if (*(argv + 1)) {
argv++;
cons_cmd = TRUE;
bufsz = CMDLINESZ;
}
}
dump_buf = malloc(bufsz);
if (dump_buf == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n", bufsz);
return -1;
}
memset(dump_buf, 0, bufsz);
while (*argv) {
/* add space delimiter if this is not the first section name */
if (dump_buf[0] != '\0')
strcat(dump_buf, " ");
strcat(dump_buf, *argv);
argv++;
}
if (cons_cmd) {
ret = wlu_iovar_set(wl, cmd->name, dump_buf, bufsz);
} else {
ret = wlu_iovar_get(wl, cmd->name, dump_buf, bufsz);
if (!ret)
fputs(dump_buf, stdout);
}
free(dump_buf);
return ret;
}
static int
wl_staprio(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_USAGE_ERROR;
wl_staprio_cfg_t staprio_cfg;
char *endptr = NULL;
if (!*++argv) return -1;
/* get link mac address */
if (!wl_ether_atoe(*argv++, &staprio_cfg.ea))
goto error;
if (argv[0]) {
staprio_cfg.prio = (uint8)strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
goto error;
if (staprio_cfg.prio > 3) {
printf("prio %d out of range [0, 3]\n", staprio_cfg.prio);
goto error;
}
else {
printf("Set SCB prio: 0x%x\n", staprio_cfg.prio);
ret = wlu_iovar_setbuf(wl, cmd->name, (void *) &staprio_cfg,
sizeof(wl_staprio_cfg_t), buf, WLC_IOCTL_MEDLEN);
}
}
else {
if ((ret = wlu_iovar_getbuf(wl, cmd->name, (void *) &staprio_cfg,
sizeof(wl_staprio_cfg_t), buf, WLC_IOCTL_MEDLEN)) >= 0) {
printf("SCB prio: 0x%x\n", ((wl_staprio_cfg_t *)buf)->prio);
}
}
error:
return ret;
}
static int
wl_aibss_bcn_force_config(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_USAGE_ERROR;
aibss_bcn_force_config_t bcn_config;
if (!*++argv) {
/* Get */
memset(&bcn_config, 0, sizeof(aibss_bcn_force_config_t));
/* get current rateset */
if ((ret = wlu_iovar_get(wl, cmd->name, &bcn_config,
sizeof(aibss_bcn_force_config_t))) < 0)
goto error;
printf("AIBSS Initial beacon check duration: %d \r\n"
"AIBSS beacon check duration:%d \r\n"
"AIBSS beacon flood duration:%d\r\n",
bcn_config.initial_min_bcn_dur, bcn_config.min_bcn_dur,
bcn_config.bcn_flood_dur);
}
else {
char *p = argv[0];
char *endptr = NULL;
/* Extract the content */
if (!p || *p == '\0')
goto error;
bcn_config.initial_min_bcn_dur = strtoul(p, &endptr, 0);
p = endptr;
/* check and skip , */
if (*p == '\0' || *++p == '\0')
goto error;
bcn_config.min_bcn_dur = strtoul(p, &endptr, 0);
p = endptr;
/* check and skip , */
if (*p == '\0' || *++p == '\0')
goto error;
bcn_config.bcn_flood_dur = strtoul(p, &endptr, 0);
if (*endptr != '\0')
goto error;
bcn_config.version = AIBSS_BCN_FORCE_CONFIG_VER_0;
bcn_config.len = sizeof(aibss_bcn_force_config_t);
ret = wlu_iovar_set(wl, cmd->name, (void *) &bcn_config,
sizeof(aibss_bcn_force_config_t));
}
error:
return ret;
}
static int
wlu_srdump(void *wl, cmd_t *cmd, char **argv)
{
int ret, i, nw, nb = 0;
uint16 *words = (uint16 *)&buf[8];
srom_rw_t *srt;
/* srom has been expanded a few times, at the moment sromrev11 are the largest */
nw = SROM11_WORDS;
/* allow reading a larger (or any other-size one) if specified */
if (*++argv != NULL) {
nb = (int)strtol(*argv, NULL, 0);
if (nb & 1) {
printf("Byte count %d is odd\n", nb);
return BCME_BADARG;
}
nw = nb / 2;
}
srt = (srom_rw_t *)buf;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * nw);
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
if (words[SROM11_SIGN] == SROM11_SIGNATURE) {
nw = SROM11_WORDS;
} else if (words[SROM10_SIGN] == SROM4_SIGNATURE) {
nw = SROM10_WORDS;
} else {
nw = SROM4_WORDS;
if ((words[SROM4_SIGN] != SROM4_SIGNATURE) &&
(words[SROM8_SIGN] != SROM4_SIGNATURE))
nw = nb ? nw : SROM_WORDS;
}
for (i = 0; i < nw; i++) {
if ((i % 8) == 0)
printf("\n srom[%03d]: ", i);
printf("0x%04x ", words[i]);
}
printf("\n");
return 0;
}
static int
wlu_srwrite(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#else
char *arg;
char *endptr;
FILE *fp = NULL;
int ret = 0, erase, srcrc;
uint i, len;
srom_rw_t *srt = (srom_rw_t *)buf;
erase = !strcmp(*argv, "srclear");
srcrc = !strcmp(*argv, "srcrc");
/* We need at least one arg */
if (!*++argv)
return BCME_USAGE_ERROR;
arg = *argv++;
if (erase) {
if (*argv)
return BCME_USAGE_ERROR;
len = strtoul(arg, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "error parsing value \"%s\" as an integer for byte count\n",
arg);
return BCME_USAGE_ERROR;
}
srt->byteoff = 0x55aa;
} else if (!*argv) { /* srwrite or srcrc */
/* Only one arg, it better be a file name */
if (!(fp = fopen(arg, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", arg);
return BCME_BADARG;
}
len = fread(srt->buf, 1, SROM_MAX + 1, fp);
if ((ret = ferror(fp))) {
printf("\nerror %d reading %s\n", ret, arg);
ret = BCME_ERROR;
goto out;
}
if (!feof(fp)) {
printf("\nFile %s is too large\n", arg);
ret = BCME_ERROR;
goto out;
}
if (len == SROM4_WORDS * 2) {
if ((srt->buf[SROM4_SIGN] != SROM4_SIGNATURE) &&
(srt->buf[SROM8_SIGN] != SROM4_SIGNATURE)) {
printf("\nFile %s is %d bytes but lacks a REV4/ signature\n",
arg, SROM4_WORDS * 2);
ret = BCME_ERROR;
goto out;
}
} else if (len == SROM11_WORDS * 2) {
if (srt->buf[SROM11_SIGN] != SROM11_SIGNATURE) {
printf("\nFile %s is %d bytes but lacks a REV11/ signature\n",
arg, SROM11_WORDS * 2);
ret = BCME_ERROR;
goto out;
}
} else if ((len != SROM_WORDS * 2) && (len != SROM10_WORDS * 2) &&
(len != SROM_MAX)) {
printf("\nFile %s is %d bytes, not %d or %d or %d or %d bytes\n", arg, len,
SROM_WORDS * 2, SROM4_WORDS * 2, SROM10_WORDS, SROM_MAX);
ret = BCME_ERROR;
goto out;
}
srt->byteoff = 0;
} else {
if (srcrc) {
printf("srcrc only takes one arg\n");
ret = BCME_USAGE_ERROR;
goto out;
}
/* More than 1 arg, first is offset, rest are data. */
srt->byteoff = strtoul(arg, &endptr, 0);
if (*endptr != '\0')
goto nout;
i = 0;
while ((arg = *argv++) != NULL) {
srt->buf[i++] = (uint16)strtoul(arg, &endptr, 0);
if (*endptr != '\0') {
nout:
printf("\n%s is not an integer\n", arg);
ret = BCME_USAGE_ERROR;
goto out;
}
}
if (srt->byteoff & 1) {
printf("Byte offset (%d) is odd or negative\n", srt->byteoff);
ret = BCME_BADARG;
goto out;
}
len = 2 * i;
if ((srt->byteoff + len) > SROM_MAX) {
printf("Data extends past %d bytes\n", SROM_MAX);
ret = BCME_BUFTOOLONG;
goto out;
}
}
srt->nbytes = len;
if (srcrc) {
srt->byteoff = 0x55ab; /* Hack for srcrc */
if ((ret = wlu_get(wl, cmd->get, buf, len + 8)) == 0)
printf("0x%x\n", (uint8)buf[0]);
} else {
printf("Writing srom. ioctl %d, iolen %d, sroff %d, len %d\n",
cmd->set, len + 8, srt->byteoff, srt->nbytes);
ret = wlu_set(wl, cmd->set, buf, len + 8);
}
out:
fflush(stdout);
if (fp)
fclose(fp);
return ret;
#endif /* BWL_FILESYSTEM_SUPPORT */
}
static int
wlu_ciswrite(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#else
char *arg, *bufp;
FILE *fp = NULL;
int ret = 0;
uint32 len;
cis_rw_t cish;
char *cisp, *cisdata;
UNUSED_PARAMETER(cmd);
/* We need extacly one arg */
if (!*++argv || argv[1])
return BCME_USAGE_ERROR;
/* initialize buffer with iovar */
bufp = buf;
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(bufp, "ciswrite");
bufp += strlen("ciswrite") + 1;
cisp = bufp;
cisdata = cisp + sizeof(cish);
cish.source = htod32(0);
/* grab the filename arg */
arg = *argv;
if (!(fp = fopen(arg, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", arg);
return BCME_BADARG;
}
len = fread(cisdata, 1, SROM_MAX + 1, fp);
if ((ret = ferror(fp))) {
printf("\nerror %d reading %s\n", ret, arg);
ret = BCME_ERROR;
goto out;
}
if (!feof(fp)) {
printf("\nFile %s is too large\n", arg);
ret = BCME_ERROR;
goto out;
}
/* fill in offset and length */
cish.byteoff = htod32(0);
cish.nbytes = htod32(len);
memcpy(cisp, (char*)&cish, sizeof(cish));
printf("len %d sizeof(cish) %d total %d\n", len, (int)sizeof(cish),
(int)(len + sizeof(cish)));
ret = wl_set(wl, WLC_SET_VAR, buf, (cisp - buf) + sizeof(cish) + len);
if (ret < 0) {
fprintf(stderr, "ciswrite failed: %d\n", ret);
}
out:
if (fp)
fclose(fp);
return ret;
#endif /* BWL_FILESYSTEM_SUPPORT */
}
static int
wlu_cisupdate(void *wl, cmd_t *cmd, char **argv)
{
char *bufp, *endptr;
int ret = 0;
int preview = 0;
uint32 off;
uint32 len;
uint32 updatelen;
uint32 i;
char hexstr[3];
char bytes[SROM_MAX];
cis_rw_t cish;
char *cisp;
UNUSED_PARAMETER(cmd);
/* validate arg count */
if (!*++argv || !argv[1])
return BCME_USAGE_ERROR;
if (argv[2] && !strcmp(argv[2], "--preview"))
preview = 1;
/* grab byte offset */
off = (uint32)strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
bufp = argv[1];
updatelen = strlen(bufp);
if (updatelen % 2) {
fprintf(stderr, "cisupdate hex string must contain an even number of digits\n");
goto done;
}
updatelen /= 2;
/* convert and store hex byte values */
for (i = 0; i < updatelen; i++) {
hexstr[0] = *bufp;
hexstr[1] = *(bufp + 1);
if (!isxdigit((int)hexstr[0]) || !isxdigit((int)hexstr[1])) {
fprintf(stderr, "cisupdate invalid hex digit(s) in %s\n", argv[1]);
goto done;
}
hexstr[2] = '\0';
bytes[i] = (char) strtol(hexstr, NULL, 16);
bufp += 2;
}
/* Prepare the read info */
cish.source = 0;
cish.byteoff = 0;
cish.nbytes = 0;
/* set up the buffer and do the get (+9 allows space for "ciswrite" string later) */
memset(buf + 9, 0, WLC_IOCTL_MAXLEN);
strcpy(buf + 9, "cisdump");
bufp = buf + strlen("cisdump") + 1 + 9;
memcpy(bufp, (char*)&cish, sizeof(cish));
bufp += sizeof(cish);
ret = wl_get(wl, WLC_GET_VAR, buf + 9, (bufp - (buf + 9)) + SROM_MAX);
if (ret < 0) {
fprintf(stderr, "cisupdate failed to read cis: %d\n", ret);
goto done;
}
/* pull off the cis_rw_t */
bufp = buf + 9;
memcpy((char*)&cish, bufp, sizeof(cish));
len = dtoh32(cish.nbytes);
if ((off + updatelen) > len) {
fprintf(stderr, "cisupdate offset %d plus update len %d exceeds CIS len %d\n",
off, updatelen, len);
goto done;
}
/* move past to the data */
bufp += sizeof(cish);
/* update the bytes */
if (cish.source == WLC_CIS_SROM) {
for (i = 0; i < updatelen; ++i)
bufp[off + i] = bytes[i] & 0xff;
} else {
for (i = 0; i < updatelen; ++i) {
if (~bytes[i] & bufp[off + i]) {
fprintf(stderr, "cisupdate: OTP update incompatible:"
" update[%d](0x%02x)->cis[%d](0x%02x)\n",
i, bytes[i], off + i, bufp[off + i]);
goto done;
}
bufp[off + i] |= bytes[i];
}
}
/* initialize buffer with iovar */
bufp = buf;
strcpy(bufp, "ciswrite");
bufp += strlen("ciswrite") + 1;
cisp = bufp;
/* fill in cis_rw_t fields */
cish.source = 0;
cish.byteoff = 0;
cish.nbytes = htod32(len);
memcpy(cisp, (char*)&cish, sizeof(cish));
/* write the data back to the device */
printf("offset %d data %s cislen %d\n", off, argv[1], len);
if (preview) {
bufp += sizeof(cish);
for (i = 0; i < len; i++) {
if ((i % 8) == 0)
printf("\nByte %3d: ", i);
printf("0x%02x ", (uint8)bufp[i]);
}
printf("\n");
} else {
ret = wl_set(wl, WLC_SET_VAR, buf, (cisp - buf) + sizeof(cish) + len);
if (ret < 0) {
fprintf(stderr, "cisupdate cis write failed: %d\n", ret);
}
}
done:
return ret;
}
static int
wlu_cisdump(void *wl, cmd_t *cmd, char **argv)
{
char *bufp;
int i, ret = 0;
cis_rw_t cish;
uint nbytes = 0;
char *fname = NULL;
UNUSED_PARAMETER(cmd);
/* Grab and move past optional output file argument */
if ((argv[1] != NULL) && (strcmp(argv[1], "-b") == 0)) {
fname = argv[2];
argv += 2;
}
/* check for a length argument */
if (*++argv != NULL) {
nbytes = (int)strtol(*argv, NULL, 0);
if (nbytes & 1) {
printf("Invalid byte count %d, must be even\n", nbytes);
ret = BCME_BADARG;
goto done;
}
if (nbytes > SROM_MAX) {
printf("Count %d too large\n", nbytes);
ret = BCME_BUFTOOLONG;
goto done;
}
}
/* Prepare the read info */
cish.source = 0;
cish.byteoff = 0;
cish.nbytes = htod32(nbytes);
/* set up the buffer and do the get */
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "cisdump");
bufp = buf + strlen("cisdump") + 1;
memcpy(bufp, (char*)&cish, sizeof(cish));
bufp += sizeof(cish);
ret = wl_get(wl, WLC_GET_VAR, buf, (bufp - buf) + (nbytes ? nbytes : SROM_MAX));
if (ret < 0) {
fprintf(stderr, "Failed cisdump request: %d\n", ret);
goto done;
}
/* pull off the cis_rw_t */
bufp = buf;
memcpy((char*)&cish, bufp, sizeof(cish));
cish.source = dtoh32(cish.source);
cish.byteoff = dtoh32(cish.byteoff);
cish.nbytes = dtoh32(cish.nbytes);
/* move past to the data */
bufp += sizeof(cish);
printf("Source: %d (%s)", cish.source,
(cish.source == WLC_CIS_DEFAULT) ? "Built-in default" :
(cish.source == WLC_CIS_SROM) ? "External SPROM" :
(cish.source == WLC_CIS_OTP) ? "Internal OTP" : "Unknown?");
if (!nbytes)
printf("\nMaximum length: %d bytes", cish.nbytes);
for (i = 0; i < (int)cish.nbytes; i++) {
if ((i % 8) == 0)
printf("\nByte %3d: ", i);
printf("0x%02x ", (uint8)bufp[i]);
}
printf("\n");
#if defined(BWL_FILESYSTEM_SUPPORT)
if (fname != NULL) {
FILE *fp;
if (!nbytes)
nbytes = cish.nbytes;
fp = fopen(fname, "wb");
if (fp != NULL) {
ret = fwrite(bufp, 1, nbytes, fp);
if (ret != (int)nbytes) {
fprintf(stderr, "Error writing %d bytes to file, rc %d!\n",
(int)nbytes, ret);
ret = BCME_ERROR;
} else {
printf("Wrote %d bytes to %s\n", ret, fname);
ret = 0;
}
fclose(fp);
} else {
fprintf(stderr, "Problem opening file %s\n", fname);
ret = BCME_BADARG;
}
}
#endif /* BWL_FILESYSTEM_SUPPORT */
done:
return ret;
}
/* linux, MacOS, NetBSD: ffs is in the standard C library */
/* CFE, HNDRTE & IOPOS: Not needed, the code below is ifdef out */
/* VX wants prototypes even for static functions. */
static char* find_pattern(char **argv, const char *pattern, uint *val);
static char* find_pattern2(char **argv, const char *pattern, uint *val, int vnum);
static int newtuple(char *b, int *cnt, uint8 tag, const cis_tuple_t *srv);
static int parsecis(char *b, char **argv, int sromrev);
static const sromvar_t *srvlookup(const sromvar_t *tab, char *name, int nlen, int sromrev);
/* Find an entry in argv[][] in this form
* name=value, could be pattern=(0x)1234 or pattern=ABC
*
* If *val is NULL, return the pointer to value.
* If *val is not NULL, fill the value into val, return the pointer to name if found,
* return NULL if no match found.
*/
static char*
find_pattern(char **argv, const char *pattern, uint *val)
{
char *ret = NULL, *name = NULL, **pargv = argv;
/* clear val first */
if (val) *val = 0;
while ((name = *pargv++)) {
if ((ret = strstr(name, pattern))) {
char *p = ret, *q = NULL;
/* Extracting the content */
p += strlen(pattern);
/* var name could have same prefix */
if (*p++ != '=') {
ret = NULL;
continue;
}
if (!val)
return (ret+strlen(pattern)+1);
*val = strtoul(p, &q, 0);
if (p == q) {
printf("Bad value: %s\n", ret);
return NULL;
}
break;
}
}
return ret;
}
/* Find an entry in argv[][] in this form
* name=value1,value2,...,value(n)
* n is indicated by vnum
* could be pattern=(0x)1234,... or pattern=ABC,...
*
* If *val is NULL, return the pointer to value.
* If *val is not NULL, fill the value into val, return the pointer to name if found,
* return NULL if no match found.
*/
static char*
find_pattern2(char **argv, const char *pattern, uint *val, int vnum)
{
char *ret = NULL, *name = NULL, **pargv = argv;
int i;
while ((name = *pargv++)) {
if ((ret = strstr(name, pattern))) {
char *p = ret, *q = NULL;
/* Extracting the content */
p += strlen(pattern);
/* var name could have same prefix */
if (*p++ != '=') {
ret = NULL;
continue;
}
if (!val)
return (ret+strlen(pattern)+1);
for (i = 0; i < vnum; i ++)
{
val[i] = strtoul(p, &q, 0);
if (p == q) {
printf("Bad value: %s\n", ret);
return NULL;
}
p = q + 1; /* skip ',' */
}
break;
}
}
return ret;
}
static int
newtuple(char *b, int *cnt, uint8 tag, const cis_tuple_t *srv)
{
memset(b, 0, srv->len + 2);
b[0] = tag;
b[1] = (char)srv->len;
b[2] = (char)srv->tag;
if (cnt)
*cnt += 3;
return 0;
}
/**
* When programming OTP or SROM, driver expects to receive a CIS from the wl utility.
* This function converts a caller supplied string (in **argv) containing nvram variables pairs into
* a CIS (in *b). Caller can dictate the binary CIS contents by using nvram string 'RAW=...' or
* 'RAW1=...'. Function will only create tuples for values in caller supplied nvram string.
*/
static int
parsecis(char *b, char **argv, int sromrev)
{
/* built-in list of known tuples and nvram var(s) associated with a specific tuple */
const cis_tuple_t *srv = cis_hnbuvars;
char *cpar = NULL, *p = NULL;
char *par;
char delimit[2] = " \0";
int cnt = 0, i = 0;
uint sromrev_mask = 0xffffffff;
if (sromrev > 0 && sromrev <= 31) {
sromrev_mask = 1 << sromrev;
} else {
printf("Invalid sromrev %d.\n", sromrev);
return BCME_BADARG;
}
/* Walk through built-in list of tuples, create append buffer */
while (srv->tag != 0xFF) {
uint val = 0;
/* Skip srv if not supported in sromrev */
if (!(sromrev_mask & srv->revmask)) {
srv++;
continue;
}
/* Special cases (Raw Data / macaddr / ccode / fem) */
if (srv->tag == OTP_RAW) {
if ((p = find_pattern(argv, "RAW", &val))) {
p += (strlen("RAW") + 1); /* RAW= */
for (;;) {
b[cnt++] = (unsigned char) strtoul(p, &p, 16);
if (!*p++)
break;
}
}
} else if (srv->tag == OTP_RAW1) {
if ((p = find_pattern(argv, "RAW1", NULL))) {
for (;;) {
b[cnt++] = (unsigned char) strtoul(p, &p, 16);
if (!*p++)
break;
}
}
} else if (srv->tag == OTP_VERS_1) {
uint l1 = 1, l2 = 1;
char *p2 = NULL;
if ((p = find_pattern(argv, "manf", NULL)))
l1 += strlen(p);
if ((p2 = find_pattern(argv, "productname", NULL)))
l2 += strlen(p2);
if ((p != NULL) | (p2 != NULL)) {
b[cnt++] = CISTPL_VERS_1;
b[cnt++] = 2 + l1 + l2;
b[cnt++] = 8;
b[cnt++] = 0;
if (p) {
char *q = p;
/* Replace '_' by space */
while ((q = strchr(q, '_')))
*q = ' ';
memcpy(&b[cnt], p, l1);
} else
b[cnt] = '\0';
cnt += l1;
if (p2) {
char *q = p2;
/* Replace '_' by space */
while ((q = strchr(q, '_')))
*q = ' ';
memcpy(&b[cnt], p2, l2);
} else
b[cnt] = '\0';
cnt += l2;
}
} else if (srv->tag == OTP_MANFID) {
bool found = FALSE;
uint manfid = 0, prodid = 0;
if ((p = find_pattern(argv, "manfid", &manfid)))
found = TRUE;
if ((p = find_pattern(argv, "prodid", &prodid)))
found = TRUE;
if (found) {
b[cnt++] = CISTPL_MANFID;
b[cnt++] = srv->len;
b[cnt++] = (uint8)(manfid & 0xff);
b[cnt++] = (uint8)((manfid >> 8) & 0xff);
b[cnt++] = (uint8)(prodid & 0xff);
b[cnt++] = (uint8)((prodid >> 8) & 0xff);
}
} else if (srv->tag == HNBU_MACADDR) {
if ((p = find_pattern(argv, "macaddr", NULL))) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
if (!wl_ether_atoe(p, (struct ether_addr*)&b[cnt]))
printf("Argument does not look like a MAC "
"address: %s\n", p);
cnt += sizeof(struct ether_addr);
}
} else if (srv->tag == HNBU_CCODE) {
bool found = FALSE;
char tmp[3] = "\0\0\0";
if ((p = find_pattern(argv, "ccode", NULL))) {
found = TRUE;
tmp[0] = *p++;
tmp[1] = *p++;
}
if ((p = find_pattern(argv, "cctl", &val))) {
found = TRUE;
tmp[2] = (uint8)val;
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
memcpy(&b[cnt], tmp, 3);
cnt += 3; /* contents filled already */
}
} else if (srv->tag == HNBU_RSSISMBXA2G) {
bool found = FALSE;
char tmp[2] = "\0\0";
if ((p = find_pattern(argv, "rssismf2g", &val))) {
found = TRUE;
tmp[0] |= val & 0xf;
}
if ((p = find_pattern(argv, "rssismc2g", &val))) {
found = TRUE;
tmp[0] |= (val & 0xf) << 4;
}
if ((p = find_pattern(argv, "rssisav2g", &val))) {
found = TRUE;
tmp[1] |= val & 0x7;
}
if ((p = find_pattern(argv, "bxa2g", &val))) {
found = TRUE;
tmp[1] |= (val & 0x3) << 3;
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
memcpy(&b[cnt], tmp, 2);
cnt += 2; /* contents filled already */
}
} else if (srv->tag == HNBU_RSSISMBXA5G) {
bool found = FALSE;
char tmp[2] = "\0\0";
if ((p = find_pattern(argv, "rssismf5g", &val))) {
found = TRUE;
tmp[0] |= val & 0xf;
}
if ((p = find_pattern(argv, "rssismc5g", &val))) {
found = TRUE;
tmp[0] |= (val & 0xf) << 4;
}
if ((p = find_pattern(argv, "rssisav5g", &val))) {
found = TRUE;
tmp[1] |= val & 0x7;
}
if ((p = find_pattern(argv, "bxa5g", &val))) {
found = TRUE;
tmp[1] |= (val & 0x3) << 3;
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
memcpy(&b[cnt], tmp, 2);
cnt += 2; /* contents filled already */
}
} else if (srv->tag == HNBU_FEM) {
bool found = FALSE;
uint16 tmp2g = 0, tmp5g = 0;
if ((p = find_pattern(argv, "antswctl2g", &val))) {
found = TRUE;
tmp2g |= ((val << SROM8_FEM_ANTSWLUT_SHIFT) &
SROM8_FEM_ANTSWLUT_MASK);
}
if ((p = find_pattern(argv, "triso2g", &val))) {
found = TRUE;
tmp2g |= ((val << SROM8_FEM_TR_ISO_SHIFT) &
SROM8_FEM_TR_ISO_MASK);
}
if ((p = find_pattern(argv, "pdetrange2g", &val))) {
found = TRUE;
tmp2g |= ((val << SROM8_FEM_PDET_RANGE_SHIFT) &
SROM8_FEM_PDET_RANGE_MASK);
}
if ((p = find_pattern(argv, "extpagain2g", &val))) {
found = TRUE;
tmp2g |= ((val << SROM8_FEM_EXTPA_GAIN_SHIFT) &
SROM8_FEM_EXTPA_GAIN_MASK);
}
if ((p = find_pattern(argv, "tssipos2g", &val))) {
found = TRUE;
tmp2g |= ((val << SROM8_FEM_TSSIPOS_SHIFT) &
SROM8_FEM_TSSIPOS_MASK);
}
if ((p = find_pattern(argv, "antswctl5g", &val))) {
found = TRUE;
tmp5g |= ((val << SROM8_FEM_ANTSWLUT_SHIFT) &
SROM8_FEM_ANTSWLUT_MASK);
}
if ((p = find_pattern(argv, "triso5g", &val))) {
found = TRUE;
tmp5g |= ((val << SROM8_FEM_TR_ISO_SHIFT) &
SROM8_FEM_TR_ISO_MASK);
}
if ((p = find_pattern(argv, "pdetrange5g", &val))) {
found = TRUE;
tmp5g |= ((val << SROM8_FEM_PDET_RANGE_SHIFT) &
SROM8_FEM_PDET_RANGE_MASK);
}
if ((p = find_pattern(argv, "extpagain5g", &val))) {
found = TRUE;
tmp5g |= ((val << SROM8_FEM_EXTPA_GAIN_SHIFT) &
SROM8_FEM_EXTPA_GAIN_MASK);
}
if ((p = find_pattern(argv, "tssipos5g", &val))) {
found = TRUE;
tmp5g |= ((val << SROM8_FEM_TSSIPOS_SHIFT) &
SROM8_FEM_TSSIPOS_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(tmp2g & 0xff);
b[cnt++] = (uint8)((tmp2g >> 8) & 0xff);
b[cnt++] = (uint8)(tmp5g & 0xff);
b[cnt++] = (uint8)((tmp5g >> 8) & 0xff);
}
} else if (srv->tag == HNBU_UUID) {
char *uuidstr = NULL;
char nibble[3] = {0, 0, 0};
if ((uuidstr = find_pattern(argv, "uuid", NULL)) != NULL) {
/* uuid format 12345678-1234-5678-1234-567812345678 */
if (strlen(uuidstr) == 36) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
while (*uuidstr != '\0') {
if (*uuidstr == '-') {
uuidstr++;
continue;
}
nibble[0] = *uuidstr++;
nibble[1] = *uuidstr++;
b[cnt ++] = (char)strtoul(nibble, NULL, 16);
}
}
}
} else if (srv->tag == HNBU_TEMPTHRESH) {
bool found = FALSE;
char tmp[6] = "\0\0\0\0\0\0";
if ((p = find_pattern(argv, "tempthresh", &val))) {
found = TRUE;
tmp[0] = val;
}
if ((p = find_pattern(argv, "temps_period", &val))) {
found = TRUE;
tmp[1] |= ((val << SROM11_TEMPS_PERIOD_SHIFT) &
SROM11_TEMPS_PERIOD_MASK);
}
if ((p = find_pattern(argv, "temps_hysteresis", &val))) {
found = TRUE;
tmp[1] |= ((val << SROM11_TEMPS_HYSTERESIS_SHIFT) &
SROM11_TEMPS_HYSTERESIS_MASK);
}
if ((p = find_pattern(argv, "tempoffset", &val))) {
found = TRUE;
tmp[2] = val;
}
if ((p = find_pattern(argv, "tempsense_slope", &val))) {
found = TRUE;
tmp[3] = val;
}
if ((p = find_pattern(argv, "tempcorrx", &val))) {
found = TRUE;
tmp[4] |= ((val << SROM11_TEMPCORRX_SHIFT) &
SROM11_TEMPCORRX_MASK);
}
if ((p = find_pattern(argv, "tempsense_option", &val))) {
found = TRUE;
tmp[4] |= ((val << SROM11_TEMPSENSE_OPTION_SHIFT) &
SROM11_TEMPSENSE_OPTION_MASK);
}
if ((p = find_pattern(argv, "phycal_tempdelta", &val))) {
found = TRUE;
tmp[5] = val;
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
memcpy(&b[cnt], tmp, 6);
cnt += 6; /* contents filled already */
}
} else if (srv->tag == HNBU_FEM_CFG) {
bool found = FALSE;
uint16 fem_cfg1 = 0, fem_cfg2 = 0;
if ((p = find_pattern(argv, "femctrl", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_FEMCTRL_SHIFT) &
SROM11_FEMCTRL_MASK);
}
if ((p = find_pattern(argv, "papdcap2g", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_PAPDCAP_SHIFT) &
SROM11_PAPDCAP_MASK);
}
if ((p = find_pattern(argv, "tworangetssi2g", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_TWORANGETSSI_SHIFT) &
SROM11_TWORANGETSSI_MASK);
}
if ((p = find_pattern(argv, "pdgain2g", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_PDGAIN_SHIFT) &
SROM11_PDGAIN_MASK);
}
if ((p = find_pattern(argv, "epagain2g", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_EPAGAIN_SHIFT) &
SROM11_EPAGAIN_MASK);
}
if ((p = find_pattern(argv, "tssiposslope2g", &val))) {
found = TRUE;
fem_cfg1 |= ((val << SROM11_TSSIPOSSLOPE_SHIFT) &
SROM11_TSSIPOSSLOPE_MASK);
}
if ((p = find_pattern(argv, "gainctrlsph", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_GAINCTRLSPH_SHIFT) &
SROM11_GAINCTRLSPH_MASK);
}
if ((p = find_pattern(argv, "papdcap5g", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_PAPDCAP_SHIFT) &
SROM11_PAPDCAP_MASK);
}
if ((p = find_pattern(argv, "tworangetssi5g", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_TWORANGETSSI_SHIFT) &
SROM11_TWORANGETSSI_MASK);
}
if ((p = find_pattern(argv, "pdgain5g", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_PDGAIN_SHIFT) &
SROM11_PDGAIN_MASK);
}
if ((p = find_pattern(argv, "epagain5g", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_EPAGAIN_SHIFT) &
SROM11_EPAGAIN_MASK);
}
if ((p = find_pattern(argv, "tssiposslope5g", &val))) {
found = TRUE;
fem_cfg2 |= ((val << SROM11_TSSIPOSSLOPE_SHIFT) &
SROM11_TSSIPOSSLOPE_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(fem_cfg1 & 0xff);
b[cnt++] = (uint8)((fem_cfg1 >> 8) & 0xff);
b[cnt++] = (uint8)(fem_cfg2 & 0xff);
b[cnt++] = (uint8)((fem_cfg2 >> 8) & 0xff);
}
} else if (srv->tag == HNBU_ACRXGAINS_C0) {
bool found = FALSE;
uint16 rxgains = 0, rxgains1 = 0;
if ((p = find_pattern(argv, "rxgains5gtrelnabypa0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gtrisoa0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRISOA_SHIFT) &
SROM11_RXGAINS5GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gelnagaina0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GELNAGAINA_SHIFT) &
SROM11_RXGAINS5GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrelnabypa0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRELNABYPA_SHIFT) &
SROM11_RXGAINS2GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrisoa0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRISOA_SHIFT) &
SROM11_RXGAINS2GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gelnagaina0", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GELNAGAINA_SHIFT) &
SROM11_RXGAINS2GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrelnabypa0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GHTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrisoa0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRISOA_SHIFT) &
SROM11_RXGAINS5GHTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghelnagaina0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHELNAGAINA_SHIFT) &
SROM11_RXGAINS5GHELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrelnabypa0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GMTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrisoa0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRISOA_SHIFT) &
SROM11_RXGAINS5GMTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmelnagaina0", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMELNAGAINA_SHIFT) &
SROM11_RXGAINS5GMELNAGAINA_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(rxgains & 0xff);
b[cnt++] = (uint8)((rxgains >> 8) & 0xff);
b[cnt++] = (uint8)(rxgains1 & 0xff);
b[cnt++] = (uint8)((rxgains1 >> 8) & 0xff);
}
} else if (srv->tag == HNBU_ACRXGAINS_C1) {
bool found = FALSE;
uint16 rxgains = 0, rxgains1 = 0;
if ((p = find_pattern(argv, "rxgains5gtrelnabypa1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gtrisoa1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRISOA_SHIFT) &
SROM11_RXGAINS5GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gelnagaina1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GELNAGAINA_SHIFT) &
SROM11_RXGAINS5GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrelnabypa1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRELNABYPA_SHIFT) &
SROM11_RXGAINS2GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrisoa1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRISOA_SHIFT) &
SROM11_RXGAINS2GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gelnagaina1", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GELNAGAINA_SHIFT) &
SROM11_RXGAINS2GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrelnabypa1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GHTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrisoa1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRISOA_SHIFT) &
SROM11_RXGAINS5GHTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghelnagaina1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHELNAGAINA_SHIFT) &
SROM11_RXGAINS5GHELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrelnabypa1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GMTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrisoa1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRISOA_SHIFT) &
SROM11_RXGAINS5GMTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmelnagaina1", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMELNAGAINA_SHIFT) &
SROM11_RXGAINS5GMELNAGAINA_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(rxgains & 0xff);
b[cnt++] = (uint8)((rxgains >> 8) & 0xff);
b[cnt++] = (uint8)(rxgains1 & 0xff);
b[cnt++] = (uint8)((rxgains1 >> 8) & 0xff);
}
} else if (srv->tag == HNBU_ACRXGAINS_C2) {
bool found = FALSE;
uint16 rxgains = 0, rxgains1 = 0;
if ((p = find_pattern(argv, "rxgains5gtrelnabypa2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gtrisoa2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GTRISOA_SHIFT) &
SROM11_RXGAINS5GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gelnagaina2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS5GELNAGAINA_SHIFT) &
SROM11_RXGAINS5GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrelnabypa2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRELNABYPA_SHIFT) &
SROM11_RXGAINS2GTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gtrisoa2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GTRISOA_SHIFT) &
SROM11_RXGAINS2GTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains2gelnagaina2", &val))) {
found = TRUE;
rxgains |= ((val << SROM11_RXGAINS2GELNAGAINA_SHIFT) &
SROM11_RXGAINS2GELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrelnabypa2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GHTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghtrisoa2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHTRISOA_SHIFT) &
SROM11_RXGAINS5GHTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5ghelnagaina2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GHELNAGAINA_SHIFT) &
SROM11_RXGAINS5GHELNAGAINA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrelnabypa2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRELNABYPA_SHIFT) &
SROM11_RXGAINS5GMTRELNABYPA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmtrisoa2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMTRISOA_SHIFT) &
SROM11_RXGAINS5GMTRISOA_MASK);
}
if ((p = find_pattern(argv, "rxgains5gmelnagaina2", &val))) {
found = TRUE;
rxgains1 |= ((val << SROM11_RXGAINS5GMELNAGAINA_SHIFT) &
SROM11_RXGAINS5GMELNAGAINA_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(rxgains & 0xff);
b[cnt++] = (uint8)((rxgains >> 8) & 0xff);
b[cnt++] = (uint8)(rxgains1 & 0xff);
b[cnt++] = (uint8)((rxgains1 >> 8) & 0xff);
}
} else if (srv->tag == HNBU_PDOFF_2G) {
bool found = FALSE;
uint16 tmppdoff2g = 0;
if ((p = find_pattern(argv, "pdoffset2g40ma0", &val))) {
found = TRUE;
tmppdoff2g |= ((val << SROM11_PDOFF_2G_40M_A0_SHIFT) &
SROM11_PDOFF_2G_40M_A0_MASK);
}
if ((p = find_pattern(argv, "pdoffset2g40ma1", &val))) {
found = TRUE;
tmppdoff2g |= ((val << SROM11_PDOFF_2G_40M_A1_SHIFT) &
SROM11_PDOFF_2G_40M_A1_MASK);
}
if ((p = find_pattern(argv, "pdoffset2g40ma2", &val))) {
found = TRUE;
tmppdoff2g |= ((val << SROM11_PDOFF_2G_40M_A2_SHIFT) &
SROM11_PDOFF_2G_40M_A2_MASK);
}
if ((p = find_pattern(argv, "pdoffset2g40mvalid", &val))) {
found = TRUE;
tmppdoff2g |= ((val << SROM11_PDOFF_2G_40M_VALID_SHIFT) &
SROM11_PDOFF_2G_40M_VALID_MASK);
}
if (found) {
newtuple(&b[cnt], &cnt, CISTPL_BRCM_HNBU, srv);
b[cnt++] = (uint8)(tmppdoff2g & 0xff);
b[cnt++] = (uint8)((tmppdoff2g >> 8) & 0xff);
}
} else { /* All other tuples */
int found = FALSE, varlen = 0;
char *cur = &b[cnt];
uint newtp = TRUE;
/* example srv->params contents: "1aa2g 1aa5g" */
par = malloc(strlen(srv->params)+1);
if (!par)
return BCME_NOMEM;
/* Walk through each parameters in one tuple */
strcpy(par, srv->params);
cpar = strtok (par, delimit); /* current param */
while (cpar) {
int array_sz = 1;
val = 0;
/* Fill the CIS tuple to b but don't commit cnt yet */
if (newtp) {
newtuple(cur, NULL, CISTPL_BRCM_HNBU, srv);
cur += 3;
newtp = FALSE;
}
/* the first byte of each parameter indicates its length */
varlen = (*cpar++) - '0';
/* parse array size if any */
if (*cpar == '*') {
array_sz = 0;
while (((*++cpar) >= '0') && (*cpar <= '9'))
array_sz = (array_sz * 10) + *cpar - '0';
}
/* Find the parameter in the input argument list */
if ((p = find_pattern(argv, cpar, &val)))
found = TRUE;
else
val = 0;
while (found && array_sz--) {
*cur++ = (uint8)(val & 0xff);
if (varlen >= 2)
*cur++ = (uint8)((val >> 8) & 0xff);
if (varlen >= 4) {
*cur++ = (uint8)((val >> 16) & 0xff);
*cur++ = (uint8)((val >> 24) & 0xff);
}
/* skip the "," if more array elements */
if (p && array_sz) {
char *q = NULL;
p = strstr (p, ","); /* current param */
if (p) {
p++;
val = strtoul(p, &q, strncmp(p, "0x", 2) ?
10 : 16);
} else {
printf("Input array size error!");
free(par);
return BCME_BADARG;
}
}
}
/* move to the next parameter string */
cpar = strtok(NULL, delimit);
}
free(par);
/* commit the tuple if its valid */
if (found)
cnt += (cur - &b[cnt]);
}
srv++;
}
printf("sromrev %d buffer size %d bytes:\n", sromrev, cnt);
for (i = 0; i < cnt; i++) {
printf("0x%.02x ", b[i] & 0xff);
if (i%8 == 7) printf("\n");
}
printf("\n");
return cnt;
}
static const sromvar_t *
srvlookup(const sromvar_t *tab, char *name, int nlen, int sromrev)
{
uint32 srrmask;
const sromvar_t *srv = tab;
srrmask = 1 << sromrev;
while (srv->name) {
if ((strncmp(name, srv->name, nlen) == 0) &&
((srrmask & srv->revmask) != 0))
break;
while (srv->flags & SRFL_MORE)
srv++;
srv++;
}
return srv;
}
static int
wl_nvsource(void *wl, cmd_t *cmd, char **argv)
{
int32 val, err;
if ((err = wl_var_get(wl, cmd, argv)))
return (err);
val = dtoh32(*(int32*)buf);
switch (val) {
case 0:
printf("SROM\n");
break;
case 1:
printf("OTP\n");
break;
case 2:
printf("NVRAM\n");
break;
default:
printf("Unrecognized source %d\n", val);
break;
}
return 0;
}
/** read/write caller supplied NVRAM variables in OTP or SROM */
static int
wlu_srvar(void *wl, cmd_t *cmd, char **argv)
{
int ret, nw, nlen, ro, co, wr, sromrev, shift = 0;
bool otp = FALSE;
uint32 val32 = 0;
char *name, *p, *newval;
const sromvar_t *srv;
uint16 w, *words = (uint16 *)&buf[8];
srom_rw_t *srt;
struct ether_addr ea;
ro = !strcmp(*argv, "rdvar");
wr = !strcmp(*argv, "wrvar");
co = !strcmp(*argv, "cisconvert");
if (!*++argv)
return BCME_USAGE_ERROR;
/* Query the driver on where the cis comes from: OTP or SROM */
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "cis_source");
ret = wl_get(wl, WLC_GET_VAR, buf, strlen(buf)+1);
if (ret < 0) {
; /* printf("Error %x: cannot get cis_source\n", ret); */
}
if (buf[0] == WLC_CIS_OTP)
otp = TRUE;
if (otp && ro) {
/* read caller supplied nvram variable from driver */
wl_nvget(wl, cmd, --argv);
return ret;
}
/*
* Before OTP can be written, the caller supplied nvram string has to be converted into a
* list of CIS tuples. This CIS format is SROM rev dependent.
*/
if ((otp && wr) || co) {
int cnt = 0, err = 0;
uint sromrev = 8;
void *p = NULL;
/* Read all nvram variables from driver and retrieve srom revision from that */
if ((err = wlu_var_getbuf(wl, "nvram_dump", NULL, 0, &p)) < 0) {
err = wlu_get(wl, WLC_NVRAM_DUMP, &buf[0], WLC_IOCTL_MAXLEN);
}
if (err) {
printf("Fail to get sromrev from nvram file!\n");
return err;
}
if ((p = strstr(p, "sromrev"))) {
char *q = NULL;
p = (void*)((char*)p + 8);
/* for OTP, its either srom rev 10 or 16 */
sromrev = strtoul(p, &q, strncmp(p, "0x", 2) ? 10 : 16);
} else {
printf("sromrev not defined in nvram file!\n");
return BCME_ERROR;
}
/* convert caller supplied nvram string (in argv) into a list of tuples (in buf) */
if ((cnt = parsecis(buf, argv, sromrev)) <= 0) {
printf("CIS parse failure!\n");
return BCME_ERROR;
}
/* leave an empty srom_rw_t at the front for backward
* compatibility
*/
if (!co) {
/*
* Pass the CIS containing caller supplied nvram vars to driver so driver
* can write OTP. Driver decides which OTP region (hardware,software) will
* be written, depending on chip type and bus type.
*/
ret = wlu_iovar_set(wl, "cisvar", buf, cnt); /* IOV_BMAC_CISVAR */
}
return ret;
}
/* First read the srom and find out the sromrev */
srt = (srom_rw_t *)buf;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * SROM4_WORDS);
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
if (words[SROM11_SIGN] == SROM11_SIGNATURE) {
sromrev = 11;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * SROM11_WORDS);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
} else {
if ((words[SROM4_SIGN] != SROM4_SIGNATURE) &&
(words[SROM8_SIGN] != SROM4_SIGNATURE))
nw = SROM_CRCREV;
else
nw = SROM4_CRCREV;
sromrev = words[nw] & 0xff;
}
if ((sromrev < 2) || (sromrev > SROM_MAXREV)) {
return BCME_ERROR;
}
nw = 0;
while ((name = *argv++) != NULL) {
int off, off_base;
newval = strchr(name, '=');
if (newval)
*newval++ = '\0';
nlen = strlen(name);
if ((nlen == 0) || (nlen > 16)) {
printf("Bad variable name: %s\n", name);
continue;
}
off = 0;
srv = srvlookup(pci_sromvars, name, nlen + 1, sromrev);
if (srv->name == NULL) {
int path;
srv = srvlookup(perpath_pci_sromvars, name, nlen - 1, sromrev);
path = name[nlen - 1] - '0';
if ((srv->name == NULL) || (path < 0) || (path >= MAX_PATH_SROM)) {
printf("Variable %s does not exist in sromrev %d\n",
name, sromrev);
continue;
}
if (sromrev >= 11) {
if (path == 0) {
off = SROM11_PATH0;
} else if (path == 1) {
off = SROM11_PATH1;
} else if (path == 2) {
off = SROM11_PATH2;
}
} else if (sromrev >= 8) {
if (path == 0) {
off = SROM8_PATH0;
} else if (path == 1) {
off = SROM8_PATH1;
} else if (path == 2) {
off = SROM8_PATH2;
} else if (path == 3) {
off = SROM8_PATH3;
}
} else
off = (path == 0) ? SROM4_PATH0 : SROM4_PATH1;
}
off_base = off;
off += srv->off;
if (ro) {
/* This code is cheating a bit: it knows that SRFL_ETHADDR means three
* whole words, and SRFL_MORE means 2 whole words (i.e. the masks for
* them are all 0xffff).
*/
if (srv->flags & SRFL_ETHADDR) {
w = words[off];
ea.octet[0] = w >> 8;
ea.octet[1] = w & 0xff;
w = words[off + 1];
ea.octet[2] = w >> 8;
ea.octet[3] = w & 0xff;
w = words[off + 2];
ea.octet[4] = w >> 8;
ea.octet[5] = w & 0xff;
} else if (srv->flags & SRFL_MORE) {
val32 = words[off];
val32 |= words[srv[1].off] << 16;
} else {
shift = ffs(srv->mask) - 1;
val32 = (words[off] & srv->mask) >> shift;
}
/* OK, print it */
if (srv->flags & SRFL_ETHADDR)
printf("%s=%s", name, wl_ether_etoa(&ea));
else if (srv->flags & SRFL_PRHEX)
printf("%s=0x%x", name, val32);
else if (srv->flags & SRFL_PRSIGN)
printf("%s=%d", name, val32);
else
printf("%s=%u", name, val32);
if (srv->flags & SRFL_ARRAY) {
do {
srv ++;
off = off_base + srv->off;
if (srv->name == NULL)
break;
shift = ffs(srv->mask) - 1;
val32 = (words[off] & srv->mask) >> shift;
if (srv->flags & SRFL_PRHEX)
printf(",0x%x", val32);
else if (srv->flags & SRFL_PRSIGN)
printf(",%d", val32);
else
printf(",%u", val32);
} while (srv->flags & SRFL_ARRAY);
}
printf("\n");
} else { /* wr */
/* Make the change in the image we read */
if (!newval) {
printf("wrvar missing value to write for variable %s\n", name);
ro = 1;
break;
}
/* Cheating again as above */
if (srv->flags & SRFL_ETHADDR) {
if (!wl_ether_atoe(newval, &ea)) {
printf("Argument does not look like a MAC address: %s\n",
newval);
ret = BCME_USAGE_ERROR;
ro = 1;
break;
}
words[off] = (ea.octet[0] << 8) | ea.octet[1];
words[off + 1] = (ea.octet[2] << 8) | ea.octet[3];
words[off + 2] = (ea.octet[4] << 8) | ea.octet[5];
off += 2;
} else {
val32 = strtoul(newval, &p, 0);
if (p == newval) {
printf("Bad value: %s for variable %s\n", newval, name);
ro = 1;
break;
}
if (srv->flags & SRFL_MORE) {
words[off] = val32 & 0xffff;
words[off + 1] = val32 >> 16;
off++;
} else {
shift = ffs(srv->mask) - 1;
words[off] = (((val32 << shift) & srv->mask) |
(words[off] & ~srv->mask));
if (srv->flags & SRFL_ARRAY) {
do {
srv ++;
off = off_base + srv->off;
if (srv->name == NULL)
break;
newval = p + 1;
val32 = strtoul(newval, &p, 0);
if (p == newval) {
printf(
"Bad value: %s for variable %s\n",
newval, name);
ro = 1;
break;
}
shift = ffs(srv->mask) - 1;
words[off] =
(((val32 << shift) & srv->mask) |
(words[off] & ~srv->mask));
} while (srv->flags & SRFL_ARRAY);
}
}
}
if (off > nw)
nw = off;
}
}
if (!ro) {
/* Now write all the changes */
nw++;
srt->byteoff = 0;
srt->nbytes = htod32(2 * nw);
ret = wlu_set(wl, cmd->set, buf, (2 * nw) + 8);
if (ret < 0)
printf("Error %d writing the srom\n", ret);
}
return ret;
}
/* All 32bits are used. Please populate wl_msgs2[] with further entries */
static dbg_msg_t wl_msgs[] = {
{WL_ERROR_VAL, "error"},
{WL_ERROR_VAL, "err"},
{WL_TRACE_VAL, "trace"},
{WL_PRHDRS_VAL, "prhdrs"},
{WL_PRPKT_VAL, "prpkt"},
{WL_INFORM_VAL, "inform"},
{WL_INFORM_VAL, "info"},
{WL_INFORM_VAL, "inf"},
{WL_TMP_VAL, "tmp"},
{WL_OID_VAL, "oid"},
{WL_RATE_VAL, "rate"},
{WL_ASSOC_VAL, "assoc"},
{WL_ASSOC_VAL, "as"},
{WL_PRUSR_VAL, "prusr"},
{WL_PS_VAL, "ps"},
{WL_TXPWR_VAL, "txpwr"},
{WL_TXPWR_VAL, "pwr"},
{WL_PORT_VAL, "port"},
{WL_DUAL_VAL, "dual"},
{WL_WSEC_VAL, "wsec"},
{WL_WSEC_DUMP_VAL, "wsec_dump"},
{WL_LOG_VAL, "log"},
{WL_NRSSI_VAL, "nrssi"},
{WL_LOFT_VAL, "loft"},
{WL_REGULATORY_VAL, "regulatory"},
{WL_RADAR_VAL, "radar"},
{WL_MPC_VAL, "mpc"},
{WL_APSTA_VAL, "apsta"},
{WL_DFS_VAL, "dfs"},
{WL_MBSS_VAL, "mbss"},
{WL_CAC_VAL, "cac"},
{WL_AMSDU_VAL, "amsdu"},
{WL_AMPDU_VAL, "ampdu"},
{WL_FFPLD_VAL, "ffpld"},
{0, NULL}
};
/* msglevels which use wl_msg_level2 should go here */
static dbg_msg_t wl_msgs2[] = {
{WL_DPT_VAL, "dpt"},
{WL_SCAN_VAL, "scan"},
{WL_WOWL_VAL, "wowl"},
{WL_COEX_VAL, "coex"},
{WL_RTDC_VAL, "rtdc"},
{WL_PROTO_VAL, "proto"},
#ifdef WLBTAMP
{WL_BTA_VAL, "bta"},
#endif
{WL_CHANINT_VAL, "chanim"},
{WL_WMF_VAL, "wmf"},
#ifdef WLP2P
{WL_P2P_VAL, "p2p"},
#endif
{WL_ITFR_VAL, "itfr"},
#ifdef WLMCHAN
{WL_MCHAN_VAL, "mchan"},
#endif
#ifdef WLTDLS
{WL_TDLS_VAL, "tdls"},
#endif
{WL_PSTA_VAL, "psta"},
{WL_MCNX_VAL, "mcnx"},
{WL_PROT_VAL, "prot"},
{WL_TBTT_VAL, "tbtt"},
{WL_NIC_VAL, "nic"},
{WL_TIMESTAMP_VAL, "time"},
{WL_PWRSEL_VAL, "lpc"},
{WL_TSO_VAL, "tso"},
{WL_MQ_VAL, "mq"},
#ifdef WLP2PO
{WL_P2PO_VAL, "p2po"},
#endif
{WL_WNM_VAL, "wnm"},
{0, NULL}
};
static int
wl_msglevel(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint hval = 0, len, val = 0, found, last_val = 0, msglevel = 0, msglevel2_add = 0;
uint msglevel2_del = 0, msglevel_add = 0, msglevel_del = 0, supported = 1;
char *endptr = NULL;
dbg_msg_t *dbg_msg = wl_msgs, *dbg_msg2 = wl_msgs2;
void *ptr = NULL;
struct wl_msglevel2 msglevel64, *reply;
const char *cmdname = "msglevel";
UNUSED_PARAMETER(cmd);
/* but preseve older IOCTL call for older drivers */
if ((ret = wlu_var_getbuf_sm(wl, cmdname, &msglevel64, sizeof(msglevel64), &ptr) < 0)) {
if ((ret = wlu_get(wl, WLC_GET_MSGLEVEL, &msglevel, sizeof(int))) < 0)
return (ret);
supported = 0;
msglevel = dtoh32(msglevel);
if (!*++argv) {
printf("0x%x ", msglevel);
for (i = 0; (val = dbg_msg[i].value); i++) {
if ((msglevel & val) && (val != last_val))
printf(" %s", dbg_msg[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char *s = *argv;
if (*s == '+' || *s == '-')
s++;
else
msglevel_del = ~0; /* make the whole list absolute */
val = strtoul(s, &endptr, 0);
if (val == 0xFFFFFFFF) {
fprintf(stderr,
"Bits >32 are not supported on this driver version\n");
val = 1;
}
/* not an integer if not all the string was parsed by strtoul */
if (*endptr != '\0') {
for (i = 0; (val = dbg_msg[i].value); i++)
if (stricmp(dbg_msg[i].string, s) == 0)
break;
if (!val)
goto usage;
}
if (**argv == '-')
msglevel_del |= val;
else
msglevel_add |= val;
++argv;
}
msglevel &= ~msglevel_del;
msglevel |= msglevel_add;
msglevel = htod32(msglevel);
return (wlu_set(wl, WLC_SET_MSGLEVEL, &msglevel, sizeof(int)));
} else { /* 64bit message level */
reply = (struct wl_msglevel2 *)ptr;
reply->low = dtoh32(reply->low);
reply->high = dtoh32(reply->high);
if (!*++argv) {
if (reply->high != 0)
printf("0x%x%08x", reply->high, reply->low);
else
printf("0x%x ", reply->low);
for (i = 0; (val = dbg_msg2[i].value); i++) {
if (((reply->high & val)) && (val != last_val))
printf(" %s", dbg_msg2[i].string);
last_val = val;
}
last_val = 0;
for (i = 0; (val = dbg_msg[i].value); i++) {
if (((reply->low & val)) && (val != last_val))
printf(" %s", dbg_msg[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char* s = *argv;
char t[32];
found = 0;
if (*s == '+' || *s == '-')
s++;
else {
msglevel_del = ~0; /* make the whole list absolute */
msglevel2_del = ~0;
}
val = strtoul(s, &endptr, 0);
if (val == 0xFFFFFFFF){ /* Assume >32 bit hex passed in */
if (!(*s == '0' && *(s+1) == 'x')) {
fprintf(stderr,
"Msg bits >32 take only numerical input in hex\n");
val = 0;
} else {
char c[32] = "0x";
len = strlen(s);
hval = strtoul(strncpy(t, s, len-8), &endptr, 0);
*endptr = 0;
s = s+strlen(t);
s = strcat(c, s);
val = strtoul(s, &endptr, 0);
if (hval == 0xFFFFFFFF) {
fprintf(stderr, "Invalid entry for msglevel\n");
hval = 0;
val = 0;
}
}
}
if (*endptr != '\0') {
for (i = 0; (val = dbg_msg[i].value); i++) {
if (stricmp(dbg_msg[i].string, s) == 0) {
found = 1;
break;
}
}
if (!found) {
for (i = 0; (hval = dbg_msg2[i].value); i++)
if (stricmp(dbg_msg2[i].string, s) == 0)
break;
}
if (!val && !hval)
goto usage;
}
if (**argv == '-') {
msglevel_del |= val;
if (!found)
msglevel2_del |= hval;
}
else {
msglevel_add |= val;
if (!found)
msglevel2_add |= hval;
}
++argv;
}
reply->low &= ~msglevel_del;
reply->high &= ~msglevel2_del;
reply->low |= msglevel_add;
reply->high |= msglevel2_add;
reply->low = htod32(reply->low);
reply->high = htod32(reply->high);
msglevel64.low = reply->low;
msglevel64.high = reply->high;
return (wlu_var_setbuf(wl, cmdname, &msglevel64, sizeof(msglevel64)));
}
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (val = dbg_msg[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, dbg_msg[i].string);
else
fprintf(stderr, ", %s", dbg_msg[i].string);
last_val = val;
}
if (supported) {
for (i = 0; (val = dbg_msg2[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%x00000000 %s", val, dbg_msg2[i].string);
else
fprintf(stderr, ", %s", dbg_msg2[i].string);
last_val = val;
}
}
fprintf(stderr, "\n");
return 0;
}
#include <phy_dbg_api.h>
static phy_msg_t wl_phy_msgs[] = {
{PHYHAL_ERROR, "error"},
{PHYHAL_ERROR, "err"},
{PHYHAL_TRACE, "trace"},
{PHYHAL_INFORM, "inform"},
{PHYHAL_TMP, "tmp"},
{PHYHAL_TXPWR, "txpwr"},
{PHYHAL_CAL, "cal"},
{PHYHAL_RADAR, "radar"},
{PHYHAL_THERMAL, "thermal"},
{PHYHAL_PAPD, "papd"},
{PHYHAL_RXIQ, "rxiq"},
{PHYHAL_FCBS, "fcbs"},
{0, NULL}
};
static int
wl_phymsglevel(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint val = 0, last_val = 0;
uint phymsglevel = 0, phymsglevel_add = 0, phymsglevel_del = 0;
char *endptr;
phy_msg_t *phy_msg = wl_phy_msgs;
const char *cmdname = "phymsglevel";
UNUSED_PARAMETER(cmd);
if ((ret = wlu_iovar_getint(wl, cmdname, (int *)&phymsglevel) < 0)) {
return ret;
}
phymsglevel = dtoh32(phymsglevel);
if (!*++argv) {
printf("0x%x ", phymsglevel);
for (i = 0; (val = phy_msg[i].value); i++) {
if ((phymsglevel & val) && (val != last_val))
printf(" %s", phy_msg[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char *s = *argv;
if (*s == '+' || *s == '-')
s++;
else
phymsglevel_del = ~0; /* make the whole list absolute */
val = strtoul(s, &endptr, 0);
if (val == 0xFFFFFFFF) {
fprintf(stderr,
"Bits >32 are not supported on this driver version\n");
val = 1;
}
/* not an integer if not all the string was parsed by strtoul */
if (*endptr != '\0') {
for (i = 0; (val = phy_msg[i].value); i++)
if (stricmp(phy_msg[i].string, s) == 0)
break;
if (!val)
goto usage;
}
if (**argv == '-')
phymsglevel_del |= val;
else
phymsglevel_add |= val;
++argv;
}
phymsglevel &= ~phymsglevel_del;
phymsglevel |= phymsglevel_add;
phymsglevel = htod32(phymsglevel);
return (wlu_iovar_setint(wl, cmdname, (int)phymsglevel));
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (val = phy_msg[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, phy_msg[i].string);
else
fprintf(stderr, ", %s", phy_msg[i].string);
last_val = val;
}
return 0;
}
struct d11_mcs_rate_info {
uint8 constellation_bits;
uint8 coding_q;
uint8 coding_d;
};
static const struct d11_mcs_rate_info wlu_mcs_info[] = {
{ 1, 1, 2 }, /* MCS 0: MOD: BPSK, CR 1/2 */
{ 2, 1, 2 }, /* MCS 1: MOD: QPSK, CR 1/2 */
{ 2, 3, 4 }, /* MCS 2: MOD: QPSK, CR 3/4 */
{ 4, 1, 2 }, /* MCS 3: MOD: 16QAM, CR 1/2 */
{ 4, 3, 4 }, /* MCS 4: MOD: 16QAM, CR 3/4 */
{ 6, 2, 3 }, /* MCS 5: MOD: 64QAM, CR 2/3 */
{ 6, 3, 4 }, /* MCS 6: MOD: 64QAM, CR 3/4 */
{ 6, 5, 6 }, /* MCS 7: MOD: 64QAM, CR 5/6 */
{ 8, 3, 4 }, /* MCS 8: MOD: 256QAM, CR 3/4 */
{ 8, 5, 6 } /* MCS 9: MOD: 256QAM, CR 5/6 */
};
static uint
wl_mcs2rate(uint mcs, uint nss, uint bw, int sgi)
{
const int ksps = 250; /* kilo symbols per sec, 4 us sym */
const int Nsd_20MHz = 52;
const int Nsd_40MHz = 108;
const int Nsd_80MHz = 234;
const int Nsd_160MHz = 468;
uint rate;
if (mcs == 32) {
/* just return fixed values for mcs32 instead of trying to parametrize */
rate = (sgi == 0) ? 6000 : 6700;
} else if (mcs <= 9) {
/* This calculation works for 11n HT and 11ac VHT if the HT mcs values
* are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
* That is, HT MCS 23 is a base MCS = 7, Nss = 3
*/
/* find the number of complex numbers per symbol */
if (bw == 20) {
rate = Nsd_20MHz;
} else if (bw == 40) {
rate = Nsd_40MHz;
} else if (bw == 80) {
rate = Nsd_80MHz;
} else if (bw == 160) {
rate = Nsd_160MHz;
} else {
rate = 1;
}
/* multiply by bits per number from the constellation in use */
rate = rate * wlu_mcs_info[mcs].constellation_bits;
/* adjust for the number of spatial streams */
rate = rate * nss;
/* adjust for the coding rate given as a quotient and divisor */
rate = (rate * wlu_mcs_info[mcs].coding_q) / wlu_mcs_info[mcs].coding_d;
/* multiply by Kilo symbols per sec to get Kbps */
rate = rate * ksps;
/* adjust the symbols per sec for SGI
* symbol duration is 4 us without SGI, and 3.6 us with SGI,
* so ratio is 10 / 9
*/
if (sgi) {
/* add 4 for rounding of division by 9 */
rate = ((rate * 10) + 4) / 9;
}
} else {
rate = 0;
}
return rate;
}
/* take a wl_ratespec arg and return phy rate in 500Kbps units */
static int
wl_ratespec2rate(uint32 rspec)
{
const char* fn_name = "wl_ratespec2rate";
int rate = -1;
int sgi = ((rspec & WL_RSPEC_SGI) != 0);
if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) {
rate = (rspec & WL_RSPEC_RATE_MASK);
} else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) {
uint mcs = (rspec & WL_RSPEC_RATE_MASK);
if (mcs > 32) {
fprintf(stderr, "%s: MCS %u out of range (>32) in ratespec 0x%X\n",
fn_name, mcs, rspec);
} else if (mcs == 32) {
rate = wl_mcs2rate(mcs, 1, 40, sgi) / 500;
} else {
uint nss = 1 + (mcs / 8);
mcs = mcs % 8;
rate = wl_mcs2rate(mcs, nss, 20, sgi) / 500;
}
} else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) {
uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
uint nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
rate = wl_mcs2rate(mcs, nss, 20, sgi) / 500;
} else {
fprintf(stderr, "%s: expected rate encoding in ratespec 0x%X\n",
fn_name, (uint)rspec);
}
return rate;
}
/* take rate arg in units of 500Kbits/s and print it in units of Mbit/s */
static void
wl_printrate(int val)
{
char rate_buf[32];
printf("%s\n", rate_int2string(rate_buf, val));
}
/* convert rate string in Mbit/s format, like "11", "5.5", to internal 500 Kbit/s units */
static int
rate_string2int(char *s)
{
if (!stricmp(s, "-1"))
return (0);
if (!stricmp(s, "5.5"))
return (11);
return (atoi(s) * 2);
}
/* convert rate internal 500 Kbits/s units to string in Mbits/s format, like "11", "5.5" */
static char*
rate_int2string(char *rate_buf, int val)
{
if ((val == -1) || (val == 0))
sprintf(rate_buf, "auto");
else
sprintf(rate_buf, "%d%s Mbps", (val / 2), (val & 1) ? ".5" : "");
return (rate_buf);
}
/* Format a ratespec for "nrate" output
* Will handle both current wl_ratespec and legacy (ioctl_version 1) nrate ratespec
*/
static void
wl_nrate_print(uint32 rspec)
{
const char * rspec_auto = "auto";
uint encode, rate, txexp = 0, bw_val;
const char* stbc = "";
const char* ldpc = "";
const char* sgi = "";
const char* bw = "";
int stf;
if (rspec == 0) {
encode = WL_RSPEC_ENCODE_RATE;
} else if (ioctl_version == 1) {
encode = (rspec & OLD_NRATE_MCS_INUSE) ? WL_RSPEC_ENCODE_HT : WL_RSPEC_ENCODE_RATE;
stf = (int)((rspec & OLD_NRATE_STF_MASK) >> OLD_NRATE_STF_SHIFT);
rate = (rspec & OLD_NRATE_RATE_MASK);
if (rspec & OLD_NRATE_OVERRIDE) {
if (rspec & OLD_NRATE_OVERRIDE_MCS_ONLY)
rspec_auto = "fixed mcs only";
else
rspec_auto = "fixed";
}
} else {
int siso;
encode = (rspec & WL_RSPEC_ENCODING_MASK);
rate = (rspec & WL_RSPEC_RATE_MASK);
txexp = (rspec & WL_RSPEC_TXEXP_MASK) >> WL_RSPEC_TXEXP_SHIFT;
stbc = ((rspec & WL_RSPEC_STBC) != 0) ? " stbc" : "";
ldpc = ((rspec & WL_RSPEC_LDPC) != 0) ? " ldpc" : "";
sgi = ((rspec & WL_RSPEC_SGI) != 0) ? " sgi" : "";
bw_val = (rspec & WL_RSPEC_BW_MASK);
if (bw_val == WL_RSPEC_BW_20MHZ) {
bw = "bw20";
} else if (bw_val == WL_RSPEC_BW_40MHZ) {
bw = "bw40";
} else if (bw_val == WL_RSPEC_BW_80MHZ) {
bw = "bw80";
} else if (bw_val == WL_RSPEC_BW_160MHZ) {
bw = "bw160";
}
/* initialize stf mode to an illegal value and
* fix to a backward compatable value if possible
*/
stf = -1;
/* for stf calculation, determine if the rate is single stream.
* Legacy rates WL_RSPEC_ENCODE_RATE are single stream, and
* HT rates for mcs 0-7 are single stream
*/
siso = (encode == WL_RSPEC_ENCODE_RATE) ||
((encode == WL_RSPEC_ENCODE_HT) && rate < 8);
/* calc a value for nrate stf mode */
if (txexp == 0) {
if ((rspec & WL_RSPEC_STBC) && siso) {
/* STF mode STBC */
stf = OLD_NRATE_STF_STBC;
} else {
/* STF mode SISO or SDM */
stf = (siso) ? OLD_NRATE_STF_SISO : OLD_NRATE_STF_SDM;
}
} else if (txexp == 1 && siso) {
/* STF mode CDD */
stf = OLD_NRATE_STF_CDD;
}
if (rspec & WL_RSPEC_OVERRIDE_RATE) {
rspec_auto = "fixed";
}
}
if (encode == WL_RSPEC_ENCODE_RATE) {
if (rspec == 0) {
printf("auto\n");
} else {
printf("legacy rate %d%s Mbps stf mode %d %s\n",
rate/2, (rate % 2)?".5":"", stf, rspec_auto);
}
} else if (encode == WL_RSPEC_ENCODE_HT) {
printf("mcs index %d stf mode %d %s\n",
rate, stf, rspec_auto);
} else {
uint vht = (rspec & WL_RSPEC_VHT_MCS_MASK);
uint Nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
printf("vht mcs %d Nss %d Tx Exp %d %s%s%s%s %s\n",
vht, Nss, txexp, bw, stbc, ldpc, sgi, rspec_auto);
}
}
/*
* Format a ratespec for output of any of the wl_rate() iovars
*/
static char*
wl_rate_print(char *rate_buf, uint32 rspec)
{
uint encode, rate, txexp, bw_val;
const char* stbc;
const char* ldpc;
const char* sgi;
const char* bw;
encode = (rspec & WL_RSPEC_ENCODING_MASK);
rate = (rspec & WL_RSPEC_RATE_MASK);
txexp = (rspec & WL_RSPEC_TXEXP_MASK) >> WL_RSPEC_TXEXP_SHIFT;
bw_val = (rspec & WL_RSPEC_BW_MASK);
stbc = ((rspec & WL_RSPEC_STBC) != 0) ? " stbc" : "";
ldpc = ((rspec & WL_RSPEC_LDPC) != 0) ? " ldpc" : "";
sgi = ((rspec & WL_RSPEC_SGI) != 0) ? " sgi" : "";
if (bw_val == WL_RSPEC_BW_UNSPECIFIED) {
bw = "auto";
} else if (bw_val == WL_RSPEC_BW_20MHZ) {
bw = "20";
} else if (bw_val == WL_RSPEC_BW_40MHZ) {
bw = "40";
} else if (bw_val == WL_RSPEC_BW_80MHZ) {
bw = "80";
} else if (bw_val == WL_RSPEC_BW_160MHZ) {
bw = "160";
} else {
bw = "???";
}
if ((rspec & ~WL_RSPEC_TXEXP_MASK) == 0) { /* Ignore TxExpansion for NULL rspec check */
sprintf(rate_buf, "auto");
} else if (encode == WL_RSPEC_ENCODE_RATE) {
sprintf(rate_buf, "rate %d%s Mbps Tx Exp %d",
rate/2, (rate % 2)?".5":"", txexp);
} else if (encode == WL_RSPEC_ENCODE_HT) {
sprintf(rate_buf, "ht mcs %d Tx Exp %d BW %s%s%s%s",
rate, txexp, bw, stbc, ldpc, sgi);
} else if (encode == WL_RSPEC_ENCODE_VHT) {
uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
uint Nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
sprintf(rate_buf, "vht mcs %d Nss %d Tx Exp %d BW %s%s%s%s",
mcs, Nss, txexp, bw, stbc, ldpc, sgi);
} else {
sprintf(rate_buf, "<unknown encoding for ratespec 0x%08X>", rspec);
}
return rate_buf;
}
/* handles both "rate" and "mrate", which makes the flow a bit complex */
static int
wl_rate_mrate(void *wl, cmd_t *cmd, char **argv)
{
const char* fn_name = "wl_rate_mrate";
int error;
int val;
int band;
int list[3];
char aname[sizeof("5g_mrate") + 1];
char bgname[sizeof("2g_mrate") + 1];
char *name;
sprintf(aname, "5g_%s", cmd->name);
sprintf(bgname, "2g_%s", cmd->name);
if ((error = wlu_get(wl, WLC_GET_BAND, &band, sizeof(uint))) < 0)
return error;
band = dtoh32(band);
if ((error = wlu_get(wl, WLC_GET_BANDLIST, list, sizeof(list))) < 0)
return error;
list[0] = dtoh32(list[0]);
list[1] = dtoh32(list[1]);
list[2] = dtoh32(list[2]);
if (!list[0])
return BCME_ERROR;
else if (list[0] > 2)
list[0] = 2;
/* toss the command name from the args */
argv++;
if ((!strcmp(cmd->name, "rate"))) {
/* it is "rate" */
if (!*argv) {
/* it is "rate" get. handle it here */
/* WLC_GET_RATE processing */
if ((error = wlu_get(wl, cmd->get, &val, sizeof(int))) < 0)
return error;
val = dtoh32(val);
wl_printrate(val);
return 0;
}
}
switch (band) {
case WLC_BAND_AUTO :
if (list[0] > 1) {
fprintf(stderr,
"%s: driver band must be locked to %s %s, use %s/%s instead\n",
fn_name, (*argv ? "set" : "get"), cmd->name,
bgname, aname);
return BCME_BADARG;
} else if (list[1] == WLC_BAND_5G)
name = (char *)aname;
else if (list[1] == WLC_BAND_2G)
name = (char *)bgname;
else
return BCME_ERROR;
break;
case WLC_BAND_5G :
name = (char *)aname;
break;
case WLC_BAND_2G :
name = (char *)bgname;
break;
default :
return BCME_ERROR;
}
if (!*argv) {
/* it is "mrate" get */
if ((error = wlu_iovar_getint(wl, name, &val) < 0))
return error;
val = dtoh32(val);
if (ioctl_version == 1) {
wl_printrate(val);
} else {
wl_printrate(wl_ratespec2rate((uint32)val));
}
} else {
/* create the ioctl value based on the major ioctl version */
if (ioctl_version == 1) {
/* for ver=1 ioctl interfaces, override values for 2g_(m)rate/5g_(m)rate
* are just given as 500 Kbps units
*/
val = rate_string2int(*argv);
} else {
/* for ver>1 ioctl interfaces, override values for 2g_(m)rate/5g_(m)rate
* are a wl_ratespec of a legacy rate.
*/
val = WL_RSPEC_ENCODE_RATE | rate_string2int(*argv);
}
val = htod32(val);
error = wlu_iovar_setint(wl, name, val);
}
return error;
}
/* parse the -v/--vht or -c argument for the wl_rate() command.
* return FALSE if the arg does not look like MxS or cMsS, where M and S are single digits
* return TRUE if the arg does look like MxS or cMsS, setting mcsp to M, and nssp to S
*/
static int
wl_parse_vht_spec(const char* cp, int* mcsp, int* nssp)
{
char *startp, *endp;
char c;
int mcs, nss;
char sx;
if (cp == NULL || cp[0] == '\0') {
return FALSE;
}
if (cp[0] == 'c') {
startp = (char*)cp + 1;
sx = 's';
}
else {
startp = (char*)cp;
sx = 'x';
}
mcs = (int)strtol(startp, &endp, 10);
/* verify MCS 0-9, and next char is 's' or 'x' */
if (mcs < 0 || mcs > 9 || endp[0] != sx) {
return FALSE;
}
/* grab the char after the 'x'/'s' and convert to value */
c = endp[1];
nss = 0;
if (isdigit((int)c)) {
nss = c - '0';
}
/* consume trailing space after digit */
cp = &endp[2];
while (isspace((int)(*cp))) {
cp++;
}
/* check for trailing garbage after digit */
if (cp[0] != '\0') {
return FALSE;
}
if (nss < 1 || nss > 8) {
return FALSE;
}
*mcsp = mcs;
*nssp = nss;
return TRUE;
}
static int
wl_rate(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t mo;
char *option_name = NULL;
char* endp;
const int option_name_len = 64;
const char* iov_name;
const char* fn_name = "wl_rate";
bool options = FALSE;
bool auto_set = FALSE;
bool legacy_set = FALSE, ht_set = FALSE, vht_set = FALSE;
int rate, mcs, Nss, tx_exp, bw;
bool stbc, ldpc, sgi;
int err, opt_err;
uint32 rspec = 0;
/* set default values */
rate = 0;
mcs = 0;
Nss = 0;
tx_exp = 0;
stbc = FALSE;
ldpc = FALSE;
sgi = FALSE;
bw = 0;
/* save the command name */
iov_name = *argv++;
if (ioctl_version == 1) {
fprintf(stderr,
"cmd %s not supported in this version of the driver, ioctl version 1.\n",
iov_name);
return BCME_USAGE_ERROR;
}
/* process a GET */
if (!*argv) {
uint32 val = 0;
char * rate_str;
const int rate_str_len = 64;
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_getint(wl, iov_name, (int*)&val)) < 0)
return err;
rate_str = malloc(rate_str_len);
if (rate_str == NULL) {
fprintf(stderr, "%s: malloc failure for rate string buffer.\n", fn_name);
return BCME_NOMEM;
}
memset(rate_str, 0, rate_str_len);
wl_rate_print(rate_str, val);
printf("%s\n", rate_str);
free(rate_str);
return 0;
}
/* process a SET */
/* initialze to common error for the miniopt processing */
err = BCME_USAGE_ERROR;
/* alloc option name for error messages */
option_name = malloc(option_name_len);
if (option_name == NULL) {
fprintf(stderr, "%s: malloc failure for option_name buffer.\n", fn_name);
return BCME_NOMEM;
}
memset(option_name, 0, option_name_len);
miniopt_init(&mo, fn_name, "lg", TRUE);
while ((opt_err = miniopt(&mo, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += mo.consumed;
/* track whether or not the command line is just a single positional
* parameter of "auto" or a legacy rate, or is using options
*/
if (!mo.positional) {
options = TRUE; /* command line is using options */
}
if (mo.positional) {
/* command line is using a positional parameter,
* complain if options are also being used
*/
if (options) {
fprintf(stderr,
"%s: cannot mix positional args and options. "
"Got positional arg \"%s\" after options.\n",
fn_name, mo.valstr);
goto exit;
}
/* complain if there are any more parameters since there should only
* be one positional param, "auto" or a legacy rate.
*/
if (*argv != NULL) {
fprintf(stderr,
"%s: unexpected parameter \"%s\" after rate value.\n",
fn_name, *argv);
goto exit;
}
/* test for "auto" to clear the rate override */
if (!strcmp(mo.valstr, "auto")) {
auto_set = TRUE;
} else {
/* pretend there was a '-r' option */
mo.opt = 'r';
}
}
/* format the option name for error messages */
if (mo.key[0] != '\0') {
/* would like to do the following, but snprintf() is not availble in
* all target builds. Fails in win_mfgtest_wl build.
*
* snprintf(option_name, option_name_len, "--%s", mo.key);
* option_name[option_name_len - 1] = '\0';
*/
size_t key_len;
key_len = strlen(mo.key);
/* limit key_len to space in str buffer minus the '--' and null */
key_len = MIN((uint)(option_name_len - 3), key_len);
memcpy(option_name, "--", 2);
memcpy(option_name + 2, mo.key, key_len);
option_name[2 + key_len] = '\0';
} else {
sprintf(option_name, "-%c", mo.opt);
}
/* Option: -r or --rate */
if (mo.opt == 'r' ||
!strcmp(mo.key, "rate")) {
if (mo.valstr == NULL) {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
/* special case check for "-r 5.5" */
if (!strcmp(mo.valstr, "5.5")) {
rate = 11;
} else if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
} else {
rate = mo.val*2;
}
legacy_set = TRUE;
}
/* Option: -h or --ht */
if (mo.opt == 'h' ||
!strcmp(mo.key, "ht")) {
if (mo.valstr == NULL) {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
mcs = mo.val;
ht_set = TRUE;
}
/* Option: -v or --vht */
if (mo.opt == 'v' ||
!strcmp(mo.key, "vht")) {
if (mo.valstr == NULL || mo.valstr[0] == 'c') {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
mcs = (int)strtol(mo.valstr, &endp, 10);
if (*endp == '\0') {
mcs = mo.val;
vht_set = TRUE;
} else if (wl_parse_vht_spec(mo.valstr, &mcs, &Nss)) {
vht_set = TRUE;
} else {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
}
/* Option: -c (system team's c notiation: c<MCS>s<Nss>) */
if (mo.opt == 'c') {
if (mo.valstr == NULL || mo.valstr[0] != 'c') {
fprintf(stderr,
"%s: expected a value start with c for %s option\n",
fn_name, option_name);
goto exit;
}
mcs = (int)strtol(mo.valstr + 1, &endp, 10);
if (*endp == '\0') {
vht_set = TRUE;
} else if (wl_parse_vht_spec(mo.valstr, &mcs, &Nss)) {
vht_set = TRUE;
} else {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
}
/* Option: -s or --ss */
if (mo.opt == 's' ||
!strcmp(mo.key, "ss")) {
if (mo.valstr == NULL) {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
Nss = mo.val;
}
/* Option: -x or --exp */
if (mo.opt == 'x' ||
!strcmp(mo.key, "exp")) {
if (mo.valstr == NULL) {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
tx_exp = mo.val;
if (tx_exp < 0 || tx_exp > 3) {
fprintf(stderr,
"%s: tx expansion %d out of range [0-3]\n",
fn_name, tx_exp);
err = BCME_RANGE;
goto exit;
}
}
/* Option: --stbc */
if (!strcmp(mo.key, "stbc")) {
if (mo.valstr != NULL) {
fprintf(stderr,
"%s: unexpected a value for %s option\n",
fn_name, option_name);
goto exit;
}
stbc = TRUE;
}
/* Option: -l or --ldpc */
if (mo.opt == 'l' ||
!strcmp(mo.key, "ldpc")) {
if (mo.valstr != NULL) {
fprintf(stderr,
"%s: unexpected a value for %s option\n",
fn_name, option_name);
goto exit;
}
ldpc = TRUE;
}
/* Option: -g or --sgi */
if (mo.opt == 'g' ||
!strcmp(mo.key, "sgi")) {
if (mo.valstr != NULL) {
fprintf(stderr,
"%s: unexpected a value for %s option\n",
fn_name, option_name);
goto exit;
}
sgi = TRUE;
}
/* Option: -b or --bandwidth */
if (mo.opt == 'b' ||
!strcmp(mo.key, "bandwidth")) {
if (mo.valstr == NULL) {
fprintf(stderr,
"%s: expected a value for %s option\n",
fn_name, option_name);
goto exit;
}
if (!mo.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a value for %s option\n",
fn_name, mo.valstr, option_name);
goto exit;
}
if (mo.val == 20) {
bw = WL_RSPEC_BW_20MHZ;
} else if (mo.val == 40) {
bw = WL_RSPEC_BW_40MHZ;
} else if (mo.val == 80) {
bw = WL_RSPEC_BW_80MHZ;
} else if (mo.val == 160) {
bw = WL_RSPEC_BW_160MHZ;
} else {
fprintf(stderr,
"%s: unexpected bandwidth specified \"%s\", "
"expected 20, 40, 80, or 160\n",
fn_name, mo.valstr);
goto exit;
}
}
}
/*
* check for necessary use of one of -r/-h/-v or auto
*/
if (!auto_set && !legacy_set && !ht_set && !vht_set) {
fprintf(stderr, "%s: must specify one of \"auto\", legacy rate -r/--rate, "
"HT (11n) rate -h/--ht, or VHT (11ac) rate -v/--vht\n",
fn_name);
goto exit;
}
/*
* check for incompatible use of -r/-h/-v
*/
if (legacy_set && ht_set) {
fprintf(stderr, "%s: cannot use legacy rate -r/--rate and "
"HT rate -h/--ht at the same time\n",
fn_name);
goto exit;
}
if (legacy_set && vht_set) {
fprintf(stderr, "%s: cannot use legacy rate -r/--rate and "
"HT rate -v/--vht at the same time\n",
fn_name);
goto exit;
}
if (ht_set && vht_set) {
fprintf(stderr, "%s: cannot use HT rate -h/--ht and "
"HT rate -v/--vht at the same time\n",
fn_name);
goto exit;
}
/* Nss can only be used with VHT */
if (!vht_set && Nss != 0) {
fprintf(stderr, "%s: cannot use -s/--ss option with non VHT rate\n",
fn_name);
goto exit;
}
/* STBC, LDPC, SGI can only be used with HT/VHT rates */
if ((stbc || ldpc || sgi) && !(ht_set || vht_set)) {
fprintf(stderr, "%s: cannot use STBC/LDPC/SGI options with non HT/VHT rates\n",
fn_name);
goto exit;
}
/* set the ratespec encoding type and basic rate value */
if (auto_set) {
rspec = 0;
} else if (legacy_set) {
rspec = WL_RSPEC_ENCODE_RATE; /* 11abg */
rspec |= rate;
} else if (ht_set) {
rspec = WL_RSPEC_ENCODE_HT; /* 11n HT */
rspec |= mcs;
} else {
rspec = WL_RSPEC_ENCODE_VHT; /* 11ac VHT */
if (Nss == 0) {
Nss = 1; /* default Nss = 1 if --ss option not given */
}
rspec |= (Nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs;
}
/* set the other rspec fields */
rspec |= (tx_exp << WL_RSPEC_TXEXP_SHIFT);
rspec |= bw;
rspec |= (stbc ? WL_RSPEC_STBC : 0);
rspec |= (ldpc ? WL_RSPEC_LDPC : 0);
rspec |= (sgi ? WL_RSPEC_SGI : 0);
err = wlu_iovar_setint(wl, iov_name, (int)rspec);
exit:
if (option_name != NULL) {
free(option_name);
}
return err;
}
static int
wl_wepstatus(void *wl, cmd_t *cmd, char **argv)
{
int val, error;
const char *name = "wsec";
int wsec;
UNUSED_PARAMETER(cmd);
if (!*++argv) {
if ((error = wlu_iovar_getint(wl, name, &val) < 0))
return error;
printf("%d\n", val);
return 0;
} else {
val = atoi(*argv);
if ((error = wlu_iovar_getint(wl, name, &wsec) < 0))
return error;
if (val)
wsec |= WEP_ENABLED;
else
wsec &= ~WEP_ENABLED;
return wlu_iovar_setint(wl, name, wsec);
}
}
static int
wl_bss_max(void *wl, cmd_t *cmd, char **argv)
{
int val = 1;
int error;
UNUSED_PARAMETER(argv);
/* Get the CAP variable; search for mbss4 or mbss16 */
strcpy(buf, "cap");
if ((error = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MEDLEN)) < 0)
return (error);
buf[WLC_IOCTL_MEDLEN] = '\0';
if (strstr(buf, "mbss16"))
val = 16;
else if (strstr(buf, "mbss4"))
val = 4;
printf("%d\n", val);
return (0);
}
static int
wl_phy_rate(void *wl, cmd_t *cmd, char **argv)
{
int val, error;
const char *name = cmd->name;
static const struct {
const char* orig;
const char* new;
} aliases[] = {
{"bg_rate", "2g_rate"},
{"bg_mrate", "2g_mrate"},
{"a_rate", "5g_rate"},
{"a_mrate", "5g_mrate"},
};
/* toss the command name from the args */
argv++;
/* if we are not using the legacy ioctl driver, translate the
* bg_* prefix to 2g_* and a_* to 5g_* iovars
*/
if (ioctl_version > 1) {
int i;
for (i = 0; i < 4; i++) {
if (!strcmp(name, aliases[i].orig)) {
name = aliases[i].new;
break;
}
}
}
if (!*argv) {
error = wlu_iovar_getint(wl, name, &val);
if (error < 0)
return (error);
val = dtoh32(val);
if (ioctl_version > 1) {
wl_printrate(wl_ratespec2rate((uint32)val));
} else {
wl_printrate(val);
}
} else {
/* create the ioctl value based on the major ioctl version */
if (ioctl_version == 1) {
/* for ver=1 ioctl interfaces, override values for 2g_(m)rate/5g_(m)rate
* are just given as 500 Kbps units
*/
val = rate_string2int(*argv);
} else {
/* for ver>1 ioctl interfaces, override values for 2g_(m)rate/5g_(m)rate
* are a wl_ratespec of a legacy rate.
*/
val = WL_RSPEC_ENCODE_RATE | rate_string2int(*argv);
}
val = htod32(val);
error = wlu_iovar_setint(wl, name, val);
}
return error;
}
static int
wl_nrate(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_nrate";
bool mcs_set = FALSE, legacy_set = FALSE, stf_set = FALSE;
bool mcs_only = FALSE;
int err, opt_err;
uint32 val = 0;
uint32 rate = 0;
uint32 nrate = 0;
uint32 mcs = 0;
uint stf = 0;
/* toss the command name */
argv++;
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_getint(wl, "nrate", (int*)&val)) < 0)
return err;
/* print a user readable line for the nrate rspec */
wl_nrate_print(val);
return 0;
}
/* check for a single argument of "auto" or -1 */
if ((!strcmp(argv[0], "auto") || !strcmp(argv[0], "-1")) &&
argv[1] == NULL) {
/* clear the nrate override */
err = wlu_iovar_setint(wl, "nrate", 0);
goto exit;
}
miniopt_init(&to, fn_name, "w", FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'r') {
if (!to.good_int) {
/* special case check for "-r 5.5" */
if (!strcmp(to.valstr, "5.5")) {
to.val = 11;
} else {
fprintf(stderr,
"%s: could not parse \"%s\" as a rate value\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
} else
to.val = to.val*2;
if (mcs_set) {
fprintf(stderr, "%s: cannot use -r and -m\n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
legacy_set = TRUE;
rate = to.val;
}
if (to.opt == 'm') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for mcs\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (legacy_set) {
fprintf(stderr, "%s: cannot use -r and -m\n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
mcs_set = TRUE;
mcs = to.val;
}
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for stf mode\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
stf = to.val;
stf_set = TRUE;
}
if (to.opt == 'w') {
mcs_only = TRUE;
}
}
if ((mcs_only && !mcs_set) || (mcs_only && (stf_set || legacy_set))) {
fprintf(stderr, "%s: can use -w only with -m\n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
if (!stf_set) {
if (legacy_set)
stf = OLD_NRATE_STF_SISO; /* SISO */
else if (mcs_set) {
if ((nrate & OLD_NRATE_RATE_MASK) <= HIGHEST_SINGLE_STREAM_MCS ||
(nrate & OLD_NRATE_RATE_MASK) == 32)
stf = OLD_NRATE_STF_SISO; /* SISO */
else
stf = OLD_NRATE_STF_SDM; /* SDM */
}
}
if (!legacy_set && !mcs_set) {
fprintf(stderr, "%s: you need to set a legacy or mcs rate\n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
if (ioctl_version == 1) {
if (legacy_set) {
nrate = rate;
} else {
nrate = mcs;
nrate |= OLD_NRATE_MCS_INUSE;
if (mcs_only) {
nrate |= OLD_NRATE_OVERRIDE_MCS_ONLY;
}
}
nrate |= (stf << OLD_NRATE_STF_SHIFT) & OLD_NRATE_STF_MASK;
} else {
uint tx_exp = 0;
/* set the ratespec encoding type and basic rate value */
if (legacy_set) {
nrate = WL_RSPEC_ENCODE_RATE; /* 11abg */
nrate |= rate;
} else {
nrate = WL_RSPEC_ENCODE_HT; /* 11n HT */
nrate |= mcs;
}
/* decode nrate stf value into tx expansion and STBC */
if (stf == OLD_NRATE_STF_CDD) {
tx_exp = 1;
} else if (stf == OLD_NRATE_STF_STBC) {
nrate |= WL_RSPEC_STBC;
}
nrate |= (tx_exp << WL_RSPEC_TXEXP_SHIFT);
}
err = wlu_iovar_setint(wl, "nrate", (int)nrate);
exit:
return err;
}
static int
wl_assoc_info(void *wl, cmd_t *cmd, char **argv)
{
uint i, req_ies_len = 0, resp_ies_len = 0;
wl_assoc_info_t assoc_info;
int ret;
uint8 *pbuf;
UNUSED_PARAMETER(argv);
/* get the generic association information */
strcpy(buf, cmd->name);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_SMLEN)) < 0)
return ret;
memcpy(&assoc_info, buf, sizeof(wl_assoc_info_t));
assoc_info.req_len = htod32(assoc_info.req_len);
assoc_info.resp_len = htod32(assoc_info.resp_len);
assoc_info.flags = htod32(assoc_info.flags);
printf("Assoc req:\n");
printf("\tlen 0x%x\n", assoc_info.req_len);
if (assoc_info.req_len) {
printf("\tcapab 0x%x\n", ltoh16(assoc_info.req.capability));
printf("\tlisten 0x%x\n", ltoh16(assoc_info.req.listen));
req_ies_len = assoc_info.req_len - sizeof(struct dot11_assoc_req);
if (assoc_info.flags & WLC_ASSOC_REQ_IS_REASSOC) {
printf("\treassoc bssid %s\n",
wl_ether_etoa(&assoc_info.reassoc_bssid));
req_ies_len -= ETHER_ADDR_LEN;
}
}
/* get the association req IE's if there are any */
if (req_ies_len) {
strcpy(buf, "assoc_req_ies");
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_SMLEN)) < 0)
return ret;
printf("assoc req IEs:\n\t");
for (i = 1, pbuf = (uint8*)buf; i <= req_ies_len; i++) {
printf("0x%02x ", *pbuf++);
if (!(i%8))
printf("\n\t");
}
}
printf("\nAssoc resp:\n");
printf("\tlen 0x%x\n", assoc_info.resp_len);
if (assoc_info.resp_len) {
printf("\tcapab 0x%x\n", ltoh16(assoc_info.resp.capability));
printf("\tstatus 0x%x\n", ltoh16(assoc_info.resp.status));
printf("\taid 0x%x\n", ltoh16(assoc_info.resp.aid));
resp_ies_len = assoc_info.resp_len - sizeof(struct dot11_assoc_resp);
}
/* get the association resp IE's if there are any */
if (resp_ies_len) {
strcpy(buf, "assoc_resp_ies");
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_SMLEN)) < 0)
return ret;
printf("assoc resp IEs:\n\t");
for (i = 1, pbuf = (uint8*)buf; i <= resp_ies_len; i++) {
printf(" 0x%02x ", *pbuf++);
if (!(i%8))
printf("\n\t");
}
}
printf("\n");
return 0;
}
static void dump_management_fields(uint8 *data, int len)
{
int i, tag_len;
uint8 tag;
char temp[64];
uint8 *p;
while (len > 0) {
/* Get the tag */
tag = *data;
data++; len--;
/* Get the tag length */
tag_len = (int) *data;
data++; len--;
printf("Tag:%d Len:%d - ", tag, tag_len);
switch (tag) {
case DOT11_MNG_SSID_ID:
for (i = 0; i < tag_len; i++) {
temp[i] = data[i];
}
if (i < 64) {
temp[i] = '\0';
}
printf("SSID: '%s'\n", temp);
break;
case DOT11_MNG_FH_PARMS_ID:
printf("FH Parameter Set\n");
break;
case DOT11_MNG_DS_PARMS_ID:
printf("DS Parameter Set\n");
break;
case DOT11_MNG_CF_PARMS_ID:
printf("CF Parameter Set\n");
break;
case DOT11_MNG_RATES_ID:
printf("Supported Rates\n");
break;
case DOT11_MNG_TIM_ID:
printf("Traffic Indication Map (TIM)\n");
break;
case DOT11_MNG_IBSS_PARMS_ID:
printf("IBSS Parameter Set\n");
break;
case DOT11_MNG_COUNTRY_ID:
p = data;
printf("Country '%c%c%c'\n",
data[0], data[1], data[2]);
p += DOT11_MNG_COUNTRY_ID_LEN;
while (((data+tag_len) - p) >= DOT11_MNG_COUNTRY_ID_LEN) {
printf("Start Channel: %d, Channels: %d, "
"Max TX Power: %d dBm\n",
p[0], p[1], p[2]);
p += DOT11_MNG_COUNTRY_ID_LEN;
}
break;
case DOT11_MNG_HOPPING_PARMS_ID:
printf("Hopping Pattern Parameters\n");
break;
case DOT11_MNG_HOPPING_TABLE_ID:
printf("Hopping Pattern Table\n");
break;
case DOT11_MNG_REQUEST_ID:
printf("Request\n");
break;
case DOT11_MNG_QBSS_LOAD_ID:
printf("QBSS Load\n");
break;
case DOT11_MNG_EDCA_PARAM_ID:
printf("EDCA Parameter\n");
break;
case DOT11_MNG_CHALLENGE_ID:
printf("Challenge text\n");
break;
case DOT11_MNG_PWR_CONSTRAINT_ID:
printf("Power Constraint\n");
break;
case DOT11_MNG_PWR_CAP_ID:
printf("Power Capability\n");
break;
case DOT11_MNG_TPC_REQUEST_ID:
printf("Transmit Power Control (TPC) Request\n");
break;
case DOT11_MNG_TPC_REPORT_ID:
printf("Transmit Power Control (TPC) Report\n");
break;
case DOT11_MNG_SUPP_CHANNELS_ID:
printf("Supported Channels\n");
break;
case DOT11_MNG_CHANNEL_SWITCH_ID:
printf("Channel Switch Announcement\n");
break;
case DOT11_MNG_MEASURE_REQUEST_ID:
printf("Measurement Request\n");
break;
case DOT11_MNG_MEASURE_REPORT_ID:
printf("Measurement Report\n");
break;
case DOT11_MNG_QUIET_ID:
printf("Quiet\n");
break;
case DOT11_MNG_IBSS_DFS_ID:
printf("IBSS DFS\n");
break;
case DOT11_MNG_ERP_ID:
printf("ERP Information\n");
break;
case DOT11_MNG_TS_DELAY_ID:
printf("TS Delay\n");
break;
case DOT11_MNG_HT_CAP:
printf("HT Capabilities\n");
break;
case DOT11_MNG_QOS_CAP_ID:
printf("QoS Capability\n");
break;
case DOT11_MNG_NONERP_ID:
printf("NON-ERP\n");
break;
case DOT11_MNG_RSN_ID:
printf("RSN\n");
break;
case DOT11_MNG_EXT_RATES_ID:
printf("Extended Supported Rates\n");
break;
case DOT11_MNG_AP_CHREP_ID:
printf("AP Channel Report\n");
break;
case DOT11_MNG_NEIGHBOR_REP_ID:
printf("Neighbor Report\n");
break;
case DOT11_MNG_MDIE_ID:
printf("Mobility Domain\n");
break;
case DOT11_MNG_FTIE_ID:
printf("Fast BSS Transition\n");
break;
case DOT11_MNG_FT_TI_ID:
printf("802.11R Timeout Interval\n");
break;
case DOT11_MNG_REGCLASS_ID:
printf("Regulatory Class\n");
break;
case DOT11_MNG_EXT_CSA_ID:
printf("Extended CSA\n");
break;
case DOT11_MNG_HT_ADD:
printf("HT Information\n");
break;
case DOT11_MNG_EXT_CHANNEL_OFFSET:
printf("Ext Channel\n");
break;
#ifdef BCMWAPI_WAI
case DOT11_MNG_WAPI_ID:
printf("WAPI\n");
break;
#endif
case DOT11_MNG_RRM_CAP_ID:
printf("Radio Measurement\n");
break;
case DOT11_MNG_HT_BSS_COEXINFO_ID:
printf("OBSS Coexistence INFO\n");
break;
case DOT11_MNG_HT_BSS_CHANNEL_REPORT_ID:
printf("OBSS Intolerant Channel List\n");
break;
case DOT11_MNG_HT_OBSS_ID:
printf("OBSS HT Info\n");
break;
#ifdef DOT11_MNG_CHANNEL_USAGE
case DOT11_MNG_CHANNEL_USAGE:
printf("Channel Usage\n");
break;
#endif
case DOT11_MNG_LINK_IDENTIFIER_ID:
printf("TDLS Link Identifier\n");
break;
case DOT11_MNG_WAKEUP_SCHEDULE_ID:
printf("TDLS Wakeup Schedule\n");
break;
case DOT11_MNG_CHANNEL_SWITCH_TIMING_ID:
printf("TDLS Channel Switch Timing\n");
break;
case DOT11_MNG_PTI_CONTROL_ID:
printf("TDLS PTI Control\n");
break;
case DOT11_MNG_PU_BUFFER_STATUS_ID:
printf("TDLS PU Buffer Status\n");
break;
case DOT11_MNG_EXT_CAP_ID:
printf("Management Ext Capability\n");
break;
case DOT11_MNG_PROPR_ID:
printf("Proprietary\n");
break;
default:
if (tag_len <= len) {
printf("Unsupported tag\n");
} else {
/* Just dump the remaining data */
printf("Unsupported tag error/malformed\n");
tag_len = len;
}
break;
} /* switch */
wl_hexdump(data, tag_len);
data += tag_len;
len -= tag_len;
} /* while */
}
static void dump_management_info(uint8 *data, int len)
{
struct dot11_management_header hdr;
struct dot11_bcn_prb parms;
if (len <= (int) (sizeof(hdr)+sizeof(parms))) {
/* Management packet invalid */
return;
}
memcpy(&hdr, data, sizeof(hdr));
data += sizeof(hdr);
len -= sizeof(hdr);
memcpy(&parms, data, sizeof(parms));
data += sizeof(parms);
len -= sizeof(parms);
/* 802.11 MAC header */
printf("Frame Ctl: 0x%04x\n", ltoh16(hdr.fc));
printf("Duration : 0x%04x\n", ltoh16(hdr.durid));
printf("Dest addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
hdr.da.octet[0],
hdr.da.octet[1],
hdr.da.octet[2],
hdr.da.octet[3],
hdr.da.octet[4],
hdr.da.octet[5]);
printf("Src addr : %02x:%02x:%02x:%02x:%02x:%02x\n",
hdr.sa.octet[0],
hdr.sa.octet[1],
hdr.sa.octet[2],
hdr.sa.octet[3],
hdr.sa.octet[4],
hdr.sa.octet[5]);
printf("BSSID : %02x:%02x:%02x:%02x:%02x:%02x\n",
hdr.bssid.octet[0],
hdr.bssid.octet[1],
hdr.bssid.octet[2],
hdr.bssid.octet[3],
hdr.bssid.octet[4],
hdr.bssid.octet[5]);
printf("Seq ctl : 0x%04x\n", hdr.seq);
/* 802.11 management frame */
printf("Timestamp: 0x%08x%08x\n",
ltoh32(parms.timestamp[0]), ltoh32(parms.timestamp[1]));
printf("Beacon Interval: 0x%04x\n", ltoh16(parms.beacon_interval));
printf("Capabilities: 0x%04x\n", ltoh32(parms.capability));
dump_management_fields(data, len);
}
static int
wl_management_info(void *wl, cmd_t *cmd, char**argv)
{
int ret = 0;
int len;
uint8 *data;
FILE *fp = NULL;
char *fname = NULL;
int raw = 0;
/* Skip the command name */
argv++;
while (*argv) {
char *s = *argv;
if (!strcmp(s, "-f") && argv[1] != NULL) {
/* Write packet to a file */
fname = argv[1];
argv += 2;
} else if (!strcmp(s, "-r")) {
/* Do a hex dump to console */
raw = 1;
argv++;
} else
return BCME_USAGE_ERROR;
}
/* Get the beacon information */
strcpy(buf, cmd->name);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
/*
* Dump out the beacon data. The first word (4 bytes) is the
* length of the management packet followed by the data itself.
*/
len = dtoh32(*(int *)buf);
if (len <= 0) {
/* Nothing to do */
return ret;
}
data = (uint8 *) (buf + sizeof(int));
printf("Data: %p Len: %d bytes\n", data, len);
if (fname != NULL) {
/* Write the packet to a file */
if ((fp = fopen(fname, "wb")) == NULL) {
fprintf(stderr, "Failed to open file %s\n",
fname);
ret = BCME_BADARG;
} else {
ret = fwrite(data, 1, len, fp);
if (ret != len) {
fprintf(stderr,
"Error write %d bytes to file %s, rc %d!\n",
len, fname, ret);
ret = -1;
}
}
} else if (raw) {
/* Hex dump */
wl_hexdump(data, len);
} else {
/* Print management (w/some decode) */
dump_management_info(data, len);
}
if (fp)
fclose(fp);
return ret;
}
static int
wl_pmkid_info(void *wl, cmd_t *cmd, char**argv)
{
int i, j, ret;
pmkid_list_t *pmkid_info;
if (!*++argv) {
strcpy(buf, cmd->name);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_SMLEN)) < 0)
return ret;
pmkid_info = (pmkid_list_t *)buf;
pmkid_info->npmkid = dtoh32(pmkid_info->npmkid);
printf("\npmkid entries : %d\n", pmkid_info->npmkid);
for (i = 0; i < (int)pmkid_info->npmkid; i++) {
printf("\tPMKID[%d]: %s =",
i, wl_ether_etoa(&pmkid_info->pmkid[i].BSSID));
for (j = 0; j < WPA2_PMKID_LEN; j++)
printf("%02x ", pmkid_info->pmkid[i].PMKID[j]);
printf("\n");
}
}
else {
#ifdef test_pmkid_info
char eaddr[6] = {0x0, 0x0, 0x1, 0x2, 0x3, 0x5};
char eaddr1[6] = {0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
char id[WPA2_PMKID_LEN], id1[WPA2_PMKID_LEN];
int i, len = (sizeof(uint32) + 2*(sizeof(pmkid_t)));
/* check that the set uses to "test" cmd */
if (strcmp(*argv, "test")) {
printf("\t wl pmkid_info only supports `test` a test specific set\n");
return BCME_USAGE_ERROR;
}
if ((pmkid_info = (pmkid_list_t *)malloc(len)) == NULL) {
printf("\tfailed to allocate buffer\n");
return BCME_NOMEM;
}
printf("\toverwriting pmkid table with test pattern\n");
for (i = 0; i < (int)sizeof(id); i++) {
id[i] = i;
id1[i] = (i*2);
}
/* "test" - creates two PMKID entries and sets the table to that */
pmkid_info->npmkid = htod32(2);
memcpy(&pmkid_info->pmkid[0].BSSID.octet[0], &eaddr[0], ETHER_ADDR_LEN);
memcpy(&pmkid_info->pmkid[0].PMKID[0], &id[0], WPA2_PMKID_LEN);
memcpy(&pmkid_info->pmkid[1].BSSID.octet[0], &eaddr1[0], ETHER_ADDR_LEN);
memcpy(&pmkid_info->pmkid[1].PMKID[0], &id1[0], WPA2_PMKID_LEN);
ret = wlu_var_setbuf(wl, cmd->name, pmkid_info, len);
free(pmkid_info);
return ret;
#else
printf("\tset cmd ignored\n");
#endif /* test_pmkid_info */
}
return 0;
}
static int
wl_rateset(void *wl, cmd_t *cmd, char **argv)
{
wl_rateset_args_t rs, defrs;
int error;
uint i;
UNUSED_PARAMETER(cmd);
error = 0;
argv++;
if (*argv == NULL) {
/* get current rateset */
if ((error = wlu_iovar_get(wl, "cur_rateset", &rs, sizeof(rs))) < 0)
return (error);
dump_rateset(rs.rates, dtoh32(rs.count));
printf("\n");
wl_print_mcsset((char *)rs.mcs);
wl_print_vhtmcsset((uint16 *)rs.vht_mcs);
} else {
/* get default rateset and mcsset */
if ((error = wlu_iovar_get(wl, "rateset", &defrs,
sizeof(wl_rateset_args_t))) < 0)
return (error);
defrs.count = dtoh32(defrs.count);
if (!stricmp(*argv, "all")) {
for (i = 0; i < defrs.count; i++)
defrs.rates[i] |= 0x80;
defrs.count = htod32(defrs.count);
error = wlu_iovar_set(wl, "rateset", &defrs,
sizeof(wl_rateset_args_t));
}
else if (!stricmp(*argv, "default")) {
defrs.count = htod32(defrs.count);
error = wlu_iovar_set(wl, "rateset", &defrs,
sizeof(wl_rateset_args_t));
}
else { /* arbitrary list */
error = wl_parse_rateset(wl, &defrs, argv);
if (!error) {
/* check for common error of no basic rates */
for (i = 0; i < defrs.count; i++) {
if (defrs.rates[i] & 0x80)
break;
}
if (i < defrs.count) {
defrs.count = htod32(defrs.count);
error = wlu_iovar_set(wl, "rateset", &defrs,
sizeof(wl_rateset_args_t));
} else {
error = BCME_BADARG;
fprintf(stderr,
"Bad Args: at least one rate must be marked Basic\n");
}
}
}
}
return (error);
}
static int
wl_default_rateset(void *wl, cmd_t *cmd, char **argv)
{
int error = 0;
wl_rates_info_t rates_info;
UNUSED_PARAMETER(cmd);
memset((char*)&rates_info.rs_tgt, 0, sizeof(wl_rateset_t));
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse phy type\n");
return BCME_USAGE_ERROR;
}
/* phy_type */
if (!stricmp(*argv, "a"))
rates_info.phy_type = 0;
else if (!stricmp(*argv, "b"))
rates_info.phy_type = 2;
else if (!stricmp(*argv, "g"))
rates_info.phy_type = 2;
else if (!stricmp(*argv, "n"))
rates_info.phy_type = 4;
else if (!stricmp(*argv, "lp"))
rates_info.phy_type = 5;
else if (!stricmp(*argv, "ssn"))
rates_info.phy_type = 6;
else if (!stricmp(*argv, "ht"))
rates_info.phy_type = 7;
else if (!stricmp(*argv, "lcn"))
rates_info.phy_type = 8;
else if (!stricmp(*argv, "lcn40"))
rates_info.phy_type = 10;
else if (!stricmp(*argv, "ac"))
rates_info.phy_type = 11;
else {
fprintf(stderr, "Wrong phy type: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse band type\n");
return BCME_USAGE_ERROR;
}
/* band type */
if (!stricmp(*argv, "5"))
rates_info.bandtype = WLC_BAND_5G;
else if (!stricmp(*argv, "2"))
rates_info.bandtype = WLC_BAND_2G;
else {
fprintf(stderr, "Wrong band type: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse cck\n");
return BCME_USAGE_ERROR;
}
/* cck only */
if (!stricmp(*argv, "0"))
rates_info.cck_only = FALSE;
else if (!stricmp(*argv, "1"))
rates_info.cck_only = TRUE;
else {
fprintf(stderr, "Wrong cck: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse basic rates\n");
return BCME_USAGE_ERROR;
}
/* rate_mask */
if (!stricmp(*argv, "0"))
rates_info.rate_mask = 0x7f;
else if (!stricmp(*argv, "1"))
rates_info.rate_mask = 0xff;
else {
fprintf(stderr, "Wrong basic rates: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse mcs\n");
return BCME_USAGE_ERROR;
}
/* mcs */
if (!stricmp(*argv, "0"))
rates_info.mcsallow = FALSE;
else if (!stricmp(*argv, "1"))
rates_info.mcsallow = TRUE;
else {
fprintf(stderr, "Wrong mcs: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse bandwidth\n");
return BCME_USAGE_ERROR;
}
/* channel bandwidth */
if (!stricmp(*argv, "10"))
rates_info.bw = 10;
else if (!stricmp(*argv, "20"))
rates_info.bw = 20;
else if (!stricmp(*argv, "40"))
rates_info.bw = 40;
else if (!stricmp(*argv, "80"))
rates_info.bw = 80;
else if (!stricmp(*argv, "160"))
rates_info.bw = 160;
else {
fprintf(stderr, "Wrong bandwidth: %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
/* not enough params */
if (*argv == NULL) {
fprintf(stderr, "Can't parse tx/rx streams\n");
return BCME_USAGE_ERROR;
}
/* mcs */
if (!stricmp(*argv, "tx")) {
int txstreams;
if ((error = wlu_iovar_getint(wl, "txstreams", &txstreams)) < 0) {
fprintf(stderr, "Can't get tx streams\n");
return BCME_USAGE_ERROR;
}
rates_info.txstreams = txstreams;
}
else if (!stricmp(*argv, "rx")) {
int rxstreams;
if ((error = wlu_iovar_getint(wl, "rxstreams", &rxstreams)) < 0) {
fprintf(stderr, "Can't get rx streams\n");
return BCME_USAGE_ERROR;
}
rates_info.txstreams = rxstreams;
}
else {
fprintf(stderr, "Wrong tx/rx streams: %s\n", *argv);
return BCME_USAGE_ERROR;
}
/* get default rates */
if ((error = wlu_iovar_getbuf(wl, "default_rateset", NULL, 0, &rates_info,
sizeof(wl_rates_info_t)))) {
fprintf(stderr, "default_rateset failed\n");
return (error);
}
dump_rateset(rates_info.rs_tgt.rates, dtoh32(rates_info.rs_tgt.count));
return (error);
}
static int
wl_sup_rateset(void *wl, cmd_t *cmd, char **argv)
{
wl_rateset_args_t rs;
bool got_basic;
int error;
uint i;
error = 0;
memset((char*)&rs, 0, sizeof(wl_rateset_args_t));
argv++;
if (*argv == NULL) {
/* get rateset */
if ((error = wlu_get(wl, cmd->get, &rs, sizeof(wl_rateset_t))) < 0)
return (error);
dump_rateset(rs.rates, dtoh32(rs.count));
printf("\n");
} else {
if (!stricmp(*argv, "-1") || !stricmp(*argv, "0")) {
/* set an empty rateset */
error = wlu_set(wl, cmd->set, &rs, sizeof(wl_rateset_t));
}
else { /* set the specified rateset */
wl_parse_rateset(wl, &rs, argv);
/* check for common error of including a basic rate */
got_basic = FALSE;
for (i = 0; i < rs.count; i++) {
if (rs.rates[i] & 0x80) {
rs.rates[i] &= 0x7F;
got_basic = TRUE;
}
}
if (got_basic) {
fprintf(stderr,
"Warning: Basic rate attribute ignored for \"%s\" command\n",
cmd->name);
}
rs.count = htod32(rs.count);
error = wlu_set(wl, cmd->set, &rs, sizeof(wl_rateset_t));
}
}
return (error);
}
/*
* Parse the rateset command arguments into the passed wl_rateset_args_t structure.
*
* Returns 0 on success, or an appropriate error code (BCME_USAGE_ERROR, BCME_BADARG).
*
*/
static int
wl_parse_rateset(void *wl, wl_rateset_args_t* rs, char **argv)
{
char* endp = NULL;
char* arg;
int r;
int mcs_index = 0;
uint32 mcs_mask;
int error = 0;
wl_rateset_args_t cur_rs;
bool mcs_args, vht_args;
mcs_args = vht_args = FALSE;
memset(rs, 0, sizeof(*rs));
while ((arg = *argv++) != NULL) {
/* mcs rates */
if (!stricmp(arg, "-m")) {
mcs_args = TRUE;
break;
}
/* vht rates */
if (!stricmp(arg, "-v")) {
vht_args = TRUE;
break;
}
/* Parse legacy rates */
if (rs->count >= WL_MAXRATES_IN_SET) {
fprintf(stderr,
"parsing \"%s\", too many rates specified, max is %d rates\n",
arg, WL_MAXRATES_IN_SET);
error = BCME_USAGE_ERROR;
break;
}
/* convert the rate number to a 500kbps rate by multiplying by 2 */
r = (int)(strtoul(arg, &endp, 0) * 2);
if (endp == arg) {
fprintf(stderr, "unable to convert the rate parameter \"%s\"\n", arg);
error = BCME_USAGE_ERROR;
break;
}
/* parse a .5 specially */
if (!strncmp(endp, ".5", 2)) {
r += 1;
endp += 2;
}
/* strip trailing space */
while (isspace((int)endp[0]))
endp++;
/* check for a basic rate specifier */
if (!stricmp(endp, "b") || !stricmp(endp, "(b)")) {
r |= 0x80;
} else if (endp[0] != '\0') {
fprintf(stderr,
"unable to convert trailing characters"
" \"%s\" in the rate parameter \"%s\"\n",
endp, arg);
error = BCME_USAGE_ERROR;
break;
}
/* no legacy rates specified */
if ((rs->count == 0) && (r == 0)) {
fprintf(stderr, "empty legacy rateset not supported\n");
error = BCME_USAGE_ERROR;
break;
}
rs->rates[rs->count++] = r;
}
if (error)
return error;
if (!mcs_args && !vht_args && !rs->count)
return BCME_USAGE_ERROR; /* Cannot happen, really */
/*
* If one of the rate sets was not specified, keep its current setting.
*/
error = wlu_iovar_get(wl, "cur_rateset", &cur_rs, sizeof(cur_rs));
if (error)
return error;
if (!rs->count) { /* No legacy rates specified -- keep what we have */
rs->count = cur_rs.count;
memcpy(&rs->rates, &cur_rs.rates, rs->count);
}
if (!mcs_args) { /* No MCS rates specified */
memcpy(rs->mcs, cur_rs.mcs, MCSSET_LEN);
}
if (!vht_args) { /* No VHT rates specified, keep current values */
memcpy(rs->vht_mcs, cur_rs.vht_mcs,
VHT_CAP_MCS_MAP_NSS_MAX * sizeof(rs->vht_mcs[0]));
}
/* If no more args, return. */
if (!arg) {
return error;
}
/* Parse mcs or VHT rateset values */
while ((arg = *argv++) != NULL) {
if (mcs_args) {
if (mcs_index >= MCSSET_LEN) {
fprintf(stderr, "parsing \"%s\", too many mcs rates "
"specified, max is %d rates\n", arg, MCSSET_LEN);
error = BCME_USAGE_ERROR;
break;
}
mcs_mask = strtoul(arg, &endp, 16);
if (endp == arg) {
fprintf(stderr, "unable to convert the mcs parameter \"%s\"\n", arg);
error = BCME_BADARG;
break;
}
/* clear the mcs rates */
if (mcs_mask == 0) {
memset(rs->mcs, 0, MCSSET_LEN);
break;
}
/* copy the mcs rates bitmap octets */
rs->mcs[mcs_index++] = mcs_mask;
} else { /* vht_args */
/*
* Specified as rate masks for Nss=0, Nss=1, etc.
*/
if (mcs_index >= VHT_CAP_MCS_MAP_NSS_MAX) {
fprintf(stderr,
"Error: Too many VHT rate masks specified, max %d\n",
VHT_CAP_MCS_MAP_NSS_MAX);
error = BCME_USAGE_ERROR;
break;
}
mcs_mask = strtoul( arg, &endp, 16 ); /* Base 16 for consistency with -m */
if ((*arg == '\0') || (*endp != '\0')) {
fprintf(stderr, "Error converting VHT rate mask value '%s'\n", arg);
error = BCME_USAGE_ERROR;
break;
}
/*
* Can only specify 0, 0xff, 0x1ff, 0x3ff because of the way the rates
* are encoded in the driver (0-3).
*/
if ((mcs_mask != 0x0000) && /* vht disabled */
(mcs_mask != 0x00ff) && /* vht mcs0-7 */
(mcs_mask != 0x01ff) && /* vht mcs0-8 */
(mcs_mask != 0x03ff)) { /* vht mcs0-9 */
fprintf(stderr, "Error: VHT rate mask must be 0 (disabled),"
" 0xff (MCS0-7), 0x1ff (MCS0-8), or 0x3ff (MCS0-9).\n");
error = BCME_BADARG;
break;
}
rs->vht_mcs[mcs_index++] = mcs_mask;
}
}
return error;
}
/*
* Get or Set Pwr Sel Params
* wl powersel_params \
* <tp_ratio_thresh> <rate_stab_thresh> <pwr_stab_thresh> <pwr_sel_exp_time>
*/
static int
wl_power_sel_params(void *wl, cmd_t *cmd, char **argv)
{
int err, argc;
powersel_params_t pwrsel_params;
UNUSED_PARAMETER(cmd);
argv++;
if (*argv == NULL) {
/* get current powersel params */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &pwrsel_params,
(sizeof(powersel_params_t)))) < 0)
return (err);
printf("- Link Power Control parameters -\n");
printf("tp_ratio_thresh\t\t= %d\nrate_stab_thresh\t= %d\n",
pwrsel_params.tp_ratio_thresh, pwrsel_params.rate_stab_thresh);
printf("pwr_stab_thresh\t\t= %d\npwr_sel_exp_time\t= %d\n",
pwrsel_params.pwr_stab_thresh, pwrsel_params.pwr_sel_exp_time);
} else {
char *endptr;
/* Validate num of entries */
for (argc = 0; argv[argc]; argc++);
if (argc != 4)
return BCME_USAGE_ERROR;
argc = 0;
pwrsel_params.tp_ratio_thresh = strtol(argv[argc], &endptr, 0);
argc++;
pwrsel_params.rate_stab_thresh = strtol(argv[argc], &endptr, 0);
argc++;
pwrsel_params.pwr_stab_thresh = strtol(argv[argc], &endptr, 0);
argc++;
pwrsel_params.pwr_sel_exp_time = strtol(argv[argc], &endptr, 0);
/* Set powersel params */
err = wlu_iovar_set(wl, cmd->name, (void *) &pwrsel_params,
(sizeof(powersel_params_t)));
}
return err;
}
/* Set per-band bandwidth */
static int wl_bw_cap(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
struct {
uint32 band;
uint32 bw_cap;
} param = { 0, 0 };
char *s = NULL;
void *ptr = NULL;
/* Skip the command name */
argv++;
if (*argv) {
if (!strcmp(*argv, "a") || !strcmp(*argv, "5") || !strcmp(*argv, "5g")) {
param.band = WLC_BAND_5G;
} else if (!strcmp(*argv, "b") || !strcmp(*argv, "2") || !strcmp(*argv, "2g")) {
param.band = WLC_BAND_2G;
} else {
fprintf(stderr,
"%s: invalid band %s\n",
cmd->name, *argv);
err = BCME_USAGE_ERROR;
goto exit;
}
argv++;
if (*argv) {
/* Optional 2nd arg is used to set the bandwidth cap */
s = NULL;
param.bw_cap = (uint32) strtoul(*argv, &s, 0);
if (s && *s != '\0') {
fprintf(stderr, "%s: invalid bandwidth '%s'\n",
cmd->name, *argv);
err = BCME_USAGE_ERROR;
goto exit;
}
}
} else {
fprintf(stderr, "%s: band unspecified\n", cmd->name);
err = BCME_USAGE_ERROR;
goto exit;
}
if (param.bw_cap == 0) {
if ((err = wlu_var_getbuf(wl, cmd->name, &param, sizeof(param), &ptr)) < 0)
return err;
printf("0x%x\n", *((uint32 *)ptr));
} else {
err = wlu_var_setbuf(wl, cmd->name, &param, sizeof(param));
}
exit:
return err;
}
static int
wl_channel(void *wl, cmd_t *cmd, char **argv)
{
int ret;
channel_info_t ci;
if (!*++argv) {
memset(&ci, 0, sizeof(ci));
if ((ret = wlu_get(wl, cmd->get, &ci, sizeof(channel_info_t))) < 0)
return ret;
ci.hw_channel = dtoh32(ci.hw_channel);
ci.scan_channel = dtoh32(ci.scan_channel);
ci.target_channel = dtoh32(ci.target_channel);
if (ci.scan_channel) {
printf("Scan in progress.\n");
printf("current scan channel\t%d\n", ci.scan_channel);
} else {
printf("No scan in progress.\n");
}
printf("current mac channel\t%d\n", ci.hw_channel);
printf("target channel\t%d\n", ci.target_channel);
return 0;
} else {
ci.target_channel = htod32(atoi(*argv));
ret = wlu_set(wl, cmd->set, &ci.target_channel, sizeof(int));
return ret;
}
}
static int
wl_chanspec(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_chanspec";
bool band_set = FALSE, ch_set = FALSE, bw_set = FALSE, ctl_sb_set = FALSE;
int err, opt_err;
uint32 val = 0;
chanspec_t chanspec = 0;
/* toss the command name */
argv++;
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_getint(wl, cmd->name, (int*)&val)) < 0)
return err;
chanspec = wl_chspec32_from_driver(val);
wf_chspec_ntoa(chanspec, buf);
printf("%s (0x%x)\n", buf, chanspec);
return 0;
}
chanspec = wf_chspec_aton(*argv);
if (chanspec != 0) {
val = wl_chspec32_to_driver(chanspec);
if (val != INVCHANSPEC) {
err = wlu_iovar_setint(wl, cmd->name, val);
} else {
err = BCME_USAGE_ERROR;
}
} else {
miniopt_init(&to, fn_name, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for"
" the channel\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val > 224) {
fprintf(stderr, "%s: invalid channel %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
chanspec |= to.val;
ch_set = TRUE;
}
if (to.opt == 'b') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for band\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 5) && (to.val != 2)) {
fprintf(stderr,
"%s: invalid band %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
if (to.val == 5)
chanspec |= WL_CHANSPEC_BAND_5G;
else
chanspec |= WL_CHANSPEC_BAND_2G;
band_set = TRUE;
}
if (to.opt == 'w') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for"
" bandwidth\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 20) && (to.val != 40) && (to.val != 80)) {
fprintf(stderr,
"%s: invalid bandwidth %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
if (to.val == 20)
chanspec |= WL_CHANSPEC_BW_20;
else if (to.val == 40)
chanspec |= WL_CHANSPEC_BW_40;
else
chanspec |= WL_CHANSPEC_BW_80;
bw_set = TRUE;
}
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for"
" ctl sideband\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 1) && (to.val != 0) && (to.val != -1)) {
fprintf(stderr,
"%s: invalid ctl sideband %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
if (to.val == -1)
chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
else if (to.val == 1)
chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
ctl_sb_set = TRUE;
}
}
/* set ctl sb to 20 if not set and 20mhz is selected */
if (!ctl_sb_set && CHSPEC_IS20(chanspec)) {
ctl_sb_set = TRUE;
}
if (ch_set && band_set && bw_set && ctl_sb_set) {
val = wl_chspec32_to_driver(chanspec);
if (val != INVCHANSPEC) {
err = wlu_iovar_setint(wl, cmd->name, val);
} else {
err = BCME_USAGE_ERROR;
}
} else {
if (!ch_set)
fprintf(stderr, "%s: you need to set a channel,"
" '-c <ch>'\n", fn_name);
if (!band_set)
fprintf(stderr, "%s: you need to set a band,"
" '-b <5|2>'\n", fn_name);
if (!bw_set)
fprintf(stderr, "%s: you need to set a bandwidth,"
" '-w <20|40>'\n", fn_name);
if (!ctl_sb_set)
fprintf(stderr, "%s: you need to set a ctl sideband,"
" '-s <-1|0|1>'\n", fn_name);
err = BCME_USAGE_ERROR;
}
}
if (!err)
printf("Chanspec set to 0x%x\n", chanspec);
exit:
return err;
}
static int
wl_rclass(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_USAGE_ERROR;
chanspec_t chanspec = 0;
void *ptr;
/* toss the command name */
argv++;
if (*argv) {
chanspec = wf_chspec_aton(*argv);
if (chanspec != 0) {
err = wlu_var_getbuf(wl, cmd->name, &chanspec, sizeof(chanspec_t), &ptr);
if (err)
printf("Read rclass fails: chanspec:0x%x\n", chanspec);
else
printf("rclass=0x%x\n", *((int *)ptr));
}
}
return err;
}
static int
wl_chanim_state(void *wl, cmd_t *cmd, char **argv)
{
uint32 chanspec;
int argc = 0;
int ret, val;
argv++;
/* find the arg count */
while (argv[argc])
argc++;
if (argc != 1)
return BCME_USAGE_ERROR;
chanspec = wf_chspec_aton(*argv);
chanspec = wl_chspec32_to_driver(chanspec);
if (chanspec == INVCHANSPEC) {
return BCME_USAGE_ERROR;
}
ret = wlu_iovar_getbuf(wl, cmd->name, &chanspec, sizeof(chanspec),
buf, WLC_IOCTL_SMLEN);
if (ret < 0)
return ret;
val = *(int*)buf;
val = dtoh32(val);
printf("%d\n", val);
return 0;
}
static int
wl_chanim_mode(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char *endptr;
int mode;
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_iovar_getint(wl, cmd->name, &mode)) < 0)
return ret;
switch (mode) {
case CHANIM_DISABLE:
printf("CHANIM mode: disabled.\n");
break;
case CHANIM_DETECT:
printf("CHANIM mode: detect only.\n");
break;
case CHANIM_EXT:
printf("CHANIM mode: external (acsd).\n");
break;
case CHANIM_ACT:
printf("CHANIM mode: detect + act.\n");
break;
}
return 0;
} else {
mode = CHANIM_DETECT;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
switch (val) {
case 0:
mode = CHANIM_DISABLE;
break;
case 1:
mode = CHANIM_DETECT;
break;
case 2:
mode = CHANIM_EXT;
break;
case 3:
mode = CHANIM_ACT;
break;
default:
return BCME_BADARG;
}
mode = htod32(mode);
return wlu_iovar_setint(wl, cmd->name, mode);
}
}
int
wl_ether_atoe(const char *a, struct ether_addr *n)
{
char *c = NULL;
int i = 0;
memset(n, 0, ETHER_ADDR_LEN);
for (;;) {
n->octet[i++] = (uint8)strtoul(a, &c, 16);
if (!*c++ || i == ETHER_ADDR_LEN)
break;
a = c;
}
return (i == ETHER_ADDR_LEN);
}
char *
wl_ether_etoa(const struct ether_addr *n)
{
static char etoa_buf[ETHER_ADDR_LEN * 3];
char *c = etoa_buf;
int i;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
if (i)
*c++ = ':';
c += sprintf(c, "%02X", n->octet[i] & 0xff);
}
return etoa_buf;
}
int
wl_atoip(const char *a, struct ipv4_addr *n)
{
char *c = NULL;
int i = 0;
for (;;) {
n->addr[i++] = (uint8)strtoul(a, &c, 0);
if (*c++ != '.' || i == IPV4_ADDR_LEN)
break;
a = c;
}
return (i == IPV4_ADDR_LEN);
}
int wl_ipv6_colon(const char *a, char *x)
{
int i;
const char *t;
int colons = 0;
int double_colons = 0;
int zero_req = 0;
if (*a == ':' && *(a+1) != ':')
return 1; /* Illegal */
t = a;
while ((t = strstr(t, "::")) != NULL) {
++t;
++double_colons;
}
if (double_colons == 0) {
strcpy(x, a); /* No double colon in the address */
return 0;
}
if (double_colons > 1) {
return 1; /* Illegal */
}
t = a;
while ((t = strchr(t, ':')) != NULL) {
++t;
++colons;
}
zero_req = 8 - colons;
if (zero_req) {
t = a;
while (*t) {
if (*t == ':' && *(t+1) == ':') {
if (t == a) {
*x++ = '0';
}
*x++ = *t++;
for (i = 0; i < zero_req; i++) {
*x++ = '0';
*x++ = ':';
}
t++;
} else {
*x++ = *t++;
}
}
} else {
strcpy(x, a);
}
return 0;
}
int
wl_atoipv6(const char *a, struct ipv6_addr *n)
{
char *c = NULL;
int i = 0;
uint16 *addr16;
char x[64];
char *t = x;
memset(x, 0, 64);
if (wl_ipv6_colon(a, x) == 1) {
return 0;
}
for (;;) {
addr16 = (uint16 *)&n->addr[i];
*addr16 = hton16((uint16)strtoul((char *)t, &c, 16));
i += 2;
if (*c++ != ':' || i == IPV6_ADDR_LEN)
break;
t = c;
}
return (i == IPV6_ADDR_LEN);
}
char *
wl_ipv6toa(const void *ipv6)
{
/* Implementing RFC 5952 Sections 4 + 5 */
/* Not thoroughly tested */
uint16 *a = (uint16 *)ipv6;
/* Returned buffer is from a static circular pool to permit several calls in a printf */
#define IPV6_BUFFER_CNT 4
static char buffer[IPV6_BUFFER_CNT][IPV6_ADDR_LEN * 4];
static int idx = 0;
char *p = buffer[idx++ % IPV6_BUFFER_CNT];
int i, i_max = -1, cnt = 0, cnt_max = 1;
uint8 *a4 = NULL;
for (i = 0; i < IPV6_ADDR_LEN/2; i++) {
if (a[i]) {
if (cnt > cnt_max) {
cnt_max = cnt;
i_max = i - cnt;
}
cnt = 0;
} else
cnt++;
}
if (cnt > cnt_max) {
cnt_max = cnt;
i_max = i - cnt;
}
if (i_max == 0 &&
/* IPv4-translated: ::ffff:0:a.b.c.d */
((cnt_max == 4 && a[4] == 0xffff && a[5] == 0) ||
/* IPv4-mapped: ::ffff:a.b.c.d */
(cnt_max == 5 && a[5] == 0xffff)))
a4 = (uint8*) (a + 6);
for (i = 0; i < IPV6_ADDR_LEN/2; i++) {
if ((uint8*) (a + i) == a4) {
sprintf(p, ":%u.%u.%u.%u", a4[0], a4[1], a4[2], a4[3]);
break;
} else if (i == i_max) {
*p++ = ':';
i += cnt_max - 1;
p[0] = ':';
p[1] = '\0';
} else {
if (i)
*p++ = ':';
p += sprintf(p, "%x", ntoh16(a[i]));
}
}
/* Sub-buffer start is found back by rounding p with the sub-buffer size */
return buffer[(p - buffer[0]) / sizeof(buffer[0])];
}
char *
wl_iptoa(const struct ipv4_addr *n)
{
static char iptoa_buf[IPV4_ADDR_LEN * 4];
sprintf(iptoa_buf, "%u.%u.%u.%u",
n->addr[0], n->addr[1], n->addr[2], n->addr[3]);
return iptoa_buf;
}
int
wl_format_ssid(char* ssid_buf, uint8* ssid, int ssid_len)
{
int i, c;
char *p = ssid_buf;
if (ssid_len > 32)
ssid_len = 32;
for (i = 0; i < ssid_len; i++) {
c = (int)ssid[i];
if (c == '\\') {
*p++ = '\\';
*p++ = '\\';
} else if (isprint((uchar)c)) {
*p++ = (char)c;
} else {
p += sprintf(p, "\\x%02X", c);
}
}
*p = '\0';
return p - ssid_buf;
}
/* pretty hex print a contiguous buffer */
void
wl_hexdump(uchar *dump_buf, uint nbytes)
{
char line[256];
char* p;
uint i;
if (nbytes == 0) {
printf("\n");
return;
}
p = line;
for (i = 0; i < nbytes; i++) {
if (i % 16 == 0 && nbytes > 16) {
p += sprintf(p, "%04d: ", i); /* line prefix */
}
p += sprintf(p, "%02x ", dump_buf[i]);
if (i % 16 == 15) {
printf("%s\n", line); /* flush line */
p = line;
}
}
/* flush last partial line */
if (p != line)
printf("%s\n", line);
}
static int
wl_plcphdr(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
if (!*++argv) {
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(int))) < 0)
return ret;
val = dtoh32(val);
if (val == WLC_PLCP_AUTO)
printf("long");
else if (val == WLC_PLCP_SHORT)
printf("auto");
else if (val == WLC_PLCP_LONG)
printf("debug");
else
printf("unknown");
printf("\n");
return 0;
} else {
if (!stricmp(*argv, "long"))
val = WLC_PLCP_AUTO;
else if (!stricmp(*argv, "auto"))
val = WLC_PLCP_SHORT;
else if (!stricmp(*argv, "debug"))
val = WLC_PLCP_LONG;
else
return BCME_USAGE_ERROR;
val = htod32(val);
return wlu_set(wl, cmd->set, &val, sizeof(int));
}
}
/* WLC_GET_RADIO and WLC_SET_RADIO in driver operate on radio_disabled which
* is opposite of "wl radio [1|0]". So invert for user.
* In addition, display WL_RADIO_SW_DISABLE and WL_RADIO_HW_DISABLE bits.
*/
static int
wl_radio(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint val;
char *endptr = NULL;
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(int))) < 0)
return ret;
val = dtoh32(val);
printf("0x%04x\n", val);
return 0;
} else {
if (cmd->set < 0)
return -1;
if (!stricmp(*argv, "on"))
val = WL_RADIO_SW_DISABLE << 16;
else if (!stricmp(*argv, "off"))
val = WL_RADIO_SW_DISABLE << 16 | WL_RADIO_SW_DISABLE;
else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
/* raw bits setting, add the mask if not provided */
if ((val >> 16) == 0) {
val |= val << 16;
}
}
val = htod32(val);
return wlu_set(wl, cmd->set, &val, sizeof(int));
}
}
static char *
ver2str(unsigned int vms, unsigned int vls)
{
static char verstr[100];
unsigned int maj, year, month, day, build;
maj = (vms >> 16) & 0xFFFF;
if (maj > 1000) {
/* it is probably a date... */
year = (vms >> 16) & 0xFFFF;
month = vms & 0xFFFF;
day = (vls >> 16) & 0xFFFF;
build = vls & 0xFFFF;
sprintf(verstr, "%d/%d/%d build %d",
month, day, year, build);
} else {
/* it is a tagged release. */
sprintf(verstr, "%d.%d RC%d.%d",
(vms>>16)&0xFFFF, vms&0xFFFF,
(vls>>16)&0xFFFF, vls&0xFFFF);
}
return verstr;
}
static int
wl_version(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int bcmerr = 0;
char *p = NULL;
char *dump_buf;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
printf("%s\n",
ver2str(((EPI_MAJOR_VERSION) << 16) | EPI_MINOR_VERSION,
(EPI_RC_NUMBER << 16) | EPI_INCREMENTAL_NUMBER));
dump_buf = malloc(WLC_IOCTL_SMLEN);
if (dump_buf == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n", WLC_IOCTL_SMLEN);
return BCME_NOMEM;
}
memset(dump_buf, 0, WLC_IOCTL_SMLEN);
/* query for 'ver' to get version info */
ret = wlu_iovar_get(wl, "ver", dump_buf, WLC_IOCTL_SMLEN);
/* if the query is successful, continue on and print the result. */
/* if the query fails, check for a legacy driver that does not support
* the "dump" iovar, and instead issue a WLC_DUMP ioctl.
*/
if (ret) {
wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (bcmerr == BCME_UNSUPPORTED) {
ret = wlu_get(wl, WLC_DUMP, dump_buf, WLC_IOCTL_SMLEN);
}
}
if (ret) {
fprintf(stderr, "Error %d on query of driver dump\n", (int)ret);
free(dump_buf);
return ret;
}
/* keep only the first line from the dump buf output */
p = strchr(dump_buf, '\n');
if (p)
*p = '\0';
printf("%s\n", dump_buf);
free(dump_buf);
return 0;
}
static int
wl_rateparam(void *wl, cmd_t *cmd, char **argv)
{
int val[2];
if (!*++argv)
return BCME_USAGE_ERROR;
val[0] = htod32(atoi(*argv));
if (!*++argv)
return BCME_USAGE_ERROR;
val[1] = htod32(atoi(*argv));
return wlu_set(wl, cmd->set, val, 2 * sizeof(val));
}
/* wl scan
* -s --ssid=ssid_list
* -t T --scan_type=T : [active|passive]
* --bss_type=T : [infra|bss|adhoc|ibss]
* -b --bssid=
* -n --nprobes=
* -a --active=
* -p --passive=
* -h --home=
* -c --channels=
* ssid_list
*/
/* Parse a comma-separated list from list_str into ssid array, starting
* at index idx. Max specifies size of the ssid array. Parses ssids
* and returns updated idx; if idx >= max not all fit, the excess have
* not been copied. Returns -1 on empty string, or on ssid too long.
*/
static int
wl_parse_ssid_list(char* list_str, wlc_ssid_t* ssid, int idx, int max)
{
char *str, *ptr;
if (list_str == NULL)
return -1;
for (str = list_str; str != NULL; str = ptr) {
if ((ptr = strchr(str, ',')) != NULL)
*ptr++ = '\0';
if (strlen(str) > DOT11_MAX_SSID_LEN) {
fprintf(stderr, "ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN);
return -1;
}
if (strlen(str) == 0)
ssid[idx].SSID_len = 0;
if (idx < max) {
strcpy((char*)ssid[idx].SSID, str);
ssid[idx].SSID_len = strlen(str);
}
idx++;
}
return idx;
}
static int
wl_scan_prep(void *wl, cmd_t *cmd, char **argv, wl_scan_params_t *params, int *params_size)
{
int val = 0;
char key[64];
int keylen;
char *p, *eq, *valstr, *endptr = NULL;
char opt;
bool positional_param;
bool good_int;
bool opt_end;
int err = 0;
int i;
int nchan = 0;
int nssid = 0;
wlc_ssid_t ssids[WL_SCAN_PARAMS_SSID_MAX];
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(cmd);
memcpy(&params->bssid, &ether_bcast, ETHER_ADDR_LEN);
params->bss_type = DOT11_BSSTYPE_ANY;
params->scan_type = 0;
params->nprobes = -1;
params->active_time = -1;
params->passive_time = -1;
params->home_time = -1;
params->channel_num = 0;
memset(ssids, 0, WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t));
/* skip the command name */
argv++;
opt_end = FALSE;
while ((p = *argv) != NULL) {
argv++;
positional_param = FALSE;
memset(key, 0, sizeof(key));
opt = '\0';
valstr = NULL;
good_int = FALSE;
if (opt_end) {
positional_param = TRUE;
valstr = p;
}
else if (!strcmp(p, "--")) {
opt_end = TRUE;
continue;
}
else if (!strncmp(p, "--", 2)) {
eq = strchr(p, '=');
if (eq == NULL) {
fprintf(stderr,
"wl_scan: missing \" = \" in long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
keylen = eq - (p + 2);
if (keylen > 63)
keylen = 63;
memcpy(key, p + 2, keylen);
valstr = eq + 1;
if (*valstr == '\0') {
fprintf(stderr,
"wl_scan: missing value after \" = \" in long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
}
else if (!strncmp(p, "-", 1)) {
opt = p[1];
if (strlen(p) > 2) {
fprintf(stderr,
"wl_scan: only single char options, error on param \"%s\"\n", p);
err = BCME_BADARG;
goto exit;
}
if (*argv == NULL) {
fprintf(stderr,
"wl_scan: missing value parameter after \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
valstr = *argv;
argv++;
} else {
positional_param = TRUE;
valstr = p;
}
/* parse valstr as int just in case */
if (valstr) {
val = (int)strtol(valstr, &endptr, 0);
if (*endptr == '\0') {
/* not all the value string was parsed by strtol */
good_int = TRUE;
}
}
if (opt == 's' || !strcmp(key, "ssid") || positional_param) {
nssid = wl_parse_ssid_list(valstr, ssids, nssid, WL_SCAN_PARAMS_SSID_MAX);
if (nssid < 0) {
err = BCME_BADARG;
goto exit;
}
}
/* scan_type is a bitmap value and can have multiple options */
if (opt == 't' || !strcmp(key, "scan_type")) {
if (!strcmp(valstr, "active")) {
/* do nothing - scan_type is initialized outside of while loop */
} else if (!strcmp(valstr, "passive")) {
params->scan_type |= WL_SCANFLAGS_PASSIVE;
} else if (!strcmp(valstr, "prohibit")) {
params->scan_type |= WL_SCANFLAGS_PROHIBITED;
} else if (!strcmp(valstr, "offchan")) {
params->scan_type |= WL_SCANFLAGS_OFFCHAN;
} else if (!strcmp(valstr, "hotspot")) {
params->scan_type |= WL_SCANFLAGS_HOTSPOT;
} else {
fprintf(stderr,
"scan_type value should be \"active\", "
"\"passive\", \"prohibit\", \"offchan\" "
"or \"hotspot\", but got \"%s\"\n", valstr);
err = BCME_USAGE_ERROR;
goto exit;
}
}
if (!strcmp(key, "bss_type")) {
if (!strcmp(valstr, "bss") || !strcmp(valstr, "infra")) {
params->bss_type = DOT11_BSSTYPE_INFRASTRUCTURE;
} else if (!strcmp(valstr, "ibss") || !strcmp(valstr, "adhoc")) {
params->bss_type = DOT11_BSSTYPE_INDEPENDENT;
} else if (!strcmp(valstr, "any")) {
params->bss_type = DOT11_BSSTYPE_ANY;
} else {
fprintf(stderr,
"bss_type value should be "
"\"bss\", \"ibss\", or \"any\", but got \"%s\"\n", valstr);
err = BCME_USAGE_ERROR;
goto exit;
}
}
if (opt == 'b' || !strcmp(key, "bssid")) {
if (!wl_ether_atoe(valstr, &params->bssid)) {
fprintf(stderr,
"could not parse \"%s\" as an ethernet MAC address\n", valstr);
err = BCME_USAGE_ERROR;
goto exit;
}
}
if (opt == 'n' || !strcmp(key, "nprobes")) {
if (!good_int) {
fprintf(stderr,
"could not parse \"%s\" as an int for value nprobes\n", valstr);
err = BCME_BADARG;
goto exit;
}
params->nprobes = val;
}
if (opt == 'a' || !strcmp(key, "active")) {
if (!good_int) {
fprintf(stderr,
"could not parse \"%s\" as an int for active dwell time\n",
valstr);
err = BCME_BADARG;
goto exit;
}
params->active_time = val;
}
if (opt == 'p' || !strcmp(key, "passive")) {
if (!good_int) {
fprintf(stderr,
"could not parse \"%s\" as an int for passive dwell time\n",
valstr);
err = BCME_BADARG;
goto exit;
}
params->passive_time = val;
}
if (opt == 'h' || !strcmp(key, "home")) {
if (!good_int) {
fprintf(stderr,
"could not parse \"%s\" as an int for home channel dwell time\n",
valstr);
err = BCME_BADARG;
goto exit;
}
params->home_time = val;
}
if (opt == 'c' || !strcmp(key, "channels")) {
nchan = wl_parse_channel_list(valstr, params->channel_list,
WL_NUMCHANNELS);
if (nchan == -1) {
fprintf(stderr, "error parsing channel list arg\n");
err = BCME_BADARG;
goto exit;
}
}
}
if (nssid > WL_SCAN_PARAMS_SSID_MAX) {
fprintf(stderr, "ssid count %d exceeds max of %d\n",
nssid, WL_SCAN_PARAMS_SSID_MAX);
err = BCME_BADARG;
goto exit;
}
params->nprobes = htod32(params->nprobes);
params->active_time = htod32(params->active_time);
params->passive_time = htod32(params->passive_time);
params->home_time = htod32(params->home_time);
for (i = 0; i < nchan; i++) {
params->channel_list[i] = htodchanspec(params->channel_list[i]);
}
for (i = 0; i < nssid; i++) {
ssids[i].SSID_len = htod32(ssids[i].SSID_len);
}
/* For a single ssid, use the single fixed field */
if (nssid == 1) {
nssid = 0;
memcpy(&params->ssid, &ssids[0], sizeof(ssids[0]));
}
/* Copy ssid array if applicable */
if (nssid > 0) {
i = OFFSETOF(wl_scan_params_t, channel_list) + nchan * sizeof(uint16);
i = ROUNDUP(i, sizeof(uint32));
if (i + nssid * sizeof(wlc_ssid_t) > (uint)*params_size) {
fprintf(stderr, "additional ssids exceed params_size\n");
err = BCME_BADARG;
goto exit;
}
p = (char*)params + i;
memcpy(p, ssids, nssid * sizeof(wlc_ssid_t));
p += nssid * sizeof(wlc_ssid_t);
} else {
p = (char*)params->channel_list + nchan * sizeof(uint16);
}
params->channel_num = htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) |
(nchan & WL_SCAN_PARAMS_COUNT_MASK));
*params_size = p - (char*)params + nssid * sizeof(wlc_ssid_t);
exit:
return err;
}
static int
wl_roamparms(void *wl, cmd_t *cmd, char **argv)
{
int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
wl_scan_params_t *params;
wl_join_scan_params_t jparms;
int err = 0;
params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_scan_params_t*)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
memset(&jparms, 0, sizeof(jparms));
if (!(argv[1])) {
err = wlu_iovar_getbuf(wl, "roamscan_parms", &jparms, sizeof(jparms),
buf, WLC_IOCTL_SMLEN);
memset(&jparms, 0, sizeof(jparms));
memcpy(&jparms, buf, sizeof(jparms));
printf("Roam Scan Parameters:\n");
printf("scan_type: %d\n", jparms.scan_type);
printf("nprobes: %d\n", jparms.nprobes);
printf("active_time: %d\n", jparms.active_time);
printf("passive_time: %d\n", jparms.passive_time);
printf("home_time: %d\n", jparms.home_time);
goto done;
}
printf("Setting Roam Scan parameters \n");
err = wl_scan_prep(wl, cmd, argv, params, &params_size);
if (err)
goto done;
/* after all of that arg processing we're going to toss some of them
* and only send down the ones relevant to a wl_join__scan_params_t
*/
jparms.scan_type = params->scan_type;
jparms.nprobes = params->nprobes;
jparms.active_time = params->active_time;
jparms.passive_time = params->passive_time;
jparms.home_time = params->home_time;
err = wlu_iovar_setbuf(wl, "roamscan_parms", &jparms, sizeof(jparms), buf, WLC_IOCTL_SMLEN);
done:
free(params);
return err;
}
static int
wl_scan(void *wl, cmd_t *cmd, char **argv)
{
int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
wl_scan_params_t *params;
int err = 0;
params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_scan_params_t*)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
err = wl_scan_prep(wl, cmd, argv, params, &params_size);
if (!err) {
err = wlu_set(wl, cmd->set, params, params_size);
}
free(params);
return err;
}
extern time_t time(time_t *ptr);
static int
wl_escan(void *wl, cmd_t *cmd, char **argv)
{
int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)) +
(WL_NUMCHANNELS * sizeof(uint16));
wl_escan_params_t *params;
int err = 0;
uint16 action = WL_SCAN_ACTION_START;
if (!stricmp(*argv, "escan"))
/* start an escan */
action = WL_SCAN_ACTION_START;
else if (!stricmp(*argv, "escanabort"))
/* abort an escan */
action = WL_SCAN_ACTION_ABORT;
else {
printf("unknown escan command: %s\n", *argv);
return BCME_USAGE_ERROR;
}
params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_escan_params_t*)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
err = wl_scan_prep(wl, cmd, argv, &params->params, &params_size);
if (!err) {
params->version = htod32(ESCAN_REQ_VERSION);
params->action = htod16(action);
srand((unsigned)time(NULL));
params->sync_id = htod16(rand() & 0xffff);
params_size += OFFSETOF(wl_escan_params_t, params);
err = wlu_iovar_setbuf(wl, "escan", params, params_size, buf, WLC_IOCTL_MAXLEN);
}
free(params);
return err;
}
static int
wl_iscan(void *wl, cmd_t *cmd, char **argv)
{
int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params)) +
(WL_NUMCHANNELS * sizeof(uint16));
wl_iscan_params_t *params;
int err = 0;
uint16 action = WL_SCAN_ACTION_START;
char **p;
uint16 iscan_duration = 0;
if (!stricmp(*argv, "iscan_s"))
action = WL_SCAN_ACTION_START;
else if (!stricmp(*argv, "iscan_c"))
action = WL_SCAN_ACTION_CONTINUE;
else {
printf("unknown iscan command: %s\n", *argv);
return BCME_USAGE_ERROR;
}
/* look for iscan_duration parameter */
p = argv;
while (*p != NULL) {
if (!strcmp(*p, "-d") || !strncmp(*p, "--duration=", 11)) {
char *valptr;
int val;
char *endptr;
if (!strcmp(*p, "-d"))
valptr = *(++p);
else
valptr = *p + 11;
val = (int)strtol(valptr, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr,
"could not parse \"%s\" as an int for duration\n",
valptr);
err = -1;
goto exit;
}
iscan_duration = (uint16) val;
break;
}
++p;
}
params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_iscan_params_t*)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
err = wl_scan_prep(wl, cmd, argv, &params->params, &params_size);
if (!err) {
params->version = htod32(ISCAN_REQ_VERSION);
params->action = htod16(action);
params->scan_duration = htod16(iscan_duration);
params_size += OFFSETOF(wl_iscan_params_t, params);
err = wlu_iovar_setbuf(wl, "iscan", params, params_size, buf, WLC_IOCTL_MAXLEN);
}
free(params);
exit:
return err;
}
static int
wl_parse_assoc_params(char **argv, wl_assoc_params_t *params, bool *prescanned)
{
int err = BCME_OK;
char *p, *eq, *valstr;
char opt;
bool opt_end;
int keylen;
char key[64];
int i;
bool bssid_set = FALSE;
bool ch_set = FALSE;
opt_end = FALSE;
while ((p = *argv) != NULL) {
argv++;
memset(key, 0, sizeof(key));
opt = '\0';
valstr = NULL;
if (opt_end) {
valstr = p;
}
else if (!strcmp(p, "--")) {
opt_end = TRUE;
continue;
}
else if (!strcmp(p, "prescanned")) {
if (prescanned)
*prescanned = TRUE;
continue;
}
else if (!strncmp(p, "--", 2)) {
eq = strchr(p, '=');
if (eq == NULL) {
fprintf(stderr, "wl_parse_assoc_params: missing \" = \" in "
"long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
keylen = eq - (p + 2);
if (keylen > 63)
keylen = 63;
memcpy(key, p + 2, keylen);
valstr = eq + 1;
if (*valstr == '\0') {
fprintf(stderr, "wl_parse_assoc_params: missing value after "
"\" = \" in long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
}
else if (!strncmp(p, "-", 1)) {
opt = p[1];
if (strlen(p) > 2) {
fprintf(stderr, "wl_parse_assoc_params: only single char options, "
"error on param \"%s\"\n", p);
err = BCME_BADARG;
goto exit;
}
if (*argv == NULL) {
fprintf(stderr, "wl_parse_assoc_params: missing value parameter "
"after \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
valstr = *argv++;
} else {
valstr = p;
}
/* handle -o v or --option=val */
if (opt == 'b' || !stricmp(key, "bssid")) {
if (!wl_ether_atoe(valstr, &params->bssid)) {
fprintf(stderr, "could not parse as an ethernet MAC address\n");
err = BCME_USAGE_ERROR;
goto exit;
}
bssid_set = TRUE;
}
else if (opt == 'c' || !strcmp(key, "chanspecs")) {
params->chanspec_num =
wl_parse_chanspec_list(valstr, params->chanspec_list, WL_NUMCHANNELS);
if (params->chanspec_num == -1) {
fprintf(stderr, "error parsing chanspec list arg\n");
err = BCME_BADARG;
goto exit;
}
ch_set = TRUE;
}
}
if (prescanned && *prescanned && (ch_set || bssid_set)) {
fprintf(stderr, "cannot use bssid/channel options with prescan option\n");
err = BCME_BADARG;
goto exit;
}
/* prepare the chanspec using the channel number and the user provided options */
for (i = 0; i < params->chanspec_num; i++) {
chanspec_t chanspec = wl_chspec_to_driver(params->chanspec_list[i]);
if (chanspec == INVCHANSPEC) {
err = BCME_USAGE_ERROR;
goto exit;
}
params->chanspec_list[i] = chanspec;
}
params->chanspec_num = htod32(params->chanspec_num);
exit:
return err;
}
/* wl reassoc <bssid>
* Options:
* -c CL, --chanspecs=CL, where CL is a comma or space separated list of chanspecs
*/
static int
wl_reassoc(void *wl, cmd_t *cmd, char **argv)
{
int params_size = WL_REASSOC_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(chanspec_t);
wl_reassoc_params_t *params;
int err = 0;
UNUSED_PARAMETER(cmd);
if (*++argv == NULL) {
fprintf(stderr, "no arguments to wl_reassoc\n");
return BCME_USAGE_ERROR;
}
params = (wl_reassoc_params_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
if (!wl_ether_atoe(*argv, &params->bssid)) {
fprintf(stderr, "could not parse %s as an Ethernet MAC address\n", *argv);
err = BCME_USAGE_ERROR;
goto exit;
}
/* default to plain old ioctl */
params_size = ETHER_ADDR_LEN;
if (*++argv != NULL) {
if ((err = wl_parse_reassoc_params(argv, params)) != BCME_OK) {
fprintf(stderr, "could not parse reassociation parameters\n");
goto exit;
}
params_size = WL_REASSOC_PARAMS_FIXED_SIZE +
dtoh32(params->chanspec_num) * sizeof(chanspec_t);
}
err = wlu_set(wl, WLC_REASSOC, params, params_size);
exit:
free(params);
return err;
}
#ifdef EXTENDED_SCAN
/* wl extdscan
* -s --ssid=ssid1 ssid2 ssid3
* -b --split_scan=0 : [split_scan]
* -t --scan_type=0 : [background/forcedbackground/foreground]
* -n --nprobes=
* -c --channels=
*/
static int
wl_extdscan(void *wl, cmd_t *cmd, char **argv)
{
wl_extdscan_params_t *params;
int params_size = WL_EXTDSCAN_PARAMS_FIXED_SIZE +
(WL_NUMCHANNELS * sizeof(chan_scandata_t));
int val = 0;
char *p, *eq, *valstr, *endptr;
char opt;
bool positional_param;
bool good_int;
bool opt_end;
int err = 0;
int keylen;
char key[64];
int i;
int nssid = 0;
fprintf(stderr, "params alloc size is %d\n", params_size);
params = (wl_extdscan_params_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
params->scan_type = EXTDSCAN_FORCEDBACKGROUND_SCAN;
params->nprobes = 3;
params->band = WLC_BAND_2G;
params->split_scan = 0;
/* skip the command name */
argv++;
if (*argv == NULL) {
fprintf(stderr, "no arguments to wl_extdscan\n");
err = BCME_USAGE_ERROR;
goto exit;
}
opt_end = FALSE;
while ((p = *argv) != NULL) {
argv++;
positional_param = FALSE;
memset(key, 0, sizeof(key));
opt = '\0';
valstr = NULL;
good_int = FALSE;
if (opt_end) {
positional_param = TRUE;
valstr = p;
}
else if (!strcmp(p, "--")) {
opt_end = TRUE;
continue;
}
else if (!strncmp(p, "--", 2)) {
eq = strchr(p, '=');
if (eq == NULL) {
fprintf(stderr,
"wl_extdscan: missing \" = \" in long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
keylen = eq - (p + 2);
if (keylen > 63)
keylen = 63;
memcpy(key, p + 2, keylen);
valstr = eq + 1;
if (*valstr == '\0') {
fprintf(stderr,
"extdscan: missing value after \" = \" in long param \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
}
else if (!strncmp(p, "-", 1)) {
opt = p[1];
if (strlen(p) > 2) {
fprintf(stderr,
"extdscan: only single char options, error on param \"%s\"\n", p);
err = BCME_BADARG;
goto exit;
}
if (*argv == NULL) {
fprintf(stderr,
"extdscan: missing value parameter after \"%s\"\n", p);
err = BCME_USAGE_ERROR;
goto exit;
}
valstr = *argv;
argv++;
} else {
positional_param = TRUE;
valstr = p;
}
/* parse valstr as int just in case */
if (valstr) {
val = (int)strtol(valstr, &endptr, 0);
if (*endptr == '\0') {
/* not all the value string was parsed by strtol */
good_int = TRUE;
}
}
if (opt == 's' || !strcmp(key, "ssid") || positional_param) {
nssid = wl_parse_ssid_list(valstr, params->ssid,
nssid, WLC_EXTDSCAN_MAX_SSID);
if (nssid < 0) {
err = BCME_BADARG;
goto exit;
}
}
if (opt == 'b' || !strcmp(key, "band")) {
if (!strcmp(valstr, "5G")) {
params->band = WLC_BAND_5G;
}
else if (!strcmp(valstr, "2.4G")) {
params->band = WLC_BAND_2G;
}
else if (!strcmp(valstr, "all")) {
params->band = WLC_BAND_ALL;
} else {
fprintf(stderr,
"scan_type value should be \"5G\" "
"or \"2.4G\" " "or \"all\" but got \"%s\"\n", valstr);
err = BCME_USAGE_ERROR;
goto exit;
}
}
if (opt == 't' || !strcmp(key, "scan_type")) {
if (!strcmp(valstr, "background")) {
params->scan_type = EXTDSCAN_BACKGROUND_SCAN;
} else if (!strcmp(valstr, "fbackground")) {
params->scan_type = EXTDSCAN_FORCEDBACKGROUND_SCAN;
} else if (!strcmp(valstr, "foreground")) {
params->scan_type = EXTDSCAN_FOREGROUND_SCAN;
} else {
fprintf(stderr,
"scan_type value should be \"background\" "
"or \"fbackground\" " "or \"foreground\" but got \"%s\"\n", valstr);
err = BCME_USAGE_ERROR;
goto exit;
}
}
if (opt == 'n' || !strcmp(key, "nprobes")) {
if (!good_int) {
fprintf(stderr,
"could not parse \"%s\" as an int for value nprobes\n", valstr);
err = BCME_BADARG;
goto exit;
}
params->nprobes = val;
}
if (opt == 'x' || !strcmp(key, "split_scan")) {
if (val != 0)
params->split_scan = 1;
}
if (opt == 'c' || !strcmp(key, "channels")) {
params->channel_num = wl_parse_extdchannel_list(valstr,
params->channel_list, WL_NUMCHANNELS);
if (params->channel_num == -1) {
fprintf(stderr, "error parsing channel list arg\n");
err = BCME_BADARG;
goto exit;
}
}
}
if (nssid > WLC_EXTDSCAN_MAX_SSID) {
fprintf(stderr, "ssid count %d exceeds max of %d\n",
nssid, WLC_EXTDSCAN_MAX_SSID);
err = BCME_BADARG;
goto exit;
}
params_size = WL_EXTDSCAN_PARAMS_FIXED_SIZE +
(params->channel_num * sizeof(chan_scandata_t));
fprintf(stderr, "ssid list is %s(%d) %s(%d) %s(%d) %s(%d) %s(%d)\n",
(char *)&params->ssid[0].SSID, params->ssid[0].SSID_len,
(char *)&params->ssid[1].SSID, params->ssid[1].SSID_len,
(char *)&params->ssid[2].SSID, params->ssid[2].SSID_len,
(char *)&params->ssid[3].SSID, params->ssid[3].SSID_len,
(char *)&params->ssid[4].SSID, params->ssid[4].SSID_len);
if (params->split_scan)
fprintf(stderr, "split scan is enabled\n");
else
fprintf(stderr, "split scan is not enabled\n");
fprintf(stderr, "scan type is %d, nprobes are %d, band is %d, channels are %d\n",
params->scan_type, params->nprobes, params->band, params->channel_num);
fprintf(stderr, "params size is %d\n", params_size);
params->scan_type = htodenum(params->scan_type);
for (i = 0; i < WLC_EXTDSCAN_MAX_SSID; i++) {
params->ssid[i].SSID_len = htod32(params->ssid[i].SSID_len);
}
for (i = 0; i < params->channel_num; i++) {
chanspec_t chanspec = params->channel_list[i].channel;
chanspec = wl_chspec_to_driver(chanspec);
if (chanspec == INVCHANSPEC) {
err = BCME_USAGE_ERROR;
goto exit;
}
params->channel_list[i].channel = chanspec;
params->channel_list[i].channel_mintime =
htod32(params->channel_list[i].channel_mintime);
params->channel_list[i].channel_maxtime =
htod32(params->channel_list[i].channel_maxtime);
}
params->channel_num = htod32(params->channel_num);
err = wlu_var_setbuf(wl, cmd->name, params, params_size);
exit:
free(params);
return err;
}
static int
wl_parse_extdchannel_list(char* list_str, chan_scandata_t* channel_list, int channel_num)
{
int num;
int val;
char* str;
char* endptr;
if (list_str == NULL)
return -1;
str = list_str;
num = 0;
while (*str != '\0') {
val = (int)strtol(str, &endptr, 0);
if (endptr == str) {
fprintf(stderr,
"could not parse channel number starting at"
" substring \"%s\" in list:\n%s\n",
str, list_str);
return -1;
}
str = endptr + strspn(endptr, " ,");
if (num == channel_num) {
fprintf(stderr, "too many channels (more than %d) in channel list:\n%s\n",
channel_num, list_str);
return -1;
}
channel_list->channel = (uint16)val;
channel_list++;
num++;
}
return num;
}
#endif /* EXTENDED_SCAN */
static int
wl_parse_channel_list(char* list_str, uint16* channel_list, int channel_num)
{
int num;
int val;
char* str;
char* endptr = NULL;
if (list_str == NULL)
return -1;
str = list_str;
num = 0;
while (*str != '\0') {
val = (int)strtol(str, &endptr, 0);
if (endptr == str) {
fprintf(stderr,
"could not parse channel number starting at"
" substring \"%s\" in list:\n%s\n",
str, list_str);
return -1;
}
str = endptr + strspn(endptr, " ,");
if (num == channel_num) {
fprintf(stderr, "too many channels (more than %d) in channel list:\n%s\n",
channel_num, list_str);
return -1;
}
channel_list[num++] = (uint16)val;
}
return num;
}
static int
wl_parse_chanspec_list(char *list_str, chanspec_t *chanspec_list, int chanspec_num)
{
int num = 0;
chanspec_t chanspec;
char *next, str[8];
size_t len;
if ((next = list_str) == NULL)
return BCME_ERROR;
while ((len = strcspn(next, " ,")) > 0) {
if (len >= sizeof(str)) {
fprintf(stderr, "string \"%s\" before ',' or ' ' is too long\n", next);
return BCME_ERROR;
}
strncpy(str, next, len);
str[len] = 0;
chanspec = wf_chspec_aton(str);
if (chanspec == 0) {
fprintf(stderr, "could not parse chanspec starting at "
"\"%s\" in list:\n%s\n", str, list_str);
return BCME_ERROR;
}
if (num == chanspec_num) {
fprintf(stderr, "too many chanspecs (more than %d) in chanspec list:\n%s\n",
chanspec_num, list_str);
return BCME_ERROR;
}
chanspec_list[num++] = chanspec;
next += len;
next += strspn(next, " ,");
}
return num;
}
/* channel info structure */
typedef struct {
uint chan; /* channel number */
uint freq; /* in Mhz */
} chan_info_t;
static chan_info_t chan_info[] = {
/* B channels */
{ 1, 2412},
{ 2, 2417},
{ 3, 2422},
{ 4, 2427},
{ 5, 2432},
{ 6, 2437},
{ 7, 2442},
{ 8, 2447},
{ 9, 2452},
{ 10, 2457},
{ 11, 2462},
{ 12, 2467},
{ 13, 2472},
{ 14, 2484},
/* A channels */
/* 11a usa low */
{ 36, 5180},
{ 40, 5200},
{ 44, 5220},
{ 48, 5240},
{ 52, 5260},
{ 56, 5280},
{ 60, 5300},
{ 64, 5320},
/* 11a Europe */
{ 100, 5500},
{ 104, 5520},
{ 108, 5540},
{ 112, 5560},
{ 116, 5580},
{ 120, 5600},
{ 124, 5620},
{ 128, 5640},
{ 132, 5660},
{ 136, 5680},
{ 140, 5700},
/* 11a usa high */
{ 149, 5745},
{ 153, 5765},
{ 157, 5785},
{ 161, 5805},
/* 11a japan */
{ 184, 4920},
{ 188, 4940},
{ 192, 4960},
{ 196, 4980},
{ 200, 5000},
{ 204, 5020},
{ 208, 5040},
{ 212, 5060},
{ 216, 5080}
};
uint
freq2channel(uint freq)
{
int i;
for (i = 0; i < (int)ARRAYSIZE(chan_info); i++) {
if (chan_info[i].freq == freq)
return (chan_info[i].chan);
}
return (0);
}
void
dump_rateset(uint8 *rates, uint count)
{
uint i;
uint r;
bool b;
printf("[ ");
for (i = 0; i < count; i++) {
r = rates[i] & 0x7f;
b = rates[i] & 0x80;
if (r == 0)
break;
printf("%d%s%s ", (r / 2), (r % 2)?".5":"", b?"(b)":"");
}
printf("]");
}
/* Helper routine to print the infrastructure mode while pretty printing the BSS list */
static const char *
capmode2str(uint16 capability)
{
capability &= (DOT11_CAP_ESS | DOT11_CAP_IBSS);
if (capability == DOT11_CAP_ESS)
return "Managed";
else if (capability == DOT11_CAP_IBSS)
return "Ad Hoc";
else
return "<unknown>";
}
/*
* Traverse a string of 1-byte tag/1-byte length/variable-length value
* triples, returning a pointer to the substring whose first element
* matches tag
*/
static uint8 *
wlu_parse_tlvs(uint8 *tlv_buf, int buflen, uint key)
{
uint8 *cp;
int totlen;
cp = tlv_buf;
totlen = buflen;
/* find tagged parameter */
while (totlen >= 2) {
uint tag;
int len;
tag = *cp;
len = *(cp +1);
/* validate remaining totlen */
if ((tag == key) && (totlen >= (len + 2)))
return (cp);
cp += (len + 2);
totlen -= (len + 2);
}
return NULL;
}
static int
wlu_bcmp(const void *b1, const void *b2, int len)
{
return (memcmp(b1, b2, len));
}
/* Is this body of this tlvs entry a WPA entry? If */
/* not update the tlvs buffer pointer/length */
static bool
wlu_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, uint *tlvs_len)
{
uint8 *ie = *wpaie;
/* If the contents match the WPA_OUI and type=1 */
if ((ie[1] >= 6) && !wlu_bcmp(&ie[2], WPA_OUI "\x01", 4)) {
return TRUE;
}
/* point to the next ie */
ie += ie[1] + 2;
/* calculate the length of the rest of the buffer */
*tlvs_len -= (int)(ie - *tlvs);
/* update the pointer to the start of the buffer */
*tlvs = ie;
return FALSE;
}
static void
wl_dump_wpa_rsn_ies(uint8* cp, uint len)
{
uint8 *parse = cp;
uint parse_len = len;
uint8 *wpaie;
uint8 *rsnie;
while ((wpaie = wlu_parse_tlvs(parse, parse_len, DOT11_MNG_WPA_ID)))
if (wlu_is_wpa_ie(&wpaie, &parse, &parse_len))
break;
if (wpaie)
wl_rsn_ie_dump((bcm_tlv_t*)wpaie);
rsnie = wlu_parse_tlvs(cp, len, DOT11_MNG_RSN_ID);
if (rsnie)
wl_rsn_ie_dump((bcm_tlv_t*)rsnie);
return;
}
static void
wl_rsn_ie_dump(bcm_tlv_t *ie)
{
int i;
int rsn;
wpa_ie_fixed_t *wpa = NULL;
rsn_parse_info_t rsn_info;
wpa_suite_t *suite;
uint8 std_oui[3];
int unicast_count = 0;
int akm_count = 0;
uint16 capabilities;
uint cntrs;
int err;
if (ie->id == DOT11_MNG_RSN_ID) {
rsn = TRUE;
memcpy(std_oui, WPA2_OUI, WPA_OUI_LEN);
err = wl_rsn_ie_parse_info(ie->data, ie->len, &rsn_info);
} else {
rsn = FALSE;
memcpy(std_oui, WPA_OUI, WPA_OUI_LEN);
wpa = (wpa_ie_fixed_t*)ie;
err = wl_rsn_ie_parse_info((uint8*)&wpa->version, wpa->length - WPA_IE_OUITYPE_LEN,
&rsn_info);
}
if (err || rsn_info.version != WPA_VERSION)
return;
if (rsn)
printf("RSN:\n");
else
printf("WPA:\n");
/* Check for multicast suite */
if (rsn_info.mcast) {
printf("\tmulticast cipher: ");
if (!wlu_bcmp(rsn_info.mcast->oui, std_oui, 3)) {
switch (rsn_info.mcast->type) {
case WPA_CIPHER_NONE:
printf("NONE\n");
break;
case WPA_CIPHER_WEP_40:
printf("WEP64\n");
break;
case WPA_CIPHER_WEP_104:
printf("WEP128\n");
break;
case WPA_CIPHER_TKIP:
printf("TKIP\n");
break;
case WPA_CIPHER_AES_OCB:
printf("AES-OCB\n");
break;
case WPA_CIPHER_AES_CCM:
printf("AES-CCMP\n");
break;
default:
printf("Unknown-%s(#%d)\n", rsn ? "RSN" : "WPA",
rsn_info.mcast->type);
break;
}
}
#ifdef BCMCCX
else if (!wlu_bcmp(rsn_info.mcast->oui, CISCO_AIRONET_OUI, 3)) {
switch (rsn_info.mcast->type + CISCO_BASE) {
case WPA_CIPHER_CKIP:
printf("CKIP\n");
break;
case WPA_CIPHER_CKIP_MMH:
printf("CKIP+CMIC\n");
break;
case WPA_CIPHER_WEP_MMH:
printf("CMIC\n");
break;
default:
break;
}
}
#endif /* BCMCCX */
else {
printf("Unknown-%02X:%02X:%02X(#%d) ",
rsn_info.mcast->oui[0], rsn_info.mcast->oui[1],
rsn_info.mcast->oui[2], rsn_info.mcast->type);
}
}
/* Check for unicast suite(s) */
if (rsn_info.ucast) {
unicast_count = ltoh16_ua(&rsn_info.ucast->count);
printf("\tunicast ciphers(%d): ", unicast_count);
for (i = 0; i < unicast_count; i++) {
suite = &rsn_info.ucast->list[i];
if (!wlu_bcmp(suite->oui, std_oui, 3)) {
switch (suite->type) {
case WPA_CIPHER_NONE:
printf("NONE ");
break;
case WPA_CIPHER_WEP_40:
printf("WEP64 ");
break;
case WPA_CIPHER_WEP_104:
printf("WEP128 ");
break;
case WPA_CIPHER_TKIP:
printf("TKIP ");
break;
case WPA_CIPHER_AES_OCB:
printf("AES-OCB ");
break;
case WPA_CIPHER_AES_CCM:
printf("AES-CCMP ");
break;
default:
printf("WPA-Unknown-%s(#%d) ", rsn ? "RSN" : "WPA",
suite->type);
break;
}
}
#ifdef BCMCCX
else if (!wlu_bcmp(suite->oui, CISCO_AIRONET_OUI, 3)) {
switch (suite->type + CISCO_BASE) {
case WPA_CIPHER_CKIP:
printf("CKIP ");
break;
case WPA_CIPHER_CKIP_MMH:
printf("CKIP+CMIC ");
break;
case WPA_CIPHER_WEP_MMH:
printf("CMIC ");
break;
default:
printf("Cisco-Unknown(#%d) ", suite->type);
break;
}
}
#endif /* BCMCCX */
else {
printf("Unknown-%02X:%02X:%02X(#%d) ",
suite->oui[0], suite->oui[1], suite->oui[2],
suite->type);
}
}
printf("\n");
}
/* Authentication Key Management */
if (rsn_info.akm) {
akm_count = ltoh16_ua(&rsn_info.akm->count);
printf("\tAKM Suites(%d): ", akm_count);
for (i = 0; i < akm_count; i++) {
suite = &rsn_info.akm->list[i];
if (!wlu_bcmp(suite->oui, std_oui, 3)) {
switch (suite->type) {
case RSN_AKM_NONE:
printf("None ");
break;
case RSN_AKM_UNSPECIFIED:
printf("WPA ");
break;
case RSN_AKM_PSK:
printf("WPA-PSK ");
break;
case RSN_AKM_FBT_1X:
printf("FT-802.1x ");
break;
case RSN_AKM_FBT_PSK:
printf("FT-PSK ");
break;
default:
printf("Unknown-%s(#%d) ",
rsn ? "RSN" : "WPA", suite->type);
break;
}
}
#ifdef BCMCCX
else if (!wlu_bcmp(suite->oui, CISCO_AIRONET_OUI, 3)) {
switch (suite->type + CISCO_BASE) {
case WPA_AUTH_CCKM:
printf("CCKM ");
break;
default:
printf("Cisco-Unknown(#%d) ", suite->type);
break;
}
}
#endif /* BCMCCX */
else {
printf("Unknown-%02X:%02X:%02X(#%d) ",
suite->oui[0], suite->oui[1], suite->oui[2],
suite->type);
}
}
printf("\n");
}
/* Capabilities */
if (rsn_info.capabilities) {
capabilities = ltoh16_ua(rsn_info.capabilities);
printf("\tCapabilities(0x%04x): ", capabilities);
if (rsn)
printf("%sPre-Auth, ", (capabilities & RSN_CAP_PREAUTH) ? "" : "No ");
printf("%sPairwise, ", (capabilities & RSN_CAP_NOPAIRWISE) ? "No " : "");
cntrs = wl_rsn_ie_decode_cntrs((capabilities & RSN_CAP_PTK_REPLAY_CNTR_MASK) >>
RSN_CAP_PTK_REPLAY_CNTR_SHIFT);
printf("%d PTK Replay Ctr%s", cntrs, (cntrs > 1)?"s":"");
if (rsn) {
cntrs = wl_rsn_ie_decode_cntrs(
(capabilities & RSN_CAP_GTK_REPLAY_CNTR_MASK) >>
RSN_CAP_GTK_REPLAY_CNTR_SHIFT);
printf("%d GTK Replay Ctr%s\n", cntrs, (cntrs > 1)?"s":"");
} else {
printf("\n");
}
} else {
printf("\tNo %s Capabilities advertised\n", rsn ? "RSN" : "WPA");
}
}
/* Validates and parses the RSN or WPA IE contents into a rsn_parse_info_t structure
* Returns 0 on success, or 1 if the information in the buffer is not consistant with
* an RSN IE or WPA IE.
* The buf pointer passed in should be pointing at the version field in either an RSN IE
* or WPA IE.
*/
static int
wl_rsn_ie_parse_info(uint8* rsn_buf, uint len, rsn_parse_info_t *rsn)
{
uint16 count;
memset(rsn, 0, sizeof(rsn_parse_info_t));
/* version */
if (len < sizeof(uint16))
return 1;
rsn->version = ltoh16_ua(rsn_buf);
len -= sizeof(uint16);
rsn_buf += sizeof(uint16);
/* Multicast Suite */
if (len < sizeof(wpa_suite_mcast_t))
return 0;
rsn->mcast = (wpa_suite_mcast_t*)rsn_buf;
len -= sizeof(wpa_suite_mcast_t);
rsn_buf += sizeof(wpa_suite_mcast_t);
/* Unicast Suite */
if (len < sizeof(uint16))
return 0;
count = ltoh16_ua(rsn_buf);
if (len < (sizeof(uint16) + count * sizeof(wpa_suite_t)))
return 1;
rsn->ucast = (wpa_suite_ucast_t*)rsn_buf;
len -= (sizeof(uint16) + count * sizeof(wpa_suite_t));
rsn_buf += (sizeof(uint16) + count * sizeof(wpa_suite_t));
/* AKM Suite */
if (len < sizeof(uint16))
return 0;
count = ltoh16_ua(rsn_buf);
if (len < (sizeof(uint16) + count * sizeof(wpa_suite_t)))
return 1;
rsn->akm = (wpa_suite_auth_key_mgmt_t*)rsn_buf;
len -= (sizeof(uint16) + count * sizeof(wpa_suite_t));
rsn_buf += (sizeof(uint16) + count * sizeof(wpa_suite_t));
/* Capabilites */
if (len < sizeof(uint16))
return 0;
rsn->capabilities = rsn_buf;
return 0;
}
static uint
wl_rsn_ie_decode_cntrs(uint cntr_field)
{
uint cntrs;
switch (cntr_field) {
case RSN_CAP_1_REPLAY_CNTR:
cntrs = 1;
break;
case RSN_CAP_2_REPLAY_CNTRS:
cntrs = 2;
break;
case RSN_CAP_4_REPLAY_CNTRS:
cntrs = 4;
break;
case RSN_CAP_16_REPLAY_CNTRS:
cntrs = 16;
break;
default:
cntrs = 0;
break;
}
return cntrs;
}
void
wl_dump_raw_ie(bcm_tlv_t *ie, uint len)
{
uint dump_len;
if (len == 0) {
return;
} else if (len == 1) {
printf("IE header truncated: ID: 0x%02X\n", ie->id);
return;
} else if (len < (uint)(ie->len + TLV_HDR_LEN)) {
printf("IE data truncated: ID: 0x%02X Len: %d\n", ie->id, ie->len);
dump_len = len - TLV_HDR_LEN;
} else {
printf("ID: 0x%02X Len: %d\n", ie->id, ie->len);
dump_len = ie->len;
}
/* choose how to format the data based on data len */
if (dump_len > 16)
printf("Data:\n");
else if (dump_len > 0)
printf("Data: ");
if (dump_len > 0)
wl_hexdump(ie->data, dump_len);
if (dump_len < ie->len)
printf("<missing %d bytes>\n", ie->len - dump_len);
return;
}
/* Pretty print the BSS list */
static void
dump_networks(char *network_buf)
{
wl_scan_results_t *list = (wl_scan_results_t*)network_buf;
wl_bss_info_t *bi;
uint i;
if (list->count == 0)
return;
else if (list->version != WL_BSS_INFO_VERSION &&
list->version != LEGACY2_WL_BSS_INFO_VERSION &&
list->version != LEGACY_WL_BSS_INFO_VERSION) {
fprintf(stderr, "Sorry, your driver has bss_info_version %d "
"but this program supports only version %d.\n",
list->version, WL_BSS_INFO_VERSION);
return;
}
bi = list->bss_info;
for (i = 0; i < list->count; i++, bi = (wl_bss_info_t*)((int8*)bi + dtoh32(bi->length))) {
dump_bss_info(bi);
}
}
static void
bcm_wps_version(uint8 *wps_ie)
{
uint16 wps_len;
uint16 wps_off, wps_suboff;
uint16 wps_key;
uint8 wps_field_len;
wps_len = (uint16)*(wps_ie+TLV_LEN_OFF); /* Get the length of the WPS OUI header */
wps_off = WPS_OUI_FIXED_HEADER_OFF; /* Skip the fixed headers */
wps_field_len = 1;
/* Parsing the OUI header looking for version number */
while ((wps_len >= wps_off + 2) && (wps_field_len))
{
wps_key = (((uint8)wps_ie[wps_off]*256) + (uint8)wps_ie[wps_off+1]);
if (wps_key == WPS_ID_VENDOR_EXT) {
/* Key found */
wps_suboff = wps_off + WPS_OUI_HEADER_SIZE;
/* Looking for the Vendor extension code 0x00 0x37 0x2A
* and the Version 2 sudId 0x00
* if found then the next byte is the len of field which is always 1
* for version field the byte after is the version number
*/
if (!wlu_bcmp(&wps_ie[wps_suboff], WFA_VENDOR_EXT_ID, WPS_OUI_LEN)&&
(wps_ie[wps_suboff+WPS_WFA_SUBID_V2_OFF] == WPS_WFA_SUBID_VERSION2))
{
printf("V%d.%d ", (wps_ie[wps_suboff+WPS_WFA_V2_OFF]>>4),
(wps_ie[wps_suboff+WPS_WFA_V2_OFF] & 0x0f));
return;
}
}
/* Jump to next field */
wps_field_len = wps_ie[wps_off+WPS_OUI_HEADER_LEN+1];
wps_off += WPS_OUI_HEADER_SIZE + wps_field_len;
}
/* If nothing found from the parser then this is the WPS version 1.0 */
printf("V1.0 ");
}
static void
bcm_is_wps_configured(uint8 *wps_ie)
{
/* Before calling this function the test of WPS_OUI type 4 should be already done
* If the contents match the WPS_OUI_SC_STATE
*/
uint16 wps_key;
wps_key = (wps_ie[WPS_SCSTATE_OFF]*256) + wps_ie[WPS_SCSTATE_OFF+1];
if ((wps_ie[TLV_LEN_OFF] > (WPS_SCSTATE_OFF+5))&&
(wps_key == WPS_ID_SC_STATE))
{
switch (wps_ie[WPS_SCSTATE_OFF+WPS_OUI_HEADER_SIZE])
{
case WPS_SCSTATE_UNCONFIGURED:
printf("Unconfigured\n");
break;
case WPS_SCSTATE_CONFIGURED:
printf("Configured\n");
break;
default:
printf("Unknown State\n");
}
}
}
/* Looking for WPS OUI in the propriatary_ie */
static bool
bcm_is_wps_ie(uint8 *ie, uint8 **tlvs, uint32 *tlvs_len)
{
bool retval = FALSE;
/* If the contents match the WPS_OUI and type=4 */
if ((ie[TLV_LEN_OFF] > (WPS_OUI_LEN+1)) &&
!wlu_bcmp(&ie[TLV_BODY_OFF], WPS_OUI "\x04", WPS_OUI_LEN + 1)) {
retval = TRUE;
}
/* point to the next ie */
ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
/* calculate the length of the rest of the buffer */
*tlvs_len -= (int)(ie - *tlvs);
/* update the pointer to the start of the buffer */
*tlvs = ie;
return retval;
}
static void
wl_dump_wps(uint8* cp, uint len)
{
uint8 *parse = cp;
uint32 parse_len = len;
uint8 *proprietary_ie;
while ((proprietary_ie = wlu_parse_tlvs(parse, parse_len, DOT11_MNG_WPA_ID))) {
if (bcm_is_wps_ie(proprietary_ie, &parse, &parse_len)) {
/* Print WPS status */
printf("WPS: ");
/* Print the version get from vendor extension field */
bcm_wps_version(proprietary_ie);
/* Print the WPS configure or Unconfigure option */
bcm_is_wps_configured(proprietary_ie);
break;
}
}
}
void
dump_bss_info(wl_bss_info_t *bi)
{
char ssidbuf[SSID_FMT_BUF_LEN];
char chspec_str[CHANSPEC_STR_LEN];
wl_bss_info_107_t *old_bi;
int mcs_idx = 0;
uint16 capability;
/* Convert version 107 to 109 */
if (dtoh32(bi->version) == LEGACY_WL_BSS_INFO_VERSION) {
old_bi = (wl_bss_info_107_t *)bi;
bi->chanspec = CH20MHZ_CHSPEC(old_bi->channel);
bi->ie_length = old_bi->ie_length;
bi->ie_offset = sizeof(wl_bss_info_107_t);
} else {
/* do endian swap and format conversion for chanspec if we have
* not created it from legacy bi above
*/
bi->chanspec = wl_chspec_from_driver(bi->chanspec);
}
wl_format_ssid(ssidbuf, bi->SSID, bi->SSID_len);
printf("SSID: \"%s\"\n", ssidbuf);
printf("Mode: %s\t", capmode2str(dtoh16(bi->capability)));
printf("RSSI: %d dBm\t", (int16)(dtoh16(bi->RSSI)));
/*
* SNR has valid value in only 109 version.
* So print SNR for 109 version only.
*/
if (dtoh32(bi->version) == WL_BSS_INFO_VERSION) {
printf("SNR: %d dB\t", (int16)(dtoh16(bi->SNR)));
}
printf("noise: %d dBm\t", bi->phy_noise);
if (bi->flags) {
uint16 flags = dtoh16(bi->flags);
printf("Flags: ");
if (flags & WL_BSS_FLAGS_FROM_BEACON)
printf("FromBcn ");
if (flags & WL_BSS_FLAGS_FROM_CACHE)
printf("Cached ");
if (flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)
printf("RSSI on-channel ");
printf("\t");
}
printf("Channel: %s\n", wf_chspec_ntoa(bi->chanspec, chspec_str));
printf("BSSID: %s\t", wl_ether_etoa(&bi->BSSID));
printf("Capability: ");
capability = dtoh16(bi->capability);
if (capability & DOT11_CAP_ESS)
printf("ESS ");
if (capability & DOT11_CAP_IBSS)
printf("IBSS ");
if (capability & DOT11_CAP_POLLABLE)
printf("Pollable ");
if (capability & DOT11_CAP_POLL_RQ)
printf("PollReq ");
if (capability & DOT11_CAP_PRIVACY)
printf("WEP ");
if (capability & DOT11_CAP_SHORT)
printf("ShortPre ");
if (capability & DOT11_CAP_PBCC)
printf("PBCC ");
if (capability & DOT11_CAP_AGILITY)
printf("Agility ");
if (capability & DOT11_CAP_SHORTSLOT)
printf("ShortSlot ");
if (capability & DOT11_CAP_RRM)
printf("RRM ");
if (capability & DOT11_CAP_CCK_OFDM)
printf("CCK-OFDM ");
printf("\n");
printf("Supported Rates: ");
dump_rateset(bi->rateset.rates, dtoh32(bi->rateset.count));
printf("\n");
if (dtoh32(bi->ie_length))
wl_dump_wpa_rsn_ies((uint8 *)(((uint8 *)bi) + dtoh16(bi->ie_offset)),
dtoh32(bi->ie_length));
if (dtoh32(bi->version) != LEGACY_WL_BSS_INFO_VERSION && bi->n_cap) {
if (bi->vht_cap)
printf("VHT Capable:\n");
else
printf("HT Capable:\n");
printf("\tChanspec: %sGHz channel %d %dMHz (0x%x)\n",
CHSPEC_IS2G(bi->chanspec)?"2.4":"5", CHSPEC_CHANNEL(bi->chanspec),
(CHSPEC_IS80(bi->chanspec) ?
80 : (CHSPEC_IS40(bi->chanspec) ?
40 : (CHSPEC_IS20(bi->chanspec) ? 20 : 10))),
bi->chanspec);
printf("\tPrimary channel: %d\n", bi->ctl_ch);
printf("\tHT Capabilities: ");
if (dtoh32(bi->nbss_cap) & HT_CAP_40MHZ)
printf("40Mhz ");
if (dtoh32(bi->nbss_cap) & HT_CAP_SHORT_GI_20)
printf("SGI20 ");
if (dtoh32(bi->nbss_cap) & HT_CAP_SHORT_GI_40)
printf("SGI40 ");
printf("\n\tSupported MCS : [ ");
for (mcs_idx = 0; mcs_idx < (MCSSET_LEN * 8); mcs_idx++)
if (isset(bi->basic_mcs, mcs_idx))
printf("%d ", mcs_idx);
printf("]\n");
if (bi->vht_cap) {
int i;
uint mcs;
printf("\tVHT Capabilities: \n");
printf("\tSupported VHT (tx) Rates:\n");
for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) {
mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i, dtoh16(bi->vht_txmcsmap));
if (mcs != VHT_CAP_MCS_MAP_NONE)
printf("\t\tNSS: %d MCS: %s\n", i,
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-9" :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8" : "0-7")));
}
printf("\tSupported VHT (rx) Rates:\n");
for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) {
mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i, dtoh16(bi->vht_rxmcsmap));
if (mcs != VHT_CAP_MCS_MAP_NONE)
printf("\t\tNSS: %d MCS: %s\n", i,
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-9" :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8" : "0-7")));
}
}
bi->chanspec = wl_chspec_to_driver(bi->chanspec);
}
if (dtoh32(bi->ie_length))
{
wl_dump_wps((uint8 *)(((uint8 *)bi) + dtoh16(bi->ie_offset)),
dtoh32(bi->ie_length));
}
if (dtoh16(bi->flags) & WL_BSS_FLAGS_HS20) {
printf("Hotspot 2.0 capable\n");
}
printf("\n");
}
static int
_wl_dump_lq(void *wl)
{
int ret = BCME_OK, noise = 0;
wl_lq_t *plq = NULL;
void *ptr = NULL;
memset(buf, 0, sizeof(wl_lq_t));
/* Display stats when disabled */
if ((ret = wlu_get(wl, WLC_GET_PHY_NOISE, &noise, sizeof(int))) < 0) {
printf("wlc_get noise failed with retcode:%d\n", ret);
return ret;
}
if ((ret = wlu_var_getbuf_sm (wl, "monitor_lq_status", NULL, 0, &ptr)) < 0) {
printf("wlc_get lq_status failed with retcode:%d\n", ret);
return ret;
}
plq = (wl_lq_t *)ptr;
if (!plq->isvalid) {
printf("Stats collection currently disabled"
"['wl monitor_lq 1' to enable statistics collection]\n");
return ret;
}
noise = dtoh32(noise);
plq->rssi[LQ_IDX_MIN] = dtoh32(plq->rssi[LQ_IDX_MIN]);
plq->rssi[LQ_IDX_MAX] = dtoh32(plq->rssi[LQ_IDX_MAX]);
plq->rssi[LQ_IDX_AVG] = dtoh32(plq->rssi[LQ_IDX_AVG]);
printf("rss: %d, %d, %d\nsnr: %d, %d, %d\n",
plq->rssi[LQ_IDX_MIN],
plq->rssi[LQ_IDX_AVG],
plq->rssi[LQ_IDX_MAX],
plq->rssi[LQ_IDX_MIN]-noise,
plq->rssi[LQ_IDX_AVG]-noise,
plq->rssi[LQ_IDX_MAX]-noise);
return ret;
} /* _wl_dump_lq */
static int
wl_dump_lq(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_OK;
UNUSED_PARAMETER(cmd);
if (!*++argv)
ret = _wl_dump_lq(wl);
return ret;
} /* wl_dump_lq */
static int
wl_monitor_lq(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_OK;
char *endptr = NULL;
char **startptr = argv;
if (!*++startptr) { /* Get */
ret = wl_varint(wl, cmd, argv);
}
else {
int val = *startptr[0];
val = strtol(*startptr, &endptr, 0);
if (*endptr != '\0') {
return BCME_USAGE_ERROR;
}
val = htod32(val);
if (val == LQ_STOP_MONITOR) {
if ((ret = _wl_dump_lq(wl)))
return ret;
}
ret = wl_varint(wl, cmd, argv); /* Standard set call after getting stats */
}
return ret;
} /* wl_monitor_lq */
static int
wl_bcnlenhist(void *wl, cmd_t *cmd, char **argv)
{
wlc_bcn_len_hist_t *bcnlenhist = NULL;
uint32 *bcns_len = NULL;
char* dump_buf = NULL;
uint32 counter = 0;
int index = 0;
int err = 0;
UNUSED_PARAMETER(cmd);
dump_buf = malloc(WLC_IOCTL_SMLEN);
if (dump_buf == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n", WLC_IOCTL_SMLEN);
return -1;
}
memset(dump_buf, 0, WLC_IOCTL_SMLEN);
if (argv[1])
err = wlu_iovar_getbuf(wl, "bcnlenhist", argv[1], 1, dump_buf, WLC_IOCTL_SMLEN);
else
err = wlu_iovar_getbuf(wl, "bcnlenhist", NULL, 0, dump_buf, WLC_IOCTL_SMLEN);
if (BCME_OK == err) {
bcnlenhist = (wlc_bcn_len_hist_t *)dump_buf;
index = bcnlenhist->cur_index;
counter = bcnlenhist->ringbuff_len;
bcns_len = bcnlenhist->bcnlen_ring;
index--;
printf("LAST %d BEACON LENGTH's: ", counter);
for (; counter--; index--) {
if (index < 0)
index = bcnlenhist->ringbuff_len - 1;
printf("%d ", bcns_len[index]);
}
printf("\nMAX BCNLEN: %d\n", bcnlenhist->max_bcnlen);
if (bcnlenhist->min_bcnlen == (int)0x7fffffff)
printf("MIN BCNLEN: 0\n\n");
else
printf("MIN BCNLEN: %d\n\n", bcnlenhist->min_bcnlen);
}
free(dump_buf);
return err;
}
static int
wl_dump_networks(void *wl, cmd_t *cmd, char **argv)
{
int ret;
char *dump_buf, *dump_buf_orig;
uint32 status = 0;
bool iscan = FALSE;
dump_buf_orig = dump_buf = malloc(WL_DUMP_BUF_LEN);
if (dump_buf == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n", WL_DUMP_BUF_LEN);
return BCME_NOMEM;
}
iscan = (cmd->get != WLC_SCAN_RESULTS);
if (iscan) {
int buflen = 1920; /* usually fits about 10 BSS infos */
if (*(++argv)) {
char *endptr = NULL;
buflen = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
ret = BCME_USAGE_ERROR;
goto exit;
}
}
ret = wl_get_iscan(wl, dump_buf, buflen);
} else
ret = wl_get_scan(wl, WLC_SCAN_RESULTS, dump_buf, WL_DUMP_BUF_LEN);
if (ret == 0) {
if (iscan) {
status = dtoh32(((wl_iscan_results_t *)dump_buf)->status);
dump_buf += OFFSETOF(wl_iscan_results_t, results);
}
dump_networks(dump_buf);
if (iscan) {
switch (status) {
case WL_SCAN_RESULTS_PARTIAL:
printf("iscanresults incomplete\n");
break;
case WL_SCAN_RESULTS_SUCCESS:
printf("iscanresults complete\n");
break;
case WL_SCAN_RESULTS_PENDING:
printf("iscanresults pending\n");
break;
case WL_SCAN_RESULTS_ABORTED:
printf("iscanresults aborted\n");
break;
default:
printf("iscanresults returned unknown status %d\n", status);
break;
}
}
}
exit:
free(dump_buf_orig);
return ret;
}
static int
wl_dump_chanlist(void *wl, cmd_t *cmd, char **argv)
{
uint32 chan_buf[WL_NUMCHANNELS + 1];
wl_uint32_list_t *list;
int ret;
uint i;
UNUSED_PARAMETER(argv);
list = (wl_uint32_list_t *)(void *)chan_buf;
list->count = htod32(WL_NUMCHANNELS);
ret = wlu_get(wl, cmd->get, chan_buf, sizeof(chan_buf));
if (ret < 0)
return ret;
for (i = 0; i < dtoh32(list->count); i++)
printf("%d ", dtoh32(list->element[i]));
printf("\n");
return ret;
}
static int
wl_cur_mcsset(void *wl, cmd_t *cmd, char **argv)
{
int ret;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(buf, 0, WLC_IOCTL_SMLEN);
ret = wlu_iovar_get(wl, "cur_mcsset", &buf[0], MCSSET_LEN);
if (ret < 0)
return ret;
wl_print_mcsset(buf);
return ret;
}
/* Dump chanspecs based on the driver's current configuration of band, band-width & locale. */
static int
wl_dump_chanspecs_defset(void *wl, cmd_t *cmd, char **argv)
{
const char* fn_name = "wl_dump_chanspecs_defset";
wl_uint32_list_t *list;
int ret, buflen;
chanspec_t c = 0;
uint i;
int err;
char chspec_str[CHANSPEC_STR_LEN];
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "chanspecs_defset");
buflen = strlen(buf) + 1;
/* toss the command name */
argv++;
/* Validate arguments if any */
if (*argv) {
fprintf(stderr,
"%s: This IOVAR doesn't take any arguments.\n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
/* Add list */
list = (wl_uint32_list_t *)(buf + buflen);
list->count = htod32(WL_NUMCHANSPECS);
buflen += sizeof(uint32)*(WL_NUMCHANSPECS + 1);
ret = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen);
if (ret < 0)
return ret;
list = (wl_uint32_list_t *)buf;
for (i = 0; i < dtoh32(list->count); i++) {
c = (chanspec_t)dtoh32(list->element[i]);
wf_chspec_ntoa(c, chspec_str);
printf("%s (0x%04x)\n", chspec_str, c);
}
printf("\n");
return ret;
exit:
return err;
}
static int
wl_dump_chanspecs(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_dump_chanspecs";
wl_uint32_list_t *list;
chanspec_t c = 0, *chanspec;
int ret, buflen;
uint i;
int err, opt_err;
bool band_set = FALSE, bw_set = FALSE;
char abbrev[WLC_CNTRY_BUF_SZ] = ""; /* default.. current locale */
char chspec_str[CHANSPEC_STR_LEN];
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(buf, 0, WLC_IOCTL_MAXLEN);
/* multiple commands are using this API to dump a channel list:
* chanspecs
* roam_channels_in_cache
* roam_channels_in_hotlist
*/
strcpy(buf, cmd->name);
buflen = strlen(buf) + 1;
/* toss the command name */
argv++;
/* Validate arguments if any */
if (*argv) {
miniopt_init(&to, fn_name, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'b') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for band\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 5) && (to.val != 2)) {
fprintf(stderr,
"%s: invalid band %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
if (to.val == 5)
c |= WL_CHANSPEC_BAND_5G;
else
c |= WL_CHANSPEC_BAND_2G;
band_set = TRUE;
}
if (to.opt == 'w') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for"
" bandwidth\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 20) && (to.val != 40) && (to.val != 80)) {
fprintf(stderr,
"%s: invalid bandwidth %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
if (to.val == 20)
c |= WL_CHANSPEC_BW_20;
else if (to.val == 40)
c |= WL_CHANSPEC_BW_40;
else {
if (ioctl_version == 1) {
fprintf(stderr,
"%s: bandwidth 80 MHz is not supported by "
"this version driver.\n",
fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
c |= WL_CHANSPEC_BW_80;
}
bw_set = TRUE;
}
if (to.opt == 'c') {
if (!to.valstr) {
fprintf(stderr,
"%s: please provide country abbrev \n", fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
strncpy(abbrev, to.valstr, WLC_CNTRY_BUF_SZ - 1);
abbrev[WLC_CNTRY_BUF_SZ - 1] = '\0';
}
}
if (!bw_set || !band_set) {
if (!band_set)
fprintf(stderr, "%s: you need to set a band, '-b <5|2>'\n",
fn_name);
if (!bw_set)
fprintf(stderr,
"%s: you need to set a bandwidth, '-w <20|40|80>'\n",
fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
}
/* convert chanspec to legacy if needed */
if (c != 0) {
c = wl_chspec_to_driver(c);
if (c == INVCHANSPEC) {
err = BCME_USAGE_ERROR;
goto exit;
}
}
/* Add chanspec argument */
chanspec = (chanspec_t *) (buf + buflen);
*chanspec = c;
buflen += (sizeof(chanspec_t));
/* Add country abbrev */
strncpy(buf + buflen, abbrev, WLC_CNTRY_BUF_SZ);
buflen += WLC_CNTRY_BUF_SZ;
/* Add list */
list = (wl_uint32_list_t *)(buf + buflen);
list->count = htod32(WL_NUMCHANSPECS);
buflen += sizeof(uint32)*(WL_NUMCHANSPECS + 1);
ret = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen);
if (ret < 0)
return ret;
list = (wl_uint32_list_t *)buf;
for (i = 0; i < dtoh32(list->count); i++) {
c = wl_chspec32_from_driver(list->element[i]);
wf_chspec_ntoa(c, chspec_str);
printf("%s (0x%04x)\n", chspec_str, c);
}
printf("\n");
return ret;
exit:
return err;
}
static int
wl_channels_in_country(void *wl, cmd_t *cmd, char **argv)
{
wl_channels_in_country_t *cic;
int ret;
uint i, len;
cic = (wl_channels_in_country_t *)buf;
cic->buflen = WLC_IOCTL_MAXLEN;
cic->count = 0;
/* country abbrev must follow */
if (!*++argv) {
fprintf(stderr, "missing country abbrev\n");
return BCME_USAGE_ERROR;
}
len = strlen(*argv);
if ((len > 3) || (len < 2)) {
fprintf(stderr, "invalid country abbrev: %s\n", *argv);
return BCME_BADARG;
}
strcpy(cic->country_abbrev, *argv);
/* band must follow */
if (!*++argv) {
fprintf(stderr, "missing band\n");
return BCME_USAGE_ERROR;
}
if (!stricmp(*argv, "a"))
cic->band = WLC_BAND_5G;
else if (!stricmp(*argv, "b"))
cic->band = WLC_BAND_2G;
else {
fprintf(stderr, "unsupported band: %s\n", *argv);
return BCME_UNSUPPORTED;
}
cic->buflen = htod32(cic->buflen);
cic->band = htod32(cic->band);
cic->count = htod32(cic->count);
ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
for (i = 0; i < dtoh32(cic->count); i++)
printf("%d ", dtoh32(cic->channel[i]));
printf("\n");
return ret;
}
int
wl_get_scan(void *wl, int opc, char *scan_buf, uint buf_len)
{
wl_scan_results_t *list = (wl_scan_results_t*)scan_buf;
int ret;
list->buflen = htod32(buf_len);
ret = wlu_get(wl, opc, scan_buf, buf_len);
if (ret < 0)
return ret;
ret = 0;
list->buflen = dtoh32(list->buflen);
list->version = dtoh32(list->version);
list->count = dtoh32(list->count);
if (list->buflen == 0) {
list->version = 0;
list->count = 0;
} else if (list->version != WL_BSS_INFO_VERSION &&
list->version != LEGACY2_WL_BSS_INFO_VERSION &&
list->version != LEGACY_WL_BSS_INFO_VERSION) {
fprintf(stderr, "Sorry, your driver has bss_info_version %d "
"but this program supports only version %d.\n",
list->version, WL_BSS_INFO_VERSION);
list->buflen = 0;
list->count = 0;
}
return ret;
}
static int
wl_get_iscan(void *wl, char *scan_buf, uint buf_len)
{
wl_iscan_results_t list;
wl_scan_results_t *results;
int ret;
memset(&list, '\0', sizeof(list));
list.results.buflen = htod32(buf_len);
ret = wlu_iovar_getbuf(wl, "iscanresults", &list, WL_ISCAN_RESULTS_FIXED_SIZE,
scan_buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
ret = 0;
results = &((wl_iscan_results_t*)scan_buf)->results;
results->buflen = dtoh32(results->buflen);
results->version = dtoh32(results->version);
results->count = dtoh32(results->count);
if (results->buflen == 0) {
printf("wl_get_iscan buflen 0\n");
results->version = 0;
results->count = 0;
} else if (results->version != WL_BSS_INFO_VERSION &&
results->version != LEGACY2_WL_BSS_INFO_VERSION &&
results->version != LEGACY_WL_BSS_INFO_VERSION) {
fprintf(stderr, "Sorry, your driver has bss_info_version %d "
"but this program supports only version %d.\n",
results->version, WL_BSS_INFO_VERSION);
results->buflen = 0;
results->count = 0;
}
return ret;
}
static int
wl_spect(void *wl, cmd_t *cmd, char **argv)
{
int ret, spect;
char *endptr = NULL;
if (!*++argv) {
if ((ret = wlu_get(wl, cmd->get, &spect, sizeof(spect))) < 0) {
return ret;
}
spect = dtoh32(spect);
switch (spect) {
case SPECT_MNGMT_OFF:
printf("Off\n");
break;
case SPECT_MNGMT_LOOSE_11H:
printf("Loose interpretation of 11h spec - may join non 11h AP.\n");
break;
case SPECT_MNGMT_STRICT_11H:
printf("Strict interpretation of 11h spec - may not join non 11h AP.\n");
break;
case SPECT_MNGMT_STRICT_11D:
printf("802.11d mode\n");
break;
case SPECT_MNGMT_LOOSE_11H_D:
printf("Loose interpretation of 11h+d spec - may join non-11h APs\n");
break;
default:
printf("invalid value 0x%x\n", spect);
return BCME_BADARG;
}
return (0);
} else {
spect = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (spect < SPECT_MNGMT_OFF || spect > SPECT_MNGMT_LOOSE_11H_D)
return BCME_BADARG;
spect = htod32(spect);
return wlu_set(wl, cmd->set, &spect, sizeof(spect));
}
}
static int
wl_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct ether_addr bssid;
wlc_ssid_t ssid;
char ssidbuf[SSID_FMT_BUF_LEN];
wl_bss_info_t *bi;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
if ((ret = wlu_get(wl, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN)) == 0) {
/* The adapter is associated. */
*(uint32*)buf = htod32(WLC_IOCTL_MAXLEN);
if ((ret = wlu_get(wl, WLC_GET_BSS_INFO, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
bi = (wl_bss_info_t*)(buf + 4);
if (dtoh32(bi->version) == WL_BSS_INFO_VERSION ||
dtoh32(bi->version) == LEGACY2_WL_BSS_INFO_VERSION ||
dtoh32(bi->version) == LEGACY_WL_BSS_INFO_VERSION)
dump_bss_info(bi);
else
fprintf(stderr, "Sorry, your driver has bss_info_version %d "
"but this program supports only version %d.\n",
bi->version, WL_BSS_INFO_VERSION);
} else {
printf("Not associated. Last associated with ");
if ((ret = wlu_get(wl, WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t))) < 0) {
printf("\n");
return ret;
}
wl_format_ssid(ssidbuf, ssid.SSID, dtoh32(ssid.SSID_len));
printf("SSID: \"%s\"\n", ssidbuf);
}
return 0;
}
static int
wl_deauth_rc(void *wl, cmd_t *cmd, char **argv)
{
scb_val_t scb_val;
int ret;
if (!*++argv) {
fprintf(stderr, "STA MAC not specified, deauth all\n");
ret = wlu_set(wl, WLC_SCB_DEAUTHENTICATE, (void *)&ether_bcast,
ETHER_ADDR_LEN);
return ret;
} else if (!wl_ether_atoe(*argv, &scb_val.ea)) {
fprintf(stderr, "Malformed STA MAC parameter\n");
ret = BCME_USAGE_ERROR;
} else if (!*++argv) {
/* No reason code furnished, so driver will use its default */
ret = wlu_set(wl, WLC_SCB_DEAUTHENTICATE, &scb_val.ea,
ETHER_ADDR_LEN);
} else {
scb_val.val = htod32((uint32)strtoul(*argv, NULL, 0));
ret = wlu_set(wl, cmd->set, &scb_val, sizeof(scb_val));
}
return ret;
}
static int
wl_wpa_auth(void *wl, cmd_t *cmd, char **argv)
{
int bsscfg_idx = 0;
int consumed;
int wpa_auth = 0;
int ret = 0;
int i;
static struct {
int val;
const char *name;
} auth_mode[] =
/* Keep the numeric values in the staticly initialized
* help string consistent. Unfortunately, there isn't
* an automatic way for that.
*/
{{WPA_AUTH_NONE, "WPA-NONE"},
{WPA_AUTH_UNSPECIFIED, "WPA-802.1x"},
{WPA_AUTH_PSK, "WPA-PSK"},
#ifdef BCMCCX
{WPA_AUTH_CCKM, "CCKM(WPA)"},
{WPA2_AUTH_CCKM, "CCKM(WPA2)"},
#endif /* BCMCCX */
{WPA2_AUTH_UNSPECIFIED, "WPA2-802.1x"},
{WPA2_AUTH_PSK, "WPA2-PSK"},
#ifdef BCMWAPI_WAI
{WAPI_AUTH_UNSPECIFIED, "WAPI-AS"},
{WAPI_AUTH_PSK, "WAPI-PSK"},
#endif /* BCMWAPI_WAI */
{WPA2_AUTH_FT, "FT"},
{WPA_AUTH_DISABLED, "disabled"}};
/* skip the command name */
argv++;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv, cmd->name, &bsscfg_idx, &consumed)) != 0)
return ret;
argv += consumed;
if (!*argv) {
/* no arg, so this is a GET. */
if (!consumed)
ret = wlu_iovar_getint(wl, "wpa_auth", &wpa_auth);
else
ret = wl_bssiovar_getint(wl, "wpa_auth", bsscfg_idx, &wpa_auth);
if (ret < 0)
return ret;
/* Show all AKM suites enabled */
printf("0x%x", wpa_auth);
if (wpa_auth == WPA_AUTH_DISABLED)
printf(" Disabled");
for (i = 0; i < (int)ARRAYSIZE(auth_mode); i++) {
if (wpa_auth & auth_mode[i].val)
printf(" %s", auth_mode[i].name);
}
printf("\n");
return ret;
} else {
/* there's an arg, so this is a SET. */
ret = 1;
/* Validate the user input range */
if (isdigit((int)*argv[0])) {
unsigned int range = 0;
/* param is a number; look for value in the list */
wpa_auth = strtoul(*argv, NULL, 0);
/* Validate the user input range */
for (i = 0; i < (int)ARRAYSIZE(auth_mode); i++)
range |= auth_mode[i].val;
range = (~range) & 0xFFFF;
if (range & wpa_auth) {
ret = 1;
goto usage;
} else {
ret = 0;
}
} else {
int arg_count = 0;
char** p_argv;
int j = 0;
unsigned int range = 0;
wpa_auth = 0;
p_argv = argv;
for (i = 0; i < (int)ARRAYSIZE(auth_mode); i++)
range |= auth_mode[i].val;
range = (~range) & (0xFFFF);
while (*p_argv) {
arg_count++;
p_argv++;
}
p_argv = argv;
for (j = 0; j < arg_count; j++) {
bool found = FALSE;
argv = p_argv + j;
/* treat param as string to be matched in list */
for (i = 0; i < (int)ARRAYSIZE(auth_mode); i++) {
if (!stricmp(auth_mode[i].name, *argv)) {
found = TRUE;
wpa_auth |= auth_mode[i].val;
ret = 0;
/* traverse the list */
argv++;
if (!*argv)
break;
}
}
if ((found == FALSE) || (range & wpa_auth))
goto usage;
}
}
if (ret)
fprintf(stderr, "%s is not a valid WPA auth mode\n", *argv);
else {
if (!consumed)
ret = wlu_iovar_setint(wl, "wpa_auth", wpa_auth);
else
ret = wl_bssiovar_setint(wl, "wpa_auth", bsscfg_idx, wpa_auth);
}
}
return ret;
usage:
fprintf(stderr, "Inavlid user argument.\n");
fprintf(stderr, "Values may be a bitvector or list of names from the set.\n");
for (i = 0; i < (int)ARRAYSIZE(auth_mode); i++) {
fprintf(stderr, "\n0x%04x %s", auth_mode[i].val, auth_mode[i].name);
}
printf("\n");
return ret;
}
static int
wl_set_pmk(void *wl, cmd_t *cmd, char **argv)
{
wsec_pmk_t psk;
size_t key_len;
if (!*++argv) {
return BCME_USAGE_ERROR;
}
key_len = strlen(*argv);
if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
fprintf(stderr, "passphrase must be between %d and %d characters long\n",
WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN);
return BCME_BADARG;
}
psk.key_len = htod16((ushort) key_len);
psk.flags = htod16(WSEC_PASSPHRASE);
memcpy(psk.key, *argv, key_len);
return wlu_set(wl, cmd->set, &psk, sizeof(psk));
}
static int
wl_wsec(void *wl, cmd_t *cmd, char **argv)
{
int wsec;
int bsscfg_idx = 0;
int consumed;
char *endptr = NULL;
int error;
UNUSED_PARAMETER(cmd);
argv++;
/* parse a bsscfg_idx option if present */
if ((error = wl_cfg_option(argv, "wsec", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
if (!*argv) {
/* This is a GET */
if (consumed == 0) {
error = wlu_get(wl, WLC_GET_WSEC, &wsec, sizeof(uint32));
wsec = dtoh32(wsec);
}
else
error = wl_bssiovar_getint(wl, "wsec", bsscfg_idx, &wsec);
if (!error)
wl_printint(wsec);
} else {
/* This is a SET */
if (!stricmp(*argv, "off"))
wsec = 0;
else {
wsec = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
if (consumed == 0) {
wsec = htod32(wsec);
error = wlu_set(wl, WLC_SET_WSEC, &wsec, sizeof(uint32));
}
else
error = wl_bssiovar_setint(wl, "wsec", bsscfg_idx, wsec);
}
return error;
}
static int
parse_wep(char **argv, wl_wsec_key_t *key, bool options)
{
char hex[] = "XX";
unsigned char *data = key->data;
char *keystr = *argv;
switch (strlen(keystr)) {
case 5:
case 13:
case 16:
key->len = strlen(keystr);
memcpy(data, keystr, key->len + 1);
break;
case 12:
case 28:
case 34:
case 66:
/* strip leading 0x */
if (!strnicmp(keystr, "0x", 2))
keystr += 2;
else
return -1;
/* fall through */
case 10:
case 26:
case 32:
case 64:
key->len = strlen(keystr) / 2;
while (*keystr) {
strncpy(hex, keystr, 2);
*data++ = (char) strtoul(hex, NULL, 16);
keystr += 2;
}
break;
default:
return -1;
}
switch (key->len) {
case 5:
key->algo = CRYPTO_ALGO_WEP1;
break;
case 13:
key->algo = CRYPTO_ALGO_WEP128;
break;
case 16:
/* default to AES-CCM */
key->algo = CRYPTO_ALGO_AES_CCM;
break;
case 32:
key->algo = CRYPTO_ALGO_TKIP;
break;
default:
return -1;
}
/* Set as primary key by default */
key->flags |= WL_PRIMARY_KEY;
if (options) {
/* Get options */
while (*++argv) {
if (!strnicmp("ccm", *argv, 3) && key->len == 16)
key->algo = CRYPTO_ALGO_AES_CCM;
#ifdef BCMWAPI_WPI
else if (!strnicmp("wapi", *argv, 4) && key->len == 32)
key->algo = CRYPTO_ALGO_SMS4;
#endif /* BCMWAPI_WPI */
else if (!strnicmp("ocb", *argv, 3) && key->len == 16)
key->algo = CRYPTO_ALGO_AES_OCB_MPDU;
else if (!strnicmp("notx", *argv, 4))
key->flags &= ~WL_PRIMARY_KEY;
else if (!wl_ether_atoe(*argv, &key->ea))
memset(&key->ea, 0, ETHER_ADDR_LEN);
}
}
return 0;
}
static int
wl_primary_key(void *wl, cmd_t *cmd, char **argv)
{
int i, val, ret = 0;
if (!*++argv) {
i = 0;
do {
val = htod32(i);
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(val))) < 0) {
return ret;
}
if (dtoh32(val)) {
printf("Key %d is primary\n", i);
return 0;
}
} while (++i < DOT11_MAX_DEFAULT_KEYS);
printf("No primary key set\n");
} else {
val = htod32(atoi(*argv));
ret = wlu_set(wl, cmd->set, &val, sizeof(val));
}
return ret;
}
static int
wl_addwep(void *wl, cmd_t *cmd, char **argv)
{
wl_wsec_key_t key;
int bsscfg_idx = 0;
int consumed;
int error;
memset(&key, 0, sizeof(key));
argv++;
/* parse a bsscfg_idx option if present */
if ((error = wl_cfg_option(argv, "addwep", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
/* GET operation not allowed */
if (!*argv)
return BCME_USAGE_ERROR;
key.index = atoi(*argv++);
if (!*argv) {
fprintf(stderr, "No key specified\n");
return BCME_USAGE_ERROR;
}
if (parse_wep(argv, &key, TRUE))
return BCME_BADARG;
key.index = htod32(key.index);
key.len = htod32(key.len);
key.algo = htod32(key.algo);
key.flags = htod32(key.flags);
if (consumed == 0) {
error = wlu_set(wl, cmd->set, &key, sizeof(key));
} else {
error = wlu_bssiovar_setbuf(wl, "wsec_key", bsscfg_idx,
&key, sizeof(key), buf, WLC_IOCTL_MAXLEN);
}
return error;
}
static int
wl_rmwep(void *wl, cmd_t *cmd, char **argv)
{
wl_wsec_key_t key;
int bsscfg_idx = 0;
int consumed;
int error;
memset(&key, 0, sizeof(key));
argv++;
/* parse a bsscfg_idx option if present */
if ((error = wl_cfg_option(argv, "rmwep", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
/* GET operation not allowed */
if (!*argv)
return BCME_USAGE_ERROR;
key.index = htod32(atoi(*argv++));
if (*argv) {
if (!(wl_ether_atoe(*argv, &key.ea)))
return BCME_USAGE_ERROR;
}
if (consumed == 0) {
error = wlu_set(wl, cmd->set, &key, sizeof(key));
} else {
error = wlu_var_setbuf(wl, "wsec_key", &key, sizeof(key));
}
return error;
}
static struct {
uint value;
const char *string;
} wsec_test[] = {
{WSEC_GEN_MIC_ERROR, "mic_error"},
{WSEC_GEN_REPLAY, "replay"},
{WSEC_GEN_ICV_ERROR, "icv_error"},
{WSEC_GEN_MFP_ACT_ERROR, "act_error"},
{WSEC_GEN_MFP_DISASSOC_ERROR, "disassoc_error"},
{WSEC_GEN_MFP_DEAUTH_ERROR, "deauth_error"},
{0, NULL}
};
static int
wl_wsec_test(void *wl, cmd_t *cmd, char **argv)
{
wl_wsec_key_t *key;
int i, len;
char *endptr = NULL, *wsec_buf = NULL;
uint32 val, last_val;
int err = 0;
if (!*++argv) {
err = BCME_USAGE_ERROR;
goto usage;
}
val = strtol(*argv, &endptr, 0);
if (endptr == *argv) {
/* the value string was not parsed by strtol */
for (i = 0; wsec_test[i].value; i++)
if (stricmp(wsec_test[i].string, *argv) == 0) {
val = wsec_test[i].value;
break;
}
if (wsec_test[i].value == 0) {
err = BCME_BADARG;
goto usage;
}
}
++argv;
switch (val) {
case WSEC_GEN_REPLAY:
case WSEC_GEN_MIC_ERROR:
case WSEC_GEN_ICV_ERROR:
case WSEC_GEN_MFP_ACT_ERROR:
case WSEC_GEN_MFP_DISASSOC_ERROR:
case WSEC_GEN_MFP_DEAUTH_ERROR:
if (!*argv) {
fprintf(stderr, "insufficient arguments\n");
return BCME_USAGE_ERROR;
}
len = sizeof(wl_wsec_key_t) + 4;
wsec_buf = malloc(len);
if (wsec_buf == NULL) {
fprintf(stderr, "Error allocating memory failed for wsec_buf");
return BCME_NOMEM;
}
*(uint32 *)wsec_buf = htod32(val);
key = (wl_wsec_key_t *)&wsec_buf[4];
memset(key, 0, sizeof(wl_wsec_key_t));
/* If it doesn't look like an ether addr, suppose it's a key index */
if (!(wl_ether_atoe(*argv, &key->ea))) {
memset(&key->ea, 0, ETHER_ADDR_LEN);
key->index = htod32(atoi(*argv));
}
break;
default:
goto usage;
}
err = wlu_set(wl, cmd->set, wsec_buf, len);
free(wsec_buf);
goto exit;
usage:
fprintf(stderr, "wsec test_type may be a number or name from the following set:");
last_val = 0xffffffff;
for (i = 0; (val = wsec_test[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, wsec_test[i].string);
else
fprintf(stderr, ", %s", wsec_test[i].string);
last_val = val;
}
fprintf(stderr, "\n");
exit:
return err;
}
static int
wl_keys(void *wl, cmd_t *cmd, char **argv)
{
uint i, j;
union {
int index;
wl_wsec_key_t key;
} u;
int wep_is_on = 0;
const char *addr;
int ret = BCME_OK;
UNUSED_PARAMETER(argv);
if (wlu_iovar_getint(wl, "wsec", &wep_is_on) < 0)
fprintf(stderr, "Could not query wsec status.\n");
for (i = 0; i < 256; i++) {
memset(&u, 0, sizeof(u));
u.index = htod32(i);
ret = wlu_get(wl, cmd->get, &u, sizeof(u));
if (ret == BCME_IOCTL_ERROR) {
int bcmerr;
if (wlu_iovar_getint(wl, "bcmerror", &bcmerr) >= 0 &&
(bcmerr == BCME_BADKEYIDX)) {
ret = BCME_OK;
}
}
if (ret != BCME_OK)
break;
/* ignore empty keys */
if (dtoh32(u.key.algo) == CRYPTO_ALGO_OFF || (u.key.len == 0))
continue;
if (ETHER_ISNULLADDR(&u.key.ea))
addr = "(default)";
else
addr = wl_ether_etoa(&u.key.ea);
printf("%3d: %-17s Key %d: %s ", i, addr, dtoh32(u.key.index),
bcm_crypto_algo_name(dtoh32(u.key.algo)));
if (wep_is_on && dtoh32(u.key.flags) & WL_PRIMARY_KEY)
printf("*");
printf("\t");
if (dtoh32(u.key.len) == 0)
printf("No key present");
else {
if (dtoh32(u.key.flags) & WL_SOFT_KEY)
printf("soft ");
printf("len %d, data 0x", dtoh32(u.key.len));
for (j = 0; j < dtoh32(u.key.len); j++)
printf("%02X", u.key.data[j]);
for (j = 0; j < dtoh32(u.key.len); j++)
if (!isprint(u.key.data[j]))
break;
if (j == dtoh32(u.key.len))
printf(" (%.*s)", (int)dtoh32(u.key.len), u.key.data);
}
printf("\n");
}
return ret;
}
static int
wl_tsc(void *wl, cmd_t *cmd, char **argv)
{
union {
int32 index;
uint8 tsc[DOT11_WPA_KEY_RSC_LEN];
} u;
uint32 hi, lo;
int idx, ret;
if (!*++argv)
return BCME_USAGE_ERROR;
idx = atoi(*argv);
if (idx < 0) {
fprintf(stderr, "Key index %d out of range. Should be positive.\n", idx);
return BCME_BADARG;
}
u.index = htod32(idx);
if ((ret = wlu_get(wl, cmd->get, &u, sizeof(u))) < 0)
return ret;
lo = u.tsc[0] | (u.tsc[1] << 8) | (u.tsc[2] << 16) | (u.tsc[3] << 24);
hi = u.tsc[4] | (u.tsc[5] << 8) | (u.tsc[6] << 16) | (u.tsc[7] << 24);
printf("Key %d TSC: 0x%04x:%08x\n", idx, hi, lo);
return 0;
}
static void
wl_txppr_print(ppr_t *ppr, int cck, uint flags)
{
switch (ppr_get_ch_bw(ppr)) {
case WL_TX_BW_20:
printf("\n20MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20);
break;
case WL_TX_BW_40:
printf("\n20 in 40MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN40);
printf("\n40MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40);
break;
case WL_TX_BW_80:
printf("\n20 in 80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN80);
printf("\n40 in 80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40IN80);
printf("\n80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_80);
break;
default:
break;
}
/* MCS32 value is obsoleted */
/* printf("MCS32 %2d\n", ppr->mcs32); */
printf("\n");
}
/* get a power value from the opaque ppr structure */
static int8 wl_ppr_get_pwr(ppr_t* pprptr, reg_rate_index_t rate_idx, wl_tx_bw_t bw)
{
clm_rate_group_id_t group_id = ppr_table[rate_idx].id;
int8 power = WL_RATE_DISABLED;
switch (ppr_group_table[group_id].rate_type) {
case PPR_RATE_DSSS:
{
ppr_dsss_rateset_t rateset;
ppr_get_dsss(pprptr, bw, ppr_group_table[group_id].chain, &rateset);
power = rateset.pwr[rate_idx-ppr_group_table[group_id].first_rate];
}
break;
case PPR_RATE_OFDM:
{
ppr_ofdm_rateset_t rateset;
ppr_get_ofdm(pprptr, bw, ppr_group_table[group_id].mode,
ppr_group_table[group_id].chain, &rateset);
power = rateset.pwr[rate_idx-ppr_group_table[group_id].first_rate];
}
break;
case PPR_RATE_HT:
{
ppr_ht_mcs_rateset_t rateset;
ppr_get_ht_mcs(pprptr, bw, ppr_group_table[group_id].nss,
ppr_group_table[group_id].mode,
ppr_group_table[group_id].chain, &rateset);
power = rateset.pwr[rate_idx-ppr_group_table[group_id].first_rate];
}
break;
case PPR_RATE_VHT:
{
ppr_vht_mcs_rateset_t rateset;
ppr_get_vht_mcs(pprptr, bw, ppr_group_table[group_id].nss,
ppr_group_table[group_id].mode,
ppr_group_table[group_id].chain, &rateset);
power = rateset.pwr[rate_idx-ppr_group_table[group_id].first_rate];
}
break;
default:
break;
}
return power;
}
#define PRINT_PPR_RATE_LOOP(idx, len, rates) \
for (idx = 0; idx < len; idx++) { \
if (rates[idx] == WL_RATE_DISABLED) \
printf(" -"); \
else \
printf(" %2d", rates[idx]); \
}
/* print power offset for for a given bandwidth */
static void
wl_txppr_print_bw(ppr_t *ppr, int cck, uint flags, wl_tx_bw_t bw)
{
uint i, j, rlen;
uint n = WL_NUM_2x2_ELEMENTS;
uint offset = 0;
int8 *ptr, *vhtptr;
const char *str = "";
bool siso = ((flags & WL_TX_POWER_F_MIMO) == 0);
bool vht = ((flags & WL_TX_POWER_F_VHT) != 0);
ppr_ofdm_rateset_t ofdm_rate;
ppr_vht_mcs_rateset_t vhtrate;
if (!siso) {
offset = n = WL_NUM_3x3_ELEMENTS;
}
if (cck) {
ppr_dsss_rateset_t rate;
ppr_get_dsss(ppr, bw, WL_TX_CHAINS_1, &rate);
printf("CCK ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_DSSS, rate.pwr);
if (!siso) {
ppr_get_dsss(ppr, bw, WL_TX_CHAINS_2, &rate);
printf("\nCCK CDD 1x2 ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_DSSS, rate.pwr);
ppr_get_dsss(ppr, bw, WL_TX_CHAINS_3, &rate);
printf("\nCCK CDD 1x3 ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_DSSS, rate.pwr);
}
}
ppr_get_ofdm(ppr, bw, WL_TX_MODE_NONE, WL_TX_CHAINS_1, &ofdm_rate);
printf("\nOFDM ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_OFDM, ofdm_rate.pwr);
ppr_get_ofdm(ppr, bw, WL_TX_MODE_CDD, WL_TX_CHAINS_2, &ofdm_rate);
printf("\nOFDM-CDD ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_OFDM, ofdm_rate.pwr);
printf("\n");
for (i = 0; i < n; i++) {
wl_tx_nss_t nss;
wl_tx_mode_t mode;
wl_tx_chains_t chains;
switch (i + offset) {
case 0:
str = "MCS-SISO ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_1;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 1:
str = "MCS-CDD ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 2:
str = "MCS STBC ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_STBC;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 3:
str = "MCS 8~15 ";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 4:
case 5:
ptr = NULL;
vhtptr = NULL;
break;
case 6:
str = "1 Nsts 1 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_1;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 7:
str = "1 Nsts 2 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 8:
str = "1 Nsts 3 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 9:
str = "2 Nsts 2 Tx";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 10:
str = "2 Nsts 3 Tx";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 11:
str = "3 Nsts 3 Tx";
nss = WL_TX_NSS_3;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
default:
ptr = NULL;
vhtptr = NULL;
break;
}
if (ptr == NULL)
continue;
ppr_get_vht_mcs(ppr, bw, nss, mode, chains, &vhtrate);
printf("%s ", str);
if (vht && vhtptr)
rlen = WL_RATESET_SZ_VHT_MCS;
else
rlen = WL_RATESET_SZ_HT_MCS;
PRINT_PPR_RATE_LOOP(j, rlen, ptr);
printf("\n");
}
}
static int
wl_get_current_txppr(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint flags;
chanspec_t chanspec;
char chanspec_str[CHANSPEC_STR_LEN];
uint pprsize = ppr_ser_size_by_bw(ppr_get_max_bw());
wl_txppr_t *wl_txppr;
ppr_t *pprptr = NULL;
wl_txppr = (wl_txppr_t *)malloc(sizeof(*wl_txppr) + pprsize);
if (wl_txppr == NULL) {
fprintf(stderr, "Error allocating memory failed for curppr");
return BCME_NOMEM;
}
memset(wl_txppr, 0, sizeof(*wl_txppr));
wl_txppr->buflen = pprsize;
if ((err = ppr_init_ser_mem_by_bw(wl_txppr->pprbuf, ppr_get_max_bw(), pprsize))
!= BCME_OK) {
free(wl_txppr);
return err;
}
if (WLC_IOCTL_MAXLEN < sizeof(wl_txppr_t) + pprsize) {
free(wl_txppr);
return BCME_ERROR;
}
argv++;
if (*argv)
fprintf(stderr, "Ignoring arguments for %s\n", cmd->name);
wl_txppr->ver = WL_TXPPR_VERSION;
wl_txppr->len = WL_TXPPR_LENGTH;
if ((err = wlu_iovar_getbuf(wl, "curppr", wl_txppr, sizeof(*wl_txppr) + pprsize,
buf, WLC_IOCTL_MAXLEN)) < 0) {
free(wl_txppr);
return err;
}
/* the input buffer is no longer needed, output results are in buf */
free(wl_txppr);
wl_txppr = (wl_txppr_t *)buf;
/* parse */
wl_txppr->flags = dtoh32(wl_txppr->flags);
wl_txppr->chanspec = wl_chspec_from_driver(wl_txppr->chanspec);
wl_txppr->local_chanspec = wl_chspec_from_driver(wl_txppr->local_chanspec);
chanspec = wl_txppr->chanspec;
flags = (wl_txppr->flags & WL_TX_POWER_F_VHT) |
(wl_txppr->flags & WL_TX_POWER_F_HT) |
(wl_txppr->flags & WL_TX_POWER_F_MIMO) |
(wl_txppr->flags & WL_TX_POWER_F_SISO);
/* dump */
printf("Current channel:\t %s\n",
wf_chspec_ntoa(wl_txppr->chanspec, chanspec_str));
printf("BSS channel:\t\t %s\n",
wf_chspec_ntoa(wl_txppr->local_chanspec, chanspec_str));
printf("Power/Rate Dump (in 1/4dB): Channel %d\n", CHSPEC_CHANNEL(chanspec));
if ((err = ppr_deserialize_create(NULL, wl_txppr->pprbuf, pprsize, &pprptr)) == BCME_OK) {
wl_txppr_print(pprptr, CHSPEC_IS2G(chanspec), flags);
ppr_delete(NULL, pprptr);
}
return err;
}
/* This version number must be incremented for every
* modification to the curpower output format. Minor changes
* warrant a decimal point increment. Major (potential
* script-breaking) changes should be met with a major increment.
*/
#define CURPOWER_OUTPUT_FORMAT_VERSION "5.1"
#define RATE_STR_LEN 64
static int
wl_get_current_power(void *wl, cmd_t *cmd, char **argv)
{
int err;
int mimo;
int i;
chanspec_t chanspec;
char chanspec_str[CHANSPEC_STR_LEN];
bool verbose = FALSE;
bool brief = FALSE;
int16 power_target;
char rate_str[RATE_STR_LEN];
int clk;
uint8 *ppr_ser;
size_t pprsize = ppr_ser_size_by_bw(ppr_get_max_bw());
tx_pwr_rpt_t *ppr_wl = NULL;
/* firmware will crash if clk = 0 while using curpower */
if ((err = wlu_get(wl, WLC_GET_CLK, &clk, sizeof(int))) < 0)
return err;
if (!clk) {
fprintf(stderr, "Error: clock not active, do wl up (if not done already) "
"and force mpc 0 to active clock\n");
return BCME_ERROR;
}
/* Allocate memory for curpower report structure with 2 ppr data block */
ppr_wl = (tx_pwr_rpt_t *)malloc(sizeof(tx_pwr_rpt_t) + pprsize*2);
if (ppr_wl == NULL) {
fprintf(stderr, "Allocating mem failed for curpower\n");
return BCME_NOMEM;
}
ppr_ser = ppr_wl->pprdata;
memset(ppr_wl, 0, sizeof(tx_pwr_rpt_t) + pprsize*2);
ppr_wl->board_limit_len = pprsize;
ppr_wl->target_len = pprsize;
ppr_wl->version = TX_POWER_T_VERSION;
/* init allocated mem for serialisation */
ppr_init_ser_mem_by_bw(ppr_ser, ppr_get_max_bw(), ppr_wl->board_limit_len);
ppr_ser += ppr_wl->board_limit_len;
ppr_init_ser_mem_by_bw(ppr_ser, ppr_get_max_bw(), ppr_wl->target_len);
if (argv[1] && (!strcmp(argv[1], "--verbose") || !strcmp(argv[1], "-v"))) {
verbose = TRUE;
argv++;
}
if (argv[1] && (!strcmp(argv[1], "--brief") || !strcmp(argv[1], "-b"))) {
brief = TRUE;
argv++;
}
argv++;
if (*argv)
fprintf(stderr, "Ignoring arguments for %s\n", cmd->name);
if ((err = wlu_get(wl, cmd->get, ppr_wl, sizeof(tx_pwr_rpt_t) + pprsize*2)) < 0) {
fprintf(stderr, "Error: Curpower failed. ");
fprintf(stderr, "Bring up interface and disable mpc if necessary (wl mpc 0)\n");
free(ppr_wl);
return err;
}
/* parse */
if (ppr_wl->version != TX_POWER_T_VERSION) {
printf("error: version mismatch - driver %d, wl executable was expecting %d\n",
ppr_wl->version, TX_POWER_T_VERSION);
err = BCME_ERROR;
} else {
ppr_t* ppr_board = NULL;
ppr_t* ppr_target = NULL;
ppr_wl->flags = dtoh32(ppr_wl->flags);
ppr_wl->chanspec = wl_chspec_from_driver(ppr_wl->chanspec);
ppr_wl->local_chanspec = wl_chspec_from_driver(ppr_wl->local_chanspec);
chanspec = ppr_wl->chanspec;
mimo = (ppr_wl->flags & WL_TX_POWER_F_HT) |
(ppr_wl->flags & WL_TX_POWER_F_MIMO) |
(ppr_wl->flags & WL_TX_POWER_F_SISO);
if ((err = ppr_deserialize_create(NULL, ppr_wl->pprdata, ppr_wl->board_limit_len,
&ppr_board)) != BCME_OK) {
goto exit;
}
if ((err = ppr_deserialize_create(NULL, ppr_ser, ppr_wl->target_len, &ppr_target))
!= BCME_OK) {
goto exit;
}
/* dump */
if (verbose)
printf("%-23s%s\n", "Output Format Version:",
CURPOWER_OUTPUT_FORMAT_VERSION);
printf("%-23s%s, %s\n", "Power Control:",
(ppr_wl->flags & WL_TX_POWER_F_ENABLED) ? "On" : "Off",
(ppr_wl->flags & WL_TX_POWER_F_HW) ? "HW" : "SW");
printf("%-23s%s\n", "Current Channel:",
wf_chspec_ntoa(ppr_wl->chanspec, chanspec_str));
printf("%-23s%s\n", "BSS Channel:",
wf_chspec_ntoa(ppr_wl->local_chanspec, chanspec_str));
printf("%-23s%d.%d dBm\n", "BSS Local Max:",
DIV_QUO(ppr_wl->local_max, 4), DIV_REM(ppr_wl->local_max, 4));
printf("%-23s%d.%d dB\n", "BSS Local Constraint:",
DIV_QUO(ppr_wl->local_constraint, 4), DIV_REM(ppr_wl->local_constraint, 4));
printf("%-23s%s\n", "Channel Width:",
((ppr_wl->channel_bandwidth == WL_BW_20MHZ) ? "20MHz" :
((ppr_wl->channel_bandwidth == WL_BW_40MHZ) ? "40MHz" : "80MHz")));
printf("%-23s%d.%d dBm\n", "User Target:",
DIV_QUO(ppr_wl->user_target, 4), DIV_REM(ppr_wl->user_target, 4));
printf("%-23s%d.%d dB\n", "SROM Antgain 2G:",
DIV_QUO(ppr_wl->antgain[0], 4), DIV_REM(ppr_wl->antgain[0], 4));
printf("%-23s%d.%d dB\n", "SROM Antgain 5G:",
DIV_QUO(ppr_wl->antgain[1], 4), DIV_REM(ppr_wl->antgain[1], 4));
printf("%-23s", "SAR:");
if (ppr_wl->sar != WLC_TXPWR_MAX)
printf("%d.%d dB\n", DIV_QUO(ppr_wl->sar, 4), DIV_REM(ppr_wl->sar, 4));
else
printf("-\n");
printf("%-23s", "Open loop:");
if (ppr_wl->flags & WL_TX_POWER_F_OPENLOOP)
printf("On\n");
else
printf("Off\n");
printf("%-23s", "Current rate:");
wl_rate_print(rate_str, ppr_wl->last_tx_ratespec);
printf("[%s] %s\n", get_reg_rate_string_from_ratespec(ppr_wl->last_tx_ratespec),
rate_str);
printf("\n");
printf("Regulatory Limits:\n");
if (brief)
{
wl_txpwr_regulatory_array_row_print(ppr_wl->clm_limits,
ppr_wl->clm_limits_subchan1, ppr_wl->clm_limits_subchan2,
ppr_wl->channel_bandwidth,
get_reg_rate_index_from_ratespec(ppr_wl->last_tx_ratespec));
}
else
{
wl_txpwr_regulatory_array_print(ppr_wl->clm_limits,
ppr_wl->clm_limits_subchan1,
ppr_wl->clm_limits_subchan2, ppr_wl->channel_bandwidth, verbose);
}
printf("\n");
printf("%-23s%d\n", "Core Index:", ppr_wl->display_core);
printf("Board Limits:\n");
if (brief)
{
wl_txpwr_array_row_print(ppr_target, ppr_wl->channel_bandwidth,
get_reg_rate_index_from_ratespec(ppr_wl->last_tx_ratespec));
}
else
{
wl_txpwr_array_print(ppr_board, ppr_wl->channel_bandwidth, verbose,
CHSPEC_IS5G(chanspec));
printf("\n");
}
printf("Power Targets:\n");
if (brief)
{
wl_txpwr_array_row_print(ppr_target, ppr_wl->channel_bandwidth,
get_reg_rate_index_from_ratespec(ppr_wl->last_tx_ratespec));
}
else
{
wl_txpwr_array_print(ppr_target, ppr_wl->channel_bandwidth, verbose,
CHSPEC_IS5G(chanspec));
}
printf("\n");
/* print the different power estimate combinations */
if (mimo) {
printf("Maximum Power Target among all rates:\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO(ppr_wl->tx_power_max[i], 4),
DIV_REM(ppr_wl->tx_power_max[i], 4));
printf("\n");
printf("Last est. power :\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO(ppr_wl->est_Pout[i], 4),
DIV_REM(ppr_wl->est_Pout[i], 4));
printf("\n");
printf("Power Target for the current rate :\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
{
if (ppr_wl->target_offsets[i] != WL_RATE_DISABLED)
{
power_target = ppr_wl->tx_power_max[i] -
ppr_wl->target_offsets[i];
/* for ACPHY, clip the power_target if it
is larger than the SAR limit for the
current path. For non-ACPHY or
WLC_SARLIMIT disabled, this threshold is
set to be MAX pwr, ie. 127
*/
if (power_target > ppr_wl->SARLIMIT[i])
power_target = ppr_wl->SARLIMIT[i];
printf("%2d.%02d ",
DIV_QUO(power_target, 4),
DIV_REM(power_target, 4));
}
else
{
printf("- ");
}
}
printf("\n");
printf("Last adjusted est. power :\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO(ppr_wl->est_Pout[i], 4),
DIV_REM(ppr_wl->est_Pout[i], 4));
printf("\n");
} else {
printf("Last est. power:\t%2d.%02d dBm\n",
DIV_QUO(ppr_wl->est_Pout[0], 4),
DIV_REM(ppr_wl->est_Pout[0], 4));
}
if (!mimo && CHSPEC_IS2G(chanspec)) {
printf("Last CCK est. power:\t%2d.%02d dBm\n",
DIV_QUO(ppr_wl->est_Pout_cck, 4),
DIV_REM(ppr_wl->est_Pout_cck, 4));
}
exit:
if (ppr_board != NULL) {
ppr_delete(NULL, ppr_board);
}
if (ppr_target != NULL) {
ppr_delete(NULL, ppr_target);
}
}
free(ppr_wl);
return err;
}
static int wl_get_txpwr_target_max(void *wl, cmd_t *cmd, char **argv)
{
int err;
txpwr_target_max_t target_pwr;
int i;
UNUSED_PARAMETER(argv);
if ((err = wlu_iovar_get(wl, cmd->name, (void *)&target_pwr, sizeof(target_pwr))) < 0) {
fprintf(stderr, "Error: txpwr_target failed. Make sure interface is up.\n");
return err;
}
if (target_pwr.version != TXPWR_TARGET_VERSION) {
fprintf(stderr, "Error: version [%d] mismatch Driver version:%d\n",
TXPWR_TARGET_VERSION, target_pwr.version);
return err;
}
printf("Maximum Tx Power Target (chanspec:0x%x):\t", target_pwr.chanspec);
for (i = 0; i < target_pwr.rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO(target_pwr.txpwr[i], 4),
DIV_REM(target_pwr.txpwr[i], 4));
printf("\n");
return err;
}
/* print a single row of the power data.
convert data from dB to qdB;
decide if the pwr data is 20 or 40MHz data;
print "-" in the other channels
*/
static void
wl_txpwr_print_row(const char *label, uint8 chains, int8 pwr20,
int8 pwr20in40, int8 pwr40, int8 pwr80, int8 pwr20in80, int8 pwr40in80,
int8 unsupported_rate, int8 channel_bandwidth, bool verbose)
{
/* homebrew conversion to qdB, pseudo-floating point representation. */
int pwr20_q = DIV_QUO(pwr20, 4);
int pwr20_r = DIV_REM(pwr20, 4);
int pwr20in40_q = DIV_QUO(pwr20in40, 4);
int pwr20in40_r = DIV_REM(pwr20in40, 4);
int pwr40_q = DIV_QUO(pwr40, 4);
int pwr40_r = DIV_REM(pwr40, 4);
int pwr80_q = DIV_QUO(pwr80, 4);
int pwr80_r = DIV_REM(pwr80, 4);
int pwr20in80_q = DIV_QUO(pwr20in80, 4);
int pwr20in80_r = DIV_REM(pwr20in80, 4);
int pwr40in80_q = DIV_QUO(pwr40in80, 4);
int pwr40in80_r = DIV_REM(pwr40in80, 4);
char rate20[] = "- ";
char rate20in40[] = "- ";
char rate40[] = "- ";
char rate80[] = "- ";
char rate20in80[] = "- ";
char rate40in80[] = "- ";
char pad[] = " ";
if (pwr20 != unsupported_rate)
sprintf(rate20, "%2d.%-2d", pwr20_q, pwr20_r);
if (pwr20in40 != unsupported_rate)
sprintf(rate20in40, "%2d.%-2d", pwr20in40_q, pwr20in40_r);
if (pwr40 != unsupported_rate)
sprintf(rate40, "%2d.%-2d", pwr40_q, pwr40_r);
if (pwr80 != unsupported_rate)
sprintf(rate80, "%2d.%-2d", pwr80_q, pwr80_r);
if (pwr20in80 != unsupported_rate)
sprintf(rate20in80, "%2d.%-2d", pwr20in80_q, pwr20in80_r);
if (pwr40in80 != unsupported_rate)
sprintf(rate40in80, "%2d.%-2d", pwr40in80_q, pwr40in80_r);
printf("%-23s%d ", label, chains);
if (!verbose) {
switch (channel_bandwidth) {
case WL_BW_20MHZ:
printf("%s\n", rate20);
break;
case WL_BW_40MHZ:
printf("%s%s%s\n", rate20in40, pad, rate40);
break;
case WL_BW_80MHZ:
printf("%s%s%s%s%s\n", rate20in80, pad, rate40in80, pad, rate80);
break;
}
} else {
printf("%s%s%s%s%s%s%s%s%s%s%s\n", rate20, pad, rate20in40, pad,
rate40, pad, rate20in80, pad, rate40in80, pad, rate80);
}
}
static void
wl_txpwr_array_row_print(ppr_t *pprptr, int8 channel_bandwidth, reg_rate_index_t rate_index)
{
const char *label;
int8 pwr20 = WL_RATE_DISABLED;
int8 pwr20in40 = WL_RATE_DISABLED;
int8 pwr40 = WL_RATE_DISABLED;
int8 pwr80 = WL_RATE_DISABLED;
int8 pwr20in80 = WL_RATE_DISABLED;
int8 pwr40in80 = WL_RATE_DISABLED;
if (rate_index == NO_RATE)
{
printf("(NO_RATE) - - - - - - -\n");
}
else
{
clm_rate_group_id_t group_id = ppr_table[rate_index].id;
label = ppr_table[rate_index].label;
switch (channel_bandwidth) {
case WL_BW_20MHZ:
pwr20 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20);
break;
case WL_BW_40MHZ:
pwr20in40 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN40);
pwr40 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40);
break;
case WL_BW_80MHZ:
pwr80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_80);
pwr20in80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN80);
pwr40in80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40IN80);
break;
}
wl_txpwr_print_row(label, ppr_group_table[group_id].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80, pwr40in80,
WL_RATE_DISABLED, channel_bandwidth, TRUE);
}
}
static void
wl_txpwr_print_header(int8 channel_bandwidth, bool verbose)
{
printf("Rate Chains ");
if (!verbose)
{
switch (channel_bandwidth) {
case WL_BW_20MHZ:
printf("20MHz\n");
break;
case WL_BW_40MHZ:
printf("20in40 40MHz\n");
break;
case WL_BW_80MHZ:
printf("20in80 40in80 80MHz\n");
break;
}
} else {
printf("20MHz 20in40 40MHz 20in80 40in80 80MHz\n");
}
}
static void
wl_txpwr_array_print(ppr_t* pprptr, int8 bw, bool verbose, bool is5G)
{
clm_rate_group_id_t i;
reg_rate_index_t rate_index = DSSS1;
wl_txpwr_print_header(bw, verbose);
for (i = RATE_GROUP_ID_DSSS; i < RATE_GROUP_ID_COUNT; i++) {
wl_txpwr_ppr_print(pprptr, verbose, ppr_group_table[i].rate_type, i, bw,
&rate_index, is5G);
/* VHT rates are printed in two parts: MCS + VHT8,9 */
if (ppr_group_table[i].rate_type == PPR_RATE_VHT)
i++;
}
}
/* print power value for each group of rates, if not in verbose mode and rates
* are uniform, one power value is printed out for the whole group
*/
static void
wl_txpwr_ppr_print(ppr_t* pprptr, int vb, ppr_rate_type_t type,
clm_rate_group_id_t gid, int8 bw, reg_rate_index_t *rate_index, bool is5G)
{
uint i;
const char *label;
int buniform = FALSE;
int8 pwr20 = WL_RATE_DISABLED;
int8 pwr20in40 = WL_RATE_DISABLED;
int8 pwr40 = WL_RATE_DISABLED;
int8 pwr80 = WL_RATE_DISABLED;
int8 pwr20in80 = WL_RATE_DISABLED;
int8 pwr40in80 = WL_RATE_DISABLED;
uint len = 0;
if (pprptr == NULL) {
fprintf(stderr, "illegal ppr data!\n");
return;
}
switch (type) {
case PPR_RATE_DSSS:
{
int tx_pwr_min = WL_RATE_DISABLED;
if (is5G) {
tx_pwr_min = ppr_get_min(pprptr, WL_RATE_DISABLED);
}
len = sizeof(ppr_dsss_rateset_t);
if (bw == WL_BW_20MHZ) {
ppr_dsss_rateset_t rate;
ppr_get_dsss(pprptr, WL_TX_BW_20, ppr_group_table[gid].chain,
&rate);
if (is5G) {
uint idx;
for (idx = 0; idx < sizeof(rate); idx++) {
if (rate.pwr[idx] == tx_pwr_min)
rate.pwr[idx] = WL_RATE_DISABLED;
}
}
buniform = wl_array_check_val(rate.pwr,
sizeof(rate), rate.pwr[0]) && !vb;
if (buniform) {
pwr20 = rate.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20 = rate.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_40MHZ) {
ppr_dsss_rateset_t rate20in40;
ppr_dsss_rateset_t rate40;
ppr_get_dsss(pprptr, WL_TX_BW_20IN40, ppr_group_table[gid].chain,
&rate20in40);
ppr_get_dsss(pprptr, WL_TX_BW_40, ppr_group_table[gid].chain,
&rate40);
if (is5G) {
uint idx;
for (idx = 0; idx < sizeof(ppr_dsss_rateset_t); idx++) {
if (rate20in40.pwr[idx] == tx_pwr_min)
rate20in40.pwr[idx] = WL_RATE_DISABLED;
if (rate40.pwr[idx] == tx_pwr_min)
rate40.pwr[idx] = WL_RATE_DISABLED;
}
}
buniform = wl_array_check_val(rate40.pwr,
sizeof(ppr_dsss_rateset_t), rate40.pwr[0]) && !vb &&
wl_array_check_val(rate20in40.pwr,
sizeof(ppr_dsss_rateset_t), rate20in40.pwr[0]);
if (buniform) {
pwr20in40 = rate20in40.pwr[0];
pwr40 = rate40.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in40 = rate20in40.pwr[i];
pwr40 = rate40.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_80MHZ) {
ppr_dsss_rateset_t rate20in80;
ppr_dsss_rateset_t rate40in80;
ppr_dsss_rateset_t rate80;
ppr_get_dsss(pprptr, WL_TX_BW_20IN80, ppr_group_table[gid].chain,
&rate20in80);
ppr_get_dsss(pprptr, WL_TX_BW_40IN80, ppr_group_table[gid].chain,
&rate40in80);
ppr_get_dsss(pprptr, WL_TX_BW_80, ppr_group_table[gid].chain,
&rate80);
if (is5G) {
uint idx;
for (idx = 0; idx < sizeof(ppr_dsss_rateset_t); idx++) {
if (rate20in80.pwr[idx] == tx_pwr_min)
rate20in80.pwr[idx] = WL_RATE_DISABLED;
if (rate40in80.pwr[idx] == tx_pwr_min)
rate40in80.pwr[idx] = WL_RATE_DISABLED;
if (rate80.pwr[idx] == tx_pwr_min)
rate80.pwr[idx] = WL_RATE_DISABLED;
}
}
buniform = wl_array_check_val(rate80.pwr,
sizeof(ppr_dsss_rateset_t), rate80.pwr[0]) && !vb &&
wl_array_check_val(rate20in80.pwr,
sizeof(ppr_dsss_rateset_t), rate20in80.pwr[0]) &&
wl_array_check_val(rate40in80.pwr,
sizeof(ppr_dsss_rateset_t), rate40in80.pwr[0]);
if (buniform) {
pwr20in80 = rate20in80.pwr[0];
pwr40in80 = rate40in80.pwr[0];
pwr80 = rate80.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in80 = rate20in80.pwr[i];
pwr40in80 = rate40in80.pwr[i];
pwr80 = rate80.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
}
*rate_index += len;
}
break;
case PPR_RATE_OFDM:
{
len = sizeof(ppr_ofdm_rateset_t);
if (bw == WL_BW_20MHZ) {
ppr_ofdm_rateset_t rate;
ppr_get_ofdm(pprptr, WL_TX_BW_20, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate);
buniform = wl_array_check_val(rate.pwr,
sizeof(rate), rate.pwr[0]) && !vb;
if (buniform) {
pwr20 = rate.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20 = rate.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_40MHZ) {
ppr_ofdm_rateset_t rate20in40;
ppr_ofdm_rateset_t rate40;
ppr_get_ofdm(pprptr, WL_TX_BW_20IN40, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate20in40);
ppr_get_ofdm(pprptr, WL_TX_BW_40, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate40);
buniform = wl_array_check_val(rate40.pwr,
sizeof(ppr_ofdm_rateset_t), rate40.pwr[0]) && !vb &&
wl_array_check_val(rate20in40.pwr,
sizeof(ppr_ofdm_rateset_t), rate20in40.pwr[0]);
if (buniform) {
pwr20in40 = rate20in40.pwr[0];
pwr40 = rate40.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in40 = rate20in40.pwr[i];
pwr40 = rate40.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_80MHZ) {
ppr_ofdm_rateset_t rate20in80;
ppr_ofdm_rateset_t rate40in80;
ppr_ofdm_rateset_t rate80;
ppr_get_ofdm(pprptr, WL_TX_BW_20IN80, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate20in80);
ppr_get_ofdm(pprptr, WL_TX_BW_40IN80, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate40in80);
ppr_get_ofdm(pprptr, WL_TX_BW_80, ppr_group_table[gid].mode,
ppr_group_table[gid].chain, &rate80);
buniform = wl_array_check_val(rate80.pwr,
sizeof(ppr_ofdm_rateset_t), rate80.pwr[0]) && !vb &&
wl_array_check_val(rate20in80.pwr,
sizeof(ppr_ofdm_rateset_t), rate20in80.pwr[0]) &&
wl_array_check_val(rate40in80.pwr,
sizeof(ppr_ofdm_rateset_t), rate40in80.pwr[0]);
if (buniform) {
pwr20in80 = rate20in80.pwr[0];
pwr40in80 = rate40in80.pwr[0];
pwr80 = rate80.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in80 = rate20in80.pwr[i];
pwr40in80 = rate40in80.pwr[i];
pwr80 = rate80.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
}
*rate_index += len;
}
break;
case PPR_RATE_HT:
{
len = sizeof(ppr_ht_mcs_rateset_t);
if (bw == WL_BW_20MHZ) {
ppr_ht_mcs_rateset_t rate;
ppr_get_ht_mcs(pprptr, WL_TX_BW_20, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate);
buniform = wl_array_check_val(rate.pwr,
sizeof(rate), rate.pwr[0]) && !vb;
if (buniform) {
pwr20 = rate.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20 = rate.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_40MHZ) {
ppr_ht_mcs_rateset_t rate20in40;
ppr_ht_mcs_rateset_t rate40;
ppr_get_ht_mcs(pprptr, WL_TX_BW_20IN40, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate20in40);
ppr_get_ht_mcs(pprptr, WL_TX_BW_40, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate40);
buniform = wl_array_check_val(rate40.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate40.pwr[0]) && !vb &&
wl_array_check_val(rate20in40.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate20in40.pwr[0]);
if (buniform) {
pwr20in40 = rate20in40.pwr[0];
pwr40 = rate40.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in40 = rate20in40.pwr[i];
pwr40 = rate40.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_80MHZ) {
ppr_ht_mcs_rateset_t rate20in80;
ppr_ht_mcs_rateset_t rate40in80;
ppr_ht_mcs_rateset_t rate80;
ppr_get_ht_mcs(pprptr, WL_TX_BW_20IN80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate20in80);
ppr_get_ht_mcs(pprptr, WL_TX_BW_40IN80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate40in80);
ppr_get_ht_mcs(pprptr, WL_TX_BW_80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate80);
buniform = wl_array_check_val(rate80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate80.pwr[0]) && !vb &&
wl_array_check_val(rate20in80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate20in80.pwr[0]) &&
wl_array_check_val(rate40in80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate40in80.pwr[0]);
if (buniform) {
pwr20in80 = rate20in80.pwr[0];
pwr40in80 = rate40in80.pwr[0];
pwr80 = rate80.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < len; i++) {
pwr20in80 = rate20in80.pwr[i];
pwr40in80 = rate40in80.pwr[i];
pwr80 = rate80.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
}
*rate_index += len;
}
break;
case PPR_RATE_VHT:
{
uint bvhtuniform = FALSE;
uint lenht = sizeof(ppr_ht_mcs_rateset_t);
len = sizeof(ppr_vht_mcs_rateset_t);
/* VHT RATES are printed as two parts: MCS0-7 and VTH8-9 */
if (bw == WL_BW_20MHZ) {
ppr_vht_mcs_rateset_t rate;
ppr_get_vht_mcs(pprptr, WL_TX_BW_20, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate);
buniform = wl_array_check_val(rate.pwr, lenht, rate.pwr[0])&& !vb;
bvhtuniform = wl_array_check_val(&rate.pwr[lenht], len-lenht,
rate.pwr[lenht]) && !vb;
if (buniform) {
pwr20 = rate.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < lenht; i++) {
pwr20 = rate.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
if (bvhtuniform) {
pwr20 = rate.pwr[lenht];
/* print VHT8-9 label */
label = get_clm_rate_group_label(gid+1);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = lenht; i < len; i++) {
pwr20 = rate.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_40MHZ) {
ppr_vht_mcs_rateset_t rate20in40;
ppr_vht_mcs_rateset_t rate40;
ppr_get_vht_mcs(pprptr, WL_TX_BW_20IN40, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate20in40);
ppr_get_vht_mcs(pprptr, WL_TX_BW_40, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate40);
buniform = wl_array_check_val(rate40.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate40.pwr[0]) && !vb &&
wl_array_check_val(rate20in40.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate20in40.pwr[0]);
bvhtuniform = wl_array_check_val(&rate40.pwr[lenht], len-lenht,
rate40.pwr[lenht]) && !vb &&
wl_array_check_val(&rate20in40.pwr[lenht], len-lenht,
rate20in40.pwr[lenht]);
if (buniform) {
pwr20in40 = rate20in40.pwr[0];
pwr40 = rate40.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < lenht; i++) {
pwr20in40 = rate20in40.pwr[i];
pwr40 = rate40.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
if (bvhtuniform) {
pwr20in40 = rate20in40.pwr[lenht];
pwr40 = rate40.pwr[lenht];
/* print VHT8-9 label */
label = get_clm_rate_group_label(gid+1);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = lenht; i < len; i++) {
pwr20in40 = rate20in40.pwr[i];
pwr40 = rate40.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
} else if (bw == WL_BW_80MHZ) {
ppr_vht_mcs_rateset_t rate20in80;
ppr_vht_mcs_rateset_t rate40in80;
ppr_vht_mcs_rateset_t rate80;
ppr_get_vht_mcs(pprptr, WL_TX_BW_20IN80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate20in80);
ppr_get_vht_mcs(pprptr, WL_TX_BW_40IN80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate40in80);
ppr_get_vht_mcs(pprptr, WL_TX_BW_80, ppr_group_table[gid].nss,
ppr_group_table[gid].mode, ppr_group_table[gid].chain,
&rate80);
buniform = wl_array_check_val(rate80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate80.pwr[0]) && !vb &&
wl_array_check_val(rate20in80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate20in80.pwr[0]) &&
wl_array_check_val(rate40in80.pwr,
sizeof(ppr_ht_mcs_rateset_t), rate40in80.pwr[0]);
bvhtuniform = wl_array_check_val(&rate80.pwr[lenht], len-lenht,
rate80.pwr[lenht]) && !vb &&
wl_array_check_val(&rate20in80.pwr[lenht], len-lenht,
rate20in80.pwr[lenht]) &&
wl_array_check_val(&rate40in80.pwr[lenht], len-lenht,
rate40in80.pwr[lenht]);
if (buniform) {
pwr20in80 = rate20in80.pwr[0];
pwr40in80 = rate40in80.pwr[0];
pwr80 = rate80.pwr[0];
label = get_clm_rate_group_label(gid);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = 0; i < lenht; i++) {
pwr20in80 = rate20in80.pwr[i];
pwr40in80 = rate40in80.pwr[i];
pwr80 = rate80.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
if (bvhtuniform) {
pwr20in80 = rate20in80.pwr[lenht];
pwr40in80 = rate40in80.pwr[lenht];
pwr80 = rate80.pwr[lenht];
/* print VHT8-9 label */
label = get_clm_rate_group_label(gid+1);
wl_txpwr_print_row(label, ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED, bw, vb);
} else {
for (i = lenht; i < len; i++) {
pwr20in80 = rate20in80.pwr[i];
pwr40in80 = rate40in80.pwr[i];
pwr80 = rate80.pwr[i];
label = ppr_table[*rate_index+i].label;
wl_txpwr_print_row(label,
ppr_group_table[gid].chain,
pwr20, pwr20in40, pwr40, pwr80, pwr20in80,
pwr40in80, WL_RATE_DISABLED,
bw, vb);
}
}
}
*rate_index += len;
}
break;
}
}
/* helper function to check if the array are uniformly same as the input value */
static int wl_array_check_val(int8 *pwr, uint count, int8 val)
{
uint i;
for (i = 0; i < count; i++) {
if (pwr[i] != val)
return FALSE;
}
return TRUE;
}
static void
wl_txpwr_regulatory_array_row_print(int8 *powers, int8 *powers_subchan1,
int8 *powers_subchan2, int8 channel_bandwidth, reg_rate_index_t rate_index)
{
const int RATE_UNSUPPORTED = -128; /* CLM uses -128 as the unsupported rate */
const char* label;
uint8 chains;
int pwr20 = RATE_UNSUPPORTED;
int pwr20in40 = RATE_UNSUPPORTED;
int pwr40 = RATE_UNSUPPORTED;
int pwr80 = RATE_UNSUPPORTED;
int pwr20in80 = RATE_UNSUPPORTED;
int pwr40in80 = RATE_UNSUPPORTED;
wl_txpwr_print_header(channel_bandwidth, TRUE);
if (rate_index == NO_RATE)
{
printf("(NO_RATE) - - - - - - -\n");
}
else
{
label = ppr_table[rate_index].label;
chains = ppr_group_table[ppr_table[rate_index].id].chain;
switch (channel_bandwidth) {
case WL_BW_20MHZ:
pwr20 = powers[ppr_table[rate_index].rate];
break;
case WL_BW_40MHZ:
pwr40 = powers[ppr_table[rate_index].rate];
pwr20in40 = powers_subchan1[ppr_table[rate_index].rate];
break;
case WL_BW_80MHZ:
pwr80 = powers[ppr_table[rate_index].rate];
pwr40in80 = powers_subchan1[ppr_table[rate_index].rate];
pwr20in80 = powers_subchan2[ppr_table[rate_index].rate];
break;
}
wl_txpwr_print_row(label, chains, pwr20, pwr20in40,
pwr40, pwr80, pwr20in80, pwr40in80, RATE_UNSUPPORTED,
channel_bandwidth, TRUE);
}
}
/* Essentially the same as wl_txpwr_array_print, but the
* format of the regulatory data is now diffreent enough that we need a separate function.
* The data format should be unified, and this code removed.
*/
static void
wl_txpwr_regulatory_array_print(int8 *powers, int8 *powers_subchan1,
int8 *powers_subchan2, int8 channel_bandwidth, bool verbose)
{
int i, j;
const int RATE_UNSUPPORTED = -128; /* CLM uses -128 as the unsupported rate */
const char* label;
/* This code assumes that all of the rates in a group will be contiguous */
wl_txpwr_print_header(channel_bandwidth, verbose);
for (i = 0; i < RATE_GROUP_ID_COUNT; i++) {
int first_rate = 0;
int num_rates = 0;
uint8 chains;
bool compress_rate = FALSE;
int pwr20 = RATE_UNSUPPORTED;
int pwr20in40 = RATE_UNSUPPORTED;
int pwr40 = RATE_UNSUPPORTED;
int pwr80 = RATE_UNSUPPORTED;
int pwr20in80 = RATE_UNSUPPORTED;
int pwr40in80 = RATE_UNSUPPORTED;
/* find the first rate index in this group. */
while ((int)ppr_table[first_rate].id != i)
first_rate++;
/* how many rates in that group? */
while ((first_rate + num_rates < WL_NUMRATES) &&
((int)ppr_table[first_rate + num_rates].id == i))
num_rates++;
/* are the regulatory power settings the same? */
switch (channel_bandwidth) {
case WL_BW_20MHZ:
compress_rate = !verbose &&
wl_array_uniform((uint8*)&powers[ppr_table[first_rate].rate], 0,
num_rates);
break;
case WL_BW_40MHZ:
compress_rate = !verbose &&
wl_array_uniform((uint8*)&powers[ppr_table[first_rate].rate], 0,
num_rates) &&
wl_array_uniform((uint8*)&powers_subchan1[ppr_table
[first_rate].rate], 0, num_rates);
break;
case WL_BW_80MHZ:
compress_rate = !verbose &&
wl_array_uniform((uint8*)&powers[ppr_table[first_rate].rate], 0,
num_rates) &&
wl_array_uniform((uint8*)&powers_subchan1[ppr_table
[first_rate].rate], 0, num_rates) &&
wl_array_uniform((uint8*)&powers_subchan2[ppr_table
[first_rate].rate], 0, num_rates);
break;
}
if (compress_rate)
j = num_rates - 1;
else
j = 0;
for (; j < num_rates; j++) {
if (compress_rate)
label = get_clm_rate_group_label(i);
else
label = ppr_table[first_rate + j].label;
chains = ppr_group_table[ppr_table[first_rate + j].id].chain;
switch (channel_bandwidth) {
case WL_BW_20MHZ:
pwr20 = powers[ppr_table[first_rate + j].rate];
break;
case WL_BW_40MHZ:
pwr40 = powers[ppr_table[first_rate + j].rate];
pwr20in40 = powers_subchan1[ppr_table[first_rate + j].rate];
break;
case WL_BW_80MHZ:
pwr80 = powers[ppr_table[first_rate + j].rate];
pwr40in80 = powers_subchan1[ppr_table[first_rate + j].rate];
pwr20in80 = powers_subchan2[ppr_table[first_rate + j].rate];
break;
}
wl_txpwr_print_row(label, chains, pwr20, pwr20in40,
pwr40, pwr80, pwr20in80, pwr40in80, RATE_UNSUPPORTED,
channel_bandwidth, verbose);
}
}
}
/* return TRUE if all the values in the array are uniformly the same */
static int
wl_array_uniform(uint8 *pwr, int start, int count)
{
int i, rate;
for (i = 1, rate = start + 1; i < count; i++, rate++)
if (pwr[rate] != pwr[rate - 1])
return FALSE;
return TRUE;
}
static int
wl_get_instant_power(void *wl, cmd_t *cmd, char **argv)
{
int ret;
tx_inst_power_t *power;
uint band_list[3];
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
strcpy(buf, "txinstpwr");
if ((ret = wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_MAXLEN)) < 0) {
return ret;
}
power = (tx_inst_power_t *)buf;
/* Make the most of the info returned in band_list!
* b/g and a
* b/g-uni
* a-uni
* NOTE: NO a and b/g case ...
*/
if ((ret = wlu_get(wl, WLC_GET_BANDLIST, band_list, sizeof(band_list))) < 0)
return (ret);
band_list[0] = dtoh32(band_list[0]);
band_list[1] = dtoh32(band_list[1]);
band_list[2] = dtoh32(band_list[2]);
/* If B/G is present it's always the lower index */
if (band_list[1] == WLC_BAND_2G) {
printf("Last B phy CCK est. power:\t%2d.%d dBm\n",
DIV_QUO(power->txpwr_est_Pout[0], 4),
DIV_REM(power->txpwr_est_Pout[0], 4));
printf("Last B phy OFDM est. power:\t%2d.%d dBm\n",
DIV_QUO(power->txpwr_est_Pout_gofdm, 4),
DIV_REM(power->txpwr_est_Pout_gofdm, 4));
printf("\n");
}
/* A band */
if (band_list[1] == WLC_BAND_5G || (band_list[0] > 1 && band_list[2] == WLC_BAND_5G)) {
printf("Last A phy est. power:\t\t%2d.%d dBm\n",
DIV_QUO(power->txpwr_est_Pout[1], 4),
DIV_REM(power->txpwr_est_Pout[1], 4));
}
return ret;
}
static int
wl_evm(void *wl, cmd_t *cmd, char **argv)
{
int val[3];
/* Get channel */
if (!*++argv) {
fprintf(stderr, "Need to specify at least one parameter\n");
return BCME_USAGE_ERROR;
}
if (!stricmp(*argv, "off"))
val[0] = 0;
else
val[0] = atoi(*argv);
/* set optional parameters to default */
val[1] = 4; /* rate in 500Kb units */
val[2] = 0; /* This is ignored */
/* Get optional rate and convert to 500Kb units */
if (*++argv)
val[1] = rate_string2int(*argv);
val[0] = htod32(val[0]);
val[1] = htod32(val[1]);
val[2] = htod32(val[2]);
return wlu_set(wl, cmd->set, val, sizeof(val));
}
#define WL_JOIN_PARAMS_MAX WL_JOIN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(chanspec_t)
/* when prescanned option is specified */
static int
wl_join_prescanned(void *wl, wl_join_params_t *join_params, uint *join_params_size)
{
/* load with prescanned channels and bssids */
int ret, err = 0;
char *destbuf;
wl_scan_results_t *scanres = NULL;
wl_bss_info_t *bi;
uint i, cnt, bssid_cnt, bi_len;
if ((destbuf = malloc(WL_DUMP_BUF_LEN)) == NULL) {
fprintf(stderr, "Failed to allocate %d-byte buffer for scanresults\n",
WL_DUMP_BUF_LEN);
err = BCME_NOMEM;
goto pexit;
}
if ((ret = wl_get_scan(wl, WLC_SCAN_RESULTS, destbuf, WL_DUMP_BUF_LEN)) != 0) {
fprintf(stderr, "failed to fetch scan results, err %d\n", ret);
err = ret;
goto pexit;
}
scanres = (wl_scan_results_t *)destbuf;
if (scanres->version != WL_BSS_INFO_VERSION) {
fprintf(stderr, "scan parsing failed (expect version %d, got %d)\n",
WL_BSS_INFO_VERSION, scanres->version);
err = -1;
goto pexit;
}
/* find matching ssids to fill the channel list */
for (cnt = i = 0, bi = scanres->bss_info; i < scanres->count;
i++, bi = (wl_bss_info_t*)((int8*)bi + bi_len)) {
bi_len = dtoh32(bi->length);
if ((bi->SSID_len != join_params->ssid.SSID_len) ||
memcmp(bi->SSID, join_params->ssid.SSID,
join_params->ssid.SSID_len)) {
continue;
} else {
dump_bss_info(bi);
printf("--------------------------------\n");
}
memcpy(&join_params->params.chanspec_list[cnt],
&bi->chanspec, sizeof(chanspec_t));
cnt++;
}
bssid_cnt = (uint16)cnt;
/* append the corresponding bssids */
destbuf = (char*)&join_params->params.chanspec_list[cnt];
*join_params_size = destbuf - (char*)join_params;
*join_params_size += (cnt * sizeof(struct ether_addr));
if (*join_params_size > WL_JOIN_PARAMS_MAX) {
fprintf(stderr, "Can't fit bssids for all %d APs found\n", cnt);
err = -1;
goto pexit;
}
for (cnt = i = 0, bi = scanres->bss_info;
(i < scanres->count) && (cnt < bssid_cnt);
i++, bi = (wl_bss_info_t*)((int8*)bi + bi_len)) {
bi_len = dtoh32(bi->length);
if ((bi->SSID_len != join_params->ssid.SSID_len) ||
memcmp(bi->SSID, join_params->ssid.SSID,
join_params->ssid.SSID_len)) {
continue;
}
memcpy(destbuf, &bi->BSSID, sizeof(struct ether_addr));
destbuf += sizeof(struct ether_addr);
cnt++;
}
if (cnt != bssid_cnt) {
fprintf(stderr, "Mismatched channel and bssid counts!\n");
err = -1;
goto pexit;
}
if (cnt == 0) {
printf("No matches found, issuing normal join.\n");
} else {
printf("Passing %d channel/bssid pairs.\n", cnt);
}
join_params->params.bssid_cnt = htod16(bssid_cnt);
pexit:
if (scanres)
free((char*)scanres);
else
free(destbuf);
return err;
}
/* wl join <ssid> [key <0-3>:xxxxx]
* [imode bss|ibss]
* [amode open|shared|openshared|wpa|wpapsk|wpa2|wpa2psk|wpanone|ftpsk]
* [options]
* Options:
* -b MAC, --bssid=MAC, where MAC is in xx:xx:xx:xx:xx:xx format
* -c CL, --chanspecs=CL, where CL is a comma or space separated list of chanspecs
* -p, -passive: uses join iovar instead of SET_SSID ioctl to force passive assoc scan
*/
static int
wl_join(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_OK, idx = 0;
wl_join_params_t *join_params;
uint join_params_size;
wl_wsec_key_t key;
int wsec = 0, auth = 0, infra = 1;
int wpa_auth = WPA_AUTH_DISABLED;
char* cmd_name;
bool prescanned = FALSE;
int passive = 0;
UNUSED_PARAMETER(cmd);
cmd_name = *argv++;
/* allocate the max storage */
join_params_size = WL_JOIN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(chanspec_t);
if ((join_params = malloc(join_params_size)) == NULL) {
fprintf(stderr, "Error allocating %d bytes for assoc params\n", join_params_size);
return BCME_NOMEM;
}
memset(join_params, 0, join_params_size);
memcpy(&join_params->params.bssid, &ether_bcast, ETHER_ADDR_LEN);
/* verify that SSID was specified and is a valid length */
if (!*argv || (strlen(*argv) > DOT11_MAX_SSID_LEN)) {
ret = BCME_USAGE_ERROR;
goto exit;
}
join_params->ssid.SSID_len = strlen(*argv);
memcpy(join_params->ssid.SSID, *argv, join_params->ssid.SSID_len);
/* default to plain old ioctl */
join_params_size = sizeof(wlc_ssid_t);
/* get current wsec */
if (wlu_iovar_getint(wl, "wsec", &wsec) < 0)
wsec = 0;
while (*++argv) {
if (!stricmp(*argv, "wepkey") || !stricmp(*argv, "wep") || !stricmp(*argv, "key")) {
/* specified wep key */
memset(&key, 0, sizeof(key));
if (!*++argv) {
ret = BCME_USAGE_ERROR;
goto exit;
}
/* WEP index specified */
if (*(argv[0]+1) == ':') {
idx = *argv[0] - 0x30;
if (idx < 0 || idx > 3) {
fprintf(stderr, "Invalid key index %d specified\n", idx);
ret = BCME_BADARG;
goto exit;
}
argv[0] += 2; /* colon + digit */
}
key.index = idx;
if (parse_wep(argv, &key, FALSE)) {
ret = BCME_BADARG;
goto exit;
}
key.index = htod32(key.index);
key.len = htod32(key.len);
key.algo = htod32(key.algo);
key.flags = htod32(key.flags);
if ((ret = wlu_set(wl, WLC_SET_KEY, &key, sizeof(wl_wsec_key_t))) < 0) {
goto exit;
}
wsec |= WEP_ENABLED;
}
/* specified infrastructure mode */
else if (!stricmp(*argv, "imode") ||
!stricmp(*argv, "infra") ||
!stricmp(*argv, "mode")) {
if (!*++argv) {
fprintf(stderr, "%s %s: expected argument after \"infra\" keyword "
"but command line ended.\n", wlu_av0, cmd_name);
ret = BCME_USAGE_ERROR;
goto exit;
} else if (!stricmp(*argv, "ibss") ||
!stricmp(*argv, "adhoc") ||
!stricmp(*argv, "ad-hoc")) {
infra = 0;
} else if (!stricmp(*argv, "bss") ||
!stricmp(*argv, "managed") ||
!strnicmp(*argv, "infra", 5)) {
infra = 1;
} else {
fprintf(stderr, "%s %s: unrecongnized parameter \"%s\" after "
"\"infra\" keyword\n", wlu_av0, cmd_name, *argv);
ret = BCME_USAGE_ERROR;
goto exit;
}
}
/* specified authentication mode */
else if (!stricmp(*argv, "amode") || !strnicmp(*argv, "auth", 4)) {
if (!*++argv) {
ret = BCME_USAGE_ERROR;
goto exit;
}
if (!stricmp(*argv, "open"))
auth = WL_AUTH_OPEN_SYSTEM;
else if (!stricmp(*argv, "shared"))
auth = WL_AUTH_SHARED_KEY;
else if (!stricmp(*argv, "openshared"))
auth = WL_AUTH_OPEN_SHARED;
#ifdef BCMCCX
else if (!stricmp(*argv, "cckm"))
wpa_auth = WPA_AUTH_CCKM;
else if (!stricmp(*argv, "cckmwpa2"))
wpa_auth = WPA2_AUTH_CCKM;
#endif /* BCMCCX */
else if (!stricmp(*argv, "wpanone"))
wpa_auth = WPA_AUTH_NONE;
else if (!stricmp(*argv, "wpa"))
wpa_auth = WPA_AUTH_UNSPECIFIED;
else if (!stricmp(*argv, "wpapsk"))
wpa_auth = WPA_AUTH_PSK;
else if (!stricmp(*argv, "wpa2"))
wpa_auth = WPA2_AUTH_UNSPECIFIED;
else if (!stricmp(*argv, "wpa2psk"))
wpa_auth = WPA2_AUTH_PSK;
#ifdef BCMWAPI_WAI
else if (!stricmp(*argv, "wapi"))
wpa_auth = WAPI_AUTH_UNSPECIFIED;
else if (!stricmp(*argv, "wapipsk"))
wpa_auth = WAPI_AUTH_PSK;
#endif /* BCMWAPI_WAI */
else if (!stricmp(*argv, "ftpsk"))
wpa_auth = WPA2_AUTH_PSK | WPA2_AUTH_FT;
else {
ret = BCME_USAGE_ERROR;
goto exit;
}
}
else if (!stricmp(*argv, "-passive") || !stricmp(*argv, "-p")) {
/* Use extended join iovar to assoc_scan passively */
passive = 1;
}
/* optional assoc params */
else if ((ret = wl_parse_assoc_params(argv, &join_params->params, &prescanned)) ==
BCME_OK) {
join_params_size = WL_JOIN_PARAMS_FIXED_SIZE +
dtoh32(join_params->params.chanspec_num) * sizeof(chanspec_t);
break;
}
else {
fprintf(stderr, "%s %s: unable to parse parameter \"%s\"\n",
wlu_av0, cmd_name, *argv);
goto exit;
}
}
/* set infrastructure mode */
infra = htod32(infra);
if ((ret = wlu_set(wl, WLC_SET_INFRA, &infra, sizeof(int))) < 0)
goto exit;
/* set authentication mode */
auth = htod32(auth);
if ((ret = wlu_set(wl, WLC_SET_AUTH, &auth, sizeof(int))) < 0)
goto exit;
/* set wsec mode */
if ((ret = wlu_iovar_setint(wl, "wsec", wsec)) < 0)
goto exit;
/* set WPA_auth mode */
wpa_auth = htod32(wpa_auth);
if ((ret = wlu_set(wl, WLC_SET_WPA_AUTH, &wpa_auth, sizeof(wpa_auth))) < 0)
goto exit;
if (passive) {
wl_extjoin_params_t *extjoin_params;
int extjoin_size;
int i;
printf("Using passive assoc scan\n");
extjoin_size = WL_EXTJOIN_PARAMS_FIXED_SIZE +
join_params->params.chanspec_num * sizeof(chanspec_t);
if ((extjoin_params = malloc(extjoin_size)) == NULL) {
fprintf(stderr, "Error allocating %d bytes for extjoin \n", extjoin_size);
ret = BCME_NOMEM;
goto exit;
}
/* Copy assoc params from legacy struct into extended struct */
memset(extjoin_params, 0, extjoin_size);
memcpy(&extjoin_params->ssid.SSID, &join_params->ssid.SSID, DOT11_MAX_SSID_LEN);
extjoin_params->ssid.SSID_len = htod32(join_params->ssid.SSID_len);
memcpy(&extjoin_params->assoc.bssid, &join_params->params.bssid, ETHER_ADDR_LEN);
extjoin_params->assoc.chanspec_num = join_params->params.chanspec_num;
for (i = 0; i < join_params->params.chanspec_num; i++) {
extjoin_params->assoc.chanspec_list[i] =
join_params->params.chanspec_list[i];
}
extjoin_params->scan.scan_type = WL_SCANFLAGS_PASSIVE;
extjoin_params->scan.nprobes = -1;
extjoin_params->scan.active_time = -1;
extjoin_params->scan.passive_time = -1;
extjoin_params->scan.home_time = -1;
ret = wlu_var_setbuf(wl, "join", extjoin_params, extjoin_size);
free(extjoin_params);
} else {
/* join parameters starts with the ssid */
join_params->ssid.SSID_len = htod32(join_params->ssid.SSID_len);
if (prescanned) {
if ((ret = wl_join_prescanned(wl, join_params, &join_params_size)) < 0)
goto exit;
}
/* set ssid with extend assoc params (if any) */
join_params->ssid.SSID_len = htod32(join_params->ssid.SSID_len);
ret = wlu_set(wl, WLC_SET_SSID, join_params, join_params_size);
}
exit:
free(join_params);
return ret;
}
/* Set or Get the "bssid" iovar, with an optional config index argument:
* wl bssid [-C N]|[--cfg=N] bssid
*
* Option:
* -C N
* --cfg=N
* --config=N
* --configuration=N
* specify the config index N
* If cfg index not given on a set, the WLC_SET_BSSID ioctl will be used
*/
static int
wl_bssid(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr ea;
int bsscfg_idx = 0;
int consumed;
int error;
UNUSED_PARAMETER(cmd);
argv++;
/* parse a bsscfg_idx option if present */
if ((error = wl_cfg_option(argv, "bssid", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
if (*argv == NULL) {
if (consumed == 0) {
/* no config index, use WLC_GET_BSSID on the interface */
error = wlu_get(wl, WLC_GET_BSSID, &ea, ETHER_ADDR_LEN);
} else {
/* use "bssid" iovar since a config option was given */
error = wlu_bssiovar_get(wl, "bssid", bsscfg_idx, &ea, ETHER_ADDR_LEN);
}
if (error < 0)
return error;
printf("%s\n", wl_ether_etoa(&ea));
} else {
if (!wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
if (consumed == 0) {
/* no config index given, use WLC_SET_BSSID */
error = wlu_set(wl, WLC_SET_BSSID, &ea, ETHER_ADDR_LEN);
} else {
/* use "bssid" iovar since a config option was given */
error = wl_bssiovar_set(wl, "bssid", bsscfg_idx, &ea, ETHER_ADDR_LEN);
}
}
return error;
}
/* Set or Get the "ssid" iovar, with an optional config index argument:
* wl ssid [-C N]|[--cfg=N] ssid
*
* Option:
* -C N
* --cfg=N
* --config=N
* --configuration=N
* specify the config index N
* If cfg index not given on a set, the WLC_SET_SSID ioctl will be used
*/
static int
wl_ssid(void *wl, cmd_t *cmd, char **argv)
{
char ssidbuf[SSID_FMT_BUF_LEN];
wlc_ssid_t ssid = { 0, {0} };
int bsscfg_idx = 0;
int consumed;
int error;
argv++;
/* parse a bsscfg_idx option if present */
if ((error = wl_cfg_option(argv, "ssid", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
if (*argv == NULL) {
if (consumed == 0) {
/* no config index, use WLC_GET_SSID on the interface */
if (cmd->get == WLC_GET_SSID)
error = wlu_get(wl, WLC_GET_SSID, &ssid, sizeof(ssid));
else
error = wlu_iovar_get(wl, cmd->name, &ssid, sizeof(ssid));
} else {
if (cmd->get == WLC_GET_SSID) {
/* use "ssid" iovar since a config option was given */
error = wlu_bssiovar_get(wl, "ssid", bsscfg_idx, &ssid,
sizeof(ssid));
} else {
error = wlu_bssiovar_get(wl, cmd->name, bsscfg_idx, &ssid,
sizeof(ssid));
}
}
if (error < 0)
return error;
ssid.SSID_len = dtoh32(ssid.SSID_len);
wl_format_ssid(ssidbuf, ssid.SSID, ssid.SSID_len);
printf("Current %s: \"%s\"\n",
(cmd->get == WLC_GET_SSID)? "SSID": cmd->name,
ssidbuf);
} else {
if (strlen(argv[0]) > DOT11_MAX_SSID_LEN) {
fprintf(stderr, "SSID arg \"%s\" must be 32 chars or less\n", argv[0]);
return BCME_BADARG;
}
ssid.SSID_len = strlen(argv[0]);
memcpy(ssid.SSID, argv[0], ssid.SSID_len);
wl_format_ssid(ssidbuf, ssid.SSID, ssid.SSID_len);
printf("Setting %s: \"%s\"\n", (cmd->set == WLC_SET_SSID)? "SSID": cmd->name,
ssidbuf);
ssid.SSID_len = htod32(ssid.SSID_len);
if (consumed == 0) {
/* no config index given, use WLC_SET_SSID */
if (cmd->set == WLC_SET_SSID) {
error = wlu_set(wl, WLC_SET_SSID, &ssid, sizeof(wlc_ssid_t));
} else {
error = wlu_iovar_set(wl, cmd->name, &ssid, sizeof(wlc_ssid_t));
}
} else {
if (cmd->set == WLC_SET_SSID) {
/* use "ssid" iovar since a config option was given */
error = wl_bssiovar_set(wl, "ssid", bsscfg_idx, &ssid,
sizeof(wlc_ssid_t));
} else
error = wl_bssiovar_set(wl, cmd->name, bsscfg_idx, &ssid,
sizeof(wlc_ssid_t));
}
}
return error;
}
static const char*
wl_smfs_map_type(uint8 type)
{
static const struct {uint8 type; char name[32];} type_names[] = {
{SMFS_TYPE_AUTH, "Authentication_Request"},
{SMFS_TYPE_ASSOC, "Association_Request"},
{SMFS_TYPE_REASSOC, "Reassociation_Request"},
{SMFS_TYPE_DISASSOC_TX, "Disassociation_Request_TX"},
{SMFS_TYPE_DISASSOC_RX, "Disassociation_Request_RX"},
{SMFS_TYPE_DEAUTH_TX, "Deauthentication_Request_TX"},
{SMFS_TYPE_DEAUTH_RX, "Deauthentication_Request_RX"}
};
const char *tname = "UNKNOWN";
uint i;
for (i = 0; i < ARRAYSIZE(type_names); i++) {
if (type_names[i].type == type)
tname = type_names[i].name;
}
return tname;
}
static int
wl_disp_smfs(char *inbuf)
{
static const char *codename[] = {"Status_code", "Reason_code"};
wl_smf_stats_t *smf_stats;
wl_smfs_elem_t *elemt = NULL;
const char *namebuf;
uint32 version;
int count;
smf_stats = (wl_smf_stats_t *) inbuf;
namebuf = wl_smfs_map_type(smf_stats->type);
version = dtoh32(smf_stats->version);
if (version != SMFS_VERSION) {
fprintf(stderr, "Sorry, your driver has smfs_version %d "
"but this program supports only version %d.\n",
version, SMFS_VERSION);
return -1;
}
printf("Frame type: %s\n", namebuf);
printf("\tIgnored Count: %d\n", dtoh32(smf_stats->ignored_cnt));
printf("\tMalformed Count: %d\n", dtoh32(smf_stats->malformed_cnt));
count = dtoh32(smf_stats->count_total);
if (count) {
namebuf = codename[dtoh32(smf_stats->codetype)];
printf("\tSuccessful/Failed Count:\n");
elemt = &smf_stats->elem[0];
}
while (count) {
printf("\t\t%s %d Count: %d\n", namebuf, dtoh16(elemt->code),
dtoh32(elemt->count));
elemt ++;
count --;
}
return 0;
}
/*
* Check for the smfstats parameters. One of defined parameters can be passed in.
*/
static int
wl_smfs_option(char **argv, int* idx, int *consumed, int* clear)
{
int err = 0;
char *p;
char const * smfs_opt[] = {"auth", "assoc", "reassoc", "disassoc_tx",
"disassoc_rx", "deauth_tx", "deauth_rx"};
char const * clear_opt = "clear";
int i;
char const * cur_opt;
if (*argv == NULL) {
goto exit;
}
p = *argv++;
for (i = 0; i < SMFS_TYPE_MAX; i++) {
cur_opt = smfs_opt[i];
if (!strcmp(p, cur_opt)) {
*idx = i;
*consumed += 1;
goto exit;
}
}
if (!strcmp(p, clear_opt))
*clear = 1;
exit:
return err;
}
/* Get or Clear (set) the "smfstats" iovar, with an optional config index argument:
* wl smfstats [-C N]|[--cfg=N] 0
*
* Option:
* -C N
* --cfg=N
* --config=N
* --configuration=N
* specify the config index N
* If cfg index not given on a set, the WLC_SET_SMF_STATS ioctl will be used
*/
static int
wl_smfstats(void *wl, cmd_t *cmd, char **argv)
{
int bsscfg_idx = 0;
int cfg_consumed = 0, smfs_consumed = 0;
int err;
int i, val;
int smf_index = 0;
int smfs_clear = 0;
BCM_REFERENCE(cmd);
argv++;
/* parse a bsscfg_idx option if present */
if ((err = wl_cfg_option(argv, "smfstats", &bsscfg_idx, &cfg_consumed)) != 0)
return err;
argv += cfg_consumed;
if ((err = wl_smfs_option(argv, &smf_index, &smfs_consumed, &smfs_clear)) != 0)
return err;
if (!smfs_clear) {
if (cfg_consumed == 0) {
if (smfs_consumed) {
err = wlu_iovar_getbuf(wl, "smfstats", &smf_index, sizeof(int),
buf, WLC_IOCTL_SMLEN);
if (!err)
err = wl_disp_smfs(buf);
}
else {
for (i = 0; i < SMFS_TYPE_MAX; i++) {
smf_index = i;
err = wlu_iovar_getbuf(wl, "smfstats", &smf_index,
sizeof(int), buf, WLC_IOCTL_SMLEN);
if (!err)
err = wl_disp_smfs(buf);
}
}
} else {
/* use "stats" iovar since a config option was given */
if (smfs_consumed) {
err = wl_bssiovar_getbuf(wl, "smfstats", bsscfg_idx, &smf_index,
sizeof(int), buf, WLC_IOCTL_SMLEN);
if (!err)
err = wl_disp_smfs(buf);
}
else {
for (i = 0; i < SMFS_TYPE_MAX; i++) {
smf_index = i;
err = wl_bssiovar_getbuf(wl, "smfstats", bsscfg_idx,
&smf_index, sizeof(int), buf, WLC_IOCTL_SMLEN);
if (!err)
err = wl_disp_smfs(buf);
}
}
}
if (err < 0)
return err;
} else {
val = 0;
if (cfg_consumed == 0)
err = wlu_iovar_setint(wl, "smfstats", val);
else
err = wl_bssiovar_setint(wl, "smfstats", bsscfg_idx, val);
}
return err;
}
#ifdef BCMCCX
static int
wl_leap(void *wl, cmd_t *cmd, char **argv)
{
wl_leap_list_t leap_list, *get_list;
wl_leap_info_t *list_item;
size_t len;
int ret, i;
if (!*++argv) {
/* no parameter means this is a `get'. */
len = sizeof(wl_leap_list_t) + 42 * sizeof(wl_leap_info_t);
get_list = (wl_leap_list_t *)malloc(len);
if (!get_list) {
fprintf(stderr, "memory alloc failure\n");
return BCME_NOMEM;
}
get_list->buflen = htod32(len);
if ((ret = wlu_get(wl, cmd->get, get_list, len)) < 0) {
free(get_list);
return ret;
}
get_list->buflen = dtoh32(get_list->buflen);
get_list->version = dtoh32(get_list->version);
get_list->count = dtoh32(get_list->count);
printf("buflen %d version %d count %d\n",
get_list->buflen, get_list->version, get_list->count);
for (i = 0, list_item = get_list->leap_info;
i < (int) get_list->count;
i++, list_item++) {
char user[sizeof(leap_list.leap_info[0].user)+1];
char pw[sizeof(leap_list.leap_info[0].password)+1];
char dom[sizeof(leap_list.leap_info[0].domain)+1];
char ssidbuf[SSID_FMT_BUF_LEN];
list_item->ssid.SSID_len = dtoh32(list_item->ssid.SSID_len);
wl_format_ssid(ssidbuf, list_item->ssid.SSID,
list_item->ssid.SSID_len);
memcpy(user, list_item->user, list_item->user_len);
user[list_item->user_len] = '\0';
memcpy(pw, list_item->password, list_item->password_len);
pw[list_item->password_len] = '\0';
memcpy(dom, list_item->domain, list_item->domain_len);
dom[list_item->domain_len] = '\0';
printf(" % 2d: ssid \"%s\", user \"%s\", password \"%s\", domain \"%s\"\n",
i, ssidbuf, user, pw, dom);
}
free(get_list);
return 0;
}
len = strlen(*argv);
/* ssid has to be a leagal size */
if (len > DOT11_MAX_SSID_LEN)
return BCME_BADARG;
memset(&leap_list, 0, sizeof(leap_list));
leap_list.buflen = htod32(sizeof(leap_list));
leap_list.count = htod32(1);
leap_list.leap_info[0].ssid.SSID_len = htod32(len);
memcpy(leap_list.leap_info[0].ssid.SSID, *argv, len);
/* user name must follow */
if (!*++argv)
return BCME_USAGE_ERROR;
len = strlen(*argv);
if (len > sizeof(leap_list.leap_info[0].user))
return BCME_BADARG;
leap_list.leap_info[0].user_len = len;
memcpy(leap_list.leap_info[0].user, *argv, len);
/* password must follow */
if (!*++argv)
return BCME_USAGE_ERROR;
len = strlen(*argv);
if (len > sizeof(leap_list.leap_info[0].password))
return BCME_BADARG;
leap_list.leap_info[0].password_len = len;
memcpy(leap_list.leap_info[0].password, *argv, len);
/* If there's more, it's a domain name */
if (*++argv) {
len = strlen(*argv);
if (len > sizeof(leap_list.leap_info[0].domain))
return BCME_BADARG;
leap_list.leap_info[0].domain_len = len;
memcpy(leap_list.leap_info[0].domain, *argv, len);
}
return wlu_set(wl, cmd->set, &leap_list, sizeof(leap_list));
}
#endif /* BCMCCX */
static int
wl_tssi(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
UNUSED_PARAMETER(argv);
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, &val, sizeof(int))) < 0)
return ret;
val = dtoh32(val);
printf("CCK %d OFDM %d\n", (val & 0xff), (val >> 8) & 0xff);
return 0;
}
/* Quarter dBm units to mW
* Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
* Table is offset so the last entry is largest mW value that fits in
* a uint16.
*/
#define QDBM_OFFSET 153 /* QDBM_OFFSET */
#define QDBM_TABLE_LEN 40 /* QDBM_TABLE_LEN */
/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
* Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
*/
#define QDBM_TABLE_LOW_BOUND 6493 /* QDBM_TABLE_LOW_BOUND */
/* Largest mW value that will round down to the last table entry,
* QDBM_OFFSET + QDBM_TABLE_LEN-1.
* Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
*/
#define QDBM_TABLE_HIGH_BOUND 64938 /* QDBM_TABLE_HIGH_BOUND */
static const uint16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
};
static uint16
wl_qdbm_to_mw(uint8 qdbm)
{
uint factor = 1;
int idx = qdbm - QDBM_OFFSET;
if (idx >= QDBM_TABLE_LEN) {
/* clamp to max uint16 mW value */
return 0xFFFF;
}
/* scale the qdBm index up to the range of the table 0-40
* where an offset of 40 qdBm equals a factor of 10 mW.
*/
while (idx < 0) {
idx += 40;
factor *= 10;
}
/* return the mW value scaled down to the correct factor of 10,
* adding in factor/2 to get proper rounding.
*/
return ((nqdBm_to_mW_map[idx] + factor/2) / factor);
}
static uint8
wl_mw_to_qdbm(uint16 mw)
{
uint8 qdbm;
int offset;
uint mw_uint = mw;
uint boundary;
/* handle boundary case */
if (mw_uint <= 1)
return 0;
offset = QDBM_OFFSET;
/* move mw into the range of the table */
while (mw_uint < QDBM_TABLE_LOW_BOUND) {
mw_uint *= 10;
offset -= 40;
}
for (qdbm = 0; qdbm < QDBM_TABLE_LEN-1; qdbm++) {
boundary = nqdBm_to_mW_map[qdbm] +
(nqdBm_to_mW_map[qdbm+1] - nqdBm_to_mW_map[qdbm])/2;
if (mw_uint < boundary) break;
}
qdbm += (uint8)offset;
return (qdbm);
}
#define UNIT_MW 1 /* UNIT_MW */
#define UNIT_QDBM 2 /* UNIT_QDBM */
#define UNIT_DBM 3 /* UNIT_DBM */
static int
wl_txpwr1(void *wl, cmd_t *cmd, char **argv)
{
int ret, val, new_val = 0, unit;
const char *name = "qtxpower";
bool override = FALSE;
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_iovar_getint(wl, name, &val)) < 0)
return ret;
override = ((val & WL_TXPWR_OVERRIDE) != 0);
val &= ~WL_TXPWR_OVERRIDE;
printf("TxPower is %d qdbm, %d.%d dbm, %d mW Override is %s\n",
val, DIV_QUO(val, 4), DIV_REM(val, 4),
wl_qdbm_to_mw((uint8)(MIN(val, 0xff))),
override ? "On" : "Off");
return 0;
} else {
/* for set */
unit = UNIT_DBM; /* default units */
/* override can be used in combo with any unit */
if (!strcmp(*argv, "-o")) {
override = TRUE;
if (!*++argv)
return BCME_USAGE_ERROR;
}
if (!strcmp(*argv, "-d")) {
unit = UNIT_DBM;
argv++;
}
else if (!strcmp(*argv, "-q")) {
unit = UNIT_QDBM;
argv++;
}
else if (!strcmp(*argv, "-m")) {
unit = UNIT_MW;
argv++;
}
/* override can be used in combo with any unit */
if (!strcmp(*argv, "-o")) {
override = TRUE;
argv++;
}
if (!*argv)
return BCME_USAGE_ERROR;
val = atoi(*argv);
if (val == -1) {
val = 127; /* Max val of 127 qdbm */
unit = UNIT_QDBM;
}
if (val <= 0) {
return BCME_BADARG;
}
switch (unit) {
case UNIT_MW:
new_val = wl_mw_to_qdbm((uint16)MIN(val, 0xffff));
break;
case UNIT_DBM:
new_val = val * 4;
break;
case UNIT_QDBM:
new_val = val;
break;
}
if (override)
new_val |= WL_TXPWR_OVERRIDE;
return wlu_iovar_setint(wl, name, new_val);
}
}
static int
wl_txpwr(void *wl, cmd_t *cmd, char **argv)
{
int error;
uint32 val;
char *endptr = NULL;
uint32 override;
const char *name = "qtxpower";
UNUSED_PARAMETER(cmd);
if (!*++argv) {
if ((error = wlu_iovar_getint(wl, name, (int *)&val)) < 0)
return error;
/* Report power in mw with WL_TXPWR_OVERRIDE
* bit indicating the status
*/
override = ((val & WL_TXPWR_OVERRIDE) != 0);
val &= ~WL_TXPWR_OVERRIDE;
printf("%d.%d dBm = %d mw. %s\n", DIV_QUO(val, 4), DIV_REM(val, 4),
wl_qdbm_to_mw((uint8)(MIN(val, 0xff))), (override ? "(Override ON)" : ""));
return 0;
} else {
if (!strcmp(*argv, "-u")) {
override = 0;
argv++;
} else
override = WL_TXPWR_OVERRIDE;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
val = wl_mw_to_qdbm((uint16)MIN(val, 0xffff));
/* wl command input power will override current power set if told so */
val |= override;
return wlu_iovar_setint(wl, name, val);
}
}
static int
wl_get_txpwr_limit(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint8 val_qdbm;
uint16 val_mw;
tx_power_legacy_t power;
UNUSED_PARAMETER(argv);
ret = wlu_get(wl, cmd->get, &power, sizeof(power));
if (ret < 0)
return ret;
val_qdbm = MIN(power.txpwr_band_max[0], power.txpwr_local_max);
val_mw = wl_qdbm_to_mw((uint8)(MIN(val_qdbm, 0xff)));
printf("%d mW (%d.%d dBm)\n", val_mw, DIV_QUO(val_qdbm, 4), DIV_REM(val_qdbm, 4));
return ret;
}
static int
wl_atten(void *wl, cmd_t *cmd, char **argv)
{
int ret;
atten_t atten;
char *endptr;
memset(&atten, 0, sizeof(atten_t));
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, &atten, sizeof(atten_t))) < 0)
return ret;
printf("tx %s bb/radio/ctl1 %d/%d/%d\n",
(dtoh16(atten.auto_ctrl) ? "auto" : ""),
dtoh16(atten.bb), dtoh16(atten.radio), dtoh16(atten.txctl1));
return 0;
} else {
if (cmd->set < 0)
return -1;
if (!stricmp(*argv, "auto")) {
atten.auto_ctrl = WL_ATTEN_PCL_ON;
atten.auto_ctrl = htod16(atten.auto_ctrl);
}
else if (!stricmp(*argv, "manual")) {
atten.auto_ctrl = WL_ATTEN_PCL_OFF;
atten.auto_ctrl = htod16(atten.auto_ctrl);
}
else {
atten.auto_ctrl = WL_ATTEN_APP_INPUT_PCL_OFF;
atten.auto_ctrl = htod16(atten.auto_ctrl);
atten.bb = (uint16)strtoul(*argv, &endptr, 0);
atten.bb = htod16(atten.bb);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
if (!*++argv)
return BCME_USAGE_ERROR;
atten.radio = (uint16)strtoul(*argv, &endptr, 0);
atten.radio = htod16(atten.radio);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
if (!*++argv)
return BCME_USAGE_ERROR;
atten.txctl1 = (uint16)strtoul(*argv, &endptr, 0);
atten.txctl1 = htod16(atten.txctl1);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
return BCME_USAGE_ERROR;
}
}
return wlu_set(wl, cmd->set, &atten, sizeof(atten_t));
}
}
static int
wl_maclist(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct maclist *maclist = (struct maclist *) buf;
struct ether_addr *ea;
uint i, max = (WLC_IOCTL_MAXLEN - sizeof(int)) / ETHER_ADDR_LEN;
uint len;
if (!*++argv) {
if (cmd->get < 0)
return -1;
maclist->count = htod32(max);
if ((ret = wlu_get(wl, cmd->get, maclist, WLC_IOCTL_MAXLEN)) < 0)
return ret;
maclist->count = dtoh32(maclist->count);
for (i = 0, ea = maclist->ea; i < maclist->count && i < max; i++, ea++)
printf("%s %s\n", cmd->name, wl_ether_etoa(ea));
return 0;
} else {
if (cmd->set < 0)
return -1;
/* Clear list */
maclist->count = htod32(0);
if (!stricmp(*argv, "none") || !stricmp(*argv, "clear"))
return wlu_set(wl, cmd->set, maclist, sizeof(int));
/* Get old list */
maclist->count = htod32(max);
if ((ret = wlu_get(wl, cmd->get, maclist, WLC_IOCTL_MAXLEN)) < 0)
return ret;
/* Append to old list */
maclist->count = dtoh32(maclist->count);
ea = &maclist->ea[maclist->count];
while (*argv && maclist->count < max) {
if (!wl_ether_atoe(*argv, ea)) {
printf("Problem parsing MAC address \"%s\".\n", *argv);
return BCME_USAGE_ERROR;
}
maclist->count++;
ea++;
argv++;
}
/* Set new list */
len = sizeof(maclist->count) + maclist->count * sizeof(maclist->ea);
maclist->count = htod32(maclist->count);
return wlu_set(wl, cmd->set, maclist, len);
}
}
static int
wl_maclist_1(void *wl, cmd_t *cmd, char **argv)
{
struct maclist *maclist;
struct ether_addr *ea;
uint i;
int ret;
strcpy(buf, argv[0]);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
maclist = (struct maclist *)buf;
for (i = 0, ea = maclist->ea; i < dtoh32(maclist->count); i++, ea++)
printf("%s %s\n", cmd->name, wl_ether_etoa(ea));
return 0;
}
static int
wl_out(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
return wlu_set(wl, WLC_OUT, NULL, 0);
}
static int
wl_band(void *wl, cmd_t *cmd, char **argv)
{
uint band;
int error;
UNUSED_PARAMETER(cmd);
error = 0;
argv++;
if (*argv == NULL) { /* get current band */
if ((error = wlu_get(wl, WLC_GET_BAND, &band, sizeof(uint))) < 0)
return (error);
band = dtoh32(band);
if (band == WLC_BAND_AUTO)
printf("auto\n");
else if (band == WLC_BAND_5G)
printf("a\n");
else if (band == WLC_BAND_2G)
printf("b\n");
else {
printf("unrecognized band value %d\n", band);
error = BCME_ERROR;
}
} else { /* set the band */
if (!stricmp(*argv, "auto"))
band = WLC_BAND_AUTO;
else if (!stricmp(*argv, "a"))
band = WLC_BAND_5G;
else if (!stricmp(*argv, "b"))
band = WLC_BAND_2G;
else {
printf("unsupported band: %s\n", *argv);
return BCME_UNSUPPORTED;
}
band = htod32(band);
error = wlu_set(wl, WLC_SET_BAND, &band, sizeof(uint));
}
return (error);
}
static int
wl_bandlist(void *wl, cmd_t *cmd, char **argv)
{
uint list[3];
int error;
uint i;
UNUSED_PARAMETER(cmd);
error = 0;
argv++;
if ((error = wlu_get(wl, WLC_GET_BANDLIST, list, sizeof(list))) < 0)
return (error);
list[0] = dtoh32(list[0]);
list[1] = dtoh32(list[1]);
list[2] = dtoh32(list[2]);
/* list[0] is count, followed by 'count' bands */
if (list[0] > 2)
list[0] = 2;
for (i = 1; i <= list[0]; i++)
if (list[i] == WLC_BAND_5G)
printf("a ");
else if (list[i] == WLC_BAND_2G)
printf("b ");
else
printf("? ");
printf("\n");
return (0);
}
static int
wl_phylist(void *wl, cmd_t *cmd, char **argv)
{
char phylist_buf[128];
int error;
char *cp;
UNUSED_PARAMETER(cmd);
error = 0;
argv++;
if ((error = wlu_get(wl, WLC_GET_PHYLIST, phylist_buf, sizeof(phylist_buf))) < 0)
return (error);
cp = phylist_buf;
for (; *cp; cp++)
printf("%c ", *cp);
printf("\n");
return (0);
}
#define UPGRADE_BUFSIZE 512 /* upgrade buffer size */
static int
wl_upgrade(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#else
FILE *fp;
int ret = 0;
struct {
uint32 offset;
char buf[UPGRADE_BUFSIZE];
} block;
uint32 offset;
uint len;
if (!*++argv)
return BCME_USAGE_ERROR;
if (!(fp = fopen(*argv, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", *argv);
return BCME_BADARG;
}
printf("Programming %s...", *argv);
fflush(stdout);
offset = 0;
block.offset = htod32(offset);
while ((len = fread(block.buf, 1, sizeof(block.buf), fp))) {
if ((ret = wlu_set(wl, cmd->set, &block, 4 + len)) < 0)
break;
offset += len;
block.offset = htod32(offset);
printf(".");
fflush(stdout);
}
if (ferror(fp)) {
ret = ferror(fp);
printf("\nerror reading %s\n", *argv);
} else {
long status = WLC_UPGRADE_PENDING;
int retries;
printf("\nCommitting image to flash...\n");
while (status == WLC_UPGRADE_PENDING) {
retries = 10;
retry:
if ((ret = wlu_get(wl, WLC_UPGRADE_STATUS,
&status, sizeof(status))) < 0) {
/* the first attempt to get status will
* likely fail due to dev reset
*/
if (retries--)
goto retry;
break;
}
status = dtoh32(status);
}
if (status == WLC_UPGRADE_SUCCESS)
printf("\nDone\n\nSuccessfully downloaded %d bytes\n", block.offset);
else
fprintf(stderr, "\n*** UPGRADE FAILED! *** (status %ld)\n", status);
}
fclose(fp);
return ret;
#endif /* BWL_FILESYSTEM_SUPPORT */
}
#include <bcmnvram.h>
static int
wl_otpraw(void *wl, cmd_t *cmd, char **argv)
{
char var[392];
uint32 offset;
uint32 bits;
uint32 len;
bool get = TRUE;
void *ptr = NULL;
char *endptr;
uint32 i;
if (argv[1]) {
offset = htod32(strtoul(argv[1], &endptr, 0));
memcpy(var, (char *)&offset, sizeof(offset));
len = sizeof(offset);
}
else
return BCME_USAGE_ERROR;
if (argv[2]) {
bits = htod32(strtoul(argv[2], &endptr, 0));
if (bits > 3072)
{
printf("bit size (%d) too long or negative!!\n", bits);
return BCME_BADARG;
}
}
else
bits = 1;
memcpy(&var[len], (char *)&bits, sizeof(bits));
len += sizeof(bits);
if (argv[3]) {
unsigned char data[768];
uint32 patlen;
char *inptr = argv[3];
get = FALSE;
if (*inptr == '0' && toupper((int)(*(inptr + 1))) == 'X')
inptr += 2;
patlen = strlen(inptr);
if (patlen > 768 || (patlen * 4) < bits)
{
printf("data length (%d) too long or small!!\n", patlen);
return BCME_USAGE_ERROR;
}
for (i = 1; i <= patlen; i++)
{
int n = (int)((unsigned char)*inptr++);
if (!isxdigit(n)) {
fprintf(stderr, "invalid hex digit %c\n", n);
return BCME_USAGE_ERROR;
}
data[patlen - i] = (unsigned char)(isdigit(n) ? (n - '0')
: ((islower(n) ? (toupper(n)) : n) - 'A' + 10));
}
for (i = 0; i < patlen; i += 2)
{
unsigned char v;
v = data[i];
if (i + 1 < patlen)
v += (data[i+1] * 16);
memcpy(&var[len], (char *)&v, sizeof(v));
len += sizeof(v);
}
printf("OTP RAM Write:");
for (i = 0; i < bits; i += 8)
{
unsigned char v;
v = var[2*sizeof(uint32) + (i/8)];
if ((i % 64) == 0)
printf("\nbit %4d:", offset + i);
printf(" 0x%x", v);
}
printf("\n");
}
if (get) {
int ret;
unsigned char v, *cptr;
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0) {
printf("Error reading from OTP data\n");
return ret;
}
cptr = (unsigned char *)ptr;
printf("OTP RAM Read:");
for (i = 0; i < bits; i += 8)
{
v = *cptr++;
if ((i % 64) == 0)
printf("\nbit %4d:", offset + i);
printf(" 0x%02x", v);
}
printf("\n");
return 0;
}
return wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
}
static int
wl_otpw(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#else
FILE *fp;
int ret = 0;
struct nvram_header *nvr;
char *p, otpw_buf[1024 - 128];
const char *msg;
int len;
if (!*++argv)
return BCME_USAGE_ERROR;
if (!(fp = fopen(*argv, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", *argv);
return BCME_BADARG;
}
len = fread(otpw_buf, 1, sizeof(otpw_buf) - 1, fp);
if ((ret = ferror(fp))) {
printf("\nerror %d reading %s\n", ret, *argv);
ret = BCME_ERROR;
goto out;
}
if (!feof(fp)) {
printf("\nFile %s too large\n", *argv);
ret = BCME_ERROR;
goto out;
}
/* Got the bits, do they look like the output of nvserial? */
nvr = (struct nvram_header *)otpw_buf;
if (nvr->magic == NVRAM_MAGIC) {
if (cmd->set == WLC_OTPW) {
printf("File %s looks like an nvserial file, use nvotpw\n", *argv);
fflush(stdout);
ret = BCME_ERROR;
goto out;
}
len = nvr->len - sizeof(struct nvram_header);
if (len <= 0) {
printf("Invalid length (%d)\n", len);
ret = BCME_ERROR;
goto out;
}
if (len & 1) {
otpw_buf[len++] = '\0';
}
p = (char *)(nvr + 1);
msg = "nvserial";
} else {
if (cmd->set == WLC_NVOTPW) {
printf("File %s is not an nvserial file\n", *argv);
ret = BCME_ERROR;
goto out;
}
if (len & 1) {
printf("File %s has an odd length (%d)\n", *argv, len);
ret = BCME_ERROR;
goto out;
}
p = otpw_buf;
msg = "raw";
}
printf("Writing %d bytes from %s file %s to otp ...\n", len, msg, *argv);
fflush(stdout);
if ((ret = wlu_set(wl, cmd->set, p, len)) < 0) {
printf("\nError %d writing %s to otp\n", ret, *argv);
}
out:
fclose(fp);
return ret;
#endif /* BWL_FILESYSTEM_SUPPORT */
}
static int
wl_get_pktcnt(void *wl, cmd_t *cmd, char **argv)
{
int ret;
get_pktcnt_t pktcnt;
UNUSED_PARAMETER(argv);
memset(&pktcnt, 0, sizeof(pktcnt));
if ((ret = wlu_get(wl, cmd->get, &pktcnt, sizeof(pktcnt))) < 0)
return ret;
printf("Receive: good packet %d, bad packet %d, othercast good packet %d\n",
dtoh32(pktcnt.rx_good_pkt), dtoh32(pktcnt.rx_bad_pkt),
dtoh32(pktcnt.rx_ocast_good_pkt));
printf("Transmit: good packet %d, bad packet %d\n",
dtoh32(pktcnt.tx_good_pkt), dtoh32(pktcnt.tx_bad_pkt));
return ret;
}
static int
wl_interfere(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char *endptr = NULL;
int mode;
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, &mode, sizeof(mode))) < 0)
return ret;
mode = dtoh32(mode);
switch (mode & 0x7f) {
case INTERFERE_NONE:
printf("All interference mitigation is disabled. (mode 0)\n");
break;
case NON_WLAN:
printf("Non-wireless LAN Interference mitigation is enabled. (mode 1)\n");
break;
case WLAN_MANUAL:
printf("Wireless LAN Interference mitigation is enabled. (mode 2)\n");
break;
case WLAN_AUTO:
printf("Auto Wireless LAN Interference mitigation is enabled and ");
if (mode & AUTO_ACTIVE)
printf("active. (mode 3)\n");
else
printf("not active. (mode 3)\n");
break;
case WLAN_AUTO_W_NOISE:
printf("Auto Wireless LAN Interference mitigation is enabled and ");
if (mode & AUTO_ACTIVE)
printf("active, ");
else
printf("not active, ");
printf("and noise reduction is enabled. (mode 4)\n");
break;
}
return 0;
} else {
mode = INTERFERE_NONE;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
switch (val) {
case 0:
mode = INTERFERE_NONE;
break;
case 1:
mode = NON_WLAN;
break;
case 2:
mode = WLAN_MANUAL;
break;
case 3:
mode = WLAN_AUTO;
break;
case 4:
mode = WLAN_AUTO_W_NOISE;
break;
default:
return BCME_BADARG;
}
mode = htod32(mode);
return wlu_set(wl, cmd->set, &mode, sizeof(mode));
}
}
static int
wl_interfere_override(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char *endptr;
int mode;
if (!*++argv) {
if (cmd->get < 0)
return -1;
if ((ret = wlu_get(wl, cmd->get, &mode, sizeof(mode))) < 0) {
return ret;
}
mode = dtoh32(mode);
switch (mode) {
case INTERFERE_NONE:
printf("Interference override NONE, "
"all mitigation disabled. (mode 0)\n");
break;
case NON_WLAN:
printf("Interference override enabled. "
" Non-wireless LAN Interference mitigation is enabled. (mode 1)\n");
break;
case WLAN_MANUAL:
printf("Interference override enabled. "
" Wireless LAN Interference mitigation is enabled. (mode 2)\n");
break;
case WLAN_AUTO:
printf("Interference override enabled. "
" Interference mitigation is enabled and ");
if (mode & AUTO_ACTIVE)
printf("active. (mode 3)\n");
else
printf("not active. (mode 3)\n");
break;
case WLAN_AUTO_W_NOISE:
printf("Interference override enabled. "
" Interference mitigation is enabled and ");
if (mode & AUTO_ACTIVE)
printf("active, ");
else
printf("not active, ");
printf("and noise reduction is enabled. (mode 4)\n");
break;
case INTERFERE_OVRRIDE_OFF:
printf("Interference override disabled. \n");
break;
}
return 0;
} else {
mode = INTERFERE_NONE;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
switch (val) {
case 0:
mode = INTERFERE_NONE;
break;
case 1:
mode = NON_WLAN;
break;
case 2:
mode = WLAN_MANUAL;
break;
case 3:
mode = WLAN_AUTO;
break;
case 4:
mode = WLAN_AUTO_W_NOISE;
break;
case INTERFERE_OVRRIDE_OFF:
mode = INTERFERE_OVRRIDE_OFF;
break;
default:
return BCME_BADARG;
}
mode = htod32(mode);
return wlu_set(wl, cmd->set, &mode, sizeof(mode));
}
}
static cntry_name_t *
wlc_cntry_name_to_country(char *long_name)
{
cntry_name_t *cntry;
for (cntry = cntry_names; cntry->name &&
stricmp(long_name, cntry->name); cntry++);
return (!cntry->name ? NULL : cntry);
}
static cntry_name_t *
wlc_cntry_abbrev_to_country(const char *abbrev)
{
cntry_name_t *cntry;
if (!*abbrev || strlen(abbrev) > 3 || strlen(abbrev) < 2)
return (NULL);
for (cntry = cntry_names; cntry->name &&
strnicmp(abbrev, cntry->abbrev, strlen(abbrev)); cntry++);
return (!cntry->name ? NULL : cntry);
}
static int
wl_parse_country_spec(const char *spec, char *ccode, int *regrev)
{
char *revstr;
char *endptr = NULL;
int ccode_len;
int rev = -1;
revstr = strchr(spec, '/');
if (revstr) {
rev = strtol(revstr + 1, &endptr, 10);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
fprintf(stderr,
"Could not parse \"%s\" as a regulatory revision "
"in the country string \"%s\"\n",
revstr + 1, spec);
return BCME_USAGE_ERROR;
}
}
if (revstr)
ccode_len = (int)(uintptr)(revstr - spec);
else
ccode_len = (int)strlen(spec);
if (ccode_len > 3) {
fprintf(stderr,
"Could not parse a 2-3 char country code "
"in the country string \"%s\"\n",
spec);
return BCME_USAGE_ERROR;
}
memcpy(ccode, spec, ccode_len);
ccode[ccode_len] = '\0';
*regrev = rev;
return 0;
}
int
wl_country(void *wl, cmd_t *cmd, char **argv)
{
cntry_name_t *cntry;
wl_country_t cspec = {{0}, 0, {0}};
int argc = 0;
int err;
int bcmerr = 1;
/* skip the command name */
argv++;
/* find the arg count */
while (argv[argc])
argc++;
/* check arg list count */
if (argc > 2) {
fprintf(stderr, "Too many arguments (%d) for command %s\n", argc, cmd->name);
return BCME_USAGE_ERROR;
}
buf[0] = 0;
if (argc == 0) {
const char* name = "<unknown>";
/* first try the country iovar */
err = wlu_iovar_get(wl, "country", &cspec, sizeof(cspec));
if (!err) {
cntry = wlc_cntry_abbrev_to_country(cspec.country_abbrev);
if (cntry)
name = cntry->name;
cspec.rev = dtoh32(cspec.rev);
printf("%s (%s/%d) %s\n",
cspec.country_abbrev, cspec.ccode, cspec.rev, name);
return 0;
}
/* if there was an error other than BCME_UNSUPPORTED, fail now */
wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (bcmerr != BCME_UNSUPPORTED)
return err;
/* if the "country" iovar is unsupported, try the WLC_SET_COUNTRY ioctl */
if ((err = wlu_get(wl, cmd->get, &buf[0], WLC_IOCTL_SMLEN)) < 0)
return err;
if (strlen(buf) == 0) {
printf("No country set\n");
return 0;
}
cntry = wlc_cntry_abbrev_to_country(buf);
if (cntry != NULL)
name = cntry->name;
printf("%s () %s\n", buf, name);
return 0;
}
if (!stricmp(*argv, "list")) {
uint i;
const char* abbrev;
wl_country_list_t *cl = (wl_country_list_t *)buf;
cl->buflen = WLC_IOCTL_MAXLEN;
cl->count = 0;
/* band may follow */
if (*++argv) {
cl->band_set = TRUE;
if (!stricmp(*argv, "a"))
cl->band = WLC_BAND_5G;
else if (!stricmp(*argv, "b") || !stricmp(*argv, "g"))
cl->band = WLC_BAND_2G;
else {
printf("unsupported band: %s\n", *argv);
return BCME_UNSUPPORTED;
}
} else {
cl->band_set = FALSE;
}
cl->buflen = htod32(cl->buflen);
cl->band_set = htod32(cl->band_set);
cl->band = htod32(cl->band);
cl->count = htod32(cl->count);
err = wlu_get(wl, WLC_GET_COUNTRY_LIST, buf, WLC_IOCTL_MAXLEN);
if (err < 0)
return err;
printf("Supported countries: country code and long name\n");
for (i = 0; i < dtoh32(cl->count); i++) {
abbrev = &cl->country_abbrev[i*WLC_CNTRY_BUF_SZ];
cntry = wlc_cntry_abbrev_to_country(abbrev);
printf("%s\t%s\n", abbrev, cntry ? cntry->name : "");
}
return 0;
}
memset(&cspec, 0, sizeof(cspec));
cspec.rev = -1;
if (argc == 1) {
/* check for the first arg being a country name, e.g. "United States",
* or country spec, "US/1", or just a country code, "US"
*/
if ((cntry = wlc_cntry_name_to_country(argv[0])) != NULL) {
/* arg matched a country name */
memcpy(cspec.country_abbrev, cntry->abbrev, WLC_CNTRY_BUF_SZ);
err = 0;
} else {
/* parse a country spec, e.g. "US/1", or a country code.
* cspec.rev will be -1 if not specified.
*/
err = wl_parse_country_spec(argv[0], cspec.country_abbrev, &cspec.rev);
}
if (err) {
fprintf(stderr,
"Argument \"%s\" could not be parsed as a country name, "
"country code, or country code and regulatory revision.\n",
argv[0]);
return BCME_USAGE_ERROR;
}
/* if the arg was a country spec, then fill out ccdoe and rev,
* and leave country_abbrev defaulted to the ccode
*/
if (cspec.rev != -1)
memcpy(cspec.ccode, cspec.country_abbrev, WLC_CNTRY_BUF_SZ);
} else {
/* for two args, the first needs to be a country code or country spec */
err = wl_parse_country_spec(argv[0], cspec.ccode, &cspec.rev);
if (err) {
fprintf(stderr,
"Argument 1 \"%s\" could not be parsed as a country code, or "
"country code and regulatory revision.\n",
argv[0]);
return BCME_USAGE_ERROR;
}
/* the second arg needs to be a country name or country code */
if ((cntry = wlc_cntry_name_to_country(argv[1])) != NULL) {
/* arg matched a country name */
memcpy(cspec.country_abbrev, cntry->abbrev, WLC_CNTRY_BUF_SZ);
} else {
int rev;
err = wl_parse_country_spec(argv[1], cspec.country_abbrev, &rev);
if (rev != -1) {
fprintf(stderr,
"Argument \"%s\" had a revision. Arg 2 must be "
"a country name or country code without a revision\n",
argv[1]);
return BCME_USAGE_ERROR;
}
}
if (err) {
fprintf(stderr,
"Argument 2 \"%s\" could not be parsed as "
"a country name or country code\n",
argv[1]);
return BCME_USAGE_ERROR;
}
}
/* first try the country iovar */
if (cspec.rev == -1 && cspec.ccode[0] == '\0')
err = wlu_iovar_set(wl, "country", &cspec, WLC_CNTRY_BUF_SZ);
else {
cspec.rev = htod32(cspec.rev);
err = wlu_iovar_set(wl, "country", &cspec, sizeof(cspec));
}
if (err == 0)
return 0;
/* if there was an error other than BCME_UNSUPPORTED, fail now */
wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (bcmerr != BCME_UNSUPPORTED)
return err;
/* if the "country" iovar is unsupported, try the WLC_SET_COUNTRY ioctl if possible */
if (cspec.rev != -1 || cspec.ccode[0] != '\0') {
fprintf(stderr,
"Driver does not support full country spec interface, "
"only a country name or code may be sepcified\n");
return err;
}
/* use the legacy ioctl */
err = wlu_set(wl, WLC_SET_COUNTRY, cspec.country_abbrev, WLC_CNTRY_BUF_SZ);
return err;
}
int
wl_country_ie_override(void *wl, cmd_t *cmd, char **argv)
{
int argc = 0;
int error, i;
/* skip the command name */
argv++;
/* find the arg count */
while (argv[argc])
argc++;
if (argc == 0) {
void *ptr;
bcm_tlv_t *ie;
if ((error = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return (error);
ie = (bcm_tlv_t *)ptr;
printf("ie tag:0x%x ie len:0x%x ie data:", ie->id, ie->len);
for (i = 0; i < ie->len; i++)
printf("0x%x ", ie->data[i]);
printf("\n");
return error;
} else {
/* Set */
char *endptr = NULL;
uchar *valsp;
int8 ie_len, pad = 0;
/* retrieve the ie len in advance to check for padding */
ie_len = (int8)strtol(*(argv + 1), NULL, 0);
if (ie_len & 1) {
fprintf(stderr, "country ie len is odd(%d), padding by 1 octet\n", ie_len);
pad = 1;
}
valsp = (uchar*)malloc(argc + pad);
if (valsp == NULL) {
fprintf(stderr, "Error allocating %d bytes country ie\n", argc);
return BCME_NOMEM;
}
memset(valsp, 0, argc + pad);
for (i = 0; i < argc; i++, argv++) {
valsp[i] = (uchar)strtol(*argv, &endptr, 0);
/* make sure all the value string was parsed by strtol */
if (*endptr != '\0') {
free(valsp);
return BCME_USAGE_ERROR;
}
}
/* update ie len if padded */
if (pad) {
valsp[1] += 1;
valsp[ie_len + TLV_HDR_LEN] = 0;
}
error = wlu_var_setbuf(wl, cmd->name, valsp, argc + pad);
free(valsp);
return error;
}
}
#define ACI_SPIN "spin"
#define ACI_ENTER "enter"
#define ACI_EXIT "exit"
#define ACI_GLITCH "glitch"
#define NPHY_ACI_ADCPWR_ENTER "adcpwr_enter"
#define NPHY_ACI_ADCPWR_EXIT "adcpwr_exit"
#define NPHY_ACI_REPEAT_CTR "repeat"
#define NPHY_ACI_NUM_SAMPLES "samples"
#define NPHY_ACI_UNDETECT "undetect_sz"
#define NPHY_ACI_LOPWR "loaci"
#define NPHY_ACI_MDPWR "mdaci"
#define NPHY_ACI_HIPWR "hiaci"
#define NPHY_ACI_NOISE_NOASSOC_GLITCH_TH_UP "nphy_noise_noassoc_glitch_th_up"
#define NPHY_ACI_NOISE_NOASSOC_GLITCH_TH_DN "nphy_noise_noassoc_glitch_th_dn"
#define NPHY_ACI_NOISE_ASSOC_GLITCH_TH_UP "nphy_noise_assoc_glitch_th_up"
#define NPHY_ACI_NOISE_ASSOC_GLITCH_TH_DN "nphy_noise_assoc_glitch_th_dn"
#define NPHY_ACI_NOISE_ASSOC_ACI_GLITCH_TH_UP "nphy_noise_assoc_aci_glitch_th_up"
#define NPHY_ACI_NOISE_ASSOC_ACI_GLITCH_TH_DN "nphy_noise_assoc_aci_glitch_th_dn"
#define NPHY_ACI_NOISE_NOASSOC_ENTER_TH "nphy_noise_noassoc_enter_th"
#define NPHY_ACI_NOISE_ASSOC_ENTER_TH "nphy_noise_assoc_enter_th"
#define NPHY_ACI_NOISE_ASSOC_RX_GLITCH_BADPLCP_ENTER_TH \
"nphy_noise_assoc_rx_glitch_badplcp_enter_th"
#define NPHY_ACI_NOISE_ASSOC_CRSIDX_INCR "nphy_noise_assoc_crsidx_incr"
#define NPHY_ACI_NOISE_NOASSOC_CRSIDX_INCR "nphy_noise_noassoc_crsidx_incr"
#define NPHY_ACI_NOISE_CRSIDX_DECR "nphy_noise_crsidx_decr"
#if defined(BWL_FILESYSTEM_SUPPORT)
static int
wl_do_samplecollect_lcn40(void *wl, wl_samplecollect_args_t *collect, uint8 *buff, FILE *fp)
{
uint32 cnt;
int ret = 0;
uint32 *data;
int16 IData, QData;
uint16 wordlength = 14;
uint16 mask = ((0x1 << wordlength) - 1);
uint16 wrap = (0x1 << (wordlength - 1));
uint16 maxd = (0x1 << wordlength);
ret = wlu_iovar_getbuf(wl, "sample_collect", collect, sizeof(wl_samplecollect_args_t),
buff, WLC_SAMPLECOLLECT_MAXLEN);
if (ret)
return ret;
data = (uint32*)buff;
for (cnt = 0; cnt < collect->nsamps; cnt++) {
IData = data[cnt] & mask;
QData = ((data[cnt] >> 16) & mask);
if (IData >= wrap) {
IData = IData - maxd;
}
if (QData >= wrap) {
QData = QData - maxd;
}
fprintf(fp, "%d %d\n", IData, QData);
}
return cnt;
}
static int
wl_do_samplecollect_n(void *wl, wl_samplecollect_args_t *collect, uint8 *buff, FILE *fp)
{
uint16 nbytes;
int ret = 0;
ret = wlu_iovar_getbuf(wl, "sample_collect", collect, sizeof(wl_samplecollect_args_t),
buff, WLC_SAMPLECOLLECT_MAXLEN);
if (ret)
return ret;
/* bytes 1:0 indicate capture length */
while ((nbytes = ltoh16_ua(buff))) {
nbytes += 2;
ret = fwrite(buff, 1, nbytes, fp);
if (ret != nbytes) {
fprintf(stderr, "Error writing %d bytes to file, rc %d!\n",
nbytes, ret);
ret = -1;
break;
} else {
fprintf(stderr, "Wrote %d bytes\n", nbytes);
}
buff += nbytes;
}
return (ret);
}
#endif /* defined(BWL_FILESYSTEM_SUPPORT) */
#if defined(BWL_FILESYSTEM_SUPPORT)
static int
wl_do_samplecollect(void *wl, wl_samplecollect_args_t *collect, int sampledata_version,
uint32 *buff, FILE *fp)
{
uint16 nbytes, tag;
uint32 flag, *header, sync;
uint8 *ptr;
int err;
wl_sampledata_t *sample_collect;
wl_sampledata_t sample_data, *psample;
err = wlu_iovar_getbuf(wl, "sample_collect", collect, sizeof(wl_samplecollect_args_t),
buff, WLC_SAMPLECOLLECT_MAXLEN);
if (err)
return err;
sample_collect = (wl_sampledata_t *)buff;
header = (uint32 *)&sample_collect[1];
tag = ltoh16_ua(&sample_collect->tag);
if (tag != WL_SAMPLEDATA_HEADER_TYPE) {
fprintf(stderr, "Expect SampleData Header type %d, receive type %d\n",
WL_SAMPLEDATA_HEADER_TYPE, tag);
return -1;
}
nbytes = ltoh16_ua(&sample_collect->length);
flag = ltoh32_ua(&sample_collect->flag);
sync = ltoh32_ua(&header[0]);
if (sync != 0xACDC2009) {
fprintf(stderr, "Header sync word mismatch (0x%08x)\n", sync);
return -1;
}
err = fwrite((uint8 *)header, 1, nbytes, fp);
if (err != (int)nbytes)
fprintf(stderr, "Failed write file-header to file %d\n", err);
memset(&sample_data, 0, sizeof(wl_sampledata_t));
sample_data.version = sampledata_version;
sample_data.size = htol16(sizeof(wl_sampledata_t));
flag = 0;
/* new format, used in htphy */
do {
sample_data.tag = htol16(WL_SAMPLEDATA_TYPE);
sample_data.length = htol16(WLC_SAMPLECOLLECT_MAXLEN);
/* mask seq# */
sample_data.flag = htol32((flag & 0xff));
err = wlu_iovar_getbuf(wl, "sample_data", &sample_data, sizeof(wl_sampledata_t),
buff, WLC_SAMPLECOLLECT_MAXLEN);
if (err) {
fprintf(stderr, "Error reading back sample collected data\n");
err = -1;
break;
}
ptr = (uint8 *)buff + sizeof(wl_sampledata_t);
psample = (wl_sampledata_t *)buff;
tag = ltoh16_ua(&psample->tag);
nbytes = ltoh16_ua(&psample->length);
flag = ltoh32_ua(&psample->flag);
if (tag != WL_SAMPLEDATA_TYPE) {
fprintf(stderr, "Expect SampleData type %d, receive type %d\n",
WL_SAMPLEDATA_TYPE, tag);
err = -1;
break;
}
if (nbytes == 0) {
fprintf(stderr, "Done retrieving sample data\n");
err = -1;
break;
}
err = fwrite(ptr, 1, nbytes, fp);
if (err != (int)nbytes) {
fprintf(stderr, "Error writing %d bytes to file, rc %d!\n",
(int)nbytes, err);
err = -1;
break;
} else {
printf("Wrote %d bytes\n", err);
err = 0;
}
} while (flag & WL_SAMPLEDATA_MORE_DATA);
return err;
}
#endif /* defined(BWL_FILESYSTEM_SUPPORT) */
static int
wl_sample_collect(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#else
int ret = -1;
uint8 *buff = NULL;
wl_samplecollect_args_t collect;
wlc_rev_info_t revinfo;
uint32 phytype;
uint32 phyrev;
const char *fname = "sample_collect.dat";
FILE *fp = NULL;
/* Default setting for sampledata_version */
int sampledata_version = htol16(WL_SAMPLEDATA_T_VERSION);
UNUSED_PARAMETER(cmd);
memset(&revinfo, 0, sizeof(revinfo));
if ((ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return ret;
phytype = dtoh32(revinfo.phytype);
phyrev = dtoh32(revinfo.phyrev);
/* Assign some default params first */
/* 60us is roughly the max we can store (for NPHY with NREV < 7). */
collect.coll_us = 60;
collect.cores = -1;
collect.bitStart = -1;
/* extended settings */
collect.trigger = TRIGGER_NOW;
collect.mode = 1;
collect.post_dur = 10;
collect.pre_dur = 10;
collect.gpio_sel = 0;
collect.downsamp = FALSE;
collect.be_deaf = FALSE;
collect.timeout = 1000;
collect.agc = FALSE;
collect.filter = FALSE;
collect.trigger_state = 0;
collect.module_sel1 = 2;
collect.module_sel2 = 6;
collect.nsamps = 2048;
collect.version = WL_SAMPLECOLLECT_T_VERSION;
collect.length = sizeof(wl_samplecollect_args_t);
/* Skip the command name */
argv++;
ret = -1;
while (*argv) {
char *s = *argv;
if (argv[1] == NULL) {
ret = BCME_USAGE_ERROR;
goto exit;
}
if (!strcmp(s, "-f")) {
fname = argv[1];
} else if (!strcmp(s, "-u"))
collect.coll_us = atoi(argv[1]);
else if (!strcmp(s, "-c"))
collect.cores = atoi(argv[1]);
/* extended settings */
else if (!strcmp(s, "-t")) {
/* event trigger */
if (!strcmp(argv[1], "crs"))
collect.trigger = TRIGGER_CRS;
else if (!strcmp(argv[1], "crs_deassert"))
collect.trigger = TRIGGER_CRSDEASSERT;
else if (!strcmp(argv[1], "good_fcs"))
collect.trigger = TRIGGER_GOODFCS;
else if (!strcmp(argv[1], "bad_fcs"))
collect.trigger = TRIGGER_BADFCS;
else if (!strcmp(argv[1], "bad_plcp"))
collect.trigger = TRIGGER_BADPLCP;
else if (!strcmp(argv[1], "crs_glitch"))
collect.trigger = TRIGGER_CRSGLITCH;
}
else if (!strcmp(s, "-m")) {
if (!strcmp(argv[1], "gpio")) {
if (phytype == WLC_PHY_TYPE_HT) {
collect.mode = 4;
} else {
/* MIMOPHY */
collect.mode = 0xff;
}
} else {
collect.mode = atoi(argv[1]);
}
}
else if (!strcmp(s, "-k"))
collect.gpioCapMask = atoi(argv[1]);
else if (!strcmp(s, "-s"))
collect.bitStart = atoi(argv[1]);
else if (!strcmp(s, "-b"))
collect.pre_dur = atoi(argv[1]);
else if (!strcmp(s, "-a"))
collect.post_dur = atoi(argv[1]);
else if (!strcmp(s, "-g"))
collect.gpio_sel = atoi(argv[1]);
else if (!strcmp(s, "-d"))
collect.downsamp = atoi(argv[1]);
else if (!strcmp(s, "-e"))
collect.be_deaf = atoi(argv[1]);
else if (!strcmp(s, "-i"))
collect.timeout = atoi(argv[1]);
else if (!strcmp(s, "--agc")) {
/* perform software agc for sample collect */
collect.agc = atoi(argv[1]);
}
else if (!strcmp(s, "--filter")) {
/* Set HPC for LPF to lowest possible value (0x1) */
collect.filter = atoi(argv[1]);
}
else if (!strcmp(s, "-v"))
sampledata_version = atoi(argv[1]);
else if (!strcmp(s, "-s"))
collect.trigger_state = atoi(argv[1]);
else if (!strcmp(s, "-x"))
collect.module_sel1 = atoi(argv[1]);
else if (!strcmp(s, "-y"))
collect.module_sel2 = atoi(argv[1]);
else if (!strcmp(s, "-n"))
collect.nsamps = atoi(argv[1]);
else {
ret = BCME_USAGE_ERROR;
goto exit;
}
argv += 2;
}
buff = malloc(WLC_SAMPLECOLLECT_MAXLEN);
if (buff == NULL) {
fprintf(stderr, "Failed to allocate dump buffer of %d bytes\n",
WLC_SAMPLECOLLECT_MAXLEN);
return BCME_NOMEM;
}
memset(buff, 0, WLC_SAMPLECOLLECT_MAXLEN);
if ((fp = fopen(fname, "wb")) == NULL) {
fprintf(stderr, "Problem opening file %s\n", fname);
ret = BCME_BADARG;
goto exit;
}
if ((phytype == WLC_PHY_TYPE_HT) || (phytype == WLC_PHY_TYPE_AC)) {
ret = wl_do_samplecollect(wl, &collect, sampledata_version, (uint32 *)buff, fp);
}
else if (phytype == WLC_PHY_TYPE_N) {
if (phyrev < 7) {
ret = wl_do_samplecollect_n(wl, &collect, buff, fp);
} else {
ret = wl_do_samplecollect(wl, &collect, sampledata_version,
(uint32 *)buff, fp);
}
} else if (phytype == WLC_PHY_TYPE_LCN40) {
if (collect.nsamps > (WLC_SAMPLECOLLECT_MAXLEN >> 2)) {
fprintf(stderr, "Max number of samples supported = %d\n",
WLC_SAMPLECOLLECT_MAXLEN >> 2);
ret = -1;
goto exit;
}
ret = wl_do_samplecollect_lcn40(wl, &collect, buff, fp);
}
exit:
if (buff) free(buff);
if (fp) fclose(fp);
return ret;
#endif /* !BWL_FILESYSTEM_SUPPORT */
}
/* WLOTA_EN START */
#define WL_OTA_STRING_MAX_LEN 100
#define WL_OTA_CMDSTREAM_MAX_LEN 200
/* test_setup cmd argument ordering */
enum {
WL_OTA_SYNC_TIMEOUT = 1, /* Timeout in seconds */
WL_OTA_SYNCFAIL_ACTION, /* Fail actio -1/0/1 */
WL_OTA_SYNC_MAC, /* Mac address for sync */
WL_OTA_TX_MAC, /* Mac address for tx test */
WL_OTA_RX_MAC, /* Mac address for rx test */
WL_OTA_LOOP_TEST /* Put test into loop mode */
};
/* ota_tx / ota_rx format ordering */
enum {
WL_OTA_CUR_TEST, /* ota_tx or ota_rx */
WL_OTA_CHAN, /* cur channel */
WL_OTA_BW, /* cur bandwidth */
WL_OTA_CONTROL_BAND, /* cur control band */
WL_OTA_RATE, /* cur rate */
WL_OTA_STF_MODE, /* cur stf mode */
WL_OTA_TXANT, /* tx ant to be used */
WL_OTA_RXANT, /* rx ant to be used */
WL_OTA_TX_IFS, /* ifs */
WL_OTA_TX_PKT_LEN, /* pkt length */
WL_OTA_TX_NUM_PKT, /* num of packets */
WL_OTA_PWR_CTRL_ON, /* power control on/off */
WL_OTA_PWR_SWEEP /* start:delta:stop */
};
/* Various error chcking options */
enum {
WL_OTA_SYNCFAILACTION,
WL_OTA_CTRLBANDVALID,
WL_OTA_TXANTVALID,
WL_OTA_RXANTVALID,
WL_OTA_PWRCTRLVALID
};
/* Display init test seq */
static void
wl_ota_display_test_init_info(wl_ota_test_status_t *init_info)
{
printf("Test Init Summary\n");
printf("----------------------------------------------------------\n");
printf("Toatl Number of test req %d\n\n", init_info->test_cnt);
printf("Sync timeout %d synch fail action: %d \n", init_info->sync_timeout,
init_info->sync_fail_action);
printf("Sync Mac address : \t");
printf("%s\n", wl_ether_etoa(&(init_info->sync_mac)));
printf("Tx Mac address : \t");
printf("%s\n", wl_ether_etoa(&(init_info->tx_mac)));
printf("Rx Mac address : \t");
printf("%s\n", wl_ether_etoa(&(init_info->rx_mac)));
printf("Test in Loop mode : %d \n", init_info->loop_test);
printf("\n\n\n");
}
static void
wl_ota_display_rt_info(uint8 rate)
{
#ifndef D11AC_IOTYPES
if (rate & NRATE_MCS_INUSE) {
printf("m");
printf("%d ", rate & NRATE_RATE_MASK);
} else if (rate == 11) {
printf("5.5 ");
} else {
printf("%d ", (rate & NRATE_RATE_MASK) / 2);
}
#else
printf("format changed : fix it %x \n", rate);
#endif
}
/* display nth tesr arg details */
static void
wl_ota_display_test_option(wl_ota_test_args_t *test_arg, int16 cnt)
{
uint8 i;
printf("Test cnt %d \n", cnt);
printf("-----------------------------------------------------------\n");
printf("Curr Test : %s\n", ((test_arg->cur_test == 0) ? "TX" : "RX"));
printf("Wait for sync enabled %d \n", test_arg->wait_for_sync);
printf("Channel : %d", test_arg->chan);
printf("\t Bandwidth : %s ", ((test_arg->bw == WL_OTA_TEST_BW_20MHZ) ? "20" :
((test_arg->bw == WL_OTA_TEST_BW_40MHZ) ? "40" : "20 in 40")));
printf("\t Control Band : %c \n", test_arg->control_band);
printf("Rates : ");
for (i = 0; i < test_arg->rt_info.rate_cnt; i++)
wl_ota_display_rt_info(test_arg->rt_info.rate_val_mbps[i]);
printf("\nStf mode : %d \n", test_arg->stf_mode);
printf("Txant: %d rxant: %d \n", test_arg->txant, test_arg->rxant);
printf("Pkt eng Options : ifs %d len: %d num: %d \n", test_arg->pkteng.delay,
test_arg->pkteng.length, test_arg->pkteng.nframes);
printf("Tx power sweep options :\nPower control %d \nstart pwr: %d "
"delta: %d end pwr : %d \n", test_arg->pwr_info.pwr_ctrl_on,
test_arg->pwr_info.start_pwr, test_arg->pwr_info.delta_pwr,
test_arg->pwr_info.end_pwr);
}
/* do minimum string validations possible */
/* Make stricter conditions in future */
static int
wl_ota_validate_string(uint8 arg, void* value)
{
int ret = 0;
switch (arg) {
case WL_OTA_TXANTVALID:
case WL_OTA_RXANTVALID:
if (*(uint8*)value > 3)
ret = -1;
break;
case WL_OTA_CTRLBANDVALID:
if ((strncmp((char *)value, "l", 1)) && (strncmp((char *)value, "u", 1)))
ret = -1;
break;
case WL_OTA_PWRCTRLVALID:
case WL_OTA_SYNCFAILACTION:
if ((*(int8 *)value < -1) || (*(int8 *)value > 1))
ret = -1;
break;
default:
break;
}
return ret;
}
/* convert power info string to integer */
/* start:delta:end */
static int
wl_ota_pwrinfo_parse(const char *tok_bkp, wl_ota_test_args_t *test_arg)
{
char *endptr = NULL;
int ret = 0;
/* convert string to int */
/* Read start pwr */
test_arg->pwr_info.start_pwr = (int8)strtol(tok_bkp, &endptr, 10);
if (*endptr == ':') {
endptr++;
tok_bkp = endptr;
} else {
return -1;
}
/* read delta pwr */
test_arg->pwr_info.delta_pwr = (int8)strtol(tok_bkp, &endptr, 10);
if (*endptr == ':') {
endptr++;
tok_bkp = endptr;
} else {
return -1;
}
/* read end pwr */
test_arg->pwr_info.end_pwr = (int8)strtol(tok_bkp, &endptr, 10);
if ((*endptr != '\0') && (*endptr != '\n') && (*endptr != ' '))
ret = -1;
return ret;
}
/* parsing the test init seq line */
static int
wl_ota_parse_test_init(wl_ota_test_vector_t * init_info, char * tok, uint16 cnt)
{
int ret = 0;
char * endptr = NULL;
switch (cnt) {
case WL_OTA_SYNC_TIMEOUT:
init_info->sync_timeout = (uint8)strtol(tok, &endptr, 10);
if (*endptr != '\0')
ret = -1;
break;
case WL_OTA_SYNCFAIL_ACTION:
init_info->sync_fail_action = (int8)strtol(tok, &endptr, 10);
if (*endptr != '\0') {
ret = -1;
break;
} else {
ret = wl_ota_validate_string(WL_OTA_SYNCFAILACTION,
&(init_info->sync_fail_action));
}
break;
case WL_OTA_SYNC_MAC:
if (!wl_ether_atoe(tok, &(init_info->sync_mac)))
ret = -1;
break;
case WL_OTA_TX_MAC:
if (!wl_ether_atoe(tok, &(init_info->tx_mac)))
ret = -1;
break;
case WL_OTA_RX_MAC:
if (!wl_ether_atoe(tok, &(init_info->rx_mac)))
ret = -1;
break;
case WL_OTA_LOOP_TEST:
init_info->loop_test = (int8)strtol(tok, &endptr, 10);
if ((*endptr != '\0') && (*endptr != '\n') && (*endptr != ' '))
ret = -1;
break;
default:
break;
}
return ret;
}
/* parse test arguments */
static int
wl_ota_test_parse_test_option(wl_ota_test_args_t *test_arg, char * tok, uint16 cnt,
char rt_string[])
{
char * endptr = NULL;
uint16 tok_len = 0;
int ret = 0;
if (test_arg->cur_test == WL_OTA_TEST_RX) {
switch (cnt) {
case WL_OTA_PWR_CTRL_ON:
case WL_OTA_PWR_SWEEP:
return 0;
break;
default:
break;
}
}
switch (cnt) {
case WL_OTA_CUR_TEST:
if (strncmp(tok, "ota_tx", 6) == 0)
test_arg->cur_test = WL_OTA_TEST_TX;
else if (strncmp(tok, "ota_rx", 6) == 0)
test_arg->cur_test = WL_OTA_TEST_RX;
else
ret = -1;
break;
case WL_OTA_CHAN:
test_arg->chan = (uint8)strtol(tok, &endptr, 10);
if (*endptr != '\0')
ret = -1;
break;
case WL_OTA_BW:
if (strncmp(tok, "20/40", 5) == 0) {
test_arg->bw = WL_OTA_TEST_BW_20_IN_40MHZ;
} else if (strncmp(tok, "20", 2) == 0) {
test_arg->bw = WL_OTA_TEST_BW_20MHZ;
} else if (strncmp(tok, "40", 2) == 0) {
test_arg->bw = WL_OTA_TEST_BW_40MHZ;
} else {
ret = -1;
}
break;
case WL_OTA_CONTROL_BAND:
test_arg->control_band = *tok;
ret = wl_ota_validate_string(WL_OTA_CTRLBANDVALID, tok);
break;
case WL_OTA_RATE:
tok_len = strlen(tok);
if (tok_len > WL_OTA_STRING_MAX_LEN) {
ret = -1;
goto fail;
}
strncpy(rt_string, tok, tok_len);
break;
case WL_OTA_STF_MODE:
#ifndef D11AC_IOTYPES
if (strncmp(tok, "siso", 4) == 0)
test_arg->stf_mode = NRATE_STF_SISO;
else if (strncmp(tok, "cdd", 3) == 0)
test_arg->stf_mode = NRATE_STF_CDD;
else if (strncmp(tok, "stbc", 4) == 0)
test_arg->stf_mode = NRATE_STF_STBC;
else if (strncmp(tok, "sdm", 3) == 0)
test_arg->stf_mode = NRATE_STF_SDM;
else
ret = -1;
#endif
break;
case WL_OTA_TXANT:
test_arg->txant = (uint8)strtol(tok, &endptr, 10);
if (*endptr != '\0') {
ret = -1;
goto fail;
}
ret = wl_ota_validate_string(WL_OTA_TXANTVALID, &test_arg->txant);
break;
case WL_OTA_RXANT:
test_arg->rxant = (uint8)strtol(tok, &endptr, 10);
if (*endptr != '\0') {
ret = -1;
goto fail;
}
ret = wl_ota_validate_string(WL_OTA_RXANTVALID, &test_arg->rxant);
break;
case WL_OTA_TX_IFS:
test_arg->pkteng.delay = (uint16)strtol(tok, &endptr, 10);
if (*endptr != '\0')
ret = -1;
break;
case WL_OTA_TX_PKT_LEN:
test_arg->pkteng.length = (uint16)strtol(tok, &endptr, 10);
if (*endptr != '\0')
ret = -1;
break;
case WL_OTA_TX_NUM_PKT:
test_arg->pkteng.nframes = (uint16)strtol(tok, &endptr, 10);
if ((*endptr != '\0') && (*endptr != '\n') && (*endptr != ' '))
ret = -1;
break;
case WL_OTA_PWR_CTRL_ON:
test_arg->pwr_info.pwr_ctrl_on = (int8)strtol(tok, &endptr, 10);
if (*endptr != '\0') {
ret = -1;
goto fail;
}
ret = wl_ota_validate_string(WL_OTA_PWRCTRLVALID,
&test_arg->pwr_info.pwr_ctrl_on);
break;
case WL_OTA_PWR_SWEEP:
ret = wl_ota_pwrinfo_parse(tok, test_arg);
default:
break;
}
fail:
return ret;
}
static int
wl_ota_test_parse_rate_string(wl_ota_test_args_t *test_arg, char rt_string[100])
{
uint8 cnt = 0;
char * tok = NULL;
char rate_st[5] = "\0";
uint8 int_val = 0;
uint16 tok_len = 0;
int ret = 0;
tok = strtok(rt_string, ",");
/* convert rate strings to int array */
while (tok != NULL) {
strncpy(rate_st, " ", 4);
/* skip rate parsing if its rx test case */
if (test_arg->cur_test == WL_OTA_TEST_RX) {
test_arg->rt_info.rate_val_mbps[cnt] = 0;
cnt = 1;
break;
}
/* Support a max of 30 rates */
if (cnt >= WL_OTA_TEST_MAX_NUM_RATE) {
ret = -1;
break;
}
tok_len = strlen(tok);
if (tok_len > 5) {
ret = -1;
break;
}
strncpy(rate_st, tok, tok_len);
if (strncmp(rate_st, "5.5", 3) == 0) {
int_val = 11;
} else {
if (rate_st[0] == 'm') {
rate_st[0] = ' ';
int_val = atoi(rate_st);
#ifndef D11AC_IOTYPES
int_val |= NRATE_MCS_INUSE;
#endif
} else {
int_val = 2 * atoi(rate_st);
}
}
test_arg->rt_info.rate_val_mbps[cnt] = int_val;
tok = strtok(NULL, ",");
cnt++;
}
test_arg->rt_info.rate_cnt = cnt;
return ret;
}
static int
wl_ota_test_parse_arg(char line[], wl_ota_test_vector_t *ota_test_vctr, uint16 *test_cnt,
uint8 *ota_sync_found)
{
char * tok = NULL;
char rt_string[WL_OTA_STRING_MAX_LEN] = "\0";
uint16 cnt = 0;
int ret = 0;
tok = strtok(line, " ");
/* Initialize the power arguments */
ota_test_vctr->test_arg[*test_cnt].pwr_info.pwr_ctrl_on = -1;
ota_test_vctr->test_arg[*test_cnt].pwr_info.start_pwr = -1;
ota_test_vctr->test_arg[*test_cnt].pwr_info.delta_pwr = -1;
ota_test_vctr->test_arg[*test_cnt].pwr_info.end_pwr = -1;
if (!strncmp(tok, "test_setup", 10)) {
/* Parse test setup details */
cnt = 0;
while (tok != NULL) {
if ((ret = wl_ota_parse_test_init(ota_test_vctr, tok, cnt)) != 0)
return ret;
tok = strtok(NULL, " ");
cnt++;
}
} else if (!(strncmp(tok, "ota_tx", 6)) || (!strncmp(tok, "ota_rx", 6))) {
/* parse tx /rx test argumenst */
cnt = 0;
while (tok != NULL) {
if ((ret = wl_ota_test_parse_test_option
(&(ota_test_vctr->test_arg[*test_cnt]),
tok, cnt, rt_string)) != 0) {
goto fail;
}
tok = strtok(NULL, " ");
cnt++;
}
/* split rate string into integer array */
if ((ret = wl_ota_test_parse_rate_string(&(ota_test_vctr->test_arg[*test_cnt]),
rt_string)) != 0) {
goto fail;
}
/* Add sync option if specified by user */
ota_test_vctr->test_arg[*test_cnt].wait_for_sync = (*ota_sync_found);
/* Reset ota_sync_found for next test arg */
*ota_sync_found = 0;
/* Increment test cnt */
*test_cnt = *test_cnt + 1;
} else if (strncmp(tok, "ota_sync", 8) == 0) {
/* detect if a sync packet is required */
*ota_sync_found = 1;
ret = 0;
}
fail:
return (ret);
}
static int
wl_load_cmd_stream(void *wl, cmd_t *cmd, char **argv)
{
int ret = -1;
char test_arg[WL_OTA_CMDSTREAM_MAX_LEN] = "\0";
uint16 test_cnt = 0;
uint8 * ptr1 = NULL;
uint8 i, num_loop = 0;
uint8 ota_sync_found = 0;
wl_seq_cmd_pkt_t *next_cmd;
wl_ota_test_vector_t *ota_test_vctr = NULL;
argv++;
if (*argv == NULL) {
return ret;
} else if (!strncmp(argv[0], "start", 5)) {
ret = wl_seq_start(wl, cmd, argv);
} else if (!strncmp(argv[0], "stop", 4)) {
ret = 0;
/* test info pointer */
ota_test_vctr = (wl_ota_test_vector_t *)malloc(sizeof(wl_ota_test_vector_t));
if (ota_test_vctr == NULL) {
fprintf(stderr, "Failed to allocate %d bytes of memory \n",
(uint16)sizeof(wl_ota_test_vector_t));
return BCME_NOMEM;
}
/* Assign a new pointer so that byte wise operation is possible */
ptr1 = (uint8 *)ota_test_vctr;
/* Passing test structure to dongle happens in steps */
/* For OTA implementations its split up into chunks of 1200 bytes */
num_loop = sizeof(wl_ota_test_vector_t) / WL_OTA_ARG_PARSE_BLK_SIZE;
if (!cmd_batching_mode) {
printf("calling ota_stream stop when it's already out of batching mode\n");
ret = BCME_ERROR;
goto fail;
}
cmd_batching_mode = FALSE;
next_cmd = cmd_list.head;
if (next_cmd == NULL) {
printf("no command batched\n");
ret = 0;
goto fail;
}
test_cnt = 0;
while (next_cmd != NULL) {
/* Max number of test options is ARRAYSIZE(ota_test_vctr->test_arg) */
if (test_cnt == ARRAYSIZE(ota_test_vctr->test_arg))
break;
if ((ret = wl_ota_test_parse_arg(next_cmd->data, ota_test_vctr, &test_cnt,
&ota_sync_found)) != 0) {
printf("Error Parsing the test command \n");
ret = BCME_BADARG;
goto fail;
}
next_cmd = next_cmd->next;
}
ota_test_vctr->test_cnt = test_cnt;
/* Full size of wl_ota_test_vector_t can not be parse through wl */
/* max size whihc can be passed from host to dongle is limited by eth size */
for (i = 0; i <= num_loop; i++) {
/* pass on the test info to wl->test_info structure */
if ((ret = wlu_var_setbuf(wl, "ota_loadtest", ptr1 + i *
WL_OTA_ARG_PARSE_BLK_SIZE, WL_OTA_ARG_PARSE_BLK_SIZE)) < 0) {
fprintf(stderr, "host to dongle download failed to pass %d"
"bytes in stage %d \n",
WL_OTA_ARG_PARSE_BLK_SIZE, i);
}
}
fail:
clean_up_cmd_list();
free(ota_test_vctr);
} else {
while (*argv) {
strncat(test_arg, *argv, strlen(*argv));
strncat(test_arg, " ", 1);
argv++;
}
return add_one_batched_cmd(WLC_SET_VAR, test_arg, strlen(test_arg));
}
return ret;
}
static int
wl_ota_loadtest(void *wl, cmd_t *cmd, char **argv)
{
int ret = -1;
FILE *fp;
const char *fname = "ota_test.txt";
char line[WL_OTA_CMDSTREAM_MAX_LEN] = "\0";
char line_bkp[WL_OTA_CMDSTREAM_MAX_LEN] = "\0";
uint16 test_cnt = 0;
uint8 * ptr1 = NULL;
uint8 i, num_loop = 0;
uint8 ota_sync_found = 0;
wl_ota_test_vector_t *ota_test_vctr = NULL;
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd);
/* Read the file name */
if (argv[1]) {
fname = argv[1];
}
/* test info pointer */
ota_test_vctr = (wl_ota_test_vector_t *)malloc(sizeof(wl_ota_test_vector_t));
if (ota_test_vctr == NULL) {
fprintf(stderr, "Failed to allocate %d bytes of memory \n",
(uint16)sizeof(wl_ota_test_vector_t));
return BCME_NOMEM;
}
/* Assign a new pointer so that byte wide operation is possible */
ptr1 = (uint8 *)ota_test_vctr;
/* find number of iterations required to parse full block form host to dongle */
num_loop = sizeof(wl_ota_test_vector_t) / WL_OTA_ARG_PARSE_BLK_SIZE;
/* open the flow file */
if ((fp = fopen(fname, "r")) == NULL) {
fprintf(stderr, "Problem opening file %s\n", fname);
free(ota_test_vctr);
return BCME_BADARG;
}
test_cnt = 0;
while (1) {
fgets(line, WL_OTA_CMDSTREAM_MAX_LEN - 1, fp);
if (feof(fp)) {
break;
}
/* Max number of test options is ARRAYSIZE(ota_test_vctr->test_arg) */
if (test_cnt == ARRAYSIZE(ota_test_vctr->test_arg))
break;
strncpy(line_bkp, line, WL_OTA_CMDSTREAM_MAX_LEN - 1);
if ((ret = wl_ota_test_parse_arg(line_bkp, ota_test_vctr,
&test_cnt, &ota_sync_found)) != 0) {
printf("Flow File Error: \nError Parsing string : %s \n", line);
ret = BCME_BADARG;
goto fail;
}
}
if (ota_sync_found) {
ret = -1;
printf("Flow File Error : \nFile can not end with ota_sync\n");
goto fail;
}
ota_test_vctr->test_cnt = test_cnt;
/* Full size of wl_ota_test_vector_t can not be parse through wl */
/* max size whihc can be passed from host to dongle is limited by eth size */
for (i = 0; i <= num_loop; i++) {
/* pass on the test info to wl->test_info structure */
if ((ret = wlu_var_setbuf(wl, cmd->name, ptr1 + i * WL_OTA_ARG_PARSE_BLK_SIZE,
WL_OTA_ARG_PARSE_BLK_SIZE)) < 0) {
fprintf(stderr, "host to dongle download failed to pass %d"
"bytes in stage %d \n",
WL_OTA_ARG_PARSE_BLK_SIZE, i);
break;
}
}
fail:
/* close the fp */
if (fp)
fclose(fp);
free(ota_test_vctr);
return ret;
}
static void
wl_otatest_display_skip_test_reason(int8 skip_test_reason)
{
switch (skip_test_reason) {
case 0 :
printf("Test successfully finished\n");
break;
case WL_OTA_SKIP_TEST_CAL_FAIL:
printf("Phy cal Failure \n");
break;
case WL_OTA_SKIP_TEST_SYNCH_FAIL:
printf("Sync Packet failure \n");
break;
case WL_OTA_SKIP_TEST_FILE_DWNLD_FAIL:
printf("File download Failure \n");
break;
case WL_OTA_SKIP_TEST_NO_TEST_FOUND:
printf("No test found in the flow file \n");
break;
case WL_OTA_SKIP_TEST_WL_NOT_UP:
printf("WL Not UP \n");
break;
case WL_OTA_SKIP_TEST_UNKNOWN_CALL:
printf("Erroneous scheduling of test. Not intended \n");
break;
default:
printf("Unknown test state \n");
break;
}
}
static int
wl_otatest_status(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
int16 cnt = 0;
wl_ota_test_status_t *test_status = NULL;
wl_ota_test_vector_t *ota_test_vctr = NULL;
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd);
test_status = (wl_ota_test_status_t *)buf;
if (argv[1]) {
cnt = atoi(argv[1]);
if ((cnt < 1) || ((uint16)cnt > ARRAYSIZE(ota_test_vctr->test_arg))) {
printf("Error, Out of range \n");
return BCME_RANGE;
}
/* read nth test arg details */
if ((ret = wlu_iovar_getbuf(wl, cmd->name, &cnt, sizeof(uint16),
buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
if (cnt > (test_status->test_cnt)) {
printf("Error : Number of test seq downloaded %d \n",
test_status->test_cnt);
return BCME_RANGE;
}
/* Display Test init info */
wl_ota_display_test_init_info(test_status);
/* Dsiplay test arg info */
wl_ota_display_test_option(&(test_status->test_arg), cnt);
} else {
/* read back current state */
if ((ret = wlu_iovar_getbuf(wl, cmd->name, NULL, 0,
buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
cnt = test_status->cur_test_cnt;
switch (test_status->test_stage) {
case WL_OTA_TEST_IDLE: /* Idle state */
printf("Init state \n");
break;
case WL_OTA_TEST_ACTIVE: /* Active test state */
/* Read back details for current test arg */
cnt++;
ret = wlu_iovar_getbuf(wl, cmd->name, &cnt, sizeof(uint16),
buf, WLC_IOCTL_MAXLEN);
if (test_status->sync_status == WL_OTA_SYNC_ACTIVE)
printf("Waiting for sync \n");
else
wl_ota_display_test_option(&(test_status->test_arg), cnt);
break;
case WL_OTA_TEST_SUCCESS: /* Test Finished */
printf("Test completed \n");
break;
case WL_OTA_TEST_FAIL: /* Test Failed to complete */
wl_otatest_display_skip_test_reason(test_status->skip_test_reason);
break;
default:
printf("Invalid test Phase \n");
break;
}
}
return ret;
}
/* To stop the ota test suite */
static int
wl_ota_teststop(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(argv);
return (wlu_iovar_setint(wl, cmd->name, 1));
}
/* WLOTA_EN END */
static int
wl_ampdu_activate_test(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_activate_test";
struct agg {
bool val1;
bool val2;
} x;
int err = 0;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
x.val1 = atoi(param);
if ((param = *++argv)) {
x.val2 = atoi(param);
printf("%d %d\n", x.val1, x.val2);
err = wlu_var_setbuf(wl, cmdname, &x, sizeof(x));
}
return err;
}
static int
wl_ampdu_tid(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_tid";
struct ampdu_tid_control atc, *reply;
uint8 tid;
int err;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
tid = atoi(param);
if (tid > MAXPRIO)
return BCME_USAGE_ERROR;
atc.tid = tid;
if ((param = *++argv)) {
atc.enable = atoi(param);
err = wlu_var_setbuf(wl, cmdname, &atc, sizeof(atc));
} else {
if ((err = wlu_var_getbuf_sm(wl, cmdname, &atc, sizeof(atc), &ptr) < 0))
return err;
reply = (struct ampdu_tid_control *)ptr;
printf("AMPDU for tid %d: %d\n", tid, reply->enable);
}
return err;
}
static int
wl_ampdu_retry_limit_tid(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_retry_limit_tid";
struct ampdu_retry_tid retry_limit, *reply;
uint8 tid;
int err;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
tid = atoi(param);
if (tid > MAXPRIO)
return BCME_USAGE_ERROR;
retry_limit.tid = tid;
if ((param = *++argv)) {
retry_limit.retry = atoi(param);
err = wlu_var_setbuf(wl, cmdname, &retry_limit, sizeof(retry_limit));
} else {
if ((err = wlu_var_getbuf(wl, cmdname, &retry_limit,
sizeof(retry_limit), &ptr)) < 0)
return err;
reply = (struct ampdu_retry_tid *)ptr;
printf("AMPDU retry limit for tid %d: %d\n", tid, reply->retry);
}
return err;
}
static int
wl_ampdu_rr_retry_limit_tid(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_rr_retry_limit_tid";
struct ampdu_retry_tid retry_limit, *reply;
uint8 tid;
int err;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
tid = atoi(param);
if (tid > MAXPRIO)
return BCME_USAGE_ERROR;
retry_limit.tid = tid;
if ((param = *++argv)) {
retry_limit.retry = atoi(param);
err = wlu_var_setbuf(wl, cmdname, &retry_limit, sizeof(retry_limit));
} else {
if ((err = wlu_var_getbuf(wl, cmdname, &retry_limit,
sizeof(retry_limit), &ptr)) < 0)
return err;
reply = (struct ampdu_retry_tid *)ptr;
printf("AMPDU regular rate retry limit for tid %d: %d\n", tid, reply->retry);
}
return err;
}
static int
wl_ampdu_send_addba(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_send_addba";
struct ampdu_ea_tid aet;
uint8 tid;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
tid = atoi(param);
if (tid > MAXPRIO)
return BCME_USAGE_ERROR;
aet.tid = tid;
argv++;
if (!*argv) {
printf("error: missing address\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &aet.ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmdname, &aet, sizeof(aet));
}
static int
wl_ampdu_send_delba(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "ampdu_send_delba";
struct ampdu_ea_tid aet;
uint8 tid;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
tid = atoi(param);
if (tid > MAXPRIO)
return BCME_USAGE_ERROR;
aet.tid = tid;
argv++;
if (!*argv) {
printf("error: missing address\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &aet.ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmdname, &aet, sizeof(aet));
}
static int
wl_dpt_deny(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "dpt_deny";
dpt_iovar_t info;
UNUSED_PARAMETER(cmd);
if (!*++argv)
return BCME_USAGE_ERROR;
info.pad = 0;
if (!strcmp("add", *argv))
info.mode = DPT_DENY_LIST_ADD;
else if (!strcmp("remove", *argv))
info.mode = DPT_DENY_LIST_REMOVE;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing mode value\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &info.ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmdname, &info, sizeof(info));
}
static int
wl_dpt_endpoint(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "dpt_endpoint";
dpt_iovar_t info;
UNUSED_PARAMETER(cmd);
if (!*++argv)
return BCME_USAGE_ERROR;
info.pad = 0;
if (!strcmp("create", *argv))
info.mode = DPT_MANUAL_EP_CREATE;
else if (!strcmp("modify", *argv))
info.mode = DPT_MANUAL_EP_MODIFY;
else if (!strcmp("delete", *argv))
info.mode = DPT_MANUAL_EP_DELETE;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing ea\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &info.ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmdname, &info, sizeof(info));
}
#ifdef WLTDLS
static int
wl_tdls_endpoint(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname_tdls = "tdls_endpoint";
tdls_iovar_t info;
chanspec_t chanspec;
if (strcmp(cmd->name, cmdname_tdls)) {
printf("error: invalid command name.\n");
return BCME_USAGE_ERROR;
}
if (!*++argv)
return BCME_USAGE_ERROR;
memset(&info, 0, sizeof(tdls_iovar_t));
if (!strcmp("create", *argv))
info.mode = TDLS_MANUAL_EP_CREATE;
else if (!strcmp("modify", *argv))
info.mode = TDLS_MANUAL_EP_MODIFY;
else if (!strcmp("delete", *argv))
info.mode = TDLS_MANUAL_EP_DELETE;
else if (!strcmp("PM", *argv))
info.mode = TDLS_MANUAL_EP_PM;
else if (!strcmp("wake", *argv))
info.mode = TDLS_MANUAL_EP_WAKE;
else if (!strcmp("disc", *argv))
info.mode = TDLS_MANUAL_EP_DISCOVERY;
else if (!strcmp("cw", *argv)) {
info.mode = TDLS_MANUAL_EP_CHSW;
}
else if (!strcmp("wfd_disc", *argv))
info.mode = TDLS_MANUAL_EP_WFD_TPQ;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing ea\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &info.ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
if (info.mode == TDLS_MANUAL_EP_CHSW) {
argv++;
if (!*argv) {
printf("error: missing target channel number\n");
return BCME_USAGE_ERROR;
}
if (atoi(*argv) != 0) {
chanspec = wf_chspec_aton(*argv);
if (chanspec == 0) {
printf("error: bad chanspec \"%s\".\n", *argv);
return BCME_USAGE_ERROR;
}
chanspec = wl_chspec_to_driver(chanspec);
if (chanspec == INVCHANSPEC) {
return BCME_USAGE_ERROR;
}
info.chanspec = chanspec;
}
}
return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info));
}
static int
wl_pattern_atoh(char *, char *);
#define WFD_DEV 0
#define WFD_DEV_LEN 6
#define WFD_IP 8
#define WFD_IP_LEN 5
#define WFD_ALT_MAC 10
#define WFD_ALT_MAC_LEN 6
static int
wl_tdls_wfd_ie(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname_tdls = "tdls_wfd_ie";
tdls_wfd_ie_iovar_t info;
tdls_wfd_ie_iovar_t* buf_info = (tdls_wfd_ie_iovar_t*) buf;
int ret;
uint8* ptr;
uint8 element, subelement;
uint16 offset;
uint8 buffer[TDLS_WFD_IE_SIZE - (WFA_OUI_LEN + 3)];
uint16 length, element_length, current_length;
bcm_tlv_t * ie;
unsigned long value;
struct ether_addr ea;
struct ipv4_addr ipa_set;
if (strcmp(cmd->name, cmdname_tdls)) {
printf("error: invalid command name.\n");
return BCME_USAGE_ERROR;
}
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp(*argv, "clr")) {
memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
info.mode = TDLS_WFD_IE_TX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info));
} else if (!strcmp(*argv, "get")) {
memset(buf_info, 0, sizeof(*buf_info));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
buf_info->mode = TDLS_WFD_IE_TX;
else if (wl_ether_atoe(*argv, &buf_info->ea))
buf_info->mode = TDLS_WFD_IE_RX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
if ((ret = wlu_iovar_getbuf(wl, cmd->name, buf_info,
sizeof(*buf_info), buf, WLC_IOCTL_MAXLEN)) < 0) {
return ret;
}
/* empty */
if (!buf_info->length)
return ret;
if (!*++argv)
wl_hexdump((uchar *)buf_info->data, buf_info->length);
else {
if (!strcmp("ip", *argv)) {
element = WFD_IP;
element_length = WFD_IP_LEN;
} else if (!strcmp("port", *argv)) {
element = WFD_DEV;
element_length = WFD_DEV_LEN;
} else {
printf("error: unknown element\n");
return BCME_USAGE_ERROR;
}
/* Reassemble the WFD IE (without header) */
ptr = buf_info->data;
length = buf_info->length;
offset = 0;
current_length = 0;
while (length - offset > WFA_OUI_LEN + 3) {
if ((ie = bcm_parse_tlvs(ptr + offset,
length - offset, DOT11_MNG_VS_ID)) != NULL) {
if (ie->len > WFA_OUI_LEN + 1) {
if ((!memcmp(ie->data, WFA_OUI, WFA_OUI_LEN)) &&
(*(ie->data + WFA_OUI_LEN) ==
WFA_OUI_TYPE_WFD)) {
/* WFD */
memcpy(buffer + current_length,
ie->data + WFA_OUI_LEN + 1,
ie->len - WFA_OUI_LEN - 1);
current_length += ie->len - WFA_OUI_LEN - 1;
}
}
offset = (uint16)((uint8*)ie - ptr + ie->len + 2);
}
else
break;
}
/* Find the elements */
ptr = buffer;
length = current_length;
while (length > 3) {
current_length = (ptr[1] << 8) + ptr[2];
if ((ptr[0] == element) && (current_length == element_length) &&
(current_length <= length - 3)) {
switch (element) {
case WFD_IP:
/* we do not care about the IP version i.e. ptr[3] */
printf("%u.%u.%u.%u\n", ptr[4], ptr[5], ptr[6], ptr[7]);
break;
case WFD_DEV:
/* just get the RTSP TCP valid port */
printf("%u\n", (ptr[5] << 8) + ptr[6]);
break;
}
break;
} else {
if (current_length + 3 < length) {
length -= current_length + 3;
ptr += current_length + 3;
} else
break;
}
}
}
return ret;
} else if (!strcmp(*argv, "set")) {
memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
info.mode = TDLS_WFD_IE_TX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing IE string\n");
return BCME_USAGE_ERROR;
}
if (strlen((char*)*argv) - 2 > sizeof(info.data) * 2) {
printf("error: IE string too long; max is %u bytes\n",
(unsigned int)sizeof(info.data));
return BCME_BADARG;
}
ret = wl_pattern_atoh(*argv, (char*)info.data);
if (ret <= 0) {
printf("error: could not parse IE string address %s\n", *argv);
return BCME_USAGE_ERROR;
}
info.length = ret;
if (*++argv) {
/* IP specified */
/* watchdog */
if (info.length != 32) {
printf(
"if one or several set fields are used, "
"the following the IE string must be\n"
"exactly 32 bytes and must have the following order:\n"
"\t6-byte header (0xDD1E506F9A0A)\n"
"\t9-byte subelement 0 (WFD device information)\n"
"\t9-byte subelement 1 (BSSID)\n"
"\t8-byte subelement 8 (IP address)\n");
return BCME_USAGE_ERROR;
}
if (!wl_atoip(*argv, &ipa_set))
return BCME_USAGE_ERROR;
memcpy(&info.data[28], (uint8*) &ipa_set, sizeof(ipa_set));
if (*++argv) {
/* port specified */
value = strtoul(*argv, NULL, 0);
info.data[11] = (uint8) (0xFF & (value >> 8));
info.data[12] = (uint8) (0xFF & value);
if (*++argv) {
/* WFD type (Source or Primary Sink) specified */
element = (uint8) (0x01 & strtoul(*argv, NULL, 0));
if (element)
info.data[10] |= 0x01;
else
info.data[10] &= ~0x01;
if (*++argv) {
/* BSSID specified */
if (!wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
memcpy(&info.data[18], (uint8*) &ea, sizeof(ea));
}
}
}
}
return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info));
} else if (!strcmp(*argv, "clr2")) {
memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
info.mode = TDLS_WFD_PROBE_IE_TX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info));
} else if (!strcmp(*argv, "get2")) {
memset(buf_info, 0, sizeof(*buf_info));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
buf_info->mode = TDLS_WFD_PROBE_IE_TX;
else if (wl_ether_atoe(*argv, &buf_info->ea))
buf_info->mode = TDLS_WFD_PROBE_IE_RX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
if ((ret = wlu_iovar_getbuf(wl, cmd->name, buf_info,
sizeof(*buf_info), buf, WLC_IOCTL_MAXLEN)) < 0) {
return ret;
}
/* empty */
if (!buf_info->length)
return ret;
if (!*++argv)
wl_hexdump((uchar *)buf_info->data, buf_info->length);
else {
if (!strcmp("alt_mac", *argv)) {
element = WFD_ALT_MAC;
element_length = WFD_ALT_MAC_LEN;
} else if (!strcmp("port", *argv)) {
element = WFD_DEV;
element_length = WFD_DEV_LEN;
subelement = 1;
} else if (!strcmp("PC_bit", *argv)) {
element = WFD_DEV;
element_length = WFD_DEV_LEN;
subelement = 0;
} else {
printf("error: unknown element\n");
return BCME_USAGE_ERROR;
}
/* Reassemble the WFD IE (without header) */
ptr = buf_info->data;
length = buf_info->length;
offset = 0;
current_length = 0;
while (length - offset > WFA_OUI_LEN + 3) {
if ((ie = bcm_parse_tlvs(ptr + offset,
length - offset, DOT11_MNG_VS_ID)) != NULL) {
if (ie->len > WFA_OUI_LEN + 1) {
if ((!memcmp(ie->data, WFA_OUI, WFA_OUI_LEN)) &&
(*(ie->data + WFA_OUI_LEN) ==
WFA_OUI_TYPE_WFD)) {
/* WFD */
memcpy(buffer + current_length,
ie->data + WFA_OUI_LEN + 1,
ie->len - WFA_OUI_LEN - 1);
current_length += ie->len - WFA_OUI_LEN - 1;
}
}
offset = (uint16)((uint8*)ie - ptr + ie->len + 2);
}
else
break;
}
/* Find the elements */
ptr = buffer;
length = current_length;
while (length > 3) {
current_length = (ptr[1] << 8) + ptr[2];
if ((ptr[0] == element) && (current_length == element_length) &&
(current_length <= length - 3)) {
switch (element) {
case WFD_ALT_MAC:
printf("%02X:%02X:%02X:%02X:%02X:%02X\n",
ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], ptr[8]);
break;
case WFD_DEV:
if (subelement)
/* just get the RTSP TCP valid port */
printf("%u\n", (ptr[5] << 8) + ptr[6]);
else
/* just get the Preferred Connection bit */
printf("%u\n", ptr[4] >> 7);
break;
}
break;
} else {
if (current_length + 3 < length) {
length -= current_length + 3;
ptr += current_length + 3;
} else
break;
}
}
}
return ret;
} else if (!strcmp(*argv, "set2")) {
memset(&info, 0, sizeof(tdls_wfd_ie_iovar_t));
if (!*++argv)
return BCME_USAGE_ERROR;
if (!strcmp("own", *argv))
info.mode = TDLS_WFD_PROBE_IE_TX;
else {
printf("error: invalid mode string\n");
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing IE string\n");
return BCME_USAGE_ERROR;
}
if (strlen((char*)*argv) - 2 > sizeof(info.data) * 2) {
printf("error: IE string too long; max is %u bytes\n",
(unsigned int)sizeof(info.data));
return BCME_USAGE_ERROR;
}
ret = wl_pattern_atoh(*argv, (char*)info.data);
if (ret <= 0) {
printf("error: could not parse IE string address %s\n", *argv);
return BCME_USAGE_ERROR;
}
info.length = ret;
if (*++argv) {
/* alt MAC specified */
/* watchdog */
if (info.length != 24) {
printf(
"if one or several set2 fields are used, "
"the following the IE string must be\n"
"exactly 24 bytes and must have the following order:\n"
"\t6-byte header (0xDD16506F9A0A)\n"
"\t9-byte subelement 0 (WFD device information)\n"
"\t9-byte subelement 10 (alternate MAC address)\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
memcpy(&info.data[18], (uint8*) &ea, sizeof(ea));
if (*++argv) {
/* port specified */
value = strtoul(*argv, NULL, 0);
info.data[11] = (uint8) (0xFF & (value >> 8));
info.data[12] = (uint8) (0xFF & value);
if (*++argv) {
/* WFD type (Source or Primary Sink) specified */
element = (uint8) (0x01 & strtoul(*argv, NULL, 0));
if (element)
info.data[10] |= 0x01;
else
info.data[10] &= ~0x01;
}
}
}
return wlu_var_setbuf(wl, cmd->name, &info, sizeof(info));
} else {
printf("error: unknown operation\n");
return BCME_USAGE_ERROR;
}
}
#endif /* WLTDLS */
static int
wl_actframe(void *wl, cmd_t *cmd, char **argv)
{
wl_action_frame_t * action_frame;
wl_af_params_t * af_params;
struct ether_addr ea;
int argc;
int err = 0;
UNUSED_PARAMETER(cmd);
if (!argv[1] || !argv[2]) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if ((af_params = (wl_af_params_t *) malloc(WL_WIFI_AF_PARAMS_SIZE)) == NULL) {
printf("wl_actframe: unable to allocate frame \n");
return BCME_NOMEM;
}
af_params->channel = 0;
af_params->dwell_time = -1;
action_frame = &af_params->action_frame;
/* Add the packet Id */
action_frame->packetId = (uint32)(uintptr)action_frame;
/* convert the ea string into an ea struct */
if (!wl_ether_atoe(argv[1], &ea)) {
free(af_params);
printf(" ERROR: no valid ether addr provided\n");
return BCME_USAGE_ERROR;
}
memcpy(&action_frame->da, (char*)&ea, ETHER_ADDR_LEN);
/* set default BSSID */
memcpy(&af_params->BSSID, (char*)&ea, ETHER_ADDR_LEN);
/* add the length */
if (argv[2]) {
action_frame->len = htod16(strlen(argv[2])) / 2;
}
/* add the channel */
if (argc > 3 && argv[3]) {
af_params->channel = htod32(atoi(argv[3]));
}
/* add the dwell_time */
if (argc > 4 && argv[4]) {
af_params->dwell_time = htod32(atoi(argv[4]));
}
/* add the BSSID */
if (argc > 5 && argv[5]) {
if (!wl_ether_atoe(argv[5], &ea)) {
free(af_params);
printf(" ERROR: no valid ether addr provided\n");
return BCME_USAGE_ERROR;
}
memcpy(&af_params->BSSID, (char*)&ea, ETHER_ADDR_LEN);
}
if ((err = get_ie_data ((uchar *)argv[2],
&action_frame->data[0],
action_frame->len))) {
free(af_params);
fprintf(stderr, "Error parsing data arg\n");
return err;
}
err = wlu_var_setbuf(wl, "actframe", af_params, WL_WIFI_AF_PARAMS_SIZE);
free(af_params);
return (err);
}
static int
wl_dpt_pmk(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "dpt_pmk";
wsec_pmk_t psk;
size_t key_len;
UNUSED_PARAMETER(cmd);
if (!*++argv)
return BCME_USAGE_ERROR;
key_len = strlen(*argv);
if (key_len < WSEC_MIN_PSK_LEN || key_len > WSEC_MAX_PSK_LEN) {
fprintf(stderr, "passphrase must be between %d and %d characters long\n",
WSEC_MIN_PSK_LEN, WSEC_MAX_PSK_LEN);
return BCME_BADARG;
}
psk.key_len = htod16((ushort) key_len);
psk.flags = htod16(WSEC_PASSPHRASE);
memcpy(psk.key, *argv, key_len);
return wlu_var_setbuf(wl, cmdname, &psk, sizeof(wsec_pmk_t));
}
static int
wl_dpt_fname(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "dpt_fname";
int err;
dpt_fname_t fname, *reply;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv)) {
fname.len = strlen(param);
if (fname.len >= (DPT_FNAME_LEN - 1)) {
fprintf(stderr, "Name must be less than 32 characters\n");
return BCME_BADARG;
}
memcpy(fname.name, param, fname.len);
fname.name[fname.len] = '\0';
err = wlu_var_setbuf(wl, cmdname, &fname, sizeof(fname));
} else {
if ((err = wlu_var_getbuf(wl, cmdname, &fname, sizeof(fname), &ptr) < 0))
return err;
reply = (dpt_fname_t *)ptr;
printf("%s\n", reply->name);
}
return err;
}
static int
wl_dpt_list(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint32 i;
dpt_list_t *list;
if (*++argv)
return BCME_USAGE_ERROR;
strcpy(buf, cmd->name);
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
list = (dpt_list_t *) buf;
printf("List of DPT connections:\n");
for (i = 0; i < list->num; i++) {
printf("%s: status 0x%x rx %d tx %d rssi %d\n",
list->status[i].name,
list->status[i].status,
list->status[i].sta.rx_ucast_pkts,
list->status[i].sta.tx_pkts,
list->status[i].rssi);
}
return err;
}
#ifdef WLBTAMP
#define MATCH_OP(op, opstr) (strlen(op) == strlen(opstr) && strncmp(op, opstr, strlen(op)) == 0)
static int
wl_HCI_cmd(void *wl, cmd_t *cmd, char **argv)
{
union {
char buf[HCI_CMD_PREAMBLE_SIZE + HCI_CMD_DATA_SIZE];
uint32 alignme;
} cbuf;
amp_hci_cmd_t *cpkt = (amp_hci_cmd_t *)&cbuf.buf[0];
char *op;
uint8 plen;
UNUSED_PARAMETER(cmd);
if (!*++argv)
return BCME_USAGE_ERROR;
/* recognize and encode operations */
op = *argv++;
if (MATCH_OP(op, "Read_Link_Quality")) {
cpkt->opcode = HCI_Read_Link_Quality;
} else if (MATCH_OP(op, "Read_Local_AMP_Info")) {
cpkt->opcode = HCI_Read_Local_AMP_Info;
} else if (MATCH_OP(op, "Read_Local_AMP_ASSOC")) {
cpkt->opcode = HCI_Read_Local_AMP_ASSOC;
} else if (MATCH_OP(op, "Write_Remote_AMP_ASSOC")) {
cpkt->opcode = HCI_Write_Remote_AMP_ASSOC;
} else if (MATCH_OP(op, "Create_Physical_Link")) {
cpkt->opcode = HCI_Create_Physical_Link;
} else if (MATCH_OP(op, "Accept_Physical_Link_Request")) {
cpkt->opcode = HCI_Accept_Physical_Link_Request;
} else if (MATCH_OP(op, "Disconnect_Physical_Link")) {
cpkt->opcode = HCI_Disconnect_Physical_Link;
} else if (MATCH_OP(op, "Create_Logical_Link")) {
cpkt->opcode = HCI_Create_Logical_Link;
} else if (MATCH_OP(op, "Accept_Logical_Link")) {
cpkt->opcode = HCI_Accept_Logical_Link;
} else if (MATCH_OP(op, "Disconnect_Logical_Link")) {
cpkt->opcode = HCI_Disconnect_Logical_Link;
} else if (MATCH_OP(op, "Logical_Link_Cancel")) {
cpkt->opcode = HCI_Logical_Link_Cancel;
} else if (MATCH_OP(op, "Short_Range_Mode")) {
cpkt->opcode = HCI_Short_Range_Mode;
} else if (MATCH_OP(op, "Read_Connection_Accept_Timeout")) {
cpkt->opcode = HCI_Read_Connection_Accept_Timeout;
} else if (MATCH_OP(op, "Write_Connection_Accept_Timeout")) {
cpkt->opcode = HCI_Write_Connection_Accept_Timeout;
} else if (MATCH_OP(op, "Read_Link_Supervision_Timeout")) {
cpkt->opcode = HCI_Read_Link_Supervision_Timeout;
} else if (MATCH_OP(op, "Write_Link_Supervision_Timeout")) {
cpkt->opcode = HCI_Write_Link_Supervision_Timeout;
} else if (MATCH_OP(op, "Reset")) {
cpkt->opcode = HCI_Reset;
} else if (MATCH_OP(op, "Enhanced_Flush")) {
cpkt->opcode = HCI_Enhanced_Flush;
} else if (MATCH_OP(op, "Read_Best_Effort_Flush_Timeout")) {
cpkt->opcode = HCI_Read_Best_Effort_Flush_Timeout;
} else if (MATCH_OP(op, "Write_Best_Effort_Flush_Timeout")) {
cpkt->opcode = HCI_Write_Best_Effort_Flush_Timeout;
} else if (MATCH_OP(op, "Read_Logical_Link_Accept_Timeout")) {
cpkt->opcode = HCI_Read_Logical_Link_Accept_Timeout;
} else if (MATCH_OP(op, "Write_Logical_Link_Accept_Timeout")) {
cpkt->opcode = HCI_Write_Logical_Link_Accept_Timeout;
} else if (MATCH_OP(op, "Read_Buffer_Size")) {
cpkt->opcode = HCI_Read_Buffer_Size;
} else if (MATCH_OP(op, "Read_Data_Block_Size")) {
cpkt->opcode = HCI_Read_Data_Block_Size;
} else if (MATCH_OP(op, "Set_Event_Mask_Page_2")) {
cpkt->opcode = HCI_Set_Event_Mask_Page_2;
} else if (MATCH_OP(op, "Flow_Spec_Modify")) {
cpkt->opcode = HCI_Flow_Spec_Modify;
} else if (MATCH_OP(op, "Read_Local_Version_Info")) {
cpkt->opcode = HCI_Read_Local_Version_Info;
} else if (MATCH_OP(op, "Read_Local_Supported_Commands")) {
cpkt->opcode = HCI_Read_Local_Supported_Commands;
} else if (MATCH_OP(op, "Read_Failed_Contact_Counter")) {
cpkt->opcode = HCI_Read_Failed_Contact_Counter;
} else if (MATCH_OP(op, "Reset_Failed_Contact_Counter")) {
cpkt->opcode = HCI_Reset_Failed_Contact_Counter;
} else {
printf("unsupported HCI command: %s\n", op);
return BCME_UNSUPPORTED;
}
plen = 0;
while (*argv && (plen < HCI_CMD_DATA_SIZE)) {
cpkt->parms[plen++] = (uint8)strtol(*argv++, NULL, 0);
}
cpkt->plen = plen;
return wlu_var_setbuf(wl, cmd->name, cpkt, HCI_CMD_PREAMBLE_SIZE + plen);
}
static int
wl_HCI_ACL_data(void *wl, cmd_t *cmd, char **argv)
{
amp_hci_ACL_data_t *dpkt;
uint16 dlen;
int ret;
if (!*++argv)
return BCME_USAGE_ERROR;
dpkt = (amp_hci_ACL_data_t *) malloc(HCI_ACL_DATA_PREAMBLE_SIZE + 2048);
if (!dpkt)
return BCME_NOMEM;
/* get logical link handle */
dpkt->handle = (HCI_ACL_DATA_BC_FLAGS | HCI_ACL_DATA_PB_FLAGS);
dpkt->handle |= (uint16)strtol(*argv++, NULL, 0);
/* get data */
dlen = 0;
while (*argv && (dlen < 2048)) {
dpkt->data[dlen++] = (uint8)strtol(*argv++, NULL, 0);
}
dpkt->dlen = dlen;
ret = wlu_var_setbuf(wl, cmd->name, dpkt, HCI_ACL_DATA_PREAMBLE_SIZE + dlen);
free(dpkt);
return ret;
}
static int
wl_get_btamp_log(void *wl, cmd_t *cmd, char **argv)
{
int err, i, j;
char *val_name;
uint8 *state;
uint8 idx = 0;
void *ptr = buf;
UNUSED_PARAMETER(cmd);
/* command name */
val_name = *argv++;
if (!*argv) {
if ((err = wlu_var_getbuf_sm (wl, cmd->name, NULL, 0, &ptr)))
return err;
state = (uint8 *)ptr;
idx = *state++;
for (i = 0; i < BTA_STATE_LOG_SZ; i++, idx--) {
j = (idx & (BTA_STATE_LOG_SZ - 1));
switch (state[j]) {
case HCIReset:
printf("%2d: HCI Reset\n", state[j]);
break;
case HCIReadLocalAMPInfo:
printf("%2d: HCI Read Local AMPInfo\n", state[j]);
break;
case HCIReadLocalAMPASSOC:
printf("%2d: HCI Read Local AMPASSOC\n", state[j]);
break;
case HCIWriteRemoteAMPASSOC:
printf("%2d: HCI Write Remote AMPASSOC\n", state[j]);
break;
case HCICreatePhysicalLink:
printf("%2d: HCI Create Physical Link\n", state[j]);
break;
case HCIAcceptPhysicalLinkRequest:
printf("%2d: HCI Accept Physical Link Request\n", state[j]);
break;
case HCIDisconnectPhysicalLink:
printf("%2d: HCI Disconnect Physical Link\n", state[j]);
break;
case HCICreateLogicalLink:
printf("%2d: HCI Create Logical Link\n", state[j]);
break;
case HCIAcceptLogicalLink:
printf("%2d: HCI Accept Logical Link\n", state[j]);
break;
case HCIDisconnectLogicalLink:
printf("%2d: HCI Disconnect Logical Link\n", state[j]);
break;
case HCILogicalLinkCancel:
printf("%2d: HCI Logical Link Cancel\n", state[j]);
break;
case HCIAmpStateChange:
printf("%2d: HCI Amp State Change\n", state[j]);
break;
default:
break;
}
}
return 0;
} else
err = wlu_iovar_setint(wl, val_name, (int)idx);
return err;
}
#endif /* WLBTAMP */
/*
* RADAR detection parameter control
*/
static int
wl_radar_args(void *wl, cmd_t *cmd, char **argv)
{
int ret;
wl_radar_args_t ra;
/* Skip the command name */
argv++;
if (*argv == NULL) {
/* Get */
if ((ret = wlu_iovar_get(wl, cmd->name, &ra, sizeof(ra))) < 0)
return ret;
if (ra.version != WL_RADAR_ARGS_VERSION) {
printf("\tIncorrect version of RADAR_ARGS struct: expected %d; got %d\n",
WL_RADAR_ARGS_VERSION, ra.version);
return -1;
}
printf("version %d npulses %d ncontig %d min_pw %d max_pw %d thresh0 0x%x "
"thresh1 0x%x\n",
ra.version, ra.npulses, ra.ncontig, ra.min_pw,
ra.max_pw, ra.thresh0, ra.thresh1);
printf("blank 0x%x fmdemodcfg 0x%x npulses_lp %d min_pw_lp %d "
"max_pw_lp %d\n",
ra.blank, ra.fmdemodcfg, ra.npulses_lp, ra.min_pw_lp,
ra.max_pw_lp);
printf("min_fm_lp %d max_span_lp %d min_deltat %d max_deltat %d\n",
ra.min_fm_lp, ra.max_span_lp, ra.min_deltat, ra.max_deltat);
printf("autocorr 0x%x st_level_time 0x%x t2_min %d fra_pulse_err %d\n",
ra.autocorr, ra.st_level_time, ra.t2_min, ra.fra_pulse_err);
printf("npulses_fra %d npulses_stg2 %d npulses_stg3 %d percal_mask 0x%x quant %d\n",
ra.npulses_fra, ra.npulses_stg2, ra.npulses_stg3, ra.percal_mask,
ra.quant);
printf("min_burst_intv_lp %d max_burst_intv_lp %d nskip_rst_lp %d max_pw_tol %d "
"feature_mask 0x%x\n",
ra.min_burst_intv_lp, ra.max_burst_intv_lp, ra.nskip_rst_lp,
ra.max_pw_tol, ra.feature_mask);
/* this part prints only param values */
printf("%d %d %d %d %d 0x%x "
"0x%x",
ra.version, ra.npulses, ra.ncontig, ra.min_pw,
ra.max_pw, ra.thresh0, ra.thresh1);
printf(" 0x%x 0x%x %d %d "
"%d",
ra.blank, ra.fmdemodcfg, ra.npulses_lp, ra.min_pw_lp,
ra.max_pw_lp);
printf(" %d %d %d %d",
ra.min_fm_lp, ra.max_span_lp, ra.min_deltat, ra.max_deltat);
printf(" 0x%x 0x%x %d %d",
ra.autocorr, ra.st_level_time, ra.t2_min, ra.fra_pulse_err);
printf(" %d %d %d 0x%x %d",
ra.npulses_fra, ra.npulses_stg2, ra.npulses_stg3, ra.percal_mask,
ra.quant);
printf(" %d %d %d %d "
"0x%x\n",
ra.min_burst_intv_lp, ra.max_burst_intv_lp, ra.nskip_rst_lp,
ra.max_pw_tol, ra.feature_mask);
} else {
/* Set */
char *endptr = NULL;
int val_count = 30;
long vals[30];
long *pval;
int i;
for (i = 0; i < val_count; i++, argv++) {
/* verify that there is another arg */
if (*argv == NULL)
return BCME_USAGE_ERROR;
vals[i] = strtol(*argv, &endptr, 0);
/* make sure all the value string was parsed by strtol */
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
pval = vals;
ra.version = *pval++;
ra.npulses = *pval++;
ra.ncontig = *pval++;
ra.min_pw = *pval++;
ra.max_pw = *pval++;
ra.thresh0 = (uint16)*pval++;
ra.thresh1 = (uint16)*pval++;
ra.blank = (uint16)*pval++;
ra.fmdemodcfg = (uint16)*pval++;
ra.npulses_lp = *pval++;
ra.min_pw_lp = *pval++;
ra.max_pw_lp = *pval++;
ra.min_fm_lp = *pval++;
ra.max_span_lp = *pval++;
ra.min_deltat = *pval++;
ra.max_deltat = *pval++;
ra.autocorr = (uint16)*pval++;
ra.st_level_time = (uint16)*pval++;
ra.t2_min = (uint16)*pval++;
ra.fra_pulse_err = (uint32)*pval++;
ra.npulses_fra = (int)*pval++;
ra.npulses_stg2 = (int)*pval++;
ra.npulses_stg3 = (int)*pval++;
ra.percal_mask = (int)*pval++;
ra.quant = (int)*pval++;
ra.min_burst_intv_lp = (uint32)*pval++;
ra.max_burst_intv_lp = (uint32)*pval++;
ra.nskip_rst_lp = (int)*pval++;
ra.max_pw_tol = (int)*pval++;
ra.feature_mask = (uint16)*pval++;
return wlu_var_setbuf(wl, cmd->name, &ra, sizeof(wl_radar_args_t));
}
return ret;
}
static int
wl_radar_thrs(void *wl, cmd_t *cmd, char **argv)
{
int ret = -1;
wl_radar_thr_t radar_thrs;
if (*++argv) {
/* Set */
char *endptr;
int val_count = 12;
uint16 vals[12];
uint16 *pval;
int i;
for (i = 0; i < val_count; i++, argv++) {
/* verify that there is another arg */
if (*argv == NULL)
return BCME_USAGE_ERROR;
vals[i] = (uint16)strtol(*argv, &endptr, 0);
/* make sure all the value string was parsed by strtol */
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
radar_thrs.version = WL_RADAR_THR_VERSION;
/* Order thresh0_20_lo, thresh1_20_lo, thresh0_40_lo, thresh1_40_lo
* thresh0_20_hi, thresh1_20_hi, thresh0_40_hi, thresh1_40_hi
*/
pval = vals;
radar_thrs.thresh0_20_lo = (uint16)*pval++;
radar_thrs.thresh1_20_lo = (uint16)*pval++;
radar_thrs.thresh0_40_lo = (uint16)*pval++;
radar_thrs.thresh1_40_lo = (uint16)*pval++;
radar_thrs.thresh0_80_lo = (uint16)*pval++;
radar_thrs.thresh1_80_lo = (uint16)*pval++;
radar_thrs.thresh0_20_hi = (uint16)*pval++;
radar_thrs.thresh1_20_hi = (uint16)*pval++;
radar_thrs.thresh0_40_hi = (uint16)*pval++;
radar_thrs.thresh1_40_hi = (uint16)*pval++;
radar_thrs.thresh0_80_hi = (uint16)*pval++;
radar_thrs.thresh1_80_hi = (uint16)*pval++;
return wlu_var_setbuf(wl, cmd->name, &radar_thrs, sizeof(wl_radar_thr_t));
}
return ret;
}
static int
wl_dfs_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
wl_dfs_status_t *dfs_status_ptr;
char chanspec_str[CHANSPEC_STR_LEN];
const char *dfs_cacstate_str[WL_DFS_CACSTATES] = {
"IDLE",
"PRE-ISM Channel Availability Check(CAC)",
"In-Service Monitoring(ISM)",
"Channel Switching Announcement(CSA)",
"POST-ISM Channel Availability Check",
"PRE-ISM Ouf Of Channels(OOC)",
"POST-ISM Out Of Channels(OOC)"
};
void *ptr;
UNUSED_PARAMETER(argv);
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
dfs_status_ptr = (wl_dfs_status_t *)ptr;
dfs_status_ptr->state = dtoh32(dfs_status_ptr->state);
dfs_status_ptr->duration = dtoh32(dfs_status_ptr->duration);
dfs_status_ptr->chanspec_cleared = wl_chspec_from_driver(dfs_status_ptr->chanspec_cleared);
if (dfs_status_ptr->state >= WL_DFS_CACSTATES) {
printf("Unknown dfs state %d.\n", dfs_status_ptr->state);
return -1;
}
printf("state %s time elapsed %dms radar channel cleared by dfs ",
dfs_cacstate_str[dfs_status_ptr->state], dfs_status_ptr->duration);
if (dfs_status_ptr->chanspec_cleared) {
printf("channel %s (0x%04X)\n",
wf_chspec_ntoa(dfs_status_ptr->chanspec_cleared, chanspec_str),
dfs_status_ptr->chanspec_cleared);
}
else {
printf("none\n");
}
return ret;
}
static int
wl_wds_wpa_role_old(void *wl, cmd_t *cmd, char **argv)
{
uint remote[2];
uint *sup = remote;
int ret = 0;
UNUSED_PARAMETER(argv);
if ((ret = wlu_get(wl, WLC_WDS_GET_REMOTE_HWADDR, remote, sizeof(remote))) < 0) {
printf("Unable to get remote endpoint's hwaddr\n");
return ret;
}
if ((ret = wlu_get(wl, cmd->get, remote, sizeof(remote))) < 0) {
printf("Unable to get local endpoint's WPA role\n");
return ret;
}
printf("Local endpoing's WPA role: %s\n", dtoh32(*sup) ? "supplicant" : "authenticator");
return 0;
}
/*
* wlu_reg2args is a generic function that is used for setting/getting
* WL_IOVAR variables that require address for read, and
* address + data for write.
*/
static int
wlu_reg2args(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
uint32 int_val;
bool get = TRUE;
uint32 len;
void *ptr = NULL;
char *endptr;
int ret = 0;
if (argv[1]) {
len = sizeof(int_val);
int_val = htod32(strtoul(argv[1], &endptr, 0));
memcpy(var, (char *)&int_val, sizeof(int_val));
}
else
return BCME_USAGE_ERROR;
if (argv[2]) {
get = FALSE;
int_val = htod32(strtoul(argv[2], &endptr, 0));
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
if (get) {
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0)
return ret;
printf("0x%x\n", dtoh32(*(int *)ptr));
}
else
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
return ret;
}
/*
* wlu_reg3args is a generic function that is used for setting/getting
* WL_IOVAR variables that require address + offset for read, and
* address + offset + data for write.
*/
static int
wlu_reg3args(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
uint32 int_val;
bool get = TRUE;
uint32 len, i;
void *ptr = NULL;
char *endptr;
uint numargs;
int ret = 0;
len = 0;
if (!argv[1] || !argv[2]) {
printf("Wrong syntax => dev offset [val]\n");
return BCME_USAGE_ERROR;
}
if (argv[3]) {
numargs = 3;
get = FALSE;
} else
numargs = 2;
for (i = 1; i <= numargs; i++) {
int_val = htod32(strtoul(argv[i], &endptr, 0));
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
if (get) {
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0)
return ret;
printf("0x%x\n", dtoh32(*(int *)ptr));
}
else
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
return ret;
}
static int
wl_tpc_lm(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint16 val;
int8 aplm, stalm;
UNUSED_PARAMETER(argv);
if ((ret = wlu_iovar_getint(wl, cmd->name, (int *)(uintptr)&val)) < 0)
return ret;
stalm = val & 0xff;
aplm = (val >> 8) & 0xff;
printf("TPC: APs link margin:%d\t STAs link margin:%d\n", aplm, stalm);
return 0;
}
static int
wl_wds_wpa_role(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
char *mac;
char *sup;
int len;
int ret;
if (strlen("wds_wpa_role") + 1 + ETHER_ADDR_LEN + 1 > sizeof(var))
return -1;
/* build var required by WLC_GET|SET_VAR */
len = sprintf(var, "%s", "wds_wpa_role") + 1;
mac = var + len;
if ((ret = wlu_get(wl, WLC_WDS_GET_REMOTE_HWADDR, mac, ETHER_ADDR_LEN)) < 0) {
printf("Unable to get remote endpoint's hwaddr\n");
return ret;
}
len += ETHER_ADDR_LEN + 1;
if (argv[1]) {
sup = mac + ETHER_ADDR_LEN;
switch ((uchar)(*sup = atoi(argv[1]))) {
case WL_WDS_WPA_ROLE_AUTH:
case WL_WDS_WPA_ROLE_SUP:
case WL_WDS_WPA_ROLE_AUTO:
if ((ret = wlu_set(wl, cmd->set, var, len)) < 0)
printf("Unable to set local endpoint's WPA role\n");
break;
default:
printf("Invalid WPA role %s. %u:authenticator, %u:supplicant, %u:auto\n",
argv[1], WL_WDS_WPA_ROLE_AUTH,
WL_WDS_WPA_ROLE_SUP, WL_WDS_WPA_ROLE_AUTO);
break;
}
}
else if ((ret = wlu_get(wl, cmd->get, var, len)) < 0) {
printf("Unable to get local endpoint's WPA role\n");
return ret;
}
else {
sup = var;
printf("Local endpoint's WPA role: %s\n", *sup ? "supplicant" : "authenticator");
}
return ret;
}
static int
wl_measure_req(void *wl, cmd_t *cmd, char **argv)
{
uint32 val;
struct ether_addr ea;
if (!*++argv) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
if (!stricmp(*argv, "tpc"))
val = WLC_MEASURE_TPC;
else if (!stricmp(*argv, "basic"))
val = WLC_MEASURE_CHANNEL_BASIC;
else if (!stricmp(*argv, "cca"))
val = WLC_MEASURE_CHANNEL_CCA;
else if (!stricmp(*argv, "rpi"))
val = WLC_MEASURE_CHANNEL_RPI;
else {
printf("error: unknown measurement type %s\n", *argv);
return BCME_USAGE_ERROR;
}
argv++;
if (!*argv) {
printf("error: missing target address\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, &ea)) {
printf("error: could not parse MAC address %s\n", *argv);
return BCME_USAGE_ERROR;
}
val = htod32(val);
memcpy(&buf[0], &val, sizeof(uint32));
memcpy(&buf[4], ea.octet, ETHER_ADDR_LEN);
return wlu_set(wl, cmd->set, buf, sizeof(uint32) + ETHER_ADDR_LEN);
}
static int
wl_send_quiet(void *wl, cmd_t *cmd, char **argv)
{
dot11_quiet_t quiet;
if (!*++argv) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
/* Order is count, duration, offset */
quiet.count = atoi(*argv);
if (!*++argv) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
quiet.duration = atoi(*argv);
if (!*++argv) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
quiet.offset = atoi(*argv);
quiet.period = 0;
quiet.duration = htod16(quiet.duration);
quiet.offset = htod16(quiet.offset);
return (wlu_set(wl, cmd->set, &quiet, sizeof(quiet)));
}
static int
wl_send_csa(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_chan_switch_t csa_arg;
/* Order is mode, count channel */
if (!*++argv) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
csa_arg.mode = atoi(*argv) ? 1 : 0;
if (!*++argv) {
printf("error: missing count\n");
return BCME_USAGE_ERROR;
}
csa_arg.count = atoi(*argv);
if (!*++argv) {
printf("error: missing channel\n");
return BCME_USAGE_ERROR;
}
csa_arg.reg = 0;
if ((csa_arg.chspec = wf_chspec_aton(*argv))) {
csa_arg.chspec = wl_chspec_to_driver(csa_arg.chspec);
if (csa_arg.chspec == INVCHANSPEC) {
return BCME_USAGE_ERROR;
}
/* csa action frame type */
if (*++argv) {
if (strcmp(*argv, "u") == 0)
csa_arg.frame_type = CSA_UNICAST_ACTION_FRAME;
else {
printf("error: invalid frame type: %s\n", *argv);
return BCME_USAGE_ERROR;
}
} else
csa_arg.frame_type = CSA_BROADCAST_ACTION_FRAME;
err = wlu_var_setbuf(wl, cmd->name, &csa_arg, sizeof(csa_arg));
} else {
printf("Error: bad parameters \"%s\"\n", *argv);
return BCME_BADARG;
}
return err;
}
static int
wl_var_setint(void *wl, cmd_t *cmd, char **argv)
{
int32 val;
char *varname;
char *endptr = NULL;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("set: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (!*argv) {
printf("set: missing value argument for set of \"%s\"\n", varname);
return BCME_USAGE_ERROR;
}
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer for set of \"%s\"\n",
*argv, varname);
return BCME_USAGE_ERROR;
}
return wlu_iovar_setint(wl, varname, val);
}
static int
wl_var_get(void *wl, cmd_t *cmd, char **argv)
{
char *varname;
char *p;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("get: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (*argv) {
printf("get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
strcpy(buf, varname);
p = buf;
while (*p != '\0') {
*p = tolower((int)*p);
p++;
}
return (wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_MAXLEN));
}
static int
wl_var_getinthex(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 val;
if ((err = wl_var_get(wl, cmd, argv)))
return (err);
val = dtoh32(*(int32*)buf);
printf("0x%08x\n", val);
return 0;
}
static int
wl_var_getint(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 val;
char *varname;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("get: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if ((err = wlu_iovar_getint(wl, varname, &val)))
return (err);
if (val < 10)
printf("%d\n", val);
else
printf("%d (0x%x)\n", val, val);
return (0);
}
static int
wl_var_getandprintstr(void *wl, cmd_t *cmd, char **argv)
{
int err;
if ((err = wl_var_get(wl, cmd, argv)))
return (err);
printf("%s\n", buf);
return (0);
}
/* Variation: Like getandprint, but allow an int arg to be passed */
static int
wl_var_setintandprintstr(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 val;
char *varname;
char *endptr = NULL;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("set: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (!*argv) {
val = 0;
} else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer for set of \"%s\"\n",
*argv, varname);
return BCME_USAGE_ERROR;
}
}
val = htod32(val);
err = wlu_iovar_getbuf(wl, varname, &val, sizeof(int), buf, WLC_IOCTL_MAXLEN);
if (err)
return (err);
printf("%s\n", buf);
return (0);
}
void
wl_printlasterror(void *wl)
{
char error_str[128];
if (wlu_iovar_get(wl, "bcmerrorstr", error_str, sizeof(error_str)) != 0) {
fprintf(stderr, "%s: \nError getting the last error\n", wlu_av0);
} else {
fprintf(stderr, "%s: %s\n", wlu_av0, error_str);
}
}
/* just issue a wl_var_setint() or a wl_var_getint() if there is a 2nd arg */
int
wl_varint(void *wl, cmd_t *cmd, char *argv[])
{
if (argv[1])
return (wl_var_setint(wl, cmd, argv));
else
return (wl_var_getint(wl, cmd, argv));
}
int
wlu_var_getbuf(void *wl, const char *iovar, void *param, int param_len, void **bufptr)
{
int len;
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
*bufptr = buf;
return wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_MAXLEN);
}
/* get buffer for smaller sizes upto 256 bytes */
int
wlu_var_getbuf_sm(void *wl, const char *iovar, void *param, int param_len, void **bufptr)
{
int len;
memset(buf, 0, WLC_IOCTL_SMLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
*bufptr = buf;
return wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_SMLEN);
}
/* Get buffer for medium sizes upto 1500 bytes */
int
wlu_var_getbuf_med(void *wl, const char *iovar, void *param, int param_len, void **bufptr)
{
int len;
memset(buf, 0, WLC_IOCTL_MEDLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
*bufptr = buf;
return wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_MEDLEN);
}
int
wlu_var_setbuf(void *wl, const char *iovar, void *param, int param_len)
{
int len;
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
len += param_len;
return wlu_set(wl, WLC_SET_VAR, &buf[0], len);
}
int
wlu_var_setbuf_sm(void *wl, const char *iovar, void *param, int param_len)
{
int len;
memset(buf, 0, WLC_IOCTL_SMLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
len += param_len;
return wlu_set(wl, WLC_SET_VAR, &buf[0], WLC_IOCTL_SMLEN);
}
int
wlu_var_setbuf_med(void *wl, const char *iovar, void *param, int param_len)
{
int len;
memset(buf, 0, WLC_IOCTL_MEDLEN);
strcpy(buf, iovar);
/* include the null */
len = strlen(iovar) + 1;
if (param_len)
memcpy(&buf[len], param, param_len);
len += param_len;
return wlu_set(wl, WLC_SET_VAR, &buf[0], WLC_IOCTL_MEDLEN);
}
static int
wl_var_void(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(argv);
if (cmd->set < 0)
return -1;
return wlu_var_setbuf(wl, cmd->name, NULL, 0);
}
/*
* format a "prefix" indexed iovar buffer
*/
static int
wl_prefixiovar_mkbuf(const char *iovar, const char *prefix, int prefix_index, void *param,
int paramlen, void *bufptr, int buflen, int *perr)
{
int8* p;
uint prefixlen;
uint namelen;
uint iolen;
prefixlen = strlen(prefix); /* length of iovar prefix "bsscfg:ssid %d wlc:counter %d" */
namelen = strlen(iovar) + 1; /* length of iovar name + null */
iolen = prefixlen + namelen + sizeof(int) + paramlen;
/* check for overflow */
if (buflen < 0 || iolen > (uint)buflen) {
*perr = BCME_BUFTOOSHORT;
return 0;
}
p = (int8*)bufptr;
/* copy prefix, no null */
memcpy(p, prefix, prefixlen);
p += prefixlen;
/* copy iovar name including null */
memcpy(p, iovar, namelen);
p += namelen;
/* send index as first param */
prefix_index = htod32(prefix_index);
memcpy(p, &prefix_index, sizeof(int32));
p += sizeof(int32);
/* parameter buffer follows */
if (paramlen)
memcpy(p, param, paramlen);
*perr = 0;
return iolen;
}
static int
wl_bssiovar_mkbuf(const char *iovar, int bssidx, void *param,
int paramlen, void *bufptr, int buflen, int *perr)
{
const char *prefix = "bsscfg:";
return wl_prefixiovar_mkbuf(iovar, prefix, bssidx, param, paramlen, bufptr, buflen, perr);
}
/*
* set named & bss indexed driver iovar providing both parameter and i/o buffers
*/
int
wlu_bssiovar_setbuf(void* wl, const char *iovar, int bssidx,
void *param, int paramlen, void *bufptr, int buflen)
{
int err;
int iolen;
iolen = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &err);
if (err)
return err;
return wlu_set(wl, WLC_SET_VAR, bufptr, iolen);
}
/*
* get named & bss indexed driver iovar providing both parameter and i/o buffers
*/
static int
wl_bssiovar_getbuf(void* wl, const char *iovar, int bssidx,
void *param, int paramlen, void *bufptr, int buflen)
{
int err;
wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &err);
if (err)
return err;
return wlu_get(wl, WLC_GET_VAR, bufptr, buflen);
}
/*
* get named & bss indexed driver variable to buffer value
*/
int
wlu_bssiovar_get(void *wl, const char *iovar, int bssidx, void *outbuf, int len)
{
char smbuf[WLC_IOCTL_SMLEN];
int err;
/* use the return buffer if it is bigger than what we have on the stack */
if (len > (int)sizeof(smbuf)) {
err = wl_bssiovar_getbuf(wl, iovar, bssidx, NULL, 0, outbuf, len);
} else {
memset(smbuf, 0, sizeof(smbuf));
err = wl_bssiovar_getbuf(wl, iovar, bssidx, NULL, 0, smbuf, sizeof(smbuf));
if (err == 0)
memcpy(outbuf, smbuf, len);
}
return err;
}
/*
* set named & bss indexed driver variable to buffer value
*/
static int
wl_bssiovar_set(void *wl, const char *iovar, int bssidx, void *param, int paramlen)
{
char smbuf[WLC_IOCTL_SMLEN];
memset(smbuf, 0, sizeof(smbuf));
return wlu_bssiovar_setbuf(wl, iovar, bssidx, param, paramlen, smbuf, sizeof(smbuf));
}
/*
* get named & bsscfg indexed driver variable as an int value
*/
static int
wl_bssiovar_getint(void *wl, const char *iovar, int bssidx, int *pval)
{
int ret;
ret = wlu_bssiovar_get(wl, iovar, bssidx, pval, sizeof(int));
if (ret == 0)
{
*pval = dtoh32(*pval);
}
return ret;
}
/*
* set named & bsscfg indexed driver variable to int value
*/
static int
wl_bssiovar_setint(void *wl, const char *iovar, int bssidx, int val)
{
val = htod32(val);
return wl_bssiovar_set(wl, iovar, bssidx, &val, sizeof(int));
}
static int
wl_nvdump(void *wl, cmd_t *cmd, char **argv)
{
int err;
const char *iovar = "nvram_dump";
void *p = NULL;
UNUSED_PARAMETER(cmd);
/* skip the "nvdump/nvram_dump" command name */
argv++;
if (*argv) {
printf("nvdump error: extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
if ((err = wlu_var_getbuf(wl, iovar, NULL, 0, &p)) < 0) {
if ((err = wlu_get(wl, WLC_NVRAM_DUMP, &buf[0], WLC_IOCTL_MAXLEN)) < 0)
return err;
p = (void *)buf;
}
printf("%s\n", (char *)p);
return err;
}
/** Queries the driver for the value of a caller supplied nvram variable */
static int
wl_nvget(void *wl, cmd_t *cmd, char **argv)
{
int err;
char *varname;
const char *iovar = "nvram_get";
void *p;
UNUSED_PARAMETER(cmd);
/* skip the "nvget/nvram_get" command name */
argv++;
if (!*argv) {
printf("nvget: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (*argv) {
printf("nvget error: extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
if ((err = wlu_var_getbuf(wl, iovar, varname, strlen(varname) + 1, &p)) < 0) {
strcpy(buf, varname);
if ((err = wlu_get(wl, WLC_NVRAM_GET, &buf[0], WLC_IOCTL_MAXLEN)) < 0)
return err;
}
printf("%s\n", buf);
return err;
}
static int
wl_nvset(void *wl, cmd_t *cmd, char **argv)
{
char *varname;
UNUSED_PARAMETER(cmd);
/* skip the "nvset" command name if present */
if (!strcmp("nvset", *argv))
argv++;
if (!*argv) {
printf("nvset: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (*argv) {
fprintf(stderr,
"nvset error: extra arg \"%s\"; format is name=value (no spaces around '=')\n",
*argv);
return BCME_USAGE_ERROR;
}
if (!strchr(varname, '=')) {
fprintf(stderr,
"nvset error: no '=' in \"%s\", format is name=value (no spaces around '=')\n",
*argv);
return BCME_USAGE_ERROR;
}
strcpy(buf, varname);
return (wlu_set(wl, WLC_NVRAM_SET, &buf[0], strlen(buf) + 1));
}
static int
wl_chan_info(void *wl, cmd_t *cmd, char **argv)
{
uint bitmap;
uint channel;
uint32 chanspec_arg;
int buflen, err, first, last, minutes;
char *param;
bool all;
if (!*++argv) {
first = 0;
last = MAXCHANNEL;
all = TRUE;
} else {
last = first = atoi(*argv);
if (last <= 0) {
printf(" Usage: %s [channel | All ]\n", cmd->name);
return BCME_USAGE_ERROR;
}
all = FALSE;
}
for (; first <= last; first++) {
channel = first;
chanspec_arg = CH20MHZ_CHSPEC(channel);
strcpy(buf, "per_chan_info");
buflen = strlen(buf) + 1;
param = (char *)(buf + buflen);
/* there should be no problem if converting to a legacy chanspec
* since chanspec_arg is created as 20MHz
*/
chanspec_arg = wl_chspec32_to_driver(chanspec_arg);
memcpy(param, (char*)&chanspec_arg, sizeof(chanspec_arg));
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
bitmap = dtoh32(*(uint *)buf);
minutes = (bitmap >> 24) & 0xff;
if (!(bitmap & WL_CHAN_VALID_HW)) {
if (!all)
printf("Invalid Channel\n");
continue;
}
if (!(bitmap & WL_CHAN_VALID_SW)) {
if (!all)
printf("Not supported in current locale\n");
continue;
}
printf("Channel %d\t", channel);
if (bitmap & WL_CHAN_BAND_5G)
printf("A Band");
else
printf("B Band");
if (bitmap & WL_CHAN_RADAR) {
printf(", RADAR Sensitive");
}
if (bitmap & WL_CHAN_RESTRICTED) {
printf(", Restricted");
}
if (bitmap & WL_CHAN_PASSIVE) {
printf(", Passive");
}
if (bitmap & WL_CHAN_INACTIVE) {
printf(", Temporarily Out of Service for %d minutes", minutes);
}
printf("\n");
}
return (0);
}
static int
wl_test_tssi(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char* endptr = NULL;
/* toss the command name */
argv++;
if (!*argv)
return BCME_USAGE_ERROR;
val = htod32(strtol(*argv, &endptr, 0));
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer\n", *argv);
return BCME_USAGE_ERROR;
}
ret = wlu_iovar_getbuf(wl, cmd->name, &val, sizeof(val),
buf, WLC_IOCTL_MAXLEN);
if (ret)
return ret;
val = dtoh32(*(int*)buf);
wl_printint(val);
return ret;
}
static int
wl_test_tssi_offs(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char* endptr = NULL;
/* toss the command name */
argv++;
if (!*argv)
return BCME_USAGE_ERROR;
val = htod32(strtol(*argv, &endptr, 0));
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer\n", *argv);
return BCME_USAGE_ERROR;
}
ret = wlu_iovar_getbuf(wl, cmd->name, &val, sizeof(val),
buf, WLC_IOCTL_MAXLEN);
if (ret)
return ret;
val = dtoh32(*(int*)buf);
wl_printint(val);
return ret;
}
static int
wl_test_idletssi(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int val;
char* endptr = NULL;
/* toss the command name */
argv++;
if (!*argv)
return BCME_USAGE_ERROR;
val = htod32(strtol(*argv, &endptr, 0));
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer\n", *argv);
return -1;
}
if ((ret = wlu_iovar_getbuf(wl, cmd->name, &val, sizeof(val),
buf, WLC_IOCTL_MAXLEN)) >= 0) {
val = dtoh32(*(int*)buf);
wl_printint(val);
}
return ret;
}
static int
wl_sta_info(void *wl, cmd_t *cmd, char **argv)
{
sta_info_t *sta;
struct ether_addr ea;
char *param;
int buflen, err;
int i;
/* convert the ea string into an ea struct */
if (!*++argv || !wl_ether_atoe(*argv, &ea)) {
printf(" ERROR: no valid ether addr provided\n");
return BCME_USAGE_ERROR;
}
strcpy(buf, "sta_info");
buflen = strlen(buf) + 1;
param = (char *)(buf + buflen);
memcpy(param, (char*)&ea, ETHER_ADDR_LEN);
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MEDLEN)) < 0)
return err;
/* display the sta info */
sta = (sta_info_t *)buf;
sta->ver = dtoh16(sta->ver);
/* Report unrecognized version */
if (sta->ver > WL_STA_VER) {
printf(" ERROR: unknown driver station info version %d\n", sta->ver);
return BCME_ERROR;
}
sta->len = dtoh16(sta->len);
sta->cap = dtoh16(sta->cap);
sta->aid = dtoh16(sta->aid);
sta->flags = dtoh32(sta->flags);
sta->idle = dtoh32(sta->idle);
sta->rateset.count = dtoh32(sta->rateset.count);
sta->in = dtoh32(sta->in);
sta->listen_interval_inms = dtoh32(sta->listen_interval_inms);
sta->ht_capabilities = dtoh16(sta->ht_capabilities);
sta->vht_flags = dtoh16(sta->vht_flags);
printf(" STA %s:\n", *argv);
printf("\t aid:%d ", WL_STA_AID(sta->aid));
printf("\n\t rateset ");
dump_rateset(sta->rateset.rates, sta->rateset.count);
printf("\n\t idle %d seconds\n", sta->idle);
printf("\t in network %d seconds\n", sta->in);
printf("\t state:%s%s%s\n",
(sta->flags & WL_STA_AUTHE) ? " AUTHENTICATED" : "",
(sta->flags & WL_STA_ASSOC) ? " ASSOCIATED" : "",
(sta->flags & WL_STA_AUTHO) ? " AUTHORIZED" : "");
printf("\t flags 0x%x:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
sta->flags,
(sta->flags & WL_STA_BRCM) ? " BRCM" : "",
(sta->flags & WL_STA_WME) ? " WME" : "",
(sta->flags & WL_STA_PS) ? " PS" : "",
(sta->flags & WL_STA_NONERP) ? " No-ERP" : "",
(sta->flags & WL_STA_APSD_BE) ? " APSD_BE" : "",
(sta->flags & WL_STA_APSD_BK) ? " APSD_BK" : "",
(sta->flags & WL_STA_APSD_VI) ? " APSD_VI" : "",
(sta->flags & WL_STA_APSD_VO) ? " APSD_VO" : "",
(sta->flags & WL_STA_N_CAP) ? " N_CAP" : "",
(sta->flags & WL_STA_VHT_CAP) ? " VHT_CAP" : "",
(sta->flags & WL_STA_AMPDU_CAP) ? " AMPDU" : "",
(sta->flags & WL_STA_AMSDU_CAP) ? " AMSDU" : "",
(sta->flags & WL_STA_MIMO_PS) ? " MIMO-PS" : "",
(sta->flags & WL_STA_MIMO_RTS) ? " MIMO-PS-RTS" : "",
(sta->flags & WL_STA_RIFS_CAP) ? " RIFS" : "");
printf("\t HT caps 0x%x:%s%s%s%s%s%s%s%s%s\n",
sta->ht_capabilities,
(sta->ht_capabilities & WL_STA_CAP_LDPC_CODING) ? " LDPC" : "",
(sta->ht_capabilities & WL_STA_CAP_40MHZ) ? " 40MHz" : " ",
(sta->ht_capabilities & WL_STA_CAP_GF) ? " GF" : "",
(sta->ht_capabilities & WL_STA_CAP_SHORT_GI_20) ? " SGI20" : "",
(sta->ht_capabilities & WL_STA_CAP_SHORT_GI_40) ? " SGI40" : "",
(sta->ht_capabilities & WL_STA_CAP_TX_STBC) ? " STBC-Tx" : "",
(sta->ht_capabilities & WL_STA_CAP_RX_STBC_MASK) ? " STBC-Rx" : "",
(sta->ht_capabilities & WL_STA_CAP_DELAYED_BA) ? " D-BlockAck" : "",
(sta->ht_capabilities & WL_STA_CAP_40MHZ_INTOLERANT) ? " 40-Intl" : "");
if (sta->flags & WL_STA_VHT_CAP) {
printf("\t VHT caps 0x%x:%s%s%s%s%s%s%s%s%s%s%s\n",
sta->vht_flags,
(sta->vht_flags & WL_STA_VHT_LDPCCAP) ? " LDPC" : "",
(sta->vht_flags & WL_STA_SGI80) ? " SGI80" : "",
(sta->vht_flags & WL_STA_SGI160) ? " SGI160" : "",
(sta->vht_flags & WL_STA_VHT_TX_STBCCAP) ? " STBC-Tx" : "",
(sta->vht_flags & WL_STA_VHT_RX_STBCCAP) ? " STBC-Rx" : "",
(sta->vht_flags & WL_STA_SU_BEAMFORMER) ? " SU-BFR" : "",
(sta->vht_flags & WL_STA_SU_BEAMFORMEE) ? " SU-BFE" : "",
(sta->vht_flags & WL_STA_MU_BEAMFORMER) ? " MU-BFR" : "",
(sta->vht_flags & WL_STA_MU_BEAMFORMEE) ? " MU-BFE" : "",
(sta->vht_flags & WL_STA_VHT_TXOP_PS) ? " TXOPPS" : "",
(sta->vht_flags & WL_STA_HTC_VHT_CAP) ? " VHT-HTC" : "");
}
/* Driver didn't return extended station info */
if (sta->len < sizeof(sta_info_t))
return 0;
if (sta->flags & WL_STA_SCBSTATS)
{
printf("\t tx data pkts: %d\n", dtoh32(sta->tx_tot_pkts));
printf("\t tx data bytes: %llu\n", dtoh64(sta->tx_tot_bytes));
printf("\t tx ucast pkts: %d\n", dtoh32(sta->tx_pkts));
printf("\t tx ucast bytes: %llu\n", dtoh64(sta->tx_ucast_bytes));
printf("\t tx mcast/bcast pkts: %d\n", dtoh32(sta->tx_mcast_pkts));
printf("\t tx mcast/bcast bytes: %llu\n", dtoh64(sta->tx_mcast_bytes));
printf("\t tx failures: %d\n", dtoh32(sta->tx_failures));
printf("\t rx data pkts: %d\n", dtoh32(sta->rx_tot_pkts));
printf("\t rx data bytes: %llu\n", dtoh64(sta->rx_tot_bytes));
printf("\t rx ucast pkts: %d\n", dtoh32(sta->rx_ucast_pkts));
printf("\t rx ucast bytes: %llu\n", dtoh64(sta->rx_ucast_bytes));
printf("\t rx mcast/bcast pkts: %d\n", dtoh32(sta->rx_mcast_pkts));
printf("\t rx mcast/bcast bytes: %llu\n", dtoh64(sta->rx_mcast_bytes));
printf("\t rate of last tx pkt: %d kbps\n", dtoh32(sta->tx_rate));
printf("\t rate of last rx pkt: %d kbps\n", dtoh32(sta->rx_rate));
printf("\t rx decrypt succeeds: %d\n", dtoh32(sta->rx_decrypt_succeeds));
printf("\t rx decrypt failures: %d\n", dtoh32(sta->rx_decrypt_failures));
printf("\t tx data pkts retried: %d\n", dtoh32(sta->tx_pkts_retried));
printf("\t tx data pkts retry exhausted: %d\n",
dtoh32(sta->tx_pkts_retry_exhausted));
for (i = WL_ANT_IDX_1; i < WL_RSSI_ANT_MAX; i++) {
if (i == WL_ANT_IDX_1)
printf("\t per antenna rssi of last rx data frame:");
printf(" %d", dtoh32(sta->rx_lastpkt_rssi[i]));
if (i == WL_RSSI_ANT_MAX-1)
printf("\n");
}
for (i = WL_ANT_IDX_1; i < WL_RSSI_ANT_MAX; i++) {
if (i == WL_ANT_IDX_1)
printf("\t per antenna average rssi of rx data frames:");
printf(" %d", dtoh32(sta->rssi[i]));
if (i == WL_RSSI_ANT_MAX-1)
printf("\n");
}
for (i = WL_ANT_IDX_1; i < WL_RSSI_ANT_MAX; i++) {
if (i == WL_ANT_IDX_1)
printf("\t per antenna noise floor:");
printf(" %d", dtoh32(sta->nf[i]));
if (i == WL_RSSI_ANT_MAX-1)
printf("\n");
}
}
return (0);
}
static int
wl_revinfo(void *wl, cmd_t *cmd, char **argv)
{
char b[8];
int err;
wlc_rev_info_t revinfo;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(&revinfo, 0, sizeof(revinfo));
if ((err = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return err;
printf("vendorid 0x%x\n", dtoh32(revinfo.vendorid));
printf("deviceid 0x%x\n", dtoh32(revinfo.deviceid));
printf("radiorev 0x%x\n", dtoh32(revinfo.radiorev));
printf("chipnum 0x%x\n", dtoh32(revinfo.chipnum));
printf("chiprev 0x%x\n", dtoh32(revinfo.chiprev));
printf("chippackage 0x%x\n", dtoh32(revinfo.chippkg));
printf("corerev 0x%x\n", dtoh32(revinfo.corerev));
printf("boardid 0x%x\n", dtoh32(revinfo.boardid));
printf("boardvendor 0x%x\n", dtoh32(revinfo.boardvendor));
printf("boardrev %s\n", bcm_brev_str(dtoh32(revinfo.boardrev), b));
printf("driverrev 0x%x\n", dtoh32(revinfo.driverrev));
printf("ucoderev 0x%x\n", dtoh32(revinfo.ucoderev));
printf("bus 0x%x\n", dtoh32(revinfo.bus));
printf("phytype 0x%x\n", dtoh32(revinfo.phytype));
printf("phyrev 0x%x\n", dtoh32(revinfo.phyrev));
printf("anarev 0x%x\n", dtoh32(revinfo.anarev));
printf("nvramrev %d\n", dtoh32(revinfo.nvramrev));
return 0;
}
static int
wl_rm_request(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_rm_request";
wl_rm_req_t *rm_ptr;
wl_rm_req_t rm;
wl_rm_req_elt_t req;
int buflen = 0;
int err, opt_err;
int type;
bool in_measure = FALSE;
UNUSED_PARAMETER(cmd);
memset(buf, 0, WLC_IOCTL_MAXLEN);
memset(&rm, 0, WL_RM_REQ_FIXED_LEN);
memset(&req, 0, sizeof(wl_rm_req_elt_t));
strcpy(buf, "rm_req");
buflen = strlen(buf) + 1;
rm_ptr = (wl_rm_req_t*)(buf + buflen);
buflen += WL_RM_REQ_FIXED_LEN;
/* toss the command name */
argv++;
miniopt_init(&to, fn_name, "p", FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 't') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for the token\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (!in_measure)
rm.token = to.val;
else
req.token = to.val;
}
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for channel\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
req.chanspec = CH20MHZ_CHSPEC(to.val);
}
if (to.opt == 'd') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for duration\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
req.dur = to.val;
}
if (to.opt == 'p') {
req.flags = WL_RM_FLAG_PARALLEL;
}
if (to.positional) {
if (!strcmp(to.valstr, "basic")) {
type = WL_RM_TYPE_BASIC;
} else if (!strcmp(to.valstr, "cca")) {
type = WL_RM_TYPE_CCA;
} else if (!strcmp(to.valstr, "rpi")) {
type = WL_RM_TYPE_RPI;
} else {
fprintf(stderr,
"%s: could not parse \"%s\" as a measurement type\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
/* complete the previous measurement */
if (in_measure) {
req.chanspec = wl_chspec_to_driver(req.chanspec);
req.token = htod32(req.token);
req.tsf_h = htod32(req.tsf_h);
req.tsf_l = htod32(req.tsf_l);
req.dur = htod32(req.dur);
memcpy(buf + buflen, &req, sizeof(wl_rm_req_elt_t));
buflen += sizeof(wl_rm_req_elt_t);
rm.count++;
req.chanspec = wl_chspec_from_driver(req.chanspec);
req.token = dtoh32(req.token);
req.tsf_h = dtoh32(req.tsf_h);
req.tsf_l = dtoh32(req.tsf_l);
req.dur = dtoh32(req.dur);
/* measure to measure default param update */
req.token++; /* each measure gets a new token */
req.flags = 0; /* measure flags are cleared between measures */
}
in_measure = TRUE;
req.type = (int8)type;
}
}
/* complete the last measurement */
if (in_measure) {
req.chanspec = wl_chspec_to_driver(req.chanspec);
req.token = htod32(req.token);
req.tsf_h = htod32(req.tsf_h);
req.tsf_l = htod32(req.tsf_l);
req.dur = htod32(req.dur);
memcpy(buf + buflen, &req, sizeof(wl_rm_req_elt_t));
buflen += sizeof(wl_rm_req_elt_t);
rm.count++;
}
if (rm.count == 0) {
fprintf(stderr, "%s: no measurement requests specified\n",
fn_name);
err = BCME_USAGE_ERROR;
goto exit;
}
rm.token = htod32(rm.token);
rm.count = htod32(rm.count);
memcpy(rm_ptr, &rm, WL_RM_REQ_FIXED_LEN);
err = wlu_set(wl, WLC_SET_VAR, &buf[0], buflen);
exit:
return err;
}
static int
wl_rm_report(void *wl, cmd_t *cmd, char **argv)
{
wl_rm_rep_t *rep_set;
wl_rm_rep_elt_t rep;
char extra[128];
char* p;
const char* name;
uint8* data;
int err, bin;
uint32 val;
uint16 channel;
bool aband;
int len;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
strcpy(buf, "rm_rep");
if ((err = wlu_get(wl, WLC_GET_VAR, &buf[0], WLC_IOCTL_MAXLEN)) < 0)
return err;
rep_set = (wl_rm_rep_t *)buf;
rep_set->token = dtoh32(rep_set->token);
rep_set->len = dtoh32(rep_set->len);
printf("Measurement Report: token %d, length %d\n", rep_set->token, rep_set->len);
len = rep_set->len;
data = (uint8*)rep_set->rep;
for (; len > 0; (len -= rep.len), (data += rep.len)) {
if (len >= WL_RM_REP_ELT_FIXED_LEN)
memcpy(&rep, data, WL_RM_REP_ELT_FIXED_LEN);
else
break;
rep.chanspec = wl_chspec_from_driver(rep.chanspec);
rep.token = dtoh32(rep.token);
rep.tsf_h = dtoh32(rep.tsf_h);
rep.tsf_l = dtoh32(rep.tsf_l);
rep.dur = dtoh32(rep.dur);
rep.len = dtoh32(rep.len);
data += WL_RM_REP_ELT_FIXED_LEN;
len -= WL_RM_REP_ELT_FIXED_LEN;
if (rep.type == WL_RM_TYPE_BASIC)
name = "Basic";
else if (rep.type == WL_RM_TYPE_CCA)
name = "CCA";
else if (rep.type == WL_RM_TYPE_RPI)
name = "RPI";
else
name = NULL;
if (name)
printf("\nReport : %s\n", name);
else
printf("\nReport : %d <unknown>\n", rep.type);
p = extra;
if (rep.flags & WL_RM_FLAG_PARALLEL) {
if (p != extra)
p += sprintf(p, " | ");
p += sprintf(p, "Parallel");
}
if (rep.flags & WL_RM_FLAG_LATE) {
if (p != extra)
p += sprintf(p, " | ");
p += sprintf(p, "Late");
}
if (rep.flags & WL_RM_FLAG_INCAPABLE) {
if (p != extra)
p += sprintf(p, " | ");
p += sprintf(p, "Incapable");
}
if (rep.flags & WL_RM_FLAG_REFUSED) {
if (p != extra)
p += sprintf(p, " | ");
p += sprintf(p, "Refused");
}
if (p != extra) {
printf("flags : 0x%02x (%s)\n", rep.flags, extra);
} else {
printf("flags : 0x%02x\n", rep.flags);
}
printf("token : %4d\n", rep.token);
if (rep.flags & (WL_RM_FLAG_LATE |
WL_RM_FLAG_INCAPABLE |
WL_RM_FLAG_REFUSED)) {
continue;
}
channel = CHSPEC_CHANNEL(rep.chanspec);
aband = CHSPEC_IS5G(rep.chanspec);
printf("channel : %4d %s\n", channel,
aband ? "(a)":"(b)");
printf("start tsf: 0x%x:%08x\n", rep.tsf_h, rep.tsf_l);
printf("duration : %4d TU\n", rep.dur);
if (len < (int)rep.len) {
printf("Error: partial report element, %d report bytes "
"remain, element claims %d\n",
len, rep.len);
break;
}
if (rep.type == WL_RM_TYPE_BASIC) {
if (rep.len >= 4) {
memcpy(&val, data, sizeof(uint32));
val = dtoh32(val);
printf("Basic bits: 0x%08x\n", val);
}
} else if (rep.type == WL_RM_TYPE_CCA) {
if (rep.len >= 4) {
memcpy(&val, data, sizeof(uint32));
val = dtoh32(val);
printf("Carrier Fraction: %d / 255\n", val);
}
} else if (rep.type == WL_RM_TYPE_RPI) {
if (rep.len >= sizeof(wl_rm_rpi_rep_t)) {
wl_rm_rpi_rep_t rpi_rep;
int8 min = -128;
int8 max;
memcpy(&rpi_rep, data, sizeof(wl_rm_rpi_rep_t));
for (bin = 0; bin < 8; bin++) {
max = rpi_rep.rpi_max[bin];
if (bin == 0)
printf(" Power <= %3d: ",
max);
else if (bin < 7)
printf(" %3d < Power <= %3d: ",
min, max);
else
printf(" %3d < Power : ",
min);
min = max;
printf("%3d\n", rpi_rep.rpi[bin]);
}
}
}
}
return err;
}
static int
wl_join_pref(void *wl, cmd_t *cmd, char **argv)
{
char* data;
int err;
int len;
int remaining_bytes;
int i;
bcm_tlv_t *ie;
UNUSED_PARAMETER(cmd);
strcpy(buf, "join_pref");
/* set */
if (argv[1]) {
len = strlen(buf);
data = argv[1];
for (i = len + 1, len += 1 + strlen(data) / 2;
(i < len) && (i < (int)WLC_IOCTL_MAXLEN); i ++) {
char hex[] = "XX";
hex[0] = *data++;
hex[1] = *data++;
buf[i] = (uint8)strtoul(hex, NULL, 16);
}
err = wlu_set(wl, WLC_SET_VAR, buf, i);
}
/* get */
else if (!(err = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MAXLEN))) {
len = dtoh32(*(int *)buf);
data = buf + sizeof(int);
for (i = 0; i < len; i ++)
printf("%02x", (uint8)(data[i]));
printf("\n");
/* pretty print the join pref elements */
remaining_bytes = len;
ie = (bcm_tlv_t*)data;
if (!bcm_valid_tlv(ie, remaining_bytes))
ie = NULL;
while (ie) {
wl_join_pref_print_ie(ie);
ie = bcm_next_tlv(ie, &remaining_bytes);
}
}
return err;
}
static void
wl_join_pref_print_ie(bcm_tlv_t *ie)
{
int i;
uint8 band;
uint8 count;
int suite_len;
uint8 *suite;
int data_bytes;
switch (ie->id) {
case WL_JOIN_PREF_RSSI:
printf("Pref RSSI\n");
if (ie->len > 2)
printf("\t<%d extra bytes in pref data>\n", ie->len);
break;
case WL_JOIN_PREF_BAND:
printf("Pref BAND: ");
if (ie->len < 2) {
printf("len = %d <band pref data truncated>\n", ie->len);
break;
}
band = ie->data[1];
if (band == WLC_BAND_AUTO)
printf("0x%x AUTO (no preference)\n", band);
else if (band == WLC_BAND_5G)
printf("0x%x 5 GHz\n", band);
else if (band == WLC_BAND_2G)
printf("0x%x 2.4 GHz\n", band);
else if (band == WLJP_BAND_ASSOC_PREF)
printf("0x%x Use ASSOC_PREFER value\n", band);
else
printf("0x%x\n", band);
if (ie->len > 2)
printf("\t<%d extra bytes in pref data>\n", ie->len - 1);
break;
case WL_JOIN_PREF_WPA:
printf("Pref WPA: ");
if (ie->len < 2) {
printf("len = %d <WPA pref data truncated>\n", ie->len);
break;
}
count = ie->data[1];
printf("%d ACP Specs\n", count);
data_bytes = ie->len - 2;
suite_len = 4; /* WPA Suite Selector length, OUI + type */
suite = ie->data + 2;
for (i = 0; i < (int)count; i++) {
if (data_bytes < 3 * suite_len)
break;
printf("\t");
/* AKM Suite */
wl_join_pref_print_akm(suite);
printf(",");
suite = suite + suite_len;
/* Unicast Cipher Suite */
printf("U:");
wl_join_pref_print_cipher_suite(suite);
printf(",");
suite = suite + suite_len;
/* Multicast Cipher Suite */
printf("M:");
if (!memcmp(suite, WL_WPA_ACP_MCS_ANY, suite_len))
printf("Any");
else
wl_join_pref_print_cipher_suite(suite);
printf("\n");
suite = suite + suite_len;
data_bytes -= 3 * suite_len;
}
if (i != count)
printf("\t<expected %d more specs, %d bytes>\n",
count - i, suite_len * (count - i));
if (data_bytes > 0)
printf("\t<%d extra bytes>\n", data_bytes);
break;
case WL_JOIN_PREF_RSSI_DELTA:
printf("RSSI Delta for Pref BAND: ");
if (ie->len < 2) {
printf("len = %d <rssi delta pref data truncated>\n", ie->len);
break;
}
band = ie->data[1];
if (band == WLC_BAND_AUTO)
printf("0x%x AUTO (no preference)\n", band);
else if (band == WLC_BAND_5G)
printf("0x%x 5 GHz\n", band);
else if (band == WLC_BAND_2G)
printf("0x%x 2.4 GHz\n", band);
else
printf("0x%x\n", band);
printf("RSSI boost %ddb\n", ie->data[0]);
break;
default:
printf("Pref 0x%x: len = %d\n", ie->id, ie->len);
for (i = 0; i < ie->len; i++)
printf("%02x", ie->data[i]);
printf("\n");
break;
}
}
static void
wl_join_pref_print_akm(uint8* suite)
{
uint8 type = suite[3];
const char *oui_name;
if (!memcmp(suite, WPA_OUI, 3))
oui_name = "WPA";
else if (!memcmp(suite, WPA2_OUI, 3))
oui_name = "WPA2";
else
oui_name = NULL;
if (oui_name) {
if (type == RSN_AKM_NONE)
printf("%s-NONE", oui_name);
else if (type == RSN_AKM_UNSPECIFIED)
printf("%s", oui_name);
else if (type == RSN_AKM_UNSPECIFIED)
printf("%s-PSK", oui_name);
else
printf("%s/0x%x", oui_name, type);
} else {
printf("0x%02x%02x%02x/0x%02x", suite[0], suite[1], suite[2], suite[3]);
}
}
static void
wl_join_pref_print_cipher_suite(uint8* suite)
{
uint8 type = suite[3];
const char *oui_name;
if (!memcmp(suite, WPA_OUI, 3))
oui_name = "WPA";
else if (!memcmp(suite, WPA2_OUI, 3))
oui_name = "WPA2";
else
oui_name = NULL;
if (oui_name) {
if (type == WPA_CIPHER_NONE)
printf("%s/NONE", oui_name);
else if (type == WPA_CIPHER_WEP_40)
printf("%s/WEP40", oui_name);
else if (type == WPA_CIPHER_TKIP)
printf("%s/TKIP", oui_name);
else if (type == WPA_CIPHER_AES_CCM)
printf("%s/AES", oui_name);
else if (type == WPA_CIPHER_WEP_104)
printf("%s/WEP104", oui_name);
else
printf("%s/0x%x", oui_name, type);
} else {
printf("0x%02x%02x%02x/0x%02x", suite[0], suite[1], suite[2], suite[3]);
}
}
static int
wl_assoc_pref(void *wl, cmd_t *cmd, char **argv)
{
uint assoc_pref;
int err;
/* set */
if (argv[1]) {
if (!strcmp(argv[1], "auto") || !strcmp(argv[1], "0"))
assoc_pref = WLC_BAND_AUTO;
else if (!strcmp(argv[1], "a") || !strcmp(argv[1], "1"))
assoc_pref = WLC_BAND_5G;
else if (!strcmp(argv[1], "b") || !strcmp(argv[1], "g") || !strcmp(argv[1], "2"))
assoc_pref = WLC_BAND_2G;
else
return BCME_USAGE_ERROR;
assoc_pref = htod32(assoc_pref);
err = wlu_set(wl, cmd->set, &assoc_pref, sizeof(assoc_pref));
}
/* get */
else if (!(err = wlu_get(wl, cmd->get, &assoc_pref, sizeof(assoc_pref)))) {
assoc_pref = dtoh32(assoc_pref);
switch (assoc_pref) {
case WLC_BAND_AUTO:
printf("auto\n");
break;
case WLC_BAND_5G:
printf("a\n");
break;
case WLC_BAND_2G:
printf("b/g\n");
break;
}
}
return err;
}
static const char ac_names[AC_COUNT][6] = {"AC_BE", "AC_BK", "AC_VI", "AC_VO"};
/*
* Get or set WME per-AC transmit parameters
*/
static int
wme_tx_params(void *wl, cmd_t *cmd, char **argv)
{
char *val_p, *ac_str, *param;
int buflen;
int aci;
wme_tx_params_t cur_params[AC_COUNT], new_params[AC_COUNT];
int err;
int val;
UNUSED_PARAMETER(cmd);
argv++;
buflen = WLC_IOCTL_MAXLEN;
/*
* Get current acparams, using buf as an input buffer.
* Return data is array of 4 ACs of wme params.
*/
strcpy(buf, "wme_tx_params");
if ((err = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen)) < 0) {
return err;
}
memcpy(&cur_params, buf, WL_WME_TX_PARAMS_IO_BYTES);
if ((ac_str = *argv++) == NULL) {
printf("WME TX params: \n");
for (aci = 0; aci < AC_COUNT; aci++) {
printf("%s: short %d. sfb %d. long %d. lfb %d. max %d\n", ac_names[aci],
cur_params[aci].short_retry,
cur_params[aci].short_fallback,
cur_params[aci].long_retry,
cur_params[aci].long_fallback,
cur_params[aci].max_rate);
}
} else {
int chk_lim;
if (strcmp(ac_str, "be") == 0) {
aci = AC_BE;
} else if (strcmp(ac_str, "bk") == 0) {
aci = AC_BK;
} else if (strcmp(ac_str, "vi") == 0) {
aci = AC_VI;
} else if (strcmp(ac_str, "vo") == 0) {
aci = AC_VO;
} else {
printf("Unknown access class: %s\n", ac_str);
return BCME_USAGE_ERROR;
}
/* Preload new values with current values */
memcpy(&new_params, &cur_params, sizeof(new_params));
while ((param = *argv++) != NULL) {
if ((val_p = *argv++) == NULL) {
printf("Need value following %s\n", param);
return BCME_USAGE_ERROR;
}
chk_lim = 15;
val = (int)strtoul(val_p, NULL, 0);
/* All values must fit in uint8 */
if (!strcmp(param, "short")) {
new_params[aci].short_retry = (uint8)val;
} else if (!strcmp(param, "sfb")) {
new_params[aci].short_fallback = (uint8)val;
} else if (!strcmp(param, "long")) {
new_params[aci].long_retry = (uint8)val;
} else if (!strcmp(param, "lfb")) {
new_params[aci].long_fallback = (uint8)val;
} else if ((!strcmp(param, "max_rate")) || (!strcmp(param, "max")) ||
(!strcmp(param, "rate"))) {
chk_lim = 255;
new_params[aci].max_rate = (uint8)val;
} else {
printf("Unknown parameter: %s\n", param);
return BCME_USAGE_ERROR;
}
if (val > chk_lim) {
printf("Value for %s must be < %d\n", param, chk_lim + 1);
return BCME_USAGE_ERROR;
}
}
strcpy(buf, "wme_tx_params");
memcpy(buf + strlen(buf) + 1, new_params, WL_WME_TX_PARAMS_IO_BYTES);
err = wlu_set(wl, WLC_SET_VAR, &buf[0], buflen);
}
return err;
}
/*
* Get or Set WME Access Class (AC) parameters
* wl wme_ac ap|sta [be|bk|vi|vo [ecwmax|ecwmin|txop|aifsn|acm <value>] ...]
* Without args past ap|sta, print current values
*/
static int
wl_wme_ac_req(void *wl, cmd_t *cmd, char **argv)
{
const char *iovar_name;
int err;
edcf_acparam_t acparam_cur[AC_COUNT], acparam_new[AC_COUNT], *acp;
char *ac_str, *param, *val;
bool acm;
int aci, aifsn, ecwmin, ecwmax, txop;
UNUSED_PARAMETER(cmd);
argv++;
if ((param = *argv++) == NULL)
return BCME_USAGE_ERROR;
if (!strcmp(param, "ap"))
iovar_name = "wme_ac_ap";
else if (!strcmp(param, "sta"))
iovar_name = "wme_ac_sta";
else
return BCME_USAGE_ERROR;
/*
* Get current acparams into an array of 4 ACs of wme params.
*/
err = wlu_iovar_get(wl, iovar_name, &acparam_cur, sizeof(acparam_cur));
if (err < 0)
return err;
if ((ac_str = *argv++) == NULL) {
printf("AC Parameters\n");
for (aci = 0; aci < AC_COUNT; aci++) {
acp = &acparam_cur[aci];
acp->TXOP = dtoh16(acp->TXOP);
if (((acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT) != aci)
printf("Warning: AC params out of order\n");
acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
aifsn = acp->ACI & EDCF_AIFSN_MASK;
ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
txop = acp->TXOP;
printf("%s: raw: ACI 0x%x ECW 0x%x TXOP 0x%x\n",
ac_names[aci],
acp->ACI, acp->ECW, acp->TXOP);
printf(" dec: aci %d acm %d aifsn %d "
"ecwmin %d ecwmax %d txop 0x%x\n",
aci, acm, aifsn, ecwmin, ecwmax, txop);
/* CWmin = 2^(ECWmin) - 1 */
/* CWmax = 2^(ECWmax) - 1 */
/* TXOP = number of 32 us units */
printf(" eff: CWmin %d CWmax %d TXop %dusec\n",
EDCF_ECW2CW(ecwmin), EDCF_ECW2CW(ecwmax), EDCF_TXOP2USEC(txop));
}
err = 0;
} else {
if (strcmp(ac_str, "be") == 0)
aci = AC_BE;
else if (strcmp(ac_str, "bk") == 0)
aci = AC_BK;
else if (strcmp(ac_str, "vi") == 0)
aci = AC_VI;
else if (strcmp(ac_str, "vo") == 0)
aci = AC_VO;
else
return BCME_USAGE_ERROR;
/* Preload new values with current values */
memcpy(&acparam_new, &acparam_cur, sizeof(acparam_new));
acp = &acparam_new[aci];
while ((param = *argv++) != NULL) {
if ((val = *argv++) == NULL)
return BCME_USAGE_ERROR;
if (!strcmp(param, "acm")) {
if (!stricmp(val, "on") || !stricmp(val, "1"))
acp->ACI |= EDCF_ACM_MASK;
else if (!stricmp(val, "off") || !stricmp(val, "0"))
acp->ACI &= ~EDCF_ACM_MASK;
else {
fprintf(stderr, "acm value must be 1|0\n");
return BCME_USAGE_ERROR;
}
} else if (!strcmp(param, "aifsn")) {
aifsn = (int)strtol(val, NULL, 0);
if (aifsn >= EDCF_AIFSN_MIN && aifsn <= EDCF_AIFSN_MAX)
acp->ACI =
(acp->ACI & ~EDCF_AIFSN_MASK) |
(aifsn & EDCF_AIFSN_MASK);
else {
fprintf(stderr, "aifsn %d out of range (%d-%d)\n",
aifsn, EDCF_AIFSN_MIN, EDCF_AIFSN_MAX);
return BCME_USAGE_ERROR;
}
} else if (!strcmp(param, "ecwmax")) {
ecwmax = (int)strtol(val, NULL, 0);
if (ecwmax >= EDCF_ECW_MIN && ecwmax <= EDCF_ECW_MAX)
acp->ECW =
((ecwmax << EDCF_ECWMAX_SHIFT) & EDCF_ECWMAX_MASK) |
(acp->ECW & EDCF_ECWMIN_MASK);
else {
fprintf(stderr, "ecwmax %d out of range (%d-%d)\n",
ecwmax, EDCF_ECW_MIN, EDCF_ECW_MAX);
return BCME_USAGE_ERROR;
}
} else if (!strcmp(param, "ecwmin")) {
ecwmin = (int)strtol(val, NULL, 0);
if (ecwmin >= EDCF_ECW_MIN && ecwmin <= EDCF_ECW_MAX)
acp->ECW =
((acp->ECW & EDCF_ECWMAX_MASK) |
(ecwmin & EDCF_ECWMIN_MASK));
else {
fprintf(stderr, "ecwmin %d out of range (%d-%d)\n",
ecwmin, EDCF_ECW_MIN, EDCF_ECW_MAX);
return BCME_USAGE_ERROR;
}
} else if (!strcmp(param, "txop")) {
txop = (int)strtol(val, NULL, 0);
if (txop >= EDCF_TXOP_MIN && txop <= EDCF_TXOP_MAX)
acp->TXOP = htod16(txop);
else {
fprintf(stderr, "txop %d out of range (%d-%d)\n",
txop, EDCF_TXOP_MIN, EDCF_TXOP_MAX);
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "unexpected param %s\n", param);
return BCME_USAGE_ERROR;
}
}
/*
* Now set the new acparams
* NOTE: only one of the four ACs can be set at a time.
*/
err = wlu_iovar_set(wl, iovar_name, acp, sizeof(edcf_acparam_t));
}
return err;
}
/*
* Get or Set WME APSD control parameters
* wl wme_apsd_sta <max_sp_len> <be> <bk> <vi> <vo>
* <max_sp_len> is 0 (all), 2, 4, or 6
* <be>, <bk>, <vi>, <vo> are each 0 or 1 for APSD enable
* with no args, print current values
*/
static int
wl_wme_apsd_sta(void *wl, cmd_t *cmd, char **argv)
{
int err;
int buflen;
char *param;
int ap_mode;
int qosinfo;
int msp, max_sp_len, be, bk, vi, vo;
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))) < 0)
return err;
if (ap_mode) {
printf("%s: STA only\n", cmd->name);
return -1;
}
/* Display current params if no args, else set params */
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "wme_qosinfo");
buflen = WLC_IOCTL_MAXLEN;
param = *++argv;
if (param == NULL) {
if ((err = wlu_get(wl, cmd->get, &buf[0], buflen)) < 0)
return err;
memcpy(&qosinfo, buf, sizeof(qosinfo));
qosinfo = dtoh32(qosinfo);
msp = (qosinfo & WME_QI_STA_MAXSPLEN_MASK) >> WME_QI_STA_MAXSPLEN_SHIFT;
be = (qosinfo & WME_QI_STA_APSD_BE_MASK) >> WME_QI_STA_APSD_BE_SHIFT;
bk = (qosinfo & WME_QI_STA_APSD_BK_MASK) >> WME_QI_STA_APSD_BK_SHIFT;
vi = (qosinfo & WME_QI_STA_APSD_VI_MASK) >> WME_QI_STA_APSD_VI_SHIFT;
vo = (qosinfo & WME_QI_STA_APSD_VO_MASK) >> WME_QI_STA_APSD_VO_SHIFT;
max_sp_len = msp * 2;
printf("Max SP Length = %d, APSD: BE=%d BK=%d VI=%d VO=%d\n",
max_sp_len, be, bk, vi, vo);
} else {
max_sp_len = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
be = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
bk = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
vi = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
vo = (int)strtol(param, 0, 0);
if (((be | bk | vi | vo) & ~1) | (max_sp_len & ~6)) {
printf("%s: Invalid parameter\n", cmd->name);
return BCME_BADARG;
}
msp = max_sp_len / 2;
qosinfo = (msp << WME_QI_STA_MAXSPLEN_SHIFT) & WME_QI_STA_MAXSPLEN_MASK;
qosinfo |= (be << WME_QI_STA_APSD_BE_SHIFT) & WME_QI_STA_APSD_BE_MASK;
qosinfo |= (bk << WME_QI_STA_APSD_BK_SHIFT) & WME_QI_STA_APSD_BK_MASK;
qosinfo |= (vi << WME_QI_STA_APSD_VI_SHIFT) & WME_QI_STA_APSD_VI_MASK;
qosinfo |= (vo << WME_QI_STA_APSD_VO_SHIFT) & WME_QI_STA_APSD_VO_MASK;
qosinfo = htod32(qosinfo);
memcpy(&buf[strlen(buf) + 1], &qosinfo, sizeof(qosinfo));
err = wlu_set(wl, cmd->set, &buf[0], buflen);
}
return err;
}
/*
* Get or Set WME discard policy
* wl wme_dp <be> <bk> <vi> <vo>
* <be>, <bk>, <vi>, <vo> are each 0/1 for discard newest/oldest first
* with no args, print current values
*/
static int
wl_wme_dp(void *wl, cmd_t *cmd, char **argv)
{
int err;
int buflen;
char *param;
int dp;
int be, bk, vi, vo;
/* Display current params if no args, else set params */
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "wme_dp");
buflen = WLC_IOCTL_MAXLEN;
param = *++argv;
if (param == NULL) {
if ((err = wlu_get(wl, cmd->get, &buf[0], buflen)) < 0)
return err;
memcpy(&dp, buf, sizeof(dp));
dp = dtoh32(dp);
be = (dp >> AC_BE) & 1;
bk = (dp >> AC_BK) & 1;
vi = (dp >> AC_VI) & 1;
vo = (dp >> AC_VO) & 1;
printf("Discard oldest first: BE=%d BK=%d VI=%d VO=%d\n", be, bk, vi, vo);
} else {
be = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
bk = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
vi = (int)strtol(param, 0, 0);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
vo = (int)strtol(param, 0, 0);
if ((be | bk | vi | vo) & ~1) {
printf("%s: Invalid parameter\n", cmd->name);
return BCME_BADARG;
}
dp = (be << AC_BE) | (bk << AC_BK) | (vi << AC_VI) | (vo << AC_VO);
dp = htod32(dp);
memcpy(&buf[strlen(buf) + 1], &dp, sizeof(dp));
err = wlu_set(wl, cmd->set, &buf[0], buflen);
}
return err;
}
/*
* Get or Set WME lifetime parameter
* "wl lifetime be|bk|vi|vo [<value>]"},
* with no args, print current values
*/
static int
wl_lifetime(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint8 ac;
char *param, *val;
const char *cmdname = "lifetime";
wl_lifetime_t lifetime, *reply;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
if (strcmp(param, "be") == 0)
ac = AC_BE;
else if (strcmp(param, "bk") == 0)
ac = AC_BK;
else if (strcmp(param, "vi") == 0)
ac = AC_VI;
else if (strcmp(param, "vo") == 0)
ac = AC_VO;
else {
fprintf(stderr, "unexpected param %s\n", param);
return BCME_USAGE_ERROR;
}
if ((val = *++argv) == NULL) {
lifetime.ac = htod32(ac);
if ((err = wlu_var_getbuf(wl, cmdname, &lifetime, sizeof(lifetime),
&ptr)) < 0)
return err;
reply = (wl_lifetime_t *) ptr;
reply->ac = dtoh32(reply->ac);
reply->lifetime = dtoh32(reply->lifetime);
printf("Lifetime for access class '%s' is %dms\n", param, reply->lifetime);
}
else {
lifetime.ac = htod32(ac);
lifetime.lifetime = htod32((uint)strtol(val, 0, 0));
err = wlu_var_setbuf(wl, cmdname, &lifetime, sizeof(lifetime));
}
return err;
}
#define VNDR_IE_OK_FLAGS \
(VNDR_IE_BEACON_FLAG | VNDR_IE_PRBRSP_FLAG | VNDR_IE_ASSOCRSP_FLAG | \
VNDR_IE_AUTHRSP_FLAG | VNDR_IE_PRBREQ_FLAG | VNDR_IE_ASSOCREQ_FLAG | \
VNDR_IE_IWAPID_FLAG)
static int
wl_add_ie(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(cmd);
return (wl_vndr_ie(wl, "add", VNDR_IE_OK_FLAGS, argv));
}
static int
wl_del_ie(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(cmd);
return (wl_vndr_ie(wl, "del", VNDR_IE_OK_FLAGS, argv));
}
int
wl_mk_ie_setbuf(const char *command, uint32 pktflag_ok, char **argv,
vndr_ie_setbuf_t **buf, int *buf_len)
{
vndr_ie_setbuf_t *ie_setbuf;
uint32 pktflag;
int ielen, datalen, buflen, iecount;
int err = 0;
if (!argv[1] || !argv[2] || !argv[3]) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
pktflag = (uint)strtol(argv[1], 0, 0);
if (pktflag & ~pktflag_ok) {
fprintf(stderr, "Invalid packet flag 0x%x (%d)\n", pktflag, pktflag);
return BCME_BADARG;
}
ielen = atoi(argv[2]);
if (ielen > VNDR_IE_MAX_LEN) {
fprintf(stderr, "IE length is %d, should be <= %d\n", ielen, VNDR_IE_MAX_LEN);
return BCME_BADARG;
}
else if (ielen < VNDR_IE_MIN_LEN) {
fprintf(stderr, "IE length is %d, should be >= %d\n", ielen, VNDR_IE_MIN_LEN);
return BCME_BADARG;
}
if (strlen(argv[3]) != OUI_STR_SIZE) {
fprintf(stderr, "Invalid OUI length %d\n", (int)strlen(argv[3]));
return BCME_BADARG;
}
datalen = ielen - VNDR_IE_MIN_LEN;
if (datalen > 0) {
if (!argv[4]) {
fprintf(stderr, "Data bytes should be specified for IE of length %d\n",
ielen);
return BCME_USAGE_ERROR;
}
else {
/* Ensure each data byte is 2 characters long */
if ((int)strlen (argv[4]) < (datalen * 2)) {
fprintf(stderr, "Please specify all the data bytes for this IE\n");
return BCME_USAGE_ERROR;
}
}
}
if (datalen == 0 && (argv[4] != NULL))
fprintf(stderr, "Ignoring data bytes for IE of length %d", ielen);
buflen = sizeof(vndr_ie_setbuf_t) + datalen - 1;
ie_setbuf = (vndr_ie_setbuf_t *) malloc(buflen);
if (ie_setbuf == NULL) {
fprintf(stderr, "memory alloc failure\n");
return BCME_NOMEM;
}
/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
strncpy(ie_setbuf->cmd, command, VNDR_IE_CMD_LEN - 1);
ie_setbuf->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
/* Buffer contains only 1 IE */
iecount = htod32(1);
memcpy((void *)&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int));
/*
* The packet flag bit field indicates the packets that will
* contain this IE
*/
pktflag = htod32(pktflag);
memcpy((void *)&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag,
&pktflag, sizeof(uint32));
/* Now, add the IE to the buffer */
ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar) DOT11_MNG_PROPR_ID;
ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (uchar) ielen;
if ((err = get_oui_bytes ((uchar *)argv[3],
&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0]))) {
free(ie_setbuf);
fprintf(stderr, "Error parsing OUI arg\n");
return BCME_BADARG;
}
if (datalen > 0) {
if ((err = get_ie_data ((uchar *)argv[4],
&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0],
datalen))) {
free(ie_setbuf);
fprintf(stderr, "Error parsing data arg\n");
return BCME_BADARG;
}
}
/* Copy-out */
if (buf) {
*buf = ie_setbuf;
ie_setbuf = NULL;
}
if (buf_len)
*buf_len = buflen;
/* Clean-up */
if (ie_setbuf)
free(ie_setbuf);
return (err);
}
static int
wl_vndr_ie(void *wl, const char *command, uint32 pktflag_ok, char **argv)
{
vndr_ie_setbuf_t *ie_setbuf;
int buflen;
int err = 0;
int ret;
int bsscfg_idx = 0;
int consumed = 0;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv + 1, argv[0], &bsscfg_idx, &consumed)) != 0)
return ret;
if (consumed)
argv = argv + consumed;
else
bsscfg_idx = -1;
if ((err = wl_mk_ie_setbuf(command, pktflag_ok, argv, &ie_setbuf, &buflen)) != 0)
return err;
if (bsscfg_idx == -1)
err = wlu_var_setbuf(wl, "ie", ie_setbuf, buflen);
else
err = wlu_bssiovar_setbuf(wl, "ie", bsscfg_idx,
ie_setbuf, buflen, buf, WLC_IOCTL_MAXLEN);
free(ie_setbuf);
return (err);
}
int
wl_list_ie(void *wl, cmd_t *cmd, char **argv)
{
int err;
void *ptr;
ie_getbuf_t param;
BCM_REFERENCE(argv);
param.pktflag = (uint32) -1;
param.id = (uint8) DOT11_MNG_PROPR_ID;
err = wlu_var_getbuf(wl, cmd->name, &param, sizeof(param), &ptr);
if (err == 0) {
wl_dump_ie_buf((vndr_ie_buf_t *)ptr);
} else {
fprintf(stderr, "Error %d getting IOVar\n", err);
}
return err;
}
static int
_wl_list_ie(void *wl, cmd_t *cmd, char **argv)
{
int err;
const char *old = cmd->name;
cmd->name = "ie";
err = wl_list_ie(wl, cmd, argv);
cmd->name = old;
return err;
}
static void
wl_dump_ie_buf(vndr_ie_buf_t *ie_getbuf)
{
uchar *iebuf;
uchar *data;
int tot_ie, pktflag, iecount, count, datalen, col;
vndr_ie_info_t *ie_info;
vndr_ie_t *ie;
memcpy(&tot_ie, (void *)&ie_getbuf->iecount, sizeof(int));
tot_ie = dtoh32(tot_ie);
printf("Total IEs %d\n", tot_ie);
iebuf = (uchar *)&ie_getbuf->vndr_ie_list[0];
for (iecount = 0; iecount < tot_ie; iecount++) {
ie_info = (vndr_ie_info_t *) iebuf;
memcpy(&pktflag, (void *)&ie_info->pktflag, sizeof(uint32));
pktflag = dtoh32(pktflag);
iebuf += sizeof(uint32);
printf("\n");
ie = &ie_info->vndr_ie_data;
printf("IE index = %d\n", iecount);
printf("-----------------\n");
printf("Pkt Flg = 0x%x\n", pktflag);
printf("Length = %d\n", ie->len);
printf("OUI = %02x:%02x:%02x\n",
ie->oui[0], ie->oui[1], ie->oui[2]);
printf("Data:\n");
data = &ie->data[0];
datalen = ie->len - VNDR_IE_MIN_LEN;
for (count = 0; (count < datalen);) {
for (col = 0; (col < MAX_DATA_COLS) &&
(count < datalen); col++, count++) {
printf("%02x ", *data++);
}
printf("\n");
}
iebuf += ie->len + VNDR_IE_HDR_LEN;
}
}
static int
wl_rand(void *wl, cmd_t *cmd, char **argv)
{
char *randbuf;
uint16 randnum;
int err;
void *ptr;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf (wl, cmd->name, NULL, 0, &ptr)))
return (err);
randbuf = (char *)ptr;
memcpy(&randnum, randbuf, sizeof(uint16));
printf("%d\n", randnum);
return (0);
}
#define PRVAL(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh32(cnt->name))
#define PRVALSIX(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh32(cnt_six->name))
#define PRNL() pbuf += sprintf(pbuf, "\n")
#define WL_CNT_VERSION_SIX 6
static int
wl_counters(void *wl, cmd_t *cmd, char **argv)
{
char *statsbuf;
wl_cnt_t *cnt;
wl_cnt_ver_six_t *cnt_six;
int err;
uint i;
char *pbuf = buf;
void *ptr;
uint16 ver;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_med (wl, cmd->name, NULL, 0, &ptr)))
return (err);
statsbuf = (char *)ptr;
ver = *(uint16*)statsbuf;
if ((ver > WL_CNT_T_VERSION) && (ver != WL_CNT_T_VERSION_7001)) {
printf("\tIncorrect version of counters struct: expected %d; got %d\n",
WL_CNT_T_VERSION, ver);
return -1;
}
else if (ver == WL_CNT_VERSION_SIX) {
printf("\tUse version 6 counters struct\n");
}
else {
if ((ver != WL_CNT_T_VERSION) && (ver != WL_CNT_T_VERSION_7001)) {
printf("\tIncorrect version of counters struct: expected %d; got %d\n",
WL_CNT_T_VERSION, ver);
printf("\tDisplayed values may be incorrect\n");
}
}
cnt_six = (wl_cnt_ver_six_t*)malloc(sizeof(wl_cnt_ver_six_t));
if (cnt_six == NULL) {
printf("\tCan not allocate %d bytes for counters six struct\n",
(int)sizeof(wl_cnt_ver_six_t));
return BCME_NOMEM;
} else
memcpy(cnt_six, statsbuf, sizeof(wl_cnt_ver_six_t));
cnt = (wl_cnt_t*)malloc(sizeof(wl_cnt_t));
if (cnt == NULL) {
if (cnt_six) free(cnt_six);
printf("\tCan not allocate %d bytes for counters struct\n",
(int)sizeof(wl_cnt_t));
return BCME_NOMEM;
} else
memcpy(cnt, statsbuf, sizeof(wl_cnt_t));
/* summary stat counter line */
PRVAL(txframe); PRVAL(txbyte); PRVAL(txretrans); PRVAL(txerror);
PRVAL(rxframe); PRVAL(rxbyte); PRVAL(rxerror); PRNL();
PRVAL(txprshort); PRVAL(txdmawar); PRVAL(txnobuf); PRVAL(txnoassoc);
PRVAL(txchit); PRVAL(txcmiss); PRNL();
PRVAL(reset); PRVAL(txserr); PRVAL(txphyerr); PRVAL(txphycrs);
PRVAL(txfail); PRVAL(tbtt); PRNL();
pbuf += sprintf(pbuf, "d11_txfrag %u d11_txmulti %u d11_txretry %u d11_txretrie %u\n",
dtoh32(cnt->txfrag), dtoh32(cnt->txmulti), dtoh32(cnt->txretry),
dtoh32(cnt->txretrie));
pbuf += sprintf(pbuf, "d11_txrts %u d11_txnocts %u d11_txnoack %u d11_txfrmsnt %u\n",
dtoh32(cnt->txrts), dtoh32(cnt->txnocts), dtoh32(cnt->txnoack),
dtoh32(cnt->txfrmsnt));
PRVAL(rxcrc); PRVAL(rxnobuf); PRVAL(rxnondata); PRVAL(rxbadds);
PRVAL(rxbadcm); PRVAL(rxdup); PRVAL(rxfragerr); PRNL();
PRVAL(rxrunt); PRVAL(rxgiant); PRVAL(rxnoscb); PRVAL(rxbadproto);
PRVAL(rxbadsrcmac); PRNL();
pbuf += sprintf(pbuf, "d11_rxfrag %u d11_rxmulti %u d11_rxundec %u\n",
dtoh32(cnt->rxfrag), dtoh32(cnt->rxmulti), dtoh32(cnt->rxundec));
PRVAL(rxctl); PRVAL(rxbadda); PRVAL(rxfilter); PRNL();
pbuf += sprintf(pbuf, "rxuflo: ");
for (i = 0; i < NFIFO; i++)
pbuf += sprintf(pbuf, "%u ", dtoh32(cnt->rxuflo[i]));
pbuf += sprintf(pbuf, "\n");
PRVAL(txallfrm); PRVAL(txrtsfrm); PRVAL(txctsfrm); PRVAL(txackfrm); PRNL();
PRVAL(txdnlfrm); PRVAL(txbcnfrm); PRVAL(txtplunfl); PRVAL(txphyerr); PRNL();
pbuf += sprintf(pbuf, "txfunfl: ");
for (i = 0; i < NFIFO; i++)
pbuf += sprintf(pbuf, "%u ", dtoh32(cnt->txfunfl[i]));
pbuf += sprintf(pbuf, "\n");
/* WPA2 counters */
PRNL();
if ((cnt->version == WL_CNT_VERSION_SIX) && (cnt->version != WL_CNT_T_VERSION)) {
PRVALSIX(tkipmicfaill); PRVALSIX(tkipicverr); PRVALSIX(tkipcntrmsr); PRNL();
PRVALSIX(tkipreplay); PRVALSIX(ccmpfmterr); PRVALSIX(ccmpreplay); PRNL();
PRVALSIX(ccmpundec); PRVALSIX(fourwayfail); PRVALSIX(wepundec); PRNL();
PRVALSIX(wepicverr); PRVALSIX(decsuccess); PRVALSIX(rxundec); PRNL();
} else {
PRVAL(tkipmicfaill); PRVAL(tkipicverr); PRVAL(tkipcntrmsr); PRNL();
PRVAL(tkipreplay); PRVAL(ccmpfmterr); PRVAL(ccmpreplay); PRNL();
PRVAL(ccmpundec); PRVAL(fourwayfail); PRVAL(wepundec); PRNL();
PRVAL(wepicverr); PRVAL(decsuccess); PRVAL(rxundec); PRNL();
}
PRNL();
PRVAL(rxfrmtoolong); PRVAL(rxfrmtooshrt);
PRVAL(rxinvmachdr); PRVAL(rxbadfcs); PRNL();
PRVAL(rxbadplcp); PRVAL(rxcrsglitch);
PRVAL(rxstrt); PRVAL(rxdfrmucastmbss); PRNL();
PRVAL(rxmfrmucastmbss); PRVAL(rxcfrmucast);
PRVAL(rxrtsucast); PRVAL(rxctsucast); PRNL();
PRVAL(rxackucast); PRVAL(rxdfrmocast);
PRVAL(rxmfrmocast); PRVAL(rxcfrmocast); PRNL();
PRVAL(rxrtsocast); PRVAL(rxctsocast);
PRVAL(rxdfrmmcast); PRVAL(rxmfrmmcast); PRNL();
PRVAL(rxcfrmmcast); PRVAL(rxbeaconmbss);
PRVAL(rxdfrmucastobss); PRVAL(rxbeaconobss); PRNL();
PRVAL(rxrsptmout); PRVAL(bcntxcancl);
PRVAL(rxf0ovfl); PRVAL(rxf1ovfl); PRNL();
PRVAL(rxf2ovfl); PRVAL(txsfovfl); PRVAL(pmqovfl);
if (cnt->version == WL_CNT_T_VERSION_7001)
PRVAL(rxbcnlossmbss);
PRNL();
PRVAL(rxcgprqfrm); PRVAL(rxcgprsqovfl);
PRVAL(txcgprsfail); PRVAL(txcgprssuc); PRNL();
PRVAL(prs_timeout); PRVAL(rxnack); PRVAL(frmscons);
PRVAL(txnack); PRVAL(txglitch_nack); PRNL();
PRVAL(txburst); PRVAL(txphyerror); PRNL();
if ((cnt->version == WL_CNT_VERSION_SIX) && (cnt->version != WL_CNT_T_VERSION)) {
PRVALSIX(txchanrej); PRNL();
/* per-rate receive counters */
PRVALSIX(rx1mbps); PRVALSIX(rx2mbps); PRVALSIX(rx5mbps5); PRNL();
PRVALSIX(rx6mbps); PRVALSIX(rx9mbps); PRVALSIX(rx11mbps); PRNL();
PRVALSIX(rx12mbps); PRVALSIX(rx18mbps); PRVALSIX(rx24mbps); PRNL();
PRVALSIX(rx36mbps); PRVALSIX(rx48mbps); PRVALSIX(rx54mbps); PRNL();
PRVALSIX(pktengrxducast); PRVALSIX(pktengrxdmcast); PRNL();
PRVALSIX(txmpdu_sgi); PRVALSIX(rxmpdu_sgi); PRVALSIX(txmpdu_stbc);
PRVALSIX(rxmpdu_stbc); PRNL();
} else {
PRVAL(txchanrej); PRNL();
if (cnt->version >= 4) {
/* per-rate receive counters */
PRVAL(rx1mbps); PRVAL(rx2mbps); PRVAL(rx5mbps5); PRNL();
PRVAL(rx6mbps); PRVAL(rx9mbps); PRVAL(rx11mbps); PRNL();
PRVAL(rx12mbps); PRVAL(rx18mbps); PRVAL(rx24mbps); PRNL();
PRVAL(rx36mbps); PRVAL(rx48mbps); PRVAL(rx54mbps); PRNL();
}
if (cnt->version >= 5) {
PRVAL(pktengrxducast); PRVAL(pktengrxdmcast); PRNL();
}
if (cnt->version >= 6) {
PRVAL(txmpdu_sgi); PRVAL(rxmpdu_sgi); PRVAL(txmpdu_stbc);
PRVAL(rxmpdu_stbc); PRNL();
}
}
pbuf += sprintf(pbuf, "\n");
fputs(buf, stdout);
if (cnt)
free(cnt);
if (cnt_six)
free(cnt_six);
return (0);
}
static int
wl_delta_stats(void *wl, cmd_t *cmd, char **argv)
{
char *statsbuf;
wl_delta_stats_t *cnt;
int err;
char *pbuf = buf;
void *ptr;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_med (wl, cmd->name, NULL, 0, &ptr)))
return (err);
statsbuf = (char *)ptr;
cnt = (wl_delta_stats_t*)malloc(sizeof(wl_delta_stats_t));
if (cnt == NULL) {
printf("\tCan not allocate %d bytes for wl delta stats struct\n",
(int)sizeof(wl_delta_stats_t));
return BCME_NOMEM;
}
memcpy(cnt, statsbuf, sizeof(wl_delta_stats_t));
cnt->version = dtoh16(cnt->version);
cnt->length = dtoh16(cnt->length);
if (cnt->version != WL_DELTA_STATS_T_VERSION) {
printf("\tIncorrect version of delta stats struct: expected %d; got %d\n",
WL_DELTA_STATS_T_VERSION, cnt->version);
free(cnt);
return -1;
}
PRVAL(txframe); PRVAL(txbyte); PRVAL(txretrans); PRVAL(txfail); PRNL();
PRVAL(rxframe); PRVAL(rxbyte); PRNL();
PRVAL(rx1mbps); PRVAL(rx2mbps); PRVAL(rx5mbps5); PRVAL(rx6mbps); PRNL();
PRVAL(rx9mbps); PRVAL(rx11mbps); PRVAL(rx12mbps); PRVAL(rx18mbps); PRNL();
PRVAL(rx24mbps); PRVAL(rx36mbps); PRVAL(rx48mbps); PRVAL(rx54mbps); PRNL();
pbuf += sprintf(pbuf, "\n");
PRVAL(rxbadplcp); PRVAL(rxcrsglitch); PRVAL(bphy_rxcrsglitch); PRVAL(bphy_badplcp);
pbuf += sprintf(pbuf, "\n");
fputs(buf, stdout);
if (cnt != NULL)
free(cnt);
return (0);
}
static int
wl_wme_counters(void *wl, cmd_t *cmd, char **argv)
{
char *statsbuf;
wl_wme_cnt_t cnt;
int err;
void *ptr;
char *pbuf = buf;
uint ac;
int ap_mode = 0;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_sm (wl, cmd->name, NULL, 0, &ptr)))
return (err);
statsbuf = (char *)ptr;
memcpy(&cnt, statsbuf, sizeof(cnt));
cnt.version = dtoh16(cnt.version);
cnt.length = dtoh16(cnt.length);
if (cnt.version != WL_WME_CNT_VERSION) {
printf("\tIncorrect version of counters struct: expected %d; got %d\n",
WL_WME_CNT_VERSION, cnt.version);
return -1;
}
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))) < 0) {
return err;
}
ap_mode = dtoh32(ap_mode);
/* summary stat counter line */
for (ac = AC_BE; ac < AC_COUNT; ac++) {
pbuf += sprintf(pbuf, "\n%s: tx frames: %u bytes: %u failed frames: %u "
"failed bytes: %u\n",
ac_names[ac], dtoh32(cnt.tx[ac].packets), dtoh32(cnt.tx[ac].bytes),
dtoh32(cnt.tx_failed[ac].packets), dtoh32(cnt.tx_failed[ac].bytes));
pbuf += sprintf(pbuf, " rx frames: %u bytes: %u failed frames: %u "
"failed bytes: %u\n", dtoh32(cnt.rx[ac].packets),
dtoh32(cnt.rx[ac].bytes), dtoh32(cnt.rx_failed[ac].packets),
dtoh32(cnt.rx_failed[ac].bytes));
if (ap_mode)
pbuf += sprintf(pbuf, " foward frames: %u bytes: %u \n",
dtoh32(cnt.forward[ac].packets),
dtoh32(cnt.forward[ac].bytes));
pbuf += sprintf(pbuf, " tx frames time expired: %u \n",
dtoh32(cnt.tx_expired[ac].packets));
}
pbuf += sprintf(pbuf, "\n");
fputs(buf, stdout);
return (0);
}
static int
wl_devpath(void *wl, cmd_t *cmd, char **argv)
{
int err;
void *ptr;
char *pbuf = buf;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_sm (wl, cmd->name, NULL, 0, &ptr)))
return (err);
pbuf += strlen(buf);
sprintf(pbuf, "\n");
fputs(buf, stdout);
return (0);
}
static int
wl_diag(void *wl, cmd_t *cmd, char **argv)
{
uint testindex;
int buflen, err;
char *param;
uint32 testresult;
if (!*++argv) {
printf(" Usage: %s testindex[1-4]\n", cmd->name);
return BCME_USAGE_ERROR;
}
testindex = atoi(*argv);
strcpy(buf, "diag");
buflen = strlen(buf) + 1;
param = (char *)(buf + buflen);
testindex = htod32(testindex);
memcpy(param, (char*)&testindex, sizeof(testindex));
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
testresult = *(uint32 *)buf;
testindex = dtoh32(testindex);
testresult = dtoh32(testresult);
if (testresult != 0) {
printf("\ndiag test %d failed(error code %d)\n", testindex, testresult);
} else
printf("\ndiag test %d passed\n", testindex);
return (0);
}
static int
wl_phy_rssiant(void *wl, cmd_t *cmd, char **argv)
{
uint32 antindex;
int buflen, err;
char *param;
int16 antrssi;
if (!*++argv) {
printf(" Usage: %s antenna_index[0-3]\n", cmd->name);
return BCME_USAGE_ERROR;
}
antindex = htod32(atoi(*argv));
strcpy(buf, "nphy_rssiant");
buflen = strlen(buf) + 1;
param = (char *)(buf + buflen);
memcpy(param, (char*)&antindex, sizeof(antindex));
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
antindex = dtoh32(antindex);
antrssi = dtoh16(*(int16 *)buf);
printf("\nnphy_rssiant ant%d = %d\n", antindex, antrssi);
return (0);
}
static int
get_oui_bytes(uchar *oui_str, uchar *oui)
{
int idx;
uchar val;
uchar *src, *dest;
char hexstr[3];
src = oui_str;
dest = oui;
for (idx = 0; idx < MAX_OUI_SIZE; idx++) {
hexstr[0] = src[0];
hexstr[1] = src[1];
hexstr[2] = '\0';
val = (uchar) strtoul(hexstr, NULL, 16);
*dest++ = val;
src += 2;
if ((idx < (MAX_OUI_SIZE - 1)) && (*src++ != ':'))
return -1;
}
return 0;
}
static int
get_ie_data(uchar *data_str, uchar *ie_data, int len)
{
uchar *src, *dest;
uchar val;
int idx;
char hexstr[3];
src = data_str;
dest = ie_data;
for (idx = 0; idx < len; idx++) {
hexstr[0] = src[0];
hexstr[1] = src[1];
hexstr[2] = '\0';
val = (uchar) strtoul(hexstr, NULL, 16);
*dest++ = val;
src += 2;
}
return 0;
}
static int
hexstrtobitvec(const char *cp, uchar *bitvec, int veclen)
{
uchar value = 0;
int nibble; /* index of current hex-format nibble to process */
int even; /* 1 if even number of nibbles, 0 if odd number */
int i = 0;
if (cp[0] == '0' && cp[1] == 'x')
cp += 2;
memset(bitvec, '\0', veclen);
nibble = strlen(cp);
if (!nibble)
return -1;
even = ((nibble % 2) == 0);
/* convert from right to left (lsb is rightmost byte) */
--nibble;
while (nibble >= 0 && i < veclen && (isxdigit((int)cp[nibble]) &&
(value = isdigit((int)cp[nibble]) ? cp[nibble]-'0' :
(islower((int)cp[nibble]) ? toupper((int)cp[nibble]) : cp[nibble])-'A'+10) < 16)) {
if (even == ((nibble+1) % 2)) {
bitvec[i] += value*16;
++i;
} else
bitvec[i] = value;
--nibble;
}
return ((nibble == -1 && i <= veclen) ? 0 : -1);
}
static int
wl_eventbitvec(void *wl, cmd_t *cmd, char **argv)
{
char *vbuf;
int err;
uchar bitvec[WL_EVENTING_MASK_LEN];
bool skipzeros;
int i;
err = 0;
skipzeros = TRUE;
/* set */
if (argv[1]) {
memset(bitvec, '\0', sizeof(bitvec));
if (!(err = hexstrtobitvec(argv[1], bitvec, sizeof(bitvec))))
err = wlu_var_setbuf(wl, cmd->name, bitvec, sizeof(bitvec));
else
return BCME_BADARG;
}
/* get */
else {
void *ptr;
memset(buf, '\0', WLC_IOCTL_MAXLEN);
if (!(err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr))) {
vbuf = (char *)ptr;
printf("0x");
for (i = (sizeof(bitvec) - 1); i >= 0; i--) {
if (vbuf[i] || (i == 0))
skipzeros = FALSE;
if (skipzeros)
continue;
printf("%02x", vbuf[i] & 0xff);
}
printf("\n");
}
}
return (err);
}
static int
wl_auto_channel_sel(void *wl, cmd_t *cmd, char **argv)
{
/*
* The following condition(s) must be met when Auto Channel Selection
* is enabled.
* - the I/F is up (change radio channel requires it is up?)
* - the AP must not be associated (setting SSID to empty should
* make sure it for us)
*/
int chosen = 0;
wl_uint32_list_t request;
int ret = 0;
if (!*++argv) {
ret = wlu_get(wl, cmd->get, &chosen, sizeof(chosen));
chosen = wl_chspec32_from_driver(chosen);
if (ret >= 0 && chosen != 0) {
wf_chspec_ntoa((chanspec_t)chosen, buf);
printf("%s (0x%x)\n", buf, chosen);
return 0;
}
else {
if (chosen == 0)
printf("invalid chanspec (0x%x)\n", chosen);
}
} else {
if (atoi(*argv) == 1) {
request.count = htod32(0);
ret = wlu_set(wl, cmd->set, &request, sizeof(request));
} else if (atoi(*argv) == 2) {
ret = wlu_get(wl, cmd->get, &chosen, sizeof(chosen));
if (ret >= 0 && chosen != 0)
ret = wlu_iovar_setint(wl, "chanspec", (int)chosen);
} else {
ret = BCME_BADARG;
}
}
return ret;
}
static int
wl_varstr(void *wl, cmd_t *cmd, char **argv)
{
int error;
char *str;
if (!*++argv) {
void *ptr;
if ((error = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return (error);
str = (char *)ptr;
printf("%s\n", str);
return (0);
} else {
str = *argv;
/* str length include NULL */
return wlu_var_setbuf(wl, cmd->name, str, (strlen(str)+1));
}
}
/* Return TRUE if it's one of the wc cmds. If WC_TOOL is not defined,
* it'll return TRUE by default so all the commands are allowed.
*/
bool wc_cmd_check(const char *cmd_name)
{
uint j;
if (wc_cmds == NULL)
return TRUE;
for (j = 0; j < ARRAYSIZE(wc_cmds); j++)
if (strcmp(wc_cmds[j], cmd_name) == 0)
return TRUE;
return FALSE;
}
#define NUM_TSLIST_ARG 3 /* minimum number of arguments required for TSLIST */
#define NUM_TSLIST_PER_EA_ARG 3 /* minimum number of arguments required for TSLIST */
#define MIN_NUM_DELTS_ARG 4 /* minimum number of arguments required for DELTS */
#define MIN_NUM_DELTS_EA_ARG 5 /* minimum number of arguments required for DELTS */
#define MIN_NUM_ADDTS_ARG 20 /* minimum number of arguments required for ADDTS */
#define PERIODIC_TRAFFIC 1 /* Periodic traffic type */
#define VO_TID (0 << 1) /* voice TID */
#define VI_TID (1 << 1) /* signal TID */
#define UPLINK_DIRECTION (0 << 5) /* uplink direction traffic stream */
#define DOWNLINK_DIRECTION (1 << 5) /* downlink direction traffic stream */
#define BI_DIRECTION (3 << 5) /* bi direction traffic stream */
#define EDCA_ACCESS (1 << 7) /* EDCA access policy */
#define UAPSD_PSB (1 << 2) /* U-APSD power saving behavior */
#define VO_USER_PRIO (6 << 3) /* voice user priority */
#define VI_USER_PRIO (4 << 3) /* signal user priority */
#define TID_SHIFT 1 /* TID Shift */
#define UP_SHIFT 3 /* UP Shift */
static void
wl_cac_format_tspec_htod(tspec_arg_t *tspec_arg)
{
tspec_arg->version = htod16(tspec_arg->version);
tspec_arg->length = htod16(tspec_arg->length);
tspec_arg->flag = htod32(tspec_arg->flag);
tspec_arg->nom_msdu_size = htod16(tspec_arg->nom_msdu_size);
tspec_arg->max_msdu_size = htod16(tspec_arg->max_msdu_size);
tspec_arg->min_srv_interval = htod32(tspec_arg->min_srv_interval);
tspec_arg->max_srv_interval = htod32(tspec_arg->max_srv_interval);
tspec_arg->inactivity_interval = htod32(tspec_arg->inactivity_interval);
tspec_arg->suspension_interval = htod32(tspec_arg->suspension_interval);
tspec_arg->srv_start_time = htod32(tspec_arg->srv_start_time);
tspec_arg->min_data_rate = htod32(tspec_arg->min_data_rate);
tspec_arg->mean_data_rate = htod32(tspec_arg->mean_data_rate);
tspec_arg->peak_data_rate = htod32(tspec_arg->peak_data_rate);
tspec_arg->max_burst_size = htod32(tspec_arg->max_burst_size);
tspec_arg->delay_bound = htod32(tspec_arg->delay_bound);
tspec_arg->min_phy_rate = htod32(tspec_arg->min_phy_rate);
tspec_arg->surplus_bw = htod16(tspec_arg->surplus_bw);
tspec_arg->medium_time = htod16(tspec_arg->medium_time);
}
static void
wl_cac_format_tspec_dtoh(tspec_arg_t *tspec_arg)
{
tspec_arg->version = dtoh16(tspec_arg->version);
tspec_arg->length = dtoh16(tspec_arg->length);
tspec_arg->flag = dtoh32(tspec_arg->flag);
tspec_arg->nom_msdu_size = dtoh16(tspec_arg->nom_msdu_size);
tspec_arg->max_msdu_size = dtoh16(tspec_arg->max_msdu_size);
tspec_arg->min_srv_interval = dtoh32(tspec_arg->min_srv_interval);
tspec_arg->max_srv_interval = dtoh32(tspec_arg->max_srv_interval);
tspec_arg->inactivity_interval = dtoh32(tspec_arg->inactivity_interval);
tspec_arg->suspension_interval = dtoh32(tspec_arg->suspension_interval);
tspec_arg->srv_start_time = dtoh32(tspec_arg->srv_start_time);
tspec_arg->min_data_rate = dtoh32(tspec_arg->min_data_rate);
tspec_arg->mean_data_rate = dtoh32(tspec_arg->mean_data_rate);
tspec_arg->peak_data_rate = dtoh32(tspec_arg->peak_data_rate);
tspec_arg->max_burst_size = dtoh32(tspec_arg->max_burst_size);
tspec_arg->delay_bound = dtoh32(tspec_arg->delay_bound);
tspec_arg->min_phy_rate = dtoh32(tspec_arg->min_phy_rate);
tspec_arg->surplus_bw = dtoh16(tspec_arg->surplus_bw);
tspec_arg->medium_time = dtoh16(tspec_arg->medium_time);
}
static void wl_cac_addts_usage(void)
{
fprintf(stderr, "Too few arguments\n");
fprintf(stderr, "wl cac_addts ver dtoken tid dir psb up a b c d e ...\n");
fprintf(stderr, "\twhere ver is the structure version\n");
fprintf(stderr, "\twhere dtoken is the dialog token [range 1-255]\n");
fprintf(stderr, "\twhere tid is the tspec identifier [range 0-7]\n");
fprintf(stderr, "\twhere dir is direction [uplink | downlink | bi-directional]\n");
fprintf(stderr, "\twhere psb is power save mode [legacy|U-APSD]\n");
fprintf(stderr, "\twhere up is user priority [range 0-7]\n");
fprintf(stderr, "\twhere a is the nominal MSDU size\n");
fprintf(stderr, "\twhere b is bool for fixed size msdu [ 0 and 1]\n");
fprintf(stderr, "\twhere c is the maximum MSDU size\n");
fprintf(stderr, "\twhere d is the minimum service interval\n");
fprintf(stderr, "\twhere e is the maximum service interval\n");
fprintf(stderr, "\twhere f is the inactivity interval\n");
fprintf(stderr, "\twhere g is the suspension interval\n");
fprintf(stderr, "\twhere h is the minimum data rate\n");
fprintf(stderr, "\twhere i is the mean data rate\n");
fprintf(stderr, "\twhere j is the peak data rate\n");
fprintf(stderr, "\twhere k is the max burst size\n");
fprintf(stderr, "\twhere l is the delay bound\n");
fprintf(stderr, "\twhere m is the surplus bandwidth [fixed point notation]\n");
fprintf(stderr, "\twhere n is the minimum PHY rate\n");
}
static void wl_cac_delts_usage(void)
{
fprintf(stderr, "Too few arguments\n");
fprintf(stderr, "wl cac_delts ver a b c \n");
fprintf(stderr, "\twhere ver is the tspec version\n");
fprintf(stderr, "\twhere a is byte[0] of tsinfo (bits 0-7)\n");
fprintf(stderr, "\twhere b is byte[1] of tsinfo (bits 8-15)\n");
fprintf(stderr, "\twhere c is byte[2] of tsinfo (bits 16-23)\n");
}
static int
wl_cac(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
int ap_mode = 0;
int apsta_mode = 0;
int cmd_type = 0;
tspec_arg_t tspec_arg;
char *endptr = NULL;
uint buflen;
char *arg1, *user_argv;
uint8 direction = BI_DIRECTION;
uint8 user_tid, user_prio, user_psb;
uint fixed;
if ((err = wlu_iovar_get(wl, "apsta", &apsta_mode, sizeof(apsta_mode))))
return err;
if (!apsta_mode) {
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))))
return err;
else {
if (dtoh32(ap_mode)) {
fprintf(stderr,
"This command can ONLY be executed on a STA or APSTA\n");
return err;
}
}
}
if (!strcmp(*argv, "cac_addts"))
cmd_type = 1;
else if (!strcmp(*argv, "cac_delts"))
cmd_type = 2;
else {
fprintf(stderr, "unknown command\n");
return BCME_USAGE_ERROR;
}
/* eat command name */
if (!*++argv) {
(cmd_type == 1) ? wl_cac_addts_usage():wl_cac_delts_usage();
return BCME_BADARG;
}
buflen = sizeof(tspec_arg_t);
memset((uint8 *)&tspec_arg, 0, buflen);
/* get direction option */
arg1 = *argv;
/* Unidirectional DL/UL */
if (!strcmp(arg1, "UDL") || (!strcmp(arg1, "UUL")))
direction = DOWNLINK_DIRECTION;
if (cmd_type == 1) {
uint argc = 0;
/* arg count */
while (argv[argc])
argc++;
/* required argments */
if (argc < MIN_NUM_ADDTS_ARG) {
wl_cac_addts_usage();
return BCME_USAGE_ERROR;
}
tspec_arg.length = sizeof(tspec_arg_t) - (2 * sizeof(uint16));
tspec_arg.version = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.dialog_token = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
user_tid = (uint8)strtol(*argv++, &endptr, 0);
user_tid <<= TID_SHIFT;
if (*endptr != '\0')
return BCME_USAGE_ERROR;
/* store the pointer for parsing */
user_argv = *argv++;
if (!strcmp(user_argv, "uplink"))
direction = UPLINK_DIRECTION;
else if (!strcmp(user_argv, "downlink"))
direction = DOWNLINK_DIRECTION;
else if (!strcmp(user_argv, "bi-directional"))
direction = BI_DIRECTION;
else
return BCME_USAGE_ERROR;
/* store the pointer for parsing */
user_argv = *argv++;
if (!strcmp(user_argv, "legacy"))
user_psb = 0;
else if (!strcmp(user_argv, "U-APSD"))
user_psb = UAPSD_PSB;
else
return BCME_USAGE_ERROR;
user_prio = (uint8)strtol(*argv++, &endptr, 0);
user_prio <<= UP_SHIFT;
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[0] = (uint8)(user_tid |
direction | EDCA_ACCESS);
tspec_arg.tsinfo.octets[1] = (uint8)(user_prio | user_psb);
tspec_arg.tsinfo.octets[2] = 0x00;
tspec_arg.nom_msdu_size = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
fixed = (uint)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (fixed == 1)
tspec_arg.nom_msdu_size |= 0x8000;
tspec_arg.max_msdu_size = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.min_srv_interval = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.max_srv_interval = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.inactivity_interval = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.suspension_interval = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.min_data_rate = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.mean_data_rate = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.peak_data_rate = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.max_burst_size = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.delay_bound = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.surplus_bw = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.min_phy_rate = strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
printf("Setting min_phy_rate to 0x%x\n", tspec_arg.min_phy_rate);
} else {
uint argc = 0;
/* arg count */
while (argv[argc])
argc++;
/* required argments */
if (argc < MIN_NUM_DELTS_ARG) {
wl_cac_delts_usage();
return BCME_USAGE_ERROR;
}
tspec_arg.length = sizeof(tspec_arg_t) - (2 * sizeof(uint16));
tspec_arg.version = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[0] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[1] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[2] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
wl_cac_format_tspec_htod(&tspec_arg);
err = wlu_var_setbuf(wl, cmd->name, &tspec_arg, buflen);
return err;
}
/* get a list of traffic stream (TSINFO) in driver */
static int
wl_tslist(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int i;
int ap_mode, err = -1;
int apsta_mode = 0;
struct tslist *tslist;
UNUSED_PARAMETER(argv);
if ((err = wlu_iovar_get(wl, "apsta", &apsta_mode, sizeof(apsta_mode))))
return err;
if (!apsta_mode) {
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))))
return err;
else {
if (dtoh32(ap_mode)) {
fprintf(stderr,
"This command can ONLY be executed on a STA or APSTA\n");
return err;
}
}
}
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
tslist = (struct tslist *)ptr;
tslist->count = dtoh32(tslist->count);
for (i = 0; i < tslist->count; i++)
printf("tsinfo 0x%02X 0x%02X 0x%02X TID %d User Prio %d Direction %d\n",
tslist->tsinfo[i].octets[0],
tslist->tsinfo[i].octets[1],
tslist->tsinfo[i].octets[2],
WLC_CAC_GET_TID(tslist->tsinfo[i]),
WLC_CAC_GET_USER_PRIO(tslist->tsinfo[i]),
WLC_CAC_GET_DIR(tslist->tsinfo[i]));
return 0;
}
/* get specific TSPEC in driver */
static int
wl_tspec(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int ap_mode, err = -1;
tspec_arg_t *ts, tspec_arg;
char *temp = NULL;
uint argc = 0;
if ((wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))))
return err;
ap_mode = dtoh32(ap_mode);
if (ap_mode) {
fprintf(stderr, "This command can only be executed on the STA\n");
return err;
}
/* eat command name */
argv++;
/* arg count */
while (argv[argc])
argc++;
/* required argments */
if (argc < NUM_TSLIST_ARG) {
fprintf(stderr, "Too few arguments\n");
fprintf(stderr, "wl cac_tspec 0xaa 0xbb 0xcc \n");
fprintf(stderr, "\twhere 0xaa is byte[0] of tsinfo (bits 0-7)\n");
fprintf(stderr, "\twhere 0xbb is byte[1] of tsinfo (bits 8-15)\n");
fprintf(stderr, "\twhere 0xcc is byte[2] of tsinfo (bits 16-23)\n");
return BCME_USAGE_ERROR;
}
memset((uint8 *)&tspec_arg, 0, sizeof(tspec_arg_t));
tspec_arg.tsinfo.octets[0] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[1] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
tspec_arg.tsinfo.octets[2] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
if ((err = wlu_var_getbuf(wl, cmd->name, &tspec_arg, sizeof(tspec_arg_t), &ptr)) < 0)
return err;
ts = (tspec_arg_t *)ptr;
wl_cac_format_tspec_dtoh(ts);
wl_print_tspec(ts);
return 0;
}
/* get/set max bandwidth for each access category in ap */
static int
wme_maxbw_params(void *wl, cmd_t *cmd, char **argv)
{
wme_max_bandwidth_t cur_params, new_params;
char *val_p, *ac_str, *param;
int buflen;
int aci;
int err;
int val;
int ap_mode = 0;
argv++;
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))) < 0)
return err;
if (!ap_mode) {
printf("%s: AP only\n", cmd->name);
return -1;
}
buflen = WLC_IOCTL_MAXLEN;
/* get the current max bandwidth, using buf as an input buffer. */
strcpy(buf, "wme_maxbw_params");
if ((err = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen)) < 0) {
return err;
}
/* cache the current values */
memcpy(&cur_params, buf, sizeof(wme_max_bandwidth_t));
if ((ac_str = *argv) == NULL) {
printf("WME bandwidth limit: \n");
for (aci = 0; aci < AC_COUNT; aci++) {
printf("%s: bandwidth limit %d\n", ac_names[aci],
cur_params.ac[aci]);
}
} else {
/* preload new values with current values */
memcpy(&new_params, &cur_params, sizeof(new_params));
while ((param = *argv++) != NULL) {
if ((val_p = *argv++) == NULL) {
printf("Need value following %s\n", param);
return BCME_USAGE_ERROR;
}
val = (int)strtoul(val_p, NULL, 0);
if (!strcmp(param, "be")) {
new_params.ac[AC_BE] = (uint32)val;
} else if (!strcmp(param, "bk")) {
new_params.ac[AC_BK] = (uint32)val;
} else if (!strcmp(param, "vi")) {
new_params.ac[AC_VI] = (uint32)val;
} else if (!strcmp(param, "vo")) {
new_params.ac[AC_VO] = (uint32)val;
} else {
printf("Unknown access category: %s\n", param);
return BCME_USAGE_ERROR;
}
}
strcpy(buf, "wme_maxbw_params");
memcpy(buf + strlen(buf) + 1, &new_params, sizeof(wme_max_bandwidth_t));
err = wlu_set(wl, WLC_SET_VAR, &buf[0], buflen);
}
return err;
}
/* get the tspec list for the given station */
static int
wl_tslist_ea(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int i;
int ap_mode, err = -1;
struct tslist *tslist;
scb_val_t scb_val;
if (!*++argv) {
printf("MAC address must be specified\n");
return BCME_USAGE_ERROR;
} else if (!wl_ether_atoe(*argv, &scb_val.ea)) {
printf("Malformed MAC address parameter\n");
return BCME_USAGE_ERROR;
}
if ((err = wlu_get(wl, WLC_GET_AP, &ap_mode, sizeof(ap_mode))))
return err;
ap_mode = dtoh32(ap_mode);
if ((err = wlu_var_getbuf(wl, cmd->name, &scb_val.ea, ETHER_ADDR_LEN, &ptr)) < 0)
return err;
tslist = (struct tslist *)ptr;
for (i = 0; i < tslist->count; i++)
printf("tsinfo 0x%02X 0x%02X 0x%02X TID %d User Prio %d Direction %d\n",
tslist->tsinfo[i].octets[0],
tslist->tsinfo[i].octets[1],
tslist->tsinfo[i].octets[2],
WLC_CAC_GET_TID(tslist->tsinfo[i]),
WLC_CAC_GET_USER_PRIO(tslist->tsinfo[i]),
WLC_CAC_GET_DIR(tslist->tsinfo[i]));
return 0;
}
/* get specific TSPEC for a STA */
static int
wl_tspec_ea(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int err = -1;
tspec_per_sta_arg_t tsea;
tspec_arg_t *ts;
char *temp;
uint argc = 0;
/* eat command name */
argv++;
while (argv[argc])
argc++;
/* required argments */
if (argc < (NUM_TSLIST_PER_EA_ARG + 1)) {
fprintf(stderr, "Too few arguments\n");
fprintf(stderr, "wl cac_tspec 0xaa 0xbb 0xcc xx:xx:xx:xx:xx:xx\n");
fprintf(stderr, "\twhere 0xaa is byte[0] of tsinfo (bits 0-7)\n");
fprintf(stderr, "\twhere 0xbb is byte[1] of tsinfo (bits 8-15)\n");
fprintf(stderr, "\twhere 0xcc is byte[2] of tsinfo (bits 16-23)\n");
fprintf(stderr, "\twhere xx:xx:xx:xx:xx:xx is mac address )\n");
return BCME_USAGE_ERROR;
}
memset((uint8 *)&tsea, 0, sizeof(tspec_per_sta_arg_t));
ts = &tsea.ts;
ts->tsinfo.octets[0] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
ts->tsinfo.octets[1] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
ts->tsinfo.octets[2] = (uint8)strtol(*argv++, &temp, 0);
if (*temp != '\0')
return BCME_USAGE_ERROR;
/* add the ether address after tsinfo */
if (!*argv) {
printf("MAC address must be specified\n");
return BCME_USAGE_ERROR;
} else if (!wl_ether_atoe(*argv, &tsea.ea)) {
printf("Malformed MAC address parameter\n");
return BCME_USAGE_ERROR;
}
if ((err = wlu_var_getbuf(wl, cmd->name, &tsea, sizeof(tspec_per_sta_arg_t), &ptr)) < 0)
return err;
ts = (tspec_arg_t *)ptr;
wl_cac_format_tspec_dtoh(ts);
wl_print_tspec(ts);
return 0;
}
static const uint8 wlu_wme_fifo2ac[] = { AC_BK, AC_BE, AC_VI, AC_VO, AC_BE,
AC_BE };
static const uint8 wlu_prio2fifo[NUMPRIO] = {
0, /* 0 BE AC_BE Best Effort */
1, /* 1 BK AC_BK Background */
2, /* 2 -- AC_BK Background */
3, /* 3 EE AC_BE Best Effort */
4, /* 4 CL AC_VI Video */
5, /* 5 VI AC_VI Video */
6, /* 6 VO AC_VO Voice */
7 /* 7 NC AC_VO Voice */
};
#define WME_PRIO2AC(prio) wlu_wme_fifo2ac[wlu_prio2fifo[(prio)]]
static void
wl_print_tspec(tspec_arg_t *ts)
{
const char *str;
if (ts->version != TSPEC_ARG_VERSION) {
printf("\tIncorrect version of TSPEC struct: expected %d; got %d\n",
TSPEC_ARG_VERSION, ts->version);
return;
}
if (ts->length < (sizeof(tspec_arg_t) - (2 * sizeof(uint16)))) {
printf("\tTSPEC arg length too short: expected %d; got %d\n",
(int)(sizeof(tspec_arg_t) - (2 * sizeof(uint16))), ts->length);
return;
}
switch (ts->flag & TSPEC_STATUS_MASK) {
case TSPEC_PENDING:
str = "PENDING";
break;
case TSPEC_ACCEPTED:
str = "ACCEPTED";
break;
case TSPEC_REJECTED:
str = "REJECTED";
break;
default:
str = "UNKNOWN";
break;
}
printf("version %d\n", ts->version);
printf("length %d\n", ts->length);
printf("TID %d %s\n", WLC_CAC_GET_TID(ts->tsinfo), str);
printf("tsinfo 0x%02x 0x%02x 0x%02x\n", ts->tsinfo.octets[0],
ts->tsinfo.octets[1], ts->tsinfo.octets[2]);
/* breakout bitfields for apsd */
if (WLC_CAC_GET_PSB(ts->tsinfo)) {
int ac = WME_PRIO2AC(WLC_CAC_GET_USER_PRIO(ts->tsinfo));
switch (WLC_CAC_GET_DIR(ts->tsinfo)) {
case (TS_INFO_UPLINK >> TS_INFO_DIRECTION_SHIFT):
printf("AC[%d] : Trigger enabled\n", ac);
break;
case (TS_INFO_DOWNLINK >> TS_INFO_DIRECTION_SHIFT):
printf("AC[%d] : Delivery enabled\n", ac);
break;
case (TS_INFO_BIDIRECTIONAL >>
TS_INFO_DIRECTION_SHIFT):
printf("AC[%d] : Trig & Delv enabled\n", ac);
break;
}
} else {
int ac;
ac = WME_PRIO2AC(WLC_CAC_GET_USER_PRIO(ts->tsinfo));
printf("AC [%d] : Legacy Power save\n", ac);
}
printf("nom_msdu_size %d %s\n", (ts->nom_msdu_size & 0x7fff),
((ts->nom_msdu_size & 0x8000) ? "fixed size" : ""));
printf("max_msdu_size %d\n", ts->max_msdu_size);
printf("min_srv_interval %d\n", ts->min_srv_interval);
printf("max_srv_interval %d\n", ts->max_srv_interval);
printf("inactivity_interval %d\n", ts->inactivity_interval);
printf("suspension_interval %d\n", ts->suspension_interval);
printf("srv_start_time %d\n", ts->srv_start_time);
printf("min_data_rate %d\n", ts->min_data_rate);
printf("mean_data_rate %d\n", ts->mean_data_rate);
printf("peak_data_rate %d\n", ts->peak_data_rate);
printf("max_burst_size %d\n", ts->max_burst_size);
printf("delay_bound %d\n", ts->delay_bound);
printf("min_phy_rate %d\n", ts->min_phy_rate);
printf("surplus_bw %d\n", ts->surplus_bw);
printf("medium_time %d\n", ts->medium_time);
}
/* send delts for a specific ea */
/* TODO : Club this with wl_tspec_ea */
static int
wl_cac_delts_ea(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int err = -1;
char *endptr = NULL;
tspec_per_sta_arg_t tsea;
tspec_arg_t *ts;
uint argc = 0;
/* eat command name */
argv++;
while (argv[argc])
argc++;
/* required argments */
if (argc < (NUM_TSLIST_PER_EA_ARG + 1)) {
fprintf(stderr, "Too few arguments\n");
fprintf(stderr, "wl cac_delts_ea ver 0xaa 0xbb 0xcc xx:xx:xx:xx:xx:xx\n");
fprintf(stderr, "\twhere ver is the tspec version\n");
fprintf(stderr, "\twhere 0xaa is byte[0] of tsinfo (bits 0-7)\n");
fprintf(stderr, "\twhere 0xbb is byte[1] of tsinfo (bits 8-15)\n");
fprintf(stderr, "\twhere 0xcc is byte[2] of tsinfo (bits 16-23)\n");
fprintf(stderr, "\twhere xx:xx:xx:xx:xx:xx is mac address )\n");
return BCME_USAGE_ERROR;
}
memset((uint8 *)&tsea, 0, sizeof(tspec_per_sta_arg_t));
ts = &tsea.ts;
ts->length = sizeof(tspec_arg_t) - (2 * sizeof(uint16));
ts->version = (uint16)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ts->tsinfo.octets[0] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ts->tsinfo.octets[1] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ts->tsinfo.octets[2] = (uint8)strtol(*argv++, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
/* add the ether address after tsinfo */
if (!*argv) {
printf("MAC address must be specified\n");
return BCME_USAGE_ERROR;
} else if (!wl_ether_atoe(*argv, &tsea.ea)) {
printf("Malformed MAC address parameter\n");
return BCME_USAGE_ERROR;
}
wl_cac_format_tspec_htod(ts);
if ((err = wlu_var_getbuf(wl, cmd->name, &tsea, sizeof(tspec_per_sta_arg_t), &ptr)) < 0)
return err;
return 0;
}
static int
wl_antsel(void *wl, cmd_t *cmd, char **argv)
{
const char *ant_sel = "fixed";
char *val_name;
wlc_antselcfg_t val = {{0}, 0};
int err, argc, i;
char *endptr = NULL;
uint32 txchain_bitmap = 0;
uint16 antsel_mask = 0;
/* toss the command name */
val_name = *argv++;
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_get(wl, "txchain", &txchain_bitmap, sizeof(txchain_bitmap))) < 0)
return err;
/* iterate over max 4 chains */
for (i = 0; i < 4; i ++) {
if (!(txchain_bitmap & (1<<i)))
antsel_mask |= (0xF << i * 4);
}
if ((err = wlu_iovar_get(wl, val_name, &val, sizeof(wlc_antselcfg_t))) < 0)
return err;
printf("C3C2C1C0: ");
for (i = ANT_SELCFG_TX_UNICAST; i < ANT_SELCFG_MAX; i++) {
if (val.ant_config[i] & ANT_SELCFG_AUTO)
ant_sel = "auto";
printf("0x%04X %s ",
antsel_mask | (val.ant_config[i] & ANT_SELCFG_MASK), ant_sel);
}
printf("\n");
} else {
/* arg count */
for (argc = 0; argv[argc]; argc++);
if ((argc >= 2 && argc <= 3) || argc > ANT_SELCFG_MAX) {
printf("invalid %d args\n", argc);
return BCME_USAGE_ERROR;
}
val.ant_config[ANT_SELCFG_TX_UNICAST] = (uint8)strtol(*argv++, &endptr, 0);
printf("UTX 0x%02x\n", val.ant_config[ANT_SELCFG_TX_UNICAST]);
if (*endptr != '\0') {
printf("Invaild UTX parameter: %s\n", *argv);
return BCME_USAGE_ERROR;
}
if (argc == 1) {
val.ant_config[ANT_SELCFG_RX_UNICAST] =
val.ant_config[ANT_SELCFG_TX_UNICAST];
val.ant_config[ANT_SELCFG_TX_DEF] = val.ant_config[ANT_SELCFG_TX_UNICAST];
val.ant_config[ANT_SELCFG_RX_DEF] = val.ant_config[ANT_SELCFG_TX_UNICAST];
} else {
val.ant_config[ANT_SELCFG_RX_UNICAST] = (uint8)strtol(*argv++, &endptr, 0);
printf("URX 0x%02x\n", val.ant_config[ANT_SELCFG_RX_UNICAST]);
if (*endptr != '\0') {
printf("Invaild URX parameter: %s\n", *argv);
return BCME_USAGE_ERROR;
}
val.ant_config[ANT_SELCFG_TX_DEF] = (uint8)strtol(*argv++, &endptr, 0);
printf("DTX 0x%02x\n", val.ant_config[ANT_SELCFG_TX_DEF]);
if (*endptr != '\0') {
printf("Invaild DTX parameter: %s\n", *argv);
return BCME_USAGE_ERROR;
}
val.ant_config[ANT_SELCFG_RX_DEF] = (uint8)strtol(*argv++, &endptr, 0);
printf("DRX 0x%02x\n", val.ant_config[ANT_SELCFG_RX_DEF]);
if (*endptr != '\0') {
printf("Invaild DRX parameter: %s\n", *argv);
return BCME_USAGE_ERROR;
}
}
err = wlu_iovar_set(wl, val_name, &val, sizeof(wlc_antselcfg_t));
}
return err;
}
static int
wl_txcore_pwr_offset(void *wl, cmd_t *cmd, char **argv)
{
wl_txchain_pwr_offsets_t offsets;
char *endptr;
int i;
long val;
int err;
/* toss the command name */
argv++;
if (!*argv) {
err = wlu_iovar_get(wl, cmd->name, &offsets, sizeof(wl_txchain_pwr_offsets_t));
if (err < 0)
return err;
printf("txcore offsets qdBm: %d %d %d %d\n",
offsets.offset[0], offsets.offset[1],
offsets.offset[2], offsets.offset[3]);
return 0;
}
memset(&offsets, 0, sizeof(wl_txchain_pwr_offsets_t));
for (i = 0; i < WL_NUM_TXCHAIN_MAX; i++, argv++) {
if (!*argv)
break;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (val > 0)
return BCME_BADARG;
offsets.offset[i] = (int8)val;
}
err = wlu_iovar_set(wl, cmd->name, &offsets, sizeof(wl_txchain_pwr_offsets_t));
return err;
}
static int
wl_txcore(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_txcore";
int err = 0, opt_err, val;
uint8 streams = 0;
bool streams_set = FALSE;
uint8 core = 0;
bool core_set = FALSE;
uint8 cck_mask = 0;
bool cck_set = FALSE;
uint8 ofdm_mask = 0;
bool ofdm_set = FALSE;
uint8 mcs_mask[4] = {0, 0, 0, 0}; /* pre-initialize # of streams {core:4 | stream:4} */
bool mcs_set = FALSE;
uint8 idx;
uint32 coremask[2] = {0, 0};
/* toss the command name */
argv++;
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_get(wl, cmd->name, &coremask, sizeof(uint32)*2)) < 0)
return err;
printf("txcore enabled bitmap (Nsts {4..1}) 0x%02x 0x%02x 0x%02x 0x%02x\n",
(coremask[0] >> 24) & 0xff, (coremask[0] >> 16) & 0xff,
(coremask[0] >> 8) & 0xff, coremask[0] & 0xff);
printf("txcore mask OFDM 0x%02x CCK 0x%02x\n",
(coremask[1] >> 8) & 0xff, coremask[1] & 0xff);
return 0;
}
val = atoi(*argv);
if (val == -1)
goto next;
miniopt_init(&to, fn_name, "w", FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
streams_set = TRUE;
streams = (to.val & 0x0f);
if (streams > 4)
fprintf(stderr, "%s: Nsts > %d\n", fn_name, to.val);
}
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for stf core\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
core_set = TRUE;
core = (to.val & 0x0f) << 4;
if (core == 0) {
fprintf(stderr, "%s: %1d-stream core cannot be zero\n",
fn_name, streams);
err = BCME_BADARG;
goto exit;
}
}
if (to.opt == 'o') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
ofdm_set = TRUE;
ofdm_mask = (to.val & 0x0f);
if (ofdm_mask == 0) {
fprintf(stderr, "%s: OFDM core cannot be zero\n", fn_name);
err = BCME_BADARG;
goto exit;
}
}
if (to.opt == 'k') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
cck_set = TRUE;
cck_mask = (to.val & 0x0f);
if (cck_mask == 0) {
fprintf(stderr, "%s: CCK core cannot be zero\n", fn_name);
err = BCME_BADARG;
goto exit;
}
}
if (streams_set && core_set) {
streams_set = core_set = FALSE;
mcs_set = TRUE;
idx = streams - 1;
mcs_mask[idx] = (uint8)(core|streams);
}
}
if (streams_set != core_set) {
fprintf(stderr, "%s: require to set both -s x -c y\n", fn_name);
err = BCME_BADARG;
goto exit;
}
if (mcs_set) {
coremask[0] |= mcs_mask[0] << 0;
coremask[0] |= mcs_mask[1] << 8;
coremask[0] |= mcs_mask[2] << 16;
coremask[0] |= mcs_mask[3] << 24;
}
if (cck_set)
coremask[1] |= cck_mask;
if (ofdm_set)
coremask[1] |= ofdm_mask << 8;
next:
err = wlu_var_setbuf(wl, cmd->name, coremask, sizeof(uint32)*2);
exit:
return err;
}
#ifdef PLC
static int
wl_plc(void *wl, cmd_t *cmd, char **argv)
{
char *param;
char *endptr;
const char *cmdname = "plc";
wl_plc_params_t plc, *reply;
int err;
void *ptr;
UNUSED_PARAMETER(cmd);
memset(&plc, 0, sizeof(plc));
if ((param = *++argv) == NULL) {
plc.cmd = PLC_CMD_FAILOVER;
/* Get plc failover status */
err = wlu_var_getbuf_sm(wl, cmdname, &plc, sizeof(plc), &ptr);
reply = (wl_plc_params_t *)ptr;
if (err >= 0)
printf("%d\n", reply->plc_failover);
return err;
}
if ((strcmp(param, "1") == 0) || (strcmp(param, "0") == 0)) {
plc.cmd = PLC_CMD_FAILOVER;
plc.plc_failover = strtoul(param, &endptr, 0);
/* Set plc failover status */
err = wlu_var_setbuf(wl, cmdname, &plc, sizeof(plc));
return err;
} else if (strcmp(param, "node_list") == 0) {
wl_plc_nodelist_t *list = (wl_plc_nodelist_t *)buf;
uint32 i, max = (WLC_IOCTL_MAXLEN - sizeof(int)) / sizeof(wl_plc_nodelist_t);
/* Get the list of known nodes */
plc.cmd = PLC_CMD_NODE_LIST;
memset(buf, 0, WLC_IOCTL_MAXLEN);
err = wlu_iovar_getbuf(wl, cmdname, &plc, sizeof(plc), buf, WLC_IOCTL_MAXLEN);
if (err < 0)
return err;
for (i = 0; i < dtoh32(list->count) && i < max; i++) {
printf("%s %-9s %-d\n", wl_ether_etoa(&list->node[i].ea),
list->node[i].node_type == NODE_TYPE_WIFI_ONLY ? "wifi-only" :
list->node[i].node_type == NODE_TYPE_PLC_ONLY ? "plc-only" :
list->node[i].node_type == NODE_TYPE_WIFI_PLC ? "wifi-plc" :
"unknown", list->node[i].cost);
}
return err;
} else if (strcmp(param, "mac_affinity") == 0)
plc.cmd = PLC_CMD_MAC_COST;
else if (strcmp(param, "link_affinity") == 0)
plc.cmd = PLC_CMD_LINK_COST;
else
return BCME_USAGE_ERROR;
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(param, &plc.node_ea))
return BCME_USAGE_ERROR;
if ((param = *++argv) == NULL) {
/* Get plc link/mac path affinity */
err = wlu_var_getbuf_sm(wl, cmdname, &plc, sizeof(plc), &ptr);
reply = (wl_plc_params_t *)ptr;
if (err >= 0)
printf("%d\n", reply->cost);
return err;
}
/* Set plc link/mac path affinity */
plc.cost = strtoul(param, &endptr, 0);
err = wlu_var_setbuf(wl, cmdname, &plc, sizeof(plc));
return err;
}
#endif /* PLC */
static int
wl_txfifo_sz(void *wl, cmd_t *cmd, char **argv)
{
char *param;
const char *cmdname = "txfifo_sz";
wl_txfifo_sz_t ts, *reply;
uint fifo;
int err;
void *ptr = NULL;
UNUSED_PARAMETER(cmd);
if ((param = *++argv) == NULL)
return BCME_USAGE_ERROR;
fifo = atoi(param);
if (fifo > NFIFO)
return BCME_USAGE_ERROR;
ts.fifo = fifo;
ts.magic = WL_TXFIFO_SZ_MAGIC;
if ((param = *++argv)) {
ts.size = atoi(param);
err = wlu_var_setbuf(wl, cmdname, &ts, sizeof(ts));
} else {
if ((err = wlu_var_getbuf_sm(wl, cmdname, &ts, sizeof(ts), &ptr) < 0))
return err;
reply = (wl_txfifo_sz_t *)ptr;
printf("fifo %d size %d\n", fifo, reply->size);
}
return err;
}
#ifdef WLPFN
static int
wl_pfn_set(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_pfn_param_t pfn_param;
UNUSED_PARAMETER(cmd);
/* Setup default values */
pfn_param.version = PFN_VERSION;
/* Sorting based on list order, no back ground scan, no autoswitch,
* no immediate event report, no adaptvie scan, but immediate scan
*/
pfn_param.flags = (PFN_LIST_ORDER << SORT_CRITERIA_BIT | ENABLE << IMMEDIATE_SCAN_BIT);
/* Scan frequency of 30 sec */
pfn_param.scan_freq = 30;
/* slow adapt scan is off by default */
pfn_param.slow_freq = 0;
/* RSSI margin of 30 dBm */
pfn_param.rssi_margin = 30;
/* Network timeout 60 sec */
pfn_param.lost_network_timeout = 60;
/* best n = 2 by default */
pfn_param.bestn = DEFAULT_BESTN;
/* mscan m=0 by default, so not record best networks by default */
pfn_param.mscan = DEFAULT_MSCAN;
/* default repeat = 10 */
pfn_param.repeat = DEFAULT_REPEAT;
/* by default, maximum scan interval = 2^2*scan_freq when adaptive scan is turned on */
pfn_param.exp = DEFAULT_EXP;
while (*++argv) {
if (!stricmp(*argv, "scanfrq")) {
if (*++argv)
pfn_param.scan_freq = atoi(*argv);
else {
fprintf(stderr, "Missing scanfrq option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "netimeout")) {
if (*++argv)
pfn_param.lost_network_timeout = atoi(*argv);
else {
fprintf(stderr, "Missing netimeout option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "rssi_delta")) {
if (*++argv)
pfn_param.rssi_margin = atoi(*argv);
else {
fprintf(stderr, "Missing rssi_delta option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "sort")) {
if (*++argv) {
pfn_param.flags &= ~SORT_CRITERIA_MASK;
if (!stricmp(*argv, "listorder"))
pfn_param.flags |= (PFN_LIST_ORDER << SORT_CRITERIA_BIT);
else if (!stricmp(*argv, "rssi"))
pfn_param.flags |= (PFN_RSSI << SORT_CRITERIA_BIT);
else {
fprintf(stderr, "Invalid sort option %s\n", *argv);
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "Missing sort option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "immediateevent")) {
if (*++argv) {
if (!stricmp(*argv, "1")) {
pfn_param.flags |= IMMEDIATE_EVENT_MASK;
} else if (!stricmp(*argv, "0")) {
pfn_param.flags &= ~IMMEDIATE_EVENT_MASK;
} else {
fprintf(stderr, "Invalid immediateevent option\n");
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "Missing immediateevent option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "bkgscan")) {
if (*++argv) {
pfn_param.flags &= ~ENABLE_BKGRD_SCAN_MASK;
if (atoi(*argv))
pfn_param.flags |= (ENABLE << ENABLE_BKGRD_SCAN_BIT);
else
pfn_param.flags |= (DISABLE << ENABLE_BKGRD_SCAN_BIT);
} else {
fprintf(stderr, "Missing bkgscan option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "immediate")) {
pfn_param.flags &= ~IMMEDIATE_SCAN_MASK;
if (*++argv) {
if (atoi(*argv))
pfn_param.flags |= (ENABLE << IMMEDIATE_SCAN_BIT);
else
pfn_param.flags |= (DISABLE << IMMEDIATE_SCAN_BIT);
} else {
fprintf(stderr, "Missing immediate option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "bdscan")) {
if (*++argv) {
pfn_param.flags &= ~ENABLE_BD_SCAN_MASK;
if (atoi(*argv))
pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT);
else
pfn_param.flags |= (DISABLE << ENABLE_BD_SCAN_BIT);
} else {
fprintf(stderr, "Missing bdscan option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "separate")) {
if (*++argv) {
pfn_param.flags &= ~REPORT_SEPERATELY_MASK;
if (atoi(*argv))
pfn_param.flags |= (ENABLE << REPORT_SEPERATELY_BIT);
else
pfn_param.flags |= (DISABLE << REPORT_SEPERATELY_BIT);
} else {
fprintf(stderr, "Missing seperate option\n");
return -1;
}
} else if (!stricmp(*argv, "adapt")) {
if (*++argv) {
pfn_param.flags &= ~ENABLE_ADAPTSCAN_MASK;
if (!stricmp(*argv, "off")) {
pfn_param.flags |= (OFF_ADAPT << ENABLE_ADAPTSCAN_BIT);
} else if (!stricmp(*argv, "smart")) {
pfn_param.flags |= (SMART_ADAPT << ENABLE_ADAPTSCAN_BIT);
} else if (!stricmp(*argv, "strict")) {
pfn_param.flags |= (STRICT_ADAPT << ENABLE_ADAPTSCAN_BIT);
} else if (!stricmp(*argv, "slow")) {
pfn_param.flags |= (SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT);
} else {
fprintf(stderr, "Invalid adaptive scan option %s\n", *argv);
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "Missing adaptive scan option\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "bestn")) {
pfn_param.bestn = atoi(*++argv);
} else if (!stricmp(*argv, "mscan")) {
pfn_param.mscan = atoi(*++argv);
} else if (!stricmp(*argv, "repeat")) {
pfn_param.repeat = atoi(*++argv);
if (pfn_param.repeat < 1 || pfn_param.repeat > 20) {
fprintf(stderr, "repeat %d out of range (1-20)\n",
pfn_param.repeat);
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "exp")) {
pfn_param.exp = atoi(*++argv);
if (pfn_param.exp < 1 || pfn_param.exp > 5) {
fprintf(stderr, "exp %d out of range (1-5)\n",
pfn_param.exp);
return BCME_BADARG;
}
} else if (!stricmp(*argv, "slowfrq")) {
if (*++argv)
pfn_param.slow_freq = atoi(*argv);
else {
fprintf(stderr, "Missing slowfrq option\n");
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
return BCME_USAGE_ERROR;
}
}
if ((((pfn_param.flags & ENABLE_ADAPTSCAN_MASK) ==
(SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT)) &&
!pfn_param.slow_freq) ||
(((pfn_param.flags & ENABLE_ADAPTSCAN_MASK) !=
(SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT)) &&
pfn_param.slow_freq)) {
fprintf(stderr, "SLOW_ADAPT flag and slowfrq value not match\n");
return BCME_BADARG;
}
pfn_param.version = htod32(pfn_param.version);
pfn_param.scan_freq = htod32(pfn_param.scan_freq);
pfn_param.lost_network_timeout = htod32(pfn_param.lost_network_timeout);
pfn_param.flags = htod16(pfn_param.flags);
pfn_param.rssi_margin = htod16(pfn_param.rssi_margin);
pfn_param.slow_freq = htod32(pfn_param.slow_freq);
if ((err = wlu_iovar_set(wl, "pfn_set", &pfn_param, sizeof(wl_pfn_param_t))))
return (err);
return (0);
}
static bool
validate_hex(char hexchar)
{
if ((hexchar >= '0' && hexchar <= '9') ||
(hexchar >= 'a' || hexchar <= 'z') ||
(hexchar >= 'A' || hexchar <= 'Z'))
return TRUE;
else
return FALSE;
}
static uint8
char2hex(char hexchar)
{
if (hexchar >= '0' && hexchar <= '9')
return (hexchar - '0');
else if (hexchar >= 'a' && hexchar <= 'z')
return (hexchar - 'a' + 10);
else if (hexchar >= 'A' && hexchar <= 'Z')
return (hexchar - 'A' + 10);
else
{
fprintf(stderr, "non-hex\n");
return 0xff;
}
}
#define MAXNUM_SSID_PER_ADD 16
static int
wl_pfn_add(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_pfn_t *p_pfn_element = NULL;
int i, pfn_element_len, cnt;
wl_pfn_t *pssidnet = NULL;
int32 hidden;
UNUSED_PARAMETER(cmd);
pfn_element_len = MAXNUM_SSID_PER_ADD * sizeof(wl_pfn_t);
p_pfn_element = (wl_pfn_t *)malloc(pfn_element_len);
if (p_pfn_element == NULL) {
fprintf(stderr, "Failed to allocate buffer for %d bytes\n", pfn_element_len);
return BCME_NOMEM;
}
memset(p_pfn_element, '\0', pfn_element_len);
pssidnet = p_pfn_element;
for (i = 0; i < MAXNUM_SSID_PER_ADD; i++) {
/* Default setting, open, no WPA, no WEP and bss */
pssidnet->auth = DOT11_OPEN_SYSTEM;
pssidnet->wpa_auth = WPA_AUTH_DISABLED;
pssidnet->wsec = 0;
pssidnet->infra = 1;
pssidnet->flags = 0;
pssidnet++;
}
cnt = -1;
pssidnet = p_pfn_element;
while (*++argv) {
if (!stricmp(*argv, "ssid")) {
if (*++argv) {
if (++cnt >= MAXNUM_SSID_PER_ADD) {
fprintf(stderr, "exceed max 16 SSID per pfn_add\n");
err = BCME_BADARG;
goto error;
}
if (cnt > 0) {
pssidnet->flags = htod32(pssidnet->flags);
pssidnet++;
}
strncpy((char *)pssidnet->ssid.SSID, *argv,
sizeof(pssidnet->ssid.SSID));
pssidnet->ssid.SSID_len =
strlen((char *)pssidnet->ssid.SSID);
if (pssidnet->ssid.SSID_len > 32) {
fprintf(stderr, "SSID too long: %s\n", *argv);
err = BCME_BADARG;
goto error;
}
pssidnet->ssid.SSID_len = htod32(pssidnet->ssid.SSID_len);
} else {
fprintf(stderr, "no value for ssid\n");
err = BCME_USAGE_ERROR;
goto error;
}
}
else if (!stricmp(*argv, "hidden")) {
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (*++argv) {
hidden = **argv - '0';
if (hidden != ENABLE && hidden != DISABLE) {
fprintf(stderr, "invalid hidden setting, use 0/1\n");
err = BCME_USAGE_ERROR;
goto error;
}
pssidnet->flags |= hidden << WL_PFN_HIDDEN_BIT;
} else {
fprintf(stderr, "no value for hidden\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "imode")) {
if (*++argv) {
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (!stricmp(*argv, "bss")) {
pssidnet->infra = 1;
} else if (!stricmp(*argv, "ibss")) {
pssidnet->infra = 0;
} else {
fprintf(stderr, "Invalid imode arg %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
pssidnet->infra = htod32(pssidnet->infra);
} else {
fprintf(stderr, "Missing option for imode\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "amode")) {
if (*++argv) {
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (!stricmp(*argv, "open"))
pssidnet->auth = DOT11_OPEN_SYSTEM;
else if (!stricmp(*argv, "shared"))
pssidnet->auth = DOT11_SHARED_KEY;
else {
fprintf(stderr, "Invalid imode arg %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
pssidnet->auth = htod32(pssidnet->auth);
} else {
fprintf(stderr, "Missing option for amode\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "wpa_auth")) {
if (*++argv) {
uint32 wpa_auth;
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
/* figure requested auth, allow "any" */
if (!stricmp(*argv, "wpapsk"))
pssidnet->wpa_auth = WPA_AUTH_PSK;
else if (!stricmp(*argv, "wpa2psk"))
pssidnet->wpa_auth = WPA2_AUTH_PSK;
else if (!stricmp(*argv, "wpadisabled"))
pssidnet->wpa_auth = WPA_AUTH_DISABLED;
else if (!stricmp(*argv, "any"))
pssidnet->wpa_auth = WPA_AUTH_PFN_ANY;
else if ((wpa_auth = strtoul(*argv, 0, 0)))
pssidnet->wpa_auth = wpa_auth;
else {
fprintf(stderr, "Invalid wpa_auth option %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
pssidnet->wpa_auth = htod32(pssidnet->wpa_auth);
} else {
fprintf(stderr, "Missing option for wpa_auth\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "wsec")) {
if (*++argv) {
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (!stricmp(*argv, "WEP")) {
pssidnet->wsec = WEP_ENABLED;
} else if (!stricmp(*argv, "TKIP"))
pssidnet->wsec = TKIP_ENABLED;
else if (!stricmp(*argv, "AES"))
pssidnet->wsec = AES_ENABLED;
else if (!stricmp(*argv, "TKIPAES"))
pssidnet->wsec = TKIP_ENABLED | AES_ENABLED;
else {
fprintf(stderr, "Invalid wsec option %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
pssidnet->wsec = htod32(pssidnet->wsec);
} else {
fprintf(stderr, "Missing option for wsec\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "suppress")) {
if (*++argv) {
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (!stricmp(*argv, "found")) {
pssidnet->flags |= WL_PFN_SUPPRESSFOUND_MASK;
} else if (!stricmp(*argv, "lost")) {
pssidnet->flags |= WL_PFN_SUPPRESSLOST_MASK;
} else if (!stricmp(*argv, "neither")) {
pssidnet->flags &=
~(WL_PFN_SUPPRESSLOST_MASK | WL_PFN_SUPPRESSFOUND_MASK);
} else {
fprintf(stderr, "Invalid suppress option %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
} else {
fprintf(stderr, "Missing option for suppress\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "rssi")) {
if (*++argv) {
int rssi = atoi(*argv);
if (pssidnet->ssid.SSID_len == 0) {
fprintf(stderr, "Wrong! Start with SSID\n");
err = BCME_USAGE_ERROR;
goto error;
}
if (rssi >= -128 && rssi <= 0) {
pssidnet->flags |= (rssi << WL_PFN_RSSI_SHIFT)
& WL_PFN_RSSI_MASK;
} else {
fprintf(stderr, "Invalid rssi option %s\n", *argv);
err = BCME_BADARG;
goto error;
}
} else {
fprintf(stderr, "Missing option for rssi\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
}
pssidnet->flags = htod32(pssidnet->flags);
pfn_element_len = (cnt + 1) * sizeof(wl_pfn_t);
if ((err = wlu_iovar_set(wl, "pfn_add", p_pfn_element,
pfn_element_len))) {
fprintf(stderr, "pfn_add fail\n");
goto error;
}
free(p_pfn_element);
return (0);
error:
free(p_pfn_element);
return err;
}
#define MAXNUM_BSSID_PER_ADD 150
static int
wl_pfn_add_bssid(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint8 *ptr;
int i, bssidlistlen, cnt;
wl_pfn_bssid_t *bssidlist;
wl_pfn_bssid_t *pbssid = NULL;
UNUSED_PARAMETER(cmd);
if (!*(argv + 1)) {
fprintf(stderr, "Invalid command\n");
return BCME_USAGE_ERROR;
}
bssidlistlen = MAXNUM_BSSID_PER_ADD * sizeof(wl_pfn_bssid_t);
bssidlist = (wl_pfn_bssid_t *)malloc(bssidlistlen);
if (bssidlist == NULL) {
fprintf(stderr, "Failed to allocate buffer for %d bytes\n", bssidlistlen);
return BCME_NOMEM;
}
memset(bssidlist, '\0', bssidlistlen);
cnt = 0;
while (*++argv) {
if (!stricmp(*argv, "bssid")) {
if (*++argv) {
if (cnt >= MAXNUM_BSSID_PER_ADD) {
fprintf(stderr, "exceed max 150 BSSID per pfn_add_bssid\n");
err = BCME_BADARG;
goto error;
}
if (!cnt)
pbssid = bssidlist;
else {
pbssid->flags = htod16(pbssid->flags);
pbssid++;
}
ptr = (uint8 *)*argv;
for (i = 0; i < ETHER_ADDR_LEN; i++)
{
if (!validate_hex(*ptr) || !validate_hex(*(ptr + 1)))
{
fprintf(stderr, "non-hex in BSSID\n");
err = BCME_BADARG;
goto error;
}
pbssid->macaddr.octet[i] =
char2hex(*ptr) << 4 | char2hex(*(ptr+1));
ptr += 3;
}
cnt++;
} else {
fprintf(stderr, "Missing option for bssid\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "suppress")) {
if (!pbssid || ETHER_ISNULLADDR(pbssid->macaddr.octet)) {
fprintf(stderr, "Wrong! Start with BSSID\n");
err = BCME_BADARG;
goto error;
}
if (*++argv) {
if (!stricmp(*argv, "found")) {
pbssid->flags |= WL_PFN_SUPPRESSFOUND_MASK;
} else if (!stricmp(*argv, "lost")) {
pbssid->flags |= WL_PFN_SUPPRESSLOST_MASK;
} else if (!stricmp(*argv, "neither")) {
pbssid->flags &=
~(WL_PFN_SUPPRESSFOUND_MASK | WL_PFN_SUPPRESSLOST_MASK);
} else {
fprintf(stderr, "Invalid suppress option %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
} else {
fprintf(stderr, "Missing option for suppress\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else if (!stricmp(*argv, "rssi")) {
if (*++argv) {
int rssi = atoi(*argv);
if (!pbssid || ETHER_ISNULLADDR(pbssid->macaddr.octet)) {
fprintf(stderr, "Wrong! Start with BSSID\n");
err = BCME_BADARG;
goto error;
}
if (rssi >= -128 && rssi <= 0) {
pbssid->flags |= (rssi << WL_PFN_RSSI_SHIFT)
& WL_PFN_RSSI_MASK;
} else {
fprintf(stderr, "Invalid rssi option %s\n", *argv);
err = BCME_BADARG;
goto error;
}
} else {
fprintf(stderr, "Missing option for rssi\n");
err = BCME_USAGE_ERROR;
goto error;
}
} else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
err = BCME_USAGE_ERROR;
goto error;
}
}
pbssid->flags = htod16(pbssid->flags);
bssidlistlen = cnt * sizeof(wl_pfn_bssid_t);
if ((err = wlu_iovar_set(wl, "pfn_add_bssid", bssidlist,
bssidlistlen))) {
fprintf(stderr, "pfn_add_bssid fail\n");
goto error;
}
free(bssidlist);
return 0;
error:
free(bssidlist);
return err;
}
static int
wl_pfn_cfg(void *wl, cmd_t *cmd, char **argv)
{
wl_pfn_cfg_t pfncfg_param;
int nchan = 0;
int err;
UNUSED_PARAMETER(cmd);
memset(&pfncfg_param, '\0', sizeof(wl_pfn_cfg_t));
/* Setup default values */
pfncfg_param.reporttype = WL_PFN_REPORT_ALLNET;
pfncfg_param.channel_num = 0;
while (*++argv) {
if (!stricmp(*argv, "report")) {
if (*++argv) {
if (!stricmp(*argv, "all")) {
pfncfg_param.reporttype = WL_PFN_REPORT_ALLNET;
} else if (!stricmp(*argv, "ssidonly")) {
pfncfg_param.reporttype = WL_PFN_REPORT_SSIDNET;
} else if (!stricmp(*argv, "bssidonly")) {
pfncfg_param.reporttype = WL_PFN_REPORT_BSSIDNET;
} else {
fprintf(stderr, "Invalid report option %s\n", *argv);
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "no value for report\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "channel")) {
if (*++argv) {
nchan = wl_parse_channel_list(*argv, pfncfg_param.channel_list,
WL_NUMCHANNELS);
if (nchan < 0) {
fprintf(stderr, "error parsing channel\n");
return BCME_BADARG;
}
} else {
fprintf(stderr, "Missing option for channel\n");
return BCME_USAGE_ERROR;
}
} else if (!stricmp(*argv, "prohibited")) {
if (*++argv) {
pfncfg_param.flags &= ~WL_PFN_CFG_FLAGS_PROHIBITED;
if (atoi(*argv))
pfncfg_param.flags |= WL_PFN_CFG_FLAGS_PROHIBITED;
} else {
fprintf(stderr, "Missing prohibited option value\n");
return BCME_USAGE_ERROR;
}
} else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
return BCME_USAGE_ERROR;
}
}
pfncfg_param.reporttype = htod32(pfncfg_param.reporttype);
pfncfg_param.channel_num = htod32(nchan);
pfncfg_param.flags = htod32(pfncfg_param.flags);
if ((err = wlu_iovar_set(wl, "pfn_cfg", &pfncfg_param,
sizeof(wl_pfn_cfg_t)))) {
fprintf(stderr, "pfn_cfg fail\n");
return err;
}
return 0;
}
static int
wl_pfn(void *wl, cmd_t *cmd, char **argv)
{
int err, val;
UNUSED_PARAMETER(cmd);
if (*++argv) {
val = atoi(*argv);
err = wlu_iovar_setint(wl, "pfn", (val ? 1 : 0));
} else {
err = wlu_iovar_getint(wl, "pfn", &val);
if (!err)
wl_printint(val);
}
return err;
}
#define WL_PFN_BESTNET_LEN 1024
static int
wl_pfnbest(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_pfn_scanresults_t *bestnet;
wl_pfn_net_info_t *netinfo;
uint32 i, j;
UNUSED_PARAMETER(cmd);
if (*++argv) {
fprintf(stderr, "Invalid parameter %s\n", *argv);
return BCME_USAGE_ERROR;
}
bestnet = (wl_pfn_scanresults_t *)malloc(WL_PFN_BESTNET_LEN);
if (bestnet == NULL) {
fprintf(stderr, "Failed to allocate buffer of %d bytes\n", WL_PFN_BESTNET_LEN);
return BCME_NOMEM;
}
bzero(bestnet, WL_PFN_BESTNET_LEN);
while (bestnet->status != PFN_COMPLETE) {
if ((err = wlu_iovar_get(wl, "pfnbest", (void *)bestnet, WL_PFN_BESTNET_LEN))) {
fprintf(stderr, "pfnbest fail\n");
free(bestnet);
return err;
}
if (bestnet->count >
(WL_PFN_BESTNET_LEN / sizeof(wl_pfn_net_info_t)))
{
fprintf(stderr, "invalid data\n");
free(bestnet);
return -1;
}
printf("ver %d, status %d, count %d\n",
bestnet->version, bestnet->status, bestnet->count);
netinfo = bestnet->netinfo;
for (i = 0; i < bestnet->count; i++) {
for (j = 0; j < netinfo->pfnsubnet.SSID_len; j++)
printf("%c", netinfo->pfnsubnet.SSID[j]);
printf("\n");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
netinfo->pfnsubnet.BSSID.octet[0],
netinfo->pfnsubnet.BSSID.octet[1],
netinfo->pfnsubnet.BSSID.octet[2],
netinfo->pfnsubnet.BSSID.octet[3],
netinfo->pfnsubnet.BSSID.octet[4],
netinfo->pfnsubnet.BSSID.octet[5]);
printf("channel: %d, RSSI: %d, timestamp: %d\n",
netinfo->pfnsubnet.channel, netinfo->RSSI, netinfo->timestamp);
netinfo++;
}
}
free(bestnet);
return 0;
}
static int
wl_pfnlbest(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_pfn_lscanresults_t *bestnet;
wl_pfn_lnet_info_t *netinfo;
uint32 i, j;
UNUSED_PARAMETER(cmd);
if (*++argv) {
fprintf(stderr, "Invalid parameter %s\n", *argv);
return -1;
}
bestnet = (wl_pfn_lscanresults_t *)malloc(WL_PFN_BESTNET_LEN);
if (bestnet == NULL) {
fprintf(stderr, "Failed to allocate buffer of %d bytes\n", WL_PFN_BESTNET_LEN);
return -1;
}
bzero(bestnet, WL_PFN_BESTNET_LEN);
while (bestnet->status == PFN_INCOMPLETE) {
if ((err = wlu_iovar_get(wl, "pfnlbest", (void *)bestnet, WL_PFN_BESTNET_LEN))) {
fprintf(stderr, "pfnbest fail\n");
return err;
}
printf("ver %d, status %d, count %d\n",
bestnet->version, bestnet->status, bestnet->count);
netinfo = bestnet->netinfo;
for (i = 0; i < bestnet->count; i++) {
for (j = 0; j < netinfo->pfnsubnet.SSID_len; j++)
printf("%c", netinfo->pfnsubnet.SSID[j]);
printf("\n");
printf("%02x:%02x:%02x:%02x:%02x:%02x\n",
netinfo->pfnsubnet.BSSID.octet[0],
netinfo->pfnsubnet.BSSID.octet[1],
netinfo->pfnsubnet.BSSID.octet[2],
netinfo->pfnsubnet.BSSID.octet[3],
netinfo->pfnsubnet.BSSID.octet[4],
netinfo->pfnsubnet.BSSID.octet[5]);
printf("channel: %d, flags: %d, RSSI: %d, timestamp: %d\n",
netinfo->pfnsubnet.channel, netinfo->flags,
netinfo->RSSI, netinfo->timestamp);
printf("RTT0: %d, RTT1: %d\n", netinfo->rtt0, netinfo->rtt1);
netinfo++;
}
}
free(bestnet);
return 0;
}
static int
wl_pfn_suspend(void *wl, cmd_t *cmd, char **argv)
{
int err, val;
UNUSED_PARAMETER(cmd);
if (*++argv) {
val = atoi(*argv);
err = wlu_iovar_setint(wl, "pfn_suspend", (val ? 1 : 0));
} else {
err = wlu_iovar_getint(wl, "pfn_suspend", &val);
if (!err)
wl_printint(val);
}
return err;
}
static int
wl_pfn_mem(void *wl, cmd_t *cmd, char **argv)
{
int err, val;
UNUSED_PARAMETER(cmd);
if (*++argv && !stricmp(*argv, "bestn")) {
if (*++argv)
val = atoi(*argv);
else {
fprintf(stderr, "Missing bestn value\n");
return -1;
}
} else {
fprintf(stderr, "Missing bestn option\n");
return -1;
}
err = wlu_iovar_setint(wl, "pfnmem", val);
if (err) {
fprintf(stderr, "pfnmem set wrong!\n");
return err;
}
err = wlu_iovar_getint(wl, "pfnmem", &val);
if (!err)
wl_printint(val);
else
fprintf(stderr, "pfnmem get wrong!\n");
return err;
}
static void
wl_pfn_printnet(wl_pfn_scanresults_t *ptr, int event_type)
{
wl_pfn_net_info_t *netinfo = ptr->netinfo;
uint32 i, j;
if (WLC_E_PFN_NET_FOUND == event_type) {
printf("WLC_E_PFN_NET_FOUND:\n");
} else if (WLC_E_PFN_NET_LOST == event_type) {
printf("WLC_E_PFN_NET_LOST:\n");
} else if (WLC_E_PFN_BSSID_NET_FOUND == event_type) {
printf("WLC_E_PFN_BSSID_NET_FOUND:\n");
} else if (WLC_E_PFN_BSSID_NET_LOST == event_type) {
printf("WLC_E_PFN_BSSID_NET_LOST:\n");
} else {
return;
}
printf("ver %d, status %d, count %d\n",
ptr->version, ptr->status, ptr->count);
for (i = 0; i < ptr->count; i++) {
printf("%d. ", i + 1);
for (j = 0; j < netinfo->pfnsubnet.SSID_len; j++)
printf("%c", netinfo->pfnsubnet.SSID[j]);
printf("\n");
printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
netinfo->pfnsubnet.BSSID.octet[0],
netinfo->pfnsubnet.BSSID.octet[1],
netinfo->pfnsubnet.BSSID.octet[2],
netinfo->pfnsubnet.BSSID.octet[3],
netinfo->pfnsubnet.BSSID.octet[4],
netinfo->pfnsubnet.BSSID.octet[5]);
printf("channel %d, RSSI %d, timestamp %d\n",
netinfo->pfnsubnet.channel, netinfo->RSSI, netinfo->timestamp);
netinfo++;
}
}
static int
wl_pfn_event_check(void *wl, cmd_t *cmd, char **argv)
{
int fd, err;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
bcm_event_t * event;
char data[512];
int event_type;
struct ether_addr *addr;
char eabuf[ETHER_ADDR_STR_LEN];
wl_pfn_scanresults_t *ptr;
wl_pfn_net_info_t *info;
uint32 i, j;
uint32 foundcnt, lostcnt;
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(cmd);
/* Override default ifname explicitly or implicitly */
if (*++argv) {
if (strlen(*argv) >= IFNAMSIZ) {
printf("Interface name %s too long\n", *argv);
return -1;
}
strncpy(ifnames, *argv, IFNAMSIZ);
} else if (wl) {
strncpy(ifnames, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifnames, IFNAMSIZ);
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
return -1;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
return -1;
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot get index %d\n", err);
return -1;
}
while (1) {
recv(fd, data, sizeof(data), 0);
event = (bcm_event_t *)data;
addr = (struct ether_addr *)&(event->event.addr);
event_type = ntoh32(event->event.event_type);
if (addr != NULL) {
sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x",
(uchar)addr->octet[0]&0xff,
(uchar)addr->octet[1]&0xff,
(uchar)addr->octet[2]&0xff,
(uchar)addr->octet[3]&0xff,
(uchar)addr->octet[4]&0xff,
(uchar)addr->octet[5]&0xff);
}
if (ntoh32(event->event.datalen)) {
if (WLC_E_PFN_SCAN_COMPLETE == event_type) {
ptr = (wl_pfn_scanresults_t *)(data + sizeof(bcm_event_t));
info = ptr->netinfo;
foundcnt = ptr->count & 0xffff;
lostcnt = ptr->count >> 16;
printf("ver %d, status %d, found %d, lost %d\n",
ptr->version, ptr->status, foundcnt, lostcnt);
if (foundcnt)
printf("Network found:\n");
for (i = 0; i < foundcnt; i++) {
printf("%d. ", i + 1);
for (j = 0; j < info->pfnsubnet.SSID_len; j++)
printf("%c", info->pfnsubnet.SSID[j]);
printf("\n");
printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
info->pfnsubnet.BSSID.octet[0],
info->pfnsubnet.BSSID.octet[1],
info->pfnsubnet.BSSID.octet[2],
info->pfnsubnet.BSSID.octet[3],
info->pfnsubnet.BSSID.octet[4],
info->pfnsubnet.BSSID.octet[5]);
printf("channel %d, RSSI %d, timestamp %d\n",
info->pfnsubnet.channel, info->RSSI, info->timestamp);
info++;
}
if (lostcnt)
printf("Network lost:\n");
for (i = 0; i < lostcnt; i++) {
printf("%d. ", i + 1);
for (j = 0; j < info->pfnsubnet.SSID_len; j++)
printf("%c", info->pfnsubnet.SSID[j]);
printf("\n");
printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n",
info->pfnsubnet.BSSID.octet[0],
info->pfnsubnet.BSSID.octet[1],
info->pfnsubnet.BSSID.octet[2],
info->pfnsubnet.BSSID.octet[3],
info->pfnsubnet.BSSID.octet[4],
info->pfnsubnet.BSSID.octet[5]);
printf("channel %d, RSSI %d, timestamp %d\n",
info->pfnsubnet.channel, info->RSSI, info->timestamp);
info++;
}
} else if ((WLC_E_PFN_NET_FOUND == event_type) ||
(WLC_E_PFN_NET_LOST == event_type) ||
(WLC_E_PFN_BSSID_NET_FOUND == event_type) ||
(WLC_E_PFN_BSSID_NET_LOST == event_type)) {
wl_pfn_printnet(
(wl_pfn_scanresults_t *)(data + sizeof(bcm_event_t)),
event_type);
}
if (WLC_E_LINK == event_type || WLC_E_NDIS_LINK == event_type) {
if (ntoh16(event->event.flags) & WLC_EVENT_MSG_LINK)
printf("MACEVENT Link up :%s\n", eabuf);
else
printf("MACEVENT Link down :%s\n", eabuf);
}
} else {
if (WLC_E_PFN_SCAN_NONE == event_type) {
printf("Got WLC_E_PFN_SCAN_NONE\n");
}
if (WLC_E_PFN_SCAN_ALLGONE == event_type) {
printf("Got WLC_E_PFN_SCAN_ALLGONE\n");
}
if (WLC_E_PFN_BEST_BATCHING == event_type) {
printf("Got WLC_E_PFN_BEST_BATCHING\n");
}
}
}
return (0);
}
#define ESCAN_EVENTS_BUFFER_SIZE 2048
static int
wl_escan_event_check(void *wl, cmd_t *cmd, char **argv)
{
int fd, err, i, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
uint8 print_flag = 4;
bcm_event_t * event;
uint32 status;
char* data;
int event_type;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
wl_escan_result_t* escan_data;
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(cmd);
if (*++argv) {
if (strlen(*argv) >= IFNAMSIZ) {
printf("Interface name %s too long\n", *argv);
return -1;
}
strncpy(ifnames, *argv, (IFNAMSIZ - 1));
if (*++argv)
print_flag = atoi(*argv);
} else if (wl) {
strncpy(ifnames, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN);
event_inds_mask[WLC_E_ESCAN_RESULT / 8] |= 1 << (WLC_E_ESCAN_RESULT % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
return (err);
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
return -1;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
return -1;
}
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot bind %d\n", err);
return -1;
}
data = (char*)malloc(ESCAN_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
ESCAN_EVENTS_BUFFER_SIZE);
return BCME_NOMEM;
}
while (1) {
octets = recv(fd, data, ESCAN_EVENTS_BUFFER_SIZE, 0);
event = (bcm_event_t *)data;
event_type = ntoh32(event->event.event_type);
if ((event_type == WLC_E_ESCAN_RESULT) && (octets > 0)) {
escan_data = (wl_escan_result_t*)&data[sizeof(bcm_event_t)];
status = ntoh32(event->event.status);
if (print_flag & 1)
printf("WLC_E_ESCAN_RESULT, (sync_id,status) = (%d,%d)\n",
escan_data->sync_id, status);
if (print_flag & 2)
for (i = 0; i < escan_data->bss_count; i++)
dump_bss_info(&escan_data->bss_info[i]);
if (print_flag & 4) {
if (status == WLC_E_STATUS_PARTIAL) {
printf("sync_id: %d, WLC_E_STATUS_PARTIAL\n",
escan_data->sync_id);
for (i = 0; i < escan_data->bss_count; i++)
dump_bss_info(&escan_data->bss_info[i]);
}
if (status == WLC_E_STATUS_SUCCESS)
printf("sync_id: %d, WLC_E_STATUS_SUCCESS => SCAN_DONE\n",
escan_data->sync_id);
if ((status != WLC_E_STATUS_SUCCESS) &&
(status != WLC_E_STATUS_PARTIAL))
printf("sync_id: %d, status:%d, misc. error/abort\n",
escan_data->sync_id, status);
}
if (print_flag & 8) {
int remainder = escan_data->bss_info[0].ie_length;
int processed = sizeof(wl_escan_result_t);
uint8* iebuf = &((uint8*)escan_data)[sizeof(wl_escan_result_t)];
if (status != WLC_E_STATUS_PARTIAL)
continue;
printf("MOREINFO: (sync_id,buflen,ielen) = (%d,%d,%d)\n",
escan_data->sync_id,
escan_data->buflen,
escan_data->bss_info[0].ie_length);
/* do a tlv sanity check */
while (remainder > 0) {
processed += 1 + 1 + iebuf[1];
remainder -= 1 + 1 + iebuf[1];
iebuf += 1 + 1 + iebuf[1];
}
if (processed >= ESCAN_EVENTS_BUFFER_SIZE)
break;
if (remainder != 0) {
printf("ERROR: IE tlv sanity check failed for "
"(ssid,sync_id,buflen,ielen,remainder) = "
"(%s,%d,%d,%d,%d)\n",
escan_data->bss_info[0].SSID,
escan_data->sync_id, escan_data->buflen,
escan_data->bss_info[0].ie_length,
remainder);
iebuf = &((uint8*)escan_data)[sizeof(wl_escan_result_t)];
if ((escan_data->buflen - sizeof(wl_escan_result_t)) > 0) {
for (i = 0;
i < (int)(escan_data->buflen -
sizeof(wl_escan_result_t));
i++) {
printf("%02x ", iebuf[i]);
}
printf("\n");
}
}
}
}
}
/* if we ever reach here */
free(data);
return (0);
}
struct escan_bss {
struct escan_bss *next;
wl_bss_info_t bss[1];
};
#define ESCAN_BSS_FIXED_SIZE 4
static int
wl_escanresults(void *wl, cmd_t *cmd, char **argv)
{
int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params)) +
(WL_NUMCHANNELS * sizeof(uint16));
wl_escan_params_t *params;
int fd, err, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
bcm_event_t *event;
uint32 status;
char *data;
int event_type;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
wl_escan_result_t *escan_data;
struct escan_bss *escan_bss_head = NULL;
struct escan_bss *escan_bss_tail = NULL;
struct escan_bss *result;
params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_escan_params_t*)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
err = wl_scan_prep(wl, cmd, argv, &params->params, &params_size);
if (err)
goto exit2;
memset(&ifr, 0, sizeof(ifr));
if (wl)
strncpy(ifr.ifr_name, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
else
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN);
/* Read the event mask from driver and unmask the event WLC_E_ESCAN_RESULT */
if ((err = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
goto exit2;
event_inds_mask[WLC_E_ESCAN_RESULT / 8] |= 1 << (WLC_E_ESCAN_RESULT % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
goto exit2;
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
err = -1;
goto exit2;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
goto exit2;
}
/* bind the socket first before starting escan so we won't miss any event */
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot bind %d\n", err);
goto exit2;
}
params->version = htod32(ESCAN_REQ_VERSION);
params->action = htod16(WL_SCAN_ACTION_START);
srand((unsigned)time(NULL));
params->sync_id = htod16(rand() & 0xffff);
params_size += OFFSETOF(wl_escan_params_t, params);
err = wlu_iovar_setbuf(wl, "escan", params, params_size, buf, WLC_IOCTL_MAXLEN);
data = (char*)malloc(ESCAN_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
ESCAN_EVENTS_BUFFER_SIZE);
err = BCME_NOMEM;
goto exit2;
}
/* receive scan result */
while (1) {
octets = recv(fd, data, ESCAN_EVENTS_BUFFER_SIZE, 0);
event = (bcm_event_t *)data;
event_type = ntoh32(event->event.event_type);
if ((event_type == WLC_E_ESCAN_RESULT) && (octets > 0)) {
escan_data = (wl_escan_result_t*)&data[sizeof(bcm_event_t)];
status = ntoh32(event->event.status);
if (status == WLC_E_STATUS_PARTIAL) {
wl_bss_info_t *bi = &escan_data->bss_info[0];
wl_bss_info_t *bss;
/* check if we've received info of same BSSID */
for (result = escan_bss_head; result; result = result->next) {
bss = result->bss;
if (!wlu_bcmp(bi->BSSID.octet, bss->BSSID.octet,
ETHER_ADDR_LEN) &&
CHSPEC_BAND(bi->chanspec) ==
CHSPEC_BAND(bss->chanspec) &&
bi->SSID_len == bss->SSID_len &&
!wlu_bcmp(bi->SSID, bss->SSID, bi->SSID_len))
break;
}
if (!result) {
/* New BSS. Allocate memory and save it */
struct escan_bss *ebss = malloc(ESCAN_BSS_FIXED_SIZE
+ bi->length);
if (!ebss) {
perror("can't allocate memory for bss");
goto exit1;
}
ebss->next = NULL;
memcpy(&ebss->bss, bi, bi->length);
if (escan_bss_tail) {
escan_bss_tail->next = ebss;
}
else {
escan_bss_head = ebss;
}
escan_bss_tail = ebss;
}
else if (bi->RSSI != WLC_RSSI_INVALID) {
/* We've got this BSS. Update rssi if necessary */
if (((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) ==
(bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) &&
((bss->RSSI == WLC_RSSI_INVALID) ||
(bss->RSSI < bi->RSSI))) {
/* preserve max RSSI if the measurements are
* both on-channel or both off-channel
*/
bss->RSSI = bi->RSSI;
bss->SNR = bi->SNR;
bss->phy_noise = bi->phy_noise;
} else if ((bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
(bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) {
/* preserve the on-channel rssi measurement
* if the new measurement is off channel
*/
bss->RSSI = bi->RSSI;
bss->SNR = bi->SNR;
bss->phy_noise = bi->phy_noise;
bss->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL;
}
}
}
else if (status == WLC_E_STATUS_SUCCESS) {
/* Escan finished. Let's go dump the results. */
break;
}
else {
printf("sync_id: %d, status:%d, misc. error/abort\n",
escan_data->sync_id, status);
goto exit1;
}
}
}
/* print scan results */
for (result = escan_bss_head; result; result = result->next) {
dump_bss_info(result->bss);
}
exit1:
/* free scan results */
result = escan_bss_head;
while (result) {
struct escan_bss *tmp = result->next;
free(result);
result = tmp;
}
free(data);
close(fd);
exit2:
free(params);
/* Read the event mask from driver and mask the event WLC_E_ESCAN_RESULT */
if (!(err = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) {
event_inds_mask[WLC_E_ESCAN_RESULT / 8] &= (~(1 << (WLC_E_ESCAN_RESULT % 8)));
err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN);
}
return err;
}
static int
wl_event_filter(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN);
/* Register for following event for pfn */
event_inds_mask[WLC_E_LINK / 8] |= 1 << (WLC_E_LINK % 8);
event_inds_mask[WLC_E_PFN_NET_FOUND / 8] |= 1 << (WLC_E_PFN_NET_FOUND % 8);
event_inds_mask[WLC_E_PFN_NET_LOST / 8] |= 1 << (WLC_E_PFN_NET_LOST % 8);
event_inds_mask[WLC_E_PFN_SCAN_NONE/ 8] |= 1 << (WLC_E_PFN_SCAN_NONE % 8);
event_inds_mask[WLC_E_PFN_SCAN_ALLGONE/ 8] |= 1 << (WLC_E_PFN_SCAN_ALLGONE % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
return (err);
return (0);
}
#endif /* WLPFN */
#ifdef WLP2PO
#define UPNP_QUERY_VERSION "0x10"
#define BCM_SVC_RPOTYPE_ALL 0
#define BCM_SVC_RPOTYPE_BONJOUR 1
#define BCM_SVC_RPOTYPE_UPNP 2
#define BCM_SVC_RPOTYPE_WSD 3
#define BCM_SVC_RPOTYPE_VENDOR 255
static int
wl_p2po_listen(void *wl, cmd_t *cmd, char **argv)
{
int err, params_size;
wl_p2po_listen_t params;
UNUSED_PARAMETER(cmd);
if (!argv[1] || !argv[2]) {
params.period = 0;
params.interval = 0;
}
else {
params.period = atoi(argv[1]);
params.interval = atoi(argv[2]);
}
params_size = sizeof(wl_p2po_listen_t);
err = wlu_iovar_set(wl, "p2po_listen", &params, params_size);
return err;
}
static int
hexstr2hex(char *str)
{
int i, len;
char hexstr[3];
char *src;
src = str;
len = strlen(str)/2;
for (i = 0; i < len; i++) {
hexstr[0] = src[0];
hexstr[1] = src[1];
hexstr[2] = '\0';
str[i] = strtoul(hexstr, NULL, 16);
src += 2;
}
return i;
}
static int
wl_p2po_addsvc(void *wl, cmd_t *cmd, char **argv)
{
int err;
int protocol, query_len, response_len, params_size;
char *query, *response;
wl_p2po_qr_t *params;
UNUSED_PARAMETER(cmd);
if (!argv[1] || !argv[2] || !argv[3]) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
protocol = atoi(argv[1]);
if (protocol == BCM_SVC_RPOTYPE_BONJOUR) {
query = argv[2];
response = argv[3];
query_len = hexstr2hex(query);
response_len = hexstr2hex(response);
} else if (protocol == BCM_SVC_RPOTYPE_UPNP) {
if (memcmp(argv[2], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0 ||
memcmp(argv[3], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) {
fprintf(stderr, "UPnP query/response string must start with 0x10");
return BCME_USAGE_ERROR;
}
query = argv[2] + strlen(UPNP_QUERY_VERSION) - 1;
response = argv[3] + strlen(UPNP_QUERY_VERSION) - 1;
query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16);
response[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16);
query_len = strlen(query);
response_len = strlen(response);
}
else {
fprintf(stderr, "<protocol> should be <1|2>\n");
return BCME_USAGE_ERROR;
}
params_size = sizeof(wl_p2po_qr_t) + query_len + response_len - 1;
params = (wl_p2po_qr_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
params->protocol = protocol;
params->query_len = query_len;
params->response_len = response_len;
memcpy(params->qrbuf, query, query_len);
memcpy(params->qrbuf+query_len, response, response_len);
err = wlu_iovar_setbuf(wl, "p2po_addsvc", params, params_size, buf, WLC_IOCTL_MAXLEN);
free(params);
return err;
}
static int
wl_p2po_delsvc(void *wl, cmd_t *cmd, char **argv)
{
int err;
int protocol, query_len, params_size;
char *query;
wl_p2po_qr_t *params;
UNUSED_PARAMETER(cmd);
if (!argv[1] || (*argv[1] != '0' && !argv[2])) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
if (*argv[1] == '0') {
protocol = 0;
query_len = 0;
}
else {
protocol = atoi(argv[1]);
if (protocol == BCM_SVC_RPOTYPE_BONJOUR) {
query = argv[2];
query_len = hexstr2hex(query);
} else if (protocol == BCM_SVC_RPOTYPE_UPNP) {
if (memcmp(argv[2], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) {
fprintf(stderr, "UPnP query string must start with 0x10");
return BCME_USAGE_ERROR;
}
query = argv[2] + strlen(UPNP_QUERY_VERSION) - 1;
query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16);
query_len = strlen(query);
}
else {
fprintf(stderr, "<protocol> should be <1|2>\n");
return BCME_USAGE_ERROR;
}
}
params_size = sizeof(wl_p2po_qr_t) + (query_len ? query_len - 1 : 0);
params = (wl_p2po_qr_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
params->protocol = protocol;
params->query_len = query_len;
if (query_len)
memcpy(params->qrbuf, query, query_len);
err = wlu_iovar_set(wl, "p2po_delsvc", params, params_size);
free(params);
return err;
}
static int
wl_p2po_sd_reqresp(void *wl, cmd_t *cmd, char **argv)
{
int err;
int transaction_id, protocol, query_len, params_size;
char *query;
wl_p2po_qr_t *params;
UNUSED_PARAMETER(cmd);
if (!argv[1] || !argv[2]) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
transaction_id = atoi(argv[1]);
if (transaction_id < 0 || transaction_id > 255) {
fprintf(stderr, "<transaction id> should be between 0 and 255\n");
return BCME_USAGE_ERROR;
}
protocol = atoi(argv[2]);
if (!argv[3]) {
/* find everything for protocol */
query_len = 0;
} else if (protocol == BCM_SVC_RPOTYPE_BONJOUR) {
query = argv[3];
query_len = hexstr2hex(query);
} else if (protocol == BCM_SVC_RPOTYPE_UPNP) {
if (memcmp(argv[3], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) {
fprintf(stderr, "UPnP query string must start with 0x10");
return BCME_USAGE_ERROR;
}
query = argv[3] + strlen(UPNP_QUERY_VERSION) - 1;
query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16);
query_len = strlen(query);
} else {
fprintf(stderr, "<protocol> should be <0|1|2>\n");
return BCME_USAGE_ERROR;
}
params_size = sizeof(wl_p2po_qr_t) + (query_len ? query_len - 1 : 0);
params = (wl_p2po_qr_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
params->transaction_id = transaction_id;
params->protocol = protocol;
params->query_len = query_len;
if (query_len)
memcpy(params->qrbuf, query, query_len);
err = wlu_iovar_set(wl, "p2po_sd_req_resp", params, params_size);
free(params);
return err;
}
static int
wl_p2po_listen_channel(void *wl, cmd_t *cmd, char **argv)
{
uint32 val;
UNUSED_PARAMETER(cmd);
if (*++argv) {
val = atoi(*argv);
return wlu_iovar_setint(wl, "p2po_listen_channel", val);
}
fprintf(stderr, "No channel is specified\n");
return BCME_USAGE_ERROR;
}
static int
wl_p2po_stop(void *wl, cmd_t *cmd, char **argv)
{
uint32 val;
UNUSED_PARAMETER(cmd);
if (*++argv) {
val = atoi(*argv);
return wlu_iovar_setint(wl, "p2po_stop", val);
}
fprintf(stderr, "Please specify - 0:stop, 1:pause\n");
return BCME_USAGE_ERROR;
}
#define P2PO_EVENTS_BUFFER_SIZE 2048
static int
wl_p2po_results(void *wl, cmd_t *cmd, char **argv)
{
int fd, err, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
char *data;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(&ifr, 0, sizeof(ifr));
if (wl)
strncpy(ifr.ifr_name, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
else
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN);
event_inds_mask[WLC_E_SERVICE_FOUND / 8] |= 1 << (WLC_E_SERVICE_FOUND % 8);
event_inds_mask[WLC_E_GAS_FRAGMENT_RX / 8] |= 1 << (WLC_E_GAS_FRAGMENT_RX % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
goto exit2;
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
err = -1;
goto exit2;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
goto exit1;
}
/* bind the socket first before starting so we won't miss any event */
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot bind %d\n", err);
goto exit1;
}
data = (char*)malloc(P2PO_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
P2PO_EVENTS_BUFFER_SIZE);
err = BCME_NOMEM;
goto exit1;
}
/* receive result */
while (1) {
bcm_event_t *bcm_event;
int event_type;
octets = recv(fd, data, P2PO_EVENTS_BUFFER_SIZE, 0);
bcm_event = (bcm_event_t *)data;
event_type = ntoh32(bcm_event->event.event_type);
if (octets >= (int)sizeof(bcm_event_t)) {
if (event_type == WLC_E_SERVICE_FOUND) {
uint32 status = ntoh32(bcm_event->event.status);
wl_event_sd_t *sd_data =
(wl_event_sd_t *)&data[sizeof(bcm_event_t)];
sd_data->channel = dtoh16(sd_data->channel);
printf("WLC_E_SERVICE_FOUND: %s\n",
status == WLC_E_STATUS_PARTIAL ? "WLC_E_STATUS_PARTIAL" :
status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" :
status == WLC_E_STATUS_FAIL ? "WLC_E_STATUS_FAIL" :
"unknown");
printf(" channel = %d\n", sd_data->channel);
printf(" peer = %s\n",
wl_ether_etoa(&bcm_event->event.addr));
if (status == WLC_E_STATUS_SUCCESS) {
wl_sd_tlv_t *tlv;
int i;
tlv = sd_data->tlv;
for (i = 0; i < sd_data->count; i++) {
printf(" [TLV %d]\n", i);
tlv->length = dtoh16(tlv->length);
printf(" protocol type = %s\n",
tlv->protocol == 0 ? "ALL" :
tlv->protocol == BCM_SVC_RPOTYPE_BONJOUR ?
"BONJOUR" :
tlv->protocol == BCM_SVC_RPOTYPE_UPNP ?
"UPnP" :
tlv->protocol == BCM_SVC_RPOTYPE_WSD ?
"WS-Discovery" :
tlv->protocol == BCM_SVC_RPOTYPE_VENDOR ?
"Vendor specific" : "Unknown");
printf(" transaction id = %u\n",
tlv->transaction_id);
printf(" status code = %u\n",
tlv->status_code);
printf(" length = %u\n", tlv->length);
wl_hexdump(tlv->data, tlv->length);
tlv = (wl_sd_tlv_t *)((void *)tlv + tlv->length +
OFFSETOF(wl_sd_tlv_t, data));
}
}
else {
/* fragments */
}
}
}
}
free(data);
exit1:
close(fd);
exit2:
return err;
}
#endif /* WLP2PO */
#ifdef WLANQPO
#define ANQPO_EVENTS_BUFFER_SIZE 2048
static int
wl_anqpo_set(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_USAGE_ERROR, malloc_size;
char *buffer;
wl_anqpo_set_t *set;
int length;
UNUSED_PARAMETER(cmd);
malloc_size = OFFSETOF(wl_anqpo_set_t, query_data) +
(ANQPO_MAX_QUERY_SIZE * sizeof(uint8));
buffer = malloc(malloc_size);
if (buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
memset(buffer, 0, malloc_size);
set = (wl_anqpo_set_t *)buffer;
/* parameters not configured by default */
set->max_retransmit = -1;
set->response_timeout = -1;
set->max_comeback_delay = -1;
set->max_retries = -1;
while (*++argv) {
if (!stricmp(*argv, "max_retransmit")) {
if (*++argv)
set->max_retransmit = atoi(*argv);
else {
fprintf(stderr, "Missing max retransmit\n");
err = BCME_USAGE_ERROR;
goto done;
}
}
else if (!stricmp(*argv, "response_timeout")) {
if (*++argv)
set->response_timeout = atoi(*argv);
else {
fprintf(stderr, "Missing response timeout\n");
err = BCME_USAGE_ERROR;
goto done;
}
}
else if (!stricmp(*argv, "max_comeback_delay")) {
if (*++argv)
set->max_comeback_delay = atoi(*argv);
else {
fprintf(stderr, "Missing max comeback delay\n");
err = BCME_USAGE_ERROR;
goto done;
}
}
else if (!stricmp(*argv, "max_retries")) {
if (*++argv)
set->max_retries = atoi(*argv);
else {
fprintf(stderr, "Missing retries\n");
err = BCME_USAGE_ERROR;
goto done;
}
}
else if (!stricmp(*argv, "query")) {
if (*++argv) {
if ((set->query_len = hexstr2hex(*argv)) == 0) {
fprintf(stderr, "Invalid ANQP query\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (set->query_len > ANQPO_MAX_QUERY_SIZE) {
fprintf(stderr, "ANQP query size %d exceeds %d\n",
set->query_len, ANQPO_MAX_QUERY_SIZE);
err = BCME_USAGE_ERROR;
goto done;
}
memcpy(set->query_data, *argv, set->query_len);
}
else {
fprintf(stderr, "Missing ANQP query\n");
err = BCME_USAGE_ERROR;
goto done;
}
}
else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
err = BCME_USAGE_ERROR;
goto done;
}
}
length = OFFSETOF(wl_anqpo_set_t, query_data) + set->query_len;
set->max_retransmit = htod16(set->max_retransmit);
set->response_timeout = htod16(set->response_timeout);
set->max_comeback_delay = htod16(set->max_comeback_delay);
set->max_retries = htod16(set->max_retries);
set->query_len = htod16(set->query_len);
err = wlu_iovar_set(wl, cmd->name, set, length);
done:
free(buffer);
return err;
}
static int
wl_anqpo_stop_query(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
return wlu_iovar_set(wl, cmd->name, 0, 0);
}
static int
wl_anqpo_start_query(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_USAGE_ERROR, malloc_size;
char *buffer;
wl_anqpo_peer_list_t *list;
wl_anqpo_peer_t *peer;
int c;
int length;
UNUSED_PARAMETER(cmd);
malloc_size = OFFSETOF(wl_anqpo_peer_list_t, peer) +
(ANQPO_MAX_PEER_LIST * sizeof(wl_anqpo_peer_t));
buffer = malloc(malloc_size);
if (buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
memset(buffer, 0, malloc_size);
list = (wl_anqpo_peer_list_t *)buffer;
peer = &list->peer[0];
c = 1;
while (argv[c] && argv[c + 1]) {
if ((peer->channel = htod16(atoi(argv[c]))) == 0) {
fprintf(stderr, "Invalid channel\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (!wl_ether_atoe(argv[c + 1], &peer->addr)) {
fprintf(stderr, "Invalid address\n");
err = BCME_USAGE_ERROR;
goto done;
}
list->count++;
c += 2;
peer++;
}
length = OFFSETOF(wl_anqpo_peer_list_t, peer) +
(list->count * sizeof(wl_anqpo_peer_t));
list->count = htod16(list->count);
err = wlu_iovar_set(wl, cmd->name, list, length);
done:
free(buffer);
return err;
}
static int
wl_anqpo_ignore_ssid_list(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_USAGE_ERROR, malloc_size;
char *buffer;
wl_anqpo_ignore_ssid_list_t *list;
int length;
UNUSED_PARAMETER(cmd);
malloc_size = OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid) +
(ANQPO_MAX_IGNORE_SSID * (sizeof(wl_anqpo_ignore_ssid_list_t) -
OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid)));
buffer = malloc(malloc_size);
if (buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
memset(buffer, 0, malloc_size);
list = (wl_anqpo_ignore_ssid_list_t *)buffer;
/* get */
if (!argv[1]) {
int i;
if ((err = wlu_iovar_get(wl, cmd->name, list, malloc_size)) < 0)
goto done;
list->count = dtoh16(list->count);
for (i = 0; i < list->count; i++) {
char ssidbuf[SSID_FMT_BUF_LEN];
wl_format_ssid(ssidbuf, list->ssid[i].SSID, list->ssid[i].SSID_len);
printf("%s\n", ssidbuf);
}
goto done;
}
/* set */
argv++;
if (!stricmp(*argv, "clear")) {
list->is_clear = TRUE;
}
else if (!stricmp(*argv, "set")) {
list->is_clear = TRUE;
while (*++argv) {
int len = strlen(*argv);
if (list->count > ANQPO_MAX_IGNORE_SSID) {
fprintf(stderr, "Too many BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (len > 32) {
fprintf(stderr, "SSID too long\n");
err = BCME_USAGE_ERROR;
goto done;
}
list->ssid[list->count].SSID_len = len;
memcpy(list->ssid[list->count].SSID, *argv, len);
list->count++;
}
}
else if (!stricmp(*argv, "append")) {
while (*++argv) {
int len = strlen(*argv);
if (list->count > ANQPO_MAX_IGNORE_SSID) {
fprintf(stderr, "Too many BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (len > 32) {
fprintf(stderr, "SSID too long\n");
err = BCME_USAGE_ERROR;
goto done;
}
list->ssid[list->count].SSID_len = len;
memcpy(list->ssid[list->count].SSID, *argv, len);
list->count++;
}
}
else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
err = BCME_USAGE_ERROR;
goto done;
}
length = OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid) +
(list->count * (sizeof(wl_anqpo_ignore_ssid_list_t) -
OFFSETOF(wl_anqpo_ignore_ssid_list_t, ssid)));
list->count = htod16(list->count);
err = wlu_iovar_set(wl, cmd->name, list, length);
done:
free(buffer);
return err;
}
static int
wl_anqpo_ignore_bssid_list(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_USAGE_ERROR, malloc_size;
char *buffer;
wl_anqpo_ignore_bssid_list_t *list;
int length;
UNUSED_PARAMETER(cmd);
malloc_size = OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid) +
(ANQPO_MAX_IGNORE_BSSID * (sizeof(wl_anqpo_ignore_bssid_list_t) -
OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid)));
buffer = malloc(malloc_size);
if (buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
return BCME_NOMEM;
}
memset(buffer, 0, malloc_size);
list = (wl_anqpo_ignore_bssid_list_t *)buffer;
/* get */
if (!argv[1]) {
int i;
if ((err = wlu_iovar_get(wl, cmd->name, list, malloc_size)) < 0)
goto done;
list->count = dtoh16(list->count);
for (i = 0; i < list->count; i++) {
printf("%s\n", wl_ether_etoa(&list->bssid[i]));
}
goto done;
}
/* set */
argv++;
if (!stricmp(*argv, "clear")) {
list->is_clear = TRUE;
}
else if (!stricmp(*argv, "set")) {
list->is_clear = TRUE;
while (*++argv) {
if (list->count > ANQPO_MAX_IGNORE_BSSID) {
fprintf(stderr, "Too many BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (!wl_ether_atoe(*argv, &list->bssid[list->count])) {
fprintf(stderr, "Invalid BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
list->count++;
}
}
else if (!stricmp(*argv, "append")) {
while (*++argv) {
if (list->count > ANQPO_MAX_IGNORE_BSSID) {
fprintf(stderr, "Too many BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
if (!wl_ether_atoe(*argv, &list->bssid[list->count])) {
fprintf(stderr, "Invalid BSSID\n");
err = BCME_USAGE_ERROR;
goto done;
}
list->count++;
}
}
else {
fprintf(stderr, "Invalid parameter %s\n", *argv);
err = BCME_USAGE_ERROR;
goto done;
}
length = OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid) +
(list->count * (sizeof(wl_anqpo_ignore_bssid_list_t) -
OFFSETOF(wl_anqpo_ignore_bssid_list_t, bssid)));
list->count = htod16(list->count);
err = wlu_iovar_set(wl, cmd->name, list, length);
done:
free(buffer);
return err;
}
static int
wl_anqpo_results(void *wl, cmd_t *cmd, char **argv)
{
int fd, err, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
char *data;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
memset(&ifr, 0, sizeof(ifr));
if (wl)
strncpy(ifr.ifr_name, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
else
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN);
event_inds_mask[WLC_E_GAS_FRAGMENT_RX / 8] |= 1 << (WLC_E_GAS_FRAGMENT_RX % 8);
event_inds_mask[WLC_E_GAS_COMPLETE / 8] |= 1 << (WLC_E_GAS_COMPLETE % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
goto exit2;
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
err = -1;
goto exit2;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
goto exit1;
}
/* bind the socket first before starting so we won't miss any event */
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot bind %d\n", err);
goto exit1;
}
data = (char*)malloc(ANQPO_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
ANQPO_EVENTS_BUFFER_SIZE);
err = BCME_NOMEM;
goto exit1;
}
/* receive result */
while (1) {
bcm_event_t *bcm_event;
int event_type;
octets = recv(fd, data, ANQPO_EVENTS_BUFFER_SIZE, 0);
bcm_event = (bcm_event_t *)data;
event_type = ntoh32(bcm_event->event.event_type);
if (octets >= (int)sizeof(bcm_event_t)) {
uint32 status = ntoh32(bcm_event->event.status);
if (event_type == WLC_E_GAS_FRAGMENT_RX) {
wl_event_gas_t *gas_data =
(wl_event_gas_t *)&data[sizeof(bcm_event_t)];
gas_data->channel = dtoh16(gas_data->channel);
gas_data->status_code = dtoh16(gas_data->status_code);
gas_data->data_len = dtoh16(gas_data->data_len);
printf("WLC_E_GAS_FRAGMENT_RX: %s\n",
status == WLC_E_STATUS_PARTIAL ? "WLC_E_STATUS_PARTIAL" :
status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" :
status == WLC_E_STATUS_FAIL ? "WLC_E_STATUS_FAIL" :
"unknown");
printf(" channel = %d\n", gas_data->channel);
printf(" peer = %s\n",
wl_ether_etoa(&bcm_event->event.addr));
printf(" dialog token = 0x%02x (%d)\n",
gas_data->dialog_token, gas_data->dialog_token);
printf(" fragment id = 0x%02x\n", gas_data->fragment_id);
printf(" GAS status = %s\n",
gas_data->status_code == DOT11_SC_SUCCESS ? "SUCCESS" :
gas_data->status_code == DOT11_SC_FAILURE ? "UNSPECIFIED" :
gas_data->status_code == DOT11_SC_ADV_PROTO_NOT_SUPPORTED ?
"ADVERTISEMENT_PROTOCOL_NOT_SUPPORTED" :
gas_data->status_code == DOT11_SC_NO_OUTSTAND_REQ ?
"NO_OUTSTANDING_REQUEST" :
gas_data->status_code == DOT11_SC_RSP_NOT_RX_FROM_SERVER ?
"RESPONSE_NOT_RECEIVED_FROM_SERVER" :
gas_data->status_code == DOT11_SC_TIMEOUT ?
"TIMEOUT" :
gas_data->status_code == DOT11_SC_QUERY_RSP_TOO_LARGE ?
"QUERY_RESPONSE_TOO_LARGE" :
gas_data->status_code == DOT11_SC_SERVER_UNREACHABLE ?
"SERVER_UNREACHABLE" :
gas_data->status_code == DOT11_SC_TRANSMIT_FAILURE ?
"TRANSMISSION_FAILURE" : "unknown");
printf(" GAS data length = %d\n", gas_data->data_len);
if (gas_data->data_len) {
wl_hexdump(gas_data->data, gas_data->data_len);
}
}
else if (event_type == WLC_E_GAS_COMPLETE) {
printf("WLC_E_GAS_COMPLETE: %s\n",
status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" :
"unknown");
}
}
}
free(data);
exit1:
close(fd);
exit2:
return err;
}
#endif /* WLANQPO */
static int
wl_hs20_ie(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
int ret;
int bsscfg_idx = 0;
int consumed = 0;
int length;
int malloc_size;
tlv_t *tlv;
UNUSED_PARAMETER(cmd);
if (!argv[1]) {
fprintf(stderr, "Too few arguments\n");
return -1;
}
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv + 1, argv[0], &bsscfg_idx, &consumed)) != 0)
return ret;
if (consumed)
argv = argv + consumed;
length = atoi(argv[1]);
if (length < 0 || length > 255) {
fprintf(stderr, "Invalid length\n");
return -1;
}
else if (length > 0) {
if (!argv[2]) {
fprintf(stderr,
"Data bytes should be specified for non-zero length\n");
return -1;
}
else {
/* Ensure each data byte is 2 characters long */
if ((int)strlen(argv[2]) != (length * 2)) {
fprintf(stderr, "Please specify all the data bytes for this IE\n");
return -1;
}
}
}
malloc_size = OFFSETOF(tlv_t, data) + length;
tlv = malloc(malloc_size);
if (tlv == 0) {
fprintf(stderr, "Error allocating %d bytes for IE params\n", malloc_size);
return -1;
}
tlv->id = DOT11_MNG_VS_ID;
tlv->len = length;
if (length > 0) {
if ((err = get_ie_data((uchar *)argv[2], tlv->data, length))) {
fprintf(stderr, "Error parsing data arg\n");
free(tlv);
return err;
}
}
if (bsscfg_idx == -1)
err = wlu_var_setbuf(wl, "hs20_ie", tlv, malloc_size);
else
err = wlu_bssiovar_setbuf(wl, "hs20_ie", bsscfg_idx,
tlv, malloc_size, buf, WLC_IOCTL_MAXLEN);
free(tlv);
return (err);
}
static dbg_msg_t toe_cmpnt[] = {
{TOE_TX_CSUM_OL, "tx_csum_ol"},
{TOE_RX_CSUM_OL, "rx_csum_ol"},
{0, NULL}
};
static dbg_msg_t arpoe_cmpnt[] = {
{ARP_OL_AGENT, "agent"},
{ARP_OL_SNOOP, "snoop"},
{ARP_OL_HOST_AUTO_REPLY, "host_auto_reply"},
{ARP_OL_PEER_AUTO_REPLY, "peer_auto_reply"},
{0, NULL}
};
/*
* Tcpip Offload Component-wise get/set control.
*/
static int
wl_offload_cmpnt(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint val, last_val = 0, cmpnt_add = 0, cmpnt_del = 0;
char *endptr;
dbg_msg_t *dbg_msg = NULL;
void *ptr = NULL;
int cmpnt;
if (strcmp(cmd->name, "toe_ol") == 0)
dbg_msg = toe_cmpnt;
else if (strcmp(cmd->name, "arp_ol") == 0)
dbg_msg = arpoe_cmpnt;
else {
printf("Not a valid command\n");
return BCME_BADARG;
}
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return (ret);
cmpnt = dtoh32(*(int *)ptr);
if (!*++argv) {
printf("0x%x ", cmpnt);
for (i = 0; (val = dbg_msg[i].value); i++) {
if ((cmpnt & val) && (val != last_val))
printf(" %s", dbg_msg[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char *s = *argv;
if (*s == '+' || *s == '-')
s++;
else
cmpnt_del = ~0; /* make the whole list absolute */
val = strtoul(s, &endptr, 0);
/* not a plain integer if not all the string was parsed by strtoul */
if (*endptr != '\0') {
for (i = 0; (val = dbg_msg[i].value); i++)
if (stricmp(dbg_msg[i].string, s) == 0)
break;
if (!val)
goto usage;
}
if (**argv == '-')
cmpnt_del |= val;
else
cmpnt_add |= val;
++argv;
}
cmpnt &= ~cmpnt_del;
cmpnt |= cmpnt_add;
cmpnt = htod32(cmpnt);
return (wlu_var_setbuf(wl, cmd->name, &cmpnt, sizeof(int)));
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (val = dbg_msg[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, dbg_msg[i].string);
else
fprintf(stderr, ", %s", dbg_msg[i].string);
last_val = val;
}
fprintf(stderr, "\n");
return 0;
}
#if defined(WLNDOE) || defined(WLOFFLD)
/*
* If a host IP address is given, add it to the host-cache,
* e.g. "wl nd_hostip fe00:0:0:0:0:290:1fc0:18c0 ".
* If no address is given, dump all the addresses.
*/
static int
wl_hostipv6(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
struct ipv6_addr ipa_set, *ipa_get, null_ipa;
uint16 *ip_addr;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ip_addr = (uint16*)ptr;
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (ipa_get = (struct ipv6_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV6_ADDR_LEN) != 0;
ipa_get++) {
/* Print ipv6 Addr */
for (i = 0; i < 8; i++) {
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
printf("\r\n");
ip_addr += 8;
}
} else {
/* Add */
if (!wl_atoipv6(*argv, &ipa_set))
return BCME_USAGE_ERROR;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name, &ipa_set, IPV6_ADDR_LEN);
}
return ret;
}
#endif /* defined(WLOFFLD) || defined(WLNDOE) */
#ifdef WLNDOE
static int
wl_ndstatus(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
struct nd_ol_stats_t *nd_stats;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
nd_stats = (struct nd_ol_stats_t *)ptr;
printf("host_ip_entries %d\r\n", nd_stats->host_ip_entries);
printf("host_ip_overflow %d\r\n", nd_stats->host_ip_overflow);
printf("peer_request %d\r\n", nd_stats->peer_request);
printf("peer_request_drop %d\r\n", nd_stats->peer_request_drop);
printf("peer_reply_drop %d\r\n", nd_stats->peer_reply_drop);
printf("peer_service %d\r\n", nd_stats->peer_service);
} else {
printf("Cannot set nd stats\n");
}
return 0;
}
/*
* If a solicit IP address is given, add it
* e.g. "wl nd_solicitip fe00:0:0:0:0:290:1fc0:18c0 ".
* If no address is given, dump all the addresses.
*/
static int
wl_solicitipv6(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
struct ipv6_addr ipa_set, *ipa_get, null_ipa;
uint16 *ip_addr;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ip_addr = (uint16*)ptr;
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (ipa_get = (struct ipv6_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV6_ADDR_LEN) != 0;
ipa_get++) {
/* Print ipv6 Addr */
for (i = 0; i < 8; i++) {
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
printf("\r\n");
ip_addr += 8;
}
} else {
/* Add */
if (!wl_atoipv6(*argv, &ipa_set))
return BCME_USAGE_ERROR;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name, &ipa_set, IPV6_ADDR_LEN);
}
return ret;
}
/*
* If a remote IP address is given, add it
* e.g. "wl nd_remoteip fe00:0:0:0:0:290:1fc0:18c0 ".
* If no address is given, dump the addresses.
*/
static int
wl_remoteipv6(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
struct ipv6_addr ipa_set, *ipa_get, null_ipa;
uint16 *ip_addr;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ip_addr = (uint16*)ptr;
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (ipa_get = (struct ipv6_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV6_ADDR_LEN) != 0;
ipa_get++) {
/* Print ipv6 Addr */
for (i = 0; i < 8; i++) {
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
printf("\r\n");
ip_addr += 8;
}
} else {
/* Add */
if (!wl_atoipv6(*argv, &ipa_set))
return BCME_USAGE_ERROR;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name, &ipa_set, IPV6_ADDR_LEN);
}
return ret;
}
#endif /* WLNDOE */
/*
* If a host IP address is given, add it to the host-cache, e.g. "wl arp_hostip 192.168.1.1".
* If no address is given, dump all the addresses.
*/
static int
wl_hostip(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct ipv4_addr ipa_set, *ipa_get, null_ipa;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
memset(null_ipa.addr, 0, IPV4_ADDR_LEN);
for (ipa_get = (struct ipv4_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV4_ADDR_LEN) != 0;
ipa_get++)
printf("%s\n", wl_iptoa(ipa_get));
printf("Total %d host addresses\n", (int)(ipa_get - (struct ipv4_addr *)ptr));
} else {
/* Add */
if (!wl_atoip(*argv, &ipa_set))
return BCME_USAGE_ERROR;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name, &ipa_set, sizeof(IPV4_ADDR_LEN));
}
return ret;
}
/*
* If a host IP address is given, add it to the host-cache,
* e.g. "wl nd_hostip fe00:0:0:0:0:290:1fc0:18c0 ".
* If no address is given, dump all the addresses.
*/
static int
wl_nshostip(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0, i;
struct ipv6_addr ipa_set, *ipa_get, null_ipa;
uint16 *ip_addr;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ip_addr = (uint16*)ptr;
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (ipa_get = (struct ipv6_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV6_ADDR_LEN) != 0;
ipa_get++) {
/* Print ipv6 Addr */
for (i = 0; i < 8; i++)
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
printf("\r\n");
ip_addr += 8;
}
else {
/* Add */
if (!wl_atoipv6(*argv, &ipa_set))
return -1;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name,
&ipa_set, IPV6_ADDR_LEN);
}
return ret;
}
#ifdef WLOFFLD
static int
wl_ol_notify_bcn_ie(void *wl, cmd_t *cmd, char **argv)
{
int ret = -1;
void *ptr = NULL;
#define VNDR_IE_ID (221)
#define OLMSG_BCN_MAX_IE (222)
struct beacon_ie_notify_cmd {
uint32 id;
uint32 enable;
struct ipv4_addr vndriemask;
} beacon_notification_cmd;
memset(&beacon_notification_cmd, 0, sizeof(beacon_notification_cmd));
++argv; /* skip the command iteself */
if (!*argv) {
/* Get Everything */
beacon_notification_cmd.id = -1; /* -1 indicates NONE */
ret = wlu_var_getbuf(wl, cmd->name, &beacon_notification_cmd,
sizeof(beacon_notification_cmd), &ptr);
} else {
if (stricmp(*argv, "enable") == 0) { /* Enable Global flag */
beacon_notification_cmd.id = -1; /* -1 indicates NONE */
beacon_notification_cmd.enable = 1;
return wlu_var_setbuf(wl, cmd->name, &beacon_notification_cmd,
sizeof(beacon_notification_cmd));
} else if (stricmp(*argv, "disable") == 0) { /* Disable Global flag */
beacon_notification_cmd.id = -1; /* -1 indicates NONE */
beacon_notification_cmd.enable = 0;
return wlu_var_setbuf(wl, cmd->name, &beacon_notification_cmd,
sizeof(beacon_notification_cmd));
} else { /* Get/Set Enable/Disable some ID */
beacon_notification_cmd.id = (uint8) atoi(*argv);
if (beacon_notification_cmd.id == VNDR_IE_ID) {
/* Get VNDR OUI */
++argv;
if (*argv) { /* Get a specific Vendor IE */
if (!wl_atoip(*argv, &beacon_notification_cmd.vndriemask)) {
return -1;
}
++argv;
/* Get enable/disable flag */
if (*argv) { /* Set a specific Vendor ie */
if (stricmp(*argv, "enable") == 0)
beacon_notification_cmd.enable = 1;
else if (stricmp(*argv, "disable") == 0)
beacon_notification_cmd.enable = 0;
else
return -1;
return wlu_var_setbuf(wl, cmd->name,
&beacon_notification_cmd,
sizeof(beacon_notification_cmd));
} else {
ret = wlu_var_getbuf(wl, cmd->name,
&beacon_notification_cmd,
sizeof(beacon_notification_cmd),
&ptr);
}
} else { /* Get ALL Vendor IE */
ret = wlu_var_getbuf(wl, cmd->name,
&beacon_notification_cmd,
sizeof(beacon_notification_cmd),
&ptr);
}
} else {
if (beacon_notification_cmd.id > OLMSG_BCN_MAX_IE) {
return -1;
}
/* Get enable/disable flag */
++argv;
if(*argv) { /* Set IE ID */
if (stricmp(*argv, "enable") == 0)
beacon_notification_cmd.enable = 1;
else if (stricmp(*argv, "disable") == 0)
beacon_notification_cmd.enable = 0;
else
return -1;
return wlu_var_setbuf(wl, cmd->name,
&beacon_notification_cmd,
sizeof(beacon_notification_cmd));
} else { /* Get IE ID */
ret = wlu_var_getbuf(wl, cmd->name,
&beacon_notification_cmd,
sizeof(beacon_notification_cmd),
&ptr);
}
}
}
}
if ((ret >= 0) && (ptr != NULL)) {
printf("%s\n", (char *)ptr); /* Print Everything */
}
return ret;
}
#endif /* WLOFFLD */
static int
wl_mcast_ackmac(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
int i, err;
wl_rmc_entry_t ackmac_params;
wl_rmc_entry_table_t tablelist;
void *ptr = NULL;
wl_rmc_entry_table_t *reply = NULL;
wl_rmc_gbl_table_t *gtbl = NULL;
miniopt_t to;
struct ether_addr* ea = NULL;
bool index_set = FALSE; bool mac_set = FALSE;
/* muticast mac address - ipv4 & ipv6 resp. */
uint8 mc_mac[6] = {0x1, 0x0, 0x5e, 0x7f, 0xff, 0xff};
uint8 mc_mac_ipv6[6] = {0x33, 0x33, 0x0, 0x0, 0x0, 0x0};
/* index will tell us what to do */
if (!*++argv) {
/* Get and display all entries in the table */
if ((ret = wlu_var_getbuf(wl, cmd->name,
NULL,
0,
&ptr)) < 0) {
return ret;
}
reply = (wl_rmc_entry_table_t *)ptr;
printf("\n Local: Active Mask 0x%02x Transmitter mask 0x%02x ",
reply->index, reply->opcode);
printf("\n Enable\t Multi-cast Group ");
printf("\n %d \t\t Ack All \t", (reply->index & WL_RMC_ACK_MCAST_ALL)?1:0);
for (i = 0; i < WL_RMC_MAX_TABLE_ENTRY; i++) {
printf("\n %d \t %s",
(reply->index & (WL_RMC_ACK_MCAST0 << i))?1:0,
wl_ether_etoa(&reply->entry[i].addr));
}
gtbl = (wl_rmc_gbl_table_t *)((char*)ptr+sizeof(wl_rmc_entry_table_t));
printf("\n Global Table: Active Mask 0x%02x ", gtbl->activeMask);
printf("\n Multi-cast \t\t Transmitter \t");
printf("\n Ack All Entry \t\t %s",
wl_ether_etoa(&gtbl->ackAll.tr_info[0].addr));
for (i = 0; i < WL_RMC_MAX_TABLE_ENTRY; i++) {
printf("\n %s \t", wl_ether_etoa(&gtbl->mc_entry[i].mcaddr));
printf("%s \t",
wl_ether_etoa(&gtbl->mc_entry[i].tr_info[0].addr));
}
printf("\n");
return 0;
}
miniopt_init(&to, cmd->name, NULL, FALSE);
while ((err = miniopt(&to, argv)) != -1) {
if (err == 1) {
return BCME_USAGE_ERROR;
}
argv += to.consumed;
/* Index for muticast address in RMC table */
if (to.opt == 'i') {
if (!to.good_int || (to.val >= WL_RMC_MAX_TABLE_ENTRY && to.val)) {
fprintf(stderr, "%s: Invalid mode value\n", cmd->name);
err = -1;
goto exit;
}
tablelist.index = to.val;
index_set = TRUE;
}
/* Add multicast address with index "i" to RMC table */
if (to.opt == 't') {
if (!wl_ether_atoe(to.valstr, &ackmac_params.addr)) {
fprintf(stderr,
"%s: could not parse \"%s\" as a MAC address\n",
cmd->name, to.valstr);
err = -1;
goto exit;
}
mac_set = TRUE;
}
}
if (index_set) {
if (!mac_set) {
/* if table index is -1 & no mac entry, it is for ACKALL */
if (tablelist.index == 255) {
/* enable all multi-cast */
tablelist.index = 0;
tablelist.opcode = RELMCAST_ENTRY_OP_ACK_ALL;
tablelist.entry[tablelist.index].flag = RELMCAST_ENTRY_OP_ACK_ALL;
}
/* if index is not -1 and no mac entry,
it is for removing entry at the index
*/
else if (tablelist.index < WL_RMC_MAX_TABLE_ENTRY) {
/* remove multi-cast entry by setting flag to deleted */
tablelist.opcode = RELMCAST_ENTRY_OP_DELETE;
tablelist.entry[tablelist.index].flag = RELMCAST_ENTRY_OP_DELETE;
}
else {
printf("%s Invalid index %d\n", cmd->name, tablelist.index);
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmd->name,
&tablelist,
sizeof(wl_rmc_entry_table_t)+sizeof(wl_rmc_gbl_table_t));
} else {
/* mac is also set */
memcpy(&tablelist.entry[tablelist.index].addr, &ackmac_params.addr,
sizeof(struct ether_addr));
/* insert to list */
ea = &tablelist.entry[tablelist.index].addr;
tablelist.entry[tablelist.index].flag = RELMCAST_ENTRY_OP_ENABLE;
/* for ipv4, initial three bytes in mc address are standard &
2 bytes for ipv6
*/
if ((!memcmp(ea, mc_mac, 3) && !(ea->octet[3] & 0x80)) ||
!memcmp(ea, mc_mac_ipv6, 2)) {
fprintf(stderr,
"\nAdding multi-cast mac %s\n", wl_ether_etoa(ea));
return wlu_var_setbuf(wl, cmd->name,
&tablelist,
sizeof(wl_rmc_entry_table_t)+sizeof(wl_rmc_gbl_table_t));
} else {
fprintf(stderr, "multicast mac started with"
"01:00:5e:0... or 33:33:...\n");
ret = BCME_BADARG;
}
}
}
exit:
return ret;
}
static int
wl_mcast_ackreq(void *wl, cmd_t *cmd, char **argv)
{
const char* fn_name = "wl_mcast_ackreq";
int err, i = 0;
uint argc;
char *endptr = NULL;
void *ptr = NULL;
uint8 *reply_mode = NULL;
uint8 params_mode, old_mode;
wl_relmcast_status_t status;
memset(&params_mode, 0, sizeof(uint8));
/* toss the command name */
argv++;
if ((err = wlu_var_getbuf_sm(wl, cmd->name, &params_mode,
sizeof(uint8), &ptr)) != BCME_OK) {
fprintf(stderr, "Error getting variable %s\n", argv[0]);
return err;
}
reply_mode = (uint8 *)ptr;
old_mode = *reply_mode;
if (!*argv) {
fprintf(stderr, "%s mode %d \n", fn_name, *reply_mode);
return err;
}
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* required arg: mode disable, enable or initiator enabled */
if (argc < 1)
return -1;
/* get the new ackreq mode */
params_mode = strtol(argv[0], &endptr, 0);
if ((*endptr != '\0') || (params_mode > WL_RMC_MODE_INITIATOR))
return -1;
if (argc > 1) {
fprintf(stderr,
"%s: could not parse extra argument %s:\n",
fn_name, argv[1]);
err = -1;
goto exit;
}
if ((err = wlu_var_setbuf(wl, cmd->name, &params_mode, sizeof(uint8))) != BCME_OK) {
goto out_of_here;
}
if (params_mode == WL_RMC_MODE_INITIATOR ||
((params_mode == WL_RMC_MODE_RECEIVER) &&
(old_mode == WL_RMC_MODE_INITIATOR))) {
for (i = 0; i <= 10; i++) {
/* check status of the RCV bit to make sure ack are receive */
if ((err = wlu_iovar_get(wl, "rmc_status",
(void *) &status,
(sizeof(wl_relmcast_status_t)))) < 0) {
return (err);
}
if (status.err != (uint16)BCME_NOTREADY) {
if (status.err == (uint16)BCME_RXFAIL) {
fprintf(stderr, "%s: error in setting mode: no ack receive"
"%d tx code %d \n",
fn_name, params_mode, status.err);
err = -1;
} else {
err = 0;
}
goto out_of_here;
}
/* Allow ample time (by staying in loop) to get ACK
for previous TX frame
*/
{
volatile uint16 busycnt = -1;
while (--busycnt)
;
busycnt = -1;
}
} /* for loop */
}
out_of_here:
if ((err < 0) || (i > 10))
fprintf(stderr, "%s: Error setting %d err %d \n", fn_name, params_mode, err);
else
fprintf(stderr, "%s: setting %d err %d \n", fn_name, params_mode, err);
exit:
return 0;
}
static int
wl_mcast_status(void *wl, cmd_t *cmd, char **argv)
{
int err, i;
wl_relmcast_status_t status;
if (!*++argv) {
/* Get */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &status,
(sizeof(wl_relmcast_status_t)))) < 0)
return (err);
if (status.ver != WL_RMC_VER) {
printf("Wrong Version %d %d\n", WL_RMC_VER, status.ver);
} else if (status.num == 0) {
printf("No clients associated\n");
} else {
for (i = 0; i < status.num; i++)
{
printf("%s\t%d\t%c%c%c\n", wl_ether_etoa(&status.clients[i].addr),
status.clients[i].rssi,
((status.clients[i].flag & WL_RMC_FLAG_ACTIVEACKER) ?
'A' : ' '),
((status.clients[i].flag & WL_RMC_FLAG_INBLACKLIST) ?
'B' : ' '),
((status.clients[i].flag & WL_RMC_FLAG_RELMCAST) ?
'R' : ' '));
}
printf("Notification Frame TimePeriod: %d ms\n", status.actf_time);
}
} else {
printf("Cannot set reliable multicast status\n");
}
return 0;
}
static int
wl_mcast_actf_time(void *wl, cmd_t *cmd, char **argv)
{
int val, error = -1;
const char *name = cmd->name;
/* toss the command name from the args */
argv++;
if (!*argv) {
error = wlu_iovar_getint(wl, name, &val);
if (error < 0)
return (error);
printf("Action Frame tx time period: %dms\n", val);
} else {
val = (uint16)strtol(*argv, NULL, 10); /* 10 is for base 10 (decimal) */
if (val >= WL_RMC_ACTF_TIME_MIN &&
val <= WL_RMC_ACTF_TIME_MAX) {
error = wlu_iovar_setint(wl, name, val);
} else {
printf("\"Out of range\": valid range %dms - %dms\n",
WL_RMC_ACTF_TIME_MIN,
WL_RMC_ACTF_TIME_MAX);
}
}
return error;
}
static int
wl_mcast_ar_timeout(void *wl, cmd_t *cmd, char **argv)
{
int val, error = -1;
const char *name = cmd->name;
/* toss the command name from the args */
argv++;
if (!*argv) {
error = wlu_iovar_getint(wl, name, &val);
if (error < 0)
return (error);
printf("Active Receiver time out: %dms\n", val);
} else {
val = (uint16)strtol(*argv, NULL, 10);
if (val >= WL_RMC_ARTMO_MIN &&
val <= WL_RMC_ARTMO_MAX)
error = wlu_iovar_setint(wl, name, val);
else
printf("\"Out of range\": valid range %dms - %dms\n",
WL_RMC_ARTMO_MIN,
WL_RMC_ARTMO_MAX);
}
return error;
}
static int
wl_mcast_rssi_thresh(void *wl, cmd_t *cmd, char **argv)
{
int val, error = -1;
const char *name = cmd->name;
/* toss the command name from the args */
argv++;
if (!*argv) {
error = wlu_iovar_getint(wl, name, &val);
if (error < 0)
return (error);
printf("rmc rssi: %d\n", val);
} else {
val = (int8)strtol(*argv, NULL, 10);
error = wlu_iovar_setint(wl, name, val);
}
return error;
}
static int
wl_mcast_stats(void *wl, cmd_t *cmd, char **argv)
{
#define PRCNT(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh16(cnts_v.name))
#define PRCNT32(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh32(cnts_v.name))
wl_rmc_cnts_t *cnts = NULL;
wl_rmc_cnts_t cnts_v;
int err = BCME_OK;
uint8 argval, argc;
char *pbuf = buf;
char *endptr = NULL;
void *ptr = NULL;
if (!*++argv) {
/* no arg - get and display all values */
if ((err = wlu_var_getbuf (wl, cmd->name, &cnts_v, sizeof(wl_rmc_cnts_t), &ptr)))
return (err);
cnts = (wl_rmc_cnts_t*)ptr;
memcpy((wl_rmc_cnts_t*)&cnts_v, cnts, sizeof(wl_rmc_cnts_t));
cnts_v.version = dtoh16(cnts->version);
cnts_v.length = dtoh16(cnts->length);
if (cnts_v.version != WL_RMC_CNT_VERSION) {
printf("\tIncorrect version of counters struct: expected %d; got %d\n",
WL_RMC_CNT_VERSION, cnts->version);
return -1;
}
PRCNT(dupcnt); PRCNT(ackreq_err); PRCNT(af_tx_err); PRNL();
PRCNT(null_tx_err); PRCNT(af_unicast_tx_err); PRCNT(mc_no_amt_slot); PRNL();
PRCNT(mc_no_glb_slot); PRCNT(mc_not_mirrored); PRCNT(mc_existing_tr); PRNL();
PRCNT(mc_exist_in_amt); PRCNT(mc_not_exist_in_gbl); PRCNT(mc_utilized); PRNL();
PRCNT(mc_taken_other_tr); PRCNT32(rmc_rx_frames_mac); PRCNT32(rmc_tx_frames_mac);
PRNL(); PRCNT32(mc_ar_role_selected); PRCNT32(mc_ar_role_deleted);
PRCNT32(mc_noacktimer_expired); PRNL(); PRCNT32(mc_null_ar_cnt); PRNL();
fputs(buf, stdout);
} else {
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
argval = strtol(argv[0], &endptr, 0);
if (argval == 255) {
/* arg is -1, clear all the values */
fprintf(stderr, "clearing rmc counters\n");
err = wlu_var_setbuf(wl, cmd->name, &cnts_v, sizeof(wl_rmc_cnts_t));
} else {
fprintf(stderr, "Invalid arg, only -1"
"is allowed to clear counters\n");
err = BCME_BADARG;
}
}
return err;
}
static int
wl_mcast_rssi_delta(void *wl, cmd_t *cmd, char **argv)
{
int val, error = -1;
const char *name = cmd->name;
/* toss the command name from the args */
argv++;
if (!*argv) {
error = wlu_iovar_getint(wl, name, &val);
if (error < 0)
return (error);
printf("rmc rssi delta: %d\n", val);
} else {
val = (uint16)strtol(*argv, NULL, 10);
if (val >= 0) /* rssi delta value should be a whole number */
error = wlu_iovar_setint(wl, name, val);
else
printf("\"Out of range\": rmc rssi delta should be >=0\n");
}
return error;
}
static int
wl_mcast_vsie(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
void *ptr = NULL;
wl_rmc_vsie_t *reply = NULL;
wl_rmc_vsie_t vsie;
char *parse = NULL;
char tmp[4];
int idx, cnt;
if (!*++argv) {
/* Get and display all entries in the table */
if ((ret = wlu_var_getbuf(wl, cmd->name,
NULL,
0,
&ptr)) < 0) {
return ret;
}
reply = (wl_rmc_vsie_t*)ptr;
printf("0x%x%x%x 0x%x", reply->oui[0], reply->oui[1],
reply->oui[2], reply->payload);
} else {
parse = *argv++;
/* remove "0x" from the input string which is in hex */
if (strlen(parse)/2 > DOT11_OUI_LEN) {
if (!strncmp("0x", parse, strlen("0x")) ||
!strncmp("0X", parse, strlen("0X"))) {
parse += strlen("0x");
}
}
/* if OUI string is not 6 characters, simply reject */
if (strlen(parse) != DOT11_OUI_LEN * 2)
return BCME_ERROR;
/* parse oui string */
for (idx = 0; idx < DOT11_OUI_LEN; idx++) {
for (cnt = 0; cnt < 2; cnt++) {
tmp[cnt] = *parse++;
}
tmp[cnt] = '\0';
vsie.oui[idx] = (uint8)(strtoul(tmp, NULL, 16));
}
/* second argument is missing!! */
if (!*argv) {
return BCME_ERROR;
}
vsie.payload = (uint16)(strtoul(*argv, NULL, 16));
ret = wlu_var_setbuf(wl, cmd->name, &vsie, sizeof(vsie));
}
return ret;
}
static int
wl_arp_stats(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct arp_ol_stats_t *arpstats;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
arpstats = (struct arp_ol_stats_t *)ptr;
printf("host_ip_entries = %d\n", dtoh32(arpstats->host_ip_entries));
printf("host_ip_overflow = %d\n", dtoh32(arpstats->host_ip_overflow));
printf("arp_table_entries = %d\n", dtoh32(arpstats->arp_table_entries));
printf("arp_table_overflow = %d\n", dtoh32(arpstats->arp_table_overflow));
printf("host_request = %d\n", dtoh32(arpstats->host_request));
printf("host_reply = %d\n", dtoh32(arpstats->host_reply));
printf("host_service = %d\n", dtoh32(arpstats->host_service));
printf("peer_request = %d\n", dtoh32(arpstats->peer_request));
printf("peer_request_drop = %d\n", dtoh32(arpstats->peer_request_drop));
printf("peer_reply = %d\n", dtoh32(arpstats->peer_reply));
printf("peer_reply_drop = %d\n", dtoh32(arpstats->peer_reply_drop));
printf("peer_service = %d\n", dtoh32(arpstats->peer_service));
printf("host_ip_entries = %d\n", dtoh32(arpstats->host_ip_entries));
} else
printf("Cannot set arp stats, use 'wl arp_stats_clear' to clear the counters\n");
return 0;
}
static int
wl_toe_stats(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct toe_ol_stats_t *toestats;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
toestats = (struct toe_ol_stats_t *)ptr;
printf("tx_summed = %d\n", dtoh32(toestats->tx_summed));
printf("tx_iph_fill = %d\n", dtoh32(toestats->tx_iph_fill));
printf("tx_tcp_fill = %d\n", dtoh32(toestats->tx_tcp_fill));
printf("tx_udp_fill = %d\n", dtoh32(toestats->tx_udp_fill));
printf("tx_icmp_fill = %d\n", dtoh32(toestats->tx_icmp_fill));
printf("rx_iph_good = %d\n", dtoh32(toestats->rx_iph_good));
printf("rx_iph_bad = %d\n", dtoh32(toestats->rx_iph_bad));
printf("rx_tcp_good = %d\n", dtoh32(toestats->rx_tcp_good));
printf("rx_tcp_bad = %d\n", dtoh32(toestats->rx_tcp_bad));
printf("rx_udp_good = %d\n", dtoh32(toestats->rx_udp_good));
printf("rx_udp_bad = %d\n", dtoh32(toestats->rx_udp_bad));
printf("rx_icmp_good = %d\n", dtoh32(toestats->rx_icmp_good));
printf("rx_icmp_bad = %d\n", dtoh32(toestats->rx_icmp_bad));
printf("tx_tcp_errinj = %d\n", dtoh32(toestats->tx_tcp_errinj));
printf("tx_udp_errinj = %d\n", dtoh32(toestats->tx_udp_errinj));
printf("tx_icmp_errinj = %d\n", dtoh32(toestats->tx_icmp_errinj));
printf("rx_tcp_errinj = %d\n", dtoh32(toestats->rx_tcp_errinj));
printf("rx_udp_errinj = %d\n", dtoh32(toestats->rx_udp_errinj));
printf("rx_icmp_errinj = %d\n", dtoh32(toestats->rx_icmp_errinj));
} else
printf("Cannot set toe stats, use 'wl toe_stats_clear' to clear the counters\n");
return 0;
}
static void
wl_rate_histo_print(wl_mac_ratehisto_res_t *rate_histo_res)
{
uint i, nss;
printf("Rates\n");
for (i = 0; i < (DOT11_RATE_MAX + 1); i++) {
if (rate_histo_res->rate[i]) {
if (DIV_REM(i, 2))
printf("%.2d\t%d.%d Mbit/s\n",
rate_histo_res->rate[i], DIV_QUO(i, 2), DIV_REM(i, 2)/10);
else
printf("%.2d\t%d Mbit/s\n",
rate_histo_res->rate[i], DIV_QUO(i, 2));
}
}
printf("MCS indexes:\n");
for (i = 0; i < (WL_RATESET_SZ_HT_MCS * WL_TX_CHAINS_MAX); i++) {
if (rate_histo_res->mcs[i]) {
printf("%d\tMCS %d\n", rate_histo_res->mcs[i], i);
}
}
printf("VHT indexes:\n");
for (nss = 0; nss < WL_TX_CHAINS_MAX; nss++) {
for (i = 0; i < WL_RATESET_SZ_VHT_MCS; i++) {
if (rate_histo_res->vht[i][nss]) {
printf("%d\tVHT %d Nss %d\n", rate_histo_res->vht[i][nss], i,
nss + 1);
}
}
}
return;
}
static int
wl_rate_histo(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err;
wl_mac_ratehisto_res_t *rate_histo_res;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
rate_histo_res = (wl_mac_ratehisto_res_t *)ptr;
wl_rate_histo_print(rate_histo_res);
return 0;
}
static int
wl_mac_rate_histo(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr ea;
int buflen, err;
wl_mac_ratehisto_cmd_t *rate_histo_cmd;
wl_mac_ratehisto_res_t *rate_histo_res;
if (!*++argv || !wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
strcpy(buf, "mac_rate_histo");
buflen = strlen(buf) + 1;
rate_histo_cmd = (wl_mac_ratehisto_cmd_t *)(buf + buflen);
memcpy((char*)&rate_histo_cmd->ea, (char*)&ea, ETHER_ADDR_LEN);
if (*++argv)
{
/* The access category is obtained and checked for validity */
rate_histo_cmd->ac_cat = (uint8)strtol(*argv, NULL, 0);
if (!(rate_histo_cmd->ac_cat == 0x10 || rate_histo_cmd->ac_cat == 0x4)) {
printf("Only Access Category 0x10 and 0x4 is supported\n");
return BCME_BADARG;
}
if (*++argv) {
/* The number of pkts to avg is obtained and checked for valid range */
rate_histo_cmd->num_pkts = (uint8)strtol(*argv, NULL, 10);
} else {
/* Set default value as maximum of all access categories
* so that it is set to the max value below
*/
rate_histo_cmd->num_pkts = 64;
}
if (rate_histo_cmd->ac_cat == 0x10 && rate_histo_cmd->num_pkts > 64) {
rate_histo_cmd->num_pkts = 64;
} else if (rate_histo_cmd->ac_cat == 0x4 && rate_histo_cmd->num_pkts > 32) {
rate_histo_cmd->num_pkts = 32;
}
} else {
return BCME_USAGE_ERROR;
}
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
rate_histo_res = (wl_mac_ratehisto_res_t *)buf;
wl_rate_histo_print(rate_histo_res);
printf("First TSF Timestamp: %08x%08x\n", rate_histo_res->tsf_timer[0][1],
rate_histo_res->tsf_timer[0][0]);
printf("Last TSF Timestamp : %08x%08x\n", rate_histo_res->tsf_timer[1][1],
rate_histo_res->tsf_timer[1][0]);
return 0;
}
static int
wl_pkteng_stats(void *wl, cmd_t *cmd, char **argv)
{
wl_pkteng_stats_t *stats;
void *ptr = NULL;
int err;
uint16 *pktstats;
int i, j;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
stats = ptr;
printf("Lost frame count %d\n", dtoh32(stats->lostfrmcnt));
printf("RSSI %d\n", dtoh32(stats->rssi));
printf("Signal to noise ratio %d\n", dtoh32(stats->snr));
printf("rx1mbps %d rx2mbps %d rx5mbps5 %d\n"
"rx6mbps %d rx9mbps %d, rx11mbps %d\n"
"rx12mbps %d rx18mbps %d rx24mbps %d\n"
"rx36mbps %d rx48mbps %d rx54mbps %d\n",
stats->rxpktcnt[3], stats->rxpktcnt[1], stats->rxpktcnt[2],
stats->rxpktcnt[7], stats->rxpktcnt[11], stats->rxpktcnt[0],
stats->rxpktcnt[6], stats->rxpktcnt[10], stats->rxpktcnt[5],
stats->rxpktcnt[9], stats->rxpktcnt[4], stats->rxpktcnt[8]);
pktstats = &stats->rxpktcnt[NUM_80211b_RATES+NUM_80211ag_RATES];
for (i = 0; i < NUM_80211n_RATES/4; i++) {
for (j = 0; j < 4; j++) {
printf("rxmcs%d %d ", j+4*i, pktstats[j+4*i]);
}
printf("\n");
}
printf("rxmcsother %d\n", stats->rxpktcnt[NUM_80211_RATES]);
return 0;
}
#define LPPHY_PAPD_EPS_TBL_SIZE 64
static int
wl_phy_papdepstbl(void *wl, cmd_t *cmd, char **argv)
{
int32 eps_real, eps_imag;
int i;
uint32 eps_tbl[LPPHY_PAPD_EPS_TBL_SIZE];
int err;
UNUSED_PARAMETER(argv);
if ((err = wlu_iovar_get(wl, cmd->name, &eps_tbl, sizeof(eps_tbl))) < 0)
return err;
for (i = 0; i < LPPHY_PAPD_EPS_TBL_SIZE; i++) {
if ((eps_real = (int32)(eps_tbl[i] >> 12)) > 0x7ff)
eps_real -= 0x1000; /* Sign extend */
if ((eps_imag = (int32)(eps_tbl[i] & 0xfff)) > 0x7ff)
eps_imag -= 0x1000; /* Sign extend */
printf("%d %d\n", eps_real, eps_imag);
}
return 0;
}
static int
wl_phy_txiqcc(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err;
int32 iqccValues[4];
int32 value;
char *endptr;
int32 a, b, a1, b1;
wlc_rev_info_t revinfo;
uint32 phytype;
memset(&revinfo, 0, sizeof(revinfo));
if ((err = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return err;
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_N) {
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, iqccValues, 2*sizeof(int32))) < 0)
return err;
a = (int16)iqccValues[0];
b = (int16)iqccValues[1];
/* sign extend a, b from 10 bit signed value to 32 bit signed value */
a = ((a << 22) >> 22);
b = ((b << 22) >> 22);
printf("%d %d\n", a, b);
}
else
{
for (i = 0; i < 2; i++) {
value = strtol(*argv++, &endptr, 0);
if (value > 511 || value < -512) {
return BCME_BADARG;
}
iqccValues[i] = value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, iqccValues, 2*sizeof(int32))) < 0)
return err;
}
} else {
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, iqccValues, 4*sizeof(int32))) < 0)
return err;
a = (int16)iqccValues[0];
b = (int16)iqccValues[1];
a1 = (int16)iqccValues[2];
b1 = (int16)iqccValues[3];
/* sign extend a, b from 10 bit signed value to 32 bit signed value */
a = ((a << 22) >> 22);
b = ((b << 22) >> 22);
a1 = ((a1 << 22) >> 22);
b1 = ((b1 << 22) >> 22);
printf("%d %d %d %d\n", a, b, a1, b1);
}
else
{
for (i = 0; i < 4; i++) {
value = strtol(*argv++, &endptr, 0);
if (value > 511 || value < -512) {
return BCME_BADARG;
}
iqccValues[i] = value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, iqccValues, 4*sizeof(int32))) < 0)
return err;
}
}
return 0;
}
static int
wl_phy_txlocc(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err;
int8 loccValues[12];
int32 value;
char *endptr;
wlc_rev_info_t revinfo;
uint32 phytype;
memset(&revinfo, 0, sizeof(revinfo));
if ((err = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return err;
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_N) {
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, loccValues,
sizeof(loccValues))) < 0)
return err;
/* sign extend the loccValues */
loccValues[2] = (loccValues[2] << 3) >> 3;
loccValues[3] = (loccValues[3] << 3) >> 3;
loccValues[4] = (loccValues[4] << 3) >> 3;
loccValues[5] = (loccValues[5] << 3) >> 3;
printf("%d %d %d %d %d %d\n", loccValues[0],
loccValues[1], loccValues[2], loccValues[3],
loccValues[4], loccValues[5]);
}
else
{
for (i = 0; i < 6; i++) {
value = strtol(*argv++, &endptr, 0);
if ((i >= 2) && (value > 15 || value < -15)) {
return BCME_BADARG;
}
loccValues[i] = (int8)value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, loccValues, 6*sizeof(int8))) < 0)
return err;
}
} else {
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, loccValues,
sizeof(loccValues))) < 0)
return err;
/* sign extend the loccValues */
loccValues[2] = (loccValues[2] << 3) >> 3;
loccValues[3] = (loccValues[3] << 3) >> 3;
loccValues[4] = (loccValues[4] << 3) >> 3;
loccValues[5] = (loccValues[5] << 3) >> 3;
loccValues[8] = (loccValues[8] << 3) >> 3;
loccValues[9] = (loccValues[9] << 3) >> 3;
loccValues[10] = (loccValues[10] << 3) >> 3;
loccValues[11] = (loccValues[11] << 3) >> 3;
printf("%d %d %d %d %d %d %d %d %d %d %d %d\n", loccValues[0],
loccValues[1], loccValues[2], loccValues[3], loccValues[4],
loccValues[5], loccValues[6], loccValues[7], loccValues[8],
loccValues[9], loccValues[10], loccValues[11]);
}
else
{
for (i = 0; i < 12; i++) {
value = strtol(*argv++, &endptr, 0);
if (((i < 2) && (value > 63 || value < -64)) ||
((i >= 2) && (value > 15 || value < -15))) {
return BCME_BADARG;
}
loccValues[i] = (int8)value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, loccValues, 12*sizeof(int8))) < 0)
return err;
}
}
return 0;
}
static int
wl_rssi_cal_freq_grp_2g(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
uint8 nvramValues[14];
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint8 N = 0;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
if (!*++argv) {
/* Reading the NVRAM variable */
if ((err = wlu_iovar_get(wl, cmd->name, nvramValues, sizeof(nvramValues))) < 0)
return err;
N = 14; /* 14 corresponds to number of channels in 2g */
for (i = 0; i < N-2; i++) {
printf("0x%x%x,", nvramValues[i], nvramValues[i+1]);
i++;
}
printf("0x%x%x\n", nvramValues[i], nvramValues[i+1]);
} else {
/* Writing to NVRAM variable */
char *splt;
int8 tmp;
splt = strtok(*argv, ",");
/* N = 14 corresponds to number of channels in 2g */
/* N = N /2 to package 2 channel's nibbles into 1 byte */
N = 7;
i = 0;
while (splt != NULL) {
/* Splitting the input based on charecter ','
* Further each byte is divided into 2 nibbles
* and saved into 2 elements of array.
*/
tmp = strtol(splt, &endptr, 0);
nvramValues[i] = (tmp >> 4) & 0xf;
i++;
nvramValues[i] = tmp & 0xf;
splt = strtok(NULL, ",");
i++;
}
if (i != 14) {
printf("Insufficient arguments \n");
return BCME_BADARG;
}
if ((err = wlu_var_setbuf(wl, cmd->name, nvramValues, N*2*sizeof(int8))) < 0)
return err;
}
return 0;
}
static int
wl_phy_rssi_gain_delta_2g_sub(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
int8 deltaValues[28];
int32 value;
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint32 phytype;
uint8 N = 0;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_AC) {
return err;
}
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
N = 18; /* 9 entries per core, 4350 - 18 MAX entries; 4345 9 MAX entries */
for (i = 0; i < N; i++) {
if (i%9 == 0 && i > 0) {
printf("\n");
if (deltaValues[i] == -1) break;
}
printf("%d ", deltaValues[i]);
}
if (i == N)
printf("\n");
} else {
int argc = 0;
int index = 0;
while (argv[argc])
argc++;
/* ACPHY : 8/9 entries for a core; core 0 delta's can be
* given with or with out core_num as first element
*/
N = argc;
for (i = 0; i < N; i++) {
value = strtol(*argv++, &endptr, 0);
if ((value > 63 || value < -64)) {
return BCME_BADARG;
}
if (argc == 9) {
/* If number of arguments is 9, then core
* number has been provided.
* And 8 elements related to 2
* (BWs - 20 n 40) and 4 gain settings
* (elna_on, elna_off, rout_1, rout_2) are
* provided. So, 2 * 4 = 8 + 1 core_num = 9
*/
deltaValues[i] = (int8)value;
} else {
/* If the number of elements is not eq to 9,
* then, core number was not provided.
* So, if only 8 elements are provided, only
* core 0's info is given. So, for i = 0,
* deltaValues element is 0 (core_num). If 16
* elements are provided, then core 0 and 1's info is
* provided. So, i =0 element has core_num = 0,
* then, next 8 elements are core 0's
* deltas. For i = 8, core 1's core_num = 1
* is inserted into deltaValues array.
* Similarly for third core data.
*/
if (i == 0) {
deltaValues[index] = 0;
index++;
} else if (i == 8) {
deltaValues[index] = 1;
index++;
} else if (i == 16) {
deltaValues[index] = 2;
index++;
}
deltaValues[index] = (int8)value;
index++;
}
}
/* If argc == 8, then only 1 core's info was given,
* so, setbuf() is called once.
* If argc == 16 then core 0 and 1's info was given.
* So, setbuf() is called twice.
* If argc == 24 then core 0, 1 and 2's info was given.
* So, setbuf() is called thrice.
*/
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues, 9*sizeof(int8))) < 0)
return err;
if (argc >= 16) {
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues + 9, 9*sizeof(int8))) < 0)
return err;
}
if (argc == 24) {
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues + 18, 9*sizeof(int8))) < 0)
return err;
}
}
return 0;
}
static int
wl_phy_rssi_gain_delta_2g(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
int8 deltaValues[18];
int32 value;
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint32 phytype;
uint8 N = 0;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_AC) {
return err;
}
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
if (phytype == WLC_PHY_TYPE_AC)
N = 15; /* ACPHY: 3 cores max x 5 entries */
for (i = 0; i < N; i++) {
if ((phytype == WLC_PHY_TYPE_AC) && (i%5 == 0)) {
if (i > 0) printf("\n");
if (deltaValues[i] == -1) break;
}
printf("%d ", deltaValues[i]);
}
if (i == N)
printf("\n");
} else {
int argc = 0;
int index = 0;
while (argv[argc])
argc++;
if (phytype == WLC_PHY_TYPE_AC) {
/* N = 5; ACPHY : 5 entries for a core */
N = argc;
}
for (i = 0; i < N; i++) {
value = strtol(*argv++, &endptr, 0);
if ((value > 63 || value < -64)) {
return BCME_BADARG;
}
if (argc == 5) {
/* If number of arguments is 5, then core number has been provided.
* And 8 elements related to 2 (BWs - 20 n 40) and 2 gain settings
* (elna_on, elna_off) are provided. So, 2 * 2 = 4 + 1 core_num = 5
*/
deltaValues[i] = (int8)value;
} else {
/* If the number of elements is not eq to 5,
* then, core number was not provided.
* So, if only 4 elements are provided, only
* core 0's info is given. So, for i = 0,
* deltaValues element is 0 (core_num). If 8
* elements are provided, then core 0 and 1's info is
* provided. So, i =0 element has core_num = 0,
* then, next 4 elements are core 0's
* deltas. For i = 4, core 1's core_num = 1
* is inserted into deltaValues array.
* Similarly for third core data.
*/
if (i == 0) {
deltaValues[index] = 0;
index++;
} else if (i == 4) {
deltaValues[index] = 1;
index++;
} else if (i == 8) {
deltaValues[index] = 2;
index++;
}
deltaValues[index] = (int8)value;
index++;
}
}
/* If argc == 4, then only 1 core's info was given,
* so, setbuf() is called once.
* If argc == 8 then core 0 and 1's info was given.
* So, setbuf() is called twice.
* If argc == 12 then core 0, 1 and 2's info was given.
* So, setbuf() is called thrice.
*/
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues, 5*sizeof(int8))) < 0)
return err;
if (argc >= 8) {
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues + 5, 5*sizeof(int8))) < 0)
return err;
}
if (argc == 12) {
if ((err = wlu_var_setbuf(wl, cmd->name,
deltaValues + 10, 5*sizeof(int8))) < 0)
return err;
}
}
return 0;
}
static int
wl_phy_rssi_gain_delta_5g(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
int8 deltaValues[28];
int32 value;
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint32 phytype;
uint8 N = 0, n_per_core, n_per_core_p1;
char *varname = "rssi_cal_rev";
const char *iovar = "nvram_get";
void *p;
err = wlu_var_getbuf(wl, iovar, varname, strlen(varname) + 1, &p);
if (err == 0) {
/* This means, NVRAM variable found. */
/* Calls new function for accommodating ROuts */
value = strtol(buf, &endptr, 0);
}
if ((err < 0) || ((err == 0) && (value == 0))) {
/* This means, NVRAM variable not found or Variable is 0 */
/* Calls old function */
n_per_core = 6;
} else {
n_per_core = 12;
}
n_per_core_p1 = n_per_core + 1;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_AC) {
return err;
}
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
if (phytype == WLC_PHY_TYPE_AC)
N = n_per_core_p1 * 3; /* ACPHY: 3 cores max x 7 entries */
for (i = 0; i < N; i++) {
if ((phytype == WLC_PHY_TYPE_AC) && (i%n_per_core_p1 == 0)) {
if (i > 0) printf("\n");
if (deltaValues[i] == -1) break;
}
printf("%d ", deltaValues[i]);
}
if (i == N)
printf("\n");
} else {
int argc = 0;
int index = 0;
while (argv[argc])
argc++;
if (phytype == WLC_PHY_TYPE_AC) {
/* N = 7; ACPHY : 7 entries for a core */
N = argc;
}
for (i = 0; i < N; i++) {
value = strtol(*argv++, &endptr, 0);
if ((value > 63 || value < -64)) {
return BCME_BADARG;
}
if (argc == n_per_core_p1) {
/* For Old implementation, ie, no Routs, n_per_core_p1 == 5
* for New implementation, ie, no Routs, n_per_core_p1 == 9
* If number of arguments is "n_per_core_p1",
* then core number has been provided.
*/
deltaValues[i] = (int8)value;
} else {
/* If the number of elements is not eq to
*"n_per_core", then, core number was not provided.
* So, if only "n_per_core" elements are provided,
* only core 0's info is given. So, for i = 0,
* deltaValues element is 0 (core_num). If "n_per_core * 2"
* elements are provided, then core 0 and 1's info is
* provided. So, i =0 element has core_num = 0, then,
* next "n_per_core" elements are core 0's
* deltas. For i = "n_per_core", core 1's
* core_num = 1 is inserted into deltaValues array.
* Similarly for third core data.
*/
if (i == (n_per_core * 0)) {
deltaValues[index] = 0;
index++;
}
if (i == (n_per_core * 1)) {
deltaValues[index] = 1;
index++;
}
if (i == (n_per_core * 2)) {
deltaValues[index] = 2;
index++;
}
deltaValues[index] = (int8)value;
index++;
}
}
/* If argc == "n_per_core", then only 1 core's infoxs
* was given, so, setbuf() is called once.
* If argc == "n_per_core * 2" then core 0 and 1's info
* was given. So, setbuf() is called twice.
* If argc == "n_per_core * 3" then core 0, 1 and 2's
* info was given. So, setbuf() is called thrice.
*/
if ((err = wlu_var_setbuf(wl, cmd->name, deltaValues,
n_per_core_p1*sizeof(int8))) < 0)
return err;
if (argc >= (n_per_core * 2)) {
if ((err = wlu_var_setbuf(wl, cmd->name, deltaValues +
(n_per_core_p1 * 1), n_per_core_p1*sizeof(int8))) < 0)
return err;
}
if (argc == (n_per_core * 3)) {
if ((err = wlu_var_setbuf(wl, cmd->name, deltaValues +
(n_per_core_p1 * 2), n_per_core_p1*sizeof(int8))) < 0)
return err;
}
}
return 0;
}
static int
wl_phy_rxgainerr_2g(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
int8 deltaValues[18];
int32 value;
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint32 phytype;
uint8 N = 0;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_AC) {
return err;
}
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
if (phytype == WLC_PHY_TYPE_AC)
N = 2; /* ACPHY: 3 cores 1 entry per core */
for (i = 0; i < N; i++) {
printf("%d ", deltaValues[i]);
}
if (i == N)
printf("\n");
} else {
int argc = 0;
while (argv[argc])
argc++;
if (argc != 2) {
printf("IOVAR works only for 2 cores scenario. \n");
return err;
}
if (phytype == WLC_PHY_TYPE_AC)
N = 2; /* ACPHY : 5 entries for a core */
for (i = 0; i < N; i++) {
value = strtol(*argv++, &endptr, 0);
if ((value > 63 || value < -64)) {
return BCME_BADARG;
}
deltaValues[i] = (int8)value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, deltaValues, N*sizeof(int8))) < 0)
return err;
}
return 0;
}
static int
wl_phy_rxgainerr_5g(void *wl, cmd_t *cmd, char **argv)
{
int i;
int err = -1;
int8 deltaValues[28];
int32 value;
char *endptr;
int ret = -1;
wlc_rev_info_t revinfo;
uint32 phytype;
uint8 N = 0;
memset(&revinfo, 0, sizeof(revinfo));
ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
if (ret) {
return ret;
}
phytype = dtoh32(revinfo.phytype);
if (phytype != WLC_PHY_TYPE_AC) {
return err;
}
if (!*++argv) {
if ((err = wlu_iovar_get(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
if (phytype == WLC_PHY_TYPE_AC)
N = 2; /* ACPHY: 3 cores 1 entry per core */
for (i = 0; i < N; i++) {
printf("%d ", deltaValues[i]);
}
if (i == N)
printf("\n");
} else {
int argc = 0;
while (argv[argc])
argc++;
if (argc != 2) {
printf("IOVAR works only for 2 cores scenario. \n");
return err;
}
if (phytype == WLC_PHY_TYPE_AC)
N = 2; /* ACPHY : 7 entries for a core */
for (i = 0; i < N; i++) {
value = strtol(*argv++, &endptr, 0);
if ((value > 63 || value < -64)) {
return BCME_BADARG;
}
deltaValues[i] = (int8)value;
}
if ((err = wlu_var_setbuf(wl, cmd->name, deltaValues, sizeof(deltaValues))) < 0)
return err;
}
return 0;
}
static int
wl_phytable(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 tableInfo[4];
int32 value;
char *endptr;
void *ptr = NULL;
int32 tableId, tableOffset, tableWidth, tableElement;
if (*++argv != NULL)
tableId = strtol(*argv, &endptr, 0);
else
return BCME_USAGE_ERROR;
if (*++argv != NULL)
tableOffset = strtol(*argv, &endptr, 0);
else
return BCME_USAGE_ERROR;
if (*++argv != NULL)
tableWidth = strtol(*argv, &endptr, 0);
else
return BCME_USAGE_ERROR;
if ((tableId < 0) || (tableOffset < 0))
return BCME_BADARG;
if ((tableWidth != 8) && (tableWidth != 16) && (tableWidth != 32))
return BCME_BADARG;
if (!*++argv) {
tableInfo[0] = tableId;
tableInfo[1] = tableOffset;
tableInfo[2] = tableWidth;
if ((err = wlu_var_getbuf(wl, cmd->name, tableInfo, 4*sizeof(int32), &ptr)) < 0)
return err;
tableElement = ((int32*)ptr)[0];
printf("0x%x(%d)\n", tableElement, tableElement);
}
else
{
value = strtol(*argv++, &endptr, 0);
tableElement = value;
tableInfo[0] = tableId;
tableInfo[1] = tableOffset;
tableInfo[2] = tableWidth;
tableInfo[3] = tableElement;
if ((err = wlu_var_setbuf(wl, cmd->name, tableInfo, 4*sizeof(int32))) < 0)
return err;
}
return 0;
}
static int
wl_phy_txpwrindex(void *wl, cmd_t *cmd, char **argv)
{
uint i;
int ret;
uint32 txpwridx[4] = { 0 };
int8 idx[4] = { 0 };
uint argc;
char *endptr;
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
for (i = 0; i < 4; i++) {
if (argc > i) {
txpwridx[i] = strtol(argv[1 + i], &endptr, 0);
if (*endptr != '\0') {
printf("error\n");
return BCME_USAGE_ERROR;
}
}
}
if (argc == 0) {
if ((ret = wlu_iovar_getint(wl, cmd->name, (int*)&txpwridx[0])) < 0) {
return (ret);
}
txpwridx[0] = dtoh32(txpwridx[0]);
idx[0] = (int8)(txpwridx[0] & 0xff);
idx[1] = (int8)((txpwridx[0] >> 8) & 0xff);
idx[2] = (int8)((txpwridx[0] >> 16) & 0xff);
idx[3] = (int8)((txpwridx[0] >> 24) & 0xff);
printf("txpwrindex for core{0...3}: %d %d %d %d\n", idx[0], idx[1],
idx[2], idx[3]);
} else {
wlc_rev_info_t revinfo;
uint32 phytype;
memset(&revinfo, 0, sizeof(revinfo));
if ((ret = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return ret;
phytype = dtoh32(revinfo.phytype);
if (phytype == WLC_PHY_TYPE_HT) {
if (argc != 3) {
printf("HTPHY must specify 3 core txpwrindex\n");
return BCME_USAGE_ERROR;
}
} else if (phytype == WLC_PHY_TYPE_N) {
if (argc != 2) {
printf("NPHY must specify 2 core txpwrindex\n");
return BCME_USAGE_ERROR;
}
}
ret = wlu_iovar_setbuf(wl, cmd->name, txpwridx, 4*sizeof(uint32),
buf, WLC_IOCTL_MAXLEN);
}
return ret;
}
static int
wl_phy_force_vsdb_chans(void *wl, cmd_t *cmd, char **argv)
{
uint16 *chans = NULL;
int ret = 0;
void *ptr;
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd);
if (argv[1] == NULL) {
if ((ret = wlu_var_getbuf(wl, "force_vsdb_chans", NULL, 0, &ptr) < 0)) {
printf("wl_phy_maxpower: fail to get maxpower\n");
return ret;
}
chans = (uint16*)ptr;
printf("Chans : %x %x \n", chans[0], chans[1]);
} else if (argv[1] && argv[2]) {
/* Allocate memory */
chans = (uint16*)malloc(2 * sizeof(uint16));
if (chans == NULL) {
printf("unable to allocate Memory \n");
return BCME_NOMEM;
}
chans[0] = wf_chspec_aton(argv[1]);
chans[1] = wf_chspec_aton(argv[2]);
if (((chans[0] & 0xff) == 0) || ((chans[1] & 0xff) == 0)) {
chans[0] = 0;
chans[1] = 0;
}
ret = wlu_iovar_setbuf(wl, cmd->name, chans, 2 * sizeof(uint16),
buf, WLC_IOCTL_MAXLEN);
if (chans)
free(chans);
} else {
ret = BCME_USAGE_ERROR;
}
return ret;
}
#ifdef SR_DEBUG
/* Displays PMU related info on screen */
static int
wl_dump_pmu(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
pmu_reg_t *pmu_var;
int err;
uint i;
uint32 pmu_chip_ctl_reg;
uint32 pmu_chip_reg_reg;
uint32 pmu_chip_pll_reg;
uint32 pmu_chip_res_reg;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_med (wl, cmd->name, NULL, 0, &ptr)))
return (err);
pmu_var = (pmu_reg_t *)ptr;
printf("PMU Control : 0x%08x\n", pmu_var->pmu_control);
printf("PMU Capabilities : 0x%08x\n", pmu_var->pmu_capabilities);
printf("PMU Status : 0x%08x\n", pmu_var->pmu_status);
printf("Resource State : 0x%08x\n", pmu_var->res_state);
printf("Resurce Pending : 0x%08x\n", pmu_var->res_pending);
printf("PMU Timer : 0x%08x\n", pmu_var->pmu_timer1);
printf("Minimum Resource Mask: 0x%08x\n", pmu_var->min_res_mask);
printf("Maximum Resource Mask: 0x%08x\n", pmu_var->max_res_mask);
/* Displays values of the 5 PMU Chip Control Registers */
pmu_chip_ctl_reg = (pmu_var->pmu_capabilities & 0xf8000000);
pmu_chip_ctl_reg = pmu_chip_ctl_reg >> 27;
for (i = 0; i < pmu_chip_ctl_reg; i++) {
printf("PMU ChipControl[%d] : 0x%08x\n", i, pmu_var->pmu_chipcontrol1[i]);
}
/* Displays values of the 6 PMU Reg Control Registers */
pmu_chip_reg_reg = (pmu_var->pmu_capabilities & 0x07c00000);
pmu_chip_reg_reg = pmu_chip_reg_reg >> 22;
for (i = 0; i < pmu_chip_reg_reg; i++) {
printf("PMU RegControl[%d] : 0x%08x\n", i, pmu_var->pmu_regcontrol[i]);
}
/* Displays values of the 6 PMU Pll Control Registers */
pmu_chip_pll_reg = (pmu_var->pmu_capabilities & 0x003e0000);
pmu_chip_pll_reg = pmu_chip_pll_reg >> 17;
for (i = 0; i < pmu_chip_pll_reg; i++) {
printf("PMU PllControl[%d] : 0x%08x\n", i, pmu_var->pmu_pllcontrol[i]);
}
/* Displays values of the 31 PMU Resource Up/Down Timer */
pmu_chip_res_reg = (pmu_var->pmu_capabilities & 0x00001f00);
pmu_chip_res_reg = pmu_chip_res_reg >> 8;
for (i = 0; i < pmu_chip_res_reg; i++) {
printf("PMU Resource Up/Down Timer[%d] : 0x%08x\n", i,
pmu_var->pmu_rsrc_up_down_timer[i]);
}
/* Displays values of the 31 PMU Resource Dependancy Mask */
pmu_chip_res_reg = (pmu_var->pmu_capabilities & 0x00001f00);
pmu_chip_res_reg = pmu_chip_res_reg >> 8;
for (i = 0; i < pmu_chip_res_reg; i++) {
printf("PMU Resource Dependancy Mask[%d] : 0x%08x\n", i,
pmu_var->rsrc_dep_mask[i]);
}
return 0;
}
static int
wl_pmu_keep_on(void *wl, cmd_t *cmd, char **argv)
{
int err;
void *ptr;
uint32 val;
pmu_min_res_t *pmu_min_res_var;
if (argv[1] == NULL) {
if ((err = wlu_var_getbuf_med (wl, cmd->name, NULL, 0, &ptr)))
return (err);
pmu_min_res_var = (pmu_min_res_t *)ptr;
if (pmu_min_res_var->pmu_flag == 1) {
printf("Minimum Resource Mask:0x%x\n",
pmu_min_res_var->pmu_min_res_val);
} else {
printf("The PMU is kept on upto resource# %d\n",
pmu_min_res_var->pmu_min_res_val);
}
}
else {
val = atoi(argv[1]);
wlu_iovar_setbuf(wl, cmd->name, &val, sizeof(uint32), buf, WLC_IOCTL_MAXLEN);
}
return 0;
}
#endif /* SR_DEBUG */
static int
wl_sarlimit(void *wl, cmd_t *cmd, char **argv)
{
uint i;
int ret;
sar_limit_t sar;
uint argc;
char *endptr;
/* arg count */
for (argc = 0; argv[argc]; argc++);
argc--;
if (argc != 0 && argc != sizeof(sar_limit_t)) {
printf("Error: Input %d SAR values, need total %d SAR values\n",
argc, (int)sizeof(sar_limit_t));
return BCME_USAGE_ERROR;
}
if (argc == 0) {
if ((ret = wlu_iovar_get(wl, cmd->name, &sar, sizeof(sar_limit_t))) < 0) {
return (ret);
}
printf("\t2G: %4d %4d %4d %4d\n",
sar.band2g[0], sar.band2g[1], sar.band2g[2], sar.band2g[3]);
for (i = 0; i < WLC_SUBBAND_MAX; i++) {
printf("\t5G[%1d] %4d %4d %4d %4d\n", i,
sar.band5g[i][0], sar.band5g[i][1],
sar.band5g[i][2], sar.band5g[i][3]);
}
} else {
uint8 *ptr = (uint8 *)&sar;
memset(ptr, WLC_TXPWR_MAX, sizeof(sar_limit_t));
for (i = 0; i < argc; i++) {
ptr[i] = (uint8)(strtol(argv[1 + i], &endptr, 0));
if (*endptr != '\0') {
printf("error\n");
return BCME_USAGE_ERROR;
}
}
printf("\t2G: %4d %4d %4d %4d\n",
sar.band2g[0], sar.band2g[1], sar.band2g[2], sar.band2g[3]);
for (i = 0; i < WLC_SUBBAND_MAX; i++) {
printf("\t5G[%1d] %4d %4d %4d %4d\n", i,
sar.band5g[i][0], sar.band5g[i][1],
sar.band5g[i][2], sar.band5g[i][3]);
}
ret = wlu_iovar_set(wl, cmd->name, &sar, sizeof(sar_limit_t));
}
return ret;
}
static int
wl_bmon_bssid(void *wl, cmd_t *cmd, char **argv)
{
uint argc;
uint8 params[ETHER_ADDR_LEN + 1];
argv ++;
/* arg count */
argc = ARGCNT(argv);
if (argc < 2)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], (struct ether_addr *)&params[0]))
return BCME_USAGE_ERROR;
params[ETHER_ADDR_LEN] = (uint8)strtoul(argv[1], NULL, 0);
return wlu_iovar_set(wl, cmd->name, params, sizeof(params));
}
static int
wl_phy_pavars(void *wl, cmd_t *cmd, char **argv)
{
const pavars_t *pav = pavars;
uint16 inpa[WL_PHY_PAVARS_LEN];
char *cpar = NULL, *p = NULL;
char *par;
char delimit[2] = " \0";
int err = 0;
unsigned int val, val2[16];
void *ptr = NULL;
int paparambwver = 0;
const char *iovar = "nvram_dump";
void *p1 = NULL;
if ((err = wlu_var_getbuf(wl, iovar, NULL, 0, &p1)) < 0) {
if ((err = wlu_get(wl, WLC_NVRAM_DUMP, &buf[0], WLC_IOCTL_MAXLEN)) < 0)
return err;
p1 = (void *)buf;
}
if ((p1 = strstr(p1, "paparambwver"))) {
char *q = NULL;
p1 = (void*)((char*)p1 + 13);
paparambwver = strtoul(p1, &q, 10);
}
if (paparambwver == 1)
pav = pavars_bwver_1;
else if (paparambwver == 2)
pav = pavars_bwver_2;
else
pav = pavars;
if (*++argv) { /* set */
while (pav->phy_type != PHY_TYPE_NULL) {
bool found = FALSE;
int i = 0;
inpa[i++] = pav->phy_type;
inpa[i++] = pav->bandrange;
inpa[i++] = pav->chain;
par = malloc(strlen(pav->vars)+1);
if (!par)
return BCME_NOMEM;
strcpy(par, pav->vars);
cpar = strtok (par, delimit); /* current param */
if (pav->phy_type == PHY_TYPE_AC) {
int pnum = 0, n;
if (pav->bandrange == WL_CHAN_FREQ_RANGE_2G)
pnum = 3;
else if (pav->bandrange == WL_CHAN_FREQ_RANGE_5G_4BAND)
pnum = 12;
/* Find the parameter in the input argument list */
if ((p = find_pattern2(argv, cpar, val2, pnum))) {
found = TRUE;
for (n = 0; n < pnum; n ++)
inpa[i + n] = (uint16)val2[n];
}
} else {
do {
val = 0;
/* Find the parameter in the input argument list */
if ((p = find_pattern(argv, cpar, &val))) {
found = TRUE;
inpa[i] = (uint16)val;
} else
inpa[i] = 0;
i++;
} while ((cpar = strtok (NULL, delimit)));
}
free(par);
if (found) {
if ((err = wlu_var_setbuf(wl, cmd->name, inpa,
WL_PHY_PAVARS_LEN * sizeof(uint16))) < 0) {
printf("wl_phy_pavars: fail to set\n");
return err;
}
}
pav++;
}
} else { /* get */
while (pav->phy_type != PHY_TYPE_NULL) {
int i = 0;
uint16 *outpa;
inpa[i++] = pav->phy_type;
inpa[i++] = pav->bandrange;
inpa[i++] = pav->chain;
par = malloc(strlen(pav->vars)+1);
if (!par)
return BCME_NOMEM;
strcpy(par, pav->vars);
if ((err = wlu_var_getbuf_sm(wl, cmd->name, inpa,
WL_PHY_PAVARS_LEN * sizeof(uint16), &ptr)) < 0) {
printf("phy %x band %x chain %d err %d\n", pav->phy_type,
pav->chain, pav->bandrange, err);
free(par);
break;
}
outpa = (uint16*)ptr;
if (outpa[0] == PHY_TYPE_NULL) {
pav++;
free(par);
continue;
}
cpar = strtok(par, delimit); /* current param */
if (pav->phy_type == PHY_TYPE_AC) {
int pnum = 0, n;
if (pav->bandrange == WL_CHAN_FREQ_RANGE_2G)
pnum = 3;
else if (pav->bandrange == WL_CHAN_FREQ_RANGE_5G_4BAND)
pnum = 12;
printf("%s=", cpar);
for (n = 0; n < pnum; n ++) {
if (n != 0)
printf(",");
printf("0x%x", outpa[i + n]);
}
printf("\n");
} else {
do {
printf("%s=0x%x\n", cpar, outpa[i++]);
} while ((cpar = strtok (NULL, delimit)));
}
pav++;
free(par);
}
}
return err;
}
static int
wl_phy_povars(void *wl, cmd_t *cmd, char **argv)
{
const povars_t *pov = povars;
wl_po_t inpo;
char *cpar = NULL, *p = NULL;
char *par; /* holds longest povars->vars */
char delimit[2] = " \0";
int err = 0;
uint val;
void *ptr = NULL;
if (*++argv) { /* set */
while (pov->phy_type != PHY_TYPE_NULL) {
bool found = FALSE;
int i = 0;
inpo.phy_type = pov->phy_type;
inpo.band = pov->bandrange;
par = malloc(strlen(pov->vars)+1);
if (!par)
return BCME_NOMEM;
strcpy(par, pov->vars);
/* Take care of cck and ofdm before walking through povars->vars */
if (pov->bandrange == WL_CHAN_FREQ_RANGE_2G) {
p = find_pattern(argv, "cck2gpo", &val);
if (p) found = TRUE;
inpo.cckpo = p ? (uint16)val : 0;
p = find_pattern(argv, "ofdm2gpo", &val);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GL) {
p = find_pattern(argv, "ofdm5glpo", &val);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GM) {
p = find_pattern(argv, "ofdm5gpo", &val);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GH) {
p = find_pattern(argv, "ofdm5ghpo", &val);
}
inpo.ofdmpo = p ? (uint32)val : 0;
if (p) found = TRUE;
cpar = strtok (par, delimit); /* current param */
do {
val = 0;
/* Find the parameter in the input argument list */
p = find_pattern(argv, cpar, &val);
if (p) found = TRUE;
inpo.mcspo[i] = p ? (uint16)val : 0;
i++;
} while ((cpar = strtok (NULL, delimit)));
if (found) {
if ((err = wlu_var_setbuf(wl, cmd->name, &inpo,
sizeof(wl_po_t))) < 0) {
printf("wl_phy_povars: fail to set\n");
free(par);
return err;
}
}
pov++;
free(par);
}
} else { /* get */
while (pov->phy_type != PHY_TYPE_NULL) {
int i = 0;
wl_po_t *outpo;
inpo.phy_type = pov->phy_type;
inpo.band = pov->bandrange;
par = malloc(strlen(pov->vars)+1);
if (!par)
return BCME_NOMEM;
strcpy(par, pov->vars);
if ((err = wlu_var_getbuf(wl, cmd->name, &inpo, sizeof(povars_t),
&ptr)) < 0) {
printf("phy %x band %x err %d\n", pov->phy_type,
pov->bandrange, err);
free(par);
break;
}
outpo = (wl_po_t*)ptr;
if (outpo->phy_type == PHY_TYPE_NULL) {
pov++;
free(par);
continue;
}
/* Take care of cck and ofdm before walking through povars->vars */
if (outpo->band == WL_CHAN_FREQ_RANGE_2G) {
printf("cck2gpo=0x%x\n", outpo->cckpo);
printf("ofdm2gpo=0x%x\n", outpo->ofdmpo);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GL) {
printf("ofdm5glpo=0x%x\n", outpo->ofdmpo);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GM) {
printf("ofdm5gpo=0x%x\n", outpo->ofdmpo);
} else if (pov->bandrange == WL_CHAN_FREQ_RANGE_5GH) {
printf("ofdm5ghpo=0x%x\n", outpo->ofdmpo);
}
cpar = strtok(par, delimit); /* current param */
do {
printf("%s=0x%x\n", cpar, outpo->mcspo[i++]);
} while ((cpar = strtok (NULL, delimit)));
pov++;
free(par);
}
}
return err;
}
static int
wl_phy_rpcalvars(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, k;
unsigned int val;
wl_rpcal_t rpcal[WL_NUM_RPCALVARS], *rpcal_out;
void *ptr = NULL;
if (*++argv) { /* set */
bool found = FALSE;
/* initialization */
memset(&(rpcal[0]), 0, sizeof(wl_rpcal_t)*WL_NUM_RPCALVARS);
if (find_pattern(argv, "rpcal2g", &val)) {
found = TRUE;
rpcal[WL_CHAN_FREQ_RANGE_2G].value = (uint16) val;
rpcal[WL_CHAN_FREQ_RANGE_2G].update = 1;
}
if (find_pattern(argv, "rpcal5gb0", &val)) {
found = TRUE;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND0].value = (uint16) val;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND0].update = 1;
}
if (find_pattern(argv, "rpcal5gb1", &val)) {
found = TRUE;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND1].value = (uint16) val;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND1].update = 1;
}
if (find_pattern(argv, "rpcal5gb2", &val)) {
found = TRUE;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND2].value = (uint16) val;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND2].update = 1;
}
if (find_pattern(argv, "rpcal5gb3", &val)) {
found = TRUE;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND3].value = (uint16) val;
rpcal[WL_CHAN_FREQ_RANGE_5G_BAND3].update = 1;
}
if (found) {
err = wlu_var_setbuf(wl, cmd->name, &(rpcal[0]),
sizeof(wl_rpcal_t)*WL_NUM_RPCALVARS);
if (err < 0) {
printf("wl_phy_rpcalvars: fail to set\n");
return err;
}
} else {
printf("wl_phy_rpcalvars: fail to found matching rpcalvar name\n");
return err;
}
} else { /* get */
err = wlu_var_getbuf(wl, cmd->name, &(rpcal[0]),
sizeof(wl_rpcal_t)*WL_NUM_RPCALVARS, &ptr);
if (err < 0) {
printf("wl_phy_rpcalvars: fail to get\n");
return err;
} else {
rpcal_out = (wl_rpcal_t*) ptr;
}
for (k = 0; k < WL_NUM_RPCALVARS; k++) {
switch (k) {
case WL_CHAN_FREQ_RANGE_2G:
printf("rpcal2g=0x%x ", rpcal_out[k].value);
break;
case WL_CHAN_FREQ_RANGE_5G_BAND0:
printf("rpcal5gb0=0x%x ", rpcal_out[k].value);
break;
case WL_CHAN_FREQ_RANGE_5G_BAND1:
printf("rpcal5gb1=0x%x ", rpcal_out[k].value);
break;
case WL_CHAN_FREQ_RANGE_5G_BAND2:
printf("rpcal5gb2=0x%x ", rpcal_out[k].value);
break;
case WL_CHAN_FREQ_RANGE_5G_BAND3:
printf("rpcal5gb3=0x%x\n", rpcal_out[k].value);
break;
}
}
}
return 0;
}
static int
wl_phy_fem(void *wl, cmd_t *cmd, char **argv)
{
srom_fem_t fem;
srom_fem_t *rfem;
void *ptr;
bool found = FALSE;
int err = 0;
uint val;
UNUSED_PARAMETER(cmd);
if (*++argv) { /* write fem */
/* fem2g */
memset(&fem, 0, sizeof(srom_fem_t));
if (find_pattern(argv, "tssipos2g", &val)) {
found = TRUE;
fem.tssipos = val;
}
if (find_pattern(argv, "extpagain2g", &val)) {
found = TRUE;
fem.extpagain = val;
}
if (find_pattern(argv, "pdetrange2g", &val)) {
found = TRUE;
fem.pdetrange = val;
}
if (find_pattern(argv, "triso2g", &val)) {
found = TRUE;
fem.triso = val;
}
if (find_pattern(argv, "antswctl2g", &val)) {
found = TRUE;
fem.antswctrllut = val;
}
if (found) {
if ((err = wlu_var_setbuf(wl, "fem2g", &fem, sizeof(srom_fem_t)) < 0))
printf("wl_phy_fem: fail to set fem2g\n");
else
printf("fem2g set\n");
}
found = FALSE;
/* fem5g */
memset(&fem, 0, sizeof(srom_fem_t));
if (find_pattern(argv, "tssipos5g", &val)) {
found = TRUE;
fem.tssipos = val;
}
if (find_pattern(argv, "extpagain5g", &val)) {
found = TRUE;
fem.extpagain = val;
}
if (find_pattern(argv, "pdetrange5g", &val)) {
found = TRUE;
fem.pdetrange = val;
}
if (find_pattern(argv, "triso5g", &val)) {
found = TRUE;
fem.triso = val;
}
if (find_pattern(argv, "antswctl5g", &val)) {
found = TRUE;
fem.antswctrllut = val;
}
if (found) {
if ((err = wlu_var_setbuf(wl, "fem5g", &fem, sizeof(srom_fem_t)) < 0))
printf("wl_phy_fem: fail to set fem5g\n");
else
printf("fem5g set\n");
}
} else {
if ((err = wlu_var_getbuf(wl, "fem2g", NULL, 0, (void**)&ptr) < 0)) {
printf("wl_phy_fem: fail to get fem2g\n");
} else {
rfem = (srom_fem_t*)ptr; /* skip the "fem2g" */
printf("tssipos2g=0x%x extpagain2g=0x%x pdetrange2g=0x%x"
" triso2g=0x%x antswctl2g=0x%x\n",
rfem->tssipos, rfem->extpagain, rfem->pdetrange,
rfem->triso, rfem->antswctrllut);
}
if ((err = wlu_var_getbuf(wl, "fem5g", NULL, 0, (void**)&ptr) < 0)) {
printf("wl_phy_fem: fail to get fem5g\n");
} else {
rfem = (srom_fem_t*)ptr; /* skip the "fem2g" */
printf("tssipos5g=0x%x extpagain5g=0x%x pdetrange5g=0x%x"
" triso5g=0x%x antswctl5g=0x%x\n",
rfem->tssipos, rfem->extpagain, rfem->pdetrange,
rfem->triso, rfem->antswctrllut);
}
}
return err;
}
static int
wl_phy_maxpower(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
uint val;
uint8 maxp[8];
void *ptr;
uint8 *rmaxp;
UNUSED_PARAMETER(cmd);
if (*++argv) { /* write maxpower */
if (find_pattern(argv, "maxp2ga0", &val))
maxp[0] = val;
else
printf("Missing maxp2ga0\n");
if (find_pattern(argv, "maxp2ga1", &val))
maxp[1] = val;
else
printf("Missing maxp2ga1\n");
if (find_pattern(argv, "maxp5ga0", &val))
maxp[2] = val;
else
printf("Missing maxp5ga0\n");
if (find_pattern(argv, "maxp5ga1", &val))
maxp[3] = val;
else
printf("Missing maxp5ga1\n");
if (find_pattern(argv, "maxp5gla0", &val))
maxp[4] = val;
else
printf("Missing maxp5gla0\n");
if (find_pattern(argv, "maxp5gla1", &val))
maxp[5] = val;
else
printf("Missing maxp5gla1\n");
if (find_pattern(argv, "maxp5gha0", &val))
maxp[6] = val;
else
printf("Missing maxp5gha0\n");
if (find_pattern(argv, "maxp5gha1", &val))
maxp[7] = val;
else
printf("Missing maxp5gha1\n");
if ((err = wlu_var_setbuf(wl, "maxpower", &maxp, 8 * sizeof(uint8)) < 0)) {
printf("wl_phy_maxpower: fail to set\n");
}
} else {
if ((err = wlu_var_getbuf(wl, "maxpower", NULL, 0, &ptr) < 0)) {
printf("wl_phy_maxpower: fail to get maxpower\n");
return err;
}
rmaxp = (uint8*)ptr;
printf("maxp2ga0=%x\n", rmaxp[0]);
printf("maxp2ga1=%x\n", rmaxp[1]);
printf("maxp5ga0=%x\n", rmaxp[2]);
printf("maxp5ga1=%x\n", rmaxp[3]);
printf("maxp5gla0=%x\n", rmaxp[4]);
printf("maxp5gla1=%x\n", rmaxp[5]);
printf("maxp5gha0=%x\n", rmaxp[6]);
printf("maxp5gha1=%x\n", rmaxp[7]);
}
return err;
}
static int
wl_antgain(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
uint val;
uint8 ag[2];
uint8 *rag;
void *ptr;
UNUSED_PARAMETER(cmd);
if (*++argv) { /* write maxpower */
if (find_pattern(argv, "ag0", &val))
ag[0] = val & 0xff;
else {
printf("Missing ag0\n");
return BCME_USAGE_ERROR;
}
if (find_pattern(argv, "ag1", &val))
ag[1] = val & 0xff;
else {
printf("Missing ag1\n");
return BCME_USAGE_ERROR;
}
if ((err = wlu_var_setbuf(wl, "antgain", &ag, 2 * sizeof(uint8)) < 0)) {
printf("wl_antgain: fail to set\n");
}
} else {
if ((err = wlu_var_getbuf(wl, "antgain", NULL, 0, &ptr) < 0)) {
printf("wl_antgain: fail to get antgain\n");
return err;
}
rag = (uint8*)ptr;
printf("ag0=%x\n", rag[0]);
printf("ag1=%x\n", rag[1]);
}
return err;
}
static int
wl_pkteng(void *wl, cmd_t *cmd, char **argv)
{
wl_pkteng_t pkteng;
memset(&pkteng, 0, sizeof(pkteng));
if (strcmp(cmd->name, "pkteng_stop") == 0) {
if (!*++argv)
return BCME_USAGE_ERROR;
if (strcmp(*argv, "tx") == 0)
pkteng.flags = WL_PKTENG_PER_TX_STOP;
else if (strcmp(*argv, "rx") == 0)
pkteng.flags = WL_PKTENG_PER_RX_STOP;
else
return BCME_USAGE_ERROR;
}
else if (strcmp(cmd->name, "pkteng_start") == 0) {
if (!*++argv)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(*argv, (struct ether_addr *)&pkteng.dest))
return BCME_USAGE_ERROR;
if (!*++argv)
return BCME_USAGE_ERROR;
if ((strcmp(*argv, "tx") == 0) || (strcmp(*argv, "txwithack") == 0)) {
if (strcmp(*argv, "tx") == 0)
pkteng.flags = WL_PKTENG_PER_TX_START;
else
pkteng.flags = WL_PKTENG_PER_TX_WITH_ACK_START;
if (!*++argv)
return BCME_USAGE_ERROR;
if (strcmp(*argv, "async") == 0)
pkteng.flags &= ~WL_PKTENG_SYNCHRONOUS;
else if (strcmp(*argv, "sync") == 0)
pkteng.flags |= WL_PKTENG_SYNCHRONOUS;
else
/* neither optional parameter [async|sync] */
--argv;
if (!*++argv)
return BCME_USAGE_ERROR;
pkteng.delay = strtoul(*argv, NULL, 0);
if (!*++argv)
return BCME_USAGE_ERROR;
pkteng.length = strtoul(*argv, NULL, 0);
if (!*++argv)
return BCME_USAGE_ERROR;
pkteng.nframes = strtoul(*argv, NULL, 0);
if (*++argv)
if (!wl_ether_atoe(*argv, (struct ether_addr *)&pkteng.src))
return BCME_USAGE_ERROR;
}
else if ((strcmp(*argv, "rx") == 0) || (strcmp(*argv, "rxwithack") == 0)) {
if ((strcmp(*argv, "rx") == 0))
pkteng.flags = WL_PKTENG_PER_RX_START;
else
pkteng.flags = WL_PKTENG_PER_RX_WITH_ACK_START;
if (*++argv) {
if (strcmp(*argv, "async") == 0)
pkteng.flags &= ~WL_PKTENG_SYNCHRONOUS;
else if (strcmp(*argv, "sync") == 0) {
pkteng.flags |= WL_PKTENG_SYNCHRONOUS;
/* sync mode requires number of frames and timeout */
if (!*++argv)
return BCME_USAGE_ERROR;
pkteng.nframes = strtoul(*argv, NULL, 0);
if (!*++argv)
return BCME_USAGE_ERROR;
pkteng.delay = strtoul(*argv, NULL, 0);
}
}
}
else
return BCME_USAGE_ERROR;
}
else {
printf("Invalid command name %s\n", cmd->name);
return 0;
}
pkteng.flags = htod32(pkteng.flags);
pkteng.delay = htod32(pkteng.delay);
pkteng.nframes = htod32(pkteng.nframes);
pkteng.length = htod32(pkteng.length);
return (wlu_var_setbuf(wl, "pkteng", &pkteng, sizeof(pkteng)));
}
static int
wl_rxiq(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_rxiqest";
int err, argc, opt_err;
uint32 rxiq;
uint8 resolution = 0;
uint8 lpf_hpc = 1;
uint8 dig_lpf = 1;
uint8 gain_correct = 0;
uint8 extra_gain_3dBsteps = 0;
uint8 force_gain_type = 0;
uint8 antenna = 3;
/* arg count */
for (argc = 0; argv[argc]; argc++);
/* DEFAULT:
* gain_correct = 0 (disable gain correction),
* lpf_hpc = 1 (sets lpf hpc to lowest value),
* dig_lpf = 1; (sets to ltrn_lpf mode)
* resolution = 0 (coarse),
* samples = 1024 (2^10) and antenna = 3
* force_gain_type = 0 (init gain mode)
*/
rxiq = (extra_gain_3dBsteps << 28) | (gain_correct << 24) | (dig_lpf << 22)
| (lpf_hpc << 20) | (resolution << 16) | (10 << 8) | (force_gain_type << 4)
| antenna;
if (argc != 0) {
miniopt_init(&to, fn_name, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'g') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for gain-correction (0, 1, 2, 3, 4, 7, 8)\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 8)) {
fprintf(stderr, "%s: invalid gain-correction select %d"
" (0,1,2,3,4,7,8)\n", fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
gain_correct = to.val & 0xf;
rxiq = ((gain_correct << 24) | (rxiq & 0xf0ffffff));
}
if (to.opt == 'f') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for lpf-hpc override select (0, 1)\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 1)) {
fprintf(stderr, "%s: invalid lpf-hpc override select %d"
" (0,1)\n", fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
lpf_hpc = to.val & 0xf;
rxiq = ((lpf_hpc << 20) | (rxiq & 0xff0fffff));
}
if (to.opt == 'w') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for dig-lpf override select (0, 1 or 2)\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 2)) {
fprintf(stderr, "%s: invalid dig-lpf override select %d"
" (0,1,2)\n", fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
dig_lpf = to.val & 0x3;
rxiq = ((dig_lpf << 22) | (rxiq & 0xff3fffff));
}
if (to.opt == 'r') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for resolution (0, 1)\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 1)) {
fprintf(stderr, "%s: invalid resolution select %d"
" (0,1)\n", fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
resolution = to.val & 0xf;
rxiq = ((resolution << 16) | (rxiq & 0xfff0ffff));
}
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for"
" the sample count\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val < 0 || to.val > 16) {
fprintf(stderr, "%s: sample count too large %d"
"(10 <= x <= 16)\n", fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
rxiq = (((to.val & 0xff) << 8) | (rxiq & 0xffff00ff));
}
if (to.opt == 'a') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for antenna (0, 1, 3)\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 3)) {
fprintf(stderr, "%s: invalid antenna select %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
rxiq = ((rxiq & 0xffffff00) | (to.val & 0xff));
}
if (to.opt == 'e') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for extra INITgain\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) || (to.val > 24) || (to.val % 3 != 0)) {
fprintf(stderr,
"%s: Valid extra INITgain = {0, 3, .., 21, 24}\n",
fn_name);
err = BCME_BADARG;
goto exit;
}
rxiq = ((((to.val/3) & 0xf) << 28) | (rxiq & 0x0fffffff));
}
if (to.opt == 'i') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int"
" for init or clipLO mode\n", fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 0) && (to.val != 1) &&
(to.val != 2) && (to.val != 3) && (to.val != 4)) {
fprintf(stderr,
"%s: Valid options - 0(default gain), 1(fixed high gain)"
"or 4(fixed low gain). \n",
fn_name);
err = BCME_BADARG;
goto exit;
}
rxiq = ((rxiq & 0xffffff0f) | ((to.val << 4) & 0xf0));
}
}
}
if ((err = wlu_iovar_setint(wl, cmd->name, (int)rxiq)) < 0)
return err;
if ((err = wlu_iovar_getint(wl, cmd->name, (int*)&rxiq)) < 0)
return err;
if (resolution == 1) {
/* fine resolution power reporting (0.25dB resolution) */
uint8 core;
int16 tmp;
if (rxiq >> 20) {
/* Three chains: */
for (core = 0; core < 3; core ++) {
tmp = (rxiq >> (10*core)) & 0x3ff;
tmp = ((int16)(tmp << 6)) >> 6; /* sign extension */
if (tmp < 0) {
tmp = -1*tmp;
printf("-%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
} else {
printf("%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
}
}
printf("\n");
} else if (rxiq >> 10) {
/* 2 chains */
for (core = 0; core < 2; core ++) {
tmp = (rxiq >> (10*core)) & 0x3ff;
tmp = ((int16)(tmp << 6)) >> 6; /* sign extension */
if (tmp < 0) {
tmp = -1*tmp;
printf("-%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
} else {
printf("%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
}
}
printf("\n");
} else {
/* 1 chain */
tmp = rxiq & 0x3ff;
tmp = ((int16)(tmp << 6)) >> 6; /* sign extension */
if (tmp < 0) {
tmp = -1*tmp;
printf("-%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
} else {
printf("%d.%ddBm ", (tmp >> 2), (tmp & 0x3)*25);
}
printf("\n");
}
} else {
if (rxiq >> 16)
printf("%ddBm %ddBm %ddBm\n", (int8)(rxiq & 0xff),
(int8)((rxiq >> 8) & 0xff), (int8)((rxiq >> 16) & 0xff));
else if (rxiq >> 8)
printf("%ddBm %ddBm\n", (int8)(rxiq & 0xff), (int8)((rxiq >> 8) & 0xff));
else
printf("%ddBm\n", (int8)(rxiq & 0xff));
}
exit:
return err;
}
/* Convert user's input in hex pattern to byte-size mask */
static int
wl_pattern_atoh(char *src, char *dst)
{
int i;
if (strncmp(src, "0x", 2) != 0 &&
strncmp(src, "0X", 2) != 0) {
printf("Data invalid format. Needs to start with 0x\n");
return -1;
}
src = src + 2; /* Skip past 0x */
if (strlen(src) % 2 != 0) {
printf("Data invalid format. Needs to be of even length\n");
return -1;
}
for (i = 0; *src != '\0'; i++) {
char num[3];
strncpy(num, src, 2);
num[2] = '\0';
dst[i] = (uint8)strtoul(num, NULL, 16);
src += 2;
}
return i;
}
static int
wl_wowl_status(void *wl, cmd_t *cmd, char **argv)
{
int flags_prev = 0;
int err;
UNUSED_PARAMETER(cmd);
argv++;
if ((err = wlu_iovar_getint(wl, "wowl_status", &flags_prev)))
return err;
printf("Status of last wakeup:\n");
printf("\tflags:0x%x\n", flags_prev);
if (flags_prev & WL_WOWL_BCN)
printf("\t\tWake-on-Loss-of-Beacons enabled\n");
if (flags_prev & WL_WOWL_MAGIC)
printf("\t\tWake-on-Magic frame enabled\n");
if (flags_prev & WL_WOWL_NET)
printf("\t\tWake-on-Net pattern enabled\n");
if (flags_prev & WL_WOWL_DIS)
printf("\t\tWake-on-Deauth enabled\n");
if (flags_prev & WL_WOWL_RETR)
printf("\t\tRetrograde TSF enabled\n");
if (flags_prev & WL_WOWL_TST)
printf("\t\tTest-mode enabled\n");
printf("\n");
return 0;
}
static int
wl_wowl_wakeind(void *wl, cmd_t *cmd, char **argv)
{
wl_wowl_wakeind_t wake = {0, 0};
int err;
UNUSED_PARAMETER(cmd);
argv++;
if (*argv) {
if (strcmp(*argv, "clear"))
return BCME_USAGE_ERROR;
err = wlu_iovar_set(wl, "wowl_wakeind", *argv, strlen(*argv));
return err;
}
if ((err = wlu_iovar_get(wl, "wowl_wakeind", &wake, sizeof(wl_wowl_wakeind_t))) < 0)
return err;
if (wake.pci_wakeind)
printf("PCI Indication set\n");
if (wake.ucode_wakeind != 0) {
printf("MAC Indication set\n");
if ((wake.ucode_wakeind & WL_WOWL_MAGIC) == WL_WOWL_MAGIC)
printf("\tMAGIC packet received\n");
if ((wake.ucode_wakeind & WL_WOWL_NET) == WL_WOWL_NET)
printf("\tPacket received with Netpattern\n");
if ((wake.ucode_wakeind & WL_WOWL_DIS) == WL_WOWL_DIS)
printf("\tDisassociation/Deauth received\n");
if ((wake.ucode_wakeind & WL_WOWL_RETR) == WL_WOWL_RETR)
printf("\tRetrograde TSF detected\n");
if ((wake.ucode_wakeind & WL_WOWL_BCN) == WL_WOWL_BCN)
printf("\tBeacons Lost\n");
if ((wake.ucode_wakeind & WL_WOWL_TST) == WL_WOWL_TST)
printf("\tTest Mode\n");
if ((wake.ucode_wakeind & WL_WOWL_M1) == WL_WOWL_M1)
printf("\tPTK Refresh received.\n");
if ((wake.ucode_wakeind & WL_WOWL_EAPID) == WL_WOWL_EAPID)
printf("\tEAP-Identity request received\n");
if ((wake.ucode_wakeind & WL_WOWL_GTK_FAILURE) == WL_WOWL_GTK_FAILURE)
printf("\tWake on GTK failure.\n");
if ((wake.ucode_wakeind & WL_WOWL_EXTMAGPAT) == WL_WOWL_EXTMAGPAT)
printf("\tExtended Magic Packet received.\n");
if ((wake.ucode_wakeind & WL_WOWL_KEYROT) == WL_WOWL_KEYROT)
printf("\tKey Rotation Packet received.\n");
if ((wake.ucode_wakeind & WL_WOWL_SCANOL) == WL_WOWL_SCANOL)
printf("\tWake on Scan offload.\n");
if ((wake.ucode_wakeind & WL_WOWL_MDNS_CONFLICT) == WL_WOWL_MDNS_CONFLICT)
printf("\tWake on MDNS Conflict.\n");
if ((wake.ucode_wakeind & WL_WOWL_MDNS_SERVICE) == WL_WOWL_MDNS_SERVICE)
printf("\tWake on MDNS Service.\n");
if ((wake.ucode_wakeind & WL_WOWL_TCPKEEP_DATA) == WL_WOWL_TCPKEEP_DATA)
printf("\tWake on TCP Keepalive Data.\n");
if ((wake.ucode_wakeind & WL_WOWL_TCPKEEP_TIME) == WL_WOWL_TCPKEEP_TIME)
printf("\tWake on TCP Keepalive Timeout.\n");
if ((wake.ucode_wakeind & WL_WOWL_FW_HALT) == WL_WOWL_FW_HALT)
printf("\tWake on Firmware died in wowl mode.\n");
if ((wake.ucode_wakeind & (WL_WOWL_NET | WL_WOWL_MAGIC | WL_WOWL_EXTMAGPAT))) {
if ((wake.ucode_wakeind & WL_WOWL_BCAST) == WL_WOWL_BCAST)
printf("\t\tBroadcast/Mcast frame received\n");
else
printf("\t\tUnicast frame received\n");
}
}
if (!wake.pci_wakeind && wake.ucode_wakeind == 0)
printf("No wakeup indication set\n");
return 0;
}
/* Used by NINTENDO2 */
static int
wl_wowl_wake_reason(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
wl_wr_t wr;
UNUSED_PARAMETER(cmd);
if (!*++argv) {
err = wlu_iovar_get(wl, "wakeup_reason", &wr, sizeof(wl_wr_t));
if (err)
return err;
if (wr.reason && wr.reason < REASON_LAST) {
printf("ID: %d\t", wr.id);
if (wr.reason == LCD_ON)
printf("Reason: LCD_ON\n");
else if (wr.reason == LCD_OFF)
printf("Reason: LCD_OFF\n");
else if (wr.reason == DRC1_WAKE)
printf("Reason: DRC1_WAKE\n");
else if (wr.reason == DRC2_WAKE)
printf("Reason: DRC2_WAKE\n");
}
else
printf("Unknown wakeup Reason\n");
}
return err;
}
/* Send a wakeup frame to sta in WAKE mode */
static int
wl_wowl_pkt(void *wl, cmd_t *cmd, char **argv)
{
char *arg = buf;
const char *str;
char *dst;
uint tot = 0;
uint16 type, pkt_len;
int dst_ea = 0; /* 0 == manual, 1 == bcast, 2 == ucast */
char *ea[ETHER_ADDR_LEN];
if (!*++argv)
return BCME_USAGE_ERROR;
UNUSED_PARAMETER(cmd);
str = "wowl_pkt";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
dst = arg + strlen(str) + 1;
tot += strlen(str) + 1;
pkt_len = (uint16)htod32(strtoul(*argv, NULL, 0));
*((uint16*)dst) = pkt_len;
dst += sizeof(pkt_len);
tot += sizeof(pkt_len);
if (!*++argv) {
printf("Dest of the packet needs to be provided\n");
return BCME_USAGE_ERROR;
}
/* Dest of the frame */
if (!strcmp(*argv, "bcast")) {
dst_ea = 1;
if (!wl_ether_atoe("ff:ff:ff:ff:ff:ff", (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
} else if (!strcmp(*argv, "ucast")) {
dst_ea = 2;
if (!*++argv) {
printf("EA of ucast dest of the packet needs to be provided\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
/* Store it */
memcpy(ea, dst, ETHER_ADDR_LEN);
} else if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
dst += ETHER_ADDR_LEN;
tot += ETHER_ADDR_LEN;
if (!*++argv) {
printf("type - magic/net needs to be provided\n");
return BCME_USAGE_ERROR;
}
if (strncmp(*argv, "magic", strlen("magic")) == 0)
type = WL_WOWL_MAGIC;
else if (strncmp(*argv, "net", strlen("net")) == 0)
type = WL_WOWL_NET;
else if (strncmp(*argv, "eapid", strlen("eapid")) == 0)
type = WL_WOWL_EAPID;
else
return BCME_USAGE_ERROR;
*((uint16*)dst) = type;
dst += sizeof(type);
tot += sizeof(type);
if (type == WL_WOWL_MAGIC) {
if (pkt_len < MAGIC_PKT_MINLEN)
return BCME_BADARG;
if (dst_ea == 2)
memcpy(dst, ea, ETHER_ADDR_LEN);
else {
if (!*++argv)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
}
tot += ETHER_ADDR_LEN;
} else if (type == WL_WOWL_NET) {
wl_wowl_pattern_t *wl_pattern;
wl_pattern = (wl_wowl_pattern_t *)dst;
if (!*++argv) {
printf("Starting offset not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->offset = (uint)htod32(strtoul(*argv, NULL, 0));
wl_pattern->masksize = 0;
wl_pattern->patternoffset = (uint)htod32(sizeof(wl_wowl_pattern_t));
dst += sizeof(wl_wowl_pattern_t);
if (!*++argv) {
printf("pattern not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->patternsize =
(uint)htod32(wl_pattern_atoh((char *)(uintptr)*argv, dst));
dst += wl_pattern->patternsize;
tot += sizeof(wl_wowl_pattern_t) + wl_pattern->patternsize;
wl_pattern->reasonsize = 0;
if (*++argv) {
wl_pattern->reasonsize =
(uint)htod32(wl_pattern_atoh((char *)(uintptr)*argv, dst));
tot += wl_pattern->reasonsize;
}
} else { /* eapid */
if (!*++argv) {
printf("EAPOL identity string not provided\n");
return BCME_USAGE_ERROR;
}
*dst++ = strlen(*argv);
strncpy(dst, *argv, strlen(*argv));
tot += 1 + strlen(*argv);
}
return (wlu_set(wl, WLC_SET_VAR, arg, tot));
}
static int
wl_wowl_pattern(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint i, j;
uint8 *ptr;
wl_wowl_pattern_t *wl_pattern;
UNUSED_PARAMETER(cmd);
if (*++argv) {
char *arg = buf;
const char *str;
char *dst;
uint tot = 0;
if (strcmp(*argv, "add") != 0 && strcmp(*argv, "del") != 0 &&
strcmp(*argv, "clr") != 0) {
return BCME_USAGE_ERROR;
}
str = "wowl_pattern";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
dst = arg + strlen(str) + 1;
tot += strlen(str) + 1;
str = *argv;
strncpy(dst, str, strlen(str));
tot += strlen(str) + 1;
if (strcmp(str, "clr") != 0) {
wl_pattern = (wl_wowl_pattern_t *)(dst + strlen(str) + 1);
dst = (char*)wl_pattern + sizeof(wl_wowl_pattern_t);
if (!*++argv) {
printf("Starting offset not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->offset = htod32(strtoul(*argv, NULL, 0));
if (!*++argv) {
printf("Mask not provided\n");
return BCME_USAGE_ERROR;
}
/* Parse the mask */
str = *argv;
wl_pattern->masksize = htod32(wl_pattern_atoh((char *)(uintptr)str, dst));
if (wl_pattern->masksize == (uint)-1)
return BCME_USAGE_ERROR;
dst += wl_pattern->masksize;
wl_pattern->patternoffset = htod32((sizeof(wl_wowl_pattern_t) +
wl_pattern->masksize));
if (!*++argv) {
printf("Pattern value not provided\n");
return BCME_USAGE_ERROR;
}
/* Parse the value */
str = *argv;
wl_pattern->patternsize =
htod32(wl_pattern_atoh((char *)(uintptr)str, dst));
if (wl_pattern->patternsize == (uint)-1)
return BCME_USAGE_ERROR;
tot += sizeof(wl_wowl_pattern_t) + wl_pattern->patternsize +
wl_pattern->masksize;
}
return (wlu_set(wl, WLC_SET_VAR, arg, tot));
} else {
wl_wowl_pattern_list_t *list;
if ((err = wlu_iovar_get(wl, "wowl_pattern", buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
list = (wl_wowl_pattern_list_t *)buf;
printf("#of patterns :%d\n", list->count);
ptr = (uint8 *)list->pattern;
for (i = 0; i < list->count; i++) {
uint8 *pattern;
wl_pattern = (wl_wowl_pattern_t *)ptr;
printf("Pattern %d:\n", i+1);
printf("ID :0x%x\n"
"Offset :%d\n"
"Masksize :%d\n"
"Mask :0x",
(uint32)wl_pattern->id, wl_pattern->offset, wl_pattern->masksize);
pattern = ((uint8 *)wl_pattern + sizeof(wl_wowl_pattern_t));
for (j = 0; j < wl_pattern->masksize; j++)
printf("%02x", pattern[j]);
printf("\n"
"PatternSize:%d\n"
"Pattern :0x", wl_pattern->patternsize);
/* Go to end to find pattern */
pattern = ((uint8*)wl_pattern + wl_pattern->patternoffset);
for (j = 0; j < wl_pattern->patternsize; j++)
printf("%02x", pattern[j]);
printf("\n\n");
ptr += (wl_pattern->masksize + wl_pattern->patternsize +
sizeof(wl_wowl_pattern_t));
}
}
return err;
}
static int
wl_wowl_extended_magic(void *wl, cmd_t *cmd, char **argv)
{
char *arg = buf;
const char *str;
char *dst;
uint tot;
int ret;
str = "wowl_ext_magic";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
if (*++argv) {
dst = arg + strlen(str) + 1;
tot = strlen(str) + 1;
ret = wl_pattern_atoh(*argv, dst);
if (ret == -1)
return BCME_USAGE_ERROR;
if (ret != 6) {
printf("Extended magic pattern must be 6-byte length\n");
return BCME_USAGE_ERROR;
}
tot += 6;
ret = wlu_set(wl, cmd->set, arg, tot);
return ret;
}
if ((ret = wlu_get(wl, cmd->get, arg, WLC_IOCTL_MAXLEN)) < 0)
return ret;
printf("0x");
for (ret = 0; ret < 6; ret++)
printf("%02x", (uint8)arg[ret]);
printf("\n");
return 0;
}
static int
wl_rifs(void *wl, cmd_t *cmd, char **argv)
{
int err;
int val, rifs;
char *val_name;
UNUSED_PARAMETER(cmd);
/* command name */
val_name = *argv++;
if (!*argv) {
if ((err = wlu_iovar_getint(wl, val_name, (int*)&rifs)) < 0)
return err;
printf("%s\n", ((rifs & 0xff) ? "On" : "Off"));
return 0;
}
val = rifs = (atoi(*argv) ? 1 : 0);
if (rifs != 0 && rifs != 1)
return BCME_USAGE_ERROR;
if ((err = wlu_set(wl, WLC_SET_FAKEFRAG, &val, sizeof(int))) < 0) {
printf("Set frameburst error %d\n", err);
return err;
}
if ((err = wlu_iovar_setint(wl, val_name, (int)rifs)) < 0)
printf("Set rifs error %d\n", err);
return err;
}
static int
wl_rifs_advert(void *wl, cmd_t *cmd, char **argv)
{
int err;
int rifs_advert;
char *val_name;
BCM_REFERENCE(cmd);
/* command name */
val_name = *argv++;
if (!*argv) {
if ((err = wlu_iovar_getint(wl, val_name, (int*)&rifs_advert)) < 0)
return err;
printf("%s\n", ((rifs_advert & 0xff) ? "On" : "Off"));
return 0;
}
if (strcmp(*argv, "-1") && strcmp(*argv, "0"))
return BCME_USAGE_ERROR;
rifs_advert = atoi(*argv);
if ((err = wlu_iovar_setint(wl, val_name, (int)rifs_advert)) < 0)
printf("Set rifs mode advertisement error %d\n", err);
return err;
}
/* this is the batched command packet size. now for remoteWL, we set it to 512 bytes */
#define MEMBLOCK (512 - 32) /* allow 32 bytes for overhead (header, alignment, etc) */
int wl_seq_batch_in_client(bool enable)
{
batch_in_client = enable;
return 0;
}
int
wl_seq_start(void *wl, cmd_t *cmd, char **argv)
{
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
if (!batch_in_client) {
return wlu_iovar_setbuf(wl, "seq_start", NULL, 0, buf, WLC_IOCTL_MAXLEN);
}
else {
if (cmd_batching_mode) {
printf("calling seq_start() when it's already in batching mode\n");
clean_up_cmd_list();
cmd_batching_mode = FALSE;
return BCME_USAGE_ERROR;
}
else {
cmd_batching_mode = TRUE;
cmd_pkt_list_num = 0;
cmd_list.head = NULL;
cmd_list.tail = NULL;
}
}
return 0;
}
int
wl_seq_stop(void *wl, cmd_t *cmd, char **argv)
{
char *bufp;
int ret = 0;
int seq_list_len;
int len;
wl_seq_cmd_pkt_t *next_cmd;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
if (!batch_in_client) {
return wlu_iovar_setbuf(wl, "seq_stop", NULL, 0, buf, WLC_IOCTL_MAXLEN);
}
else {
if (!cmd_batching_mode) {
printf("calling seq_stop when it's already out of batching mode\n");
return BCME_USAGE_ERROR;
}
cmd_batching_mode = FALSE;
next_cmd = cmd_list.head;
/* dump batched commands to the DUT */
if (next_cmd == NULL) {
printf("no command batched\n");
return BCME_USAGE_ERROR;
}
ret = wlu_iovar_setbuf(wl, "seq_start", NULL, 0, buf, WLC_IOCTL_MAXLEN);
if (ret) {
printf("failed to send seq_start\n");
goto fail;
}
while (next_cmd != NULL) {
bufp = buf;
memset(bufp, 0, WLC_IOCTL_MAXLEN);
strcpy(bufp, "seq_list");
bufp += (strlen("seq_list") + 1);
bufp = ALIGN_ADDR(bufp, WL_SEQ_CMD_ALIGN_BYTES);
seq_list_len = bufp - buf;
while ((seq_list_len < MEMBLOCK) && (next_cmd != NULL)) {
len = ROUNDUP(next_cmd->cmd_header.len, WL_SEQ_CMD_ALIGN_BYTES);
len += (seq_list_len + sizeof(wl_seq_cmd_ioctl_t));
if (len < MEMBLOCK) {
memcpy(bufp, &(next_cmd->cmd_header),
sizeof(wl_seq_cmd_ioctl_t));
bufp += sizeof(wl_seq_cmd_ioctl_t);
memcpy(bufp, next_cmd->data, next_cmd->cmd_header.len);
bufp += next_cmd->cmd_header.len;
bufp = ALIGN_ADDR(bufp, WL_SEQ_CMD_ALIGN_BYTES);
seq_list_len = len;
next_cmd = next_cmd->next;
}
else
break;
}
ret = wl_set(wl, WLC_SET_VAR, &buf[0], seq_list_len);
if (ret) {
printf("failed to send seq_list\n");
goto fail;
}
}
ret = wlu_iovar_setbuf(wl, "seq_stop", NULL, 0, buf, WLC_IOCTL_MAXLEN);
if (ret) {
printf("failed to send seq_stop\n");
}
fail:
clean_up_cmd_list();
return ret;
}
}
static int
wl_obss_scan_params_range_chk(wl_obss_scan_arg_t *obss_scan_arg)
{
if (obss_scan_arg->passive_dwell < 0)
obss_scan_arg->passive_dwell = WLC_OBSS_SCAN_PASSIVE_DWELL_DEFAULT;
else if (obss_scan_arg->passive_dwell < WLC_OBSS_SCAN_PASSIVE_DWELL_MIN ||
obss_scan_arg->passive_dwell > WLC_OBSS_SCAN_PASSIVE_DWELL_MAX) {
printf("passive dwell not in range %d\n", obss_scan_arg->passive_dwell);
return -1;
}
if (obss_scan_arg->active_dwell < 0)
obss_scan_arg->active_dwell = WLC_OBSS_SCAN_ACTIVE_DWELL_DEFAULT;
else if (obss_scan_arg->active_dwell < WLC_OBSS_SCAN_ACTIVE_DWELL_MIN ||
obss_scan_arg->active_dwell > WLC_OBSS_SCAN_ACTIVE_DWELL_MAX) {
printf("active dwell not in range %d\n", obss_scan_arg->active_dwell);
return -1;
}
if (obss_scan_arg->bss_widthscan_interval < 0)
obss_scan_arg->bss_widthscan_interval =
WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_DEFAULT;
else if (obss_scan_arg->bss_widthscan_interval < WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MIN ||
obss_scan_arg->bss_widthscan_interval > WLC_OBSS_SCAN_WIDTHSCAN_INTERVAL_MAX) {
printf("Width Trigger Scan Interval not in range %d\n",
obss_scan_arg->bss_widthscan_interval);
return -1;
}
if (obss_scan_arg->chanwidth_transition_delay < 0)
obss_scan_arg->chanwidth_transition_delay =
WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_DEFAULT;
else if ((obss_scan_arg->chanwidth_transition_delay <
WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MIN) ||
(obss_scan_arg->chanwidth_transition_delay >
WLC_OBSS_SCAN_CHANWIDTH_TRANSITION_DLY_MAX)) {
printf("Width Channel Transition Delay Factor not in range %d\n",
obss_scan_arg->chanwidth_transition_delay);
return -1;
}
if (obss_scan_arg->passive_total < 0)
obss_scan_arg->passive_total = WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_DEFAULT;
else if (obss_scan_arg->passive_total < WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MIN ||
obss_scan_arg->passive_total > WLC_OBSS_SCAN_PASSIVE_TOTAL_PER_CHANNEL_MAX) {
printf("Passive Total per Channel not in range %d\n", obss_scan_arg->passive_total);
return -1;
}
if (obss_scan_arg->active_total < 0)
obss_scan_arg->active_total = WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_DEFAULT;
if (obss_scan_arg->active_total < WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MIN ||
obss_scan_arg->active_total > WLC_OBSS_SCAN_ACTIVE_TOTAL_PER_CHANNEL_MAX) {
printf("Active Total per Channel not in range %d\n", obss_scan_arg->active_total);
return -1;
}
if (obss_scan_arg->activity_threshold < 0)
obss_scan_arg->activity_threshold = WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_DEFAULT;
else if (obss_scan_arg->activity_threshold < WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MIN ||
obss_scan_arg->activity_threshold > WLC_OBSS_SCAN_ACTIVITY_THRESHOLD_MAX) {
printf("Activity Threshold not in range %d\n", obss_scan_arg->activity_threshold);
return -1;
}
return 0;
}
/* Send a periodic keep-alive packet at the specificed interval. */
static int
wl_keep_alive(void *wl, cmd_t *cmd, char **argv)
{
const char *str;
wl_keep_alive_pkt_t keep_alive_pkt;
wl_keep_alive_pkt_t *keep_alive_pktp;
int buf_len;
int str_len;
int i;
int rc;
void *ptr = NULL;
if (*++argv == NULL) {
/*
** Get current keep-alive status.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
keep_alive_pktp = (wl_keep_alive_pkt_t *) ptr;
printf("Period (msec) :%d\n"
"Length :%d\n"
"Packet :0x",
dtoh32(keep_alive_pktp->period_msec),
dtoh16(keep_alive_pktp->len_bytes));
for (i = 0; i < keep_alive_pktp->len_bytes; i++)
printf("%02x", keep_alive_pktp->data[i]);
printf("\n");
}
else {
/*
** Set keep-alive attributes.
*/
str = "keep_alive";
str_len = strlen(str);
strncpy(buf, str, str_len);
buf[ str_len ] = '\0';
keep_alive_pktp = (wl_keep_alive_pkt_t *) (buf + str_len + 1);
keep_alive_pkt.period_msec = htod32(strtoul(*argv, NULL, 0));
buf_len = str_len + 1;
if (keep_alive_pkt.period_msec == 0) {
keep_alive_pkt.len_bytes = 0;
buf_len += sizeof(wl_keep_alive_pkt_t);
}
else {
if (NULL != *++argv) {
keep_alive_pkt.len_bytes =
htod16(wl_pattern_atoh(*argv, (char *) keep_alive_pktp->data));
buf_len += (WL_KEEP_ALIVE_FIXED_LEN + keep_alive_pkt.len_bytes);
}
else {
keep_alive_pkt.len_bytes = 0;
buf_len += WL_KEEP_ALIVE_FIXED_LEN;
}
}
/* Keep-alive attributes are set in local variable (keep_alive_pkt), and
* then memcpy'ed into buffer (keep_alive_pktp) since there is no
* guarantee that the buffer is properly aligned.
*/
memcpy((char *)keep_alive_pktp, &keep_alive_pkt, WL_KEEP_ALIVE_FIXED_LEN);
rc = wlu_set(wl,
WLC_SET_VAR,
buf,
buf_len);
}
return (rc);
}
/* mkeep-alive : Send a periodic keep-alive packet or null-data at the specificed interval. */
/* wowl_keepalive : Send a periodic keep alive packet the specificed interval in wowl mode. */
static int
wl_mkeep_alive(void *wl, cmd_t *cmd, char **argv)
{
const char *str;
wl_mkeep_alive_pkt_t mkeep_alive_pkt;
wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
int buf_len;
int str_len;
int len_bytes;
int i;
int rc;
void *ptr = NULL;
memset(&mkeep_alive_pkt, 0, sizeof(wl_mkeep_alive_pkt_t));
str = *argv; /* mkeep_alive or wowl_keepalive */
if (*++argv == NULL) {
return BCME_USAGE_ERROR;
}
else {
/* read the packet index */
int mkeep_alive_id = htod32(strtoul(*argv, NULL, 0));
if (*++argv == NULL) {
/*
** Get current keep-alive status.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, &mkeep_alive_id,
sizeof(int), &ptr)) < 0)
return rc;
mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) ptr;
printf("Id :%d\n"
"Period (msec) :%d\n"
"Length :%d\n"
"Packet :0x",
mkeep_alive_pktp->keep_alive_id,
dtoh32(mkeep_alive_pktp->period_msec),
dtoh16(mkeep_alive_pktp->len_bytes));
for (i = 0; i < mkeep_alive_pktp->len_bytes; i++)
printf("%02x", mkeep_alive_pktp->data[i]);
printf("\n");
return rc;
}
str_len = strlen(str);
strncpy(buf, str, str_len);
buf[ str_len ] = '\0';
mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1);
mkeep_alive_pkt.period_msec = htod32(strtoul(*argv, NULL, 0));
buf_len = str_len + 1;
mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION);
mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
mkeep_alive_pkt.keep_alive_id = mkeep_alive_id;
len_bytes = 0;
buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
if (mkeep_alive_pkt.period_msec != 0) {
if (NULL != *++argv) {
len_bytes = wl_pattern_atoh(*argv, (char *) mkeep_alive_pktp->data);
buf_len += len_bytes;
}
}
mkeep_alive_pkt.len_bytes = htod16(len_bytes);
/* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and
* then memcpy'ed into buffer (mkeep_alive_pktp) since there is no
* guarantee that the buffer is properly aligned.
*/
memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN);
rc = wlu_set(wl,
WLC_SET_VAR,
buf,
buf_len);
}
return (rc);
}
/* Enable/disable installed packet filter. */
static int
wl_pkt_filter_enable(void *wl, cmd_t *cmd, char **argv)
{
wl_pkt_filter_enable_t enable_parm;
int rc;
if (*++argv == NULL) {
printf("No args provided\n");
return BCME_USAGE_ERROR;
}
/* Parse packet filter id. */
enable_parm.id = htod32(strtoul(*argv, NULL, 0));
if (*++argv == NULL) {
printf("Enable/disable value not provided\n");
return BCME_USAGE_ERROR;
}
/* Parse enable/disable value. */
enable_parm.enable = htod32(strtoul(*argv, NULL, 0));
/* Enable/disable the specified filter. */
rc = wlu_var_setbuf(wl,
cmd->name,
&enable_parm,
sizeof(wl_pkt_filter_enable_t));
return (rc);
}
/* Install a new packet filter. */
static int
wl_pkt_filter_add(void *wl, cmd_t *cmd, char **argv)
{
const char *str;
wl_pkt_filter_t pkt_filter;
wl_pkt_filter_t *pkt_filterp;
int buf_len;
int str_len;
int rc;
uint32 mask_size;
uint32 pattern_size;
UNUSED_PARAMETER(cmd);
str = "pkt_filter_add";
str_len = strlen(str);
strncpy(buf, str, str_len);
buf[ str_len ] = '\0';
buf_len = str_len + 1;
pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
/* Parse packet filter id. */
if (*++argv == NULL) {
printf("No args provided\n");
return BCME_USAGE_ERROR;
}
pkt_filter.id = htod32(strtoul(*argv, NULL, 0));
/* Parse filter polarity. */
if (*++argv == NULL) {
printf("Polarity not provided\n");
return BCME_USAGE_ERROR;
}
pkt_filter.negate_match = htod32(strtoul(*argv, NULL, 0));
/* Parse filter type. */
if (*++argv == NULL) {
printf("Filter type not provided\n");
return BCME_USAGE_ERROR;
}
pkt_filter.type = htod32(strtoul(*argv, NULL, 0));
if ((pkt_filter.type != WL_PKT_FILTER_TYPE_PATTERN_MATCH) &&
(pkt_filter.type != WL_PKT_FILTER_TYPE_PATTERN_MATCH_TIMEOUT)) {
printf("Invalid filter type %d\n", pkt_filter.type);
return BCME_USAGE_ERROR;
}
/* Handle basic pattern filter */
if (pkt_filter.type == WL_PKT_FILTER_TYPE_PATTERN_MATCH) {
wl_pkt_filter_pattern_t *pfilter = &pkt_filterp->u.pattern;
/* Parse pattern filter offset. */
if (*++argv == NULL) {
printf("Offset not provided\n");
return BCME_USAGE_ERROR;
}
pkt_filter.u.pattern.offset = htod32(strtoul(*argv, NULL, 0));
/* Parse pattern filter mask. */
if (*++argv == NULL) {
printf("Bitmask not provided\n");
return BCME_USAGE_ERROR;
}
rc = wl_pattern_atoh(*argv, (char *)pfilter->mask_and_pattern);
if (rc <= 0) {
printf("Invalid mask provided: %s\n", *argv);
return BCME_USAGE_ERROR;
}
mask_size = htod32(rc);
/* Parse pattern filter pattern. */
if (*++argv == NULL) {
printf("Pattern not provided\n");
return BCME_USAGE_ERROR;
}
rc = wl_pattern_atoh(*argv, (char *)&pfilter->mask_and_pattern[rc]);
if (rc <= 0) {
printf("Invalid pattern provided: %s\n", *argv);
return BCME_USAGE_ERROR;
}
pattern_size = htod32(rc);
if (mask_size != pattern_size) {
printf("Mask and pattern not the same size\n");
return BCME_USAGE_ERROR;
}
pkt_filter.u.pattern.size_bytes = mask_size;
buf_len += WL_PKT_FILTER_FIXED_LEN;
buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * rc);
/* The fields that were put in a local for alignment purposes now
* get copied to the right place in the ioctl buffer.
*/
memcpy((char *)pkt_filterp, &pkt_filter,
WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
}
/* Handle pattern filter with timeout event */
if (pkt_filter.type == WL_PKT_FILTER_TYPE_PATTERN_MATCH_TIMEOUT) {
wl_pkt_filter_pattern_timeout_t *pfilter = &pkt_filterp->u.pattern_timeout;
/* Parse pattern filter offset. */
if (*++argv == NULL) {
printf("Offset not provided\n");
return BCME_USAGE_ERROR;
}
pkt_filter.u.pattern.offset = htod32(strtoul(*argv, NULL, 0));
/* Parse pattern filter mask. */
if (*++argv == NULL) {
printf("Bitmask not provided\n");
return BCME_USAGE_ERROR;
}
rc = wl_pattern_atoh(*argv, (char *)pfilter->mask_and_pattern);
if (rc <= 0) {
printf("Invalid mask provided: %s\n", *argv);
return BCME_USAGE_ERROR;
}
mask_size = htod32(rc);
/* Parse pattern filter pattern. */
if (*++argv == NULL) {
printf("Pattern not provided\n");
return BCME_USAGE_ERROR;
}
rc = wl_pattern_atoh(*argv, (char *)&pfilter->mask_and_pattern[rc]);
if (rc <= 0) {
printf("Rejecting: %s\n", *argv);
return BCME_USAGE_ERROR;
}
pattern_size = htod32(rc);
if (mask_size != pattern_size) {
printf("Mask and pattern not the same size\n");
return BCME_USAGE_ERROR;
}
if (*++argv == NULL) {
printf("timeout not specified\n");
return BCME_USAGE_ERROR;
}
pkt_filter.u.pattern_timeout.timeout = htod32(strtoul(*argv, NULL, 0));
pkt_filter.u.pattern.size_bytes = mask_size;
buf_len += WL_PKT_FILTER_FIXED_LEN;
buf_len += (WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN + 2 * rc);
/* The fields that were put in a local for alignment purposes now
* get copied to the right place in the ioctl buffer.
*/
memcpy((char *)pkt_filterp, &pkt_filter,
WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN);
}
rc = wlu_set(wl, WLC_SET_VAR, buf, buf_len);
return (rc);
}
static void
wl_pkt_filter_list_mask_pat(uint8 *bytes, uint size, char *indent)
{
uint j;
printf("%sMask :0x", indent);
for (j = 0; j < size; j++)
printf("%02x", bytes[j]);
printf("\n%sPattern :0x", indent);
for (; j < 2 * size; j++)
printf("%02x", bytes[j]);
printf("\n\n");
}
/* List installed packet filters. */
static int
wl_pkt_filter_list(void *wl, cmd_t *cmd, char **argv)
{
wl_pkt_filter_list_t *list;
wl_pkt_filter_t *filterp;
void *ptr = NULL;
unsigned int i;
int rc;
unsigned int filter_len;
uint32 enable;
if (*++argv == NULL) {
printf("No args provided\n");
return BCME_USAGE_ERROR;
}
/* Parse filter list to retrieve (enabled/disabled). */
enable = htod32(strtoul(*argv, NULL, 0));
/*
** Get list of installed packet filters.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, &enable, sizeof(enable), &ptr)) < 0)
return rc;
list = (wl_pkt_filter_list_t *) ptr;
printf("Num filters: %d\n\n", list->num);
filterp = list->filter;
for (i = 0; i < list->num; i++)
{
uint type = dtoh32(filterp->type);
if (type == WL_PKT_FILTER_TYPE_PATTERN_MATCH) {
printf("Id :%d\n"
"Negate :%d\n"
"Type :%d\n"
"Offset :%d\n"
"Pattern len :%d\n",
dtoh32(filterp->id),
dtoh32(filterp->negate_match),
dtoh32(filterp->type),
dtoh32(filterp->u.pattern.offset),
dtoh32(filterp->u.pattern.size_bytes));
wl_pkt_filter_list_mask_pat(filterp->u.pattern.mask_and_pattern,
dtoh32(filterp->u.pattern.size_bytes), "");
filter_len = WL_PKT_FILTER_PATTERN_FIXED_LEN +
2 * dtoh32(filterp->u.pattern.size_bytes);
} else if (type == WL_PKT_FILTER_TYPE_PATTERN_MATCH_TIMEOUT) {
printf("Id :%d\n"
"Negate :%d\n"
"Type :%d\n"
"Offset :%d\n"
"Pattern len :%d\n"
"Timeout :%d seconds\n",
dtoh32(filterp->id),
dtoh32(filterp->negate_match),
dtoh32(filterp->type),
dtoh32(filterp->u.pattern_timeout.offset),
dtoh32(filterp->u.pattern_timeout.size_bytes),
dtoh32(filterp->u.pattern_timeout.timeout));
wl_pkt_filter_list_mask_pat(filterp->u.pattern_timeout.mask_and_pattern,
dtoh32(filterp->u.pattern_timeout.size_bytes), "");
filter_len = WL_PKT_FILTER_PATTERN_TIMEOUT_FIXED_LEN +
2 * dtoh32(filterp->u.pattern_timeout.size_bytes);
}
filter_len += WL_PKT_FILTER_FIXED_LEN;
filterp = (wl_pkt_filter_t *) ((uint8 *)filterp + filter_len);
filterp = ALIGN_ADDR(filterp, sizeof(uint32));
}
return (rc);
}
/* Get packet filter debug statistics. */
static int
wl_pkt_filter_stats(void *wl, cmd_t *cmd, char **argv)
{
wl_pkt_filter_stats_t *stats;
uint32 id;
int rc;
void *ptr = NULL;
if (*++argv == NULL) {
printf("No args provided\n");
return BCME_USAGE_ERROR;
}
/* Parse filter id to retrieve. */
id = htod32(strtoul(*argv, NULL, 0));
/* Get debug stats. */
if ((rc = wlu_var_getbuf(wl, cmd->name, &id, sizeof(id), &ptr)) < 0)
return rc;
stats = (wl_pkt_filter_stats_t *) ptr;
printf("Packets matched for filter '%d': %d\n"
"Total packets discarded : %d\n"
"Total packet forwarded : %d\n",
id,
dtoh32(stats->num_pkts_matched),
dtoh32(stats->num_pkts_discarded),
dtoh32(stats->num_pkts_forwarded));
return (rc);
}
#ifdef TRAFFIC_MGMT
/* Get/set traffic management configuration. */
static int
wl_trf_mgmt_config(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
uint32 i;
trf_mgmt_config_t *ptrf_mgmt_config;
uint8 buf[sizeof(trf_mgmt_config_t)];
int buf_len;
char *endptr = NULL;
int rc = -1;
void *ptr = NULL;
if (!*++argv) {
/*
* Get current traffic management configuration.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
ptrf_mgmt_config = (trf_mgmt_config_t *)ptr;
printf("Enabled : %d\n",
dtoh32(ptrf_mgmt_config->trf_mgmt_enabled));
printf("Host IP Address : %s\n",
wl_iptoa((void *)&ptrf_mgmt_config->host_ip_addr));
printf("Host IP Subnet Mask : %s\n",
wl_iptoa((void *)&ptrf_mgmt_config->host_subnet_mask));
printf("Downlink Bandwidth : %d\n",
dtoh32(ptrf_mgmt_config->downlink_bandwidth));
printf("Uplink Bandwidth : %d\n",
dtoh32(ptrf_mgmt_config->uplink_bandwidth));
printf("\n");
printf("Minimum Tx Bandwidth[BK] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[0]));
printf("Minimum Tx Bandwidth[BE] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[1]));
printf("Minimum Tx Bandwidth[VI] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[2]));
printf("\n");
printf("Minimum Rx Bandwidth[BK] : %d\n",
dtoh32(ptrf_mgmt_config->min_rx_bandwidth[0]));
printf("Minimum Rx Bandwidth[BE] : %d\n",
dtoh32(ptrf_mgmt_config->min_rx_bandwidth[1]));
printf("Minimum Rx Bandwidth[VI] : %d\n",
dtoh32(ptrf_mgmt_config->min_rx_bandwidth[2]));
printf("\n");
printf("Flags : 0x%04X\n",
dtoh32(ptrf_mgmt_config->flags));
}
else {
/* arg count */
while (argv[argc])
argc++;
/* required arguments */
if ((argc != 1) && (argc != 5) && (argc != 6)) {
fprintf(stderr, "Too few/many arguments (require 1 or 5 or 6 , got %d)\n", argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_config = (trf_mgmt_config_t *)buf;
buf_len = sizeof(trf_mgmt_config_t);
memset((uint8 *)buf, 0, buf_len);
ptrf_mgmt_config->trf_mgmt_enabled = htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0') {
return BCME_USAGE_ERROR;
}
if (argc > 1) {
if (ptrf_mgmt_config->trf_mgmt_enabled) {
if (!wl_atoip(*argv++, (void *)&ptrf_mgmt_config->host_ip_addr)) {
return BCME_USAGE_ERROR;
}
if (!wl_atoip(*argv++, (void *)&ptrf_mgmt_config->host_subnet_mask)) {
return BCME_USAGE_ERROR;
}
ptrf_mgmt_config->downlink_bandwidth =
htod32((int32)strtol(*argv++, &endptr, 0));
ptrf_mgmt_config->uplink_bandwidth =
htod32((int32)strtol(*argv++, &endptr, 0));
/*
* Zero-fill min bandwidth based. This will cause the driver to use
* defult settings
*/
for (i = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
ptrf_mgmt_config->min_tx_bandwidth[i] = 0;
ptrf_mgmt_config->min_rx_bandwidth[i] = 0;
}
if (argc == 6) {
ptrf_mgmt_config->flags = htod32((int32)strtol(*argv++, &endptr, 0));
}
} else {
return BCME_BADARG;
}
}
rc = wlu_var_setbuf(wl, cmd->name, ptrf_mgmt_config, buf_len);
}
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Sets a traffic management filter. */
static int
wl_trf_mgmt_filters_add(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
trf_mgmt_filter_list_t *ptrf_mgmt_filter_list;
trf_mgmt_filter_t *ptrf_mgmt_filter;
uint8 buf[sizeof(trf_mgmt_filter_list_t)];
int buf_len;
char *param;
char *endptr = NULL;
int rc = -1;
(void)param;
/* arg count */
param = *++argv;
while (argv[argc])
argc++;
/* required arguments */
if (argc != 4) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 4, argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buf;
buf_len = sizeof(trf_mgmt_filter_list_t);
memset((uint8 *)buf, 0, buf_len);
ptrf_mgmt_filter_list->num_filters = 1;
ptrf_mgmt_filter = &ptrf_mgmt_filter_list->filter[0];
ptrf_mgmt_filter->dst_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->src_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->prot = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->priority = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
rc = wlu_var_setbuf(wl, cmd->name, ptrf_mgmt_filter_list, buf_len);
return rc;
}
/* Sets a traffic management filter L2/L3/L4 */
static int
wl_trf_mgmt_filters_addex(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
trf_mgmt_filter_list_t *ptrf_mgmt_filter_list;
trf_mgmt_filter_t *ptrf_mgmt_filter;
uint8 buf[sizeof(trf_mgmt_filter_list_t)];
int buf_len;
char *param;
char *endptr = NULL;
int rc = -1;
(void)param;
(void)cmd;
/* arg count */
param = *++argv;
while (argv[argc])
argc++;
/* required arguments */
if (argc < 3) {
fprintf(stderr, "Too few arguments (require > 3 got %d)\n", argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buf;
buf_len = sizeof(trf_mgmt_filter_list_t);
memset((uint8 *)buf, 0, buf_len);
ptrf_mgmt_filter_list->num_filters = 1;
ptrf_mgmt_filter = &ptrf_mgmt_filter_list->filter[0];
ptrf_mgmt_filter->flags = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (ptrf_mgmt_filter->flags & TRF_FILTER_MAC_ADDR) {
if (argc != 3) {
fprintf(stderr, "Too many arguments (require 3 got %d)\n", argc);
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv++, &ptrf_mgmt_filter->dst_ether_addr))
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->priority = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
} else {
/* required arguments */
if (argc != 5) {
fprintf(stderr, "Too few/many arguments (require 5 got %d)\n", argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter->dst_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->src_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->prot = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->priority = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
rc = wlu_var_setbuf(wl, "trf_mgmt_filters_add", ptrf_mgmt_filter_list, buf_len);
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Removes a traffic management filter. */
static int
wl_trf_mgmt_filters_remove(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
trf_mgmt_filter_list_t *ptrf_mgmt_filter_list;
trf_mgmt_filter_t *ptrf_mgmt_filter;
uint8 buf[sizeof(trf_mgmt_filter_list_t)];
int buf_len;
char *endptr = NULL;
char *param;
int rc = -1;
(void)param;
/* arg count */
param = *++argv;
while (argv[argc])
argc++;
/* required arguments */
if (argc != 3) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 3, argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buf;
buf_len = sizeof(trf_mgmt_filter_list_t);
memset((uint8 *)buf, 0, buf_len);
ptrf_mgmt_filter_list->num_filters = 1;
ptrf_mgmt_filter = &ptrf_mgmt_filter_list->filter[0];
ptrf_mgmt_filter->dst_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->src_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->prot = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
rc = wlu_var_setbuf(wl, cmd->name, ptrf_mgmt_filter_list, buf_len);
return rc;
}
/* Removes a traffic management filter for L2/L3/L4 */
static int
wl_trf_mgmt_filters_removeex(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
trf_mgmt_filter_list_t *ptrf_mgmt_filter_list;
trf_mgmt_filter_t *ptrf_mgmt_filter;
uint8 buf[sizeof(trf_mgmt_filter_list_t)];
int buf_len;
char *endptr = NULL;
char *param;
int rc = -1;
(void)param;
(void)cmd;
/* arg count */
param = *++argv;
while (argv[argc])
argc++;
/* required arguments */
if (argc < 2) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 2, argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buf;
buf_len = sizeof(trf_mgmt_filter_list_t);
memset((uint8 *)buf, 0, buf_len);
ptrf_mgmt_filter_list->num_filters = 1;
ptrf_mgmt_filter = &ptrf_mgmt_filter_list->filter[0];
ptrf_mgmt_filter->flags = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0') {
return BCME_USAGE_ERROR;
}
if (ptrf_mgmt_filter->flags & TRF_FILTER_MAC_ADDR) {
if (argc != 2) {
fprintf(stderr, "Too many arguments (require 2 got %d)\n", argc);
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv++, &ptrf_mgmt_filter->dst_ether_addr)) {
return BCME_USAGE_ERROR;
}
} else {
if (argc < 4) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 4, argc);
return BCME_USAGE_ERROR;
}
ptrf_mgmt_filter->dst_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->src_port = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_filter->prot = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
rc = wlu_var_setbuf(wl, "trf_mgmt_filters_remove", ptrf_mgmt_filter_list, buf_len);
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* lists the current traffic management filters. */
static int
wl_trf_mgmt_filters_list(void *wl, cmd_t *cmd, char **argv)
{
trf_mgmt_filter_list_t *ptrf_mgmt_filter_list;
trf_mgmt_filter_t *ptrf_mgmt_filter;
uint i;
int rc = -1;
void *ptr = NULL;
UNUSED_PARAMETER(argv);
/*
* Get current traffic management filters.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
ptrf_mgmt_filter_list = (trf_mgmt_filter_list_t *)ptr;
printf("Number of filters : %d\n", dtoh32(ptrf_mgmt_filter_list->num_filters));
for (i = 0; i < ptrf_mgmt_filter_list->num_filters; i++) {
ptrf_mgmt_filter = &ptrf_mgmt_filter_list->filter[i];
if (ptrf_mgmt_filter->flags & TRF_FILTER_MAC_ADDR) {
printf("\n");
printf("Filter #%d\n", i);
printf("Flags : 0x%02x\n", dtoh32(ptrf_mgmt_filter->flags));
printf("Dst EtherAddr : %s\n",
wl_ether_etoa(&ptrf_mgmt_filter->dst_ether_addr));
printf("Priority : %d\n", dtoh32(ptrf_mgmt_filter->priority));
} else {
printf("\n");
printf("Filter #%d\n", i);
printf("Dst Port : %d\n", dtoh32(ptrf_mgmt_filter->dst_port));
printf("Src Port : %d\n", dtoh32(ptrf_mgmt_filter->src_port));
printf("Protocol : %d\n", dtoh32(ptrf_mgmt_filter->prot));
printf("Flags : 0x%02x\n", dtoh32(ptrf_mgmt_filter->flags));
printf("Priority : %d\n", dtoh32(ptrf_mgmt_filter->priority));
}
}
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Clears the traffic management filters. */
static int
wl_trf_mgmt_filters_clear(void *wl, cmd_t *cmd, char **argv)
{
int rc = -1;
UNUSED_PARAMETER(argv);
rc = wlu_var_setbuf(wl, cmd->name, NULL, 0);
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/*
* Get/set traffic management bandwidth configuration. We support the ability to get/set just the
* bandwidth parameters in the global trf_mgmt_config_t structure.
*/
static int
wl_trf_mgmt_bandwidth(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
trf_mgmt_config_t *ptrf_mgmt_config;
uint8 buf[sizeof(trf_mgmt_config_t)];
int buf_len;
char *endptr = NULL;
int i, total_bandwidth;
int rc = -1;
void *ptr = NULL;
if (!*++argv) {
/*
* Get current traffic management bandwidth settings.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
ptrf_mgmt_config = (trf_mgmt_config_t *)ptr;
printf("Downlink Bandwidth : %d\n",
dtoh32(ptrf_mgmt_config->downlink_bandwidth));
printf("Uplink Bandwidth : %d\n",
dtoh32(ptrf_mgmt_config->uplink_bandwidth));
printf("\n");
printf("Minimum Tx Bandwidth[BK] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[0]));
printf("Minimum Tx Bandwidth[BE] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[1]));
printf("Minimum Tx Bandwidth[VI] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[2]));
printf("\n");
printf("Minimum Rx Bandwidth[BK] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[0]));
printf("Minimum Rx Bandwidth[BE] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[1]));
printf("Minimum Rx Bandwidth[VI] : %d\n",
dtoh32(ptrf_mgmt_config->min_tx_bandwidth[2]));
}
else {
/* arg count */
while (argv[argc])
argc++;
/* required arguments */
if (argc < 5) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 5, argc);
return BCME_USAGE_ERROR;
}
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
ptrf_mgmt_config = (trf_mgmt_config_t *)buf;
buf_len = sizeof(trf_mgmt_config_t);
memcpy(buf, ptr, buf_len);
ptrf_mgmt_config->downlink_bandwidth = htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
ptrf_mgmt_config->uplink_bandwidth = htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
for (i = 0, total_bandwidth = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
ptrf_mgmt_config->min_tx_bandwidth[i] =
htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
total_bandwidth += ptrf_mgmt_config->min_tx_bandwidth[i];
}
if (total_bandwidth != 100) {
fprintf(stderr,
"Sum of gauranteed bandwidth levels must equal 100 (got %d)\n",
total_bandwidth);
return BCME_BADARG;
}
if (argc > 5) {
for (i = 0, total_bandwidth = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
ptrf_mgmt_config->min_rx_bandwidth[i] =
htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
total_bandwidth += ptrf_mgmt_config->min_rx_bandwidth[i];
}
if (total_bandwidth != 100) {
fprintf(stderr,
"Sum of gauranteed rx bandwidth levels must equal 100 (got %d)\n",
total_bandwidth);
return BCME_BADARG;
}
} else {
for (i = 0, total_bandwidth = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
ptrf_mgmt_config->min_rx_bandwidth[i] = ptrf_mgmt_config->min_tx_bandwidth[i];
}
}
rc = wlu_var_setbuf(wl, cmd->name, ptrf_mgmt_config, buf_len);
}
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/*
* Get/set traffic management operational flags. We use this to change flags that
* can't be set by GUI. This allows us to configure certain options that we may want to
* enable/disable in the shipping product.
*/
static int
wl_trf_mgmt_flags(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
uint32 flags;
char *endptr = NULL;
int rc = -1;
void *ptr = NULL;
if (!*++argv) {
/*
* Get current traffic management bandwidth settings.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
flags = *(uint32 *)ptr;
printf("Flags : 0x%04X\n", flags);
}
else {
/* arg count */
while (argv[argc])
argc++;
/* required arguments */
if (argc != 1) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 1, argc);
return BCME_USAGE_ERROR;
}
flags = htod32((int32)strtol(*argv++, &endptr, 0));
if (*endptr != '\0') {
return BCME_USAGE_ERROR;
}
rc = wlu_var_setbuf(wl, cmd->name, &flags, sizeof(uint32));
}
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Print traffic management statistics. */
static void
wl_trf_mgmt_print_stats(void *ptr, uint index)
{
trf_mgmt_stats_array_t *ptrf_mgmt_statistics_array;
trf_mgmt_stats_t *ptrf_mgmt_statistics;
ptrf_mgmt_statistics_array = (trf_mgmt_stats_array_t *)ptr;
ptrf_mgmt_statistics = &ptrf_mgmt_statistics_array->tx_queue_stats[index];
printf("Statistics for Tx Queue[%d]\n", index);
printf("\n");
printf("Num. packets processed : %d\n",
dtoh32(ptrf_mgmt_statistics->num_processed_packets));
printf("Num. bytes processed : %d\n",
dtoh32(ptrf_mgmt_statistics->num_processed_bytes));
printf("Num. packets discarded : %d\n",
dtoh32(ptrf_mgmt_statistics->num_discarded_packets));
ptrf_mgmt_statistics = &ptrf_mgmt_statistics_array->rx_queue_stats[index];
printf("\n");
printf("Statistics for Rx Queue[%d]\n", index);
printf("\n");
printf("Num. packets processed : %d\n",
dtoh32(ptrf_mgmt_statistics->num_processed_packets));
printf("Num. bytes processed : %d\n",
dtoh32(ptrf_mgmt_statistics->num_processed_bytes));
printf("Num. packets discarded : %d\n",
dtoh32(ptrf_mgmt_statistics->num_discarded_packets));
printf("\n");
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Get traffic management statistics. */
static int
wl_trf_mgmt_stats(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
uint i;
int rc = -1;
char *endptr = NULL;
void *ptr = NULL;
/*
* Get current traffic management statistics.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
if (!*++argv) {
/*
* Print all of the current traffic management statistics.
*/
for (i = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
wl_trf_mgmt_print_stats(ptr, i);
}
}
else {
/* arg count */
while (argv[argc])
argc++;
/* required arguments */
if (argc != 1) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 1, argc);
return BCME_USAGE_ERROR;
}
i = htod16((int16)strtol(*argv, &endptr, 0));
if (i >= TRF_MGMT_MAX_PRIORITIES) {
fprintf(stderr, "Index must be < %d)\n", TRF_MGMT_MAX_PRIORITIES);
return BCME_BADARG;
}
/* Print the current traffic management statistics for the specified queue index. */
wl_trf_mgmt_print_stats(ptr, i);
}
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Clears the traffic management statistics. */
static int
wl_trf_mgmt_stats_clear(void *wl, cmd_t *cmd, char **argv)
{
int rc = -1;
UNUSED_PARAMETER(argv);
rc = wlu_var_setbuf(wl, cmd->name, NULL, 0);
return rc;
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Print traffic management shaping info. */
static void
wl_trf_mgmt_print_global_shaping_info(void *ptr)
{
trf_mgmt_shaping_info_array_t *ptrf_mgmt_shaping_info_array;
trf_mgmt_global_info_t *ptrf_mgmt_global_info;
ptrf_mgmt_shaping_info_array = (trf_mgmt_shaping_info_array_t *)ptr;
ptrf_mgmt_global_info = &ptrf_mgmt_shaping_info_array->tx_global_shaping_info;
printf("Global shaping info. for Tx Queues\n");
printf("\n");
printf("Maximum bytes/second : %d\n",
ptrf_mgmt_global_info->maximum_bytes_per_second);
printf("Maximum bytes/sampling period : %d\n",
ptrf_mgmt_global_info->maximum_bytes_per_sampling_period);
printf("Total bytes consumed per second : %d\n",
ptrf_mgmt_global_info->total_bytes_consumed_per_second);
printf("Total bytes consumed per sampling period : %d\n",
ptrf_mgmt_global_info->total_bytes_consumed_per_sampling_period);
printf("Unused bytes for current sampling period : %d\n",
ptrf_mgmt_global_info->total_unused_bytes_per_sampling_period);
printf("\n");
ptrf_mgmt_global_info = &ptrf_mgmt_shaping_info_array->rx_global_shaping_info;
printf("Global shaping info. for Rx Queues\n");
printf("\n");
printf("Maximum bytes/second : %d\n",
ptrf_mgmt_global_info->maximum_bytes_per_second);
printf("Maximum bytes/sampling period : %d\n",
ptrf_mgmt_global_info->maximum_bytes_per_sampling_period);
printf("Total bytes consumed per second : %d\n",
ptrf_mgmt_global_info->total_bytes_consumed_per_second);
printf("Total bytes consumed per sampling period : %d\n",
ptrf_mgmt_global_info->total_bytes_consumed_per_sampling_period);
printf("Unused bytes for current sampling period : %d\n",
ptrf_mgmt_global_info->total_unused_bytes_per_sampling_period);
printf("\n");
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
static void
wl_trf_mgmt_print_shaping_info(void *ptr, uint index)
{
trf_mgmt_shaping_info_array_t *ptrf_mgmt_shaping_info_array;
trf_mgmt_shaping_info_t *ptrf_mgmt_shaping_info;
ptrf_mgmt_shaping_info_array = (trf_mgmt_shaping_info_array_t *)ptr;
ptrf_mgmt_shaping_info = &ptrf_mgmt_shaping_info_array->tx_queue_shaping_info[index];
printf("Shaping info. for Tx Queue[%d]\n", index);
printf("\n");
printf("Gauranteed bandwidth percentage : %d%%\n",
dtoh32(ptrf_mgmt_shaping_info->gauranteed_bandwidth_percentage));
printf("Guaranteed bytes/second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->guaranteed_bytes_per_second));
printf("Guaranteed bytes/sampling period : %d\n",
dtoh32(ptrf_mgmt_shaping_info->guaranteed_bytes_per_sampling_period));
printf("Num. bytes produced per second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_bytes_produced_per_second));
printf("Num. bytes consumed per second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_bytes_consumed_per_second));
printf("Num. packets pending : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_queued_packets));
printf("Num. bytes pending : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_queued_bytes));
ptrf_mgmt_shaping_info = &ptrf_mgmt_shaping_info_array->rx_queue_shaping_info[index];
printf("\n");
printf("Shaping info. for Rx Queue[%d]\n", index);
printf("\n");
printf("Gauranteed bandwidth percentage : %d%%\n",
dtoh32(ptrf_mgmt_shaping_info->gauranteed_bandwidth_percentage));
printf("Guaranteed bytes/second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->guaranteed_bytes_per_second));
printf("Guaranteed bytes/sampling period : %d\n",
dtoh32(ptrf_mgmt_shaping_info->guaranteed_bytes_per_sampling_period));
printf("Num. bytes produced per second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_bytes_produced_per_second));
printf("Num. bytes consumed per second : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_bytes_consumed_per_second));
printf("Num. packets pending : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_queued_packets));
printf("Num. bytes pending : %d\n",
dtoh32(ptrf_mgmt_shaping_info->num_queued_bytes));
printf("\n");
}
#endif /* TRAFFIC_MGMT */
#ifdef TRAFFIC_MGMT
/* Get traffic management shaping info. */
static int
wl_trf_mgmt_shaping_info(void *wl, cmd_t *cmd, char **argv)
{
uint argc = 0;
uint i;
int rc = -1;
char *endptr = NULL;
void *ptr = NULL;
/*
* Get current traffic management shaping info.
*/
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
if (!*++argv) {
/*
* Print all of the current traffic management shaping info.
*/
wl_trf_mgmt_print_global_shaping_info(ptr);
for (i = 0; i < TRF_MGMT_MAX_PRIORITIES; i++) {
wl_trf_mgmt_print_shaping_info(ptr, i);
}
}
else {
/* arg count */
while (argv[argc])
argc++;
/* required arguments */
if (argc != 1) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n", 1, argc);
return BCME_USAGE_ERROR;
}
i = htod16((int16)strtol(*argv, &endptr, 0));
if (i >= TRF_MGMT_MAX_PRIORITIES) {
fprintf(stderr, "Index must be < %d)\n", TRF_MGMT_MAX_PRIORITIES);
return BCME_BADARG;
}
/* Print the current traffic management shaping info for the specified queue index. */
wl_trf_mgmt_print_global_shaping_info(ptr);
wl_trf_mgmt_print_shaping_info(ptr, i);
}
return rc;
}
#endif /* TRAFFIC_MGMT */
static int
wl_obss_scan(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
wl_obss_scan_arg_t obss_scan_arg;
char *endptr = NULL;
uint buflen;
uint argc = 0;
if (!*++argv) {
void *ptr = NULL;
wl_obss_scan_arg_t *obss_scan_param;
err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr);
if (err < 0)
return err;
obss_scan_param = (wl_obss_scan_arg_t *)ptr;
printf("%d %d %d %d %d %d %d\n",
dtoh16(obss_scan_param->passive_dwell),
dtoh16(obss_scan_param->active_dwell),
dtoh16(obss_scan_param->bss_widthscan_interval),
dtoh16(obss_scan_param->passive_total),
dtoh16(obss_scan_param->active_total),
dtoh16(obss_scan_param->chanwidth_transition_delay),
dtoh16(obss_scan_param->activity_threshold));
return 0;
}
/* arg count */
while (argv[argc])
argc++;
buflen = WL_OBSS_SCAN_PARAM_LEN;
memset((uint8 *)&obss_scan_arg, 0, buflen);
/* required argments */
if (argc < WL_MIN_NUM_OBSS_SCAN_ARG) {
fprintf(stderr, "Too few/many arguments (require %d, got %d)\n",
WL_MIN_NUM_OBSS_SCAN_ARG, argc);
return BCME_USAGE_ERROR;
}
obss_scan_arg.passive_dwell = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.active_dwell = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.bss_widthscan_interval = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.passive_total = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.active_total = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.chanwidth_transition_delay = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
obss_scan_arg.activity_threshold = htod16((int16)strtol(*argv++, &endptr, 0));
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (wl_obss_scan_params_range_chk(&obss_scan_arg))
return BCME_RANGE;
err = wlu_var_setbuf(wl, cmd->name, &obss_scan_arg, buflen);
return err;
}
#ifdef RWL_WIFI
/* Function added to support RWL_WIFI Transport
* Used to find the remote server with proper mac address given by
* the user,this cmd is specific to RWL_WIFIi protocol
*/
static int wl_wifiserver(void *wl, cmd_t *cmd, char **argv)
{
int ret;
if ((ret = wlu_iovar_set(wl, cmd->name, *argv, strlen(*argv))) < 0) {
printf("Error finding the remote server %s\n", argv[0]);
return ret;
}
return ret;
}
#endif
static int
wl_obss_coex_action(void *wl, cmd_t *cmd, char **argv)
{
int err;
char var[256];
wl_action_obss_coex_req_t *req = (wl_action_obss_coex_req_t *)var;
int val;
int num = 0;
uint8 options = 0;
argv++;
memset(&var, 0, sizeof(wl_action_obss_coex_req_t));
while (*argv) {
if (!strncmp(*argv, "-i", 2) && ((options & 0x1) != 0x1)) {
argv++;
if (!*argv)
return BCME_USAGE_ERROR;
val = atoi(*argv);
if ((val != 0) && (val != 1))
return BCME_BADARG;
req->info |= val ? WL_COEX_40MHZ_INTOLERANT : 0;
options |= 0x1;
}
else if (!strncmp(*argv, "-w", 2) && ((options & 0x2) != 0x2)) {
argv++;
if (!*argv)
return BCME_USAGE_ERROR;
val = atoi(*argv);
if ((val != 0) && (val != 1))
return BCME_BADARG;
req->info |= val ? WL_COEX_WIDTH20 : 0;
options |= 0x2;
}
else if (!strncmp(*argv, "-c", 2) && ((options & 0x4) != 0x4)) {
argv++;
while (*argv) {
if (isdigit((unsigned char)(**argv))) {
val = htod32(strtoul(*argv, NULL, 0));
if ((val == 0) || (val > 14)) {
printf("Invalid channel %d\n", val);
return BCME_BADARG;
}
req->ch_list[num] = (uint8)val;
num++;
argv++;
if (num > 14) {
printf("Too many channels (max 14)\n");
return BCME_BADARG;
}
} else
break;
}
if (!num) {
printf("With option '-c' specified, a channel list is required\n");
return BCME_BADARG;
}
req->num = num;
options |= 0x4;
continue;
}
else
return BCME_USAGE_ERROR;
argv++;
}
if (!options)
return BCME_BADARG;
err = wlu_var_setbuf(wl, cmd->name, &var, (sizeof(wl_action_obss_coex_req_t) +
(req->num ? (req->num - 1) * sizeof(uint8) : 0)));
return err;
}
static int
wl_srchmem(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
struct args {
int reg;
uint32 ssidlen;
uint8 ssid[DOT11_MAX_SSID_LEN];
} x;
char *endptr;
uint argc;
char *iovar;
UNUSED_PARAMETER(cmd);
memset(&x, 0, sizeof(x));
/* save command name */
iovar = argv[0];
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* required arg: reg offset */
if (argc < 1)
return BCME_USAGE_ERROR;
x.reg = strtol(argv[0], &endptr, 0);
if (*endptr != '\0' || x.reg > 15)
return BCME_USAGE_ERROR;
if (argc > 2)
return BCME_USAGE_ERROR;
if (argc == 2) {
uint32 len;
len = strlen(argv[1]);
if (len > sizeof(x.ssid)) {
printf("ssid too long\n");
return BCME_BADARG;
}
memcpy(x.ssid, argv[1], len);
x.ssidlen = len;
}
/* issue the get or set ioctl */
if (argc == 1) {
x.reg = htod32(x.reg);
ret = wlu_iovar_getbuf(wl, iovar, &x, sizeof(x), buf, WLC_IOCTL_SMLEN);
if (ret < 0) {
printf("get returned error 0x%x\n", ret);
return (ret);
}
wl_hexdump((uchar *)buf, sizeof(x.ssidlen) + sizeof(x.ssid));
} else {
x.reg = htod32(x.reg);
x.ssidlen = htod32(x.ssidlen);
ret = wlu_iovar_set(wl, iovar, &x, sizeof(x));
if (ret < 0) {
printf("set returned error 0x%x\n", ret);
return (ret);
}
}
return (ret);
}
cntry_name_t cntry_names[] = {
{"AFGHANISTAN", "AF"},
{"ALBANIA", "AL"},
{"ALGERIA", "DZ"},
{"AMERICAN SAMOA", "AS"},
{"ANDORRA", "AD"},
{"ANGOLA", "AO"},
{"ANGUILLA", "AI"},
{"ANTARCTICA", "AQ"},
{"ANTIGUA AND BARBUDA", "AG"},
{"ARGENTINA", "AR"},
{"ARMENIA", "AM"},
{"ARUBA", "AW"},
{"ASCENSION ISLAND", "AC"},
{"AUSTRALIA", "AU"},
{"AUSTRIA", "AT"},
{"AZERBAIJAN", "AZ"},
{"BAHAMAS", "BS"},
{"BAHRAIN", "BH"},
{"BANGLADESH", "BD"},
{"BARBADOS", "BB"},
{"BELARUS", "BY"},
{"BELGIUM", "BE"},
{"BELIZE", "BZ"},
{"BENIN", "BJ"},
{"BERMUDA", "BM"},
{"BHUTAN", "BT"},
{"BOLIVIA", "BO"},
{"BOSNIA AND HERZEGOVINA", "BA"},
{"BOTSWANA", "BW"},
{"BOUVET ISLAND", "BV"},
{"BRAZIL", "BR"},
{"BRITISH INDIAN OCEAN TERRITORY", "IO"},
{"BRUNEI DARUSSALAM", "BN"},
{"BULGARIA", "BG"},
{"BURKINA FASO", "BF"},
{"BURUNDI", "BI"},
{"CAMBODIA", "KH"},
{"CAMEROON", "CM"},
{"CANADA", "CA"},
{"CAPE VERDE", "CV"},
{"CAYMAN ISLANDS", "KY"},
{"CENTRAL AFRICAN REPUBLIC", "CF"},
{"CHAD", "TD"},
{"CHILE", "CL"},
{"CHINA", "CN"},
{"CHRISTMAS ISLAND", "CX"},
{"CLIPPERTON ISLAND", "CP"},
{"COCOS (KEELING) ISLANDS", "CC"},
{"COLOMBIA", "CO"},
{"COMOROS", "KM"},
{"CONGO", "CG"},
{"CONGO, THE DEMOCRATIC REPUBLIC OF THE", "CD"},
{"COOK ISLANDS", "CK"},
{"COSTA RICA", "CR"},
{"COTE D'IVOIRE", "CI"},
{"CROATIA", "HR"},
{"CUBA", "CU"},
{"CYPRUS", "CY"},
{"CZECH REPUBLIC", "CZ"},
{"DENMARK", "DK"},
{"DJIBOUTI", "DJ"},
{"DOMINICA", "DM"},
{"DOMINICAN REPUBLIC", "DO"},
{"ECUADOR", "EC"},
{"EGYPT", "EG"},
{"EL SALVADOR", "SV"},
{"EQUATORIAL GUINEA", "GQ"},
{"ERITREA", "ER"},
{"ESTONIA", "EE"},
{"ETHIOPIA", "ET"},
{"FALKLAND ISLANDS (MALVINAS)", "FK"},
{"FAROE ISLANDS", "FO"},
{"FIJI", "FJ"},
{"FINLAND", "FI"},
{"FRANCE", "FR"},
{"FRENCH GUIANA", "GF"},
{"FRENCH POLYNESIA", "PF"},
{"FRENCH SOUTHERN TERRITORIES", "TF"},
{"GABON", "GA"},
{"GAMBIA", "GM"},
{"GEORGIA", "GE"},
{"GERMANY", "DE"},
{"GHANA", "GH"},
{"GIBRALTAR", "GI"},
{"GREECE", "GR"},
{"GREENLAND", "GL"},
{"GRENADA", "GD"},
{"GUADELOUPE", "GP"},
{"GUAM", "GU"},
{"GUATEMALA", "GT"},
{"GUERNSEY", "GG"},
{"GUINEA", "GN"},
{"GUINEA-BISSAU", "GW"},
{"GUYANA", "GY"},
{"HAITI", "HT"},
{"HEARD ISLAND AND MCDONALD ISLANDS", "HM"},
{"HOLY SEE (VATICAN CITY STATE)", "VA"},
{"HONDURAS", "HN"},
{"HONG KONG", "HK"},
{"HUNGARY", "HU"},
{"ICELAND", "IS"},
{"INDIA", "IN"},
{"INDONESIA", "ID"},
{"IRAN, ISLAMIC REPUBLIC OF", "IR"},
{"IRAQ", "IQ"},
{"IRELAND", "IE"},
{"ISRAEL", "IL"},
{"ITALY", "IT"},
{"JAMAICA", "JM"},
{"JAPAN", "JP"},
{"JERSEY", "JE"},
{"JORDAN", "JO"},
{"KAZAKHSTAN", "KZ"},
{"KENYA", "KE"},
{"KIRIBATI", "KI"},
{"KOREA, DEMOCRATIC PEOPLE'S REPUBLIC OF", "KP"},
{"KOREA, REPUBLIC OF", "KR"},
{"KUWAIT", "KW"},
{"KYRGYZSTAN", "KG"},
{"LAO PEOPLE'S DEMOCRATIC REPUBLIC", "LA"},
{"LATVIA", "LV"},
{"LEBANON", "LB"},
{"LESOTHO", "LS"},
{"LIBERIA", "LR"},
{"LIBYAN ARAB JAMAHIRIYA", "LY"},
{"LIECHTENSTEIN", "LI"},
{"LITHUANIA", "LT"},
{"LUXEMBOURG", "LU"},
{"MACAO", "MO"},
{"MACEDONIA, THE FORMER YUGOSLAV REPUBLIC OF", "MK"},
{"MADAGASCAR", "MG"},
{"MALAWI", "MW"},
{"MALAYSIA", "MY"},
{"MALDIVES", "MV"},
{"MALI", "ML"},
{"MALTA", "MT"},
{"MAN, ISLE OF", "IM"},
{"MARSHALL ISLANDS", "MH"},
{"MARTINIQUE", "MQ"},
{"MAURITANIA", "MR"},
{"MAURITIUS", "MU"},
{"MAYOTTE", "YT"},
{"MEXICO", "MX"},
{"MICRONESIA, FEDERATED STATES OF", "FM"},
{"MOLDOVA, REPUBLIC OF", "MD"},
{"MONACO", "MC"},
{"MONGOLIA", "MN"},
{"MONTENEGRO", "ME"},
{"MONTSERRAT", "MS"},
{"MOROCCO", "MA"},
{"MOZAMBIQUE", "MZ"},
{"MYANMAR", "MM"},
{"NAMIBIA", "NA"},
{"NAURU", "NR"},
{"NEPAL", "NP"},
{"NETHERLANDS", "NL"},
{"NETHERLANDS ANTILLES", "AN"},
{"NEW CALEDONIA", "NC"},
{"NEW ZEALAND", "NZ"},
{"NICARAGUA", "NI"},
{"NIGER", "NE"},
{"NIGERIA", "NG"},
{"NIUE", "NU"},
{"NORFOLK ISLAND", "NF"},
{"NORTHERN MARIANA ISLANDS", "MP"},
{"NORWAY", "NO"},
{"OMAN", "OM"},
{"PAKISTAN", "PK"},
{"PALAU", "PW"},
{"PALESTINIAN TERRITORY, OCCUPIED", "PS"},
{"PANAMA", "PA"},
{"PAPUA NEW GUINEA", "PG"},
{"PARAGUAY", "PY"},
{"PERU", "PE"},
{"PHILIPPINES", "PH"},
{"PITCAIRN", "PN"},
{"POLAND", "PL"},
{"PORTUGAL", "PT"},
{"PUERTO RICO", "PR"},
{"QATAR", "QA"},
{"Q1", "Q1"},
{"REUNION", "RE"},
{"ROMANIA", "RO"},
{"RUSSIAN FEDERATION", "RU"},
{"RWANDA", "RW"},
{"SAINT HELENA", "SH"},
{"SAINT KITTS AND NEVIS", "KN"},
{"SAINT LUCIA", "LC"},
{"SAINT PIERRE AND MIQUELON", "PM"},
{"SAINT VINCENT AND THE GRENADINES", "VC"},
{"SAMOA", "WS"},
{"SAN MARINO", "SM"},
{"SAO TOME AND PRINCIPE", "ST"},
{"SAUDI ARABIA", "SA"},
{"SENEGAL", "SN"},
{"SERBIA", "RS"},
{"SEYCHELLES", "SC"},
{"SIERRA LEONE", "SL"},
{"SINGAPORE", "SG"},
{"SLOVAKIA", "SK"},
{"SLOVENIA", "SI"},
{"SOLOMON ISLANDS", "SB"},
{"SOMALIA", "SO"},
{"SOUTH AFRICA", "ZA"},
{"SOUTH GEORGIA AND THE SOUTH SANDWICH ISLANDS", "GS"},
{"SPAIN", "ES"},
{"SRI LANKA", "LK"},
{"SUDAN", "SD"},
{"SURINAME", "SR"},
{"SVALBARD AND JAN MAYEN", "SJ"},
{"SWAZILAND", "SZ"},
{"SWEDEN", "SE"},
{"SWITZERLAND", "CH"},
{"SYRIAN ARAB REPUBLIC", "SY"},
{"TAIWAN, PROVINCE OF CHINA", "TW"},
{"TAJIKISTAN", "TJ"},
{"TANZANIA, UNITED REPUBLIC OF", "TZ"},
{"THAILAND", "TH"},
{"TIMOR-LESTE (EAST TIMOR)", "TL"},
{"TOGO", "TG"},
{"TOKELAU", "TK"},
{"TONGA", "TO"},
{"TRINIDAD AND TOBAGO", "TT"},
{"TRISTAN DA CUNHA", "TA"},
{"TUNISIA", "TN"},
{"TURKEY", "TR"},
{"TURKMENISTAN", "TM"},
{"TURKS AND CAICOS ISLANDS", "TC"},
{"TUVALU", "TV"},
{"UGANDA", "UG"},
{"UKRAINE", "UA"},
{"UNITED ARAB EMIRATES", "AE"},
{"UNITED KINGDOM", "GB"},
{"UNITED STATES", "US"},
{"UNITED STATES MINOR OUTLYING ISLANDS", "UM"},
{"URUGUAY", "UY"},
{"UZBEKISTAN", "UZ"},
{"VANUATU", "VU"},
{"VENEZUELA", "VE"},
{"VIET NAM", "VN"},
{"VIRGIN ISLANDS, BRITISH", "VG"},
{"VIRGIN ISLANDS, U.S.", "VI"},
{"WALLIS AND FUTUNA", "WF"},
{"WESTERN SAHARA", "EH"},
{"YEMEN", "YE"},
{"YUGOSLAVIA", "YU"},
{"ZAMBIA", "ZM"},
{"ZIMBABWE", "ZW"},
{"RADAR CHANNELS", "RDR"},
{"ALL CHANNELS", "ALL"},
{NULL, NULL}
};
static void
wl_print_mcsset(char *mcsset)
{
int i;
printf("MCS SET : [ ");
for (i = 0; i < (MCSSET_LEN * 8); i++)
if (isset(mcsset, i))
printf("%d ", i);
printf("]\n");
}
static void
wl_print_vhtmcsset(uint16 *mcsset)
{
int i, j;
for (i = 0; i < VHT_CAP_MCS_MAP_NSS_MAX; i++) {
if (mcsset[i]) {
if (i == 0)
printf("VHT SET : ");
else
printf(" : ");
for (j = 0; j <= 9; j++)
if (isbitset(mcsset[i], j))
printf("%dx%d ", j, i + 1);
printf("\n");
} else {
break;
}
}
}
static int
wl_txmcsset(void *wl, cmd_t *cmd, char **argv)
{
int err;
if ((err = wl_var_get(wl, cmd, argv)) < 0)
return err;
wl_print_mcsset(buf);
return err;
}
static int
wl_rxmcsset(void *wl, cmd_t *cmd, char **argv)
{
int err;
if ((err = wl_var_get(wl, cmd, argv)) < 0)
return err;
wl_print_mcsset(buf);
return err;
}
static int
wl_mimo_stf(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
int32 int_val;
bool get = TRUE;
uint32 len = 0;
void *ptr = NULL;
char *endptr;
int i = 0, j = 0;
int ret = 0;
while (argv[i])
i++;
if (i > 4)
return BCME_USAGE_ERROR;
/* toss the command name */
argv++;
j = 1;
if (i == 1) {
int_val = -1;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
else {
if (isdigit((unsigned char)(**argv))) {
get = FALSE;
int_val = htod32(strtoul(*argv, &endptr, 0));
if ((int_val != 0) && (int_val != 1)) {
fprintf(stderr, "wl mimo_ss_stf: bad stf mode.\n");
return BCME_BADARG;
}
memcpy(var, (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
argv++;
j++;
}
if (j == i) {
int_val = -1;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
else if (!strncmp(*argv, "-b", 2)) {
argv++;
j++;
if (j == i)
return BCME_BADARG;
if (!strcmp(*argv, "a"))
int_val = 1;
else if (!strcmp(*argv, "b"))
int_val = 0;
else {
fprintf(stderr,
"wl mimo_ss_stf: wrong -b option, \"-b a\" or \"-b b\"\n");
return BCME_USAGE_ERROR;
}
j++;
if (j < i)
return BCME_BADARG;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
}
if (get) {
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0)
return ret;
printf("0x%x\n", dtoh32(*(int *)ptr));
}
else
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
return ret;
}
#ifdef WLEXTLOG
static int
wl_extlog(void *wl, cmd_t *cmd, char **argv)
{
int argc;
char *endptr;
int err;
int from_last;
int i, j;
char *log_p = NULL;
wlc_extlog_req_t r_args;
wlc_extlog_results_t *results;
void *ptr = NULL;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc > 3)
return BCME_USAGE_ERROR;
if (argc == 1)
from_last = 0;
else {
from_last = htod32(strtoul(argv[1], &endptr, 0));
if ((from_last != 0) && (from_last != 1))
return BCME_BADARG;
}
r_args.from_last = from_last;
if (argc == 3)
r_args.num = htod32(strtoul(argv[2], &endptr, 0));
else
r_args.num = 32;
if ((err = wlu_var_getbuf(wl, cmd->name, &r_args, sizeof(wlc_extlog_req_t), &ptr)) < 0)
return err;
results = (wlc_extlog_results_t *)buf;
printf("get external log records: %d\n", results->num);
if (!results->num)
return 0;
if (results->version != EXTLOG_CUR_VER) {
printf("version mismatch: version = 0x%x, expected 0x%0x\n",
results->version, EXTLOG_CUR_VER);
return 0;
}
log_p = (char *)&results->logs[0];
printf("Seq:\tTime(ms) Log\n");
for (i = 0; i < (int)results->num; i++) {
printf("%d:\t%d\t ", ((log_record_t*)log_p)->seq_num,
((log_record_t*)log_p)->time);
for (j = 0; j < FMTSTR_MAX_ID; j++)
if (((log_record_t*)log_p)->id == extlog_fmt_str[j].id)
break;
if (j == FMTSTR_MAX_ID) {
printf("fmt string not found for id %d\n", ((log_record_t*)log_p)->id);
log_p += results->record_len;
continue;
}
switch (extlog_fmt_str[j].arg_type) {
case LOG_ARGTYPE_NULL:
printf("%s", extlog_fmt_str[j].fmt_str);
break;
case LOG_ARGTYPE_STR:
printf(extlog_fmt_str[j].fmt_str, ((log_record_t*)log_p)->str);
break;
case LOG_ARGTYPE_INT:
printf(extlog_fmt_str[j].fmt_str, ((log_record_t*)log_p)->arg);
break;
case LOG_ARGTYPE_INT_STR:
printf(extlog_fmt_str[j].fmt_str, ((log_record_t*)log_p)->arg,
((log_record_t*)log_p)->str);
break;
case LOG_ARGTYPE_STR_INT:
printf(extlog_fmt_str[j].fmt_str, ((log_record_t*)log_p)->str,
((log_record_t*)log_p)->arg);
break;
}
log_p += results->record_len;
}
return 0;
}
static int
wl_extlog_cfg(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
char *endptr;
wlc_extlog_cfg_t *r_cfg;
wlc_extlog_cfg_t w_cfg;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc == 1) {
err = wl_var_get(wl, cmd, argv);
if (err < 0)
return err;
r_cfg = (wlc_extlog_cfg_t *)buf;
printf("max_number=%d, module=%x, level=%d, flag=%d, version=0x%04x\n",
r_cfg->max_number, r_cfg->module, r_cfg->level,
r_cfg->flag, r_cfg->version);
}
else if (argc == 4) {
w_cfg.module = htod16((uint16)(strtoul(argv[1], &endptr, 0)));
w_cfg.level = (uint8)strtoul(argv[2], &endptr, 0);
w_cfg.flag = (uint8)strtoul(argv[3], &endptr, 0);
err = wlu_var_setbuf(wl, cmd->name, &w_cfg, sizeof(wlc_extlog_cfg_t));
}
else {
fprintf(stderr, "illegal command!\n");
return BCME_USAGE_ERROR;
}
return err;
}
#endif /* WLEXTLOG */
static int
wl_assertlog(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err;
int i;
char *log_p = NULL;
assertlog_results_t *results;
void *ptr = NULL;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc > 1)
return BCME_USAGE_ERROR;
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
results = (assertlog_results_t *)buf;
printf("get external assert logs: %d\n", results->num);
if (!results->num)
return 0;
if (results->version != ASSERTLOG_CUR_VER) {
printf("Version mismatch: version = 0x%x, expected 0x%x\n",
results->version, ASSERTLOG_CUR_VER);
return 0;
}
log_p = (char *)&results->logs[0];
printf("id: \ttime(ms) \tstring\n");
for (i = 0; i < (int)results->num; i++) {
printf("%d: \t%d \t%s", i, ((assert_record_t *)log_p)->time,
((assert_record_t *)log_p)->str);
log_p += results->record_len;
}
return 0;
}
extern cca_congest_channel_req_t *
cca_per_chan_summary(cca_congest_channel_req_t *input,
cca_congest_channel_req_t *avg, bool percent);
extern int
cca_analyze(cca_congest_channel_req_t *input[], int num_chans, uint flags, chanspec_t *answer);
static const char *
cca_level(int score, int med, int hi)
{
if (score < med)
return ("Low");
if (score >= med && score < hi)
return ("Medium");
if (score >= hi)
return ("High");
return NULL;
}
static const char *cca_errors[] = {
"No error",
"Preferred band",
"Dwell Duration too low",
"Channel prefs",
"Interference too high",
"Only 1 channel inoput"
};
static int
wl_cca_get_stats(void *wl, cmd_t *cmd, char **argv)
{
cca_congest_channel_req_t *results;
cca_congest_channel_req_t req;
cca_congest_t *chptr;
cca_congest_channel_req_t *avg[MAX_CCA_CHANNELS]; /* Max num of channels */
void *ptr = NULL;
char *param, *val_p;
int base, limit, i, channel, err = 0;
int ibss_per, obss_per, inter_per, val;
const char *ibss_lvl = NULL;
const char *obss_lvl = NULL;
const char *inter_lvl = NULL;
int tmp_channel;
chanspec_t new_chanspec, cur_chanspec;
bool do_average = TRUE;
bool do_individ = FALSE;
bool do_analyze = TRUE;
bool curband = FALSE;
int avg_chan_idx = 0;
uint32 flags;
req.num_secs = 10;
tmp_channel = 0xff;
argv++;
/* Parse args */
while ((param = *argv++) != NULL) {
if (stricmp(param, "-n") == 0) {
do_analyze = FALSE;
continue;
}
if (stricmp(param, "-i") == 0) {
do_individ = TRUE;
continue;
}
if (stricmp(param, "-curband") == 0) {
curband = TRUE;
continue;
}
if ((val_p = *argv++) == NULL) {
printf("Need value following %s\n", param);
return BCME_USAGE_ERROR;
}
if (stricmp(param, "-c") == 0) {
tmp_channel = (int)strtoul(val_p, NULL, 0);
}
if (stricmp(param, "-cs") == 0) {
if ((new_chanspec = wf_chspec_aton(val_p)))
tmp_channel = wf_chspec_ctlchan(new_chanspec);
}
if (stricmp(param, "-s") == 0) {
req.num_secs = (int)strtoul(val_p, NULL, 0);
if (req.num_secs == 0 || req.num_secs > MAX_CCA_SECS) {
printf("%d: Num of seconds must be <= %d\n",
req.num_secs, MAX_CCA_SECS);
return BCME_USAGE_ERROR;
}
}
}
if (!do_average && !do_individ) {
printf("Must pick at least one of averages or individual secs\n");
return BCME_USAGE_ERROR;
}
if (tmp_channel == 0) {
/* do all channels */
base = 1; limit = 255;
} else {
/* Use current channel as default if none specified */
if (tmp_channel == 0xff) {
if ((err = wlu_iovar_getint(wl, "chanspec", (int*)&val)) < 0) {
printf("CCA: Can't get currrent chanspec\n");
return err;
}
cur_chanspec = wl_chspec32_from_driver(val);
tmp_channel = wf_chspec_ctlchan(cur_chanspec);
printf("Using channel %d\n", tmp_channel);
}
base = limit = tmp_channel;
}
for (channel = base; channel <= limit; channel++) {
/* Get stats for each channel */
req.chanspec = CH20MHZ_CHSPEC(channel);
req.chanspec = wl_chspec_to_driver(req.chanspec);
if ((err = wlu_var_getbuf(wl, cmd->name, &req, sizeof(req), &ptr)) < 0)
return err;
results = (cca_congest_channel_req_t *)ptr;
results->chanspec = wl_chspec_from_driver(results->chanspec);
if (results->chanspec == 0 || results->num_secs == 0)
continue;
if (results->num_secs > MAX_CCA_SECS) {
printf("Bogus num of seconds returned %d\n", results->num_secs);
return -1;
}
/* Summarize and save summary for this channel */
if (do_average) {
avg[avg_chan_idx] = (cca_congest_channel_req_t *)
malloc(sizeof(cca_congest_channel_req_t));
if (avg[avg_chan_idx] == NULL) {
printf("unable to allocate memory\n");
return BCME_NOMEM;
}
cca_per_chan_summary(results, avg[avg_chan_idx], 1);
if (avg[avg_chan_idx]->num_secs)
avg_chan_idx++;
}
/* printf stats for each second of each channel */
if (do_individ) {
if (channel == base)
printf("chan dur ibss obss"
" interf time\n");
for (i = 0; i < results->num_secs; i++) {
chptr = &results->secs[i];
if (chptr->duration) {
/* Percentages */
ibss_per = chptr->congest_ibss * 100 /chptr->duration;
obss_per = chptr->congest_obss * 100 /chptr->duration;
inter_per = chptr->interference * 100 /chptr->duration;
/* Levels */
ibss_lvl = cca_level(ibss_per, IBSS_MED, IBSS_HI);
obss_lvl = cca_level(obss_per, OBSS_MED, OBSS_HI);
inter_lvl = cca_level(inter_per, INTERFER_MED, INTERFER_HI);
printf("%-3u %4d %4u %2d%% %-6s %4u %2d%% %-6s %4u %2d%% %-6s %d\n",
CHSPEC_CHANNEL(results->chanspec),
chptr->duration,
chptr->congest_ibss, ibss_per, ibss_lvl,
chptr->congest_obss, obss_per, obss_lvl,
chptr->interference, inter_per, inter_lvl,
chptr->timestamp);
}
}
}
}
/* Print summary stats of each channel */
if (do_average) {
int j;
printf("Summaries:\n");
printf("chan dur ibss obss interf num seconds\n");
for (j = 0; j < avg_chan_idx; j++) {
/* Percentages */
ibss_per = avg[j]->secs[0].congest_ibss;
obss_per = avg[j]->secs[0].congest_obss;
inter_per = avg[j]->secs[0].interference;
/* Levels */
ibss_lvl = cca_level(ibss_per, IBSS_MED, IBSS_HI);
obss_lvl = cca_level(obss_per, OBSS_MED, OBSS_HI);
inter_lvl = cca_level(inter_per, INTERFER_MED, INTERFER_HI);
if (avg[j]->num_secs) {
printf("%-3u %4d %4s %2d%% %-6s %4s %2d%% %-6s %4s %2d%% %-6s %d\n",
CHSPEC_CHANNEL(avg[j]->chanspec),
avg[j]->secs[0].duration,
"", avg[j]->secs[0].congest_ibss, ibss_lvl,
"", avg[j]->secs[0].congest_obss, obss_lvl,
"", avg[j]->secs[0].interference, inter_lvl,
avg[j]->num_secs);
}
}
}
if (!do_analyze)
return err;
if ((err = wlu_iovar_getint(wl, "chanspec", (int *)&val)) < 0) {
printf("CCA: Can't get currrent chanspec\n");
return err;
}
cur_chanspec = wl_chspec32_from_driver(val);
flags = 0;
if (curband) {
if (CHSPEC_IS5G(cur_chanspec))
flags |= CCA_FLAG_5G_ONLY;
if (CHSPEC_IS2G(cur_chanspec))
flags |= CCA_FLAG_2G_ONLY;
}
if ((err = cca_analyze(avg, avg_chan_idx, flags, &new_chanspec)) != 0) {
printf("Cannot find a good channel due to: %s\n", cca_errors[err]);
return BCME_ERROR;
}
printf("Recommended channel: %d\n", wf_chspec_ctlchan(new_chanspec));
return 0;
}
static int
wl_itfr_get_stats(void *wl, cmd_t *cmd, char **argv)
{
int err;
interference_source_rep_t *iftr_stats = NULL;
const char *iftr_source[] = {"none", "wireless phone", "wireless video camera",
"microwave oven", "wireless baby monitor", "bluetooth device",
"wireless video camera or baby monitor", "bluetooth or baby monitor",
"video camera or phone", "unidentified"}; /* sync with interference_source_t */
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, (void*)&iftr_stats)) < 0)
return err;
if (iftr_stats->flags & ITFR_NOISY_ENVIRONMENT)
printf("Feature is stopped due to noisy environment\n");
else
printf("Interference %s detected. last interference at timestamp %d: "
"source is %s on %s channel\n",
(iftr_stats->flags & ITFR_INTERFERENCED) ? "is" : "is not",
iftr_stats->timestamp, iftr_source[iftr_stats->source],
(iftr_stats->flags & ITFR_HOME_CHANNEL) ? "home" : "non-home");
return err;
}
static int
wl_chanim_acs_record(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err = 0, i;
wl_acs_record_t *result;
/* need to add to this str if new acs trigger type is added */
const char *trig_str[] = {"None", "IOCTL", "CHANIM", "TIMER", "BTA"};
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
result = (wl_acs_record_t *) ptr;
if (!result->count) {
printf("There is no ACS recorded\n");
return err;
}
printf("current timestamp: %u (ms)\n", result->timestamp);
printf("Timestamp(ms) ACS Trigger Selected Channel Glitch Count CCA Count\n");
for (i = 0; i < result->count; i++) {
uint8 idx = CHANIM_ACS_RECORD - result->count + i;
chanim_acs_record_t * record = &result->acs_record[idx];
record->selected_chspc = wl_chspec_from_driver(record->selected_chspc);
printf("%10u \t%s \t%10d \t%12d \t%8d\n", record->timestamp,
trig_str[record->trigger], wf_chspec_ctlchan(record->selected_chspc),
record->glitch_cnt, record->ccastats);
}
return err;
}
static int
wl_chanim_stats(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_chanim_stats_t *list;
wl_chanim_stats_t param;
chanim_stats_t *stats;
void *ptr;
int j;
UNUSED_PARAMETER(argv);
param.buflen = htod32(sizeof(wl_chanim_stats_t));
param.count = htod32(WL_CHANIM_COUNT_ONE);
if ((err = wlu_var_getbuf(wl, cmd->name, &param, sizeof(wl_chanim_stats_t), &ptr)) < 0) {
printf("failed to get chanim results");
return err;
}
list = (wl_chanim_stats_t*)ptr;
list->buflen = dtoh32(list->buflen);
list->version = dtoh32(list->version);
list->count = dtoh32(list->count);
printf("version: %d \n", list->version);
if (list->buflen == 0) {
list->version = 0;
list->count = 0;
} else if (list->version != WL_CHANIM_STATS_VERSION) {
printf("Sorry, your driver has wl_chanim_stats version %d "
"but this program supports only version %d.\n",
list->version, WL_CHANIM_STATS_VERSION);
list->buflen = 0;
list->count = 0;
}
stats = list->stats;
stats->glitchcnt = htod32(stats->glitchcnt);
stats->badplcp = htod32(stats->badplcp);
stats->chanspec = htod16(stats->chanspec);
stats->timestamp = htod32(stats->timestamp);
printf("chanspec tx inbss obss nocat nopkt doze txop "
"goodtx badtx glitch badplcp knoise idle timestamp\n");
printf("0x%4x\t", stats->chanspec);
for (j = 0; j < CCASTATS_MAX; j++)
printf("%d\t", stats->ccastats[j]);
printf("%d\t%d\t%d\t%d\t%d", dtoh32(stats->glitchcnt), dtoh32(stats->badplcp),
stats->bgnoise, stats->chan_idle, dtoh32(stats->timestamp));
printf("\n");
return (err);
}
static int
wl_txdelay_params(void *wl, cmd_t *cmd, char **argv)
{
int err;
txdelay_params_t param;
int argc;
argv++;
if (*argv == NULL) {
/* get current txdelay params */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &param,
(sizeof(txdelay_params_t)))) < 0)
return (err);
printf("Txdelay params: ratio[%d] cnt[%d] period[%d] tune[%d]\n",
param.ratio, param.cnt, param.period, param.tune);
}
else {
char *endptr;
/* Validate num of entries */
for (argc = 0; argv[argc]; argc++);
if (argc != 4)
return BCME_USAGE_ERROR;
argc = 0;
param.ratio = strtol(argv[argc], &endptr, 0);
argc++;
param.cnt = strtol(argv[argc], &endptr, 0);
argc++;
param.period = strtol(argv[argc], &endptr, 0);
argc++;
param.tune = strtol(argv[argc], &endptr, 0);
/* Set txdelay params */
err = wlu_iovar_set(wl, cmd->name, (void *) &param,
(sizeof(txdelay_params_t)));
}
return (err);
}
static int
wl_intfer_params(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_intfer_params_t param;
int argc;
char *endptr = NULL;
argv++;
if (*argv == NULL) {
/* get current txdelay params */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &param,
(sizeof(wl_intfer_params_t)))) < 0)
goto error;
if (param.version != INTFER_VERSION) {
printf("Interference params structure version (%d) is not the "
"version (%d) supported by this tool",
INTFER_VERSION, param.version);
err = BCME_USAGE_ERROR;
}
else
printf("Intference params: period[%x] cnt[%x] txfail_thresh[%x]"
" tcptxfail_thresh[%x]\n", param.period, param.cnt,
param.txfail_thresh, param.tcptxfail_thresh);
}
else {
/* Validate num of entries */
err = BCME_USAGE_ERROR;
for (argc = 0; argv[argc]; argc++);
if (argc != 4)
goto error;
param.period = (uint8)strtol(argv[0], &endptr, 0);
if (*endptr != '\0')
goto error;
param.cnt = (uint8)strtol(argv[1], &endptr, 0);
if (*endptr != '\0')
goto error;
param.txfail_thresh = (uint8)strtol(argv[2], &endptr, 0);
if (*endptr != '\0')
goto error;
param.tcptxfail_thresh = (uint8)strtol(argv[3], &endptr, 0);
if (*endptr != '\0')
goto error;
/* Set intfer params */
param.version = INTFER_VERSION;
err = wlu_iovar_set(wl, cmd->name, (void *) &param,
(sizeof(wl_intfer_params_t)));
}
error:
return (err);
}
#ifdef WLP2P
static int
wl_p2p_state(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_disc_st_t st;
int count;
char *endptr;
argv++;
count = ARGCNT(argv);
if (count < 1)
return BCME_USAGE_ERROR;
st.state = (uint8) strtol(argv[0], &endptr, 0);
if (st.state == WL_P2P_DISC_ST_LISTEN) {
if (count != 3)
return BCME_USAGE_ERROR;
if ((st.chspec = wf_chspec_aton(argv[1])) == 0) {
fprintf(stderr, "error parsing chanspec arg \"%s\"\n", argv[1]);
return BCME_BADARG;
}
st.chspec = wl_chspec_to_driver(st.chspec);
if (st.chspec == INVCHANSPEC) {
return BCME_USAGE_ERROR;
}
st.dwell = (uint16) strtol(argv[2], &endptr, 0);
}
return wlu_var_setbuf(wl, cmd->name, &st, sizeof(st));
}
static int
wl_p2p_scan(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_scan_t *params = NULL;
int params_size = 0;
int malloc_size = 0;
int sparams_size = 0;
int err = 0;
if (*(argv + 1) != NULL) {
malloc_size = sizeof(wl_p2p_scan_t);
switch (toupper(**(argv + 1))) {
case 'S':
malloc_size += WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
break;
case 'E':
malloc_size += OFFSETOF(wl_escan_params_t, params) +
WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
break;
}
}
if (malloc_size == 0) {
fprintf(stderr, "wrong syntax, need 'S' or 'E'\n");
return BCME_BADARG;
}
malloc_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_p2p_scan_t *)malloc(malloc_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", malloc_size);
return BCME_NOMEM;
}
memset(params, 0, malloc_size);
switch (toupper(**(argv + 1))) {
case 'S': {
wl_scan_params_t *sparams = (wl_scan_params_t *)(params+1);
sparams_size = malloc_size - sizeof(wl_p2p_scan_t);
params->type = 'S';
if ((err = wl_scan_prep(wl, cmd, argv + 1, sparams, &sparams_size)) == 0)
params_size = sizeof(wl_p2p_scan_t) + sparams_size;
break;
}
case 'E': {
wl_escan_params_t *eparams = (wl_escan_params_t *)(params+1);
sparams_size = malloc_size - sizeof(wl_p2p_scan_t) - sizeof(wl_escan_params_t);
params->type = 'E';
eparams->version = htod32(ESCAN_REQ_VERSION);
eparams->action = htod16(WL_SCAN_ACTION_START);
srand((unsigned)time(NULL));
eparams->sync_id = htod16(rand() & 0xffff);
if ((err = wl_scan_prep(wl, cmd, argv + 1, &eparams->params, &sparams_size)) == 0)
params_size = sizeof(wl_p2p_scan_t) + sizeof(wl_escan_params_t) +
sparams_size;
break;
}
}
if (!err)
err = wlu_iovar_setbuf(wl, cmd->name, params, params_size, buf, WLC_IOCTL_MAXLEN);
free(params);
return err;
}
static int
wl_p2p_ifadd(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_if_t ifreq;
int count;
argv++;
count = ARGCNT(argv);
if (count < 2)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &ifreq.addr))
return BCME_USAGE_ERROR;
if (stricmp(argv[1], "go") == 0)
ifreq.type = WL_P2P_IF_GO;
else if (stricmp(argv[1], "client") == 0)
ifreq.type = WL_P2P_IF_CLIENT;
else if (stricmp(argv[1], "dyngo") == 0)
ifreq.type = WL_P2P_IF_DYNBCN_GO;
else
return BCME_USAGE_ERROR;
if (ifreq.type == WL_P2P_IF_GO || ifreq.type == WL_P2P_IF_DYNBCN_GO) {
if (count > 2) {
if ((ifreq.chspec = wf_chspec_aton(argv[2])) == 0) {
fprintf(stderr, "error parsing chanspec arg \"%s\"\n", argv[2]);
return BCME_BADARG;
}
ifreq.chspec = wl_chspec_to_driver(ifreq.chspec);
if (ifreq.chspec == INVCHANSPEC) {
return BCME_BADARG;
}
}
else
ifreq.chspec = 0;
}
return wlu_var_setbuf(wl, cmd->name, &ifreq, sizeof(ifreq));
}
static int
wl_p2p_ifdel(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr addr;
int count;
argv++;
count = ARGCNT(argv);
if (count != 1)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &addr))
return BCME_USAGE_ERROR;
return wlu_var_setbuf(wl, cmd->name, &addr, sizeof(addr));
}
static int
wl_p2p_ifupd(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_if_t ifreq;
int count;
int ret;
int bsscfg_idx = 0;
int consumed = 0;
argv++;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv, cmd->name, &bsscfg_idx, &consumed)) != 0)
return ret;
argv += consumed;
if (consumed == 0)
bsscfg_idx = -1;
count = ARGCNT(argv);
if (count < 2)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &ifreq.addr))
return BCME_USAGE_ERROR;
if (stricmp(argv[1], "go") == 0)
ifreq.type = WL_P2P_IF_GO;
else if (stricmp(argv[1], "client") == 0)
ifreq.type = WL_P2P_IF_CLIENT;
else
return BCME_USAGE_ERROR;
ifreq.chspec = 0;
if (bsscfg_idx == -1)
return wlu_var_setbuf(wl, cmd->name, &ifreq, sizeof(ifreq));
return wlu_bssiovar_setbuf(wl, cmd->name, bsscfg_idx,
&ifreq, sizeof(ifreq),
buf, WLC_IOCTL_MAXLEN);
}
static int
wl_p2p_if(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr addr;
int count;
wl_p2p_ifq_t *ptr;
int err;
argv++;
count = ARGCNT(argv);
if (count != 1)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &addr))
return BCME_USAGE_ERROR;
err = wlu_var_getbuf(wl, cmd->name, &addr, sizeof(addr), (void*)&ptr);
if (err >= 0)
printf("%u %s\n", dtoh32(ptr->bsscfgidx), (ptr->ifname));
return err;
}
static int
wl_p2p_ops(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_ops_t ops;
int count;
char *endptr;
argv++;
count = ARGCNT(argv);
if (count < 1) {
wl_p2p_ops_t *ops;
int err;
err = wlu_var_getbuf(wl, cmd->name, NULL, 0, (void *)&ops);
if (err != BCME_OK) {
fprintf(stderr, "%s: error %d\n", cmd->name, err);
return err;
}
printf("ops: %u ctw: %u\n", ops->ops, ops->ctw);
return BCME_OK;
}
ops.ops = (uint8) strtol(argv[0], &endptr, 0);
if (ops.ops != 0) {
if (count != 2)
return BCME_USAGE_ERROR;
ops.ctw = (uint8) strtol(argv[1], &endptr, 0);
}
else
ops.ctw = 0;
return wlu_var_setbuf(wl, cmd->name, &ops, sizeof(ops));
}
static int
wl_p2p_noa(void *wl, cmd_t *cmd, char **argv)
{
int count;
wl_p2p_sched_t *noa;
int len;
int i;
char *endptr;
argv ++;
strcpy(buf, cmd->name);
count = ARGCNT(argv);
if (count < 2) {
int err = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MAXLEN);
wl_p2p_sched_t *sched;
int i;
if (err != BCME_OK) {
fprintf(stderr, "%s: error %d\n", cmd->name, err);
return err;
}
sched = (wl_p2p_sched_t *)buf;
for (i = 0; i < 16; i ++) {
if (sched->desc[i].count == 0)
break;
printf("start: %u interval: %u duration: %u count: %u\n",
sched->desc[i].start, sched->desc[i].interval,
sched->desc[i].duration, sched->desc[i].count);
}
return BCME_OK;
}
len = strlen(buf);
noa = (wl_p2p_sched_t *)&buf[len + 1];
len += 1;
noa->type = (uint8)strtol(argv[0], &endptr, 0);
len += sizeof(noa->type);
noa->action = (uint8)strtol(argv[1], &endptr, 0);
len += sizeof(noa->action);
argv += 2;
count -= 2;
/* action == -1 is to cancel the current schedule */
if (noa->action == WL_P2P_SCHED_ACTION_RESET) {
/* the fixed portion of wl_p2p_sched_t with action == WL_P2P_SCHED_ACTION_RESET
* is required to cancel the curret schedule.
*/
len += (char *)&noa->desc[0] - ((char *)buf + len);
}
/* Take care of any special cases only and let all other cases fall through
* as normal 'start/interval/duration/count' descriptions.
* All cases start with 'type' 'action' 'option'.
* Any count value greater than 255 is to repeat unlimited.
*/
else {
switch (noa->type) {
case WL_P2P_SCHED_TYPE_ABS:
case WL_P2P_SCHED_TYPE_REQ_ABS:
if (count < 1)
return BCME_USAGE_ERROR;
noa->option = (uint8)strtol(argv[0], &endptr, 0);
len += sizeof(noa->option);
argv += 1;
count -= 1;
break;
}
/* add any paddings before desc field */
len += (char *)&noa->desc[0] - ((char *)buf + len);
switch (noa->type) {
case WL_P2P_SCHED_TYPE_ABS:
switch (noa->option) {
case WL_P2P_SCHED_OPTION_BCNPCT:
if (count == 1) {
noa->desc[0].duration = htod32(strtol(argv[0], &endptr, 0));
noa->desc[0].start = 100 - noa->desc[0].duration;
}
else if (count == 2) {
noa->desc[0].start = htod32(strtol(argv[0], &endptr, 0));
noa->desc[0].duration = htod32(strtol(argv[1], &endptr, 0));
}
else {
fprintf(stderr, "Usage: wl p2p_noa 0 %d 1 "
"<start-pct> <duration-pct>\n",
noa->action);
return BCME_USAGE_ERROR;
}
len += sizeof(wl_p2p_sched_desc_t);
break;
default:
if (count < 4 || (count % 4) != 0) {
fprintf(stderr, "Usage: wl p2p_noa 0 %d 0 "
"<start> <interval> <duration> <count> ...\n",
noa->action);
return BCME_USAGE_ERROR;
}
goto normal;
}
break;
default:
if (count != 4) {
fprintf(stderr, "Usage: wl p2p_noa 1 %d "
"<start> <interval> <duration> <count> ...\n",
noa->action);
return BCME_USAGE_ERROR;
}
/* fall through... */
normal:
for (i = 0; i < count; i += 4) {
noa->desc[i / 4].start = htod32(strtoul(argv[i], &endptr, 0));
noa->desc[i / 4].interval = htod32(strtol(argv[i + 1], &endptr, 0));
noa->desc[i / 4].duration = htod32(strtol(argv[i + 2], &endptr, 0));
noa->desc[i / 4].count = htod32(strtol(argv[i + 3], &endptr, 0));
len += sizeof(wl_p2p_sched_desc_t);
}
break;
}
}
return wlu_set(wl, WLC_SET_VAR, buf, len);
}
#endif /* WLP2P */
static int
wl_rpmt(void *wl, cmd_t *cmd, char **argv)
{
int count;
int len;
char *endptr;
uint32 val;
argv ++;
count = ARGCNT(argv);
if (count != 2) {
return BCME_USAGE_ERROR;
}
strcpy(buf, cmd->name);
len = strlen(buf) + 1;
val = htod32(strtoul(argv[0], &endptr, 0));
memcpy(&buf[len], &val, sizeof(uint32));
len += sizeof(uint32);
val = htod32(strtoul(argv[1], &endptr, 0));
memcpy(&buf[len], &val, sizeof(uint32));
len += sizeof(uint32);
return wlu_set(wl, WLC_SET_VAR, buf, len);
}
static int
wl_ledbh(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_led_info_t led;
void *ptr = NULL;
memset(&led, 0, sizeof(wl_led_info_t));
if (*++argv == NULL) {
printf("Usage: ledbh [led#] [behavior#]\n");
return BCME_USAGE_ERROR;
}
led.index = (int)strtoul(*argv, NULL, 10);
if (led.index > 3) {
printf("only 4 led supported\n");
return BCME_BADARG;
}
if (*++argv) { /* set */
/* Read the original back so we don't toggle the activehi */
if ((err = wlu_var_getbuf(wl, cmd->name, (void*)&led,
sizeof(wl_led_info_t), &ptr)) < 0) {
printf("wl_ledbh: fail to get. code %x\n", err);
}
led.behavior = (int)strtoul(*argv, NULL, 10);
led.activehi = ((wl_led_info_t*)ptr)->activehi;
if ((err = wlu_var_setbuf(wl, cmd->name, (void*)&led,
sizeof(wl_led_info_t))) < 0) {
printf("wl_ledbh: fail to set\n");
}
} else { /* get */
wl_led_info_t *ledo;
if ((err = wlu_var_getbuf(wl, cmd->name, (void*)&led,
sizeof(wl_led_info_t), &ptr)) < 0) {
printf("wl_ledbh: fail to get\n");
}
ledo = (wl_led_info_t*)ptr;
printf("led %d behavior %d\n", ledo->index, ledo->behavior);
}
return err;
}
static int
wl_led_blink_sync(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
int in_arg[2];
void *ptr = NULL;
char *endptr;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc > 3 || argc < 2)
return BCME_USAGE_ERROR;
in_arg[0] = htod32((uint32)(strtoul(argv[1], &endptr, 0)));
if (in_arg[0] > 3) {
printf("only 4 led supported\n");
return BCME_BADARG;
}
if (argc == 2) {
err = wlu_var_getbuf(wl, cmd->name, (void*)in_arg, sizeof(int), &ptr);
if (err < 0)
return err;
printf("led%d, blink_sync is %s\n", in_arg[0],
(dtoh32(*(int*)ptr) != 0) ? "TRUE" : "FALSE");
}
else if (argc == 3) {
in_arg[1] = htod32((uint32)(strtoul(argv[2], &endptr, 0)));
err = wlu_var_setbuf(wl, cmd->name, in_arg, sizeof(in_arg));
}
else {
fprintf(stderr, "illegal command!\n");
return BCME_USAGE_ERROR;
}
return err;
}
/* RM Enable Capabilities */
static dbg_msg_t rrm_msgs[] = {
{DOT11_RRM_CAP_LINK, "Link_Measurement"}, /* bit0 */
{DOT11_RRM_CAP_NEIGHBOR_REPORT, "Neighbor_Report"}, /* bit1 */
{DOT11_RRM_CAP_PARALLEL, "Parallel_Measurement"}, /* bit2 */
{DOT11_RRM_CAP_REPEATED, "Repeated_Measurement"}, /* bit3 */
{DOT11_RRM_CAP_BCN_PASSIVE, "Beacon_Passive"}, /* bit4 */
{DOT11_RRM_CAP_BCN_ACTIVE, "Beacon_Active"}, /* bit5 */
{DOT11_RRM_CAP_BCN_TABLE, "Beacon_Table"}, /* bit6 */
{DOT11_RRM_CAP_BCN_REP_COND, "Beacon_measurement_Reporting_Condition"}, /* bit7 */
{DOT11_RRM_CAP_FM, "Frame_Measurement"}, /* bit8 */
{DOT11_RRM_CAP_CLM, "Channel_load_Measurement"}, /* bit9 */
{DOT11_RRM_CAP_NHM, "Noise_Histogram_measurement"}, /* bit10 */
{DOT11_RRM_CAP_SM, "Statistics_Measurement"}, /* bit11 */
{DOT11_RRM_CAP_LCIM, "LCI_Measurement"}, /* bit12 */
{DOT11_RRM_CAP_LCIA, "LCI_Azimuth"}, /* bit13 */
{DOT11_RRM_CAP_TSCM, "Tx_Stream_Category_Measurement"}, /* bit14 */
{DOT11_RRM_CAP_TTSCM, "Triggered_Tx_stream_Category_Measurement"}, /* bit15 */
{DOT11_RRM_CAP_AP_CHANREP, "AP_Channel_Report"}, /* bit16 */
{DOT11_RRM_CAP_RMMIB, "RM_MIB"}, /* bit17 */
/* bit 18-26, unused */
{DOT11_RRM_CAP_MPTI, "Measurement_Pilot_Transmission_Information"}, /* bit27 */
{DOT11_RRM_CAP_NBRTSFO, "Neighbor_Report_TSF_Offset"}, /* bit28 */
{DOT11_RRM_CAP_RCPI, "RCPI_Measurement"}, /* bit29 */
{DOT11_RRM_CAP_RSNI, "RSNI_Measurement"}, /* bit30 */
{DOT11_RRM_CAP_BSSAAD, "BSS_Average_Access_Delay"}, /* bit31 */
{DOT11_RRM_CAP_BSSAAC, "BSS_Available_Admission_Capacity"}, /* bit32 */
{DOT11_RRM_CAP_AI, "Antenna_Information"}, /* bit33 */
{0, NULL}
};
static bool rrm_input_validation(uint val, uint hval, dbg_msg_t *dbg_msg)
{
int i;
uint32 flag = 0;
if ((val == 0) && (hval == 0))
return TRUE;
for (i = 0; dbg_msg[i].value <= DOT11_RRM_CAP_BSSAAD; i++)
flag |= 1 << dbg_msg[i].value;
flag = ~flag;
if (val & flag)
return FALSE;
flag = 0;
if (hval != 0) {
for (; dbg_msg[i].value; i++) {
flag |= 1 << (dbg_msg[i].value - DOT11_RRM_CAP_BSSAAC);
}
flag = ~flag;
if (hval & flag)
return FALSE;
}
return TRUE;
}
static int
wl_rrm(void *wl, cmd_t *cmd, char **argv)
{
int err, i;
uint hval = 0, val = 0, len, found, rmcap_del = 0, rmcap2_del = 0;
uint rmcap_add = 0, rmcap2_add = 0;
char *endptr = NULL;
dbg_msg_t *dbg_msg = rrm_msgs;
void *ptr = NULL;
dot11_rrm_cap_ie_t rrm_cap, *reply;
uint high = 0, low = 0, bit = 0, hbit = 0;
const char *cmdname = "rrm";
UNUSED_PARAMETER(cmd);
err = wlu_var_getbuf_sm(wl, cmdname, &rrm_cap, sizeof(rrm_cap), &ptr);
if (err < 0)
return err;
reply = (dot11_rrm_cap_ie_t *)ptr;
high = reply->cap[4];
low = reply->cap[0] | (reply->cap[1] << 8) | (reply->cap[2] << 16) | (reply->cap[3] << 24);
if (!*++argv) {
if (high != 0)
printf("0x%x%08x", high, low);
else
printf("0x%x ", low);
for (i = 0; ((bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD); i++) {
if (low & (1 << bit))
printf(" %s", dbg_msg[i].string);
}
for (; (hbit = dbg_msg[i].value); i++) {
if (high & (1 << hbit))
printf(" %s", dbg_msg[i].string);
}
printf("\n");
return err;
}
while (*argv) {
char *s = *argv;
char t[32];
found = 0;
if (*s == '+' || *s == '-')
s++;
else {
/* used for clearing previous value */
rmcap_del = ~0;
rmcap2_del = ~0;
}
val = strtoul(s, &endptr, 0);
/* Input is decimal number or hex with prefix 0x and > 32 bits */
if (val == 0xFFFFFFFF) {
if (!(*s == '0' && *(s+1) == 'x')) {
fprintf(stderr,
"Msg bits >32 take only numerical input in hex\n");
val = 0;
} else {
/* Input number with prefix 0x */
char c[32] = "0x";
len = strlen(s);
hval = strtoul(strncpy(t, s, len-8), &endptr, 0);
*endptr = 0;
s = s + strlen(t);
s = strcat(c, s);
val = strtoul(s, &endptr, 0);
/* Input number > 64bit */
if (hval == 0xFFFFFFFF) {
fprintf(stderr, "Invalid entry for RM Capabilities\n");
hval = 0;
val = 0;
}
}
}
/* validet the input number */
if (!rrm_input_validation(val, hval, dbg_msg))
goto usage;
/* Input is a string */
if (*endptr != '\0') {
for (i = 0; ((bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD); i++) {
if (stricmp(dbg_msg[i].string, s) == 0) {
found = 1;
break;
}
}
if (!found) {
for (; (hbit = dbg_msg[i].value); i++) {
if (stricmp(dbg_msg[i].string, s) == 0)
break;
}
if (hbit)
hval = 1 << (hbit - DOT11_RRM_CAP_BSSAAC);
else
hval = 0;
} else {
val = 1 << bit;
}
if (!val && !hval)
goto usage;
}
if (**argv == '-') {
rmcap_del |= val;
if (!found)
rmcap2_del |= hval;
}
else {
rmcap_add |= val;
if (!found)
rmcap2_add |= hval;
}
++argv;
}
low &= ~rmcap_del;
high &= ~rmcap2_del;
low |= rmcap_add;
high |= rmcap2_add;
rrm_cap.cap[4] = high;
rrm_cap.cap[0] = low & 0x000000ff;
rrm_cap.cap[1] = (low & 0x0000ff00) >> 8;
rrm_cap.cap[2] = (low & 0x00ff0000) >> 16;
rrm_cap.cap[3] = (low & 0xff000000) >> 24;
err = wlu_var_setbuf(wl, cmdname, &rrm_cap, sizeof(dot11_rrm_cap_ie_t));
return err;
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD; i++) {
fprintf(stderr, "\n0x%04x %s", (1 << bit), dbg_msg[i].string);
}
for (; (hbit = dbg_msg[i].value); i++) {
hbit -= DOT11_RRM_CAP_BSSAAC;
fprintf(stderr, "\n0x%x00000000 %s", (1 << hbit), dbg_msg[i].string);
}
fprintf(stderr, "\n");
return BCME_OK;
}
static int
wl_rrm_stat_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_stat_req";
statreq_t sreq_buf;
UNUSED_PARAMETER(cmd);
memset(&sreq_buf, 0, sizeof(statreq_t));
if (argv[1]) {
/* da */
if (!wl_ether_atoe(argv[1], &sreq_buf.da)) {
printf("wl_rrm_stat_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* random interval */
if (argv[2]) {
sreq_buf.random_int = htod32(strtoul(argv[2], NULL, 0));
}
/* duration */
if (argv[3]) {
sreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* peer address */
if (argv[4]) {
if (!wl_ether_atoe(argv[4], &sreq_buf.peer)) {
printf("wl_rrm_stat_req parsing peer failed\n");
return BCME_USAGE_ERROR;
}
}
/* group id */
if (argv[5]) {
sreq_buf.group_id =
htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
sreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &sreq_buf, sizeof(sreq_buf));
return err;
}
static int
wl_rrm_frame_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_frame_req";
framereq_t freq_buf;
UNUSED_PARAMETER(cmd);
memset(&freq_buf, 0, sizeof(framereq_t));
if (argv[1]) {
/* Regulatory class */
freq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &freq_buf.da)) {
printf("wl_rrm_frame_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
freq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
freq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
freq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* transmit address */
if (argv[6]) {
if (!wl_ether_atoe(argv[6], &freq_buf.ta)) {
printf("wl_rrm_frame_req parsing ta failed\n");
return BCME_USAGE_ERROR;
}
}
/* repetitions */
if (argv[7]) {
freq_buf.reps = htod32(strtoul(argv[7], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &freq_buf, sizeof(freq_buf));
return err;
}
static int
wl_rrm_chload_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_chload_req";
rrmreq_t chreq_buf;
UNUSED_PARAMETER(cmd);
memset(&chreq_buf, 0, sizeof(rrmreq_t));
if (argv[1]) {
/* Regulatory class */
chreq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &chreq_buf.da)) {
printf("wl_rrm_chload_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
chreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
chreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
chreq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
chreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &chreq_buf, sizeof(chreq_buf));
return err;
}
static int
wl_rrm_noise_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_noise_req";
rrmreq_t nreq_buf;
UNUSED_PARAMETER(cmd);
printf("wl_rrm_noise_req\n");
memset(&nreq_buf, 0, sizeof(rrmreq_t));
if (argv[1]) {
/* Regulatory class */
nreq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &nreq_buf.da)) {
printf("wl_rrm_noise_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
nreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
nreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
nreq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
nreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &nreq_buf, sizeof(nreq_buf));
return err;
}
static int
wl_rrm_bcn_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
uint8 mode = 0;
const char *cmdname = "rrm_bcn_req";
bcnreq_t bcnreq_buf;
wlc_ssid_t ssid;
UNUSED_PARAMETER(cmd);
memset(&bcnreq_buf, 0, sizeof(bcnreq_t));
if (argv[1]) {
/* bcn mode: ACTIVE/PASSIVE/SCAN_CACHE */
mode = htod32(strtoul(argv[1], NULL, 0));
if (mode > 2) {
printf("wl_rrm_bcn_req parsing bcn mode failed\n");
return BCME_BADARG;
}
bcnreq_buf.bcn_mode = mode;
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &bcnreq_buf.da)) {
printf("wl_rrm_bcn_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
bcnreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
bcnreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
bcnreq_buf.channel = htod32(strtoul(argv[5], NULL, 0));
}
printf("wl_rrm_bcn_req:bcn mode: %d, duration: %d, "
"chan: %d\n", mode,
bcnreq_buf.dur, bcnreq_buf.channel);
/* SSID */
if (argv[6]) {
uint32 len;
len = strlen(argv[6]);
if (len > DOT11_MAX_SSID_LEN) {
printf("ssid too long\n");
return BCME_BADARG;
}
memset(&ssid, 0, sizeof(wlc_ssid_t));
memcpy(ssid.SSID, argv[6], len);
ssid.SSID_len = len;
memcpy(&bcnreq_buf.ssid, &ssid, sizeof(wlc_ssid_t));
}
/* repetitions */
if (argv[7]) {
bcnreq_buf.reps = htod32(strtoul(argv[7], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &bcnreq_buf, sizeof(bcnreq_buf));
return err;
}
static int
wl_rrm_lm_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_lm_req";
struct ether_addr da;
UNUSED_PARAMETER(cmd);
if (argv[1]) {
if (!wl_ether_atoe(argv[1], &da)) {
printf("wl_rrm_lm_req parsing arg1 failed\n");
return BCME_USAGE_ERROR;
}
}
err = wlu_iovar_set(wl, cmdname, &da, sizeof(da));
return err;
}
static int
wl_rrm_nbr_req(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wlc_ssid_t ssid;
UNUSED_PARAMETER(cmd);
strcpy(buf, "rrm_nbr_req");
buflen = strlen("rrm_nbr_req") + 1;
if (*++argv) {
uint32 len;
len = strlen(*argv);
if (len > DOT11_MAX_SSID_LEN) {
printf("ssid too long\n");
return BCME_BADARG;
}
memset(&ssid, 0, sizeof(wlc_ssid_t));
memcpy(ssid.SSID, *argv, len);
ssid.SSID_len = len;
memcpy(&buf[buflen], &ssid, sizeof(wlc_ssid_t));
buflen += sizeof(wlc_ssid_t);
}
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
/* For mapping customer's user space command, two calls of the same iovar. */
static int
wl_rrm_nbr_list(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, buflen, i;
uint16 list_cnt;
nbr_element_t *nbr_elt;
uint8 *ptr;
UNUSED_PARAMETER(cmd);
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "rrm_nbr_list");
buflen = strlen("rrm_nbr_list") + 1;
if (*++argv != NULL)
return BCME_USAGE_ERROR;
if ((err = wlu_get(wl, WLC_GET_VAR, buf, buflen)) < 0)
return err;
list_cnt = *(uint16 *)buf;
if (list_cnt == 0)
return err;
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "rrm_nbr_list");
buflen = strlen("rrm_nbr_list") + 1;
memcpy(&buf[buflen], &list_cnt, sizeof(uint16));
printf("RRM Neighbor Report List:\n");
if ((err = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
ptr = (uint8 *)buf;
for (i = 0; i < list_cnt; i++) {
nbr_elt = (nbr_element_t *)ptr;
printf("AP %2d: ", i + 1);
printf("bssid %02x:%02x:%02x:%02x:%02x:%02x ", nbr_elt->bssid.octet[0],
nbr_elt->bssid.octet[1], nbr_elt->bssid.octet[2], nbr_elt->bssid.octet[3],
nbr_elt->bssid.octet[4], nbr_elt->bssid.octet[5]);
printf("bssid_info %08x ", load32_ua(&nbr_elt->bssid_info));
printf("reg %2d channel %3d phytype %d\n", nbr_elt->reg,
nbr_elt->channel, nbr_elt->phytype);
ptr += TLV_HDR_LEN + DOT11_NEIGHBOR_REP_IE_FIXED_LEN;
}
return err;
}
static int
wl_rrm_nbr_del_nbr(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_nbr_del_nbr";
struct ether_addr ea;
UNUSED_PARAMETER(cmd);
if (*++argv == NULL) {
printf("no bssid specified\n");
return BCME_USAGE_ERROR;
} else {
if (!wl_ether_atoe(*argv, &ea)) {
printf("Incorrect bssid format\n");
return BCME_ERROR;
}
err = wlu_iovar_set(wl, cmdname, &ea, sizeof(ea));
}
return err;
}
static int
wl_rrm_nbr_add_nbr(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
const char *cmdname = "rrm_nbr_add_nbr";
nbr_element_t nbr_elt;
UNUSED_PARAMETER(cmd);
memset(&nbr_elt, 0, sizeof(nbr_element_t));
for (argc = 0; argv[argc]; argc++)
;
if (argc != 6)
return BCME_USAGE_ERROR;
/* bssid */
if (!wl_ether_atoe(argv[1], &nbr_elt.bssid)) {
printf("wl_rrm_nbr_add_nbr parsing bssid failed\n");
return BCME_USAGE_ERROR;
}
/* bssid info */
nbr_elt.bssid_info = htod32(strtoul(argv[2], NULL, 0));
/* Regulatory class */
nbr_elt.reg = htod32(strtoul(argv[3], NULL, 0));
/* channel */
nbr_elt.channel = htod32(strtoul(argv[4], NULL, 0));
/* phytype */
nbr_elt.phytype = htod32(strtoul(argv[5], NULL, 0));
nbr_elt.id = DOT11_MNG_NEIGHBOR_REP_ID;
nbr_elt.len = DOT11_NEIGHBOR_REP_IE_FIXED_LEN;
err = wlu_iovar_set(wl, cmdname, &nbr_elt, TLV_HDR_LEN + DOT11_NEIGHBOR_REP_IE_FIXED_LEN);
return err;
}
static dbg_msg_t wl_wnm_msgs[] = {
{WL_WNM_BSSTRANS, "BSS-Transition"},
{WL_WNM_PROXYARP, "Proxy-ARP"},
{WL_WNM_MAXIDLE, "BSS-Max-Idle-Period"},
{WL_WNM_TIMBC, "TIM-Broadcast"},
{WL_WNM_TFS, "Traffic-Filtering"},
{WL_WNM_SLEEP, "WNM-Sleep-Mode"},
{WL_WNM_DMS, "Directed-Multicast"},
{WL_WNM_FMS, "Flexible-Multicast"},
{WL_WNM_NOTIF, "Notification"},
{0, ""}
};
static int
wl_wnm(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint val;
int wnmmask;
char *s;
ret = wlu_iovar_getint(wl, cmd->name, &wnmmask);
if (ret < 0)
return ret;
if (!*++argv) {
printf("0x%x:", wnmmask);
for (i = 0; wl_wnm_msgs[i].value; i++)
if (wnmmask & wl_wnm_msgs[i].value)
printf(" %s", wl_wnm_msgs[i].string);
printf("\n");
return 0;
}
s = *argv;
if (*s == '+' || *s == '-')
s++;
if (!*s)
goto usage;
val = strtoul(s, &s, 0);
if (*s)
goto usage;
if (**argv == '+')
wnmmask |= val;
else if (**argv == '-')
wnmmask &= ~val;
else
wnmmask = val;
return wlu_iovar_setint(wl, cmd->name, wnmmask);
usage:
fprintf(stderr, "WNM mask is a bitfield from the following set. "
"Use + or - prefix to combine with current value:\n");
for (i = 0; wl_wnm_msgs[i].value; i++)
fprintf(stderr, " 0x%04x %s\n", wl_wnm_msgs[i].value, wl_wnm_msgs[i].string);
return 0;
}
static int
wl_wnm_bsstq(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wlc_ssid_t ssid;
UNUSED_PARAMETER(cmd);
strcpy(buf, "wnm_bsstrans_query");
buflen = strlen("wnm_bsstrans_query") + 1;
if (*++argv) {
uint32 len;
len = strlen(*argv);
if (len > DOT11_MAX_SSID_LEN) {
printf("ssid too long\n");
return (-1);
}
memset(&ssid, 0, sizeof(wlc_ssid_t));
memcpy(ssid.SSID, *argv, len);
ssid.SSID_len = len;
memcpy(&buf[buflen], &ssid, sizeof(wlc_ssid_t));
buflen += sizeof(wlc_ssid_t);
}
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
#define TCLAS_ARG_CHECK(argv, str) \
do { \
if (*++(argv) == NULL) { \
printf("TCLAS Frame Classifier: %s not provided\n", (str)); \
return BCME_ERROR; \
} \
} while (0)
static int
wl_tclas_add(void *wl, cmd_t *cmd, char **argv)
{
int err = -1, buflen;
dot11_tclas_fc_t *fc;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
/* Check the user priority value */
TCLAS_ARG_CHECK(argv, "user priority");
buf[buflen++] = (uint8)strtoul(*argv, NULL, 0);
/* unaligned! */
fc = (dot11_tclas_fc_t *) (buf + buflen);
memset((void *)fc, 0, sizeof(dot11_tclas_fc_t));
/* Parse frame classifier type and mask */
TCLAS_ARG_CHECK(argv, "type");
fc->hdr.type = (uint8)strtoul(*argv, NULL, 0);
TCLAS_ARG_CHECK(argv, "mask");
fc->hdr.mask = (uint8)strtoul(*argv, NULL, 0);
if (fc->hdr.type == DOT11_TCLAS_FC_0_ETH) {
dot11_tclas_fc_0_eth_t *fc_eth = (dot11_tclas_fc_0_eth_t *)fc;
TCLAS_ARG_CHECK(argv, "src mac");
if (strlen(*argv) > 1) {
if (!wl_ether_atoe(*argv, (struct ether_addr *)fc_eth->sa)) {
printf(" ERROR: no valid src ether addr provided\n");
return BCME_ERROR;
}
}
else
memset(fc_eth->sa, 0, ETHER_ADDR_LEN);
TCLAS_ARG_CHECK(argv, "dst mac");
if (strlen(*argv) > 1) {
if (!wl_ether_atoe(*argv, (struct ether_addr *)fc_eth->da)) {
printf(" ERROR: no valid dst ether addr provided\n");
return BCME_ERROR;
}
}
else
memset(fc_eth->da, 0, ETHER_ADDR_LEN);
TCLAS_ARG_CHECK(argv, "ether type");
fc_eth->eth_type = hton16((uint16)strtoul(*argv, NULL, 0));
buflen += DOT11_TCLAS_FC_0_ETH_LEN;
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
}
else if (fc->hdr.type == DOT11_TCLAS_FC_1_IP ||
fc->hdr.type == DOT11_TCLAS_FC_4_IP_HIGHER) {
uint8 version;
TCLAS_ARG_CHECK(argv, "ip version");
version = (uint8)strtoul(*argv, NULL, 0);
if (version == IP_VER_6) {
dot11_tclas_fc_4_ipv6_t *fc_ipv6 = (dot11_tclas_fc_4_ipv6_t *)fc;
uint32 fl;
fc_ipv6->version = version;
TCLAS_ARG_CHECK(argv, "ipv6 source ip");
if (!wl_atoipv6(*argv, (struct ipv6_addr *)&fc_ipv6->saddr)) {
printf("incorrect ipv6 source ip format\n");
return BCME_ERROR;
}
TCLAS_ARG_CHECK(argv, "ipv6 dest ip");
if (!wl_atoipv6(*argv, (struct ipv6_addr *)&fc_ipv6->daddr)) {
printf("incorrect ipv6 dest ip format\n");
return BCME_ERROR;
}
TCLAS_ARG_CHECK(argv, "ipv6 source port");
fc_ipv6->src_port = hton16((uint16)strtoul(*argv, NULL, 0));
TCLAS_ARG_CHECK(argv, "ipv6 dest port");
fc_ipv6->dst_port = hton16((uint16)strtoul(*argv, NULL, 0));
TCLAS_ARG_CHECK(argv, "ipv6 dscp");
fc_ipv6->dscp = (uint8)strtoul(*argv, NULL, 0);
TCLAS_ARG_CHECK(argv, "ipv6 next header");
fc_ipv6->nexthdr = (uint8)strtoul(*argv, NULL, 0);
TCLAS_ARG_CHECK(argv, "ipv6 flow label");
fl = (uint32) strtoul(*argv, NULL, 0);
fc_ipv6->flow_lbl[2] = (fl >> 16) & 0xFF;
fc_ipv6->flow_lbl[1] = (fl >> 8) & 0xFF;
fc_ipv6->flow_lbl[0] = (fl >> 0) & 0xFF;
buflen += DOT11_TCLAS_FC_4_IPV6_LEN;
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
} else
if (version == IP_VER_4) {
dot11_tclas_fc_1_ipv4_t *fc_ipv4 = (dot11_tclas_fc_1_ipv4_t *)fc;
fc_ipv4->version = version;
TCLAS_ARG_CHECK(argv, "ipv4 source ip");
if (!wl_atoip(*argv, (struct ipv4_addr *)&fc_ipv4->src_ip)) {
printf("incorrect source ip format\n");
return BCME_ERROR;
}
TCLAS_ARG_CHECK(argv, "ipv4 dest ip");
if (!wl_atoip(*argv, (struct ipv4_addr *)&fc_ipv4->dst_ip)) {
printf("incorrect dest ip format\n");
return BCME_ERROR;
}
TCLAS_ARG_CHECK(argv, "ipv4 source port");
fc_ipv4->src_port = (uint16)strtoul(*argv, NULL, 0);
fc_ipv4->src_port = hton16(fc_ipv4->src_port);
TCLAS_ARG_CHECK(argv, "ipv4 dest port");
fc_ipv4->dst_port = (uint16)strtoul(*argv, NULL, 0);
fc_ipv4->dst_port = hton16(fc_ipv4->dst_port);
TCLAS_ARG_CHECK(argv, "ipv4 dscp");
fc_ipv4->dscp = (uint8)strtoul(*argv, NULL, 0);
TCLAS_ARG_CHECK(argv, "ipv4 protocol");
fc_ipv4->protocol = (uint8)strtoul(*argv, NULL, 0);
buflen += DOT11_TCLAS_FC_1_IPV4_LEN;
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
} else
return BCME_ERROR;
} else {
printf("Unsupported frame classifier type 0x%2x\n", fc->hdr.type);
return BCME_ERROR;
}
return err;
}
static int
wl_tclas_del(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen, totlen;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
totlen = buflen + 2;
/* Set initial length */
buf[buflen] = 0; /* first value present for init index */
buf[buflen + 1] = 0; /* second value present for delete count */
/* Check idx and add if present */
if (*++argv == NULL)
goto set;
buf[buflen] = 1;
buf[buflen + 1] = (uint8)strtoul(*argv, NULL, 0);
/* Check len and add if present */
if (*++argv == NULL)
goto set;
buf[buflen] = 2;
buf[buflen + 2] = (uint8)strtoul(*argv, NULL, 0);
if (*++argv) {
printf("Too much args provided\n");
return BCME_ERROR;
}
set:
err = wlu_set(wl, WLC_SET_VAR, buf, totlen);
return err;
}
static void
wl_tclas_dump(wl_tclas_t *tclas)
{
dot11_tclas_fc_hdr_t *fc = &tclas->fc.hdr;
dot11_tclas_fc_0_eth_t *eth = &tclas->fc.t0_eth;
dot11_tclas_fc_1_ipv4_t *ipv4 = &tclas->fc.t1_ipv4;
dot11_tclas_fc_4_ipv6_t *ipv6 = &tclas->fc.t4_ipv6;
#define TCDMP(x) ((fc->mask & (x))?'+':'-')
printf("up %d type %d mask 0x%x ", tclas->user_priority, fc->type, fc->mask);
/* Check frame classifier parameter type */
if (fc->type == DOT11_TCLAS_FC_0_ETH) {
printf("(Eth: ");
printf("%cSA %s ", TCDMP(1), wl_ether_etoa((struct ether_addr*)eth->sa));
printf("%cDA %s ", TCDMP(2), wl_ether_etoa((struct ether_addr*)eth->da));
printf("%ctype 0x%04x", TCDMP(4), ntoh16(eth->eth_type));
printf(")\n");
} else if (fc->type == DOT11_TCLAS_FC_1_IP ||
(fc->type == DOT11_TCLAS_FC_4_IP_HIGHER && ipv4->version == IP_VER_4)) {
/* Check parameter type 1/4 IPv4 version */
printf("(IPv4: ");
printf("%cver %d ", TCDMP(1), ipv4->version);
printf("%csip %s ", TCDMP(2), wl_iptoa((struct ipv4_addr *)&ipv4->src_ip));
printf("%cdip %s ", TCDMP(4), wl_iptoa((struct ipv4_addr *)&ipv4->dst_ip));
printf("%csp %d ", TCDMP(8), ntoh16(ipv4->src_port));
printf("%cdp %d ", TCDMP(16), ntoh16(ipv4->dst_port));
printf("%cdscp 0x%x ", TCDMP(32), ipv4->dscp);
printf("%cprot %d", TCDMP(64), ipv4->protocol);
printf(")\n");
} else if (fc->type == DOT11_TCLAS_FC_4_IP_HIGHER && ipv6->version == IP_VER_6) {
/* Check parameter type 4 IPv6 version */
printf("(IPv6: ");
printf("%cver %d ", TCDMP(1), ipv6->version);
printf("%csip %s ", TCDMP(2), wl_ipv6toa(ipv6->saddr));
printf("%cdip %s ", TCDMP(4), wl_ipv6toa(ipv6->daddr));
printf("%csp %d ", TCDMP(8), ntoh16(ipv6->src_port));
printf("%cdp %d ", TCDMP(16), ntoh16(ipv6->dst_port));
printf("%cdscp 0x%x ", TCDMP(32), ipv6->dscp);
printf("%cprot %d", TCDMP(64), ipv6->nexthdr);
printf("%cfl %d", TCDMP(64),
ipv6->flow_lbl[2]<<16 | ipv6->flow_lbl[1]<<8 | ipv6->flow_lbl[0]);
printf(")\n");
} else {
printf("(type unsupported)\n");
}
}
static int
wl_tclas_list_parse(wl_tclas_list_t *list)
{
uint32 i;
uint8 *ptr = (uint8 *)&list->tclas[0];
int retlen = 0;
for (i = 0; i < list->num; i++) {
wl_tclas_t *tclas = (wl_tclas_t *)ptr;
printf("tclas idx %d: ", i);
wl_tclas_dump(tclas);
retlen += WL_TCLAS_FIXED_SIZE + tclas->fc_len;
/* Move to the next tclas frame classifier parameter */
ptr += WL_TCLAS_FIXED_SIZE + tclas->fc_len;
}
return retlen;
}
static int
wl_tclas_list(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_tclas_list_t *tclas_list = NULL;
void *ptr = NULL;
if (*++argv) {
printf("Too much args provided\n");
return BCME_ERROR;
}
err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr);
if (err == BCME_OK) {
tclas_list = (wl_tclas_list_t *)ptr;
if (tclas_list->num == 0)
printf("No tclas frame classifier parameter entry\n");
else
wl_tclas_list_parse(tclas_list);
}
return err;
}
#ifdef WLWNM
static int
wl_wnm_tfsreq_add(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = -1, buflen;
wl_tfs_req_t tfs_req;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
strcpy(buf, "wnm_tfsreq_add");
buflen = strlen("wnm_tfsreq_add") + 1;
if (argc != 5 || *++(argv) == NULL) {
printf("Incorrect args provided\n");
return BCME_ERROR;
}
tfs_req.tfs_id = (uint8)strtoul(*argv++, NULL, 0);
if (*argv != NULL)
tfs_req.tfs_actcode = (uint8)strtoul(*argv++, NULL, 0);
else {
printf("Incorrect args provided\n");
return BCME_ERROR;
}
if (*argv != NULL)
tfs_req.tfs_subelem_id = (uint8)strtoul(*argv++, NULL, 0);
else {
printf("Incorrect args provided\n");
return BCME_ERROR;
}
if (*argv != NULL)
tfs_req.send = strtoul(*argv, NULL, 0) == 0 ? FALSE : TRUE;
else {
printf("Incorrect args provided\n");
return BCME_ERROR;
}
if (tfs_req.tfs_id == 0 ||
tfs_req.tfs_actcode > 3 ||
tfs_req.tfs_subelem_id > 1) {
printf("Input args not in range\n");
return BCME_ERROR;
}
buf[buflen++] = tfs_req.tfs_id;
buf[buflen++] = tfs_req.tfs_actcode;
buf[buflen++] = tfs_req.tfs_subelem_id;
buf[buflen++] = tfs_req.send;
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
static int
wl_wnm_dms_set(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wl_dms_set_t *dms_set;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
dms_set = (wl_dms_set_t *) (buf + buflen);
dms_set->user_id = 0;
dms_set->tclas_proc = 0;
buflen += sizeof(wl_dms_set_t);
if (*++(argv) == NULL) {
printf("Missing <send> argument\n");
return BCME_ERROR;
}
dms_set->send = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
dms_set->user_id = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
dms_set->tclas_proc = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv)) {
printf("extra argument\n");
return BCME_ERROR;
}
set:
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
static int
wl_wnm_dms_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
wl_dms_status_t *dms_list = (wl_dms_status_t *)buf;
uint8 *p = (uint8 *)dms_list->desc;
strcpy(buf, argv[0]);
ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
dms_list->cnt = dtoh32(dms_list->cnt);
while (dms_list->cnt--) {
wl_dms_desc_t *desc = (wl_dms_desc_t *)p;
printf("DMS desc UserID %d:\n", desc->user_id);
printf("\tstatus:%d token:%d DMS ID:%d TCLAS proc:%d\n",
desc->status, desc->token, desc->dms_id, desc->tclas_proc);
p += WL_DMS_DESC_FIXED_SIZE;
if (desc->mac_len) {
printf("\tRegistered STA:\n");
while (desc->mac_len) {
printf("\t\t- %s\n", wl_ether_etoa((struct ether_addr*)p));
p += ETHER_ADDR_LEN;
desc->mac_len -= ETHER_ADDR_LEN;
}
}
printf("\tTCLAS:\n");
while (desc->tclas_len) {
wl_tclas_t *tclas = (wl_tclas_t *)p;
printf("\t\t- ");
wl_tclas_dump(tclas);
p += WL_TCLAS_FIXED_SIZE + tclas->fc_len;
desc->tclas_len -= WL_TCLAS_FIXED_SIZE + tclas->fc_len;
}
}
return 0;
}
static int
wl_wnm_dms_term(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wl_dms_term_t *dms_term;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
dms_term = (wl_dms_term_t *) (buf + buflen);
dms_term->user_id = 0;
buflen += sizeof(wl_dms_term_t);
if (*++(argv) == NULL) {
printf("Missing <del> argument\n");
return BCME_ERROR;
}
dms_term->del = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
dms_term->user_id = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv)) {
printf("extra argument\n");
return BCME_ERROR;
}
set:
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
static int
wl_wnm_service_term(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wl_service_term_t *term;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
term = (wl_service_term_t *) (buf + buflen);
term->u.dms.user_id = 0;
buflen += sizeof(wl_service_term_t);
if (*++(argv) == NULL) {
printf("Missing <service> argument\n");
return BCME_ERROR;
}
term->service = (uint8) strtoul(*argv, NULL, 0);
/* Need per service processing from here? */
if (*++(argv) == NULL) {
printf("Missing <del> argument\n");
return BCME_ERROR;
}
term->u.dms.del = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
term->u.dms.user_id = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv)) {
printf("extra argument\n");
return BCME_ERROR;
}
set:
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
static int
wl_wnm_timbc_offset(void *wl, cmd_t *cmd, char **argv)
{
wl_timbc_offset_t *timbc_offset;
char *p = buf;
int size;
UNUSED_PARAMETER(cmd);
size = sprintf(p, "%s", *argv++) + 1;
if (*argv == NULL) {
/* retrieve bss idle period if argument count incorrect */
int err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN);
if (err < 0)
return err;
timbc_offset = (wl_timbc_offset_t *)buf;
printf("TIMBC offset: %d, tsf_present: %d, fix_interval: %d, rate_override: %d\n",
timbc_offset->offset, timbc_offset->tsf_present, timbc_offset->fix_intv,
timbc_offset->rate_override);
return BCME_OK;
} else {
if (!isdigit((int)*argv[0]))
return BCME_ERROR;
timbc_offset = (wl_timbc_offset_t *)(p + size);
timbc_offset->offset = (int16)strtoul(*argv++, NULL, 0);
timbc_offset->tsf_present = TRUE;
timbc_offset->fix_intv = 0;
timbc_offset->rate_override = 0;
if (*argv != NULL) {
timbc_offset->tsf_present = (bool)strtoul(*argv++, NULL, 0);
if (*argv != NULL) {
timbc_offset->fix_intv = (uint16)strtoul(*argv++, NULL, 0);
if (*argv != NULL) {
timbc_offset->rate_override =
(uint16)strtoul(*argv++, NULL, 0);
}
}
}
return wlu_set(wl, WLC_SET_VAR, buf, size + sizeof(wl_timbc_offset_t));
}
}
static int
wl_wnm_timbc_set(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wl_timbc_set_t *req;
UNUSED_PARAMETER(cmd);
buflen = sprintf(buf, "%s", *argv) + 1;
req = (wl_timbc_set_t *) (buf + buflen);
req->flags = 0;
req->rate_min = 0;
req->rate_max = 0;
buflen += sizeof(wl_timbc_set_t);
if (*++(argv) == NULL) {
printf("Missing <interval> argument\n");
return BCME_ERROR;
}
req->interval = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
req->flags = (uint8) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL)
goto set;
req->rate_min = htod16(rate_string2int(*argv));
if (*++(argv) == NULL)
goto set;
req->rate_max = htod16(rate_string2int(*argv));
if (*++(argv)) {
printf("Too many arguments\n");
return BCME_ERROR;
}
set:
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
static int
wl_wnm_timbc_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
wl_timbc_status_t *status = (wl_timbc_status_t *)buf;
char hrate[16], lrate[16];
strcpy(buf, argv[0]);
ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
printf("TIM BC current status: %d status_ap: %d\n"
" interval: %d offset: %d\n"
" high rate: %s low rate: %s\n",
status->status_sta,
status->status_ap,
status->interval, status->offset,
rate_int2string(hrate, status->rate_high),
rate_int2string(lrate, status->rate_low));
return BCME_OK;
}
static int
wl_wnm_maxidle(void *wl, cmd_t *cmd, char **argv)
{
struct {
int period;
int protect;
} params = { 0, 0 };
char *endptr;
if (*++argv == NULL) {
/* retrieve bss idle period if argument count incorrect */
int err = wlu_iovar_getint(wl, cmd->name, &params.period);
if (err < 0)
return err;
printf("BSS Max Idle Period: %d\n", dtoh32(params.period));
return BCME_OK;
} else {
/* parse the idle period */
params.period = strtoul(*argv++, &endptr, 0);
htod32(params.period);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
/* parse the optional keep-alive protect flag */
if (*argv) {
params.protect = strtoul(*argv, &endptr, 0);
htod32(params.protect);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
}
return wlu_var_setbuf(wl, cmd->name, &params, sizeof(params));
}
}
static int
wl_wnm_bsstrans_req(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
wl_bsstrans_req_t bsstrans_req;
int buflen = sprintf(buf, "%s", *argv) + 1;
int tmp;
UNUSED_PARAMETER(cmd);
/* req mode parsing */
if (*++argv == NULL) {
fprintf(stderr, "%s: error: reqmode missing\n", __FUNCTION__);
return BCME_ERROR;
}
bsstrans_req.reqmode = strtoul(*argv, NULL, 0);
/* tbtt parsing */
if (*++argv == NULL) {
fprintf(stderr, "%s: error: tbtt missing\n", __FUNCTION__);
return BCME_ERROR;
}
tmp = strtoul(*argv, NULL, 0);
if (tmp > 65535 || tmp < 0) {
fprintf(stderr, "%s: error: tbtt out of range(%d)\n", __FUNCTION__, tmp);
return BCME_ERROR;
}
else
bsstrans_req.tbtt = tmp;
/* dur parsing */
if (*++argv == NULL) {
fprintf(stderr, "%s: error: dur missing\n", __FUNCTION__);
return BCME_ERROR;
}
tmp = strtoul(*argv, NULL, 0);
if (tmp > 65535 || tmp < 0) {
fprintf(stderr, "%s: error: dur out of range(%d)\n", __FUNCTION__, tmp);
return BCME_ERROR;
}
else
bsstrans_req.dur = tmp;
/* unicast/broadcast parsing */
if (*++argv != NULL)
bsstrans_req.unicast = strtoul(*argv, NULL, 0);
else
bsstrans_req.unicast = 1;
memcpy(&buf[buflen], &bsstrans_req, sizeof(bsstrans_req));
buflen += sizeof(bsstrans_req);
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
/*
* Get or Set wnm keepalive configuration
*/
static int
wl_wnm_keepalives_max_idle(void *wl, cmd_t *cmd, char **argv)
{
int err, argc;
keepalives_max_idle_t keepalive;
UNUSED_PARAMETER(cmd);
argv++;
if (*argv == NULL) {
/* get current params */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &keepalive,
(sizeof(keepalive)))) < 0)
return (err);
printf("Keepalives_max_idle parameters -\n");
printf("num_of_keepalives_per_bss_max_idle\t\t= %d\nmkeep_alive_index\t= %d"
"\nmax_interval\t= %d\n", keepalive.keepalive_count,
keepalive.mkeepalive_index, keepalive.max_interval);
} else {
char *endptr;
/* Validate num of entries */
for (argc = 0; argv[argc]; argc++);
if (argc < 2 || argc > 3)
return BCME_ERROR;
keepalive.keepalive_count = strtol(argv[0], &endptr, 0);
keepalive.mkeepalive_index = strtol(argv[1], &endptr, 0);
if (argc == 3)
keepalive.max_interval = strtol(argv[2], &endptr, 0);
else
keepalive.max_interval = 0;
/* Set params */
err = wlu_iovar_set(wl, cmd->name, (void *) &keepalive,
(sizeof(keepalives_max_idle_t)));
}
return err;
}
#endif /* WLWNM */
static int
wl_tsf(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "tsf";
struct tsf {
uint32 low;
uint32 high;
} tsf_buf;
char *endptr;
int err;
UNUSED_PARAMETER(cmd);
/* toss the command name */
argv++;
if (*argv == NULL) {
/* get */
err = wlu_iovar_get(wl, cmdname, &tsf_buf, sizeof(tsf_buf));
if (err)
return err;
printf("0x%08X 0x%08X\n", htod32(tsf_buf.high), htod32(tsf_buf.low));
} else {
/* set */
if (argv[1] == NULL)
return BCME_USAGE_ERROR;
tsf_buf.high = (uint32)strtoul(*argv, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "%s: %s: error parsing \"%s\" as an integer\n",
wlu_av0, cmdname, *argv);
return BCME_USAGE_ERROR;
}
argv++;
tsf_buf.low = (uint32)strtoul(*argv, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "%s: %s: error parsing \"%s\" as an integer\n",
wlu_av0, cmdname, *argv);
return BCME_USAGE_ERROR;
}
tsf_buf.low = dtoh32(tsf_buf.low);
tsf_buf.high = dtoh32(tsf_buf.high);
err = wlu_iovar_set(wl, cmdname, &tsf_buf, sizeof(tsf_buf));
if (err)
return err;
}
return err;
}
static int
wl_mfp_config(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
int flag = 0;
const char *cmdname = "mfp";
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)buf = flag;
}
err = wlu_iovar_set(wl, cmdname, buf, 256);
return (err);
}
static int
wl_mfp_sha256(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
int flag = 0;
const char *cmdname = "mfp_sha256";
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)buf = flag;
err = wlu_iovar_set(wl, cmdname, buf, 256);
} else {
/* get */
err = wlu_iovar_getint(wl, cmdname, &flag);
if (err == BCME_OK)
printf("%d\n", flag);
}
return (err);
}
static int
wl_mfp_sa_query(void *wl, cmd_t *cmd, char **argv)
{
wl_sa_query_t * query;
int argc;
int err = 0;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if ((query = (wl_sa_query_t *) malloc(sizeof(wl_sa_query_t))) == NULL) {
printf("unable to allocate frame \n");
return BCME_NOMEM;
}
memset(query, 0, sizeof(wl_sa_query_t));
/* add the flag */
if (argc > 1 && argv[1]) {
query->flag = htod32(atoi(argv[1]));
}
/* add the action */
if (argc > 2 && argv[2]) {
query->action = htod32(atoi(argv[2]));
}
/* add the id */
if (argc > 3 && argv[3]) {
query->id = htod32(atoi(argv[3]));
}
err = wlu_var_setbuf(wl, "mfp_sa_query", query, sizeof(wl_sa_query_t));
free(query);
return (err);
}
static int
wl_mfp_disassoc(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "mfp_disassoc";
int argc;
int flag;
char varbuf[256];
int err;
UNUSED_PARAMETER(cmd);
memset(varbuf, 0, 256);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* add the action */
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)varbuf = flag;
}
if (argc > 2 && argv[2]) {
flag = htod32(atoi(argv[2]));
*(int *)(varbuf + sizeof(flag)) = flag;
}
err = wlu_iovar_set(wl, cmdname, varbuf, 256);
return err;
}
static int
wl_mfp_deauth(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "mfp_deauth";
int argc;
int flag;
char varbuf[256];
int err;
UNUSED_PARAMETER(cmd);
memset(varbuf, 0, 256);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* add the action */
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)varbuf = flag;
}
if (argc > 2 && argv[2]) {
flag = htod32(atoi(argv[2]));
*(int *)(varbuf + sizeof(flag)) = flag;
}
err = wlu_iovar_set(wl, cmdname, varbuf, 256);
return err;
}
static int
wl_mfp_assoc(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "mfp_assoc";
int argc;
int flag;
char varbuf[256];
int err;
UNUSED_PARAMETER(cmd);
memset(varbuf, 0, 256);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* add the action */
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)varbuf = flag;
}
if (argc > 2 && argv[2]) {
flag = htod32(atoi(argv[2]));
*(int *)(varbuf + sizeof(int)) = flag;
}
err = wlu_iovar_set(wl, cmdname, varbuf, 256);
return err;
}
static int
wl_mfp_auth(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "mfp_auth";
int argc;
int flag;
char varbuf[256];
int err;
UNUSED_PARAMETER(cmd);
memset(varbuf, 0, 256);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* add the action */
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)varbuf = flag;
}
if (argc > 2 && argv[2]) {
flag = htod32(atoi(argv[2]));
*(int *)(varbuf + sizeof(int)) = flag;
}
err = wlu_iovar_set(wl, cmdname, varbuf, 256);
return err;
}
static int
wl_mfp_reassoc(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "mfp_reassoc";
int argc;
int flag;
char varbuf[256];
int err;
UNUSED_PARAMETER(cmd);
memset(varbuf, 0, 256);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* add the action */
if (argc > 1 && argv[1]) {
flag = htod32(atoi(argv[1]));
*(int *)varbuf = flag;
}
if (argc > 2 && argv[2]) {
flag = htod32(atoi(argv[2]));
*(int *)(varbuf + sizeof(int)) = flag;
}
err = wlu_iovar_set(wl, cmdname, varbuf, 256);
return err;
}
#ifdef EVENT_LOG_COMPILE
static int wl_event_log_set_init(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "event_log_set_init";
wl_el_set_params_t pars;
int argc;
int err;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc != 3) {
return BCME_USAGE_ERROR;
}
memset(&pars, sizeof(wl_el_set_params_t), 0);
pars.set = atoi(argv[1]);
pars.size = htod32(atoi(argv[2]));
err = wlu_iovar_set(wl, cmdname, &pars, sizeof(wl_el_set_params_t));
return err;
}
static int wl_event_log_set_expand(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "event_log_set_expand";
wl_el_set_params_t pars;
int argc;
int err;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc != 3) {
return BCME_USAGE_ERROR;
}
memset(&pars, sizeof(wl_el_set_params_t), 0);
pars.set = atoi(argv[1]);
pars.size = htod32(atoi(argv[2]));
err = wlu_iovar_set(wl, cmdname, &pars, sizeof(wl_el_set_params_t));
return err;
}
static int wl_event_log_set_shrink(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "event_log_set_shrink";
wl_el_set_params_t pars;
int argc;
int err;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc != 2) {
return BCME_USAGE_ERROR;
}
memset(&pars, sizeof(wl_el_set_params_t), 0);
pars.set = atoi(argv[1]);
err = wlu_iovar_set(wl, cmdname, &pars, sizeof(wl_el_set_params_t));
return err;
}
static int wl_event_log_tag_control(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "event_log_tag_control";
wl_el_tag_params_t pars;
int argc;
int err;
int flags = 0;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc < 4) {
return BCME_USAGE_ERROR;
}
argv++;
memset(&pars, sizeof(wl_el_tag_params_t), 0);
pars.tag = htod16(atoi(*argv++));
pars.set = atoi(*argv++);
while (*argv) {
if (!strcmp(*argv, "LOG")) {
flags |= EVENT_LOG_TAG_FLAG_LOG;
} else if (!strcmp(*argv, "PRINT")) {
flags |= EVENT_LOG_TAG_FLAG_PRINT;
} else if (!strcmp(*argv, "NONE")) {
flags |= EVENT_LOG_TAG_FLAG_NONE;
} else {
return BCME_USAGE_ERROR;
}
argv++;
}
pars.flags = flags;
err = wlu_iovar_set(wl, cmdname, &pars, sizeof(wl_el_set_params_t));
return err;
}
#endif /* EVENT_LOG_COMPILE */
static int
wl_spatial_policy(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err, i, *reply;
int mode[SPATIAL_MODE_MAX_IDX] = {-1, -1, -1, -1, -1};
/* Order is 2G, 5G-LOW, 5G-MID, 5G-HIGH, 5G-UPPER
* if only one argument given, than all band or sub-band take the
* same value
*/
if (!*++argv) {
bool all_same = TRUE;
if ((err = wlu_var_getbuf(wl, cmd->name, &mode, sizeof(mode), &ptr)) < 0)
return err;
reply = (int *)ptr;
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++) {
/* check if return values for each band/sub-band is same or not */
if (reply[i-1] != reply[i])
all_same = FALSE;
}
if (all_same)
printf("%2d\n", reply[0]);
else {
printf("2.4GHz : %2d\n", reply[SPATIAL_MODE_2G_IDX]);
printf("5GHz (lower) : %2d\n", reply[SPATIAL_MODE_5G_LOW_IDX]);
printf("5GHz (middle): %2d\n", reply[SPATIAL_MODE_5G_MID_IDX]);
printf("5GHz (high) : %2d\n", reply[SPATIAL_MODE_5G_HIGH_IDX]);
printf("5GHz (upper) : %2d\n", reply[SPATIAL_MODE_5G_UPPER_IDX]);
}
return 0;
}
mode[0] = atoi(*argv);
if (!*++argv) {
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++)
mode[i] = mode[0];
} else {
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++) {
mode[i] = atoi(*argv);
if (!*++argv && i < (SPATIAL_MODE_MAX_IDX - 1)) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
}
}
err = wlu_var_setbuf(wl, cmd->name, &mode, sizeof(mode));
return err;
}
static int
wl_ratetbl_ppr(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err, i, *reply;
int val[12];
/* Order is 2G, 5G-LOW, 5G-MID, 5G-HIGH, 5G-UPPER
* if only one argument given, than all band or sub-band take the
* same value
*/
memset(&val, 0, sizeof(val));
if (!*++argv) {
if ((err = wlu_var_getbuf(wl, cmd->name, &val, sizeof(val), &ptr)) < 0)
return err;
reply = (int *)ptr;
for (i = 0; i < 12; i++)
printf("%s: %2d\n", (reply[i] & 0x80) ? "OFDM" : "CCK ", (reply[i] & 0x7f));
return 0;
}
val[0] = atoi(*argv++);
val[1] = atoi(*argv++);
err = wlu_var_setbuf(wl, cmd->name, &val, sizeof(val));
return err;
}
static int
wlu_mempool(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int ret;
int i;
wl_mempool_stats_t *stats;
bcm_mp_stats_t *mp_stats;
UNUSED_PARAMETER(argv);
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
stats = (wl_mempool_stats_t *) ptr;
mp_stats = stats->s;
printf("%-8s %8s %8s %8s %8s %8s\n", "Name", "SZ", "Max", "Curr", "HiWater", "Failed");
for (i = 0; i < stats->num; i++) {
printf("%-8s %8d %8d %8d %8d %8d\n", mp_stats->name, (int) mp_stats->objsz,
mp_stats->nobj, mp_stats->num_alloc, mp_stats->high_water,
mp_stats->failed_alloc);
mp_stats++;
}
return (0);
}
static int
wl_ie(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int err;
uchar *data;
int bsscfg_idx = 0;
int consumed = 0;
int iecount;
ie_setbuf_t *ie_setbuf;
ie_getbuf_t param;
uchar datalen, type, count, col;
/* parse a bsscfg_idx option if present */
if ((err = wl_cfg_option(argv + 1, argv[0], &bsscfg_idx, &consumed)) != 0)
return err;
if (consumed)
argv = argv + consumed;
else
bsscfg_idx = -1;
if (!*++argv) {
fprintf(stderr, "missing parameter type\n");
return BCME_USAGE_ERROR;
}
/* get IE type */
type = (uchar)atoi(argv[0]);
if (!*++argv) {
param.id = type;
ptr = buf;
if (bsscfg_idx == -1)
err = wlu_var_getbuf(wl, cmd->name, &param, sizeof(param), &ptr);
else
err = wl_bssiovar_getbuf(wl, cmd->name, bsscfg_idx, &param, sizeof(param),
buf, WLC_IOCTL_MAXLEN);
if (err == 0) {
data = (uchar *)ptr;
datalen = data[1]+2;
printf("%s len %d\n", cmd->name, datalen);
printf("%s Data:\n", cmd->name);
for (count = 0; (count < datalen);) {
for (col = 0; (col < MAX_DATA_COLS) &&
(count < datalen); col++, count++) {
printf("%02x", *data++);
}
printf("\n");
}
}
else {
fprintf(stderr, "Error %d getting IOVar\n", err);
}
return err;
}
/* get IE length */
datalen = (uchar)atoi(argv[0]);
if (datalen > 0) {
if (!argv[1]) {
fprintf(stderr, "Data bytes should be specified for IE of length %d\n",
datalen);
return BCME_USAGE_ERROR;
}
else {
/* Ensure each data byte is 2 characters long */
if ((int)strlen (argv[1]) < (datalen * 2)) {
fprintf(stderr, "Please specify all the data bytes for this IE\n");
return BCME_BADARG;
}
}
}
if ((datalen == 0) && (argv[1] != NULL))
fprintf(stderr, "Ignoring data bytes for IE of length %d\n", datalen);
count = sizeof(ie_setbuf_t) + datalen - 1;
data = malloc(count);
if (data == NULL) {
fprintf(stderr, "memory alloc failure\n");
return BCME_NOMEM;
}
ie_setbuf = (ie_setbuf_t *) data;
/* Copy the ie SET command ("add") to the buffer */
strncpy(ie_setbuf->cmd, "add", VNDR_IE_CMD_LEN - 1);
ie_setbuf->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
/* Buffer contains only 1 IE */
iecount = htod32(1);
memcpy((void *)&ie_setbuf->ie_buffer.iecount, &iecount, sizeof(int));
/* Now, add the IE to the buffer */
ie_setbuf->ie_buffer.ie_list[0].ie_data.id = type;
ie_setbuf->ie_buffer.ie_list[0].ie_data.len = datalen;
if (datalen > 0) {
if ((err = get_ie_data ((uchar *)argv[1],
(uchar *)&ie_setbuf->ie_buffer.ie_list[0].ie_data.data[0],
datalen))) {
free(data);
fprintf(stderr, "Error parsing data arg\n");
return err;
}
}
if (bsscfg_idx == -1)
err = wlu_var_setbuf(wl, cmd->name, data, count);
else
err = wlu_bssiovar_setbuf(wl, cmd->name, bsscfg_idx,
data, count, buf, WLC_IOCTL_MAXLEN);
free(data);
return (err);
}
static int
wl_wnm_url(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int err;
uchar *data;
uchar datalen, count;
wnm_url_t *url;
if (!*++argv) {
err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr);
if (err == 0) {
data = (uchar *)ptr;
datalen = data[0];
data ++;
*(data + datalen) = '\0';
printf("%s URL len %d\n", cmd->name, datalen);
printf("%s URL: %s\n", cmd->name, data);
}
else {
fprintf(stderr, "Error %d getting IOVar\n", err);
}
return err;
}
/* get URL length */
datalen = (uchar)atoi(argv[0]);
if (datalen > 0) {
if (!argv[1]) {
fprintf(stderr, "URL string should be specified for URL length %d\n",
datalen);
return BCME_BADARG;
}
else {
if ((int)strlen (argv[1]) != datalen) {
fprintf(stderr, "length is not matching to string\n");
return BCME_BADARG;
}
}
}
if ((datalen == 0) && (argv[1] != NULL))
fprintf(stderr, "Ignoring data bytes for length %d\n", datalen);
count = sizeof(wnm_url_t) + datalen - 1;
data = malloc(count);
if (data == NULL) {
fprintf(stderr, "memory alloc failure\n");
return BCME_NOMEM;
}
url = (wnm_url_t *) data;
url->len = datalen;
if (datalen > 0) {
memcpy(url->data, argv[1], datalen);
}
err = wlu_var_setbuf(wl, cmd->name, data, count);
free(data);
return (err);
}
/* Restore the ignored warnings status */
static int
wl_sleep_ret_ext(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int argc, i;
uint32 val;
char *endptr = NULL;
wl_pm2_sleep_ret_ext_t sleep_ret_ext;
wl_pm2_sleep_ret_ext_t* sleep_ret_ext_ptr;
/* Skip the command name */
UNUSED_PARAMETER(cmd);
argv++;
/* If no arguments are given, print the existing settings */
argc = ARGCNT(argv);
if (argc == 0) {
char *logic_str;
/* Get and print the values */
if ((ret = wlu_var_getbuf_med(wl, cmd->name, NULL, 0, (void*) &sleep_ret_ext_ptr)))
return ret;
if (sleep_ret_ext_ptr->logic == WL_DFRTS_LOGIC_OFF)
logic_str = "DISABLED";
else if (sleep_ret_ext_ptr->logic == WL_DFRTS_LOGIC_OR)
logic_str = "OR";
else if (sleep_ret_ext_ptr->logic == WL_DFRTS_LOGIC_AND)
logic_str = "AND";
else
logic_str = "ERROR";
printf("logic: %d (%s)\n",
sleep_ret_ext_ptr->logic, logic_str);
if (sleep_ret_ext_ptr->logic != WL_DFRTS_LOGIC_OFF) {
printf("low_ms: %d\n", sleep_ret_ext_ptr->low_ms);
printf("high_ms: %d\n", sleep_ret_ext_ptr->high_ms);
printf("rx_pkts_threshold: %d\n",
sleep_ret_ext_ptr->rx_pkts_threshold);
printf("tx_pkts_threshold: %d\n",
sleep_ret_ext_ptr->tx_pkts_threshold);
printf("txrx_pkts_threshold: %d\n",
sleep_ret_ext_ptr->txrx_pkts_threshold);
printf("rx_bytes_threshold: %d\n",
sleep_ret_ext_ptr->rx_bytes_threshold);
printf("tx_bytes_threshold: %d\n",
sleep_ret_ext_ptr->tx_bytes_threshold);
printf("txrx_bytes_threshold: %d\n",
sleep_ret_ext_ptr->txrx_bytes_threshold);
}
return 0;
}
memset(&sleep_ret_ext, 0, sizeof(wl_pm2_sleep_ret_ext_t));
i = 0;
/* Get the first 'logic' argument. */
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
if (val != WL_DFRTS_LOGIC_OFF && val != WL_DFRTS_LOGIC_OR &&
val != WL_DFRTS_LOGIC_AND) {
printf("Invalid logic value %u\n", val);
goto usage;
}
sleep_ret_ext.logic = val;
++i;
/* If logic is 0 (disable) then no more arguments are needed */
if (sleep_ret_ext.logic == 0)
goto set;
if (argc < 9)
goto usage;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.low_ms = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.high_ms = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.rx_pkts_threshold = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.tx_pkts_threshold = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.txrx_pkts_threshold = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.rx_bytes_threshold = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.tx_bytes_threshold = val;
++i;
val = strtoul(argv[i], &endptr, 0);
if (*endptr != '\0')
goto usage;
sleep_ret_ext.txrx_bytes_threshold = val;
++i;
if (i != argc)
goto usage;
set:
return wlu_var_setbuf(wl, cmd->name, &sleep_ret_ext,
sizeof(wl_pm2_sleep_ret_ext_t));
usage:
printf("Usage: %s [logic] [<low_ms> <high_ms>"
" <rxP> <txP> <txrxP> <rxB> <txB> <txrxB>\n", wlu_av0);
printf("Parameters:\n");
printf("logic : 0=disable, 1=OR, 2=AND all non-zero switching thresholds.\n");
printf("low_ms : Low pm2_sleep_ret value.\n");
printf("high_ms : High pm2_sleep_ret value.\n");
printf("rxP : Switching threshold in # of rx packets.\n");
printf(" eg. Switch from the low to high FRTS value if rxP or\n");
printf(" more packets are received in a PM2 radio wake period.\n");
printf(" 0 means ignore this threshold.\n");
printf("txP : Switching threshold in # of tx packets.\n");
printf("txrxP : Switching threshold in # of combined tx+rx packets.\n");
printf("rxB : Switching threshold in # of rx bytes.\n");
printf("txB : Switching threshold in # of tx bytes.\n");
printf("txrxB : Switching threshold in # of combined tx+rx bytes.\n");
return -1;
}
static int wl_stamon_sta_config(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
wlc_stamon_sta_config_t stamon_cfg;
memset(&stamon_cfg, 0, sizeof(wlc_stamon_sta_config_t));
if (!*++argv) {
err = wlu_iovar_get(wl, cmd->name, &stamon_cfg,
sizeof(wlc_stamon_sta_config_t));
if (!err)
printf("%s \n", wl_ether_etoa(&stamon_cfg.ea));
} else {
if (!stricmp(*argv, "add"))
stamon_cfg.cmd = STAMON_CFG_CMD_ADD;
else if (!stricmp(*argv, "del"))
stamon_cfg.cmd = STAMON_CFG_CMD_DEL;
else {
printf("error: unknown operation option%s\n", *argv);
err = -1;
}
if (!err) {
argv++;
if (!*argv || !wl_ether_atoe(*argv, &stamon_cfg.ea)) {
printf(" ERROR: no valid ether addr provided\n");
err = -1;
} else {
err = wlu_iovar_set(wl, cmd->name,
&stamon_cfg, sizeof(wlc_stamon_sta_config_t));
}
}
}
return err;
}
static monitor_promisc_level_msg_t wl_monpromisc_level_msgs[] = {
{WL_MONPROMISC_PROMISC, "promisc"},
{WL_MONPROMISC_CTRL, "ctrl"},
{WL_MONPROMISC_FCS, "fcs"},
{0, NULL}
};
static int
wl_monitor_promisc_level(void *wl, cmd_t *cmd, char **argv)
{
int ret, i;
uint val = 0, last_val = 0;
uint promiscbitmap = 0, promiscbitmap_add = 0, promiscbitmap_del = 0;
char *endptr;
const char *cmdname = "monitor_promisc_level";
UNUSED_PARAMETER(cmd);
if ((ret = wlu_iovar_getint(wl, cmdname, (int *)&promiscbitmap) < 0)) {
return ret;
}
promiscbitmap = dtoh32(promiscbitmap);
if (!*++argv) {
printf("0x%x ", promiscbitmap);
for (i = 0; (val = wl_monpromisc_level_msgs[i].value); i++) {
if ((promiscbitmap & val) && (val != last_val))
printf(" %s", wl_monpromisc_level_msgs[i].string);
last_val = val;
}
printf("\n");
return (0);
}
while (*argv) {
char *s = *argv;
if (*s == '+' || *s == '-')
s++;
else
promiscbitmap_del = ~0; /* make the whole list absolute */
val = strtoul(s, &endptr, 0);
if (val == 0xFFFFFFFF) {
fprintf(stderr,
"Bits >32 are not supported on this driver version\n");
val = 1;
}
/* not an integer if not all the string was parsed by strtoul */
if (*endptr != '\0') {
for (i = 0; (val = wl_monpromisc_level_msgs[i].value); i++)
if (stricmp(wl_monpromisc_level_msgs[i].string, s) == 0)
break;
if (!val)
goto usage;
}
if (**argv == '-')
promiscbitmap_del |= val;
else
promiscbitmap_add |= val;
++argv;
}
promiscbitmap &= ~promiscbitmap_del;
promiscbitmap |= promiscbitmap_add;
promiscbitmap = htod32(promiscbitmap);
return (wlu_iovar_setint(wl, cmdname, (int)promiscbitmap));
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (val = wl_monpromisc_level_msgs[i].value); i++) {
if (val != last_val)
fprintf(stderr, "\n0x%04x %s", val, wl_monpromisc_level_msgs[i].string);
else
fprintf(stderr, ", %s", wl_monpromisc_level_msgs[i].string);
last_val = val;
}
fprintf(stderr, "\n");
return 0;
}
#if defined(DWDS)
static int
wl_dwds_config(void *wl, cmd_t *cmd, char **argv)
{
wlc_dwds_config_t dwds;
int err;
memset(&dwds, 0, sizeof(wlc_dwds_config_t));
if (!*++argv) {
printf("error: missing arguments\n");
return -1;
}
if (!stricmp(*argv, "enable"))
dwds.enable = 1;
else if (!stricmp(*argv, "disable"))
dwds.enable = 0;
else {
printf("error: unknown mode option %s\n", *argv);
return -1;
}
argv++;
/* look for sta/dwds */
if (!stricmp(*argv, "sta"))
dwds.mode = 1;
else if (!stricmp(*argv, "ap"))
dwds.mode = 0;
else {
printf("error: unknown mode option %s\n", *argv);
return -1;
}
argv++;
/* convert the ea string into an ea struct */
if (!*argv || !wl_ether_atoe(*argv, &dwds.ea)) {
printf(" ERROR: no valid ether addr provided\n");
return -1;
}
if ((err = wlu_iovar_set(wl, cmd->name, &dwds, sizeof(wlc_dwds_config_t))) < 0)
return err;
return (0);
}
#endif /* DWDS */
static int
wl_bss_peer_info(void *wl, cmd_t *cmd, char **argv)
{
bss_peer_list_info_t *info;
bss_peer_info_t *peer_info;
bss_peer_info_param_t param;
int err, i;
void *ptr;
memset(&param, 0, sizeof(bss_peer_info_param_t));
param.version = htod16(BSS_PEER_INFO_PARAM_CUR_VER);
if (*++argv) {
if (!wl_ether_atoe(*argv, &param.ea)) {
printf(" ERROR: no valid ether addr provided\n");
return -1;
}
}
if ((err = wlu_var_getbuf_med(wl, cmd->name, &param, sizeof(bss_peer_info_param_t),
&ptr)) < 0)
return err;
info = (bss_peer_list_info_t*)ptr;
if ((dtoh16(info->version) != BSS_PEER_LIST_INFO_CUR_VER) ||
(dtoh16(info->bss_peer_info_len) != sizeof(bss_peer_info_t))) {
printf("BSS peer info version/structure size mismatch driver %d firmware %d \r\n",
BSS_PEER_LIST_INFO_CUR_VER, dtoh16(info->version));
return -1;
}
if (WLC_IOCTL_MEDLEN < (BSS_PEER_LIST_INFO_FIXED_LEN +
(dtoh32(info->count) * sizeof(bss_peer_info_t)))) {
printf("ERROR : peer list received exceed the buffer size\r\n");
}
for (i = 0; i < (int)dtoh32(info->count); i++) {
peer_info = &info->peer_info[i];
peer_info->rateset.count = dtoh32(peer_info->rateset.count);
printf("PEER%d: MAC: %s: RSSI %d TxRate %d kbps RxRate %d kbps age : %ds\r\n",
i, wl_ether_etoa(&peer_info->ea), peer_info->rssi,
dtoh32(peer_info->tx_rate), dtoh32(peer_info->rx_rate),
dtoh32(peer_info->age));
printf("\t rateset ");
dump_rateset(peer_info->rateset.rates, peer_info->rateset.count);
printf("\r\n");
}
return 0;
}
static int
wl_aibss_txfail_config(void *wl, cmd_t *cmd, char **argv)
{
int ret = USAGE_ERROR;
aibss_txfail_config_t txfail_config;
if (!*++argv) {
/* Get */
memset(&txfail_config, 0, sizeof(txfail_config));
/* get current rateset */
if ((ret = wlu_iovar_get(wl, cmd->name, &txfail_config,
sizeof(txfail_config))) < 0)
goto error;
printf("AIBSS TXFAIL config beacon timeout duration: %d \r\n"
"Max consecutive Tx failure before TXFAIL event:%d \r\n",
txfail_config.bcn_timeout, txfail_config.max_tx_retry);
}
else {
char *p = argv[0];
char *endptr = NULL;
/* Extract the content */
if (!p || *p == '\0')
goto error;
txfail_config.bcn_timeout = strtoul(p, &endptr, 0);
p = endptr;
/* check and skip , */
if (*p == '\0' || *++p == '\0')
goto error;
txfail_config.max_tx_retry = strtoul(p, &endptr, 0);
if (*endptr != '\0')
goto error;
txfail_config.version = AIBSS_TXFAIL_CONFIG_VER_0;
txfail_config.len = sizeof(txfail_config);
ret = wlu_iovar_set(wl, cmd->name, (void *) &txfail_config,
sizeof(txfail_config));
}
error:
return ret;
}
static int
wl_bssload_static(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
wl_bssload_static_t bssload;
UNUSED_PARAMETER(cmd);
/* get */
if (!argv[1]) {
if ((err = wlu_iovar_get(wl, cmd->name, &bssload, sizeof(bssload))) < 0)
return err;
if (bssload.is_static) {
printf("station count: %d\n", dtoh16(bssload.sta_count));
printf("channel utilization: %d\n", bssload.chan_util);
printf("avail admission capacity: %d\n", dtoh16(bssload.aac));
}
}
else {
/* set */
argv++;
memset(&bssload, 0, sizeof(bssload));
if (!stricmp(*argv, "off")) {
bssload.is_static = FALSE;
}
else {
bssload.sta_count = htod16(strtoul(*argv, NULL, 0));
if (*++argv == NULL) {
printf("wl_bssload_static: "
"channel utilization not provided\n");
return -1;
}
bssload.chan_util = strtoul(*argv, NULL, 0);
if (*++argv == NULL) {
printf("wl_bssload_static: "
"avail admission capacity not provided\n");
return -1;
}
bssload.aac = htod16(strtoul(*argv, NULL, 0));
bssload.is_static = TRUE;
}
err = wlu_iovar_set(wl, cmd->name, &bssload, sizeof(bssload));
}
return err;
}
#ifdef TBTT_OFFSET_STAT
static int
wl_tbtt_offset_stat(void *wl, cmd_t *cmd, char **argv)
{
int ret;
tbtt_offset_stat_t *tbtt_offset_stat = NULL;
UNUSED_PARAMETER(argv);
/* Get only */
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, (void **)(&tbtt_offset_stat))) < 0)
return (ret);
printf("avg: %d\nmin: %d\nmax: %d\n",
dtoh32(tbtt_offset_stat->tbtt_offset_avg),
dtoh32(tbtt_offset_stat->tbtt_offset_min),
dtoh32(tbtt_offset_stat->tbtt_offset_max));
return 0;
}
#endif /* TBTT_OFFSET_STAT */
#ifdef WIN32
#pragma warning(pop)
#endif